Skip to content
This repository was archived by the owner on Mar 1, 2025. It is now read-only.

Commit 1955f28

Browse files
[CAM] implement multipass profile operations (FreeCAD#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>
1 parent 00f6fbe commit 1955f28

File tree

4 files changed

+115
-5
lines changed

4 files changed

+115
-5
lines changed

src/Mod/CAM/App/Area.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2420,14 +2420,10 @@ void Area::makeOffset(list<shared_ptr<CArea>>& areas,
24202420
#endif
24212421

24222422
if (offset < 0) {
2423-
stepover = -fabs(stepover);
24242423
if (count < 0) {
24252424
if (!last_stepover) {
24262425
last_stepover = offset * 0.5;
24272426
}
2428-
else {
2429-
last_stepover = -fabs(last_stepover);
2430-
}
24312427
}
24322428
else {
24332429
last_stepover = 0;

src/Mod/CAM/Gui/Resources/panels/PageOpProfileFullEdit.ui

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,43 @@
115115
</property>
116116
</widget>
117117
</item>
118+
<item row="3" column="0">
119+
<widget class="QLabel" name="numPassesLabel">
120+
<property name="text">
121+
<string>Number of Passes</string>
122+
</property>
123+
</widget>
124+
</item>
125+
<item row="3" column="1">
126+
<widget class="QSpinBox" name="numPasses">
127+
<property name="minimum">
128+
<number>1</number>
129+
</property>
130+
<property name="toolTip">
131+
<string>The number of passes to do. If more than one, requires a non-zero value for Pass Stepover.</string>
132+
</property>
133+
</widget>
134+
</item>
135+
<item row="4" column="0">
136+
<widget class="QLabel" name="stepoverLabel">
137+
<property name="text">
138+
<string>Pass Stepover</string>
139+
</property>
140+
</widget>
141+
</item>
142+
<item row="4" column="1">
143+
<widget class="Gui::InputField" name="stepover">
144+
<property name="sizePolicy">
145+
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
146+
<horstretch>0</horstretch>
147+
<verstretch>0</verstretch>
148+
</sizepolicy>
149+
</property>
150+
<property name="toolTip">
151+
<string>If doing multiple passes, the extra offset of each additional pass.</string>
152+
</property>
153+
</widget>
154+
</item>
118155
</layout>
119156
</widget>
120157
</item>

src/Mod/CAM/Path/Op/Gui/Profile.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ def getFields(self, obj):
7777
if obj.Direction != str(self.form.direction.currentData()):
7878
obj.Direction = str(self.form.direction.currentData())
7979
PathGuiUtil.updateInputField(obj, "OffsetExtra", self.form.extraOffset)
80+
obj.NumPasses = self.form.numPasses.value()
81+
PathGuiUtil.updateInputField(obj, "Stepover", self.form.stepover)
8082

8183
if obj.UseComp != self.form.useCompensation.isChecked():
8284
obj.UseComp = self.form.useCompensation.isChecked()
@@ -100,6 +102,10 @@ def setFields(self, obj):
100102
self.form.extraOffset.setText(
101103
FreeCAD.Units.Quantity(obj.OffsetExtra.Value, FreeCAD.Units.Length).UserString
102104
)
105+
self.form.numPasses.setValue(obj.NumPasses)
106+
self.form.stepover.setText(
107+
FreeCAD.Units.Quantity(obj.Stepover.Value, FreeCAD.Units.Length).UserString
108+
)
103109

104110
self.form.useCompensation.setChecked(obj.UseComp)
105111
self.form.useStartPoint.setChecked(obj.UseStartPoint)
@@ -117,6 +123,8 @@ def getSignalsForUpdate(self, obj):
117123
signals.append(self.form.cutSide.currentIndexChanged)
118124
signals.append(self.form.direction.currentIndexChanged)
119125
signals.append(self.form.extraOffset.editingFinished)
126+
signals.append(self.form.numPasses.editingFinished)
127+
signals.append(self.form.stepover.editingFinished)
120128
signals.append(self.form.useCompensation.stateChanged)
121129
signals.append(self.form.useStartPoint.stateChanged)
122130
signals.append(self.form.processHoles.stateChanged)
@@ -148,8 +156,11 @@ def updateVisibility(self):
148156
self.form.processHoles.hide()
149157
self.form.processPerimeter.hide()
150158

159+
self.form.stepover.setEnabled(self.obj.NumPasses > 1)
160+
151161
def registerSignalHandlers(self, obj):
152162
self.form.useCompensation.stateChanged.connect(self.updateVisibility)
163+
self.form.numPasses.editingFinished.connect(self.updateVisibility)
153164

154165

155166
# Eclass

src/Mod/CAM/Path/Op/Profile.py

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,24 @@ def areaOpProperties(self):
172172
"App::Property", "Make True, if using Cutter Radius Compensation"
173173
),
174174
),
175+
(
176+
"App::PropertyInteger",
177+
"NumPasses",
178+
"Profile",
179+
QT_TRANSLATE_NOOP(
180+
"App::Property",
181+
"The number of passes to do. If more than one, requires a non-zero value for Stepover",
182+
),
183+
),
184+
(
185+
"App::PropertyDistance",
186+
"Stepover",
187+
"Profile",
188+
QT_TRANSLATE_NOOP(
189+
"App::Property",
190+
"If doing multiple passes, the extra offset of each additional pass",
191+
),
192+
),
175193
]
176194

177195
@classmethod
@@ -235,6 +253,8 @@ def areaOpPropertyDefaults(self, obj, job):
235253
"processCircles": False,
236254
"processHoles": False,
237255
"processPerimeter": True,
256+
"Stepover": 0,
257+
"NumPasses": 1,
238258
}
239259

