Merge branch 'master' into adam-urbanczyk-OCC-version-update
This commit is contained in:
@ -1311,12 +1311,15 @@ class Workplane(CQ):
|
|||||||
newCenter = p + Vector(xDist, yDist, 0)
|
newCenter = p + Vector(xDist, yDist, 0)
|
||||||
return self.newObject([self.plane.toWorldCoords(newCenter)])
|
return self.newObject([self.plane.toWorldCoords(newCenter)])
|
||||||
|
|
||||||
def spline(self, listOfXYTuple, forConstruction=False):
|
def spline(self, listOfXYTuple, tangents=None, periodic=False,
|
||||||
|
forConstruction=False):
|
||||||
"""
|
"""
|
||||||
Create a spline interpolated through the provided points.
|
Create a spline interpolated through the provided points.
|
||||||
|
|
||||||
:param listOfXYTuple: points to interpolate through
|
:param listOfXYTuple: points to interpolate through
|
||||||
:type listOfXYTuple: list of 2-tuple
|
:type listOfXYTuple: list of 2-tuple
|
||||||
|
:param tangents: tuple of Vectors specifying start and finish tangent
|
||||||
|
:param periodic: creation of peridic curves
|
||||||
:return: a Workplane object with the current point at the end of the spline
|
:return: a Workplane object with the current point at the end of the spline
|
||||||
|
|
||||||
The spline will begin at the current point, and
|
The spline will begin at the current point, and
|
||||||
@ -1344,12 +1347,16 @@ class Workplane(CQ):
|
|||||||
* provide access to control points
|
* provide access to control points
|
||||||
"""
|
"""
|
||||||
gstartPoint = self._findFromPoint(False)
|
gstartPoint = self._findFromPoint(False)
|
||||||
gEndPoint = self.plane.toWorldCoords(listOfXYTuple[-1])
|
|
||||||
|
|
||||||
vecs = [self.plane.toWorldCoords(p) for p in listOfXYTuple]
|
vecs = [self.plane.toWorldCoords(p) for p in listOfXYTuple]
|
||||||
allPoints = [gstartPoint] + vecs
|
allPoints = [gstartPoint] + vecs
|
||||||
|
|
||||||
|
if tangents:
|
||||||
|
t1, t2 = tangents
|
||||||
|
tangents = (self.plane.toWorldCoords(t1),
|
||||||
|
self.plane.toWorldCoords(t2))
|
||||||
|
|
||||||
e = Edge.makeSpline(allPoints)
|
e = Edge.makeSpline(allPoints, tangents=tangents, periodic=periodic)
|
||||||
|
|
||||||
if not forConstruction:
|
if not forConstruction:
|
||||||
self._addPendingEdge(e)
|
self._addPendingEdge(e)
|
||||||
@ -2133,7 +2140,7 @@ class Workplane(CQ):
|
|||||||
newS = newS.clean()
|
newS = newS.clean()
|
||||||
return newS
|
return newS
|
||||||
|
|
||||||
def extrude(self, distance, combine=True, clean=True, both=False):
|
def extrude(self, distance, combine=True, clean=True, both=False, taper=None):
|
||||||
"""
|
"""
|
||||||
Use all un-extruded wires in the parent chain to create a prismatic solid.
|
Use all un-extruded wires in the parent chain to create a prismatic solid.
|
||||||
|
|
||||||
@ -2142,6 +2149,7 @@ class Workplane(CQ):
|
|||||||
:param boolean combine: True to combine the resulting solid with parent solids if found.
|
:param boolean combine: True to combine the resulting solid with parent solids if found.
|
||||||
:param boolean clean: call :py:meth:`clean` afterwards to have a clean shape
|
:param boolean clean: call :py:meth:`clean` afterwards to have a clean shape
|
||||||
:param boolean both: extrude in both directions symmetrically
|
:param boolean both: extrude in both directions symmetrically
|
||||||
|
:param float taper: angle for optional tapered extrusion
|
||||||
:return: a CQ object with the resulting solid selected.
|
:return: a CQ object with the resulting solid selected.
|
||||||
|
|
||||||
extrude always *adds* material to a part.
|
extrude always *adds* material to a part.
|
||||||
@ -2159,7 +2167,7 @@ class Workplane(CQ):
|
|||||||
selected may not be planar
|
selected may not be planar
|
||||||
"""
|
"""
|
||||||
r = self._extrude(
|
r = self._extrude(
|
||||||
distance, both=both) # returns a Solid (or a compound if there were multiple)
|
distance, both=both, taper=taper) # returns a Solid (or a compound if there were multiple)
|
||||||
|
|
||||||
if combine:
|
if combine:
|
||||||
newS = self._combineWithBase(r)
|
newS = self._combineWithBase(r)
|
||||||
@ -2396,7 +2404,7 @@ class Workplane(CQ):
|
|||||||
|
|
||||||
return self.newObject([newS])
|
return self.newObject([newS])
|
||||||
|
|
||||||
def cutBlind(self, distanceToCut, clean=True):
|
def cutBlind(self, distanceToCut, clean=True, taper=None):
|
||||||
"""
|
"""
|
||||||
Use all un-extruded wires in the parent chain to create a prismatic cut from existing solid.
|
Use all un-extruded wires in the parent chain to create a prismatic cut from existing solid.
|
||||||
|
|
||||||
@ -2407,6 +2415,7 @@ class Workplane(CQ):
|
|||||||
:type distanceToCut: float, >0 means in the positive direction of the workplane normal,
|
:type distanceToCut: float, >0 means in the positive direction of the workplane normal,
|
||||||
<0 means in the negative direction
|
<0 means in the negative direction
|
||||||
:param boolean clean: call :py:meth:`clean` afterwards to have a clean shape
|
:param boolean clean: call :py:meth:`clean` afterwards to have a clean shape
|
||||||
|
:param float taper: angle for optional tapered extrusion
|
||||||
:raises: ValueError if there is no solid to subtract from in the chain
|
:raises: ValueError if there is no solid to subtract from in the chain
|
||||||
:return: a CQ object with the resulting object selected
|
:return: a CQ object with the resulting object selected
|
||||||
|
|
||||||
@ -2416,7 +2425,7 @@ class Workplane(CQ):
|
|||||||
Cut Up to Surface
|
Cut Up to Surface
|
||||||
"""
|
"""
|
||||||
# first, make the object
|
# first, make the object
|
||||||
toCut = self._extrude(distanceToCut)
|
toCut = self._extrude(distanceToCut, taper=taper)
|
||||||
|
|
||||||
# now find a solid in the chain
|
# now find a solid in the chain
|
||||||
|
|
||||||
@ -2468,7 +2477,7 @@ class Workplane(CQ):
|
|||||||
|
|
||||||
return self.newObject([r])
|
return self.newObject([r])
|
||||||
|
|
||||||
def _extrude(self, distance, both=False):
|
def _extrude(self, distance, both=False, taper=None):
|
||||||
"""
|
"""
|
||||||
Make a prismatic solid from the existing set of pending wires.
|
Make a prismatic solid from the existing set of pending wires.
|
||||||
|
|
||||||
@ -2518,14 +2527,20 @@ class Workplane(CQ):
|
|||||||
# return r
|
# return r
|
||||||
|
|
||||||
toFuse = []
|
toFuse = []
|
||||||
for ws in wireSets:
|
|
||||||
thisObj = Solid.extrudeLinear(ws[0], ws[1:], eDir)
|
if taper:
|
||||||
toFuse.append(thisObj)
|
for ws in wireSets:
|
||||||
|
thisObj = Solid.extrudeLinear(ws[0], [], eDir, taper)
|
||||||
if both:
|
toFuse.append(thisObj)
|
||||||
thisObj = Solid.extrudeLinear(
|
else:
|
||||||
ws[0], ws[1:], eDir.multiply(-1.))
|
for ws in wireSets:
|
||||||
toFuse.append(thisObj)
|
thisObj = Solid.extrudeLinear(ws[0], ws[1:], eDir)
|
||||||
|
toFuse.append(thisObj)
|
||||||
|
|
||||||
|
if both:
|
||||||
|
thisObj = Solid.extrudeLinear(
|
||||||
|
ws[0], ws[1:], eDir.multiply(-1.))
|
||||||
|
toFuse.append(thisObj)
|
||||||
|
|
||||||
return Compound.makeCompound(toFuse)
|
return Compound.makeCompound(toFuse)
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ from OCC.Core.gp import (gp_Vec, gp_Pnt, gp_Ax1, gp_Ax2, gp_Ax3, gp_Dir, gp_Circ
|
|||||||
gp_Trsf, gp_Pln, gp_GTrsf, gp_Pnt2d, gp_Dir2d)
|
gp_Trsf, gp_Pln, gp_GTrsf, gp_Pnt2d, gp_Dir2d)
|
||||||
|
|
||||||
# collection of pints (used for spline construction)
|
# collection of pints (used for spline construction)
|
||||||
from OCC.Core.TColgp import TColgp_Array1OfPnt
|
from OCC.Core.TColgp import TColgp_HArray1OfPnt
|
||||||
from OCC.Core.BRepAdaptor import BRepAdaptor_Curve, BRepAdaptor_Surface
|
from OCC.Core.BRepAdaptor import BRepAdaptor_Curve, BRepAdaptor_Surface
|
||||||
from OCC.Core.BRepBuilderAPI import (BRepBuilderAPI_MakeVertex,
|
from OCC.Core.BRepBuilderAPI import (BRepBuilderAPI_MakeVertex,
|
||||||
BRepBuilderAPI_MakeEdge,
|
BRepBuilderAPI_MakeEdge,
|
||||||
@ -54,7 +54,7 @@ from OCC.Core.TopoDS import (TopoDS_Shell,
|
|||||||
|
|
||||||
from OCC.Core.GC import GC_MakeArcOfCircle # geometry construction
|
from OCC.Core.GC import GC_MakeArcOfCircle # geometry construction
|
||||||
from OCC.Core.GCE2d import GCE2d_MakeSegment
|
from OCC.Core.GCE2d import GCE2d_MakeSegment
|
||||||
from OCC.Core.GeomAPI import (GeomAPI_PointsToBSpline,
|
from OCC.Core.GeomAPI import (GeomAPI_Interpolate,
|
||||||
GeomAPI_ProjectPointOnSurf)
|
GeomAPI_ProjectPointOnSurf)
|
||||||
|
|
||||||
from OCC.Core.BRepFill import brepfill_Shell, brepfill_Face
|
from OCC.Core.BRepFill import brepfill_Shell, brepfill_Face
|
||||||
@ -102,6 +102,10 @@ from OCC.Core.BRepTools import breptools_Write
|
|||||||
|
|
||||||
from OCC.Core.Visualization import Tesselator
|
from OCC.Core.Visualization import Tesselator
|
||||||
|
|
||||||
|
from OCC.LocOpe import LocOpe_DPrism
|
||||||
|
|
||||||
|
from OCC.BRepCheck import BRepCheck_Analyzer
|
||||||
|
|
||||||
from math import pi, sqrt
|
from math import pi, sqrt
|
||||||
|
|
||||||
TOLERANCE = 1e-6
|
TOLERANCE = 1e-6
|
||||||
@ -313,7 +317,7 @@ class Shape(object):
|
|||||||
return self.wrapped.IsEqual(other.wrapped)
|
return self.wrapped.IsEqual(other.wrapped)
|
||||||
|
|
||||||
def isValid(self): # seems to be not used in the codebase -- remove?
|
def isValid(self): # seems to be not used in the codebase -- remove?
|
||||||
raise NotImplemented
|
return BRepCheck_Analyzer(self.wrapped).IsValid()
|
||||||
|
|
||||||
def BoundingBox(self, tolerance=0.1): # need to implement that in GEOM
|
def BoundingBox(self, tolerance=0.1): # need to implement that in GEOM
|
||||||
return BoundBox._fromTopoDS(self.wrapped)
|
return BoundBox._fromTopoDS(self.wrapped)
|
||||||
@ -462,7 +466,11 @@ class Shape(object):
|
|||||||
return [Solid(i) for i in self._entities('Solid')]
|
return [Solid(i) for i in self._entities('Solid')]
|
||||||
|
|
||||||
def Area(self):
|
def Area(self):
|
||||||
raise NotImplementedError
|
Properties = GProp_GProps()
|
||||||
|
brepgprop_SurfaceProperties(self.wrapped,
|
||||||
|
Properties)
|
||||||
|
|
||||||
|
return Properties.Mass()
|
||||||
|
|
||||||
def Volume(self):
|
def Volume(self):
|
||||||
# when density == 1, mass == volume
|
# when density == 1, mass == volume
|
||||||
@ -631,6 +639,10 @@ class Mixin1D(object):
|
|||||||
brepgprop_LinearProperties(self.wrapped, Properties)
|
brepgprop_LinearProperties(self.wrapped, Properties)
|
||||||
|
|
||||||
return Properties.Mass()
|
return Properties.Mass()
|
||||||
|
|
||||||
|
def IsClosed(self):
|
||||||
|
|
||||||
|
return BRep_Tool.IsClosed(self.wrapped)
|
||||||
|
|
||||||
|
|
||||||
class Edge(Shape, Mixin1D):
|
class Edge(Shape, Mixin1D):
|
||||||
@ -671,20 +683,17 @@ class Edge(Shape, Mixin1D):
|
|||||||
|
|
||||||
return Vector(curve.Value(umax))
|
return Vector(curve.Value(umax))
|
||||||
|
|
||||||
def tangentAt(self, locationVector=None):
|
def tangentAt(self, locationParam=0.5):
|
||||||
"""
|
"""
|
||||||
Compute tangent vector at the specified location.
|
Compute tangent vector at the specified location.
|
||||||
:param locationVector: location to use. Use the center point if None
|
:param locationParam: location to use in [0,1]
|
||||||
:return: tangent vector
|
:return: tangent vector
|
||||||
"""
|
"""
|
||||||
|
|
||||||
curve = self._geomAdaptor()
|
curve = self._geomAdaptor()
|
||||||
|
|
||||||
if locationVector:
|
umin, umax = curve.FirstParameter(), curve.LastParameter()
|
||||||
raise NotImplementedError
|
umid = (1-locationParam)*umin + locationParam*umax
|
||||||
else:
|
|
||||||
umin, umax = curve.FirstParameter(), curve.LastParameter()
|
|
||||||
umid = 0.5 * (umin + umax)
|
|
||||||
|
|
||||||
# TODO what are good parameters for those?
|
# TODO what are good parameters for those?
|
||||||
curve_props = BRepLProp_CLProps(curve, 2, curve.Tolerance())
|
curve_props = BRepLProp_CLProps(curve, 2, curve.Tolerance())
|
||||||
@ -726,18 +735,28 @@ class Edge(Shape, Mixin1D):
|
|||||||
return cls(BRepBuilderAPI_MakeEdge(circle_geom).Edge())
|
return cls(BRepBuilderAPI_MakeEdge(circle_geom).Edge())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeSpline(cls, listOfVector):
|
def makeSpline(cls, listOfVector, tangents=None, periodic=False,
|
||||||
|
tol = 1e-6):
|
||||||
"""
|
"""
|
||||||
Interpolate a spline through the provided points.
|
Interpolate a spline through the provided points.
|
||||||
:param cls:
|
:param cls:
|
||||||
:param listOfVector: a list of Vectors that represent the points
|
:param listOfVector: a list of Vectors that represent the points
|
||||||
|
:param tangents: tuple of Vectors specifying start and finish tangent
|
||||||
|
:param periodic: creation of peridic curves
|
||||||
|
:param tol: tolerance of the algorithm (consult OCC documentation)
|
||||||
:return: an Edge
|
:return: an Edge
|
||||||
"""
|
"""
|
||||||
pnts = TColgp_Array1OfPnt(0, len(listOfVector) - 1)
|
pnts = TColgp_HArray1OfPnt(1, len(listOfVector))
|
||||||
for ix, v in enumerate(listOfVector):
|
for ix, v in enumerate(listOfVector):
|
||||||
pnts.SetValue(ix, v.toPnt())
|
pnts.SetValue(ix+1, v.toPnt())
|
||||||
|
|
||||||
spline_geom = GeomAPI_PointsToBSpline(pnts).Curve()
|
spline_builder = GeomAPI_Interpolate(pnts.GetHandle(), periodic, tol)
|
||||||
|
if tangents:
|
||||||
|
v1,v2 = tangents
|
||||||
|
spline_builder.Load(v1.wrapped,v2.wrapped)
|
||||||
|
|
||||||
|
spline_builder.Perform()
|
||||||
|
spline_geom = spline_builder.Curve()
|
||||||
|
|
||||||
return cls(BRepBuilderAPI_MakeEdge(spline_geom).Edge())
|
return cls(BRepBuilderAPI_MakeEdge(spline_geom).Edge())
|
||||||
|
|
||||||
@ -1280,13 +1299,14 @@ class Solid(Shape, Mixin3D):
|
|||||||
return cls(BRepAlgoAPI_Cut(outer_solid, inner_comp).Shape())
|
return cls(BRepAlgoAPI_Cut(outer_solid, inner_comp).Shape())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def extrudeLinear(cls, outerWire, innerWires, vecNormal):
|
def extrudeLinear(cls, outerWire, innerWires, vecNormal, taper=0):
|
||||||
"""
|
"""
|
||||||
Attempt to extrude the list of wires into a prismatic solid in the provided direction
|
Attempt to extrude the list of wires into a prismatic solid in the provided direction
|
||||||
|
|
||||||
:param outerWire: the outermost wire
|
:param outerWire: the outermost wire
|
||||||
:param innerWires: a list of inner wires
|
:param innerWires: a list of inner wires
|
||||||
:param vecNormal: a vector along which to extrude the wires
|
:param vecNormal: a vector along which to extrude the wires
|
||||||
|
:param taper: taper angle, default=0
|
||||||
:return: a Solid object
|
:return: a Solid object
|
||||||
|
|
||||||
The wires must not intersect
|
The wires must not intersect
|
||||||
@ -1303,18 +1323,20 @@ class Solid(Shape, Mixin3D):
|
|||||||
reliable.
|
reliable.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# one would think that fusing faces into a compound and then extruding would work,
|
if taper==0:
|
||||||
# but it doesnt-- the resulting compound appears to look right, ( right number of faces, etc),
|
face = Face.makeFromWires(outerWire, innerWires)
|
||||||
# but then cutting it from the main solid fails with BRep_NotDone.
|
prism_builder = BRepPrimAPI_MakePrism(
|
||||||
# the work around is to extrude each and then join the resulting solids, which seems to work
|
face.wrapped, vecNormal.wrapped, True)
|
||||||
|
else:
|
||||||
# FreeCAD allows this in one operation, but others might not
|
face = Face.makeFromWires(outerWire)
|
||||||
|
faceNormal = face.normalAt()
|
||||||
face = Face.makeFromWires(outerWire, innerWires)
|
d = 1 if vecNormal.getAngle(faceNormal)<90 * DEG2RAD else -1
|
||||||
prism_builder = BRepPrimAPI_MakePrism(
|
prism_builder = LocOpe_DPrism(face.wrapped,
|
||||||
face.wrapped, vecNormal.wrapped, True)
|
d * vecNormal.Length,
|
||||||
|
d * taper * DEG2RAD)
|
||||||
|
|
||||||
return cls(prism_builder.Shape())
|
return cls(prism_builder.Shape())
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def revolve(cls, outerWire, innerWires, angleDegrees, axisStart, axisEnd):
|
def revolve(cls, outerWire, innerWires, angleDegrees, axisStart, axisEnd):
|
||||||
|
@ -170,7 +170,7 @@ class BaseDirSelector(Selector):
|
|||||||
r.append(o)
|
r.append(o)
|
||||||
elif type(o) == Edge and (o.geomType() == 'LINE' or o.geomType() == 'PLANE'):
|
elif type(o) == Edge and (o.geomType() == 'LINE' or o.geomType() == 'PLANE'):
|
||||||
# an edge is parallel to a direction if its underlying geometry is plane or line
|
# an edge is parallel to a direction if its underlying geometry is plane or line
|
||||||
tangent = o.tangentAt(None)
|
tangent = o.tangentAt()
|
||||||
if self.test(tangent):
|
if self.test(tangent):
|
||||||
r.append(o)
|
r.append(o)
|
||||||
|
|
||||||
|
@ -362,6 +362,38 @@ class TestCadQuery(BaseTest):
|
|||||||
self.assertEqual(2, result.faces().size())
|
self.assertEqual(2, result.faces().size())
|
||||||
self.assertEqual(2, result.vertices().size())
|
self.assertEqual(2, result.vertices().size())
|
||||||
self.assertEqual(3, result.edges().size())
|
self.assertEqual(3, result.edges().size())
|
||||||
|
|
||||||
|
def testSpline(self):
|
||||||
|
"""
|
||||||
|
Tests construction of splines
|
||||||
|
"""
|
||||||
|
pts = [
|
||||||
|
(0, 1),
|
||||||
|
(1, 2),
|
||||||
|
(2, 4)
|
||||||
|
]
|
||||||
|
|
||||||
|
# Spline path - just a smoke test
|
||||||
|
path = Workplane("XZ").spline(pts).val()
|
||||||
|
|
||||||
|
# Closed spline
|
||||||
|
path_closed = Workplane("XZ").spline(pts,periodic=True).val()
|
||||||
|
self.assertTrue(path_closed.IsClosed())
|
||||||
|
|
||||||
|
# attempt to build a valid face
|
||||||
|
w = Wire.assembleEdges([path_closed,])
|
||||||
|
f = Face.makeFromWires(w)
|
||||||
|
self.assertTrue(f.isValid())
|
||||||
|
|
||||||
|
# attempt to build an invalid face
|
||||||
|
w = Wire.assembleEdges([path,])
|
||||||
|
f = Face.makeFromWires(w)
|
||||||
|
self.assertFalse(f.isValid())
|
||||||
|
|
||||||
|
# Spline with explicit tangents
|
||||||
|
path_const = Workplane("XZ").spline(pts,tangents=((0,1),(1,0))).val()
|
||||||
|
self.assertFalse(path.tangentAt(0) == path_const.tangentAt(0))
|
||||||
|
self.assertFalse(path.tangentAt(1) == path_const.tangentAt(1))
|
||||||
|
|
||||||
def testSweep(self):
|
def testSweep(self):
|
||||||
"""
|
"""
|
||||||
@ -1668,11 +1700,24 @@ class TestCadQuery(BaseTest):
|
|||||||
|
|
||||||
def testExtrude(self):
|
def testExtrude(self):
|
||||||
"""
|
"""
|
||||||
Test symmetric extrude
|
Test extrude
|
||||||
"""
|
"""
|
||||||
r = 1.
|
r = 1.
|
||||||
h = 1.
|
h = 1.
|
||||||
decimal_places = 9.
|
decimal_places = 9.
|
||||||
|
|
||||||
|
# extrude in one direction
|
||||||
|
s = Workplane("XY").circle(r).extrude(h, both=False)
|
||||||
|
|
||||||
|
top_face = s.faces(">Z")
|
||||||
|
bottom_face = s.faces("<Z")
|
||||||
|
|
||||||
|
# calculate the distance between the top and the bottom face
|
||||||
|
delta = top_face.val().Center().sub(bottom_face.val().Center())
|
||||||
|
|
||||||
|
self.assertTupleAlmostEquals(delta.toTuple(),
|
||||||
|
(0., 0., h),
|
||||||
|
decimal_places)
|
||||||
|
|
||||||
# extrude symmetrically
|
# extrude symmetrically
|
||||||
s = Workplane("XY").circle(r).extrude(h, both=True)
|
s = Workplane("XY").circle(r).extrude(h, both=True)
|
||||||
@ -1686,6 +1731,42 @@ class TestCadQuery(BaseTest):
|
|||||||
self.assertTupleAlmostEquals(delta.toTuple(),
|
self.assertTupleAlmostEquals(delta.toTuple(),
|
||||||
(0., 0., 2. * h),
|
(0., 0., 2. * h),
|
||||||
decimal_places)
|
decimal_places)
|
||||||
|
|
||||||
|
def testTaperedExtrudeCutBlind(self):
|
||||||
|
|
||||||
|
h = 1.
|
||||||
|
r = 1.
|
||||||
|
t = 5
|
||||||
|
|
||||||
|
# extrude with a positive taper
|
||||||
|
s = Workplane("XY").circle(r).extrude(h, taper=t)
|
||||||
|
|
||||||
|
top_face = s.faces(">Z")
|
||||||
|
bottom_face = s.faces("<Z")
|
||||||
|
|
||||||
|
# top and bottom face area
|
||||||
|
delta = top_face.val().Area() - bottom_face.val().Area()
|
||||||
|
|
||||||
|
self.assertTrue(delta < 0)
|
||||||
|
|
||||||
|
# extrude with a negative taper
|
||||||
|
s = Workplane("XY").circle(r).extrude(h, taper=-t)
|
||||||
|
|
||||||
|
top_face = s.faces(">Z")
|
||||||
|
bottom_face = s.faces("<Z")
|
||||||
|
|
||||||
|
# top and bottom face area
|
||||||
|
delta = top_face.val().Area() - bottom_face.val().Area()
|
||||||
|
|
||||||
|
self.assertTrue(delta > 0)
|
||||||
|
|
||||||
|
# cut a tapered hole
|
||||||
|
s = Workplane("XY").rect(2*r,2*r).extrude(2*h).faces('>Z').workplane()\
|
||||||
|
.rect(r,r).cutBlind(-h, taper=t)
|
||||||
|
|
||||||
|
middle_face = s.faces('>Z[-2]')
|
||||||
|
|
||||||
|
self.assertTrue(middle_face.val().Area() < 1)
|
||||||
|
|
||||||
def testClose(self):
|
def testClose(self):
|
||||||
# Close without endPoint and startPoint coincide.
|
# Close without endPoint and startPoint coincide.
|
||||||
|
Reference in New Issue
Block a user