Download src/Mod/CAM/CAMTests/TestPathOpUtil.py from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 38.1 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/CAM/CAMTests/TestPathOpUtil.py
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/CAM/CAMTests/TestPathOpUtil.py
-
curl -L -o TestPathOpUtil.py https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/CAM/CAMTests/TestPathOpUtil.py
38.1 kB
| # SPDX-License-Identifier: LGPL-2.1-or-later | |
| # *************************************************************************** | |
| # * Copyright (c) 2018 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 Part | |
| import Path | |
| import Path.Op.Util as PathOpUtil | |
| import CAMTests.PathTestUtils as PathTestUtils | |
| import math | |
| from FreeCAD import Vector | |
| Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule()) | |
| # Path.Log.trackModule(Path.Log.thisModule()) | |
| DOC = FreeCAD.getHomePath() + "Mod/CAM/CAMTests/test_geomop.fcstd" | |
| def getWire(obj, nr=0): | |
| return obj.Tip.Profile[0].Shape.Wires[nr] | |
| def getWireInside(obj): | |
| w1 = getWire(obj, 0) | |
| w2 = getWire(obj, 1) | |
| if w2.BoundBox.isInside(w1.BoundBox): | |
| return w1 | |
| return w2 | |
| def getWireOutside(obj): | |
| w1 = getWire(obj, 0) | |
| w2 = getWire(obj, 1) | |
| if w2.BoundBox.isInside(w1.BoundBox): | |
| return w2 | |
| return w1 | |
| def getPositiveShape(obj): | |
| return obj.Tool.Shape | |
| def getNegativeShape(obj): | |
| return obj.Shape | |
| def makeWire(pts): | |
| edges = [] | |
| first = pts[0] | |
| last = pts[0] | |
| for p in pts[1:]: | |
| edges.append(Part.Edge(Part.LineSegment(last, p))) | |
| last = p | |
| edges.append(Part.Edge(Part.LineSegment(last, first))) | |
| return Part.Wire(edges) | |
| def wireMarkers(wire): | |
| pts = [wire.Edges[0].valueAt(wire.Edges[0].FirstParameter)] | |
| for edge in wire.Edges: | |
| pts.append(edge.valueAt(edge.LastParameter)) | |
| return pts | |
| class TestPathOpUtil(PathTestUtils.PathTestBase): | |
| def setUpClass(cls): | |
| FreeCAD.ConfigSet("SuppressRecomputeRequiredDialog", "True") | |
| cls.doc = FreeCAD.openDocument(DOC) | |
| FreeCAD.ConfigSet("SuppressRecomputeRequiredDialog", "") | |
| def tearDownClass(cls): | |
| FreeCAD.closeDocument(cls.doc.Name) | |
| def test00(self): | |
| """Verify isWireClockwise for polygon wires.""" | |
| pa = Vector(1, 1, 0) | |
| pb = Vector(1, 5, 0) | |
| pc = Vector(5, 5, 0) | |
| pd = Vector(5, 1, 0) | |
| self.assertTrue(PathOpUtil.isWireClockwise(makeWire([pa, pb, pc, pd]))) | |
| self.assertFalse(PathOpUtil.isWireClockwise(makeWire([pa, pd, pc, pb]))) | |
| def test01(self): | |
| """Verify isWireClockwise for single edge circle wires.""" | |
| self.assertTrue( | |
| PathOpUtil.isWireClockwise(Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, -1))) | |
| ) | |
| self.assertFalse( | |
| PathOpUtil.isWireClockwise(Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, +1))) | |
| ) | |
| def test02(self): | |
| """Verify isWireClockwise for two half circle wires.""" | |
| e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, -1), 0, 180) | |
| e1 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, -1), 180, 360) | |
| self.assertTrue(PathOpUtil.isWireClockwise(Part.Wire([e0, e1]))) | |
| e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, +1), 0, 180) | |
| e1 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, +1), 180, 360) | |
| self.assertFalse(PathOpUtil.isWireClockwise(Part.Wire([e0, e1]))) | |
| def test03(self): | |
| """Verify isWireClockwise for two edge wires with an arc.""" | |
| e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, -1), 0, 180) | |
| e2 = Part.makeLine(e0.valueAt(e0.LastParameter), e0.valueAt(e0.FirstParameter)) | |
| self.assertTrue(PathOpUtil.isWireClockwise(Part.Wire([e0, e2]))) | |
| e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, +1), 0, 180) | |
| e2 = Part.makeLine(e0.valueAt(e0.LastParameter), e0.valueAt(e0.FirstParameter)) | |
| self.assertFalse(PathOpUtil.isWireClockwise(Part.Wire([e0, e2]))) | |
| def test04(self): | |
| """Verify isWireClockwise for unoriented wires.""" | |
| e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, -1), 0, 180) | |
| e3 = Part.makeLine(e0.valueAt(e0.FirstParameter), e0.valueAt(e0.LastParameter)) | |
| self.assertTrue(PathOpUtil.isWireClockwise(Part.Wire([e0, e3]))) | |
| e0 = Part.makeCircle(5, Vector(1, 2, 3), Vector(0, 0, +1), 0, 180) | |
| e3 = Part.makeLine(e0.valueAt(e0.FirstParameter), e0.valueAt(e0.LastParameter)) | |
| self.assertFalse(PathOpUtil.isWireClockwise(Part.Wire([e0, e3]))) | |
| def test11(self): | |
| """Check offsetting a circular hole.""" | |
| obj = self.doc.getObjectsByLabel("offset-circle")[0] | |
| small = getWireInside(obj) | |
| self.assertRoughly(10, small.Edges[0].Curve.Radius) | |
| wire = PathOpUtil.offsetWire(small, obj.Shape, 3, True) | |
| self.assertIsNotNone(wire) | |
| self.assertEqual(1, len(wire.Edges)) | |
| self.assertRoughly(7, wire.Edges[0].Curve.Radius) | |
| self.assertCoincide(Vector(0, 0, 1), wire.Edges[0].Curve.Axis) | |
| wire = PathOpUtil.offsetWire(small, obj.Shape, 9.9, True) | |
| self.assertIsNotNone(wire) | |
| self.assertEqual(1, len(wire.Edges)) | |
| self.assertRoughly(0.1, wire.Edges[0].Curve.Radius) | |
| self.assertCoincide(Vector(0, 0, 1), wire.Edges[0].Curve.Axis) | |
| def test12(self): | |
| """Check offsetting a circular hole by the radius or more makes the hole vanish.""" | |
| obj = self.doc.getObjectsByLabel("offset-circle")[0] | |
| small = getWireInside(obj) | |
| self.assertRoughly(10, small.Edges[0].Curve.Radius) | |
| wire = PathOpUtil.offsetWire(small, obj.Shape, 10, True) | |
| self.assertIsNone(wire) | |
| wire = PathOpUtil.offsetWire(small, obj.Shape, 15, True) | |
| self.assertIsNone(wire) | |
| def test13(self): | |
| """Check offsetting a cylinder succeeds.""" | |
| obj = self.doc.getObjectsByLabel("offset-circle")[0] | |
| big = getWireOutside(obj) | |
| self.assertRoughly(20, big.Edges[0].Curve.Radius) | |
| wire = PathOpUtil.offsetWire(big, obj.Shape, 10, True) | |
| self.assertIsNotNone(wire) | |
| self.assertEqual(1, len(wire.Edges)) | |
| self.assertRoughly(30, wire.Edges[0].Curve.Radius) | |
| self.assertCoincide(Vector(0, 0, -1), wire.Edges[0].Curve.Axis) | |
| wire = PathOpUtil.offsetWire(big, obj.Shape, 20, True) | |
| self.assertIsNotNone(wire) | |
| self.assertEqual(1, len(wire.Edges)) | |
| self.assertRoughly(40, wire.Edges[0].Curve.Radius) | |
| self.assertCoincide(Vector(0, 0, -1), wire.Edges[0].Curve.Axis) | |
| def test14(self): | |
| """Check offsetting a hole with Placement.""" | |
| obj = self.doc.getObjectsByLabel("offset-placement")[0] | |
| wires = [ | |
| w | |
| for w in obj.Shape.Wires | |
| if 1 == len(w.Edges) and Path.Geom.isRoughly(0, w.Edges[0].Vertexes[0].Point.z) | |
| ] | |
| self.assertEqual(2, len(wires)) | |
| w = wires[1] if wires[0].BoundBox.isInside(wires[1].BoundBox) else wires[0] | |
| self.assertRoughly(10, w.Edges[0].Curve.Radius) | |
| # make sure there is a placement and I didn't mess up the model | |
| self.assertFalse(Path.Geom.pointsCoincide(Vector(), w.Edges[0].Placement.Base)) | |
| wire = PathOpUtil.offsetWire(w, obj.Shape, 2, True) | |
| self.assertIsNotNone(wire) | |
| self.assertEqual(1, len(wire.Edges)) | |
| self.assertRoughly(8, wire.Edges[0].Curve.Radius) | |
| self.assertCoincide(Vector(0, 0, 0), wire.Edges[0].Curve.Center) | |
| self.assertCoincide(Vector(0, 0, 1), wire.Edges[0].Curve.Axis) | |
| def test15(self): | |
| """Check offsetting a cylinder with Placement.""" | |
| obj = self.doc.getObjectsByLabel("offset-placement")[0] | |
| wires = [ | |
| w | |
| for w in obj.Shape.Wires | |
| if 1 == len(w.Edges) and Path.Geom.isRoughly(0, w.Edges[0].Vertexes[0].Point.z) | |
| ] | |
| self.assertEqual(2, len(wires)) | |
| w = wires[0] if wires[0].BoundBox.isInside(wires[1].BoundBox) else wires[1] | |
| self.assertRoughly(20, w.Edges[0].Curve.Radius) | |
| # make sure there is a placement and I didn't mess up the model | |
| self.assertFalse(Path.Geom.pointsCoincide(Vector(), w.Edges[0].Placement.Base)) | |
| wire = PathOpUtil.offsetWire(w, obj.Shape, 2, True) | |
| self.assertIsNotNone(wire) | |
| self.assertEqual(1, len(wire.Edges)) | |
| self.assertRoughly(22, wire.Edges[0].Curve.Radius) | |
| self.assertCoincide(Vector(0, 0, 0), wire.Edges[0].Curve.Center) | |
| self.assertCoincide(Vector(0, 0, -1), wire.Edges[0].Curve.Axis) | |
| def test20(self): | |
| """Check offsetting hole wire succeeds.""" | |
| obj = self.doc.getObjectsByLabel("offset-edge")[0] | |
| small = getWireInside(obj) | |
| # sanity check | |
| y = 10 | |
| x = 10 * math.cos(math.pi / 6) | |
| self.assertLines( | |
| small.Edges, | |
| False, | |
| [ | |
| Vector(0, y, 0), | |
| Vector(-x, -y / 2, 0), | |
| Vector(x, -y / 2, 0), | |
| Vector(0, y, 0), | |
| ], | |
| ) | |
| wire = PathOpUtil.offsetWire(small, obj.Shape, 3, True) | |
| self.assertIsNotNone(wire) | |
| self.assertEqual(3, len(wire.Edges)) | |
| self.assertTrue(wire.isClosed()) | |
| # for holes processing "forward" means CCW | |
| self.assertFalse(PathOpUtil.isWireClockwise(wire)) | |
| y = 4 # offset works in both directions | |
| x = 4 * math.cos(math.pi / 6) | |
| self.assertLines( | |
| wire.Edges, | |
| False, | |
| [Vector(0, 4, 0), Vector(-x, -2, 0), Vector(x, -2, 0), Vector(0, 4, 0)], | |
| ) | |
| def test21(self): | |
| """Check offsetting hole wire for more than it's size makes hole vanish.""" | |
| obj = self.doc.getObjectsByLabel("offset-edge")[0] | |
| small = getWireInside(obj) | |
| # sanity check | |
| y = 10 | |
| x = 10 * math.cos(math.pi / 6) | |
| self.assertLines( | |
| small.Edges, | |
| False, | |
| [ | |
| Vector(0, y, 0), | |
| Vector(-x, -y / 2, 0), | |
| Vector(x, -y / 2, 0), | |
| Vector(0, y, 0), | |
| ], | |
| ) | |
| wire = PathOpUtil.offsetWire(small, obj.Shape, 5, True) | |
| self.assertIsNone(wire) | |
| def test22(self): | |
| """Check offsetting a body wire succeeds.""" | |
| obj = self.doc.getObjectsByLabel("offset-edge")[0] | |
| big = getWireOutside(obj) | |
| # sanity check | |
| y = 20 | |
| x = 20 * math.cos(math.pi / 6) | |
| self.assertLines( | |
| big.Edges, | |
| False, | |
| [ | |
| Vector(0, y, 0), | |
| Vector(-x, -y / 2, 0), | |
| Vector(x, -y / 2, 0), | |
| Vector(0, y, 0), | |
| ], | |
| ) | |
| wire = PathOpUtil.offsetWire(big, obj.Shape, 5, True) | |
| self.assertIsNotNone(wire) | |
| self.assertEqual(6, len(wire.Edges)) | |
| lastAngle = None | |
| refAngle = math.pi / 3 | |
| for e in wire.Edges: | |
| if isinstance(e.Curve, Part.Circle): | |
| self.assertRoughly(5, e.Curve.Radius) | |
| self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis) | |
| else: | |
| self.assertRoughly(34.641, e.Length, 0.001) | |
| begin = e.Vertexes[0].Point | |
| end = e.Vertexes[1].Point | |
| v = end - begin | |
| angle = Path.Geom.getAngle(v) | |
| if Path.Geom.isRoughly(0, angle) or Path.Geom.isRoughly(math.pi, math.fabs(angle)): | |
| if lastAngle: | |
| self.assertRoughly(-refAngle, lastAngle) | |
| elif Path.Geom.isRoughly(+refAngle, angle): | |
| if lastAngle: | |
| self.assertRoughly(math.pi, math.fabs(lastAngle)) | |
| elif Path.Geom.isRoughly(-refAngle, angle): | |
| if lastAngle: | |
| self.assertRoughly(+refAngle, lastAngle) | |
| else: | |
| self.assertIsNone("%s: angle=%s" % (type(e.Curve), angle)) | |
| lastAngle = angle | |
| self.assertTrue(PathOpUtil.isWireClockwise(wire)) | |
| def test31(self): | |
| """Check offsetting a cylinder.""" | |
| obj = self.doc.getObjectsByLabel("circle-cut")[0] | |
| wire = PathOpUtil.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, True) | |
| self.assertEqual(1, len(wire.Edges)) | |
| edge = wire.Edges[0] | |
| self.assertCoincide(Vector(), edge.Curve.Center) | |
| self.assertCoincide(Vector(0, 0, -1), edge.Curve.Axis) | |
| 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) | |
| self.assertEqual(1, len(wire.Edges)) | |
| edge = wire.Edges[0] | |
| self.assertCoincide(Vector(), edge.Curve.Center) | |
| self.assertCoincide(Vector(0, 0, +1), edge.Curve.Axis) | |
| self.assertRoughly(33, edge.Curve.Radius) | |
| def test32(self): | |
| """Check offsetting a box.""" | |
| obj = self.doc.getObjectsByLabel("square-cut")[0] | |
| wire = PathOpUtil.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, True) | |
| self.assertEqual(8, len(wire.Edges)) | |
| self.assertEqual(4, len([e for e in wire.Edges if isinstance(e.Curve, Part.Line)])) | |
| self.assertEqual(4, len([e for e in wire.Edges if isinstance(e.Curve, Part.Circle)])) | |
| for e in wire.Edges: | |
| if isinstance(e.Curve, Part.Line): | |
| if Path.Geom.isRoughly(e.Vertexes[0].Point.x, e.Vertexes[1].Point.x): | |
| self.assertEqual(40, e.Length) | |
| if Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y): | |
| self.assertEqual(60, e.Length) | |
| if isinstance(e.Curve, Part.Circle): | |
| self.assertRoughly(3, e.Curve.Radius) | |
| self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis) | |
| self.assertTrue(PathOpUtil.isWireClockwise(wire)) | |
| # change offset orientation | |
| 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 isinstance(e.Curve, Part.Line)])) | |
| self.assertEqual(4, len([e for e in wire.Edges if isinstance(e.Curve, Part.Circle)])) | |
| for e in wire.Edges: | |
| if isinstance(e.Curve, Part.Line): | |
| if Path.Geom.isRoughly(e.Vertexes[0].Point.x, e.Vertexes[1].Point.x): | |
| self.assertEqual(40, e.Length) | |
| if Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y): | |
| self.assertEqual(60, e.Length) | |
| if isinstance(e.Curve, Part.Circle): | |
| self.assertRoughly(3, e.Curve.Radius) | |
| self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis) | |
| self.assertFalse(PathOpUtil.isWireClockwise(wire)) | |
| def test33(self): | |
| """Check offsetting a triangle.""" | |
| obj = self.doc.getObjectsByLabel("triangle-cut")[0] | |
| wire = PathOpUtil.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, True) | |
| self.assertEqual(6, len(wire.Edges)) | |
| self.assertEqual(3, len([e for e in wire.Edges if isinstance(e.Curve, Part.Line)])) | |
| self.assertEqual(3, len([e for e in wire.Edges if isinstance(e.Curve, Part.Circle)])) | |
| length = 60 * math.sin(math.radians(60)) | |
| for e in wire.Edges: | |
| if isinstance(e.Curve, Part.Line): | |
| self.assertRoughly(length, e.Length) | |
| if isinstance(e.Curve, Part.Circle): | |
| self.assertRoughly(3, e.Curve.Radius) | |
| self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis) | |
| # change offset orientation | |
| 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 isinstance(e.Curve, Part.Line)])) | |
| self.assertEqual(3, len([e for e in wire.Edges if isinstance(e.Curve, Part.Circle)])) | |
| for e in wire.Edges: | |
| if isinstance(e.Curve, Part.Line): | |
| self.assertRoughly(length, e.Length) | |
| if isinstance(e.Curve, Part.Circle): | |
| self.assertRoughly(3, e.Curve.Radius) | |
| self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis) | |
| def test34(self): | |
| """Check offsetting a shape.""" | |
| obj = self.doc.getObjectsByLabel("shape-cut")[0] | |
| wire = PathOpUtil.offsetWire(getWire(obj.Tool), getPositiveShape(obj), 3, True) | |
| self.assertEqual(6, len(wire.Edges)) | |
| self.assertEqual(3, len([e for e in wire.Edges if isinstance(e.Curve, Part.Line)])) | |
| self.assertEqual(3, len([e for e in wire.Edges if isinstance(e.Curve, Part.Circle)])) | |
| length = 40 | |
| radius = 20 + 3 | |
| for e in wire.Edges: | |
| if isinstance(e.Curve, Part.Line): | |
| self.assertRoughly(length, e.Length) | |
| if isinstance(e.Curve, Part.Circle): | |
| self.assertRoughly(radius, e.Curve.Radius) | |
| self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis) | |
| # change offset orientation | |
| 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 isinstance(e.Curve, Part.Line)])) | |
| self.assertEqual(3, len([e for e in wire.Edges if isinstance(e.Curve, Part.Circle)])) | |
| for e in wire.Edges: | |
| if isinstance(e.Curve, Part.Line): | |
| self.assertRoughly(length, e.Length) | |
| if isinstance(e.Curve, Part.Circle): | |
| self.assertRoughly(radius, e.Curve.Radius) | |
| self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis) | |
| def test35(self): | |
| """Check offsetting a cylindrical hole.""" | |
| obj = self.doc.getObjectsByLabel("circle-cut")[0] | |
| wire = PathOpUtil.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, True) | |
| self.assertEqual(1, len(wire.Edges)) | |
| edge = wire.Edges[0] | |
| self.assertCoincide(Vector(), edge.Curve.Center) | |
| self.assertCoincide(Vector(0, 0, +1), edge.Curve.Axis) | |
| 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) | |
| self.assertEqual(1, len(wire.Edges)) | |
| edge = wire.Edges[0] | |
| self.assertCoincide(Vector(), edge.Curve.Center) | |
| self.assertCoincide(Vector(0, 0, -1), edge.Curve.Axis) | |
| self.assertRoughly(27, edge.Curve.Radius) | |
| def test36(self): | |
| """Check offsetting a square hole.""" | |
| obj = self.doc.getObjectsByLabel("square-cut")[0] | |
| wire = PathOpUtil.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, True) | |
| self.assertEqual(4, len(wire.Edges)) | |
| self.assertEqual(4, len([e for e in wire.Edges if isinstance(e.Curve, Part.Line)])) | |
| for e in wire.Edges: | |
| if Path.Geom.isRoughly(e.Vertexes[0].Point.x, e.Vertexes[1].Point.x): | |
| self.assertRoughly(34, e.Length) | |
| if Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y): | |
| self.assertRoughly(54, e.Length) | |
| self.assertFalse(PathOpUtil.isWireClockwise(wire)) | |
| # change offset orientation | |
| 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 isinstance(e.Curve, Part.Line)])) | |
| for e in wire.Edges: | |
| if Path.Geom.isRoughly(e.Vertexes[0].Point.x, e.Vertexes[1].Point.x): | |
| self.assertRoughly(34, e.Length) | |
| if Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y): | |
| self.assertRoughly(54, e.Length) | |
| self.assertTrue(PathOpUtil.isWireClockwise(wire)) | |
| def test37(self): | |
| """Check offsetting a triangular holee.""" | |
| obj = self.doc.getObjectsByLabel("triangle-cut")[0] | |
| wire = PathOpUtil.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, True) | |
| self.assertEqual(3, len(wire.Edges)) | |
| self.assertEqual(3, len([e for e in wire.Edges if isinstance(e.Curve, Part.Line)])) | |
| length = 48 * math.sin(math.radians(60)) | |
| for e in wire.Edges: | |
| self.assertRoughly(length, e.Length) | |
| self.assertFalse(PathOpUtil.isWireClockwise(wire)) | |
| # change offset orientation | |
| 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 isinstance(e.Curve, Part.Line)])) | |
| for e in wire.Edges: | |
| self.assertRoughly(length, e.Length) | |
| self.assertTrue(PathOpUtil.isWireClockwise(wire)) | |
| def test38(self): | |
| """Check offsetting a shape hole.""" | |
| obj = self.doc.getObjectsByLabel("shape-cut")[0] | |
| wire = PathOpUtil.offsetWire(getWire(obj.Tool), getNegativeShape(obj), 3, True) | |
| self.assertEqual(6, len(wire.Edges)) | |
| self.assertEqual(3, len([e for e in wire.Edges if isinstance(e.Curve, Part.Line)])) | |
| self.assertEqual(3, len([e for e in wire.Edges if isinstance(e.Curve, Part.Circle)])) | |
| length = 40 | |
| radius = 20 - 3 | |
| for e in wire.Edges: | |
| if isinstance(e.Curve, Part.Line): | |
| self.assertRoughly(length, e.Length) | |
| if isinstance(e.Curve, Part.Circle): | |
| self.assertRoughly(radius, e.Curve.Radius) | |
| self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis) | |
| # change offset orientation | |
| 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 isinstance(e.Curve, Part.Line)])) | |
| self.assertEqual(3, len([e for e in wire.Edges if isinstance(e.Curve, Part.Circle)])) | |
| for e in wire.Edges: | |
| if isinstance(e.Curve, Part.Line): | |
| self.assertRoughly(length, e.Length) | |
| if isinstance(e.Curve, Part.Circle): | |
| self.assertRoughly(radius, e.Curve.Radius) | |
| self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis) | |
| def test40(self): | |
| """Check offsetting a single outside edge forward.""" | |
| obj = self.doc.getObjectsByLabel("offset-edge")[0] | |
| w = getWireOutside(obj) | |
| length = 40 * math.cos(math.pi / 6) | |
| for e in w.Edges: | |
| self.assertRoughly(length, e.Length) | |
| # let's offset the horizontal edge for starters | |
| hEdges = [ | |
| e for e in w.Edges if Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y) | |
| ] | |
| x = length / 2 | |
| y = -10 | |
| self.assertEqual(1, len(hEdges)) | |
| edge = hEdges[0] | |
| self.assertCoincide(Vector(-x, y, 0), edge.Vertexes[0].Point) | |
| self.assertCoincide(Vector(+x, y, 0), edge.Vertexes[1].Point) | |
| wire = PathOpUtil.offsetWire(Part.Wire([edge]), obj.Shape, 5, True) | |
| self.assertEqual(1, len(wire.Edges)) | |
| y = y - 5 | |
| self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[0].Point) | |
| self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[1].Point) | |
| # make sure we get the same result even if the edge is oriented the other way | |
| edge = Path.Geom.flipEdge(edge) | |
| wire = PathOpUtil.offsetWire(Part.Wire([edge]), obj.Shape, 5, True) | |
| self.assertEqual(1, len(wire.Edges)) | |
| self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[0].Point) | |
| self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[1].Point) | |
| def test41(self): | |
| """Check offsetting a single outside edge not forward.""" | |
| obj = self.doc.getObjectsByLabel("offset-edge")[0] | |
| w = getWireOutside(obj) | |
| length = 40 * math.cos(math.pi / 6) | |
| for e in w.Edges: | |
| self.assertRoughly(length, e.Length) | |
| # let's offset the horizontal edge for starters | |
| hEdges = [ | |
| e for e in w.Edges if Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y) | |
| ] | |
| x = length / 2 | |
| y = -10 | |
| self.assertEqual(1, len(hEdges)) | |
| edge = hEdges[0] | |
| self.assertCoincide(Vector(-x, y, 0), edge.Vertexes[0].Point) | |
| self.assertCoincide(Vector(+x, y, 0), edge.Vertexes[1].Point) | |
| wire = PathOpUtil.offsetWire(Part.Wire([edge]), obj.Shape, 5, False) | |
| self.assertEqual(1, len(wire.Edges)) | |
| y = y - 5 | |
| self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[0].Point) | |
| self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[1].Point) | |
| # make sure we get the same result on a reversed edge | |
| edge = Path.Geom.flipEdge(edge) | |
| wire = PathOpUtil.offsetWire(Part.Wire([edge]), obj.Shape, 5, False) | |
| self.assertEqual(1, len(wire.Edges)) | |
| self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[0].Point) | |
| self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[1].Point) | |
| def test42(self): | |
| """Check offsetting multiple outside edges.""" | |
| obj = self.doc.getObjectsByLabel("offset-edge")[0] | |
| obj.Shape.tessellate(0.01) | |
| self.doc.recompute() | |
| w = getWireOutside(obj) | |
| length = 40 * math.cos(math.pi / 6) | |
| # let's offset the other two legs | |
| lEdges = [ | |
| e | |
| for e in w.Edges | |
| if not Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y) | |
| ] | |
| self.assertEqual(2, len(lEdges)) | |
| wire = PathOpUtil.offsetWire(Part.Wire(lEdges), obj.Shape, 2, True) | |
| x = length / 2 + 2 * math.cos(math.pi / 6) | |
| y = -10 + 2 * math.sin(math.pi / 6) | |
| self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[0].Point) | |
| self.assertCoincide(Vector(+x, y, 0), wire.Edges[-1].Vertexes[1].Point) | |
| rEdges = [e for e in wire.Edges if isinstance(e.Curve, Part.Circle)] | |
| self.assertEqual(1, len(rEdges)) | |
| self.assertCoincide(Vector(0, 20, 0), rEdges[0].Curve.Center) | |
| self.assertCoincide(Vector(0, 0, -1), rEdges[0].Curve.Axis) | |
| # offset the other way | |
| wire = PathOpUtil.offsetWire(Part.Wire(lEdges), obj.Shape, 2, False) | |
| self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[0].Point) | |
| self.assertCoincide(Vector(-x, y, 0), wire.Edges[-1].Vertexes[1].Point) | |
| rEdges = [e for e in wire.Edges if isinstance(e.Curve, Part.Circle)] | |
| self.assertEqual(1, len(rEdges)) | |
| self.assertCoincide(Vector(0, 20, 0), rEdges[0].Curve.Center) | |
| self.assertCoincide(Vector(0, 0, +1), rEdges[0].Curve.Axis) | |
| def test43(self): | |
| """Check offsetting multiple backwards outside edges.""" | |
| # This is exactly the same as test32, except that the wire is flipped to make | |
| # sure the input orientation doesn't matter | |
| obj = self.doc.getObjectsByLabel("offset-edge")[0] | |
| w = getWireOutside(obj) | |
| length = 40 * math.cos(math.pi / 6) | |
| # let's offset the other two legs | |
| lEdges = [ | |
| e | |
| for e in w.Edges | |
| if not Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y) | |
| ] | |
| self.assertEqual(2, len(lEdges)) | |
| w = Path.Geom.flipWire(Part.Wire(lEdges)) | |
| wire = PathOpUtil.offsetWire(w, obj.Shape, 2, True) | |
| x = length / 2 + 2 * math.cos(math.pi / 6) | |
| y = -10 + 2 * math.sin(math.pi / 6) | |
| self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[0].Point) | |
| self.assertCoincide(Vector(+x, y, 0), wire.Edges[-1].Vertexes[1].Point) | |
| rEdges = [e for e in wire.Edges if isinstance(e.Curve, Part.Circle)] | |
| self.assertEqual(1, len(rEdges)) | |
| self.assertCoincide(Vector(0, 20, 0), rEdges[0].Curve.Center) | |
| self.assertCoincide(Vector(0, 0, -1), rEdges[0].Curve.Axis) | |
| # offset the other way | |
| wire = PathOpUtil.offsetWire(Part.Wire(lEdges), obj.Shape, 2, False) | |
| self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[0].Point) | |
| self.assertCoincide(Vector(-x, y, 0), wire.Edges[-1].Vertexes[1].Point) | |
| rEdges = [e for e in wire.Edges if isinstance(e.Curve, Part.Circle)] | |
| self.assertEqual(1, len(rEdges)) | |
| self.assertCoincide(Vector(0, 20, 0), rEdges[0].Curve.Center) | |
| self.assertCoincide(Vector(0, 0, +1), rEdges[0].Curve.Axis) | |
| def test44(self): | |
| """Check offsetting a single inside edge forward.""" | |
| obj = self.doc.getObjectsByLabel("offset-edge")[0] | |
| w = getWireInside(obj) | |
| length = 20 * math.cos(math.pi / 6) | |
| for e in w.Edges: | |
| self.assertRoughly(length, e.Length) | |
| # let's offset the horizontal edge for starters | |
| hEdges = [ | |
| e for e in w.Edges if Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y) | |
| ] | |
| x = length / 2 | |
| y = -5 | |
| self.assertEqual(1, len(hEdges)) | |
| edge = hEdges[0] | |
| self.assertCoincide(Vector(-x, y, 0), edge.Vertexes[0].Point) | |
| self.assertCoincide(Vector(+x, y, 0), edge.Vertexes[1].Point) | |
| wire = PathOpUtil.offsetWire(Part.Wire([edge]), obj.Shape, 2, True) | |
| self.assertEqual(1, len(wire.Edges)) | |
| y = y + 2 | |
| self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[0].Point) | |
| self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[1].Point) | |
| # make sure we get the same result even if the edge is oriented the other way | |
| edge = Path.Geom.flipEdge(edge) | |
| wire = PathOpUtil.offsetWire(Part.Wire([edge]), obj.Shape, 2, True) | |
| self.assertEqual(1, len(wire.Edges)) | |
| self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[0].Point) | |
| self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[1].Point) | |
| def test45(self): | |
| """Check offsetting a single inside edge not forward.""" | |
| obj = self.doc.getObjectsByLabel("offset-edge")[0] | |
| w = getWireInside(obj) | |
| length = 20 * math.cos(math.pi / 6) | |
| for e in w.Edges: | |
| self.assertRoughly(length, e.Length) | |
| # let's offset the horizontal edge for starters | |
| hEdges = [ | |
| e for e in w.Edges if Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y) | |
| ] | |
| x = length / 2 | |
| y = -5 | |
| self.assertEqual(1, len(hEdges)) | |
| edge = hEdges[0] | |
| self.assertCoincide(Vector(-x, y, 0), edge.Vertexes[0].Point) | |
| self.assertCoincide(Vector(+x, y, 0), edge.Vertexes[1].Point) | |
| wire = PathOpUtil.offsetWire(Part.Wire([edge]), obj.Shape, 2, False) | |
| self.assertEqual(1, len(wire.Edges)) | |
| y = y + 2 | |
| self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[0].Point) | |
| self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[1].Point) | |
| # make sure we get the same result even if the edge is oriented the other way | |
| edge = Path.Geom.flipEdge(edge) | |
| wire = PathOpUtil.offsetWire(Part.Wire([edge]), obj.Shape, 2, False) | |
| self.assertEqual(1, len(wire.Edges)) | |
| self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[0].Point) | |
| self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[1].Point) | |
| def test46(self): | |
| """Check offsetting multiple inside edges.""" | |
| obj = self.doc.getObjectsByLabel("offset-edge")[0] | |
| w = getWireInside(obj) | |
| length = 20 * math.cos(math.pi / 6) | |
| # let's offset the other two legs | |
| lEdges = [ | |
| e | |
| for e in w.Edges | |
| if not Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y) | |
| ] | |
| self.assertEqual(2, len(lEdges)) | |
| wire = PathOpUtil.offsetWire(Part.Wire(lEdges), obj.Shape, 2, True) | |
| x = length / 2 - 2 * math.cos(math.pi / 6) | |
| y = -5 - 2 * math.sin(math.pi / 6) | |
| self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[0].Point) | |
| self.assertCoincide(Vector(-x, y, 0), wire.Edges[-1].Vertexes[1].Point) | |
| rEdges = [e for e in wire.Edges if isinstance(e.Curve, Part.Circle)] | |
| self.assertEqual(0, len(rEdges)) | |
| # offset the other way | |
| wire = PathOpUtil.offsetWire(Part.Wire(lEdges), obj.Shape, 2, False) | |
| self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[0].Point) | |
| self.assertCoincide(Vector(+x, y, 0), wire.Edges[-1].Vertexes[1].Point) | |
| rEdges = [e for e in wire.Edges if isinstance(e.Curve, Part.Circle)] | |
| self.assertEqual(0, len(rEdges)) | |
| def test47(self): | |
| """Check offsetting multiple backwards inside edges.""" | |
| # This is exactly the same as test36 except that the wire is flipped to make | |
| # sure it's orientation doesn't matter | |
| obj = self.doc.getObjectsByLabel("offset-edge")[0] | |
| w = getWireInside(obj) | |
| length = 20 * math.cos(math.pi / 6) | |
| # let's offset the other two legs | |
| lEdges = [ | |
| e | |
| for e in w.Edges | |
| if not Path.Geom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y) | |
| ] | |
| self.assertEqual(2, len(lEdges)) | |
| w = Path.Geom.flipWire(Part.Wire(lEdges)) | |
| wire = PathOpUtil.offsetWire(w, obj.Shape, 2, True) | |
| x = length / 2 - 2 * math.cos(math.pi / 6) | |
| y = -5 - 2 * math.sin(math.pi / 6) | |
| self.assertCoincide(Vector(+x, y, 0), wire.Edges[0].Vertexes[0].Point) | |
| self.assertCoincide(Vector(-x, y, 0), wire.Edges[-1].Vertexes[1].Point) | |
| rEdges = [e for e in wire.Edges if isinstance(e.Curve, Part.Circle)] | |
| self.assertEqual(0, len(rEdges)) | |
| # offset the other way | |
| wire = PathOpUtil.offsetWire(Part.Wire(lEdges), obj.Shape, 2, False) | |
| self.assertCoincide(Vector(-x, y, 0), wire.Edges[0].Vertexes[0].Point) | |
| self.assertCoincide(Vector(+x, y, 0), wire.Edges[-1].Vertexes[1].Point) | |
| rEdges = [e for e in wire.Edges if isinstance(e.Curve, Part.Circle)] | |
| self.assertEqual(0, len(rEdges)) | |
| def test50(self): | |
| """Orient an already oriented wire""" | |
| p0 = Vector() | |
| p1 = Vector(1, 2, 3) | |
| p2 = Vector(2, 3, 4) | |
| pts = [p0, p1, p2] | |
| e0 = Part.Edge(Part.LineSegment(p0, p1)) | |
| e1 = Part.Edge(Part.LineSegment(p1, p2)) | |
| wire = PathOpUtil.orientWire(Part.Wire([e0, e1])) | |
| wirePts = wireMarkers(wire) | |
| self.assertPointsMatch(wirePts, pts) | |
| def test51(self): | |
| """Orient a potentially misoriented wire""" | |
| p0 = Vector() | |
| p1 = Vector(1, 2, 3) | |
| p2 = Vector(2, 3, 4) | |
| pts = [p0, p1, p2] | |
| e0p = Part.Edge(Part.LineSegment(p0, p1)) | |
| e0m = Part.Edge(Part.LineSegment(p1, p0)) | |
| e1p = Part.Edge(Part.LineSegment(p1, p2)) | |
| e1m = Part.Edge(Part.LineSegment(p2, p1)) | |
| wire = PathOpUtil.orientWire(Part.Wire([e0p, e1p])) | |
| self.assertPointsMatch(wireMarkers(wire), pts) | |
| wire = PathOpUtil.orientWire(Part.Wire([e0p, e1m])) | |
| self.assertPointsMatch(wireMarkers(wire), pts) | |
| wire = PathOpUtil.orientWire(Part.Wire([e0m, e1p])) | |
| self.assertPointsMatch(wireMarkers(wire), pts) | |
| wire = PathOpUtil.orientWire(Part.Wire([e0m, e1m])) | |
| self.assertPointsMatch(wireMarkers(wire), pts) | |
| def test52(self): | |
| """Orient a potentially misoriented longer wire""" | |
| p0 = Vector() | |
| p1 = Vector(1, 2, 3) | |
| p2 = Vector(4, 5, 6) | |
| p3 = Vector(7, 8, 9) | |
| pts = [p0, p1, p2, p3] | |
| e0p = Part.Edge(Part.LineSegment(p0, p1)) | |
| e0m = Part.Edge(Part.LineSegment(p1, p0)) | |
| e1p = Part.Edge(Part.LineSegment(p1, p2)) | |
| e1m = Part.Edge(Part.LineSegment(p2, p1)) | |
| e2p = Part.Edge(Part.LineSegment(p2, p3)) | |
| e2m = Part.Edge(Part.LineSegment(p3, p2)) | |
| wire = PathOpUtil.orientWire(Part.Wire([e0p, e1p, e2p])) | |
| self.assertPointsMatch(wireMarkers(wire), pts) | |
| wire = PathOpUtil.orientWire(Part.Wire([e0p, e1m, e2p])) | |
| self.assertPointsMatch(wireMarkers(wire), pts) | |
| wire = PathOpUtil.orientWire(Part.Wire([e0m, e1p, e2p])) | |
| self.assertPointsMatch(wireMarkers(wire), pts) | |
| wire = PathOpUtil.orientWire(Part.Wire([e0m, e1m, e2p])) | |
| self.assertPointsMatch(wireMarkers(wire), pts) | |
| wire = PathOpUtil.orientWire(Part.Wire([e0p, e1p, e2m])) | |
| self.assertPointsMatch(wireMarkers(wire), pts) | |
| wire = PathOpUtil.orientWire(Part.Wire([e0p, e1m, e2m])) | |
| self.assertPointsMatch(wireMarkers(wire), pts) | |
| wire = PathOpUtil.orientWire(Part.Wire([e0m, e1p, e2m])) | |
| self.assertPointsMatch(wireMarkers(wire), pts) | |
| wire = PathOpUtil.orientWire(Part.Wire([e0m, e1m, e2m])) | |
| self.assertPointsMatch(wireMarkers(wire), pts) | |