240260
def areaOpApplyPropertyDefaults(self, obj, job, propList):
@@ -295,6 +315,26 @@ def areaOpOnDocumentRestored(self, obj):
295315
self.initAreaOpProperties(obj, warn=True)
296316
self.areaOpSetDefaultValues(obj, PathUtils.findParentJob(obj))
297317
self.setOpEditorProperties(obj)
318+
if not hasattr(obj, "NumPasses"):
319+
obj.addProperty(
320+
"App::PropertyInteger",
321+
"NumPasses",
322+
"Profile",
323+
QT_TRANSLATE_NOOP(
324+
"App::Property",
325+
"The number of passes to do. Requires a non-zero value for Stepover",
326+
),
327+
)
328+
if not hasattr(obj, "Stepover"):
329+
obj.addProperty(
330+
"App::PropertyDistance",
331+
"Stepover",
332+
"Profile",
333+
QT_TRANSLATE_NOOP(
334+
"App::Property",
335+
"If doing multiple passes, the extra offset of each additional pass",
336+
),
337+
)
298338

299339
def areaOpOnChanged(self, obj, prop):
300340
"""areaOpOnChanged(obj, prop) ... updates certain property visibilities depending on changed properties."""
@@ -311,13 +351,31 @@ def areaOpAreaParams(self, obj, isHole):
311351
params["SectionCount"] = -1
312352

313353
offset = obj.OffsetExtra.Value # 0.0
354+
num_passes = max(1, obj.NumPasses)
355+
stepover = obj.Stepover.Value
356+
if num_passes > 1 and stepover == 0:
357+
# This check is important because C++ code has a default value for stepover if it's 0 and extra passes are requested
358+
num_passes = 1
359+
Path.Log.warning(
360+
"Multipass profile requires a non-zero stepover. Reducing to a single pass."
361+
)
362+
314363
if obj.UseComp:
315364
offset = self.radius + obj.OffsetExtra.Value
316365
if obj.Side == "Inside":
317366
offset = 0 - offset
367+
stepover = -stepover
318368
if isHole:
319369
offset = 0 - offset
370+
stepover = -stepover
371+
372+
# Modify offset and stepover to do passes from most-offset to least
373+
offset += stepover * (num_passes - 1)
374+
stepover = -stepover
375+
320376
params["Offset"] = offset
377+
params["ExtraPass"] = num_passes - 1
378+
params["Stepover"] = stepover
321379

322380
jointype = ["Round", "Square", "Miter"]
323381
params["JoinType"] = jointype.index(obj.JoinType)
@@ -356,6 +414,10 @@ def areaOpPathParams(self, obj, isHole):
356414
else:
357415
params["orientation"] = 0
358416

417+
if obj.NumPasses > 1:
418+
# Disable path sorting to ensure that offsets appear in order, from farthest offset to closest, on all layers
419+
params["sort_mode"] = 0
420+
359421
return params
360422

361423
def areaOpUseProjection(self, obj):
@@ -590,7 +652,11 @@ def _processEdges(self, obj, remainingObjBaseFeatures):
590652
if flattened and zDiff >= self.JOB.GeometryTolerance.Value:
591653
cutWireObjs = False
592654
openEdges = []
593-
passOffsets = [self.ofstRadius]
655+
params = self.areaOpAreaParams(obj, False)
656+
passOffsets = [
657+
self.ofstRadius + i * abs(params["Stepover"])
658+
for i in range(params["ExtraPass"] + 1)
659+
][::-1]
594660
(origWire, flatWire) = flattened
595661

596662
self._addDebugObject("FlatWire", flatWire)

0 commit comments

Comments
 (0)