Smooth path with fillets.
This commit is contained in:
parent
0ba1030163
commit
23713a2c51
|
@ -1717,7 +1717,15 @@ def fillet(lEdges,r,chamfer=False):
|
|||
rndEdges[1] = Part.Edge(Part.Line(arcPt1,arcPt3))
|
||||
else:
|
||||
rndEdges[1] = Part.Edge(Part.Arc(arcPt1,arcPt2,arcPt3))
|
||||
|
||||
if lVertexes[0].Point == arcPt1:
|
||||
# fillet consumes entire first edge
|
||||
rndEdges.pop(0)
|
||||
else:
|
||||
rndEdges[0] = Part.Edge(Part.Line(lVertexes[0].Point,arcPt1))
|
||||
|
||||
if lVertexes[2].Point != arcPt3:
|
||||
# fillet does not consume entire second edge
|
||||
rndEdges += [Part.Edge(Part.Line(arcPt3,lVertexes[2].Point))]
|
||||
|
||||
return rndEdges
|
||||
|
|
|
@ -27,6 +27,8 @@ import Path
|
|||
from PathScripts import PathUtils
|
||||
from PySide import QtCore, QtGui
|
||||
import math
|
||||
import Part
|
||||
import DraftGeomUtils
|
||||
|
||||
"""Dogbone Dressup object and FreeCAD command"""
|
||||
|
||||
|
@ -45,6 +47,20 @@ except AttributeError:
|
|||
movecommands = ['G0', 'G00', 'G1', 'G01', 'G2', 'G02', 'G3', 'G03']
|
||||
movestraight = ['G1', 'G01']
|
||||
|
||||
def debugMarker(vector, label):
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::Sphere", label)
|
||||
obj.Label = label
|
||||
obj.Radius = 0.5
|
||||
obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0,0,1), 0))
|
||||
|
||||
def debugCircle(vector, r, label):
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::Cylinder", label)
|
||||
obj.Label = label
|
||||
obj.Radius = r
|
||||
obj.Height = 1
|
||||
obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0,0,1), 0))
|
||||
obj.ViewObject.Transparency = 95
|
||||
|
||||
class Style:
|
||||
Dogbone = 'Dogbone'
|
||||
Tbone_H = 'T-bone horizontal'
|
||||
|
@ -155,6 +171,9 @@ class Chord (object):
|
|||
def g1Command(self):
|
||||
return Path.Command("G1", {"X": self.End.x, "Y": self.End.y})
|
||||
|
||||
def arcCommand(self, orientation):
|
||||
return self.g1Command()
|
||||
|
||||
def isAPlungeMove(self):
|
||||
return self.End.z != self.Start.z
|
||||
|
||||
|
@ -195,7 +214,14 @@ class Chord (object):
|
|||
else:
|
||||
print("Now this really sucks")
|
||||
|
||||
return (x, y)
|
||||
return FreeCAD.Vector(x, y, self.End.z)
|
||||
|
||||
def perpendicular(self):
|
||||
v = self.asVector()
|
||||
return FreeCAD.Vector(-v.y, v.x, 0)
|
||||
|
||||
def footOfPerpendicularFrom(self, vector):
|
||||
return self.intersection(Chord(vector, vector + self.perpendicular()))
|
||||
|
||||
class ObjectDressup:
|
||||
|
||||
|
@ -235,21 +261,11 @@ class ObjectDressup:
|
|||
def shouldInsertDogbone(self, obj, inChord, outChord):
|
||||
return outChord.foldsBackOrTurns(inChord, self.theOtherSideOf(obj.Side))
|
||||
|
||||
# draw circles where dogbones go, easier to spot during testing
|
||||
def debugCircleBone(self, obj,inChord, outChord):
|
||||
di = 0.
|
||||
dj = 0.5
|
||||
if inChord.Start.x < outChord.End.x:
|
||||
dj = -dj
|
||||
circle = Path.Command("G1", {"I": di, "J": dj}).Parameters
|
||||
circle.update({"X": inChord.End.x, "Y": inChord.End.y})
|
||||
return [ Path.Command("G3", circle) ]
|
||||
|
||||
def adaptiveBoneLength(self, obj, inChord, outChord, angle):
|
||||
iChord = inChord.offsetBy(self.toolRadius)
|
||||
oChord = outChord.offsetBy(self.toolRadius)
|
||||
x,y = iChord.intersection(oChord, self.toolRadius)
|
||||
dest = inChord.moveTo(FreeCAD.Vector(x, y, inChord.End.z))
|
||||
v = iChord.intersection(oChord, self.toolRadius)
|
||||
dest = inChord.moveTo(FreeCAD.Vector(v.x, v.y, v.z))
|
||||
destAngle = dest.getAngleXY()
|
||||
distance = dest.getLength() - self.toolRadius * math.fabs(math.cos(destAngle - angle))
|
||||
#print("adapt")
|
||||
|
@ -258,13 +274,43 @@ class ObjectDressup:
|
|||
#print(" = (%.2f, %.2f) -> %.2f (%.2f %.2f) -> %.2f" % (x, y, dest.getLength(), destAngle/math.pi, angle/math.pi, distance))
|
||||
return distance
|
||||
|
||||
def smoothChordCommands(self, inChord, outChord, smooth):
|
||||
def smoothChordCommands(self, inChord, outChord, side, smooth):
|
||||
if smooth == 0:
|
||||
return [ inChord.g1Command(), outChord.g1Command() ]
|
||||
print("(%.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()
|
||||
if inAngle == outAngle: # straight line, combine g1
|
||||
return [ Chord(inChord.Start, outChord.End).g1Command() ]
|
||||
print(" inAngle = %.2f outAngle = %.2f" % (inAngle/math.pi, outAngle/math.pi))
|
||||
if inAngle == outAngle: # straight line, outChord includes inChord
|
||||
print(" ---> (%.2f, %.2f)" %(outChord.End.x, outChord.End.y))
|
||||
return [ outChord.g1Command() ]
|
||||
print("%s :: %s" % (inChord, outChord))
|
||||
inEdge = DraftGeomUtils.edg(inChord.Start, inChord.End)
|
||||
outEdge = DraftGeomUtils.edg(outChord.Start, outChord.End)
|
||||
#wire = Part.Wire([inEdge, outEdge])
|
||||
#print(" => %s" % wire)
|
||||
#wire = wire.makeOffset2D(self.toolRadius)
|
||||
#print(" ==> %s" % wire)
|
||||
#wire = wire.makeOffset2D(-self.toolRadius)
|
||||
#print(" ===> %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):
|
||||
print("Oh, we got a problem, try smaller radius")
|
||||
radius = radius - 0.1 * self.toolRadius
|
||||
continue
|
||||
print("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, 0)
|
||||
commands += cmds
|
||||
path = Path.Path(commands)
|
||||
return path.Commands
|
||||
return [ inChord.g1Command(), outChord.g1Command() ]
|
||||
|
||||
def inOutBoneCommands(self, obj, inChord, outChord, angle, fixedLength, smooth):
|
||||
|
@ -279,9 +325,12 @@ class ObjectDressup:
|
|||
boneInChord = inChord.moveBy(x, y, 0)
|
||||
boneOutChord = boneInChord.moveTo(outChord.Start)
|
||||
|
||||
debugCircle(boneInChord.Start, self.toolRadius, 'boneStart')
|
||||
debugCircle(boneInChord.End, self.toolRadius, 'boneEnd')
|
||||
|
||||
bones = []
|
||||
bones.extend(self.smoothChordCommands(inChord, boneInChord, smooth & Smooth.In))
|
||||
bones.extend(self.smoothChordCommands(boneOutChord, outChord, smooth & Smooth.Out))
|
||||
bones.extend(self.smoothChordCommands(inChord, boneInChord, obj.Side, smooth & Smooth.In))
|
||||
bones.extend(self.smoothChordCommands(boneOutChord, outChord, obj.Side, smooth & Smooth.Out))
|
||||
return bones
|
||||
|
||||
def dogboneAngle(self, obj, inChord, outChord):
|
||||
|
@ -349,12 +398,7 @@ class ObjectDressup:
|
|||
return (blacklisted, parentConsumed)
|
||||
|
||||
# Generate commands necessary to execute the dogbone
|
||||
def boneCommands(self, obj, boneId, inChord, outChord, smooth):
|
||||
loc = (inChord.End.x, inChord.End.y)
|
||||
blacklisted, inaccessible = self.boneIsBlacklisted(obj, boneId, loc)
|
||||
enabled = not blacklisted
|
||||
self.bones.append((boneId, loc, enabled, inaccessible))
|
||||
|
||||
def boneCommands(self, obj, enabled, inChord, outChord, smooth):
|
||||
if enabled:
|
||||
if obj.Style == Style.Dogbone:
|
||||
return self.dogbone(obj, inChord, outChord, smooth)
|
||||
|
@ -366,13 +410,22 @@ class ObjectDressup:
|
|||
return self.tboneLongEdge(obj, inChord, outChord, smooth)
|
||||
if obj.Style == Style.Tbone_S:
|
||||
return self.tboneShortEdge(obj, inChord, outChord, smooth)
|
||||
return self.debugCircleBone(obj, inChord, outChord)
|
||||
else:
|
||||
return []
|
||||
return [ inChord.g1Command(), outChord.g1Command() ]
|
||||
|
||||
def insertBone(self, boneId, obj, inChord, outChord, commands, smooth):
|
||||
bones = self.boneCommands(obj, boneId, inChord, outChord, smooth)
|
||||
print(">----------------------------------- %d --------------------------------------" % boneId)
|
||||
loc = (inChord.End.x, inChord.End.y)
|
||||
blacklisted, inaccessible = self.boneIsBlacklisted(obj, boneId, loc)
|
||||
enabled = not blacklisted
|
||||
self.bones.append((boneId, loc, enabled, inaccessible))
|
||||
|
||||
if boneId < 9:
|
||||
bones = self.boneCommands(obj, enabled, inChord, outChord, smooth)
|
||||
else:
|
||||
bones = self.boneCommands(obj, False, inChord, outChord, smooth)
|
||||
commands.extend(bones[:-1])
|
||||
print("<----------------------------------- %d --------------------------------------" % boneId)
|
||||
return boneId + 1, bones[-1]
|
||||
|
||||
def execute(self, obj):
|
||||
|
@ -424,6 +477,8 @@ class ObjectDressup:
|
|||
commands.append(lastCommand)
|
||||
lastCommand = None
|
||||
commands.append(thisCmd)
|
||||
#for cmd in commands:
|
||||
# print("cmd = '%s'" % cmd)
|
||||
path = Path.Path(commands)
|
||||
obj.Path = path
|
||||
|
||||
|
|
|
@ -233,22 +233,7 @@ def reverseEdge(e):
|
|||
|
||||
return newedge
|
||||
|
||||
|
||||
def convert(toolpath, Z=0.0, PlungeAngle=90.0, Zprevious=None, StopLength=None, vf=1.0, hf=2.0) :
|
||||
'''convert(toolpath,Z=0.0,vf=1.0,hf=2.0,PlungeAngle=90.0,Zprevious=None,StopLength=None) Converts lines and arcs to G1,G2,G3 moves. Returns a string.'''
|
||||
|
||||
if PlungeAngle != 90.0:
|
||||
if Zprevious is None:
|
||||
raise Exception("Cannot use PlungeAngle != 90.0 degrees without parameter Zprevious")
|
||||
tanA = math.tan(math.pi * PlungeAngle / 180.0)
|
||||
minA = (Zprevious - Z) / sum(edge.Length for edge in toolpath)
|
||||
if tanA < minA:
|
||||
tanA = minA
|
||||
#FreeCAD.Console.PrintMessage('Increasing ramp angle to {0} degrees, to be able to make a full round\n'.format(math.atan(tanA) * 180.0 / math.pi))
|
||||
else:
|
||||
Zprevious = Z
|
||||
|
||||
def edge_to_path(lastpt, edge, Z):
|
||||
def edge_to_path(lastpt, edge, Z, hf=2.0):
|
||||
if isinstance(edge.Curve, Part.Circle):
|
||||
# FreeCAD.Console.PrintMessage("arc\n")
|
||||
arcstartpt = edge.valueAt(edge.FirstParameter)
|
||||
|
@ -293,6 +278,21 @@ def convert(toolpath, Z=0.0, PlungeAngle=90.0, Zprevious=None, StopLength=None,
|
|||
# FreeCAD.Console.PrintMessage("last pt line= " + str(lastpt)+ "\n")
|
||||
return lastpt, output
|
||||
|
||||
|
||||
def convert(toolpath, Z=0.0, PlungeAngle=90.0, Zprevious=None, StopLength=None, vf=1.0, hf=2.0) :
|
||||
'''convert(toolpath,Z=0.0,vf=1.0,hf=2.0,PlungeAngle=90.0,Zprevious=None,StopLength=None) Converts lines and arcs to G1,G2,G3 moves. Returns a string.'''
|
||||
|
||||
if PlungeAngle != 90.0:
|
||||
if Zprevious is None:
|
||||
raise Exception("Cannot use PlungeAngle != 90.0 degrees without parameter Zprevious")
|
||||
tanA = math.tan(math.pi * PlungeAngle / 180.0)
|
||||
minA = (Zprevious - Z) / sum(edge.Length for edge in toolpath)
|
||||
if tanA < minA:
|
||||
tanA = minA
|
||||
#FreeCAD.Console.PrintMessage('Increasing ramp angle to {0} degrees, to be able to make a full round\n'.format(math.atan(tanA) * 180.0 / math.pi))
|
||||
else:
|
||||
Zprevious = Z
|
||||
|
||||
lastpt = None
|
||||
output = ""
|
||||
path_length = 0.0
|
||||
|
@ -334,13 +334,13 @@ def convert(toolpath, Z=0.0, PlungeAngle=90.0, Zprevious=None, StopLength=None,
|
|||
subwire = edge.split(t)
|
||||
assert(len(subwire.Edges) == 2)
|
||||
Z_cur = Z
|
||||
lastpt, codes = edge_to_path(lastpt, subwire.Edges[0], Z_cur)
|
||||
lastpt, codes = edge_to_path(lastpt, subwire.Edges[0], Z_cur, hf)
|
||||
output += codes
|
||||
edge = subwire.Edges[1]
|
||||
else:
|
||||
Z_cur = Z_next
|
||||
|
||||
lastpt, codes = edge_to_path(lastpt, edge, Z_cur)
|
||||
lastpt, codes = edge_to_path(lastpt, edge, Z_cur, hf)
|
||||
output += codes
|
||||
|
||||
if StopLength:
|
||||
|
|
Loading…
Reference in New Issue
Block a user