Refactored geom functions into PathGeomOp.
This commit is contained in:
@@ -45,6 +45,7 @@ SET(PathScripts_SRCS
|
||||
PathScripts/PathEngraveGui.py
|
||||
PathScripts/PathFixture.py
|
||||
PathScripts/PathGeom.py
|
||||
PathScripts/PathGeomOp.py
|
||||
PathScripts/PathGetPoint.py
|
||||
PathScripts/PathGui.py
|
||||
PathScripts/PathHelix.py
|
||||
@@ -123,6 +124,7 @@ SET(PathTests_SRCS
|
||||
PathTests/TestPathDressupDogbone.py
|
||||
PathTests/TestPathDressupHoldingTags.py
|
||||
PathTests/TestPathGeom.py
|
||||
PathTests/TestPathGeomOp.py
|
||||
PathTests/TestPathLog.py
|
||||
PathTests/TestPathPost.py
|
||||
PathTests/TestPathSetupSheet.py
|
||||
@@ -133,7 +135,7 @@ SET(PathTests_SRCS
|
||||
PathTests/TestPathUtil.py
|
||||
PathTests/boxtest.fcstd
|
||||
PathTests/test_centroid_00.ngc
|
||||
PathTests/test_chamfer.fcstd
|
||||
PathTests/test_geomop.fcstd
|
||||
PathTests/test_linuxcnc_00.ngc
|
||||
)
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import FreeCAD
|
||||
import Part
|
||||
import Path
|
||||
import PathScripts.PathEngraveBase as PathEngraveBase
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathGeomOp as PathGeomOp
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathOp as PathOp
|
||||
import math
|
||||
@@ -43,170 +43,6 @@ else:
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
def orientWire(w, forward=True):
|
||||
'''orientWire(w, forward=True) ... orients given wire in a specific direction.
|
||||
If forward = True (the default) the wire is oriented clockwise, looking down the negative Z axis.
|
||||
If forward = False the wire is oriented counter clockwise.
|
||||
If forward = None the orientation is determined by the order in which the edges appear in the wire.'''
|
||||
# first, we must ensure all edges are oriented the same way
|
||||
# one would thing this is the way it should be, but it turns out it isn't
|
||||
# on top of that, when creating a face the axis of the face seems to depend
|
||||
# the axis of any included arcs, and not in the order of the edges
|
||||
e0 = w.Edges[0]
|
||||
# well, even the very first edge could be misoriented, so let's try and connect it to the second
|
||||
if 1 < len(w.Edges):
|
||||
last = e0.valueAt(e0.LastParameter)
|
||||
e1 = w.Edges[1]
|
||||
if not PathGeom.pointsCoincide(last, e1.valueAt(e1.FirstParameter)) and not PathGeom.pointsCoincide(last, e1.valueAt(e1.LastParameter)):
|
||||
e0 = PathGeom.flipEdge(e0)
|
||||
|
||||
edges = [e0]
|
||||
last = e0.valueAt(e0.LastParameter)
|
||||
for e in w.Edges[1:]:
|
||||
edge = e if PathGeom.pointsCoincide(last, e.valueAt(e.FirstParameter)) else PathGeom.flipEdge(e)
|
||||
edges.append(edge)
|
||||
last = edge.valueAt(edge.LastParameter)
|
||||
wire = Part.Wire(edges)
|
||||
if forward is not None:
|
||||
# now that we have a wire where all edges are oriented in the same way which
|
||||
# also matches their order - we can create a face and get it's axis to determine
|
||||
# the orientation of the wire - which is all we need here
|
||||
face = Part.Face(wire)
|
||||
cw = 0 < face.Surface.Axis.z
|
||||
if forward != cw:
|
||||
PathLog.track('orientWire - needs flipping')
|
||||
return PathGeom.flipWire(wire)
|
||||
PathLog.track('orientWire - ok')
|
||||
return wire
|
||||
|
||||
def isCircleAt(edge, center):
|
||||
'''isCircleAt(edge, center) ... helper function returns True if edge is a circle at the given center.'''
|
||||
if Circel == type(edge.Curve) or ArcOfCircle == type(edge.Curve):
|
||||
return PathGeom.pointsCoincide(edge.Curve.Center, center)
|
||||
return False
|
||||
|
||||
def offsetWire(wire, base, offset, forward):
|
||||
'''offsetWire(wire, base, offset, forward) ... offsets the wire away from base and orients the wire accordingly.
|
||||
The function tries to avoid most of the pitfalls of Part.makeOffset2D which is possible because all offsetting
|
||||
happens in the XY plane.
|
||||
'''
|
||||
PathLog.track('offsetWire')
|
||||
|
||||
if 1 == len(wire.Edges):
|
||||
edge = wire.Edges[0]
|
||||
curve = edge.Curve
|
||||
if Part.Circle == type(curve) and wire.isClosed():
|
||||
# it's a full circle and there are some problems with that, see
|
||||
# http://www.freecadweb.org/wiki/Part%20Offset2D
|
||||
# it's easy to construct them manually though
|
||||
z = -1 if forward else 1
|
||||
edge = Part.makeCircle(curve.Radius + offset, curve.Center, FreeCAD.Vector(0, 0, z))
|
||||
if base.isInside(edge.Vertexes[0].Point, offset/2, True):
|
||||
if offset > curve.Radius or PathGeom.isRoughly(offset, curve.Radius):
|
||||
# offsetting a hole by its own radius (or more) makes the hole vanish
|
||||
return None
|
||||
edge = Part.makeCircle(curve.Radius - offset, curve.Center, FreeCAD.Vector(0, 0, -z))
|
||||
w = Part.Wire([edge])
|
||||
return w
|
||||
if Part.Line == type(curve) or Part.LineSegment == type(curve):
|
||||
# offsetting a single edge doesn't work because there is an infinite
|
||||
# possible planes into which the edge could be offset
|
||||
# luckily, the plane here must be the XY-plane ...
|
||||
n = (edge.Vertexes[1].Point - edge.Vertexes[0].Point).cross(FreeCAD.Vector(0, 0, 1))
|
||||
o = n.normalize() * offset
|
||||
edge.translate(o)
|
||||
if base.isInside(edge.valueAt((edge.FirstParameter + edge.LastParameter)/2), offset/2, True):
|
||||
edge.translate(-2 * o)
|
||||
w = Part.Wire([edge])
|
||||
return orientWire(w, forward)
|
||||
# if we get to this point the assumption is that makeOffset2D can deal with the edge
|
||||
pass
|
||||
|
||||
w = wire.makeOffset2D(offset)
|
||||
|
||||
if wire.isClosed():
|
||||
if not base.isInside(w.Edges[0].Vertexes[0].Point, offset/2, True):
|
||||
PathLog.track('closed - outside')
|
||||
return orientWire(w, forward)
|
||||
PathLog.track('closed - inside')
|
||||
try:
|
||||
w = wire.makeOffset2D(-offset)
|
||||
except:
|
||||
# most likely offsetting didn't work because the wire is a hole
|
||||
# and the offset is too big - making the hole vanish
|
||||
return None
|
||||
return orientWire(w, forward)
|
||||
|
||||
# An edge is considered to be inside of shape if the mid point is inside
|
||||
# Of the remaining edges we take the longest wire to be the engraving side
|
||||
# Looking for a circle with the start vertex as center marks and end
|
||||
# starting from there follow the edges until a circle with the end vertex as center is found
|
||||
# if the traversed edges include any oof the remainig from above, all those edges are remaining
|
||||
# this is to also include edges which might partially be inside shape
|
||||
# if they need to be discarded, split, that should happen in a post process
|
||||
# Depending on the Axis of the circle, and which side remains we know if the wire needs to be flipped
|
||||
|
||||
# find edges that are not inside the shape
|
||||
def isInside(edge):
|
||||
if shape.Shape.isInside(edge.Vertexes[0].Point, offset/2, True) and shape.Shape.isInside(edge.Vertexes[-1].Point, offset/2, True):
|
||||
return True
|
||||
return False
|
||||
outside = [e for e in edges if not isInside(e)]
|
||||
# discard all edges that are not part of the longest wire
|
||||
longestWire = None
|
||||
for w in [Part.Wire(el) for el in Part.sortEdges(outside)]:
|
||||
if not longestWire or longestWire.Length < w.Length:
|
||||
longestWire = w
|
||||
|
||||
# find the start and end point
|
||||
start = wire.Vertexes[0].Point
|
||||
end = wire.Vertexes[-1].Point
|
||||
|
||||
collectLeft = False
|
||||
collectRight = False
|
||||
leftSideEdges = []
|
||||
rightSideEdges = []
|
||||
|
||||
for e in (w.Edges + w.Edges):
|
||||
if isCircleAt(e, start):
|
||||
if PathGeom.pointsCoincide(e.Curve.Axis, FreeCAD.Vector(0, 0, 1)):
|
||||
if not collectLeft and leftSideEdges:
|
||||
break
|
||||
collectLeft = True
|
||||
collectRight = False
|
||||
else:
|
||||
if not collectRight and rightSideEdges:
|
||||
break
|
||||
collectLeft = False
|
||||
collectRight = True
|
||||
elif isCircleAt(e, end):
|
||||
if PathGeom.pointsCoincide(e.Curve.Axis, FreeCAD.Vector(0, 0, 1)):
|
||||
if not collectRight and rightSideEdges:
|
||||
break
|
||||
collectLeft = False
|
||||
collectRight = True
|
||||
else:
|
||||
if not collectLeft and leftSideEdges:
|
||||
break
|
||||
collectLeft = True
|
||||
collectRight = False
|
||||
elif collectLeft:
|
||||
leftSideEdges.append(e)
|
||||
elif collectRight:
|
||||
rightSideEdges.append(e)
|
||||
|
||||
edges = leftSideEdges
|
||||
for e in longestWire.Edges:
|
||||
for e0 in rightSideEdges:
|
||||
if PathGeom.edgesMatch(e, e0):
|
||||
if forward:
|
||||
edges = [PathGeom.flipEdge(edge) for edge in rightSideEdges]
|
||||
return Part.Wire(edges)
|
||||
|
||||
if not forward:
|
||||
edges = [PathGeom.flipEdge(edge) for edge in rightSideEdges]
|
||||
return Part.Wire(edges)
|
||||
|
||||
def toolDepthAndOffset(width, extraDepth, tool):
|
||||
'''toolDepthAndOffset(width, extraDepth, tool) ... return tuple for given parameters.'''
|
||||
angle = tool.CuttingEdgeAngle
|
||||
@@ -260,7 +96,7 @@ class ObjectChamfer(PathEngraveBase.ObjectOp):
|
||||
|
||||
for w in self.adjustWirePlacement(obj, base, basewires):
|
||||
self.adjusted_basewires.append(w)
|
||||
wire = offsetWire(w, base.Shape, offset, True)
|
||||
wire = PathGeomOp.offsetWire(w, base.Shape, offset, True)
|
||||
if wire:
|
||||
wires.append(wire)
|
||||
|
||||
|
||||
207
src/Mod/Path/PathScripts/PathGeomOp.py
Normal file
207
src/Mod/Path/PathScripts/PathGeomOp.py
Normal file
@@ -0,0 +1,207 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * 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 PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathLog as PathLog
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
# Qt tanslation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
def orientWire(w, forward=True):
|
||||
'''orientWire(w, forward=True) ... orients given wire in a specific direction.
|
||||
If forward = True (the default) the wire is oriented clockwise, looking down the negative Z axis.
|
||||
If forward = False the wire is oriented counter clockwise.
|
||||
If forward = None the orientation is determined by the order in which the edges appear in the wire.'''
|
||||
# first, we must ensure all edges are oriented the same way
|
||||
# one would thing this is the way it should be, but it turns out it isn't
|
||||
# on top of that, when creating a face the axis of the face seems to depend
|
||||
# the axis of any included arcs, and not in the order of the edges
|
||||
e0 = w.Edges[0]
|
||||
# well, even the very first edge could be misoriented, so let's try and connect it to the second
|
||||
if 1 < len(w.Edges):
|
||||
last = e0.valueAt(e0.LastParameter)
|
||||
e1 = w.Edges[1]
|
||||
if not PathGeom.pointsCoincide(last, e1.valueAt(e1.FirstParameter)) and not PathGeom.pointsCoincide(last, e1.valueAt(e1.LastParameter)):
|
||||
e0 = PathGeom.flipEdge(e0)
|
||||
|
||||
edges = [e0]
|
||||
last = e0.valueAt(e0.LastParameter)
|
||||
for e in w.Edges[1:]:
|
||||
edge = e if PathGeom.pointsCoincide(last, e.valueAt(e.FirstParameter)) else PathGeom.flipEdge(e)
|
||||
edges.append(edge)
|
||||
last = edge.valueAt(edge.LastParameter)
|
||||
wire = Part.Wire(edges)
|
||||
if forward is not None:
|
||||
# now that we have a wire where all edges are oriented in the same way which
|
||||
# also matches their order - we can create a face and get it's axis to determine
|
||||
# the orientation of the wire - which is all we need here
|
||||
face = Part.Face(wire)
|
||||
cw = 0 < face.Surface.Axis.z
|
||||
if forward != cw:
|
||||
PathLog.track('orientWire - needs flipping')
|
||||
return PathGeom.flipWire(wire)
|
||||
PathLog.track('orientWire - ok')
|
||||
return wire
|
||||
|
||||
def offsetWire(wire, base, offset, forward):
|
||||
'''offsetWire(wire, base, offset, forward) ... offsets the wire away from base and orients the wire accordingly.
|
||||
The function tries to avoid most of the pitfalls of Part.makeOffset2D which is possible because all offsetting
|
||||
happens in the XY plane.
|
||||
'''
|
||||
PathLog.track('offsetWire')
|
||||
|
||||
if 1 == len(wire.Edges):
|
||||
edge = wire.Edges[0]
|
||||
curve = edge.Curve
|
||||
if Part.Circle == type(curve) and wire.isClosed():
|
||||
# it's a full circle and there are some problems with that, see
|
||||
# http://www.freecadweb.org/wiki/Part%20Offset2D
|
||||
# it's easy to construct them manually though
|
||||
z = -1 if forward else 1
|
||||
edge = Part.makeCircle(curve.Radius + offset, curve.Center, FreeCAD.Vector(0, 0, z))
|
||||
if base.isInside(edge.Vertexes[0].Point, offset/2, True):
|
||||
if offset > curve.Radius or PathGeom.isRoughly(offset, curve.Radius):
|
||||
# offsetting a hole by its own radius (or more) makes the hole vanish
|
||||
return None
|
||||
edge = Part.makeCircle(curve.Radius - offset, curve.Center, FreeCAD.Vector(0, 0, -z))
|
||||
w = Part.Wire([edge])
|
||||
return w
|
||||
if Part.Line == type(curve) or Part.LineSegment == type(curve):
|
||||
# offsetting a single edge doesn't work because there is an infinite
|
||||
# possible planes into which the edge could be offset
|
||||
# luckily, the plane here must be the XY-plane ...
|
||||
n = (edge.Vertexes[1].Point - edge.Vertexes[0].Point).cross(FreeCAD.Vector(0, 0, 1))
|
||||
o = n.normalize() * offset
|
||||
edge.translate(o)
|
||||
if base.isInside(edge.valueAt((edge.FirstParameter + edge.LastParameter)/2), offset/2, True):
|
||||
edge.translate(-2 * o)
|
||||
w = Part.Wire([edge])
|
||||
return orientWire(w, forward)
|
||||
# if we get to this point the assumption is that makeOffset2D can deal with the edge
|
||||
pass
|
||||
|
||||
w = wire.makeOffset2D(offset)
|
||||
|
||||
if wire.isClosed():
|
||||
if not base.isInside(w.Edges[0].Vertexes[0].Point, offset/2, True):
|
||||
PathLog.track('closed - outside')
|
||||
return orientWire(w, forward)
|
||||
PathLog.track('closed - inside')
|
||||
try:
|
||||
w = wire.makeOffset2D(-offset)
|
||||
except:
|
||||
# most likely offsetting didn't work because the wire is a hole
|
||||
# and the offset is too big - making the hole vanish
|
||||
return None
|
||||
return orientWire(w, forward)
|
||||
|
||||
# An edge is considered to be inside of shape if the mid point is inside
|
||||
# Of the remaining edges we take the longest wire to be the engraving side
|
||||
# Looking for a circle with the start vertex as center marks and end
|
||||
# starting from there follow the edges until a circle with the end vertex as center is found
|
||||
# if the traversed edges include any oof the remainig from above, all those edges are remaining
|
||||
# this is to also include edges which might partially be inside shape
|
||||
# if they need to be discarded, split, that should happen in a post process
|
||||
# Depending on the Axis of the circle, and which side remains we know if the wire needs to be flipped
|
||||
|
||||
# find edges that are not inside the shape
|
||||
def isInside(edge):
|
||||
if base.isInside(edge.Vertexes[0].Point, offset/2, True) and base.isInside(edge.Vertexes[-1].Point, offset/2, True):
|
||||
return True
|
||||
return False
|
||||
outside = [e for e in w.Edges if not isInside(e)]
|
||||
# discard all edges that are not part of the longest wire
|
||||
longestWire = None
|
||||
for w in [Part.Wire(el) for el in Part.sortEdges(outside)]:
|
||||
if not longestWire or longestWire.Length < w.Length:
|
||||
longestWire = w
|
||||
|
||||
# find the start and end point
|
||||
start = wire.Vertexes[0].Point
|
||||
end = wire.Vertexes[-1].Point
|
||||
|
||||
def isCircleAt(edge, center):
|
||||
'''isCircleAt(edge, center) ... helper function returns True if edge is a circle at the given center.'''
|
||||
if Part.Circel == type(edge.Curve) or Part.ArcOfCircle == type(edge.Curve):
|
||||
return PathGeom.pointsCoincide(edge.Curve.Center, center)
|
||||
return False
|
||||
|
||||
|
||||
collectLeft = False
|
||||
collectRight = False
|
||||
leftSideEdges = []
|
||||
rightSideEdges = []
|
||||
|
||||
for e in (w.Edges + w.Edges):
|
||||
if isCircleAt(e, start):
|
||||
if PathGeom.pointsCoincide(e.Curve.Axis, FreeCAD.Vector(0, 0, 1)):
|
||||
if not collectLeft and leftSideEdges:
|
||||
break
|
||||
collectLeft = True
|
||||
collectRight = False
|
||||
else:
|
||||
if not collectRight and rightSideEdges:
|
||||
break
|
||||
collectLeft = False
|
||||
collectRight = True
|
||||
elif isCircleAt(e, end):
|
||||
if PathGeom.pointsCoincide(e.Curve.Axis, FreeCAD.Vector(0, 0, 1)):
|
||||
if not collectRight and rightSideEdges:
|
||||
break
|
||||
collectLeft = False
|
||||
collectRight = True
|
||||
else:
|
||||
if not collectLeft and leftSideEdges:
|
||||
break
|
||||
collectLeft = True
|
||||
collectRight = False
|
||||
elif collectLeft:
|
||||
leftSideEdges.append(e)
|
||||
elif collectRight:
|
||||
rightSideEdges.append(e)
|
||||
|
||||
edges = leftSideEdges
|
||||
for e in longestWire.Edges:
|
||||
for e0 in rightSideEdges:
|
||||
if PathGeom.edgesMatch(e, e0):
|
||||
if forward:
|
||||
edges = [PathGeom.flipEdge(edge) for edge in rightSideEdges]
|
||||
return Part.Wire(edges)
|
||||
|
||||
if not forward:
|
||||
edges = [PathGeom.flipEdge(edge) for edge in rightSideEdges]
|
||||
return Part.Wire(edges)
|
||||
|
||||
@@ -22,452 +22,19 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
import FreeCAD
|
||||
import Part
|
||||
import Path
|
||||
import PathScripts.PathChamfer as PathChamfer
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathTests.PathTestUtils as PathTestUtils
|
||||
import math
|
||||
|
||||
from FreeCAD import Vector
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
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
|
||||
|
||||
doc = None
|
||||
triangle = None
|
||||
shape = None
|
||||
|
||||
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)
|
||||
|
||||
|
||||
class TestPathChamfer(PathTestUtils.PathTestBase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
global doc
|
||||
doc = FreeCAD.openDocument(FreeCAD.getHomePath() + 'Mod/Path/PathTests/test_chamfer.fcstd')
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
FreeCAD.closeDocument("test_chamfer")
|
||||
|
||||
def test00(self):
|
||||
'''Check that face orientation has anything to do with the wire orientation.'''
|
||||
pa = Vector(1, 1, 0)
|
||||
pb = Vector(1, 5, 0)
|
||||
pc = Vector(5, 5, 0)
|
||||
pd = Vector(5, 1, 0)
|
||||
|
||||
w = makeWire([pa, pb, pc, pd])
|
||||
f = Part.Face(w)
|
||||
self.assertCoincide(Vector(0, 0, -1), f.Surface.Axis)
|
||||
|
||||
w = makeWire([pa, pd, pc, pb])
|
||||
f = Part.Face(w)
|
||||
self.assertCoincide(Vector(0, 0, +1), f.Surface.Axis)
|
||||
|
||||
def test01(self):
|
||||
'''Check offsetting a circular hole.'''
|
||||
obj = doc.getObjectsByLabel('offset-circle')[0]
|
||||
|
||||
small = getWireInside(obj)
|
||||
self.assertRoughly(10, small.Edges[0].Curve.Radius)
|
||||
|
||||
wire = PathChamfer.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 = PathChamfer.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 test02(self):
|
||||
'''Check offsetting a circular hole by the radius or more makes the hole vanish.'''
|
||||
obj = doc.getObjectsByLabel('offset-circle')[0]
|
||||
|
||||
small = getWireInside(obj)
|
||||
self.assertRoughly(10, small.Edges[0].Curve.Radius)
|
||||
wire = PathChamfer.offsetWire(small, obj.Shape, 10, True)
|
||||
self.assertIsNone(wire)
|
||||
|
||||
wire = PathChamfer.offsetWire(small, obj.Shape, 15, True)
|
||||
self.assertIsNone(wire)
|
||||
|
||||
def test03(self):
|
||||
'''Check offsetting a cylinder succeeds.'''
|
||||
obj = doc.getObjectsByLabel('offset-circle')[0]
|
||||
|
||||
big = getWireOutside(obj)
|
||||
self.assertRoughly(20, big.Edges[0].Curve.Radius)
|
||||
|
||||
wire = PathChamfer.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 = PathChamfer.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 test04(self):
|
||||
'''Check offsetting a hole with Placement.'''
|
||||
obj = doc.getObjectsByLabel('offset-placement')[0]
|
||||
|
||||
wires = [w for w in obj.Shape.Wires if 1 == len(w.Edges) and PathGeom.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(PathGeom.pointsCoincide(Vector(), w.Edges[0].Placement.Base))
|
||||
|
||||
wire = PathChamfer.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 test05(self):
|
||||
'''Check offsetting a cylinder with Placement.'''
|
||||
obj = doc.getObjectsByLabel('offset-placement')[0]
|
||||
|
||||
wires = [w for w in obj.Shape.Wires if 1 == len(w.Edges) and PathGeom.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(PathGeom.pointsCoincide(Vector(), w.Edges[0].Placement.Base))
|
||||
|
||||
wire = PathChamfer.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 test10(self):
|
||||
'''Check offsetting hole wire succeeds.'''
|
||||
obj = 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 = PathChamfer.offsetWire(small, obj.Shape, 3, True)
|
||||
self.assertIsNotNone(wire)
|
||||
self.assertEqual(3, len(wire.Edges))
|
||||
self.assertTrue(wire.isClosed())
|
||||
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)])
|
||||
f = Part.Face(wire)
|
||||
self.assertCoincide(Vector(0, 0, 1), f.Surface.Axis)
|
||||
|
||||
def test11(self):
|
||||
'''Check offsetting hole wire for more than it's size makes hole vanish.'''
|
||||
obj = 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 = PathChamfer.offsetWire(small, obj.Shape, 5, True)
|
||||
self.assertIsNone(wire)
|
||||
|
||||
def test12(self):
|
||||
'''Check offsetting a body wire succeeds.'''
|
||||
obj = 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 = PathChamfer.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 Part.Circle == type(e.Curve):
|
||||
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 = PathGeom.getAngle(v)
|
||||
if PathGeom.isRoughly(0, angle) or PathGeom.isRoughly(math.pi, math.fabs(angle)):
|
||||
if lastAngle:
|
||||
self.assertRoughly(-refAngle, lastAngle)
|
||||
elif PathGeom.isRoughly(+refAngle, angle):
|
||||
if lastAngle:
|
||||
self.assertRoughly(math.pi, math.fabs(lastAngle))
|
||||
elif PathGeom.isRoughly(-refAngle, angle):
|
||||
if lastAngle:
|
||||
self.assertRoughly(+refAngle, lastAngle)
|
||||
else:
|
||||
self.assertIsNone("%s: angle=%s" % (type(e.Curve), angle))
|
||||
lastAngle = angle
|
||||
f = Part.Face(wire)
|
||||
self.assertCoincide(Vector(0, 0, -1), f.Surface.Axis)
|
||||
|
||||
def test21(self):
|
||||
'''Check offsetting a cylinder.'''
|
||||
obj = doc.getObjectsByLabel('circle-cut')[0]
|
||||
|
||||
wire = PathChamfer.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 = PathChamfer.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 test22(self):
|
||||
'''Check offsetting a box.'''
|
||||
obj = doc.getObjectsByLabel('square-cut')[0]
|
||||
|
||||
wire = PathChamfer.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 Part.Line == type(e.Curve)]))
|
||||
self.assertEqual(4, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
|
||||
for e in wire.Edges:
|
||||
if Part.Line == type(e.Curve):
|
||||
if PathGeom.isRoughly(e.Vertexes[0].Point.x, e.Vertexes[1].Point.x):
|
||||
self.assertEqual(40, e.Length)
|
||||
if PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y):
|
||||
self.assertEqual(60, e.Length)
|
||||
if Part.Circle == type(e.Curve):
|
||||
self.assertRoughly(3, e.Curve.Radius)
|
||||
self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis)
|
||||
|
||||
# change offset orientation
|
||||
wire = PathChamfer.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(4, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
|
||||
for e in wire.Edges:
|
||||
if Part.Line == type(e.Curve):
|
||||
if PathGeom.isRoughly(e.Vertexes[0].Point.x, e.Vertexes[1].Point.x):
|
||||
self.assertEqual(40, e.Length)
|
||||
if PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y):
|
||||
self.assertEqual(60, e.Length)
|
||||
if Part.Circle == type(e.Curve):
|
||||
self.assertRoughly(3, e.Curve.Radius)
|
||||
self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis)
|
||||
|
||||
|
||||
def test23(self):
|
||||
'''Check offsetting a triangle.'''
|
||||
obj = doc.getObjectsByLabel('triangle-cut')[0]
|
||||
|
||||
wire = PathChamfer.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 Part.Line == type(e.Curve)]))
|
||||
self.assertEqual(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
|
||||
length = 60 * math.sin(math.radians(60))
|
||||
for e in wire.Edges:
|
||||
if Part.Line == type(e.Curve):
|
||||
self.assertRoughly(length, e.Length)
|
||||
if Part.Circle == type(e.Curve):
|
||||
self.assertRoughly(3, e.Curve.Radius)
|
||||
self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis)
|
||||
|
||||
# change offset orientation
|
||||
wire = PathChamfer.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(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
|
||||
for e in wire.Edges:
|
||||
if Part.Line == type(e.Curve):
|
||||
self.assertRoughly(length, e.Length)
|
||||
if Part.Circle == type(e.Curve):
|
||||
self.assertRoughly(3, e.Curve.Radius)
|
||||
self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis)
|
||||
|
||||
def test24(self):
|
||||
'''Check offsetting a shape.'''
|
||||
obj = doc.getObjectsByLabel('shape-cut')[0]
|
||||
|
||||
wire = PathChamfer.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 Part.Line == type(e.Curve)]))
|
||||
self.assertEqual(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
|
||||
length = 40
|
||||
radius = 20 + 3
|
||||
for e in wire.Edges:
|
||||
if Part.Line == type(e.Curve):
|
||||
self.assertRoughly(length, e.Length)
|
||||
if Part.Circle == type(e.Curve):
|
||||
self.assertRoughly(radius, e.Curve.Radius)
|
||||
self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis)
|
||||
|
||||
# change offset orientation
|
||||
wire = PathChamfer.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(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
|
||||
for e in wire.Edges:
|
||||
if Part.Line == type(e.Curve):
|
||||
self.assertRoughly(length, e.Length)
|
||||
if Part.Circle == type(e.Curve):
|
||||
self.assertRoughly(radius, e.Curve.Radius)
|
||||
self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis)
|
||||
|
||||
def test25(self):
|
||||
'''Check offsetting a cylindrical hole.'''
|
||||
obj = doc.getObjectsByLabel('circle-cut')[0]
|
||||
|
||||
wire = PathChamfer.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 = PathChamfer.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 test26(self):
|
||||
'''Check offsetting a square hole.'''
|
||||
obj = doc.getObjectsByLabel('square-cut')[0]
|
||||
|
||||
wire = PathChamfer.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 Part.Line == type(e.Curve)]))
|
||||
for e in wire.Edges:
|
||||
if PathGeom.isRoughly(e.Vertexes[0].Point.x, e.Vertexes[1].Point.x):
|
||||
self.assertRoughly(34, e.Length)
|
||||
if PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y):
|
||||
self.assertRoughly(54, e.Length)
|
||||
|
||||
# change offset orientation
|
||||
wire = PathChamfer.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:
|
||||
if PathGeom.isRoughly(e.Vertexes[0].Point.x, e.Vertexes[1].Point.x):
|
||||
self.assertRoughly(34, e.Length)
|
||||
if PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y):
|
||||
self.assertRoughly(54, e.Length)
|
||||
|
||||
|
||||
def test27(self):
|
||||
'''Check offsetting a triangular holee.'''
|
||||
obj = doc.getObjectsByLabel('triangle-cut')[0]
|
||||
|
||||
wire = PathChamfer.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 Part.Line == type(e.Curve)]))
|
||||
length = 48 * math.sin(math.radians(60))
|
||||
for e in wire.Edges:
|
||||
self.assertRoughly(length, e.Length)
|
||||
f = Part.Face(wire)
|
||||
self.assertCoincide(Vector(0, 0, +1), f.Surface.Axis)
|
||||
|
||||
# change offset orientation
|
||||
wire = PathChamfer.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:
|
||||
self.assertRoughly(length, e.Length)
|
||||
f = Part.Face(wire)
|
||||
self.assertCoincide(Vector(0, 0, -1), f.Surface.Axis)
|
||||
|
||||
def test28(self):
|
||||
'''Check offsetting a shape hole.'''
|
||||
obj = doc.getObjectsByLabel('shape-cut')[0]
|
||||
|
||||
wire = PathChamfer.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 Part.Line == type(e.Curve)]))
|
||||
self.assertEqual(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
|
||||
length = 40
|
||||
radius = 20 - 3
|
||||
for e in wire.Edges:
|
||||
if Part.Line == type(e.Curve):
|
||||
self.assertRoughly(length, e.Length)
|
||||
if Part.Circle == type(e.Curve):
|
||||
self.assertRoughly(radius, e.Curve.Radius)
|
||||
self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis)
|
||||
|
||||
# change offset orientation
|
||||
wire = PathChamfer.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(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
|
||||
for e in wire.Edges:
|
||||
if Part.Line == type(e.Curve):
|
||||
self.assertRoughly(length, e.Length)
|
||||
if Part.Circle == type(e.Curve):
|
||||
self.assertRoughly(radius, e.Curve.Radius)
|
||||
self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis)
|
||||
|
||||
|
||||
def test50(self):
|
||||
'''Verify chamfer depth and offset for an end mill.'''
|
||||
tool = Path.Tool()
|
||||
tool.Diameter = 20
|
||||
@@ -485,7 +52,7 @@ class TestPathChamfer(PathTestUtils.PathTestBase):
|
||||
self.assertRoughly(0.01, depth)
|
||||
self.assertRoughly(9, offset)
|
||||
|
||||
def test51(self):
|
||||
def test01(self):
|
||||
'''Verify chamfer depth and offset for a 90° v-bit.'''
|
||||
tool = Path.Tool()
|
||||
tool.FlatRadius = 0
|
||||
@@ -499,7 +66,7 @@ class TestPathChamfer(PathTestUtils.PathTestBase):
|
||||
self.assertRoughly(1.2, depth)
|
||||
self.assertRoughly(0.2, offset)
|
||||
|
||||
def test52(self):
|
||||
def test02(self):
|
||||
'''Verify chamfer depth and offset for a 90° v-bit with non 0 flat radius.'''
|
||||
tool = Path.Tool()
|
||||
tool.FlatRadius = 0.3
|
||||
@@ -513,7 +80,7 @@ class TestPathChamfer(PathTestUtils.PathTestBase):
|
||||
self.assertRoughly(2.2, depth)
|
||||
self.assertRoughly(0.5, offset)
|
||||
|
||||
def test53(self):
|
||||
def test03(self):
|
||||
'''Verify chamfer depth and offset for a 60° v-bit with non 0 flat radius.'''
|
||||
tool = Path.Tool()
|
||||
tool.FlatRadius = 10
|
||||
|
||||
470
src/Mod/Path/PathTests/TestPathGeomOp.py
Normal file
470
src/Mod/Path/PathTests/TestPathGeomOp.py
Normal file
@@ -0,0 +1,470 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * 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 PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathGeomOp as PathGeomOp
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathTests.PathTestUtils as PathTestUtils
|
||||
import math
|
||||
|
||||
from FreeCAD import Vector
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
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
|
||||
|
||||
doc = None
|
||||
triangle = None
|
||||
shape = None
|
||||
|
||||
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)
|
||||
|
||||
|
||||
class TestPathGeomOp(PathTestUtils.PathTestBase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
global doc
|
||||
doc = FreeCAD.openDocument(FreeCAD.getHomePath() + 'Mod/Path/PathTests/test_geomop.fcstd')
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
FreeCAD.closeDocument("test_geomop")
|
||||
|
||||
def test00(self):
|
||||
'''Check that face orientation has anything to do with the wire orientation.'''
|
||||
pa = Vector(1, 1, 0)
|
||||
pb = Vector(1, 5, 0)
|
||||
pc = Vector(5, 5, 0)
|
||||
pd = Vector(5, 1, 0)
|
||||
|
||||
w = makeWire([pa, pb, pc, pd])
|
||||
f = Part.Face(w)
|
||||
self.assertCoincide(Vector(0, 0, -1), f.Surface.Axis)
|
||||
|
||||
w = makeWire([pa, pd, pc, pb])
|
||||
f = Part.Face(w)
|
||||
self.assertCoincide(Vector(0, 0, +1), f.Surface.Axis)
|
||||
|
||||
def test01(self):
|
||||
'''Check offsetting a circular hole.'''
|
||||
obj = doc.getObjectsByLabel('offset-circle')[0]
|
||||
|
||||
small = getWireInside(obj)
|
||||
self.assertRoughly(10, small.Edges[0].Curve.Radius)
|
||||
|
||||
wire = PathGeomOp.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 = PathGeomOp.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 test02(self):
|
||||
'''Check offsetting a circular hole by the radius or more makes the hole vanish.'''
|
||||
obj = doc.getObjectsByLabel('offset-circle')[0]
|
||||
|
||||
small = getWireInside(obj)
|
||||
self.assertRoughly(10, small.Edges[0].Curve.Radius)
|
||||
wire = PathGeomOp.offsetWire(small, obj.Shape, 10, True)
|
||||
self.assertIsNone(wire)
|
||||
|
||||
wire = PathGeomOp.offsetWire(small, obj.Shape, 15, True)
|
||||
self.assertIsNone(wire)
|
||||
|
||||
def test03(self):
|
||||
'''Check offsetting a cylinder succeeds.'''
|
||||
obj = doc.getObjectsByLabel('offset-circle')[0]
|
||||
|
||||
big = getWireOutside(obj)
|
||||
self.assertRoughly(20, big.Edges[0].Curve.Radius)
|
||||
|
||||
wire = PathGeomOp.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 = PathGeomOp.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 test04(self):
|
||||
'''Check offsetting a hole with Placement.'''
|
||||
obj = doc.getObjectsByLabel('offset-placement')[0]
|
||||
|
||||
wires = [w for w in obj.Shape.Wires if 1 == len(w.Edges) and PathGeom.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(PathGeom.pointsCoincide(Vector(), w.Edges[0].Placement.Base))
|
||||
|
||||
wire = PathGeomOp.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 test05(self):
|
||||
'''Check offsetting a cylinder with Placement.'''
|
||||
obj = doc.getObjectsByLabel('offset-placement')[0]
|
||||
|
||||
wires = [w for w in obj.Shape.Wires if 1 == len(w.Edges) and PathGeom.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(PathGeom.pointsCoincide(Vector(), w.Edges[0].Placement.Base))
|
||||
|
||||
wire = PathGeomOp.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 test10(self):
|
||||
'''Check offsetting hole wire succeeds.'''
|
||||
obj = 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 = PathGeomOp.offsetWire(small, obj.Shape, 3, True)
|
||||
self.assertIsNotNone(wire)
|
||||
self.assertEqual(3, len(wire.Edges))
|
||||
self.assertTrue(wire.isClosed())
|
||||
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)])
|
||||
f = Part.Face(wire)
|
||||
self.assertCoincide(Vector(0, 0, 1), f.Surface.Axis)
|
||||
|
||||
def test11(self):
|
||||
'''Check offsetting hole wire for more than it's size makes hole vanish.'''
|
||||
obj = 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 = PathGeomOp.offsetWire(small, obj.Shape, 5, True)
|
||||
self.assertIsNone(wire)
|
||||
|
||||
def test12(self):
|
||||
'''Check offsetting a body wire succeeds.'''
|
||||
obj = 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 = PathGeomOp.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 Part.Circle == type(e.Curve):
|
||||
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 = PathGeom.getAngle(v)
|
||||
if PathGeom.isRoughly(0, angle) or PathGeom.isRoughly(math.pi, math.fabs(angle)):
|
||||
if lastAngle:
|
||||
self.assertRoughly(-refAngle, lastAngle)
|
||||
elif PathGeom.isRoughly(+refAngle, angle):
|
||||
if lastAngle:
|
||||
self.assertRoughly(math.pi, math.fabs(lastAngle))
|
||||
elif PathGeom.isRoughly(-refAngle, angle):
|
||||
if lastAngle:
|
||||
self.assertRoughly(+refAngle, lastAngle)
|
||||
else:
|
||||
self.assertIsNone("%s: angle=%s" % (type(e.Curve), angle))
|
||||
lastAngle = angle
|
||||
f = Part.Face(wire)
|
||||
self.assertCoincide(Vector(0, 0, -1), f.Surface.Axis)
|
||||
|
||||
def test21(self):
|
||||
'''Check offsetting a cylinder.'''
|
||||
obj = doc.getObjectsByLabel('circle-cut')[0]
|
||||
|
||||
wire = PathGeomOp.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 = PathGeomOp.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 test22(self):
|
||||
'''Check offsetting a box.'''
|
||||
obj = doc.getObjectsByLabel('square-cut')[0]
|
||||
|
||||
wire = PathGeomOp.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 Part.Line == type(e.Curve)]))
|
||||
self.assertEqual(4, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
|
||||
for e in wire.Edges:
|
||||
if Part.Line == type(e.Curve):
|
||||
if PathGeom.isRoughly(e.Vertexes[0].Point.x, e.Vertexes[1].Point.x):
|
||||
self.assertEqual(40, e.Length)
|
||||
if PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y):
|
||||
self.assertEqual(60, e.Length)
|
||||
if Part.Circle == type(e.Curve):
|
||||
self.assertRoughly(3, e.Curve.Radius)
|
||||
self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis)
|
||||
|
||||
# change offset orientation
|
||||
wire = PathGeomOp.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(4, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
|
||||
for e in wire.Edges:
|
||||
if Part.Line == type(e.Curve):
|
||||
if PathGeom.isRoughly(e.Vertexes[0].Point.x, e.Vertexes[1].Point.x):
|
||||
self.assertEqual(40, e.Length)
|
||||
if PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y):
|
||||
self.assertEqual(60, e.Length)
|
||||
if Part.Circle == type(e.Curve):
|
||||
self.assertRoughly(3, e.Curve.Radius)
|
||||
self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis)
|
||||
|
||||
|
||||
def test23(self):
|
||||
'''Check offsetting a triangle.'''
|
||||
obj = doc.getObjectsByLabel('triangle-cut')[0]
|
||||
|
||||
wire = PathGeomOp.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 Part.Line == type(e.Curve)]))
|
||||
self.assertEqual(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
|
||||
length = 60 * math.sin(math.radians(60))
|
||||
for e in wire.Edges:
|
||||
if Part.Line == type(e.Curve):
|
||||
self.assertRoughly(length, e.Length)
|
||||
if Part.Circle == type(e.Curve):
|
||||
self.assertRoughly(3, e.Curve.Radius)
|
||||
self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis)
|
||||
|
||||
# change offset orientation
|
||||
wire = PathGeomOp.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(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
|
||||
for e in wire.Edges:
|
||||
if Part.Line == type(e.Curve):
|
||||
self.assertRoughly(length, e.Length)
|
||||
if Part.Circle == type(e.Curve):
|
||||
self.assertRoughly(3, e.Curve.Radius)
|
||||
self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis)
|
||||
|
||||
def test24(self):
|
||||
'''Check offsetting a shape.'''
|
||||
obj = doc.getObjectsByLabel('shape-cut')[0]
|
||||
|
||||
wire = PathGeomOp.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 Part.Line == type(e.Curve)]))
|
||||
self.assertEqual(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
|
||||
length = 40
|
||||
radius = 20 + 3
|
||||
for e in wire.Edges:
|
||||
if Part.Line == type(e.Curve):
|
||||
self.assertRoughly(length, e.Length)
|
||||
if Part.Circle == type(e.Curve):
|
||||
self.assertRoughly(radius, e.Curve.Radius)
|
||||
self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis)
|
||||
|
||||
# change offset orientation
|
||||
wire = PathGeomOp.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(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
|
||||
for e in wire.Edges:
|
||||
if Part.Line == type(e.Curve):
|
||||
self.assertRoughly(length, e.Length)
|
||||
if Part.Circle == type(e.Curve):
|
||||
self.assertRoughly(radius, e.Curve.Radius)
|
||||
self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis)
|
||||
|
||||
def test25(self):
|
||||
'''Check offsetting a cylindrical hole.'''
|
||||
obj = doc.getObjectsByLabel('circle-cut')[0]
|
||||
|
||||
wire = PathGeomOp.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 = PathGeomOp.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 test26(self):
|
||||
'''Check offsetting a square hole.'''
|
||||
obj = doc.getObjectsByLabel('square-cut')[0]
|
||||
|
||||
wire = PathGeomOp.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 Part.Line == type(e.Curve)]))
|
||||
for e in wire.Edges:
|
||||
if PathGeom.isRoughly(e.Vertexes[0].Point.x, e.Vertexes[1].Point.x):
|
||||
self.assertRoughly(34, e.Length)
|
||||
if PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y):
|
||||
self.assertRoughly(54, e.Length)
|
||||
|
||||
# change offset orientation
|
||||
wire = PathGeomOp.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:
|
||||
if PathGeom.isRoughly(e.Vertexes[0].Point.x, e.Vertexes[1].Point.x):
|
||||
self.assertRoughly(34, e.Length)
|
||||
if PathGeom.isRoughly(e.Vertexes[0].Point.y, e.Vertexes[1].Point.y):
|
||||
self.assertRoughly(54, e.Length)
|
||||
|
||||
|
||||
def test27(self):
|
||||
'''Check offsetting a triangular holee.'''
|
||||
obj = doc.getObjectsByLabel('triangle-cut')[0]
|
||||
|
||||
wire = PathGeomOp.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 Part.Line == type(e.Curve)]))
|
||||
length = 48 * math.sin(math.radians(60))
|
||||
for e in wire.Edges:
|
||||
self.assertRoughly(length, e.Length)
|
||||
f = Part.Face(wire)
|
||||
self.assertCoincide(Vector(0, 0, +1), f.Surface.Axis)
|
||||
|
||||
# change offset orientation
|
||||
wire = PathGeomOp.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:
|
||||
self.assertRoughly(length, e.Length)
|
||||
f = Part.Face(wire)
|
||||
self.assertCoincide(Vector(0, 0, -1), f.Surface.Axis)
|
||||
|
||||
def test28(self):
|
||||
'''Check offsetting a shape hole.'''
|
||||
obj = doc.getObjectsByLabel('shape-cut')[0]
|
||||
|
||||
wire = PathGeomOp.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 Part.Line == type(e.Curve)]))
|
||||
self.assertEqual(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
|
||||
length = 40
|
||||
radius = 20 - 3
|
||||
for e in wire.Edges:
|
||||
if Part.Line == type(e.Curve):
|
||||
self.assertRoughly(length, e.Length)
|
||||
if Part.Circle == type(e.Curve):
|
||||
self.assertRoughly(radius, e.Curve.Radius)
|
||||
self.assertCoincide(Vector(0, 0, +1), e.Curve.Axis)
|
||||
|
||||
# change offset orientation
|
||||
wire = PathGeomOp.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(3, len([e for e in wire.Edges if Part.Circle == type(e.Curve)]))
|
||||
for e in wire.Edges:
|
||||
if Part.Line == type(e.Curve):
|
||||
self.assertRoughly(length, e.Length)
|
||||
if Part.Circle == type(e.Curve):
|
||||
self.assertRoughly(radius, e.Curve.Radius)
|
||||
self.assertCoincide(Vector(0, 0, -1), e.Curve.Axis)
|
||||
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ from PathTests.TestPathLog import TestPathLog
|
||||
from PathTests.TestPathCore import TestPathCore
|
||||
#from PathTests.TestPathPost import PathPostTestCases
|
||||
from PathTests.TestPathGeom import TestPathGeom
|
||||
from PathTests.TestPathGeomOp import TestPathGeomOp
|
||||
from PathTests.TestPathUtil import TestPathUtil
|
||||
from PathTests.TestPathDepthParams import depthTestCases
|
||||
from PathTests.TestPathDressupHoldingTags import TestHoldingTags
|
||||
|
||||
Reference in New Issue
Block a user