This commit is contained in:
Mario Voigt 2024-01-18 11:36:46 +01:00
parent 371d5f936d
commit 68e1dd9ac4
2 changed files with 234 additions and 215 deletions

View File

@ -12,6 +12,7 @@
</param> </param>
<param name="snap_ends" type="bool" gui-text="Snap connecting ends together" gui-description="This will deduplicate (merge) two nodes to one node">false</param> <param name="snap_ends" type="bool" gui-text="Snap connecting ends together" gui-description="This will deduplicate (merge) two nodes to one node">false</param>
<param name="close_loops" type="bool" gui-text="Close loops (start/end of the same path)">true</param> <param name="close_loops" type="bool" gui-text="Close loops (start/end of the same path)">true</param>
<param name="limit" type="int" min="0" max="99999" gui-text="Maximum items to process" gui-description="The more items at once are selected, the slower the process gets. Repeating in smaller steps is better. Set 0 for umlimited selection, else the selection gets cut off.">2000</param>
<param name="debug" type="bool" gui-text="Debug output">false</param> <param name="debug" type="bool" gui-text="Debug output">false</param>
<!-- Keep in sync with chain_paths.py line 19 __version__ = ... --> <!-- Keep in sync with chain_paths.py line 19 __version__ = ... -->
<label appearance="url">https://github.com/fablabnbg/inkscape-chain-paths</label> <label appearance="url">https://github.com/fablabnbg/inkscape-chain-paths</label>

View File

