How do I calculate pairs of Bezier S-curves such that the space between them has constant thickness? - d3.js

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:
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).

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?.

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.

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.
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.

Related

Point of intersection between bezier curve and circle

I am aiming to create the following (a directed arrow that connects two nodes) :
At the moment I have this (a quadratic bezier curve drawn from the center point of one node to the center of another):
(Note I have drawn the bezier above the nodes to show where it begins and ends)
I need a method - heuristic or otherwise - to calculate the point of intersection (circled in red, above) between the bezier curve and the node's (ellipse) circumference.
With this, I could calculate the angle between the node center and the point of intersection to draw the arrow head lines at the correct location and angle.
As a last resort, I could use the quadratic Bézier formula to generate a list of points that lie along the curve and also generate a list of points that lie on the circumference of the circle and use one of the two coordinates that have the least euclidian distance between each other as my intersection point. I'm hoping any answers can leverage geometry or whatever else to better solve it.
The general problem is uneasy as the intersection equation is quartic ((X(t)-Xc)² + (Y(t)-Yc)²=R²), where X and Y are quadratic polynomials). If you have a quartic solver handy you can use it but you'll have to select the right root.
A more reasonable approach is just to intersect the circle with the line segment between the control points. This is approximate but probably unnoticeable if the circle radius is small.
If you want more accuracy, perform one or two Newton's iterations from this point.

Bezier curve fitting with known end points

I want to fit a bezier curve with known end points (p0 and p3) to noisy 2d data. This seems like an easier problem than traditional 4-point bezier curve fitting but still too hard for me to figure out.
Can someone point me to existing code or an algorithm to find the best values for the control points p1 and p2?
edit: The points that I'm trying to fit with a bezier curve comes from curves drawn with a mouse (imagine drawing something with a brush in Paint, there could be hundreds of recorded points in one long stroke). The anchor points p0 and p3 are created in advance but the control points p1 and p2 should be calculated so that the bezier fits the shape of the curve sketched out with the mouse.
I stumbled on a paper called "Approximation of data using cubic Bezier curve least square fitting" by "M.Khan" which describes an algorithm to calculate the exact thing I'm looking for.
Implementation in javascript was easy. It works quite good and is fast but the resulting bezier curves are not perfect. Could be a bug in my code but I suspect that better curves could be obtained by iteratively adjusting the matching points on the bezier curve to better fit the data .
edit: It turns out you can use newton-raphson to optimize each individual t-value for the bezier curve. After doing that the curve fits great, atleast for curves with only few points that don't self intersect but I have to do some more testing.

Creating a circular arc tangent to two curves with specified radius

The operation mentioned in the title is common in many Computer Aided Design (CAD) softwares such as AutoCAD, where it is called fillet. However, I found it is really difficult to implement this function in my own program.
The method I thought of is to use the condition that the distances of the arc center to the tangent lines of the curves are equal to the specified radius. Considering that actual curves are defined with piece-wise nonlinear functions, and the contact points could be anywhere on the curves, it is not easy to get the solution. Anyone good ideas?
Given that you don't describe in enough details the characteristics of the curves, it's hard to come with a specific/specified algo, but let's try a descriptive approach:
take a circle of the given radius and roll it on one curve until the circle touches the other one.
I assume you can parametrize you curves.
To "roll the circle" along the curve you need the tangent (or better said the normal, which of course is normal to the tangent) in the point "rolling track curve"-to-circle tangent point. You have this normal, you know the radius, you can compute your circle. You have the circle, you can see if/where it intersects the other curve.
The idea of "rolling" is to bracket your solution (parameter of the tangent-point on one curve) between a point when the circle does not intersect the other curve and another point where it intersects (possible in more than 1 point).
Once you have the bracket, go with a bisection method (binary search) between the two positions until your circle becomes "tangent enough" to the other curve (i.e the intersection points with the other curve are so close that they fall below your acceptable epsilon).
You will now have two points (one on each curves) and the circle that realizes the solution: just keep the arc on this circle corresponding what make sense (based on the convergence or divergence of the two tangents).
To find the arc center you need two robust and strategic algorithms:
Curve offset
Curve intersection
What AutoCAD does to find the arc center is to offset the two curves of the arc radius distance and intersect them. Depending on the curve offset direction you can easily switch between all possible solutions to the problem.
At this point, trimming the curves at tangent points will be trivial.

