Merge pull request #18880 from dbtayl/adaptive_roughing
[CAM] Adaptive roughing/overhang detection
This commit is contained in:
@@ -101,18 +101,14 @@ class TestPathAdaptive(PathTestBase):
|
||||
pass
|
||||
|
||||
# Unit tests
|
||||
def test00(self):
|
||||
"""test00() Empty test."""
|
||||
return
|
||||
|
||||
def test01(self):
|
||||
"""test01() Verify path generated on Face3."""
|
||||
def testFaceSingleSimple(self):
|
||||
"""testFaceSingleSimple() Verify path generated on Face3."""
|
||||
|
||||
# Instantiate a Adaptive operation and set Base Geometry
|
||||
adaptive = PathAdaptive.Create("Adaptive")
|
||||
adaptive.Base = [(self.doc.Fusion, ["Face3"])] # (base, subs_list)
|
||||
adaptive.Label = "test01+"
|
||||
adaptive.Comment = "test01() Verify path generated on Face3."
|
||||
adaptive.Label = "testFaceSingleSimple+"
|
||||
adaptive.Comment = "testFaceSingleSimple() Verify path generated on Face3."
|
||||
|
||||
# Set additional operation properties
|
||||
# setDepthsAndHeights(adaptive)
|
||||
@@ -138,14 +134,15 @@ class TestPathAdaptive(PathTestBase):
|
||||
# "expected_moves_test01: {}\noperationMoves: {}".format(expected_moves_test01, operationMoves))
|
||||
self.assertTrue(len(adaptive.Path.Commands) > 100, "Command count not greater than 100.")
|
||||
|
||||
def test02(self):
|
||||
"""test02() Verify path generated on adjacent, combined Face3 and Face10. The Z heights are different."""
|
||||
def testFacesMergedDifferentZ(self):
|
||||
"""testFacesMergedDifferentZ() Verify path generated on adjacent, combined
|
||||
Face3 and Face10. The Z heights are different."""
|
||||
|
||||
# Instantiate a Adaptive operation and set Base Geometry
|
||||
adaptive = PathAdaptive.Create("Adaptive")
|
||||
adaptive.Base = [(self.doc.Fusion, ["Face3", "Face10"])] # (base, subs_list)
|
||||
adaptive.Label = "test02+"
|
||||
adaptive.Comment = "test02() Verify path generated on adjacent, combined Face3 and Face10. The Z heights are different."
|
||||
adaptive.Label = "testFacesMergedDifferentZ+"
|
||||
adaptive.Comment = "testFacesMergedDifferentZ() Verify path generated on adjacent, combined Face3 and Face10. The Z heights are different. UseOutline = False"
|
||||
|
||||
# Set additional operation properties
|
||||
# setDepthsAndHeights(adaptive)
|
||||
@@ -165,14 +162,15 @@ class TestPathAdaptive(PathTestBase):
|
||||
|
||||
self.assertTrue(len(adaptive.Path.Commands) > 100, "Command count not greater than 100.")
|
||||
|
||||
def test03(self):
|
||||
"""test03() Verify path generated on adjacent, combined Face3 and Face10. The Z heights are different."""
|
||||
def testFacesMergedDifferentZUseOutline(self):
|
||||
"""testFacesMergedDifferentZUseOutline() Verify path generated on adjacent, combined Face3 and Face10.
|
||||
The Z heights are different."""
|
||||
|
||||
# Instantiate a Adaptive operation and set Base Geometry
|
||||
adaptive = PathAdaptive.Create("Adaptive")
|
||||
adaptive.Base = [(self.doc.Fusion, ["Face3", "Face10"])] # (base, subs_list)
|
||||
adaptive.Label = "test03+"
|
||||
adaptive.Comment = "test03() Verify path generated on adjacent, combined Face3 and Face10. The Z heights are different."
|
||||
adaptive.Label = "testFacesMergedDifferentZUseOutline+"
|
||||
adaptive.Comment = "testFacesMergedDifferentZUseOutline() Verify path generated on adjacent, combined Face3 and Face10. The Z heights are different. UseOutline = True."
|
||||
|
||||
# Set additional operation properties
|
||||
# setDepthsAndHeights(adaptive)
|
||||
@@ -192,8 +190,8 @@ class TestPathAdaptive(PathTestBase):
|
||||
|
||||
self.assertTrue(len(adaptive.Path.Commands) > 100, "Command count not greater than 100.")
|
||||
|
||||
def test04(self):
|
||||
"""test04() Verify path generated non-closed edges with differing Z-heights that are closed with Z=1 projection: "Edge9", "Edge2", "Edge8", "Edge15", "Edge30", "Edge31", "Edge29", "Edge19"."""
|
||||
def testOutlineDifferentZDiscontinuousEdges(self):
|
||||
"""testOutlineDifferentZDiscontinuous() Verify path generated non-closed edges with differing Z-heights that are closed with Z=1 projection: "Edge9", "Edge2", "Edge8", "Edge15", "Edge30", "Edge31", "Edge29", "Edge19"."""
|
||||
|
||||
# Instantiate a Adaptive operation and set Base Geometry
|
||||
adaptive = PathAdaptive.Create("Adaptive")
|
||||
@@ -212,8 +210,8 @@ class TestPathAdaptive(PathTestBase):
|
||||
],
|
||||
)
|
||||
] # (base, subs_list)
|
||||
adaptive.Label = "test04+"
|
||||
adaptive.Comment = 'test04() Verify path generated non-closed edges with differing Z-heights that are closed with Z=1 projection: "Edge9", "Edge2", "Edge8", "Edge15", "Edge30", "Edge31", "Edge29", "Edge19".'
|
||||
adaptive.Label = "testOutlineDifferentZDiscontinuous+"
|
||||
adaptive.Comment = 'testOutlineDifferentZDiscontinuous() Verify path generated non-closed edges with differing Z-heights that are closed with Z=1 projection: "Edge9", "Edge2", "Edge8", "Edge15", "Edge30", "Edge31", "Edge29", "Edge19".'
|
||||
|
||||
# Set additional operation properties
|
||||
# setDepthsAndHeights(adaptive)
|
||||
@@ -233,7 +231,7 @@ class TestPathAdaptive(PathTestBase):
|
||||
|
||||
self.assertTrue(len(adaptive.Path.Commands) > 100, "Command count not greater than 100.")
|
||||
|
||||
def test05(self):
|
||||
def testOutlineDifferentZContinuousEdges(self):
|
||||
"""test05() Verify path generated closed wire with differing Z-heights: "Edge13", "Edge7", "Edge9", "Edge2", "Edge8", "Edge15", "Edge30", "Edge31", "Edge29", "Edge19"."""
|
||||
|
||||
# Instantiate a Adaptive operation and set Base Geometry
|
||||
@@ -255,8 +253,8 @@ class TestPathAdaptive(PathTestBase):
|
||||
],
|
||||
)
|
||||
] # (base, subs_list)
|
||||
adaptive.Label = "test05+"
|
||||
adaptive.Comment = 'test05() Verify path generated closed wire with differing Z-heights: "Edge13", "Edge7", "Edge9", "Edge2", "Edge8", "Edge15", "Edge30", "Edge31", "Edge29", "Edge19".'
|
||||
adaptive.Label = "testOutlineDifferentZContinuous+"
|
||||
adaptive.Comment = 'testOutlineDifferentZContinuous() Verify path generated closed wire with differing Z-heights: "Edge13", "Edge7", "Edge9", "Edge2", "Edge8", "Edge15", "Edge30", "Edge31", "Edge29", "Edge19".'
|
||||
|
||||
# Set additional operation properties
|
||||
# setDepthsAndHeights(adaptive)
|
||||
@@ -276,8 +274,8 @@ class TestPathAdaptive(PathTestBase):
|
||||
|
||||
self.assertTrue(len(adaptive.Path.Commands) > 100, "Command count not greater than 100.")
|
||||
|
||||
def test06(self):
|
||||
"""test06() Verify path generated with outer and inner edge loops at same Z height: "Edge15", "Edge30", "Edge31", "Edge29", "Edge19", "Edge18", "Edge35", "Edge32", "Edge34", "Edge33"."""
|
||||
def testOutlineWithCutout(self):
|
||||
"""testOutlineWithCutout() Verify path generated with outer and inner edge loops at same Z height: "Edge15", "Edge30", "Edge31", "Edge29", "Edge19", "Edge18", "Edge35", "Edge32", "Edge34", "Edge33"."""
|
||||
|
||||
# Instantiate a Adaptive operation and set Base Geometry
|
||||
adaptive = PathAdaptive.Create("Adaptive")
|
||||
@@ -298,8 +296,8 @@ class TestPathAdaptive(PathTestBase):
|
||||
],
|
||||
)
|
||||
] # (base, subs_list)
|
||||
adaptive.Label = "test06+"
|
||||
adaptive.Comment = 'test06() Verify path generated with outer and inner edge loops at same Z height: "Edge15", "Edge30", "Edge31", "Edge29", "Edge19", "Edge18", "Edge35", "Edge32", "Edge34", "Edge33".'
|
||||
adaptive.Label = "testOutlineWithCutout+"
|
||||
adaptive.Comment = 'testOutlineWithCutout() Verify path generated with outer and inner edge loops at same Z height: "Edge15", "Edge30", "Edge31", "Edge29", "Edge19", "Edge18", "Edge35", "Edge32", "Edge34", "Edge33".'
|
||||
|
||||
# Set additional operation properties
|
||||
# setDepthsAndHeights(adaptive)
|
||||
@@ -335,14 +333,14 @@ class TestPathAdaptive(PathTestBase):
|
||||
break
|
||||
self.assertFalse(isInBox, "Paths originating within the inner hole.")
|
||||
|
||||
def test07(self):
|
||||
"""test07() Verify path generated on donut-shaped Face10."""
|
||||
def testFaceWithCutout(self):
|
||||
"""testFaceWithCutout() Verify path generated on donut-shaped Face10."""
|
||||
|
||||
# Instantiate a Adaptive operation and set Base Geometry
|
||||
adaptive = PathAdaptive.Create("Adaptive")
|
||||
adaptive.Base = [(self.doc.Fusion, ["Face10"])] # (base, subs_list)
|
||||
adaptive.Label = "test07+"
|
||||
adaptive.Comment = "test07() Verify path generated on donut-shaped Face10."
|
||||
adaptive.Label = "testFaceWithCutout+"
|
||||
adaptive.Comment = "testFaceWithCutout() Verify path generated on donut-shaped Face10."
|
||||
|
||||
# Set additional operation properties
|
||||
# setDepthsAndHeights(adaptive)
|
||||
@@ -396,10 +394,340 @@ class TestPathAdaptive(PathTestBase):
|
||||
break
|
||||
self.assertTrue(isInBox, "No paths originating within the inner hole.")
|
||||
|
||||
def testModelStockAwareness(self):
|
||||
"""testModelStockAwareness() Tests stock awareness- avoids cutting into the model regardless
|
||||
of bounding box selected."""
|
||||
# Instantiate a Adaptive operation and set Base Geometry
|
||||
adaptive = PathAdaptive.Create("Adaptive")
|
||||
adaptive.Base = [(self.doc.Fusion, ["Face3", "Face10"])] # (base, subs_list)
|
||||
adaptive.Label = "testModelStockAwareness+"
|
||||
adaptive.Comment = "testModelStockAwareness() Verify path generated on adjacent, combined Face3 and Face10. The Z heights are different. Result should be the combination at Z=10 (faces from (0,0) to (40,25), minus tool radius), and only the lower face at Z=5: (15,0) to (40,25)."
|
||||
|
||||
# Set additional operation properties
|
||||
setDepthsAndHeights(adaptive, 15, 0)
|
||||
adaptive.FinishingProfile = False
|
||||
adaptive.HelixAngle = 75.0
|
||||
adaptive.HelixDiameterLimit.Value = 1.0
|
||||
adaptive.LiftDistance.Value = 1.0
|
||||
adaptive.StepOver = 75
|
||||
adaptive.UseOutline = False
|
||||
adaptive.setExpression("StepDown", None)
|
||||
adaptive.StepDown.Value = (
|
||||
5.0 # Have to set expression to None before numerical value assignment
|
||||
)
|
||||
# Don't use helix entry- ensures helix moves are counted in the path
|
||||
# boundary calculation. This should be unnecessary, as the helices are
|
||||
# grown out of the cut area, and thus must be inside of it.
|
||||
adaptive.UseHelixArcs = False
|
||||
|
||||
_addViewProvider(adaptive)
|
||||
self.doc.recompute()
|
||||
|
||||
# Check:
|
||||
# - Bounding box at Z=10 stays within Face3 and Face10- so -X for Face3,
|
||||
# +X and +/-Y for Face10
|
||||
# - bounding box at Z=5 stays within Face10
|
||||
# - No toolpaths at Z=0
|
||||
|
||||
paths = [c for c in adaptive.Path.Commands if c.Name in ["G0", "G00", "G1", "G01"]]
|
||||
toolr = adaptive.OpToolDiameter.Value / 2
|
||||
tol = adaptive.Tolerance
|
||||
|
||||
# Make clean up math below- combine tool radius and tolerance into a
|
||||
# single field that can be added/subtracted to/from bounding boxes
|
||||
moffset = toolr - tol
|
||||
|
||||
zDict = getPathBoundaries(paths, [10, 5, 0])
|
||||
|
||||
# NOTE: Face3 is at Z=10, Face10 is at Z=5
|
||||
bbf3 = self.doc.Fusion.Shape.getElement("Face3").BoundBox
|
||||
bbf10 = self.doc.Fusion.Shape.getElement("Face10").BoundBox
|
||||
|
||||
okAt10 = (
|
||||
zDict[10] is not None
|
||||
and zDict[10]["min"][0] >= bbf3.XMin + moffset
|
||||
and zDict[10]["min"][1] >= bbf10.YMin + moffset
|
||||
and zDict[10]["max"][0] <= bbf10.XMax - moffset
|
||||
and zDict[10]["max"][1] <= bbf10.YMax - moffset
|
||||
)
|
||||
|
||||
okAt5 = (
|
||||
zDict[5] is not None
|
||||
and zDict[5]["min"][0] >= bbf10.XMin + moffset
|
||||
and zDict[5]["min"][1] >= bbf10.YMin + moffset
|
||||
and zDict[5]["max"][0] < bbf10.XMax - moffset
|
||||
and zDict[5]["max"][1] < bbf10.YMax - moffset
|
||||
)
|
||||
|
||||
okAt0 = not zDict[0]
|
||||
|
||||
self.assertTrue(okAt10 and okAt5 and okAt0, "Path boundaries outside of expected regions")
|
||||
|
||||
def testZStockToLeave(self):
|
||||
"""testZStockToLeave() Tests Z stock to leave- with 1mm Z stock to leave, machining
|
||||
at the top of the model should not touch the top model face"""
|
||||
# Instantiate a Adaptive operation and set Base Geometry
|
||||
adaptive = PathAdaptive.Create("Adaptive")
|
||||
adaptive.Base = [(self.doc.Fusion, ["Face3", "Face10"])] # (base, subs_list)
|
||||
adaptive.Label = "testZStockToLeave+"
|
||||
adaptive.Comment = "testZStockToLeave() Verify Z stock is left as requested"
|
||||
|
||||
# Set additional operation properties
|
||||
setDepthsAndHeights(adaptive, 15, 10)
|
||||
adaptive.FinishingProfile = False
|
||||
adaptive.HelixAngle = 75.0
|
||||
adaptive.HelixDiameterLimit.Value = 1.0
|
||||
adaptive.LiftDistance.Value = 1.0
|
||||
adaptive.StepOver = 75
|
||||
adaptive.UseOutline = False
|
||||
adaptive.setExpression("StepDown", None)
|
||||
adaptive.StepDown.Value = (
|
||||
5.0 # Have to set expression to None before numerical value assignment
|
||||
)
|
||||
# Add some Z stock to leave so we avoid Face3 in this stepdown at Z=10
|
||||
adaptive.setExpression("ZStockToLeave", None)
|
||||
adaptive.ZStockToLeave.Value = 1
|
||||
|
||||
_addViewProvider(adaptive)
|
||||
self.doc.recompute()
|
||||
|
||||
# Check:
|
||||
# - No feed path at depth Z=10 touchs Face3
|
||||
toolr = adaptive.OpToolDiameter.Value / 2
|
||||
tol = adaptive.Tolerance
|
||||
|
||||
# Make clean up math below- combine tool radius and tolerance into a
|
||||
# single field that can be added/subtracted to/from bounding boxes
|
||||
moffset = toolr - tol
|
||||
|
||||
# Offset the face we don't expect to touch, verify no move is within
|
||||
# that boundary
|
||||
# NOTE: This isn't a perfect test (won't catch moves that start and end
|
||||
# outside of our face, but cut through/across it), but combined with
|
||||
# other tests should be sufficient.
|
||||
noPathTouchesFace3 = True
|
||||
foffset = self.doc.Fusion.Shape.getElement("Face3").makeOffset2D(moffset)
|
||||
# NOTE: Face3 is at Z=10, and the only feed moves will be at Z=10
|
||||
lastpt = FreeCAD.Vector(0, 0, 10)
|
||||
for p in [c.Parameters for c in adaptive.Path.Commands if c.Name in ["G1", "G01"]]:
|
||||
pt = FreeCAD.Vector(lastpt)
|
||||
if "X" in p:
|
||||
pt.x = p.get("X")
|
||||
if "Y" in p:
|
||||
pt.x = p.get("Y")
|
||||
|
||||
if foffset.isInside(pt, 0.001, True):
|
||||
noPathTouchesFace3 = False
|
||||
break
|
||||
|
||||
lastpt = pt
|
||||
|
||||
self.assertTrue(noPathTouchesFace3, "No feed moves within the top face.")
|
||||
|
||||
def testFullModelAdaptiveRoughing(self):
|
||||
"""testFullModelAdaptiveRoughing() Tests full roughing- should machine entire model with no inputs"""
|
||||
# Instantiate a Adaptive operation and set Base Geometry
|
||||
adaptive = PathAdaptive.Create("Adaptive")
|
||||
adaptive.Base = [(self.doc.Fusion, [])] # (base, subs_list)
|
||||
adaptive.Label = "testFullModelAdaptiveRoughing+"
|
||||
adaptive.Comment = (
|
||||
"testFullModelAdaptiveRoughing() Verify path generated with no subs roughs entire model"
|
||||
)
|
||||
|
||||
# Set additional operation properties
|
||||
setDepthsAndHeights(adaptive, 15, 0)
|
||||
adaptive.FinishingProfile = False
|
||||
adaptive.HelixAngle = 75.0
|
||||
adaptive.HelixDiameterLimit.Value = 1.0
|
||||
adaptive.LiftDistance.Value = 1.0
|
||||
adaptive.StepOver = 75
|
||||
adaptive.UseOutline = False
|
||||
adaptive.setExpression("StepDown", None)
|
||||
adaptive.StepDown.Value = (
|
||||
5.0 # Have to set expression to None before numerical value assignment
|
||||
)
|
||||
# Don't use helix entry- ensures helix moves are counted in the path
|
||||
# boundary calculation. This should be unnecessary, as the helices are
|
||||
# grown out of the cut area, and thus must be inside of it.
|
||||
adaptive.UseHelixArcs = False
|
||||
|
||||
_addViewProvider(adaptive)
|
||||
self.doc.recompute()
|
||||
|
||||
# Check:
|
||||
# - Bounding box at Z=0 goes outside the model box + tool diameter
|
||||
# (has to profile the model)
|
||||
# - Bounding box at Z=5 should go past the model in -X, but only up to the
|
||||
# stock edges in +X and Y
|
||||
# - Bounding box at Z=10 goes to at least stock bounding box edges,
|
||||
# minus tool diameter (has to machine the entire top of the stock off)
|
||||
# - [Should maybe check] At least one move Z = [10,5] is within the model
|
||||
# - [Should maybe check] No moves at Z = 0 are within the model
|
||||
|
||||
paths = [c for c in adaptive.Path.Commands if c.Name in ["G0", "G00", "G1", "G01"]]
|
||||
toolr = adaptive.OpToolDiameter.Value / 2
|
||||
tol = adaptive.Tolerance
|
||||
|
||||
# Make clean up math below- combine tool radius and tolerance into a
|
||||
# single field that can be added/subtracted to/from bounding boxes
|
||||
moffset = toolr - tol
|
||||
|
||||
zDict = getPathBoundaries(paths, [10, 5, 0])
|
||||
mbb = self.doc.Fusion.Shape.BoundBox
|
||||
sbb = adaptive.Document.Stock.Shape.BoundBox
|
||||
|
||||
okAt10 = (
|
||||
zDict[10] is not None
|
||||
and zDict[10]["min"][0] <= sbb.XMin + moffset
|
||||
and zDict[10]["min"][1] <= sbb.YMin + moffset
|
||||
and zDict[10]["max"][0] >= sbb.XMax - moffset
|
||||
and zDict[10]["max"][1] >= sbb.YMax - moffset
|
||||
)
|
||||
|
||||
okAt5 = (
|
||||
zDict[5] is not None
|
||||
and zDict[5]["min"][0] <= mbb.XMin - moffset
|
||||
and zDict[5]["min"][1] <= sbb.YMin + moffset
|
||||
and zDict[5]["max"][0] >= sbb.XMax - moffset
|
||||
and zDict[5]["max"][1] >= sbb.YMax - moffset
|
||||
)
|
||||
|
||||
okAt0 = (
|
||||
zDict[0] is not None
|
||||
and zDict[0]["min"][0] <= mbb.XMin - moffset
|
||||
and zDict[0]["min"][1] <= mbb.YMin - moffset
|
||||
and zDict[0]["max"][0] >= mbb.XMax + moffset
|
||||
and zDict[0]["max"][1] >= mbb.YMax + moffset
|
||||
)
|
||||
|
||||
self.assertTrue(
|
||||
okAt10 and okAt5 and okAt0, "Path boundaries don't include expected regions"
|
||||
)
|
||||
|
||||
def testStockLimitsAwareness(self):
|
||||
"""testStockLimitsAwareness() Tests stock handling- should rough full model, but not cut
|
||||
air excessively where there's not stock"""
|
||||
# Instantiate a Adaptive operation and set Base Geometry
|
||||
adaptive = PathAdaptive.Create("Adaptive")
|
||||
adaptive.Base = [(self.doc.Fusion, [])] # (base, subs_list)
|
||||
adaptive.Label = "testStockLimitsAwareness+"
|
||||
adaptive.Comment = (
|
||||
"testStockLimitsAwareness() Verify machining region is limited to the stock"
|
||||
)
|
||||
|
||||
# Set additional operation properties
|
||||
setDepthsAndHeights(adaptive, 15, 5)
|
||||
adaptive.FinishingProfile = False
|
||||
adaptive.HelixAngle = 75.0
|
||||
adaptive.HelixDiameterLimit.Value = 1.0
|
||||
adaptive.LiftDistance.Value = 1.0
|
||||
adaptive.StepOver = 75
|
||||
adaptive.UseOutline = False
|
||||
adaptive.setExpression("StepDown", None)
|
||||
adaptive.StepDown.Value = (
|
||||
5.0 # Have to set expression to None before numerical value assignment
|
||||
)
|
||||
# Don't use helix entry- ensures helix moves are counted in the path
|
||||
# boundary calculation. This should be unnecessary, as the helices are
|
||||
# grown out of the cut area, and thus must be inside of it.
|
||||
adaptive.UseHelixArcs = False
|
||||
|
||||
# Create and assign new stock that will create different bounds at
|
||||
# different stepdowns
|
||||
btall = Part.makeBox(17, 27, 11, FreeCAD.Vector(-1, -1, 0))
|
||||
bshort = Part.makeBox(42, 27, 6, FreeCAD.Vector(-1, -1, 0))
|
||||
adaptive.Document.Job.Stock.Shape = btall.fuse(bshort)
|
||||
|
||||
_addViewProvider(adaptive)
|
||||
# NOTE: Do NOT recompute entire doc, which will undo our stock change!
|
||||
adaptive.recompute()
|
||||
|
||||
# Check:
|
||||
# - Bounding box at Z=10 stays basically above "btall"
|
||||
# - Bounding box at Z=5 and Z=0 are outside of stock
|
||||
|
||||
paths = [c for c in adaptive.Path.Commands if c.Name in ["G1", "G01"]]
|
||||
toolr = adaptive.OpToolDiameter.Value / 2
|
||||
tol = adaptive.Tolerance
|
||||
|
||||
# Make clean up math below- combine tool radius and tolerance into a
|
||||
# single field that can be added/subtracted to/from bounding boxes
|
||||
# NOTE: ADD tol here, since we're effectively flipping our normal
|
||||
# comparison and want tolerance to make our check looser
|
||||
moffset = toolr + tol
|
||||
|
||||
zDict = getPathBoundaries(paths, [10, 5])
|
||||
sbb = adaptive.Document.Stock.Shape.BoundBox
|
||||
sbb10 = btall.BoundBox
|
||||
|
||||
# These should be no more than a tool radius outside of the "btall"
|
||||
# XY section of the stock
|
||||
okAt10 = (
|
||||
zDict[10] is not None
|
||||
and zDict[10]["min"][0] >= sbb10.XMin - moffset
|
||||
and zDict[10]["min"][1] >= sbb10.YMin - moffset
|
||||
and zDict[10]["max"][0] <= sbb10.XMax + moffset
|
||||
and zDict[10]["max"][1] <= sbb10.YMax + moffset
|
||||
)
|
||||
|
||||
# These should be no more than a tool radius outside of the overall
|
||||
# stock bounding box
|
||||
okAt5 = (
|
||||
zDict[5] is not None
|
||||
and zDict[5]["min"][0] >= sbb.XMin - moffset
|
||||
and zDict[5]["min"][1] >= sbb.YMin - moffset
|
||||
and zDict[5]["max"][0] <= sbb.XMax + moffset
|
||||
and zDict[5]["max"][1] <= sbb.YMax + moffset
|
||||
)
|
||||
|
||||
self.assertTrue(okAt10 and okAt5, "Path feeds extend excessively in +X")
|
||||
|
||||
# POSSIBLY MISSING TESTS:
|
||||
# - Something for region ordering
|
||||
# - Known-edge cases: cones/spheres/cylinders (especially partials on edges
|
||||
# of model + strange angles- especially for cylinders)
|
||||
# - Multiple models/stock
|
||||
# - XY stock to leave
|
||||
|
||||
|
||||
# Eclass
|
||||
|
||||
|
||||
def getPathBoundaries(paths, zLevels):
|
||||
"""getPathBoundaries(paths, zLevels): Takes the list of paths and list of Z
|
||||
depths of interest, and finds the bounding box of the paths at each depth.
|
||||
A dictionary of depth: {"min": (x,y), "max": (x,y)} entries is returned.
|
||||
|
||||
NOTE: You'd think that using Path.BoundBox would give us what we want,
|
||||
but... no, for whatever reason it appears to always extend to (0,0,0)
|
||||
"""
|
||||
last = FreeCAD.Vector(0.0, 0.0, 0.0)
|
||||
# First make sure each element has X, Y, and Z coordinates
|
||||
for p in paths:
|
||||
params = p.Parameters
|
||||
last.x = p.X if "X" in params else last.x
|
||||
last.y = p.Y if "Y" in params else last.y
|
||||
last.z = p.Z if "Z" in params else last.z
|
||||
|
||||
p.X = last.x
|
||||
p.Y = last.y
|
||||
p.Z = last.z
|
||||
|
||||
zDict = {}
|
||||
for z in zLevels:
|
||||
zpaths = [k for k in paths if k.Z == z]
|
||||
if not zpaths:
|
||||
zDict[z] = None
|
||||
continue
|
||||
xmin = min([k.X for k in zpaths])
|
||||
xmax = max([k.X for k in zpaths])
|
||||
ymin = min([k.Y for k in zpaths])
|
||||
ymax = max([k.Y for k in zpaths])
|
||||
zDict[z] = {"min": (xmin, ymin), "max": (xmax, ymax)}
|
||||
|
||||
return zDict
|
||||
|
||||
|
||||
def setDepthsAndHeights(op, strDep=20.0, finDep=0.0):
|
||||
"""setDepthsAndHeights(op, strDep=20.0, finDep=0.0)... Sets default depths and heights for `op` passed to it"""
|
||||
|
||||
@@ -421,43 +749,29 @@ def getGcodeMoves(cmdList, includeRapids=True, includeLines=True, includeArcs=Tr
|
||||
"""getGcodeMoves(cmdList, includeRapids=True, includeLines=True, includeArcs=True)...
|
||||
Accepts command dict and returns point string coordinate.
|
||||
"""
|
||||
|
||||
# NOTE: Can NOT just check "if p.get("X")" or similar- that chokes when X is
|
||||
# zero. That becomes especially obvious when Z=0, and moves end up on the
|
||||
# wrong depth
|
||||
gcode_list = list()
|
||||
last = FreeCAD.Vector(0.0, 0.0, 0.0)
|
||||
for c in cmdList:
|
||||
p = c.Parameters
|
||||
name = c.Name
|
||||
if includeRapids and name in ["G0", "G00"]:
|
||||
if (includeRapids and name in ["G0", "G00"]) or (includeLines and name in ["G1", "G01"]):
|
||||
gcode = name
|
||||
x = last.x
|
||||
y = last.y
|
||||
z = last.z
|
||||
if p.get("X"):
|
||||
if "X" in p:
|
||||
x = round(p["X"], 2)
|
||||
gcode += " X" + str(x)
|
||||
if p.get("Y"):
|
||||
gcode += " X" + str(x)
|
||||
if "Y" in p:
|
||||
y = round(p["Y"], 2)
|
||||
gcode += " Y" + str(y)
|
||||
if p.get("Z"):
|
||||
gcode += " Y" + str(y)
|
||||
if "Z" in p:
|
||||
z = round(p["Z"], 2)
|
||||
gcode += " Z" + str(z)
|
||||
last.x = x
|
||||
last.y = y
|
||||
last.z = z
|
||||
gcode_list.append(gcode)
|
||||
elif includeLines and name in ["G1", "G01"]:
|
||||
gcode = name
|
||||
x = last.x
|
||||
y = last.y
|
||||
z = last.z
|
||||
if p.get("X"):
|
||||
x = round(p["X"], 2)
|
||||
gcode += " X" + str(x)
|
||||
if p.get("Y"):
|
||||
y = round(p["Y"], 2)
|
||||
gcode += " Y" + str(y)
|
||||
if p.get("Z"):
|
||||
z = round(p["Z"], 2)
|
||||
gcode += " Z" + str(z)
|
||||
gcode += " Z" + str(z)
|
||||
last.x = x
|
||||
last.y = y
|
||||
last.z = z
|
||||
@@ -470,23 +784,23 @@ def getGcodeMoves(cmdList, includeRapids=True, includeLines=True, includeArcs=Tr
|
||||
i = 0.0
|
||||
j = 0.0
|
||||
k = 0.0
|
||||
if p.get("I"):
|
||||
if "I" in p:
|
||||
i = round(p["I"], 2)
|
||||
gcode += " I" + str(i)
|
||||
if p.get("J"):
|
||||
if "J" in p:
|
||||
j = round(p["J"], 2)
|
||||
gcode += " J" + str(j)
|
||||
if p.get("K"):
|
||||
if "K" in p:
|
||||
k = round(p["K"], 2)
|
||||
gcode += " K" + str(k)
|
||||
|
||||
if p.get("X"):
|
||||
if "X" in p:
|
||||
x = round(p["X"], 2)
|
||||
gcode += " X" + str(x)
|
||||
if p.get("Y"):
|
||||
if "Y" in p:
|
||||
y = round(p["Y"], 2)
|
||||
gcode += " Y" + str(y)
|
||||
if p.get("Z"):
|
||||
if "Z" in p:
|
||||
z = round(p["Z"], 2)
|
||||
gcode += " Z" + str(z)
|
||||
|
||||
@@ -501,7 +815,7 @@ def pathOriginatesInBox(cmd, minPoint, maxPoint):
|
||||
p = cmd.Parameters
|
||||
name = cmd.Name
|
||||
if name in ["G0", "G00", "G1", "G01"]:
|
||||
if p.get("X") and p.get("Y"):
|
||||
if "X" in p and "Y" in p:
|
||||
x = p.get("X")
|
||||
y = p.get("Y")
|
||||
if x > minPoint.x and y > minPoint.y and x < maxPoint.x and y < maxPoint.y:
|
||||
|
||||
@@ -43,103 +43,19 @@
|
||||
<item>
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="25" column="0">
|
||||
<widget class="QCheckBox" name="useOutline">
|
||||
<property name="text">
|
||||
<string>Use Outline</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="18" column="0">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string>Keep Tool Down Ratio</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Helix Max Diameter</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="OperationType">
|
||||
<property name="toolTip">
|
||||
<string>Type of adaptive operation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Cut Region</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Step Over Percent</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="23" column="0">
|
||||
<widget class="QCheckBox" name="ForceInsideOut">
|
||||
<property name="text">
|
||||
<string>Force Clearing Inside-out</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Lift Distance</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="20" column="0">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="text">
|
||||
<string>Stock to Leave</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Operation Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Helix Ramp Angle</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="24" column="0">
|
||||
<widget class="QCheckBox" name="FinishingProfile">
|
||||
<property name="text">
|
||||
<string>Finishing Profile</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<widget class="QFrame" name="frame_3">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
@@ -152,7 +68,9 @@
|
||||
<item>
|
||||
<widget class="QSlider" name="Tolerance">
|
||||
<property name="toolTip">
|
||||
<string>Influences calculation performance vs stability and accuracy</string>
|
||||
<string>Influences calculation performance vs stability and accuracy.
|
||||
|
||||
Larger values (further to the right) will calculate faster; smaller values (further to the left) will result in more accurate toolpaths.</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>5</number>
|
||||
@@ -167,7 +85,7 @@
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="tickInterval">
|
||||
<number>1</number>
|
||||
@@ -177,6 +95,13 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="23" column="0">
|
||||
<widget class="QCheckBox" name="ForceInsideOut">
|
||||
<property name="text">
|
||||
<string>Force Clearing Inside-out</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="Side">
|
||||
<property name="toolTip">
|
||||
@@ -184,53 +109,37 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="OperationType">
|
||||
<property name="toolTip">
|
||||
<string>Type of adaptive operation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="24" column="0">
|
||||
<widget class="QCheckBox" name="FinishingProfile">
|
||||
<property name="text">
|
||||
<string>Helix Cone Angle</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="HelixDiameterLimit">
|
||||
<property name="toolTip">
|
||||
<string>If greater than zero it limits the helix ramp diameter, otherwise 75 percent of tool diameter is used</string>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="LiftDistance">
|
||||
<property name="toolTip">
|
||||
<string>How much to lift the tool up during the rapid linking moves over cleared regions. If linking path is not clear tool is raised to clearance height.</string>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="18" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="KeepToolDownRatio">
|
||||
<property name="toolTip">
|
||||
<string>Max length of keep-tool-down linking path compared to direct distance between points. If exceeded link will be done by raising the tool to clearance height.</string>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true"/>
|
||||
<string>Finishing Profile</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="20" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="StockToLeave">
|
||||
<widget class="Gui::QuantitySpinBox" name="StockToLeave" native="true">
|
||||
<property name="toolTip">
|
||||
<string>How much material to leave (i.e. for finishing operation)</string>
|
||||
<string>How much material to leave in the XY plane (i.e. for finishing operation)</string>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="20" column="0">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="text">
|
||||
<string>XY Stock to Leave</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QSpinBox" name="stepOverPercent">
|
||||
<property name="toolTip">
|
||||
@@ -250,13 +159,10 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<widget class="Gui::InputField" name="HelixAngle">
|
||||
<property name="toolTip">
|
||||
<string>Angle of the helix ramp entry</string>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true"/>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Step Over Percent</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -270,6 +176,128 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Helix Ramp Angle</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="25" column="0">
|
||||
<widget class="QCheckBox" name="useOutline">
|
||||
<property name="text">
|
||||
<string>Use Outline</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Operation Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="LiftDistance" native="true">
|
||||
<property name="toolTip">
|
||||
<string>How much to lift the tool up during the rapid linking moves over cleared regions. If linking path is not clear tool is raised to clearance height.</string>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="18" column="0">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string>Keep Tool Down Ratio</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="HelixDiameterLimit" native="true">
|
||||
<property name="toolTip">
|
||||
<string>If greater than zero it limits the helix ramp diameter, otherwise 75 percent of tool diameter is used</string>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Helix Cone Angle</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<widget class="Gui::InputField" name="HelixAngle">
|
||||
<property name="toolTip">
|
||||
<string>Angle of the helix ramp entry</string>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Lift Distance</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Cut Region</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="18" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="KeepToolDownRatio" native="true">
|
||||
<property name="toolTip">
|
||||
<string>Max length of keep-tool-down linking path compared to direct distance between points. If exceeded link will be done by raising the tool to clearance height.</string>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Helix Max Diameter</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="26" column="0">
|
||||
<widget class="QCheckBox" name="orderCutsByRegion">
|
||||
<property name="toolTip">
|
||||
<string>After calculating toolpaths, the default cut order is by depth- all regions at a given stepdown are cleared before moving to the next stepdown.
|
||||
|
||||
This option changes that behavior to cut each discrete area to its full depth before moving on to the next.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Order cuts by region</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="21" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Z Stock to Leave</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="21" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="ZStockToLeave" native="true">
|
||||
<property name="toolTip">
|
||||
<string>How much material to leave along the Z axis (i.e. for finishing operation)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -283,7 +311,7 @@
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -50,6 +50,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
)
|
||||
self.form.KeepToolDownRatio.setProperty("unit", obj.KeepToolDownRatio.getUserPreferred()[2])
|
||||
self.form.StockToLeave.setProperty("unit", obj.StockToLeave.getUserPreferred()[2])
|
||||
self.form.ZStockToLeave.setProperty("unit", obj.ZStockToLeave.getUserPreferred()[2])
|
||||
|
||||
def getSignalsForUpdate(self, obj):
|
||||
"""getSignalsForUpdate(obj) ... return list of signals for updating obj"""
|
||||
@@ -65,10 +66,12 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
signals.append(self.form.LiftDistance.valueChanged)
|
||||
signals.append(self.form.KeepToolDownRatio.valueChanged)
|
||||
signals.append(self.form.StockToLeave.valueChanged)
|
||||
signals.append(self.form.ZStockToLeave.valueChanged)
|
||||
signals.append(self.form.coolantController.currentIndexChanged)
|
||||
signals.append(self.form.ForceInsideOut.stateChanged)
|
||||
signals.append(self.form.FinishingProfile.stateChanged)
|
||||
signals.append(self.form.useOutline.stateChanged)
|
||||
signals.append(self.form.orderCutsByRegion.stateChanged)
|
||||
signals.append(self.form.StopButton.toggled)
|
||||
return signals
|
||||
|
||||
@@ -97,9 +100,13 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
if hasattr(obj, "StockToLeave"):
|
||||
self.form.StockToLeave.setProperty("rawValue", obj.StockToLeave.Value)
|
||||
|
||||
if hasattr(obj, "ZStockToLeave"):
|
||||
self.form.ZStockToLeave.setProperty("rawValue", obj.ZStockToLeave.Value)
|
||||
|
||||
self.form.ForceInsideOut.setChecked(obj.ForceInsideOut)
|
||||
self.form.FinishingProfile.setChecked(obj.FinishingProfile)
|
||||
self.form.useOutline.setChecked(obj.UseOutline)
|
||||
self.form.orderCutsByRegion.setChecked(obj.OrderCutsByRegion)
|
||||
self.setupToolController(obj, self.form.ToolController)
|
||||
self.setupCoolant(obj, self.form.coolantController)
|
||||
self.form.StopButton.setChecked(obj.Stopped)
|
||||
@@ -130,9 +137,13 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
if hasattr(obj, "StockToLeave"):
|
||||
PathGuiUtil.updateInputField(obj, "StockToLeave", self.form.StockToLeave)
|
||||
|
||||
if hasattr(obj, "ZStockToLeave"):
|
||||
PathGuiUtil.updateInputField(obj, "ZStockToLeave", self.form.ZStockToLeave)
|
||||
|
||||
obj.ForceInsideOut = self.form.ForceInsideOut.isChecked()
|
||||
obj.FinishingProfile = self.form.FinishingProfile.isChecked()
|
||||
obj.UseOutline = self.form.useOutline.isChecked()
|
||||
obj.OrderCutsByRegion = self.form.orderCutsByRegion.isChecked()
|
||||
obj.Stopped = self.form.StopButton.isChecked()
|
||||
if obj.Stopped:
|
||||
self.form.StopButton.setChecked(False) # reset the button
|
||||
|
||||
Reference in New Issue
Block a user