Skip to content

Commit

Permalink
Merge pull request #4624 from mlampert/bugfix/dogbone-on-straight-edg…
Browse files Browse the repository at this point in the history
…es-and-noop-moves

[Path]: Bugfix/dogbone on straight edges and noop moves
  • Loading branch information
sliptonic committed Mar 23, 2021
2 parents 28d09cf + c81a43a commit 05b0ff2
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 17 deletions.
43 changes: 28 additions & 15 deletions src/Mod/Path/PathScripts/PathDressupDogbone.py
Expand Up @@ -40,7 +40,7 @@
LOG_MODULE = PathLog.thisModule()

PathLog.setLevel(PathLog.Level.NOTICE, LOG_MODULE)
#PathLog.setLevel(PathLog.Level.DEBUG, LOG_MODULE)
#PathLog.trackModule(LOG_MODULE)


# Qt translation handling
Expand Down Expand Up @@ -227,6 +227,9 @@ def move(self, distance, angle):
def asVector(self):
return self.End - self.Start

def asDirection(self):
return self.asVector().normalize()

def asLine(self):
return Part.LineSegment(self.Start, self.End)

Expand All @@ -237,8 +240,9 @@ def getLength(self):
return self.asVector().Length

def getDirectionOfVector(self, B):
A = self.asVector()
A = self.asDirection()
# if the 2 vectors are identical, they head in the same direction
PathLog.debug(" {}.getDirectionOfVector({})".format(A, B))
if PathGeom.pointsCoincide(A, B):
return 'Straight'
d = -A.x*B.y + A.y*B.x
Expand All @@ -251,8 +255,8 @@ def getDirectionOfVector(self, B):

def getDirectionOf(self, chordOrVector):
if type(chordOrVector) is Chord:
return self.getDirectionOfVector(chordOrVector.asVector())
return self.getDirectionOfVector(chordOrVector)
return self.getDirectionOfVector(chordOrVector.asDirection())
return self.getDirectionOfVector(chordOrVector.normalize())

def getAngleOfVector(self, ref):
angle = self.asVector().getAngle(ref)
Expand All @@ -265,8 +269,8 @@ def getAngleOfVector(self, ref):

def getAngle(self, refChordOrVector):
if type(refChordOrVector) is Chord:
return self.getAngleOfVector(refChordOrVector.asVector())
return self.getAngleOfVector(refChordOrVector)
return self.getAngleOfVector(refChordOrVector.asDirection())
return self.getAngleOfVector(refChordOrVector.normalize())

def getAngleXY(self):
return self.getAngle(FreeCAD.Vector(1, 0, 0))
Expand Down Expand Up @@ -297,6 +301,10 @@ def g3Command(self, center, f):
def isAPlungeMove(self):
return not PathGeom.isRoughly(self.End.z, self.Start.z)

def isANoopMove(self):
PathLog.debug("{}.isANoopMove(): {}".format(self, PathGeom.pointsCoincide(self.Start, self.End)))
return PathGeom.pointsCoincide(self.Start, self.End)

def foldsBackOrTurns(self, chord, side):
direction = chord.getDirectionOf(self)
PathLog.info(" - direction = %s/%s" % (direction, side))
Expand Down Expand Up @@ -435,7 +443,7 @@ def theOtherSideOf(self, side):

# Answer true if a dogbone could be on either end of the chord, given its command
def canAttachDogbone(self, cmd, chord):
return cmd.Name in movestraight and not chord.isAPlungeMove()
return cmd.Name in movestraight and not chord.isAPlungeMove() and not chord.isANoopMove()

def shouldInsertDogbone(self, obj, inChord, outChord):
return outChord.foldsBackOrTurns(inChord, self.theOtherSideOf(obj.Side))
Expand Down Expand Up @@ -766,7 +774,7 @@ def execute(self, obj, forReal=True):
thisIsACandidate = self.canAttachDogbone(thisCommand, thisChord)