Sort cubic bezier splines around a point by outgoing tangent

I need to sort cubic bezier splines around a point by their outgoing tangent. My first attempt was to determine the outgoing tangent angle and sort by that. For a spline with four points p0, p1, p2, p3, the outgoing tangent angle is:
p0 != p1 ? angle(p0, p1) : p0 != p2 ? angle(p0, p2) : angle(p0, p3)
This handles degenerate a cubic that is actually a quadratic or even a line. However, the point may have two outgoing splines with the same tangent angle but a different control point or end point position later on down the spline that impacts the sorting order.
Is there a nice closed-form algorithm for sorting two arbitrary bezier splines by outgoing angle that handles degenerate cases and also uses the rest of the spline to disambiguate cases when the tangents are equal? I can do it by trying t values until I disambiguate but it seems like there could be a closed-form algorithm.
As far as I am aware of, there is no closed form solution for this particular problem.
Consider that you wish to sort a set of curves by the outgoing tangent at a point; but if the tangents are equal at that point, the curves are already sorted! I understand that what you actually want is to find out whether the tangents diverge as you move along the curve away from the starting point.
So let's do that; compare the tangents at a small increment from t = 0 (t is the curve-time parameter of any parametric curve).
Here is a small demo in javascript.
The compareTan function compares the angle of the tangent at a small increment (t = 1e-5). All it depends on is a method to return the tangent at a given t parameter. It is not very hard to implement the quadratic case —for lines, you can compare them directly.

Reverse engineering a bezier curve

Given a few sample points on a bézier curve, is it possible to work out the set of possible curves these points may lie on?
In my specific application there is a limited set of endpoints the curve may have, so I want to generate the set of possible curves, enumerate all of them and pick out all the ones which may end on a valid end point.
Some people have asked for more detail.
I have a set of points which I know are on a quadratic bezier curve, I want to calculate the formula of the curve and be able to extrapolate new points on the curve.
Bezier curves will always go through starting and ending control points.
If the degree of the curve is equal to the number of sample points then there will be only one curve that will pass through all your points (in a normal case, where all points are different and they don't lie on a bezier curve of a lesser degree).
If the degree of a curve is less then the number of the sample points then, in general case, you will not be able to make the curve go through all the points (in a normal case).
If the degree of a curve is higher then the number of the sample points then, in general case, you will be able to draw infinite number of curves.
In the wiki article you will find references to control points only, but still I believe that I remember the above properties correctly and that they hold for the points on the curves as well.
I think you need to redefine your question and exactly define what type of curves (and of which degree) do you need. Also as Georg pointed out you might be looking for paths - a combination of curves.
EDIT:
First a correction - curve is defined with degree plus one number of control points points (quadratic need three). Control points are not the same as points on the curve - and for three points on the curve and quadratic curve you could have infinite number of solutions (see this for quadratic curve and four points)
As for the solution (but still under assumption that you are looking at a single curve):
For an equation for single quadratic curve you have
B(t) = (1-t)^2*P0 + 2*(1-t)*t*P1 + t^2*P2
Capital letters above are vectors, and P0 corresponds to starting control point (first point), P2 corresponds to ending control point (last point), so you still need to find P1. The variable t is scalar that ranges from 0 to 1.
If working with 2D curves the above vector equation gives two scalar equations for each point on the curve.
Still there is t as an unknown, so you should take 2 more points (4 in total) which will give you 4 unknowns (t for first point, t for second point, x and y of the P1, middle control point) and 4 equation to solve (2 from each sample point).
Solve that with your favourite numerical method and you will get the original curve on which the points came from.
If you still think that you can get more curves and that you will have to choose something then you are not working with bezier curves, but with bezier splines (in a sense of multiple curves joined together). Still the same principle applies and if you work out a way to solve a single curve from the above equations (and if you have enough points) then you can divide the problem into n-segments of actual bezier curves and solve each as outlined above.
If it turns out that you don't have enough points then look at the linked article again - you are probably looking for the smoothest curve and there are some suggestions in the article on how to get there as looking for the exact solution (shortest curve/smoothest curve) seems to be rather complex.

Resources