From: Johan Engelen <jbc.engelen@sw...>  20121012 21:11:55

On 12102012 14:59, Jasper van de Gronde wrote: > On 20121010 09:52, Tavmjong Bah wrote: >> For my Cairo hack, I used the last three points (two handles, end >> point) of the cubic Bezier curve to determine the curvature. If the >> last two points are degenerate, I uses the start point, first handle, >> and end point. This seems to work fine (and as Cairo only uses cubic >> Bezier and lines at the rendering point I don't have to worry about >> arcs, quadratic Beziers, etc.). The math for finding the curvature of >> a cubic Bezier can be found at: >> http://tavmjong.free.fr/SVG/LINEJOIN/index.html > Nice exposition! The cross product is illdefined in 2D though, so what > you can do is take the determinant of the matrix with row (or column > vectors) corresponding to v and v'. Alternatively, you can extend the > vectors to be 3D of course, but that would essentially just be a > roundabout way to compute a determinant of a 2x2 matrix. > > To deal with zerolength handles, the easiest (and correct) method is > probably to specify that the curvature is zero at the endpoint with the > zerolength handle (if both handles are zerolength, then the Bézier > curve forms a line segment). That this is correct can be derived from > the fact that the unit tangent vector at the endpoint with a zerolength > handle points in the same direction as the second derivative (so locally > it's a straight line). This is a consequence of l'Hôpital's rule btw. I don't think this is correct. It is possible to make segments with and without a zerolength handle that look exactly the same. Also, as you are moving the handle closer and closer to the knot, it does not converge to zero curvature, in fact it tends to lead to higher curvature instead of lower curvature. Wouldn't l'Hopital imply that you can get away by using +1 higher derivs instead? > > If the first handle is zerolength and the second handle corresponds to > the starting point, then again the whole curve is straight, and the > curvature should be zero everywhere. > > One way of doing this "automatically", is to simply add a small epsilon > to the denominator (or clamp it to some small positive value). (Perhaps > a bit obvious, but it's nice to know that it actually "does the right > thing".) >> ... I noticed in your code that if the two circles don't intersect, you fall >> back to bevel join. I am thinking in the SVG 2 spec about falling back >> to miter join. > It might make more sense to use (something like) miterlimit, in some > cases at least. Essentially there are two cases, either one circle is > inside the other, or not. If they are "inside" each other, it might > simply make perfect sense to just fill the area between the two circles, > unless the area gets (really) large, then you'd probably want to limit > it (using something akin to miterlimit). If the circles are not inside > each other, the area "between" is always infinite (the entire plane > minus the inside of the two circles). In that case, you really have to > limit the extent of the join. But even then, it should be possible to > still show part of the "true" join. > > One possible way to specify the restriction for this kind of join would > be to limit the arclength on either side of the join. The main problem > is then joining the end points, as a line segment between the two is not > guaranteed to not intersect the circles. Some experimentation suggests > that it might be possible to define a sensible boundary using > interpolation between the two end points that is guaranteed not to > intersect either of the circles. I have no clue what you are saying here. :) I am starting to strongly dislike the miter fallback btw, simply because of it not being clear whether you have arc extension or a miter. Bevel fallback has a much better UI experience for me. Cheers, Johan 