PATH: Feature/dogbone ii (#7660)
* Start of new dogbone dressup * Added Instruction and tangents support for G2/3 moves * Added Maneuver class to represent a set of moves and process them coherently * Created kinks and verify their creation. * Added dogbone detection and verification * Simplified gcode strings * Added horizontal t-bones generation * Added support for vertical t-bone * Consolidated t-bone creation * Added support for pathLength * Added support for tbone on short edge * Added support for long edges * Added support for dogbones * Fixed dogbone for non-horizontal lead-in * Horizontal bone adaptive length tests * Fixed dogbone angle and adaptive length * Some code cleanup * Added adaptive length tests for dogbones * Split base data classes into their own PathLanguage module. * Splitting dogboneII implementation into its constituents * Moved adaptive length into DogbonII module * Separate dogboneII generator test cases and changed interface to allow for dynamic length calculations * Unit tests for length calculation * Initial DogboneII unit test * Unit tests and fixes for plunge move handling * Unit tests for the remaining styles and incision strategies * Basic DogboneII gui * Added support for markers * Better color and selection scheme for markers * Cleaned up import statements * Added DogboneII to Path WB init * Support for dogbone on dogbone and fixed t-bone generation * Fixed t-bone on short leg bones * Fixed tbone on short edge when short edge is m1 * Fixed t-bone on long edge for m0/m1 and CW/CCW * Removed redundant code * Removed redundant 'Dress-up' from menu entries * black code formatting * added generator to cmake * Fixed typos
This commit is contained in:
@@ -35,6 +35,7 @@ class MockToolBit(object):
|
||||
self.FlatRadius = 0
|
||||
self.CuttingEdgeAngle = 60
|
||||
|
||||
|
||||
class TestPathDeburr(PathTestUtils.PathTestBase):
|
||||
def test00(self):
|
||||
"""Verify chamfer depth and offset for an end mill."""
|
||||
|
||||
640
src/Mod/Path/PathTests/TestPathDressupDogboneII.py
Normal file
640
src/Mod/Path/PathTests/TestPathDressupDogboneII.py
Normal file
@@ -0,0 +1,640 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2022 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * *
|
||||
# * 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 Path
|
||||
import Path.Base.Generator.dogboneII as dogboneII
|
||||
import Path.Base.Language as PathLanguage
|
||||
import Path.Dressup.DogboneII
|
||||
import PathTests.PathTestUtils as PathTestUtils
|
||||
import math
|
||||
|
||||
PI = math.pi
|
||||
|
||||
|
||||
class MockTB(object):
|
||||
def __init__(self, dia):
|
||||
self.Name = "ToolBit"
|
||||
self.Label = "ToolBit"
|
||||
self.Diameter = FreeCAD.Units.Quantity(dia, FreeCAD.Units.Length)
|
||||
|
||||
|
||||
class MockTC(object):
|
||||
def __init__(self, dia=2):
|
||||
self.Name = "TC"
|
||||
self.Label = "TC"
|
||||
self.Tool = MockTB(dia)
|
||||
|
||||
|
||||
class MockOp(object):
|
||||
def __init__(self, path, dia=2):
|
||||
self.Path = Path.Path(path)
|
||||
self.ToolController = MockTC(dia)
|
||||
|
||||
|
||||
class MockFeaturePython(object):
|
||||
def __init__(self, name):
|
||||
self.prop = {}
|
||||
self.addProperty("App::PropertyString", "Name", val=name)
|
||||
self.addProperty("App::PropertyString", "Label", val=name)
|
||||
self.addProperty("App::PropertyLink", "Proxy")
|
||||
self.addProperty("Path::Path", "Path", val=Path.Path())
|
||||
|
||||
def addProperty(self, typ, name, grp=None, desc=None, val=None):
|
||||
self.prop[name] = (typ, val)
|
||||
|
||||
def setEditorMode(self, name, mode):
|
||||
pass
|
||||
|
||||
def __setattr__(self, name, val):
|
||||
if name == "prop":
|
||||
return super().__setattr__(name, val)
|
||||
self.prop[name] = (self.prop[name][0], val)
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name == "prop":
|
||||
return super().__getattr__(name)
|
||||
typ, val = self.prop.get(name, (None, None))
|
||||
if typ is None and val is None:
|
||||
raise AttributeError
|
||||
if typ == "App::PropertyLength":
|
||||
if type(val) == float or type(val) == int:
|
||||
return FreeCAD.Units.Quantity(val, FreeCAD.Units.Length)
|
||||
return FreeCAD.Units.Quantity(val)
|
||||
return val
|
||||
|
||||
|
||||
def CreateDressup(path):
|
||||
op = MockOp(path)
|
||||
obj = MockFeaturePython("DogboneII")
|
||||
db = Path.Dressup.DogboneII.Proxy(obj, op)
|
||||
obj.Proxy = db
|
||||
return obj
|
||||
|
||||
|
||||
def MNVR(gcode, begin=None):
|
||||
# 'turns out the replace() isn't really necessary
|
||||
# leave it here anyway for clarity
|
||||
return PathLanguage.Maneuver.FromGCode(gcode.replace("/", "\n"), begin)
|
||||
|
||||
|
||||
def INSTR(gcode, begin=None):
|
||||
return MNVR(gcode, begin).instr[0]
|
||||
|
||||
|
||||
def KINK(gcode, begin=None):
|
||||
maneuver = MNVR(gcode, begin)
|
||||
if len(maneuver.instr) != 2:
|
||||
return None
|
||||
return dogboneII.Kink(maneuver.instr[0], maneuver.instr[1])
|
||||
|
||||
|
||||
class TestDressupDogboneII(PathTestUtils.PathTestBase):
|
||||
"""Unit tests for the DogboneII dressup."""
|
||||
|
||||
def assertEqualPath(self, path, s):
|
||||
def cmd2str(cmd):
|
||||
param = [
|
||||
f"{k}{v:g}" if Path.Geom.isRoughly(0, v - int(v)) else f"{k}{v:.2f}"
|
||||
for k, v in cmd.Parameters.items()
|
||||
]
|
||||
return f"{cmd.Name}{''.join(param)}"
|
||||
|
||||
p = "/".join([cmd2str(cmd) for cmd in path.Commands])
|
||||
self.assertEqual(p, s)
|
||||
|
||||
def test00(self):
|
||||
"""Verify adaptive length"""
|
||||
|
||||
def adaptive(k, a, n):
|
||||
return Path.Dressup.DogboneII.calc_length_adaptive(k, a, n, n)
|
||||
|
||||
if True:
|
||||
# horizontal bones
|
||||
self.assertRoughly(adaptive(KINK("G1X1/G1X2"), 0, 1), 0)
|
||||
self.assertRoughly(adaptive(KINK("G1X1/G1Y1"), 0, 1), 1)
|
||||
self.assertRoughly(adaptive(KINK("G1X1/G1X2Y1"), 0, 1), 0.414214)
|
||||
self.assertRoughly(adaptive(KINK("G1X1/G1X0Y1"), 0, 1), 2.414211)
|
||||
self.assertRoughly(adaptive(KINK("G1X1/G1X0"), 0, 1), 1)
|
||||
self.assertRoughly(adaptive(KINK("G1X1/G1X0Y-1"), 0, 1), 2.414211)
|
||||
self.assertRoughly(adaptive(KINK("G1X1/G1X1Y-1"), 0, 1), 1)
|
||||
self.assertRoughly(adaptive(KINK("G1X1/G1X2Y-1"), 0, 1), 0.414214)
|
||||
self.assertRoughly(adaptive(KINK("G1X1Y1/G1X0Y2"), 0, 1), 0.414214)
|
||||
|
||||
if True:
|
||||
# more horizontal and some vertical bones
|
||||
self.assertRoughly(adaptive(KINK("G1Y1/G1Y2"), 0, 1), 0)
|
||||
self.assertRoughly(adaptive(KINK("G1Y1/G1Y1X1"), PI, 1), 1)
|
||||
self.assertRoughly(adaptive(KINK("G1Y1/G1Y2X1"), PI, 1), 0.089820)
|
||||
self.assertRoughly(adaptive(KINK("G1Y1/G1Y2X1"), PI / 2, 1), 0.414214)
|
||||
self.assertRoughly(adaptive(KINK("G1Y1/G1Y0X1"), PI / 2, 1), 2.414211)
|
||||
self.assertRoughly(adaptive(KINK("G1Y1/G1Y0"), 0, 1), 1)
|
||||
self.assertRoughly(adaptive(KINK("G1Y1/G1Y0X-1"), PI / 2, 1), 2.414211)
|
||||
self.assertRoughly(adaptive(KINK("G1Y1/G1Y1X-1"), 0, 1), 1)
|
||||
self.assertRoughly(adaptive(KINK("G1Y1/G1Y2X-1"), 0, 1), 0.089820)
|
||||
self.assertRoughly(adaptive(KINK("G1Y1/G1Y2X-1"), PI / 2, 1), 0.414214)
|
||||
|
||||
if True:
|
||||
# dogbones
|
||||
self.assertRoughly(adaptive(KINK("G1X1/G1Y1"), -PI / 4, 1), 0.414214)
|
||||
self.assertRoughly(adaptive(KINK("G1X1/G1X0Y1"), -PI / 8, 1), 1.613126)
|
||||
self.assertRoughly(adaptive(KINK("G1X1/G1Y-1"), PI / 4, 1), 0.414214)
|
||||
self.assertRoughly(adaptive(KINK("G1X1/G1X0Y-1"), PI / 8, 1), 1.613126)
|
||||
self.assertRoughly(adaptive(KINK("G1Y1/G1X-1"), PI / 4, 1), 0.414214)
|
||||
self.assertRoughly(adaptive(KINK("G1Y1/G1X1"), 3 * PI / 4, 1), 0.414214)
|
||||
self.assertRoughly(adaptive(KINK("G1Y-1/G1X1"), -3 * PI / 4, 1), 0.414214)
|
||||
self.assertRoughly(adaptive(KINK("G1Y-1/G1X-1"), -PI / 4, 1), 0.414214)
|
||||
self.assertRoughly(adaptive(KINK("G1X1Y1/G1X0Y2"), 0, 1), 0.414214)
|
||||
self.assertRoughly(adaptive(KINK("G1X-1Y1/G1X0Y2"), PI, 1), 0.414214)
|
||||
self.assertRoughly(adaptive(KINK("G1X1Y1/G1X2Y0"), PI / 2, 2), 0.828428)
|
||||
self.assertRoughly(adaptive(KINK("G1X-1Y-1/G1X-2Y0"), -PI / 2, 2), 0.828428)
|
||||
self.assertRoughly(adaptive(KINK("G1X-1Y1/G1X-2Y0"), PI / 2, 2), 0.828428)
|
||||
self.assertRoughly(adaptive(KINK("G1X1Y-1/G1X2Y0"), -PI / 2, 2), 0.828428)
|
||||
|
||||
def test01(self):
|
||||
"""Verify nominal length"""
|
||||
|
||||
def nominal(k, a, n):
|
||||
return Path.Dressup.DogboneII.calc_length_nominal(k, a, n, 0)
|
||||
|
||||
# neither angle nor kink matter
|
||||
self.assertRoughly(nominal(KINK("G1X1/G1X2"), 0, 13), 13)
|
||||
self.assertRoughly(nominal(KINK("G1X1/G1X2"), PI / 2, 13), 13)
|
||||
self.assertRoughly(nominal(KINK("G1X1/G1X2"), PI, 13), 13)
|
||||
self.assertRoughly(nominal(KINK("G1X1/G1X2"), -PI / 2, 13), 13)
|
||||
self.assertRoughly(nominal(KINK("G1X8/G1X12"), 0, 13), 13)
|
||||
self.assertRoughly(nominal(KINK("G1X9/G1X0"), 0, 13), 13)
|
||||
self.assertRoughly(nominal(KINK("G1X7/G1X9"), 0, 13), 13)
|
||||
self.assertRoughly(nominal(KINK("G1X5/G1X1"), 0, 13), 13)
|
||||
|
||||
def test02(self):
|
||||
"""Verify custom length"""
|
||||
|
||||
def custom(k, a, c):
|
||||
return Path.Dressup.DogboneII.calc_length_custom(k, a, 0, c)
|
||||
|
||||
# neither angle nor kink matter
|
||||
self.assertRoughly(custom(KINK("G1X1/G1X2"), 0, 7), 7)
|
||||
self.assertRoughly(custom(KINK("G1X1/G1X2"), PI / 2, 7), 7)
|
||||
self.assertRoughly(custom(KINK("G1X1/G1X2"), PI, 7), 7)
|
||||
self.assertRoughly(custom(KINK("G1X1/G1X2"), -PI / 2, 7), 7)
|
||||
self.assertRoughly(custom(KINK("G1X8/G1X12"), 0, 7), 7)
|
||||
self.assertRoughly(custom(KINK("G1X9/G1X0"), 0, 7), 7)
|
||||
self.assertRoughly(custom(KINK("G1X7/G1X9"), 0, 7), 7)
|
||||
self.assertRoughly(custom(KINK("G1X5/G1X1"), 0, 7), 7)
|
||||
|
||||
def test10(self):
|
||||
"""Verify basic op dressup"""
|
||||
|
||||
obj = CreateDressup("G1X10/G1Y20")
|
||||
obj.Incision = Path.Dressup.DogboneII.Incision.Fixed
|
||||
obj.Style = Path.Dressup.DogboneII.Style.Tbone_H
|
||||
|
||||
# bones on right side
|
||||
obj.Side = Path.Dressup.DogboneII.Side.Right
|
||||
obj.Proxy.execute(obj)
|
||||
self.assertEqualPath(obj.Path, "G1X10/G1X11/G1X10/G1Y20")
|
||||
|
||||
# no bones on left side
|
||||
obj.Side = Path.Dressup.DogboneII.Side.Left
|
||||
obj.Proxy.execute(obj)
|
||||
self.assertEqualPath(obj.Path, "G1X10/G1Y20")
|
||||
|
||||
def test11(self):
|
||||
"""Verify retaining non-move instructions"""
|
||||
|
||||
obj = CreateDressup("G1X10/(some comment)/G1Y20")
|
||||
obj.Incision = Path.Dressup.DogboneII.Incision.Fixed
|
||||
obj.Style = Path.Dressup.DogboneII.Style.Tbone_H
|
||||
|
||||
# bone on right side
|
||||
obj.Side = Path.Dressup.DogboneII.Side.Right
|
||||
obj.Proxy.execute(obj)
|
||||
self.assertEqualPath(obj.Path, "G1X10/(some comment)/G1X11/G1X10/G1Y20")
|
||||
|
||||
# no bone on left side
|
||||
obj.Side = Path.Dressup.DogboneII.Side.Left
|
||||
obj.Proxy.execute(obj)
|
||||
self.assertEqualPath(obj.Path, "G1X10/(some comment)/G1Y20")
|
||||
|
||||
def test20(self):
|
||||
"""Verify bone on plunge moves"""
|
||||
|
||||
obj = CreateDressup("G0Z10/G1Z0/G1X10/G1Y10/G1X0/G1Y0/G0Z10")
|
||||
obj.Incision = Path.Dressup.DogboneII.Incision.Fixed
|
||||
obj.Style = Path.Dressup.DogboneII.Style.Tbone_H
|
||||
obj.Side = Path.Dressup.DogboneII.Side.Right
|
||||
|
||||
obj.Proxy.execute(obj)
|
||||
self.assertEqualPath(
|
||||
obj.Path,
|
||||
"G0Z10/G1Z0/G1X10/G1X11/G1X10/G1Y10/G1X11/G1X10/G1X0/G1X-1/G1X0/G1Y0/G1X-1/G1X0/G0Z10",
|
||||
)
|
||||
|
||||
def test21(self):
|
||||
"""Verify ignoring plunge moves that don't connect"""
|
||||
|
||||
obj = CreateDressup("G0Z10/G1Z0/G1X10/G1Y10/G1X0/G1Y5/G0Z10")
|
||||
obj.Incision = Path.Dressup.DogboneII.Incision.Fixed
|
||||
obj.Style = Path.Dressup.DogboneII.Style.Tbone_H
|
||||
obj.Side = Path.Dressup.DogboneII.Side.Right
|
||||
|
||||
obj.Proxy.execute(obj)
|
||||
self.assertEqualPath(
|
||||
obj.Path,
|
||||
"G0Z10/G1Z0/G1X10/G1X11/G1X10/G1Y10/G1X11/G1X10/G1X0/G1X-1/G1X0/G1Y5/G0Z10",
|
||||
)
|
||||
|
||||
def test30(self):
|
||||
"""Verify TBone_V style"""
|
||||
|
||||
def check_tbone(d, i, path, out, right):
|
||||
obj = CreateDressup(f"({d}.{i:02})/{path}")
|
||||
obj.Incision = Path.Dressup.DogboneII.Incision.Fixed
|
||||
if right:
|
||||
obj.Side = Path.Dressup.DogboneII.Side.Right
|
||||
else:
|
||||
obj.Side = Path.Dressup.DogboneII.Side.Left
|
||||
obj.Style = Path.Dressup.DogboneII.Style.Tbone_V
|
||||
obj.Proxy.execute(obj)
|
||||
self.assertEqualPath(obj.Path, f"({d}.{i:02})/{out}")
|
||||
|
||||
# test data with a horizontal lead in
|
||||
test_data_h = [
|
||||
# top right quadrant
|
||||
("G1X10Y0/G1X10Y10", "G1X10Y0/G1Y-1/G1Y0/G1X10Y10", True),
|
||||
("G1X10Y0/G1X20Y10", "G1X10Y0/G1Y-1/G1Y0/G1X20Y10", True),
|
||||
("G1X10Y0/G1X90Y10", "G1X10Y0/G1Y-1/G1Y0/G1X90Y10", True),
|
||||
("G1X10Y0/G1X0Y10", "G1X10Y0/G1Y-1/G1Y0/G1X0Y10", True),
|
||||
# bottom right quadrant
|
||||
("G1X10Y0/G1X90Y-10", "G1X10Y0/G1Y1/G1Y0/G1X90Y-10", False),
|
||||
("G1X10Y0/G1X20Y-10", "G1X10Y0/G1Y1/G1Y0/G1X20Y-10", False),
|
||||
("G1X10Y0/G1X10Y-10", "G1X10Y0/G1Y1/G1Y0/G1X10Y-10", False),
|
||||
("G1X10Y0/G1X0Y-10", "G1X10Y0/G1Y1/G1Y0/G1X0Y-10", False),
|
||||
# top left quadrant
|
||||
("G1X-10Y0/G1X-10Y10", "G1X-10Y0/G1Y-1/G1Y0/G1X-10Y10", False),
|
||||
("G1X-10Y0/G1X-20Y10", "G1X-10Y0/G1Y-1/G1Y0/G1X-20Y10", False),
|
||||
("G1X-10Y0/G1X-90Y10", "G1X-10Y0/G1Y-1/G1Y0/G1X-90Y10", False),
|
||||
("G1X-10Y0/G1X-0Y10", "G1X-10Y0/G1Y-1/G1Y0/G1X-0Y10", False),
|
||||
# bottom left quadrant
|
||||
("G1X-10Y0/G1X-90Y-10", "G1X-10Y0/G1Y1/G1Y0/G1X-90Y-10", True),
|
||||
("G1X-10Y0/G1X-20Y-10", "G1X-10Y0/G1Y1/G1Y0/G1X-20Y-10", True),
|
||||
("G1X-10Y0/G1X-10Y-10", "G1X-10Y0/G1Y1/G1Y0/G1X-10Y-10", True),
|
||||
("G1X-10Y0/G1X-0Y-10", "G1X-10Y0/G1Y1/G1Y0/G1X-0Y-10", True),
|
||||
]
|
||||
|
||||
for i, (path, out, right) in enumerate(test_data_h):
|
||||
check_tbone("h", i, path, out, right)
|
||||
|
||||
# test data with a vertical lead in
|
||||
test_data_v = [
|
||||
# top right quadrant
|
||||
("G1X0Y10/G1X10Y10", "G1X0Y10/G1Y11/G1Y10/G1X10Y10", False),
|
||||
("G1X0Y10/G1X10Y20", "G1X0Y10/G1Y11/G1Y10/G1X10Y20", False),
|
||||
("G1X0Y10/G1X10Y90", "G1X0Y10/G1Y11/G1Y10/G1X10Y90", False),
|
||||
("G1X0Y10/G1X10Y0", "G1X0Y10/G1Y11/G1Y10/G1X10Y0", False),
|
||||
# bottom right quadrant
|
||||
("G1X0Y-10/G1X10Y-90", "G1X0Y-10/G1Y-11/G1Y-10/G1X10Y-90", True),
|
||||
("G1X0Y-10/G1X10Y-20", "G1X0Y-10/G1Y-11/G1Y-10/G1X10Y-20", True),
|
||||
("G1X0Y-10/G1X10Y-10", "G1X0Y-10/G1Y-11/G1Y-10/G1X10Y-10", True),
|
||||
("G1X0Y-10/G1X10Y-0", "G1X0Y-10/G1Y-11/G1Y-10/G1X10Y-0", True),
|
||||
# top left quadrant
|
||||
("G1X0Y10/G1X-10Y10", "G1X0Y10/G1Y11/G1Y10/G1X-10Y10", True),
|
||||
("G1X0Y10/G1X-10Y20", "G1X0Y10/G1Y11/G1Y10/G1X-10Y20", True),
|
||||
("G1X0Y10/G1X-10Y90", "G1X0Y10/G1Y11/G1Y10/G1X-10Y90", True),
|
||||
("G1X0Y10/G1X-10Y0", "G1X0Y10/G1Y11/G1Y10/G1X-10Y0", True),
|
||||
# bottom left quadrant
|
||||
("G1X0Y-10/G1X-10Y-90", "G1X0Y-10/G1Y-11/G1Y-10/G1X-10Y-90", False),
|
||||
("G1X0Y-10/G1X-10Y-20", "G1X0Y-10/G1Y-11/G1Y-10/G1X-10Y-20", False),
|
||||
("G1X0Y-10/G1X-10Y-10", "G1X0Y-10/G1Y-11/G1Y-10/G1X-10Y-10", False),
|
||||
("G1X0Y-10/G1X-10Y-0", "G1X0Y-10/G1Y-11/G1Y-10/G1X-10Y-0", False),
|
||||
]
|
||||
|
||||
for i, (path, out, right) in enumerate(test_data_v):
|
||||
check_tbone("v", i, path, out, right)
|
||||
|
||||
def test40(self):
|
||||
"""Verify TBone_S style"""
|
||||
|
||||
def check_tbone_s(d, i, path, out, right):
|
||||
obj = CreateDressup(f"(m{d}.{i:02})/{path}")
|
||||
obj.Incision = Path.Dressup.DogboneII.Incision.Fixed
|
||||
if right:
|
||||
obj.Side = Path.Dressup.DogboneII.Side.Right
|
||||
else:
|
||||
obj.Side = Path.Dressup.DogboneII.Side.Left
|
||||
obj.Style = Path.Dressup.DogboneII.Style.Tbone_S
|
||||
obj.Proxy.execute(obj)
|
||||
self.assertEqualPath(obj.Path, f"(m{d}.{i:02})/{out}")
|
||||
|
||||
# short edge m0
|
||||
test_data_0 = [
|
||||
# CCW
|
||||
("G1X10/G1Y20", "G1X10/G1Y-1/G1Y0/G1Y20", True),
|
||||
("G1X10Y10/G1X-10Y30", "G1X10Y10/G1X10.71Y9.29/G1X10Y10/G1X-10Y30", True),
|
||||
("G1Y10/G1X-20", "G1Y10/G1X1/G1X0/G1X-20", True),
|
||||
(
|
||||
"G1X-10Y10/G1X-30Y-10",
|
||||
"G1X-10Y10/G1X-9.29Y10.71/G1X-10Y10/G1X-30Y-10",
|
||||
True,
|
||||
),
|
||||
("G1X-10/G1Y-20", "G1X-10/G1Y1/G1Y0/G1Y-20", True),
|
||||
(
|
||||
"G1X-10Y-10/G1X10Y-30",
|
||||
"G1X-10Y-10/G1X-10.71Y-9.29/G1X-10Y-10/G1X10Y-30",
|
||||
True,
|
||||
),
|
||||
("G1Y-10/G1X20", "G1Y-10/G1X-1/G1X0/G1X20", True),
|
||||
("G1X10Y-10/G1X30Y10", "G1X10Y-10/G1X9.29Y-10.71/G1X10Y-10/G1X30Y10", True),
|
||||
# CW
|
||||
("G1X10/G1Y-20", "G1X10/G1Y1/G1Y0/G1Y-20", False),
|
||||
("G1X10Y10/G1X30Y-10", "G1X10Y10/G1X9.29Y10.71/G1X10Y10/G1X30Y-10", False),
|
||||
("G1Y10/G1X20", "G1Y10/G1X-1/G1X0/G1X20", False),
|
||||
(
|
||||
"G1X-10Y10/G1X10Y30",
|
||||
"G1X-10Y10/G1X-10.71Y9.29/G1X-10Y10/G1X10Y30",
|
||||
False,
|
||||
),
|
||||
("G1X-10/G1Y20", "G1X-10/G1Y-1/G1Y0/G1Y20", False),
|
||||
(
|
||||
"G1X-10Y-10/G1X-30Y10",
|
||||
"G1X-10Y-10/G1X-9.29Y-10.71/G1X-10Y-10/G1X-30Y10",
|
||||
False,
|
||||
),
|
||||
("G1Y-10/G1X-20", "G1Y-10/G1X1/G1X0/G1X-20", False),
|
||||
(
|
||||
"G1X10Y-10/G1X-10Y-30",
|
||||
"G1X10Y-10/G1X10.71Y-9.29/G1X10Y-10/G1X-10Y-30",
|
||||
False,
|
||||
),
|
||||
]
|
||||
|
||||
for i, (path, out, right) in enumerate(test_data_0):
|
||||
check_tbone_s("0", i, path, out, right)
|
||||
|
||||
# short edge m1
|
||||
test_data_1 = [
|
||||
# CCW
|
||||
("G1X20/G1Y10", "G1X20/G1X21/G1X20/G1Y10", True),
|
||||
("G1X20Y20/G1X10Y30", "G1X20Y20/G1X20.71Y20.71/G1X20Y20/G1X10Y30", True),
|
||||
("G1Y20/G1X-10", "G1Y20/G1Y21/G1Y20/G1X-10", True),
|
||||
(
|
||||
"G1X-20Y20/G1X-30Y10",
|
||||
"G1X-20Y20/G1X-20.71Y20.71/G1X-20Y20/G1X-30Y10",
|
||||
True,
|
||||
),
|
||||
("G1X-20/G1Y-10", "G1X-20/G1X-21/G1X-20/G1Y-10", True),
|
||||
(
|
||||
"G1X-20Y-20/G1X-10Y-30",
|
||||
"G1X-20Y-20/G1X-20.71Y-20.71/G1X-20Y-20/G1X-10Y-30",
|
||||
True,
|
||||
),
|
||||
("G1Y-20/G1X10", "G1Y-20/G1Y-21/G1Y-20/G1X10", True),
|
||||
(
|
||||
"G1X20Y-20/G1X30Y-10",
|
||||
"G1X20Y-20/G1X20.71Y-20.71/G1X20Y-20/G1X30Y-10",
|
||||
True,
|
||||
),
|
||||
# CW
|
||||
("G1X20/G1Y-10", "G1X20/G1X21/G1X20/G1Y-10", False),
|
||||
("G1X20Y20/G1X30Y10", "G1X20Y20/G1X20.71Y20.71/G1X20Y20/G1X30Y10", False),
|
||||
("G1Y20/G1X10", "G1Y20/G1Y21/G1Y20/G1X10", False),
|
||||
(
|
||||
"G1X-20Y20/G1X-10Y30",
|
||||
"G1X-20Y20/G1X-20.71Y20.71/G1X-20Y20/G1X-10Y30",
|
||||
False,
|
||||
),
|
||||
("G1X-20/G1Y10", "G1X-20/G1X-21/G1X-20/G1Y10", False),
|
||||
(
|
||||
"G1X-20Y-20/G1X-30Y-10",
|
||||
"G1X-20Y-20/G1X-20.71Y-20.71/G1X-20Y-20/G1X-30Y-10",
|
||||
False,
|
||||
),
|
||||
("G1Y-20/G1X-10", "G1Y-20/G1Y-21/G1Y-20/G1X-10", False),
|
||||
(
|
||||
"G1X20Y-20/G1X10Y-30",
|
||||
"G1X20Y-20/G1X20.71Y-20.71/G1X20Y-20/G1X10Y-30",
|
||||
False,
|
||||
),
|
||||
]
|
||||
|
||||
for i, (path, out, right) in enumerate(test_data_1):
|
||||
check_tbone_s("1", i, path, out, right)
|
||||
|
||||
def test50(self):
|
||||
"""Verify TBone_L style"""
|
||||
|
||||
def check_tbone_l(d, i, path, out, right):
|
||||
obj = CreateDressup(f"(m{d}.{i:02})/{path}")
|
||||
obj.Incision = Path.Dressup.DogboneII.Incision.Fixed
|
||||
if right:
|
||||
obj.Side = Path.Dressup.DogboneII.Side.Right
|
||||
else:
|
||||
obj.Side = Path.Dressup.DogboneII.Side.Left
|
||||
obj.Style = Path.Dressup.DogboneII.Style.Tbone_L
|
||||
obj.Proxy.execute(obj)
|
||||
self.assertEqualPath(obj.Path, f"(m{d}.{i:02})/{out}")
|
||||
|
||||
# long edge m1
|
||||
test_data_1 = [
|
||||
# CCW
|
||||
("G1X10/G1Y20", "G1X10/G1X11/G1X10/G1Y20", True),
|
||||
("G1X10Y10/G1X-10Y30", "G1X10Y10/G1X10.71Y10.71/G1X10Y10/G1X-10Y30", True),
|
||||
("G1Y10/G1X-20", "G1Y10/G1Y11/G1Y10/G1X-20", True),
|
||||
(
|
||||
"G1X-10Y10/G1X-30Y-10",
|
||||
"G1X-10Y10/G1X-10.71Y10.71/G1X-10Y10/G1X-30Y-10",
|
||||
True,
|
||||
),
|
||||
("G1X-10/G1Y-20", "G1X-10/G1X-11/G1X-10/G1Y-20", True),
|
||||
(
|
||||
"G1X-10Y-10/G1X10Y-30",
|
||||
"G1X-10Y-10/G1X-10.71Y-10.71/G1X-10Y-10/G1X10Y-30",
|
||||
True,
|
||||
),
|
||||
("G1Y-10/G1X20", "G1Y-10/G1Y-11/G1Y-10/G1X20", True),
|
||||
(
|
||||
"G1X10Y-10/G1X30Y10",
|
||||
"G1X10Y-10/G1X10.71Y-10.71/G1X10Y-10/G1X30Y10",
|
||||
True,
|
||||
),
|
||||
# CW
|
||||
("G1X10/G1Y-20", "G1X10/G1X11/G1X10/G1Y-20", False),
|
||||
("G1X10Y10/G1X30Y-10", "G1X10Y10/G1X10.71Y10.71/G1X10Y10/G1X30Y-10", False),
|
||||
("G1Y10/G1X20", "G1Y10/G1Y11/G1Y10/G1X20", False),
|
||||
(
|
||||
"G1X-10Y10/G1X10Y30",
|
||||
"G1X-10Y10/G1X-10.71Y10.71/G1X-10Y10/G1X10Y30",
|
||||
False,
|
||||
),
|
||||
("G1X-10/G1Y20", "G1X-10/G1X-11/G1X-10/G1Y20", False),
|
||||
(
|
||||
"G1X-10Y-10/G1X-30Y10",
|
||||
"G1X-10Y-10/G1X-10.71Y-10.71/G1X-10Y-10/G1X-30Y10",
|
||||
False,
|
||||
),
|
||||
("G1Y-10/G1X-20", "G1Y-10/G1Y-11/G1Y-10/G1X-20", False),
|
||||
(
|
||||
"G1X10Y-10/G1X-10Y-30",
|
||||
"G1X10Y-10/G1X10.71Y-10.71/G1X10Y-10/G1X-10Y-30",
|
||||
False,
|
||||
),
|
||||
]
|
||||
|
||||
for i, (path, out, right) in enumerate(test_data_1):
|
||||
check_tbone_l("1", i, path, out, right)
|
||||
|
||||
# long edge m0
|
||||
test_data_0 = [
|
||||
# CCW
|
||||
("G1X20/G1Y10", "G1X20/G1Y-1/G1Y0/G1Y10", True),
|
||||
("G1X20Y20/G1X10Y30", "G1X20Y20/G1X20.71Y19.29/G1X20Y20/G1X10Y30", True),
|
||||
("G1Y20/G1X-10", "G1Y20/G1X1/G1X0/G1X-10", True),
|
||||
(
|
||||
"G1X-20Y20/G1X-30Y10",
|
||||
"G1X-20Y20/G1X-19.29Y20.71/G1X-20Y20/G1X-30Y10",
|
||||
True,
|
||||
),
|
||||
("G1X-20/G1Y-10", "G1X-20/G1Y1/G1Y0/G1Y-10", True),
|
||||
(
|
||||
"G1X-20Y-20/G1X-10Y-30",
|
||||
"G1X-20Y-20/G1X-20.71Y-19.29/G1X-20Y-20/G1X-10Y-30",
|
||||
True,
|
||||
),
|
||||
("G1Y-20/G1X10", "G1Y-20/G1X-1/G1X0/G1X10", True),
|
||||
(
|
||||
"G1X20Y-20/G1X30Y-10",
|
||||
"G1X20Y-20/G1X19.29Y-20.71/G1X20Y-20/G1X30Y-10",
|
||||
True,
|
||||
),
|
||||
# CW
|
||||
("G1X20/G1Y-10", "G1X20/G1Y1/G1Y0/G1Y-10", False),
|
||||
("G1X20Y20/G1X30Y10", "G1X20Y20/G1X19.29Y20.71/G1X20Y20/G1X30Y10", False),
|
||||
("G1Y20/G1X10", "G1Y20/G1X-1/G1X0/G1X10", False),
|
||||
(
|
||||
"G1X-20Y20/G1X-10Y30",
|
||||
"G1X-20Y20/G1X-20.71Y19.29/G1X-20Y20/G1X-10Y30",
|
||||
False,
|
||||
),
|
||||
("G1X-20/G1Y10", "G1X-20/G1Y-1/G1Y0/G1Y10", False),
|
||||
(
|
||||
"G1X-20Y-20/G1X-30Y-10",
|
||||
"G1X-20Y-20/G1X-19.29Y-20.71/G1X-20Y-20/G1X-30Y-10",
|
||||
False,
|
||||
),
|
||||
("G1Y-20/G1X-10", "G1Y-20/G1X1/G1X0/G1X-10", False),
|
||||
(
|
||||
"G1X20Y-20/G1X10Y-30",
|
||||
"G1X20Y-20/G1X20.71Y-19.29/G1X20Y-20/G1X10Y-30",
|
||||
False,
|
||||
),
|
||||
]
|
||||
|
||||
for i, (path, out, right) in enumerate(test_data_0):
|
||||
check_tbone_l("0", i, path, out, right)
|
||||
|
||||
def test60(self):
|
||||
"""Verify Dogbone style"""
|
||||
|
||||
obj = CreateDressup("G1X10/G1Y20")
|
||||
obj.Incision = Path.Dressup.DogboneII.Incision.Fixed
|
||||
obj.Side = Path.Dressup.DogboneII.Side.Right
|
||||
|
||||
obj.Style = Path.Dressup.DogboneII.Style.Dogbone
|
||||
obj.Proxy.execute(obj)
|
||||
self.assertEqualPath(obj.Path, "G1X10/G1X10.71Y-0.71/G1X10Y0/G1Y20")
|
||||
|
||||
def test70(self):
|
||||
"""Verify custom length."""
|
||||
|
||||
obj = CreateDressup("G0Z10/G1Z0/G1X10/G1Y10/G1X0/G1Y0/G0Z10")
|
||||
obj.Style = Path.Dressup.DogboneII.Style.Tbone_H
|
||||
obj.Side = Path.Dressup.DogboneII.Side.Right
|
||||
|
||||
obj.Incision = Path.Dressup.DogboneII.Incision.Custom
|
||||
obj.Custom = 3
|
||||
obj.Proxy.execute(obj)
|
||||
self.assertEqualPath(
|
||||
obj.Path,
|
||||
"G0Z10/G1Z0/G1X10/G1X13/G1X10/G1Y10/G1X13/G1X10/G1X0/G1X-3/G1X0/G1Y0/G1X-3/G1X0/G0Z10",
|
||||
)
|
||||
|
||||
obj.Custom = 2
|
||||
obj.Proxy.execute(obj)
|
||||
self.assertEqualPath(
|
||||
obj.Path,
|
||||
"G0Z10/G1Z0/G1X10/G1X12/G1X10/G1Y10/G1X12/G1X10/G1X0/G1X-2/G1X0/G1Y0/G1X-2/G1X0/G0Z10",
|
||||
)
|
||||
|
||||
def test80(self):
|
||||
"""Verify adaptive length."""
|
||||
|
||||
obj = CreateDressup("G1X10/G1Y20")
|
||||
obj.Incision = Path.Dressup.DogboneII.Incision.Adaptive
|
||||
obj.Side = Path.Dressup.DogboneII.Side.Right
|
||||
|
||||
obj.Style = Path.Dressup.DogboneII.Style.Dogbone
|
||||
obj.Proxy.execute(obj)
|
||||
self.assertEqualPath(obj.Path, "G1X10/G1X10.29Y-0.29/G1X10Y0/G1Y20")
|
||||
|
||||
def test81(self):
|
||||
"""Verify adaptive length II."""
|
||||
|
||||
obj = CreateDressup("G1X10/G1X20Y20")
|
||||
obj.Incision = Path.Dressup.DogboneII.Incision.Adaptive
|
||||
obj.Side = Path.Dressup.DogboneII.Side.Right
|
||||
|
||||
obj.Style = Path.Dressup.DogboneII.Style.Dogbone
|
||||
obj.Proxy.execute(obj)
|
||||
self.assertEqualPath(obj.Path, "G1X10/G1X10.09Y-0.15/G1X10Y0/G1X20Y20")
|
||||
|
||||
def test90(self):
|
||||
"""Verify dogbone blacklist"""
|
||||
|
||||
obj = CreateDressup("G0Z10/G1Z0/G1X10/G1Y10/G1X0/G1Y0/G0Z10")
|
||||
obj.Incision = Path.Dressup.DogboneII.Incision.Fixed
|
||||
obj.Style = Path.Dressup.DogboneII.Style.Tbone_H
|
||||
obj.Side = Path.Dressup.DogboneII.Side.Right
|
||||
obj.BoneBlacklist = [0, 2]
|
||||
obj.Proxy.execute(obj)
|
||||
self.assertEqualPath(
|
||||
obj.Path, "G0Z10/G1Z0/G1X10/G1Y10/G1X11/G1X10/G1X0/G1Y0/G1X-1/G1X0/G0Z10"
|
||||
)
|
||||
return obj
|
||||
|
||||
def test91(self):
|
||||
"""Verify dogbone on dogbone"""
|
||||
|
||||
obj = self.test90()
|
||||
|
||||
obj2 = MockFeaturePython("DogboneII_")
|
||||
db2 = Path.Dressup.DogboneII.Proxy(obj2, obj)
|
||||
obj2.Proxy = db2
|
||||
obj2.Incision = Path.Dressup.DogboneII.Incision.Fixed
|
||||
obj2.Style = Path.Dressup.DogboneII.Style.Tbone_H
|
||||
obj2.Side = Path.Dressup.DogboneII.Side.Right
|
||||
obj2.BoneBlacklist = [1]
|
||||
obj2.Proxy.execute(obj2)
|
||||
self.assertEqualPath(
|
||||
obj2.Path,
|
||||
"G0Z10/G1Z0/G1X10/G1X11/G1X10/G1Y10/G1X11/G1X10/G1X0/G1X-1/G1X0/G1Y0/G1X-1/G1X0/G0Z10",
|
||||
)
|
||||
@@ -93,9 +93,7 @@ class TestPathDrillable(PathTestUtils.PathTestBase):
|
||||
self.assertFalse(Drillable.isDrillable(self.obj.Shape, candidate))
|
||||
|
||||
# Passing None as vector
|
||||
self.assertTrue(
|
||||
Drillable.isDrillable(self.obj.Shape, candidate, vector=None)
|
||||
)
|
||||
self.assertTrue(Drillable.isDrillable(self.obj.Shape, candidate, vector=None))
|
||||
|
||||
# Passing explicit vector
|
||||
self.assertTrue(
|
||||
@@ -125,9 +123,7 @@ class TestPathDrillable(PathTestUtils.PathTestBase):
|
||||
self.assertFalse(Drillable.isDrillable(self.obj.Shape, candidate))
|
||||
|
||||
# Passing None as vector
|
||||
self.assertFalse(
|
||||
Drillable.isDrillable(self.obj.Shape, candidate, vector=None)
|
||||
)
|
||||
self.assertFalse(Drillable.isDrillable(self.obj.Shape, candidate, vector=None))
|
||||
|
||||
# raised cylinder
|
||||
candidate = self.obj.getSubObject("Face32")
|
||||
@@ -136,9 +132,7 @@ class TestPathDrillable(PathTestUtils.PathTestBase):
|
||||
self.assertFalse(Drillable.isDrillable(self.obj.Shape, candidate))
|
||||
|
||||
# Passing None as vector
|
||||
self.assertFalse(
|
||||
Drillable.isDrillable(self.obj.Shape, candidate, vector=None)
|
||||
)
|
||||
self.assertFalse(Drillable.isDrillable(self.obj.Shape, candidate, vector=None))
|
||||
|
||||
# cylinder on slope
|
||||
candidate = self.obj.getSubObject("Face24")
|
||||
@@ -146,9 +140,7 @@ class TestPathDrillable(PathTestUtils.PathTestBase):
|
||||
self.assertTrue(Drillable.isDrillable(self.obj.Shape, candidate))
|
||||
|
||||
# Passing None as vector
|
||||
self.assertTrue(
|
||||
Drillable.isDrillable(self.obj.Shape, candidate, vector=None)
|
||||
)
|
||||
self.assertTrue(Drillable.isDrillable(self.obj.Shape, candidate, vector=None))
|
||||
|
||||
# Circular Faces
|
||||
candidate = self.obj.getSubObject("Face54")
|
||||
@@ -157,15 +149,11 @@ class TestPathDrillable(PathTestUtils.PathTestBase):
|
||||
self.assertTrue(Drillable.isDrillable(self.obj.Shape, candidate))
|
||||
|
||||
# Passing None as vector
|
||||
self.assertTrue(
|
||||
Drillable.isDrillable(self.obj.Shape, candidate, vector=None)
|
||||
)
|
||||
self.assertTrue(Drillable.isDrillable(self.obj.Shape, candidate, vector=None))
|
||||
|
||||
# Passing explicit vector
|
||||
self.assertTrue(
|
||||
Drillable.isDrillable(
|
||||
self.obj.Shape, candidate, vector=App.Vector(0, 0, 1)
|
||||
)
|
||||
Drillable.isDrillable(self.obj.Shape, candidate, vector=App.Vector(0, 0, 1))
|
||||
)
|
||||
|
||||
# Drilling with smaller bit
|
||||
@@ -185,9 +173,7 @@ class TestPathDrillable(PathTestUtils.PathTestBase):
|
||||
self.assertFalse(Drillable.isDrillable(self.obj.Shape, candidate))
|
||||
|
||||
# Passing None as vector
|
||||
self.assertTrue(
|
||||
Drillable.isDrillable(self.obj.Shape, candidate, vector=None)
|
||||
)
|
||||
self.assertTrue(Drillable.isDrillable(self.obj.Shape, candidate, vector=None))
|
||||
|
||||
# Passing explicit vector
|
||||
self.assertTrue(
|
||||
@@ -202,9 +188,7 @@ class TestPathDrillable(PathTestUtils.PathTestBase):
|
||||
self.assertTrue(Drillable.isDrillable(self.obj.Shape, candidate))
|
||||
|
||||
# Passing None as vector
|
||||
self.assertTrue(
|
||||
Drillable.isDrillable(self.obj.Shape, candidate, vector=None)
|
||||
)
|
||||
self.assertTrue(Drillable.isDrillable(self.obj.Shape, candidate, vector=None))
|
||||
|
||||
# interrupted Face
|
||||
candidate = self.obj.getSubObject("Face50")
|
||||
@@ -212,9 +196,7 @@ class TestPathDrillable(PathTestUtils.PathTestBase):
|
||||
self.assertFalse(Drillable.isDrillable(self.obj.Shape, candidate))
|
||||
|
||||
# Passing None as vector
|
||||
self.assertFalse(
|
||||
Drillable.isDrillable(self.obj.Shape, candidate, vector=None)
|
||||
)
|
||||
self.assertFalse(Drillable.isDrillable(self.obj.Shape, candidate, vector=None))
|
||||
|
||||
# donut face
|
||||
candidate = self.obj.getSubObject("Face48")
|
||||
@@ -222,9 +204,7 @@ class TestPathDrillable(PathTestUtils.PathTestBase):
|
||||
self.assertTrue(Drillable.isDrillable(self.obj.Shape, candidate))
|
||||
|
||||
# Passing None as vector
|
||||
self.assertTrue(
|
||||
Drillable.isDrillable(self.obj.Shape, candidate, vector=None)
|
||||
)
|
||||
self.assertTrue(Drillable.isDrillable(self.obj.Shape, candidate, vector=None))
|
||||
|
||||
# Test edges
|
||||
# circular edge
|
||||
@@ -234,15 +214,11 @@ class TestPathDrillable(PathTestUtils.PathTestBase):
|
||||
self.assertTrue(Drillable.isDrillable(self.obj.Shape, candidate))
|
||||
|
||||
# Passing None as vector
|
||||
self.assertTrue(
|
||||
Drillable.isDrillable(self.obj.Shape, candidate, vector=None)
|
||||
)
|
||||
self.assertTrue(Drillable.isDrillable(self.obj.Shape, candidate, vector=None))
|
||||
|
||||
# Passing explicit vector
|
||||
self.assertTrue(
|
||||
Drillable.isDrillable(
|
||||
self.obj.Shape, candidate, vector=App.Vector(0, 0, 1)
|
||||
)
|
||||
Drillable.isDrillable(self.obj.Shape, candidate, vector=App.Vector(0, 0, 1))
|
||||
)
|
||||
|
||||
# Drilling with smaller bit
|
||||
@@ -264,15 +240,11 @@ class TestPathDrillable(PathTestUtils.PathTestBase):
|
||||
self.assertFalse(Drillable.isDrillable(self.obj.Shape, candidate))
|
||||
|
||||
# Passing None as vector
|
||||
self.assertTrue(
|
||||
Drillable.isDrillable(self.obj.Shape, candidate, vector=None)
|
||||
)
|
||||
self.assertTrue(Drillable.isDrillable(self.obj.Shape, candidate, vector=None))
|
||||
|
||||
# Passing explicit vector
|
||||
self.assertTrue(
|
||||
Drillable.isDrillable(
|
||||
self.obj.Shape, candidate, vector=App.Vector(0, 1, 0)
|
||||
)
|
||||
Drillable.isDrillable(self.obj.Shape, candidate, vector=App.Vector(0, 1, 0))
|
||||
)
|
||||
|
||||
# incomplete circular edge
|
||||
@@ -281,9 +253,7 @@ class TestPathDrillable(PathTestUtils.PathTestBase):
|
||||
self.assertFalse(Drillable.isDrillable(self.obj.Shape, candidate))
|
||||
|
||||
# Passing None as vector
|
||||
self.assertFalse(
|
||||
Drillable.isDrillable(self.obj.Shape, candidate, vector=None)
|
||||
)
|
||||
self.assertFalse(Drillable.isDrillable(self.obj.Shape, candidate, vector=None))
|
||||
|
||||
# elliptical edge
|
||||
candidate = self.obj.getSubObject("Edge56")
|
||||
@@ -291,9 +261,7 @@ class TestPathDrillable(PathTestUtils.PathTestBase):
|
||||
self.assertFalse(Drillable.isDrillable(self.obj.Shape, candidate))
|
||||
|
||||
# Passing None as vector
|
||||
self.assertFalse(
|
||||
Drillable.isDrillable(self.obj.Shape, candidate, vector=None)
|
||||
)
|
||||
self.assertFalse(Drillable.isDrillable(self.obj.Shape, candidate, vector=None))
|
||||
|
||||
def test20(self):
|
||||
"""Test getDrillableTargets"""
|
||||
@@ -303,7 +271,5 @@ class TestPathDrillable(PathTestUtils.PathTestBase):
|
||||
results = Drillable.getDrillableTargets(self.obj, vector=None)
|
||||
self.assertEqual(len(results), 20)
|
||||
|
||||
results = Drillable.getDrillableTargets(
|
||||
self.obj, ToolDiameter=20, vector=None
|
||||
)
|
||||
results = Drillable.getDrillableTargets(self.obj, ToolDiameter=20, vector=None)
|
||||
self.assertEqual(len(results), 5)
|
||||
|
||||
354
src/Mod/Path/PathTests/TestPathGeneratorDogboneII.py
Normal file
354
src/Mod/Path/PathTests/TestPathGeneratorDogboneII.py
Normal file
@@ -0,0 +1,354 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2022 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * *
|
||||
# * 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 Path
|
||||
import Path.Base.Generator.dogboneII as dogboneII
|
||||
import Path.Base.Language as PathLanguage
|
||||
import PathTests.PathTestUtils as PathTestUtils
|
||||
import math
|
||||
|
||||
|
||||
# Path.Log.setLevel(Path.Log.Level.DEBUG)
|
||||
Path.Log.setLevel(Path.Log.Level.NOTICE)
|
||||
|
||||
PI = math.pi
|
||||
DebugMode = Path.Log.getLevel(Path.Log.thisModule()) == Path.Log.Level.DEBUG
|
||||
|
||||
|
||||
def createKinks(maneuver):
|
||||
k = []
|
||||
moves = maneuver.getMoves()
|
||||
if moves:
|
||||
move0 = moves[0]
|
||||
prev = move0
|
||||
for m in moves[1:]:
|
||||
k.append(dogboneII.Kink(prev, m))
|
||||
prev = m
|
||||
if Path.Geom.pointsCoincide(move0.positionBegin(), prev.positionEnd()):
|
||||
k.append(dogboneII.Kink(prev, move0))
|
||||
return k
|
||||
|
||||
|
||||
def findDogboneKinks(maneuver, threshold):
|
||||
if threshold > 0:
|
||||
return [k for k in createKinks(maneuver) if k.deflection() > threshold]
|
||||
if threshold < 0:
|
||||
return [k for k in createKinks(maneuver) if k.deflection() < threshold]
|
||||
return createKinks(maneuver)
|
||||
|
||||
|
||||
def MNVR(gcode, begin=None):
|
||||
# 'turns out the replace() isn't really necessary
|
||||
# leave it here anyway for clarity
|
||||
return PathLanguage.Maneuver.FromGCode(gcode.replace("/", "\n"), begin)
|
||||
|
||||
|
||||
def INSTR(gcode, begin=None):
|
||||
return MNVR(gcode, begin).instr[0]
|
||||
|
||||
|
||||
def KINK(gcode, begin=None):
|
||||
maneuver = MNVR(gcode, begin)
|
||||
if len(maneuver.instr) != 2:
|
||||
return None
|
||||
return dogboneII.Kink(maneuver.instr[0], maneuver.instr[1])
|
||||
|
||||
|
||||
def GEN(generator, length):
|
||||
return generator(lambda k, a, n, c: n, length, 1)
|
||||
|
||||
|
||||
class TestGeneratorDogboneII(PathTestUtils.PathTestBase):
|
||||
"""Unit tests for the dogboneII generator."""
|
||||
|
||||
def assertKinks(self, maneuver, s):
|
||||
kinks = [f"{k.deflection():4.2f}" for k in createKinks(maneuver)]
|
||||
self.assertEqual(f"[{', '.join(kinks)}]", s)
|
||||
|
||||
def assertBones(self, maneuver, threshold, s):
|
||||
bones = [
|
||||
f"({int(b.x())},{int(b.y())})"
|
||||
for b in findDogboneKinks(maneuver, threshold)
|
||||
]
|
||||
self.assertEqual(f"[{', '.join(bones)}]", s)
|
||||
|
||||
def assertBone(self, bone, s, digits=0):
|
||||
if DebugMode and FreeCAD.GuiUp:
|
||||
Path.show(dogboneII.kink_to_path(bone.kink))
|
||||
FreeCAD.ActiveDocument.Objects[-1].Visibility = False
|
||||
Path.show(dogboneII.bone_to_path(bone))
|
||||
FreeCAD.ActiveDocument.Objects[-1].Visibility = False
|
||||
Path.Log.debug(f"{bone.kink} : {bone.angle / PI:.2f}")
|
||||
|
||||
b = [i.str(digits) for i in bone.instr]
|
||||
self.assertEqual(f"[{', '.join(b)}]", s)
|
||||
|
||||
def test20(self):
|
||||
"""Verify kinks of maneuvers"""
|
||||
self.assertKinks(MNVR("G1X1/G1Y1"), "[1.57]")
|
||||
self.assertKinks(MNVR("G1X1/G1Y-1"), "[-1.57]")
|
||||
self.assertKinks(MNVR("G1X1/G1Y1/G1X0"), "[1.57, 1.57]")
|
||||
self.assertKinks(MNVR("G1X1/G1Y1/G1X0/G1Y0"), "[1.57, 1.57, 1.57, 1.57]")
|
||||
|
||||
self.assertKinks(MNVR("G1Y1/G1X1"), "[-1.57]")
|
||||
self.assertKinks(MNVR("G1Y1/G1X1/G1Y0"), "[-1.57, -1.57]")
|
||||
self.assertKinks(MNVR("G1Y1/G1X1/G1Y0/G1X0"), "[-1.57, -1.57, -1.57, -1.57]")
|
||||
|
||||
# tangential arc moves
|
||||
self.assertKinks(MNVR("G1X1/G3Y2J1"), "[0.00]")
|
||||
self.assertKinks(MNVR("G1X1/G3Y2J1G1X0"), "[0.00, 0.00]")
|
||||
|
||||
# folding back arc moves
|
||||
self.assertKinks(MNVR("G1X1/G2Y2J1"), "[-3.14]")
|
||||
self.assertKinks(MNVR("G1X1/G2Y2J1G1X0"), "[-3.14, 3.14]")
|
||||
|
||||
def test30(self):
|
||||
"""Verify dogbone detection"""
|
||||
self.assertBones(
|
||||
MNVR("G1X1/G1Y1/G1X0/G1Y0"), PI / 4, "[(1,0), (1,1), (0,1), (0,0)]"
|
||||
)
|
||||
self.assertBones(MNVR("G1X1/G1Y1/G1X0/G1Y0"), -PI / 4, "[]")
|
||||
|
||||
# no bones on flat angle
|
||||
self.assertBones(MNVR("G1X1/G1X3Y1/G1X0/G1Y0"), PI / 4, "[(3,1), (0,1), (0,0)]")
|
||||
self.assertBones(MNVR("G1X1/G1X3Y1/G1X0/G1Y0"), -PI / 4, "[]")
|
||||
|
||||
# no bones on tangential arc
|
||||
self.assertBones(MNVR("G1X1/G3Y2J1/G1X0/G1Y0"), PI / 4, "[(0,2), (0,0)]")
|
||||
self.assertBones(MNVR("G1X1/G3Y2J1/G1X0/G1Y0"), -PI / 4, "[]")
|
||||
|
||||
# a bone on perpendicular arc
|
||||
self.assertBones(
|
||||
MNVR("G1X1/G3X3I1/G1Y1/G1X0/G1Y0"), PI / 4, "[(3,1), (0,1), (0,0)]"
|
||||
)
|
||||
self.assertBones(MNVR("G1X1/G3X3I1/G1Y1/G1X0/G1Y0"), -PI / 4, "[(1,0)]")
|
||||
|
||||
def test40(self):
|
||||
"""Verify horizontal t-bone creation"""
|
||||
# Uses test data from test30, if that broke, this can't succeed
|
||||
|
||||
horizontal = GEN(dogboneII.GeneratorTBoneHorizontal, 1)
|
||||
|
||||
# single move right
|
||||
maneuver = MNVR("G1X1/G1Y1")
|
||||
kinks = findDogboneKinks(maneuver, PI / 4)
|
||||
self.assertEqual(len(kinks), 1)
|
||||
k = kinks[0]
|
||||
p = k.position()
|
||||
self.assertEqual(f"({int(p.x)}, {int(p.y)})", "(1, 0)")
|
||||
bone = horizontal.generate(k)
|
||||
self.assertBone(bone, "[G1{X: 2}, G1{X: 1}]")
|
||||
|
||||
# full loop CCW
|
||||
kinks = findDogboneKinks(MNVR("G1X1/G1Y1/G1X0/G1Y0"), PI / 4)
|
||||
bones = [horizontal.generate(k) for k in kinks]
|
||||
self.assertEqual(len(bones), 4)
|
||||
self.assertBone(bones[0], "[G1{X: 2}, G1{X: 1}]")
|
||||
self.assertBone(bones[1], "[G1{X: 2}, G1{X: 1}]")
|
||||
self.assertBone(bones[2], "[G1{X: -1}, G1{X: 0}]")
|
||||
self.assertBone(bones[3], "[G1{X: -1}, G1{X: 0}]")
|
||||
|
||||
# single move left
|
||||
maneuver = MNVR("G1X1/G1Y-1")
|
||||
kinks = findDogboneKinks(maneuver, -PI / 4)
|
||||
self.assertEqual(len(kinks), 1)
|
||||
k = kinks[0]
|
||||
p = k.position()
|
||||
self.assertEqual(f"({int(p.x)}, {int(p.y)})", "(1, 0)")
|
||||
bone = horizontal.generate(k)
|
||||
self.assertBone(bone, "[G1{X: 2}, G1{X: 1}]")
|
||||
|
||||
# full loop CW
|
||||
kinks = findDogboneKinks(MNVR("G1X1/G1Y-1/G1X0/G1Y0"), -PI / 4)
|
||||
bones = [horizontal.generate(k) for k in kinks]
|
||||
self.assertEqual(len(bones), 4)
|
||||
self.assertBone(bones[0], "[G1{X: 2}, G1{X: 1}]")
|
||||
self.assertBone(bones[1], "[G1{X: 2}, G1{X: 1}]")
|
||||
self.assertBone(bones[2], "[G1{X: -1}, G1{X: 0}]")
|
||||
self.assertBone(bones[3], "[G1{X: -1}, G1{X: 0}]")
|
||||
|
||||
# bones on arcs
|
||||
kinks = findDogboneKinks(MNVR("G1X1/G3X3I1/G1Y1/G1X0/G1Y0"), PI / 4)
|
||||
bones = [horizontal.generate(k) for k in kinks]
|
||||
self.assertEqual(len(bones), 3)
|
||||
self.assertBone(bones[0], "[G1{X: 4}, G1{X: 3}]")
|
||||
self.assertBone(bones[1], "[G1{X: -1}, G1{X: 0}]")
|
||||
self.assertBone(bones[2], "[G1{X: -1}, G1{X: 0}]")
|
||||
|
||||
# bones on arcs
|
||||
kinks = findDogboneKinks(MNVR("G1X1/G3X3I1/G1Y1/G1X0/G1Y0"), -PI / 4)
|
||||
bones = [horizontal.generate(k) for k in kinks]
|
||||
self.assertEqual(len(bones), 1)
|
||||
self.assertBone(bones[0], "[G1{X: 2}, G1{X: 1}]")
|
||||
|
||||
def test50(self):
|
||||
"""Verify vertical t-bone creation"""
|
||||
# Uses test data from test30, if that broke, this can't succeed
|
||||
|
||||
vertical = GEN(dogboneII.GeneratorTBoneVertical, 1)
|
||||
|
||||
# single move right
|
||||
maneuver = MNVR("G1X1/G1Y1")
|
||||
kinks = findDogboneKinks(maneuver, PI / 4)
|
||||
self.assertEqual(len(kinks), 1)
|
||||
k = kinks[0]
|
||||
p = k.position()
|
||||
self.assertEqual(f"({int(p.x)}, {int(p.y)})", "(1, 0)")
|
||||
bone = vertical.generate(k)
|
||||
self.assertBone(bone, "[G1{Y: -1}, G1{Y: 0}]")
|
||||
|
||||
# full loop CCW
|
||||
kinks = findDogboneKinks(MNVR("G1X1/G1Y1/G1X0/G1Y0"), PI / 4)
|
||||
bones = [vertical.generate(k) for k in kinks]
|
||||
self.assertEqual(len(bones), 4)
|
||||
self.assertBone(bones[0], "[G1{Y: -1}, G1{Y: 0}]")
|
||||
self.assertBone(bones[1], "[G1{Y: 2}, G1{Y: 1}]")
|
||||
self.assertBone(bones[2], "[G1{Y: 2}, G1{Y: 1}]")
|
||||
self.assertBone(bones[3], "[G1{Y: -1}, G1{Y: 0}]")
|
||||
|
||||
# single move left
|
||||
maneuver = MNVR("G1X1/G1Y-1")
|
||||
kinks = findDogboneKinks(maneuver, -PI / 4)
|
||||
self.assertEqual(len(kinks), 1)
|
||||
k = kinks[0]
|
||||
p = k.position()
|
||||
self.assertEqual(f"({int(p.x)}, {int(p.y)})", "(1, 0)")
|
||||
bone = vertical.generate(k)
|
||||
self.assertBone(bone, "[G1{Y: 1}, G1{Y: 0}]")
|
||||
|
||||
# full loop CW
|
||||
kinks = findDogboneKinks(MNVR("G1X1/G1Y-1/G1X0/G1Y0"), -PI / 4)
|
||||
bones = [vertical.generate(k) for k in kinks]
|
||||
self.assertEqual(len(bones), 4)
|
||||
self.assertBone(bones[0], "[G1{Y: 1}, G1{Y: 0}]")
|
||||
self.assertBone(bones[1], "[G1{Y: -2}, G1{Y: -1}]")
|
||||
self.assertBone(bones[2], "[G1{Y: -2}, G1{Y: -1}]")
|
||||
self.assertBone(bones[3], "[G1{Y: 1}, G1{Y: 0}]")
|
||||
|
||||
# bones on arcs
|
||||
kinks = findDogboneKinks(MNVR("G1X1/G3X3I1/G1Y1/G1X0/G1Y0"), PI / 4)
|
||||
bones = [vertical.generate(k) for k in kinks]
|
||||
self.assertEqual(len(bones), 3)
|
||||
self.assertBone(bones[0], "[G1{Y: 2}, G1{Y: 1}]")
|
||||
self.assertBone(bones[1], "[G1{Y: 2}, G1{Y: 1}]")
|
||||
self.assertBone(bones[2], "[G1{Y: -1}, G1{Y: 0}]")
|
||||
|
||||
# bones on arcs
|
||||
kinks = findDogboneKinks(MNVR("G1X1/G3X3I1/G1Y1/G1X0/G1Y0"), -PI / 4)
|
||||
bones = [vertical.generate(k) for k in kinks]
|
||||
self.assertEqual(len(bones), 1)
|
||||
self.assertBone(bones[0], "[G1{Y: 1}, G1{Y: 0}]")
|
||||
|
||||
def test60(self):
|
||||
"""Verify t-bones on edges"""
|
||||
|
||||
on_short_1 = GEN(dogboneII.GeneratorTBoneOnShort, 1)
|
||||
on_short_5 = GEN(dogboneII.GeneratorTBoneOnShort, 5)
|
||||
|
||||
# horizontal short edge
|
||||
bone = on_short_1.generate(KINK("G1X1/G1Y2"))
|
||||
self.assertBone(bone, "[G1{Y: -1}, G1{Y: 0}]")
|
||||
|
||||
bone = on_short_1.generate(KINK("G1X-1/G1Y2"))
|
||||
self.assertBone(bone, "[G1{Y: -1}, G1{Y: 0}]")
|
||||
|
||||
# vertical short edge
|
||||
bone = on_short_1.generate(KINK("G1Y1/G1X2"))
|
||||
self.assertBone(bone, "[G1{X: -1}, G1{X: 0}]")
|
||||
|
||||
bone = on_short_1.generate(KINK("G1Y1/G1X-2"))
|
||||
self.assertBone(bone, "[G1{X: 1}, G1{X: 0}]")
|
||||
|
||||
# some other angle
|
||||
bone = on_short_5.generate(KINK("G1X1Y1/G1Y-1"))
|
||||
self.assertBone(bone, "[G1{X: -2.5, Y: 4.5}, G1{X: 1.0, Y: 1.0}]", 2)
|
||||
|
||||
bone = on_short_5.generate(KINK("G1X-1Y-1/G1Y1"))
|
||||
self.assertBone(bone, "[G1{X: 2.5, Y: -4.5}, G1{X: -1.0, Y: -1.0}]", 2)
|
||||
|
||||
# some other angle
|
||||
bone = on_short_5.generate(KINK("G1X2Y1/G1Y-3"))
|
||||
self.assertBone(bone, "[G1{X: -0.24, Y: 5.5}, G1{X: 2.0, Y: 1.0}]", 2)
|
||||
|
||||
bone = on_short_5.generate(KINK("G1X-2Y-1/G1Y3"))
|
||||
self.assertBone(bone, "[G1{X: 0.24, Y: -5.5}, G1{X: -2.0, Y: -1.0}]", 2)
|
||||
|
||||
# short edge - the 2nd
|
||||
bone = on_short_1.generate(KINK("G1Y2/G1X1"))
|
||||
self.assertBone(bone, "[G1{Y: 3}, G1{Y: 2}]")
|
||||
bone = on_short_1.generate(KINK("G1Y2/G1X-1"))
|
||||
self.assertBone(bone, "[G1{Y: 3}, G1{Y: 2}]")
|
||||
|
||||
bone = on_short_5.generate(KINK("G1Y-3/G1X2Y-2"))
|
||||
self.assertBone(bone, "[G1{X: 2.2, Y: -7.5}, G1{X: 0.0, Y: -3.0}]", 2)
|
||||
|
||||
bone = on_short_5.generate(KINK("G1Y3/G1X-2Y2"))
|
||||
self.assertBone(bone, "[G1{X: -2.2, Y: 7.5}, G1{X: 0.0, Y: 3.0}]", 2)
|
||||
|
||||
# long edge
|
||||
on_long_1 = GEN(dogboneII.GeneratorTBoneOnLong, 1)
|
||||
on_long_5 = GEN(dogboneII.GeneratorTBoneOnLong, 5)
|
||||
|
||||
bone = on_long_1.generate(
|
||||
KINK("G1X2/G1Y1"),
|
||||
)
|
||||
self.assertBone(bone, "[G1{Y: -1}, G1{Y: 0}]")
|
||||
bone = on_long_1.generate(KINK("G1X-2/G1Y1"))
|
||||
self.assertBone(bone, "[G1{Y: -1}, G1{Y: 0}]")
|
||||
|
||||
bone = on_long_5.generate(KINK("G1Y-1/G1X2Y0"))
|
||||
self.assertBone(bone, "[G1{X: 2.2, Y: -5.5}, G1{X: 0.0, Y: -1.0}]", 2)
|
||||
|
||||
bone = on_long_5.generate(KINK("G1Y1/G1X-2Y0"))
|
||||
self.assertBone(bone, "[G1{X: -2.2, Y: 5.5}, G1{X: 0.0, Y: 1.0}]", 2)
|
||||
|
||||
def test70(self):
|
||||
"""Verify dogbone angles"""
|
||||
self.assertRoughly(180 * KINK("G1X1/G1Y+1").normAngle() / PI, -45)
|
||||
self.assertRoughly(180 * KINK("G1X1/G1Y-1").normAngle() / PI, 45)
|
||||
|
||||
self.assertRoughly(180 * KINK("G1X1/G1X2Y1").normAngle() / PI, -67.5)
|
||||
self.assertRoughly(180 * KINK("G1X1/G1X2Y-1").normAngle() / PI, 67.5)
|
||||
|
||||
self.assertRoughly(180 * KINK("G1Y1/G1X+1").normAngle() / PI, 135)
|
||||
self.assertRoughly(180 * KINK("G1Y1/G1X-1").normAngle() / PI, 45)
|
||||
|
||||
self.assertRoughly(180 * KINK("G1X-1/G1Y+1").normAngle() / PI, -135)
|
||||
self.assertRoughly(180 * KINK("G1X-1/G1Y-1").normAngle() / PI, 135)
|
||||
|
||||
self.assertRoughly(180 * KINK("G1Y-1/G1X-1").normAngle() / PI, -45)
|
||||
self.assertRoughly(180 * KINK("G1Y-1/G1X+1").normAngle() / PI, -135)
|
||||
|
||||
def test71(self):
|
||||
"""Verify dogbones"""
|
||||
|
||||
dogbone = GEN(dogboneII.GeneratorDogbone, 1)
|
||||
|
||||
bone = dogbone.generate(KINK("G1X1/G1Y1"))
|
||||
self.assertBone(bone, "[G1{X: 1.7, Y: -0.71}, G1{X: 1.0, Y: 0.0}]", 2)
|
||||
|
||||
bone = dogbone.generate(KINK("G1X1/G1X3Y-1"))
|
||||
self.assertBone(bone, "[G1{X: 1.2, Y: 0.97}, G1{X: 1.0, Y: 0.0}]", 2)
|
||||
|
||||
bone = dogbone.generate(KINK("G1X1Y1/G1X2"))
|
||||
self.assertBone(bone, "[G1{X: 0.62, Y: 1.9}, G1{X: 1.0, Y: 1.0}]", 2)
|
||||
@@ -89,22 +89,28 @@ class TestPathGeom(PathTestBase):
|
||||
)
|
||||
|
||||
self.assertRoughly(
|
||||
Path.Geom.diffAngle(+math.pi / 4, +0 * math.pi / 4, "CCW") / math.pi, 7 / 4.0
|
||||
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, "CCW") / math.pi, 2 / 4.0
|
||||
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, "CCW") / math.pi, 6 / 4.0
|
||||
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, "CCW") / math.pi, 1 / 4.0
|
||||
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, "CCW") / math.pi, 4 / 4.0
|
||||
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, "CCW") / math.pi, 0 / 4.0
|
||||
Path.Geom.diffAngle(-math.pi / 4, -1 * math.pi / 4, "CCW") / math.pi,
|
||||
0 / 4.0,
|
||||
)
|
||||
|
||||
def test02(self):
|
||||
@@ -607,7 +613,9 @@ class TestPathGeom(PathTestBase):
|
||||
|
||||
def cmds(center, radius, up=True):
|
||||
norm = Vector(0, 0, 1) if up else Vector(0, 0, -1)
|
||||
return Path.Geom.cmdsForEdge(Part.Edge(Part.Circle(center, norm, radius)))[0]
|
||||
return Path.Geom.cmdsForEdge(Part.Edge(Part.Circle(center, norm, radius)))[
|
||||
0
|
||||
]
|
||||
|
||||
def cmd(g, end, off):
|
||||
return Path.Command(
|
||||
|
||||
@@ -41,6 +41,7 @@ def createTool(name="t1", diameter=1.75):
|
||||
}
|
||||
return PathToolBit.Factory.CreateFromAttrs(attrs, name)
|
||||
|
||||
|
||||
class TestPathHelpers(PathTestBase):
|
||||
def setUp(self):
|
||||
self.doc = FreeCAD.newDocument("TestPathUtils")
|
||||
@@ -133,7 +134,9 @@ class TestPathHelpers(PathTestBase):
|
||||
self.assertTrue(len(results) == 2)
|
||||
e1 = results[0]
|
||||
self.assertTrue(isinstance(e1.Curve, Part.Circle))
|
||||
self.assertTrue(Path.Geom.pointsCoincide(edge.Curve.Location, e1.Curve.Location))
|
||||
self.assertTrue(
|
||||
Path.Geom.pointsCoincide(edge.Curve.Location, e1.Curve.Location)
|
||||
)
|
||||
self.assertTrue(edge.Curve.Radius == e1.Curve.Radius)
|
||||
|
||||
# filter a 180 degree arc
|
||||
|
||||
124
src/Mod/Path/PathTests/TestPathLanguage.py
Normal file
124
src/Mod/Path/PathTests/TestPathLanguage.py
Normal file
@@ -0,0 +1,124 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2022 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * *
|
||||
# * 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 Path.Base.Language as PathLanguage
|
||||
import PathTests.PathTestUtils as PathTestUtils
|
||||
import math
|
||||
|
||||
PI = math.pi
|
||||
|
||||
|
||||
def MNVR(gcode, begin=None):
|
||||
# 'turns out the replace() isn't really necessary
|
||||
# leave it here anyway for clarity
|
||||
return PathLanguage.Maneuver.FromGCode(gcode.replace("/", "\n"), begin)
|
||||
|
||||
|
||||
def INSTR(gcode, begin=None):
|
||||
return MNVR(gcode, begin).instr[0]
|
||||
|
||||
|
||||
class TestPathLanguage(PathTestUtils.PathTestBase):
|
||||
"""Unit tests for the Language classes."""
|
||||
|
||||
def assertTangents(self, instr, t1):
|
||||
"""Assert that the two tangent angles are identical"""
|
||||
t0 = instr.anglesOfTangents()
|
||||
self.assertRoughly(t0[0], t1[0])
|
||||
self.assertRoughly(t0[1], t1[1])
|
||||
|
||||
def test00(self):
|
||||
"""Verify G0 instruction construction"""
|
||||
self.assertEqual(str(MNVR("")), "")
|
||||
self.assertEqual(len(MNVR("").instr), 0)
|
||||
|
||||
self.assertEqual(str(MNVR("G0")), "G0{}")
|
||||
self.assertEqual(str(MNVR("G0X3")), "G0{'X': 3.0}")
|
||||
self.assertEqual(str(MNVR("G0X3Y7")), "G0{'X': 3.0, 'Y': 7.0}")
|
||||
self.assertEqual(
|
||||
str(MNVR("G0X3Y7/G0Z0")), "G0{'X': 3.0, 'Y': 7.0}\nG0{'Z': 0.0}"
|
||||
)
|
||||
self.assertEqual(len(MNVR("G0X3Y7").instr), 1)
|
||||
self.assertEqual(len(MNVR("G0X3Y7/G0Z0").instr), 2)
|
||||
self.assertEqual(type(MNVR("G0X3Y7").instr[0]), PathLanguage.MoveStraight)
|
||||
|
||||
def test10(self):
|
||||
"""Verify G1 instruction construction"""
|
||||
self.assertEqual(str(MNVR("G1")), "G1{}")
|
||||
self.assertEqual(str(MNVR("G1X3")), "G1{'X': 3.0}")
|
||||
self.assertEqual(str(MNVR("G1X3Y7")), "G1{'X': 3.0, 'Y': 7.0}")
|
||||
self.assertEqual(
|
||||
str(MNVR("G1X3Y7/G1Z0")), "G1{'X': 3.0, 'Y': 7.0}\nG1{'Z': 0.0}"
|
||||
)
|
||||
self.assertEqual(len(MNVR("G1X3Y7").instr), 1)
|
||||
self.assertEqual(len(MNVR("G1X3Y7/G1Z0").instr), 2)
|
||||
self.assertEqual(type(MNVR("G1X3Y7").instr[0]), PathLanguage.MoveStraight)
|
||||
|
||||
def test20(self):
|
||||
"""Verify G2 instruction construction"""
|
||||
self.assertEqual(str(MNVR("G2X2Y2I1")), "G2{'I': 1.0, 'X': 2.0, 'Y': 2.0}")
|
||||
self.assertEqual(len(MNVR("G2X2Y2I1").instr), 1)
|
||||
self.assertEqual(type(MNVR("G2X2Y2I1").instr[0]), PathLanguage.MoveArcCW)
|
||||
|
||||
def test30(self):
|
||||
"""Verify G3 instruction construction"""
|
||||
self.assertEqual(str(MNVR("G3X2Y2I1")), "G3{'I': 1.0, 'X': 2.0, 'Y': 2.0}")
|
||||
self.assertEqual(len(MNVR("G3X2Y2I1").instr), 1)
|
||||
self.assertEqual(type(MNVR("G3X2Y2I1").instr[0]), PathLanguage.MoveArcCCW)
|
||||
|
||||
def test40(self):
|
||||
"""Verify pathLength correctness"""
|
||||
self.assertRoughly(MNVR("G1X3").instr[0].pathLength(), 3)
|
||||
self.assertRoughly(MNVR("G1X-7").instr[0].pathLength(), 7)
|
||||
self.assertRoughly(MNVR("G1X3").instr[0].pathLength(), 3)
|
||||
|
||||
self.assertRoughly(MNVR("G1X3Y4").instr[0].pathLength(), 5)
|
||||
self.assertRoughly(MNVR("G1X3Y-4").instr[0].pathLength(), 5)
|
||||
self.assertRoughly(MNVR("G1X-3Y-4").instr[0].pathLength(), 5)
|
||||
self.assertRoughly(MNVR("G1X-3Y4").instr[0].pathLength(), 5)
|
||||
|
||||
self.assertRoughly(MNVR("G2X2I1").instr[0].pathLength(), PI)
|
||||
self.assertRoughly(MNVR("G2X1Y1I1").instr[0].pathLength(), PI / 2)
|
||||
|
||||
self.assertRoughly(MNVR("G3X2I1").instr[0].pathLength(), PI)
|
||||
self.assertRoughly(MNVR("G3X1Y1I1").instr[0].pathLength(), 3 * PI / 2)
|
||||
|
||||
def test50(self):
|
||||
"""Verify tangents of moves."""
|
||||
|
||||
self.assertTangents(INSTR("G1 X0 Y0"), (0, 0)) # by declaration
|
||||
self.assertTangents(INSTR("G1 X1 Y0"), (0, 0))
|
||||
self.assertTangents(INSTR("G1 X-1 Y0"), (PI, PI))
|
||||
self.assertTangents(INSTR("G1 X0 Y1"), (PI / 2, PI / 2))
|
||||
self.assertTangents(INSTR("G1 X0 Y-1"), (-PI / 2, -PI / 2))
|
||||
self.assertTangents(INSTR("G1 X1 Y1"), (PI / 4, PI / 4))
|
||||
self.assertTangents(INSTR("G1 X-1 Y1"), (3 * PI / 4, 3 * PI / 4))
|
||||
self.assertTangents(INSTR("G1 X-1 Y -1"), (-3 * PI / 4, -3 * PI / 4))
|
||||
self.assertTangents(INSTR("G1 X1 Y-1"), (-PI / 4, -PI / 4))
|
||||
|
||||
self.assertTangents(INSTR("G2 X2 Y0 I1 J0"), (PI / 2, -PI / 2))
|
||||
self.assertTangents(INSTR("G2 X2 Y2 I1 J1"), (3 * PI / 4, -PI / 4))
|
||||
self.assertTangents(INSTR("G2 X0 Y-2 I0 J-1"), (0, -PI))
|
||||
|
||||
self.assertTangents(INSTR("G3 X2 Y0 I1 J0"), (-PI / 2, PI / 2))
|
||||
self.assertTangents(INSTR("G3 X2 Y2 I1 J1"), (-PI / 4, 3 * PI / 4))
|
||||
self.assertTangents(INSTR("G3 X0 Y-2 I0 J-1"), (PI, 0))
|
||||
@@ -363,9 +363,7 @@ class TestPathOpUtil(PathTestUtils.PathTestBase):
|
||||
self.assertRoughly(33, edge.Curve.Radius)
|
||||
|
||||
# the other way around everything's the same except the axis is negative
|
||||
wire = PathOpUtil.offsetWire(
|
||||
getWire(obj.Tool), getPositiveShape(obj), 3, False
|
||||
)
|
||||
wire = PathOpUtil.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, False)
|
||||
self.assertEqual(1, len(wire.Edges))
|
||||
edge = wire.Edges[0]
|
||||
self.assertCoincide(Vector(), edge.Curve.Center)
|
||||
@@ -394,9 +392,7 @@ class TestPathOpUtil(PathTestUtils.PathTestBase):
|
||||
self.assertTrue(PathOpUtil.isWireClockwise(wire))
|
||||
|
||||
# change offset orientation
|
||||
wire = PathOpUtil.offsetWire(
|
||||
getWire(obj.Tool), getPositiveShape(obj), 3, False
|
||||
)
|
||||
wire = PathOpUtil.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, False)
|
||||
self.assertEqual(8, len(wire.Edges))
|
||||
self.assertEqual(4, len([e for e in wire.Edges if Part.Line == type(e.Curve)]))
|
||||
self.assertEqual(
|
||||
@@ -432,9 +428,7 @@ class TestPathOpUtil(PathTestUtils.PathTestBase):
|
||||
self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis)
|
||||
|
||||
# change offset orientation
|
||||
wire = PathOpUtil.offsetWire(
|
||||
getWire(obj.Tool), getPositiveShape(obj), 3, False
|
||||
)
|
||||
wire = PathOpUtil.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, False)
|
||||
self.assertEqual(6, len(wire.Edges))
|
||||
self.assertEqual(3, len([e for e in wire.Edges if Part.Line == type(e.Curve)]))
|
||||
self.assertEqual(
|
||||
@@ -467,9 +461,7 @@ class TestPathOpUtil(PathTestUtils.PathTestBase):
|
||||
self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis)
|
||||
|
||||
# change offset orientation
|
||||
wire = PathOpUtil.offsetWire(
|
||||
getWire(obj.Tool), getPositiveShape(obj), 3, False
|
||||
)
|
||||
wire = PathOpUtil.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, False)
|
||||
self.assertEqual(6, len(wire.Edges))
|
||||
self.assertEqual(3, len([e for e in wire.Edges if Part.Line == type(e.Curve)]))
|
||||
self.assertEqual(
|
||||
@@ -494,9 +486,7 @@ class TestPathOpUtil(PathTestUtils.PathTestBase):
|
||||
self.assertRoughly(27, edge.Curve.Radius)
|
||||
|
||||
# the other way around everything's the same except the axis is negative
|
||||
wire = PathOpUtil.offsetWire(
|
||||
getWire(obj.Tool), getNegativeShape(obj), 3, False
|
||||
)
|
||||
wire = PathOpUtil.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, False)
|
||||
self.assertEqual(1, len(wire.Edges))
|
||||
edge = wire.Edges[0]
|
||||
self.assertCoincide(Vector(), edge.Curve.Center)
|
||||
@@ -518,9 +508,7 @@ class TestPathOpUtil(PathTestUtils.PathTestBase):
|
||||
self.assertFalse(PathOpUtil.isWireClockwise(wire))
|
||||
|
||||
# change offset orientation
|
||||
wire = PathOpUtil.offsetWire(
|
||||
getWire(obj.Tool), getNegativeShape(obj), 3, False
|
||||
)
|
||||
wire = PathOpUtil.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, False)
|
||||
self.assertEqual(4, len(wire.Edges))
|
||||
self.assertEqual(4, len([e for e in wire.Edges if Part.Line == type(e.Curve)]))
|
||||
for e in wire.Edges:
|
||||
@@ -543,9 +531,7 @@ class TestPathOpUtil(PathTestUtils.PathTestBase):
|
||||
self.assertFalse(PathOpUtil.isWireClockwise(wire))
|
||||
|
||||
# change offset orientation
|
||||
wire = PathOpUtil.offsetWire(
|
||||
getWire(obj.Tool), getNegativeShape(obj), 3, False
|
||||
)
|
||||
wire = PathOpUtil.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, False)
|
||||
self.assertEqual(3, len(wire.Edges))
|
||||
self.assertEqual(3, len([e for e in wire.Edges if Part.Line == type(e.Curve)]))
|
||||
for e in wire.Edges:
|
||||
@@ -572,9 +558,7 @@ class TestPathOpUtil(PathTestUtils.PathTestBase):
|
||||
self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis)
|
||||
|
||||
# change offset orientation
|
||||
wire = PathOpUtil.offsetWire(
|
||||
getWire(obj.Tool), getNegativeShape(obj), 3, False
|
||||
)
|
||||
wire = PathOpUtil.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, False)
|
||||
self.assertEqual(6, len(wire.Edges))
|
||||
self.assertEqual(3, len([e for e in wire.Edges if Part.Line == type(e.Curve)]))
|
||||
self.assertEqual(
|
||||
|
||||
@@ -39,7 +39,9 @@ class TestPathPreferences(PathTestUtils.PathTestBase):
|
||||
def test02(self):
|
||||
"""Path/Post/scripts is part of the posts search path."""
|
||||
paths = Path.Preferences.searchPathsPost()
|
||||
self.assertEqual(len([p for p in paths if p.endswith("/Path/Post/scripts/")]), 1)
|
||||
self.assertEqual(
|
||||
len([p for p in paths if p.endswith("/Path/Post/scripts/")]), 1
|
||||
)
|
||||
|
||||
def test03(self):
|
||||
"""Available post processors include linuxcnc, grbl and opensbp."""
|
||||
@@ -51,7 +53,9 @@ class TestPathPreferences(PathTestUtils.PathTestBase):
|
||||
def test10(self):
|
||||
"""Default paths for tools are resolved correctly"""
|
||||
|
||||
self.assertTrue(Path.Preferences.pathDefaultToolsPath().endswith("/Path/Tools/"))
|
||||
self.assertTrue(
|
||||
Path.Preferences.pathDefaultToolsPath().endswith("/Path/Tools/")
|
||||
)
|
||||
self.assertTrue(
|
||||
Path.Preferences.pathDefaultToolsPath("Bit").endswith("/Path/Tools/Bit")
|
||||
)
|
||||
|
||||
@@ -26,7 +26,7 @@ import Path.Base.SetupSheet as PathSetupSheet
|
||||
import json
|
||||
import sys
|
||||
|
||||
#Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule())
|
||||
# Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule())
|
||||
|
||||
from PathTests.PathTestUtils import PathTestBase
|
||||
|
||||
@@ -34,14 +34,15 @@ from PathTests.PathTestUtils import PathTestBase
|
||||
def refstring(string):
|
||||
return string.replace(" u'", " '")
|
||||
|
||||
class SomeOp (object):
|
||||
|
||||
class SomeOp(object):
|
||||
def __init__(self, obj):
|
||||
Path.Log.track(obj, type(obj))
|
||||
obj.addProperty('App::PropertyPercent', 'StepOver', 'Base', 'Some help you are')
|
||||
obj.addProperty("App::PropertyPercent", "StepOver", "Base", "Some help you are")
|
||||
|
||||
@classmethod
|
||||
def SetupProperties(cls):
|
||||
return ['StepOver']
|
||||
return ["StepOver"]
|
||||
|
||||
@classmethod
|
||||
def Create(cls, name, obj=None, parentJob=None):
|
||||
@@ -51,6 +52,7 @@ class SomeOp (object):
|
||||
obj.Proxy = SomeOp(obj)
|
||||
return obj
|
||||
|
||||
|
||||
class TestPathSetupSheet(PathTestBase):
|
||||
def setUp(self):
|
||||
self.doc = FreeCAD.newDocument("TestPathSetupSheet")
|
||||
@@ -321,28 +323,32 @@ class TestPathSetupSheet(PathTestBase):
|
||||
def test20(self):
|
||||
"""Verify SetupSheet template op attributes roundtrip."""
|
||||
|
||||
opname = 'whoop'
|
||||
opname = "whoop"
|
||||
|
||||
o1 = PathSetupSheet.Create()
|
||||
|
||||
PathSetupSheet.RegisterOperation(opname, SomeOp.Create, SomeOp.SetupProperties)
|
||||
ptt = PathSetupSheet._RegisteredOps[opname].prototype('whoopsy')
|
||||
pptt = ptt.getProperty('StepOver')
|
||||
pptt.setupProperty(o1, PathSetupSheet.OpPropertyName(opname, pptt.name), PathSetupSheet.OpPropertyGroup(opname), 75)
|
||||
ptt = PathSetupSheet._RegisteredOps[opname].prototype("whoopsy")
|
||||
pptt = ptt.getProperty("StepOver")
|
||||
pptt.setupProperty(
|
||||
o1,
|
||||
PathSetupSheet.OpPropertyName(opname, pptt.name),
|
||||
PathSetupSheet.OpPropertyGroup(opname),
|
||||
75,
|
||||
)
|
||||
|
||||
# save setup sheet in json "file"
|
||||
attrs = o1.Proxy.templateAttributes(False, False, False, False, [opname])
|
||||
encdd = o1.Proxy.encodeTemplateAttributes(attrs)
|
||||
j1 = json.dumps({'SetupSheet' : encdd}, sort_keys=True, indent=2)
|
||||
j1 = json.dumps({"SetupSheet": encdd}, sort_keys=True, indent=2)
|
||||
|
||||
# restore setup sheet from json "file"
|
||||
j2 = json.loads(j1)
|
||||
|
||||
o2 = PathSetupSheet.Create()
|
||||
o2.Proxy.setFromTemplate(j2['SetupSheet'])
|
||||
o2.Proxy.setFromTemplate(j2["SetupSheet"])
|
||||
|
||||
op = SomeOp.Create(opname)
|
||||
self.assertEqual(op.StepOver, 0)
|
||||
o2.Proxy.setOperationProperties(op, opname)
|
||||
self.assertEqual(op.StepOver, 75)
|
||||
|
||||
|
||||
@@ -29,13 +29,13 @@ from PathTests.PathTestUtils import PathTestBase
|
||||
|
||||
|
||||
class TestObject(object):
|
||||
|
||||
def __init__(self, orientation, direction, zTop, zBottom):
|
||||
self.ThreadOrientation = orientation
|
||||
self.Direction = direction
|
||||
self.StartDepth = FreeCAD.Units.Quantity(zTop, FreeCAD.Units.Length)
|
||||
self.FinalDepth = FreeCAD.Units.Quantity(zBottom, FreeCAD.Units.Length)
|
||||
|
||||
|
||||
def radii(internal, major, minor, toolDia, toolCrest):
|
||||
"""test radii function for simple testing"""
|
||||
if internal:
|
||||
@@ -56,13 +56,17 @@ class TestPathThreadMilling(PathTestBase):
|
||||
self.assertRoughly(have[i], want[i])
|
||||
|
||||
def assertSetupInternal(self, obj, c, begin, end):
|
||||
cmd, zBegin, zEnd = PathThreadMilling.threadSetupInternal(obj, obj.StartDepth.Value, obj.FinalDepth.Value)
|
||||
cmd, zBegin, zEnd = PathThreadMilling.threadSetupInternal(
|
||||
obj, obj.StartDepth.Value, obj.FinalDepth.Value
|
||||
)
|
||||
self.assertEqual(cmd, c)
|
||||
self.assertEqual(zBegin, begin)
|
||||
self.assertEqual(zEnd, end)
|
||||
|
||||
def assertSetupExternal(self, obj, c, begin, end):
|
||||
cmd, zBegin, zEnd = PathThreadMilling.threadSetupExternal(obj, obj.StartDepth.Value, obj.FinalDepth.Value)
|
||||
cmd, zBegin, zEnd = PathThreadMilling.threadSetupExternal(
|
||||
obj, obj.StartDepth.Value, obj.FinalDepth.Value
|
||||
)
|
||||
self.assertEqual(cmd, c)
|
||||
self.assertEqual(zBegin, begin)
|
||||
self.assertEqual(zEnd, end)
|
||||
@@ -120,30 +124,45 @@ class TestPathThreadMilling(PathTestBase):
|
||||
|
||||
hand = PathThreadMilling.RightHand
|
||||
|
||||
self.assertSetupInternal(TestObject(hand, PathThreadMilling.DirectionConventional, 1, 0), "G2", 1, 0)
|
||||
self.assertSetupInternal(TestObject(hand, PathThreadMilling.DirectionClimb, 1, 0), "G3", 0, 1)
|
||||
self.assertSetupInternal(
|
||||
TestObject(hand, PathThreadMilling.DirectionConventional, 1, 0), "G2", 1, 0
|
||||
)
|
||||
self.assertSetupInternal(
|
||||
TestObject(hand, PathThreadMilling.DirectionClimb, 1, 0), "G3", 0, 1
|
||||
)
|
||||
|
||||
def test41(self):
|
||||
"""Verify internal left hand thread setup."""
|
||||
|
||||
hand = PathThreadMilling.LeftHand
|
||||
|
||||
self.assertSetupInternal(TestObject(hand, PathThreadMilling.DirectionConventional, 1, 0), "G2", 0, 1)
|
||||
self.assertSetupInternal(TestObject(hand, PathThreadMilling.DirectionClimb, 1, 0), "G3", 1, 0)
|
||||
self.assertSetupInternal(
|
||||
TestObject(hand, PathThreadMilling.DirectionConventional, 1, 0), "G2", 0, 1
|
||||
)
|
||||
self.assertSetupInternal(
|
||||
TestObject(hand, PathThreadMilling.DirectionClimb, 1, 0), "G3", 1, 0
|
||||
)
|
||||
|
||||
def test50(self):
|
||||
"""Verify exteranl right hand thread setup."""
|
||||
|
||||
hand = PathThreadMilling.RightHand
|
||||
|
||||
self.assertSetupExternal(TestObject(hand, PathThreadMilling.DirectionClimb, 1, 0), "G2", 1, 0)
|
||||
self.assertSetupExternal(TestObject(hand, PathThreadMilling.DirectionConventional, 1, 0), "G3", 0, 1)
|
||||
self.assertSetupExternal(
|
||||
TestObject(hand, PathThreadMilling.DirectionClimb, 1, 0), "G2", 1, 0
|
||||
)
|
||||
self.assertSetupExternal(
|
||||
TestObject(hand, PathThreadMilling.DirectionConventional, 1, 0), "G3", 0, 1
|
||||
)
|
||||
|
||||
def test51(self):
|
||||
"""Verify exteranl left hand thread setup."""
|
||||
|
||||
hand = PathThreadMilling.LeftHand
|
||||
|
||||
self.assertSetupExternal(TestObject(hand, PathThreadMilling.DirectionClimb, 1, 0), "G2", 0, 1)
|
||||
self.assertSetupExternal(TestObject(hand, PathThreadMilling.DirectionConventional, 1, 0), "G3", 1, 0)
|
||||
|
||||
self.assertSetupExternal(
|
||||
TestObject(hand, PathThreadMilling.DirectionClimb, 1, 0), "G2", 0, 1
|
||||
)
|
||||
self.assertSetupExternal(
|
||||
TestObject(hand, PathThreadMilling.DirectionConventional, 1, 0), "G3", 1, 0
|
||||
)
|
||||
|
||||
@@ -253,7 +253,9 @@ G21
|
||||
"""
|
||||
self.docobj.Path = Path.Path([])
|
||||
postables = [self.docobj]
|
||||
gcode: str = postprocessor.export(postables, "gcode.tmp", "--output_all_arguments")
|
||||
gcode: str = postprocessor.export(
|
||||
postables, "gcode.tmp", "--output_all_arguments"
|
||||
)
|
||||
# The argparse help routine turns out to be sensitive to the
|
||||
# number of columns in the terminal window that the tests
|
||||
# are run from. This affects the indenting in the output.
|
||||
@@ -315,7 +317,9 @@ G21
|
||||
|
||||
def test00120(self):
|
||||
"""Test axis-precision."""
|
||||
self.compare_third_line("G0 X10 Y20 Z30", "G0 X10.00 Y20.00 Z30.00", "--axis-precision=2")
|
||||
self.compare_third_line(
|
||||
"G0 X10 Y20 Z30", "G0 X10.00 Y20.00 Z30.00", "--axis-precision=2"
|
||||
)
|
||||
|
||||
def test00130(self):
|
||||
"""Test comments."""
|
||||
@@ -605,26 +609,42 @@ G21
|
||||
"""Test G10 command Generation."""
|
||||
self.compare_third_line("G10 L1 P2 Z1.23456", "G10 L1 Z1.235 P2", "")
|
||||
self.compare_third_line(
|
||||
"G10 L1 P2 R1.23456 I2.34567 J3.456789 Q3", "G10 L1 I2.346 J3.457 R1.235 P2 Q3", ""
|
||||
"G10 L1 P2 R1.23456 I2.34567 J3.456789 Q3",
|
||||
"G10 L1 I2.346 J3.457 R1.235 P2 Q3",
|
||||
"",
|
||||
)
|
||||
self.compare_third_line(
|
||||
"G10 L2 P3 X1.23456 Y2.34567 Z3.456789", "G10 L2 X1.235 Y2.346 Z3.457 P3", ""
|
||||
)
|
||||
self.compare_third_line("G10 L2 P0 X0 Y0 Z0", "G10 L2 X0.000 Y0.000 Z0.000 P0", "")
|
||||
self.compare_third_line(
|
||||
"G10 L10 P1 X1.23456 Y2.34567 Z3.456789", "G10 L10 X1.235 Y2.346 Z3.457 P1", ""
|
||||
"G10 L2 P3 X1.23456 Y2.34567 Z3.456789",
|
||||
"G10 L2 X1.235 Y2.346 Z3.457 P3",
|
||||
"",
|
||||
)
|
||||
self.compare_third_line(
|
||||
"G10 L10 P2 R1.23456 I2.34567 J3.456789 Q3", "G10 L10 I2.346 J3.457 R1.235 P2 Q3", ""
|
||||
"G10 L2 P0 X0 Y0 Z0", "G10 L2 X0.000 Y0.000 Z0.000 P0", ""
|
||||
)
|
||||
self.compare_third_line(
|
||||
"G10 L11 P1 X1.23456 Y2.34567 Z3.456789", "G10 L11 X1.235 Y2.346 Z3.457 P1", ""
|
||||
"G10 L10 P1 X1.23456 Y2.34567 Z3.456789",
|
||||
"G10 L10 X1.235 Y2.346 Z3.457 P1",
|
||||
"",
|
||||
)
|
||||
self.compare_third_line(
|
||||
"G10 L11 P2 R1.23456 I2.34567 J3.456789 Q3", "G10 L11 I2.346 J3.457 R1.235 P2 Q3", ""
|
||||
"G10 L10 P2 R1.23456 I2.34567 J3.456789 Q3",
|
||||
"G10 L10 I2.346 J3.457 R1.235 P2 Q3",
|
||||
"",
|
||||
)
|
||||
self.compare_third_line(
|
||||
"G10 L20 P9 X1.23456 Y2.34567 Z3.456789", "G10 L20 X1.235 Y2.346 Z3.457 P9", ""
|
||||
"G10 L11 P1 X1.23456 Y2.34567 Z3.456789",
|
||||
"G10 L11 X1.235 Y2.346 Z3.457 P1",
|
||||
"",
|
||||
)
|
||||
self.compare_third_line(
|
||||
"G10 L11 P2 R1.23456 I2.34567 J3.456789 Q3",
|
||||
"G10 L11 I2.346 J3.457 R1.235 P2 Q3",
|
||||
"",
|
||||
)
|
||||
self.compare_third_line(
|
||||
"G10 L20 P9 X1.23456 Y2.34567 Z3.456789",
|
||||
"G10 L20 X1.235 Y2.346 Z3.457 P9",
|
||||
"",
|
||||
)
|
||||
|
||||
def test01170(self):
|
||||
@@ -779,7 +799,9 @@ G21
|
||||
Path.Command(
|
||||
"G52 X1.234567 Y2.345678 Z3.456789 A4.567891 B5.678912 C6.789123 U7.891234 V8.912345 W9.123456"
|
||||
),
|
||||
Path.Command("G52 X0 Y0.0 Z0.00 A0.000 B0.0000 C0.00000 U0.000000 V0 W0"),
|
||||
Path.Command(
|
||||
"G52 X0 Y0.0 Z0.00 A0.000 B0.0000 C0.00000 U0.000000 V0 W0"
|
||||
),
|
||||
],
|
||||
"""G90
|
||||
G21
|
||||
@@ -793,7 +815,9 @@ G52 X0.000 Y0.000 Z0.000 A0.000 B0.000 C0.000 U0.000 V0.000 W0.000
|
||||
Path.Command(
|
||||
"G52 X1.234567 Y2.345678 Z3.456789 A4.567891 B5.678912 C6.789123 U7.891234 V8.912345 W9.123456"
|
||||
),
|
||||
Path.Command("G52 X0 Y0.0 Z0.00 A0.000 B0.0000 C0.00000 U0.000000 V0 W0"),
|
||||
Path.Command(
|
||||
"G52 X0 Y0.0 Z0.00 A0.000 B0.0000 C0.00000 U0.000000 V0 W0"
|
||||
),
|
||||
],
|
||||
"""G90
|
||||
G20
|
||||
@@ -916,7 +940,9 @@ G52 X0.0000 Y0.0000 Z0.0000 A0.0000 B0.0000 C0.0000 U0.0000 V0.0000 W0.0000
|
||||
self.compare_third_line("G64", "G64", "")
|
||||
self.compare_third_line("G64 P3.456789", "G64 P3.457", "")
|
||||
self.compare_third_line("G64 P3.456789 Q4.567891", "G64 P3.457 Q4.568", "")
|
||||
self.compare_third_line("G64 P3.456789 Q4.567891", "G64 P0.1361 Q0.1798", "--inches")
|
||||
self.compare_third_line(
|
||||
"G64 P3.456789 Q4.567891", "G64 P0.1361 Q0.1798", "--inches"
|
||||
)
|
||||
|
||||
def test01730(self):
|
||||
"""Test G73 command Generation."""
|
||||
|
||||
Reference in New Issue
Block a user