@ -53,6 +53,7 @@ class ChainPaths(inkex.EffectExtension):
self.arg_parser.add_argument('-V', '--version', type=inkex.Boolean, default=False, help = 'Just print version number ("' + __version__ + '") and exit.') self.arg_parser.add_argument('-V', '--version', type=inkex.Boolean, default=False, help = 'Just print version number ("' + __version__ + '") and exit.')
self.arg_parser.add_argument('-s', '--snap_ends', type=inkex.Boolean, default=True, help='snap end-points together when connecting') self.arg_parser.add_argument('-s', '--snap_ends', type=inkex.Boolean, default=True, help='snap end-points together when connecting')
self.arg_parser.add_argument('-c', '--close_loops', type=inkex.Boolean, default=True, help='close loops (start/end of the same path)') self.arg_parser.add_argument('-c', '--close_loops', type=inkex.Boolean, default=True, help='close loops (start/end of the same path)')
self.arg_parser.add_argument('-l', '--limit', type=int, default=2000, help='Maximum items to process')
self.arg_parser.add_argument('-u', '--units', default="mm", help="measurement unit for epsilon") self.arg_parser.add_argument('-u', '--units', default="mm", help="measurement unit for epsilon")
self.arg_parser.add_argument('-e', '--chain_epsilon', type=float, default=0.01, help="Max. distance to connect [mm]") self.arg_parser.add_argument('-e', '--chain_epsilon', type=float, default=0.01, help="Max. distance to connect [mm]")
self.arg_parser.add_argument('-d', '--debug', type=inkex.Boolean, default=False, help='Debug') self.arg_parser.add_argument('-d', '--debug', type=inkex.Boolean, default=False, help='Debug')
@ -84,7 +85,7 @@ class ChainPaths(inkex.EffectExtension):
if not id in self.segments_done: if not id in self.segments_done:
self.segments_done[id] = {} self.segments_done[id] = {}
self.segments_done[id][n] = True self.segments_done[id][n] = True
if so.debug: inkex.utils.debug("done {} {} {}".format(id), n, msg) if so.debug: inkex.utils.debug("done {} {} {}".format(id, n, msg))
def is_segment_done(self, id, n): def is_segment_done(self, id, n):
if not id in self.segments_done: if not id in self.segments_done:
@ -139,12 +140,20 @@ class ChainPaths(inkex.EffectExtension):
if self.chain_epsilon < 0.001: self.chain_epsilon = 0.001 # keep a minimum. if self.chain_epsilon < 0.001: self.chain_epsilon = 0.001 # keep a minimum.
self.eps_sq = self.chain_epsilon * self.unit_factor * self.chain_epsilon * self.unit_factor self.eps_sq = self.chain_epsilon * self.unit_factor * self.chain_epsilon * self.unit_factor
if not len(self.svg.selected.items()): selected = self.svg.selected.items()
itemsCount = len(selected)
if not itemsCount:
inkex.errormsg("Please select one or more objects.") inkex.errormsg("Please select one or more objects.")
return return
#selected = dict(reversed(list(selected))) #reverse
if so.limit > 0 and itemsCount > so.limit:
inkex.utils.debug("Maximum items to process is set to {}. You selected {} items. We continue with processing until limit is reached.".format(so.limit, itemsCount))
segments = [] segments = []
for id, node in self.svg.selected.items(): workedon = 0
for id, node in selected:
if node.tag != inkex.addNS('path', 'svg'): if node.tag != inkex.addNS('path', 'svg'):
inkex.errormsg("Object id {} is not a path. Try\n - Path->Object to Path\n - Object->Ungroup".format(node.get('id'))) inkex.errormsg("Object id {} is not a path. Try\n - Path->Object to Path\n - Object->Ungroup".format(node.get('id')))
return return
@ -171,6 +180,10 @@ class ChainPaths(inkex.EffectExtension):
segments.append({'id': id, 'n': sub_idx, 'end1': end1, 'end2':end2, 'seg': sub}) segments.append({'id': id, 'n': sub_idx, 'end1': end1, 'end2':end2, 'seg': sub})
if node.get(inkex.addNS('type', 'sodipodi')): if node.get(inkex.addNS('type', 'sodipodi')):
del node.attrib[inkex.addNS('type', 'sodipodi')] del node.attrib[inkex.addNS('type', 'sodipodi')]
workedon += 1
if workedon >= so.limit and so.limit > 0:
break
if so.debug: inkex.utils.debug("-------- seen: ") if so.debug: inkex.utils.debug("-------- seen: ")
for s in segments: for s in segments:
if so.debug: inkex.utils.debug("{}, {}, {}, {}".format(s['id'], s['n'], s['end1'], s['end2'])) if so.debug: inkex.utils.debug("{}, {}, {}, {}".format(s['id'], s['n'], s['end1'], s['end2']))
@ -178,7 +191,9 @@ class ChainPaths(inkex.EffectExtension):
# chain the segments # chain the segments
obsoleted = 0 obsoleted = 0
remaining = 0 remaining = 0
for id, node in self.svg.selected.items():
workedon = 0
for id, node in selected:
path_d = CubicSuperPath(Path(node.get('d'))) path_d = CubicSuperPath(Path(node.get('d')))
# ATTENTION: for parsePath() it is the same, if first and last point coincide, or if the path is really closed. # ATTENTION: for parsePath() it is the same, if first and last point coincide, or if the path is really closed.
path_closed = True if re.search(r'z\s*$', node.get('d')) else False path_closed = True if re.search(r'z\s*$', node.get('d')) else False
@ -272,6 +287,9 @@ class ChainPaths(inkex.EffectExtension):
if path_closed: p_fmt += " z" if path_closed: p_fmt += " z"
if so.debug: inkex.utils.debug("new path: {}".format(p_fmt)) if so.debug: inkex.utils.debug("new path: {}".format(p_fmt))
node.set('d', p_fmt) node.set('d', p_fmt)
workedon += 1
if workedon >= so.limit and so.limit > 0:
break
# statistics: # statistics:
if so.debug: inkex.utils.debug("Path nodes obsoleted: {}\nPath nodes remaining: {}".format(obsoleted, remaining)) if so.debug: inkex.utils.debug("Path nodes obsoleted: {}\nPath nodes remaining: {}".format(obsoleted, remaining))