fixes in unwind paths; added mpdify path start extension
This commit is contained in:
parent
8c1ff97e8d
commit
11d09b7f92
@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||||
|
<name>Modify Path Start Node</name>
|
||||||
|
<id>fablabchemnitz.de.modify_path_start</id>
|
||||||
|
<param name="tab" type="notebook">
|
||||||
|
<page name="tab_settings" gui-text="Modify Path Start Node">
|
||||||
|
<param name="closed_only" type="bool" gui-text="Handle closed paths only" gui-description="If disabled we also apply on open (sub)paths. Warning: This REMOVES segments!">true</param>
|
||||||
|
<param name="movenode" type="int" min="-99999" max="99999" gui-text="Move starting node n nodes further">0</param>
|
||||||
|
<param name="visualize_result" type="bool" gui-text="Visualize first two nodes" gui-description="If enabled first two nodes get a number and a dot">false</param>
|
||||||
|
<param name="fontsize" type="string" gui-text="Font size:">10px</param>
|
||||||
|
<param name="dotsize" type="string" gui-text="Dot size:">10px</param>
|
||||||
|
<param name="debug" type="bool" gui-text="Debug Output">false</param>
|
||||||
|
<label appearance="header">Notes</label>
|
||||||
|
<label>Use "Path > Reverse" to change path direction</label>
|
||||||
|
<label>Use extension "Chain Paths" to make closed paths out of segments.</label>
|
||||||
|
</page>
|
||||||
|
<page name="tab_about" gui-text="About">
|
||||||
|
<label appearance="header">Modify Path Start Node</label>
|
||||||
|
<label>Extension to change starting node of a path and visualize it by dots and numbers</label>
|
||||||
|
<label>2021 / written by Mario Voigt (Stadtfabrikanten e.V. / FabLab Chemnitz)</label>
|
||||||
|
<spacer/>
|
||||||
|
<label appearance="header">Online Documentation</label>
|
||||||
|
<label appearance="url">https://y.stadtfabrikanten.org/modifypathstartnode</label>
|
||||||
|
<spacer/>
|
||||||
|
<label appearance="header">Contributing</label>
|
||||||
|
<label appearance="url">https://gitea.fablabchemnitz.de/MarioVoigt/mightyscape-1.X</label>
|
||||||
|
<label appearance="url">mailto:mario.voigt@stadtfabrikanten.org</label>
|
||||||
|
<spacer/>
|
||||||
|
<label appearance="header">MightyScape Extension Collection</label>
|
||||||
|
<label>This piece of software is part of the MightyScape for Inkscape Extension Collection and is licensed under GNU GPL v3</label>
|
||||||
|
<label appearance="url">https://y.stadtfabrikanten.org/mightyscape-overview</label>
|
||||||
|
</page>
|
||||||
|
<page name="tab_donate" gui-text="Donate">
|
||||||
|
<label appearance="header">Coffee + Pizza</label>
|
||||||
|
<label>We are the Stadtfabrikanten, running the FabLab Chemnitz since 2016. A FabLab is an open workshop that gives people access to machines and digital tools like 3D printers, laser cutters and CNC milling machines.</label>
|
||||||
|
<spacer/>
|
||||||
|
<label>You like our work and want to support us? You can donate to our non-profit organization by different ways:</label>
|
||||||
|
<label appearance="url">https://y.stadtfabrikanten.org/donate</label>
|
||||||
|
<spacer/>
|
||||||
|
<label>Thanks for using our extension and helping us!</label>
|
||||||
|
<image>../000_about_fablabchemnitz.svg</image>
|
||||||
|
</page>
|
||||||
|
</param>
|
||||||
|
<effect>
|
||||||
|
<object-type>path</object-type>
|
||||||
|
<effects-menu>
|
||||||
|
<submenu name="FabLab Chemnitz">
|
||||||
|
<submenu name="Modify existing Path(s)"/>
|
||||||
|
</submenu>
|
||||||
|
</effects-menu>
|
||||||
|
</effect>
|
||||||
|
<script>
|
||||||
|
<command location="inx" interpreter="python">modify_path_start.py</command>
|
||||||
|
</script>
|
||||||
|
</inkscape-extension>
|
177
extensions/fablabchemnitz/modify_path_start/modify_path_start.py
Normal file
177
extensions/fablabchemnitz/modify_path_start/modify_path_start.py
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
Author: Mario Voigt / FabLab Chemnitz
|
||||||
|
Mail: mario.voigt@stadtfabrikanten.org
|
||||||
|
Date: 19.05.2021
|
||||||
|
Last patch: 19.05.2021
|
||||||
|
License: GNU GPL v3
|
||||||
|
|
||||||
|
ToDo:
|
||||||
|
add group id like path id
|
||||||
|
link for usage: plotting/laser cutting, path unwinding plugin > startpunkt für anwicklung
|
||||||
|
'''
|
||||||
|
|
||||||
|
import copy
|
||||||
|
import inkex
|
||||||
|
from inkex import Circle, TextElement, Path, PathElement, CubicSuperPath
|
||||||
|
|
||||||
|
class ModifyStartDirection(inkex.EffectExtension):
|
||||||
|
|
||||||
|
def modify(self, element):
|
||||||
|
raw = element.path.to_arrays()
|
||||||
|
subpaths, prev = [], 0
|
||||||
|
for i in range(len(raw)): # Breaks compound paths into simple paths
|
||||||
|
if raw[i][0] == 'M' and i != 0:
|
||||||
|
subpaths.append(raw[prev:i])
|
||||||
|
prev = i
|
||||||
|
subpaths.append(raw[prev:])
|
||||||
|
if self.options.debug is True:
|
||||||
|
self.msg("Element {} has {} subpath(s)".format(element.get('id'), len(subpaths)))
|
||||||
|
|
||||||
|
subpathNr = 0
|
||||||
|
for path in subpaths:
|
||||||
|
subpathNr += 1
|
||||||
|
newSubpaths = subpaths #we will overwrite them later
|
||||||
|
|
||||||
|
pathIsClosed = False
|
||||||
|
if path[-1][0] == 'Z' or \
|
||||||
|
(path[-1][0] == 'L' and path[0][1] == path[-1][1]) or \
|
||||||
|
(path[-1][0] == 'C' and path[0][1] == [path[-1][1][-2], path[-1][1][-1]]) \
|
||||||
|
: #if first is last point the path is also closed. The "Z" command is not required
|
||||||
|
pathIsClosed = True
|
||||||
|
|
||||||
|
if self.options.debug is True:
|
||||||
|
self.msg("pathIsClosed = " + str(pathIsClosed))
|
||||||
|
|
||||||
|
if self.options.closed_only is True and pathIsClosed is False:
|
||||||
|
self.msg("Path {}/subpath {} is not closed!".format(element.get('id'), subpathNr))
|
||||||
|
continue #skip this open path
|
||||||
|
|
||||||
|
if path[-1][0] == 'Z': #replace Z with another L command (which moves to the coordinates of the first M command in path) to have better overview
|
||||||
|
path[-1][0] = 'L'
|
||||||
|
path[-1][1] = path[0][1]
|
||||||
|
|
||||||
|
#adjust if entered move number is higher than actual node count. We handle as infinite looping
|
||||||
|
moves = self.options.movenode % len(path)
|
||||||
|
if pathIsClosed is True: #if closed start and end collapse and "duplicate"
|
||||||
|
moves = self.options.movenode % (len(path) - 1)
|
||||||
|
|
||||||
|
if self.options.debug is True:
|
||||||
|
self.msg("root path:")
|
||||||
|
self.msg(path)
|
||||||
|
self.msg("-"*25)
|
||||||
|
|
||||||
|
if self.options.debug is True:
|
||||||
|
self.msg("moves = " + str(moves))
|
||||||
|
|
||||||
|
for i in range(moves):
|
||||||
|
if len(path) > 2: #the path needs at least more than two nodes
|
||||||
|
#we move the first node to the end of the list
|
||||||
|
move = path[0]
|
||||||
|
del path[0]
|
||||||
|
path.append(move)
|
||||||
|
|
||||||
|
if self.options.debug is True:
|
||||||
|
self.msg("moved path:")
|
||||||
|
self.msg(path)
|
||||||
|
self.msg("-"*25)
|
||||||
|
|
||||||
|
oldseg = copy.deepcopy(path[0]) #if we assign like "oldseg = path[0]", it will get overwritten. So we need copy
|
||||||
|
|
||||||
|
#Now we messed the integrity of the path. It does not begin with 'M' now. But we need an 'M'.
|
||||||
|
#It now either starts with L or C. H, V, Z cannot occure here.
|
||||||
|
if path[0][0] == 'C': #and path[-1][0] == 'M':
|
||||||
|
#self.msg("C to M")
|
||||||
|
path[0][1] = [path[0][1][-2], path[0][1][-1]]
|
||||||
|
elif path[0][0] == 'L': #and path[-1][0] == 'M':
|
||||||
|
#self.msg("L to M")
|
||||||
|
path[0][1] = [path[0][1][0], path[0][1][1]]
|
||||||
|
#else:
|
||||||
|
# self.msg("no idea")
|
||||||
|
path[0][0] = 'M' #we really need M. Does not matter if 'L' or 'C'.
|
||||||
|
|
||||||
|
if pathIsClosed is True:
|
||||||
|
if path[-1][0] == 'M' and len(oldseg[1]) == 2: #data of an 'L' command
|
||||||
|
path[-1][0] = 'L'
|
||||||
|
path[-1][1] = path[0][1]
|
||||||
|
elif path[-1][0] == 'M' and len(oldseg[1]) > 2: #data of an 'C' command
|
||||||
|
path[-1][0] = 'C'
|
||||||
|
path[-1][1] = oldseg[1]
|
||||||
|
else:
|
||||||
|
if path[-1][0] == 'M': #if open path we just drop the dangling 'M' command completely
|
||||||
|
del path[-1]
|
||||||
|
|
||||||
|
if self.options.debug is True:
|
||||||
|
self.msg("final path:")
|
||||||
|
self.msg(path)
|
||||||
|
self.msg("-"*25)
|
||||||
|
|
||||||
|
newSubpaths[subpathNr - 1] = path
|
||||||
|
#else:
|
||||||
|
# inkex.utils.debug("More moves entered than possible to apply")
|
||||||
|
|
||||||
|
composedPath = inkex.Path()
|
||||||
|
for newSubpath in newSubpaths:
|
||||||
|
composedPath.extend(newSubpath)
|
||||||
|
|
||||||
|
if self.options.debug is True:
|
||||||
|
self.msg("Composed path = " + str(composedPath))
|
||||||
|
|
||||||
|
element.path = composedPath
|
||||||
|
|
||||||
|
def visualizeFirstTwo(self, element):
|
||||||
|
"""Add a dot label for this path element"""
|
||||||
|
group = element.getparent().add(inkex.Group())
|
||||||
|
dot_group = group.add(inkex.Group(id="dot-group-" + element.get('id')))
|
||||||
|
num_group = group.add(inkex.Group(id="num-group-" + element.get('id')))
|
||||||
|
group.transform = element.transform
|
||||||
|
radius = self.svg.unittouu(self.options.dotsize) / 2
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
for step, (x, y) in enumerate(element.path.end_points):
|
||||||
|
count += 1
|
||||||
|
circle = Circle(cx=str(x), cy=str(y), r=str(radius))
|
||||||
|
circle.style = inkex.Style({'stroke': 'none', 'fill': '#000'})
|
||||||
|
|
||||||
|
text = TextElement(x=str(x + radius), y=str(y - radius))
|
||||||
|
text.text = str(count) #we start with #1
|
||||||
|
text.style = inkex.Style({'font-size': self.svg.unittouu(self.options.fontsize), 'fill-opacity': '1.0', 'stroke': 'none',
|
||||||
|
'font-weight': 'normal', 'font-style': 'normal', 'fill': '#999'})
|
||||||
|
|
||||||
|
dot_group.append(circle)
|
||||||
|
num_group.append(text)
|
||||||
|
|
||||||
|
if count > 1: #we only display first two points to see the position of the first node and the path direction
|
||||||
|
break
|
||||||
|
|
||||||
|
def add_arguments(self, pars):
|
||||||
|
pars.add_argument("--tab")
|
||||||
|
pars.add_argument("--closed_only", type=inkex.Boolean, default=False, help="If disabled we also apply on open (sub)path. Warning: This REMOVES segments!")
|
||||||
|
pars.add_argument("--movenode", type=int, default=0, help="Move starting node n nodes further")
|
||||||
|
pars.add_argument('--visualize_result', type=inkex.Boolean, default=False, help="If enabled each node gets a number and a dot")
|
||||||
|
pars.add_argument("--dotsize", default="10px", help="Size of the dots on the path nodes")
|
||||||
|
pars.add_argument("--fontsize", default="20px", help="Size of node labels")
|
||||||
|
pars.add_argument("--debug", type=inkex.Boolean, default=False, help="Debug Output")
|
||||||
|
|
||||||
|
def effect(self):
|
||||||
|
if len(self.svg.selected) > 0:
|
||||||
|
elements = self.svg.selection.filter(PathElement).values()
|
||||||
|
if len(elements) > 0:
|
||||||
|
for element in elements:
|
||||||
|
#move starting element / change direction
|
||||||
|
self.modify(element)
|
||||||
|
|
||||||
|
#finally apply dots to visualize the result
|
||||||
|
if self.options.visualize_result is True:
|
||||||
|
self.visualizeFirstTwo(element)
|
||||||
|
else:
|
||||||
|
inkex.errormsg('Selection seems not to contain path elements. Maybe you have selected a group instead?')
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
inkex.errormsg('Please select some objects first.')
|
||||||
|
return
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
ModifyStartDirection().run()
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
#
|
#
|
||||||
# Copyright (C) 2005 Aaron Spike, aaron@ekips.org
|
# Copyright (C) 2005 Aaron Spike, aaron@ekips.org
|
||||||
# Modified by Ellen Wasbo, ellen@wasbo.net 2021 - number subpaths and mark start/end node with green/red dot
|
# Modified by Ellen Wasbo, ellen@wasbo.net 2021 - number subpaths and mark start/end element with green/red dot
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@ -22,34 +22,34 @@ import inkex
|
|||||||
from inkex import TextElement, Circle
|
from inkex import TextElement, Circle
|
||||||
|
|
||||||
class NumberSubpaths(inkex.EffectExtension):
|
class NumberSubpaths(inkex.EffectExtension):
|
||||||
"""Mark start and end nodes with numbered dots according to the options"""
|
"""Mark start and end elements with numbered dots according to the options"""
|
||||||
def add_arguments(self, pars):
|
def add_arguments(self, pars):
|
||||||
pars.add_argument("--dotsize", default="10px", help="Size of the dots on the path nodes")
|
pars.add_argument("--dotsize", default="10px", help="Size of the dots on the path elements")
|
||||||
pars.add_argument("--fontsize", default="10px", help="Size of node labels")
|
pars.add_argument("--fontsize", default="10px", help="Size of element labels")
|
||||||
pars.add_argument("--showID", type=inkex.Boolean, default=False)
|
pars.add_argument("--showID", type=inkex.Boolean, default=False)
|
||||||
|
|
||||||
def effect(self):
|
def effect(self):
|
||||||
if not self.svg.selected:
|
if not self.svg.selected:
|
||||||
raise inkex.AbortExtension("Please select an object.")
|
raise inkex.AbortExtension("Please select an object.")
|
||||||
for id, node in self.svg.selection.id_dict().items():
|
for element in self.svg.selection.values():
|
||||||
self.add_dot(node)
|
self.add_dot(element)
|
||||||
|
|
||||||
def add_dot(self, node):
|
def add_dot(self, element):
|
||||||
"""Add a dot label for this path element"""
|
"""Add a dot label for this path element"""
|
||||||
group = node.getparent().add(inkex.Group())
|
group = element.getparent().add(inkex.Group())
|
||||||
dot_group = group.add(inkex.Group())
|
dot_group = group.add(inkex.Group())
|
||||||
num_group = group.add(inkex.Group())
|
num_group = group.add(inkex.Group())
|
||||||
group.transform = node.transform
|
group.transform = element.transform
|
||||||
|
|
||||||
styleStart = inkex.Style({'stroke': 'none', 'fill': '#00ff00'})
|
styleStart = inkex.Style({'stroke': 'none', 'fill': '#00ff00'})
|
||||||
styleEnd = inkex.Style({'stroke': 'none', 'fill': '#ff0000'})
|
styleEnd = inkex.Style({'stroke': 'none', 'fill': '#ff0000'})
|
||||||
|
|
||||||
idTxt=''
|
idTxt=''
|
||||||
if self.options.showID==True:
|
if self.options.showID==True:
|
||||||
idTxt=node.get('id')+', '
|
idTxt=element.get('id')+', '
|
||||||
|
|
||||||
cc=0
|
cc=0
|
||||||
for sub in node.path.to_superpath():
|
for sub in element.path.to_superpath():
|
||||||
x=sub[0][1][0]
|
x=sub[0][1][0]
|
||||||
y=sub[0][1][1]
|
y=sub[0][1][1]
|
||||||
circle = dot_group.add(Circle(cx=str(x), cy=str(y), r=str(self.svg.unittouu(self.options.dotsize) / 2)))
|
circle = dot_group.add(Circle(cx=str(x), cy=str(y), r=str(self.svg.unittouu(self.options.dotsize) / 2)))
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<name>Unwind Paths</name>
|
<name>Unwind Paths</name>
|
||||||
<id>fablabchemnitz.de.unwind_paths</id>
|
<id>fablabchemnitz.de.unwind_paths</id>
|
||||||
<param name="tab" type="notebook">
|
<param name="tab" type="notebook">
|
||||||
<page name="tab_settings" gui-text="Paperfold for Inkscape">
|
<page name="tab_settings" gui-text="Unwind Paths">
|
||||||
<label appearance="header">Settings</label>
|
<label appearance="header">Settings</label>
|
||||||
<param name="keep_original" type="bool" gui-text="Keep original paths" gui-description="If selected, the original paths get deleted">false</param>
|
<param name="keep_original" type="bool" gui-text="Keep original paths" gui-description="If selected, the original paths get deleted">false</param>
|
||||||
<param name="break_apart" type="bool" gui-text="Break apart paths" gui-description="Split each path into single curve segments">false</param>
|
<param name="break_apart" type="bool" gui-text="Break apart paths" gui-description="Split each path into single curve segments">false</param>
|
||||||
|
@ -40,7 +40,7 @@ class UnwindPaths(inkex.EffectExtension):
|
|||||||
def add_arguments(self, pars):
|
def add_arguments(self, pars):
|
||||||
pars.add_argument('--tab')
|
pars.add_argument('--tab')
|
||||||
pars.add_argument('--keep_original', type=inkex.Boolean, default=False, help="If selected, the original paths get deleted")
|
pars.add_argument('--keep_original', type=inkex.Boolean, default=False, help="If selected, the original paths get deleted")
|
||||||
pars.add_argument('--break_apart', type=inkex.Boolean, default=False)
|
pars.add_argument('--break_apart', type=inkex.Boolean, default=False, help="Split each path into single curve segments")
|
||||||
pars.add_argument('--colorize', type=inkex.Boolean, default=False, help="Requires enabled 'Break apart' option")
|
pars.add_argument('--colorize', type=inkex.Boolean, default=False, help="Requires enabled 'Break apart' option")
|
||||||
pars.add_argument('--extrude', type=inkex.Boolean, default=False)
|
pars.add_argument('--extrude', type=inkex.Boolean, default=False)
|
||||||
pars.add_argument('--extrude_height', type=float, default=10.000)
|
pars.add_argument('--extrude_height', type=float, default=10.000)
|
||||||
@ -88,126 +88,128 @@ class UnwindPaths(inkex.EffectExtension):
|
|||||||
|
|
||||||
if len(self.svg.selected) > 0:
|
if len(self.svg.selected) > 0:
|
||||||
#we break apart combined paths to get distinct contours
|
#we break apart combined paths to get distinct contours
|
||||||
|
breakApartPaths = []
|
||||||
for element in self.svg.selection.filter(inkex.PathElement).values():
|
for element in self.svg.selection.filter(inkex.PathElement).values():
|
||||||
breakApartPaths = self.breakContours(element)
|
breakApartPaths.append(self.breakContours(element))
|
||||||
|
|
||||||
for element in breakApartPaths:
|
for breakApartPath in breakApartPaths:
|
||||||
elemGroup = self.svg.get_current_layer().add(inkex.Group(id="unwinding-" + element.get('id')))
|
for element in breakApartPath:
|
||||||
|
elemGroup = self.svg.get_current_layer().add(inkex.Group(id="unwinding-" + element.get('id')))
|
||||||
#beginning point of the unwind band:
|
|
||||||
bbox = element.bounding_box() #shift the element to the bottom of the element
|
|
||||||
xmin = bbox.left
|
|
||||||
ymax = bbox.bottom + bbox.height * 0.1 #10% additional spacing
|
|
||||||
|
|
||||||
csp = element.path.to_superpath()
|
|
||||||
subCount = len(element.path)
|
|
||||||
|
|
||||||
#generate random colors; used to identify glue tab pairs
|
|
||||||
if self.options.colorize is True:
|
|
||||||
randomColorSet = []
|
|
||||||
while len(randomColorSet) < subCount - 1:
|
|
||||||
r = lambda: random.randint(0,255)
|
|
||||||
newColor = '#%02X%02X%02X' % (r(),r(),r())
|
|
||||||
if newColor not in randomColorSet:
|
|
||||||
randomColorSet.append(newColor)
|
|
||||||
|
|
||||||
for sub in csp:
|
|
||||||
#generate new horizontal line data by measuring each segment
|
|
||||||
new = []
|
|
||||||
new.append([sub[0]])
|
|
||||||
i = 1
|
|
||||||
topPathData = "m {:0.6f},{:0.6f} ".format(xmin, ymax)
|
|
||||||
bottomPathData = "m {:0.6f},{:0.6f} ".format(xmin, ymax + shifting)
|
|
||||||
lengths = []
|
|
||||||
|
|
||||||
if self.options.break_apart is True:
|
|
||||||
topLineGroup = self.svg.get_current_layer().add(inkex.Group(id="hline-top-" + element.get('id')))
|
|
||||||
bottomLineGroup = self.svg.get_current_layer().add(inkex.Group(id="hline-bottom-" + element.get('id')))
|
|
||||||
newOriginalPathGroup = self.svg.get_current_layer().add(inkex.Group(id="new-original-" + element.get('id')))
|
|
||||||
elemGroup.append(topLineGroup)
|
|
||||||
elemGroup.append(bottomLineGroup)
|
|
||||||
self.svg.get_current_layer().append(newOriginalPathGroup) #we want this to be one level above unwound stuff
|
|
||||||
|
|
||||||
if self.options.extrude is True:
|
|
||||||
vlinesGroup = self.svg.get_current_layer().add(inkex.Group(id="vlines-" + element.get('id')))
|
|
||||||
elemGroup.append(vlinesGroup)
|
|
||||||
|
|
||||||
while i <= len(sub) - 1:
|
|
||||||
stroke_color = '#000000'
|
|
||||||
if self.options.colorize is True and self.options.break_apart is True:
|
|
||||||
stroke_color =randomColorSet[i-1]
|
|
||||||
|
|
||||||
horizontal_line_style = {'stroke':stroke_color,'stroke-width':'1px','fill':'none'}
|
|
||||||
|
|
||||||
length = bezier.cspseglength(new[-1][-1], sub[i]) #sub path length
|
|
||||||
segment = "h {:0.6f} ".format(length)
|
|
||||||
topPathData += segment
|
|
||||||
bottomPathData += segment
|
|
||||||
new[-1].append(sub[i]) #important line!
|
|
||||||
if self.options.break_apart is True:
|
|
||||||
self.drawline("m {:0.6f},{:0.0f} ".format(xmin + sum([length for length in lengths]), ymax) + segment,
|
|
||||||
"segmented-top-{}-{}".format(element.get('id'), i), topLineGroup, horizontal_line_style)
|
|
||||||
if self.options.extrude is True:
|
|
||||||
self.drawline("m {:0.6f},{:0.0f} ".format(xmin + sum([length for length in lengths]), ymax + shifting) + segment,
|
|
||||||
"segmented-bottom-{}-{}".format(element.get('id'), i), bottomLineGroup, horizontal_line_style)
|
|
||||||
lengths.append(length)
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
|
|
||||||
if self.options.break_apart is False:
|
|
||||||
self.drawline(topPathData, "combined-top-{0}".format(element.get('id')), elemGroup, horizontal_line_style)
|
|
||||||
if self.options.extrude is True:
|
|
||||||
self.drawline(bottomPathData, "combined-bottom-{0}".format(element.get('id')), elemGroup, horizontal_line_style)
|
|
||||||
|
|
||||||
#draw as much vertical lines as segments in bezier + start + end vertical line
|
|
||||||
vertical_end_lines_style = {'stroke':'#000000','stroke-width':'1px','fill':'none'}
|
|
||||||
if self.options.extrude is True:
|
|
||||||
#render start line
|
|
||||||
self.drawline("m {:0.6f},{:0.6f} v {:0.6f}".format(xmin, ymax, shifting),"vline-{}-start".format(element.get('id')), vlinesGroup, vertical_end_lines_style)
|
|
||||||
#render divider lines
|
|
||||||
if self.options.render_vertical_dividers is True:
|
|
||||||
vertical_mid_lines_style = {'stroke':'#000000','stroke-width':'1px','fill':'none'}
|
|
||||||
if self.options.render_with_dashes is True:
|
|
||||||
vertical_mid_lines_style = {'stroke':'#000000','stroke-width':'1px',"stroke-dasharray":"2 2", 'fill':'none'}
|
|
||||||
x = 0
|
|
||||||
for n in range(0, i-2):
|
|
||||||
x += lengths[n]
|
|
||||||
self.drawline("m {:0.6f},{:0.6f} v {:0.6f}".format(xmin + x, ymax, shifting),"vline-{}-{}".format(element.get('id'), n + 1), vlinesGroup, vertical_mid_lines_style)
|
|
||||||
#render end line
|
|
||||||
self.drawline("m {:0.6f},{:0.6f} v {:0.6f}".format(xmin + sum([length for length in lengths]), ymax, shifting),"vline-{}-end".format(element.get('id')), vlinesGroup, vertical_end_lines_style)
|
|
||||||
|
|
||||||
if self.options.break_apart is True:
|
|
||||||
# Split (already broken apart) paths into detached segments
|
|
||||||
raw = Path(element.get("d")).to_arrays() #returns Uppercase Command Letters; does not include H, V
|
|
||||||
for i in range(len(raw)):
|
|
||||||
if i > 0:
|
|
||||||
|
|
||||||
if raw[i-1][0] in ("M", "L"):
|
|
||||||
startPoint = "M {},{}".format(raw[i-1][1][0], raw[i-1][1][1])
|
|
||||||
elif raw[i-1][0] == 'C':
|
|
||||||
startPoint = "M {},{}".format(raw[i-1][1][-2], raw[i-1][1][-1])
|
|
||||||
else:
|
|
||||||
inkex.utils.debug("Start point error. Unknown command!")
|
|
||||||
|
|
||||||
if raw[i][0] in ("M", "L"):
|
|
||||||
segment = " {},{}".format(raw[i][1][0], raw[i][1][1])
|
|
||||||
elif raw[i][0] == 'C':
|
|
||||||
segment = "{} {}".format(raw[i][0], ''.join(str(raw[i][1]))[1:-1])
|
|
||||||
elif raw[i][0] == 'Z':
|
|
||||||
segment = "{},{}".format(raw[0][1][0], raw[0][1][1])
|
|
||||||
else:
|
|
||||||
inkex.utils.debug("Segment error. Unknown command!")
|
|
||||||
|
|
||||||
d = str(Path("{} {}".format(startPoint, segment)))
|
#beginning point of the unwind band:
|
||||||
|
bbox = element.bounding_box() #shift the element to the bottom of the element
|
||||||
|
xmin = bbox.left
|
||||||
|
ymax = bbox.bottom + bbox.height * 0.1 #10% additional spacing
|
||||||
|
|
||||||
|
csp = element.path.to_superpath()
|
||||||
|
subCount = len(element.path)
|
||||||
|
|
||||||
|
#generate random colors; used to identify glue tab pairs
|
||||||
|
if self.options.colorize is True:
|
||||||
|
randomColorSet = []
|
||||||
|
while len(randomColorSet) < subCount - 1:
|
||||||
|
r = lambda: random.randint(0,255)
|
||||||
|
newColor = '#%02X%02X%02X' % (r(),r(),r())
|
||||||
|
if newColor not in randomColorSet:
|
||||||
|
randomColorSet.append(newColor)
|
||||||
|
|
||||||
|
for sub in csp:
|
||||||
|
#generate new horizontal line data by measuring each segment
|
||||||
|
new = []
|
||||||
|
new.append([sub[0]])
|
||||||
|
i = 1
|
||||||
|
topPathData = "m {:0.6f},{:0.6f} ".format(xmin, ymax)
|
||||||
|
bottomPathData = "m {:0.6f},{:0.6f} ".format(xmin, ymax + shifting)
|
||||||
|
lengths = []
|
||||||
|
|
||||||
|
if self.options.break_apart is True:
|
||||||
|
topLineGroup = self.svg.get_current_layer().add(inkex.Group(id="hline-top-" + element.get('id')))
|
||||||
|
bottomLineGroup = self.svg.get_current_layer().add(inkex.Group(id="hline-bottom-" + element.get('id')))
|
||||||
|
newOriginalPathGroup = self.svg.get_current_layer().add(inkex.Group(id="new-original-" + element.get('id')))
|
||||||
|
elemGroup.append(topLineGroup)
|
||||||
|
elemGroup.append(bottomLineGroup)
|
||||||
|
self.svg.get_current_layer().append(newOriginalPathGroup) #we want this to be one level above unwound stuff
|
||||||
|
|
||||||
|
if self.options.extrude is True:
|
||||||
|
vlinesGroup = self.svg.get_current_layer().add(inkex.Group(id="vlines-" + element.get('id')))
|
||||||
|
elemGroup.append(vlinesGroup)
|
||||||
|
|
||||||
|
while i <= len(sub) - 1:
|
||||||
stroke_color = '#000000'
|
stroke_color = '#000000'
|
||||||
if self.options.colorize is True:
|
if self.options.colorize is True and self.options.break_apart is True:
|
||||||
stroke_color =randomColorSet[i-1]
|
stroke_color =randomColorSet[i-1]
|
||||||
new_original_line_style = {'stroke':stroke_color,'stroke-width':'1px','fill':'none'}
|
|
||||||
self.drawline(d, "segmented-" + element.get('id'), newOriginalPathGroup, new_original_line_style)
|
horizontal_line_style = {'stroke':stroke_color,'stroke-width':'1px','fill':'none'}
|
||||||
|
|
||||||
if self.options.keep_original is False:
|
length = bezier.cspseglength(new[-1][-1], sub[i]) #sub path length
|
||||||
element.delete()
|
segment = "h {:0.6f} ".format(length)
|
||||||
|
topPathData += segment
|
||||||
|
bottomPathData += segment
|
||||||
|
new[-1].append(sub[i]) #important line!
|
||||||
|
if self.options.break_apart is True:
|
||||||
|
self.drawline("m {:0.6f},{:0.0f} ".format(xmin + sum([length for length in lengths]), ymax) + segment,
|
||||||
|
"segmented-top-{}-{}".format(element.get('id'), i), topLineGroup, horizontal_line_style)
|
||||||
|
if self.options.extrude is True:
|
||||||
|
self.drawline("m {:0.6f},{:0.0f} ".format(xmin + sum([length for length in lengths]), ymax + shifting) + segment,
|
||||||
|
"segmented-bottom-{}-{}".format(element.get('id'), i), bottomLineGroup, horizontal_line_style)
|
||||||
|
lengths.append(length)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
|
||||||
|
if self.options.break_apart is False:
|
||||||
|
self.drawline(topPathData, "combined-top-{0}".format(element.get('id')), elemGroup, horizontal_line_style)
|
||||||
|
if self.options.extrude is True:
|
||||||
|
self.drawline(bottomPathData, "combined-bottom-{0}".format(element.get('id')), elemGroup, horizontal_line_style)
|
||||||
|
|
||||||
|
#draw as much vertical lines as segments in bezier + start + end vertical line
|
||||||
|
vertical_end_lines_style = {'stroke':'#000000','stroke-width':'1px','fill':'none'}
|
||||||
|
if self.options.extrude is True:
|
||||||
|
#render start line
|
||||||
|
self.drawline("m {:0.6f},{:0.6f} v {:0.6f}".format(xmin, ymax, shifting),"vline-{}-start".format(element.get('id')), vlinesGroup, vertical_end_lines_style)
|
||||||
|
#render divider lines
|
||||||
|
if self.options.render_vertical_dividers is True:
|
||||||
|
vertical_mid_lines_style = {'stroke':'#000000','stroke-width':'1px','fill':'none'}
|
||||||
|
if self.options.render_with_dashes is True:
|
||||||
|
vertical_mid_lines_style = {'stroke':'#000000','stroke-width':'1px',"stroke-dasharray":"2 2", 'fill':'none'}
|
||||||
|
x = 0
|
||||||
|
for n in range(0, i-2):
|
||||||
|
x += lengths[n]
|
||||||
|
self.drawline("m {:0.6f},{:0.6f} v {:0.6f}".format(xmin + x, ymax, shifting),"vline-{}-{}".format(element.get('id'), n + 1), vlinesGroup, vertical_mid_lines_style)
|
||||||
|
#render end line
|
||||||
|
self.drawline("m {:0.6f},{:0.6f} v {:0.6f}".format(xmin + sum([length for length in lengths]), ymax, shifting),"vline-{}-end".format(element.get('id')), vlinesGroup, vertical_end_lines_style)
|
||||||
|
|
||||||
|
if self.options.break_apart is True:
|
||||||
|
# Split (already broken apart) paths into detached segments
|
||||||
|
raw = Path(element.get("d")).to_arrays() #returns Uppercase Command Letters; does not include H, V
|
||||||
|
for i in range(len(raw)):
|
||||||
|
if i > 0:
|
||||||
|
|
||||||
|
if raw[i-1][0] in ("M", "L"):
|
||||||
|
startPoint = "M {},{}".format(raw[i-1][1][0], raw[i-1][1][1])
|
||||||
|
elif raw[i-1][0] == 'C':
|
||||||
|
startPoint = "M {},{}".format(raw[i-1][1][-2], raw[i-1][1][-1])
|
||||||
|
else:
|
||||||
|
inkex.utils.debug("Start point error. Unknown command!")
|
||||||
|
|
||||||
|
if raw[i][0] in ("M", "L"):
|
||||||
|
segment = " {},{}".format(raw[i][1][0], raw[i][1][1])
|
||||||
|
elif raw[i][0] == 'C':
|
||||||
|
segment = "{} {}".format(raw[i][0], ''.join(str(raw[i][1]))[1:-1])
|
||||||
|
elif raw[i][0] == 'Z':
|
||||||
|
segment = "{},{}".format(raw[0][1][0], raw[0][1][1])
|
||||||
|
else:
|
||||||
|
inkex.utils.debug("Segment error. Unknown command!")
|
||||||
|
|
||||||
|
d = str(Path("{} {}".format(startPoint, segment)))
|
||||||
|
|
||||||
|
stroke_color = '#000000'
|
||||||
|
if self.options.colorize is True:
|
||||||
|
stroke_color =randomColorSet[i-1]
|
||||||
|
new_original_line_style = {'stroke':stroke_color,'stroke-width':'1px','fill':'none'}
|
||||||
|
self.drawline(d, "segmented-" + element.get('id'), newOriginalPathGroup, new_original_line_style)
|
||||||
|
|
||||||
|
if self.options.keep_original is False:
|
||||||
|
element.delete()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.msg('Please select some paths first.')
|
self.msg('Please select some paths first.')
|
||||||
|
Reference in New Issue
Block a user