added merge cut edges option to paperfold; some fix in tab generator
This commit is contained in:
parent
b00f3274b1
commit
9efad2f2cf
@ -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>
|
||||||
|
@ -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.")
|
||||||
|
@ -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 \
|
||||||
|
Reference in New Issue
Block a user