2014-12-26 22:15:48 +00:00
|
|
|
#!/usr/bin/python
|
|
|
|
#
|
|
|
|
# Important resources:
|
|
|
|
# lxml interface for walking SVG tree:
|
|
|
|
# http://codespeak.net/lxml/tutorial.html#elementpath
|
|
|
|
# Inkscape library for extracting paths from SVG:
|
|
|
|
# http://wiki.inkscape.org/wiki/index.php/Python_modules_for_extensions#simplepath.py
|
|
|
|
# Shapely computational geometry library:
|
|
|
|
# http://gispython.org/shapely/manual.html#multipolygons
|
|
|
|
# Embroidery file format documentation:
|
|
|
|
# http://www.achatina.de/sewing/main/TECHNICL.HTM
|
|
|
|
|
|
|
|
import sys
|
2017-11-25 21:22:56 +00:00
|
|
|
import traceback
|
2014-12-26 22:15:48 +00:00
|
|
|
sys.path.append("/usr/share/inkscape/extensions")
|
|
|
|
import os
|
|
|
|
|
2018-03-31 00:37:11 +00:00
|
|
|
import inkex
|
Fix simulate (#42)
* Simulate now works regardless of the output format you chose when you ran Embroider.
* Simulate (and the preview in Params) now respects TRIMs.
* Inkscape restart required (embroider.inx changed).
This one kind of grew in the telling. #37 was a theoretically simple bug, but in reality, the code necessary to fix it was the straw that broke the camel's back, and I had to do a fair bit of (much needed) code reorganization. Mostly the reorganization was just under the hood, but there was one user-facing change around the Embroider extension's settings window.
Way back in the day, the only way to control things like the stitch length or satin density was through global options specified in the extension settings. We've long since moved to per-object params, but for backward compatibility, ink/stitch defaulted to the command-line arguments.
That means that it was possible to get different stitch results from the same SVG file if you changed the extension's settings. For that reason, I never touched mine. I didn't intend for my users to use those extension-level settings at all, and I've planned to remove those settings for awhile now.
At this point, the extension settings just getting in the way of implementing more features, so I'm getting rid of them and moving the defaults into the parameters system. I've still left things like the output format and the collapse length (although I'm considering moving that one too).
2018-01-28 21:10:37 +00:00
|
|
|
import inkstitch
|
2018-03-31 00:37:11 +00:00
|
|
|
from inkstitch import _, PIXELS_PER_MM
|
|
|
|
from inkstitch.extensions import InkstitchExtension
|
|
|
|
from inkstitch.stitch_plan import patches_to_stitch_plan
|
|
|
|
from inkstitch.svg import render_stitch_plan
|
2016-10-20 00:47:30 +00:00
|
|
|
|
2016-11-07 00:30:49 +00:00
|
|
|
|
2018-03-31 00:37:11 +00:00
|
|
|
class Embroider(InkstitchExtension):
|
2016-10-31 02:58:51 +00:00
|
|
|
def __init__(self, *args, **kwargs):
|
2018-03-31 00:37:11 +00:00
|
|
|
InkstitchExtension.__init__(self)
|
2016-10-31 02:58:51 +00:00
|
|
|
self.OptionParser.add_option("-c", "--collapse_len_mm",
|
|
|
|
action="store", type="float",
|
2018-02-28 00:43:15 +00:00
|
|
|
dest="collapse_length_mm", default=3.0,
|
2016-10-31 02:58:51 +00:00
|
|
|
help="max collapse length (mm)")
|
|
|
|
self.OptionParser.add_option("--hide_layers",
|
|
|
|
action="store", type="choice",
|
|
|
|
choices=["true", "false"],
|
|
|
|
dest="hide_layers", default="true",
|
|
|
|
help="Hide all other layers when the embroidery layer is generated")
|
|
|
|
self.OptionParser.add_option("-O", "--output_format",
|
2018-01-24 01:13:37 +00:00
|
|
|
action="store", type="string",
|
|
|
|
dest="output_format", default="csv",
|
|
|
|
help="Output file extenstion (default: csv)")
|
2016-10-31 02:58:51 +00:00
|
|
|
self.OptionParser.add_option("-P", "--path",
|
|
|
|
action="store", type="string",
|
|
|
|
dest="path", default=".",
|
|
|
|
help="Directory in which to store output file")
|
2017-11-05 00:44:57 +00:00
|
|
|
self.OptionParser.add_option("-F", "--output-file",
|
|
|
|
action="store", type="string",
|
2017-11-09 01:04:13 +00:00
|
|
|
dest="output_file",
|
2017-11-05 00:44:57 +00:00
|
|
|
help="Output filename.")
|
2016-10-31 02:58:51 +00:00
|
|
|
self.OptionParser.add_option("-b", "--max-backups",
|
|
|
|
action="store", type="int",
|
|
|
|
dest="max_backups", default=5,
|
|
|
|
help="Max number of backups of output files to keep.")
|
2018-02-05 03:38:24 +00:00
|
|
|
self.OptionParser.usage += _("\n\nSeeing a 'no such option' message? Please restart Inkscape to fix.")
|
2016-10-29 17:28:37 +00:00
|
|
|
|
2016-10-31 02:58:51 +00:00
|
|
|
def get_output_path(self):
|
2017-11-05 00:44:57 +00:00
|
|
|
if self.options.output_file:
|
|
|
|
output_path = os.path.join(self.options.path, self.options.output_file)
|
|
|
|
else:
|
|
|
|
svg_filename = self.document.getroot().get(inkex.addNS('docname', 'sodipodi'), "embroidery.svg")
|
2018-01-24 01:13:37 +00:00
|
|
|
csv_filename = svg_filename.replace('.svg', '.%s' % self.options.output_format)
|
2017-11-05 00:44:57 +00:00
|
|
|
output_path = os.path.join(self.options.path, csv_filename)
|
2016-05-17 02:08:50 +00:00
|
|
|
|
2016-10-31 02:58:51 +00:00
|
|
|
def add_suffix(path, suffix):
|
|
|
|
if suffix > 0:
|
|
|
|
path = "%s.%s" % (path, suffix)
|
2016-05-17 02:08:50 +00:00
|
|
|
|
2016-10-31 02:58:51 +00:00
|
|
|
return path
|
2016-05-17 02:08:50 +00:00
|
|
|
|
2016-10-31 02:58:51 +00:00
|
|
|
def move_if_exists(path, suffix=0):
|
|
|
|
source = add_suffix(path, suffix)
|
2016-10-20 01:43:20 +00:00
|
|
|
|
2016-10-31 02:58:51 +00:00
|
|
|
if suffix >= self.options.max_backups:
|
|
|
|
return
|
2016-10-25 00:51:43 +00:00
|
|
|
|
2016-10-31 02:58:51 +00:00
|
|
|
dest = add_suffix(path, suffix + 1)
|
2016-10-20 01:43:20 +00:00
|
|
|
|
2016-10-31 02:58:51 +00:00
|
|
|
if os.path.exists(source):
|
|
|
|
move_if_exists(path, suffix + 1)
|
2018-02-23 04:06:27 +00:00
|
|
|
|
|
|
|
if os.path.exists(dest):
|
|
|
|
os.remove(dest)
|
|
|
|
|
2016-10-31 02:58:51 +00:00
|
|
|
os.rename(source, dest)
|
2016-10-20 01:43:20 +00:00
|
|
|
|
2016-10-31 02:58:51 +00:00
|
|
|
move_if_exists(output_path)
|
2016-05-17 02:08:50 +00:00
|
|
|
|
2016-10-31 02:58:51 +00:00
|
|
|
return output_path
|
2016-10-21 02:27:48 +00:00
|
|
|
|
2016-10-31 02:58:51 +00:00
|
|
|
def effect(self):
|
2018-03-31 00:37:11 +00:00
|
|
|
if not self.get_elements():
|
2016-10-31 02:58:51 +00:00
|
|
|
return
|
2016-10-25 00:51:43 +00:00
|
|
|
|
2016-10-31 02:58:51 +00:00
|
|
|
if self.options.hide_layers:
|
2018-03-31 00:37:11 +00:00
|
|
|
self.hide_all_layers()
|
2016-10-20 01:43:20 +00:00
|
|
|
|
2018-03-31 00:37:11 +00:00
|
|
|
patches = self.elements_to_patches(self.elements)
|
|
|
|
stitch_plan = patches_to_stitch_plan(patches, self.options.collapse_length_mm * PIXELS_PER_MM)
|
|
|
|
inkstitch.write_embroidery_file(self.get_output_path(), stitch_plan, self.document.getroot())
|
|
|
|
render_stitch_plan(self.document.getroot(), stitch_plan)
|
2016-05-17 02:08:50 +00:00
|
|
|
|
2014-12-26 22:15:48 +00:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2016-10-29 17:28:37 +00:00
|
|
|
sys.setrecursionlimit(100000)
|
2016-01-18 23:31:10 +00:00
|
|
|
e = Embroider()
|
2017-11-25 21:22:56 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
e.affect()
|
|
|
|
except KeyboardInterrupt:
|
2018-03-31 00:37:11 +00:00
|
|
|
# for use at the command prompt for debugging
|
|
|
|
print >> sys.stderr, "interrupted!"
|
|
|
|
print >> sys.stderr, traceback.format_exc()
|