From 02c0a6dd7bb28293bf264160a5839ac15881c6be Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Mon, 15 Mar 2021 21:57:51 -0700 Subject: [PATCH 1/3] Don't insert dogbone between two straight segments --- .../Path/PathScripts/PathDressupDogbone.py | 23 +++++++++++-------- .../Path/PathTests/TestPathDressupDogbone.py | 18 +++++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDressupDogbone.py b/src/Mod/Path/PathScripts/PathDressupDogbone.py index ce134a23c3..e3e4b94304 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)) @@ -766,7 +770,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 +787,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 @@ -947,10 +952,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..8bc9aca3b9 100644 --- a/src/Mod/Path/PathTests/TestPathDressupDogbone.py +++ b/src/Mod/Path/PathTests/TestPathDressupDogbone.py @@ -143,3 +143,21 @@ 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 + G1 Y0 + G1 X15 + G1 Y10 + G1 X10 + G0 Z10 + ''') + obj = TestFeature() + db = PathDressupDogbone.ObjectDressup(obj, base) + db.setup(obj, True) + db.execute(obj, False) + self.assertEqual(len(db.bones), 0) From eb91c02925098d477f2934927c2b5f9cca17c641 Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Mon, 15 Mar 2021 22:28:09 -0700 Subject: [PATCH 2/3] Dogbone resiliency against comments between moves --- .../Path/PathScripts/PathDressupDogbone.py | 22 ++++--- .../Path/PathTests/TestPathDressupDogbone.py | 66 +++++++++++++++++-- 2 files changed, 76 insertions(+), 12 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDressupDogbone.py b/src/Mod/Path/PathScripts/PathDressupDogbone.py index e3e4b94304..6200ca03aa 100644 --- a/src/Mod/Path/PathScripts/PathDressupDogbone.py +++ b/src/Mod/Path/PathScripts/PathDressupDogbone.py @@ -39,8 +39,8 @@ Part = LazyLoader('Part', globals(), 'Part') LOG_MODULE = PathLog.thisModule() -PathLog.setLevel(PathLog.Level.NOTICE, LOG_MODULE) -#PathLog.trackModule(LOG_MODULE) +PathLog.setLevel(PathLog.Level.DEBUG, LOG_MODULE) +PathLog.trackModule(LOG_MODULE) # Qt translation handling @@ -301,6 +301,9 @@ class Chord (object): def isAPlungeMove(self): return not PathGeom.isRoughly(self.End.z, self.Start.z) + def isANoopMove(self): + return PathGeom.pointsCoincide(self.Start, self.End) + def foldsBackOrTurns(self, chord, side): direction = chord.getDirectionOf(self) PathLog.info(" - direction = %s/%s" % (direction, side)) @@ -439,7 +442,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)) @@ -809,6 +812,8 @@ class ObjectDressup: commands.append(lastCommand) lastCommand = thisCommand lastBone = None + elif thisChord.isANoopMove(): + PathLog.info(" dropping noop move") else: PathLog.info(" nope") if lastCommand: @@ -823,12 +828,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) diff --git a/src/Mod/Path/PathTests/TestPathDressupDogbone.py b/src/Mod/Path/PathTests/TestPathDressupDogbone.py index 8bc9aca3b9..89ac002c53 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) @@ -149,11 +165,11 @@ class TestDressupDogbone(PathTestBase): base = TestProfile('Inside', 'CW', ''' G0 X10 Y10 Z10 G1 Z0 - G1 X0 + G1 X0 ( start) G1 Y0 G1 X15 G1 Y10 - G1 X10 + G1 X10 ( straight line move to start) G0 Z10 ''') obj = TestFeature() @@ -161,3 +177,45 @@ class TestDressupDogbone(PathTestBase): 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])) + From a589be64d9cdc5f3cdcf64f9cc07fec5313d95ca Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Mon, 15 Mar 2021 22:37:03 -0700 Subject: [PATCH 3/3] Dogbone resiliency against noop moves --- .../Path/PathScripts/PathDressupDogbone.py | 8 ++-- .../Path/PathTests/TestPathDressupDogbone.py | 42 +++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDressupDogbone.py b/src/Mod/Path/PathScripts/PathDressupDogbone.py index 6200ca03aa..a5a46d6058 100644 --- a/src/Mod/Path/PathScripts/PathDressupDogbone.py +++ b/src/Mod/Path/PathScripts/PathDressupDogbone.py @@ -39,8 +39,8 @@ Part = LazyLoader('Part', globals(), 'Part') LOG_MODULE = PathLog.thisModule() -PathLog.setLevel(PathLog.Level.DEBUG, LOG_MODULE) -PathLog.trackModule(LOG_MODULE) +PathLog.setLevel(PathLog.Level.NOTICE, LOG_MODULE) +#PathLog.trackModule(LOG_MODULE) # Qt translation handling @@ -302,6 +302,7 @@ class Chord (object): 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): @@ -813,7 +814,8 @@ class ObjectDressup: lastCommand = thisCommand lastBone = None elif thisChord.isANoopMove(): - PathLog.info(" dropping noop move") + PathLog.info(" ignoring and dropping noop move") + continue else: PathLog.info(" nope") if lastCommand: diff --git a/src/Mod/Path/PathTests/TestPathDressupDogbone.py b/src/Mod/Path/PathTests/TestPathDressupDogbone.py index 89ac002c53..68a213ccc4 100644 --- a/src/Mod/Path/PathTests/TestPathDressupDogbone.py +++ b/src/Mod/Path/PathTests/TestPathDressupDogbone.py @@ -219,3 +219,45 @@ class TestDressupDogbone(PathTestBase): 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])) +