diff --git a/src/Mod/Path/PathScripts/PathDressupDogbone.py b/src/Mod/Path/PathScripts/PathDressupDogbone.py index ce134a23c3..a5a46d6058 100644 --- a/src/Mod/Path/PathScripts/PathDressupDogbone.py +++ b/src/Mod/Path/PathScripts/PathDressupDogbone.py @@ -40,7 +40,7 @@ Part = LazyLoader('Part', globals(), 'Part') 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 @@ -227,6 +227,9 @@ class Chord (object): 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) @@ -237,8 +240,9 @@ class Chord (object): 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 @@ -251,8 +255,8 @@ class Chord (object): 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) @@ -265,8 +269,8 @@ class Chord (object): 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)) @@ -297,6 +301,10 @@ class Chord (object): 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)) @@ -435,7 +443,7 @@ class ObjectDressup: # 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)) @@ -766,7 +774,7 @@ class ObjectDressup: 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 @@ -783,6 +791,7 @@ class ObjectDressup: 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 @@ -804,6 +813,9 @@ class ObjectDressup: commands.append(lastCommand) lastCommand = thisCommand lastBone = None + elif thisChord.isANoopMove(): + PathLog.info(" ignoring and dropping noop move") + continue else: PathLog.info(" nope") if lastCommand: @@ -818,12 +830,13 @@ class ObjectDressup: 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) @@ -947,10 +960,10 @@ class TaskPanel: 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: diff --git a/src/Mod/Path/PathTests/TestPathDressupDogbone.py b/src/Mod/Path/PathTests/TestPathDressupDogbone.py index 5db688320d..68a213ccc4 100644 --- a/src/Mod/Path/PathTests/TestPathDressupDogbone.py +++ b/src/Mod/Path/PathTests/TestPathDressupDogbone.py @@ -56,7 +56,15 @@ class TestDressupDogbone(PathTestBase): 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) @@ -69,7 +77,15 @@ class TestDressupDogbone(PathTestBase): 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) @@ -143,3 +159,105 @@ class TestDressupDogbone(PathTestBase): 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])) +