added more efforts on vpype extensions

This commit is contained in:
leyghisbb 2021-04-02 23:07:28 +02:00
parent 16779bb3af
commit f504fbfa80
3 changed files with 132 additions and 28 deletions

View File

@ -1,17 +1,27 @@
#!/usr/bin/env python3 #!/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 import logging
for key in logging.Logger.manager.loggerDict: for key in logging.Logger.manager.loggerDict:
print(key) print(key)
logging.getLogger().setLevel(logging.CRITICAL) 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 sys
import os import os
from lxml import etree from lxml import etree
import inkex import inkex
from inkex import transforms from inkex import transforms, bezier
from inkex.paths import CubicSuperPath from inkex.paths import CubicSuperPath
from inkex.command import inkscape from inkex.command import inkscape
@ -26,8 +36,8 @@ from shapely.geometry import LineString, Point
Extension for InkScape 1.X Extension for InkScape 1.X
Author: Mario Voigt / FabLab Chemnitz Author: Mario Voigt / FabLab Chemnitz
Mail: mario.voigt@stadtfabrikanten.org Mail: mario.voigt@stadtfabrikanten.org
Date: 01.04.2021 Date: 02.04.2021
Last patch: 01.04.2021 Last patch: 02.04.2021
License: GNU GPL v3 License: GNU GPL v3
Used version of vpype: commit id https://github.com/abey79/vpype/commit/0b0dc8dd7e32998dbef639f9db578c3bff02690b 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) 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 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 - 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): class vpypetools (inkex.EffectExtension):
def __init__(self): def __init__(self):
inkex.Effect.__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") 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("--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_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_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("--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): 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 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(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") 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): def convertPath(node):
if node.tag == inkex.addNS('path','svg'): if node.tag == inkex.addNS('path','svg'):
nodesToConvert.append(node) nodesToConvert.append(node)
if self.options.flattenbezier is True:
flatten(node)
d = node.get('d') d = node.get('d')
p = CubicSuperPath(d) p = CubicSuperPath(d)
points = [] points = []
@ -108,7 +147,7 @@ class vpypetools (inkex.EffectExtension):
#l c.as_mls() #cast LineString array to MultiLineString #l c.as_mls() #cast LineString array to MultiLineString
if len(lc) == 0: 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 return
doc = vpype.Document() #create new vpype document doc = vpype.Document() #create new vpype document
@ -121,12 +160,25 @@ class vpypetools (inkex.EffectExtension):
traveling_length_before = doc.pen_up_length() traveling_length_before = doc.pen_up_length()
# build and execute the conversion command # build and execute the conversion command
command = "linesort " ##########################################
if self.options.linesort_no_flip:
command += " --no-flip" # Line Sort
# inkex.utils.debug(command) 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) doc = execute(command, doc)
##########################################
# show the vpype document visually # show the vpype document visually
# there are missing options to set pen_width and pen_opacity. This is anchored in "Engine" class # there are missing options to set pen_width and pen_opacity. This is anchored in "Engine" class
if self.options.output_show: if self.options.output_show:
@ -157,25 +209,39 @@ class vpypetools (inkex.EffectExtension):
output_fileIO.close() 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." # 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 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: 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(_("Inkscape returned the following output when trying to run the vpype object to path back-conversion:"))
self.debug(cli_output) self.debug(cli_output)
# new parse the SVG file and insert it as new group into the current document tree # new parse the SVG file and insert it as new group into the current document tree
# converted = etree.parse(output_file).getroot() #vpype_svg = etree.parse(output_file).getroot().xpath("//svg:g", namespaces=inkex.NSS)
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()) # the label id is the number of layer_id=None (will start with 1)
newGroup.set('style', 'stroke:#000000;stroke-width:1px;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:none') lines = etree.parse(output_file).getroot().xpath("//svg:g[@inkscape:label='1']",namespaces=inkex.NSS)
for child in converted[0].getchildren(): vpypeLinesGroup = self.document.getroot().add(inkex.Group())
newGroup.append(child) vpypeLinesGroup.set('style', 'stroke:#000000;stroke-width:1px;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:none')
newGroup.attrib['transform'] = 'translate(' + str(bbox.left) + ',' + str(bbox.top) + ')' for item in lines:
newGroupId = self.svg.get_unique_id('vpypetools-') for child in item.getchildren():
newGroup.set('id', newGroupId) 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 # 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 #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 # we apply transformations also for new group to remove the "translate()" again
@ -193,4 +259,4 @@ class vpypetools (inkex.EffectExtension):
node.getparent().remove(node) node.getparent().remove(node)
if __name__ == '__main__': if __name__ == '__main__':
vpypetools().run() vpypetools().run()

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Line Merging (vpype)</name>
<id>fablabchemnitz.de.vpype_linemerging</id>
<label appearance="header">Line Merging</label>
<label>Merge lines whose endings and starts overlap or are very close.</label>
<param name="linemerge" type="bool" gui-hidden="true">true</param>
<param name="linemerge_tolerance" type="float" min="0.0" gui-text="tolerance (mm)" gui-description="Maximum distance between two line endings that should be merged (default 0.5 mm)">0.5</param>
<param name="linemerge_no_flip" type="bool" gui-text="no flip" gui-description="Disable reversing stroke direction for merging">false</param>
<spacer/>
<label appearance="header">General Settings</label>
<param name="flattenbezier" type="bool" gui-text="Flatten bezier curves to polylines" gui-description="Converts bezier curves to polylines.">true</param>
<param name="flatness" type="float" min="0.001" precision="3" gui-text="flatness" gui-description="Minimum flatness = 0.001. The smaller the value the more fine segments you will get.">0.1</param>
<param name="apply_transformations" type="bool" gui-text="Use 'Apply Transformations' extension" gui-description="Run 'Apply Transformations' extension before running vpype. Helps avoiding geometry shifting">false</param>
<param name="output_show" type="bool" gui-text="Show debug output" gui-description="This will open a new matplotlib window showing modified SVG data">false</param>
<param name="output_stats" type="bool" gui-text="Show conversion statistics" gui-description="Show output statistics before/after conversion">false</param>
<param name="output_trajectories" type="bool" gui-text="Import travel trajectories" gui-description="Add paths for the travel trajectories">false</param>
<param name="keep_selection" type="bool" gui-text="Keep selected paths" gui-description="If false, selected paths (original objects) will be removed">false</param>
<param name="strokes_to_paths" type="bool" gui-text="Auto-convert low-level strokes to paths" gui-description="Recommended option. Performs 'Path' > 'Stroke to Path' (CTRL + ALT + C) to convert vpype converted lines back to regular path objects">true</param>
<label appearance="header">About</label>
<separator/>
<label appearance="url">https://fablabchemnitz.de</label>
<label>License: GNU GPL v3</label>
<effect needs-live-preview="true">
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Modify existing Path(s)"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">vpypetools.py</command>
</script>
</inkscape-extension>

