Spiro splines are a novel way of defining curvilinear paths developed by Raph Levien. Recently, Spiro support was added to the FontForge font editor, and now it is available in Inkscape too. It takes some getting used to, but for certain tasks Spiros have a clear advantage over Bezier curves.
A Spiro path is defined by a sequence of points, but unlike a regular path with Bezier curves, all Spiro points lie on the path and there are no off-path handles. The curvature of the path is defined entirely by the positions of the points and their types. The path behaves very similar to a springy rod which is forced to pass through the given points and which uses the minimum possible curvature to satisfy the requirement. As such, it feels quite natural and the resulting path is very smooth - with the kind of smoothness you can achieve with Beziers only after much tweaking.
To create a Spiro path, select any path and assign the "Spiro spline" path effect to it. There are no parameters. Each node of your path becomes a point of a Spiro path, depending on the type of node:
* Smooth nodes (those with two collinear Bezier handles) become curve points of the Spiro path; such points bend the curve but must remain smooth. Note that the length or direction of the Bezier handles of the source path is ignored; the only thing that matters is that the node is smooth.
* Cusp nodes of the source path become corner points of the Spiro path. Between two corner points, the path is always a straight line.
* Half-smooth nodes - those with one Bezier handle collinear with a straight line segment on the other hand - become "left" or "right" points on the Spiro path which behave exactly the same: they sit between a straight line and a curve and enforce that these two segments join smoothly without a cusp.
Note that what matters is the actual collinearity of a node's handles, regardless of the node type that the node has in the Node tool; for example, if a cusp (diamond-shaped) node has collinear handles, it will become a curve point of the Spiro path. For creating half-smooth nodes, use Ctrl+dragging of the node handle to make it snap to the direction of the straight line segment on the other side, or press Shift+S to lock it to that direction.
Some configurations of points do not converge and produce wild loop and spirals instead of a smooth curve. According to Raph, "The spline solver in this release is _not_ numerically robust. When you start drawing random points, you'll quickly run into divergence. However, "sensible" plates based on real fonts usually converge." Avoid too sharp changes in direction between points to prevent divergence. Hopefully, the robustness of the algorithm will be improved in future releases.
For now, to edit Spiro paths viewing the result in real time, you have to use the Node tool. The Pen tool does not yet allow you to preview a Spiro as you draw, although you can paste the Spiro effect on the path and see the result as soon as the path is finalized. You can always use the Node tool to continue a Spiro path by duplicating and dragging away its end nodes. Also, when you have a Spiro path selected, you can add a new subpath to it with Pen or Pencil if you start drawing with Shift.
Raph: Thanks for your code, and let me know when you have a new version :) I have copied only spiro.c/h and bezctx.c/h to Inkscape tree, renaming .c to .cpp and removing debug output, but otherwise unchanged.
On Sat, Apr 12, 2008 at 7:18 PM, Alexandre Prokoudine <alexandre.prokoudine@...400...> wrote:
On Sat, Apr 12, 2008 at 1:37 PM, bulia byak wrote:
Spiro splines are a novel way of defining curvilinear paths developed by Raph Levien. Recently, Spiro support was added to the FontForge font editor, and now it is available in Inkscape too.
Oh. My. God!
Alexandre
Holy Sh..... weeet!
rock rock rock rock rock
just tried out the Spiro Splines and I am awestruck, this is completely intuitive and 'self-evident', congratulations and bravo ... question, there is this temporary red spline that appears for a second and then disappears, and bears no relationship to the actual spline, where does it come from? regardless, who said math wasn't fun ?
On Fri, Apr 25, 2008 at 8:36 PM, Alvin Penner <penner@...1856...> wrote:
just tried out the Spiro Splines and I am awestruck, this is completely intuitive and 'self-evident', congratulations and bravo ... question, there is this temporary red spline that appears for a second and then disappears, and bears no relationship to the actual spline, where does it come from?
it's the original path from which your spiro uses only nodes
go to prefs, tools, node, and disable "flash on mouseover"
bulia byak wrote:
go to prefs, tools, node, and disable "flash on mouseover"
that solved it, thanks.
I feel dumb, but after having read everything here, I still have no idea how to turn this feature on. It sounds like it's in the effects menu, but I can't find anything there or elsewhere. Using latest trunk build.
Rygle
OK, found it, but it's sure buried.
Rygle.
rygle wrote:
I feel dumb, but after having read everything here, I still have no idea how to turn this feature on. It sounds like it's in the effects menu, but I can't find anything there or elsewhere. Using latest trunk build.
Rygle
I have removed the default helper path highlighting for paths with the Spiro effect, and made a couple improvements in the Node tool shortcuts:
* If a node is already cusp (diamond shaped), pressing Shift+C again on it will retract both its handles. As this works for any number of selected nodes, you can always retract all handles in all nodes by selecting all nodes and pressing Shift+C twice. * If a non-smooth node is next to a straight line segment, pressing Shift+S once makes it half-smooth: it now has one handle aligned with that line segment. Another press of Shift+S will expand the second handle as well turning it into a full smooth node. If a node is between two curve segments, Shift+S will expand both handles as before.
With these changes, and if you disable display of node handles (button on the Node tool bar) and the flashing of the mouseovered path (in Preferences), it is easy to edit Spiro paths without any distractions: Drag/transform nodes to reshape, use shortcuts to switch node types. The only thing that's slightly problematic is adding new nodes: without seeing the actual underlying Bezier path, you cannot do this by doubleclicking on it. You can, however, always select two nodes and press Ins to create a new node between them, then move it to where you want it to be.
Overall, I'm very pleased by how easy it was to integrate the Spiro math into Inkscape using the LPE mechanism, and by how flexible our Node tool turned out to support Spiro editing almost out of the box. Of course further improvements are possible, but even now Spiro editing is fully workable.
I have created about a dozen letters of a new font with this new toy, in a few hours of work, and they turned out incredibly nice and easy. I don't know how long it would take me to do the same with Beziers - most likely I would have quit, frustrated, after a couple letters. Beziers are really clumsy and inconvenient once you get used to Spiros :)
-----Original Message----- From: inkscape-devel-bounces@lists.sourceforge.net [mailto:inkscape-devel-bounces@lists.sourceforge.net] On Behalf Of bulia byak Sent: maandag 14 april 2008 5:28 To: inkscape-devel; raph.levien@...400... Subject: Re: [Inkscape-devel] NEW: Spiro splines LPE
I have removed the default helper path highlighting for paths with the Spiro effect
How do we want this to work? Now I actively disable helper path highlighting and handles showing. (this does not yet update the togglebuttons) Should it actively do this (I think yes), or should it respect the current preferences?
I have created about a dozen letters of a new font with this new toy, in a few hours of work, and they turned out incredibly nice and easy. I don't know how long it would take me to do the same with Beziers - most likely I would have quit, frustrated, after a couple letters. Beziers are really clumsy and inconvenient once you get used to Spiros :)
I tried a bit and they do work nicely, although a but funky while sometimes moving a node only a small distance can dramatically change the result path. Maybe we can implement more splines like this? For example, B-splines or more standard interpolating splines?
- Johan
On Mon, Apr 14, 2008 at 5:06 AM, <J.B.C.Engelen@...1578...> wrote:
How do we want this to work? Now I actively disable helper path highlighting and handles showing. (this does not yet update the togglebuttons) Should it actively do this (I think yes), or should it respect the current preferences?
Ideally a path with this LPE should default to both displays off, with the toolbar buttons set in sync, but if I set them to on it should respect that, at least until I switch away from the tool.
-----Original Message----- From: bulia byak [mailto:buliabyak@...400...] Sent: maandag 14 april 2008 10:17 To: Engelen, J.B.C. (Johan) Cc: inkscape-devel@lists.sourceforge.net; raph.levien@...400... Subject: Re: [Inkscape-devel] NEW: Spiro splines LPE
On Mon, Apr 14, 2008 at 5:06 AM, <J.B.C.Engelen@...1578...> wrote:
How do we want this to work? Now I actively disable helper
path highlighting and handles showing. (this does not yet update the togglebuttons) Should it actively do this (I think yes), or should it respect the current preferences?
Ideally a path with this LPE should default to both displays off, with the toolbar buttons set in sync,
That's what I meant by 'actively disable'.
but if I set them to on it should respect that, at least until I switch away from the tool.
There are two cases that are different imo: (including my proposed action) 1. Set them on *before* starting to edit: -- set them off, but don't save to preferences, for editing spiro path 2. Set them on *during* editing: -- set them on, save to pref.
bulia byak wrote:
A Spiro path is defined by a sequence of points, but unlike a regular path with Bezier curves, all Spiro points lie on the path and there are no off-path handles. The curvature of the path is defined entirely by the positions of the points and their types. The path behaves very similar to a springy rod which is forced to pass through the given points and which uses the minimum possible curvature to satisfy the requirement. As such, it feels quite natural and the resulting path is very smooth - with the kind of smoothness you can achieve with Beziers only after much tweaking.
That's really cool. It's definitely something I will use, plus it's another feature that will make Inkscape more friendly for vector novices.
woow! This a great new addition!! I was longing for Spiro curves! Thanks a lot.
Btw: if I am not wrong, Spiro are naturally arc-length parametrized... It could be worth defining a new pattern-along path stuff with the skeleton being a Spiro curve. I wrote the 2geom code for the arc-length parametrization of bezier curves, and this is a delicate point, that still needs to be improved (in particular to handle cusps in a better way)... Does some bezier to Spiro converter exist somewhere? (respecting the shape of the curve, I mean). A robust one would be interesting with this respect...
Spiro curve are so nice. Thank's again. jfb.
Sat, 12 Apr 2008 06:37:13 -0300 "bulia byak" <buliabyak@...400...> kirjoitti:
Spiro splines are a novel way of defining curvilinear paths developed by Raph Levien. Recently, Spiro support was added to the FontForge font editor, and now it is available in Inkscape too. It takes some getting used to, but for certain tasks Spiros have a clear advantage over Bezier curves.
I bumped into a problem playing around with this. Inkscape just freezed when moving the control points. Here's some debug info I managed to catch, I'll file a bug report later when I'm not on Polish WLAN ;)
I attached into the inkscape process with gdb, so this is just a random point, where I stopped it.
(gdb) bt #0 std::vector<Shape::dg_point, std::allocatorShape::dg_point
::operator[] (this=0x46e0c38, __n=35191)
at /usr/lib/gcc/x86_64-pc-linux-gnu/4.1.2/include/g++-v4/bits/stl_vector.h:494 #1 0x0000000000723aa1 in Shape::SwapPoints (this=0x46e0b60, a=35191, b=61747) at livarot/Shape.cpp:482 #2 0x0000000000723fc4 in Shape::SortPointsRounded (this=0x46e0b60, s=0, e=61747) at livarot/Shape.cpp:1088 #3 0x000000000072400a in Shape::SortPointsRounded (this=0x46e0b60, s=0, e=61748) at livarot/Shape.cpp:1104 #4 0x000000000072400a in Shape::SortPointsRounded (this=0x46e0b60, s=0, e=61749) at livarot/Shape.cpp:1104 [snip...] #3786 0x000000000072400a in Shape::SortPointsRounded (this=0x46e0b60, s=0, e=65531) at livarot/Shape.cpp:1104 #3787 0x000000000072400a in Shape::SortPointsRounded (this=0x46e0b60, s=0, e=65532) at livarot/Shape.cpp:1104 #3788 0x000000000072400a in Shape::SortPointsRounded (this=0x46e0b60, s=0, e=65533) at livarot/Shape.cpp:1104 #3789 0x000000000072400a in Shape::SortPointsRounded (this=0x46e0b60, s=0, e=65537) at livarot/Shape.cpp:1104 #3790 0x000000000072400a in Shape::SortPointsRounded (this=0x46e0b60, s=0, e=65538) at livarot/Shape.cpp:1104 #3791 0x000000000073b376 in Shape::ConvertToShape (this=0x4075200, a=0x46e0b60, directed=fill_oddEven, invert=false) at livarot/ShapeSweep.cpp:199 #3792 0x00000000006816d6 in nr_arena_shape_update_fill (shape=0x355d000, gc=0x7fff63b89a70, area=0x7fff63b8a1f8, force_shape=<value optimized out>) at display/nr-arena-shape.cpp:474 #3793 0x00000000006829f7 in nr_arena_shape_render (ct=0x4defc00, item=0x355d000, area=0x7fff63b89ce0, pb=0x7fff63b8a1e0, flags=0) at display/nr-arena-shape.cpp:871 ...
(gdb) print {Shape}0x46e0b60 $1 = {_vptr.Shape = 0x964cd0, ebData = {<std::_Vector_base<Shape::back_data,std::allocatorShape::back_data
= { _M_impl = {<std::allocatorShape::back_data> = {<__gnu_cxx::new_allocatorShape::back_data> = {<No data fields>}, <No data fields>}, _M_start = 0x0, _M_finish = 0x0, _M_end_of_storage = 0x0}}, <No data fields>},
vorpData = {<std::_Vector_base<Shape::voronoi_point,std::allocatorShape::voronoi_point
= { _M_impl = {<std::allocatorShape::voronoi_point> = {<__gnu_cxx::new_allocatorShape::voronoi_point> = {<No data fields>}, <No data fields>}, _M_start = 0x0, _M_finish = 0x0,
_M_end_of_storage = 0x0}}, <No data fields>}, voreData = {<std::_Vector_base<Shape::voronoi_edge,std::allocatorShape::voronoi_edge
= { _M_impl = {<std::allocatorShape::voronoi_edge> = {<__gnu_cxx::new_allocatorShape::voronoi_edge> = {<No data fields>}, <No data fields>}, _M_start = 0x0, _M_finish = 0x0,
_M_end_of_storage = 0x0}}, <No data fields>}, nbQRas = 96, firstQRas = 0, lastQRas = 96, qrsData = 0x0, chgts = {<std::_Vector_base<Shape::sTreeChange,std::allocatorShape::sTreeChange
= { _M_impl = {<std::allocatorShape::sTreeChange> = {<__gnu_cxx::new_allocatorShape::sTreeChange> = {<No data fields>}, <No data fields>}, ---Type <return> to continue, or q fields><return> to quit---
_M_start = 0x0, _M_finish = 0x0, _M_end_of_storage = 0x0}}, <No data fields>}, nbInc = 0, maxInc = -524288, iData = 0x0, sTree = 0x0, sEvts = 0x0, leftX = 0, topY = 0, rightX = 0, bottomY = 0, maxPt = 65539, maxAr = 65539, type = 0, _need_points_sorting = true, _need_edges_sorting = true, _has_points_data = true, _point_data_initialised = true, _has_edges_data = true, _has_sweep_src_data = true, _has_sweep_dest_data = false, _has_raster_data = false, _has_quick_raster_data = false, _has_back_data = false, _has_voronoi_data = false, _bbox_up_to_date = false, _pts = {<std::_Vector_base<Shape::dg_point,std::allocatorShape::dg_point >> = { _M_impl = {<std::allocatorShape::dg_point> = {<__gnu_cxx::new_allocatorShape::dg_point> = {<No data fields>}, <No data fields>}, _M_start = 0x619e040, _M_finish = 0x641e0b8, _M_end_of_storage = 0x669e040}}, <No data fields>}, _aretes = {<std::_Vector_base<Shape::dg_arete,std::allocatorShape::dg_arete >> = { _M_impl = {<std::allocatorShape::dg_arete> = {<__gnu_cxx::new_allocatorShape::dg_arete> = {<No data fields>}, <No data fields>}, _M_start = 0x549cda0, _M_finish = 0x549ce90, _M_end_of_storage = 0x549cee0}}, <No data fields>}, eData = {<std::_Vector_base<Shape::edge_data,std::allocatorShape::edge_data
= {
_M_impl = {<std::allocatorShape::edge_data> = {<__gnu_cxx::new_allocatorShape::edge_data> = {<No data fields>}, <No data fields>}, _M_start = 0x669e050, _M_finish = 0x6b1e128, _M_end_of_storage = 0x6b1e128}}, <No data fields>}, swsData = {<std::_Vector_base<Shape::sweep_src_data,std::allocatorShape::sweep_src_data
= { _M_impl = {<std::allocatorShape::sweep_src_data> = {<__gnu_cxx::new_allocatorShape::sweep_src_data> = {<No data fields>}, <No data fields>}, _M_start = 0x6b1e130, _M_finish = fields>0x6f1e1f0,
_M_end_of_storage = 0x6f1e1f0}}, <No data fields>}, swdData = {<std::_Vector_base<Shape::sweep_dest_data,std::allocatorShape::sweep_dest_data
= { _M_impl = {<std::allocatorShape::sweep_dest_data> = {<__gnu_cxx::new_allocatorShape::sweep_dest_data> = {<No data fields>}, <No data fields>}, _M_start = 0x0, _M_finish = 0x0,
_M_end_of_storage = 0x0}}, <No data fields>}, swrData = {<std::_Vector_base<Shape::raster_data,std::allocatorShape::raster_data
= { _M_impl = {<std::allocatorShape::raster_data> = {<__gnu_cxx::new_allocatorShape::raster_data> = {<No data fields>}, <No data fields>}, _M_start = 0x0, _M_finish = 0x0,
_M_end_of_storage = 0x0}}, <No data fields>}, pData = {<std::_Vector_base<Shape::point_data,std::allocatorShape::point_data
= { _M_impl = {<std::allocatorShape::point_data> = {<__gnu_cxx::new_allocatorShape::point_data> = {<No data fields>}, <No data fields>}, _M_start = 0x58de010, _M_finish = 0x5c5e0b8,
_M_end_of_storage = 0x5c5e0b8}}, <No data fields>}}
On Fri, May 9, 2008 at 12:47 PM, Niko Kiirala <niko@...1267...> wrote:
I bumped into a problem playing around with this. Inkscape just freezed when moving the control points. Here's some debug info I managed to catch, I'll file a bug report later when I'm not on Polish WLAN ;)
I attached into the inkscape process with gdb, so this is just a random point, where I stopped it.
Since the bt points to livarot, I think it is not directly related to Spiros. Most likely, what happened is Spiros went into runaway instability and produced a really huge path, which livarot then tried to render and failed. I had freezes like this but couldn't get a bt. If you can reproduce it, I would like you to try doing that in outline mode (where cairo does the rendering, not livarot) and see if there's a difference.
2008/4/12 bulia byak <buliabyak@...400...>:
Hopefully, the robustness of the algorithm will be improved in future releases.
www.fontly.com/sandbox/spiro.html is a reimplementation of Spiro in Javascript with much improved robustness; perhaps Raph will port it to C one day, or someone else will :-)
On Sun, May 18, 2008 at 7:16 AM, Dave Crossland <dave@...1555...> wrote:
www.fontly.com/sandbox/spiro.html is a reimplementation of Spiro in Javascript with much improved robustness; perhaps Raph will port it to C one day, or someone else will :-)
Yes! That would be fantastic.
participants (10)
-
unknown@example.com
-
Alexandre Prokoudine
-
Alvin Penner
-
Andy Fitzsimon
-
bulia byak
-
Dave Crossland
-
jf barraud
-
microUgly
-
Niko Kiirala
-
rygle