if thisIsACandidate and lastCommand and self.shouldInsertDogbone(obj, lastChord, thisChord):
PathLog.info(" Found bone corner")
PathLog.info(" Found bone corner: {}".format(lastChord.End))
bone = Bone(boneId, obj, lastCommand, lastChord, thisChord, Smooth.InAndOut, thisCommand.Parameters.get('F'))
bones = self.insertBone(bone)
boneId += 1
Expand All @@ -783,6 +791,7 @@ def execute(self, obj, forReal=True):
for chord in (chord for chord in oddsAndEnds if lastChord.connectsTo(chord)):
if self.shouldInsertDogbone(obj, lastChord, chord):
PathLog.info(" and there is one")
PathLog.debug(" odd/end={} last={}".format(chord, lastChord))
bone = Bone(boneId, obj, lastCommand, lastChord, chord, Smooth.In, lastCommand.Parameters.get('F'))
bones = self.insertBone(bone)
boneId += 1
Expand All @@ -804,6 +813,9 @@ def execute(self, obj, forReal=True):
commands.append(lastCommand)
lastCommand = thisCommand
lastBone = None
elif thisChord.isANoopMove():
PathLog.info(" ignoring and dropping noop move")
continue
else:
PathLog.info(" nope")
if lastCommand:
Expand All @@ -818,12 +830,13 @@ def execute(self, obj, forReal=True):

lastChord = thisChord
else:
PathLog.info(" Clean slate")
if lastCommand:
commands.append(lastCommand)
lastCommand = None
if thisCommand.Name[0] != '(':
PathLog.info(" Clean slate")
if lastCommand:
commands.append(lastCommand)
lastCommand = None
lastBone = None
commands.append(thisCommand)
lastBone = None
# for cmd in commands:
# PathLog.debug("cmd = '%s'" % cmd)
path = Path.Path(commands)
Expand Down Expand Up @@ -947,10 +960,10 @@ def updateUI(self):
for obj in FreeCAD.ActiveDocument.Objects:
if obj.Name.startswith('Shape'):
FreeCAD.ActiveDocument.removeObject(obj.Name)
print('object name %s' % self.obj.Name)
PathLog.info('object name %s' % self.obj.Name)
if hasattr(self.obj.Proxy, "shapes"):
PathLog.info("showing shapes attribute")
for shapes in self.obj.Proxy.shapes.itervalues():
for shapes in self.obj.Proxy.shapes.values():
for shape in shapes:
Part.show(shape)
else:
Expand Down
122 changes: 120 additions & 2 deletions src/Mod/Path/PathTests/TestPathDressupDogbone.py
Expand Up @@ -56,7 +56,15 @@ def formatBone(self, bone):

def test00(self):
'''Verify bones are inserted for simple moves.'''
base = TestProfile('Inside', 'CW', 'G0 X10 Y10 Z10\nG1 Z0\nG1 Y100\nG1 X12\nG1 Y10\nG1 X10\nG1 Z10')
base = TestProfile('Inside', 'CW', '''
G0 X10 Y10 Z10
G1 Z0
G1 Y100
G1 X12
G1 Y10
G1 X10
G1 Z10
''')
obj = TestFeature()
db = PathDressupDogbone.ObjectDressup(obj, base)
db.setup(obj, True)
Expand All @@ -69,7 +77,15 @@ def test00(self):

def test01(self):
'''Verify bones are inserted if hole ends with rapid move out.'''
base = TestProfile('Inside', 'CW', 'G0 X10 Y10 Z10\nG1 Z0\nG1 Y100\nG1 X12\nG1 Y10\nG1 X10\nG0 Z10')
base = TestProfile('Inside', 'CW', '''
G0 X10 Y10 Z10
G1 Z0
G1 Y100
G1 X12
G1 Y10
G1 X10
G0 Z10
''')
obj = TestFeature()
db = PathDressupDogbone.ObjectDressup(obj, base)
db.setup(obj, True)
Expand Down Expand Up @@ -143,3 +159,105 @@ def formatBoneLoc(pt):
self.assertEqual("(72.50, 72.50)", formatBoneLoc(locs[7]))

