From 6f9ab6fb13ba4c1100c5ea6d00d7b90be3422f64 Mon Sep 17 00:00:00 2001 From: Mario Voigt Date: Tue, 6 Apr 2021 12:48:54 +0200 Subject: [PATCH] Several bugfixes in vpypetools --- .../fablabchemnitz/vpypetools/vpypetools.py | 76 ++++++++++--------- .../vpypetools/vpypetools_filter.inx | 14 ++-- .../vpypetools/vpypetools_freemode.inx | 41 ++++++++-- .../vpypetools/vpypetools_linemerge.inx | 14 ++-- .../vpypetools/vpypetools_linesimplify.inx | 14 ++-- .../vpypetools/vpypetools_linesort.inx | 14 ++-- .../vpypetools/vpypetools_multipass.inx | 14 ++-- .../vpypetools/vpypetools_occult.inx | 14 ++-- .../vpypetools/vpypetools_relooping.inx | 14 ++-- .../vpypetools/vpypetools_splitall.inx | 14 ++-- .../vpypetools/vpypetools_trim.inx | 14 ++-- 11 files changed, 157 insertions(+), 86 deletions(-) diff --git a/extensions/fablabchemnitz/vpypetools/vpypetools.py b/extensions/fablabchemnitz/vpypetools/vpypetools.py index e30a74a3..f2243952 100644 --- a/extensions/fablabchemnitz/vpypetools/vpypetools.py +++ b/extensions/fablabchemnitz/vpypetools/vpypetools.py @@ -22,7 +22,7 @@ logger = logging.getLogger() logger.setLevel(level=logging.WARNING) #after importing vpype we enabled logging again import warnings # we import this to suppress moderngl warnings from vpype_viewer - + from shapely.geometry import LineString, Point """ @@ -30,7 +30,7 @@ Extension for InkScape 1.X Author: Mario Voigt / FabLab Chemnitz Mail: mario.voigt@stadtfabrikanten.org Date: 02.04.2021 -Last patch: 04.04.2021 +Last patch: 06.04.2021 License: GNU GPL v3 This piece of spaghetti-code, called "vpypetools", is a wrapper to pass (pipe) line elements from InkScape selection (or complete canvas) to vpype. @@ -50,10 +50,7 @@ CLI / API docs: Todo's - allow to change pen width / opacity in vpype viewer: https://github.com/abey79/vpype/issues/243 -- command chain is really slow on Windows (takes ~5 times longer than Linux) -- allow to select other units than mm for tolerance, trimming, ... At the moment vpype uses millimeters regardless of the document units/display units in namedview -- handle styles of layers -- allow to select single layers instead of whole canvas (4th mode) +- command chain is really slow on Windows (takes ~5 times longer than Linux). Find ways to speed up """ class vpypetools (inkex.EffectExtension): @@ -122,7 +119,7 @@ class vpypetools (inkex.EffectExtension): # General Settings self.arg_parser.add_argument("--input_handling", default="paths", help="Input handling") 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("--flatness", type=float, default=0.1, help="Minimum flatness = 0.1. The smaller the value the more fine segments you will get (quantization).") 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") @@ -181,38 +178,45 @@ class vpypetools (inkex.EffectExtension): doc = None #create a vpype document - # if 'paths' we process paths only. Objects like rectangles or strokes like polygon have to be converted before accessing them - # if 'layers' we can process all elements. + ''' + if 'paths' we process paths only. Objects like rectangles or strokes like polygon have to be converted before accessing them + if 'layers' we can process all layers in the complete document + ''' if self.options.input_handling == "paths": # getting the bounding box of the current selection. We use to calculate the offset XY from top-left corner of the canvas. This helps us placing back the elements input_bbox = None + if self.options.apply_transformations is True and applyTransformAvailable is True: + ''' + we need to apply transfoms to the complete document even if there are only some single paths selected. + If we apply it to selected nodes only the parent groups still might contain transforms. + This messes with the coordinates and creates hardly controllable behaviour + ''' + applytransform.ApplyTransform().recursiveFuseTransform(self.document.getroot()) if len(self.svg.selected) == 0: - if self.options.apply_transformations is True and applyTransformAvailable is True: - applytransform.ApplyTransform().recursiveFuseTransform(self.document.getroot()) convertPath(self.document.getroot()) for element in nodesToWork: input_bbox += element.bounding_box() else: for id, item in self.svg.selected.items(): - if self.options.apply_transformations is True and applyTransformAvailable is True: - applytransform.ApplyTransform().recursiveFuseTransform(item) convertPath(item) - input_bbox = inkex.elements._selected.ElementList.bounding_box(self.svg.selected) # get BoundingBox for selection + #input_bbox = inkex.elements._selected.ElementList.bounding_box(self.svg.selected) # get BoundingBox for selection + input_bbox = self.svg.selection.bounding_box() # get BoundingBox for selection if len(lc) == 0: 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 # find the first object in selection which has a style attribute (skips groups and other things which have no style) firstElementStyle = None for node in nodesToWork: - if node.get('style') != None: + if node.attrib.has_key('style'): firstElementStyle = node.get('style') doc = vpype.Document(page_size=(input_bbox.width + input_bbox.left, input_bbox.height + input_bbox.top)) #create new vpype document doc.add(lc, layer_id=None) # we add the lineCollection (converted selection) to the vpype document elif self.options.input_handling == "layers": - doc = vpype.read_multilayer_svg(self.options.input_file, quantization = 0.1, crop = False, simplify = False, parallel = False, \ - default_width = self.document.getroot().get('width'), default_height = self.document.getroot().get('height')) - for node in self.document.getroot().getchildren(): + doc = vpype.read_multilayer_svg(self.options.input_file, quantization = self.options.flatness, crop = False, simplify = False, parallel = False, \ + default_width = self.document.getroot().get('width'), default_height = self.document.getroot().get('height')) + + for node in self.document.getroot().xpath("//svg:g", namespaces=inkex.NSS): #all groups/layers nodesToWork.append(node) tooling_length_before = doc.length() @@ -294,8 +298,7 @@ class vpypetools (inkex.EffectExtension): self.options.freemode_cmd3_enabled is False and \ self.options.freemode_cmd4_enabled is False and \ self.options.freemode_cmd5_enabled is False: - inkex.utils.debug("Please enabled at least one set of commands") - return + inkex.utils.debug("Warning: empty vpype pipeline. With this you are just getting read-write layerset/lineset.") else: if self.options.freemode_show_cmd is True: inkex.utils.debug("Your command pipe will be the following:") @@ -338,6 +341,7 @@ class vpypetools (inkex.EffectExtension): # vpype_viewer.show(doc, view_mode=ViewMode.PREVIEW, show_pen_up=self.options.output_trajectories, show_points=False, pen_width=0.1, pen_opacity=1.0, argv=None) vpype_viewer.show(doc, view_mode=ViewMode.PREVIEW, show_pen_up=self.options.output_trajectories, show_points=False, argv=None) # https://vpype.readthedocs.io/en/stable/api/vpype_viewer.ViewMode.html warnings.filterwarnings("default") # reset warning filter + exit(0) #we leave the code loop because we only want to preview. We don't want to import the geometry # save the vpype document to new svg file and close it afterwards output_file = self.options.input_file + ".vpype.svg" @@ -345,8 +349,6 @@ class vpypetools (inkex.EffectExtension): # vpype.write_svg(output_fileIO, doc, page_size=None, center=False, source_string='', layer_label_format='%d', show_pen_up=self.options.output_trajectories, color_mode='layer', no_basic_shapes = True) vpype.write_svg(output_fileIO, doc, page_size=None, center=False, source_string='', layer_label_format='%d', show_pen_up=self.options.output_trajectories, color_mode='layer') #vpype.write_svg(output_fileIO, doc, page_size=(self.svg.unittouu(self.document.getroot().get('width')), self.svg.unittouu(self.document.getroot().get('height'))), center=False, source_string='', layer_label_format='%d', show_pen_up=self.options.output_trajectories, color_mode='layer') - - 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." @@ -356,9 +358,7 @@ class vpypetools (inkex.EffectExtension): 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 - # the label id is the number of layer_id=None (will start with 1) - #import_doc = etree.parse(output_file) + # parse the SVG file try: stream = open(output_file, 'r') except FileNotFoundError as e: @@ -373,30 +373,38 @@ class vpypetools (inkex.EffectExtension): if self.options.output_trajectories is True: if len(trajectoriesLayer) > 0: trajectoriesLayer[0].set('style', 'stroke:#0000ff;stroke-width:'+ str(self.options.trajectories_stroke_width) + 'px;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:none') + trajectoriesLayer[0].attrib.pop('stroke') # remove unneccesary stroke attribute + trajectoriesLayer[0].attrib.pop('fill') # remove unneccesary fill attribute else: if len(trajectoriesLayer) > 0: trajectoriesLayer[0].getparent().remove(trajectoriesLayer[0]) - # lineLayers = import_doc.getroot().xpath("//svg:g[@inkscape:label='1']", namespaces=inkex.NSS) lineLayers = import_doc.getroot().xpath("//svg:g[not(@id='pen_up_trajectories')]", namespaces=inkex.NSS) #all layer except the pen_up trajectories layer - if self.options.input_handling == "paths" and self.options.use_style_of_first_element is True and firstElementStyle is not None: + if self.options.use_style_of_first_element is True and self.options.input_handling == "paths" and firstElementStyle is not None: for lineLayer in lineLayers: lineLayer.set('style', firstElementStyle) + lineLayer.attrib.pop('stroke') # remove unneccesary stroke attribute + lineLayer.attrib.pop('fill') # remove unneccesary fill attribute + else: - for lineLayer in lineLayers: - lineLayer.set('style', 'stroke:#000000;stroke-width:'+ str(self.options.lines_stroke_width) + 'px;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:none') - + for lineLayer in lineLayers: + if lineLayer.attrib.has_key('stroke'): + color = lineLayer.get('stroke') + lineLayer.set('style', 'stroke:' + color + ';stroke-width:'+ str(self.options.lines_stroke_width) + 'px;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:none') + lineLayer.attrib.pop('stroke') # remove unneccesary stroke attribute + lineLayer.attrib.pop('fill') # remove unneccesary fill attribute + import_viewBox = import_doc.getroot().get('viewBox').split(" ") self_viewBox = self.document.getroot().get('viewBox').split(" ") scaleX = self.svg.unittouu(self_viewBox[2]) / self.svg.unittouu(import_viewBox[2]) scaleY = self.svg.unittouu(self_viewBox[3]) / self.svg.unittouu(import_viewBox[3]) - # for element in import_doc.getroot().iter("*"): # through all for element in import_doc.getroot().iter("{http://www.w3.org/2000/svg}g"): - element.set('transform', 'scale(' + str(scaleX) + ',' + str(scaleY) + ')') #imported groups need to be transformed. Or they have wrong size. Reason: different viewBox sizes/units in namedview definitions self.document.getroot().append(element) - if self.options.apply_transformations is True and applyTransformAvailable is True: #we apply the transforms directly after adding them - applytransform.ApplyTransform().recursiveFuseTransform(element) + if self.options.input_handling == "layers": + element.set('transform', 'scale(' + str(scaleX) + ',' + str(scaleY) + ')') #imported groups need to be transformed. Or they have wrong size. Reason: different viewBox sizes/units in namedview definitions + if self.options.apply_transformations is True and applyTransformAvailable is True: #we apply the transforms directly after adding them + applytransform.ApplyTransform().recursiveFuseTransform(element) # Delete the temporary file again because we do not need it anymore if os.path.exists(output_file): diff --git a/extensions/fablabchemnitz/vpypetools/vpypetools_filter.inx b/extensions/fablabchemnitz/vpypetools/vpypetools_filter.inx index 281fcf3a..eed0f65b 100644 --- a/extensions/fablabchemnitz/vpypetools/vpypetools_filter.inx +++ b/extensions/fablabchemnitz/vpypetools/vpypetools_filter.inx @@ -14,17 +14,21 @@ 0.000 - true + + + + + true 0.100 false - false + false false false false true - true - 1.000 - 1.000 + true + 1.000 + 1.000 diff --git a/extensions/fablabchemnitz/vpypetools/vpypetools_freemode.inx b/extensions/fablabchemnitz/vpypetools/vpypetools_freemode.inx index 8ab86698..6a4dc6ae 100644 --- a/extensions/fablabchemnitz/vpypetools/vpypetools_freemode.inx +++ b/extensions/fablabchemnitz/vpypetools/vpypetools_freemode.inx @@ -20,23 +20,50 @@ false - - - + + + - true + true 0.100 false - false + false false false false true - true + true 1.000 - 1.000 + 1.000 + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/fablabchemnitz/vpypetools/vpypetools_linemerge.inx b/extensions/fablabchemnitz/vpypetools/vpypetools_linemerge.inx index f99b77c9..a6500681 100644 --- a/extensions/fablabchemnitz/vpypetools/vpypetools_linemerge.inx +++ b/extensions/fablabchemnitz/vpypetools/vpypetools_linemerge.inx @@ -9,17 +9,21 @@ false - true + + + + + true 0.100 false - false + false false false false true - true - 1.000 - 1.000 + true + 1.000 + 1.000 diff --git a/extensions/fablabchemnitz/vpypetools/vpypetools_linesimplify.inx b/extensions/fablabchemnitz/vpypetools/vpypetools_linesimplify.inx index 4c4bd915..1707890d 100644 --- a/extensions/fablabchemnitz/vpypetools/vpypetools_linesimplify.inx +++ b/extensions/fablabchemnitz/vpypetools/vpypetools_linesimplify.inx @@ -8,17 +8,21 @@ 0.050 - true + + + + + true 0.100 false - false + false false false false true - true - 1.000 - 1.000 + true + 1.000 + 1.000 diff --git a/extensions/fablabchemnitz/vpypetools/vpypetools_linesort.inx b/extensions/fablabchemnitz/vpypetools/vpypetools_linesort.inx index 83faa5de..dab44cc7 100644 --- a/extensions/fablabchemnitz/vpypetools/vpypetools_linesort.inx +++ b/extensions/fablabchemnitz/vpypetools/vpypetools_linesort.inx @@ -8,17 +8,21 @@ false - true + + + + + true 0.100 false - false + false false false false true - true - 1.000 - 1.000 + true + 1.000 + 1.000 diff --git a/extensions/fablabchemnitz/vpypetools/vpypetools_multipass.inx b/extensions/fablabchemnitz/vpypetools/vpypetools_multipass.inx index b8756d10..3288695c 100644 --- a/extensions/fablabchemnitz/vpypetools/vpypetools_multipass.inx +++ b/extensions/fablabchemnitz/vpypetools/vpypetools_multipass.inx @@ -8,17 +8,21 @@ 2 - true + + + + + true 0.100 false - false + false false false false true - true - 1.000 - 1.000 + true + 1.000 + 1.000 diff --git a/extensions/fablabchemnitz/vpypetools/vpypetools_occult.inx b/extensions/fablabchemnitz/vpypetools/vpypetools_occult.inx index 1edf2cc5..ce39406c 100644 --- a/extensions/fablabchemnitz/vpypetools/vpypetools_occult.inx +++ b/extensions/fablabchemnitz/vpypetools/vpypetools_occult.inx @@ -8,17 +8,21 @@ 0.01 - true + + + + + true 0.100 false - false + false false false false true - true - 1.000 - 1.000 + true + 1.000 + 1.000 diff --git a/extensions/fablabchemnitz/vpypetools/vpypetools_relooping.inx b/extensions/fablabchemnitz/vpypetools/vpypetools_relooping.inx index 4362c923..741603fd 100644 --- a/extensions/fablabchemnitz/vpypetools/vpypetools_relooping.inx +++ b/extensions/fablabchemnitz/vpypetools/vpypetools_relooping.inx @@ -8,17 +8,21 @@ 0.500 - true + + + + + true 0.100 false - false + false false false false true - true - 1.000 - 1.000 + true + 1.000 + 1.000 diff --git a/extensions/fablabchemnitz/vpypetools/vpypetools_splitall.inx b/extensions/fablabchemnitz/vpypetools/vpypetools_splitall.inx index 562f6cfe..119c6416 100644 --- a/extensions/fablabchemnitz/vpypetools/vpypetools_splitall.inx +++ b/extensions/fablabchemnitz/vpypetools/vpypetools_splitall.inx @@ -7,17 +7,21 @@ true - true + + + + + true 0.100 false - false + false false false false true - true - 1.000 - 1.000 + true + 1.000 + 1.000 diff --git a/extensions/fablabchemnitz/vpypetools/vpypetools_trim.inx b/extensions/fablabchemnitz/vpypetools/vpypetools_trim.inx index 94a09b8d..998aaff1 100644 --- a/extensions/fablabchemnitz/vpypetools/vpypetools_trim.inx +++ b/extensions/fablabchemnitz/vpypetools/vpypetools_trim.inx @@ -9,17 +9,21 @@ 0.000 - true + + + + + true 0.100 false - false + false false false false true - true - 1.000 - 1.000 + true + 1.000 + 1.000