Merge pull request #9832 from rmu75/master
Path: fix g-code orientation from Path.Op.Profile in case eff. offset is 0.0
This commit is contained in:
@@ -279,6 +279,7 @@ SET(PathTests_SRCS
|
||||
PathTests/drill_test1.FCStd
|
||||
PathTests/PathTestUtils.py
|
||||
PathTests/test_adaptive.fcstd
|
||||
PathTests/test_profile.fcstd
|
||||
PathTests/test_centroid_00.ngc
|
||||
PathTests/test_filenaming.fcstd
|
||||
PathTests/test_geomop.fcstd
|
||||
@@ -306,6 +307,7 @@ SET(PathTests_SRCS
|
||||
PathTests/TestPathOpUtil.py
|
||||
PathTests/TestPathPost.py
|
||||
PathTests/TestPathPreferences.py
|
||||
PathTests/TestPathProfile.py
|
||||
PathTests/TestPathPropertyBag.py
|
||||
PathTests/TestPathRotationGenerator.py
|
||||
PathTests/TestPathSetupSheet.py
|
||||
|
||||
@@ -352,7 +352,10 @@ class ObjectProfile(PathAreaOp.ObjectOp):
|
||||
else:
|
||||
params["orientation"] = 1
|
||||
|
||||
if not obj.UseComp:
|
||||
offset = obj.OffsetExtra.Value
|
||||
if obj.UseComp:
|
||||
offset = self.radius + obj.OffsetExtra.Value
|
||||
if offset == 0.0:
|
||||
if direction == "CCW":
|
||||
params["orientation"] = 1
|
||||
else:
|
||||
|
||||
230
src/Mod/Path/PathTests/TestPathProfile.py
Normal file
230
src/Mod/Path/PathTests/TestPathProfile.py
Normal file
@@ -0,0 +1,230 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2023 Robert Schöftner <rs@unfoo.net> *
|
||||
# * Copyright (c) 2021 Russell Johnson (russ4262) <russ4262@gmail.com> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
import FreeCAD
|
||||
import Part
|
||||
import Path.Op.Profile as PathProfile
|
||||
import Path.Main.Job as PathJob
|
||||
from PathTests.PathTestUtils import PathTestBase
|
||||
from PathTests.TestPathAdaptive import getGcodeMoves
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import Path.Main.Gui.Job as PathJobGui
|
||||
import Path.Op.Gui.Profile as PathProfileGui
|
||||
|
||||
|
||||
class TestPathProfile(PathTestBase):
|
||||
"""Unit tests for the Adaptive operation."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""setUpClass()...
|
||||
This method is called upon instantiation of this test class. Add code and objects here
|
||||
that are needed for the duration of the test() methods in this class. In other words,
|
||||
set up the 'global' test environment here; use the `setUp()` method to set up a 'local'
|
||||
test environment.
|
||||
This method does not have access to the class `self` reference, but it
|
||||
is able to call static methods within this same class.
|
||||
"""
|
||||
cls.needsInit = True
|
||||
|
||||
@classmethod
|
||||
def initClass(cls):
|
||||
# Open existing FreeCAD document with test geometry
|
||||
cls.needsInit = False
|
||||
cls.doc = FreeCAD.open(
|
||||
FreeCAD.getHomePath() + "Mod/Path/PathTests/test_profile.fcstd"
|
||||
)
|
||||
|
||||
# Create Job object, adding geometry objects from file opened above
|
||||
cls.job = PathJob.Create("Job", [cls.doc.Body], None)
|
||||
cls.job.GeometryTolerance.Value = 0.001
|
||||
if FreeCAD.GuiUp:
|
||||
cls.job.ViewObject.Proxy = PathJobGui.ViewProvider(cls.job.ViewObject)
|
||||
|
||||
# Instantiate an Profile operation for querying available properties
|
||||
cls.prototype = PathProfile.Create("Profile")
|
||||
cls.prototype.Base = [(cls.doc.Body, ["Face18"])]
|
||||
cls.prototype.Label = "Prototype"
|
||||
_addViewProvider(cls.prototype)
|
||||
|
||||
cls.doc.recompute()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""tearDownClass()...
|
||||
This method is called prior to destruction of this test class. Add code and objects here
|
||||
that cleanup the test environment after the test() methods in this class have been executed.
|
||||
This method does not have access to the class `self` reference. This method
|
||||
is able to call static methods within this same class.
|
||||
"""
|
||||
# FreeCAD.Console.PrintMessage("TestPathAdaptive.tearDownClass()\n")
|
||||
|
||||
# Close geometry document without saving
|
||||
if not cls.needsInit:
|
||||
FreeCAD.closeDocument(cls.doc.Name)
|
||||
|
||||
# Setup and tear down methods called before and after each unit test
|
||||
def setUp(self):
|
||||
"""setUp()...
|
||||
This method is called prior to each `test()` method. Add code and objects here
|
||||
that are needed for multiple `test()` methods.
|
||||
"""
|
||||
if self.needsInit:
|
||||
self.initClass()
|
||||
|
||||
def tearDown(self):
|
||||
"""tearDown()...
|
||||
This method is called after each test() method. Add cleanup instructions here.
|
||||
Such cleanup instructions will likely undo those in the setUp() method.
|
||||
"""
|
||||
pass
|
||||
|
||||
# Unit tests
|
||||
def test00(self):
|
||||
"""test00() Empty test."""
|
||||
return
|
||||
|
||||
def test01(self):
|
||||
"""test01() Verify path generated on Face18, outside, with tool compensation."""
|
||||
|
||||
# Instantiate a Profile operation and set Base Geometry
|
||||
profile = PathProfile.Create("Profile1")
|
||||
profile.Base = [(self.doc.Body, ["Face18"])] # (base, subs_list)
|
||||
profile.Label = "test01+"
|
||||
profile.Comment = "test01() Verify path generated on Face18, outside, with tool compensation."
|
||||
|
||||
# Set additional operation properties
|
||||
# setDepthsAndHeights(adaptive)
|
||||
profile.processCircles = True
|
||||
profile.processHoles = True
|
||||
profile.UseComp = True
|
||||
profile.Direction = "CW"
|
||||
_addViewProvider(profile)
|
||||
self.doc.recompute()
|
||||
|
||||
moves = getGcodeMoves(profile.Path.Commands, includeRapids=False)
|
||||
operationMoves = "; ".join(moves)
|
||||
#FreeCAD.Console.PrintMessage("test01_moves: " + operationMoves + "\n")
|
||||
|
||||
|
||||
expected_moves = \
|
||||
"G1 X16.47 Y16.47 Z10.0; G3 I-2.48 J-2.48 K0.0 X13.93 Y17.5 Z10.0; " \
|
||||
"G1 X-13.93 Y17.5 Z10.0; G3 I-0.06 J-3.51 K0.0 X-17.5 Y13.93 Z10.0; " \
|
||||
"G1 X-17.5 Y-13.93 Z10.0; G3 I3.51 J-0.06 K0.0 X-13.93 Y-17.5 Z10.0; " \
|
||||
"G1 X13.93 Y-17.5 Z10.0; G3 I0.06 J3.51 K0.0 X17.5 Y-13.93 Z10.0; " \
|
||||
"G1 X17.5 Y13.93 Z10.0; G3 I-3.51 J0.06 K0.0 X16.47 Y16.47 Z10.0; " \
|
||||
"G1 X23.55 Y23.54 Z10.0; G2 I-9.55 J-9.54 K0.0 X27.5 Y14.1 Z10.0; " \
|
||||
"G1 X27.5 Y-14.0 Z10.0; G2 I-13.5 J0.0 K0.0 X14.1 Y-27.5 Z10.0; " \
|
||||
"G1 X-14.0 Y-27.5 Z10.0; G2 I0.0 J13.5 K0.0 X-27.5 Y-14.1 Z10.0; " \
|
||||
"G1 X-27.5 Y14.0 Z10.0; G2 I13.5 J-0.0 K0.0 X-14.1 Y27.5 Z10.0; " \
|
||||
"G1 X14.0 Y27.5 Z10.0; G2 I-0.0 J-13.5 K0.0 X23.55 Y23.54 Z10.0"
|
||||
self.assertTrue(expected_moves == operationMoves,
|
||||
"expected_moves: {}\noperationMoves: {}".format(expected_moves, operationMoves))
|
||||
|
||||
def test02(self):
|
||||
"""test02() Verify path generated on Face18, outside, without compensation."""
|
||||
|
||||
# Instantiate a Profile operation and set Base Geometry
|
||||
profile = PathProfile.Create("Profile2")
|
||||
profile.Base = [(self.doc.Body, ["Face18"])] # (base, subs_list)
|
||||
profile.Label = "test02+"
|
||||
profile.Comment = "test02() Verify path generated on Face18, outside, without compensation."
|
||||
|
||||
# Set additional operation properties
|
||||
# setDepthsAndHeights(adaptive)
|
||||
profile.processCircles = True
|
||||
profile.processHoles = True
|
||||
profile.UseComp = False
|
||||
profile.Direction = "CW"
|
||||
_addViewProvider(profile)
|
||||
self.doc.recompute()
|
||||
|
||||
moves = getGcodeMoves(profile.Path.Commands, includeRapids=False)
|
||||
operationMoves = "; ".join(moves)
|
||||
#FreeCAD.Console.PrintMessage("test02_moves: " + operationMoves + "\n")
|
||||
|
||||
expected_moves = "G1 X18.24 Y18.24 Z10.0; G3 I-4.24 J-4.24 K0.0 X14.0 Y20.0 Z10.0; " \
|
||||
"G1 X-14.0 Y20.0 Z10.0; G3 I0.0 J-6.0 K0.0 X-20.0 Y14.0 Z10.0; " \
|
||||
"G1 X-20.0 Y-14.0 Z10.0; G3 I6.0 J0.0 K0.0 X-14.0 Y-20.0 Z10.0; " \
|
||||
"G1 X14.0 Y-20.0 Z10.0; G3 I-0.0 J6.0 K0.0 X20.0 Y-14.0 Z10.0; " \
|
||||
"G1 X20.0 Y14.0 Z10.0; G3 I-6.0 J-0.0 K0.0 X18.24 Y18.24 Z10.0; " \
|
||||
"G1 X21.78 Y21.78 Z10.0; G2 I-7.78 J-7.78 K0.0 X25.0 Y14.0 Z10.0; " \
|
||||
"G1 X25.0 Y-14.0 Z10.0; G2 I-11.0 J0.0 K0.0 X14.0 Y-25.0 Z10.0; " \
|
||||
"G1 X-14.0 Y-25.0 Z10.0; G2 I0.0 J11.0 K0.0 X-25.0 Y-14.0 Z10.0; " \
|
||||
"G1 X-25.0 Y14.0 Z10.0; G2 I11.0 J-0.0 K0.0 X-14.0 Y25.0 Z10.0; " \
|
||||
"G1 X14.0 Y25.0 Z10.0; G2 I-0.0 J-11.0 K0.0 X21.78 Y21.78 Z10.0"
|
||||
|
||||
self.assertTrue(expected_moves == operationMoves,
|
||||
"expected_moves: {}\noperationMoves: {}".format(expected_moves, operationMoves))
|
||||
|
||||
def test03(self):
|
||||
"""test03() Verify path generated on Face18, outside,
|
||||
with compensation and extra offset -radius."""
|
||||
|
||||
# Instantiate a Profile operation and set Base Geometry
|
||||
profile = PathProfile.Create("Profile3")
|
||||
profile.Base = [(self.doc.Body, ["Face18"])] # (base, subs_list)
|
||||
profile.Label = "test03+"
|
||||
profile.Comment = "test03() Verify path generated on Face4, " \
|
||||
"with compensation and extra offset -radius"
|
||||
|
||||
# Set additional operation properties
|
||||
# setDepthsAndHeights(adaptive)
|
||||
profile.processCircles = True
|
||||
profile.processHoles = True
|
||||
profile.UseComp = True
|
||||
profile.Direction = "CW"
|
||||
profile.OffsetExtra = -profile.OpToolDiameter / 2.0
|
||||
_addViewProvider(profile)
|
||||
self.doc.recompute()
|
||||
|
||||
moves = getGcodeMoves(profile.Path.Commands, includeRapids=False)
|
||||
operationMoves = "; ".join(moves)
|
||||
#FreeCAD.Console.PrintMessage("test03_moves: " + operationMoves + "\n")
|
||||
|
||||
expected_moves = "G1 X18.24 Y18.24 Z10.0; G3 I-4.24 J-4.24 K0.0 X14.0 Y20.0 Z10.0; " \
|
||||
"G1 X-14.0 Y20.0 Z10.0; G3 I0.0 J-6.0 K0.0 X-20.0 Y14.0 Z10.0; " \
|
||||
"G1 X-20.0 Y-14.0 Z10.0; G3 I6.0 J0.0 K0.0 X-14.0 Y-20.0 Z10.0; " \
|
||||
"G1 X14.0 Y-20.0 Z10.0; G3 I-0.0 J6.0 K0.0 X20.0 Y-14.0 Z10.0; " \
|
||||
"G1 X20.0 Y14.0 Z10.0; G3 I-6.0 J-0.0 K0.0 X18.24 Y18.24 Z10.0; " \
|
||||
"G1 X21.78 Y21.78 Z10.0; G2 I-7.78 J-7.78 K0.0 X25.0 Y14.0 Z10.0; " \
|
||||
"G1 X25.0 Y-14.0 Z10.0; G2 I-11.0 J0.0 K0.0 X14.0 Y-25.0 Z10.0; " \
|
||||
"G1 X-14.0 Y-25.0 Z10.0; G2 I0.0 J11.0 K0.0 X-25.0 Y-14.0 Z10.0; " \
|
||||
"G1 X-25.0 Y14.0 Z10.0; G2 I11.0 J-0.0 K0.0 X-14.0 Y25.0 Z10.0; " \
|
||||
"G1 X14.0 Y25.0 Z10.0; G2 I-0.0 J-11.0 K0.0 X21.78 Y21.78 Z10.0"
|
||||
|
||||
self.assertTrue(expected_moves == operationMoves,
|
||||
"expected_moves: {}\noperationMoves: {}".format(expected_moves, operationMoves))
|
||||
|
||||
|
||||
def _addViewProvider(profileOp):
|
||||
if FreeCAD.GuiUp:
|
||||
PathOpGui = PathProfileGui.PathOpGui
|
||||
cmdRes = PathProfileGui.Command.res
|
||||
profileOp.ViewObject.Proxy = PathOpGui.ViewProvider(
|
||||
profileOp.ViewObject, cmdRes
|
||||
)
|
||||
|
||||
BIN
src/Mod/Path/PathTests/test_profile.fcstd
Normal file
BIN
src/Mod/Path/PathTests/test_profile.fcstd
Normal file
Binary file not shown.
@@ -22,6 +22,8 @@
|
||||
|
||||
import TestApp
|
||||
|
||||
from PathTests.TestPathProfile import TestPathProfile
|
||||
|
||||
from PathTests.TestPathAdaptive import TestPathAdaptive
|
||||
from PathTests.TestPathCore import TestPathCore
|
||||
from PathTests.TestPathDepthParams import depthTestCases
|
||||
@@ -47,6 +49,7 @@ from PathTests.TestPathPost import TestBuildPostList
|
||||
from PathTests.TestPathPost import TestOutputNameSubstitution
|
||||
|
||||
from PathTests.TestPathPreferences import TestPathPreferences
|
||||
from PathTests.TestPathProfile import TestPathProfile
|
||||
from PathTests.TestPathPropertyBag import TestPathPropertyBag
|
||||
from PathTests.TestPathRotationGenerator import TestPathRotationGenerator
|
||||
from PathTests.TestPathSetupSheet import TestPathSetupSheet
|
||||
@@ -92,6 +95,7 @@ False if TestPathOpUtil.__name__ else True
|
||||
# False if TestPathPost.__name__ else True
|
||||
False if TestPathPostUtils.__name__ else True
|
||||
False if TestPathPreferences.__name__ else True
|
||||
False if TestPathProfile.__name__ else True
|
||||
False if TestPathPropertyBag.__name__ else True
|
||||
False if TestPathRotationGenerator.__name__ else True
|
||||
False if TestPathSetupSheet.__name__ else True
|
||||
|
||||
Reference in New Issue
Block a user