View File

@ -4,14 +4,18 @@
<id>fablabchemnitz.de.vpype_linesorting</id> <id>fablabchemnitz.de.vpype_linesorting</id>
<label appearance="header">Line Sorting</label> <label appearance="header">Line Sorting</label>
<label>Sort lines to minimize travel distances.</label> <label>Sort lines to minimize travel distances.</label>
<param name="linesort" type="bool" gui-hidden="true">true</param>
<param name="linesort_no_flip" type="bool" gui-text="Disable flipping" gui-description="Disable reversing stroke direction for optimization '--no-flip'">false</param> <param name="linesort_no_flip" type="bool" gui-text="Disable flipping" gui-description="Disable reversing stroke direction for optimization '--no-flip'">false</param>
<spacer/> <spacer/>
<label appearance="header">General Settings</label> <label appearance="header">General Settings</label>
<param name="flattenbezier" type="bool" gui-text="Flatten bezier curves to polylines" gui-description="Converts bezier curves to polylines.">true</param>
<param name="flatness" type="float" min="0.001" precision="3" gui-text="flatness" gui-description="Minimum flatness = 0.001. The smaller the value the more fine segments you will get.">0.1</param>
<param name="apply_transformations" type="bool" gui-text="Use 'Apply Transformations' extension" gui-description="Run 'Apply Transformations' extension before running vpype. Helps avoiding geometry shifting">false</param> <param name="apply_transformations" type="bool" gui-text="Use 'Apply Transformations' extension" gui-description="Run 'Apply Transformations' extension before running vpype. Helps avoiding geometry shifting">false</param>
<param name="output_show" type="bool" gui-text="Show debug output" gui-description="This will open a new matplotlib window showing modified SVG data">false</param> <param name="output_show" type="bool" gui-text="Show debug output" gui-description="This will open a new matplotlib window showing modified SVG data">false</param>
<param name="output_stats" type="bool" gui-text="Show conversion statistics" gui-description="Show output statistics before/after conversion">false</param> <param name="output_stats" type="bool" gui-text="Show conversion statistics" gui-description="Show output statistics before/after conversion">false</param>
<param name="output_trajectories" type="bool" gui-text="Import travel trajectories" gui-description="Add paths for the travel trajectories">false</param> <param name="output_trajectories" type="bool" gui-text="Import travel trajectories" gui-description="Add paths for the travel trajectories">false</param>
<param name="keep_selection" type="bool" gui-text="Keep selected paths" gui-description="If false, selected paths will be removed">false</param> <param name="keep_selection" type="bool" gui-text="Keep selected paths" gui-description="If false, selected paths (original objects) will be removed">false</param>
<param name="strokes_to_paths" type="bool" gui-text="Auto-convert low-level strokes to paths" gui-description="Recommended option. Performs 'Path' > 'Stroke to Path' (CTRL + ALT + C) to convert vpype converted lines back to regular path objects">true</param>
<label appearance="header">About</label> <label appearance="header">About</label>
<separator/> <separator/>
<label appearance="url">https://fablabchemnitz.de</label> <label appearance="url">https://fablabchemnitz.de</label>