Hi all, After busting my brain for two evenings, I've succeeded in creating properly rounded joins for powerstroked paths... The quick explanation: - for constant stroke width paths: looks like SVG rounded joins (circular arc) - for non-constant width paths, it is not possible to round with a circle and keep the curve smooth (tangents will not match). So the rounding is done with an elliptical arc, for which the tangents *do* match. There are many possible ellipses, and I decided for the one with minimum eccentricity. This converges to a circle for constant stroke width.
Please test and tell me if it is bugged. (bzr rev 11111)
Cheers, Johan
On 21-03-12 23:52, Johan Engelen wrote:
Hi all, After busting my brain for two evenings, I've succeeded in creating properly rounded joins for powerstroked paths... The quick explanation:
- for constant stroke width paths: looks like SVG rounded joins
(circular arc)
- for non-constant width paths, it is not possible to round with a
circle and keep the curve smooth (tangents will not match). So the rounding is done with an elliptical arc, for which the tangents *do* match. There are many possible ellipses, and I decided for the one with minimum eccentricity. This converges to a circle for constant stroke width.
Another interesting option could be a spiro curve, as it has constant change in curvature and will probably also converge to a circle.
On 22-3-2012 10:17, Jasper van de Gronde wrote:
On 21-03-12 23:52, Johan Engelen wrote:
Hi all, After busting my brain for two evenings, I've succeeded in creating properly rounded joins for powerstroked paths... The quick explanation:
- for constant stroke width paths: looks like SVG rounded joins
(circular arc)
- for non-constant width paths, it is not possible to round with a
circle and keep the curve smooth (tangents will not match). So the rounding is done with an elliptical arc, for which the tangents *do* match. There are many possible ellipses, and I decided for the one with minimum eccentricity. This converges to a circle for constant stroke width.
Another interesting option could be a spiro curve, as it has constant change in curvature and will probably also converge to a circle.
Good idea! Would it be OK to pin down the start and end tangent of the spiro curve by simply duplicating the start and end node and moving them slightly in the direction along the tangent?
Ciao, Johan
On 23-03-12 01:19, Johan Engelen wrote:
On 22-3-2012 10:17, Jasper van de Gronde wrote:
On 21-03-12 23:52, Johan Engelen wrote:
Hi all, After busting my brain for two evenings, I've succeeded in creating properly rounded joins for powerstroked paths... The quick explanation:
- for constant stroke width paths: looks like SVG rounded joins
(circular arc)
- for non-constant width paths, it is not possible to round with a
circle and keep the curve smooth (tangents will not match). So the rounding is done with an elliptical arc, for which the tangents *do* match. There are many possible ellipses, and I decided for the one with minimum eccentricity. This converges to a circle for constant stroke width.
Another interesting option could be a spiro curve, as it has constant change in curvature and will probably also converge to a circle.
Good idea! Would it be OK to pin down the start and end tangent of the spiro curve by simply duplicating the start and end node and moving them slightly in the direction along the tangent?
That's a good point. In principle spiro curves should simply accept a start and end tangent angle, but I see that the existing code does not cater for this... I think the most principled approach would be to indeed duplicate the start and end node, but then move them in the other direction (so in the direction opposite to what you expect for Bezier curves), in combination with one-way constraints as described in Raph Levien's thesis. As far as I can tell from the source code (I haven't tried it yet) this should be supported. Unfortunately the code does not seem to be very well documented, so it might take some experimenting to get the right node types (it seems to support eight, while his thesis only gives six options, which makes it less obvious how they correspond).
I would expect that you'd have to feed it a path like this: {x0-dx0 , y0-dy0 , "startNode"} {x0 , y0 , "one way from left"} {x1 , y1 , "one way from right"} {x1+dx1 , y1+dy1 , "endNode"}
The dx/dy values are assumed to be normalized (or not, really shouldn't matter, as you're going to trim off the first and last segment anyway), and pointing in the direction of the parametrization (so if you take them to point "inwards", as if they were Bezier control points, then you'd have to change the signs in the definition of the last point).
The strings stand for the "ty"'s that you'd still have to figure out. My guess is that the first and last ty should be "{" and "}", respectively, so that only leaves the middle two to be determined (you might want to try "[" and "]").
Once you run something like the above through "run_spiro", I would expect to effectively get three segments again (defined using four spiro_cp structures whose x,y coordinates are the same as for the input). Of these segments you only need the middle one, so the first and last spiro_cp structure could be thrown away, and you should probably reset the ty's of the remaining two spiro_cp structures to indicate that they are the start and end node.
The above is completely untested, merely based on inspection of the code, but I think it should work.
On 23-3-2012 10:11, Jasper van de Gronde wrote:
On 23-03-12 01:19, Johan Engelen wrote:
On 22-3-2012 10:17, Jasper van de Gronde wrote:
On 21-03-12 23:52, Johan Engelen wrote:
Hi all, After busting my brain for two evenings, I've succeeded in creating properly rounded joins for powerstroked paths... The quick explanation:
- for constant stroke width paths: looks like SVG rounded joins
(circular arc)
- for non-constant width paths, it is not possible to round with a
circle and keep the curve smooth (tangents will not match). So the rounding is done with an elliptical arc, for which the tangents *do* match. There are many possible ellipses, and I decided for the one with minimum eccentricity. This converges to a circle for constant stroke width.
Another interesting option could be a spiro curve, as it has constant change in curvature and will probably also converge to a circle.
Good idea! Would it be OK to pin down the start and end tangent of the spiro curve by simply duplicating the start and end node and moving them slightly in the direction along the tangent?
That's a good point. In principle spiro curves should simply accept a start and end tangent angle, but I see that the existing code does not cater for this... I think the most principled approach would be to indeed duplicate the start and end node, but then move them in the other direction (so in the direction opposite to what you expect for Bezier curves), in combination with one-way constraints as described in Raph Levien's thesis. As far as I can tell from the source code (I haven't tried it yet) this should be supported. Unfortunately the code does not seem to be very well documented, so it might take some experimenting to get the right node types (it seems to support eight, while his thesis only gives six options, which makes it less obvious how they correspond).
I would expect that you'd have to feed it a path like this: {x0-dx0 , y0-dy0 , "startNode"} {x0 , y0 , "one way from left"} {x1 , y1 , "one way from right"} {x1+dx1 , y1+dy1 , "endNode"}
The dx/dy values are assumed to be normalized (or not, really shouldn't matter, as you're going to trim off the first and last segment anyway), and pointing in the direction of the parametrization (so if you take them to point "inwards", as if they were Bezier control points, then you'd have to change the signs in the definition of the last point).
The strings stand for the "ty"'s that you'd still have to figure out. My guess is that the first and last ty should be "{" and "}", respectively, so that only leaves the middle two to be determined (you might want to try "[" and "]").
Once you run something like the above through "run_spiro", I would expect to effectively get three segments again (defined using four spiro_cp structures whose x,y coordinates are the same as for the input). Of these segments you only need the middle one, so the first and last spiro_cp structure could be thrown away, and you should probably reset the ty's of the remaining two spiro_cp structures to indicate that they are the start and end node.
The above is completely untested, merely based on inspection of the code, but I think it should work.
Yep, I arrived at the same solution :) See rev. 11128
-Johan
participants (2)
-
Jasper van de Gronde
-
Johan Engelen