Merge pull request #17826 from jbaehr/cam-fix-rename-cw-ccw-climb-conventional-main

CAM: Port #17655 to main: "Fix CW/CCW to Climb/Conventional rename"
This commit is contained in:
Chris Hennes
2024-12-02 11:41:40 -05:00
committed by GitHub
22 changed files with 874 additions and 672 deletions

Binary file not shown.

View File

@@ -65,14 +65,14 @@ class PathTestBase(unittest.TestCase):
for i in range(0, len(edges)):
self.assertLine(edges[i], points[i], points[i + 1])
def assertArc(self, edge, pt1, pt2, direction="Climb"):
def assertArc(self, edge, pt1, pt2, direction="CW"):
"""Verify that edge is an arc between pt1 and pt2 with the given direction."""
self.assertIs(type(edge.Curve), Part.Circle)
self.assertCoincide(edge.valueAt(edge.FirstParameter), pt1)
self.assertCoincide(edge.valueAt(edge.LastParameter), pt2)
ptm = edge.valueAt((edge.LastParameter + edge.FirstParameter) / 2)
side = Path.Geom.Side.of(pt2 - pt1, ptm - pt1)
if "Climb" == direction:
if "CW" == direction:
self.assertEqual(side, Path.Geom.Side.Left)
else:
self.assertEqual(side, Path.Geom.Side.Right)
@@ -185,3 +185,14 @@ class PathTestBase(unittest.TestCase):
self.assertEqual(len(pts0), len(pts1))
for i in range(len(pts0)):
self.assertCoincide(pts0[i], pts1[i])
def assertSuccessfulRecompute(self, doc, *objs, msg=None):
"""Asserts that the given objects can be successfully recomputed."""
if len(objs) == 0:
doc.recompute()
objs = doc.Objects
else:
doc.recompute(objs)
failed_objects = [o.Name for o in objs if "Invalid" in o.State]
if len(failed_objects) > 0:
self.fail(msg or f"Recompute failed for {failed_objects}")

View File

