New SVG close path command.
Hi,
I'm sure we've talked about it before but in the context of discussing the new Catmull-Rom path commands[1] yesterday at the SVG Working group meeting we briefly discussed adding a new close path command that would insure smoothness when closing a path. There is significant support for doing so. Can somebody whip up a quick proposal that I can present to the group? (SVG with a couple of figures would be good.) Or at least give me some input into the idea. We have two more days of meetings, Monday and Tuesday.
Thanks,
Tav
[1] https://svgwg.org/svg2-draft/paths.html#PathDataCatmullRomCommand
Hi Tav, In the Catmull-Rom path specification, what is missing is how to form the segment between (x1,y1) and (x2,y2). Catmull-Rom forms a segment between p2 and p3 when points p1,p2,p3, and p4 are given. The draft says to start drawing a path from p1. Does that mean that the start point is doubled in the calculation? (powerstroke's behavior at the moment, doubling the start and end knots)
Issue 9: Yuksel et al. "On the Parameterization of Catmull-Rom Curves", http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf
-Johan
On 24-8-2014 8:49, Tavmjong Bah wrote:
Hi,
I'm sure we've talked about it before but in the context of discussing the new Catmull-Rom path commands[1] yesterday at the SVG Working group meeting we briefly discussed adding a new close path command that would insure smoothness when closing a path. There is significant support for doing so. Can somebody whip up a quick proposal that I can present to the group? (SVG with a couple of figures would be good.) Or at least give me some input into the idea. We have two more days of meetings, Monday and Tuesday.
Thanks,
Tav
[1] https://svgwg.org/svg2-draft/paths.html#PathDataCatmullRomCommand
Hello Johan,
Sunday, August 24, 2014, 12:19:06 PM, you wrote:
In the Catmull-Rom path specification, what is missing is how to form the segment between (x1,y1) and (x2,y2).
That has changed as of yesterday; see minutes http://www.w3.org/2014/08/23-svg-minutes.html#item03
Effectively these points are now all shifted over by one; the first drawn segment goes from currentPoint(x,y) to x1,y1 (i.e from where the previously drawn path segment ends).
Hi Chris, Seems like a good change, so it behaves similar to bezier segments.
Then my question becomes, how to form the segment between currentPoint(x,y) and (x1,y1)? Is currentPoint(x,y) to be doubled? Would be fine to me.
The other question is, at which point to stop drawing. I read that people want 2 points to result in a straight line, i.e. M 0,0 r 1,1 should create a straight line. However, there are only 2 points there. This would need startpoint and endpoint doubling. Again, would be fine to me. It conflicts with the current draft where it is specified that drawing stops at second to last point.
regards, Johan
On 24-8-2014 21:00, Chris Lilley wrote:
Hello Johan,
Sunday, August 24, 2014, 12:19:06 PM, you wrote:
In the Catmull-Rom path specification, what is missing is how to form
the segment between (x1,y1) and (x2,y2).
That has changed as of yesterday; see minutes http://www.w3.org/2014/08/23-svg-minutes.html#item03
Effectively these points are now all shifted over by one; the first drawn segment goes from currentPoint(x,y) to x1,y1 (i.e from where the previously drawn path segment ends).
On Sun, 2014-08-24 at 21:38 +0200, Johan Engelen wrote:
Hi Chris, Seems like a good change, so it behaves similar to bezier segments.
Then my question becomes, how to form the segment between currentPoint(x,y) and (x1,y1)? Is currentPoint(x,y) to be doubled? Would be fine to me.
The other question is, at which point to stop drawing. I read that people want 2 points to result in a straight line, i.e. M 0,0 r 1,1 should create a straight line. However, there are only 2 points there. This would need startpoint and endpoint doubling. Again, would be fine to me. It conflicts with the current draft where it is specified that drawing stops at second to last point.
Draft has probably not been fully updated. In the case you asked about, the "zeroth" point would be the second point mirrored around the first (-1,-1) and the "third" point would be the first mirrored around the second (2,2).
If there is a Bezier to Catmull-Rom transition, the "zeroth" point would be the previous "handle" point so that the line is smooth across the node. This behavior can be inhibited by inserting a 'm 0,0' into the path between the Bezier and Catmull-Rom.
regards, Johan
On 24-8-2014 21:00, Chris Lilley wrote:
Hello Johan,
Sunday, August 24, 2014, 12:19:06 PM, you wrote:
In the Catmull-Rom path specification, what is missing is how to form
the segment between (x1,y1) and (x2,y2).
That has changed as of yesterday; see minutes http://www.w3.org/2014/08/23-svg-minutes.html#item03
Effectively these points are now all shifted over by one; the first drawn segment goes from currentPoint(x,y) to x1,y1 (i.e from where the previously drawn path segment ends).
On 24-8-2014 21:54, Tavmjong Bah wrote:
On Sun, 2014-08-24 at 21:38 +0200, Johan Engelen wrote:
Hi Chris, Seems like a good change, so it behaves similar to bezier segments.
Then my question becomes, how to form the segment between currentPoint(x,y) and (x1,y1)? Is currentPoint(x,y) to be doubled? Would be fine to me.
The other question is, at which point to stop drawing. I read that people want 2 points to result in a straight line, i.e. M 0,0 r 1,1 should create a straight line. However, there are only 2 points there. This would need startpoint and endpoint doubling. Again, would be fine to me. It conflicts with the current draft where it is specified that drawing stops at second to last point.
Draft has probably not been fully updated. In the case you asked about, the "zeroth" point would be the second point mirrored around the first (-1,-1) and the "third" point would be the first mirrored around the second (2,2).
If there is a Bezier to Catmull-Rom transition, the "zeroth" point would be the previous "handle" point so that the line is smooth across the node. This behavior can be inhibited by inserting a 'm 0,0' into the path between the Bezier and Catmull-Rom.
Any specific reason for mirroring? The result is not identical to node-doubling. I thought node-doubling is more common.
-Johan
Hello Johan,
Sunday, August 24, 2014, 9:38:55 PM, you wrote:
Hi Chris, Seems like a good change, so it behaves similar to bezier segments.
Then my question becomes, how to form the segment between currentPoint(x,y) and (x1,y1)? Is currentPoint(x,y) to be doubled? Would be fine to me.
If the previous curve command provides a tangent and position (e.g. a cubic or quadratic curve) then this is used as a virtual (x-1, y-1). Otherwise, the line cp(x,y) to (x1,y1) is used as the tangent and virtual point (x-1, y-1) is reflected along that line by the euclidian distance between cp(x,y) to (x1,y1).
If that is clear (hopefully) then there is a further refinement; as we are using centrepetial Catmull-Rom the distances are not uniform; in the Centripetal case, each time interval is the square root of the Euclidean distance between the points. http://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline#Methodol...
Centrepetial Catmull-Rom avoids the tendency to form self-intersecting loops if the points are close together (you can see this happen in Doug's dotty example code, also linked from the minutes, which uses uniform Catmull-Rom). http://schepers.cc/svg/path/dotty.svg
The spec will be updated to reflect these decisions in the next few days (as I said these discussion happened Saturday).
The other question is, at which point to stop drawing. I read that people want 2 points to result in a straight line, i.e. M 0,0 r 1,1
Now, you always have two points because current point is one of them, so x1 y1 in the R command is the second. This will give a straight line. Three points (cp plus two in the R command) will give a curve.
I'm thinking this may need an appendix (like the elliptical arc appendix) to explain the general case where there are four points in hand (xi-1 yi-1),(xi yi), (xi+1 yi+1), (xi+2, yi+2) to draw the curve between (xi yi), (xi+1 yi+1) and the degenerate cases near the start and end of the command which require use of currentPoint and also the computation of virtual points so that each curve segment has the required four points.
If all that is clear (please ask if not) then if should be clear that given a single point in the R command:
R(x1,y1)
plus the always existing current point xcp, ycp
there are three cases for joining to the previous path segment:
a) the previous command provides a tangent and distance; these are used to make the virtual first point (using the square root of the distance between the virtual point and xcp, ycp)
b) the previous command provides a tangent only; this plus the reflected distance to x1,y1 are used to make the virtual first point (using the square root of the distance between the virtual point and xcp, ycp)
c) the previous command provides no tangent (e.g M or m); the tangent from xcp,ycp to x1,y1 is used to provide the tangent and the distance (using the square root of the distance between xcp, ycp and x1,y1)
similarly for the last curve segment produced by the R command there are four cases:
a) the next command provides a tangent and distance; these are used to make the virtual last point (using the square root of the distance between the virtual point and y1, y1)
b) the next command provides a tangent only; this plus the reflected distance to xcp,ycp are used to make the virtual first point (using the square root of the distance between the virtual point and x1, y1)
c) the next command provides no tangent (e.g M or m) or no n=tangent and no next point (e.g. no next command); the tangent from x1,y1 xcp,ycp is used to provide the tangent and the distance (using the square root of the distance between xcp, ycp and x1,y1)
If the previous path is case c and the next is also case c, this will result in a straight line because only xcp,ycp and x1,y1 control the curve; the other two points are virtual and unaffected by previous or next path segments. In the other combinations, some sort of curve will be produced. If the previous path is case a and the next is also case a, then the curve will exhibit curve continuity with the previous and next segments.
If that is still clear(!) we can return to the subject of the thread and consider some new closepath command (not Z, lets call it Y) which attempts to close up the path with a curve rather than a straight line segment as Z do.
Like Z, Y takes no arguments. It has available the current point xcp, ycp and also the start of the path xs,ys.
I propose that Y be defined to be equivalent to R (xs,ys). Unless the last path segment is case c and the first path segment is case c, this will result in a curve. (If they are both case c t=you get a line same as Z, so it is at least well defined). If ther last path segment is case a and the first is also case a you will have curve continuity at both ends of the closepath curve. Otherwise, you willat least get a curve rather than a straight line.
Thanks for the details.
I implemented centripetal Catmull-Rom interpolation in Inkscape experimental branch yesterday (with start and endnode doubling). If you want to play with it, try LPE Interpolate points or Catmull-Rom interpolation in LPE Powerstroke.
(Side remark: Catmull-Rom paths will probably cause a lot of headaches to mix conveniently with bezier paths in Inkscape/2Geom, because Catmull-Rom "segments" are not independent.)
regards, Johan
On 24-8-2014 22:49, Chris Lilley wrote:
Hello Johan,
Sunday, August 24, 2014, 9:38:55 PM, you wrote:
Hi Chris, Seems like a good change, so it behaves similar to bezier segments. Then my question becomes, how to form the segment between currentPoint(x,y) and (x1,y1)? Is currentPoint(x,y) to be doubled? Would be fine to me.
If the previous curve command provides a tangent and position (e.g. a cubic or quadratic curve) then this is used as a virtual (x-1, y-1). Otherwise, the line cp(x,y) to (x1,y1) is used as the tangent and virtual point (x-1, y-1) is reflected along that line by the euclidian distance between cp(x,y) to (x1,y1).
If that is clear (hopefully) then there is a further refinement; as we are using centrepetial Catmull-Rom the distances are not uniform; in the Centripetal case, each time interval is the square root of the Euclidean distance between the points. http://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline#Methodol...
Centrepetial Catmull-Rom avoids the tendency to form self-intersecting loops if the points are close together (you can see this happen in Doug's dotty example code, also linked from the minutes, which uses uniform Catmull-Rom). http://schepers.cc/svg/path/dotty.svg
The spec will be updated to reflect these decisions in the next few days (as I said these discussion happened Saturday).
The other question is, at which point to stop drawing. I read that people want 2 points to result in a straight line, i.e. M 0,0 r 1,1
Now, you always have two points because current point is one of them, so x1 y1 in the R command is the second. This will give a straight line. Three points (cp plus two in the R command) will give a curve.
I'm thinking this may need an appendix (like the elliptical arc appendix) to explain the general case where there are four points in hand (xi-1 yi-1),(xi yi), (xi+1 yi+1), (xi+2, yi+2) to draw the curve between (xi yi), (xi+1 yi+1) and the degenerate cases near the start and end of the command which require use of currentPoint and also the computation of virtual points so that each curve segment has the required four points.
If all that is clear (please ask if not) then if should be clear that given a single point in the R command:
R(x1,y1)
plus the always existing current point xcp, ycp
there are three cases for joining to the previous path segment:
a) the previous command provides a tangent and distance; these are used to make the virtual first point (using the square root of the distance between the virtual point and xcp, ycp)
b) the previous command provides a tangent only; this plus the reflected distance to x1,y1 are used to make the virtual first point (using the square root of the distance between the virtual point and xcp, ycp)
c) the previous command provides no tangent (e.g M or m); the tangent from xcp,ycp to x1,y1 is used to provide the tangent and the distance (using the square root of the distance between xcp, ycp and x1,y1)
similarly for the last curve segment produced by the R command there are four cases:
a) the next command provides a tangent and distance; these are used to make the virtual last point (using the square root of the distance between the virtual point and y1, y1)
b) the next command provides a tangent only; this plus the reflected distance to xcp,ycp are used to make the virtual first point (using the square root of the distance between the virtual point and x1, y1)
c) the next command provides no tangent (e.g M or m) or no n=tangent and no next point (e.g. no next command); the tangent from x1,y1 xcp,ycp is used to provide the tangent and the distance (using the square root of the distance between xcp, ycp and x1,y1)
If the previous path is case c and the next is also case c, this will result in a straight line because only xcp,ycp and x1,y1 control the curve; the other two points are virtual and unaffected by previous or next path segments. In the other combinations, some sort of curve will be produced. If the previous path is case a and the next is also case a, then the curve will exhibit curve continuity with the previous and next segments.
If that is still clear(!) we can return to the subject of the thread and consider some new closepath command (not Z, lets call it Y) which attempts to close up the path with a curve rather than a straight line segment as Z do.
Like Z, Y takes no arguments. It has available the current point xcp, ycp and also the start of the path xs,ys.
I propose that Y be defined to be equivalent to R (xs,ys). Unless the last path segment is case c and the first path segment is case c, this will result in a curve. (If they are both case c t=you get a line same as Z, so it is at least well defined). If ther last path segment is case a and the first is also case a you will have curve continuity at both ends of the closepath curve. Otherwise, you willat least get a curve rather than a straight line.
On Sun, Aug 24, 2014 at 11:18:10PM +0200, Johan Engelen wrote:
Thanks for the details.
Yes, thanks Chris. This is indeed exciting times for SVG.
I implemented centripetal Catmull-Rom interpolation in Inkscape experimental branch yesterday (with start and endnode doubling). If you want to play with it, try LPE Interpolate points or Catmull-Rom interpolation in LPE Powerstroke.
Nice work!
(Side remark: Catmull-Rom paths will probably cause a lot of headaches to mix conveniently with bezier paths in Inkscape/2Geom, because Catmull-Rom "segments" are not independent.)
Could you write a proposal for how to represent these overlapped curve structures (spiros and b-splines have the same problem). Something simple is probably a good idea.
njh
2014-08-24 8:49 GMT+02:00 Tavmjong Bah <tavmjong@...8...>:
Hi,
I'm sure we've talked about it before but in the context of discussing the new Catmull-Rom path commands[1] yesterday at the SVG Working group meeting we briefly discussed adding a new close path command that would insure smoothness when closing a path. There is significant support for doing so. Can somebody whip up a quick proposal that I can present to the group? (SVG with a couple of figures would be good.) Or at least give me some input into the idea. We have two more days of meetings, Monday and Tuesday.
What I would like to see is a close path command that does not create a new segment but can instead replace the final point of any other command; for instance, X. One would then write something like:
M -5,0 A 5,5 0 0 0 5,0 A 5,5 0 0 0 X
to draw a circle of radius 5 centered at 0,0. Under the current spec, special care needs to be taken when writing out the last segment, because otherwise numerical errors can cause this extra closing segment to unexpectedly appear.
Regards, Krzysztof
On Mon, 2014-08-25 at 09:11 +0200, Krzysztof Kosiński wrote:
2014-08-24 8:49 GMT+02:00 Tavmjong Bah <tavmjong@...8...>:
Hi,
I'm sure we've talked about it before but in the context of discussing the new Catmull-Rom path commands[1] yesterday at the SVG Working group meeting we briefly discussed adding a new close path command that would insure smoothness when closing a path. There is significant support for doing so. Can somebody whip up a quick proposal that I can present to the group? (SVG with a couple of figures would be good.) Or at least give me some input into the idea. We have two more days of meetings, Monday and Tuesday.
What I would like to see is a close path command that does not create a new segment but can instead replace the final point of any other command; for instance, X. One would then write something like:
M -5,0 A 5,5 0 0 0 5,0 A 5,5 0 0 0 X
to draw a circle of radius 5 centered at 0,0. Under the current spec, special care needs to be taken when writing out the last segment, because otherwise numerical errors can cause this extra closing segment to unexpectedly appear.
Regards, Krzysztof
I presented this to the WG but didn't get very far. We considered three different syntax alternatives but someone seemed to object to each one. One member in particular didn't see a problem with the current spec but I think I can convince the group if I prepare a demo ahead of time to show problems with rendering (markers is one place I know about where there is a problem).
Tav
participants (5)
-
Chris Lilley
-
Johan Engelen
-
Krzysztof Kosiński
-
Nathan Hurst
-
Tavmjong Bah