3

Using SVG and cubic beziers, I'm attempting to generate the outline of S-curve shapes of various thicknesses, widths and heights, such as these:

Rough example curves

I'm stuck on figuring out where the Bezier handle for each of the four corner nodes should be positioned in order to give the curve formed by their in-between area a constant thickness (or at least approximately).

I can see that the horizontal distance between the inner and outer handle at each end is dependent on the thickness of the curve and the overall curve width and height, but I'm stumped trying to get something that links them all together.

Is there a formula that would give me the horizontal position for each of the handles?

(I'm using d3js, so if there's a plugin or function I've missed that would help with this, that'd be even better).

nathanielw
  • 184
  • 1
  • 7
  • Have you seen [this example](https://www.jasondavies.com/animated-bezier)? – Lars Kotthoff Dec 19 '15 at 05:16
  • @LarsKotthoff Thanks for the link, I hadn't seen that. Unless I'm missing something, I don't think it helps with drawing thick curves/calculating where to position the handles though. – nathanielw Dec 19 '15 at 07:59
  • Aren't these just the negative directions of the first one ? Check [this answer](http://stackoverflow.com/a/34268505/3702797) where I had a similar approach with d3. – Kaiido Dec 19 '15 at 10:01
  • @Kaiido Unfortunately it doesn't seem to be that simple. As you say in the comment on that answer, that technique ensures the vertical thickness (i.e. vertical distance between the curve's edges) is constant, but the thickness across the curve's normals get thinner in the middle. – nathanielw Dec 19 '15 at 10:23
  • Is the structure of the SVG representation important to you? If not, you can get the same visual effect by [positioning a single Bezier between those two, and giving it a thick stroke](http://i.stack.imgur.com/KMrZ8.png). – Anko - inactive in protest Dec 20 '15 at 18:10
  • @Anko I was hoping to be able to give each end of the curve a slightly different thickness, which is why using a stroke wouldn't work. However, I've realized that the chart will work almost as well if I scale everything so that both ends of the curve are the same size, so it's looking like I will just be going with the stroked-Bezier approach. – nathanielw Dec 20 '15 at 22:39

3 Answers3

1

I recommend the Tiller-Hanson algorithm used in FreeType's ftstroke module. It is normally used for converting a zero-width line into a stroke with some finite width; you can use the way it creates one of the two envelope lines (one on either side of the original line) to get your parallel curve. It handles any line made from straight segments and quadratic and cubic Bézier splines.

For more information see my answer to this question: How to get the outline of a stroke?.

Community
  • 1
  • 1
Graham Asher
  • 1,648
  • 1
  • 24
  • 34
0

Unfortunately, unless your curve has a uniform curvature, you can't get a fixed distance offset curve simply by linear scaling the original curve.

In order to get an offset curve for a Bezier, you have to do a bit of calculus to figure out how to split up the curve into "safe" segments, which can be offset by scaling, and then scale each piece.

More details over on http://pomax.github.io/bezierinfo/#offsetting, but basically what you want is something that usually you have someone else implement for you because it's a chore to get right.

Mike 'Pomax' Kamermans
  • 49,297
  • 16
  • 112
  • 153
0

You can actually offset the control polygon of one cubic Bezier curve to generate the control polygon for the "offset" curve, which will still be a cubic Bezier curve.

The following picture shall make it clear about what does "offset control polygon" mean. Given the cubic Bezier curve defined by P0, P1, P2 and P3, we can offset the 3 infinite lines L01 (defined by P0 and P1), L12 (defined by P1 and P2) and L23 (defined by P2 and P3). The resulting lines are denoted as L01*, L12* and L23*. The intersection points of L01* and L12* will be Q1 and that between L12* and L23* will be Q2. Q0 and Q3 will be the direct projected point of P0 and P3 onto L01* and L23*. The offset curve will be the cubic Bezier curve defined by Q0, Q1, Q2 and Q3.

enter image description here

Of course, the "offset" curve created in this way is not the exact offset but just an approximation. But as long as the offset distance is not too big and you are not too picky about the accuracy, the result is generally good enough. There are two advantages of this approach (besides its simplicity):

  • the approximate offset curve will honor the true offset curve's tangent direction at the start and end of the curve.
  • if you offset the offset curve in the opposite direction by the same distance, you will get back the original cubic Bezier curve.
fang
  • 3,473
  • 1
  • 13
  • 19