added merge cut edges option to paperfold; some fix in tab generator

This commit is contained in:
Mario Voigt 2021-07-05 23:21:12 +02:00
parent b00f3274b1
commit 9efad2f2cf
3 changed files with 152 additions and 4 deletions

View File

@ -38,6 +38,7 @@
<param name="fontSize" type="int" min="1" max="100" gui-text="Label font size (%)">15</param> <param name="fontSize" type="int" min="1" max="100" gui-text="Label font size (%)">15</param>
<param name="flipLabels" type="bool" gui-text="Flip labels">false</param> <param name="flipLabels" type="bool" gui-text="Flip labels">false</param>
<param name="dashes" type="bool" gui-text="Dashes for cut/coplanar lines">true</param> <param name="dashes" type="bool" gui-text="Dashes for cut/coplanar lines">true</param>
<param name="merge_cut_lines" type="bool" gui-text="Merge cut lines" gui-description="This will merge all cut lines to a single path">true</param>
<param name="edgeStyle" type="optiongroup" appearance="combo" gui-text="Edge style"> <param name="edgeStyle" type="optiongroup" appearance="combo" gui-text="Edge style">
<option value="regular">regular</option> <option value="regular">regular</option>
<option value="saturationsForAngles">saturations for angles</option> <option value="saturationsForAngles">saturations for angles</option>

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import math import math
import inkex import inkex
from inkex import Transform, TextElement, Tspan, Color, Circle, PathElement, CubicSuperPath
import tempfile import tempfile
import os import os
import random import random
@ -8,7 +9,7 @@ import numpy as np
import openmesh as om import openmesh as om
import networkx as nx import networkx as nx
from lxml import etree from lxml import etree
from inkex import Transform, TextElement, Tspan, Color, Circle import copy
""" """
Extension for InkScape 1.0 Extension for InkScape 1.0
@ -68,6 +69,8 @@ class Paperfold(inkex.EffectExtension):
# Check if two lines intersect # Check if two lines intersect
def lineIntersection(self, v1, v2, v3, v4, epsilon): def lineIntersection(self, v1, v2, v3, v4, epsilon):
d = (v4[1] - v3[1]) * (v2[0] - v1[0]) - (v4[0] - v3[0]) * (v2[1] - v1[1]) d = (v4[1] - v3[1]) * (v2[0] - v1[0]) - (v4[0] - v3[0]) * (v2[1] - v1[1])
u = (v4[0] - v3[0]) * (v1[1] - v3[1]) - (v4[1] - v3[1]) * (v1[0] - v3[0]) u = (v4[0] - v3[0]) * (v1[1] - v3[1]) - (v4[1] - v3[1]) * (v1[0] - v3[0])
@ -77,6 +80,8 @@ class Paperfold(inkex.EffectExtension):
return ((0 + epsilon) <= u <= (d - epsilon)) and ((0 + epsilon) <= v <= (d - epsilon)) return ((0 + epsilon) <= u <= (d - epsilon)) and ((0 + epsilon) <= v <= (d - epsilon))
# Check if a point lies inside a triangle # Check if a point lies inside a triangle
def pointInTriangle(self, A, B, C, P, epsilon): def pointInTriangle(self, A, B, C, P, epsilon):
v0 = [C[0] - A[0], C[1] - A[1]] v0 = [C[0] - A[0], C[1] - A[1]]
v1 = [B[0] - A[0], B[1] - A[1]] v1 = [B[0] - A[0], B[1] - A[1]]
@ -91,6 +96,8 @@ class Paperfold(inkex.EffectExtension):
# Check if two triangles intersect # Check if two triangles intersect
def triangleIntersection(self, t1, t2, epsilon): def triangleIntersection(self, t1, t2, epsilon):
if self.lineIntersection(t1[0], t1[1], t2[0], t2[1], epsilon): return True if self.lineIntersection(t1[0], t1[1], t2[0], t2[1], epsilon): return True
if self.lineIntersection(t1[0], t1[1], t2[0], t2[2], epsilon): return True if self.lineIntersection(t1[0], t1[1], t2[0], t2[2], epsilon): return True
@ -115,6 +122,8 @@ class Paperfold(inkex.EffectExtension):
# Functions for visualisation and output # Functions for visualisation and output
def addVisualisationData(self, mesh, unfoldedMesh, originalHalfedges, unfoldedHalfedges, glueNumber, dihedralAngles): def addVisualisationData(self, mesh, unfoldedMesh, originalHalfedges, unfoldedHalfedges, glueNumber, dihedralAngles):
for i in range(3): for i in range(3):
dihedralAngles[unfoldedMesh.edge_handle(unfoldedHalfedges[i]).idx()] = round(math.degrees(mesh.calc_dihedral_angle(originalHalfedges[i])), self.options.roundingDigits) dihedralAngles[unfoldedMesh.edge_handle(unfoldedHalfedges[i]).idx()] = round(math.degrees(mesh.calc_dihedral_angle(originalHalfedges[i])), self.options.roundingDigits)
@ -549,7 +558,7 @@ class Paperfold(inkex.EffectExtension):
vertex1 = mesh.point(mesh.to_vertex_handle(he)) vertex1 = mesh.point(mesh.to_vertex_handle(he))
# Write a straight line between the two corners # Write a straight line between the two corners
line = edgesGroup.add(inkex.PathElement()) line = edgesGroup.add(PathElement())
line.set('d', "M {:0.6f},{:0.6f} {:0.6f},{:0.6f}".format(vertex0[0], vertex0[1], vertex1[0], vertex1[1])) line.set('d', "M {:0.6f},{:0.6f} {:0.6f},{:0.6f}".format(vertex0[0], vertex0[1], vertex1[0], vertex1[1]))
# Colour depending on folding direction # Colour depending on folding direction
lineStyle = {"fill": "none"} lineStyle = {"fill": "none"}
@ -656,6 +665,56 @@ class Paperfold(inkex.EffectExtension):
text.delete() text.delete()
tspan.delete() tspan.delete()
'''
merge cutting edges to single contour. code ripped off from "join path" extension
'''
if self.options.merge_cut_lines is True:
cutEdges = []
#find all cutting edges - they have to be sorted to build up a clean continuous line
for edge in edgesGroup:
edge_id = edge.get('id')
if "cut-edge-" in edge_id:
cutEdges.append(edge)
#find the cutting edge which starts at the previous cutting edge end point
paths = {p.get('id'): self.getPartsFromCubicSuper(CubicSuperPath(p.get('d'))) for p in cutEdges }
pathIds = [p.get('id') for p in cutEdges]
startPathId = pathIds[0]
pathIds = self.getArrangedIds(paths, startPathId)
newParts = []
firstElem = None
for key in pathIds:
parts = paths[key]
# ~ parts = getPartsFromCubicSuper(cspath)
start = parts[0][0][0]
elem = self.svg.getElementById(key)
if(len(newParts) == 0):
newParts += parts[:]
firstElem = elem
else:
if(self.vectCmpWithMargin(start, newParts[-1][-1][-1], margin = .01)):
newParts[-1] += parts[0]
else:
newSeg = [newParts[-1][-1][-1], newParts[-1][-1][-1], start, start]
newParts[-1].append(newSeg)
newParts[-1] += parts[0]
if(len(parts) > 1):
newParts += parts[1:]
parent = elem.getparent()
parent.remove(elem)
newElem = copy.copy(firstElem)
oldId = firstElem.get('id')
newElem.set('d', CubicSuperPath(self.getCubicSuperFromParts(newParts)))
newElem.set('id', oldId + '_joined')
parent.append(newElem) #insert at the end
if len(textFacesGroup) == 0: if len(textFacesGroup) == 0:
textFacesGroup.delete() #delete if empty set textFacesGroup.delete() #delete if empty set
@ -677,6 +736,90 @@ class Paperfold(inkex.EffectExtension):
return paperfoldPageGroup return paperfoldPageGroup
def floatCmpWithMargin(self, float1, float2, margin):
return abs(float1 - float2) < margin
def vectCmpWithMargin(self, vect1, vect2, margin):
return all(self.floatCmpWithMargin(vect2[i], vect1[i], margin) for i in range(0, len(vect1)))
def getPartsFromCubicSuper(self, cspath):
parts = []
for subpath in cspath:
part = []
prevBezPt = None
for i, bezierPt in enumerate(subpath):
if(prevBezPt != None):
seg = [prevBezPt[1], prevBezPt[2], bezierPt[0], bezierPt[1]]
part.append(seg)
prevBezPt = bezierPt
parts.append(part)
return parts
def getCubicSuperFromParts(self, parts):
cbsuper = []
for part in parts:
subpath = []
lastPt = None
pt = None
for seg in part:
if(pt == None):
ptLeft = seg[0]
pt = seg[0]
ptRight = seg[1]
subpath.append([ptLeft, pt, ptRight])
ptLeft = seg[2]
pt = seg[3]
subpath.append([ptLeft, pt, pt])
cbsuper.append(subpath)
return cbsuper
def getArrangedIds(self, pathMap, startPathId):
nextPathId = startPathId
orderPathIds = [nextPathId]
#Arrange in order
while(len(orderPathIds) < len(pathMap)):
minDist = 9e+100 #A large float
closestId = None
np = pathMap[nextPathId]
npPts = [np[-1][-1][-1]]
if(len(orderPathIds) == 1):#compare both the ends for the first path
npPts.append(np[0][0][0])
for key in pathMap:
if(key in orderPathIds):
continue
parts = pathMap[key]
start = parts[0][0][0]
end = parts[-1][-1][-1]
for i, npPt in enumerate(npPts):
dist = abs(start[0] - npPt[0]) + abs(start[1] - npPt[1])
if(dist < minDist):
minDist = dist
closestId = key
dist = abs(end[0] - npPt[0]) + abs(end[1] - npPt[1])
if(dist < minDist):
minDist = dist
pathMap[key] = [[[pts for pts in reversed(seg)] for seg in \
reversed(part)] for part in reversed(parts)]
closestId = key
#If start point of the first path is closer reverse its direction
if(i > 0 and closestId == key):
pathMap[nextPathId] = [[[pts for pts in reversed(seg)] for seg in \
reversed(part)] for part in reversed(np)]
orderPathIds.append(closestId)
nextPathId = closestId
return orderPathIds
def add_arguments(self, pars): def add_arguments(self, pars):
pars.add_argument("--tab") pars.add_argument("--tab")
@ -704,6 +847,7 @@ class Paperfold(inkex.EffectExtension):
pars.add_argument("--fontSize", type=int, default=15, help="Label font size (%)") pars.add_argument("--fontSize", type=int, default=15, help="Label font size (%)")
pars.add_argument("--flipLabels", type=inkex.Boolean, default=False, help="Flip labels") pars.add_argument("--flipLabels", type=inkex.Boolean, default=False, help="Flip labels")
pars.add_argument("--dashes", type=inkex.Boolean, default=True, help="Dashes for cut/coplanar edges") pars.add_argument("--dashes", type=inkex.Boolean, default=True, help="Dashes for cut/coplanar edges")
pars.add_argument("--merge_cut_lines", type=inkex.Boolean, default=True, help="Merge cut lines")
pars.add_argument("--edgeStyle", help="Adjust color saturation or opacity for folding edges. The larger the angle the darker the color") pars.add_argument("--edgeStyle", help="Adjust color saturation or opacity for folding edges. The larger the angle the darker the color")
pars.add_argument("--separateGluePairsByColor", type=inkex.Boolean, default=False, help="Separate glue pairs by color") pars.add_argument("--separateGluePairsByColor", type=inkex.Boolean, default=False, help="Separate glue pairs by color")
pars.add_argument("--colorCutEdges", type=Color, default='255', help="Cut edges") pars.add_argument("--colorCutEdges", type=Color, default='255', help="Cut edges")
@ -715,6 +859,7 @@ class Paperfold(inkex.EffectExtension):
pars.add_argument("--joineryMode", type=inkex.Boolean, default=False, help="Enable joinery mode") pars.add_argument("--joineryMode", type=inkex.Boolean, default=False, help="Enable joinery mode")
pars.add_argument("--origamiSimulatorMode", type=inkex.Boolean, default=False, help="Enable origami simulator mode") pars.add_argument("--origamiSimulatorMode", type=inkex.Boolean, default=False, help="Enable origami simulator mode")
def effect(self): def effect(self):
if not os.path.exists(self.options.inputfile): if not os.path.exists(self.options.inputfile):
inkex.utils.debug("The input file does not exist. Please select a proper file and try again.") inkex.utils.debug("The input file does not exist. Please select a proper file and try again.")

View File

@ -531,8 +531,10 @@ class Tabgen(inkex.EffectExtension):
last_letter = 'Z' last_letter = 'Z'
savid = elem.get_id() savid = elem.get_id()
idmod = 0 idmod = 0
elementPath = Path(elem.path.to_absolute().transform(elem.getparent().composed_transform())) parent = elem.getparent()
#if parent != self.svg.root:
# elem.path.transform = elem.path.transform(parent.composed_transform())
elementPath = elem.path.to_non_shorthand().to_absolute()
isClosed = False isClosed = False
raw = elementPath.to_arrays() raw = elementPath.to_arrays()
if raw[-1][0] == 'Z' or \ if raw[-1][0] == 'Z' or \