FreeCAD.closeDocument("TestDressupDogbone")

def test03(self):
'''Verify no bone is inserted for straight move interrupted by plunge.'''
base = TestProfile('Inside', 'CW', '''
G0 X10 Y10 Z10
G1 Z0
G1 X0 ( start)
G1 Y0
G1 X15
G1 Y10
G1 X10 ( straight line move to start)
G0 Z10
''')
obj = TestFeature()
db = PathDressupDogbone.ObjectDressup(obj, base)
db.setup(obj, True)
db.execute(obj, False)
self.assertEqual(len(db.bones), 0)

def test04(self):
'''Verify can handle comments between moves'''
base = TestProfile('Inside', 'CW', '''
G0 X10 Y10 Z10
G1 Z0
G1 X20
G1 Y0
G1 X10
G1 Y10
G1 Z10
''')
obj = TestFeature()
db = PathDressupDogbone.ObjectDressup(obj, base)
db.setup(obj, True)
db.execute(obj, False)
self.assertEqual(len(db.bones), 4)
self.assertEqual("1: (20.00, 10.00)", self.formatBone(db.bones[0]))
self.assertEqual("2: (20.00, 0.00)", self.formatBone(db.bones[1]))
self.assertEqual("3: (10.00, 0.00)", self.formatBone(db.bones[2]))
self.assertEqual("4: (10.00, 10.00)", self.formatBone(db.bones[3]))

base = TestProfile('Inside', 'CW', '''
G0 X10 Y10 Z10
G1 Z0
G1 X20
G1 Y0
G1 X10
(some comment or other should not change the output)
G1 Y10
G1 Z10
''')
obj = TestFeature()
db = PathDressupDogbone.ObjectDressup(obj, base)
db.setup(obj, True)
db.execute(obj, False)
self.assertEqual(len(db.bones), 4)
self.assertEqual("1: (20.00, 10.00)", self.formatBone(db.bones[0]))
self.assertEqual("2: (20.00, 0.00)", self.formatBone(db.bones[1]))
self.assertEqual("3: (10.00, 0.00)", self.formatBone(db.bones[2]))
self.assertEqual("4: (10.00, 10.00)", self.formatBone(db.bones[3]))


def test05(self):
'''Verify can handle noops between moves'''
base = TestProfile('Inside', 'CW', '''
G0 X10 Y10 Z10
G1 Z0
G1 X20
G1 Y0
G1 X10
G1 Y10
G1 Z10
''')
obj = TestFeature()
db = PathDressupDogbone.ObjectDressup(obj, base)
db.setup(obj, True)
db.execute(obj, False)
self.assertEqual(len(db.bones), 4)
self.assertEqual("1: (20.00, 10.00)", self.formatBone(db.bones[0]))
self.assertEqual("2: (20.00, 0.00)", self.formatBone(db.bones[1]))
self.assertEqual("3: (10.00, 0.00)", self.formatBone(db.bones[2]))
self.assertEqual("4: (10.00, 10.00)", self.formatBone(db.bones[3]))

base = TestProfile('Inside', 'CW', '''
G0 X10 Y10 Z10
G1 Z0
G1 X20
G1 Y0
G1 X10
G1 X10
G1 Y10
G1 Z10
''')
obj = TestFeature()
db = PathDressupDogbone.ObjectDressup(obj, base)
db.setup(obj, True)
db.execute(obj, False)
self.assertEqual(len(db.bones), 4)
self.assertEqual("1: (20.00, 10.00)", self.formatBone(db.bones[0]))
self.assertEqual("2: (20.00, 0.00)", self.formatBone(db.bones[1]))
self.assertEqual("3: (10.00, 0.00)", self.formatBone(db.bones[2]))
self.assertEqual("4: (10.00, 10.00)", self.formatBone(db.bones[3]))

0 comments on commit 05b0ff2

Please sign in to comment.