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="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="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">
<option value="regular">regular</option>
<option value="saturationsForAngles">saturations for angles</option>

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python3
import math
import inkex
from inkex import Transform, TextElement, Tspan, Color, Circle, PathElement, CubicSuperPath
import tempfile
import os
import random
@ -8,7 +9,7 @@ import numpy as np
import openmesh as om
import networkx as nx
from lxml import etree
from inkex import Transform, TextElement, Tspan, Color, Circle
import copy
"""
Extension for InkScape 1.0
@ -68,6 +69,8 @@ class Paperfold(inkex.EffectExtension):
# Check if two lines intersect
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])
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))
# Check if a point lies inside a triangle
def pointInTriangle(self, A, B, C, P, epsilon):
v0 = [C[0] - A[0], C[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
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[2], epsilon): return True
@ -115,6 +122,8 @@ class Paperfold(inkex.EffectExtension):
# Functions for visualisation and output
def addVisualisationData(self, mesh, unfoldedMesh, originalHalfedges, unfoldedHalfedges, glueNumber, dihedralAngles):
for i in range(3):
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))
# 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]))
# Colour depending on folding direction
lineStyle = {"fill": "none"}
@ -656,6 +665,56 @@ class Paperfold(inkex.EffectExtension):
text.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:
textFacesGroup.delete() #delete if empty set
@ -677,6 +736,90 @@ class Paperfold(inkex.EffectExtension):
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):
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("--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("--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("--separateGluePairsByColor", type=inkex.Boolean, default=False, help="Separate glue pairs by color")
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("--origamiSimulatorMode", type=inkex.Boolean, default=False, help="Enable origami simulator mode")
def effect(self):
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.")

View File

@ -531,8 +531,10 @@ class Tabgen(inkex.EffectExtension):
last_letter = 'Z'
savid = elem.get_id()
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
raw = elementPath.to_arrays()
if raw[-1][0] == 'Z' or \