[CAM] implement multipass profile operations (#17326)
* implement multipass profile operations * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -2420,14 +2420,10 @@ void Area::makeOffset(list<shared_ptr<CArea>>& areas,
|
||||
#endif
|
||||
|
||||
if (offset < 0) {
|
||||
stepover = -fabs(stepover);
|
||||
if (count < 0) {
|
||||
if (!last_stepover) {
|
||||
last_stepover = offset * 0.5;
|
||||
}
|
||||
else {
|
||||
last_stepover = -fabs(last_stepover);
|
||||
}
|
||||
}
|
||||
else {
|
||||
last_stepover = 0;
|
||||
|
||||
@@ -115,6 +115,43 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="numPassesLabel">
|
||||
<property name="text">
|
||||
<string>Number of Passes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QSpinBox" name="numPasses">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>The number of passes to do. If more than one, requires a non-zero value for Pass Stepover.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="stepoverLabel">
|
||||
<property name="text">
|
||||
<string>Pass Stepover</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="Gui::InputField" name="stepover">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>If doing multiple passes, the extra offset of each additional pass.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -77,6 +77,8 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
if obj.Direction != str(self.form.direction.currentData()):
|
||||
obj.Direction = str(self.form.direction.currentData())
|
||||
PathGuiUtil.updateInputField(obj, "OffsetExtra", self.form.extraOffset)
|
||||
obj.NumPasses = self.form.numPasses.value()
|
||||
PathGuiUtil.updateInputField(obj, "Stepover", self.form.stepover)
|
||||
|
||||
if obj.UseComp != self.form.useCompensation.isChecked():
|
||||
obj.UseComp = self.form.useCompensation.isChecked()
|
||||
@@ -100,6 +102,10 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
self.form.extraOffset.setText(
|
||||
FreeCAD.Units.Quantity(obj.OffsetExtra.Value, FreeCAD.Units.Length).UserString
|
||||
)
|
||||
self.form.numPasses.setValue(obj.NumPasses)
|
||||
self.form.stepover.setText(
|
||||
FreeCAD.Units.Quantity(obj.Stepover.Value, FreeCAD.Units.Length).UserString
|
||||
)
|
||||
|
||||
self.form.useCompensation.setChecked(obj.UseComp)
|
||||
self.form.useStartPoint.setChecked(obj.UseStartPoint)
|
||||
@@ -117,6 +123,8 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
signals.append(self.form.cutSide.currentIndexChanged)
|
||||
signals.append(self.form.direction.currentIndexChanged)
|
||||
signals.append(self.form.extraOffset.editingFinished)
|
||||
signals.append(self.form.numPasses.editingFinished)
|
||||
signals.append(self.form.stepover.editingFinished)
|
||||
signals.append(self.form.useCompensation.stateChanged)
|
||||
signals.append(self.form.useStartPoint.stateChanged)
|
||||
signals.append(self.form.processHoles.stateChanged)
|
||||
@@ -148,8 +156,11 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
|
||||
self.form.processHoles.hide()
|
||||
self.form.processPerimeter.hide()
|
||||
|
||||
self.form.stepover.setEnabled(self.obj.NumPasses > 1)
|
||||
|
||||
def registerSignalHandlers(self, obj):
|
||||
self.form.useCompensation.stateChanged.connect(self.updateVisibility)
|
||||
self.form.numPasses.editingFinished.connect(self.updateVisibility)
|
||||
|
||||
|
||||
# Eclass
|
||||
|
||||
@@ -172,6 +172,24 @@ class ObjectProfile(PathAreaOp.ObjectOp):
|
||||
"App::Property", "Make True, if using Cutter Radius Compensation"
|
||||
),
|
||||
),
|
||||
(
|
||||
"App::PropertyInteger",
|
||||
"NumPasses",
|
||||
"Profile",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"The number of passes to do. If more than one, requires a non-zero value for Stepover",
|
||||
),
|
||||
),
|
||||
(
|
||||
"App::PropertyDistance",
|
||||
"Stepover",
|
||||
"Profile",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"If doing multiple passes, the extra offset of each additional pass",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
@@ -235,6 +253,8 @@ class ObjectProfile(PathAreaOp.ObjectOp):
|
||||
"processCircles": False,
|
||||
"processHoles": False,
|
||||
"processPerimeter": True,
|
||||
"Stepover": 0,
|
||||
"NumPasses": 1,
|
||||
}
|
||||
|
||||
def areaOpApplyPropertyDefaults(self, obj, job, propList):
|
||||
@@ -295,6 +315,26 @@ class ObjectProfile(PathAreaOp.ObjectOp):
|
||||
self.initAreaOpProperties(obj, warn=True)
|
||||
self.areaOpSetDefaultValues(obj, PathUtils.findParentJob(obj))
|
||||
self.setOpEditorProperties(obj)
|
||||
if not hasattr(obj, "NumPasses"):
|
||||
obj.addProperty(
|
||||
"App::PropertyInteger",
|
||||
"NumPasses",
|
||||
"Profile",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"The number of passes to do. Requires a non-zero value for Stepover",
|
||||
),
|
||||
)
|
||||
if not hasattr(obj, "Stepover"):
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"Stepover",
|
||||
"Profile",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"If doing multiple passes, the extra offset of each additional pass",
|
||||
),
|
||||
)
|
||||
|
||||
def areaOpOnChanged(self, obj, prop):
|
||||
"""areaOpOnChanged(obj, prop) ... updates certain property visibilities depending on changed properties."""
|
||||
@@ -311,13 +351,31 @@ class ObjectProfile(PathAreaOp.ObjectOp):
|
||||
params["SectionCount"] = -1
|
||||
|
||||
offset = obj.OffsetExtra.Value # 0.0
|
||||
num_passes = max(1, obj.NumPasses)
|
||||
stepover = obj.Stepover.Value
|
||||
if num_passes > 1 and stepover == 0:
|
||||
# This check is important because C++ code has a default value for stepover if it's 0 and extra passes are requested
|
||||
num_passes = 1
|
||||
Path.Log.warning(
|
||||
"Multipass profile requires a non-zero stepover. Reducing to a single pass."
|
||||
)
|
||||
|
||||
if obj.UseComp:
|
||||
offset = self.radius + obj.OffsetExtra.Value
|
||||
if obj.Side == "Inside":
|
||||
offset = 0 - offset
|
||||
stepover = -stepover
|
||||
if isHole:
|
||||
offset = 0 - offset
|
||||
stepover = -stepover
|
||||
|
||||
# Modify offset and stepover to do passes from most-offset to least
|
||||
offset += stepover * (num_passes - 1)
|
||||
stepover = -stepover
|
||||
|
||||
params["Offset"] = offset
|
||||
params["ExtraPass"] = num_passes - 1
|
||||
params["Stepover"] = stepover
|
||||
|
||||
jointype = ["Round", "Square", "Miter"]
|
||||
params["JoinType"] = jointype.index(obj.JoinType)
|
||||
@@ -356,6 +414,10 @@ class ObjectProfile(PathAreaOp.ObjectOp):
|
||||
else:
|
||||
params["orientation"] = 0
|
||||
|
||||
if obj.NumPasses > 1:
|
||||
# Disable path sorting to ensure that offsets appear in order, from farthest offset to closest, on all layers
|
||||
params["sort_mode"] = 0
|
||||
|
||||
return params
|
||||
|
||||
def areaOpUseProjection(self, obj):
|
||||
@@ -590,7 +652,11 @@ class ObjectProfile(PathAreaOp.ObjectOp):
|
||||
if flattened and zDiff >= self.JOB.GeometryTolerance.Value:
|
||||
cutWireObjs = False
|
||||
openEdges = []
|
||||
passOffsets = [self.ofstRadius]
|
||||
params = self.areaOpAreaParams(obj, False)
|
||||
passOffsets = [
|
||||
self.ofstRadius + i * abs(params["Stepover"])
|
||||
for i in range(params["ExtraPass"] + 1)
|
||||
][::-1]
|
||||
(origWire, flatWire) = flattened
|
||||
|
||||
self._addDebugObject("FlatWire", flatWire)
|
||||
|
||||
Reference in New Issue
Block a user