Question

I created simple JavaScript application for drawing quadratic bezier curves. All bezier curves are part of one shape (second bezier curve starts on point where first bezier curves ends, third bezier curve starts on point where second bezier curve ends,... and last curve ends where first curve starts).

Number of bezier curves in shapes is 2 or more (not exactly specified how many).

After creating a shape, I have array of bezier curves. For example something like this:

array(3): {
   [0]:  object(8): {
      sx:  number: 130
      sy:  number: 175
      cp1x:  number: 119
      cp1y:  number: 151
      cp2x:  number: 175
      cp2y:  number: 120
      ex:  number: 212
      ey:  number: 181
   }
   [1]:  object(8): {
      sx:  number: 212
      sy:  number: 181
      cp1x:  number: 212
      cp1y:  number: 181
      cp2x:  number: 269
      cp2y:  number: 237
      ex:  number: 147
      ey:  number: 226
   }
   [2]:  object(8): {
      sx:  number: 130
      sy:  number: 175
      cp1x:  number: 130
      cp1y:  number: 175
      cp2x:  number: 147
      cp2y:  number: 226
      ex:  number: 147
      ey:  number: 226
   }
} 

I want to create the same shape but expanded for X pixels. For example, If my shape is created from 2 bezier curves that makes ellipse with radius R, I want to draw bigger ellipse with the same middle with radius R + X. I spent 3 days trying to solve this but I really don't know how to do it.

Thanks a lot for advices.

Was it helpful?

Solution

Damned Bezier Curves…I love ‘em and I hate ‘em !!

Fail#1

A bezier curve cannot be expanded using another bezier curve:

Wikepedia: The curve at a fixed offset from a given Bézier curve, often called an offset curve (lying "parallel" to the original curve, like the offset between rails in a railroad track), cannot be exactly formed by a Bézier curve.

Fail#2

If you know your curves form a closed regular shape (has a center—centroid), you could use context.translate. Problem is your example bezier set does not have a centroid. If fact, not many bezier sets have a centroid.

Should work…but the math gives headaches

  1. Calculate many X/Y’s along the path that make up the beziers.
  2. At each X/Y, calculate the tangent angle to the curve.
  3. Calculate the perpendicular angle of that tangent angle.
  4. Move outward on that perpendicular line by the length you want to expand the bezier.
  5. Save the X/Y at that expanded point.
  6. Connect all the expanded points using context.lineTo.

Simple as that you have expanded a closed set of bezier curves!

I have some math for this, but it would take time to pull it together into a finished solution. If you give it a try, I’ll help if you have problems…

OTHER TIPS

You can't offset a curve with another curve, but you can offset a curve with a poly-curve, and I explain how this is generally done over at http://pomax.github.io/bezierinfo/#offsetting -- the code is Processing, but it's pretty easy to port to (unoptimized) JavaScript. The more important part is that if you want to do this the "right" way, you have some programming (and understanding) to do. If you just want something that looks kind of right, just run through the curve at X steps (say for(var i=0; i<1; i+=0.01) {...}), find the normal at each point, offset along the normal by however many pixels you need, and connect all those dots. Linear approximations are pretty decent as long as the curves aren't thousands of pixels long.

I answered a similar question here How to generate a "thick" bezier curve?

There is a blog post explaining how to draw an offset curve http://brunoimbrizi.com/unbox/2015/03/offset-curve/

And an interactive example written in javascript: http://codepen.io/brunoimbrizi/pen/VYEWgY

// code is too big to post here, please see the source on codepen
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top