summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--images/examples/Fill Stitch Starting and Ending Point.svg215
-rw-r--r--inx/inkstitch_install.inx (renamed from inx/inkstitch_palettes.inx)6
-rw-r--r--lib/commands.py89
-rw-r--r--lib/elements/auto_fill.py26
-rw-r--r--lib/elements/element.py41
-rw-r--r--lib/elements/stroke.py3
-rw-r--r--lib/extensions/__init__.py2
-rw-r--r--lib/extensions/base.py4
-rw-r--r--lib/extensions/install.py (renamed from lib/extensions/palettes.py)61
-rw-r--r--lib/extensions/params.py3
-rw-r--r--lib/stitches/auto_fill.py197
-rw-r--r--lib/svg/__init__.py1
-rw-r--r--lib/svg/path.py20
-rw-r--r--lib/svg/tags.py5
-rw-r--r--messages.po26
-rw-r--r--palettes/InkStitch ARC Polyester.gpl (renamed from palettes/ARC Polyester.gpl)0
-rw-r--r--palettes/InkStitch ARC Rayon.gpl (renamed from palettes/ARC Rayon.gpl)0
-rw-r--r--palettes/InkStitch Admelody Polyester.gpl (renamed from palettes/Admelody Polyester.gpl)0
-rw-r--r--palettes/InkStitch Admelody Rayon.gpl (renamed from palettes/Admelody Rayon.gpl)0
-rw-r--r--palettes/InkStitch Anchor.gpl (renamed from palettes/Anchor.gpl)0
-rw-r--r--palettes/InkStitch Aurifil Lana.gpl (renamed from palettes/Aurifil Lana.gpl)0
-rw-r--r--palettes/InkStitch Aurifil Mako.gpl (renamed from palettes/Aurifil Mako.gpl)0
-rw-r--r--palettes/InkStitch Aurifil Polyester.gpl (renamed from palettes/Aurifil Polyester.gpl)0
-rw-r--r--palettes/InkStitch Aurifil Rayon.gpl (renamed from palettes/Aurifil Rayon.gpl)0
-rw-r--r--palettes/InkStitch Aurifil Royal.gpl (renamed from palettes/Aurifil Royal.gpl)0
-rw-r--r--palettes/InkStitch BFC Polyester.gpl (renamed from palettes/BFC Polyester.gpl)0
-rw-r--r--palettes/InkStitch Brildor AC.gpl (renamed from palettes/Brildor AC.gpl)0
-rw-r--r--palettes/InkStitch Brildor CO.gpl (renamed from palettes/Brildor CO.gpl)0
-rw-r--r--palettes/InkStitch Brildor MF.gpl (renamed from palettes/Brildor MF.gpl)0
-rw-r--r--palettes/InkStitch Brildor NY.gpl (renamed from palettes/Brildor NY.gpl)0
-rw-r--r--palettes/InkStitch Brildor PB.gpl (renamed from palettes/Brildor PB.gpl)0
-rw-r--r--palettes/InkStitch Brother Country.gpl (renamed from palettes/Brother Country.gpl)0
-rw-r--r--palettes/InkStitch Brother Embroidery.gpl (renamed from palettes/Brother Embroidery.gpl)0
-rw-r--r--palettes/InkStitch Coats Alcazar Jazz.gpl (renamed from palettes/Coats Alcazar Jazz.gpl)0
-rw-r--r--palettes/InkStitch Coats Alcazar.gpl (renamed from palettes/Coats Alcazar.gpl)0
-rw-r--r--palettes/InkStitch Coats Sylko USA.gpl (renamed from palettes/Coats Sylko USA.gpl)0
-rw-r--r--palettes/InkStitch Coats Sylko.gpl (renamed from palettes/Coats Sylko.gpl)0
-rw-r--r--palettes/InkStitch DMC.gpl (renamed from palettes/DMC.gpl)0
-rw-r--r--palettes/InkStitch Embroidex.gpl (renamed from palettes/Embroidex.gpl)0
-rw-r--r--palettes/InkStitch Emmel.gpl (renamed from palettes/Emmel.gpl)0
-rw-r--r--palettes/InkStitch Fil-Tec Glide.gpl (renamed from palettes/Fil-Tec Glide.gpl)0
-rw-r--r--palettes/InkStitch Floriani Polyester.gpl (renamed from palettes/Floriani Polyester.gpl)0
-rw-r--r--palettes/InkStitch FuFu Polyester.gpl (renamed from palettes/FuFu Polyester.gpl)0
-rw-r--r--palettes/InkStitch FuFu Rayon.gpl (renamed from palettes/FuFu Rayon.gpl)0
-rw-r--r--palettes/InkStitch Gunold.gpl (renamed from palettes/Gunold.gpl)0
-rw-r--r--palettes/InkStitch Gutermann Creativ Dekor.gpl (renamed from palettes/Gutermann Creativ Dekor.gpl)0
-rw-r--r--palettes/InkStitch Hemingworth.gpl (renamed from palettes/Hemingworth.gpl)0
-rw-r--r--palettes/InkStitch Isacord Polyester.gpl (renamed from palettes/Isacord Polyester.gpl)0
-rw-r--r--palettes/InkStitch Isafil Rayon.gpl (renamed from palettes/Isafil Rayon.gpl)0
-rw-r--r--palettes/InkStitch Isalon Polyester.gpl (renamed from palettes/Isalon Polyester.gpl)0
-rw-r--r--palettes/InkStitch Janome.gpl (renamed from palettes/Janome.gpl)0
-rw-r--r--palettes/InkStitch King Star.gpl (renamed from palettes/King Star.gpl)0
-rw-r--r--palettes/InkStitch MTB - Embroidex.gpl (renamed from palettes/MTB - Embroidex.gpl)0
-rw-r--r--palettes/InkStitch Madeira Burmilana.gpl (renamed from palettes/Madeira Burmilana.gpl)0
-rw-r--r--palettes/InkStitch Madeira Matt.gpl (renamed from palettes/Madeira Matt.gpl)0
-rw-r--r--palettes/InkStitch Madeira Polyneon.gpl (renamed from palettes/Madeira Polyneon.gpl)0
-rw-r--r--palettes/InkStitch Madeira Rayon.gpl (renamed from palettes/Madeira Rayon.gpl)0
-rw-r--r--palettes/InkStitch Marathon Polyester.gpl (renamed from palettes/Marathon Polyester.gpl)0
-rw-r--r--palettes/InkStitch Marathon Rayon V3.gpl (renamed from palettes/Marathon Rayon V3.gpl)0
-rw-r--r--palettes/InkStitch Marathon Rayon.gpl (renamed from palettes/Marathon Rayon.gpl)0
-rw-r--r--palettes/InkStitch Metro.gpl (renamed from palettes/Metro.gpl)0
-rw-r--r--palettes/InkStitch Mettler Embroidery.gpl (renamed from palettes/Mettler Embroidery.gpl)0
-rw-r--r--palettes/InkStitch Mettler Poly Sheen.gpl (renamed from palettes/Mettler Poly Sheen.gpl)0
-rw-r--r--palettes/InkStitch Outback Embroidery Rayon.gpl (renamed from palettes/Outback Embroidery Rayon.gpl)0
-rw-r--r--palettes/InkStitch Poly X40.gpl (renamed from palettes/Poly X40.gpl)0
-rw-r--r--palettes/InkStitch Princess.gpl (renamed from palettes/Princess.gpl)0
-rw-r--r--palettes/InkStitch RAL.gpl (renamed from palettes/RAL.gpl)0
-rw-r--r--palettes/InkStitch Radiant Rayon.gpl (renamed from palettes/Radiant Rayon.gpl)0
-rw-r--r--palettes/InkStitch Robison-Anton Polyester.gpl (renamed from palettes/Robison-Anton Polyester.gpl)0
-rw-r--r--palettes/InkStitch Robison-Anton Rayon.gpl (renamed from palettes/Robison-Anton Rayon.gpl)0
-rw-r--r--palettes/InkStitch Royal Polyester.gpl (renamed from palettes/Royal Polyester.gpl)0
-rw-r--r--palettes/InkStitch Royal Viscose Rayon.gpl (renamed from palettes/Royal Viscose Rayon.gpl)0
-rw-r--r--palettes/InkStitch Sigma.gpl (renamed from palettes/Sigma.gpl)0
-rw-r--r--palettes/InkStitch Simthread Polyester.gpl (renamed from palettes/Simthread Polyester.gpl)0
-rw-r--r--palettes/InkStitch Simthread Rayon.gpl (renamed from palettes/Simthread Rayon.gpl)0
-rw-r--r--palettes/InkStitch Sulky Polyester.gpl (renamed from palettes/Sulky Polyester.gpl)0
-rw-r--r--palettes/InkStitch Sulky Rayon.gpl (renamed from palettes/Sulky Rayon.gpl)0
-rw-r--r--palettes/InkStitch Swist Rayon.gpl (renamed from palettes/Swist Rayon.gpl)0
-rw-r--r--palettes/InkStitch Tristar Polyester.gpl (renamed from palettes/Tristar Polyester.gpl)0
-rw-r--r--palettes/InkStitch Tristar Rayon.gpl (renamed from palettes/Tristar Rayon.gpl)0
-rw-r--r--palettes/InkStitch Viking Palette.gpl (renamed from palettes/Viking Palette.gpl)0
-rw-r--r--palettes/InkStitch Vyapar Rayon.gpl (renamed from palettes/Vyapar Rayon.gpl)0
-rw-r--r--palettes/InkStitch Wonderfil Polyester.gpl (renamed from palettes/Wonderfil Polyester.gpl)0
-rw-r--r--palettes/InkStitch Wonderfil Rayon.gpl (renamed from palettes/Wonderfil Rayon.gpl)0
-rw-r--r--symbols/inkstitch.svg127
86 files changed, 687 insertions, 140 deletions
diff --git a/Makefile b/Makefile
index 250a9c5b..3a150e5c 100644
--- a/Makefile
+++ b/Makefile
@@ -10,6 +10,7 @@ dist: distclean locales
cp inx/*.inx dist
cp -a images/examples dist/inkstitch
cp -a palettes dist/inkstitch
+ cp -a symbols dist/inkstitch
mkdir -p dist/inkstitch/bin/locales
cp -a locales/* dist/inkstitch/bin/locales
cp -a print dist/inkstitch/bin/
diff --git a/images/examples/Fill Stitch Starting and Ending Point.svg b/images/examples/Fill Stitch Starting and Ending Point.svg
new file mode 100644
index 00000000..d11a24ae
--- /dev/null
+++ b/images/examples/Fill Stitch Starting and Ending Point.svg
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="4in"
+ height="4in"
+ viewBox="0 0 384 384"
+ id="svg8375"
+ version="1.1"
+ inkscape:version="0.92.3 (unknown)"
+ sodipodi:docname="fill_markers.svg">
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="169.91776"
+ inkscape:cy="93.125071"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ units="in"
+ inkscape:window-width="1366"
+ inkscape:window-height="705"
+ inkscape:window-x="-4"
+ inkscape:window-y="-4"
+ inkscape:window-maximized="1"
+ inkscape:snap-global="false" />
+ <defs
+ id="defs8377">
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible;"
+ id="marker14673"
+ refX="0.0"
+ refY="0.0"
+ orient="auto"
+ inkscape:stockid="Arrow2Mend">
+ <path
+ transform="scale(0.6) rotate(180) translate(0,0)"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
+ id="path14671" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible;"
+ id="Arrow2Mend"
+ refX="0.0"
+ refY="0.0"
+ orient="auto"
+ inkscape:stockid="Arrow2Mend">
+ <path
+ transform="scale(0.6) rotate(180) translate(0,0)"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
+ id="path14422" />
+ </marker>
+ <symbol
+ id="inkstitch_fill_start">
+ <title
+ id="title9432-5">Fill stitch ending point</title>
+ <circle
+ inkscape:label="outline"
+ style="opacity:1;vector-effect:none;fill:#fafafa;fill-opacity:1;fill-rule:evenodd;stroke:#003399;stroke-width:1.06501234;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3.19503705, 3.19503705;stroke-dashoffset:0;stroke-opacity:1"
+ id="circle13166-6-3"
+ cx="85.223907"
+ cy="106.13256"
+ r="9.2465534" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4183-5"
+ d="m 91.796747,106.13612 -10.4514,6.03412 v -12.06823 z"
+ inkscape:transform-center-y="3.0183984e-06"
+ inkscape:transform-center-x="-1.7419043"
+ style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.74180555;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </symbol>
+ <symbol
+ id="inkstitch_fill_end">
+ <title
+ id="title9427">Fill stitch starting point</title>
+ <circle
+ inkscape:label="outline"
+ style="opacity:1;vector-effect:none;fill:#fafafa;fill-opacity:1;fill-rule:evenodd;stroke:#003399;stroke-width:1.06501234;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3.19503705, 3.19503705;stroke-dashoffset:0;stroke-opacity:1"
+ id="circle13166"
+ cx="47.235394"
+ cy="105.91732"
+ r="9.2465534" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect5371-2"
+ d="m 42.691395,101.26765 h 9.140878 v 9.14087 h -9.140878 z"
+ style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.60622311;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:4.81866985, 4.81866985;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke" />
+ </symbol>
+ </defs>
+ <metadata
+ id="metadata8380">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-52.362271)">
+ <path
+ embroider_fill_underlay_inset_mm="0.3"
+ embroider_fill_underlay="True"
+ sodipodi:nodetypes="cccccccccccccccccccccccccccc"
+ inkscape:connector-curvature="0"
+ d="m 178.7757,288.69033 0.8157,-41.88278 -35.86371,-21.64781 -36.6794,20.23498 -0.81569,41.88278 35.8637,21.64781 z m 48.50895,-81.46949 0.37532,-41.88904 -36.08932,-21.26956 -36.46463,20.61949 -0.37532,41.88904 36.08931,21.26956 z m 47.09578,82.14513 0.0611,-41.89068 -36.24783,-20.99829 -36.30896,20.8924 -0.0611,41.89068 36.24783,20.99828 z M 124.8814,351.39231 59.294526,237.79251 124.8814,124.19271 h 131.17374 l 65.58687,113.5998 -65.58687,113.5998 z"
+ style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.35955402;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path9699-3-7" />
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot14860"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:26.66666603px;line-height:100%;font-family:Digitalt;-inkscape-font-specification:'Digitalt Bold';text-align:start;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.75590551;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
+ transform="translate(0,52.362271)"><flowRegion
+ id="flowRegion14862"><rect
+ id="rect14864"
+ width="144.45178"
+ height="68.690369"
+ x="374.7666"
+ y="150.14969" /></flowRegion><flowPara
+ id="flowPara14866"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.66666603px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:sans-serif">fill-stitch starting point</flowPara></flowRoot> <flowRoot
+ transform="translate(-492,52.362269)"
+ xml:space="preserve"
+ id="flowRoot14860-2"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:26.66666603px;line-height:100%;font-family:Digitalt;-inkscape-font-specification:'Digitalt Bold';text-align:start;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.75590551;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"><flowRegion
+ id="flowRegion14862-6"><rect
+ id="rect14864-5"
+ width="144.45178"
+ height="68.690369"
+ x="374.7666"
+ y="150.14969" /></flowRegion><flowPara
+ id="flowPara14866-2"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.66666603px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:sans-serif">fill-stitch ending point</flowPara></flowRoot> <flowRoot
+ transform="translate(-360,212.36227)"
+ xml:space="preserve"
+ id="flowRoot14860-6"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:26.66666603px;line-height:100%;font-family:Digitalt;-inkscape-font-specification:'Digitalt Bold';text-align:start;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.75590551;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"><flowRegion
+ id="flowRegion14862-5"><rect
+ id="rect14864-8"
+ width="462.14478"
+ height="569.72601"
+ x="374.7666"
+ y="150.14969" /></flowRegion><flowPara
+ id="flowPara14866-7"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.33333333px;line-height:72.70000064%;font-family:sans-serif;-inkscape-font-specification:sans-serif">The start and end markers above tell Ink/Stitch where to start and end the fill stitch.</flowPara><flowPara
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.33333333px;line-height:72.70000064%;font-family:sans-serif;-inkscape-font-specification:sans-serif"
+ id="flowPara14904" /><flowPara
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.33333333px;line-height:72.70000064%;font-family:sans-serif;-inkscape-font-specification:sans-serif"
+ id="flowPara14906">To use the markers, first install them using the Ink/Stitch &quot;Install add-ons for Inkscape&quot; extension and restart Inkscape.</flowPara><flowPara
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.33333333px;line-height:72.70000064%;font-family:sans-serif;-inkscape-font-specification:sans-serif"
+ id="flowPara14908" /><flowPara
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.33333333px;line-height:72.70000064%;font-family:sans-serif;-inkscape-font-specification:sans-serif"
+ id="flowPara14910">Access the markers using the Symbols tool (Object -&gt; Symbols). Select &quot;Ink/Stitch Comm ands&quot; as the symbol set. Drag a marker out onto your canvas (doesn't matter where). Use the Flow-Chart Tool (&quot;create diagram connectors&quot;) to draw a connection between the marker and the fill object to which it should apply. This will add a connector path. Adding an arrow on that path as in the above example is not necessary.</flowPara><flowPara
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.33333333px;line-height:72.70000064%;font-family:sans-serif;-inkscape-font-specification:sans-serif"
+ id="flowPara14912" /><flowPara
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.33333333px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:sans-serif"
+ id="flowPara14914"><flowSpan
+ style="line-height:72.70000064%"
+ id="flowSpan14916">Moving the marker will change the connector's position to match. You can also move the endpoints of the connector manually. The </flowSpan>connector's endpoint nearest to the fill object is the point at which stitching will start or end.</flowPara></flowRoot> </g>
+ <path
+ inkscape:connection-end="#path9699-3-7"
+ inkscape:connection-start="#use14355"
+ inkscape:connector-curvature="0"
+ inkscape:connector-type="polyline"
+ id="path14391"
+ d="M 356.92084,186.01367 321.3771,185.88908"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Mend)" />
+ <use
+ transform="translate(270.34979,79.877542)"
+ height="100%"
+ width="100%"
+ y="0"
+ x="0"
+ xlink:href="#inkstitch_fill_start"
+ id="use14355" />
+ <path
+ inkscape:connection-end="#path9699-3-7"
+ inkscape:connection-start="#use14379"
+ inkscape:connector-curvature="0"
+ inkscape:connector-type="polyline"
+ id="path14393"
+ d="m 25.78533,183.40549 34.433762,0.42335"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker14673)" />
+ <use
+ transform="translate(-21.476504,77.567401)"
+ height="100%"
+ width="100%"
+ y="0"
+ x="0"
+ xlink:href="#inkstitch_fill_end"
+ id="use14379" />
+</svg>
diff --git a/inx/inkstitch_palettes.inx b/inx/inkstitch_install.inx
index 5daa3196..7275e13a 100644
--- a/inx/inkstitch_palettes.inx
+++ b/inx/inkstitch_install.inx
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
- <_name>Install thread manufacturer color palettes</_name>
- <id>org.inkstitch.palettes</id>
+ <_name>Install add-ons for Inkscape</_name>
+ <id>org.inkstitch.install</id>
<dependency type="executable" location="extensions">inkstitch.py</dependency>
<dependency type="executable" location="extensions">inkex.py</dependency>
- <param name="extension" type="string" gui-hidden="true">palettes</param>
+ <param name="extension" type="string" gui-hidden="true">install</param>
<effect>
<object-type>all</object-type>
<effects-menu>
diff --git a/lib/commands.py b/lib/commands.py
new file mode 100644
index 00000000..ec62d716
--- /dev/null
+++ b/lib/commands.py
@@ -0,0 +1,89 @@
+import inkex
+import cubicsuperpath
+
+from .svg import apply_transforms
+from .svg.tags import SVG_USE_TAG, SVG_SYMBOL_TAG, CONNECTION_START, CONNECTION_END, XLINK_HREF
+
+
+class Command(object):
+ def __init__(self, connector):
+ self.connector = connector
+ self.svg = self.connector.getroottree().getroot()
+
+ self.parse_command()
+
+ def get_node_by_url(self, url):
+ # url will be #path12345. Find the object at the other end.
+
+ if url is None:
+ raise ValueError("url is None")
+
+ if not url.startswith('#'):
+ raise ValueError("invalid connection url: %s" % url)
+
+ id = url[1:]
+
+ try:
+ return self.svg.xpath(".//*[@id='%s']" % id)[0]
+ except (IndexError, AttributeError):
+ raise ValueError("could not find node by url %s" % id)
+
+ def parse_connector_path(self):
+ path = cubicsuperpath.parsePath(self.connector.get('d'))
+ return apply_transforms(path, self.connector)
+
+ def parse_command(self):
+ path = self.parse_connector_path()
+
+ neighbors = [
+ (self.get_node_by_url(self.connector.get(CONNECTION_START)), path[0][0][1]),
+ (self.get_node_by_url(self.connector.get(CONNECTION_END)), path[0][-1][1])
+ ]
+
+ if neighbors[0][0].tag != SVG_USE_TAG:
+ neighbors.reverse()
+
+ if neighbors[0][0].tag != SVG_USE_TAG:
+ raise ValueError("connector does not point to a use tag")
+
+ self.symbol = self.get_node_by_url(neighbors[0][0].get(XLINK_HREF))
+
+ if self.symbol.tag != SVG_SYMBOL_TAG:
+ raise ValueError("use points to non-symbol")
+
+ self.command = self.symbol.get('id')
+
+ if self.command.startswith('inkstitch_'):
+ self.command = self.command[10:]
+ else:
+ raise ValueError("symbol is not an Ink/Stitch command")
+
+ self.target = neighbors[1][0]
+ self.target_point = neighbors[1][1]
+
+ def __repr__(self):
+ return "Command('%s', %s)" % (self.command, self.target_point)
+
+def find_commands(node):
+ """Find the symbols this node is connected to and return them as Commands"""
+
+ # find all paths that have this object as a connection
+ xpath = ".//*[@inkscape:connection-start='#%(id)s' or @inkscape:connection-end='#%(id)s']" % dict(id=node.get('id'))
+ connectors = node.getroottree().getroot().xpath(xpath, namespaces=inkex.NSS)
+
+ # try to turn them into commands
+ commands = []
+ for connector in connectors:
+ try:
+ commands.append(Command(connector))
+ except ValueError:
+ import sys
+ import traceback
+ print >> sys.stderr, "not a Command:", connector.get('id'), traceback.format_exc()
+ # Parsing the connector failed, meaning it's not actually an Ink/Stitch command.
+ pass
+
+ return commands
+
+def is_command(node):
+ return CONNECTION_START in node.attrib or CONNECTION_END in node.attrib
diff --git a/lib/elements/auto_fill.py b/lib/elements/auto_fill.py
index 504bae2a..59816878 100644
--- a/lib/elements/auto_fill.py
+++ b/lib/elements/auto_fill.py
@@ -100,13 +100,28 @@ class AutoFill(Fill):
def fill_shape(self):
return self.shrink_or_grow_shape(self.expand)
+ def get_starting_point(self, last_patch):
+ # If there is a "fill_start" Command, then use that; otherwise pick
+ # the point closest to the end of the last patch.
+
+ if self.get_command('fill_start'):
+ return self.get_command('fill_start').target_point
+ elif last_patch:
+ return last_patch.stitches[-1]
+ else:
+ return None
+
+ def get_ending_point(self):
+ if self.get_command('fill_end'):
+ return self.get_command('fill_end').target_point
+ else:
+ return None
+
def to_patches(self, last_patch):
stitches = []
- if last_patch is None:
- starting_point = None
- else:
- starting_point = last_patch.stitches[-1]
+ starting_point = self.get_starting_point(last_patch)
+ ending_point = self.get_ending_point()
if self.fill_underlay:
stitches.extend(auto_fill(self.underlay_shape,
@@ -126,6 +141,7 @@ class AutoFill(Fill):
self.max_stitch_length,
self.running_stitch_length,
self.staggers,
- starting_point))
+ starting_point,
+ ending_point))
return [Patch(stitches=stitches, color=self.color)]
diff --git a/lib/elements/element.py b/lib/elements/element.py
index 39437c9f..3c31f1b0 100644
--- a/lib/elements/element.py
+++ b/lib/elements/element.py
@@ -4,7 +4,8 @@ from shapely import geometry as shgeo
from ..i18n import _
from ..utils import cache
-from ..svg import PIXELS_PER_MM, get_viewbox_transform, convert_length, get_doc_size
+from ..svg import PIXELS_PER_MM, convert_length, get_doc_size, apply_transforms
+from ..commands import find_commands
# inkscape-provided utilities
import simpletransform
@@ -171,10 +172,6 @@ class EmbroideryElement(object):
@property
def path(self):
- return cubicsuperpath.parsePath(self.node.get("d"))
-
- @cache
- def parse_path(self):
# A CSP is a "cubic superpath".
#
# A "path" is a sequence of strung-together bezier curves.
@@ -202,22 +199,32 @@ class EmbroideryElement(object):
# In a path, each element in the 3-tuple is itself a tuple of (x, y).
# Tuples all the way down. Hasn't anyone heard of using classes?
- path = self.path
-
- # start with the identity transform
- transform = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
+ return cubicsuperpath.parsePath(self.node.get("d"))
- # combine this node's transform with all parent groups' transforms
- transform = simpletransform.composeParents(self.node, transform)
+ @cache
+ def parse_path(self):
+ return apply_transforms(self.path, self.node)
- # add in the transform implied by the viewBox
- viewbox_transform = get_viewbox_transform(self.node.getroottree().getroot())
- transform = simpletransform.composeTransform(viewbox_transform, transform)
+ @property
+ @cache
+ def commands(self):
+ return find_commands(self.node)
- # apply the combined transform to this node's path
- simpletransform.applyTransformToPath(transform, path)
+ @cache
+ def get_commands(self, command):
+ return [c for c in self.commands if c.command == command]
- return path
+ @cache
+ def get_command(self, command):
+ commands = self.get_commands(command)
+
+ if len(commands) == 1:
+ return commands[0]
+ elif len(commands) > 1:
+ raise ValueError(_("%(id)s has more than one command of type '%(command)s' linked to it") %
+ dict(id=self.node.get(id), command=command))
+ else:
+ return None
def strip_control_points(self, subpath):
return [point for control_before, point, control_after in subpath]
diff --git a/lib/elements/stroke.py b/lib/elements/stroke.py
index 5239f978..eca9e0ba 100644
--- a/lib/elements/stroke.py
+++ b/lib/elements/stroke.py
@@ -4,6 +4,7 @@ from .element import param, EmbroideryElement, Patch
from ..i18n import _
from ..utils import cache, Point
from ..stitches import running_stitch
+from ..svg import parse_length_with_units
warned_about_legacy_running_stitch = False
@@ -57,7 +58,7 @@ class Stroke(EmbroideryElement):
def is_running_stitch(self):
# using stroke width <= 0.5 pixels to indicate running stitch is deprecated in favor of dashed lines
- stroke_width = float(self.get_style("stroke-width", 1))
+ stroke_width, units = parse_length_with_units(self.get_style("stroke-width", "1"))
if self.dashed:
return True
diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py
index 6d3e00d8..b8951e12 100644
--- a/lib/extensions/__init__.py
+++ b/lib/extensions/__init__.py
@@ -1,5 +1,5 @@
from embroider import Embroider
-from palettes import Palettes
+from install import Install
from params import Params
from print_pdf import Print
from simulate import Simulate
diff --git a/lib/extensions/base.py b/lib/extensions/base.py
index 831b6dc6..78f75cf1 100644
--- a/lib/extensions/base.py
+++ b/lib/extensions/base.py
@@ -7,6 +7,7 @@ from collections import MutableMapping
from ..svg.tags import *
from ..elements import AutoFill, Fill, Stroke, SatinColumn, Polyline, EmbroideryElement
from ..utils import cache
+from ..commands import is_command
SVG_METADATA_TAG = inkex.addNS("metadata", "svg")
@@ -165,7 +166,8 @@ class InkstitchExtension(inkex.Effect):
classes.append(Fill)
if element.get_style("stroke"):
- classes.append(Stroke)
+ if not is_command(element.node):
+ classes.append(Stroke)
if element.get_boolean_param("stroke_first", False):
classes.reverse()
diff --git a/lib/extensions/palettes.py b/lib/extensions/install.py
index f7a6c7a5..d55b96d0 100644
--- a/lib/extensions/palettes.py
+++ b/lib/extensions/install.py
@@ -1,3 +1,5 @@
+# -*- coding: UTF-8 -*-
+
import sys
import traceback
import os
@@ -14,26 +16,26 @@ import inkex
from ..utils import guess_inkscape_config_path
-class InstallPalettesFrame(wx.Frame):
+class InstallerFrame(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
- default_path = os.path.join(guess_inkscape_config_path(), "palettes")
+ self.path = guess_inkscape_config_path()
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
- text = wx.StaticText(panel, label=_("Directory in which to install palettes:"))
- font = wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.NORMAL)
- text.SetFont(font)
- sizer.Add(text, proportion=0, flag=wx.ALL|wx.EXPAND, border=10)
+ text_sizer = wx.BoxSizer(wx.HORIZONTAL)
- path_sizer = wx.BoxSizer(wx.HORIZONTAL)
- self.path_input = wx.TextCtrl(panel, wx.ID_ANY, value=default_path)
- path_sizer.Add(self.path_input, proportion=3, flag=wx.RIGHT|wx.EXPAND, border=20)
- chooser_button = wx.Button(panel, wx.ID_OPEN, _('Choose another directory...'))
- path_sizer.Add(chooser_button, proportion=1, flag=wx.EXPAND)
- sizer.Add(path_sizer, proportion=0, flag=wx.ALL|wx.EXPAND, border=10)
+ text = _('Ink/Stitch can install files ("add-ons") that make it easier to use Inkscape to create machine embroidery designs. These add-ons will be installed:') + \
+ "\n\n • " + _("thread manufacturer color palettes") + \
+ "\n • " + _("Ink/Stitch visual commands (Object -> Symbols...)")
+
+ static_text = wx.StaticText(panel, label=text)
+ font = wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.NORMAL)
+ static_text.SetFont(font)
+ text_sizer.Add(static_text, proportion=0, flag=wx.ALL|wx.EXPAND, border=10)
+ sizer.Add(text_sizer, proportion=3, flag=wx.ALL|wx.EXPAND, border=0)
buttons_sizer = wx.BoxSizer(wx.HORIZONTAL)
install_button = wx.Button(panel, wx.ID_ANY, _("Install"))
@@ -41,15 +43,11 @@ class InstallPalettesFrame(wx.Frame):
buttons_sizer.Add(install_button, proportion=0, flag=wx.ALIGN_RIGHT|wx.ALL, border=5)
cancel_button = wx.Button(panel, wx.ID_CANCEL, _("Cancel"))
buttons_sizer.Add(cancel_button, proportion=0, flag=wx.ALIGN_RIGHT|wx.ALL, border=5)
- sizer.Add(buttons_sizer, proportion=0, flag=wx.ALIGN_RIGHT)
-
- outer_sizer = wx.BoxSizer(wx.HORIZONTAL)
- outer_sizer.Add(sizer, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
+ sizer.Add(buttons_sizer, proportion=1, flag=wx.ALIGN_RIGHT|wx.ALIGN_BOTTOM)
- panel.SetSizer(outer_sizer)
+ panel.SetSizer(sizer)
panel.Layout()
- chooser_button.Bind(wx.EVT_BUTTON, self.chooser_button_clicked)
cancel_button.Bind(wx.EVT_BUTTON, self.cancel_button_clicked)
install_button.Bind(wx.EVT_BUTTON, self.install_button_clicked)
@@ -57,36 +55,37 @@ class InstallPalettesFrame(wx.Frame):
self.Destroy()
def chooser_button_clicked(self, event):
- dialog = wx.DirDialog(self, _("Choose Inkscape palettes directory"))
+ dialog = wx.DirDialog(self, _("Choose Inkscape directory"))
if dialog.ShowModal() != wx.ID_CANCEL:
self.path_input.SetValue(dialog.GetPath())
def install_button_clicked(self, event):
try:
- self.install_palettes()
+ self.install_addons('palettes')
+ self.install_addons('symbols')
except Exception, e:
wx.MessageDialog(self,
- _('Thread palette installation failed') + ': \n' + traceback.format_exc(),
+ _('Inkscape add-on installation failed') + ': \n' + traceback.format_exc(),
_('Installation Failed'),
wx.OK).ShowModal()
else:
wx.MessageDialog(self,
- _('Thread palette files have been installed. Please restart Inkscape to load the new palettes.'),
+ _('Inkscape add-on files have been installed. Please restart Inkscape to load the new add-ons.'),
_('Installation Completed'),
wx.OK).ShowModal()
self.Destroy()
- def install_palettes(self):
- path = self.path_input.GetValue()
- palettes_dir = self.get_bundled_palettes_dir()
- self.copy_files(glob(os.path.join(palettes_dir, "*")), path)
+ def install_addons(self, type):
+ path = os.path.join(self.path, type)
+ src_dir = self.get_bundled_dir(type)
+ self.copy_files(glob(os.path.join(src_dir, "*")), path)
- def get_bundled_palettes_dir(self):
+ def get_bundled_dir(self, name):
if getattr(sys, 'frozen', None) is not None:
- return realpath(os.path.join(sys._MEIPASS, '..', 'palettes'))
+ return realpath(os.path.join(sys._MEIPASS, '..', name))
else:
- return os.path.join(dirname(realpath(__file__)), 'palettes')
+ return realpath(os.path.join(dirname(realpath(__file__)), '..', '..', name))
if (sys.platform == "win32"):
# If we try to just use shutil.copy it says the operation requires elevation.
@@ -104,9 +103,9 @@ class InstallPalettesFrame(wx.Frame):
for palette_file in files:
shutil.copy(palette_file, dest)
-class Palettes(inkex.Effect):
+class Install(inkex.Effect):
def effect(self):
app = wx.App()
- installer_frame = InstallPalettesFrame(None, title=_("Ink/Stitch Thread Palette Installer"), size=(450, 200))
+ installer_frame = InstallerFrame(None, title=_("Ink/Stitch Add-ons Installer"), size=(550, 250))
installer_frame.Show()
app.MainLoop()
diff --git a/lib/extensions/params.py b/lib/extensions/params.py
index 9d8de41b..58fedd6b 100644
--- a/lib/extensions/params.py
+++ b/lib/extensions/params.py
@@ -19,6 +19,7 @@ from ..stitch_plan import patches_to_stitch_plan
from ..elements import EmbroideryElement, Fill, AutoFill, Stroke, SatinColumn
from ..utils import save_stderr, restore_stderr
from ..simulator import EmbroiderySimulator
+from ..commands import is_command
def presets_path():
@@ -655,7 +656,7 @@ class Params(InkstitchExtension):
classes.append(AutoFill)
classes.append(Fill)
- if element.get_style("stroke"):
+ if element.get_style("stroke") and not is_command(node):
classes.append(Stroke)
if element.get_style("stroke-dasharray") is None:
diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py
index 518a2812..6326ced2 100644
--- a/lib/stitches/auto_fill.py
+++ b/lib/stitches/auto_fill.py
@@ -2,7 +2,7 @@ import sys
import shapely
import networkx
import math
-from itertools import groupby
+from itertools import groupby, izip
from collections import deque
from .fill import intersect_region_with_grating, row_num, stitch_row
@@ -14,18 +14,38 @@ from ..utils.geometry import Point as InkstitchPoint
class MaxQueueLengthExceeded(Exception):
pass
+class PathEdge(object):
+ OUTLINE_KEYS = ("outline", "extra", "initial")
+ SEGMENT_KEY = "segment"
-def auto_fill(shape, angle, row_spacing, end_row_spacing, max_stitch_length, running_stitch_length, staggers, starting_point=None):
+ def __init__(self, nodes, key):
+ self.nodes = nodes
+ self._sorted_nodes = tuple(sorted(self.nodes))
+ self.key = key
+
+ def __getitem__(self, item):
+ return self.nodes[item]
+
+ def __hash__(self):
+ return hash((self._sorted_nodes, self.key))
+
+ def __eq__(self, other):
+ return self._sorted_nodes == other._sorted_nodes and self.key == other.key
+
+ def is_outline(self):
+ return self.key in self.OUTLINE_KEYS
+
+ def is_segment(self):
+ return self.key == self.SEGMENT_KEY
+
+def auto_fill(shape, angle, row_spacing, end_row_spacing, max_stitch_length, running_stitch_length, staggers, starting_point, ending_point=None):
stitches = []
rows_of_segments = intersect_region_with_grating(shape, angle, row_spacing, end_row_spacing)
segments = [segment for row in rows_of_segments for segment in row]
graph = build_graph(shape, segments, angle, row_spacing)
- path = find_stitch_path(graph, segments)
-
- if starting_point:
- stitches.extend(connect_points(shape, starting_point, path[0][0], running_stitch_length))
+ path = find_stitch_path(graph, segments, starting_point, ending_point)
stitches.extend(path_to_stitches(graph, path, shape, angle, row_spacing, max_stitch_length, running_stitch_length, staggers))
@@ -134,8 +154,6 @@ def build_graph(shape, segments, angle, row_spacing):
else:
edge_set = 1
- #print >> sys.stderr, outline_index, "es", edge_set, "rn", row_num, inkstitch.Point(*nodes[0]) * self.north(angle), inkstitch.Point(*nodes[1]) * self.north(angle)
-
# add an edge between each successive node
for i, (node1, node2) in enumerate(zip(nodes, nodes[1:] + [nodes[0]])):
graph.add_edge(node1, node2, key="outline")
@@ -157,14 +175,20 @@ def node_list_to_edge_list(node_list):
def bfs_for_loop(graph, starting_node, max_queue_length=2000):
to_search = deque()
- to_search.appendleft(([starting_node], set(), 0))
+ to_search.append((None, set()))
while to_search:
if len(to_search) > max_queue_length:
raise MaxQueueLengthExceeded()
- path, visited_edges, visited_segments = to_search.pop()
- ending_node = path[-1]
+ path, visited_edges = to_search.pop()
+
+ if path is None:
+ # This is the very first time through the loop, so initialize.
+ path = []
+ ending_node = starting_node
+ else:
+ ending_node = path[-1][-1]
# get a list of neighbors paired with the key of the edge I can follow to get there
neighbors = [
@@ -178,26 +202,21 @@ def bfs_for_loop(graph, starting_node, max_queue_length=2000):
for next_node, key in neighbors:
# skip if I've already followed this edge
- edge = (tuple(sorted((ending_node, next_node))), key)
+ edge = PathEdge((ending_node, next_node), key)
if edge in visited_edges:
continue
- new_path = path + [next_node]
-
- if key == "segment":
- new_visited_segments = visited_segments + 1
- else:
- new_visited_segments = visited_segments
+ new_path = path + [edge]
if next_node == starting_node:
# ignore trivial loops (down and back a doubled edge)
if len(new_path) > 3:
- return node_list_to_edge_list(new_path), new_visited_segments
+ return new_path
new_visited_edges = visited_edges.copy()
new_visited_edges.add(edge)
- to_search.appendleft((new_path, new_visited_edges, new_visited_segments))
+ to_search.appendleft((new_path, new_visited_edges))
def find_loop(graph, starting_nodes):
@@ -216,14 +235,6 @@ def find_loop(graph, starting_nodes):
somewhere else.
"""
- #loop = self.simple_loop(graph, starting_nodes[-2])
-
- #if loop:
- # print >> sys.stderr, "simple_loop success"
- # starting_nodes.pop()
- # starting_nodes.pop()
- # return loop
-
loop = None
retry = []
max_queue_length = 2000
@@ -231,7 +242,6 @@ def find_loop(graph, starting_nodes):
while not loop:
while not loop and starting_nodes:
starting_node = starting_nodes.pop()
- #print >> sys.stderr, "find loop from", starting_node
try:
# Note: if bfs_for_loop() returns None, no loop can be
@@ -240,12 +250,7 @@ def find_loop(graph, starting_nodes):
# case we discard that node and try the next.
loop = bfs_for_loop(graph, starting_node, max_queue_length)
- #if not loop:
- #print >> dbg, "failed on", starting_node
- #dbg.flush()
except MaxQueueLengthExceeded:
- #print >> dbg, "gave up on", starting_node
- #dbg.flush()
# We're giving up on this node for now. We could try
# this node again later, so add it to the bottm of the
# stack.
@@ -272,7 +277,7 @@ def insert_loop(path, loop):
start and end point. The points will be specified in order, such
that they will look like this:
- ((p1, p2), (p2, p3), (p3, p4) ... (pn, p1))
+ ((p1, p2), (p2, p3), (p3, p4), ...)
path will be modified in place.
"""
@@ -282,11 +287,59 @@ def insert_loop(path, loop):
for i, (start, end) in enumerate(path):
if start == loop_start:
break
+ else:
+ # if we didn't find the start of the loop in the list at all, it must
+ # be the endpoint of the last segment
+ i += 1
path[i:i] = loop
+def nearest_node_on_outline(graph, point, outline_index=0):
+ point = shapely.geometry.Point(*point)
+ outline_nodes = [node for node, data in graph.nodes(data=True) if data['index'] == outline_index]
+ nearest = min(outline_nodes, key=lambda node: shapely.geometry.Point(*node).distance(point))
-def find_stitch_path(graph, segments):
+ return nearest
+
+def get_outline_nodes(graph, outline_index=0):
+ outline_nodes = [(node, data['projection']) \
+ for node, data \
+ in graph.nodes(data=True) \
+ if data['index'] == outline_index]
+ outline_nodes.sort(key=lambda (node, projection): projection)
+ outline_nodes = [node for node, data in outline_nodes]
+
+ return outline_nodes
+
+def find_initial_path(graph, starting_point, ending_point=None):
+ starting_node = nearest_node_on_outline(graph, starting_point)
+
+ if ending_point is None:
+ # If they didn't give an ending point, pick either neighboring node
+ # along the outline -- doesn't matter which. We do this because
+ # the algorithm requires we start with _some_ path.
+ neighbors = [n for n, keys in graph.adj[starting_node].iteritems() if 'outline' in keys]
+ return [PathEdge((starting_node, neighbors[0]), "initial")]
+ else:
+ ending_node = nearest_node_on_outline(graph, ending_point)
+ outline_nodes = get_outline_nodes(graph)
+
+ # Multiply the outline_nodes list by 2 (duplicate it) because
+ # the ending_node may occur first.
+ outline_nodes *= 2
+ start_index = outline_nodes.index(starting_node)
+ end_index = outline_nodes.index(ending_node, start_index)
+ nodes = outline_nodes[start_index:end_index + 1]
+
+ # we have a series of sequential points, but we need to
+ # turn it into an edge list
+ path = []
+ for start, end in izip(nodes[:-1], nodes[1:]):
+ path.append(PathEdge((start, end), "initial"))
+
+ return path
+
+def find_stitch_path(graph, segments, starting_point=None, ending_point=None):
"""find a path that visits every grating segment exactly once
Theoretically, we just need to find an Eulerian Path in the graph.
@@ -294,13 +347,14 @@ def find_stitch_path(graph, segments):
The edges on the outline of the region are only there to help us get
from one grating segment to the next.
- We'll build a "cycle" (a path that ends where it starts) using
- Hierholzer's algorithm. We'll stop once we've visited every grating
- segment.
+ We'll build a Eulerian Path using Hierholzer's algorithm. A true
+ Eulerian Path would visit every single edge (including all the extras
+ we inserted in build_graph()),but we'll stop short once we've visited
+ every grating segment since that's all we really care about.
Hierholzer's algorithm says to select an arbitrary starting node at
each step. In order to produce a reasonable stitch path, we'll select
- the vertex carefully such that we get back-and-forth traversal like
+ the starting node carefully such that we get back-and-forth traversal like
mowing a lawn.
To do this, we'll use a simple heuristic: try to start from nodes in
@@ -313,40 +367,42 @@ def find_stitch_path(graph, segments):
segments_visited = 0
nodes_visited = deque()
- # start with a simple loop: down one segment and then back along the
- # outer border to the starting point.
- path = [segments[0], list(reversed(segments[0]))]
+ if starting_point is None:
+ starting_point = segments[0][0]
+
+ path = find_initial_path(graph, starting_point, ending_point)
- graph.remove_edges_from(path)
+ # Our graph is Eulerian: every node has an even degree. An Eulerian graph
+ # must have an Eulerian Circuit which visits every edge and ends where it
+ # starts.
+ #
+ # However, we're starting with a path and _not_ removing the edges of that
+ # path from the graph. By doing this, we're implicitly adding those edges
+ # to the graph, after which the starting and ending point (and only those
+ # two) will now have odd degree. A graph that's Eulerian except for two
+ # nodes must have an Eulerian Path that starts and ends at those two nodes.
+ # That's how we force the starting and ending point.
- segments_visited += 1
- nodes_visited.extend(segments[0])
+ nodes_visited.append(path[0][0])
while segments_visited < num_segments:
- result = find_loop(graph, nodes_visited)
+ loop = find_loop(graph, nodes_visited)
- if not result:
+ if not loop:
print >> sys.stderr, _("Unexpected error while generating fill stitches. Please send your SVG file to lexelby@github.")
break
- loop, segments = result
-
- #print >> dbg, "found loop:", loop
- #dbg.flush()
-
- segments_visited += segments
- nodes_visited += [edge[0] for edge in loop]
+ segments_visited += sum(1 for edge in loop if edge.is_segment())
+ nodes_visited.extend(edge[0] for edge in loop)
graph.remove_edges_from(loop)
insert_loop(path, loop)
- #if segments_visited >= 12:
- # break
-
- # Now we have a loop that covers every grating segment. It returns to
- # where it started, which is unnecessary, so we'll snip the last bit off.
- #while original_graph.has_edge(*path[-1], key="outline"):
- # path.pop()
+ if ending_point is None:
+ # If they didn't specify an ending point, then the end of the path travels
+ # around the outline back to the start (see find_initial_path()). This
+ # isn't necessary, so remove it.
+ trim_end(path)
return path
@@ -363,10 +419,10 @@ def collapse_sequential_outline_edges(graph, path):
new_path = []
for edge in path:
- if graph.has_edge(*edge, key="segment"):
+ if edge.is_segment():
if start_of_run:
# close off the last run
- new_path.append((start_of_run, edge[0]))
+ new_path.append(PathEdge((start_of_run, edge[0]), "collapsed"))
start_of_run = None
new_path.append(edge)
@@ -376,7 +432,7 @@ def collapse_sequential_outline_edges(graph, path):
if start_of_run:
# if we were still in a run, close it off
- new_path.append((start_of_run, edge[1]))
+ new_path.append(PathEdge((start_of_run, edge[1]), "collapsed"))
return new_path
@@ -416,9 +472,6 @@ def connect_points(shape, start, end, running_stitch_length):
direction = math.copysign(1.0, distance)
one_stitch = running_stitch_length * direction
- #print >> dbg, "connect_points:", outline_index, start, end, distance, stitches, direction
- #dbg.flush()
-
stitches = [InkstitchPoint(*outline.interpolate(pos).coords[0])]
for i in xrange(num_stitches):
@@ -430,11 +483,11 @@ def connect_points(shape, start, end, running_stitch_length):
if (end - stitches[-1]).length() > 0.1 * PIXELS_PER_MM:
stitches.append(end)
- #print >> dbg, "end connect_points"
- #dbg.flush()
-
return stitches
+def trim_end(path):
+ while path and path[-1].is_outline():
+ path.pop()
def path_to_stitches(graph, path, shape, angle, row_spacing, max_stitch_length, running_stitch_length, staggers):
path = collapse_sequential_outline_edges(graph, path)
@@ -442,7 +495,7 @@ def path_to_stitches(graph, path, shape, angle, row_spacing, max_stitch_length,
stitches = []
for edge in path:
- if graph.has_edge(*edge, key="segment"):
+ if edge.is_segment():
stitch_row(stitches, edge[0], edge[1], angle, row_spacing, max_stitch_length, staggers)
else:
stitches.extend(connect_points(shape, edge[0], edge[1], running_stitch_length))
diff --git a/lib/svg/__init__.py b/lib/svg/__init__.py
index 1895bba4..50543b1b 100644
--- a/lib/svg/__init__.py
+++ b/lib/svg/__init__.py
@@ -1,2 +1,3 @@
from .svg import color_block_to_point_lists, render_stitch_plan
from .units import *
+from .path import apply_transforms
diff --git a/lib/svg/path.py b/lib/svg/path.py
new file mode 100644
index 00000000..a8012774
--- /dev/null
+++ b/lib/svg/path.py
@@ -0,0 +1,20 @@
+import simpletransform
+import cubicsuperpath
+
+from .units import get_viewbox_transform
+
+def apply_transforms(path, node):
+ # start with the identity transform
+ transform = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
+
+ # combine this node's transform with all parent groups' transforms
+ transform = simpletransform.composeParents(node, transform)
+
+ # add in the transform implied by the viewBox
+ viewbox_transform = get_viewbox_transform(node.getroottree().getroot())
+ transform = simpletransform.composeTransform(viewbox_transform, transform)
+
+ # apply the combined transform to this node's path
+ simpletransform.applyTransformToPath(transform, path)
+
+ return path
diff --git a/lib/svg/tags.py b/lib/svg/tags.py
index fee59957..5488608c 100644
--- a/lib/svg/tags.py
+++ b/lib/svg/tags.py
@@ -5,8 +5,13 @@ SVG_PATH_TAG = inkex.addNS('path', 'svg')
SVG_POLYLINE_TAG = inkex.addNS('polyline', 'svg')
SVG_DEFS_TAG = inkex.addNS('defs', 'svg')
SVG_GROUP_TAG = inkex.addNS('g', 'svg')
+SVG_SYMBOL_TAG = inkex.addNS('symbol', 'svg')
+SVG_USE_TAG = inkex.addNS('use', 'svg')
INKSCAPE_LABEL = inkex.addNS('label', 'inkscape')
INKSCAPE_GROUPMODE = inkex.addNS('groupmode', 'inkscape')
+CONNECTION_START = inkex.addNS('connection-start', 'inkscape')
+CONNECTION_END = inkex.addNS('connection-end', 'inkscape')
+XLINK_HREF = inkex.addNS('href', 'xlink')
EMBROIDERABLE_TAGS = (SVG_PATH_TAG, SVG_POLYLINE_TAG)
diff --git a/messages.po b/messages.po
index fb081eb1..d6f156f2 100644
--- a/messages.po
+++ b/messages.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2018-06-16 22:33-0400\n"
+"POT-Creation-Date: 2018-06-28 20:32-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -47,6 +47,10 @@ msgstr ""
msgid "Expand"
msgstr ""
+#, python-format
+msgid "%(id)s has more than one command of type '%(command)s' linked to it"
+msgstr ""
+
msgid "TRIM after"
msgstr ""
@@ -194,10 +198,16 @@ msgid ""
"Seeing a 'no such option' message? Please restart Inkscape to fix."
msgstr ""
-msgid "Directory in which to install palettes:"
+msgid ""
+"Ink/Stitch can install files (\"add-ons\") that make it easier to use "
+"Inkscape to create machine embroidery designs. These add-ons will be "
+"installed:"
+msgstr ""
+
+msgid "thread manufacturer color palettes"
msgstr ""
-msgid "Choose another directory..."
+msgid "Ink/Stitch visual commands (Object -> Symbols...)"
msgstr ""
msgid "Install"
@@ -206,24 +216,24 @@ msgstr ""
msgid "Cancel"
msgstr ""
-msgid "Choose Inkscape palettes directory"
+msgid "Choose Inkscape directory"
msgstr ""
-msgid "Thread palette installation failed"
+msgid "Inkscape add-on installation failed"
msgstr ""
msgid "Installation Failed"
msgstr ""
msgid ""
-"Thread palette files have been installed. Please restart Inkscape to "
-"load the new palettes."
+"Inkscape add-on files have been installed. Please restart Inkscape to "
+"load the new add-ons."
msgstr ""
msgid "Installation Completed"
msgstr ""
-msgid "Ink/Stitch Thread Palette Installer"
+msgid "Ink/Stitch Add-ons Installer"
msgstr ""
msgid "These settings will be applied to 1 object."
diff --git a/palettes/ARC Polyester.gpl b/palettes/InkStitch ARC Polyester.gpl
index a9d2b2cd..a9d2b2cd 100644
--- a/palettes/ARC Polyester.gpl
+++ b/palettes/InkStitch ARC Polyester.gpl
diff --git a/palettes/ARC Rayon.gpl b/palettes/InkStitch ARC Rayon.gpl
index 909aebb2..909aebb2 100644
--- a/palettes/ARC Rayon.gpl
+++ b/palettes/InkStitch ARC Rayon.gpl
diff --git a/palettes/Admelody Polyester.gpl b/palettes/InkStitch Admelody Polyester.gpl
index 1bad74a3..1bad74a3 100644
--- a/palettes/Admelody Polyester.gpl
+++ b/palettes/InkStitch Admelody Polyester.gpl
diff --git a/palettes/Admelody Rayon.gpl b/palettes/InkStitch Admelody Rayon.gpl
index d80ec7fb..d80ec7fb 100644
--- a/palettes/Admelody Rayon.gpl
+++ b/palettes/InkStitch Admelody Rayon.gpl
diff --git a/palettes/Anchor.gpl b/palettes/InkStitch Anchor.gpl
index 96a88b2f..96a88b2f 100644
--- a/palettes/Anchor.gpl
+++ b/palettes/InkStitch Anchor.gpl
diff --git a/palettes/Aurifil Lana.gpl b/palettes/InkStitch Aurifil Lana.gpl
index 81180771..81180771 100644
--- a/palettes/Aurifil Lana.gpl
+++ b/palettes/InkStitch Aurifil Lana.gpl
diff --git a/palettes/Aurifil Mako.gpl b/palettes/InkStitch Aurifil Mako.gpl
index b8ed79af..b8ed79af 100644
--- a/palettes/Aurifil Mako.gpl
+++ b/palettes/InkStitch Aurifil Mako.gpl
diff --git a/palettes/Aurifil Polyester.gpl b/palettes/InkStitch Aurifil Polyester.gpl
index 6a8ead87..6a8ead87 100644
--- a/palettes/Aurifil Polyester.gpl
+++ b/palettes/InkStitch Aurifil Polyester.gpl
diff --git a/palettes/Aurifil Rayon.gpl b/palettes/InkStitch Aurifil Rayon.gpl
index bf608c9a..bf608c9a 100644
--- a/palettes/Aurifil Rayon.gpl
+++ b/palettes/InkStitch Aurifil Rayon.gpl
diff --git a/palettes/Aurifil Royal.gpl b/palettes/InkStitch Aurifil Royal.gpl
index d997298f..d997298f 100644
--- a/palettes/Aurifil Royal.gpl
+++ b/palettes/InkStitch Aurifil Royal.gpl
diff --git a/palettes/BFC Polyester.gpl b/palettes/InkStitch BFC Polyester.gpl
index 766f104f..766f104f 100644
--- a/palettes/BFC Polyester.gpl
+++ b/palettes/InkStitch BFC Polyester.gpl
diff --git a/palettes/Brildor AC.gpl b/palettes/InkStitch Brildor AC.gpl
index 114638e6..114638e6 100644
--- a/palettes/Brildor AC.gpl
+++ b/palettes/InkStitch Brildor AC.gpl
diff --git a/palettes/Brildor CO.gpl b/palettes/InkStitch Brildor CO.gpl
index 4f7e8dc3..4f7e8dc3 100644
--- a/palettes/Brildor CO.gpl
+++ b/palettes/InkStitch Brildor CO.gpl
diff --git a/palettes/Brildor MF.gpl b/palettes/InkStitch Brildor MF.gpl
index 588e3687..588e3687 100644
--- a/palettes/Brildor MF.gpl
+++ b/palettes/InkStitch Brildor MF.gpl
diff --git a/palettes/Brildor NY.gpl b/palettes/InkStitch Brildor NY.gpl
index 68faab6b..68faab6b 100644
--- a/palettes/Brildor NY.gpl
+++ b/palettes/InkStitch Brildor NY.gpl
diff --git a/palettes/Brildor PB.gpl b/palettes/InkStitch Brildor PB.gpl
index 60ccae7f..60ccae7f 100644
--- a/palettes/Brildor PB.gpl
+++ b/palettes/InkStitch Brildor PB.gpl
diff --git a/palettes/Brother Country.gpl b/palettes/InkStitch Brother Country.gpl
index c15e8989..c15e8989 100644
--- a/palettes/Brother Country.gpl
+++ b/palettes/InkStitch Brother Country.gpl
diff --git a/palettes/Brother Embroidery.gpl b/palettes/InkStitch Brother Embroidery.gpl
index 915989ed..915989ed 100644
--- a/palettes/Brother Embroidery.gpl
+++ b/palettes/InkStitch Brother Embroidery.gpl
diff --git a/palettes/Coats Alcazar Jazz.gpl b/palettes/InkStitch Coats Alcazar Jazz.gpl
index f9cd6c9d..f9cd6c9d 100644
--- a/palettes/Coats Alcazar Jazz.gpl
+++ b/palettes/InkStitch Coats Alcazar Jazz.gpl
diff --git a/palettes/Coats Alcazar.gpl b/palettes/InkStitch Coats Alcazar.gpl
index e1891b03..e1891b03 100644
--- a/palettes/Coats Alcazar.gpl
+++ b/palettes/InkStitch Coats Alcazar.gpl
diff --git a/palettes/Coats Sylko USA.gpl b/palettes/InkStitch Coats Sylko USA.gpl
index af220f69..af220f69 100644
--- a/palettes/Coats Sylko USA.gpl
+++ b/palettes/InkStitch Coats Sylko USA.gpl
diff --git a/palettes/Coats Sylko.gpl b/palettes/InkStitch Coats Sylko.gpl
index 50960152..50960152 100644
--- a/palettes/Coats Sylko.gpl
+++ b/palettes/InkStitch Coats Sylko.gpl
diff --git a/palettes/DMC.gpl b/palettes/InkStitch DMC.gpl
index 38d68bc8..38d68bc8 100644
--- a/palettes/DMC.gpl
+++ b/palettes/InkStitch DMC.gpl
diff --git a/palettes/Embroidex.gpl b/palettes/InkStitch Embroidex.gpl
index 06d75f75..06d75f75 100644
--- a/palettes/Embroidex.gpl
+++ b/palettes/InkStitch Embroidex.gpl
diff --git a/palettes/Emmel.gpl b/palettes/InkStitch Emmel.gpl
index 908ab78f..908ab78f 100644
--- a/palettes/Emmel.gpl
+++ b/palettes/InkStitch Emmel.gpl
diff --git a/palettes/Fil-Tec Glide.gpl b/palettes/InkStitch Fil-Tec Glide.gpl
index 7e7d30c0..7e7d30c0 100644
--- a/palettes/Fil-Tec Glide.gpl
+++ b/palettes/InkStitch Fil-Tec Glide.gpl
diff --git a/palettes/Floriani Polyester.gpl b/palettes/InkStitch Floriani Polyester.gpl
index 36f76390..36f76390 100644
--- a/palettes/Floriani Polyester.gpl
+++ b/palettes/InkStitch Floriani Polyester.gpl
diff --git a/palettes/FuFu Polyester.gpl b/palettes/InkStitch FuFu Polyester.gpl
index fe7cb8f6..fe7cb8f6 100644
--- a/palettes/FuFu Polyester.gpl
+++ b/palettes/InkStitch FuFu Polyester.gpl
diff --git a/palettes/FuFu Rayon.gpl b/palettes/InkStitch FuFu Rayon.gpl
index 2f649173..2f649173 100644
--- a/palettes/FuFu Rayon.gpl
+++ b/palettes/InkStitch FuFu Rayon.gpl
diff --git a/palettes/Gunold.gpl b/palettes/InkStitch Gunold.gpl
index efa91272..efa91272 100644
--- a/palettes/Gunold.gpl
+++ b/palettes/InkStitch Gunold.gpl
diff --git a/palettes/Gutermann Creativ Dekor.gpl b/palettes/InkStitch Gutermann Creativ Dekor.gpl
index aec5e3f1..aec5e3f1 100644
--- a/palettes/Gutermann Creativ Dekor.gpl
+++ b/palettes/InkStitch Gutermann Creativ Dekor.gpl
diff --git a/palettes/Hemingworth.gpl b/palettes/InkStitch Hemingworth.gpl
index 23438122..23438122 100644
--- a/palettes/Hemingworth.gpl
+++ b/palettes/InkStitch Hemingworth.gpl
diff --git a/palettes/Isacord Polyester.gpl b/palettes/InkStitch Isacord Polyester.gpl
index a0e14c95..a0e14c95 100644
--- a/palettes/Isacord Polyester.gpl
+++ b/palettes/InkStitch Isacord Polyester.gpl
diff --git a/palettes/Isafil Rayon.gpl b/palettes/InkStitch Isafil Rayon.gpl
index 5e82a6d8..5e82a6d8 100644
--- a/palettes/Isafil Rayon.gpl
+++ b/palettes/InkStitch Isafil Rayon.gpl
diff --git a/palettes/Isalon Polyester.gpl b/palettes/InkStitch Isalon Polyester.gpl
index 5374bba2..5374bba2 100644
--- a/palettes/Isalon Polyester.gpl
+++ b/palettes/InkStitch Isalon Polyester.gpl
diff --git a/palettes/Janome.gpl b/palettes/InkStitch Janome.gpl
index 44113b29..44113b29 100644
--- a/palettes/Janome.gpl
+++ b/palettes/InkStitch Janome.gpl
diff --git a/palettes/King Star.gpl b/palettes/InkStitch King Star.gpl
index bf2020c1..bf2020c1 100644
--- a/palettes/King Star.gpl
+++ b/palettes/InkStitch King Star.gpl
diff --git a/palettes/MTB - Embroidex.gpl b/palettes/InkStitch MTB - Embroidex.gpl
index 378f92fd..378f92fd 100644
--- a/palettes/MTB - Embroidex.gpl
+++ b/palettes/InkStitch MTB - Embroidex.gpl
diff --git a/palettes/Madeira Burmilana.gpl b/palettes/InkStitch Madeira Burmilana.gpl
index 996c9963..996c9963 100644
--- a/palettes/Madeira Burmilana.gpl
+++ b/palettes/InkStitch Madeira Burmilana.gpl
diff --git a/palettes/Madeira Matt.gpl b/palettes/InkStitch Madeira Matt.gpl
index 47973362..47973362 100644
--- a/palettes/Madeira Matt.gpl
+++ b/palettes/InkStitch Madeira Matt.gpl
diff --git a/palettes/Madeira Polyneon.gpl b/palettes/InkStitch Madeira Polyneon.gpl
index 065330eb..065330eb 100644
--- a/palettes/Madeira Polyneon.gpl
+++ b/palettes/InkStitch Madeira Polyneon.gpl
diff --git a/palettes/Madeira Rayon.gpl b/palettes/InkStitch Madeira Rayon.gpl
index 8f85b628..8f85b628 100644
--- a/palettes/Madeira Rayon.gpl
+++ b/palettes/InkStitch Madeira Rayon.gpl
diff --git a/palettes/Marathon Polyester.gpl b/palettes/InkStitch Marathon Polyester.gpl
index 4bae92d1..4bae92d1 100644
--- a/palettes/Marathon Polyester.gpl
+++ b/palettes/InkStitch Marathon Polyester.gpl
diff --git a/palettes/Marathon Rayon V3.gpl b/palettes/InkStitch Marathon Rayon V3.gpl
index 31eb396d..31eb396d 100644
--- a/palettes/Marathon Rayon V3.gpl
+++ b/palettes/InkStitch Marathon Rayon V3.gpl
diff --git a/palettes/Marathon Rayon.gpl b/palettes/InkStitch Marathon Rayon.gpl
index c70f8646..c70f8646 100644
--- a/palettes/Marathon Rayon.gpl
+++ b/palettes/InkStitch Marathon Rayon.gpl
diff --git a/palettes/Metro.gpl b/palettes/InkStitch Metro.gpl
index 9ba6dd2d..9ba6dd2d 100644
--- a/palettes/Metro.gpl
+++ b/palettes/InkStitch Metro.gpl
diff --git a/palettes/Mettler Embroidery.gpl b/palettes/InkStitch Mettler Embroidery.gpl
index 5a7993b9..5a7993b9 100644
--- a/palettes/Mettler Embroidery.gpl
+++ b/palettes/InkStitch Mettler Embroidery.gpl
diff --git a/palettes/Mettler Poly Sheen.gpl b/palettes/InkStitch Mettler Poly Sheen.gpl
index d1d91fa8..d1d91fa8 100644
--- a/palettes/Mettler Poly Sheen.gpl
+++ b/palettes/InkStitch Mettler Poly Sheen.gpl
diff --git a/palettes/Outback Embroidery Rayon.gpl b/palettes/InkStitch Outback Embroidery Rayon.gpl
index 3344ecc5..3344ecc5 100644
--- a/palettes/Outback Embroidery Rayon.gpl
+++ b/palettes/InkStitch Outback Embroidery Rayon.gpl
diff --git a/palettes/Poly X40.gpl b/palettes/InkStitch Poly X40.gpl
index c8336589..c8336589 100644
--- a/palettes/Poly X40.gpl
+++ b/palettes/InkStitch Poly X40.gpl
diff --git a/palettes/Princess.gpl b/palettes/InkStitch Princess.gpl
index 237fcb9e..237fcb9e 100644
--- a/palettes/Princess.gpl
+++ b/palettes/InkStitch Princess.gpl
diff --git a/palettes/RAL.gpl b/palettes/InkStitch RAL.gpl
index 22028e0a..22028e0a 100644
--- a/palettes/RAL.gpl
+++ b/palettes/InkStitch RAL.gpl
diff --git a/palettes/Radiant Rayon.gpl b/palettes/InkStitch Radiant Rayon.gpl
index 428a9d17..428a9d17 100644
--- a/palettes/Radiant Rayon.gpl
+++ b/palettes/InkStitch Radiant Rayon.gpl
diff --git a/palettes/Robison-Anton Polyester.gpl b/palettes/InkStitch Robison-Anton Polyester.gpl
index 4899053e..4899053e 100644
--- a/palettes/Robison-Anton Polyester.gpl
+++ b/palettes/InkStitch Robison-Anton Polyester.gpl
diff --git a/palettes/Robison-Anton Rayon.gpl b/palettes/InkStitch Robison-Anton Rayon.gpl
index d2db91ec..d2db91ec 100644
--- a/palettes/Robison-Anton Rayon.gpl
+++ b/palettes/InkStitch Robison-Anton Rayon.gpl
diff --git a/palettes/Royal Polyester.gpl b/palettes/InkStitch Royal Polyester.gpl
index efc142d6..efc142d6 100644
--- a/palettes/Royal Polyester.gpl
+++ b/palettes/InkStitch Royal Polyester.gpl
diff --git a/palettes/Royal Viscose Rayon.gpl b/palettes/InkStitch Royal Viscose Rayon.gpl
index 375e588a..375e588a 100644
--- a/palettes/Royal Viscose Rayon.gpl
+++ b/palettes/InkStitch Royal Viscose Rayon.gpl
diff --git a/palettes/Sigma.gpl b/palettes/InkStitch Sigma.gpl
index 0c95201c..0c95201c 100644
--- a/palettes/Sigma.gpl
+++ b/palettes/InkStitch Sigma.gpl
diff --git a/palettes/Simthread Polyester.gpl b/palettes/InkStitch Simthread Polyester.gpl
index aae11d37..aae11d37 100644
--- a/palettes/Simthread Polyester.gpl
+++ b/palettes/InkStitch Simthread Polyester.gpl
diff --git a/palettes/Simthread Rayon.gpl b/palettes/InkStitch Simthread Rayon.gpl
index ec166d59..ec166d59 100644
--- a/palettes/Simthread Rayon.gpl
+++ b/palettes/InkStitch Simthread Rayon.gpl
diff --git a/palettes/Sulky Polyester.gpl b/palettes/InkStitch Sulky Polyester.gpl
index 10ebb5f2..10ebb5f2 100644
--- a/palettes/Sulky Polyester.gpl
+++ b/palettes/InkStitch Sulky Polyester.gpl
diff --git a/palettes/Sulky Rayon.gpl b/palettes/InkStitch Sulky Rayon.gpl
index a30e11db..a30e11db 100644
--- a/palettes/Sulky Rayon.gpl
+++ b/palettes/InkStitch Sulky Rayon.gpl
diff --git a/palettes/Swist Rayon.gpl b/palettes/InkStitch Swist Rayon.gpl
index 2e6a90e7..2e6a90e7 100644
--- a/palettes/Swist Rayon.gpl
+++ b/palettes/InkStitch Swist Rayon.gpl
diff --git a/palettes/Tristar Polyester.gpl b/palettes/InkStitch Tristar Polyester.gpl
index f3a9f642..f3a9f642 100644
--- a/palettes/Tristar Polyester.gpl
+++ b/palettes/InkStitch Tristar Polyester.gpl
diff --git a/palettes/Tristar Rayon.gpl b/palettes/InkStitch Tristar Rayon.gpl
index 1b0954be..1b0954be 100644
--- a/palettes/Tristar Rayon.gpl
+++ b/palettes/InkStitch Tristar Rayon.gpl
diff --git a/palettes/Viking Palette.gpl b/palettes/InkStitch Viking Palette.gpl
index f25af56f..f25af56f 100644
--- a/palettes/Viking Palette.gpl
+++ b/palettes/InkStitch Viking Palette.gpl
diff --git a/palettes/Vyapar Rayon.gpl b/palettes/InkStitch Vyapar Rayon.gpl
index 4a846cab..4a846cab 100644
--- a/palettes/Vyapar Rayon.gpl
+++ b/palettes/InkStitch Vyapar Rayon.gpl
diff --git a/palettes/Wonderfil Polyester.gpl b/palettes/InkStitch Wonderfil Polyester.gpl
index 0430d4a1..0430d4a1 100644
--- a/palettes/Wonderfil Polyester.gpl
+++ b/palettes/InkStitch Wonderfil Polyester.gpl
diff --git a/palettes/Wonderfil Rayon.gpl b/palettes/InkStitch Wonderfil Rayon.gpl
index 28e82005..28e82005 100644
--- a/palettes/Wonderfil Rayon.gpl
+++ b/palettes/InkStitch Wonderfil Rayon.gpl
diff --git a/symbols/inkstitch.svg b/symbols/inkstitch.svg
new file mode 100644
index 00000000..2af52d92
--- /dev/null
+++ b/symbols/inkstitch.svg
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="100mm"
+ height="100mm"
+ viewBox="0 0 377.95276 377.95276"
+ id="svg8375"
+ version="1.1"
+ inkscape:version="0.92.3 (unknown)"
+ sodipodi:docname="inkstitch.svg">
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2"
+ inkscape:cx="64.500275"
+ inkscape:cy="322.07765"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ units="mm"
+ inkscape:window-width="1366"
+ inkscape:window-height="705"
+ inkscape:window-x="-4"
+ inkscape:window-y="-4"
+ inkscape:window-maximized="1"
+ inkscape:measure-start="128.23,226.536"
+ inkscape:measure-end="114.217,226.536"
+ inkscape:snap-global="false"
+ showguides="false">
+ <inkscape:grid
+ empspacing="2"
+ opacity="0.1254902"
+ color="#f03fff"
+ spacingy="18.897638"
+ spacingx="18.897638"
+ units="mm"
+ id="grid5001"
+ type="xygrid" />
+ </sodipodi:namedview>
+ <title
+ id="title9425">Ink/Stitch Commands</title>
+ <defs
+ id="defs8377">
+ <symbol
+ id="inkstitch_fill_end">
+ <title
+ id="title9427">Fill stitch starting point</title>
+ <circle
+ inkscape:label="outline"
+ style="opacity:1;vector-effect:none;fill:#fafafa;fill-opacity:1;fill-rule:evenodd;stroke:#003399;stroke-width:1.06501234;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3.19503705, 3.19503705;stroke-dashoffset:0;stroke-opacity:1"
+ id="circle13166"
+ cx="47.235394"
+ cy="105.91732"
+ r="9.2465534" />
+ <path
+ inkscape:connector-curvature="0"
+ id="rect5371-2"
+ d="m 42.691395,101.26765 h 9.140878 v 9.14087 h -9.140878 z"
+ style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.60622311;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:4.81866985, 4.81866985;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke" />
+ </symbol>
+ <symbol
+ id="inkstitch_fill_start">
+ <title
+ id="title9432">Fill stitch ending point</title>
+ <circle
+ inkscape:label="outline"
+ style="opacity:1;vector-effect:none;fill:#fafafa;fill-opacity:1;fill-rule:evenodd;stroke:#003399;stroke-width:1.06501234;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3.19503705, 3.19503705;stroke-dashoffset:0;stroke-opacity:1"
+ id="circle13166-6"
+ cx="85.223907"
+ cy="106.13256"
+ r="9.2465534" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4183"
+ d="m 91.796747,106.13612 -10.4514,6.03412 v -12.06823 z"
+ inkscape:transform-center-y="3.0183984e-06"
+ inkscape:transform-center-x="-1.7419043"
+ style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.74180555;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </symbol>
+ </defs>
+ <metadata
+ id="metadata8380">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ style="display:inline"
+ transform="translate(0,-58.409503)"
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <use
+ xlink:href="#inkstitch_fill_end"
+ id="use18860"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ <use
+ xlink:href="#inkstitch_fill_start"
+ id="use18871"
+ x="0"
+ y="0"
+ width="100%"
+ height="100%" />
+ </g>
+</svg>