OCC based tag line generation by extruding the edges cutting through a tag and retreiving the common with the tag solid.

This commit is contained in:
Markus Lampert 2016-12-10 12:39:23 -08:00
parent 9eca75e673
commit b26a7ad794
4 changed files with 160 additions and 965 deletions

View File

@ -47,7 +47,7 @@ except AttributeError:
def translate(context, text, disambig=None):
return QtGui.QApplication.translate(context, text, disambig)
debugDressup = True
debugDressup = False
debugComponents = ['P0', 'P1', 'P2', 'P3']
def debugPrint(comp, msg):
@ -59,7 +59,7 @@ def debugEdge(edge, prefix, comp = None):
pl = edge.valueAt(edge.LastParameter)
if comp:
debugPrint(comp, "%s %s((%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" % (prefix, type(edge.Curve), pf.x, pf.y, pf.z, pl.x, pl.y, pl.z))
else:
elif debugDressup:
print("%s %s((%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" % (prefix, type(edge.Curve), pf.x, pf.y, pf.z, pl.x, pl.y, pl.z))
def debugMarker(vector, label, color = None, radius = 0.5):
@ -127,9 +127,6 @@ class Tag:
def top(self):
return self.z + self.actualHeight
def centerLine(self):
return Part.LineSegment(self.originAt(self.bottom() - 1), self.originAt(self.top() + 1))
def createSolidsAt(self, z):
self.z = z
r1 = self.width / 2
@ -160,288 +157,6 @@ class Tag:
if self.core:
self.core.translate(self.originAt(z))
class Intersection:
# An intersection with a tag has 4 markant points, where one might be optional.
#
# P1---P2 P1---P2 P2
# | | / \ /\
# | | / \ / \
# | | / \ / \
# ---P0 P3--- ---P0 P3--- ---P0 P3---
#
# If no intersection occured the Intersection can be viewed as being
# at P3 with no additional edges.
Pnone = 1
P0 = 2
P1 = 3
P2 = 4
P3 = 5
def __init__(self, tag):
self.tag = tag
self.state = self.Pnone
self.edges = []
self.tail = None
def isComplete(self):
return self.state == self.Pnone or self.state == self.P3
def hasEdges(self):
return self.state != self.Pnone
def moveEdgeToPlateau(self, edge):
if type(edge.Curve) is Part.Line or type(edge.Curve) is Part.LineSegment:
e = copy.copy(edge)
z = edge.valueAt(edge.FirstParameter).z
elif type(edge.Curve) is Part.Circle:
# it's an arc
e = copy.copy(edge)
z = edge.Curve.Center.z
else:
# it's a helix -> transform to arc
z = 0
p1 = PathGeom.xy(edge.valueAt(edge.FirstParameter))
p2 = PathGeom.xy(edge.valueAt((edge.FirstParameter + edge.LastParameter)/2))
p3 = PathGeom.xy(edge.valueAt(edge.LastParameter))
e = Part.Edge(Part.Arc(p1, p2, p3))
print("-------- moveEdgeToPlateau")
e.translate(Vector(0, 0, self.tag.top() - z))
return e
def intersectP0Core(self, edge):
debugPrint('P0', "----- P0-core")
i = self.tag.nextIntersectionClosestTo(edge, self.tag.core, edge.valueAt(edge.FirstParameter))
if i:
if PathGeom.pointsCoincide(i, edge.valueAt(edge.FirstParameter)):
# if P0 and P1 are the same, we need to insert a segment for the rise
debugPrint('P0', "------- insert vertical rise (%s)" % i)
self.edges.append(Part.Edge(Part.LineSegment(i, FreeCAD.Vector(i.x, i.y, self.tag.top()))))
self.p1 = i
self.state = self.P1
return edge
if PathGeom.pointsCoincide(i, edge.valueAt(edge.LastParameter)):
debugPrint('P0', "------- consumed (%s)" % i)
e = edge
tail = None
else:
debugPrint('P0', "------- split at (%s)" % i)
e, tail = PathGeom.splitEdgeAt(edge, i)
self.p1 = e.valueAt(edge.LastParameter)
self.edges.extend(self.tag.mapEdgeToSolid(e, 'P0-core-1'))
self.state = self.P1
return tail
# no intersection, the entire edge fits between P0 and P1
debugPrint('P0', "------- no intersection")
self.edges.extend(self.tag.mapEdgeToSolid(edge, 'P0-core-2'))
return None
def intersectP0(self, edge):
pf = edge.valueAt(edge.FirstParameter)
pl = edge.valueAt(edge.LastParameter)
debugPrint('P0', "----- P0 %s(%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" % (type(edge.Curve), pf.x, pf.y, pf.z, pl.x, pl.y, pl.z))
if self.tag.core:
return self.intersectP0Core(edge)
# if we have no core the tip is the origin of the Tag
line = Part.Edge(self.tag.centerLine())
debugEdge(line, "------- center line", 'P0')
if type(edge.Curve) != Part.Circle and type(edge.Curve) != Part.Line:
p1 = edge.valueAt(edge.FirstParameter)
p2 = edge.valueAt((edge.FirstParameter + edge.LastParameter)/2)
p3 = edge.valueAt(edge.LastParameter)
p1.z = 0
p2.z = 0
p3.z = 0
arc = Part.Edge(Part.Arc(p1, p2, p3))
aps = DraftGeomUtils.findIntersection(line, arc)
paramScale = (edge.LastParameter - edge.FirstParameter) / (arc.LastParameter - arc.FirstParameter)
for p in aps:
print("%s - p=%.2f" % (p, arc.Curve.parameter(p)))
i = [edge.valueAt(arc.Curve.parameter(p) * paramScale) for p in aps]
else:
i = DraftGeomUtils.findIntersection(line, edge)
#i = line.Curve.intersect(edge)
if i:
debugPrint('P0', '------- P0 split @ (%.2f, %.2f, %.2f)' % (i[0].x, i[0].y, i[0].z))
if PathGeom.pointsCoincide(i[0], edge.valueAt(edge.LastParameter)):
e = edge
tail = None
else:
e, tail = PathGeom.splitEdgeAt(edge, i[0])
self.state = self.P2 # P1 and P2 are identical for triangular tags
self.p1 = i[0]
self.p2 = i[0]
else:
debugPrint('P0', '------- P0 no intersect')
e = edge
tail = None
self.edges.extend(self.tag.mapEdgeToSolid(e, 'P0'))
return tail
def intersectP1(self, edge):
pf = edge.valueAt(edge.FirstParameter)
pl = edge.valueAt(edge.LastParameter)
debugPrint('P1', "----- P1 (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" % (pf.x, pf.y, pf.z, pl.x, pl.y, pl.z))
i = self.tag.nextIntersectionClosestTo(edge, self.tag.core, edge.valueAt(edge.LastParameter))
if i:
if PathGeom.pointsCoincide(i, edge.valueAt(edge.FirstParameter)):
debugPrint('P1', "----- P1 edge too short")
#self.edges.extend(self.tag.mapEdgeToSolid(edge, 'P1'))
self.edges.append(self.moveEdgeToPlateau(edge))
return None
if PathGeom.pointsCoincide(i, edge.valueAt(edge.LastParameter)):
debugPrint('P1', "----- P1 edge at end")
e = edge
tail = None
else:
debugPrint('P1', "----- P1 split edge @ (%.2f, %.2f, %.2f)" % (i.x, i.y, i.z))
e, tail = PathGeom.splitEdgeAt(edge, i)
f = e.valueAt(e.FirstParameter)
l = e.valueAt(e.LastParameter)
debugPrint('P1', "----- P1 (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f)" % (f.x, f.y, f.z, l.x, l.y, l.z))
self.p2 = e.valueAt(e.LastParameter)
self.state = self.P2
else:
debugPrint('P1', "----- P1 no intersect")
e = edge
tail = None
f = e.valueAt(e.FirstParameter)
l = e.valueAt(e.LastParameter)
debugPrint('P1', "----- P1 (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f)" % (f.x, f.y, f.z, l.x, l.y, l.z))
self.edges.append(self.moveEdgeToPlateau(e))
return tail
def intersectP2(self, edge):
pf = edge.valueAt(edge.FirstParameter)
pl = edge.valueAt(edge.LastParameter)
debugPrint('P2', "----- P2 (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" % (pf.x, pf.y, pf.z, pl.x, pl.y, pl.z))
i = self.tag.nextIntersectionClosestTo(edge, self.tag.solid, edge.valueAt(edge.LastParameter))
if i:
if PathGeom.pointsCoincide(i, edge.valueAt(edge.FirstParameter)):
debugPrint('P2', "------- insert exit plunge (%s)" % i)
self.edges.append(Part.Edge(Part.LineSegment(FreeCAD.Vector(i.x, i.y, self.tag.top()), i)))
e = None
tail = edge
elif PathGeom.pointsCoincide(i, edge.valueAt(edge.LastParameter)):
debugPrint('P2', "------- entire segment added (%s)" % i)
e = edge
tail = None
else:
e, tail = PathGeom.splitEdgeAt(edge, i)
if tail:
pf = tail.valueAt(tail.FirstParameter)
pl = tail.valueAt(tail.LastParameter)
debugPrint('P3', "----- P3 (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" % (pf.x, pf.y, pf.z, pl.x, pl.y, pl.z))
else:
debugPrint('P3', "----- P3 (---)")
self.state = self.P3
self.tail = tail
else:
debugPrint('P2', "----- P2 no intersection")
e = edge
tail = None
if e:
pf = e.valueAt(e.FirstParameter)
pl = e.valueAt(e.LastParameter)
s = 'P2' if self.state == self.P2 else 'P3'
debugPrint(s, "----- %s (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" % (s, pf.x, pf.y, pf.z, pl.x, pl.y, pl.z))
self.edges.extend(self.tag.mapEdgeToSolid(e, 'P2'))
return tail
def intersect(self, edge):
#print("")
#print(" >>> (%s - %s)" % (edge.valueAt(edge.FirstParameter), edge.valueAt(edge.LastParameter)))
if edge and self.state == self.P0:
edge = self.intersectP0(edge)
if edge and self.state == self.P1:
edge = self.intersectP1(edge)
if edge and self.state == self.P2:
edge = self.intersectP2(edge)
return self
def mapEdgeToSolid(self, edge, label):
pf = edge.valueAt(edge.FirstParameter)
pl = edge.valueAt(edge.LastParameter)
print("--------- mapEdgeToSolid-%s: %s((%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" % (label, type(edge.Curve), pf.x, pf.y, pf.z, pl.x, pl.y, pl.z))
p1a = edge.valueAt(edge.FirstParameter)
p1a.z = self.bottom()
p1b = FreeCAD.Vector(p1a.x, p1a.y, self.height * 1.01)
e1 = Part.Edge(Part.LineSegment(p1a, p1b))
p1 = self.nextIntersectionClosestTo(e1, self.solid, p1b) # top most intersection
print("---------- p1: (%s %s) -> %s %d" % (p1a, p1b, p1, self.solid.isInside(p1, 0.0000001, True)))
if not p1:
raise Exception('no p1')
return []
p2a = edge.valueAt(edge.LastParameter)
p2a.z = self.bottom()
p2b = FreeCAD.Vector(p2a.x, p2a.y, self.height * 1.01)
e2 = Part.Edge(Part.LineSegment(p2a, p2b))
p2 = self.nextIntersectionClosestTo(e2, self.solid, p2b) # top most intersection
if not p2:
p1 = edge.valueAt(edge.FirstParameter)
p2 = edge.valueAt(edge.LastParameter)
print("---------- p1: %d%d" % (self.solid.isInside(p1, 0.0000001, True), self.solid.isInside(p1, 0.0000001, False)))
print("---------- p2: %d%d" % (self.solid.isInside(p2, 0.0000001, True), self.solid.isInside(p2, 0.0000001, False)))
#if not self.solid.isInside(p1, 0.0000001, False):
# p1 is on the solid -
raise Exception('no p2')
return []
print("---------- %s - %s" % (p1, p2))
print("---------- (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f)" % (p2.x, p2.y, p2.z, p1.x, p1.y, p1.z))
if PathGeom.pointsCoincide(p1, p2):
return []
if type(edge.Curve) == Part.Line or type(edge.Curve) == Part.LineSegment:
e = Part.Edge(Part.LineSegment(p1, p2))
debugEdge(e, "-------- >>")
return [e]
m = FreeCAD.Matrix()
m.unity()
pd = p2 - p1
if type(edge.Curve) == Part.Circle:
m.A32 = pd.z / pd.y
m.A34 = - m.A32
if pd.z < 0:
m.A34 *= p2.y
else:
m.A34 *= p1.y
e = edge.transformGeometry(m).Edges[0]
debugEdge(e, "-------- >>")
return [e]
# it's already a helix, just need to lift it to the plateau
m.A33 = pd.z / (edge.valueAt(edge.LastParameter).z - edge.valueAt(edge.FirstParameter).z)
m.A34 = (1 - m.A33)
if pd.z < 0:
m.A34 *= edge.valueAt(edge.LastParameter).z
else:
m.A34 *= edge.valueAt(edge.FirstParameter).z
#print
pf = edge.valueAt(edge.FirstParameter)
pl = edge.valueAt(edge.LastParameter)
#print("(%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f): %.2f" % (pf.x, pf.y, pf.z, pl.x, pl.y, pl.z, m.A33))
#print("**** %.2f %.2f (%.2f - %.2f)" % (pd.z, p2a.z-p1a.z, p2a.z, p1a.z))
e = edge.transformGeometry(m).Edges[0]
pf = e.valueAt(e.FirstParameter)
pl = e.valueAt(e.LastParameter)
#print("(%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f)" % (pf.x, pf.y, pf.z, pl.x, pl.y, pl.z))
#raise Exception("mensch")
debugEdge(e, "-------- >>")
return [e]
def filterIntersections(self, pts, face):
if type(face.Surface) == Part.Cone or type(face.Surface) == Part.Cylinder:
#print("it's a cone/cylinder, checking z")
@ -451,7 +166,7 @@ class Tag:
c = face.Edges[0].Curve
if (type(c) == Part.Circle):
return filter(lambda pt: (pt - c.Center).Length <= c.Radius or PathGeom.isRoughly((pt - c.Center).Length, c.Radius), pts)
print("==== we got a %s" % face.Surface)
#print("==== we got a %s" % face.Surface)
def isPointOnEdge(self, pt, edge):
param = edge.Curve.parameter(pt)
@ -461,13 +176,11 @@ class Tag:
return True
if PathGeom.isRoughly(edge.FirstParameter, param) or PathGeom.isRoughly(edge.LastParameter, param):
return True
print("-------- X %.2f <= %.2f <=%.2f (%.2f, %.2f, %.2f) %.2f:%.2f" % (edge.FirstParameter, param, edge.LastParameter, pt.x, pt.y, pt.z, edge.Curve.parameter(edge.valueAt(edge.FirstParameter)), edge.Curve.parameter(edge.valueAt(edge.LastParameter))))
#print("-------- X %.2f <= %.2f <=%.2f (%.2f, %.2f, %.2f) %.2f:%.2f" % (edge.FirstParameter, param, edge.LastParameter, pt.x, pt.y, pt.z, edge.Curve.parameter(edge.valueAt(edge.FirstParameter)), edge.Curve.parameter(edge.valueAt(edge.LastParameter))))
p1 = edge.Vertexes[0]
f1 = edge.Curve.parameter(FreeCAD.Vector(p1.X, p1.Y, p1.Z))
p2 = edge.Vertexes[1]
f2 = edge.Curve.parameter(FreeCAD.Vector(p2.X, p2.Y, p2.Z))
print("-------- (%.2f, %.2f, %.2f):%.2f (%.2f, %.2f, %.2f):%.2f" % (p1.X, p1.Y, p1.Z, f1, p2.X, p2.Y, p2.Z, f2))
print("-------- %s %s" % (edge.Placement, edge.Orientation))
return False
@ -475,62 +188,144 @@ class Tag:
ef = edge.valueAt(edge.FirstParameter)
em = edge.valueAt((edge.FirstParameter+edge.LastParameter)/2)
el = edge.valueAt(edge.LastParameter)
print("-------- intersect %s (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f) refp=(%.2f, %.2f, %.2f)" % (type(edge.Curve), ef.x, ef.y, ef.z, em.x, em.y, em.z, el.x, el.y, el.z, refPt.x, refPt.y, refPt.z))
#print("-------- intersect %s (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f) refp=(%.2f, %.2f, %.2f)" % (type(edge.Curve), ef.x, ef.y, ef.z, em.x, em.y, em.z, el.x, el.y, el.z, refPt.x, refPt.y, refPt.z))
pts = []
for index, face in enumerate(solid.Faces):
i = edge.Curve.intersect(face.Surface)[0]
print i
#print i
ps = self.filterIntersections([FreeCAD.Vector(p.X, p.Y, p.Z) for p in i], face)
pts.extend(filter(lambda pt: self.isPointOnEdge(pt, edge), ps))
if len(ps) != len(filter(lambda pt: self.isPointOnEdge(pt, edge), ps)):
filtered = filter(lambda pt: self.isPointOnEdge(pt, edge), ps)
print("-------- ++ len(ps)=%d, len(filtered)=%d" % (len(ps), len(filtered)))
#print("-------- ++ len(ps)=%d, len(filtered)=%d" % (len(ps), len(filtered)))
for p in ps:
included = '+' if p in filtered else '-'
print("-------- %s (%.2f, %.2f, %.2f)" % (included, p.x, p.y, p.z))
#print("-------- %s (%.2f, %.2f, %.2f)" % (included, p.x, p.y, p.z))
if pts:
closest = sorted(pts, key=lambda pt: (pt - refPt).Length)[0]
for p in pts:
print("-------- - intersect pt : (%.2f, %.2f, %.2f)" % (p.x, p.y, p.z))
print("-------- -> (%.2f, %.2f, %.2f)" % (closest.x, closest.y, closest.z))
#for p in pts:
# print("-------- - intersect pt : (%.2f, %.2f, %.2f)" % (p.x, p.y, p.z))
#print("-------- -> (%.2f, %.2f, %.2f)" % (closest.x, closest.y, closest.z))
return closest
print("-------- -> None")
#print("-------- -> None")
return None
def intersect(self, edge, check = True):
print("--- intersect")
inters = self.Intersection(self)
if check:
if edge.valueAt(edge.FirstParameter).z < self.top() or edge.valueAt(edge.LastParameter).z < self.top():
i = self.nextIntersectionClosestTo(edge, self.solid, edge.valueAt(edge.FirstParameter))
if i:
print("---- (%.2f, %.2f, %.2f)" % (i.x, i.y, i.z))
inters.state = self.Intersection.P0
inters.p0 = i
if PathGeom.pointsCoincide(i, edge.valueAt(edge.LastParameter)):
print("---- entire edge consumed.")
inters.edges.append(edge)
return inters
if PathGeom.pointsCoincide(i, edge.valueAt(edge.FirstParameter)):
print("---- nothing of edge consumed.")
tail = edge
else:
print("---- split edge")
e,tail = PathGeom.splitEdgeAt(edge, i)
inters.edges.append(e)
return inters.intersect(tail)
else:
print("---- No intersection found.")
else:
print("---- Fly by")
def intersects(self, edge, param):
if edge.valueAt(edge.FirstParameter).z < self.top() or edge.valueAt(edge.LastParameter).z < self.top():
return self.nextIntersectionClosestTo(edge, self.solid, edge.valueAt(param))
return None
class MapWireToTag:
def __init__(self, edge, tag, i):
self.tag = tag
if PathGeom.pointsCoincide(edge.valueAt(edge.FirstParameter), i):
tail = edge
self.commands = []
elif PathGeom.pointsCoincide(edge.valueAt(edge.LastParameter), i):
debugEdge(edge, '++++++++ .')
self.commands = PathGeom.cmdsForEdge(edge)
tail = None
else:
print("---- skipped")
# if we get here there is no intersection with the tag
inters.state = self.Intersection.Pnone
inters.tail = edge
return inters
e, tail = PathGeom.splitEdgeAt(edge, i)
debugEdge(e, '++++++++ .')
self.commands = PathGeom.cmdsForEdge(e)
self.tail = tail
self.edges = []
self.entry = i
self.complete = False
self.wire = None
def addEdge(self, edge):
if self.wire:
self.wire.add(edge)
else:
self.wire = Part.Wire(edge)
def needToFlipEdge(self, edge, p):
if PathGeom.pointsCoincide(edge.valueAt(edge.LastParameter), p):
return True, edge.valueAt(edge.FirstParameter)
return False, edge.valueAt(edge.LastParameter)
def cleanupEdges(self, edges):
# first remove all internal struts
inputEdges = copy.copy(edges)
plinths = []
for e in edges:
p1 = e.valueAt(e.FirstParameter)
p2 = e.valueAt(e.LastParameter)
if p1.x == p2.x and p1.y == p2.y:
if not (PathGeom.edgeConnectsTo(e, self.entry) or PathGeom.edgeConnectsTo(e, self.exit)):
inputEdges.remove(e)
if p1.z > p2.z:
plinths.append(p2)
else:
plinths.append(p1)
# remove all edges that are connected to the plinths of the (former) internal struts
# including the edge that connects the entry and exit point directly
for e in copy.copy(inputEdges):
if PathGeom.edgeConnectsTo(e, self.entry) and PathGeom.edgeConnectsTo(e, self.exit):
inputEdges.remove(e)
continue
for p in plinths:
if PathGeom.edgeConnectsTo(e, p):
inputEdges.remove(e)
break
# the remaining edges form walk around the tag
# they need to be ordered and potentially flipped though
outputEdges = []
p = self.entry
lastP = p
while inputEdges:
for e in inputEdges:
p1 = e.valueAt(e.FirstParameter)
p2 = e.valueAt(e.LastParameter)
if PathGeom.pointsCoincide(p1, p):
outputEdges.append((e,False))
inputEdges.remove(e)
lastP = p
p = p2
debugEdge(e, ">>>>> no flip")
break
elif PathGeom.pointsCoincide(p2, p):
outputEdges.append((e,True))
inputEdges.remove(e)
lastP = p
p = p1
debugEdge(e, ">>>>> flip")
break
#else:
# debugEdge(e, "<<<<< (%.2f, %.2f, %.2f)" % (p.x, p.y, p.z))
if lastP == p:
raise ValueError("No connection to %s" % (p))
#else:
# print("xxxxxx (%.2f, %.2f, %.2f)" % (p.x, p.y, p.z))
return outputEdges
def add(self, edge):
self.tail = None
if self.tag.solid.isInside(edge.valueAt(edge.LastParameter), 0.000001, True):
self.addEdge(edge)
else:
i = self.tag.intersects(edge, edge.LastParameter)
if PathGeom.pointsCoincide(i, edge.valueAt(edge.FirstParameter)):
self.tail = edge
else:
e, tail = PathGeom.splitEdgeAt(edge, i)
self.addEdge(e)
self.tail = tail
self.exit = i
shell = self.wire.extrude(FreeCAD.Vector(0, 0, 10))
face = shell.common(self.tag.solid)
for e,flip in self.cleanupEdges(face.Edges):
debugEdge(e, '++++++++ %s' % ('.' if not flip else '@'))
self.commands.extend(PathGeom.cmdsForEdge(e, flip, False))
self.complete = True
def mappingComplete(self):
return self.complete
class PathData:
def __init__(self, obj):
@ -548,36 +343,11 @@ class PathData:
bottom = [e for e in edges if e.Vertexes[0].Point.z == minZ and e.Vertexes[1].Point.z == minZ]
wire = Part.Wire(bottom)
if wire.isClosed():
#return Part.Wire(self.sortedBase(bottom))
return wire
# if we get here there are already holding tags, or we're not looking at a profile
# let's try and insert the missing pieces - another day
raise ValueError("Selected path doesn't seem to be a Profile operation.")
def sortedBase(self, base):
# first find the exit point, where base wire is closed
edges = [e for e in self.edges if e.valueAt(e.FirstParameter).z == self.minZ and e.valueAt(e.LastParameter).z != self.maxZ]
exit = sorted(edges, key=lambda e: -e.valueAt(e.LastParameter).z)[0]
pt = exit.valueAt(exit.FirstParameter)
# then find the first base edge, and sort them until done
ordered = []
while base:
edges = [e for e in base if e.valueAt(e.FirstParameter) == pt]
if not edges:
print ordered
print base
print("(%.2f, %.2f, %.2f)" % (pt.x, pt.y, pt.z))
for e in base:
pf = e.valueAt(e.FirstParameter)
pl = e.valueAt(e.LastParameter)
print("(%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f)" % (pf.x, pf.y, pf.z, pl.x, pl.y, pl.z))
edge = edges[0]
ordered.append(edge)
base.remove(edge)
pt = edge.valueAt(edge.LastParameter)
return ordered
def findZLimits(self, edges):
# not considering arcs and spheres in Z direction, find the highes and lowest Z values
minZ = edges[0].Vertexes[0].Point.z
@ -721,14 +491,6 @@ class ObjectDressup:
return self.pathData.generateTags(obj, count, width, height, angle, spacing)
def tagIntersection(self, face, edge):
p1 = edge.valueAt(edge.FirstParameter)
pts = edge.Curve.intersect(face.Surface)
if pts[0]:
closest = sorted(pts[0], key=lambda pt: (pt - p1).Length)[0]
return closest
return None
def createPath(self, edges, tags):
commands = []
lastEdge = 0
@ -738,36 +500,39 @@ class ObjectDressup:
inters = None
edge = None
mapper = None
while edge or lastEdge < len(edges):
print("------- lastEdge = %d/%d.%d/%d" % (lastEdge, lastTag, t, len(tags)))
#print("------- lastEdge = %d/%d.%d/%d" % (lastEdge, lastTag, t, len(tags)))
if not edge:
edge = edges[lastEdge]
debugEdge(edge, "======= new edge: %d/%d" % (lastEdge, len(edges)))
lastEdge += 1
sameTag = None
if inters:
inters = inters.intersect(edge)
else:
if mapper:
mapper.add(edge)
if mapper.mappingComplete():
commands.extend(mapper.commands)
edge = mapper.tail
mapper = None
else:
edge = None
if edge:
tIndex = (t + lastTag) % len(tags)
t += 1
print("<<<<< lastTag=%d, t=%d, tIndex=%d, sameTag=%s >>>>>>" % (lastTag, t, tIndex, sameTag))
inters = tags[tIndex].intersect(edge, True or tIndex != sameTag)
edge = inters.tail
i = tags[tIndex].intersects(edge, edge.FirstParameter)
if i:
mapper = MapWireToTag(edge, tags[tIndex], i)
edge = mapper.tail
if inters.isComplete():
if inters.hasEdges():
sameTag = (t + lastTag - 1) % len(tags)
lastTag = sameTag
t = 1
for e in inters.edges:
commands.append(PathGeom.cmdForEdge(e))
inters = None
if t >= len(tags):
if not mapper and t >= len(tags):
# gone through all tags, consume edge and move on
if edge:
commands.append(PathGeom.cmdForEdge(edge))
debugEdge(edge, '++++++++')
commands.extend(PathGeom.cmdsForEdge(edge))
edge = None
t = 0

