CAM: Adaptive: Add Z stock to leave (separate from XY stock to leave) and order-by-region/order-by-depth cut ordering options

This commit is contained in:
Dan Taylor
2025-04-02 20:47:44 -05:00
parent eece614172
commit 31ca3e742f
4 changed files with 411 additions and 154 deletions

View File

@@ -463,6 +463,67 @@ class TestPathAdaptive(PathTestBase):
self.assertTrue(okAt10 and okAt5 and okAt0, "Path boundaries outside of expected regions")
def test09(self):
"""test09() 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 = "test09+"
adaptive.Comment = "test09() 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.")
# Eclass

View File

@@ -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>

View File

@@ -628,6 +628,8 @@ def Execute(op, obj):
{
"opType": outsideOpType,
"path2d": convertTo2d(rdict["edges"]),
"id": rdict["id"],
"children": rdict["children"],
# FIXME: Kinda gross- just use this to match up with the
# appropriate stockpaths entry...
"startdepth": rdict["depths"][0],
@@ -641,6 +643,8 @@ def Execute(op, obj):
{
"opType": insideOpType,
"path2d": convertTo2d(rdict["edges"]),
"id": rdict["id"],
"children": rdict["children"],
# FIXME: Kinda gross- just use this to match up with the
# appropriate stockpaths entry...
"startdepth": rdict["depths"][0],
@@ -668,6 +672,8 @@ def Execute(op, obj):
"finishingProfile": obj.FinishingProfile,
"keepToolDownRatio": keepToolDownRatio,
"stockToLeave": obj.StockToLeave.Value,
"zStockToLeave": obj.ZStockToLeave.Value,
"orderCutsByRegion": obj.OrderCutsByRegion,
}
insideInputStateObject = {
@@ -683,6 +689,8 @@ def Execute(op, obj):
"finishingProfile": obj.FinishingProfile,
"keepToolDownRatio": keepToolDownRatio,
"stockToLeave": obj.StockToLeave.Value,
"zStockToLeave": obj.ZStockToLeave.Value,
"orderCutsByRegion": obj.OrderCutsByRegion,
}
inputStateObject = [outsideInputStateObject, insideInputStateObject]
@@ -740,6 +748,7 @@ def Execute(op, obj):
a2d.toolDiameter = op.tool.Diameter.Value
a2d.helixRampDiameter = helixDiameter
a2d.keepToolDownDistRatio = keepToolDownRatio
# NOTE: Z stock is handled in our stepdowns
a2d.stockToLeave = obj.StockToLeave.Value
a2d.tolerance = obj.Tolerance
a2d.forceInsideOut = obj.ForceInsideOut
@@ -762,9 +771,25 @@ def Execute(op, obj):
for t in alltuples:
depths += [d for d in t[0]]
depths = sorted(list(set(depths)), reverse=True)
for d in depths:
cutlist += [([d], o[1]) for o in outsidePathArray2dDepthTuples if d in o[0]]
cutlist += [([d], i[1]) for i in insidePathArray2dDepthTuples if d in i[0]]
if obj.OrderCutsByRegion:
# Translate child ID numbers to an actual reference to the
# associated tuple
for rdict in regionOps:
rdict["childTuples"] = [t for t in alltuples if t[1]["id"] in rdict["children"]]
# Helper function to recurse down children
def addToCutList(tuples):
for k in tuples:
if k in cutlist:
continue
cutlist.append(k)
addToCutList(k[1]["childTuples"])
addToCutList(alltuples)
else:
for d in depths:
cutlist += [([d], o[1]) for o in outsidePathArray2dDepthTuples if d in o[0]]
cutlist += [([d], i[1]) for i in insidePathArray2dDepthTuples if d in i[0]]
# need to convert results to python object to be JSON serializable
stepdown = max(obj.StepDown.Value, _ADAPTIVE_MIN_STEPDOWN)
@@ -972,7 +997,9 @@ def _workingEdgeHelperManual(op, obj, depths):
lastdepth = depth
continue
aboveRefined = _getSolidProjection(shps, depth)
# NOTE: Slice stock lower than cut depth to effectively leave (at least)
# obj.ZStockToLeave
aboveRefined = _getSolidProjection(shps, depth - obj.ZStockToLeave.Value)
# Create appropriate tuples and add to list, processing inside/outside
# as requested by operation
@@ -1079,10 +1106,14 @@ def _getWorkingEdges(op, obj):
# Get the stock outline at each stepdown. Used to calculate toolpaths and
# for calcuating cut regions in some instances
# NOTE: Slice stock lower than cut depth to effectively leave (at least)
# obj.ZStockToLeave
# NOTE: Stock is handled DIFFERENTLY than inside and outside regions!
# Combining different depths just adds code to look up the correct outline
# when computing inside/outside regions, for no real benefit.
stockProjectionDict = {d: _getSolidProjection(op.stock.Shape, d) for d in depths}
stockProjectionDict = {
d: _getSolidProjection(op.stock.Shape, d - obj.ZStockToLeave.Value) for d in depths
}
# If user specified edges, calculate the machining regions based on that
# input. Otherwise, process entire model
@@ -1092,6 +1123,101 @@ def _getWorkingEdges(op, obj):
# to be avoided at those depths.
insideRegions, outsideRegions = _workingEdgeHelperManual(op, obj, depths)
# Find all children of each region. A child of region X is any region Y such
# that Y is a subset of X AND Y starts within one stepdown of X (ie, direct
# children only).
# NOTE: Inside and outside regions are inverses of each other, so above
# refers to the area to be machined!
# Assign an ID number to track each region
idnumber = 0
for r in insideRegions + outsideRegions:
r["id"] = idnumber
r["children"] = list()
idnumber += 1
# NOTE: Inside and outside regions are inverses of each other
# NOTE: Outside regions can't have parents
for rx in insideRegions:
for ry in [k for k in insideRegions if k != rx]:
dist = min(rx["depths"]) - max(ry["depths"])
# Ignore regions at our level or above, or more than one step down
if dist <= 0 or dist > depthParams.step_down:
continue
if not ry["region"].cut(rx["region"]).Wires:
rx["children"].append(ry["id"])
# See which outside region this is a child of- basically inverse of above
for ry in [k for k in outsideRegions]:
dist = min(ry["depths"]) - max(rx["depths"])
# Ignore regions at our level or above, or more than one step down
if dist <= 0 or dist > depthParams.step_down:
continue
# child if there is NO overlap between the stay-outside and stay-
# inside regions
# Also a child if the outer region is NULL (includes everything)
# NOTE: See "isNull() note" at top of file
if not ry["region"].Wires or not rx["region"].common(ry["region"]).Wires:
ry["children"].append(rx["id"])
# Further split regions as necessary for when the stock changes- a region as
# reported here is where a toolpath will be generated, and can be projected
# along all of the depths associated with it. By doing this, we can minimize
# the number of toolpaths that need to be generated AND avoid more complex
# logic in depth-first vs region-first sorting of regions.
# NOTE: For internal regions, stock is "the same" if the region cut with
# the stock results in the same region.
# NOTE: For external regions, stock is "the same" if the stock cut by the
# region results in the same region
def _regionChildSplitterHelper(regions, areInsideRegions):
nonlocal stockProjectionDict
nonlocal idnumber
for r in regions:
depths = sorted(r["depths"], reverse=True)
if areInsideRegions:
rcut = r["region"].cut(stockProjectionDict[depths[0]])
else:
# NOTE: We may end up with empty "outside" regions in the space
# between the top of the stock and the top of the model- want
# to machine the entire stock in that case
# NOTE: See "isNull() note" at top of file
if not r["region"].Wires:
rcut = stockProjectionDict[depths[0]]
else:
rcut = stockProjectionDict[depths[0]].cut(r["region"])
parentdepths = depths[0:1]
# If the region cut with the stock at a new depth is different than
# the original cut, we need to split this region
# The new region gets all of the children, and becomes a child of
# the existing region.
for d in depths[1:]:
if (
areInsideRegions and r["region"].cut(stockProjectionDict[d]).cut(rcut).Wires
) or stockProjectionDict[d].cut(r["region"]).cut(rcut).Wires:
newregion = {
"id": idnumber,
"depths": [k for k in depths if k not in parentdepths],
"region": r["region"],
"children": r["children"],
}
# Update parent with the new region as a child, along with all
# the depths it was unchanged on
r["children"] = [idnumber]
r["depths"] = parentdepths
# Add the new region to the end of the list and stop processing
# this region
# When the new region is processed at the end, we'll effectively
# recurse and handle splitting that new region if required
regions.append(newregion)
idnumber += 1
continue
# If we didn't split at this depth, the parent will keep "control"
# of this depth
parentdepths.append(d)
_regionChildSplitterHelper(insideRegions, True)
_regionChildSplitterHelper(outsideRegions, False)
# Create discretized regions
def _createDiscretizedRegions(regionDicts):
discretizedRegions = list()
@@ -1100,6 +1226,8 @@ def _getWorkingEdges(op, obj):
{
"edges": [[discretize(w)] for w in rdict["region"].Wires],
"depths": rdict["depths"],
"id": rdict["id"],
"children": rdict["children"],
}
)
return discretizedRegions
@@ -1190,11 +1318,6 @@ class PathAdaptive(PathOp.ObjectOp):
"Side of selected faces that tool should cut",
),
)
# obj.Side = [
# "Outside",
# "Inside",
# ] # side of profile that cutter is on in relation to direction of profile
obj.addProperty(
"App::PropertyEnumeration",
"OperationType",
@@ -1204,11 +1327,6 @@ class PathAdaptive(PathOp.ObjectOp):
"Type of adaptive operation",
),
)
# obj.OperationType = [
# "Clearing",
# "Profiling",
# ] # side of profile that cutter is on in relation to direction of profile
obj.addProperty(
"App::PropertyFloat",
"Tolerance",
@@ -1251,7 +1369,16 @@ class PathAdaptive(PathOp.ObjectOp):
"Adaptive",
QT_TRANSLATE_NOOP(
"App::Property",
"How much stock to leave (i.e. for finishing operation)",
"How much stock to leave in the XY plane (eg for finishing operation)",
),
)
obj.addProperty(
"App::PropertyDistance",
"ZStockToLeave",
"Adaptive",
QT_TRANSLATE_NOOP(
"App::Property",
"How much stock to leave along the Z axis (eg for finishing operation)",
),
)
obj.addProperty(
@@ -1279,7 +1406,6 @@ class PathAdaptive(PathOp.ObjectOp):
QT_TRANSLATE_NOOP("App::Property", "Stop processing"),
)
obj.setEditorMode("Stopped", 2) # hide this property
obj.addProperty(
"App::PropertyBool",
"StopProcessing",
@@ -1290,7 +1416,6 @@ class PathAdaptive(PathOp.ObjectOp):
),
)
obj.setEditorMode("StopProcessing", 2) # hide this property
obj.addProperty(
"App::PropertyBool",
"UseHelixArcs",
@@ -1300,7 +1425,6 @@ class PathAdaptive(PathOp.ObjectOp):
"Use Arcs (G2) for helix ramp",
),
)
obj.addProperty(
"App::PropertyPythonObject",
"AdaptiveInputState",
@@ -1348,7 +1472,6 @@ class PathAdaptive(PathOp.ObjectOp):
"Limit helix entry diameter, if limit larger than tool diameter or 0, tool diameter is used",
),
)
obj.addProperty(
"App::PropertyBool",
"UseOutline",
@@ -1358,7 +1481,15 @@ class PathAdaptive(PathOp.ObjectOp):
"Uses the outline of the base geometry.",
),
)
obj.addProperty(
"App::PropertyBool",
"OrderCutsByRegion",
"Adaptive",
QT_TRANSLATE_NOOP(
"App::Property",
"Orders cuts by region instead of depth.",
),
)
obj.addProperty(
"Part::PropertyPartShape",
"removalshape",
@@ -1390,9 +1521,11 @@ class PathAdaptive(PathOp.ObjectOp):
obj.AdaptiveInputState = ""
obj.AdaptiveOutputState = ""
obj.StockToLeave = 0
obj.ZStockToLeave = 0
obj.KeepToolDownRatio = 3.0
obj.UseHelixArcs = False
obj.UseOutline = False
obj.OrderCutsByRegion = False
FeatureExtensions.set_default_property_values(obj, job)
def opExecute(self, obj):
@@ -1427,6 +1560,28 @@ class PathAdaptive(PathOp.ObjectOp):
"Uses the outline of the base geometry.",
)
if not hasattr(obj, "OrderCutsByRegion"):
obj.addProperty(
"App::PropertyBool",
"OrderCutsByRegion",
"Adaptive",
QT_TRANSLATE_NOOP(
"App::Property",
"Orders cuts by region instead of depth.",
),
)
if not hasattr(obj, "ZStockToLeave"):
obj.addProperty(
"App::PropertyDistance",
"ZStockToLeave",
"Adaptive",
QT_TRANSLATE_NOOP(
"App::Property",
"How much stock to leave along the Z axis (eg for finishing operation)",
),
)
if not hasattr(obj, "removalshape"):
obj.addProperty("Part::PropertyPartShape", "removalshape", "Path", "")
obj.setEditorMode("removalshape", 2) # hide
@@ -1446,6 +1601,7 @@ def SetupProperties():
"LiftDistance",
"KeepToolDownRatio",
"StockToLeave",
"ZStockToLeave",
"ForceInsideOut",
"FinishingProfile",
"Stopped",
@@ -1457,6 +1613,7 @@ def SetupProperties():
"HelixConeAngle",
"HelixDiameterLimit",
"UseOutline",
"OrderCutsByRegion",
]
return setup

View File

@@ -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