@@ -59,7 +59,7 @@ class TestDressupDogbone(PathTestBase):
"""Verify bones are inserted for simple moves."""
base = TestProfile(
"Inside",
"Climb",
"CW",
"""
G0 X10 Y10 Z10
G1 Z0
@@ -84,7 +84,7 @@ class TestDressupDogbone(PathTestBase):
"""Verify bones are inserted if hole ends with rapid move out."""
base = TestProfile(
"Inside",
"Climb",
"CW",
"""
G0 X10 Y10 Z10
G1 Z0
@@ -175,7 +175,7 @@ class TestDressupDogbone(PathTestBase):
"""Verify no bone is inserted for straight move interrupted by plunge."""
base = TestProfile(
"Inside",
"Climb",
"CW",
"""
G0 X10 Y10 Z10
G1 Z0
@@ -197,7 +197,7 @@ class TestDressupDogbone(PathTestBase):
"""Verify can handle comments between moves"""
base = TestProfile(
"Inside",
"Climb",
"CW",
"""
G0 X10 Y10 Z10
G1 Z0
@@ -220,7 +220,7 @@ class TestDressupDogbone(PathTestBase):
base = TestProfile(
"Inside",
"Climb",
"CW",
"""
G0 X10 Y10 Z10
G1 Z0
@@ -246,7 +246,7 @@ class TestDressupDogbone(PathTestBase):
"""Verify can handle noops between moves"""
base = TestProfile(
"Inside",
"Climb",
"CW",
"""
G0 X10 Y10 Z10
G1 Z0
@@ -269,7 +269,7 @@ class TestDressupDogbone(PathTestBase):
base = TestProfile(
"Inside",
"Climb",
"CW",
"""
G0 X10 Y10 Z10
G1 Z0

View File

@@ -44,64 +44,56 @@ class TestPathGeom(PathTestBase):
def test01(self):
"""Verify diffAngle functionality."""
self.assertRoughly(Path.Geom.diffAngle(0, +0 * math.pi / 4, "Climb") / math.pi, 0 / 4.0)
self.assertRoughly(Path.Geom.diffAngle(0, +3 * math.pi / 4, "Climb") / math.pi, 5 / 4.0)
self.assertRoughly(Path.Geom.diffAngle(0, -3 * math.pi / 4, "Climb") / math.pi, 3 / 4.0)
self.assertRoughly(Path.Geom.diffAngle(0, +4 * math.pi / 4, "Climb") / math.pi, 4 / 4.0)
self.assertRoughly(Path.Geom.diffAngle(0, +0 * math.pi / 4, "CW") / math.pi, 0 / 4.0)
self.assertRoughly(Path.Geom.diffAngle(0, +3 * math.pi / 4, "CW") / math.pi, 5 / 4.0)
self.assertRoughly(Path.Geom.diffAngle(0, -3 * math.pi / 4, "CW") / math.pi, 3 / 4.0)
self.assertRoughly(Path.Geom.diffAngle(0, +4 * math.pi / 4, "CW") / math.pi, 4 / 4.0)
self.assertRoughly(Path.Geom.diffAngle(0, +0 * math.pi / 4, "CCW") / math.pi, 0 / 4.0)
self.assertRoughly(Path.Geom.diffAngle(0, +3 * math.pi / 4, "CCW") / math.pi, 3 / 4.0)
self.assertRoughly(Path.Geom.diffAngle(0, -3 * math.pi / 4, "CCW") / math.pi, 5 / 4.0)
self.assertRoughly(Path.Geom.diffAngle(0, +4 * math.pi / 4, "CCW") / math.pi, 4 / 4.0)
self.assertRoughly(
Path.Geom.diffAngle(0, +0 * math.pi / 4, "Conventional") / math.pi, 0 / 4.0
Path.Geom.diffAngle(+math.pi / 4, +0 * math.pi / 4, "CW") / math.pi, 1 / 4.0
)
self.assertRoughly(
Path.Geom.diffAngle(0, +3 * math.pi / 4, "Conventional") / math.pi, 3 / 4.0
Path.Geom.diffAngle(+math.pi / 4, +3 * math.pi / 4, "CW") / math.pi, 6 / 4.0
)
self.assertRoughly(
Path.Geom.diffAngle(0, -3 * math.pi / 4, "Conventional") / math.pi, 5 / 4.0
Path.Geom.diffAngle(+math.pi / 4, -1 * math.pi / 4, "CW") / math.pi, 2 / 4.0
)
self.assertRoughly(
Path.Geom.diffAngle(0, +4 * math.pi / 4, "Conventional") / math.pi, 4 / 4.0
Path.Geom.diffAngle(-math.pi / 4, +0 * math.pi / 4, "CW") / math.pi, 7 / 4.0
)
self.assertRoughly(
Path.Geom.diffAngle(-math.pi / 4, +3 * math.pi / 4, "CW") / math.pi, 4 / 4.0
)
self.assertRoughly(
Path.Geom.diffAngle(-math.pi / 4, -1 * math.pi / 4, "CW") / math.pi, 0 / 4.0
)
self.assertRoughly(
Path.Geom.diffAngle(+math.pi / 4, +0 * math.pi / 4, "Climb") / math.pi, 1 / 4.0
)
self.assertRoughly(
Path.Geom.diffAngle(+math.pi / 4, +3 * math.pi / 4, "Climb") / math.pi, 6 / 4.0
)
self.assertRoughly(
Path.Geom.diffAngle(+math.pi / 4, -1 * math.pi / 4, "Climb") / math.pi, 2 / 4.0
)
self.assertRoughly(
Path.Geom.diffAngle(-math.pi / 4, +0 * math.pi / 4, "Climb") / math.pi, 7 / 4.0
)
self.assertRoughly(
Path.Geom.diffAngle(-math.pi / 4, +3 * math.pi / 4, "Climb") / math.pi, 4 / 4.0
)
self.assertRoughly(
Path.Geom.diffAngle(-math.pi / 4, -1 * math.pi / 4, "Climb") / math.pi, 0 / 4.0
)
self.assertRoughly(
Path.Geom.diffAngle(+math.pi / 4, +0 * math.pi / 4, "Conventional") / math.pi,
Path.Geom.diffAngle(+math.pi / 4, +0 * math.pi / 4, "CCW") / math.pi,
7 / 4.0,
)
self.assertRoughly(
Path.Geom.diffAngle(+math.pi / 4, +3 * math.pi / 4, "Conventional") / math.pi,
Path.Geom.diffAngle(+math.pi / 4, +3 * math.pi / 4, "CCW") / math.pi,
2 / 4.0,
)
self.assertRoughly(
Path.Geom.diffAngle(+math.pi / 4, -1 * math.pi / 4, "Conventional") / math.pi,
Path.Geom.diffAngle(+math.pi / 4, -1 * math.pi / 4, "CCW") / math.pi,
6 / 4.0,
)
self.assertRoughly(
Path.Geom.diffAngle(-math.pi / 4, +0 * math.pi / 4, "Conventional") / math.pi,
Path.Geom.diffAngle(-math.pi / 4, +0 * math.pi / 4, "CCW") / math.pi,
1 / 4.0,
)
self.assertRoughly(
Path.Geom.diffAngle(-math.pi / 4, +3 * math.pi / 4, "Conventional") / math.pi,
Path.Geom.diffAngle(-math.pi / 4, +3 * math.pi / 4, "CCW") / math.pi,
4 / 4.0,
)
self.assertRoughly(
Path.Geom.diffAngle(-math.pi / 4, -1 * math.pi / 4, "Conventional") / math.pi,
Path.Geom.diffAngle(-math.pi / 4, -1 * math.pi / 4, "CCW") / math.pi,
0 / 4.0,
)
@@ -431,7 +423,7 @@ class TestPathGeom(PathTestBase):
),
p1,
p2,
"Climb",
"CW",
)
self.assertArc(
Path.Geom.edgeForCmd(
@@ -440,7 +432,7 @@ class TestPathGeom(PathTestBase):
),
p2,
p1,
"Conventional",
"CCW",
)
def test30(self):

View File

@@ -20,13 +20,18 @@
# * *
# ***************************************************************************
import pathlib
import Draft
import FreeCAD
import Path
import Path.Base.SetupSheetOpPrototype as PathSetupSheetOpPrototype
import Path.Main.Job as PathJob
import Path.Op.Helix as PathHelix
import CAMTests.PathTestUtils as PathTestUtils
FIXTURE_PATH = pathlib.Path(__file__).parent / "Fixtures"
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
# Path.Log.trackModule(Path.Log.thisModule())
@@ -39,6 +44,9 @@ class TestPathHelix(PathTestUtils.PathTestBase):
self.doc = FreeCAD.open(FreeCAD.getHomePath() + "Mod/CAM/CAMTests/test_holes00.fcstd")
self.job = PathJob.Create("Job", [self.doc.Body])
# the smallest hole in the fixture is 1mm in diameter, so our tool must be smaller.
self.job.Tools.Group[0].Tool.Diameter = 0.9
def tearDown(self):
FreeCAD.closeDocument(self.doc.Name)
@@ -48,6 +56,12 @@ class TestPathHelix(PathTestUtils.PathTestBase):
op = PathHelix.Create("Helix")
op.Proxy.execute(op)
def testCreateWithPrototype(self):
"""Verify a Helix can be created on a SetupSheet's prototype instead of a real document object"""
ptt = PathSetupSheetOpPrototype.OpPrototype("Helix")
op = PathHelix.Create("OpPrototype.Helix", ptt)
def test01(self):
"""Verify Helix generates proper holes from model"""
@@ -62,8 +76,6 @@ class TestPathHelix(PathTestUtils.PathTestBase):
def test02(self):
"""Verify Helix generates proper holes for rotated model"""
self.job.Tools.Group[0].Tool.Diameter = 0.5
op = PathHelix.Create("Helix")
proxy = op.Proxy
model = self.job.Model.Group[0]
@@ -126,3 +138,103 @@ class TestPathHelix(PathTestUtils.PathTestBase):
self.assertRoughly(
round(pos.Length / 10, 0), proxy.holeDiameter(op, model, sub)
)
def testPathDirection(self):
"""Verify that the generated paths obays the given parameters"""
helix = PathHelix.Create("Helix")
def check(start_side, cut_mode, expected_direction):
with self.subTest(f"({start_side}, {cut_mode}) => {expected_direction}"):
helix.StartSide = start_side
helix.CutMode = cut_mode
self.assertSuccessfulRecompute(self.doc, helix)
self.assertEqual(
helix.Direction,
expected_direction,
msg=f"Direction was not correctly determined",
)
self.assertPathDirection(
helix.Path,
expected_direction,
msg=f"Path with wrong direction generated",
)
check("Inside", "Conventional", "CW")
check("Outside", "Climb", "CW")
check("Inside", "Climb", "CCW")
check("Outside", "Conventional", "CCW")
def testRecomputeHelixFromV021(self):
"""Verify that we can still open and recompute a Helix created with older FreeCAD"""
self.tearDown()
self.doc = FreeCAD.openDocument(str(FIXTURE_PATH / "OpHelix_v0-21.FCStd"))
created_with = f"created with {self.doc.getProgramVersion()}"
def check(helix, direction, start_side, cut_mode):
with self.subTest(f"{helix.Name}: ({direction}, {start_side}) => {cut_mode}"):
# no recompute yet, i.e. check original as precondition
self.assertPathDirection(
helix.Path,
direction,
msg=f"Path direction does not match fixture for {helix.Name} {created_with}",
)
self.assertEqual(
helix.Direction,
direction,
msg=f"Direction does not match fixture for {helix.Name} {created_with}",
)
self.assertEqual(
helix.StartSide,
start_side,
msg=f"StartSide does not match fixture for {helix.Name} {created_with}",
)
# now see whether we can recompute the object from the old document
helix.enforceRecompute()
self.assertSuccessfulRecompute(
self.doc, helix, msg=f"Cannot recompute {helix.Name} {created_with}"
)
self.assertEqual(
helix.Direction,
direction,
msg=f"Direction changed after recomputing {helix.Name} {created_with}",
)
self.assertEqual(
helix.StartSide,
start_side,
msg=f"StartSide changed after recomputing {helix.Name} {created_with}",
)
self.assertEqual(
helix.CutMode,
cut_mode,
msg=f"CutMode not correctly derived for {helix.Name} {created_with}",
)
self.assertPathDirection(
helix.Path,
direction,
msg=f"Path with wrong direction generated for {helix.Name} {created_with}",
)
# object names and expected values defined in the fixture
check(self.doc.Helix, "CW", "Inside", "Conventional")
check(self.doc.Helix001, "CW", "Outside", "Climb")
check(self.doc.Helix002, "CCW", "Inside", "Climb")
check(self.doc.Helix003, "CCW", "Outside", "Conventional")
def assertPathDirection(self, path, expected_direction, msg=None):
"""Asserts that the given path goes into the expected direction.
For the general case we'd need to check the sign of the second derivative,
but as we know we work on a helix here, we can take a short cut and just
look at the G2/G3 arc commands.
"""
has_g2 = any(filter(lambda cmd: cmd.Name == "G2", path.Commands))
has_g3 = any(filter(lambda cmd: cmd.Name == "G3", path.Commands))
if has_g2 and not has_g3:
self.assertEqual("CW", expected_direction, msg)
elif has_g3 and not has_g2:
self.assertEqual("CCW", expected_direction, msg)
else:
raise NotImplementedError("Cannot determine direction for arbitrary paths")

View File

@@ -44,7 +44,7 @@ def _resetArgs():
"step_over": 0.5,
"tool_diameter": 5.0,
"inner_radius": 0.0,
"direction": "Climb",
"direction": "CW",
"startAt": "Inside",
}

View File

@@ -123,7 +123,7 @@ class TestPathProfile(PathTestBase):
profile.processCircles = True
profile.processHoles = True
profile.UseComp = True
profile.Direction = "Climb"
profile.Direction = "CW"
_addViewProvider(profile)
self.doc.recompute()
@@ -162,7 +162,7 @@ class TestPathProfile(PathTestBase):
profile.processCircles = True
profile.processHoles = True
profile.UseComp = False
profile.Direction = "Climb"
profile.Direction = "CW"
_addViewProvider(profile)
self.doc.recompute()
@@ -205,7 +205,7 @@ class TestPathProfile(PathTestBase):
profile.processCircles = True
profile.processHoles = True
profile.UseComp = True
profile.Direction = "Climb"
profile.Direction = "CW"
profile.OffsetExtra = -profile.OpToolDiameter / 2.0
_addViewProvider(profile)
self.doc.recompute()

View File

@@ -349,6 +349,10 @@ SET(Tests_SRCS
CAMTests/Tools/Shape/test-path-tool-bit-shape-00.fcstd
)
SET(Tests_Fixtures
CAMTests/Fixtures/OpHelix_v0-21.FCStd
)
SET(PathImages_Ops
Images/Ops/chamfer.svg
)
@@ -411,6 +415,7 @@ ADD_CUSTOM_TARGET(PathScripts ALL
SET(test_files
${Path_Scripts}
${Tests_SRCS}
${Tests_Fixtures}
)
ADD_CUSTOM_TARGET(Tests ALL
@@ -536,6 +541,13 @@ INSTALL(
Mod/CAM/CAMTests
)
INSTALL(
FILES
${Tests_Fixtures}
DESTINATION
Mod/CAM/CAMTests/Fixtures
)
INSTALL(
DIRECTORY
CAMTests/Tools

View File

@@ -1,422 +1,422 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>350</width>
<height>450</height>
</rect>
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>350</width>
<height>450</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
<property name="minimumSize">
<size>
<width>125</width>
<height>0</height>
</size>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>125</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Tool Controller</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="toolController">
<property name="toolTip">
<string>The tool and its settings to be used for this operation.</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>125</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Coolant Mode</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="coolantController">
<property name="toolTip">
<string>The tool and its settings to be used for this operation.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>12</number>
</property>
<property name="rightMargin">
<number>12</number>
</property>
<item>
<widget class="QLabel" name="direction_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>125</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Direction</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="direction">
<property name="toolTip">
<string>The direction in which the profile is performed, clockwise or counterclockwise.</string>
</property>
<property name="currentText">
<string>Climb</string>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>Climb</string>
</property>
</item>
<item>
<property name="text">
<string>Conventional</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QWidget" name="widget">
<property name="minimumSize">
<size>
<width>125</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_3">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string notr="true">W =</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::InputField" name="value_W">
<property name="toolTip">
<string>Width of chamfer cut.</string>
</property>
<property name="unit" stdset="0">
<string>mm</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_4">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string notr="true">h = </string>
</property>
</widget>
</item>
<item>
<widget class="Gui::InputField" name="value_h">
<property name="toolTip">
<string>Extra depth of tool immersion.</string>
</property>
<property name="unit" stdset="0">
<string>mm</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>13</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0" colspan="2">
<widget class="QFrame" name="joinFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<property name="horizontalSpacing">
<number>3</number>
</property>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_2">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Join:</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="joinRound">
<property name="toolTip">
<string>Round joint</string>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="joinMiter">
<property name="toolTip">
<string>Miter joint</string>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="4" column="1">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>154</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_2">
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="opImage">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>150</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>150</width>
<height>150</height>
</size>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Tool Controller</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="toolController">
<property name="toolTip">
<string>The tool and its settings to be used for this operation.</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>125</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Coolant Mode</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="coolantController">
<property name="toolTip">
<string>The tool and its settings to be used for this operation.</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Gui::InputField</class>
<extends>QLineEdit</extends>
<header>Gui/InputField.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>12</number>
</property>
<property name="rightMargin">
<number>12</number>
</property>
<item>
<widget class="QLabel" name="direction_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>125</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Direction</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="direction">
<property name="toolTip">
<string>The direction in which the profile is performed, clockwise or counterclockwise.</string>
</property>
<property name="currentText">
<string>CW</string>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>CW</string>
</property>
</item>
<item>
<property name="text">
<string>CCW</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QWidget" name="widget">
<property name="minimumSize">
<size>
<width>125</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_3">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string notr="true">W =</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::InputField" name="value_W">
<property name="toolTip">
<string>Width of chamfer cut.</string>
</property>
<property name="unit" stdset="0">
<string>mm</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_4">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string notr="true">h = </string>
</property>
</widget>
</item>
<item>
<widget class="Gui::InputField" name="value_h">
<property name="toolTip">
<string>Extra depth of tool immersion.</string>
</property>
<property name="unit" stdset="0">
<string>mm</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>13</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0" colspan="2">
<widget class="QFrame" name="joinFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<property name="horizontalSpacing">
<number>3</number>
</property>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_2">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Join:</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="joinRound">
<property name="toolTip">
<string>Round joint</string>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="joinMiter">
<property name="toolTip">
<string>Miter joint</string>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="4" column="1">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>154</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_2">
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="opImage">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>150</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>150</width>
<height>150</height>
</size>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Gui::InputField</class>
<extends>QLineEdit</extends>
<header>Gui/InputField.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -1,175 +1,175 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>365</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>365</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Tool Controller</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="toolController">
<property name="toolTip">
<string>The tool and its settings to be used for this operation.</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Coolant</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="coolantController">
<property name="toolTip">
<string>The tool and its settings to be used for this operation.</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QLabel" name="label">
<property name="text">
<string>Tool Controller</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="toolController">
<property name="toolTip">
<string>The tool and its settings to be used for this operation.</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QWidget" name="widget">
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Start from</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="startSide">
<property name="toolTip">
<string>Specify if the helix operation should start at the inside and work its way outwards, or start at the outside and work its way to the center.</string>
</property>
<item>
<property name="text">
<string>Inside</string>
</property>
</item>
<item>
<property name="text">
<string>Outside</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Direction</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="direction">
<property name="toolTip">
<string>The direction for the helix, clockwise or counterclockwise.</string>
</property>
<item>
<property name="text">
<string>Climb</string>
</property>
</item>
<item>
<property name="text">
<string>Conventional</string>
</property>
</item>
</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="4" column="1">
<widget class="QSpinBox" name="stepOverPercent">
<property name="toolTip">
<string>Specify the percent of the tool diameter each helix will be offset to the previous one. A step over of 100% means no overlap of the individual cuts.</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>10</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Extra Offset</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="Gui::InputField" name="extraOffset">
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Coolant</string>
</property>
</widget>
</item>
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
<item row="1" column="1">
<widget class="QComboBox" name="coolantController">
<property name="toolTip">
<string>The tool and its settings to be used for this operation.</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Gui::InputField</class>
<extends>QLineEdit</extends>
<header>Gui/InputField.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QWidget" name="widget">
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Start from</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="startSide">
<property name="toolTip">
<string>Specify if the helix operation should start at the inside and work its way outwards, or start at the outside and work its way to the center.</string>
</property>
<item>
<property name="text">
<string>Inside</string>
</property>
</item>
<item>
<property name="text">
<string>Outside</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Direction</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="cutMode">
<property name="toolTip">
<string>The direction for the helix, clockwise or counterclockwise.</string>
</property>
<item>
<property name="text">
<string>Climb</string>
</property>
</item>
<item>
<property name="text">
<string>Conventional</string>
</property>
</item>
</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="4" column="1">
<widget class="QSpinBox" name="stepOverPercent">
<property name="toolTip">
<string>Specify the percent of the tool diameter each helix will be offset to the previous one. A step over of 100% means no overlap of the individual cuts.</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>10</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Extra Offset</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="Gui::InputField" name="extraOffset">
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Gui::InputField</class>
<extends>QLineEdit</extends>
<header>Gui/InputField.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -45,7 +45,7 @@ def generate(
step_over,
tool_diameter,
inner_radius=0.0,
direction="Climb",
direction="CW",
startAt="Outside",
):
"""generate(edge, hole_radius, inner_radius, step_over) ... generate helix commands.
@@ -96,7 +96,7 @@ def generate(
elif startAt not in ["Inside", "Outside"]:
raise ValueError("Invalid value for parameter 'startAt'")
elif direction not in ["Climb", "Conventional"]:
elif direction not in ["CW", "CCW"]:
raise ValueError("Invalid value for parameter 'direction'")
if type(step_over) not in [float, int]:
@@ -145,7 +145,7 @@ def generate(
def helix_cut_r(r):
commandlist = []
arc_cmd = "G2" if direction == "Climb" else "G3"
arc_cmd = "G2" if direction == "CW" else "G3"
commandlist.append(Path.Command("G0", {"X": startPoint.x + r, "Y": startPoint.y}))
commandlist.append(Path.Command("G1", {"Z": startPoint.z}))
for i in range(1, turncount + 1):

View File

@@ -224,6 +224,9 @@ class OpPrototype(object):
def setEditorMode(self, name, mode):
self.properties[name].setEditorMode(mode)
def setPropertyStatus(self, name, status):
pass
def getProperty(self, name):
return self.properties[name]

View File

@@ -178,12 +178,12 @@ class ObjectDressup:
def getDirectionOfPath(self, obj):
op = PathDressup.baseOp(obj.Base)
side = op.Side if hasattr(op, "Side") else "Inside"
direction = op.Direction if hasattr(op, "Direction") else "Conventional"
direction = op.Direction if hasattr(op, "Direction") else "CCW"
if side == "Outside":
return "left" if direction == "Climb" else "right"
return "left" if direction == "CW" else "right"
else:
return "right" if direction == "Climb" else "left"
return "right" if direction == "CW" else "left"
def getArcDirection(self, obj):
direction = self.getDirectionOfPath(obj)

View File

@@ -147,10 +147,10 @@ def getAngle(vector):
return a
def diffAngle(a1, a2, direction="Climb"):
"""diffAngle(a1, a2, [direction='Climb'])
def diffAngle(a1, a2, direction="CW"):
"""diffAngle(a1, a2, [direction='CW'])
Returns the difference between two angles (a1 -> a2) into a given direction."""
if direction == "Climb":
if direction == "CW":
while a1 < a2:
a1 += 2 * math.pi
a = a1 - a2
@@ -453,7 +453,7 @@ def edgeForCmd(cmd, startPoint):
cw = True
else:
cw = False
angle = diffAngle(getAngle(A), getAngle(B), "Climb" if cw else "CCW")
angle = diffAngle(getAngle(A), getAngle(B), "CW" if cw else "CCW")
height = endPoint.z - startPoint.z
pitch = height * math.fabs(2 * math.pi / angle)
if angle > 0:

View File

@@ -346,7 +346,7 @@ class ObjectOp(PathOp.ObjectOp):
verts = hWire.Wires[0].Vertexes
idx = 0
if obj.Direction == "Conventional":
if obj.Direction == "CCW":
idx = len(verts) - 1
x = verts[idx].X
y = verts[idx].Y

View File

@@ -144,7 +144,7 @@ class ObjectDeburr(PathEngraveBase.ObjectOp):
"Deburr",
QT_TRANSLATE_NOOP("App::Property", "Direction of toolpath"),
)
# obj.Direction = ["Climb", "Conventional"]
# obj.Direction = ["CW", "CCW"]
obj.addProperty(
"App::PropertyEnumeration",
"Side",
@@ -178,8 +178,8 @@ class ObjectDeburr(PathEngraveBase.ObjectOp):
# Enumeration lists for App::PropertyEnumeration properties
enums = {
"Direction": [
(translate("Path", "Climb"), "Climb"),
(translate("Path", "Conventional"), "Conventional"),
(translate("Path", "CW"), "CW"),
(translate("Path", "CCW"), "CCW"),
], # this is the direction that the profile runs
"Join": [
(translate("PathDeburr", "Round"), "Round"),
@@ -382,7 +382,7 @@ class ObjectDeburr(PathEngraveBase.ObjectOp):
wires.append(wire)
# Set direction of op
forward = obj.Direction == "Climb"
forward = obj.Direction == "CW"
# Set value of side
obj.Side = side[0]
@@ -417,7 +417,7 @@ class ObjectDeburr(PathEngraveBase.ObjectOp):
obj.Join = "Round"
obj.setExpression("StepDown", "0 mm")
obj.StepDown = "0 mm"
obj.Direction = "Climb"
obj.Direction = "CW"
obj.Side = "Outside"
obj.EntryPoint = 0

View File

@@ -52,7 +52,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
"""getForm() ... return UI"""
form = FreeCADGui.PySideUic.loadUi(":/panels/PageOpHelixEdit.ui")
comboToPropertyMap = [("startSide", "StartSide"), ("direction", "Direction")]
comboToPropertyMap = [("startSide", "StartSide"), ("cutMode", "CutMode")]
enumTups = PathHelix.ObjectHelix.helixOpPropertyEnumerations(dataType="raw")
@@ -62,8 +62,8 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
def getFields(self, obj):
"""getFields(obj) ... transfers values from UI to obj's properties"""
Path.Log.track()
if obj.Direction != str(self.form.direction.currentData()):
obj.Direction = str(self.form.direction.currentData())
if obj.CutMode != str(self.form.cutMode.currentData()):
obj.CutMode = str(self.form.cutMode.currentData())
if obj.StartSide != str(self.form.startSide.currentData()):
obj.StartSide = str(self.form.startSide.currentData())
if obj.StepOver != self.form.stepOverPercent.value():
@@ -78,7 +78,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
Path.Log.track()
self.form.stepOverPercent.setValue(obj.StepOver)
self.selectInComboBox(obj.Direction, self.form.direction)
self.selectInComboBox(obj.CutMode, self.form.cutMode)
self.selectInComboBox(obj.StartSide, self.form.startSide)
self.setupToolController(obj, self.form.toolController)
@@ -94,7 +94,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
signals.append(self.form.stepOverPercent.editingFinished)
signals.append(self.form.extraOffset.editingFinished)
signals.append(self.form.direction.currentIndexChanged)
signals.append(self.form.cutMode.currentIndexChanged)
signals.append(self.form.startSide.currentIndexChanged)
signals.append(self.form.toolController.currentIndexChanged)
signals.append(self.form.coolantController.currentIndexChanged)

View File

@@ -51,6 +51,36 @@ else:
translate = FreeCAD.Qt.translate
def _caclulatePathDirection(mode, side):
"""Calculates the path direction from cut mode and cut side"""
# NB: at the time of writing, we need py3.8 compat, thus not using py3.10 pattern machting
if mode == "Conventional" and side == "Inside":
return "CW"
elif mode == "Conventional" and side == "Outside":
return "CCW"
elif mode == "Climb" and side == "Inside":
return "CCW"
elif mode == "Climb" and side == "Outside":
return "CW"
else:
raise ValueError(f"No mapping for '{mode}'/'{side}'")
def _caclulateCutMode(direction, side):
"""Calculates the cut mode from path direction and cut side"""
# NB: at the time of writing, we need py3.8 compat, thus not using py3.10 pattern machting
if direction == "CW" and side == "Inside":
return "Conventional"
elif direction == "CW" and side == "Outside":
return "Climb"
elif direction == "CCW" and side == "Inside":
return "Climb"
elif direction == "CCW" and side == "Outside":
return "Conventional"
else:
raise ValueError(f"No mapping for '{direction}'/'{side}'")
class ObjectHelix(PathCircularHoleBase.ObjectOp):
"""Proxy class for Helix operations."""
@@ -68,13 +98,17 @@ class ObjectHelix(PathCircularHoleBase.ObjectOp):
# Enumeration lists for App::PropertyEnumeration properties
enums = {
"Direction": [
(translate("CAM_Helix", "Climb"), "Climb"),
(translate("CAM_Helix", "Conventional"), "Conventional"),
(translate("CAM_Helix", "CW"), "CW"),
(translate("CAM_Helix", "CCW"), "CCW"),
], # this is the direction that the profile runs
"StartSide": [
(translate("PathProfile", "Outside"), "Outside"),
(translate("PathProfile", "Inside"), "Inside"),
], # side of profile that cutter is on in relation to direction of profile
"CutMode": [
(translate("CAM_Helix", "Climb"), "Climb"),
(translate("CAM_Helix", "Conventional"), "Conventional"),
], # whether the tool "rolls" with or against the feed direction along the profile
}
if dataType == "raw":
@@ -103,9 +137,11 @@ class ObjectHelix(PathCircularHoleBase.ObjectOp):
"Helix Drill",
QT_TRANSLATE_NOOP(
"App::Property",
"The direction of the circular cuts, ClockWise (Climb), or CounterClockWise (Conventional)",
"The direction of the circular cuts, ClockWise (CW), or CounterClockWise (CCW)",
),
)
obj.setEditorMode("Direction", ["ReadOnly", "Hidden"])
obj.setPropertyStatus("Direction", ["ReadOnly", "Output"])
obj.addProperty(
"App::PropertyEnumeration",
@@ -114,6 +150,17 @@ class ObjectHelix(PathCircularHoleBase.ObjectOp):
QT_TRANSLATE_NOOP("App::Property", "Start cutting from the inside or outside"),
)
# TODO: revise property description once v1.0 release string freeze is lifted
obj.addProperty(
"App::PropertyEnumeration",
"CutMode",
"Helix Drill",
QT_TRANSLATE_NOOP(
"App::Property",
"The direction of the circular cuts, ClockWise (Climb), or CounterClockWise (Conventional)",
),
)
obj.addProperty(
"App::PropertyPercent",
"StepOver",
@@ -163,9 +210,34 @@ class ObjectHelix(PathCircularHoleBase.ObjectOp):
),
)
if not hasattr(obj, "CutMode"):
# TODO: consolidate the duplicate definitions from opOnDocumentRestored and
# initCircularHoleOperation once back on the main line
obj.addProperty(
"App::PropertyEnumeration",
"CutMode",
"Helix Drill",
QT_TRANSLATE_NOOP(
"App::Property",
"The direction of the circular cuts, ClockWise (Climb), or CounterClockWise (Conventional)",
),
)
obj.CutMode = ["Climb", "Conventional"]
if obj.Direction in ["Climb", "Conventional"]:
# For some month, late in the v1.0 release cycle, we had the cut mode assigned
# to the direction (see PR#14364). Let's fix files created in this time as well.
new_dir = "CW" if obj.Direction == "Climb" else "CCW"
obj.Direction = ["CW", "CCW"]
obj.Direction = new_dir
obj.CutMode = _caclulateCutMode(obj.Direction, obj.StartSide)
obj.setEditorMode("Direction", ["ReadOnly", "Hidden"])
obj.setPropertyStatus("Direction", ["ReadOnly", "Output"])
def circularHoleExecute(self, obj, holes):
"""circularHoleExecute(obj, holes) ... generate helix commands for each hole in holes"""
Path.Log.track()
obj.Direction = _caclulatePathDirection(obj.CutMode, obj.StartSide)
self.commandlist.append(Path.Command("(helix cut operation)"))
self.commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value}))
@@ -217,8 +289,9 @@ class ObjectHelix(PathCircularHoleBase.ObjectOp):
def SetupProperties():
"""Returns property names for which the "Setup Sheet" should provide defaults."""
setup = []
setup.append("Direction")
setup.append("CutMode")
setup.append("StartSide")
setup.append("StepOver")
setup.append("StartRadius")

View File

@@ -125,7 +125,7 @@ class ObjectPocket(PathAreaOp.ObjectOp):
"Pocket",
QT_TRANSLATE_NOOP(
"App::Property",
"The direction that the toolpath should go around the part ClockWise (Climb) or CounterClockWise (Conventional)",
"The direction that the toolpath should go around the part ClockWise (CW) or CounterClockWise (CCW)",
),
)
obj.addProperty(

View File

@@ -102,7 +102,7 @@ class ObjectProfile(PathAreaOp.ObjectOp):
"Profile",
QT_TRANSLATE_NOOP(
"App::Property",
"The direction that the toolpath should go around the part ClockWise (Climb) or CounterClockWise (Conventional)",
"The direction that the toolpath should go around the part ClockWise (CW) or CounterClockWise (CCW)",
),
),
(
@@ -188,8 +188,8 @@ class ObjectProfile(PathAreaOp.ObjectOp):
# Enumeration lists for App::PropertyEnumeration properties
enums = {
"Direction": [
(translate("PathProfile", "Climb"), "Climb"),
(translate("PathProfile", "Conventional"), "Conventional"),
(translate("PathProfile", "CW"), "CW"),
(translate("PathProfile", "CCW"), "CCW"),
], # this is the direction that the profile runs
"HandleMultipleFeatures": [
(translate("PathProfile", "Collectively"), "Collectively"),
@@ -225,7 +225,7 @@ class ObjectProfile(PathAreaOp.ObjectOp):
"""areaOpPropertyDefaults(obj, job) ... returns a dictionary of default values
for the operation's properties."""
return {
"Direction": "Climb",
"Direction": "CW",
"HandleMultipleFeatures": "Collectively",
"JoinType": "Round",
"MiterLimit": 0.1,
@@ -338,11 +338,11 @@ class ObjectProfile(PathAreaOp.ObjectOp):
# Reverse the direction for holes
if isHole:
direction = "Climb" if obj.Direction == "Conventional" else "Conventional"
direction = "CW" if obj.Direction == "CCW" else "CCW"
else:
direction = obj.Direction
if direction == "Conventional":
if direction == "CCW":
params["orientation"] = 0
else:
params["orientation"] = 1
@@ -351,7 +351,7 @@ class ObjectProfile(PathAreaOp.ObjectOp):
if obj.UseComp:
offset = self.radius + obj.OffsetExtra.Value
if offset == 0.0:
if direction == "Conventional":
if direction == "CCW":
params["orientation"] = 1
else:
params["orientation"] = 0

View File

@@ -370,11 +370,11 @@ def export(objectslist, filename, argstring):
STORED_COMPENSATED_OBJ = commands
# Find mill compensation
if hasattr(obj, "Side") and hasattr(obj, "Direction"):
if obj.Side == "Outside" and obj.Direction == "Climb":
if obj.Side == "Outside" and obj.Direction == "CW":
Compensation = "L"
elif obj.Side == "Outside" and obj.Direction == "Conventional":
elif obj.Side == "Outside" and obj.Direction == "CCW":
Compensation = "R"
elif obj.Side != "Outside" and obj.Direction == "Climb":
elif obj.Side != "Outside" and obj.Direction == "CW":
Compensation = "R"
else:
Compensation = "L"

View File

@@ -37,9 +37,8 @@ from CAMTests.TestPathGeneratorDogboneII import TestGeneratorDogboneII
from CAMTests.TestPathGeom import TestPathGeom
from CAMTests.TestPathLanguage import TestPathLanguage
from CAMTests.TestPathOpDeburr import TestPathOpDeburr
# from CAMTests.TestPathHelix import TestPathHelix
from CAMTests.TestPathHelpers import TestPathHelpers
from CAMTests.TestPathHelix import TestPathHelix
from CAMTests.TestPathHelixGenerator import TestPathHelixGenerator
from CAMTests.TestPathLog import TestPathLog
from CAMTests.TestPathOpUtil import TestPathOpUtil
@@ -98,7 +97,7 @@ False if TestPathOpDeburr.__name__ else True
False if TestPathDrillable.__name__ else True
False if TestPathGeom.__name__ else True
False if TestPathHelpers.__name__ else True
# False if TestPathHelix.__name__ else True
False if TestPathHelix.__name__ else True
False if TestPathLog.__name__ else True
False if TestPathOpUtil.__name__ else True
# False if TestPathPost.__name__ else True