diff --git a/extensions/fablabchemnitz/vpype_tools/vpypetools.py b/extensions/fablabchemnitz/vpypetools/vpypetools.py
similarity index 62%
rename from extensions/fablabchemnitz/vpype_tools/vpypetools.py
rename to extensions/fablabchemnitz/vpypetools/vpypetools.py
index 4a98f2b9..27295e5e 100644
--- a/extensions/fablabchemnitz/vpype_tools/vpypetools.py
+++ b/extensions/fablabchemnitz/vpypetools/vpypetools.py
@@ -1,17 +1,27 @@
#!/usr/bin/env python3
-# suppress some nasty warnings we don't want. Note that this is really generic. For developing purposes re-enable this
+# suppress some nasty warnings we don't want. Note that this is really generic. For developing purposes re-enable this to see errors/deprecations
import logging
for key in logging.Logger.manager.loggerDict:
print(key)
logging.getLogger().setLevel(logging.CRITICAL)
+#for name, logger in logging.root.manager.loggerDict.items():
+# logger.disabled=True
+#import warnings
+#warnings.filterwarnings("ignore", category=DeprecationWarning)
+#warnings.filterwarnings('always', category=DeprecationWarning)
+#with warnings.catch_warnings():
+# warnings.simplefilter("ignore", category=DeprecationWarning)
+#def noop(*args, **kargs): pass
+#warnings.warn = noop
+#logging.captureWarnings(True)
import sys
import os
from lxml import etree
import inkex
-from inkex import transforms
+from inkex import transforms, bezier
from inkex.paths import CubicSuperPath
from inkex.command import inkscape
@@ -26,8 +36,8 @@ from shapely.geometry import LineString, Point
Extension for InkScape 1.X
Author: Mario Voigt / FabLab Chemnitz
Mail: mario.voigt@stadtfabrikanten.org
-Date: 01.04.2021
-Last patch: 01.04.2021
+Date: 02.04.2021
+Last patch: 02.04.2021
License: GNU GPL v3
Used version of vpype: commit id https://github.com/abey79/vpype/commit/0b0dc8dd7e32998dbef639f9db578c3bff02690b
@@ -42,20 +52,34 @@ vpype commands could be performed differently:
working line of code (example:) doc = vpype.read_multilayer_svg(self.options.input_file, quantization=0.1, crop=False, simplify=False, parallel=False)
Todo's
-- show_pen_up does not work. does not get written to svg file properly > https://github.com/abey79/vpype/issues/242
- https://github.com/abey79/vpype/issues/243
+- command chain is slow on Windows
+- add some debugging options to remove deprecation warnings
"""
class vpypetools (inkex.EffectExtension):
def __init__(self):
inkex.Effect.__init__(self)
+
+ # Line Sorting
+ self.arg_parser.add_argument("--linesort", default=False, type=inkex.Boolean)
self.arg_parser.add_argument("--linesort_no_flip", type=inkex.Boolean, default=False, help="Disable reversing stroke direction for optimization")
+
+ # Line Merging
+ self.arg_parser.add_argument("--linemerge", default=False, type=inkex.Boolean)
+ self.arg_parser.add_argument("--linemerge_tolerance", type=float, default=False, help="Maximum distance between two line endings that should be merged (default 0.5 mm)")
+ self.arg_parser.add_argument("--linemerge_no_flip", type=inkex.Boolean, default=False, help="Disable reversing stroke direction for merging")
+
+ # General Settings
+ self.arg_parser.add_argument("--flattenbezier", type=inkex.Boolean, default=False, help="Flatten bezier curves to polylines")
+ self.arg_parser.add_argument("--flatness", type=float, default=0.1, help="Minimum flatness = 0.1. The smaller the value the more fine segments you will get.")
self.arg_parser.add_argument("--apply_transformations", type=inkex.Boolean, default=False, help="Run 'Apply Transformations' extension before running vpype. Helps avoiding geometry shifting")
self.arg_parser.add_argument("--output_show", type=inkex.Boolean, default=False, help="This will open a new matplotlib window showing modified SVG data")
self.arg_parser.add_argument("--output_stats", type=inkex.Boolean, default=False, help="Show output statistics before/after conversion")
self.arg_parser.add_argument("--output_trajectories", type=inkex.Boolean, default=False, help="Add paths for the travel trajectories")
- self.arg_parser.add_argument("--keep_selection", type=inkex.Boolean, default=True, help="If false, selected paths will be removed")
+ self.arg_parser.add_argument("--keep_selection", type=inkex.Boolean, default=False, help="If false, selected paths will be removed")
+ self.arg_parser.add_argument("--strokes_to_paths", type=inkex.Boolean, default=True, help="Recommended option. Performs 'Path' > 'Stroke to Path' (CTRL + ALT + C) to convert vpype converted lines back to regular path objects")
def effect(self):
lc = vpype.LineCollection() # create a new array of LineStrings consisting of Points. We convert selected paths to polylines and grab their points
@@ -72,10 +96,25 @@ class vpypetools (inkex.EffectExtension):
# inkex.utils.debug(e)
inkex.utils.debug("Calling 'Apply Transformations' extension failed. Maybe the extension is not installed. You can download it from official InkScape Gallery. Skipping this step")
+ def flatten(node):
+ path = node.path.to_superpath()
+ bezier.cspsubdiv(path, self.options.flatness)
+ newpath = []
+ for subpath in path:
+ first = True
+ for csp in subpath:
+ cmd = 'L'
+ if first:
+ cmd = 'M'
+ first = False
+ newpath.append([cmd, [csp[1][0], csp[1][1]]])
+ node.path = newpath
def convertPath(node):
if node.tag == inkex.addNS('path','svg'):
nodesToConvert.append(node)
+ if self.options.flattenbezier is True:
+ flatten(node)
d = node.get('d')
p = CubicSuperPath(d)
points = []
@@ -108,7 +147,7 @@ class vpypetools (inkex.EffectExtension):
#l c.as_mls() #cast LineString array to MultiLineString
if len(lc) == 0:
- inkex.errormsg('Selection does not contain any paths. Try to cast your objects to paths using CTRL + SHIFT + C or strokes to paths using CTRL + ALT+ C')
+ inkex.errormsg('Selection appears to be empty or does not contain any valid svg:path nodes. Try to cast your objects to paths using CTRL + SHIFT + C or strokes to paths using CTRL + ALT+ C')
return
doc = vpype.Document() #create new vpype document
@@ -121,12 +160,25 @@ class vpypetools (inkex.EffectExtension):
traveling_length_before = doc.pen_up_length()
# build and execute the conversion command
- command = "linesort "
- if self.options.linesort_no_flip:
- command += " --no-flip"
- # inkex.utils.debug(command)
+ ##########################################
+
+ # Line Sort
+ if self.options.linesort is True:
+ command = "linesort "
+ if self.options.linesort_no_flip is True:
+ command += " --no-flip"
+
+ # Line Merging
+ if self.options.linemerge is True:
+ command = "linemerge --tolerance " + str(self.options.linemerge_tolerance)
+ if self.options.linemerge_no_flip is True:
+ command += " --no-flip"
+
+ #inkex.utils.debug(command)
doc = execute(command, doc)
+ ##########################################
+
# show the vpype document visually
# there are missing options to set pen_width and pen_opacity. This is anchored in "Engine" class
if self.options.output_show:
@@ -157,25 +209,39 @@ class vpypetools (inkex.EffectExtension):
output_fileIO.close()
# convert vpype polylines/lines/polygons to regular paths again. We need to use "--with-gui" to respond to "WARNING: ignoring verb FileSave - GUI required for this verb."
- cli_output = inkscape(output_file, "--with-gui", actions="EditSelectAllInAllLayers;EditUnlinkClone;ObjectToPath;FileSave;FileQuit")
-
- if len(cli_output) > 0:
- self.debug(_("Inkscape returned the following output when trying to run the vpype object to path back-conversion:"))
- self.debug(cli_output)
+ if self.options.strokes_to_paths is True:
+ cli_output = inkscape(output_file, "--with-gui", actions="EditSelectAllInAllLayers;EditUnlinkClone;ObjectToPath;FileSave;FileQuit")
+ if len(cli_output) > 0:
+ self.debug(_("Inkscape returned the following output when trying to run the vpype object to path back-conversion:"))
+ self.debug(cli_output)
# new parse the SVG file and insert it as new group into the current document tree
- # converted = etree.parse(output_file).getroot()
- converted = etree.parse(output_file).getroot().xpath("//svg:g[@inkscape:label='1']",namespaces=inkex.NSS) # the label id is the number of layer_id=None (will start with 1)
- newGroup = self.document.getroot().add(inkex.Group())
- newGroup.set('style', 'stroke:#000000;stroke-width:1px;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:none')
- for child in converted[0].getchildren():
- newGroup.append(child)
- newGroup.attrib['transform'] = 'translate(' + str(bbox.left) + ',' + str(bbox.top) + ')'
- newGroupId = self.svg.get_unique_id('vpypetools-')
- newGroup.set('id', newGroupId)
+ #vpype_svg = etree.parse(output_file).getroot().xpath("//svg:g", namespaces=inkex.NSS)
+
+ # the label id is the number of layer_id=None (will start with 1)
+ lines = etree.parse(output_file).getroot().xpath("//svg:g[@inkscape:label='1']",namespaces=inkex.NSS)
+ vpypeLinesGroup = self.document.getroot().add(inkex.Group())
+ vpypeLinesGroup.set('style', 'stroke:#000000;stroke-width:1px;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:none')
+ for item in lines:
+ for child in item.getchildren():
+ vpypeLinesGroup.append(child)
+ vpypeLinesGroup.attrib['transform'] = 'translate(' + str(bbox.left) + ',' + str(bbox.top) + ')'
+ vpypeLinesGroupId = self.svg.get_unique_id('vpypetools-lines-')
+ vpypeLinesGroup.set('id', vpypeLinesGroupId)
+
+ if self.options.output_trajectories is True:
+ trajectories = etree.parse(output_file).getroot().xpath("//svg:g[@id='pen_up_trajectories']",namespaces=inkex.NSS)
+ vpypeTrajectoriesGroup = self.document.getroot().add(inkex.Group())
+ vpypeTrajectoriesGroup.set('style', 'stroke:#0000ff;stroke-width:1px;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:none')
+ for item in trajectories:
+ for child in item.getchildren():
+ vpypeTrajectoriesGroup.append(child)
+ vpypeTrajectoriesGroup.attrib['transform'] = 'translate(' + str(bbox.left) + ',' + str(bbox.top) + ')'
+ vpypeTrajectoriesId = self.svg.get_unique_id('vpypetools-trajectories-')
+ vpypeTrajectoriesGroup.set('id', vpypeTrajectoriesId)
# inkex.utils.debug(self.svg.selection.first()) # get the first selected element. Chould be None
- self.svg.selection.set(newGroupId)
+ self.svg.selection.set(vpypeLinesGroupId)
#inkex.utils.debug(self.svg.selection.first()) # get the first selected element again to check if changing selection has worked
# we apply transformations also for new group to remove the "translate()" again
@@ -193,4 +259,4 @@ class vpypetools (inkex.EffectExtension):
node.getparent().remove(node)
if __name__ == '__main__':
- vpypetools().run()
+ vpypetools().run()
\ No newline at end of file
diff --git a/extensions/fablabchemnitz/vpypetools/vpypetools_linemerge.inx b/extensions/fablabchemnitz/vpypetools/vpypetools_linemerge.inx
new file mode 100644
index 00000000..1f710f18
--- /dev/null
+++ b/extensions/fablabchemnitz/vpypetools/vpypetools_linemerge.inx
@@ -0,0 +1,34 @@
+
+
+ Line Merging (vpype)
+ fablabchemnitz.de.vpype_linemerging
+
+
+ true
+ 0.5
+ false
+
+
+ true
+ 0.1
+ false
+ false
+ false
+ false
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/extensions/fablabchemnitz/vpype_tools/vpype_linesort.inx b/extensions/fablabchemnitz/vpypetools/vpypetools_linesort.inx
similarity index 69%
rename from extensions/fablabchemnitz/vpype_tools/vpype_linesort.inx
rename to extensions/fablabchemnitz/vpypetools/vpypetools_linesort.inx
index c3e6ee55..e67d4644 100644
--- a/extensions/fablabchemnitz/vpype_tools/vpype_linesort.inx
+++ b/extensions/fablabchemnitz/vpypetools/vpypetools_linesort.inx
@@ -4,14 +4,18 @@
fablabchemnitz.de.vpype_linesorting
+ true
false
+ true
+ 0.1
false
false
false
false
- false
+ false
+ true