diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt
index e92a43661..8c52a7fc3 100644
--- a/src/Mod/Path/CMakeLists.txt
+++ b/src/Mod/Path/CMakeLists.txt
@@ -26,7 +26,6 @@ SET(PathScripts_SRCS
PathScripts/PathCopy.py
PathScripts/PathCustom.py
PathScripts/PathDressup.py
- PathScripts/PathDressupHoldingTags.py
PathScripts/PathDrilling.py
PathScripts/PathEngrave.py
PathScripts/PathFacePocket.py
@@ -73,7 +72,6 @@ SET(PathScripts_SRCS
PathScripts/opensbp_pre.py
PathScripts/rml_post.py
PathScripts/slic3r_pre.py
- PathTests/TestPathDressupHoldingTags.py
PathTests/TestPathGeom.py
PathTests/TestPathPost.py
PathTests/__init__.py
diff --git a/src/Mod/Path/Gui/Resources/Path.qrc b/src/Mod/Path/Gui/Resources/Path.qrc
index 5be33f8ea..bb147e140 100644
--- a/src/Mod/Path/Gui/Resources/Path.qrc
+++ b/src/Mod/Path/Gui/Resources/Path.qrc
@@ -53,7 +53,6 @@
panels/DogboneEdit.ui
panels/DrillingEdit.ui
panels/EngraveEdit.ui
- panels/HoldingTagsEdit.ui
panels/JobEdit.ui
panels/MillFaceEdit.ui
panels/PocketEdit.ui
diff --git a/src/Mod/Path/Gui/Resources/panels/HoldingTagsEdit.ui b/src/Mod/Path/Gui/Resources/panels/HoldingTagsEdit.ui
deleted file mode 100644
index 465b0ad6e..000000000
--- a/src/Mod/Path/Gui/Resources/panels/HoldingTagsEdit.ui
+++ /dev/null
@@ -1,240 +0,0 @@
-
-
- TaskPanel
-
-
-
- 0
- 0
- 352
- 387
-
-
-
- Holding Tags
-
-
- -
-
-
- QFrame::NoFrame
-
-
- 0
-
-
-
-
- 0
- 0
- 334
- 311
-
-
-
- Tags
-
-
-
-
-
-
-
- 0
- 0
-
-
-
- true
-
-
- 80
-
-
- false
-
-
-
- X
-
-
-
-
- Y
-
-
-
-
- Width
-
-
-
-
- Height
-
-
-
-
- Angle
-
-
-
-
- -
-
-
-
-
-
-
- Delete
-
-
-
- -
-
-
- Disable
-
-
-
- -
-
-
- Add
-
-
-
-
-
-
-
-
-
-
-
- 0
- 0
- 334
- 311
-
-
-
- Generate
-
-
- -
-
-
- Width
-
-
-
- -
-
-
- <html><head/><body><p>Width of each tag.</p></body></html>
-
-
-
- -
-
-
- Height
-
-
-
- -
-
-
- <html><head/><body><p>The height of the holding tag measured from the bottom of the path. By default this is set to the (estimated) height of the path.</p></body></html>
-
-
-
- -
-
-
- true
-
-
- Angle
-
-
-
- -
-
-
- true
-
-
- <html><head/><body><p>Angle of tag walls.</p></body></html>
-
-
-
- -
-
-
- QDialogButtonBox::Apply|QDialogButtonBox::Ok
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
- -
-
-
- Layout
-
-
-
-
-
-
- Count
-
-
-
- -
-
-
- <html><head/><body><p>Enter the number of tags you wish to have.</p><p><br/></p><p>Note that sometimes it's necessary to enter a larger than desired count number and disable the ones tags you don't want in order to get the holding tag layout you want.</p></body></html>
-
-
-
- -
-
-
- Spacing
-
-
-
- -
-
-
-
-
-
- -
-
-
- Auto Apply
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py
index e2b69f6b7..7c34fd592 100644
--- a/src/Mod/Path/InitGui.py
+++ b/src/Mod/Path/InitGui.py
@@ -74,7 +74,6 @@ class PathWorkbench (Workbench):
from PathScripts import PathProfileEdges
from PathScripts import DogboneDressup
from PathScripts import PathMillFace
- from PathScripts import PathDressupHoldingTags
import PathCommands
# build commands list
@@ -84,7 +83,7 @@ class PathWorkbench (Workbench):
twodopcmdlist = ["Path_Contour", "Path_Profile", "Path_Profile_Edges", "Path_Pocket", "Path_Drilling", "Path_Engrave", "Path_MillFace"]
threedopcmdlist = ["Path_Surfacing"]
modcmdlist = ["Path_Copy", "Path_CompoundExtended", "Path_Array", "Path_SimpleCopy" ]
- dressupcmdlist = ["Dogbone_Dressup", "DragKnife_Dressup", "PathDressup_HoldingTags"]
+ dressupcmdlist = ["Dogbone_Dressup", "DragKnife_Dressup"]
extracmdlist = ["Path_SelectLoop"]
#modcmdmore = ["Path_Hop",]
#remotecmdlist = ["Path_Remote"]
diff --git a/src/Mod/Path/PathScripts/PathDressupHoldingTags.py b/src/Mod/Path/PathScripts/PathDressupHoldingTags.py
deleted file mode 100644
index b88ae712d..000000000
--- a/src/Mod/Path/PathScripts/PathDressupHoldingTags.py
+++ /dev/null
@@ -1,1011 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# ***************************************************************************
-# * *
-# * Copyright (c) 2016 sliptonic *
-# * *
-# * This program is free software; you can redistribute it and/or modify *
-# * it under the terms of the GNU Lesser General Public License (LGPL) *
-# * as published by the Free Software Foundation; either version 2 of *
-# * the License, or (at your option) any later version. *
-# * for detail see the LICENCE text file. *
-# * *
-# * This program is distributed in the hope that it will be useful, *
-# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-# * GNU Library General Public License for more details. *
-# * *
-# * You should have received a copy of the GNU Library General Public *
-# * License along with this program; if not, write to the Free Software *
-# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
-# * USA *
-# * *
-# ***************************************************************************
-import FreeCAD
-import FreeCADGui
-import Path
-from PathScripts import PathUtils
-from PySide import QtCore, QtGui
-import math
-import Part
-import DraftGeomUtils
-
-"""Holding Tags Dressup object and FreeCAD command"""
-
-# Qt tanslation handling
-try:
- _encoding = QtGui.QApplication.UnicodeUTF8
-
- def translate(context, text, disambig=None):
- return QtGui.QApplication.translate(context, text, disambig, _encoding)
-
-except AttributeError:
-
- def translate(context, text, disambig=None):
- return QtGui.QApplication.translate(context, text, disambig)
-
-debugDressup = True
-
-def debugMarker(vector, label, color = None, radius = 0.5):
- if debugDressup:
- obj = FreeCAD.ActiveDocument.addObject("Part::Sphere", label)
- obj.Label = label
- obj.Radius = radius
- obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0,0,1), 0))
- if color:
- obj.ViewObject.ShapeColor = color
-
-movecommands = ['G0', 'G00', 'G1', 'G01', 'G2', 'G02', 'G3', 'G03']
-movestraight = ['G1', 'G01']
-movecw = ['G2', 'G02']
-moveccw = ['G3', 'G03']
-movearc = movecw + moveccw
-
-slack = 0.0000001
-
-def isAbout(v1, v2):
- return math.fabs(v1 - v2) < slack
-
-def pointsCoincide(p1, p2):
- return isAbout(p1.x, p2.x) and isAbout(p1.y, p2.y) and isAbout(p1.z, p2.z)
-
-def getAngle(v):
- a = v.getAngle(FreeCAD.Vector(1,0,0))
- if v.y < 0:
- return -a
- return a
-
-class Side:
- Left = +1
- Right = -1
- Straight = 0
- On = 0
-
- @classmethod
- def toString(cls, side):
- if side == cls.Left:
- return 'Left'
- if side == cls.Right:
- return 'Right'
- return 'On'
-
- @classmethod
- def of(cls, ptRef, pt):
- d = -ptRef.x*pt.y + ptRef.y*pt.x
- if d < 0:
- return cls.Left
- if d > 0:
- return cls.Right
- return cls.Straight
-
-def pathCommandForEdge(edge):
- pt = edge.Curve.EndPoint
- params = {'X': pt.x, 'Y': pt.y, 'Z': pt.z}
- if type(edge.Curve) == Part.Line:
- return Part.Command('G1', params)
-
- p1 = edge.Curve.StartPoint
- p2 = edge.valueAt((edge.FirstParameter + edge.LastParameter)/2)
- p3 = pt
- if Side.Left == Side.of(p2 - p1, p3 - p2):
- cmd = 'G3'
- else:
- cmd = 'G2'
- offset = pt1 - edge.Curve.Center
- params.update({'I': offset.x, 'J': offset.y, 'K': offset.z})
- return Part.Command(cmd, params)
-
-
-class Tag:
-
- @classmethod
- def FromString(cls, string):
- try:
- t = eval(string)
- return Tag(t[0], t[1], t[2], t[3], t[4], t[5])
- except:
- return None
-
- def __init__(self, x, y, width, height, angle, enabled=True, z=None):
- self.x = x
- self.y = y
- self.width = math.fabs(width)
- self.height = math.fabs(height)
- self.actualHeight = self.height
- self.angle = math.fabs(angle)
- self.enabled = enabled
- if z is not None:
- self.createSolidsAt(z)
-
- def toString(self):
- return str((self.x, self.y, self.width, self.height, self.angle, self.enabled))
-
- def originAt(self, z):
- return FreeCAD.Vector(self.x, self.y, z)
-
- def bottom(self):
- return self.z
-
- def top(self):
- return self.z + self.actualHeight
-
- def centerLine(self):
- return Part.Line(self.originAt(self.bottom()), self.originAt(self.top()))
-
- def createSolidsAt(self, z):
- self.z = z
- r1 = self.width / 2
- height = self.height
- if self.angle == 90 and height > 0:
- self.solid = Part.makeCylinder(r1, height)
- self.core = self.solid.copy()
- elif self.angle > 0.0 and height > 0.0:
- tangens = math.tan(math.radians(self.angle))
- dr = height / tangens
- if dr < r1:
- r2 = r1 - dr
- self.core = Part.makeCylinder(r2, height)
- else:
- r2 = 0
- height = r1 * tangens
- self.core = None
- self.actualHeight = height
- self.solid = Part.makeCone(r1, r2, height)
- else:
- # degenerated case - no tag
- self.solid = Part.makeSphere(r1 / 10000)
- self.core = None
- self.solid.translate(self.originAt(z))
- 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.
- P0 = 2
- P1 = 3
- P2 = 4
- P3 = 5
-
- def __init__(self, tag):
- self.tag = tag
- self.state = self.P3
- self.edges = []
- self.tail = None
-
- def isComplete(self):
- return self.state == self.P3
-
- def moveEdgeToPlateau(self, edge):
- if type(edge.Curve) is Part.Line:
- pt1 = edge.Curve.StartPoint
- pt2 = edge.Curve.EndPoint
- pt1.z = self.tag.top()
- pt2.z = self.tag.top()
- #print("\nplateau= %s - %s" %(pt1, pt2))
- return Part.Edge(Part.Line(pt1, pt2))
-
- def intersectP0Core(self, edge):
- #print("----- P0 (%s - %s)" % (edge.Curve.StartPoint, edge.Curve.EndPoint))
-
- i = self.tag.nextIntersectionClosestTo(edge, self.tag.core, edge.Curve.StartPoint)
- if i:
- if pointsCoincide(i, edge.Curve.StartPoint):
- # if P0 and P1 are the same, we need to insert a segment for the rise
- #print("------- insert vertical rise (%s)" % i)
- self.edges.append(Part.Edge(Part.Line(i, FreeCAD.Vector(i.x, i.y, self.tag.top()))))
- self.p1 = i
- self.state = self.P1
- return edge
- if pointsCoincide(i, edge.Curve.EndPoint):
- #print("------- consumed (%s)" % i)
- e = edge
- tail = None
- else:
- #print("------- split at (%s)" % i)
- e, tail = self.tag.splitEdgeAt(edge, i)
- self.p1 = e.Curve.EndPoint
- self.edges.append(self.tag.mapEdgeToSolid(e))
- self.state = self.P1
- return tail
- # no intersection, the entire edge fits between P0 and P1
- #print("------- no intersection")
- self.edges.append(self.tag.mapEdgeToSolid(edge))
- return None
-
- def intersectP0(self, edge):
- 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())
- i = DraftGeomUtils.findIntersection(line, edge)
- if i:
- if pointsCoincide(i[0], edge.Curve.EndPoint):
- e = edge
- tail = None
- else:
- e, tail = self.tag.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:
- e = edge
- tail = None
- self.edges.append(self.tag.mapEdgeToSolid(e))
- return tail
-
-
-
- def intersectP1(self, edge):
- #print("----- P1 (%s - %s)" % (edge.Curve.StartPoint, edge.Curve.EndPoint))
- i = self.tag.nextIntersectionClosestTo(edge, self.tag.core, edge.Curve.EndPoint)
- if i:
- if pointsCoincide(i, edge.Curve.StartPoint):
- self.edges.append(self.tag.mapEdgeToSolid(edge))
- return self
- if pointsCoincide(i, edge.Curve.EndPoint):
- e = edge
- tail = None
- else:
- e, tail = self.tag.splitEdgeAt(edge, i)
- self.p2 = e.Curve.EndPoint
- self.state = self.P2
- else:
- e = edge
- tail = None
- self.edges.append(self.moveEdgeToPlateau(e))
- return tail
-
- def intersectP2(self, edge):
- #print("----- P2 (%s - %s)" % (edge.Curve.StartPoint, edge.Curve.EndPoint))
- i = self.tag.nextIntersectionClosestTo(edge, self.tag.solid, edge.Curve.EndPoint)
- if i:
- if pointsCoincide(i, edge.Curve.StartPoint):
- #print("------- insert exit plunge (%s)" % i)
- self.edges.append(Part.Edge(Part.Line(FreeCAD.Vector(i.x, i.y, self.tag.top()), i)))
- e = None
- tail = edge
- elif pointsCoincide(i, edge.Curve.EndPoint):
- #print("------- entire segment added (%s)" % i)
- e = edge
- tail = None
- else:
- e, tail = self.tag.splitEdgeAt(edge, i)
- #if tail:
- # print("----- P3 (%s - %s)" % (tail.Curve.StartPoint, tail.Curve.EndPoint))
- #else:
- # print("----- P3 (---)")
- self.state = self.P3
- self.tail = tail
- else:
- e = edge
- tail = None
- if e:
- self.edges.append(self.tag.mapEdgeToSolid(e))
- return tail
-
- def intersect(self, edge):
- #print("")
- #print(" >>> (%s - %s)" % (edge.Curve.StartPoint, edge.Curve.EndPoint))
- 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 splitEdgeAt(self, edge, pt):
- p = edge.Curve.parameter(pt)
- wire = edge.split(p)
- return wire.Edges
-
- def mapEdgeToSolid(self, edge):
- #print("mapEdgeToSolid: (%s %s)" % (edge.Curve.StartPoint, edge.Curve.EndPoint))
- p1a = edge.Curve.StartPoint
- p1b = FreeCAD.Vector(p1a.x, p1a.y, p1a.z + self.height)
- e1 = Part.Edge(Part.Line(p1a, p1b))
- p1 = self.nextIntersectionClosestTo(e1, self.solid, p1b) # top most intersection
- #print(" p1: (%s %s) -> %s" % (p1a, p1b, p1))
-
- p2a = edge.Curve.EndPoint
- p2b = FreeCAD.Vector(p2a.x, p2a.y, p2a.z + self.height)
- e2 = Part.Edge(Part.Line(p2a, p2b))
- p2 = self.nextIntersectionClosestTo(e2, self.solid, p2b) # top most intersection
- #print(" p2: (%s %s) -> %s" % (p2a, p2b, p2))
-
- if type(edge.Curve) == Part.Line:
- return Part.Edge(Part.Line(p1, p2))
-
- def filterIntersections(self, pts, face):
- if type(face.Surface) == Part.Cone or type(face.Surface) == Part.Cylinder:
- return filter(lambda pt: pt.z >= self.bottom() and pt.z <= self.top(), pts)
- if type(face.Surface) == Part.Plane:
- c = face.Edges[0].Curve
- if (type(c) == Part.Circle):
- return filter(lambda pt: (pt - c.Center).Length <= c.Radius, pts)
- print("==== we got a %s" % face.Surface)
-
-
- def nextIntersectionClosestTo(self, edge, solid, refPt):
- pts = []
- for index, face in enumerate(solid.Faces):
- i = edge.Curve.intersect(face.Surface)[0]
- ps = self.filterIntersections([FreeCAD.Vector(p.X, p.Y, p.Z) for p in i], face)
- pts.extend(ps)
- if pts:
- closest = sorted(pts, key=lambda pt: (pt - refPt).Length)[0]
- #print("--pts: %s -> %s" % (pts, closest))
- return closest
- return None
-
- def intersect(self, edge):
- inters = self.Intersection(self)
- if edge.Curve.StartPoint.z < self.top() or edge.Curve.EndPoint.z < self.top():
- i = self.nextIntersectionClosestTo(edge, self.solid, edge.Curve.StartPoint)
- if i:
- inters.state = self.Intersection.P0
- inters.p0 = i
- if pointsCoincide(i, edge.Curve.EndPoint):
- inters.edges.append(edge)
- return inters
- if pointsCoincide(i, edge.Curve.StartPoint):
- tail = edge
- else:
- e,tail = self.splitEdgeAt(edge, i)
- inters.edges.append(e)
- return inters.intersect(tail)
- # if we get here there is no intersection with the tag
- inters.state = self.Intersection.P3
- inters.tail = edge
- return inters
-
-class PathData:
- def __init__(self, obj):
- self.obj = obj
- self.edges = []
- lastPt = FreeCAD.Vector(0, 0, 0)
- for cmd in obj.Base.Path.Commands:
- if cmd.Name in movecommands:
- pt = self.point(cmd, lastPt)
- if cmd.Name in movestraight:
- self.edges.append(Part.Edge(Part.Line(lastPt, pt)))
- elif cmd.Name in movearc:
- center = lastPt + self.point(cmd, FreeCAD.Vector(0,0,0), 'I', 'J', 'K')
- A = lastPt - center
- B = pt - center
- d = -B.x * A.y + B.y * A.x
-
- if d == 0:
- # we're dealing with half an circle here
- angle = getAngle(A) + math.pi/2
- if cmd.Name in movecw:
- angle -= math.pi
- else:
- #print("(%.2f, %.2f) (%.2f, %.2f)" % (A.x, A.y, B.x, B.y))
- C = A + B
- angle = getAngle(C)
-
- R = (lastPt - center).Length
- ptm = center + FreeCAD.Vector(math.cos(angle), math.sin(angle), 0) * R
-
- self.edges.append(Part.Edge(Part.Arc(lastPt, ptm, pt)))
- lastPt = pt
- self.base = self.findBottomWire(self.edges)
- # determine overall length
- self.length = self.base.Length
-
- def point(self, cmd, pt, X='X', Y='Y', Z='Z'):
- x = cmd.Parameters.get(X, pt.x)
- y = cmd.Parameters.get(Y, pt.y)
- z = cmd.Parameters.get(Z, pt.z)
- return FreeCAD.Vector(x, y, z)
-
- def findBottomWire(self, edges):
- (minZ, maxZ) = self.findZLimits(edges)
- self.minZ = minZ
- self.maxZ = maxZ
- 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))
- # 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.Curve.StartPoint.z == self.minZ and e.Curve.EndPoint.z != self.maxZ]
- exit = sorted(edges, key=lambda e: -e.Curve.EndPoint.z)[0]
- pt = exit.Curve.StartPoint
- # then find the first base edge, and sort them until done
- ordered = []
- while base:
- edge = [e for e in base if e.Curve.StartPoint == pt][0]
- ordered.append(edge)
- base.remove(edge)
- pt = edge.Curve.EndPoint
- 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
- maxZ = minZ
- for e in edges:
- for v in e.Vertexes:
- if v.Point.z < minZ:
- minZ = v.Point.z
- if v.Point.z > maxZ:
- maxZ = v.Point.z
- return (minZ, maxZ)
-
- def shortestAndLongestPathEdge(self):
- edges = sorted(self.base.Edges, key=lambda e: e.Length)
- return (edges[0], edges[-1])
-
- def generateTags(self, obj, count=None, width=None, height=None, angle=90, spacing=None):
- #print("generateTags(%s, %s, %s, %s, %s)" % (count, width, height, angle, spacing))
- #for e in self.base.Edges:
- # debugMarker(e.Vertexes[0].Point, 'base', (0.0, 1.0, 1.0), 0.2)
-
- if spacing:
- tagDistance = spacing
- else:
- if count:
- tagDistance = self.base.Length / count
- else:
- tagDistance = self.base.Length / 4
- if width:
- W = width
- else:
- W = self.tagWidth()
- if height:
- H = height
- else:
- H = self.tagHeight()
-
-
- # start assigning tags on the longest segment
- (shortestEdge, longestEdge) = self.shortestAndLongestPathEdge()
- startIndex = 0
- for i in range(0, len(self.base.Edges)):
- edge = self.base.Edges[i]
- if edge.Length == longestEdge.Length:
- startIndex = i
- break
-
- startEdge = self.base.Edges[startIndex]
- startCount = int(startEdge.Length / tagDistance)
- if (longestEdge.Length - shortestEdge.Length) > shortestEdge.Length:
- startCount = int(startEdge.Length / tagDistance) + 1
-
- lastTagLength = (startEdge.Length + (startCount - 1) * tagDistance) / 2
- currentLength = startEdge.Length
-
- minLength = min(2. * W, longestEdge.Length)
-
- #print("length=%.2f shortestEdge=%.2f(%.2f) longestEdge=%.2f(%.2f)" % (self.base.Length, shortestEdge.Length, shortestEdge.Length/self.base.Length, longestEdge.Length, longestEdge.Length / self.base.Length))
- #print(" start: index=%-2d count=%d (length=%.2f, distance=%.2f)" % (startIndex, startCount, startEdge.Length, tagDistance))
- #print(" -> lastTagLength=%.2f)" % lastTagLength)
- #print(" -> currentLength=%.2f)" % currentLength)
-
- edgeDict = { startIndex: startCount }
-
- for i in range(startIndex + 1, len(self.base.Edges)):
- edge = self.base.Edges[i]
- (currentLength, lastTagLength) = self.processEdge(i, edge, currentLength, lastTagLength, tagDistance, minLength, edgeDict)
- for i in range(0, startIndex):
- edge = self.base.Edges[i]
- (currentLength, lastTagLength) = self.processEdge(i, edge, currentLength, lastTagLength, tagDistance, minLength, edgeDict)
-
- tags = []
-
- for (i, count) in edgeDict.iteritems():
- edge = self.base.Edges[i]
- #print(" %d: %d" % (i, count))
- #debugMarker(edge.Vertexes[0].Point, 'base', (1.0, 0.0, 0.0), 0.2)
- #debugMarker(edge.Vertexes[1].Point, 'base', (0.0, 1.0, 0.0), 0.2)
- distance = (edge.LastParameter - edge.FirstParameter) / count
- for j in range(0, count):
- tag = edge.Curve.value((j+0.5) * distance)
- tags.append(Tag(tag.x, tag.y, W, H, angle, True))
-
- return tags
-
- def processEdge(self, index, edge, currentLength, lastTagLength, tagDistance, minLength, edgeDict):
- tagCount = 0
- currentLength += edge.Length
- if edge.Length > minLength:
- while lastTagLength + tagDistance < currentLength:
- tagCount += 1
- lastTagLength += tagDistance
- if tagCount > 0:
- #print(" index=%d -> count=%d" % (index, tagCount))
- edgeDict[index] = tagCount
- #else:
- #print(" skipping=%-2d (%.2f)" % (index, edge.Length))
-
- return (currentLength, lastTagLength)
-
- def tagHeight(self):
- return self.maxZ - self.minZ
-
- def tagWidth(self):
- return self.shortestAndLongestPathEdge()[1].Length / 10
-
- def tagAngle(self):
- return 90
-
- def pathLength(self):
- return self.base.Length
-
- def sortedTags(self, tags):
- ordered = []
- for edge in self.base.Edges:
- ts = [t for t in tags if DraftGeomUtils.isPtOnEdge(t.originAt(self.minZ), edge)]
- for t in sorted(ts, key=lambda t: (t.originAt(self.minZ) - edge.Curve.StartPoint).Length):
- tags.remove(t)
- ordered.append(t)
- if tags:
- raise ValueError("There's something really wrong here")
- return ordered
-
-
-class ObjectDressup:
-
- def __init__(self, obj):
- self.obj = obj
- obj.addProperty("App::PropertyLink", "Base","Base", QtCore.QT_TRANSLATE_NOOP("PathDressup_HoldingTags", "The base path to modify"))
- obj.addProperty("App::PropertyStringList", "Tags", "Tag", QtCore.QT_TRANSLATE_NOOP("PathDressup_holdingTags", "Inserted tags"))
- obj.setEditorMode("Tags", 2)
- obj.Proxy = self
-
- def __getstate__(self):
- return None
-
- def __setstate__(self, state):
- return None
-
- def generateTags(self, obj, count=None, width=None, height=None, angle=90, spacing=None):
- return self.pathData.generateTags(obj, count, width, height, angle, spacing)
-
-
- def tagIntersection(self, face, edge):
- p1 = edge.Curve.StartPoint
- 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, tagSolids):
- commands = []
- i = 0
- while i != len(edges):
- edge = edges[i]
- while edge:
- for solid in tagSolids:
- for face in solid.Faces:
- pt = self.tagIntersection(face, edge)
- if pt:
- if pt == edge.Curve.StartPoint:
- pt
- elif pt != edge.Curve.EndPoint:
- parameter = edge.Curve.parameter(pt)
- wire = edge.split(parameter)
- commands.append(pathCommandForEdge(wire.Edges[0]))
- edge = wire.Edges[1]
- break;
- else:
- commands.append(pathCommandForEdge(edge))
- edge = None
- i += 1
- break
- if not edge:
- break
- if edge:
- commands.append(pathCommandForEdge(edge))
- edge = None
- return self.obj.Path
-
-
- def execute(self, obj):
- if not obj.Base:
- return
- if not obj.Base.isDerivedFrom("Path::Feature"):
- return
- if not obj.Base.Path:
- return
- if not obj.Base.Path.Commands:
- return
-
- pathData = self.setup(obj)
- if not pathData:
- print("execute - no pathData")
- return
-
- if hasattr(obj, 'Tags') and obj.Tags:
- if self.fingerprint == obj.Tags:
- print("execute - cache valid")
- return
- print("execute - tags from property")
- tags = [Tag.FromString(tag) for tag in obj.Tags]
- else:
- print("execute - default tags")
- tags = self.generateTags(obj, 4.)
-
- if not tags:
- print("execute - no tags")
- self.tags = []
- obj.Path = obj.Base.Path
- return
-
- tagID = 0
- for tag in tags:
- tagID += 1
- if tag.enabled:
- #print("x=%s, y=%s, z=%s" % (tag.x, tag.y, pathData.minZ))
- debugMarker(FreeCAD.Vector(tag.x, tag.y, pathData.minZ), "tag-%02d" % tagID , (1.0, 0.0, 1.0), 0.5)
-
- tags = pathData.sortedTags(tags)
- for tag in tags:
- tag.createSolidsAt(pathData.minZ)
-
- self.fingerprint = [tag.toString() for tag in tags]
- self.tags = tags
-
- #obj.Path = self.createPath(pathData.edges, tags)
- obj.Path = self.Base.Path
-
- def setTags(self, obj, tags):
- obj.Tags = [tag.toString() for tag in tags]
- self.execute(obj)
-
- def getTags(self, obj):
- if hasattr(self, 'tags'):
- return self.tags
- return self.setup(obj).generateTags(obj, 4)
-
- def setup(self, obj):
- if not hasattr(self, "pathData") or not self.pathData:
- try:
- pathData = PathData(obj)
- except ValueError:
- FreeCAD.Console.PrintError(translate("PathDressup_HoldingTags", "Cannot insert holding tags for this path - please select a Profile path\n"))
- return None
-
- ## setup the object's properties, in case they're not set yet
- #obj.Count = self.tagCount(obj)
- #obj.Angle = self.tagAngle(obj)
- #obj.Blacklist = self.tagBlacklist(obj)
-
- # if the heigt isn't set, use the height of the path
- #if not hasattr(obj, "Height") or not obj.Height:
- # obj.Height = pathData.maxZ - pathData.minZ
- # try and take an educated guess at the width
- #if not hasattr(obj, "Width") or not obj.Width:
- # width = sorted(pathData.base.Edges, key=lambda e: -e.Length)[0].Length / 10
- # while obj.Count > len([e for e in pathData.base.Edges if e.Length > 3*width]):
- # width = widht / 2
- # obj.Width = width
-
- # and the tool radius, not sure yet if it's needed
- #self.toolRadius = 5
- #toolLoad = PathUtils.getLastToolLoad(obj)
- #if toolLoad is None or toolLoad.ToolNumber == 0:
- # self.toolRadius = 5
- #else:
- # tool = PathUtils.getTool(obj, toolLoad.ToolNumber)
- # if not tool or tool.Diameter == 0:
- # self.toolRadius = 5
- # else:
- # self.toolRadius = tool.Diameter / 2
- self.pathData = pathData
- return self.pathData
-
- def getHeight(self, obj):
- return self.pathData.tagHeight()
-
- def getWidth(self, obj):
- return self.pathData.tagWidth()
-
- def getAngle(self, obj):
- return self.pathData.tagAngle()
-
- def getPathLength(self, obj):
- return self.pathData.pathLength()
-
-class TaskPanel:
- DataTag = QtCore.Qt.ItemDataRole.UserRole
- DataValue = QtCore.Qt.ItemDataRole.DisplayRole
-
- def __init__(self, obj):
- self.obj = obj
- self.form = FreeCADGui.PySideUic.loadUi(":/panels/HoldingTagsEdit.ui")
- FreeCAD.ActiveDocument.openTransaction(translate("PathDressup_HoldingTags", "Edit HoldingTags Dress-up"))
-
- def reject(self):
- FreeCAD.ActiveDocument.abortTransaction()
- FreeCADGui.Control.closeDialog()
- FreeCAD.ActiveDocument.recompute()
- FreeCADGui.Selection.removeObserver(self.s)
-
- def accept(self):
- FreeCAD.ActiveDocument.commitTransaction()
- FreeCADGui.ActiveDocument.resetEdit()
- FreeCADGui.Control.closeDialog()
- FreeCAD.ActiveDocument.recompute()
- FreeCADGui.Selection.removeObserver(self.s)
- FreeCAD.ActiveDocument.recompute()
-
- def open(self):
- self.s = SelObserver()
- # install the function mode resident
- FreeCADGui.Selection.addObserver(self.s)
-
- def tableWidgetItem(self, tag, val):
- item = QtGui.QTableWidgetItem()
- item.setTextAlignment(QtCore.Qt.AlignRight)
- item.setData(self.DataTag, tag)
- item.setData(self.DataValue, val)
- return item
-
- def getFields(self):
- tags = []
- for row in range(0, self.form.twTags.rowCount()):
- x = self.form.twTags.item(row, 0).data(self.DataValue)
- y = self.form.twTags.item(row, 1).data(self.DataValue)
- w = self.form.twTags.item(row, 2).data(self.DataValue)
- h = self.form.twTags.item(row, 3).data(self.DataValue)
- a = self.form.twTags.item(row, 4).data(self.DataValue)
- tags.append(Tag(x, y, w, h, a, True))
- print("getFields: %d" % (len(tags)))
- self.obj.Proxy.setTags(self.obj, tags)
-
- def updateTags(self):
- self.tags = self.obj.Proxy.getTags(self.obj)
- self.form.twTags.blockSignals(True)
- self.form.twTags.setSortingEnabled(False)
- self.form.twTags.clearSpans()
- print("updateTags: %d" % (len(self.tags)))
- self.form.twTags.setRowCount(len(self.tags))
- for row, tag in enumerate(self.tags):
- self.form.twTags.setItem(row, 0, self.tableWidgetItem(tag, tag.x))
- self.form.twTags.setItem(row, 1, self.tableWidgetItem(tag, tag.y))
- self.form.twTags.setItem(row, 2, self.tableWidgetItem(tag, tag.width))
- self.form.twTags.setItem(row, 3, self.tableWidgetItem(tag, tag.height))
- self.form.twTags.setItem(row, 4, self.tableWidgetItem(tag, tag.angle))
- self.form.twTags.setSortingEnabled(True)
- self.form.twTags.blockSignals(False)
-
- def cleanupUI(self):
- print("cleanupUI")
- if debugDressup:
- for obj in FreeCAD.ActiveDocument.Objects:
- if obj.Name.startswith('tag'):
- FreeCAD.ActiveDocument.removeObject(obj.Name)
-
- def updateUI(self):
- print("updateUI")
- self.cleanupUI()
- self.getFields()
- if debugDressup:
- FreeCAD.ActiveDocument.recompute()
-
-
- def whenApplyClicked(self):
- print("whenApplyClicked")
- self.cleanupUI()
-
- count = self.form.sbCount.value()
- spacing = self.form.dsbSpacing.value()
- width = self.form.dsbWidth.value()
- height = self.form.dsbHeight.value()
- angle = self.form.dsbAngle.value()
-
- tags = self.obj.Proxy.generateTags(self.obj, count, width, height, angle, spacing * 0.99)
-
- self.obj.Proxy.setTags(self.obj, tags)
- self.updateTags()
- if debugDressup:
- # this causes a big of an echo and a double click on the spin buttons, don't know why though
- FreeCAD.ActiveDocument.recompute()
-
- def autoApply(self):
- print("autoApply")
- if self.form.cbAutoApply.checkState() == QtCore.Qt.CheckState.Checked:
- self.whenApplyClicked()
-
- def updateTagSpacing(self, count):
- print("updateTagSpacing")
- if count == 0:
- spacing = 0
- else:
- spacing = self.pathLength / count
- self.form.dsbSpacing.blockSignals(True)
- self.form.dsbSpacing.setValue(spacing)
- self.form.dsbSpacing.blockSignals(False)
-
- def whenCountChanged(self):
- print("whenCountChanged")
- self.updateTagSpacing(self.form.sbCount.value())
- self.autoApply()
-
- def whenSpacingChanged(self):
- print("whenSpacingChanged")
- if self.form.dsbSpacing.value() == 0:
- count = 0
- else:
- count = int(self.pathLength / self.form.dsbSpacing.value())
- self.form.sbCount.blockSignals(True)
- self.form.sbCount.setValue(count)
- self.form.sbCount.blockSignals(False)
- self.autoApply()
-
- def whenOkClicked(self):
- print("whenOkClicked")
- self.whenApplyClicked()
- self.form.toolBox.setCurrentWidget(self.form.tbpTags)
-
- def setupSpinBox(self, widget, val, decimals = 2):
- widget.setMinimum(0)
- if decimals:
- widget.setDecimals(decimals)
- widget.setValue(val)
-
- def setFields(self):
- self.pathLength = self.obj.Proxy.getPathLength(self.obj)
- vHeader = self.form.twTags.verticalHeader()
- vHeader.setResizeMode(QtGui.QHeaderView.Fixed)
- vHeader.setDefaultSectionSize(20)
- self.updateTags()
- self.setupSpinBox(self.form.sbCount, self.form.twTags.rowCount(), None)
- self.setupSpinBox(self.form.dsbSpacing, 0)
- self.setupSpinBox(self.form.dsbHeight, self.obj.Proxy.getHeight(self.obj))
- self.setupSpinBox(self.form.dsbWidth, self.obj.Proxy.getWidth(self.obj))
- self.setupSpinBox(self.form.dsbAngle, self.obj.Proxy.getAngle(self.obj))
- self.updateTagSpacing(self.form.twTags.rowCount())
-
- def setupUi(self):
- self.setFields()
- self.form.sbCount.valueChanged.connect(self.whenCountChanged)
- self.form.dsbSpacing.valueChanged.connect(self.whenSpacingChanged)
- self.form.dsbHeight.valueChanged.connect(self.autoApply)
- self.form.dsbWidth.valueChanged.connect(self.autoApply)
- self.form.dsbAngle.valueChanged.connect(self.autoApply)
- #self.form.pbAdd.clicked.connect(self.)
- self.form.buttonBox.button(QtGui.QDialogButtonBox.Apply).clicked.connect(self.whenApplyClicked)
- self.form.buttonBox.button(QtGui.QDialogButtonBox.Ok).clicked.connect(self.whenOkClicked)
- self.form.twTags.itemChanged.connect(self.updateUI)
-
-class SelObserver:
- def __init__(self):
- import PathScripts.PathSelection as PST
- PST.eselect()
-
- def __del__(self):
- import PathScripts.PathSelection as PST
- PST.clear()
-
- def addSelection(self, doc, obj, sub, pnt):
- FreeCADGui.doCommand('Gui.Selection.addSelection(FreeCAD.ActiveDocument.' + obj + ')')
- FreeCADGui.updateGui()
-
-class ViewProviderDressup:
-
- def __init__(self, vobj):
- vobj.Proxy = self
-
- def attach(self, vobj):
- self.Object = vobj.Object
- return
-
- def claimChildren(self):
- for i in self.Object.Base.InList:
- if hasattr(i, "Group"):
- group = i.Group
- for g in group:
- if g.Name == self.Object.Base.Name:
- group.remove(g)
- i.Group = group
- print i.Group
- #FreeCADGui.ActiveDocument.getObject(obj.Base.Name).Visibility = False
- return [self.Object.Base]
-
- def setEdit(self, vobj, mode=0):
- FreeCADGui.Control.closeDialog()
- panel = TaskPanel(vobj.Object)
- FreeCADGui.Control.showDialog(panel)
- panel.setupUi()
- return True
-
- def __getstate__(self):
- return None
-
- def __setstate__(self, state):
- return None
-
- def onDelete(self, arg1=None, arg2=None):
- '''this makes sure that the base operation is added back to the project and visible'''
- FreeCADGui.ActiveDocument.getObject(arg1.Object.Base.Name).Visibility = True
- PathUtils.addToJob(arg1.Object.Base)
- return True
-
-class CommandPathDressupHoldingTags:
-
- def GetResources(self):
- return {'Pixmap': 'Path-Dressup',
- 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathDressup_HoldingTags", "HoldingTags Dress-up"),
- 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathDressup_HoldingTags", "Creates a HoldingTags Dress-up object from a selected path")}
-
- def IsActive(self):
- if FreeCAD.ActiveDocument is not None:
- for o in FreeCAD.ActiveDocument.Objects:
- if o.Name[:3] == "Job":
- return True
- return False
-
- def Activated(self):
-
- # check that the selection contains exactly what we want
- selection = FreeCADGui.Selection.getSelection()
- if len(selection) != 1:
- FreeCAD.Console.PrintError(translate("PathDressup_HoldingTags", "Please select one path object\n"))
- return
- baseObject = selection[0]
- if not baseObject.isDerivedFrom("Path::Feature"):
- FreeCAD.Console.PrintError(translate("PathDressup_HoldingTags", "The selected object is not a path\n"))
- return
- if baseObject.isDerivedFrom("Path::FeatureCompoundPython"):
- FreeCAD.Console.PrintError(translate("PathDressup_HoldingTags", "Please select a Profile object"))
- return
-
- # everything ok!
- FreeCAD.ActiveDocument.openTransaction(translate("PathDressup_HoldingTags", "Create HoldingTags Dress-up"))
- FreeCADGui.addModule("PathScripts.PathDressupHoldingTags")
- FreeCADGui.addModule("PathScripts.PathUtils")
- FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "HoldingTagsDressup")')
- FreeCADGui.doCommand('dbo = PathScripts.PathDressupHoldingTags.ObjectDressup(obj)')
- FreeCADGui.doCommand('obj.Base = FreeCAD.ActiveDocument.' + selection[0].Name)
- FreeCADGui.doCommand('PathScripts.PathDressupHoldingTags.ViewProviderDressup(obj.ViewObject)')
- FreeCADGui.doCommand('PathScripts.PathUtils.addToJob(obj)')
- FreeCADGui.doCommand('Gui.ActiveDocument.getObject(obj.Base.Name).Visibility = False')
- FreeCADGui.doCommand('dbo.setup(obj)')
- FreeCAD.ActiveDocument.commitTransaction()
- FreeCAD.ActiveDocument.recompute()
-
-if FreeCAD.GuiUp:
- # register the FreeCAD command
- FreeCADGui.addCommand('PathDressup_HoldingTags', CommandPathDressupHoldingTags())
-
-FreeCAD.Console.PrintLog("Loading PathDressupHoldingTags... done\n")
diff --git a/src/Mod/Path/PathScripts/PathGeom.py b/src/Mod/Path/PathScripts/PathGeom.py
index e4d176717..4ce9b08ab 100644
--- a/src/Mod/Path/PathScripts/PathGeom.py
+++ b/src/Mod/Path/PathScripts/PathGeom.py
@@ -65,11 +65,12 @@ class Side:
class PathGeom:
"""Class to transform Path Commands into Edges and Wire and back again.
The interface might eventuallly become part of Path itself."""
+ CmdMoveFast = ['G0', 'G00']
CmdMoveStraight = ['G1', 'G01']
- CmdMoveCW = ['G2', 'G02']
- CmdMoveCCW = ['G3', 'G03']
- CmdMoveArc = CmdMoveCW + CmdMoveCCW
- CmdMove = CmdMoveStraight + CmdMoveArc
+ CmdMoveCW = ['G2', 'G02']
+ CmdMoveCCW = ['G3', 'G03']
+ CmdMoveArc = CmdMoveCW + CmdMoveCCW
+ CmdMove = CmdMoveStraight + CmdMoveArc
@classmethod
def getAngle(cls, vertex):
@@ -108,11 +109,11 @@ class PathGeom:
return Vector(pt.x, pt.y, 0)
@classmethod
- def edgeForCmd(cls, cmd, startPoint):
+ def edgeForCmd(cls, cmd, startPoint, includeFastMoves = False):
"""Returns a Curve representing the givne command, assuming a given startinPoint."""
endPoint = cls.commandEndPoint(cmd, startPoint)
- if cmd.Name in cls.CmdMoveStraight:
+ if (cmd.Name in cls.CmdMoveStraight) or (includeFastMoves and cmd.Name in cls.CmdMoveFast):
return Part.Edge(Part.Line(startPoint, endPoint))
if cmd.Name in cls.CmdMoveArc:
@@ -155,5 +156,35 @@ class PathGeom:
e = helix.Edges[0]
helix.translate(startPoint - e.valueAt(e.FirstParameter))
return helix.Edges[0]
+ return None
+ @classmethod
+ def wireForPath(cls, path, startPoint = FreeCAD.Vector(0, 0, 0)):
+ """Returns a wire representing all move commands found in the given path."""
+ edges = []
+ if hasattr(path, "Commands"):
+ for cmd in path.Commands:
+ edge = cls.edgeForCmd(cmd, startPoint, True)
+ if edge:
+ edges.append(edge)
+ startPoint = cls.commandEndPoint(cmd, startPoint)
+ return Part.Wire(edges)
+
+ @classmethod
+ def wiresForPath(cls, path, startPoint = FreeCAD.Vector(0, 0, 0)):
+ """Returns a collection of wires, each representing a continuous cutting Path in path."""
+ wires = []
+ if hasattr(path, "Commands"):
+ edges = []
+ for cmd in path.Commands:
+ if cmd.Name in cls.CmdMove:
+ edges.append(cls.edgeForCmd(cmd, startPoint, False))
+ startPoint = cls.commandEndPoint(cmd, startPoint)
+ elif cmd.Name in cls.CmdMoveFast:
+ wires.append(Part.Wire(edges))
+ edges = []
+ startPoint = cls.commandEndPoint(cmd, startPoint)
+ if edges:
+ wires.append(Part.Wire(edges))
+ return wires
diff --git a/src/Mod/Path/PathTests/TestPathDressupHoldingTags.py b/src/Mod/Path/PathTests/TestPathDressupHoldingTags.py
deleted file mode 100644
index d6adb06d0..000000000
--- a/src/Mod/Path/PathTests/TestPathDressupHoldingTags.py
+++ /dev/null
@@ -1,639 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# ***************************************************************************
-# * *
-# * Copyright (c) 2016 sliptonic *
-# * *
-# * This program is free software; you can redistribute it and/or modify *
-# * it under the terms of the GNU Lesser General Public License (LGPL) *
-# * as published by the Free Software Foundation; either version 2 of *
-# * the License, or (at your option) any later version. *
-# * for detail see the LICENCE text file. *
-# * *
-# * This program is distributed in the hope that it will be useful, *
-# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
-# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
-# * GNU Library General Public License for more details. *
-# * *
-# * You should have received a copy of the GNU Library General Public *
-# * License along with this program; if not, write to the Free Software *
-# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
-# * USA *
-# * *
-# ***************************************************************************
-
-import FreeCAD
-import Part
-import Path
-import PathScripts
-import math
-import unittest
-
-from FreeCAD import Vector
-from PathScripts.PathDressupHoldingTags import *
-
-def pointsCoincide(pt1, pt2):
- pt = pt1 - pt2
- if math.fabs(pt.x) > slack:
- return False
- if math.fabs(pt.y) > slack:
- return False
- if math.fabs(pt.z) > slack:
- return False
- return True
-
-class TagTestCaseBase(unittest.TestCase):
- """Base class for all tag test cases providing additional assert functions."""
-
- def assertCylinderAt(self, solid, pt, r, h):
- """Verify that solid is a cylinder at the specified location."""
- self.assertEqual(len(solid.Edges), 3)
-
- lid = solid.Edges[0]
- hull = solid.Edges[1]
- base = solid.Edges[2]
-
- self.assertCircle(lid, Vector(pt.x, pt.y, pt.z+h), r)
- self.assertLine(hull, Vector(pt.x+r, pt.y, pt.z), Vector(pt.x+r, pt.y, pt.z+h))
- self.assertCircle(base, Vector(pt.x, pt.y, pt.z), r)
-
- def assertConeAt(self, solid, pt, r1, r2, h):
- """Verify that solid is a cone at the specified location."""
- self.assertEqual(len(solid.Edges), 3)
-
- lid = solid.Edges[0]
- hull = solid.Edges[1]
- base = solid.Edges[2]
-
- self.assertCircle(lid, Vector(pt.x, pt.y, pt.z+h), r2)
- self.assertLine(hull, Vector(pt.x+r1, pt.y, pt.z), Vector(pt.x+r2, pt.y, pt.z+h))
- self.assertCircle(base, Vector(pt.x, pt.y, pt.z), r1)
-
- def assertCircle(self, edge, pt, r):
- """Verivy that edge is a circle at given location."""
- curve = edge.Curve
- self.assertIs(type(curve), Part.Circle)
- self.assertCoincide(curve.Center, Vector(pt.x, pt.y, pt.z))
- self.assertAbout(curve.Radius, r)
-
- def assertLine(self, edge, pt1, pt2):
- """Verify that edge is a line from pt1 to pt2."""
- curve = edge.Curve
- self.assertIs(type(curve), Part.Line)
- self.assertCoincide(curve.StartPoint, pt1)
- self.assertCoincide(curve.EndPoint, pt2)
-
- def assertCoincide(self, pt1, pt2):
- """Verify that 2 points coincide (with tolerance)."""
- self.assertAbout(pt1.x, pt2.x)
- self.assertAbout(pt1.y, pt2.y)
- self.assertAbout(pt1.z, pt2.z)
-
- def assertAbout(self, v1, v2):
- """Verify that 2 values are the same (accounting for float imprecision)."""
- if math.fabs(v1 - v2) > slack:
- self.fail("%f != %f" % (v1, v2))
-
- def assertLines(self, edgs, tail, points):
- """Check that there are 5 edges forming a trapezoid."""
- edges = list(edgs)
- if tail:
- edges.append(tail)
- self.assertEqual(len(edges), len(points) - 1)
-
- for i in range(0, len(edges)):
- self.assertLine(edges[i], points[i], points[i+1])
-
-class TestTag00BasicHolding(TagTestCaseBase):
- """Some basid test cases."""
-
- def test00(self,x=1, y=1):
- """Test getAngle."""
- self.assertAbout(getAngle(FreeCAD.Vector( 1*x, 0*y, 0)), 0)
- self.assertAbout(getAngle(FreeCAD.Vector( 1*x, 1*y, 0)), math.pi/4)
- self.assertAbout(getAngle(FreeCAD.Vector( 0*x, 1*y, 0)), math.pi/2)
- self.assertAbout(getAngle(FreeCAD.Vector(-1*x, 1*y, 0)), 3*math.pi/4)
- self.assertAbout(getAngle(FreeCAD.Vector(-1*x, 0*y, 0)), math.pi)
- self.assertAbout(getAngle(FreeCAD.Vector(-1*x,-1*y, 0)), -3*math.pi/4)
- self.assertAbout(getAngle(FreeCAD.Vector( 0*x,-1*y, 0)), -math.pi/2)
- self.assertAbout(getAngle(FreeCAD.Vector( 1*x,-1*y, 0)), -math.pi/4)
-
- def test01(self):
- """Test class Side."""
- self.assertEqual(Side.of(FreeCAD.Vector( 1, 0, 0), FreeCAD.Vector( 1, 0, 0)), Side.On)
- self.assertEqual(Side.of(FreeCAD.Vector( 1, 0, 0), FreeCAD.Vector(-1, 0, 0)), Side.On)
- self.assertEqual(Side.of(FreeCAD.Vector( 1, 0, 0), FreeCAD.Vector( 0, 1, 0)), Side.Left)
- self.assertEqual(Side.of(FreeCAD.Vector( 1, 0, 0), FreeCAD.Vector( 0,-1, 0)), Side.Right)
-
-
-class TestTag01BasicTag(TagTestCaseBase): # =============
- """Unit tests for the HoldingTags dressup."""
-
- def test00(self):
- """Check Tag origin, serialization and de-serialization."""
- tag = Tag(77, 13, 4, 5, 90, True)
- self.assertCoincide(tag.originAt(3), Vector(77, 13, 3))
- s = tag.toString()
- tagCopy = Tag.FromString(s)
- self.assertEqual(tag.x, tagCopy.x)
- self.assertEqual(tag.y, tagCopy.y)
- self.assertEqual(tag.height, tagCopy.height)
- self.assertEqual(tag.width, tagCopy.width)
- self.assertEqual(tag.enabled, tagCopy.enabled)
-
-
- def test01(self):
- """Verify solid and core for a 90 degree tag are identical cylinders."""
- tag = Tag(100, 200, 4, 5, 90, True)
- tag.createSolidsAt(17)
-
- self.assertIsNotNone(tag.solid)
- self.assertCylinderAt(tag.solid, Vector(100, 200, 17), 2, 5)
-
- self.assertIsNotNone(tag.core)
- self.assertCylinderAt(tag.core, Vector(100, 200, 17), 2, 5)
-
- def test02(self):
- """Verify trapezoidal tag has a cone shape with a lid, and cylinder core."""
- tag = Tag(0, 0, 18, 5, 45, True)
- tag.createSolidsAt(0)
-
- self.assertIsNotNone(tag.solid)
- self.assertConeAt(tag.solid, Vector(0,0,0), 9, 4, 5)
-
- self.assertIsNotNone(tag.core)
- self.assertCylinderAt(tag.core, Vector(0,0,0), 4, 5)
-
- def test03(self):
- """Verify pointy cone shape of tag with pointy end if width, angle and height match up."""
- tag = Tag(0, 0, 10, 5, 45, True)
- tag.createSolidsAt(0)
- self.assertIsNotNone(tag.solid)
- self.assertConeAt(tag.solid, Vector(0,0,0), 5, 0, 5)
-
- self.assertIsNone(tag.core)
-
- def test04(self):
- """Verify height adjustment if tag isn't wide eough for angle."""
- tag = Tag(0, 0, 5, 17, 60, True)
- tag.createSolidsAt(0)
- self.assertIsNotNone(tag.solid)
- self.assertConeAt(tag.solid, Vector(0,0,0), 2.5, 0, 2.5 * math.tan((60/180.0)*math.pi))
-
- self.assertIsNone(tag.core)
-
-class TestTag02SquareTag(TagTestCaseBase): # =============
- """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.Line(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.Line(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.Curve.StartPoint, edge.Curve.EndPoint)
- 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.Line(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.Curve.StartPoint, 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.Line(edge.Curve.EndPoint, 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.Line(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.Curve.StartPoint
- 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.Line(Vector(5, 0, 0), Vector(0, 0, 0)))
- i = tag.intersect(edge)
- edge = Part.Edge(Part.Line(edge.Curve.EndPoint, 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.Line(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.Curve.StartPoint
- p1 = Vector( 4, 0, 0)
- p2 = Vector( 4, 0, 3)
- p3 = Vector(-4, 0, 3)
- p4 = Vector(-4, 0, 0)
- p5 = edge.Curve.EndPoint
- 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.Line(Vector(5, 0, 0), Vector(+2, 0, 0)))
- e1 = Part.Edge(Part.Line(e0.Curve.EndPoint, Vector(+1, 0, 0)))
- e2 = Part.Edge(Part.Line(e1.Curve.EndPoint, Vector(+0.5, 0, 0)))
- e3 = Part.Edge(Part.Line(e2.Curve.EndPoint, Vector(-0.5, 0, 0)))
- e4 = Part.Edge(Part.Line(e3.Curve.EndPoint, Vector(-1, 0, 0)))
- e5 = Part.Edge(Part.Line(e4.Curve.EndPoint, Vector(-2, 0, 0)))
- e6 = Part.Edge(Part.Line(e5.Curve.EndPoint, 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.Curve.StartPoint, pt0, pt1, pt2, pt3, pt4, pt5, pt6, e6.Curve.StartPoint, e6.Curve.EndPoint])
- 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.Line(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.Line(Vector(5, 0, i), Vector(-5, 0, i)))
- s = tag.intersect(edge)
- self.assertTrue(s.isComplete())
- self.assertLine(s.tail, edge.Curve.StartPoint, edge.Curve.EndPoint)
-
-class TestTag03TrapezoidTag(TagTestCaseBase): # =============
- """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.Line(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.Line(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.Curve.StartPoint, edge.Curve.EndPoint)
- self.assertIsNone(i.tail)
-
- # now add another segment that doesn't reach the top of the cone
- edge = Part.Edge(Part.Line(edge.Curve.EndPoint, Vector(3, 0, 0)))
- i = i.intersect(edge)
- # still a P0 and edge fully consumed
- p1 = Vector(edge.Curve.StartPoint)
- p1.z = 0
- p2 = Vector(edge.Curve.EndPoint)
- 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.Line(edge.Curve.EndPoint, Vector(2, 0, 0)))
- i = i.intersect(edge)
- # still a P0 and edge fully consumed
- p3 = Vector(edge.Curve.EndPoint)
- 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.Line(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.Curve.StartPoint, 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.Line(edge.Curve.EndPoint, 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.Line(Vector(5, 0, 0), Vector(-1, 0, 0)))
-
- i = tag.intersect(edge)
- self.assertEqual(i.state, Tag.Intersection.P2)
- p0 = Vector(edge.Curve.StartPoint)
- 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.Line(Vector(5, 0, 0), Vector(1, 0, 0)))
- i = tag.intersect(edge)
- edge = Part.Edge(Part.Line(edge.Curve.EndPoint, 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.Line(Vector(5, 0, 0), Vector(0.5, 0, 0)))
- i = tag.intersect(edge)
- edge = Part.Edge(Part.Line(edge.Curve.EndPoint, 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.Line(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.Line(edge.Curve.EndPoint, 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.Line(edge.Curve.EndPoint, 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.Line(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.Line(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.Curve.EndPoint])
- 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.Line(Vector(5, 0, 0), Vector(+4, 0, 0)))
- e1 = Part.Edge(Part.Line(e0.Curve.EndPoint, Vector(+2, 0, 0)))
- e2 = Part.Edge(Part.Line(e1.Curve.EndPoint, Vector(+0.5, 0, 0)))
- e3 = Part.Edge(Part.Line(e2.Curve.EndPoint, Vector(-0.5, 0, 0)))
- e4 = Part.Edge(Part.Line(e3.Curve.EndPoint, Vector(-1, 0, 0)))
- e5 = Part.Edge(Part.Line(e4.Curve.EndPoint, Vector(-2, 0, 0)))
- e6 = Part.Edge(Part.Line(e5.Curve.EndPoint, 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.Curve.StartPoint, p0, p1, p2, p3, p4, p5, p6, p7, e6.Curve.EndPoint])
- 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.Line(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.Line(Vector(5, 0, i), Vector(-5, 0, i)))
- s = tag.intersect(edge)
- self.assertTrue(s.isComplete())
- self.assertLine(s.tail, edge.Curve.StartPoint, edge.Curve.EndPoint)
-
-
-class TestTag04TriangularTag(TagTestCaseBase): # ========================
- """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.Line(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.Curve.StartPoint, edge.Curve.EndPoint)
- 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.Line(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.Curve.StartPoint, p1, p2])
- self.assertIsNone(i.tail)
-
- # verify we stay in P1 if we add another segment
- edge = Part.Edge(Part.Line(edge.Curve.EndPoint, 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.Line(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.Line(edge.Curve.EndPoint, 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.Line(p0, edge.Curve.EndPoint))
- 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.Line(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.Line(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)
-
diff --git a/src/Mod/Path/PathTests/TestPathGeom.py b/src/Mod/Path/PathTests/TestPathGeom.py
index fbbc5def3..720857bc5 100644
--- a/src/Mod/Path/PathTests/TestPathGeom.py
+++ b/src/Mod/Path/PathTests/TestPathGeom.py
@@ -121,3 +121,27 @@ class TestPathGeom(PathTestBase):
PathGeom.edgeForCmd(
Path.Command('G3', {'X': p2.x, 'Y': p2.y, 'Z': p2.z, 'I': 1, 'J': 0, 'K': -1}), p1),
p1, Vector(-1/math.sqrt(2), -1/math.sqrt(2), 1), p2)
+
+ def test50(self):
+ """Verify proper wire(s) aggregation from a Path."""
+ commands = []
+ commands.append(Path.Command('G1', {'X': 1}))
+ commands.append(Path.Command('G1', {'Y': 1}))
+ commands.append(Path.Command('G0', {'X': 0}))
+ commands.append(Path.Command('G1', {'Y': 0}))
+
+ wire = PathGeom.wireForPath(Path.Path(commands))
+ self.assertEqual(len(wire.Edges), 4)
+ self.assertLine(wire.Edges[0], Vector(0,0,0), Vector(1,0,0))
+ self.assertLine(wire.Edges[1], Vector(1,0,0), Vector(1,1,0))
+ self.assertLine(wire.Edges[2], Vector(1,1,0), Vector(0,1,0))
+ self.assertLine(wire.Edges[3], Vector(0,1,0), Vector(0,0,0))
+
+ wires = PathGeom.wiresForPath(Path.Path(commands))
+ self.assertEqual(len(wires), 2)
+ self.assertEqual(len(wires[0].Edges), 2)
+ self.assertLine(wires[0].Edges[0], Vector(0,0,0), Vector(1,0,0))
+ self.assertLine(wires[0].Edges[1], Vector(1,0,0), Vector(1,1,0))
+ self.assertEqual(len(wires[1].Edges), 1)
+ self.assertLine(wires[1].Edges[0], Vector(0,1,0), Vector(0,0,0))
+
diff --git a/src/Mod/Path/TestPathApp.py b/src/Mod/Path/TestPathApp.py
index 83e8a2bc0..5f246e8c7 100644
--- a/src/Mod/Path/TestPathApp.py
+++ b/src/Mod/Path/TestPathApp.py
@@ -26,10 +26,4 @@ import TestApp
from PathTests.TestPathPost import PathPostTestCases
-from PathTests.TestPathDressupHoldingTags import TestTag00BasicHolding
-from PathTests.TestPathDressupHoldingTags import TestTag01BasicTag
-from PathTests.TestPathDressupHoldingTags import TestTag02SquareTag
-from PathTests.TestPathDressupHoldingTags import TestTag03TrapezoidTag
-from PathTests.TestPathDressupHoldingTags import TestTag04TriangularTag
-
from PathTests.TestPathGeom import TestPathGeom