From 99c17a8ddcb34ef222856c7a2c40ee5b776857ae Mon Sep 17 00:00:00 2001 From: adam-urbanczyk Date: Wed, 2 Jan 2019 20:29:26 +0100 Subject: [PATCH 01/16] Initial implementation of tapered extrusion --- cadquery/occ_impl/shapes.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index d14a42be..336411a9 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -102,6 +102,8 @@ from OCC.BRepTools import breptools_Write from OCC.Visualization import Tesselator +from OCC.LocOpe import LocOpe_DPrism + from math import pi, sqrt TOLERANCE = 1e-6 @@ -1280,13 +1282,14 @@ class Solid(Shape, Mixin3D): return cls(BRepAlgoAPI_Cut(outer_solid, inner_comp).Shape()) @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 :param outerWire: the outermost wire :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, default=None + :param taper: taper angle, default=0 :return: a Solid object The wires must not intersect @@ -1303,18 +1306,20 @@ class Solid(Shape, Mixin3D): reliable. """ - # one would think that fusing faces into a compound and then extruding would work, - # but it doesnt-- the resulting compound appears to look right, ( right number of faces, etc), - # but then cutting it from the main solid fails with BRep_NotDone. - # the work around is to extrude each and then join the resulting solids, which seems to work - - # FreeCAD allows this in one operation, but others might not - - face = Face.makeFromWires(outerWire, innerWires) - prism_builder = BRepPrimAPI_MakePrism( - face.wrapped, vecNormal.wrapped, True) - + if taper==0: + face = Face.makeFromWires(outerWire, innerWires) + prism_builder = BRepPrimAPI_MakePrism( + face.wrapped, vecNormal.wrapped, True) + else: + face = Face.makeFromWires(outerWire) + faceNormal = face.normalAt() + d = 1 if vecNormal.getAngle(faceNormal)<90 * DEG2RAD else -1 + prism_builder = LocOpe_DPrism(face.wrapped, + d * vecNormal.Length, + d * taper * DEG2RAD) + return cls(prism_builder.Shape()) + @classmethod def revolve(cls, outerWire, innerWires, angleDegrees, axisStart, axisEnd): From a1889539695d8af3cb0fa88a1158aa6725b7a98d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Urba=C5=84czyk?= Date: Wed, 30 Jan 2019 15:59:56 +0100 Subject: [PATCH 02/16] Use pythonocc 0.18.1 explicitely --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a0bb3f9d..7631ce0c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,7 +47,7 @@ before_install: - export PATH="$HOME/miniconda/bin:$HOME/miniconda/lib:$PATH"; - hash -r; - conda config --set always_yes yes --set changeps1 no; -- conda create -y -q -n test_cq -c pythonocc -c oce -c conda-forge -c dlr-sc pythonocc-core=0.18 +- conda create -y -q -n test_cq -c pythonocc -c oce -c conda-forge -c dlr-sc pythonocc-core=0.18.1 pyparsing mock; - source ~/miniconda/bin/activate test_cq; - python -c 'import OCC.gp as gp; print(gp.gp_Vec())' From 952f21b4d907b73c5d916086242c35c9d71746ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Urba=C5=84czyk?= Date: Wed, 30 Jan 2019 16:02:00 +0100 Subject: [PATCH 03/16] Use 18.1 explicitly in appveyor too --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index f07b21d0..31da27de 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -15,7 +15,7 @@ install: - set "PATH=%MINICONDA_DIRNAME%;%MINICONDA_DIRNAME%\\Scripts;%PATH%" - conda config --set always_yes yes - conda update -q conda - - conda create --quiet --name cqtest -c cadquery -c conda-forge -c pythonocc -c oce -c conda-forge -c dlr-sc pythonocc-core=0.18 python=%PYTHON_VERSION% pyparsing mock coverage codecov + - conda create --quiet --name cqtest -c cadquery -c conda-forge -c pythonocc -c oce -c conda-forge -c dlr-sc pythonocc-core=0.18.1 python=%PYTHON_VERSION% pyparsing mock coverage codecov - activate cqtest - pip install codecov - python setup.py install From 2af2d4538922c790ca2e1e67a32883311daad711 Mon Sep 17 00:00:00 2001 From: adam-urbanczyk Date: Wed, 6 Feb 2019 21:08:14 +0100 Subject: [PATCH 04/16] Implement Area() method --- cadquery/occ_impl/shapes.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index 336411a9..e9772093 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -464,7 +464,8 @@ class Shape(object): return [Solid(i) for i in self._entities('Solid')] def Area(self): - raise NotImplementedError + # when 2D density == 1, mass == area + return Shape.computeMass(self) def Volume(self): # when density == 1, mass == volume @@ -1288,7 +1289,7 @@ class Solid(Shape, Mixin3D): :param outerWire: the outermost wire :param innerWires: a list of inner wires - :param vecNormal: a vector along which to extrude the wires, default=None + :param vecNormal: a vector along which to extrude the wires :param taper: taper angle, default=0 :return: a Solid object From ced285ac0e3e180ca2be73fd3103d1be46bd4572 Mon Sep 17 00:00:00 2001 From: adam-urbanczyk Date: Wed, 6 Feb 2019 21:08:48 +0100 Subject: [PATCH 05/16] Expose tapered extrude to the users --- cadquery/cq.py | 29 ++++++++++++++++++----------- tests/TestCadQuery.py | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/cadquery/cq.py b/cadquery/cq.py index ed544364..fcaf5b62 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -2133,7 +2133,7 @@ class Workplane(CQ): newS = newS.clean() 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. @@ -2142,6 +2142,7 @@ class Workplane(CQ): :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 both: extrude in both directions symmetrically + :param float taper: angle for optional tapered extrusion :return: a CQ object with the resulting solid selected. extrude always *adds* material to a part. @@ -2159,7 +2160,7 @@ class Workplane(CQ): selected may not be planar """ 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: newS = self._combineWithBase(r) @@ -2468,7 +2469,7 @@ class Workplane(CQ): 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. @@ -2518,14 +2519,20 @@ class Workplane(CQ): # return r toFuse = [] - for ws in wireSets: - 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) + + if taper: + for ws in wireSets: + thisObj = Solid.extrudeLinear(ws[0], [], eDir, taper) + toFuse.append(thisObj) + else: + for ws in wireSets: + 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) diff --git a/tests/TestCadQuery.py b/tests/TestCadQuery.py index d34d155f..939ff9eb 100644 --- a/tests/TestCadQuery.py +++ b/tests/TestCadQuery.py @@ -1668,11 +1668,24 @@ class TestCadQuery(BaseTest): def testExtrude(self): """ - Test symmetric extrude + Test extrude """ r = 1. h = 1. 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") + bottom_face = s.faces("Z") + bottom_face = s.faces(" 0) def testClose(self): # Close without endPoint and startPoint coincide. From 4a981a1493f068866829bdf90df2364fde5ed734 Mon Sep 17 00:00:00 2001 From: adam-urbanczyk Date: Wed, 6 Feb 2019 21:19:37 +0100 Subject: [PATCH 06/16] Use pythonocc 18.1 in CI --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index a0bb3f9d..7631ce0c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,7 +47,7 @@ before_install: - export PATH="$HOME/miniconda/bin:$HOME/miniconda/lib:$PATH"; - hash -r; - conda config --set always_yes yes --set changeps1 no; -- conda create -y -q -n test_cq -c pythonocc -c oce -c conda-forge -c dlr-sc pythonocc-core=0.18 +- conda create -y -q -n test_cq -c pythonocc -c oce -c conda-forge -c dlr-sc pythonocc-core=0.18.1 pyparsing mock; - source ~/miniconda/bin/activate test_cq; - python -c 'import OCC.gp as gp; print(gp.gp_Vec())' diff --git a/appveyor.yml b/appveyor.yml index f07b21d0..31da27de 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -15,7 +15,7 @@ install: - set "PATH=%MINICONDA_DIRNAME%;%MINICONDA_DIRNAME%\\Scripts;%PATH%" - conda config --set always_yes yes - conda update -q conda - - conda create --quiet --name cqtest -c cadquery -c conda-forge -c pythonocc -c oce -c conda-forge -c dlr-sc pythonocc-core=0.18 python=%PYTHON_VERSION% pyparsing mock coverage codecov + - conda create --quiet --name cqtest -c cadquery -c conda-forge -c pythonocc -c oce -c conda-forge -c dlr-sc pythonocc-core=0.18.1 python=%PYTHON_VERSION% pyparsing mock coverage codecov - activate cqtest - pip install codecov - python setup.py install From c292a9f2dbb2dd5ced40cd73c38cf68e32dd14bd Mon Sep 17 00:00:00 2001 From: adam-urbanczyk Date: Thu, 7 Feb 2019 21:03:20 +0100 Subject: [PATCH 07/16] Implemented tapered cutBlind --- cadquery/cq.py | 5 +++-- tests/TestCadQuery.py | 20 +++++++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/cadquery/cq.py b/cadquery/cq.py index fcaf5b62..982e0a47 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -2397,7 +2397,7 @@ class Workplane(CQ): 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. @@ -2408,6 +2408,7 @@ class Workplane(CQ): :type distanceToCut: float, >0 means in the positive direction of the workplane normal, <0 means in the negative direction :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 :return: a CQ object with the resulting object selected @@ -2417,7 +2418,7 @@ class Workplane(CQ): Cut Up to Surface """ # first, make the object - toCut = self._extrude(distanceToCut) + toCut = self._extrude(distanceToCut, taper=taper) # now find a solid in the chain diff --git a/tests/TestCadQuery.py b/tests/TestCadQuery.py index 939ff9eb..329f02cb 100644 --- a/tests/TestCadQuery.py +++ b/tests/TestCadQuery.py @@ -1699,9 +1699,15 @@ class TestCadQuery(BaseTest): self.assertTupleAlmostEquals(delta.toTuple(), (0., 0., 2. * h), decimal_places) - + + def testTaperedExtrudeCutBlind(self): + + h = 1. + r = 1. + t = 5 + # extrude with a positive taper - s = Workplane("XY").circle(r).extrude(h, taper=5) + s = Workplane("XY").circle(r).extrude(h, taper=t) top_face = s.faces(">Z") bottom_face = s.faces("Z") bottom_face = s.faces(" 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): # Close without endPoint and startPoint coincide. From 244a15853a064f8f57cc6e8bf48adb126697da5f Mon Sep 17 00:00:00 2001 From: adam-urbanczyk Date: Fri, 8 Feb 2019 22:16:40 +0100 Subject: [PATCH 08/16] Initial implementation using GeomAPI_Interpolate --- cadquery/occ_impl/shapes.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index e9772093..77188dd6 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -7,7 +7,7 @@ from OCC.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) # collection of pints (used for spline construction) -from OCC.TColgp import TColgp_Array1OfPnt +from OCC.TColgp import TColgp_HArray1OfPnt from OCC.BRepAdaptor import BRepAdaptor_Curve, BRepAdaptor_Surface from OCC.BRepBuilderAPI import (BRepBuilderAPI_MakeVertex, BRepBuilderAPI_MakeEdge, @@ -54,7 +54,7 @@ from OCC.TopoDS import (TopoDS_Shell, from OCC.GC import GC_MakeArcOfCircle # geometry construction from OCC.GCE2d import GCE2d_MakeSegment -from OCC.GeomAPI import (GeomAPI_PointsToBSpline, +from OCC.GeomAPI import (GeomAPI_Interpolate, GeomAPI_ProjectPointOnSurf) from OCC.BRepFill import brepfill_Shell, brepfill_Face @@ -729,18 +729,28 @@ class Edge(Shape, Mixin1D): return cls(BRepBuilderAPI_MakeEdge(circle_geom).Edge()) @classmethod - def makeSpline(cls, listOfVector): + def makeSpline(cls, listOfVector, tangents=None, periodic=False, + tol = 1e-6): """ Interpolate a spline through the provided points. :param cls: :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 """ - pnts = TColgp_Array1OfPnt(0, len(listOfVector) - 1) + pnts = TColgp_HArray1OfPnt(1, len(listOfVector)) for ix, v in enumerate(listOfVector): - pnts.SetValue(ix, v.toPnt()) - - spline_geom = GeomAPI_PointsToBSpline(pnts).Curve() + pnts.SetValue(ix+1, v.toPnt()) + + 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()) From 653c4baa86fff4d077eef5bce7e8893ec3dcc3f3 Mon Sep 17 00:00:00 2001 From: adam-urbanczyk Date: Fri, 8 Feb 2019 22:40:32 +0100 Subject: [PATCH 09/16] Expose complete interface of makeSpline in cq.Workplane --- cadquery/cq.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cadquery/cq.py b/cadquery/cq.py index 982e0a47..47fdf159 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -1311,7 +1311,8 @@ class Workplane(CQ): newCenter = p + Vector(xDist, yDist, 0) 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. @@ -1344,12 +1345,16 @@ class Workplane(CQ): * provide access to control points """ gstartPoint = self._findFromPoint(False) - gEndPoint = self.plane.toWorldCoords(listOfXYTuple[-1]) vecs = [self.plane.toWorldCoords(p) for p in listOfXYTuple] 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: self._addPendingEdge(e) From f52f29937b954f1dfc466cac2c7dbf48ff4279ce Mon Sep 17 00:00:00 2001 From: adam-urbanczyk Date: Fri, 8 Feb 2019 22:42:22 +0100 Subject: [PATCH 10/16] Update docstring of spline --- cadquery/cq.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cadquery/cq.py b/cadquery/cq.py index 47fdf159..23e99a70 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -1318,6 +1318,8 @@ class Workplane(CQ): :param listOfXYTuple: points to interpolate through :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 The spline will begin at the current point, and From e8a428a628086e79adeee53e7826589f6d080bd2 Mon Sep 17 00:00:00 2001 From: adam-urbanczyk Date: Fri, 8 Feb 2019 23:06:33 +0100 Subject: [PATCH 11/16] Refactor tangentAt --- cadquery/occ_impl/shapes.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index 77188dd6..153f969e 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -634,6 +634,10 @@ class Mixin1D(object): brepgprop_LinearProperties(self.wrapped, Properties) return Properties.Mass() + + def IsClosed(self): + + return BRep_Tool.IsClosed(self.wrapped) class Edge(Shape, Mixin1D): @@ -674,20 +678,17 @@ class Edge(Shape, Mixin1D): return Vector(curve.Value(umax)) - def tangentAt(self, locationVector=None): + def tangentAt(self, locationParam=0.5): """ 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 """ curve = self._geomAdaptor() - if locationVector: - raise NotImplementedError - else: - umin, umax = curve.FirstParameter(), curve.LastParameter() - umid = 0.5 * (umin + umax) + umin, umax = curve.FirstParameter(), curve.LastParameter() + umid = (1-locationParam)*umin + locationParam*umax # TODO what are good parameters for those? curve_props = BRepLProp_CLProps(curve, 2, curve.Tolerance()) From 92a0bc4f096b76077189bdb59f5fe90aa7fe3864 Mon Sep 17 00:00:00 2001 From: adam-urbanczyk Date: Fri, 8 Feb 2019 23:07:03 +0100 Subject: [PATCH 12/16] Fix selectors --- cadquery/selectors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cadquery/selectors.py b/cadquery/selectors.py index a7bfc3ea..1065eb6b 100644 --- a/cadquery/selectors.py +++ b/cadquery/selectors.py @@ -170,7 +170,7 @@ class BaseDirSelector(Selector): r.append(o) 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 - tangent = o.tangentAt(None) + tangent = o.tangentAt() if self.test(tangent): r.append(o) From 60e09f6b92a7bbb00a957226de989e994b8974ed Mon Sep 17 00:00:00 2001 From: adam-urbanczyk Date: Fri, 8 Feb 2019 23:07:30 +0100 Subject: [PATCH 13/16] Add tests for new spline functionality --- tests/TestCadQuery.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/TestCadQuery.py b/tests/TestCadQuery.py index 329f02cb..da474715 100644 --- a/tests/TestCadQuery.py +++ b/tests/TestCadQuery.py @@ -362,6 +362,32 @@ class TestCadQuery(BaseTest): self.assertEqual(2, result.faces().size()) self.assertEqual(2, result.vertices().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 face + w = Wire.assembleEdges([path_closed,]) + f = Face.makeFromWires(w) + + # Spline with explicit tangets + 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): """ From 125fa54a33d376ee6e7601d42d3e72072a8d681c Mon Sep 17 00:00:00 2001 From: adam-urbanczyk Date: Sat, 9 Feb 2019 23:31:20 +0100 Subject: [PATCH 14/16] Implemented isValid() method --- cadquery/occ_impl/shapes.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index 153f969e..6d9ea56b 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -104,6 +104,8 @@ from OCC.Visualization import Tesselator from OCC.LocOpe import LocOpe_DPrism +from OCC.BRepCheck import BRepCheck_Analyzer + from math import pi, sqrt TOLERANCE = 1e-6 @@ -315,7 +317,7 @@ class Shape(object): return self.wrapped.IsEqual(other.wrapped) 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 return BoundBox._fromTopoDS(self.wrapped) @@ -464,8 +466,11 @@ class Shape(object): return [Solid(i) for i in self._entities('Solid')] def Area(self): - # when 2D density == 1, mass == area - return Shape.computeMass(self) + Properties = GProp_GProps() + brepgprop_SurfaceProperties(self.wrapped, + Properties) + + return Properties.Mass() def Volume(self): # when density == 1, mass == volume From 9e7ef243ca7f6407eb06acb0ae273571b74b55a5 Mon Sep 17 00:00:00 2001 From: adam-urbanczyk Date: Sat, 9 Feb 2019 23:31:51 +0100 Subject: [PATCH 15/16] Better testing of the new spline functionality --- tests/TestCadQuery.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/TestCadQuery.py b/tests/TestCadQuery.py index da474715..4e5dd608 100644 --- a/tests/TestCadQuery.py +++ b/tests/TestCadQuery.py @@ -380,9 +380,15 @@ class TestCadQuery(BaseTest): path_closed = Workplane("XZ").spline(pts,periodic=True).val() self.assertTrue(path_closed.IsClosed()) - # attempt to build a face + # 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 tangets path_const = Workplane("XZ").spline(pts,tangents=((0,1),(1,0))).val() From 1c92a456b3b5bac0defadccc9a10a15ba99c2beb Mon Sep 17 00:00:00 2001 From: adam-urbanczyk Date: Sat, 9 Feb 2019 23:58:04 +0100 Subject: [PATCH 16/16] Typo fix --- tests/TestCadQuery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/TestCadQuery.py b/tests/TestCadQuery.py index 4e5dd608..f815455a 100644 --- a/tests/TestCadQuery.py +++ b/tests/TestCadQuery.py @@ -390,7 +390,7 @@ class TestCadQuery(BaseTest): f = Face.makeFromWires(w) self.assertFalse(f.isValid()) - # Spline with explicit tangets + # 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))