View File

@ -130,6 +130,14 @@ class PathGeom:
@classmethod
def cmdsForEdge(cls, edge, flip = False, useHelixForBSpline = True):
"""(edge, flip = False, useHelixForBSpline = True) -> List(Path.Command)
Returns a list of Path.Command representing the given edge.
If flip is True the edge is considered to be backwards.
If useHelixForBSpline is True an Edge based on a BSplineCurve is considered
to represent a helix and results in G2 or G3 command. Otherwise edge has
no direct Path.Command mapping and will be approximated by straight segments.
Approximation is also the approach for edges that are neither straight lines
nor arcs (nor helixes)."""
pt = edge.valueAt(edge.LastParameter) if not flip else edge.valueAt(edge.FirstParameter)
params = {'X': pt.x, 'Y': pt.y, 'Z': pt.z}
if type(edge.Curve) == Part.Line or type(edge.Curve) == Part.LineSegment:
@ -147,7 +155,7 @@ class PathGeom:
cmd = 'G3'
else:
cmd = 'G2'
print("**** (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f)" % (p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, p3.x, p3.y, p3.z))
#print("**** (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f)" % (p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, p3.x, p3.y, p3.z))
pd = Part.Circle(PathGeom.xy(p1), PathGeom.xy(p2), PathGeom.xy(p3)).Center
pa = PathGeom.xy(p1)
@ -168,7 +176,7 @@ class PathGeom:
# at this point pixellation is all we can do
commands = []
segments = int(math.ceil((deviation / eStraight.Length) * 1000))
print("**** pixellation with %d segments" % segments)
#print("**** pixellation with %d segments" % segments)
dParameter = (edge.LastParameter - edge.FirstParameter) / segments
for i in range(0, segments):
if flip:
@ -176,7 +184,7 @@ class PathGeom:
else:
p = edge.valueAt(edge.FirstParameter + (i + 1) * dParameter)
cmd = Path.Command('G1', {'X': p.x, 'Y': p.y, 'Z': p.z})
print("***** %s" % cmd)
#print("***** %s" % cmd)
commands.append(cmd)
#print commands
return commands
@ -280,8 +288,8 @@ class PathGeom:
params.update({'Z': z1, 'K': (z1 - z0)/2})
command = Path.Command(cmd.Name, params)
print("- (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f): %.2f:%.2f" % (edge.Vertexes[0].X, edge.Vertexes[0].Y, edge.Vertexes[0].Z, edge.Vertexes[1].X, edge.Vertexes[1].Y, edge.Vertexes[1].Z, z0, z1))
print("- %s -> %s" % (cmd, command))
#print("- (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f): %.2f:%.2f" % (edge.Vertexes[0].X, edge.Vertexes[0].Y, edge.Vertexes[0].Z, edge.Vertexes[1].X, edge.Vertexes[1].Y, edge.Vertexes[1].Z, z0, z1))
#print("- %s -> %s" % (cmd, command))
return cls.edgeForCmd(command, FreeCAD.Vector(p1.x, p1.y, z0))

