added spirograph extension, fix in vpypetools, adjusted polyhedra, some changes in styles to layers

This commit is contained in:
Mario Voigt 2021-04-25 00:25:16 +02:00
parent 5d87b2cd13
commit 6d27ef1a30
10 changed files with 5480 additions and 13988 deletions

View File

@ -1,18 +0,0 @@
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Animate Order - Kopfteil-Laser.svg</title>
<meta name="description" content="SVG Drawing Animation">
</head>
<body>
<button onclick="vi.reset().play();">replay</button>
<br/>
<object id="animate_order" type="image/svg+xml" data="drawing.svg"></object>
<script src="./vivus-0.4.6/dist/vivus.js"></script>
<script>
var vi = new Vivus('animate_order', {type: 'oneByOne', duration:7200.0, reverseStack:false});
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 504 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -150,9 +150,9 @@ class Polyhedra(inkex.EffectExtension):
gsub_attribs = {inkex.addNS('label','inkscape'):'Polygon ' + str( poly ) + 'border' } gsub_attribs = {inkex.addNS('label','inkscape'):'Polygon ' + str( poly ) + 'border' }
gsub = etree.SubElement(g, 'g', gsub_attribs) gsub = etree.SubElement(g, 'g', gsub_attribs)
# Create SVG Path for gear # Create SVG Path
cutStyle = { 'stroke': '#0000FF', 'fill': 'none' } cutStyle = { 'stroke': '#0000FF', 'stroke-width': '1px', 'fill': 'none' }
perfStyle = { 'stroke': '#FF0000', 'fill': 'none' } perfStyle = { 'stroke': '#FF0000', 'stroke-width': '1px', 'fill': 'none' }
textStyle = { textStyle = {
'font-size': str( size/4 ), 'font-size': str( size/4 ),
'font-family': 'arial', 'font-family': 'arial',

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>SpiroGraph</name>
<id>fablabchemnitz.de.spirograph</id>
<param name="tab" type="notebook">
<page name="Cartesian plot" appearance="minimal" gui-text="Data">
<param name="curveType" type="optiongroup" appearance="combo" gui-text="Curve type:">
<option value="Epitrochoid">Epitrochoid</option>
<option value="Hypotrochoid">Hypotrochoid</option>
</param>
<label appearance="header">Curve parameters</label>
<param name="radius_R" type="int" min="0" max="1000" gui-text="Fixed circle radius (R):">10.0</param>
<param name="radius_r" type="int" min="-1000" max="1000" gui-text="Rolling circle radius (r):">5.0</param>
<param name="pencil_distance" type="int" min="-1000" max="1000" gui-text="Pencil distance¹ (d):">2</param>
<label>¹ use d=r for Epi/Hypocycloid. </label>
<label appearance="header">Plot parameters</label>
<param name="detailLevel" type="int" min="1" max="10" gui-text="Detail level:">1.0</param>
<param name="drawBaseCircles" type="bool" gui-text="Draw base circles">false</param>
<param name="animate" type="bool" gui-text="Animate">false</param>
<param type="path" name="directory" gui-text="Animation directory:" mode="folder"/>
</page>
</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu name="FabLab Chemnitz">
<submenu name="Shape/Pattern from Generator"/>
</submenu>
</effects-menu>
</effect>
<script>
<command location="inx" interpreter="python">spirograph.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,251 @@
#!/usr/bin/python
import math
import os
import numpy as np
import scipy.signal as scipySignal
import inkscapeMadeEasy.inkscapeMadeEasy_Base as inkBase
import inkscapeMadeEasy.inkscapeMadeEasy_Draw as inkDraw
import inkscapeMadeEasy.inkscapeMadeEasy_Plot as inkPlot
# least common multiplier
def myLcm(x, y):
return x * y / math.gcd(int(x), int(y))
# ---------------------------------------------
# noinspection PyAttributeOutsideInit
class Spirograph(inkBase.inkscapeMadeEasy):
def __init__(self):
inkBase.inkscapeMadeEasy.__init__(self)
self.arg_parser.add_argument("--tab", type=str, dest="tab", default="object")
self.arg_parser.add_argument("--curveType", type=str, dest="curveType", default='resistor')
self.arg_parser.add_argument("--radius_R", type=float, dest="radius_R", default=10.0)
self.arg_parser.add_argument("--radius_r", type=float, dest="radius_r", default=5.0)
self.arg_parser.add_argument("--detailLevel", type=float, dest="detailLevel", default=1.0)
self.arg_parser.add_argument("--adaptiveTheta", type=self.bool, dest="adaptiveTheta", default=False)
self.arg_parser.add_argument("--pencil_distance", type=float, dest="pencil_distance", default=1.0)
self.arg_parser.add_argument("--drawBaseCircles", type=self.bool, dest="drawBaseCircles", default=False)
self.arg_parser.add_argument("--animate", type=self.bool, dest="animate", default=False)
self.arg_parser.add_argument("--directory", type=str, dest="directory", default='./')
def effect(self):
so = self.options
# sets the position to the viewport center, round to next 10.
position = [self.svg.namedview.center[0], self.svg.namedview.center[1]]
position[0] = int(math.ceil(position[0] / 10.0)) * 10
position[1] = int(math.ceil(position[1] / 10.0)) * 10
root_layer = self.document.getroot()
group = self.createGroup(root_layer, 'Spiro')
so.tab = so.tab.replace('"', '') # removes de exceeding double quotes from the string
# curve parameters
R = so.radius_R
r = so.radius_r
d = so.pencil_distance
finalTheta = 2 * np.pi * myLcm(abs(r), R) / R
if 'hypo' in so.curveType.lower():
typeCurve = 'hypo'
if 'epi' in so.curveType.lower():
typeCurve = 'epi'
# markers and linestyles
Lgray = inkDraw.color.gray(0.8)
Dgray = inkDraw.color.gray(0.3)
# wheel
markerCenterDisk = inkDraw.marker.createDotMarker(self, nameID='diskCenter', scale=0.3, RenameMode=1, strokeColor=Dgray,
fillColor=inkDraw.color.defined('white'))
markerPen = inkDraw.marker.createDotMarker(self, nameID='diskPen', scale=0.3, RenameMode=1, strokeColor=Dgray,
fillColor=inkDraw.color.defined('white'))
[startArrowMarker, endArrowMarker] = inkDraw.marker.createArrow1Marker(self, nameID='arrowRot', RenameMode=1, scale=0.3, strokeColor=Dgray,
fillColor=Dgray)
if typeCurve == 'hypo':
self.lineStyleArrow = inkDraw.lineStyle.set(lineWidth=r / 40, lineColor=Dgray, markerStart=startArrowMarker, markerEnd=None)
else:
self.lineStyleArrow = inkDraw.lineStyle.set(lineWidth=r / 40, lineColor=Dgray, markerStart=None, markerEnd=endArrowMarker)
self.lineStyleARM = inkDraw.lineStyle.set(lineWidth=r / 40, lineColor=Dgray, markerStart=markerCenterDisk, markerEnd=markerPen)
self.lineStyleDisk = inkDraw.lineStyle.set(lineWidth=r / 40, lineColor=None, fillColor=Lgray)
# curve
self.lineStyleCurve = inkDraw.lineStyle.set(lineWidth=0.8, lineColor=inkDraw.color.defined('red'), markerStart=None, markerEnd=None,
markerMid=None)
self.lineStyleCurve2 = inkDraw.lineStyle.set(lineWidth=0.8, lineColor=inkDraw.color.defined('Dgreen'), markerStart=None, markerEnd=None,
markerMid=None)
self.lineStyleCurve3 = inkDraw.lineStyle.set(lineWidth=0.8, lineColor=inkDraw.color.defined('blue'), markerStart=None, markerEnd=None,
markerMid=None)
self.lineStylePre = inkDraw.lineStyle.set(lineWidth=1, lineColor=inkDraw.color.defined('red'))
self.constructionLine = inkDraw.lineStyle.set(lineWidth=0.5, lineColor=Dgray)
# draft Points
if so.adaptiveTheta:
nPrePoints = 10 * so.detailLevel # number of pre points per turn
thetasDraft = np.linspace(0, finalTheta, int(nPrePoints * finalTheta / (2 * np.pi)))
[pointsDraft, _, curvatureDraft] = self.calcCurve__trochoid(typeCurve, R, r, d, thetasDraft)
# find sampling points based on local curvature
nSamples = np.ones(curvatureDraft.shape)*min(2,so.detailLevel)
detailFactor=5
# treshold normalized curvatures
nSamples[curvatureDraft>0.8] *=detailFactor
detailFactor = 2.5
# check if vector changed direction abuptly
for i,p in enumerate(pointsDraft):
if i==0:
v1=pointsDraft[i+1]-pointsDraft[i]
v2=pointsDraft[i]-pointsDraft[-1]
elif i < len(pointsDraft)-1:
v1=pointsDraft[i+1]-pointsDraft[i]
v2=pointsDraft[i]-pointsDraft[i-1]
else:
v1=pointsDraft[0]-pointsDraft[i]
v2=pointsDraft[i]-pointsDraft[i-1]
v1=v1/np.linalg.norm(v1)
v2=v2/np.linalg.norm(v2)
if np.dot(v1,v2)<0.5:
nSamples[i] *=detailFactor
thetasFinal = np.array([])
for i in range(len(nSamples) - 1):
thetasFinal = np.append(thetasFinal, np.linspace(thetasDraft[i], thetasDraft[i + 1], int(nSamples[i]), endpoint=False))
thetasFinal = np.append(thetasFinal, finalTheta)
# filter the sampled angles to have a smooth transition.
Ntaps = 5
gaussWindow = scipySignal.gaussian(Ntaps, std=5)
gaussWindow = gaussWindow / np.sum(gaussWindow)
# inkPlot.plot.cartesian(self, root_layer, np.arange(thetasFinal.shape[0]), thetasFinal * 180 / np.pi, position, xTicks=False, yTicks=True, xTickStep=thetasFinal.shape[0]/10, yTickStep=120.0, xScale=10, yScale=10,xGrid=True, yGrid=True, forceXlim=None, forceYlim=None)
thetasFinal = scipySignal.filtfilt(gaussWindow, np.array([1]), thetasFinal)
# inkPlot.plot.cartesian(self, root_layer, np.arange(thetasFinal.shape[0]), thetasFinal * 180 / np.pi, position, xTicks=False, yTicks=True,xTickStep=thetasFinal.shape[0]/10, yTickStep=120.0, xScale=10, yScale=10, xGrid=True, yGrid=True, forceXlim=None, forceYlim=None,drawAxis=False)
else:
nPrePoints = 20 * so.detailLevel # number of pre points per turn
thetasFinal = np.linspace(0, finalTheta, int(nPrePoints * finalTheta / (2 * np.pi)))
# final shape
[PointsFinal, CentersFinal, curvatureFinal] = self.calcCurve__trochoid(typeCurve, R, r, d, thetasFinal)
[PointsFinal2, CentersFinal2, curvatureFinal2] = self.calcCurve__trochoid(typeCurve, R, r, -d, thetasFinal)
[PointsFinal3, CentersFinal3, curvatureFinal3] = self.calcCurve__trochoid(typeCurve, R, r, r, thetasFinal)
if so.animate:
animGroup = self.createGroup(group, 'Anim')
circle_R = inkDraw.circle.centerRadius(parent=animGroup, centerPoint=[0, 0], radius=R, offset=position, lineStyle=self.constructionLine)
# draw planetary wheel
wheelGroup = self.createGroup(animGroup, 'Anim')
circle_r = inkDraw.circle.centerRadius(wheelGroup, centerPoint=CentersFinal[0], radius=r, offset=position, lineStyle=self.lineStyleDisk)
arms1 = inkDraw.line.absCoords(wheelGroup, coordsList=[CentersFinal[0], PointsFinal[0]], offset=position, lineStyle=self.lineStyleARM)
arms2 = inkDraw.line.absCoords(wheelGroup, coordsList=[CentersFinal2[0], PointsFinal2[0]], offset=position, lineStyle=self.lineStyleARM)
arms3 = inkDraw.line.absCoords(wheelGroup, coordsList=[CentersFinal3[0], PointsFinal3[0]], offset=position, lineStyle=self.lineStyleARM)
arc1 = inkDraw.arc.centerAngStartAngEnd(wheelGroup, centerPoint=CentersFinal[0], radius=r * 0.6, angStart=40, angEnd=80, offset=position,
lineStyle=self.lineStyleArrow)
arc2 = inkDraw.arc.centerAngStartAngEnd(wheelGroup, centerPoint=CentersFinal[0], radius=r * 0.6, angStart=160, angEnd=200,
offset=position, lineStyle=self.lineStyleArrow)
arc3 = inkDraw.arc.centerAngStartAngEnd(wheelGroup, centerPoint=CentersFinal[0], radius=r * 0.6, angStart=280, angEnd=320,
offset=position, lineStyle=self.lineStyleArrow)
self.exportSVG(animGroup, os.path.join(so.directory,'outSVG_%1.5d.svg' % 0))
for i in range(1, len(thetasFinal)):
self.moveElement(wheelGroup, [CentersFinal[i][0] - CentersFinal[i - 1][0], CentersFinal[i][1] - CentersFinal[i - 1][1]])
if typeCurve == 'hypo':
self.rotateElement(wheelGroup, [position[0] + CentersFinal[i][0], position[1] + CentersFinal[i][1]],
(thetasFinal[i] - thetasFinal[i - 1]) * (R - r) / r * 180 / np.pi)
else:
self.rotateElement(wheelGroup, [position[0] + CentersFinal[i][0], position[1] + CentersFinal[i][1]],
- (thetasFinal[i] - thetasFinal[i - 1]) * (R + r) / r * 180 / np.pi)
curve1 = inkDraw.line.absCoords(parent=animGroup, coordsList=PointsFinal[:i + 1], offset=position, lineStyle=self.lineStyleCurve,
closePath=False)
curve2 = inkDraw.line.absCoords(parent=animGroup, coordsList=PointsFinal2[:i + 1], offset=position, lineStyle=self.lineStyleCurve2,
closePath=False)
curve3 = inkDraw.line.absCoords(parent=animGroup, coordsList=PointsFinal3[:i + 1], offset=position, lineStyle=self.lineStyleCurve3,
closePath=False)
self.exportSVG(animGroup, os.path.join(so.directory , 'outSVG_%1.5d.svg' % i))
self.removeElement(curve1)
self.removeElement(curve2)
self.removeElement(curve3)
self.removeElement(animGroup)
else:
if so.drawBaseCircles:
inkDraw.circle.centerRadius(parent=group, centerPoint=position, radius=R, offset=[0, 0], lineStyle=self.constructionLine)
if typeCurve == 'hypo':
inkDraw.circle.centerRadius(parent=group, centerPoint=position, radius=r, offset=[R - r, 0], lineStyle=self.constructionLine)
if typeCurve == 'epi':
inkDraw.circle.centerRadius(parent=group, centerPoint=position, radius=r, offset=[R + r, 0], lineStyle=self.constructionLine)
inkDraw.line.absCoords(group, PointsFinal, position, 'spiro', self.lineStyleCurve, closePath=True)
# plot curvatures
if False:
inkPlot.plot.polar(self, group, curvatureFinal, thetasFinal * 180 / np.pi, [position[0] + 3 * R, position[1]], rTicks=False,
tTicks=False, rTickStep=0.2, tTickStep=45.0, rScale=20, rGrid=True, tGrid=True, lineStylePlot=self.lineStyleCurve,
forceRlim=[0.0, 1.0])
return
# typeCurve: 'hypo', 'epi'
def calcCurve__trochoid(self, typeCurve, R, r, d, thetas):
j = complex(0, 1)
if typeCurve.lower() == 'hypo':
# https://www.mathcurve.com/courbes2d.gb/hypotrochoid/hypotrochoid.shtml
P_complex = (R - r) * np.exp(j * thetas) + d * np.exp(-j * thetas * (R - r) / r)
dP_complex = (R - r) * j * np.exp(j * thetas) + d * (-j) * (R - r) / r * np.exp(-j * thetas * (R - r) / r)
ddP_complex = (R - r) * (-1) * np.exp(j * thetas) + d * (-1) * ((R - r) / r) ** 2 * np.exp(-j * thetas * (R - r) / r)
centerGear = (R - r) * np.exp(j * thetas)
if typeCurve.lower() == 'epi':
# https://www.mathcurve.com/courbes2d.gb/epitrochoid/epitrochoid.shtml
P_complex = (R + r) * np.exp(j * thetas) - d * np.exp(j * thetas * (R + r) / r)
dP_complex = (R + r) * j * np.exp(j * thetas) - d * j * (R + r) / r * np.exp(j * thetas * (R + r) / r)
ddP_complex = (R + r) * (-1) * np.exp(j * thetas) - d * (-1) * ((R + r) / r) ** 2 * np.exp(j * thetas * (R + r) / r)
centerGear = (R + r) * np.exp(j * thetas)
with np.errstate(divide='ignore', invalid='ignore'):
curvature = np.divide(abs(dP_complex.real * ddP_complex.imag - dP_complex.imag * ddP_complex.real),
(dP_complex.real ** 2 + dP_complex.imag ** 2) ** (2 / 3))
# remove Nan=0/0
np.nan_to_num(curvature, copy=False)
# remove values too large
curvature[curvature > 10 * np.mean(curvature)] = 0.0
# self.Dump(curvature, '/home/fernando/lixo.txt', 'w')
# normalize curvature
curvature = self._normalizeCurvatures(curvature, 0.0, 1.0)
Points = np.column_stack((P_complex.real, P_complex.imag))
Centers = np.column_stack((centerGear.real, centerGear.imag))
return [Points, Centers, curvature]
def _normalizeCurvatures(self, curvatures, normMin=0.0, normMax=1.0):
y1 = normMin
y2 = normMax
x1 = np.min(curvatures)
x2 = np.max(curvatures)
alpha = (y2 - y1) / (x2 - x1)
return alpha * (curvatures - x1) + y1
if __name__ == '__main__':
sp = Spirograph()
sp.run()

View File

@ -23,7 +23,7 @@ class StylesToLayers(inkex.EffectExtension):
def findLayer(self, layerName): def findLayer(self, layerName):
svg_layers = self.document.xpath('//svg:g[@inkscape:groupmode="layer"]', namespaces=inkex.NSS) svg_layers = self.document.xpath('//svg:g[@inkscape:groupmode="layer"]', namespaces=inkex.NSS)
for layer in svg_layers: for layer in svg_layers:
#inkex.utils.debug(str(layer.get('inkscape:label')) + " == " + layerName) #self.msg(str(layer.get('inkscape:label')) + " == " + layerName)
if layer.get('inkscape:label') == layerName: if layer.get('inkscape:label') == layerName:
return layer return layer
return None return None
@ -31,7 +31,7 @@ class StylesToLayers(inkex.EffectExtension):
def createLayer(self, layerNodeList, layerName): def createLayer(self, layerNodeList, layerName):
svg = self.document.xpath('//svg:svg',namespaces=inkex.NSS)[0] svg = self.document.xpath('//svg:svg',namespaces=inkex.NSS)[0]
for layer in layerNodeList: for layer in layerNodeList:
#inkex.utils.debug(str(layer[0].get('inkscape:label')) + " == " + layerName) #self.msg(str(layer[0].get('inkscape:label')) + " == " + layerName)
if layer[0].get('inkscape:label') == layerName: if layer[0].get('inkscape:label') == layerName:
return layer[0] #already exists. Do not create duplicate return layer[0] #already exists. Do not create duplicate
layer = etree.SubElement(svg, 'g') layer = etree.SubElement(svg, 'g')
@ -68,8 +68,8 @@ class StylesToLayers(inkex.EffectExtension):
import applytransform import applytransform
applyTransformAvailable = True applyTransformAvailable = True
except Exception as e: except Exception as e:
# inkex.utils.debug(e) # self.msg(e)
inkex.utils.debug("Calling 'Apply Transformations' extension failed. Maybe the extension is not installed. You can download it from official InkScape Gallery. Skipping ...") self.msg("Calling 'Apply Transformations' extension failed. Maybe the extension is not installed. You can download it from official InkScape Gallery. Skipping ...")
layer_name = None layer_name = None
layerNodeList = [] #list with layer, neutral_value, element and self.options.separateby type layerNodeList = [] #list with layer, neutral_value, element and self.options.separateby type
@ -175,12 +175,11 @@ class StylesToLayers(inkex.EffectExtension):
layer_name = "fill-opacity-none" layer_name = "fill-opacity-none"
else: else:
inkex.utils.debug("No proper option selected.") self.msg("No proper option selected.")
exit(1) exit(1)
if neutral_value is not None: #apply decimals filter if neutral_value is not None: #apply decimals filter
neutral_value = float(round(neutral_value, self.options.decimals)) neutral_value = float(round(neutral_value, self.options.decimals))
if layer_name is not None: if layer_name is not None:
layer_name = layer_name.split(";")[0] #cut off existing semicolons to avoid duplicated layers with/without semicolon layer_name = layer_name.split(";")[0] #cut off existing semicolons to avoid duplicated layers with/without semicolon
currentLayer = self.findLayer(layer_name) currentLayer = self.findLayer(layer_name)
@ -188,13 +187,16 @@ class StylesToLayers(inkex.EffectExtension):
layerNodeList.append([self.createLayer(layerNodeList, layer_name), neutral_value, element, self.options.separateby]) layerNodeList.append([self.createLayer(layerNodeList, layer_name), neutral_value, element, self.options.separateby])
else: else:
layerNodeList.append([currentLayer, neutral_value, element, self.options.separateby]) #layer is existent. append items to this later layerNodeList.append([currentLayer, neutral_value, element, self.options.separateby]) #layer is existent. append items to this later
elif layer_name is None and self.options.put_unfiltered:
layer_name = 'without-' + self.options.separateby + '-in-style-attribute'
else: #if no style attribute in element and not a group else: #if no style attribute in element and not a group
if isinstance(element, inkex.Group) is False: if isinstance(element, inkex.Group) is False:
if self.options.show_info: if self.options.show_info:
inkex.utils.debug(element.get('id') + ' has no style attribute') self.msg(element.get('id') + ' has no style attribute')
if self.options.put_unfiltered: if self.options.put_unfiltered:
layer_name = 'without-style-attribute' layer_name = 'without-style-attribute'
currentLayer = self.findLayer(layer_name) currentLayer = self.findLayer(layer_name)
if currentLayer is None: #layer does not exist, so create a new one if currentLayer is None: #layer does not exist, so create a new one
layerNodeList.append([self.createLayer(layerNodeList, layer_name), None, element, None]) layerNodeList.append([self.createLayer(layerNodeList, layer_name), None, element, None])
else: else:
@ -235,11 +237,11 @@ class StylesToLayers(inkex.EffectExtension):
maxval = max(minmax_range) maxval = max(minmax_range)
sliceinterval = (maxval - minval) / self.options.subdividethreshold sliceinterval = (maxval - minval) / self.options.subdividethreshold
#inkex.utils.debug("neutral values (sorted) = " + str(minmax_range)) #self.msg("neutral values (sorted) = " + str(minmax_range))
#inkex.utils.debug("min neutral_value = " + str(minval)) #self.msg("min neutral_value = " + str(minval))
#inkex.utils.debug("max neutral_value = " + str(maxval)) #self.msg("max neutral_value = " + str(maxval))
#inkex.utils.debug("slice value (divide step size) = " + str(sliceinterval)) #self.msg("slice value (divide step size) = " + str(sliceinterval))
#inkex.utils.debug("subdivides (parent layers) = " + str(self.options.subdividethreshold)) #self.msg("subdivides (parent layers) = " + str(self.options.subdividethreshold))
for layerNode in layerNodeList: for layerNode in layerNodeList:
for x in range(0, self.options.subdividethreshold): #loop through the sorted neutral_values and determine to which layer they should belong for x in range(0, self.options.subdividethreshold): #loop through the sorted neutral_values and determine to which layer they should belong
@ -276,8 +278,8 @@ class StylesToLayers(inkex.EffectExtension):
#finally append the sublayers to the slices #finally append the sublayers to the slices
#for layer in topLevelLayerNodeList: #for layer in topLevelLayerNodeList:
#inkex.utils.debug(layer[0].get('inkscape:label')) #self.msg(layer[0].get('inkscape:label'))
#inkex.utils.debug(layer[1]) #self.msg(layer[1])
for newLayerNode in topLevelLayerNodeList: for newLayerNode in topLevelLayerNodeList:
newLayerNode[0].append(newLayerNode[1]) #append newlayer to layer newLayerNode[0].append(newLayerNode[1]) #append newlayer to layer
@ -286,7 +288,7 @@ class StylesToLayers(inkex.EffectExtension):
import cleangroups import cleangroups
cleangroups.CleanGroups.effect(self) cleangroups.CleanGroups.effect(self)
except: except:
inkex.utils.debug("Calling 'Remove Empty Groups' extension failed. Maybe the extension is not installed. You can download it from official InkScape Gallery. Skipping ...") self.msg("Calling 'Remove Empty Groups' extension failed. Maybe the extension is not installed. You can download it from official InkScape Gallery. Skipping ...")
if __name__ == '__main__': if __name__ == '__main__':
StylesToLayers().run() StylesToLayers().run()

View File

@ -30,7 +30,7 @@ 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: 02.04.2021 Date: 02.04.2021
Last patch: 08.04.2021 Last patch: 24.04.2021
License: GNU GPL v3 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. This piece of spaghetti-code, called "vpypetools", is a wrapper to pass (pipe) line elements from InkScape selection (or complete canvas) to vpype.
@ -180,7 +180,7 @@ class vpypetools (inkex.EffectExtension):
for csp in subPath: for csp in subPath:
if len(csp[1]) > 0: #we need exactly two points per straight line segment if len(csp[1]) > 0: #we need exactly two points per straight line segment
points.append(Point(round(csp[1][0], self.options.decimals), round(csp[1][1], self.options.decimals))) points.append(Point(round(csp[1][0], self.options.decimals), round(csp[1][1], self.options.decimals)))
if subPath[-1][0] == 'Z' or subPath[0][1] == subPath[-1][1]: #check if path is closed by Z or first pont == last point if len(subPath) > 2 and (subPath[-1][0] == 'Z' or subPath[0][1] == subPath[-1][1]): #check if path has more than 2 points and is closed by Z or first pont == last point
points.append(Point(round(subPath[0][1][0], self.options.decimals), round(subPath[0][1][1], self.options.decimals))) #if closed, we add the first point again points.append(Point(round(subPath[0][1][0], self.options.decimals), round(subPath[0][1][1], self.options.decimals))) #if closed, we add the first point again
lc.append(LineString(points)) lc.append(LineString(points))
@ -365,7 +365,7 @@ class vpypetools (inkex.EffectExtension):
# 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."
if self.options.strokes_to_paths is True: if self.options.strokes_to_paths is True:
cli_output = inkscape(output_file, "--with-gui", actions="EditSelectAllInAllLayers;EditUnlinkClone;ObjectToPath;FileSave;FileQuit") cli_output = inkscape(output_file, "--with-gui", actions="EditSelectAllInAllLayers;EditUnlinkClone;ObjectToPath;FileSave;FileQuit") #we do not use StrokeToPath because it will convert svg:line to a svg:path, but as closed path with four points
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)
@ -436,7 +436,7 @@ class vpypetools (inkex.EffectExtension):
if self.options.input_handling == "layers": if self.options.input_handling == "layers":
if self_viewBox is not None: if self_viewBox is not None:
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 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 if self.options.apply_transformations is True and applyTransformAvailable is True and self.options.strokes_to_paths is True: #we apply the transforms directly after adding them, but we need to have strokes_to_paths enabled!
applytransform.ApplyTransform().recursiveFuseTransform(element) applytransform.ApplyTransform().recursiveFuseTransform(element)
# Delete the temporary file again because we do not need it anymore # Delete the temporary file again because we do not need it anymore