Added support for rounded tags.
This commit is contained in:
parent
126fe81689
commit
9d544209a8
|
@ -101,6 +101,20 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Radius</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="Gui::InputField" name="ifRadius">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Radius of the fillet at the top.</p><p>If the radius is too big for the tag shape it gets reduced to the maximum possible radius - resulting in a spherical shape.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -20,13 +20,6 @@
|
|||
<string>Tag Parameters</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Default Width</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="Gui::InputField" name="ifWidth">
|
||||
<property name="toolTip">
|
||||
|
@ -34,24 +27,10 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Default Height</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="Gui::InputField" name="ifHeight">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Default height of holding tags.</p><p>If the specified height is 0 the dressup will use half the height of the part. Should the height be bigger than the height of the part the dressup will reduce the height to the height of the part.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Default Angle</string>
|
||||
<string>Default Width</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -74,6 +53,41 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="Gui::InputField" name="ifHeight">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Default height of holding tags.</p><p>If the specified height is 0 the dressup will use half the height of the part. Should the height be bigger than the height of the part the dressup will reduce the height to the height of the part.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Default Angle</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Default Height</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="Gui::InputField" name="ifRadius">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Radius of the fillet on the tag's top edge.</p><p>If the radius is bigger than than the tag shape itself supports the resulting shape will be that of a dome.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Default Radius</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -45,16 +45,8 @@ from pivy import coin
|
|||
"""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)
|
||||
def translate(text, context = "PathDressup_HoldingTags", disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
debugDressup = False
|
||||
|
||||
|
@ -106,10 +98,11 @@ def debugCone(vector, r1, r2, height, label, color = None):
|
|||
|
||||
|
||||
class HoldingTagsPreferences:
|
||||
DefaultHoldingTagWidth = 'DefaultHoldingTagWidth'
|
||||
DefaultHoldingTagHeight = 'DefaultHoldingTagHeight'
|
||||
DefaultHoldingTagAngle = 'DefaultHoldingTagAngle'
|
||||
DefaultHoldingTagCount = 'DefaultHoldingTagCount'
|
||||
DefaultHoldingTagWidth = 'DefaultHoldingTagWidth'
|
||||
DefaultHoldingTagHeight = 'DefaultHoldingTagHeight'
|
||||
DefaultHoldingTagAngle = 'DefaultHoldingTagAngle'
|
||||
DefaultHoldingTagRadius = 'DefaultHoldingTagRadius'
|
||||
DefaultHoldingTagCount = 'DefaultHoldingTagCount'
|
||||
|
||||
@classmethod
|
||||
def defaultWidth(cls, ifNotSet):
|
||||
|
@ -139,14 +132,20 @@ class HoldingTagsPreferences:
|
|||
return float(ifNotSet)
|
||||
return float(value)
|
||||
|
||||
@classmethod
|
||||
def defaultRadius(cls, ifNotSet = 0.0):
|
||||
return PathPreferences.preferences().GetFloat(cls.DefaultHoldingTagRadius, ifNotSet)
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.form = FreeCADGui.PySideUic.loadUi(":/preferences/PathDressupHoldingTags.ui")
|
||||
self.label = translate('PathDressup_HoldingTags', 'Holding Tags')
|
||||
self.label = translate('Holding Tags')
|
||||
|
||||
def loadSettings(self):
|
||||
self.form.ifWidth.setText(FreeCAD.Units.Quantity(self.defaultWidth(0), FreeCAD.Units.Length).UserString)
|
||||
self.form.ifHeight.setText(FreeCAD.Units.Quantity(self.defaultHeight(0), FreeCAD.Units.Length).UserString)
|
||||
self.form.dsbAngle.setValue(self.defaultAngle())
|
||||
self.form.ifRadius.setText(FreeCAD.Units.Quantity(self.defaultRadius(), FreeCAD.Units.Length).UserString)
|
||||
self.form.sbCount.setValue(self.defaultCount())
|
||||
|
||||
def saveSettings(self):
|
||||
|
@ -154,6 +153,7 @@ class HoldingTagsPreferences:
|
|||
pref.SetFloat(self.DefaultHoldingTagWidth, FreeCAD.Units.Quantity(self.form.ifWidth.text()).Value)
|
||||
pref.SetFloat(self.DefaultHoldingTagHeight, FreeCAD.Units.Quantity(self.form.ifHeight.text()).Value)
|
||||
pref.SetFloat(self.DefaultHoldingTagAngle, self.form.dsbAngle.value())
|
||||
pref.SetFloat(self.DefaultHoldingTagRadius, FreeCAD.Units.Quantity(self.form.ifRadius.text()))
|
||||
pref.SetUnsigned(self.DefaultHoldingTagCount, self.form.sbCount.value())
|
||||
|
||||
@classmethod
|
||||
|
@ -161,14 +161,15 @@ class HoldingTagsPreferences:
|
|||
return HoldingTagsPreferences()
|
||||
|
||||
class Tag:
|
||||
def __init__(self, x, y, width, height, angle, enabled=True):
|
||||
debugPrint("Tag(%.2f, %.2f, %.2f, %.2f, %.2f, %d)" % (x, y, width, height, angle/math.pi, enabled))
|
||||
def __init__(self, x, y, width, height, angle, radius, enabled=True):
|
||||
debugPrint("Tag(%.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d)" % (x, y, width, height, angle, radius, enabled))
|
||||
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.radius = radius
|
||||
self.enabled = enabled
|
||||
|
||||
def fullWidth(self):
|
||||
|
@ -190,15 +191,24 @@ class Tag:
|
|||
self.r1 = r1
|
||||
self.r2 = r1
|
||||
height = self.height * 1.01
|
||||
radius = 0
|
||||
if self.angle == 90 and height > 0:
|
||||
# cylinder
|
||||
self.solid = Part.makeCylinder(r1, height)
|
||||
radius = min(min(self.radius, r1), self.height)
|
||||
debugPrint("Part.makeCone(%f, %f)" % (r1, height))
|
||||
elif self.angle > 0.0 and height > 0.0:
|
||||
tangens = math.tan(math.radians(self.angle))
|
||||
# cone
|
||||
rad = math.radians(self.angle)
|
||||
tangens = math.tan(rad)
|
||||
dr = height / tangens
|
||||
if dr < r1:
|
||||
# with top
|
||||
r2 = r1 - dr
|
||||
s = height / math.sin(rad)
|
||||
radius = min(r2, s) * math.tan((math.pi - rad)/2) * 0.95
|
||||
else:
|
||||
# triangular
|
||||
r2 = 0
|
||||
height = r1 * tangens * 1.01
|
||||
self.actualHeight = height
|
||||
|
@ -215,9 +225,13 @@ class Tag:
|
|||
self.solid.rotate(FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), angle)
|
||||
debugPrint("solid.translate(%s)" % self.originAt(z))
|
||||
self.solid.translate(self.originAt(z - 0.01 * height))
|
||||
self.realRadius = radius
|
||||
if radius != 0:
|
||||
debugPrint("makeFillet(%.4f)" % radius)
|
||||
self.solid = self.solid.makeFillet(radius, [self.solid.Edges[0]])
|
||||
|
||||
def filterIntersections(self, pts, face):
|
||||
if type(face.Surface) == Part.Cone or type(face.Surface) == Part.Cylinder:
|
||||
if type(face.Surface) == Part.Cone or type(face.Surface) == Part.Cylinder or type(face.Surface) == Part.Toroid:
|
||||
#print("it's a cone/cylinder, checking z")
|
||||
return filter(lambda pt: pt.z >= self.bottom() and pt.z <= self.top(), pts)
|
||||
if type(face.Surface) == Part.Plane:
|
||||
|
@ -225,7 +239,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)
|
||||
|
@ -249,26 +263,9 @@ class Tag:
|
|||
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))
|
||||
|
||||
pts = []
|
||||
for index, face in enumerate(solid.Faces):
|
||||
i = edge.Curve.intersect(face.Surface)[0]
|
||||
#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)))
|
||||
for p in ps:
|
||||
included = '+' if p in filtered else '-'
|
||||
#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))
|
||||
return closest
|
||||
|
||||
#print("-------- -> None")
|
||||
vertexes = edge.common(solid).Vertexes
|
||||
if vertexes:
|
||||
return sorted(vertexes, key=lambda v: (v.Point - refPt).Length)[0].Point
|
||||
return None
|
||||
|
||||
def intersects(self, edge, param):
|
||||
|
@ -277,6 +274,18 @@ class Tag:
|
|||
return self.nextIntersectionClosestTo(edge, self.solid, edge.valueAt(param))
|
||||
return None
|
||||
|
||||
def bbEdges(self):
|
||||
edges = []
|
||||
for i in range(12):
|
||||
p1, p2 = self.solid.BoundBox.getEdge(i)
|
||||
edges.append(Part.Edge(Part.LineSegment(p1, p2)))
|
||||
return edges
|
||||
|
||||
def bbShow(self):
|
||||
for e in self.bbEdges():
|
||||
Part.show(e)
|
||||
|
||||
|
||||
class MapWireToTag:
|
||||
def __init__(self, edge, tag, i):
|
||||
debugEdge(edge, 'MapWireToTag(%.2f, %.2f, %.2f)' % (i.x, i.y, i.z))
|
||||
|
@ -294,9 +303,14 @@ class MapWireToTag:
|
|||
debugEdge(e, '++++++++ .')
|
||||
self.commands = PathGeom.cmdsForEdge(e)
|
||||
debugEdge(tail, '.........-')
|
||||
self.initialEdge = edge
|
||||
self.tail = tail
|
||||
self.edges = []
|
||||
self.entry = i
|
||||
if tail:
|
||||
debugPrint("MapWireToTag(%s - %s)" % (i, tail.valueAt(tail.FirstParameter)))
|
||||
else:
|
||||
debugPrint("MapWireToTag(%s - )" % i)
|
||||
self.complete = False
|
||||
self.haveProblem = False
|
||||
|
||||
|
@ -322,63 +336,114 @@ class MapWireToTag:
|
|||
return 2
|
||||
return 0
|
||||
|
||||
def cleanupEdges(self, edges, baseEdge):
|
||||
def cleanupEdges(self, edges):
|
||||
# want to remove all edges from the wire itself, and all internal struts
|
||||
#print("+cleanupEdges")
|
||||
#print(" base:")
|
||||
debugEdge(baseEdge, ' ')
|
||||
#print(" edges:")
|
||||
for e in edges:
|
||||
debugEdge(e, ' ')
|
||||
#print(":")
|
||||
self.edgesCleanup = [copy.copy(edges)]
|
||||
|
||||
haveEntry = False
|
||||
# remove any edge that has a point inside the tag solid
|
||||
# and collect all edges that are connected to the entry and/or exit
|
||||
self.entryEdges = []
|
||||
self.exitEdges = []
|
||||
self.edgePoints = []
|
||||
for e in copy.copy(edges):
|
||||
if PathGeom.edgesMatch(e, baseEdge):
|
||||
debugEdge(e, '......... X0')
|
||||
p1 = e.valueAt(e.FirstParameter)
|
||||
p2 = e.valueAt(e.LastParameter)
|
||||
self.edgePoints.append(p1)
|
||||
self.edgePoints.append(p2)
|
||||
if self.tag.solid.isInside(p1, PathGeom.Tolerance, False) or self.tag.solid.isInside(p2, PathGeom.Tolerance, False):
|
||||
edges.remove(e)
|
||||
elif self.isStrut(e):
|
||||
typ = self.isEntryOrExitStrut(e)
|
||||
debugEdge(e, '......... |%d' % typ)
|
||||
if 0 == typ: # neither entry nor exit
|
||||
debugEdge(e, '......... X1')
|
||||
edges.remove(e)
|
||||
elif 1 == typ:
|
||||
haveEntry = True
|
||||
debugEdge(e, '......... X0', False)
|
||||
else:
|
||||
if PathGeom.pointsCoincide(p1, self.entry) or PathGeom.pointsCoincide(p2, self.entry):
|
||||
self.entryEdges.append(e)
|
||||
if PathGeom.pointsCoincide(p1, self.exit) or PathGeom.pointsCoincide(p2, self.exit):
|
||||
self.exitEdges.append(e)
|
||||
self.edgesCleanup.append(copy.copy(edges))
|
||||
|
||||
# if there are no edges connected to entry/exit, it means the plunge in/out is vertical
|
||||
# we need to add in the missing segment and collect the new entry/exit edges.
|
||||
if not self.entryEdges:
|
||||
print("fill entryEdges ...")
|
||||
self.realEntry = sorted(self.edgePoints, key=lambda p: (p - self.entry).Length)[0]
|
||||
self.entryEdges = filter(lambda e: PathGeom.edgeConnectsTo(e, self.realEntry), edges)
|
||||
edges.append(Part.Edge(Part.LineSegment(self.entry, self.realEntry)))
|
||||
else:
|
||||
self.realEntry = None
|
||||
if not self.exitEdges:
|
||||
print("fill exitEdges ...")
|
||||
self.realExit = sorted(self.edgePoints, key=lambda p: (p - self.exit).Length)[0]
|
||||
self.exitEdges = filter(lambda e: PathGeom.edgeConnectsTo(e, self.realExit), edges)
|
||||
edges.append(Part.Edge(Part.LineSegment(self.realExit, self.exit)))
|
||||
else:
|
||||
self.realExit = None
|
||||
self.edgesCleanup.append(copy.copy(edges))
|
||||
|
||||
# if there are 2 edges attached to entry/exit, throw away the one that is "lower"
|
||||
if len(self.entryEdges) > 1:
|
||||
debugEdge(self.entryEdges[0], ' entry[0]', False)
|
||||
debugEdge(self.entryEdges[1], ' entry[1]', False)
|
||||
if self.entryEdges[0].BoundBox.ZMax < self.entryEdges[1].BoundBox.ZMax:
|
||||
edges.remove(self.entryEdges[0])
|
||||
debugEdge(e, '......... X1', False)
|
||||
else:
|
||||
edges.remove(self.entryEdges[1])
|
||||
debugEdge(e, '......... X2', False)
|
||||
if len(self.exitEdges) > 1:
|
||||
debugEdge(self.exitEdges[0], ' exit[0]', False)
|
||||
debugEdge(self.exitEdges[1], ' exit[1]', False)
|
||||
if self.exitEdges[0].BoundBox.ZMax < self.exitEdges[1].BoundBox.ZMax:
|
||||
if self.exitEdges[0] in edges:
|
||||
edges.remove(self.exitEdges[0])
|
||||
debugEdge(e, '......... X3', False)
|
||||
else:
|
||||
if self.exitEdges[1] in edges:
|
||||
edges.remove(self.exitEdges[1])
|
||||
debugEdge(e, '......... X4', False)
|
||||
|
||||
self.edgesCleanup.append(copy.copy(edges))
|
||||
return edges
|
||||
|
||||
def orderAndFlipEdges(self, edges):
|
||||
#print("entry(%.2f, %.2f, %.2f), exit(%.2f, %.2f, %.2f)" % (self.entry.x, self.entry.y, self.entry.z, self.exit.x, self.exit.y, self.exit.z))
|
||||
# the remaininng edges for the path from xy(baseEdge) along the tags surface
|
||||
self.edgesOrder = []
|
||||
outputEdges = []
|
||||
p0 = baseEdge.valueAt(baseEdge.FirstParameter)
|
||||
ignoreZ = False
|
||||
if not haveEntry:
|
||||
ignoreZ = True
|
||||
p0 = PathGeom.xy(p0)
|
||||
p0 = self.entry
|
||||
lastP = p0
|
||||
while edges:
|
||||
#print("(%.2f, %.2f, %.2f) %d %d" % (p0.x, p0.y, p0.z, haveEntry, ignoreZ))
|
||||
#print("(%.2f, %.2f, %.2f) %d %d" % (p0.x, p0.y, p0.z))
|
||||
for e in edges:
|
||||
p1 = e.valueAt(e.FirstParameter)
|
||||
p2 = e.valueAt(e.LastParameter)
|
||||
if PathGeom.pointsCoincide(PathGeom.xy(p1) if ignoreZ else p1, p0):
|
||||
if PathGeom.pointsCoincide(p1, p0):
|
||||
outputEdges.append((e, False))
|
||||
edges.remove(e)
|
||||
lastP = None
|
||||
ignoreZ = False
|
||||
p0 = p2
|
||||
debugEdge(e, ">>>>> no flip")
|
||||
break
|
||||
elif PathGeom.pointsCoincide(PathGeom.xy(p2) if ignoreZ else p2, p0):
|
||||
elif PathGeom.pointsCoincide(p2, p0):
|
||||
outputEdges.append((e, True))
|
||||
edges.remove(e)
|
||||
lastP = None
|
||||
ignoreZ = False
|
||||
p0 = p1
|
||||
debugEdge(e, ">>>>> flip")
|
||||
break
|
||||
else:
|
||||
debugEdge(e, "<<<<< (%.2f, %.2f, %.2f)" % (p0.x, p0.y, p0.z))
|
||||
if lastP == p0:
|
||||
self.edgesOrder.append(outputEdges)
|
||||
self.edgesOrder.append(edges)
|
||||
print('ordered edges:')
|
||||
for e, flip in outputEdges:
|
||||
debugEdge(e, ' %c ' % ('<' if flip else '>'), False)
|
||||
print('remaining edges:')
|
||||
for e in edges:
|
||||
debugEdge(e, ' ', False)
|
||||
raise ValueError("No connection to %s" % (p0))
|
||||
elif lastP:
|
||||
debugPrint("xxxxxx (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)" % (p0.x, p0.y, p0.z, lastP.x, lastP.y, lastP.z))
|
||||
|
@ -393,40 +458,50 @@ class MapWireToTag:
|
|||
p2 = PathGeom.xy(edge.valueAt(edge.LastParameter))
|
||||
return PathGeom.pointsCoincide(p1, p2)
|
||||
|
||||
def cmdsForEdge(self, edge):
|
||||
cmds = []
|
||||
|
||||
# OCC doesn't like it very much if the shapes align with each other. So if we have a slightly
|
||||
# extended edge for the last edge in list we'll use that instead for stable results.
|
||||
if PathGeom.pointsCoincide(edge.valueAt(edge.FirstParameter), self.lastEdge.valueAt(self.lastEdge.FirstParameter)):
|
||||
shell = self.lastEdge.extrude(FreeCAD.Vector(0, 0, self.tag.height + 1))
|
||||
def shell(self):
|
||||
if len(self.edges) > 1:
|
||||
wire = Part.Wire(self.initialEdge)
|
||||
else:
|
||||
shell = edge.extrude(FreeCAD.Vector(0, 0, self.tag.height + 1))
|
||||
shape = shell.common(self.tag.solid)
|
||||
edge = self.edges[0]
|
||||
if PathGeom.pointsCoincide(edge.valueAt(edge.FirstParameter), self.finalEdge.valueAt(self.finalEdge.FirstParameter)):
|
||||
wire = Part.Wire(self.finalEdge)
|
||||
elif hasattr(self, 'initialEdge') and PathGeom.pointsCoincide(edge.valueAt(edge.FirstParameter), self.initialEdge.valueAt(self.initialEdge.FirstParameter)):
|
||||
wire = Part.Wire(self.initialEdge)
|
||||
else:
|
||||
wire = Part.Wire(edge)
|
||||
|
||||
if not shape.Edges:
|
||||
self.haveProblem = True
|
||||
for edge in self.edges[1:]:
|
||||
if PathGeom.pointsCoincide(edge.valueAt(edge.FirstParameter), self.finalEdge.valueAt(self.finalEdge.FirstParameter)):
|
||||
wire.add(self.finalEdge)
|
||||
else:
|
||||
wire.add(edge)
|
||||
|
||||
for e,flip in self.cleanupEdges(shape.Edges, edge):
|
||||
debugEdge(e, '++++++++ %s' % ('.' if not flip else '@'))
|
||||
cmds.extend(PathGeom.cmdsForEdge(e, flip, False))
|
||||
return cmds
|
||||
shell = wire.extrude(FreeCAD.Vector(0, 0, self.tag.height + 1))
|
||||
return shell.removeShape(filter(lambda f: PathGeom.isRoughly(f.Area, 0), shell.Faces))
|
||||
|
||||
def commandsForEdges(self):
|
||||
commands = []
|
||||
for e in self.edges:
|
||||
if self.isStrut(e):
|
||||
continue
|
||||
commands.extend(self.cmdsForEdge(e))
|
||||
return commands
|
||||
if self.edges:
|
||||
shape = self.shell().common(self.tag.solid)
|
||||
commands = []
|
||||
for e,flip in self.orderAndFlipEdges(self.cleanupEdges(shape.Edges)):
|
||||
debugEdge(e, '++++++++ %s' % ('<' if flip else '>'), False)
|
||||
commands.extend(PathGeom.cmdsForEdge(e, flip, False))
|
||||
return commands
|
||||
return []
|
||||
|
||||
def add(self, edge):
|
||||
self.tail = None
|
||||
self.lastEdge = edge # see cmdsForEdge
|
||||
if self.tag.solid.isInside(edge.valueAt(edge.LastParameter), 0.000001, True):
|
||||
self.finalEdge = edge
|
||||
if self.tag.solid.isInside(edge.valueAt(edge.LastParameter), PathGeom.Tolerance, True):
|
||||
self.addEdge(edge)
|
||||
else:
|
||||
i = self.tag.intersects(edge, edge.LastParameter)
|
||||
if not i:
|
||||
self.offendingEdge = edge
|
||||
debugEdge(edge, 'offending Edge:', False)
|
||||
o = self.tag.originAt(self.tag.z)
|
||||
print('originAt: (%.2f, %.2f, %.2f)' % (o.x, o.y, o.z))
|
||||
i = edge.valueAt(edge.FirstParameter)
|
||||
if PathGeom.pointsCoincide(i, edge.valueAt(edge.FirstParameter)):
|
||||
self.tail = edge
|
||||
else:
|
||||
|
@ -458,7 +533,7 @@ class _RapidEdges:
|
|||
def debugPrint(self):
|
||||
debugPrint('rapid:')
|
||||
for r in self.rapid:
|
||||
debugEdge(r, ' ', True)
|
||||
debugEdge(r, ' ')
|
||||
|
||||
class PathData:
|
||||
def __init__(self, obj):
|
||||
|
@ -501,7 +576,7 @@ class PathData:
|
|||
edges = sorted(self.base.Edges, key=lambda e: e.Length)
|
||||
return (edges[0], edges[-1])
|
||||
|
||||
def generateTags(self, obj, count, width=None, height=None, angle=None, spacing=None):
|
||||
def generateTags(self, obj, count, width=None, height=None, angle=None, radius=None, spacing=None):
|
||||
debugPrint("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)
|
||||
|
@ -509,18 +584,12 @@ class PathData:
|
|||
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.defaultTagWidth()
|
||||
if height:
|
||||
H = height
|
||||
else:
|
||||
H = self.defaultTagHeight()
|
||||
tagDistance = self.base.Length / (count if count else 4)
|
||||
|
||||
W = width if width else self.defaultTagWidth()
|
||||
H = height if height else self.defaultTagHeight()
|
||||
A = angle if angle else self.defaultTagAngle()
|
||||
R = radius if radius else self.defaultTagRadius()
|
||||
|
||||
|
||||
# start assigning tags on the longest segment
|
||||
|
@ -568,7 +637,7 @@ class PathData:
|
|||
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))
|
||||
tags.append(Tag(tag.x, tag.y, W, H, A, R, True))
|
||||
|
||||
return tags
|
||||
|
||||
|
@ -604,6 +673,9 @@ class PathData:
|
|||
def defaultTagAngle(self):
|
||||
return HoldingTagsPreferences.defaultAngle()
|
||||
|
||||
def defaultTagRadius(self):
|
||||
return HoldingTagsPreferences.defaultRadius()
|
||||
|
||||
def sortedTags(self, tags):
|
||||
ordered = []
|
||||
for edge in self.base.Edges:
|
||||
|
@ -613,6 +685,7 @@ class PathData:
|
|||
ordered.append(t)
|
||||
# disable all tags that are not on the base wire.
|
||||
for tag in tags:
|
||||
FreeCAD.Console.PrintMessage("Tag #%d not on base wire - disabling\n" % len(ordered))
|
||||
tag.enabled = False
|
||||
ordered.append(tag)
|
||||
return ordered
|
||||
|
@ -632,6 +705,7 @@ class ObjectDressup:
|
|||
obj.addProperty("App::PropertyLength", "Width", "Tag", QtCore.QT_TRANSLATE_NOOP("PathDressup_HoldingTags", "Width of tags."))
|
||||
obj.addProperty("App::PropertyLength", "Height", "Tag", QtCore.QT_TRANSLATE_NOOP("PathDressup_HoldingTags", "Height of tags."))
|
||||
obj.addProperty("App::PropertyAngle", "Angle", "Tag", QtCore.QT_TRANSLATE_NOOP("PathDressup_HoldingTags", "Angle of tag plunge and ascent."))
|
||||
obj.addProperty("App::PropertyLength", "Radius", "Tag", QtCore.QT_TRANSLATE_NOOP("PathDressup_HoldingTags", "Radius of the fillet on the top the tag."))
|
||||
obj.addProperty("App::PropertyVectorList", "Positions", "Tag", QtCore.QT_TRANSLATE_NOOP("PathDressup_HoldingTags", "Locations of insterted holding tags"))
|
||||
obj.addProperty("App::PropertyIntegerList", "Disabled", "Tag", QtCore.QT_TRANSLATE_NOOP("PathDressup_HoldingTags", "Ids of disabled holding tags"))
|
||||
obj.Proxy = self
|
||||
|
@ -644,7 +718,7 @@ class ObjectDressup:
|
|||
|
||||
def generateTags(self, obj, count):
|
||||
if hasattr(self, "pathData"):
|
||||
self.tags = self.pathData.generateTags(obj, count, obj.Width.Value, obj.Height.Value, obj.Angle, None)
|
||||
self.tags = self.pathData.generateTags(obj, count, obj.Width.Value, obj.Height.Value, obj.Angle, obj.Radius.Value, None)
|
||||
obj.Positions = [tag.originAt(0) for tag in self.tags]
|
||||
obj.Disabled = []
|
||||
return False
|
||||
|
@ -726,27 +800,31 @@ class ObjectDressup:
|
|||
def createTagsPositionDisabled(self, obj, positionsIn, disabledIn):
|
||||
rawTags = []
|
||||
for i, pos in enumerate(positionsIn):
|
||||
tag = Tag(pos.x, pos.y, obj.Width.Value, obj.Height.Value, obj.Angle, not i in disabledIn)
|
||||
tag = Tag(pos.x, pos.y, obj.Width.Value, obj.Height.Value, obj.Angle, obj.Radius, not i in disabledIn)
|
||||
tag.createSolidsAt(self.pathData.minZ, self.toolRadius)
|
||||
rawTags.append(tag)
|
||||
# disable all tags that intersect with their previous tag
|
||||
bb = None
|
||||
prev = None
|
||||
tags = []
|
||||
positions = []
|
||||
disabled = []
|
||||
for i, tag in enumerate(self.pathData.sortedTags(rawTags)):
|
||||
if tag.enabled:
|
||||
if bb:
|
||||
if bb.intersect(tag.solid.BoundBox):
|
||||
if prev:
|
||||
if prev.solid.common(tag.solid).Faces:
|
||||
FreeCAD.Console.PrintMessage("Tag #%d intersects with previous tag - disabling\n" % i)
|
||||
debugPrint("this tag = %d [%s]" % (i, tag.solid.BoundBox))
|
||||
tag.enabled = False
|
||||
elif self.pathData.edges:
|
||||
e = self.pathData.edges[0]
|
||||
p0 = e.valueAt(e.FirstParameter)
|
||||
p1 = e.valueAt(e.LastParameter)
|
||||
if tag.solid.BoundBox.isInside(p0) or tag.solid.BoundBox.isInside(p1):
|
||||
if tag.solid.isInside(p0, PathGeom.Tolerance, True) or tag.solid.isInside(p1, PathGeom.Tolerance, True):
|
||||
FreeCAD.Console.PrintMessage("Tag #%d intersects with starting point - disabling\n" % i)
|
||||
tag.enabled = False
|
||||
if tag.enabled:
|
||||
bb = tag.solid.BoundBox
|
||||
prev = tag
|
||||
debugPrint("previousTag = %d [%s]" % (i, prev))
|
||||
else:
|
||||
disabled.append(i)
|
||||
tags.append(tag)
|
||||
|
@ -779,7 +857,7 @@ class ObjectDressup:
|
|||
if hasattr(obj, "Positions"):
|
||||
self.tags, positions, disabled = self.createTagsPositionDisabled(obj, obj.Positions, obj.Disabled)
|
||||
if obj.Disabled != disabled:
|
||||
printDebug("Updating properties.... %s vs. %s" % (obj.Disabled, disabled))
|
||||
debugPrint("Updating properties.... %s vs. %s" % (obj.Disabled, disabled))
|
||||
obj.Positions = positions
|
||||
obj.Disabled = disabled
|
||||
|
||||
|
@ -796,23 +874,23 @@ class ObjectDressup:
|
|||
for tag in self.tags:
|
||||
tagID += 1
|
||||
if tag.enabled:
|
||||
print("x=%s, y=%s, z=%s" % (tag.x, tag.y, self.pathData.minZ))
|
||||
debugMarker(FreeCAD.Vector(tag.x, tag.y, self.pathData.minZ), "tag-%02d" % tagID , (1.0, 0.0, 1.0), 0.5)
|
||||
if tag.angle != 90:
|
||||
debugCone(tag.originAt(self.pathData.minZ), tag.r1, tag.r2, tag.actualHeight, "tag-%02d" % tagID)
|
||||
else:
|
||||
debugCylinder(tag.originAt(self.pathData.minZ), tag.fullWidth()/2, tag.actualHeight, "tag-%02d" % tagID)
|
||||
debugPrint("x=%s, y=%s, z=%s" % (tag.x, tag.y, self.pathData.minZ))
|
||||
#debugMarker(FreeCAD.Vector(tag.x, tag.y, self.pathData.minZ), "tag-%02d" % tagID , (1.0, 0.0, 1.0), 0.5)
|
||||
#if tag.angle != 90:
|
||||
# debugCone(tag.originAt(self.pathData.minZ), tag.r1, tag.r2, tag.actualHeight, "tag-%02d" % tagID)
|
||||
#else:
|
||||
# debugCylinder(tag.originAt(self.pathData.minZ), tag.fullWidth()/2, tag.actualHeight, "tag-%02d" % tagID)
|
||||
|
||||
obj.Path = self.createPath(self.pathData.edges, self.tags, self.pathData.rapid)
|
||||
#print("execute - done")
|
||||
|
||||
def setup(self, obj, generate=False):
|
||||
#print("setup")
|
||||
debugPrint("setup")
|
||||
self.obj = obj
|
||||
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"))
|
||||
FreeCAD.Console.PrintError(translate("Cannot insert holding tags for this path - please select a Profile path\n"))
|
||||
return None
|
||||
|
||||
self.toolRadius = 5
|
||||
|
@ -830,11 +908,13 @@ class ObjectDressup:
|
|||
obj.Height = self.pathData.defaultTagHeight()
|
||||
obj.Width = self.pathData.defaultTagWidth()
|
||||
obj.Angle = self.pathData.defaultTagAngle()
|
||||
obj.Radius = self.pathData.defaultTagRadius()
|
||||
count = HoldingTagsPreferences.defaultCount()
|
||||
self.generateTags(obj, count)
|
||||
return self.pathData
|
||||
|
||||
def setXyEnabled(self, triples):
|
||||
debugPrint("setXyEnabled")
|
||||
if not hasattr(self, 'pathData'):
|
||||
self.setup(self.obj)
|
||||
positions = []
|
||||
|
@ -881,7 +961,7 @@ class TaskPanel:
|
|||
self.formPoint.hide()
|
||||
self.jvo = PathUtils.findParentJob(obj).ViewObject
|
||||
if jvoVisibility is None:
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("PathDressup_HoldingTags", "Edit HoldingTags Dress-up"))
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("Edit HoldingTags Dress-up"))
|
||||
self.jvoVisible = self.jvo.isVisible()
|
||||
if self.jvoVisible:
|
||||
self.jvo.hide()
|
||||
|
@ -925,6 +1005,7 @@ class TaskPanel:
|
|||
self.obj.Width = FreeCAD.Units.Quantity(self.formTags.ifWidth.text()).Value
|
||||
self.obj.Height = FreeCAD.Units.Quantity(self.formTags.ifHeight.text()).Value
|
||||
self.obj.Angle = self.formTags.dsbAngle.value()
|
||||
self.obj.Radius = FreeCAD.Units.Quantity(self.formTags.ifRadius.text()).Value
|
||||
|
||||
def getFields(self):
|
||||
self.getTagParameters()
|
||||
|
@ -1090,22 +1171,7 @@ class TaskPanel:
|
|||
self.formTags.ifHeight.setText(FreeCAD.Units.Quantity(self.obj.Height, FreeCAD.Units.Length).UserString)
|
||||
self.formTags.ifWidth.setText(FreeCAD.Units.Quantity(self.obj.Width, FreeCAD.Units.Length).UserString)
|
||||
self.formTags.dsbAngle.setValue(self.obj.Angle)
|
||||
|
||||
def updateModelHeight(self):
|
||||
#print('updateModelHeight')
|
||||
self.updateModel()
|
||||
|
||||
def updateModelWidth(self):
|
||||
#print('updateModelWidth')
|
||||
self.updateModel()
|
||||
|
||||
def updateModelAngle(self):
|
||||
#print('updateModelAngle')
|
||||
self.updateModel()
|
||||
|
||||
def updateModelTags(self):
|
||||
#print('updateModelTags')
|
||||
self.updateModel()
|
||||
self.formTags.ifRadius.setText(FreeCAD.Units.Quantity(self.obj.Radius, FreeCAD.Units.Length).UserString)
|
||||
|
||||
def setupUi(self):
|
||||
self.setFields()
|
||||
|
@ -1114,10 +1180,11 @@ class TaskPanel:
|
|||
self.formTags.sbCount.valueChanged.connect(self.whenCountChanged)
|
||||
self.formTags.pbGenerate.clicked.connect(self.generateNewTags)
|
||||
|
||||
self.formTags.ifHeight.editingFinished.connect(self.updateModelHeight)
|
||||
self.formTags.ifWidth.editingFinished.connect(self.updateModelWidth)
|
||||
self.formTags.dsbAngle.editingFinished.connect(self.updateModelAngle)
|
||||
self.formTags.lwTags.itemChanged.connect(self.updateModelTags)
|
||||
self.formTags.ifHeight.editingFinished.connect(self.updateModel)
|
||||
self.formTags.ifWidth.editingFinished.connect(self.updateModel)
|
||||
self.formTags.dsbAngle.editingFinished.connect(self.updateModel)
|
||||
self.formTags.ifRadius.editingFinished.connect(self.updateModel)
|
||||
self.formTags.lwTags.itemChanged.connect(self.updateModel)
|
||||
self.formTags.lwTags.itemSelectionChanged.connect(self.whenTagSelectionChanged)
|
||||
self.formTags.lwTags.itemActivated.connect(self.editTag)
|
||||
|
||||
|
@ -1307,18 +1374,18 @@ class CommandPathDressupHoldingTags:
|
|||
# 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"))
|
||||
FreeCAD.Console.PrintError(translate("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"))
|
||||
FreeCAD.Console.PrintError(translate("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"))
|
||||
FreeCAD.Console.PrintError(translate("Please select a Profile object"))
|
||||
return
|
||||
|
||||
# everything ok!
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("PathDressup_HoldingTags", "Create HoldingTags Dress-up"))
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("Create HoldingTags Dress-up"))
|
||||
FreeCADGui.addModule("PathScripts.PathDressupHoldingTags")
|
||||
FreeCADGui.addModule("PathScripts.PathUtils")
|
||||
FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "HoldingTagsDressup")')
|
||||
|
|
|
@ -29,6 +29,8 @@ import Path
|
|||
|
||||
from FreeCAD import Vector
|
||||
|
||||
PathGeomTolerance = 0.000001
|
||||
|
||||
class Side:
|
||||
"""Class to determine and define the side a Path is on, or Vectors are in relation to each other."""
|
||||
Left = +1
|
||||
|
@ -70,22 +72,24 @@ class PathGeom:
|
|||
CmdMoveArc = CmdMoveCW + CmdMoveCCW
|
||||
CmdMove = CmdMoveStraight + CmdMoveArc
|
||||
|
||||
Tolerance = PathGeomTolerance
|
||||
|
||||
@classmethod
|
||||
def isRoughly(cls, float1, float2, error=0.0000001):
|
||||
"""(float1, float2, [error=0.0000001])
|
||||
Returns true if the two values are the same within a given error."""
|
||||
def isRoughly(cls, float1, float2, error=PathGeomTolerance):
|
||||
"""(float1, float2, [error=%s])
|
||||
Returns true if the two values are the same within a given error.""" % PathGeomTolerance
|
||||
return math.fabs(float1 - float2) <= error
|
||||
|
||||
@classmethod
|
||||
def pointsCoincide(cls, p1, p2, error=0.0000001):
|
||||
"""(p1, p2, [error=0.0000001])
|
||||
Return True if two points are roughly identical (see also isRoughly)."""
|
||||
def pointsCoincide(cls, p1, p2, error=PathGeomTolerance):
|
||||
"""(p1, p2, [error=%s])
|
||||
Return True if two points are roughly identical (see also isRoughly).""" % PathGeomTolerance
|
||||
return cls.isRoughly(p1.x, p2.x, error) and cls.isRoughly(p1.y, p2.y, error) and cls.isRoughly(p1.z, p2.z, error)
|
||||
|
||||
@classmethod
|
||||
def edgesMatch(cls, e0, e1, error=0.0000001):
|
||||
"""(e0, e1, [error=0.0000001]
|
||||
Return true if the edges start and end at the same point and have the same type of curve."""
|
||||
def edgesMatch(cls, e0, e1, error=PathGeomTolerance):
|
||||
"""(e0, e1, [error=%s]
|
||||
Return true if the edges start and end at the same point and have the same type of curve.""" % PathGeomTolerance
|
||||
if type(e0.Curve) != type(e1.Curve):
|
||||
return False
|
||||
if not cls.pointsCoincide(e0.valueAt(e0.FirstParameter), e1.valueAt(e1.FirstParameter)):
|
||||
|
@ -95,9 +99,9 @@ class PathGeom:
|
|||
return True
|
||||
|
||||
@classmethod
|
||||
def edgeConnectsTo(cls, edge, vector):
|
||||
"""(edge, vector)
|
||||
Returns True if edge connects to given vector."""
|
||||
def edgeConnectsTo(cls, edge, vector, error=PathGeomTolerance):
|
||||
"""(edge, vector, error=%f)
|
||||
Returns True if edge connects to given vector.""" % PathGeomTolerance
|
||||
return cls.pointsCoincide(edge.valueAt(edge.FirstParameter), vector) or cls.pointsCoincide(edge.valueAt(edge.LastParameter), vector)
|
||||
|
||||
@classmethod
|
||||
|
@ -106,7 +110,7 @@ class PathGeom:
|
|||
Returns the angle [-pi,pi] of a vector using the X-axis as the reference.
|
||||
Positive angles for vertexes in the upper hemishpere (positive y values)
|
||||
and negative angles for the lower hemishpere."""
|
||||
a = vector.getAngle(FreeCAD.Vector(1,0,0))
|
||||
a = vector.getAngle(Vector(1,0,0))
|
||||
if vector.y < 0:
|
||||
return -a
|
||||
return a
|
||||
|
@ -132,7 +136,7 @@ class PathGeom:
|
|||
x = cmd.Parameters.get(X, defaultPoint.x)
|
||||
y = cmd.Parameters.get(Y, defaultPoint.y)
|
||||
z = cmd.Parameters.get(Z, defaultPoint.z)
|
||||
return FreeCAD.Vector(x, y, z)
|
||||
return Vector(x, y, z)
|
||||
|
||||
@classmethod
|
||||
def xy(cls, point):
|
||||
|
@ -162,12 +166,12 @@ class PathGeom:
|
|||
p1 = pt
|
||||
p3 = edge.valueAt(edge.LastParameter)
|
||||
p2 = edge.valueAt((edge.FirstParameter + edge.LastParameter)/2)
|
||||
if type(edge.Curve) == Part.Circle or (useHelixForBSpline and type(edge.Curve) == Part.BSplineCurve):
|
||||
if (type(edge.Curve) == Part.Circle and cls.pointsCoincide(edge.Curve.Axis, Vector(0, 0, 1))) or (useHelixForBSpline and type(edge.Curve) == Part.BSplineCurve):
|
||||
if Side.Left == Side.of(p2 - p1, p3 - p2):
|
||||
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)
|
||||
|
@ -232,7 +236,7 @@ class PathGeom:
|
|||
#print("arc: A=(%.2f, %.2f) B=(%.2f, %.2f) -> d=%.2f" % (A.x, A.y, B.x, B.y, d))
|
||||
#print("arc: R=%.2f angle=%.2f" % (R, angle/math.pi))
|
||||
if startPoint.z == endPoint.z:
|
||||
midPoint = center + FreeCAD.Vector(math.cos(angle), math.sin(angle), 0) * R
|
||||
midPoint = center + Vector(math.cos(angle), math.sin(angle), 0) * R
|
||||
return Part.Edge(Part.Arc(startPoint, midPoint, endPoint))
|
||||
|
||||
# It's a Helix
|
||||
|
@ -255,7 +259,7 @@ class PathGeom:
|
|||
return None
|
||||
|
||||
@classmethod
|
||||
def wireForPath(cls, path, startPoint = FreeCAD.Vector(0, 0, 0)):
|
||||
def wireForPath(cls, path, startPoint = Vector(0, 0, 0)):
|
||||
"""(path, [startPoint=Vector(0,0,0)])
|
||||
Returns a wire representing all move commands found in the given path."""
|
||||
edges = []
|
||||
|
@ -271,7 +275,7 @@ class PathGeom:
|
|||
return (Part.Wire(edges), rapid)
|
||||
|
||||
@classmethod
|
||||
def wiresForPath(cls, path, startPoint = FreeCAD.Vector(0, 0, 0)):
|
||||
def wiresForPath(cls, path, startPoint = Vector(0, 0, 0)):
|
||||
"""(path, [startPoint=Vector(0,0,0)])
|
||||
Returns a collection of wires, each representing a continuous cutting Path in path."""
|
||||
wires = []
|
||||
|
@ -306,7 +310,7 @@ class PathGeom:
|
|||
#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))
|
||||
return cls.edgeForCmd(command, Vector(p1.x, p1.y, z0))
|
||||
|
||||
|
||||
@classmethod
|
||||
|
@ -316,9 +320,9 @@ class PathGeom:
|
|||
p1 = edge.valueAt(edge.FirstParameter)
|
||||
p2 = edge.valueAt((edge.FirstParameter + edge.LastParameter)/2)
|
||||
p3 = edge.valueAt(edge.LastParameter)
|
||||
p01 = FreeCAD.Vector(p1.x, p1.y, z)
|
||||
p02 = FreeCAD.Vector(p2.x, p2.y, z)
|
||||
p03 = FreeCAD.Vector(p3.x, p3.y, z)
|
||||
p01 = Vector(p1.x, p1.y, z)
|
||||
p02 = Vector(p2.x, p2.y, z)
|
||||
p03 = Vector(p3.x, p3.y, z)
|
||||
return Part.Edge(Part.Arc(p01, p02, p03))
|
||||
|
||||
@classmethod
|
||||
|
@ -364,6 +368,6 @@ class PathGeom:
|
|||
else:
|
||||
# it's a helix
|
||||
arc = cls.helixToArc(edge, 0)
|
||||
aes = cls.splitArcAt(arc, FreeCAD.Vector(pt.x, pt.y, 0))
|
||||
aes = cls.splitArcAt(arc, Vector(pt.x, pt.y, 0))
|
||||
return [cls.arcToHelix(aes[0], p1.z, p2.z), cls.arcToHelix(aes[1], p2.z, p3.z)]
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user