View File

@ -33,7 +33,7 @@ from FreeCAD import Vector
from PathScripts.PathDressupHoldingTags import *
from PathTests.PathTestUtils import PathTestBase
class TestTag01BasicTag(PathTestBase): # =============
class TestHoldingTags(PathTestBase):
"""Unit tests for the HoldingTags dressup."""
def test00(self):
@ -89,577 +89,3 @@ class TestTag01BasicTag(PathTestBase): # =============
self.assertIsNone(tag.core)
class TestTag02SquareTag(PathTestBase): # =============
"""Unit tests for square tags."""
def test00(self):
"""Verify no intersection."""
tag = Tag( 0, 0, 4, 7, 90, True, 0)
pt1 = Vector(+5, 5, 0)
pt2 = Vector(-5, 5, 0)
edge = Part.Edge(Part.LineSegment(pt1, pt2))
i = tag.intersect(edge)
self.assertIsNotNone(i)
self.assertTrue(i.isComplete())
self.assertIsNotNone(i.edges)
self.assertFalse(i.edges)
self.assertLine(i.tail, pt1, pt2)
def test01(self):
"""Verify intersection of square tag with line ending at tag start."""
tag = Tag( 0, 0, 8, 3, 90, True, 0)
edge = Part.Edge(Part.LineSegment(Vector(5, 0, 0), Vector(4, 0, 0)))
i = tag.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P0)
self.assertEqual(len(i.edges), 1)
self.assertLine(i.edges[0], edge.valueAt(edge.FirstParameter), edge.valueAt(edge.LastParameter))
self.assertIsNone(i.tail)
def test02(self):
"""Verify intersection of square tag with line ending between P1 and P2."""
tag = Tag( 0, 0, 8, 3, 90, True, 0)
edge = Part.Edge(Part.LineSegment(Vector(5, 0, 0), Vector(1, 0, 0)))
i = tag.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P1)
self.assertEqual(len(i.edges), 3)
p1 = Vector(4, 0, 0)
p2 = Vector(4, 0, 3)
p3 = Vector(1, 0, 3)
self.assertLine(i.edges[0], edge.valueAt(edge.FirstParameter), p1)
self.assertLine(i.edges[1], p1, p2)
self.assertLine(i.edges[2], p2, p3)
self.assertIsNone(i.tail)
# verify we stay in P1 if we add another segment
edge = Part.Edge(Part.LineSegment(edge.valueAt(edge.LastParameter), Vector(0, 0, 0)))
i = i.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P1)
self.assertEqual(len(i.edges), 4)
p4 = Vector(0, 0, 3)
self.assertLine(i.edges[3], p3, p4)
self.assertIsNone(i.tail)
def test03(self):
"""Verify intesection of square tag with line ending on P2."""
tag = Tag( 0, 0, 8, 3, 90, True, 0)
edge = Part.Edge(Part.LineSegment(Vector(5, 0, 0), Vector(-4, 0, 0)))
i = tag.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P2)
self.assertEqual(len(i.edges), 3)
p0 = edge.valueAt(edge.FirstParameter)
p1 = Vector( 4, 0, 0)
p2 = Vector( 4, 0, 3)
p3 = Vector(-4, 0, 3)
self.assertLine(i.edges[0], p0, p1)
self.assertLine(i.edges[1], p1, p2)
self.assertLine(i.edges[2], p2, p3)
self.assertIsNone(i.tail)
# make sure it also works if we get there not directly
edge = Part.Edge(Part.LineSegment(Vector(5, 0, 0), Vector(0, 0, 0)))
i = tag.intersect(edge)
edge = Part.Edge(Part.LineSegment(edge.valueAt(edge.LastParameter), Vector(-4, 0, 0)))
i = i.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P2)
self.assertEqual(len(i.edges), 4)
p2a = Vector( 0, 0, 3)
self.assertLine(i.edges[0], p0, p1)
self.assertLine(i.edges[1], p1, p2)
self.assertLine(i.edges[2], p2, p2a)
self.assertLine(i.edges[3], p2a, p3)
self.assertIsNone(i.tail)
def test04(self):
"""Verify plunge down is inserted for square tag on exit."""
tag = Tag( 0, 0, 8, 3, 90, True, 0)
edge = Part.Edge(Part.LineSegment(Vector(5, 0, 0), Vector(-5, 0, 0)))
i = tag.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P3)
self.assertTrue(i.isComplete())
self.assertEqual(len(i.edges), 4)
p0 = edge.valueAt(edge.FirstParameter)
p1 = Vector( 4, 0, 0)
p2 = Vector( 4, 0, 3)
p3 = Vector(-4, 0, 3)
p4 = Vector(-4, 0, 0)
p5 = edge.valueAt(edge.LastParameter)
self.assertLine(i.edges[0], p0, p1)
self.assertLine(i.edges[1], p1, p2)
self.assertLine(i.edges[2], p2, p3)
self.assertLine(i.edges[3], p3, p4)
self.assertIsNotNone(i.tail)
self.assertLine(i.tail, p4, p5)
def test05(self):
"""Verify all lines between P0 and P3 are added."""
tag = Tag( 0, 0, 4, 7, 90, True, 0)
e0 = Part.Edge(Part.LineSegment(Vector(5, 0, 0), Vector(+2, 0, 0)))
e1 = Part.Edge(Part.LineSegment(e0.valueAt(e0.LastParameter), Vector(+1, 0, 0)))
e2 = Part.Edge(Part.LineSegment(e1.valueAt(e1.LastParameter), Vector(+0.5, 0, 0)))
e3 = Part.Edge(Part.LineSegment(e2.valueAt(e2.LastParameter), Vector(-0.5, 0, 0)))
e4 = Part.Edge(Part.LineSegment(e3.valueAt(e3.LastParameter), Vector(-1, 0, 0)))
e5 = Part.Edge(Part.LineSegment(e4.valueAt(e4.LastParameter), Vector(-2, 0, 0)))
e6 = Part.Edge(Part.LineSegment(e5.valueAt(e5.LastParameter), Vector(-5, 0, 0)))
i = tag
for e in [e0, e1, e2, e3, e4, e5]:
i = i.intersect(e)
self.assertFalse(i.isComplete())
i = i.intersect(e6)
self.assertTrue(i.isComplete())
pt0 = Vector(2, 0, 0)
pt1 = Vector(2, 0, 7)
pt2 = Vector(1, 0, 7)
pt3 = Vector(0.5, 0, 7)
pt4 = Vector(-0.5, 0, 7)
pt5 = Vector(-1, 0, 7)
pt6 = Vector(-2, 0, 7)
self.assertEqual(len(i.edges), 8)
self.assertLines(i.edges, i.tail, [e0.valueAt(e0.FirstParameter), pt0, pt1, pt2, pt3, pt4, pt5, pt6, e6.valueAt(e6.FirstParameter), e6.valueAt(e6.LastParameter)])
self.assertIsNotNone(i.tail)
def test06(self):
"""Verify intersection of different z levels."""
tag = Tag( 0, 0, 4, 7, 90, True, 0)
# for all lines below 7 we get the trapezoid
for i in range(0, 7):
p0 = Vector(5, 0, i)
p1 = Vector(2, 0, i)
p2 = Vector(2, 0, 7)
p3 = Vector(-2, 0, 7)
p4 = Vector(-2, 0, i)
p5 = Vector(-5, 0, i)
edge = Part.Edge(Part.LineSegment(p0, p5))
s = tag.intersect(edge)
self.assertTrue(s.isComplete())
self.assertLines(s.edges, s.tail, [p0, p1, p2, p3, p4, p5])
# for all edges at height or above the original line is used
for i in range(7, 9):
edge = Part.Edge(Part.LineSegment(Vector(5, 0, i), Vector(-5, 0, i)))
s = tag.intersect(edge)
self.assertTrue(s.isComplete())
self.assertLine(s.tail, edge.valueAt(edge.FirstParameter), edge.valueAt(edge.LastParameter))
def test10(self):
"""Verify intersection of square tag with an arc."""
tag = Tag( 0, 0, 8, 3, 90, True, 0)
p1 = Vector(10, -10, 0)
p2 = Vector(10, +10, 0)
edge = PathGeom.edgeForCmd(Path.Command('G2', {'X': p2.x, 'Y': p2.y, 'Z': p2.z, 'J': 10}), p1)
pi = Vector(0.8, -3.919184, 0)
pj = Vector(0.8, +3.919184, 0)
s = tag.intersect(edge)
self.assertTrue(s.isComplete())
self.assertEqual(len(s.edges), 4)
self.assertCurve(s.edges[0], p1, Vector(4.486010, -8.342417, 0), pi)
self.assertLine(s.edges[1], pi, pi + Vector(0, 0, 3))
self.assertCurve(s.edges[2], pi + Vector(0, 0, 3), Vector(0, 0, 3), pj + Vector(0, 0, 3))
self.assertLine(s.edges[3], pj + Vector(0, 0, 3), pj)
self.assertCurve(s.tail, pj, Vector(4.486010, +8.342417, 0), p2)
def test20(self):
"""Verify intersection of square tag with a helix."""
tag = Tag( 0, 0, 8, 3, 90, True, 0)
p1 = Vector(10, -10, 0)
p2 = Vector(10, +10, 2)
edge = PathGeom.edgeForCmd(Path.Command('G2', {'X': p2.x, 'Y': p2.y, 'Z': p2.z, 'J': 10, 'K': 1}), p1)
pi = Vector(0.8, -3.919184, 0.743623)
pj = Vector(0.8, +3.919184, 1.256377)
s = tag.intersect(edge)
self.assertTrue(s.isComplete())
self.assertEqual(len(s.edges), 4)
self.assertCurve(s.edges[0], p1, Vector(4.486010, -8.342417, 0.371812), pi)
self.assertLine(s.edges[1], pi, pi + Vector(0, 0, 3-pi.z))
self.assertCurve(s.edges[2], pi + Vector(0, 0, 3-pi.z), Vector(0, 0, 3), pj + Vector(0, 0, 3-pj.z))
self.assertLine(s.edges[3], pj + Vector(0, 0, 3-pj.z), pj)
self.assertCurve(s.tail, pj, Vector(4.486010, +8.342417, 1.628188), p2)
class TestTag03TrapezoidTag(PathTestBase): # =============
"""Unit tests for trapezoid tags."""
def test00(self):
"""Verify no intersection."""
tag = Tag( 0, 0, 8, 3, 45, True, 0)
pt1 = Vector(+5, 5, 0)
pt2 = Vector(-5, 5, 0)
edge = Part.Edge(Part.LineSegment(pt1, pt2))
i = tag.intersect(edge)
self.assertIsNotNone(i)
self.assertTrue(i.isComplete())
self.assertIsNotNone(i.edges)
self.assertFalse(i.edges)
self.assertLine(i.tail, pt1, pt2)
def test01(self):
"""Veify intersection of trapezoid tag with line ending before P1."""
tag = Tag( 0, 0, 8, 3, 45, True, 0)
edge = Part.Edge(Part.LineSegment(Vector(5, 0, 0), Vector(4, 0, 0)))
i = tag.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P0)
self.assertEqual(len(i.edges), 1)
self.assertLine(i.edges[0], edge.valueAt(edge.FirstParameter), edge.valueAt(edge.LastParameter))
self.assertIsNone(i.tail)
# now add another segment that doesn't reach the top of the cone
edge = Part.Edge(Part.LineSegment(edge.valueAt(edge.LastParameter), Vector(3, 0, 0)))
i = i.intersect(edge)
# still a P0 and edge fully consumed
p1 = Vector(edge.valueAt(edge.FirstParameter))
p1.z = 0
p2 = Vector(edge.valueAt(edge.LastParameter))
p2.z = 1 # height of cone @ (3,0)
self.assertEqual(i.state, Tag.Intersection.P0)
self.assertEqual(len(i.edges), 2)
self.assertLine(i.edges[1], p1, p2)
self.assertIsNone(i.tail)
# add another segment to verify starting point offset
edge = Part.Edge(Part.LineSegment(edge.valueAt(edge.LastParameter), Vector(2, 0, 0)))
i = i.intersect(edge)
# still a P0 and edge fully consumed
p3 = Vector(edge.valueAt(edge.LastParameter))
p3.z = 2 # height of cone @ (2,0)
self.assertEqual(i.state, Tag.Intersection.P0)
self.assertEqual(len(i.edges), 3)
self.assertLine(i.edges[2], p2, p3)
self.assertIsNone(i.tail)
def test02(self):
"""Verify intersection of trapezoid tag with line ending between P1 and P2"""
tag = Tag( 0, 0, 8, 3, 45, True, 0)
edge = Part.Edge(Part.LineSegment(Vector(5, 0, 0), Vector(1, 0, 0)))
i = tag.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P1)
self.assertEqual(len(i.edges), 2)
p1 = Vector(4, 0, 0)
p2 = Vector(1, 0, 3)
self.assertLine(i.edges[0], edge.valueAt(edge.FirstParameter), p1)
self.assertLine(i.edges[1], p1, p2)
self.assertIsNone(i.tail)
# verify we stay in P1 if we add another segment
edge = Part.Edge(Part.LineSegment(edge.valueAt(edge.LastParameter), Vector(0, 0, 0)))
i = i.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P1)
self.assertEqual(len(i.edges), 3)
p3 = Vector(0, 0, 3)
self.assertLine(i.edges[2], p2, p3)
self.assertIsNone(i.tail)
def test03(self):
"""Verify intersection of trapezoid tag with edge ending on P2."""
tag = Tag( 0, 0, 8, 3, 45, True, 0)
edge = Part.Edge(Part.LineSegment(Vector(5, 0, 0), Vector(-1, 0, 0)))
i = tag.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P2)
p0 = Vector(edge.valueAt(edge.FirstParameter))
p1 = Vector(4, 0, 0)
p2 = Vector(1, 0, 3)
p3 = Vector(-1, 0, 3)
self.assertLines(i.edges, i.tail, [p0, p1, p2, p3])
self.assertIsNone(i.tail)
# make sure we get the same result if there's another edge
edge = Part.Edge(Part.LineSegment(Vector(5, 0, 0), Vector(1, 0, 0)))
i = tag.intersect(edge)
edge = Part.Edge(Part.LineSegment(edge.valueAt(edge.LastParameter), Vector(-1, 0, 0)))
i = i.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P2)
self.assertLines(i.edges, i.tail, [p0, p1, p2, p3])
self.assertIsNone(i.tail)
# and also if the last segment doesn't cross the entire plateau
edge = Part.Edge(Part.LineSegment(Vector(5, 0, 0), Vector(0.5, 0, 0)))
i = tag.intersect(edge)
edge = Part.Edge(Part.LineSegment(edge.valueAt(edge.LastParameter), Vector(-1, 0, 0)))
i = i.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P2)
p2a = Vector(0.5, 0, 3)
self.assertLines(i.edges, i.tail, [p0, p1, p2, p2a, p3])
self.assertIsNone(i.tail)
def test04(self):
"""Verify proper down plunge on trapezoid tag exit."""
tag = Tag( 0, 0, 8, 3, 45, True, 0)
edge = Part.Edge(Part.LineSegment(Vector(5, 0, 0), Vector(-2, 0, 0)))
i = tag.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P2)
p0 = Vector(5, 0, 0)
p1 = Vector(4, 0, 0)
p2 = Vector(1, 0, 3)
p3 = Vector(-1, 0, 3)
p4 = Vector(-2, 0, 2)
self.assertLines(i.edges, i.tail, [p0, p1, p2, p3, p4])
self.assertIsNone(i.tail)
# make sure adding another segment doesn't change the state
edge = Part.Edge(Part.LineSegment(edge.valueAt(edge.LastParameter), Vector(-3, 0, 0)))
i = i.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P2)
self.assertEqual(len(i.edges), 5)
p5 = Vector(-3, 0, 1)
self.assertLine(i.edges[4], p4, p5)
self.assertIsNone(i.tail)
# now if we complete to P3 ....
edge = Part.Edge(Part.LineSegment(edge.valueAt(edge.LastParameter), Vector(-4, 0, 0)))
i = i.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P3)
self.assertTrue(i.isComplete())
self.assertEqual(len(i.edges), 6)
p6 = Vector(-4, 0, 0)
self.assertLine(i.edges[5], p5, p6)
self.assertIsNone(i.tail)
# verify proper operation if there is a single edge going through all
edge = Part.Edge(Part.LineSegment(Vector(5, 0, 0), Vector(-4, 0, 0)))
i = tag.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P3)
self.assertTrue(i.isComplete())
self.assertLines(i.edges, i.tail, [p0, p1, p2, p3, p6])
self.assertIsNone(i.tail)
# verify tail is added as well
edge = Part.Edge(Part.LineSegment(Vector(5, 0, 0), Vector(-5, 0, 0)))
i = tag.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P3)
self.assertTrue(i.isComplete())
self.assertLines(i.edges, i.tail, [p0, p1, p2, p3, p6, edge.valueAt(edge.LastParameter)])
self.assertIsNotNone(i.tail)
def test05(self):
"""Verify all lines between P0 and P3 are added."""
tag = Tag( 0, 0, 8, 3, 45, True, 0)
e0 = Part.Edge(Part.LineSegment(Vector(5, 0, 0), Vector(+4, 0, 0)))
e1 = Part.Edge(Part.LineSegment(e0.valueAt(e0.LastParameter), Vector(+2, 0, 0)))
e2 = Part.Edge(Part.LineSegment(e1.valueAt(e1.LastParameter), Vector(+0.5, 0, 0)))
e3 = Part.Edge(Part.LineSegment(e2.valueAt(e2.LastParameter), Vector(-0.5, 0, 0)))
e4 = Part.Edge(Part.LineSegment(e3.valueAt(e3.LastParameter), Vector(-1, 0, 0)))
e5 = Part.Edge(Part.LineSegment(e4.valueAt(e4.LastParameter), Vector(-2, 0, 0)))
e6 = Part.Edge(Part.LineSegment(e5.valueAt(e5.LastParameter), Vector(-5, 0, 0)))
i = tag
for e in [e0, e1, e2, e3, e4, e5]:
i = i.intersect(e)
self.assertFalse(i.isComplete())
i = i.intersect(e6)
self.assertTrue(i.isComplete())
p0 = Vector(4, 0, 0)
p1 = Vector(2, 0, 2)
p2 = Vector(1, 0, 3)
p3 = Vector(0.5, 0, 3)
p4 = Vector(-0.5, 0, 3)
p5 = Vector(-1, 0, 3)
p6 = Vector(-2, 0, 2)
p7 = Vector(-4, 0, 0)
self.assertLines(i.edges, i.tail, [e0.valueAt(e0.FirstParameter), p0, p1, p2, p3, p4, p5, p6, p7, e6.valueAt(e6.LastParameter)])
self.assertIsNotNone(i.tail)
def test06(self):
"""Verify intersection for different z levels."""
tag = Tag( 0, 0, 8, 3, 45, True, 0)
# for all lines below 3 we get the trapezoid
for i in range(0, 3):
p0 = Vector(5, 0, i)
p1 = Vector(4-i, 0, i)
p2 = Vector(1, 0, 3)
p3 = Vector(-1, 0, 3)
p4 = Vector(-4+i, 0, i)
p5 = Vector(-5, 0, i)
edge = Part.Edge(Part.LineSegment(p0, p5))
s = tag.intersect(edge)
self.assertTrue(s.isComplete())
self.assertLines(s.edges, s.tail, [p0, p1, p2, p3, p4, p5])
# for all edges at height or above the original line is used
for i in range(3, 5):
edge = Part.Edge(Part.LineSegment(Vector(5, 0, i), Vector(-5, 0, i)))
s = tag.intersect(edge)
self.assertTrue(s.isComplete())
self.assertLine(s.tail, edge.valueAt(edge.FirstParameter), edge.valueAt(edge.LastParameter))
def test10(self):
"""Verify intersection with an arc."""
tag = Tag( 0, 0, 8, 3, 45, True, 0)
p1 = Vector(10, -10, 0)
p2 = Vector(10, +10, 0)
edge = PathGeom.edgeForCmd(Path.Command('G2', {'X': p2.x, 'Y': p2.y, 'Z': p2.z, 'J': 10}), p1)
pi = Vector(0.8, -3.919184, 0)
pj = Vector(0.05, -0.998749, 3)
pk = Vector(0.05, +0.998749, 3)
pl = Vector(0.8, +3.919184, 0)
s = tag.intersect(edge)
self.assertTrue(s.isComplete())
self.assertEqual(len(s.edges), 4)
self.assertCurve(s.edges[0], p1, Vector(4.486010, -8.342417, 0), pi)
self.assertCurve(s.edges[1], pi, Vector(0.314296, -2.487396, 1.470795), pj)
self.assertCurve(s.edges[2], pj, Vector(0, 0, 3), pk)
self.assertCurve(s.edges[3], pk, Vector(.3142960, +2.487396, 1.470795), pl)
self.assertCurve(s.tail, pl, Vector(4.486010, +8.342417, 0), p2)
def test20(self):
"""Verify intersection with a helix."""
tag = Tag( 0, 0, 8, 3, 45, True, 0)
p1 = Vector(10, -10, 0)
p2 = Vector(10, +10, 2)
edge = PathGeom.edgeForCmd(Path.Command('G2', {'X': p2.x, 'Y': p2.y, 'Z': p2.z, 'J': 10, 'K': 1}), p1)
pi = Vector(0.513574, -3.163498, 0.795085)
pj = Vector(0.050001, -0.998749, 3)
pk = Vector(0.050001, +0.998749, 3)
pl = Vector(0.397586, +2.791711, 1.180119)
s = tag.intersect(edge)
self.assertTrue(s.isComplete())
self.assertEqual(len(s.edges), 4)
self.assertCurve(s.edges[0], p1, Vector(4.153420, -8.112798, 0.397543), pi)
self.assertCurve(s.edges[1], pi, Vector(0.221698, -2.093992, 1.897543), pj)
self.assertCurve(s.edges[2], pj, Vector(0, 0, 3), pk)
self.assertCurve(s.edges[3], pk, Vector(0.182776, 1.903182, 2.090060), pl)
self.assertCurve(s.tail, pl, Vector(3.996548, +7.997409, 1.590060), p2)
class TestTag04TriangularTag(PathTestBase): # ========================
"""Unit tests for tags that take on a triangular shape."""
def test00(self):
"""Verify intersection of triangular tag with line ending at tag start."""
tag = Tag( 0, 0, 8, 7, 45, True, 0)
edge = Part.Edge(Part.LineSegment(Vector(5, 0, 0), Vector(4, 0, 0)))
i = tag.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P0)
self.assertEqual(len(i.edges), 1)
self.assertLine(i.edges[0], edge.valueAt(edge.FirstParameter), edge.valueAt(edge.LastParameter))
self.assertIsNone(i.tail)
def test01(self):
"""Verify intersection of triangular tag with line ending between P0 and P1."""
tag = Tag( 0, 0, 8, 7, 45, True, 0)
edge = Part.Edge(Part.LineSegment(Vector(5, 0, 0), Vector(3, 0, 0)))
i = tag.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P0)
p1 = Vector(4, 0, 0)
p2 = Vector(3, 0, 1)
self.assertLines(i.edges, i.tail, [edge.valueAt(edge.FirstParameter), p1, p2])
self.assertIsNone(i.tail)
# verify we stay in P1 if we add another segment
edge = Part.Edge(Part.LineSegment(edge.valueAt(edge.LastParameter), Vector(1, 0, 0)))
i = i.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P0)
self.assertEqual(len(i.edges), 3)
p3 = Vector(1, 0, 3)
self.assertLine(i.edges[2], p2, p3)
self.assertIsNone(i.tail)
def test02(self):
"""Verify proper down plunge on exit of triangular tag."""
tag = Tag( 0, 0, 8, 7, 45, True, 0)
p0 = Vector(5, 0, 0)
p1 = Vector(4, 0, 0)
p2 = Vector(0, 0, 4)
edge = Part.Edge(Part.LineSegment(p0, FreeCAD.Vector(0,0,0)))
i = tag.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P2)
self.assertEqual(len(i.edges), 2)
self.assertLines(i.edges, i.tail, [p0, p1, p2])
# adding another segment doesn't make a difference
edge = Part.Edge(Part.LineSegment(edge.valueAt(edge.LastParameter), FreeCAD.Vector(-3,0,0)))
i = i.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P2)
self.assertEqual(len(i.edges), 3)
p3 = Vector(-3, 0, 1)
self.assertLines(i.edges, i.tail, [p0, p1, p2, p3])
# same result if all is one line
edge = Part.Edge(Part.LineSegment(p0, edge.valueAt(edge.LastParameter)))
i = tag.intersect(edge)
self.assertEqual(i.state, Tag.Intersection.P2)
self.assertLines(i.edges, i.tail, [p0, p1, p2, p3])
def test03(self):
"""Verify triangular tag shap on intersection."""
tag = Tag( 0, 0, 8, 7, 45, True, 0)
p0 = Vector(5, 0, 0)
p1 = Vector(4, 0, 0)
p2 = Vector(0, 0, 4)
p3 = Vector(-4, 0, 0)
edge = Part.Edge(Part.LineSegment(p0, p3))
i = tag.intersect(edge)
self.assertTrue(i.isComplete())
self.assertLines(i.edges, i.tail, [p0, p1, p2, p3])
self.assertIsNone(i.tail)
# this should also work if there is some excess, aka tail
p4 = Vector(-5, 0, 0)
edge = Part.Edge(Part.LineSegment(p0, p4))
i = tag.intersect(edge)
self.assertTrue(i.isComplete())
self.assertLines(i.edges, i.tail, [p0, p1, p2, p3, p4])
self.assertIsNotNone(i.tail)
def test10(self):
"""Verify intersection with an arc."""
tag = Tag( 0, 0, 8, 7, 45, True, 0)
p1 = Vector(10, -10, 0)
p2 = Vector(10, +10, 0)
edge = PathGeom.edgeForCmd(Path.Command('G2', {'X': p2.x, 'Y': p2.y, 'Z': p2.z, 'J': 10}), p1)
pi = Vector(0.8, -3.919184, 0)
pj = Vector(0.0, 0.0, 4)
pk = Vector(0.8, +3.919184, 0)
s = tag.intersect(edge)
self.assertTrue(s.isComplete())
self.assertEqual(len(s.edges), 3)
self.assertCurve(s.edges[0], p1, Vector(4.486010, -8.342417, 0), pi)
self.assertCurve(s.edges[1], pi, Vector(0.202041, -2., 1.958759), pj)
self.assertCurve(s.edges[2], pj, Vector(0.202041, +2., 1.958759), pk)
self.assertCurve(s.tail, pk, Vector(4.486010, +8.342417, 0), p2)
def test20(self):
"""Verify intersection with a helix."""
tag = Tag( 0, 0, 8, 7, 45, True, 0)
p1 = Vector(10, -10, 0)
p2 = Vector(10, +10, 2)
edge = PathGeom.edgeForCmd(Path.Command('G2', {'X': p2.x, 'Y': p2.y, 'Z': p2.z, 'J': 10, 'K': 1}), p1)
pi = Vector(0.513574, -3.163498, 0.795085)
pj = Vector(0, 0, 4)
pk = Vector(0.397586, +2.791711, 1.180119)
s = tag.intersect(edge)
self.assertTrue(s.isComplete())
self.assertEqual(len(s.edges), 3)
self.assertCurve(s.edges[0], p1, Vector(4.153420, -8.112798, 0.397543), pi)
self.assertCurve(s.edges[1], pi, Vector(0.129229, -1.602457, 2.397542), pj)
self.assertCurve(s.edges[2], pj, Vector(0.099896, 1.409940, 2.590059), pk)
self.assertCurve(s.tail, pk, Vector(3.996548, +7.997409, 1.590060), p2)

View File

@ -29,8 +29,4 @@ from PathTests.TestPathPost import PathPostTestCases
from PathTests.TestPathGeom import TestPathGeom
from PathTests.TestPathDepthParams import depthTestCases
from PathTests.TestPathDressupHoldingTags import TestTag01BasicTag
from PathTests.TestPathDressupHoldingTags import TestTag02SquareTag
from PathTests.TestPathDressupHoldingTags import TestTag03TrapezoidTag
from PathTests.TestPathDressupHoldingTags import TestTag04TriangularTag
from PathTests.TestPathDressupHoldingTags import TestHoldingTags