Calculation of pivot points.
This commit is contained in:
parent
c58852a608
commit
9a93a5775e
|
@ -47,18 +47,20 @@ except AttributeError:
|
||||||
movecommands = ['G0', 'G00', 'G1', 'G01', 'G2', 'G02', 'G3', 'G03']
|
movecommands = ['G0', 'G00', 'G1', 'G01', 'G2', 'G02', 'G3', 'G03']
|
||||||
movestraight = ['G1', 'G01']
|
movestraight = ['G1', 'G01']
|
||||||
|
|
||||||
debugDogbone = False
|
debugDogbone = True
|
||||||
|
|
||||||
def debugPrint(msg):
|
def debugPrint(msg):
|
||||||
if debugDogbone:
|
if debugDogbone:
|
||||||
print(msg)
|
print(msg)
|
||||||
|
|
||||||
def debugMarker(vector, label):
|
def debugMarker(vector, label, color = None):
|
||||||
if debugDogbone:
|
if debugDogbone:
|
||||||
obj = FreeCAD.ActiveDocument.addObject("Part::Sphere", label)
|
obj = FreeCAD.ActiveDocument.addObject("Part::Sphere", label)
|
||||||
obj.Label = label
|
obj.Label = label
|
||||||
obj.Radius = 0.5
|
obj.Radius = 0.5
|
||||||
obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0,0,1), 0))
|
obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0,0,1), 0))
|
||||||
|
if color:
|
||||||
|
obj.ViewObject.ShapeColor = color
|
||||||
|
|
||||||
def debugCircle(vector, r, label):
|
def debugCircle(vector, r, label):
|
||||||
if debugDogbone:
|
if debugDogbone:
|
||||||
|
@ -153,9 +155,20 @@ class Chord (object):
|
||||||
def moveBy(self, x, y, z):
|
def moveBy(self, x, y, z):
|
||||||
return self.moveTo(self.End + FreeCAD.Vector(x, y, z))
|
return self.moveTo(self.End + FreeCAD.Vector(x, y, z))
|
||||||
|
|
||||||
|
def move(self, distance, angle):
|
||||||
|
dx = distance * math.cos(angle)
|
||||||
|
dy = distance * math.sin(angle)
|
||||||
|
return self.moveBy(dx, dy, 0)
|
||||||
|
|
||||||
def asVector(self):
|
def asVector(self):
|
||||||
return self.End - self.Start
|
return self.End - self.Start
|
||||||
|
|
||||||
|
def asLine(self):
|
||||||
|
return Part.Line(self.Start, self.End)
|
||||||
|
|
||||||
|
def asEdge(self):
|
||||||
|
return Part.Edge(self.asLine())
|
||||||
|
|
||||||
def getLength(self):
|
def getLength(self):
|
||||||
return self.asVector().Length
|
return self.asVector().Length
|
||||||
|
|
||||||
|
@ -194,18 +207,21 @@ class Chord (object):
|
||||||
def getAngleXY(self):
|
def getAngleXY(self):
|
||||||
return self.getAngle(FreeCAD.Vector(1,0,0))
|
return self.getAngle(FreeCAD.Vector(1,0,0))
|
||||||
|
|
||||||
def offsetBy(self, distance):
|
def withLength(self, length):
|
||||||
angle = self.getAngleXY() - math.pi/2
|
return Chord(self.Start, self.Start).move(length, self.getAngleXY())
|
||||||
dx = distance * math.cos(angle)
|
|
||||||
dy = distance * math.sin(angle)
|
|
||||||
d = FreeCAD.Vector(dx, dy, 0)
|
|
||||||
return Chord(self.Start + d, self.End + d)
|
|
||||||
|
|
||||||
def g1Command(self):
|
def g1Command(self):
|
||||||
return Path.Command("G1", {"X": self.End.x, "Y": self.End.y})
|
return Path.Command("G1", {"X": self.End.x, "Y": self.End.y})
|
||||||
|
|
||||||
def arcCommand(self, orientation):
|
def arcCommand(self, cmd, center):
|
||||||
return self.g1Command()
|
d = center - self.Start
|
||||||
|
debugPrint("arc (%.2f, %.2f) (%.2f, %.2f) (%.2f, %.2f)" % (self.Start.x, self.Start.y, center.x, center.y, self.End.x, self.End.y))
|
||||||
|
return Path.Command(cmd, {"X": self.End.x, "Y": self.End.y, "I": d.x, "J": d.y, "K": 0})
|
||||||
|
|
||||||
|
def g2Command(self, center):
|
||||||
|
return self.arcCommand("G2", center)
|
||||||
|
def g3Command(self, center):
|
||||||
|
return self.arcCommand("G3", center)
|
||||||
|
|
||||||
def isAPlungeMove(self):
|
def isAPlungeMove(self):
|
||||||
return self.End.z != self.Start.z
|
return self.End.z != self.Start.z
|
||||||
|
@ -256,6 +272,16 @@ class Chord (object):
|
||||||
def footOfPerpendicularFrom(self, vector):
|
def footOfPerpendicularFrom(self, vector):
|
||||||
return self.intersection(Chord(vector, vector + self.perpendicular()))
|
return self.intersection(Chord(vector, vector + self.perpendicular()))
|
||||||
|
|
||||||
|
def offsetBy(self, distance, direction):
|
||||||
|
if direction == Side.Left:
|
||||||
|
angle = addAngle(self.getAngleXY(), math.pi/2)
|
||||||
|
else:
|
||||||
|
angle = addAngle(self.getAngleXY(), -math.pi/2)
|
||||||
|
dx = distance * math.cos(angle)
|
||||||
|
dy = distance * math.sin(angle)
|
||||||
|
d = FreeCAD.Vector(dx, dy, 0)
|
||||||
|
return Chord(self.Start + v, self.End.v)
|
||||||
|
|
||||||
class ObjectDressup:
|
class ObjectDressup:
|
||||||
|
|
||||||
def __init__(self, obj):
|
def __init__(self, obj):
|
||||||
|
@ -275,6 +301,7 @@ class ObjectDressup:
|
||||||
obj.addProperty("App::PropertyFloat", "Custom", "Dressup", QtCore.QT_TRANSLATE_NOOP("Dogbone_Dressup", "Dressup length if Incision == custom"))
|
obj.addProperty("App::PropertyFloat", "Custom", "Dressup", QtCore.QT_TRANSLATE_NOOP("Dogbone_Dressup", "Dressup length if Incision == custom"))
|
||||||
obj.Custom = 0.0
|
obj.Custom = 0.0
|
||||||
obj.Proxy = self
|
obj.Proxy = self
|
||||||
|
self.shapes = {}
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
return None
|
return None
|
||||||
|
@ -333,67 +360,89 @@ class ObjectDressup:
|
||||||
debugPrint("corner=%.2f * %.2f -> bone=%.2f * %.2f" % (cornerDistance, cornerAngle, boneDistance, boneAngle))
|
debugPrint("corner=%.2f * %.2f -> bone=%.2f * %.2f" % (cornerDistance, cornerAngle, boneDistance, boneAngle))
|
||||||
return boneDistance
|
return boneDistance
|
||||||
|
|
||||||
def smoothChordCommands(self, inChord, outChord, side, smooth):
|
def smoothChordCommands(self, obj, inChord, outChord, edge, bone, corner, smooth, color):
|
||||||
if smooth == 0:
|
if smooth == 0:
|
||||||
return [ inChord.g1Command(), outChord.g1Command() ]
|
debugPrint(" No smoothing requested")
|
||||||
debugPrint("(%.2f, %.2f) -> (%.2f, %.2f) -> (%.2f, %.2f)" % (inChord.Start.x, inChord.Start.y, inChord.End.x, inChord.End.y, outChord.End.x, outChord.End.y))
|
|
||||||
inAngle = inChord.getAngleXY()
|
|
||||||
outAngle = outChord.getAngleXY()
|
|
||||||
debugPrint(" inAngle = %.2f outAngle = %.2f" % (inAngle/math.pi, outAngle/math.pi))
|
|
||||||
if inAngle == outAngle: # straight line, outChord includes inChord
|
|
||||||
debugPrint(" ---> (%.2f, %.2f)" %(outChord.End.x, outChord.End.y))
|
|
||||||
return [ outChord.g1Command() ]
|
|
||||||
debugPrint("%s :: %s" % (inChord, outChord))
|
|
||||||
inEdge = DraftGeomUtils.edg(inChord.Start, inChord.End)
|
|
||||||
outEdge = DraftGeomUtils.edg(outChord.Start, outChord.End)
|
|
||||||
#wire = Part.Wire([inEdge, outEdge])
|
|
||||||
#debugPrint(" => %s" % wire)
|
|
||||||
#wire = wire.makeOffset2D(self.toolRadius)
|
|
||||||
#debugPrint(" ==> %s" % wire)
|
|
||||||
#wire = wire.makeOffset2D(-self.toolRadius)
|
|
||||||
#debugPrint(" ===> %s" % wire)
|
|
||||||
radius = self.toolRadius
|
|
||||||
while radius > 0:
|
|
||||||
lastpt = None
|
|
||||||
commands = ""
|
|
||||||
edges = DraftGeomUtils.fillet([inEdge, outEdge], radius)
|
|
||||||
if DraftGeomUtils.isSameLine(edges[0], inEdge) or DraftGeomUtils.isSameLine(edges[1], inEdge):
|
|
||||||
debugPrint("Oh, we got a problem, try smaller radius")
|
|
||||||
radius = radius - 0.1 * self.toolRadius
|
|
||||||
continue
|
|
||||||
debugPrint("we're good")
|
|
||||||
#for edge in wire.Edges[:-1]: # makeOffset2D closes the wire
|
|
||||||
for edge in edges:
|
|
||||||
if not lastpt:
|
|
||||||
lastpt = edge.Vertexes[0].Point
|
|
||||||
lastpt, cmds = PathUtils.edge_to_path(lastpt, edge, inChord.Start.z)
|
|
||||||
commands += cmds
|
|
||||||
path = Path.Path(commands)
|
|
||||||
return path.Commands
|
|
||||||
return [ inChord.g1Command(), outChord.g1Command() ]
|
return [ inChord.g1Command(), outChord.g1Command() ]
|
||||||
|
|
||||||
def inOutBoneCommands(self, obj, inChord, outChord, angle, fixedLength, smooth):
|
d = 'in'
|
||||||
|
if smooth == Smooth.Out:
|
||||||
|
d = 'out'
|
||||||
|
|
||||||
|
if DraftGeomUtils.areColinear(inChord.asEdge(), outChord.asEdge()):
|
||||||
|
debugPrint(" straight edge %s" % d)
|
||||||
|
return [ outChord.g1Command() ]
|
||||||
|
|
||||||
|
pivots = []
|
||||||
|
for e in bone.Edges:
|
||||||
|
for pt in DraftGeomUtils.findIntersection(edge, e, True):
|
||||||
|
if pt != corner and not pt in pivots:
|
||||||
|
pivots.append(pt)
|
||||||
|
else:
|
||||||
|
debugPrint(" corner intersect %s (%.2f, %.2f) (%.2f, %.2f)" % (d, corner.x, corner.y, pt.x, pt.y))
|
||||||
|
|
||||||
|
for p in pivots:
|
||||||
|
debugMarker(p, "pivot.%d-%s" % (self.boneId, d), color)
|
||||||
|
|
||||||
|
return [ inChord.g1Command(), outChord.g1Command() ]
|
||||||
|
|
||||||
|
def inOutBoneCommands(self, obj, inChord, outChord, boneAngle, fixedLength, smooth):
|
||||||
|
cornerDistance, cornerAngle = self.cornerDistanceAndAngle(obj, inChord, outChord)
|
||||||
|
corner = inChord.move(cornerDistance, cornerAngle).End
|
||||||
|
|
||||||
|
debugPrint("corner = (%.2f, %.2f)" % (corner.x, corner.y))
|
||||||
|
#debugMarker(corner, 'corner', (1., 0., 1.))
|
||||||
|
|
||||||
length = fixedLength
|
length = fixedLength
|
||||||
if obj.Incision == Incision.Custom:
|
if obj.Incision == Incision.Custom:
|
||||||
length = obj.Custom
|
length = obj.Custom
|
||||||
if obj.Incision == Incision.Adaptive:
|
if obj.Incision == Incision.Adaptive:
|
||||||
length = self.adaptiveBoneLength(obj, inChord, outChord, angle)
|
length = self.adaptiveBoneLength(obj, inChord, outChord, boneAngle)
|
||||||
|
|
||||||
if length == 0:
|
if length == 0:
|
||||||
# no bone after all ..
|
# no bone after all ..
|
||||||
return [ inChord.g1Command(), outChord.g1Command() ]
|
return [ inChord.g1Command(), outChord.g1Command() ]
|
||||||
|
|
||||||
x = length * math.cos(angle);
|
boneInChord = inChord.move(length, boneAngle)
|
||||||
y = length * math.sin(angle);
|
|
||||||
boneInChord = inChord.moveBy(x, y, 0)
|
|
||||||
boneOutChord = boneInChord.moveTo(outChord.Start)
|
boneOutChord = boneInChord.moveTo(outChord.Start)
|
||||||
|
|
||||||
#debugCircle(boneInChord.Start, self.toolRadius, 'boneStart')
|
|
||||||
debugCircle(boneInChord.End, self.toolRadius, 'boneEnd')
|
|
||||||
|
|
||||||
bones = []
|
bones = []
|
||||||
bones.extend(self.smoothChordCommands(inChord, boneInChord, obj.Side, 0)) #smooth & Smooth.In))
|
|
||||||
bones.extend(self.smoothChordCommands(boneOutChord, outChord, obj.Side, 0)) #smooth & Smooth.Out))
|
#debugCircle(boneInChord.Start, self.toolRadius, 'boneStart')
|
||||||
|
#debugCircle(boneInChord.End, self.toolRadius, 'boneEnd')
|
||||||
|
|
||||||
|
if smooth == 0:
|
||||||
|
return [ inChord.g1Command(), boneInChord.g1Command(), boneOutChord.g1Command(), outChord.g1Command()]
|
||||||
|
|
||||||
|
# reconstruct the corner and convert to an edge
|
||||||
|
offset = corner - inChord.End
|
||||||
|
iChord = Chord(inChord.Start + offset, inChord.End + offset)
|
||||||
|
oChord = Chord(outChord.Start + offset, outChord.End + offset)
|
||||||
|
iLine = iChord.asLine()
|
||||||
|
oLine = oChord.asLine()
|
||||||
|
cornerEdge = Part.Shape([iLine, oLine])
|
||||||
|
|
||||||
|
# construct a shape representing the cut made by the bone
|
||||||
|
vt0 = FreeCAD.Vector( 0, self.toolRadius, 0)
|
||||||
|
vt1 = FreeCAD.Vector(length, self.toolRadius, 0)
|
||||||
|
vb0 = FreeCAD.Vector( 0, -self.toolRadius, 0)
|
||||||
|
vb1 = FreeCAD.Vector(length, -self.toolRadius, 0)
|
||||||
|
vm2 = FreeCAD.Vector(length + self.toolRadius, 0, 0)
|
||||||
|
|
||||||
|
boneBot = Part.Line(vb1, vb0)
|
||||||
|
boneTop = Part.Line(vt0, vt1)
|
||||||
|
boneFlat = Part.Line(vb0, vt0)
|
||||||
|
# what we actually want is an Arc - but findIntersect only returns the coincident if one exists
|
||||||
|
# which really sucks because that's the one we're probably not interested in ....
|
||||||
|
#boneArc = Part.Arc(vt1, vm2, vb1)
|
||||||
|
boneArc = Part.Circle(FreeCAD.Vector(length, 0, 0), FreeCAD.Vector(0,0,1), self.toolRadius)
|
||||||
|
boneWire = Part.Shape([boneTop,boneArc,boneBot])
|
||||||
|
boneWire.rotate(FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), boneAngle * 180 / math.pi)
|
||||||
|
boneWire.translate(inChord.End)
|
||||||
|
self.boneShapes = [cornerEdge, boneWire]
|
||||||
|
|
||||||
|
bones.extend(self.smoothChordCommands(obj, inChord, boneInChord, Part.Edge(iLine), boneWire, corner, smooth & Smooth.In, (1., 0., 0.)))
|
||||||
|
bones.extend(self.smoothChordCommands(obj, boneOutChord, outChord, Part.Edge(oLine), boneWire, corner, smooth & Smooth.Out, (0., 1., 0.)))
|
||||||
return bones
|
return bones
|
||||||
|
|
||||||
def dogboneAngle(self, obj, inChord, outChord):
|
def dogboneAngle(self, obj, inChord, outChord):
|
||||||
|
@ -469,16 +518,19 @@ class ObjectDressup:
|
||||||
|
|
||||||
def insertBone(self, boneId, obj, inChord, outChord, commands, smooth):
|
def insertBone(self, boneId, obj, inChord, outChord, commands, smooth):
|
||||||
debugPrint(">----------------------------------- %d --------------------------------------" % boneId)
|
debugPrint(">----------------------------------- %d --------------------------------------" % boneId)
|
||||||
|
self.boneShapes = []
|
||||||
loc = (inChord.End.x, inChord.End.y)
|
loc = (inChord.End.x, inChord.End.y)
|
||||||
blacklisted, inaccessible = self.boneIsBlacklisted(obj, boneId, loc)
|
blacklisted, inaccessible = self.boneIsBlacklisted(obj, boneId, loc)
|
||||||
enabled = not blacklisted
|
enabled = not blacklisted
|
||||||
self.bones.append((boneId, loc, enabled, inaccessible))
|
self.bones.append((boneId, loc, enabled, inaccessible))
|
||||||
|
|
||||||
if debugDogbone and boneId != 2:
|
self.boneId = boneId
|
||||||
|
if debugDogbone and boneId == 0:
|
||||||
bones = self.boneCommands(obj, False, inChord, outChord, smooth)
|
bones = self.boneCommands(obj, False, inChord, outChord, smooth)
|
||||||
else:
|
else:
|
||||||
bones = self.boneCommands(obj, enabled, inChord, outChord, smooth)
|
bones = self.boneCommands(obj, enabled, inChord, outChord, smooth)
|
||||||
commands.extend(bones[:-1])
|
commands.extend(bones[:-1])
|
||||||
|
self.shapes[boneId] = self.boneShapes
|
||||||
debugPrint("<----------------------------------- %d --------------------------------------" % boneId)
|
debugPrint("<----------------------------------- %d --------------------------------------" % boneId)
|
||||||
return boneId + 1, bones[-1]
|
return boneId + 1, bones[-1]
|
||||||
|
|
||||||
|
@ -664,6 +716,15 @@ class TaskPanel:
|
||||||
self.form.custom.setValue(self.obj.Custom)
|
self.form.custom.setValue(self.obj.Custom)
|
||||||
self.updateUI()
|
self.updateUI()
|
||||||
|
|
||||||
|
if hasattr(self.obj.Proxy, "shapes"):
|
||||||
|
print("showing shapes attribute")
|
||||||
|
for shapes in self.obj.Proxy.shapes.itervalues():
|
||||||
|
for shape in shapes:
|
||||||
|
Part.show(shape)
|
||||||
|
else:
|
||||||
|
print("no shapes attribute found")
|
||||||
|
|
||||||
|
|
||||||
def open(self):
|
def open(self):
|
||||||
self.s = SelObserver()
|
self.s = SelObserver()
|
||||||
# install the function mode resident
|
# install the function mode resident
|
||||||
|
@ -753,11 +814,15 @@ class CommandDogboneDressup:
|
||||||
if len(selection) != 1:
|
if len(selection) != 1:
|
||||||
FreeCAD.Console.PrintError(translate("Dogbone_Dressup", "Please select one path object\n"))
|
FreeCAD.Console.PrintError(translate("Dogbone_Dressup", "Please select one path object\n"))
|
||||||
return
|
return
|
||||||
if not selection[0].isDerivedFrom("Path::Feature"):
|
baseObject = selection[0]
|
||||||
|
if not baseObject.isDerivedFrom("Path::Feature"):
|
||||||
FreeCAD.Console.PrintError(translate("Dogbone_Dressup", "The selected object is not a path\n"))
|
FreeCAD.Console.PrintError(translate("Dogbone_Dressup", "The selected object is not a path\n"))
|
||||||
return
|
return
|
||||||
if selection[0].isDerivedFrom("Path::FeatureCompoundPython"):
|
if baseObject.isDerivedFrom("Path::FeatureCompoundPython"):
|
||||||
FreeCAD.Console.PrintError(translate("Dogbone_Dressup", "Please select a Path object"))
|
FreeCAD.Console.PrintError(translate("Dogbone_Dressup", "Please select a Profile or Dogbone Dressup object"))
|
||||||
|
return
|
||||||
|
if not hasattr(baseObject, "Side"):
|
||||||
|
FreeCAD.Console.PrintError(translate("Dogbone_Dressup", "Please select a Profile or Dogbone Dressup object"))
|
||||||
return
|
return
|
||||||
|
|
||||||
# everything ok!
|
# everything ok!
|
||||||
|
@ -766,7 +831,7 @@ class CommandDogboneDressup:
|
||||||
FreeCADGui.addModule("PathScripts.PathUtils")
|
FreeCADGui.addModule("PathScripts.PathUtils")
|
||||||
FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "DogboneDressup")')
|
FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "DogboneDressup")')
|
||||||
FreeCADGui.doCommand('dbo = PathScripts.DogboneDressup.ObjectDressup(obj)')
|
FreeCADGui.doCommand('dbo = PathScripts.DogboneDressup.ObjectDressup(obj)')
|
||||||
FreeCADGui.doCommand('obj.Base = FreeCAD.ActiveDocument.' + selection[0].Name)
|
FreeCADGui.doCommand('obj.Base = FreeCAD.ActiveDocument.' + baseObject.Name)
|
||||||
FreeCADGui.doCommand('PathScripts.DogboneDressup.ViewProviderDressup(obj.ViewObject)')
|
FreeCADGui.doCommand('PathScripts.DogboneDressup.ViewProviderDressup(obj.ViewObject)')
|
||||||
FreeCADGui.doCommand('PathScripts.PathUtils.addToJob(obj)')
|
FreeCADGui.doCommand('PathScripts.PathUtils.addToJob(obj)')
|
||||||
FreeCADGui.doCommand('Gui.ActiveDocument.getObject(obj.Base.Name).Visibility = False')
|
FreeCADGui.doCommand('Gui.ActiveDocument.getObject(obj.Base.Name).Visibility = False')
|
||||||
|
|
Loading…
Reference in New Issue
Block a user