From 684e9cfc976790e0bd0f264cfab49b7e9b7d7ad1 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sat, 21 Dec 2019 16:41:12 +0100 Subject: [PATCH 01/70] Add files via upload --- tests/TestInterpPlate.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/TestInterpPlate.py diff --git a/tests/TestInterpPlate.py b/tests/TestInterpPlate.py new file mode 100644 index 000000000..b32e9427f --- /dev/null +++ b/tests/TestInterpPlate.py @@ -0,0 +1,16 @@ +""" + Tests interpPlate functionality +""" + +import cadquery as cq + +class TestinterpPlate(BaseTest): + + def plate(self): + # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. + thickness = 0 + edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] + surface_points = [[5.,5.,5.]] + plate_0 = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness) + + return plate_0 From 3e9468d9369f1123da0c77b122cf7c590243d7e5 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sat, 21 Dec 2019 16:41:41 +0100 Subject: [PATCH 02/70] Add files via upload --- examples/Ex101_InterpPlate.py | 86 +++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 examples/Ex101_InterpPlate.py diff --git a/examples/Ex101_InterpPlate.py b/examples/Ex101_InterpPlate.py new file mode 100644 index 000000000..f838f6bee --- /dev/null +++ b/examples/Ex101_InterpPlate.py @@ -0,0 +1,86 @@ +import numpy as np +from numpy import sin, cos, pi, sqrt +import cadquery as cq + +# TEST_1 +# example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. +thickness = 0 +edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] +surface_points = [[5.,5.,5.]] +plate_0 = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness) +plate_0 = plate_0.translate((0,6*12,0)) +show_object(plate_0) + +# EXAMPLE 1 +# Plate with 5 sides and 2 bumps, one side is not co-planar with the other sides +thickness = 0.1 +edge_points = [[-7.,-7.,0.], [-3.,-10.,3.], [7.,-7.,0.], [7.,7.,0.], [-7.,7.,0.]] +edge_wire = cq.Workplane('XY').polyline([(-7.,-7.), (7.,-7.), (7.,7.), (-7.,7.)]) +#edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(0, 45, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) +edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(0, 45, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) +surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] +plate_1 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) +#plate_1 = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness) # list of (x,y,z) points instead of wires for edges +show_object(plate_1) + +# EXAMPLE 2 +# Embossed star, need to change optional parameters to obtain nice looking result. +r1=3. +r2=10. +fn=6 +thickness = 0.1 +edge_points = [[r1*cos(i * pi/fn), r1*sin(i * pi/fn)] if i%2==0 else [r2*cos(i * pi/fn), r2*sin(i * pi/fn)] for i in range(2*fn+1)] +edge_wire = cq.Workplane('XY').polyline(edge_points) +r2=4.5 +surface_points = [[r2*cos(i * pi/fn), r2*sin(i * pi/fn), 1.] for i in range(2*fn)] + [[0.,0.,-2.]] +plate_2 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=49) +#plate_2 = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=49) # list of (x,y,z) points instead of wires for edges +plate_2 = plate_2.translate((0,2*12,0)) +show_object(plate_2) + +# EXAMPLE 3 +# Points on hexagonal pattern coordinates, use of pushpoints. +r1 = 1. +N = 3 +ca = cos(30. * pi/180.) +sa = sin(30. * pi/180.) +# EVEN ROWS +x_p = np.arange(-N*r1, N*r1, ca*2*r1) +y_p = np.arange(-N*r1, N*r1, 3*r1) +x_p, y_p = np.meshgrid(x_p, y_p) +xy_p_even = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] +print("xy_p_even = ", xy_p_even) +# ODD ROWS +x_p = np.arange(-(N-0.5)*r1*ca, (N+1.5)*r1*ca, ca*2*r1) +y_p = np.arange(-(N-2+sa)*r1, (N+1+sa)*r1, 3*r1) +x_p, y_p = np.meshgrid(x_p, y_p) +xy_p_odd = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] +pts = xy_p_even + xy_p_odd +# Spike surface +thickness = 0.1 +fn = 6 +edge_points = [[r1*cos(i * 2*pi/fn), r1*sin(i * 2*pi/fn)] for i in range(fn+1)] +surface_points = [[0.25,0,0.75], [-0.25,0,0.75], [0,0.25,0.75], [0,-0.25,0.75], [0,0,2]] +edge_wire = cq.Workplane('XY').polyline(edge_points) +be = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=2, NbPtsOnCur=20, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9) +#be = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness, combine=True, clean=True, Degree=2, NbPtsOnCur=20, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9) # list of (x,y,z) points instead of wires for edges +# Pattern on sphere +def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use with each() + return be.rotate((0,0,0),(0,0,1), 30).translate(pos).val() +plate_3 = cq.Workplane('XY').pushPoints(pts).each(face) +plate_3 = plate_3.translate((0,4*11,0)) +show_object(plate_3) + +# EXAMPLE 4 +# Gyroïd, all edges are splines on different workplanes. +thickness = 0.1 +edge_points = [[[3.54, 3.54], [1.77, 0.0], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [-1.77, 0.0], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]]] +plane_list = ['XZ', 'XY', 'YZ', 'XZ', 'YZ', 'XY'] +offset_list = [-3.54, 3.54, 3.54, 3.54, -3.54, -3.54] +edge_wire = cq.Workplane(plane_list[0]).workplane(offset=-offset_list[0]).spline(edge_points[0]) +for i in range(len(edge_points)-1): + edge_wire = edge_wire.add(cq.Workplane(plane_list[i+1]).workplane(offset=-offset_list[i+1]).spline(edge_points[i+1])) +surface_points = [[0,0,0]] +plate_4 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) +plate_4 = plate_4.translate((0,5*12,0)) +show_object(plate_4) From 716519ba8ab0c7b08c14518e22c932268475a4f9 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sat, 21 Dec 2019 16:42:34 +0100 Subject: [PATCH 03/70] Add files via upload --- cadquery/cq.py | 121 +++++++++++++++++++++++++------------------------ 1 file changed, 62 insertions(+), 59 deletions(-) diff --git a/cadquery/cq.py b/cadquery/cq.py index 23bb30e47..b1c12380c 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -1187,7 +1187,7 @@ def center(self, x, y): #this workplane is centered at x=0.5,y=0.5, the center of the upper face s = Workplane().box(1,1,1).faces(">Z").workplane() - s = s.center(-0.5,-0.5) # move the center to the corner + s.center(-0.5,-0.5) # move the center to the corner t = s.circle(0.25).extrude(0.2) assert ( t.faces().size() == 9 ) # a cube with a cylindrical nub at the top right corner @@ -2728,6 +2728,67 @@ def _sweep(self, path, multisection=False, makeSolid=True, isFrenet=False, return Compound.makeCompound(toFuse) + def interpPlate(self, surf_edges, surf_pts=[], thickness=0, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9): + """ + Returns a plate surface that is 'thickness' thick, enclosed by 'surf_edge_pts' points, and going through 'surf_pts' points. + + :param surf_edges + :type 1 surf_edges: list of [x,y,z] float ordered coordinates + :type 2 surf_edges: list of unordered CadQuery wires + :param surf_pts = [] (uses only edges if []) + :type surf_pts: list of [x,y,z] float coordinates + :param thickness = 0 (returns 2D surface if 0) + :type thickness: float >= 0 + :param combine: should the results be combined with other solids on the stack + (and each other)? + :type combine: true to combine shapes, false otherwise. + :param boolean clean: call :py:meth:`clean` afterwards to have a clean shape + :param Degree = 3 (OCCT default) + :type Degree: Integer >= 2 + :param NbPtsOnCur = 15 (OCCT default) + :type: NbPtsOnCur Integer >= 15 + :param NbIter = 2 (OCCT default) + :type: NbIterInteger >= 2 + :param Anisotropie = False (OCCT default) + :type Anisotropie: Boolean + :param: Tol2d = 0.00001 (OCCT default) + :type Tol2d: float > 0 + :param Tol3d = 0.0001 (OCCT default) + :type Tol3dReal: float > 0 + :param TolAng = 0.01 (OCCT default) + :type TolAngReal: float > 0 + :param TolCurv = 0.1 (OCCT default) + :type TolCurvReal: float > 0 + :param MaxDeg = 8 (OCCT default) + :type MaxDegInteger: Integer >= 2 (?) + :param MaxSegments = 9 (OCCT default) + :type MaxSegments: Integer >= 2 (?) + """ + + # If thickness is 0, only a 2D surface will be returned. + if thickness==0: + combine=False + + # If a list of wires is provided, make a closed wire + if not isinstance(surf_edges, list): + surf_edges = [o.vals()[0] for o in surf_edges.all()] + surf_edges = Wire.assembleEdges(surf_edges) + surf_edges = surf_edges.wrapped + + # Creates interpolated plate + def _makeplate(pnt): + return Solid.interpPlate(surf_edges, surf_pts, thickness, Degree, NbPtsOnCur, NbIter, Anisotropie, Tol2d, Tol3d, TolAng, TolCurv, MaxDeg, MaxSegments) + + plates = self.eachpoint(_makeplate, True) + + # if combination is not desired, just return the created boxes + if not combine: + return plates + else: + # combine everything --> CRASHES, probably OCC 6 CAD kernel issue, better use pushpoints through each() instead + return self.union(plates, clean=clean) + + def box(self, length, width, height, centered=(True, True, True), combine=True, clean=True): """ Return a 3d box with specified dimensions for each object on the stack. @@ -2862,64 +2923,6 @@ def _makesphere(pnt): else: return self.union(spheres, clean=clean) - def wedge(self, dx, dy, dz, xmin, zmin, xmax, zmax, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1), - centered=(True, True, True), combine=True, clean=True): - """ - :param dx: Distance along the X axis - :param dy: Distance along the Y axis - :param dz: Distance along the Z axis - :param xmin: The minimum X location - :param zmin:The minimum Z location - :param xmax:The maximum X location - :param zmax: The maximum Z location - :param pnt: A vector (or tuple) for the origin of the direction for the wedge - :param dir: The direction vector (or tuple) for the major axis of the wedge - :param combine: Whether the results should be combined with other solids on the stack - (and each other) - :param clean: true to attempt to have the kernel clean up the geometry, false otherwise - :return: A wedge object for each point on the stack - - One wedge is created for each item on the current stack. If no items are on the stack, one - wedge using the current workplane center is created. - - If combine is true, the result will be a single object on the stack: - If a solid was found in the chain, the result is that solid with all wedges produced - fused onto it otherwise, the result is the combination of all the produced wedges - - If combine is false, the result will be a list of the wedges produced - """ - - # Convert the point tuple to a vector, if needed - if isinstance(pnt, tuple): - pnt = Vector(pnt) - - # Convert the direction tuple to a vector, if needed - if isinstance(dir, tuple): - dir = Vector(dir) - - def _makewedge(pnt): - (xp, yp, zp) = pnt.toTuple() - - if not centered[0]: - xp += dx / 2. - - if not centered[1]: - yp += dy / 2. - - if not centered[2]: - zp += dx / 2. - - return Solid.makeWedge(dx, dy, dz, xmin, zmin, xmax, zmax, Vector(xp, yp, zp), dir) - - # We want a wedge for each point on the workplane - wedges = self.eachpoint(_makewedge) - - # If we don't need to combine everything, just return the created wedges - if not combine: - return wedges - else: - return self.union(wedges, clean=clean) - def clean(self): """ Cleans the current solid by removing unwanted edges from the From 9d78d9eebfcba852633355b250d9b90e0c15f5b5 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sat, 21 Dec 2019 16:43:07 +0100 Subject: [PATCH 04/70] Add files via upload --- cadquery/occ_impl/shapes.py | 159 ++++++++++++++++++++++++++++++------ 1 file changed, 135 insertions(+), 24 deletions(-) diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index 946c8843a..6322b7bdc 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -110,9 +110,16 @@ from OCC.Core.BRepClass3d import BRepClass3d_SolidClassifier +from OCC.Core.GeomAbs import GeomAbs_C0 +from OCC.Extend.TopologyUtils import TopologyExplorer, WireExplorer +from OCC.Core.GeomAbs import GeomAbs_Intersection +from OCC.Core.BRepOffsetAPI import BRepOffsetAPI_MakeFilling +from OCC.Core.BRepOffset import BRepOffset_MakeOffset, BRepOffset_Skin +from OCC.Core.ShapeFix import ShapeFix_Wire +import warnings + from math import pi, sqrt from functools import reduce -import warnings TOLERANCE = 1e-6 DEG2RAD = 2 * pi / 360. @@ -254,10 +261,10 @@ def exportStl(self, fileName, precision=1e-5): return writer.Write(self.wrapped, fileName) def exportStep(self, fileName): - + writer = STEPControl_Writer() writer.Transfer(self.wrapped, STEPControl_AsIs) - + return writer.Write(fileName) def exportBrep(self, fileName): @@ -530,7 +537,7 @@ def transformShape(self, tMatrix): """ r = Shape.cast(BRepBuilderAPI_Transform(self.wrapped, - tMatrix.wrapped.Trsf()).Shape()) + tMatrix.wrapped).Shape()) r.forConstruction = self.forConstruction return r @@ -549,7 +556,7 @@ def transformGeometry(self, tMatrix): which doesnt change the underlying type of the geometry, but cannot handle skew transformations """ r = Shape.cast(BRepBuilderAPI_GTransform(self.wrapped, - tMatrix.wrapped, + gp_GTrsf(tMatrix.wrapped), True).Shape()) r.forConstruction = self.forConstruction @@ -813,18 +820,19 @@ def assembleEdges(cls, listOfEdges): :param listOfEdges: a list of Edge objects. The edges are not to be consecutive. :return: a wire with the edges assembled """ - wire_builder = BRepBuilderAPI_MakeWire() + + # begin new edges_list = TopTools_ListOfShape() for e in listOfEdges: edges_list.Append(e.wrapped) wire_builder.Add(edges_list) if wire_builder.Error(): - w1 = 'BRepBuilderAPI_MakeWire::IsDone(): returns true if this algorithm contains a valid wire. IsDone returns false if: there are no edges in the wire, or the last edge which you tried to add was not connectable = '+ str(wire_builder.IsDone()) - w2 = 'BRepBuilderAPI_MakeWire::Error(): returns the construction status. BRepBuilderAPI_WireDone if the wire is built, or another value of the BRepBuilderAPI_WireError enumeration indicating why the construction failed = ' + str(wire_builder.Error()) - warnings.warn(w1) - warnings.warn(w2) - + w1 = 'BRepBuilderAPI_MakeWire::IsDone(): returns true if this algorithm contains a valid wire. IsDone returns false if: there are no edges in the wire, or the last edge which you tried to add was not connectable = '+ str(wire_builder.IsDone()) + w2 = 'BRepBuilderAPI_MakeWire::Error(): returns the construction status. BRepBuilderAPI_WireDone if the wire is built, or another value of the BRepBuilderAPI_WireError enumeration indicating why the construction failed = ' + str(wire_builder.Error()) + warnings.warn(w1) + warnings.warn(w2) + return cls(wire_builder.Wire()) @classmethod @@ -963,6 +971,47 @@ def innerWires(self): outer = self.outerWire() return [w for w in self.Wires() if not w.isSame(outer)] + + @classmethod + def makeNSidedSurface(cls, edges, points, continuity=GeomAbs_C0, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9): + """ + Returns a surface enclosed by a closed polygon defined by 'edges' and going through 'points'. + :param points + :type points: list of gp_Pnt + :param edges + :type edges: list of TopologyExplorer().edges() + :param continuity=GeomAbs_C0 + :type continuity: OCC.Core.GeomAbs continuity condition + :param Degree = 3 (OCCT default) + :type Degree: Integer >= 2 + :param NbPtsOnCur = 15 (OCCT default) + :type: NbPtsOnCur Integer >= 15 + :param NbIter = 2 (OCCT default) + :type: NbIterInteger >= 2 + :param Anisotropie = False (OCCT default) + :type Anisotropie: Boolean + :param: Tol2d = 0.00001 (OCCT default) + :type Tol2d: float > 0 + :param Tol3d = 0.0001 (OCCT default) + :type Tol3dReal: float > 0 + :param TolAng = 0.01 (OCCT default) + :type TolAngReal: float > 0 + :param TolCurv = 0.1 (OCCT default) + :type TolCurvReal: float > 0 + :param MaxDeg = 8 (OCCT default) + :type MaxDegInteger: Integer >= 2 (?) + :param MaxSegments = 9 (OCCT default) + :type MaxSegments: Integer >= 2 (?) + """ + + n_sided = BRepOffsetAPI_MakeFilling(Degree, NbPtsOnCur, NbIter, Anisotropie, Tol2d, Tol3d, TolAng, TolCurv, MaxDeg, MaxSegments) + for edg in edges: + n_sided.Add(edg, continuity) + for pt in points: + n_sided.Add(pt) + n_sided.Build() + face = n_sided.Shape() + return cls.cast(face).fix() @classmethod def makePlane(cls, length, width, basePnt=(0, 0, 0), dir=(0, 0, 1)): @@ -1144,7 +1193,68 @@ class Solid(Shape, Mixin3D): """ a single solid """ - + + @classmethod + def interpPlate(cls, surf_edges, surf_pts, thickness, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9): + """ + Returns a plate surface that is 'thickness' thick, enclosed by 'surf_edge_pts' points, and going through 'surf_pts' points. + + :param surf_edges + :type 1 surf_edges: list of [x,y,z] float ordered coordinates + :type 2 surf_edges: list of unordered CadQuery wires + :param surf_pts = [] (uses only edges if []) + :type surf_pts: list of [x,y,z] float coordinates + :param thickness = 0 (returns 2D surface if 0) + :type thickness: float >= 0 + :param Degree = 3 (OCCT default) + :type Degree: Integer >= 2 + :param NbPtsOnCur = 15 (OCCT default) + :type: NbPtsOnCur Integer >= 15 + :param NbIter = 2 (OCCT default) + :type: NbIterInteger >= 2 + :param Anisotropie = False (OCCT default) + :type Anisotropie: Boolean + :param: Tol2d = 0.00001 (OCCT default) + :type Tol2d: float > 0 + :param Tol3d = 0.0001 (OCCT default) + :type Tol3dReal: float > 0 + :param TolAng = 0.01 (OCCT default) + :type TolAngReal: float > 0 + :param TolCurv = 0.1 (OCCT default) + :type TolCurvReal: float > 0 + :param MaxDeg = 8 (OCCT default) + :type MaxDegInteger: Integer >= 2 (?) + :param MaxSegments = 9 (OCCT default) + :type MaxSegments: Integer >= 2 (?) + """ + + # POINTS CONSTRAINTS: List of (x,y,z) points provided + pts_array = [gp_Pnt(*pt) for pt in surf_pts] + + # EDGE CONSTRAINTS: build closed polygon + if isinstance(surf_edges, list): # List of (x,y,z) points provided + e_array = [Vector(*e) for e in surf_edges] + wire_builder = BRepBuilderAPI_MakePolygon() + for e in e_array: # Create polygon from edges + wire_builder.Add(e.toPnt()) + wire_builder.Close() + w = wire_builder.Wire() + else: # Closed wires provided + w = surf_edges + edges = [i for i in TopologyExplorer(w).edges()] + + # MAKE SURFACE + continuity = GeomAbs_C0 # Fixed, changing to anything else crashes. + face = Face.makeNSidedSurface(edges, pts_array, continuity, Degree, NbPtsOnCur, NbIter, Anisotropie, Tol2d, Tol3d, TolAng, TolCurv, MaxDeg, MaxSegments) + + if thickness>0: # Thicken surface + solid = BRepOffset_MakeOffset() + solid.Initialize(face.wrapped, thickness, 1.e-5, BRepOffset_Skin, False, False, GeomAbs_Intersection, True) #The last True is important to make solid + solid.MakeOffsetShape() + return cls(solid.Shape()) + else: # Return 2D surface only + return face + @classmethod def isSolid(cls, obj): """ @@ -1235,22 +1345,23 @@ def makeLoft(cls, listOfWire, ruled=False): return cls(loft_builder.Shape()) @classmethod - def makeWedge(cls, dx, dy, dz, xmin, zmin, xmax, zmax, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1)): + def makeWedge(cls, xmin, ymin, zmin, z2min, x2min, xmax, ymax, zmax, z2max, x2max, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1)): """ Make a wedge located in pnt By default pnt=Vector(0,0,0) and dir=Vector(0,0,1) """ - - return cls(BRepPrimAPI_MakeWedge( - gp_Ax2(pnt.toPnt(), - dir.toDir()), - dx, - dy, - dz, - xmin, - zmin, - xmax, - zmax).Solid()) + return cls(BRepPrimAPI_MakeWedge(gp_Ax2(pnt.toPnt(), + dir.toDir()), + xmin, + ymin, + zmin, + z2min, + x2min, + xmax, + ymax, + zmax, + z2max, + x2max).Solid()) @classmethod def makeSphere(cls, radius, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1), angleDegrees1=0, angleDegrees2=90, angleDegrees3=360): From fc59b6d894cf2cd907dc4d21649da7b6c2fb5b99 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sat, 21 Dec 2019 17:49:19 +0100 Subject: [PATCH 05/70] Add files via upload --- cadquery/cq.py | 65 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/cadquery/cq.py b/cadquery/cq.py index b1c12380c..f3ba3d5de 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -1187,7 +1187,7 @@ def center(self, x, y): #this workplane is centered at x=0.5,y=0.5, the center of the upper face s = Workplane().box(1,1,1).faces(">Z").workplane() - s.center(-0.5,-0.5) # move the center to the corner + s = s.center(-0.5,-0.5) # move the center to the corner t = s.circle(0.25).extrude(0.2) assert ( t.faces().size() == 9 ) # a cube with a cylindrical nub at the top right corner @@ -2728,6 +2728,7 @@ def _sweep(self, path, multisection=False, makeSolid=True, isFrenet=False, return Compound.makeCompound(toFuse) + def interpPlate(self, surf_edges, surf_pts=[], thickness=0, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9): """ Returns a plate surface that is 'thickness' thick, enclosed by 'surf_edge_pts' points, and going through 'surf_pts' points. @@ -2787,8 +2788,8 @@ def _makeplate(pnt): else: # combine everything --> CRASHES, probably OCC 6 CAD kernel issue, better use pushpoints through each() instead return self.union(plates, clean=clean) - - + + def box(self, length, width, height, centered=(True, True, True), combine=True, clean=True): """ Return a 3d box with specified dimensions for each object on the stack. @@ -2923,6 +2924,64 @@ def _makesphere(pnt): else: return self.union(spheres, clean=clean) + def wedge(self, dx, dy, dz, xmin, zmin, xmax, zmax, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1), + centered=(True, True, True), combine=True, clean=True): + """ + :param dx: Distance along the X axis + :param dy: Distance along the Y axis + :param dz: Distance along the Z axis + :param xmin: The minimum X location + :param zmin:The minimum Z location + :param xmax:The maximum X location + :param zmax: The maximum Z location + :param pnt: A vector (or tuple) for the origin of the direction for the wedge + :param dir: The direction vector (or tuple) for the major axis of the wedge + :param combine: Whether the results should be combined with other solids on the stack + (and each other) + :param clean: true to attempt to have the kernel clean up the geometry, false otherwise + :return: A wedge object for each point on the stack + + One wedge is created for each item on the current stack. If no items are on the stack, one + wedge using the current workplane center is created. + + If combine is true, the result will be a single object on the stack: + If a solid was found in the chain, the result is that solid with all wedges produced + fused onto it otherwise, the result is the combination of all the produced wedges + + If combine is false, the result will be a list of the wedges produced + """ + + # Convert the point tuple to a vector, if needed + if isinstance(pnt, tuple): + pnt = Vector(pnt) + + # Convert the direction tuple to a vector, if needed + if isinstance(dir, tuple): + dir = Vector(dir) + + def _makewedge(pnt): + (xp, yp, zp) = pnt.toTuple() + + if not centered[0]: + xp += dx / 2. + + if not centered[1]: + yp += dy / 2. + + if not centered[2]: + zp += dx / 2. + + return Solid.makeWedge(dx, dy, dz, xmin, zmin, xmax, zmax, Vector(xp, yp, zp), dir) + + # We want a wedge for each point on the workplane + wedges = self.eachpoint(_makewedge) + + # If we don't need to combine everything, just return the created wedges + if not combine: + return wedges + else: + return self.union(wedges, clean=clean) + def clean(self): """ Cleans the current solid by removing unwanted edges from the From 0227c78f3188ef0e4c86dc5e74420a5bf7260f49 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sat, 21 Dec 2019 17:49:51 +0100 Subject: [PATCH 06/70] Add files via upload --- cadquery/occ_impl/shapes.py | 40 ++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index 6322b7bdc..836f9902e 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -116,8 +116,8 @@ from OCC.Core.BRepOffsetAPI import BRepOffsetAPI_MakeFilling from OCC.Core.BRepOffset import BRepOffset_MakeOffset, BRepOffset_Skin from OCC.Core.ShapeFix import ShapeFix_Wire -import warnings +import warnings from math import pi, sqrt from functools import reduce @@ -261,10 +261,10 @@ def exportStl(self, fileName, precision=1e-5): return writer.Write(self.wrapped, fileName) def exportStep(self, fileName): - + writer = STEPControl_Writer() writer.Transfer(self.wrapped, STEPControl_AsIs) - + return writer.Write(fileName) def exportBrep(self, fileName): @@ -537,7 +537,7 @@ def transformShape(self, tMatrix): """ r = Shape.cast(BRepBuilderAPI_Transform(self.wrapped, - tMatrix.wrapped).Shape()) + tMatrix.wrapped.Trsf()).Shape()) r.forConstruction = self.forConstruction return r @@ -556,7 +556,7 @@ def transformGeometry(self, tMatrix): which doesnt change the underlying type of the geometry, but cannot handle skew transformations """ r = Shape.cast(BRepBuilderAPI_GTransform(self.wrapped, - gp_GTrsf(tMatrix.wrapped), + tMatrix.wrapped, True).Shape()) r.forConstruction = self.forConstruction @@ -822,7 +822,6 @@ def assembleEdges(cls, listOfEdges): """ wire_builder = BRepBuilderAPI_MakeWire() - # begin new edges_list = TopTools_ListOfShape() for e in listOfEdges: edges_list.Append(e.wrapped) @@ -971,7 +970,7 @@ def innerWires(self): outer = self.outerWire() return [w for w in self.Wires() if not w.isSame(outer)] - + @classmethod def makeNSidedSurface(cls, edges, points, continuity=GeomAbs_C0, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9): """ @@ -1254,7 +1253,7 @@ def interpPlate(cls, surf_edges, surf_pts, thickness, Degree=3, NbPtsOnCur=15, N return cls(solid.Shape()) else: # Return 2D surface only return face - + @classmethod def isSolid(cls, obj): """ @@ -1345,23 +1344,22 @@ def makeLoft(cls, listOfWire, ruled=False): return cls(loft_builder.Shape()) @classmethod - def makeWedge(cls, xmin, ymin, zmin, z2min, x2min, xmax, ymax, zmax, z2max, x2max, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1)): + def makeWedge(cls, dx, dy, dz, xmin, zmin, xmax, zmax, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1)): """ Make a wedge located in pnt By default pnt=Vector(0,0,0) and dir=Vector(0,0,1) """ - return cls(BRepPrimAPI_MakeWedge(gp_Ax2(pnt.toPnt(), - dir.toDir()), - xmin, - ymin, - zmin, - z2min, - x2min, - xmax, - ymax, - zmax, - z2max, - x2max).Solid()) + + return cls(BRepPrimAPI_MakeWedge( + gp_Ax2(pnt.toPnt(), + dir.toDir()), + dx, + dy, + dz, + xmin, + zmin, + xmax, + zmax).Solid()) @classmethod def makeSphere(cls, radius, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1), angleDegrees1=0, angleDegrees2=90, angleDegrees3=360): From 68e0481ca0e34e7a375514885fe6d430ea1e92f6 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sat, 21 Dec 2019 18:03:38 +0100 Subject: [PATCH 07/70] Add files via upload --- examples/Ex101_InterpPlate.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/Ex101_InterpPlate.py b/examples/Ex101_InterpPlate.py index f838f6bee..c8074f5fa 100644 --- a/examples/Ex101_InterpPlate.py +++ b/examples/Ex101_InterpPlate.py @@ -16,8 +16,8 @@ thickness = 0.1 edge_points = [[-7.,-7.,0.], [-3.,-10.,3.], [7.,-7.,0.], [7.,7.,0.], [-7.,7.,0.]] edge_wire = cq.Workplane('XY').polyline([(-7.,-7.), (7.,-7.), (7.,7.), (-7.,7.)]) -#edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(0, 45, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) -edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(0, 45, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) +#edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) +edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) # In CadQuery Sept-2019 it worked with rotate=cq.Vector(0, 45, 0). In CadQuery Dec-2019 rotate=cq.Vector(45, 0, 0) only closes the wire. surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] plate_1 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) #plate_1 = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness) # list of (x,y,z) points instead of wires for edges @@ -49,7 +49,6 @@ y_p = np.arange(-N*r1, N*r1, 3*r1) x_p, y_p = np.meshgrid(x_p, y_p) xy_p_even = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] -print("xy_p_even = ", xy_p_even) # ODD ROWS x_p = np.arange(-(N-0.5)*r1*ca, (N+1.5)*r1*ca, ca*2*r1) y_p = np.arange(-(N-2+sa)*r1, (N+1+sa)*r1, 3*r1) From e25608fa55d982df10a4de7ed50c1be65ae1b2bd Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sat, 21 Dec 2019 19:39:47 +0100 Subject: [PATCH 08/70] Add files via upload --- tests/TestinterpPlate.py | 96 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 tests/TestinterpPlate.py diff --git a/tests/TestinterpPlate.py b/tests/TestinterpPlate.py new file mode 100644 index 000000000..3cfefa0b2 --- /dev/null +++ b/tests/TestinterpPlate.py @@ -0,0 +1,96 @@ +""" + Tests interpPlate functionality +""" + +import cadquery as cq + +class TestinterpPlate(BaseTest): + + def plate_0(self): + # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. + thickness = 0 + edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] + surface_points = [[5.,5.,5.]] + plate_0 = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness) + + return plate_0 + + + def plate_1(self): + # Plate with 5 sides and 2 bumps, one side is not co-planar with the other sides + thickness = 0.1 + edge_points = [[-7.,-7.,0.], [-3.,-10.,3.], [7.,-7.,0.], [7.,7.,0.], [-7.,7.,0.]] + edge_wire = cq.Workplane('XY').polyline([(-7.,-7.), (7.,-7.), (7.,7.), (-7.,7.)]) + #edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) + edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) # In CadQuery Sept-2019 it worked with rotate=cq.Vector(0, 45, 0). In CadQuery Dec-2019 rotate=cq.Vector(45, 0, 0) only closes the wire. + surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] + plate_1 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) + + return plate_1 + + + def plate_2(self): + # Embossed star, need to change optional parameters to obtain nice looking result. + r1=3. + r2=10. + fn=6 + thickness = 0.1 + edge_points = [[r1*cos(i * pi/fn), r1*sin(i * pi/fn)] if i%2==0 else [r2*cos(i * pi/fn), r2*sin(i * pi/fn)] for i in range(2*fn+1)] + edge_wire = cq.Workplane('XY').polyline(edge_points) + r2=4.5 + surface_points = [[r2*cos(i * pi/fn), r2*sin(i * pi/fn), 1.] for i in range(2*fn)] + [[0.,0.,-2.]] + plate_2 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=49) + plate_2 = plate_2.translate((0,2*12,0)) + + return plate_2 + + + def plate_3(self): + # Points on hexagonal pattern coordinates, use of pushpoints. + r1 = 1. + N = 3 + ca = cos(30. * pi/180.) + sa = sin(30. * pi/180.) + # EVEN ROWS + x_p = np.arange(-N*r1, N*r1, ca*2*r1) + y_p = np.arange(-N*r1, N*r1, 3*r1) + x_p, y_p = np.meshgrid(x_p, y_p) + xy_p_even = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] + # ODD ROWS + x_p = np.arange(-(N-0.5)*r1*ca, (N+1.5)*r1*ca, ca*2*r1) + y_p = np.arange(-(N-2+sa)*r1, (N+1+sa)*r1, 3*r1) + x_p, y_p = np.meshgrid(x_p, y_p) + xy_p_odd = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] + pts = xy_p_even + xy_p_odd + # Spike surface + thickness = 0.1 + fn = 6 + edge_points = [[r1*cos(i * 2*pi/fn), r1*sin(i * 2*pi/fn)] for i in range(fn+1)] + surface_points = [[0.25,0,0.75], [-0.25,0,0.75], [0,0.25,0.75], [0,-0.25,0.75], [0,0,2]] + edge_wire = cq.Workplane('XY').polyline(edge_points) + be = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=2, NbPtsOnCur=20, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9) + # Pattern on sphere + def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use with each() + return be.rotate((0,0,0),(0,0,1), 30).translate(pos).val() + plate_3 = cq.Workplane('XY').pushPoints(pts).each(face) + plate_3 = plate_3.translate((0,4*11,0)) + + return plate_3 + + + def plate_4(self): + # Gyroïd, all edges are splines on different workplanes. + thickness = 0.1 + edge_points = [[[3.54, 3.54], [1.77, 0.0], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [-1.77, 0.0], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]]] + plane_list = ['XZ', 'XY', 'YZ', 'XZ', 'YZ', 'XY'] + offset_list = [-3.54, 3.54, 3.54, 3.54, -3.54, -3.54] + edge_wire = cq.Workplane(plane_list[0]).workplane(offset=-offset_list[0]).spline(edge_points[0]) + for i in range(len(edge_points)-1): + edge_wire = edge_wire.add(cq.Workplane(plane_list[i+1]).workplane(offset=-offset_list[i+1]).spline(edge_points[i+1])) + surface_points = [[0,0,0]] + plate_4 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) + plate_4 = plate_4.translate((0,5*12,0)) + + return plate_4 + + From 8b87bed0f8266d608aa7a214627f9e073e219a59 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sat, 21 Dec 2019 22:24:22 +0100 Subject: [PATCH 09/70] Add files via upload --- tests/TestInterpPlate.py | 82 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/tests/TestInterpPlate.py b/tests/TestInterpPlate.py index b32e9427f..3d53f889e 100644 --- a/tests/TestInterpPlate.py +++ b/tests/TestInterpPlate.py @@ -6,7 +6,7 @@ class TestinterpPlate(BaseTest): - def plate(self): + def plate_0(self): # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. thickness = 0 edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] @@ -14,3 +14,83 @@ def plate(self): plate_0 = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness) return plate_0 + + + def plate_1(self): + # Plate with 5 sides and 2 bumps, one side is not co-planar with the other sides + thickness = 0.1 + edge_points = [[-7.,-7.,0.], [-3.,-10.,3.], [7.,-7.,0.], [7.,7.,0.], [-7.,7.,0.]] + edge_wire = cq.Workplane('XY').polyline([(-7.,-7.), (7.,-7.), (7.,7.), (-7.,7.)]) + #edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(0, 45, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) + edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(0, 45, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) # In CadQuery Sept-2019 it worked with rotate=cq.Vector(0, 45, 0). In CadQuery Dec-2019 rotate=cq.Vector(45, 0, 0) only closes the wire. + surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] + plate_1 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) + + return plate_1 + + + def plate_2(self): + # Embossed star, need to change optional parameters to obtain nice looking result. + r1=3. + r2=10. + fn=6 + thickness = 0.1 + edge_points = [[r1*cos(i * pi/fn), r1*sin(i * pi/fn)] if i%2==0 else [r2*cos(i * pi/fn), r2*sin(i * pi/fn)] for i in range(2*fn+1)] + edge_wire = cq.Workplane('XY').polyline(edge_points) + r2=4.5 + surface_points = [[r2*cos(i * pi/fn), r2*sin(i * pi/fn), 1.] for i in range(2*fn)] + [[0.,0.,-2.]] + plate_2 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=49) + plate_2 = plate_2.translate((0,2*12,0)) + + return plate_2 + + + def plate_3(self): + # Points on hexagonal pattern coordinates, use of pushpoints. + r1 = 1. + N = 3 + ca = cos(30. * pi/180.) + sa = sin(30. * pi/180.) + # EVEN ROWS + x_p = np.arange(-N*r1, N*r1, ca*2*r1) + y_p = np.arange(-N*r1, N*r1, 3*r1) + x_p, y_p = np.meshgrid(x_p, y_p) + xy_p_even = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] + # ODD ROWS + x_p = np.arange(-(N-0.5)*r1*ca, (N+1.5)*r1*ca, ca*2*r1) + y_p = np.arange(-(N-2+sa)*r1, (N+1+sa)*r1, 3*r1) + x_p, y_p = np.meshgrid(x_p, y_p) + xy_p_odd = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] + pts = xy_p_even + xy_p_odd + # Spike surface + thickness = 0.1 + fn = 6 + edge_points = [[r1*cos(i * 2*pi/fn), r1*sin(i * 2*pi/fn)] for i in range(fn+1)] + surface_points = [[0.25,0,0.75], [-0.25,0,0.75], [0,0.25,0.75], [0,-0.25,0.75], [0,0,2]] + edge_wire = cq.Workplane('XY').polyline(edge_points) + be = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=2, NbPtsOnCur=20, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9) + # Pattern on sphere + def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use with each() + return be.rotate((0,0,0),(0,0,1), 30).translate(pos).val() + plate_3 = cq.Workplane('XY').pushPoints(pts).each(face) + plate_3 = plate_3.translate((0,4*11,0)) + + return plate_3 + + + def plate_4(self): + # Gyroïd, all edges are splines on different workplanes. + thickness = 0.1 + edge_points = [[[3.54, 3.54], [1.77, 0.0], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [-1.77, 0.0], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]]] + plane_list = ['XZ', 'XY', 'YZ', 'XZ', 'YZ', 'XY'] + offset_list = [-3.54, 3.54, 3.54, 3.54, -3.54, -3.54] + edge_wire = cq.Workplane(plane_list[0]).workplane(offset=-offset_list[0]).spline(edge_points[0]) + for i in range(len(edge_points)-1): + edge_wire = edge_wire.add(cq.Workplane(plane_list[i+1]).workplane(offset=-offset_list[i+1]).spline(edge_points[i+1])) + surface_points = [[0,0,0]] + plate_4 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) + plate_4 = plate_4.translate((0,5*12,0)) + + return plate_4 + + From 303ab2bba9a66f420e8dbd6b4ac0e3f20ce63ecd Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sat, 21 Dec 2019 22:24:47 +0100 Subject: [PATCH 10/70] Add files via upload --- examples/Ex101_InterpPlate.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/Ex101_InterpPlate.py b/examples/Ex101_InterpPlate.py index c8074f5fa..f838f6bee 100644 --- a/examples/Ex101_InterpPlate.py +++ b/examples/Ex101_InterpPlate.py @@ -16,8 +16,8 @@ thickness = 0.1 edge_points = [[-7.,-7.,0.], [-3.,-10.,3.], [7.,-7.,0.], [7.,7.,0.], [-7.,7.,0.]] edge_wire = cq.Workplane('XY').polyline([(-7.,-7.), (7.,-7.), (7.,7.), (-7.,7.)]) -#edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) -edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) # In CadQuery Sept-2019 it worked with rotate=cq.Vector(0, 45, 0). In CadQuery Dec-2019 rotate=cq.Vector(45, 0, 0) only closes the wire. +#edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(0, 45, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) +edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(0, 45, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] plate_1 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) #plate_1 = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness) # list of (x,y,z) points instead of wires for edges @@ -49,6 +49,7 @@ y_p = np.arange(-N*r1, N*r1, 3*r1) x_p, y_p = np.meshgrid(x_p, y_p) xy_p_even = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] +print("xy_p_even = ", xy_p_even) # ODD ROWS x_p = np.arange(-(N-0.5)*r1*ca, (N+1.5)*r1*ca, ca*2*r1) y_p = np.arange(-(N-2+sa)*r1, (N+1+sa)*r1, 3*r1) From 450942a0d422d518ea6193b8e19d9d963f80fc0d Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sat, 21 Dec 2019 22:25:11 +0100 Subject: [PATCH 11/70] Add files via upload --- cadquery/cq.py | 65 +++----------------------------------------------- 1 file changed, 3 insertions(+), 62 deletions(-) diff --git a/cadquery/cq.py b/cadquery/cq.py index f3ba3d5de..b1c12380c 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -1187,7 +1187,7 @@ def center(self, x, y): #this workplane is centered at x=0.5,y=0.5, the center of the upper face s = Workplane().box(1,1,1).faces(">Z").workplane() - s = s.center(-0.5,-0.5) # move the center to the corner + s.center(-0.5,-0.5) # move the center to the corner t = s.circle(0.25).extrude(0.2) assert ( t.faces().size() == 9 ) # a cube with a cylindrical nub at the top right corner @@ -2728,7 +2728,6 @@ def _sweep(self, path, multisection=False, makeSolid=True, isFrenet=False, return Compound.makeCompound(toFuse) - def interpPlate(self, surf_edges, surf_pts=[], thickness=0, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9): """ Returns a plate surface that is 'thickness' thick, enclosed by 'surf_edge_pts' points, and going through 'surf_pts' points. @@ -2788,8 +2787,8 @@ def _makeplate(pnt): else: # combine everything --> CRASHES, probably OCC 6 CAD kernel issue, better use pushpoints through each() instead return self.union(plates, clean=clean) - - + + def box(self, length, width, height, centered=(True, True, True), combine=True, clean=True): """ Return a 3d box with specified dimensions for each object on the stack. @@ -2924,64 +2923,6 @@ def _makesphere(pnt): else: return self.union(spheres, clean=clean) - def wedge(self, dx, dy, dz, xmin, zmin, xmax, zmax, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1), - centered=(True, True, True), combine=True, clean=True): - """ - :param dx: Distance along the X axis - :param dy: Distance along the Y axis - :param dz: Distance along the Z axis - :param xmin: The minimum X location - :param zmin:The minimum Z location - :param xmax:The maximum X location - :param zmax: The maximum Z location - :param pnt: A vector (or tuple) for the origin of the direction for the wedge - :param dir: The direction vector (or tuple) for the major axis of the wedge - :param combine: Whether the results should be combined with other solids on the stack - (and each other) - :param clean: true to attempt to have the kernel clean up the geometry, false otherwise - :return: A wedge object for each point on the stack - - One wedge is created for each item on the current stack. If no items are on the stack, one - wedge using the current workplane center is created. - - If combine is true, the result will be a single object on the stack: - If a solid was found in the chain, the result is that solid with all wedges produced - fused onto it otherwise, the result is the combination of all the produced wedges - - If combine is false, the result will be a list of the wedges produced - """ - - # Convert the point tuple to a vector, if needed - if isinstance(pnt, tuple): - pnt = Vector(pnt) - - # Convert the direction tuple to a vector, if needed - if isinstance(dir, tuple): - dir = Vector(dir) - - def _makewedge(pnt): - (xp, yp, zp) = pnt.toTuple() - - if not centered[0]: - xp += dx / 2. - - if not centered[1]: - yp += dy / 2. - - if not centered[2]: - zp += dx / 2. - - return Solid.makeWedge(dx, dy, dz, xmin, zmin, xmax, zmax, Vector(xp, yp, zp), dir) - - # We want a wedge for each point on the workplane - wedges = self.eachpoint(_makewedge) - - # If we don't need to combine everything, just return the created wedges - if not combine: - return wedges - else: - return self.union(wedges, clean=clean) - def clean(self): """ Cleans the current solid by removing unwanted edges from the From 1410a9516cfeb7ff89ff8a622a994baccc5040cc Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sat, 21 Dec 2019 22:25:35 +0100 Subject: [PATCH 12/70] Add files via upload --- cadquery/occ_impl/shapes.py | 40 +++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index 836f9902e..6322b7bdc 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -116,8 +116,8 @@ from OCC.Core.BRepOffsetAPI import BRepOffsetAPI_MakeFilling from OCC.Core.BRepOffset import BRepOffset_MakeOffset, BRepOffset_Skin from OCC.Core.ShapeFix import ShapeFix_Wire - import warnings + from math import pi, sqrt from functools import reduce @@ -261,10 +261,10 @@ def exportStl(self, fileName, precision=1e-5): return writer.Write(self.wrapped, fileName) def exportStep(self, fileName): - + writer = STEPControl_Writer() writer.Transfer(self.wrapped, STEPControl_AsIs) - + return writer.Write(fileName) def exportBrep(self, fileName): @@ -537,7 +537,7 @@ def transformShape(self, tMatrix): """ r = Shape.cast(BRepBuilderAPI_Transform(self.wrapped, - tMatrix.wrapped.Trsf()).Shape()) + tMatrix.wrapped).Shape()) r.forConstruction = self.forConstruction return r @@ -556,7 +556,7 @@ def transformGeometry(self, tMatrix): which doesnt change the underlying type of the geometry, but cannot handle skew transformations """ r = Shape.cast(BRepBuilderAPI_GTransform(self.wrapped, - tMatrix.wrapped, + gp_GTrsf(tMatrix.wrapped), True).Shape()) r.forConstruction = self.forConstruction @@ -822,6 +822,7 @@ def assembleEdges(cls, listOfEdges): """ wire_builder = BRepBuilderAPI_MakeWire() + # begin new edges_list = TopTools_ListOfShape() for e in listOfEdges: edges_list.Append(e.wrapped) @@ -970,7 +971,7 @@ def innerWires(self): outer = self.outerWire() return [w for w in self.Wires() if not w.isSame(outer)] - + @classmethod def makeNSidedSurface(cls, edges, points, continuity=GeomAbs_C0, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9): """ @@ -1253,7 +1254,7 @@ def interpPlate(cls, surf_edges, surf_pts, thickness, Degree=3, NbPtsOnCur=15, N return cls(solid.Shape()) else: # Return 2D surface only return face - + @classmethod def isSolid(cls, obj): """ @@ -1344,22 +1345,23 @@ def makeLoft(cls, listOfWire, ruled=False): return cls(loft_builder.Shape()) @classmethod - def makeWedge(cls, dx, dy, dz, xmin, zmin, xmax, zmax, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1)): + def makeWedge(cls, xmin, ymin, zmin, z2min, x2min, xmax, ymax, zmax, z2max, x2max, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1)): """ Make a wedge located in pnt By default pnt=Vector(0,0,0) and dir=Vector(0,0,1) """ - - return cls(BRepPrimAPI_MakeWedge( - gp_Ax2(pnt.toPnt(), - dir.toDir()), - dx, - dy, - dz, - xmin, - zmin, - xmax, - zmax).Solid()) + return cls(BRepPrimAPI_MakeWedge(gp_Ax2(pnt.toPnt(), + dir.toDir()), + xmin, + ymin, + zmin, + z2min, + x2min, + xmax, + ymax, + zmax, + z2max, + x2max).Solid()) @classmethod def makeSphere(cls, radius, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1), angleDegrees1=0, angleDegrees2=90, angleDegrees3=360): From 6c55cbfbb8578a60b7b84a7110dc62ae10a9594c Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sun, 22 Dec 2019 20:37:44 +0100 Subject: [PATCH 13/70] Add files via upload --- cadquery/cq.py | 123 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 120 insertions(+), 3 deletions(-) diff --git a/cadquery/cq.py b/cadquery/cq.py index b1c12380c..be9321db7 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -1187,7 +1187,7 @@ def center(self, x, y): #this workplane is centered at x=0.5,y=0.5, the center of the upper face s = Workplane().box(1,1,1).faces(">Z").workplane() - s.center(-0.5,-0.5) # move the center to the corner + s = s.center(-0.5,-0.5) # move the center to the corner t = s.circle(0.25).extrude(0.2) assert ( t.faces().size() == 9 ) # a cube with a cylindrical nub at the top right corner @@ -2787,8 +2787,67 @@ def _makeplate(pnt): else: # combine everything --> CRASHES, probably OCC 6 CAD kernel issue, better use pushpoints through each() instead return self.union(plates, clean=clean) - - + + def interpPlate(self, surf_edges, surf_pts=[], thickness=0, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9): + """ + Returns a plate surface that is 'thickness' thick, enclosed by 'surf_edge_pts' points, and going through 'surf_pts' points. + + :param surf_edges + :type 1 surf_edges: list of [x,y,z] float ordered coordinates + :type 2 surf_edges: list of unordered CadQuery wires + :param surf_pts = [] (uses only edges if []) + :type surf_pts: list of [x,y,z] float coordinates + :param thickness = 0 (returns 2D surface if 0) + :type thickness: float >= 0 + :param combine: should the results be combined with other solids on the stack + (and each other)? + :type combine: true to combine shapes, false otherwise. + :param boolean clean: call :py:meth:`clean` afterwards to have a clean shape + :param Degree = 3 (OCCT default) + :type Degree: Integer >= 2 + :param NbPtsOnCur = 15 (OCCT default) + :type: NbPtsOnCur Integer >= 15 + :param NbIter = 2 (OCCT default) + :type: NbIterInteger >= 2 + :param Anisotropie = False (OCCT default) + :type Anisotropie: Boolean + :param: Tol2d = 0.00001 (OCCT default) + :type Tol2d: float > 0 + :param Tol3d = 0.0001 (OCCT default) + :type Tol3dReal: float > 0 + :param TolAng = 0.01 (OCCT default) + :type TolAngReal: float > 0 + :param TolCurv = 0.1 (OCCT default) + :type TolCurvReal: float > 0 + :param MaxDeg = 8 (OCCT default) + :type MaxDegInteger: Integer >= 2 (?) + :param MaxSegments = 9 (OCCT default) + :type MaxSegments: Integer >= 2 (?) + """ + + # If thickness is 0, only a 2D surface will be returned. + if thickness==0: + combine=False + + # If a list of wires is provided, make a closed wire + if not isinstance(surf_edges, list): + surf_edges = [o.vals()[0] for o in surf_edges.all()] + surf_edges = Wire.assembleEdges(surf_edges) + surf_edges = surf_edges.wrapped + + # Creates interpolated plate + def _makeplate(pnt): + return Solid.interpPlate(surf_edges, surf_pts, thickness, Degree, NbPtsOnCur, NbIter, Anisotropie, Tol2d, Tol3d, TolAng, TolCurv, MaxDeg, MaxSegments) + + plates = self.eachpoint(_makeplate, True) + + # if combination is not desired, just return the created boxes + if not combine: + return plates + else: + # combine everything --> CRASHES, probably OCC 6 CAD kernel issue, better use pushpoints through each() instead + return self.union(plates, clean=clean) + def box(self, length, width, height, centered=(True, True, True), combine=True, clean=True): """ Return a 3d box with specified dimensions for each object on the stack. @@ -2923,6 +2982,64 @@ def _makesphere(pnt): else: return self.union(spheres, clean=clean) + def wedge(self, dx, dy, dz, xmin, zmin, xmax, zmax, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1), + centered=(True, True, True), combine=True, clean=True): + """ + :param dx: Distance along the X axis + :param dy: Distance along the Y axis + :param dz: Distance along the Z axis + :param xmin: The minimum X location + :param zmin:The minimum Z location + :param xmax:The maximum X location + :param zmax: The maximum Z location + :param pnt: A vector (or tuple) for the origin of the direction for the wedge + :param dir: The direction vector (or tuple) for the major axis of the wedge + :param combine: Whether the results should be combined with other solids on the stack + (and each other) + :param clean: true to attempt to have the kernel clean up the geometry, false otherwise + :return: A wedge object for each point on the stack + + One wedge is created for each item on the current stack. If no items are on the stack, one + wedge using the current workplane center is created. + + If combine is true, the result will be a single object on the stack: + If a solid was found in the chain, the result is that solid with all wedges produced + fused onto it otherwise, the result is the combination of all the produced wedges + + If combine is false, the result will be a list of the wedges produced + """ + + # Convert the point tuple to a vector, if needed + if isinstance(pnt, tuple): + pnt = Vector(pnt) + + # Convert the direction tuple to a vector, if needed + if isinstance(dir, tuple): + dir = Vector(dir) + + def _makewedge(pnt): + (xp, yp, zp) = pnt.toTuple() + + if not centered[0]: + xp += dx / 2. + + if not centered[1]: + yp += dy / 2. + + if not centered[2]: + zp += dx / 2. + + return Solid.makeWedge(dx, dy, dz, xmin, zmin, xmax, zmax, Vector(xp, yp, zp), dir) + + # We want a wedge for each point on the workplane + wedges = self.eachpoint(_makewedge) + + # If we don't need to combine everything, just return the created wedges + if not combine: + return wedges + else: + return self.union(wedges, clean=clean) + def clean(self): """ Cleans the current solid by removing unwanted edges from the From 278b4d6106274d90d2d249254f72087177073b4e Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sun, 22 Dec 2019 20:39:23 +0100 Subject: [PATCH 14/70] Add files via upload --- cadquery/occ_impl/shapes.py | 160 ++++++++++++++++++++++++++++++------ 1 file changed, 137 insertions(+), 23 deletions(-) diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index 6322b7bdc..0f34a0660 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -88,6 +88,12 @@ from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs, STEPControl_Controller +from OCC.Core.Interface import Interface_Static_SetCVal +from OCC.Core.Interface import Interface_Static_SetIVal +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs, STEPControl_Controller +from OCC.Core.Interface import Interface_Static_SetCVal +from OCC.Core.Interface import Interface_Static_SetIVal from OCC.Core.BRepMesh import BRepMesh_IncrementalMesh from OCC.Core.StlAPI import StlAPI_Writer @@ -116,8 +122,16 @@ from OCC.Core.BRepOffsetAPI import BRepOffsetAPI_MakeFilling from OCC.Core.BRepOffset import BRepOffset_MakeOffset, BRepOffset_Skin from OCC.Core.ShapeFix import ShapeFix_Wire + import warnings +from OCC.Core.GeomAbs import GeomAbs_C0 +from OCC.Extend.TopologyUtils import TopologyExplorer, WireExplorer +from OCC.Core.GeomAbs import GeomAbs_Intersection +from OCC.Core.BRepOffsetAPI import BRepOffsetAPI_MakeFilling +from OCC.Core.BRepOffset import BRepOffset_MakeOffset, BRepOffset_Skin +from OCC.Core.ShapeFix import ShapeFix_Wire +import warnings from math import pi, sqrt from functools import reduce @@ -261,12 +275,12 @@ def exportStl(self, fileName, precision=1e-5): return writer.Write(self.wrapped, fileName) def exportStep(self, fileName): - + writer = STEPControl_Writer() writer.Transfer(self.wrapped, STEPControl_AsIs) - - return writer.Write(fileName) + return writer.Write(fileName) + def exportBrep(self, fileName): """ Export given shape to a BREP file @@ -537,7 +551,7 @@ def transformShape(self, tMatrix): """ r = Shape.cast(BRepBuilderAPI_Transform(self.wrapped, - tMatrix.wrapped).Shape()) + tMatrix.wrapped.Trsf()).Shape()) r.forConstruction = self.forConstruction return r @@ -556,7 +570,7 @@ def transformGeometry(self, tMatrix): which doesnt change the underlying type of the geometry, but cannot handle skew transformations """ r = Shape.cast(BRepBuilderAPI_GTransform(self.wrapped, - gp_GTrsf(tMatrix.wrapped), + tMatrix.wrapped, True).Shape()) r.forConstruction = self.forConstruction @@ -822,7 +836,6 @@ def assembleEdges(cls, listOfEdges): """ wire_builder = BRepBuilderAPI_MakeWire() - # begin new edges_list = TopTools_ListOfShape() for e in listOfEdges: edges_list.Append(e.wrapped) @@ -835,7 +848,7 @@ def assembleEdges(cls, listOfEdges): return cls(wire_builder.Wire()) - @classmethod + @classmethod def makeCircle(cls, radius, center, normal): """ Makes a Circle centered at the provided point, having normal in the provided direction @@ -971,7 +984,48 @@ def innerWires(self): outer = self.outerWire() return [w for w in self.Wires() if not w.isSame(outer)] - + + @classmethod + def makeNSidedSurface(cls, edges, points, continuity=GeomAbs_C0, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9): + """ + Returns a surface enclosed by a closed polygon defined by 'edges' and going through 'points'. + :param points + :type points: list of gp_Pnt + :param edges + :type edges: list of TopologyExplorer().edges() + :param continuity=GeomAbs_C0 + :type continuity: OCC.Core.GeomAbs continuity condition + :param Degree = 3 (OCCT default) + :type Degree: Integer >= 2 + :param NbPtsOnCur = 15 (OCCT default) + :type: NbPtsOnCur Integer >= 15 + :param NbIter = 2 (OCCT default) + :type: NbIterInteger >= 2 + :param Anisotropie = False (OCCT default) + :type Anisotropie: Boolean + :param: Tol2d = 0.00001 (OCCT default) + :type Tol2d: float > 0 + :param Tol3d = 0.0001 (OCCT default) + :type Tol3dReal: float > 0 + :param TolAng = 0.01 (OCCT default) + :type TolAngReal: float > 0 + :param TolCurv = 0.1 (OCCT default) + :type TolCurvReal: float > 0 + :param MaxDeg = 8 (OCCT default) + :type MaxDegInteger: Integer >= 2 (?) + :param MaxSegments = 9 (OCCT default) + :type MaxSegments: Integer >= 2 (?) + """ + + n_sided = BRepOffsetAPI_MakeFilling(Degree, NbPtsOnCur, NbIter, Anisotropie, Tol2d, Tol3d, TolAng, TolCurv, MaxDeg, MaxSegments) + for edg in edges: + n_sided.Add(edg, continuity) + for pt in points: + n_sided.Add(pt) + n_sided.Build() + face = n_sided.Shape() + return cls.cast(face).fix() + @classmethod def makeNSidedSurface(cls, edges, points, continuity=GeomAbs_C0, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9): """ @@ -1193,7 +1247,7 @@ class Solid(Shape, Mixin3D): """ a single solid """ - + @classmethod def interpPlate(cls, surf_edges, surf_pts, thickness, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9): """ @@ -1254,7 +1308,68 @@ def interpPlate(cls, surf_edges, surf_pts, thickness, Degree=3, NbPtsOnCur=15, N return cls(solid.Shape()) else: # Return 2D surface only return face - + + @classmethod + def interpPlate(cls, surf_edges, surf_pts, thickness, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9): + """ + Returns a plate surface that is 'thickness' thick, enclosed by 'surf_edge_pts' points, and going through 'surf_pts' points. + + :param surf_edges + :type 1 surf_edges: list of [x,y,z] float ordered coordinates + :type 2 surf_edges: list of unordered CadQuery wires + :param surf_pts = [] (uses only edges if []) + :type surf_pts: list of [x,y,z] float coordinates + :param thickness = 0 (returns 2D surface if 0) + :type thickness: float >= 0 + :param Degree = 3 (OCCT default) + :type Degree: Integer >= 2 + :param NbPtsOnCur = 15 (OCCT default) + :type: NbPtsOnCur Integer >= 15 + :param NbIter = 2 (OCCT default) + :type: NbIterInteger >= 2 + :param Anisotropie = False (OCCT default) + :type Anisotropie: Boolean + :param: Tol2d = 0.00001 (OCCT default) + :type Tol2d: float > 0 + :param Tol3d = 0.0001 (OCCT default) + :type Tol3dReal: float > 0 + :param TolAng = 0.01 (OCCT default) + :type TolAngReal: float > 0 + :param TolCurv = 0.1 (OCCT default) + :type TolCurvReal: float > 0 + :param MaxDeg = 8 (OCCT default) + :type MaxDegInteger: Integer >= 2 (?) + :param MaxSegments = 9 (OCCT default) + :type MaxSegments: Integer >= 2 (?) + """ + + # POINTS CONSTRAINTS: List of (x,y,z) points provided + pts_array = [gp_Pnt(*pt) for pt in surf_pts] + + # EDGE CONSTRAINTS: build closed polygon + if isinstance(surf_edges, list): # List of (x,y,z) points provided + e_array = [Vector(*e) for e in surf_edges] + wire_builder = BRepBuilderAPI_MakePolygon() + for e in e_array: # Create polygon from edges + wire_builder.Add(e.toPnt()) + wire_builder.Close() + w = wire_builder.Wire() + else: # Closed wires provided + w = surf_edges + edges = [i for i in TopologyExplorer(w).edges()] + + # MAKE SURFACE + continuity = GeomAbs_C0 # Fixed, changing to anything else crashes. + face = Face.makeNSidedSurface(edges, pts_array, continuity, Degree, NbPtsOnCur, NbIter, Anisotropie, Tol2d, Tol3d, TolAng, TolCurv, MaxDeg, MaxSegments) + + if thickness>0: # Thicken surface + solid = BRepOffset_MakeOffset() + solid.Initialize(face.wrapped, thickness, 1.e-5, BRepOffset_Skin, False, False, GeomAbs_Intersection, True) #The last True is important to make solid + solid.MakeOffsetShape() + return cls(solid.Shape()) + else: # Return 2D surface only + return face + @classmethod def isSolid(cls, obj): """ @@ -1345,23 +1460,22 @@ def makeLoft(cls, listOfWire, ruled=False): return cls(loft_builder.Shape()) @classmethod - def makeWedge(cls, xmin, ymin, zmin, z2min, x2min, xmax, ymax, zmax, z2max, x2max, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1)): + def makeWedge(cls, dx, dy, dz, xmin, zmin, xmax, zmax, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1)): """ Make a wedge located in pnt By default pnt=Vector(0,0,0) and dir=Vector(0,0,1) """ - return cls(BRepPrimAPI_MakeWedge(gp_Ax2(pnt.toPnt(), - dir.toDir()), - xmin, - ymin, - zmin, - z2min, - x2min, - xmax, - ymax, - zmax, - z2max, - x2max).Solid()) + + return cls(BRepPrimAPI_MakeWedge( + gp_Ax2(pnt.toPnt(), + dir.toDir()), + dx, + dy, + dz, + xmin, + zmin, + xmax, + zmax).Solid()) @classmethod def makeSphere(cls, radius, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1), angleDegrees1=0, angleDegrees2=90, angleDegrees3=360): From a537ae5e5c5d2e72a7be079f919b28418cc78c01 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sun, 22 Dec 2019 20:39:43 +0100 Subject: [PATCH 15/70] Add files via upload --- examples/Ex101_InterpPlate.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/Ex101_InterpPlate.py b/examples/Ex101_InterpPlate.py index f838f6bee..97260bfa4 100644 --- a/examples/Ex101_InterpPlate.py +++ b/examples/Ex101_InterpPlate.py @@ -16,8 +16,8 @@ thickness = 0.1 edge_points = [[-7.,-7.,0.], [-3.,-10.,3.], [7.,-7.,0.], [7.,7.,0.], [-7.,7.,0.]] edge_wire = cq.Workplane('XY').polyline([(-7.,-7.), (7.,-7.), (7.,7.), (-7.,7.)]) -#edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(0, 45, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) -edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(0, 45, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) +#edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) +edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) # In CadQuery Sept-2019 it worked with rotate=cq.Vector(0, 45, 0). In CadQuery Dec-2019 rotate=cq.Vector(45, 0, 0) only closes the wire. surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] plate_1 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) #plate_1 = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness) # list of (x,y,z) points instead of wires for edges @@ -49,7 +49,6 @@ y_p = np.arange(-N*r1, N*r1, 3*r1) x_p, y_p = np.meshgrid(x_p, y_p) xy_p_even = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] -print("xy_p_even = ", xy_p_even) # ODD ROWS x_p = np.arange(-(N-0.5)*r1*ca, (N+1.5)*r1*ca, ca*2*r1) y_p = np.arange(-(N-2+sa)*r1, (N+1+sa)*r1, 3*r1) From 3f90a8ce27c66f05cefba3d4d6afee8f75371d1e Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sun, 22 Dec 2019 20:40:07 +0100 Subject: [PATCH 16/70] Add files via upload --- tests/test_interpPlate.py | 96 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 tests/test_interpPlate.py diff --git a/tests/test_interpPlate.py b/tests/test_interpPlate.py new file mode 100644 index 000000000..3cfefa0b2 --- /dev/null +++ b/tests/test_interpPlate.py @@ -0,0 +1,96 @@ +""" + Tests interpPlate functionality +""" + +import cadquery as cq + +class TestinterpPlate(BaseTest): + + def plate_0(self): + # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. + thickness = 0 + edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] + surface_points = [[5.,5.,5.]] + plate_0 = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness) + + return plate_0 + + + def plate_1(self): + # Plate with 5 sides and 2 bumps, one side is not co-planar with the other sides + thickness = 0.1 + edge_points = [[-7.,-7.,0.], [-3.,-10.,3.], [7.,-7.,0.], [7.,7.,0.], [-7.,7.,0.]] + edge_wire = cq.Workplane('XY').polyline([(-7.,-7.), (7.,-7.), (7.,7.), (-7.,7.)]) + #edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) + edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) # In CadQuery Sept-2019 it worked with rotate=cq.Vector(0, 45, 0). In CadQuery Dec-2019 rotate=cq.Vector(45, 0, 0) only closes the wire. + surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] + plate_1 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) + + return plate_1 + + + def plate_2(self): + # Embossed star, need to change optional parameters to obtain nice looking result. + r1=3. + r2=10. + fn=6 + thickness = 0.1 + edge_points = [[r1*cos(i * pi/fn), r1*sin(i * pi/fn)] if i%2==0 else [r2*cos(i * pi/fn), r2*sin(i * pi/fn)] for i in range(2*fn+1)] + edge_wire = cq.Workplane('XY').polyline(edge_points) + r2=4.5 + surface_points = [[r2*cos(i * pi/fn), r2*sin(i * pi/fn), 1.] for i in range(2*fn)] + [[0.,0.,-2.]] + plate_2 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=49) + plate_2 = plate_2.translate((0,2*12,0)) + + return plate_2 + + + def plate_3(self): + # Points on hexagonal pattern coordinates, use of pushpoints. + r1 = 1. + N = 3 + ca = cos(30. * pi/180.) + sa = sin(30. * pi/180.) + # EVEN ROWS + x_p = np.arange(-N*r1, N*r1, ca*2*r1) + y_p = np.arange(-N*r1, N*r1, 3*r1) + x_p, y_p = np.meshgrid(x_p, y_p) + xy_p_even = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] + # ODD ROWS + x_p = np.arange(-(N-0.5)*r1*ca, (N+1.5)*r1*ca, ca*2*r1) + y_p = np.arange(-(N-2+sa)*r1, (N+1+sa)*r1, 3*r1) + x_p, y_p = np.meshgrid(x_p, y_p) + xy_p_odd = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] + pts = xy_p_even + xy_p_odd + # Spike surface + thickness = 0.1 + fn = 6 + edge_points = [[r1*cos(i * 2*pi/fn), r1*sin(i * 2*pi/fn)] for i in range(fn+1)] + surface_points = [[0.25,0,0.75], [-0.25,0,0.75], [0,0.25,0.75], [0,-0.25,0.75], [0,0,2]] + edge_wire = cq.Workplane('XY').polyline(edge_points) + be = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=2, NbPtsOnCur=20, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9) + # Pattern on sphere + def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use with each() + return be.rotate((0,0,0),(0,0,1), 30).translate(pos).val() + plate_3 = cq.Workplane('XY').pushPoints(pts).each(face) + plate_3 = plate_3.translate((0,4*11,0)) + + return plate_3 + + + def plate_4(self): + # Gyroïd, all edges are splines on different workplanes. + thickness = 0.1 + edge_points = [[[3.54, 3.54], [1.77, 0.0], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [-1.77, 0.0], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]]] + plane_list = ['XZ', 'XY', 'YZ', 'XZ', 'YZ', 'XY'] + offset_list = [-3.54, 3.54, 3.54, 3.54, -3.54, -3.54] + edge_wire = cq.Workplane(plane_list[0]).workplane(offset=-offset_list[0]).spline(edge_points[0]) + for i in range(len(edge_points)-1): + edge_wire = edge_wire.add(cq.Workplane(plane_list[i+1]).workplane(offset=-offset_list[i+1]).spline(edge_points[i+1])) + surface_points = [[0,0,0]] + plate_4 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) + plate_4 = plate_4.translate((0,5*12,0)) + + return plate_4 + + From 59f9c264baa090b9a078fd19992c3264d7a9e489 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Thu, 26 Dec 2019 10:05:21 +0100 Subject: [PATCH 17/70] Add files via upload --- cadquery/occ_impl/shapes.py | 128 +++--------------------------------- 1 file changed, 9 insertions(+), 119 deletions(-) diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index 0f34a0660..fe5ca85d0 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -88,12 +88,6 @@ from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs -from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs, STEPControl_Controller -from OCC.Core.Interface import Interface_Static_SetCVal -from OCC.Core.Interface import Interface_Static_SetIVal -from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs, STEPControl_Controller -from OCC.Core.Interface import Interface_Static_SetCVal -from OCC.Core.Interface import Interface_Static_SetIVal from OCC.Core.BRepMesh import BRepMesh_IncrementalMesh from OCC.Core.StlAPI import StlAPI_Writer @@ -123,17 +117,10 @@ from OCC.Core.BRepOffset import BRepOffset_MakeOffset, BRepOffset_Skin from OCC.Core.ShapeFix import ShapeFix_Wire -import warnings -from OCC.Core.GeomAbs import GeomAbs_C0 -from OCC.Extend.TopologyUtils import TopologyExplorer, WireExplorer -from OCC.Core.GeomAbs import GeomAbs_Intersection -from OCC.Core.BRepOffsetAPI import BRepOffsetAPI_MakeFilling -from OCC.Core.BRepOffset import BRepOffset_MakeOffset, BRepOffset_Skin -from OCC.Core.ShapeFix import ShapeFix_Wire - import warnings from math import pi, sqrt from functools import reduce +import warnings TOLERANCE = 1e-6 DEG2RAD = 2 * pi / 360. @@ -280,7 +267,7 @@ def exportStep(self, fileName): writer.Transfer(self.wrapped, STEPControl_AsIs) return writer.Write(fileName) - + def exportBrep(self, fileName): """ Export given shape to a BREP file @@ -833,6 +820,11 @@ def assembleEdges(cls, listOfEdges): :param cls: :param listOfEdges: a list of Edge objects. The edges are not to be consecutive. :return: a wire with the edges assembled + :BRepBuilderAPI_MakeWire::Error() values + :BRepBuilderAPI_WireDone = 0 + :BRepBuilderAPI_EmptyWire = 1 + :BRepBuilderAPI_DisconnectedWire = 2 + :BRepBuilderAPI_NonManifoldWire = 3 """ wire_builder = BRepBuilderAPI_MakeWire() @@ -840,7 +832,7 @@ def assembleEdges(cls, listOfEdges): for e in listOfEdges: edges_list.Append(e.wrapped) wire_builder.Add(edges_list) - if wire_builder.Error(): + if wire_builder.Error()!=0: w1 = 'BRepBuilderAPI_MakeWire::IsDone(): returns true if this algorithm contains a valid wire. IsDone returns false if: there are no edges in the wire, or the last edge which you tried to add was not connectable = '+ str(wire_builder.IsDone()) w2 = 'BRepBuilderAPI_MakeWire::Error(): returns the construction status. BRepBuilderAPI_WireDone if the wire is built, or another value of the BRepBuilderAPI_WireError enumeration indicating why the construction failed = ' + str(wire_builder.Error()) warnings.warn(w1) @@ -848,7 +840,7 @@ def assembleEdges(cls, listOfEdges): return cls(wire_builder.Wire()) - @classmethod + @classmethod def makeCircle(cls, radius, center, normal): """ Makes a Circle centered at the provided point, having normal in the provided direction @@ -1026,47 +1018,6 @@ def makeNSidedSurface(cls, edges, points, continuity=GeomAbs_C0, Degree=3, NbPts face = n_sided.Shape() return cls.cast(face).fix() - @classmethod - def makeNSidedSurface(cls, edges, points, continuity=GeomAbs_C0, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9): - """ - Returns a surface enclosed by a closed polygon defined by 'edges' and going through 'points'. - :param points - :type points: list of gp_Pnt - :param edges - :type edges: list of TopologyExplorer().edges() - :param continuity=GeomAbs_C0 - :type continuity: OCC.Core.GeomAbs continuity condition - :param Degree = 3 (OCCT default) - :type Degree: Integer >= 2 - :param NbPtsOnCur = 15 (OCCT default) - :type: NbPtsOnCur Integer >= 15 - :param NbIter = 2 (OCCT default) - :type: NbIterInteger >= 2 - :param Anisotropie = False (OCCT default) - :type Anisotropie: Boolean - :param: Tol2d = 0.00001 (OCCT default) - :type Tol2d: float > 0 - :param Tol3d = 0.0001 (OCCT default) - :type Tol3dReal: float > 0 - :param TolAng = 0.01 (OCCT default) - :type TolAngReal: float > 0 - :param TolCurv = 0.1 (OCCT default) - :type TolCurvReal: float > 0 - :param MaxDeg = 8 (OCCT default) - :type MaxDegInteger: Integer >= 2 (?) - :param MaxSegments = 9 (OCCT default) - :type MaxSegments: Integer >= 2 (?) - """ - - n_sided = BRepOffsetAPI_MakeFilling(Degree, NbPtsOnCur, NbIter, Anisotropie, Tol2d, Tol3d, TolAng, TolCurv, MaxDeg, MaxSegments) - for edg in edges: - n_sided.Add(edg, continuity) - for pt in points: - n_sided.Add(pt) - n_sided.Build() - face = n_sided.Shape() - return cls.cast(face).fix() - @classmethod def makePlane(cls, length, width, basePnt=(0, 0, 0), dir=(0, 0, 1)): basePnt = Vector(basePnt) @@ -1248,67 +1199,6 @@ class Solid(Shape, Mixin3D): a single solid """ - @classmethod - def interpPlate(cls, surf_edges, surf_pts, thickness, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9): - """ - Returns a plate surface that is 'thickness' thick, enclosed by 'surf_edge_pts' points, and going through 'surf_pts' points. - - :param surf_edges - :type 1 surf_edges: list of [x,y,z] float ordered coordinates - :type 2 surf_edges: list of unordered CadQuery wires - :param surf_pts = [] (uses only edges if []) - :type surf_pts: list of [x,y,z] float coordinates - :param thickness = 0 (returns 2D surface if 0) - :type thickness: float >= 0 - :param Degree = 3 (OCCT default) - :type Degree: Integer >= 2 - :param NbPtsOnCur = 15 (OCCT default) - :type: NbPtsOnCur Integer >= 15 - :param NbIter = 2 (OCCT default) - :type: NbIterInteger >= 2 - :param Anisotropie = False (OCCT default) - :type Anisotropie: Boolean - :param: Tol2d = 0.00001 (OCCT default) - :type Tol2d: float > 0 - :param Tol3d = 0.0001 (OCCT default) - :type Tol3dReal: float > 0 - :param TolAng = 0.01 (OCCT default) - :type TolAngReal: float > 0 - :param TolCurv = 0.1 (OCCT default) - :type TolCurvReal: float > 0 - :param MaxDeg = 8 (OCCT default) - :type MaxDegInteger: Integer >= 2 (?) - :param MaxSegments = 9 (OCCT default) - :type MaxSegments: Integer >= 2 (?) - """ - - # POINTS CONSTRAINTS: List of (x,y,z) points provided - pts_array = [gp_Pnt(*pt) for pt in surf_pts] - - # EDGE CONSTRAINTS: build closed polygon - if isinstance(surf_edges, list): # List of (x,y,z) points provided - e_array = [Vector(*e) for e in surf_edges] - wire_builder = BRepBuilderAPI_MakePolygon() - for e in e_array: # Create polygon from edges - wire_builder.Add(e.toPnt()) - wire_builder.Close() - w = wire_builder.Wire() - else: # Closed wires provided - w = surf_edges - edges = [i for i in TopologyExplorer(w).edges()] - - # MAKE SURFACE - continuity = GeomAbs_C0 # Fixed, changing to anything else crashes. - face = Face.makeNSidedSurface(edges, pts_array, continuity, Degree, NbPtsOnCur, NbIter, Anisotropie, Tol2d, Tol3d, TolAng, TolCurv, MaxDeg, MaxSegments) - - if thickness>0: # Thicken surface - solid = BRepOffset_MakeOffset() - solid.Initialize(face.wrapped, thickness, 1.e-5, BRepOffset_Skin, False, False, GeomAbs_Intersection, True) #The last True is important to make solid - solid.MakeOffsetShape() - return cls(solid.Shape()) - else: # Return 2D surface only - return face - @classmethod def interpPlate(cls, surf_edges, surf_pts, thickness, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9): """ From e8aa96153a7ff8a3dc014857f06569c721a9e858 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Thu, 26 Dec 2019 10:06:50 +0100 Subject: [PATCH 18/70] Add files via upload --- cadquery/cq.py | 60 -------------------------------------------------- 1 file changed, 60 deletions(-) diff --git a/cadquery/cq.py b/cadquery/cq.py index be9321db7..fc1509fbc 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -2788,66 +2788,6 @@ def _makeplate(pnt): # combine everything --> CRASHES, probably OCC 6 CAD kernel issue, better use pushpoints through each() instead return self.union(plates, clean=clean) - def interpPlate(self, surf_edges, surf_pts=[], thickness=0, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9): - """ - Returns a plate surface that is 'thickness' thick, enclosed by 'surf_edge_pts' points, and going through 'surf_pts' points. - - :param surf_edges - :type 1 surf_edges: list of [x,y,z] float ordered coordinates - :type 2 surf_edges: list of unordered CadQuery wires - :param surf_pts = [] (uses only edges if []) - :type surf_pts: list of [x,y,z] float coordinates - :param thickness = 0 (returns 2D surface if 0) - :type thickness: float >= 0 - :param combine: should the results be combined with other solids on the stack - (and each other)? - :type combine: true to combine shapes, false otherwise. - :param boolean clean: call :py:meth:`clean` afterwards to have a clean shape - :param Degree = 3 (OCCT default) - :type Degree: Integer >= 2 - :param NbPtsOnCur = 15 (OCCT default) - :type: NbPtsOnCur Integer >= 15 - :param NbIter = 2 (OCCT default) - :type: NbIterInteger >= 2 - :param Anisotropie = False (OCCT default) - :type Anisotropie: Boolean - :param: Tol2d = 0.00001 (OCCT default) - :type Tol2d: float > 0 - :param Tol3d = 0.0001 (OCCT default) - :type Tol3dReal: float > 0 - :param TolAng = 0.01 (OCCT default) - :type TolAngReal: float > 0 - :param TolCurv = 0.1 (OCCT default) - :type TolCurvReal: float > 0 - :param MaxDeg = 8 (OCCT default) - :type MaxDegInteger: Integer >= 2 (?) - :param MaxSegments = 9 (OCCT default) - :type MaxSegments: Integer >= 2 (?) - """ - - # If thickness is 0, only a 2D surface will be returned. - if thickness==0: - combine=False - - # If a list of wires is provided, make a closed wire - if not isinstance(surf_edges, list): - surf_edges = [o.vals()[0] for o in surf_edges.all()] - surf_edges = Wire.assembleEdges(surf_edges) - surf_edges = surf_edges.wrapped - - # Creates interpolated plate - def _makeplate(pnt): - return Solid.interpPlate(surf_edges, surf_pts, thickness, Degree, NbPtsOnCur, NbIter, Anisotropie, Tol2d, Tol3d, TolAng, TolCurv, MaxDeg, MaxSegments) - - plates = self.eachpoint(_makeplate, True) - - # if combination is not desired, just return the created boxes - if not combine: - return plates - else: - # combine everything --> CRASHES, probably OCC 6 CAD kernel issue, better use pushpoints through each() instead - return self.union(plates, clean=clean) - def box(self, length, width, height, centered=(True, True, True), combine=True, clean=True): """ Return a 3d box with specified dimensions for each object on the stack. From a85847e80c8b9642ae3e581972090b712fa4754d Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Thu, 26 Dec 2019 10:07:14 +0100 Subject: [PATCH 19/70] Add files via upload From 75c16f6212e389f298e89be18f5a5c3f2f2b9326 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Thu, 26 Dec 2019 10:07:40 +0100 Subject: [PATCH 20/70] Add files via upload --- tests/test_cadquery.py | 70 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index 01562c6e9..8586ecd8d 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2176,3 +2176,73 @@ def test_assembleEdges(self): edge_wire = edge_wire.add(Workplane(plane_list[i+1]).workplane(offset=-offset_list[i+1]).spline(edge_points[i+1])) edge_wire = [o.vals()[0] for o in edge_wire.all()] edge_wire = Wire.assembleEdges(edge_wire) + def test_interpPlate(self): + + # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. + thickness = 0 + edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] + surface_points = [[5.,5.,5.]] + plate_0 = Workplane('XY').interpPlate(edge_points, surface_points, thickness) + + # Plate with 5 sides and 2 bumps, one side is not co-planar with the other sides + thickness = 0.1 + edge_points = [[-7.,-7.,0.], [-3.,-10.,3.], [7.,-7.,0.], [7.,7.,0.], [-7.,7.,0.]] + edge_wire = Workplane('XY').polyline([(-7.,-7.), (7.,-7.), (7.,7.), (-7.,7.)]) + #edge_wire = edge_wire.add(Workplane('YZ').workplane().transformed(offset=Vector(0, 0, -7), rotate=Vector(45, 0, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) + edge_wire = edge_wire.add(Workplane('YZ').workplane().transformed(offset=Vector(0, 0, -7), rotate=Vector(45, 0, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) # In CadQuery Sept-2019 it worked with rotate=Vector(0, 45, 0). In CadQuery Dec-2019 rotate=Vector(45, 0, 0) only closes the wire. + surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] + plate_1 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness) + + # Embossed star, need to change optional parameters to obtain nice looking result. + r1=3. + r2=10. + fn=6 + thickness = 0.1 + edge_points = [[r1*math.cos(i * math.pi/fn), r1*math.sin(i * math.pi/fn)] if i%2==0 else [r2*math.cos(i * math.pi/fn), r2*math.sin(i * math.pi/fn)] for i in range(2*fn+1)] + edge_wire = Workplane('XY').polyline(edge_points) + r2=4.5 + surface_points = [[r2*math.cos(i * math.pi/fn), r2*math.sin(i * math.pi/fn), 1.] for i in range(2*fn)] + [[0.,0.,-2.]] + plate_2 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotromath.pie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=49) + plate_2 = plate_2.translate((0,2*12,0)) + + # Points on hexagonal pattern coordinates, use of pushpoints. + r1 = 1. + N = 3 + ca = math.cos(30. * math.pi/180.) + sa = math.sin(30. * math.pi/180.) + # EVEN ROWS + x_p = np.arange(-N*r1, N*r1, ca*2*r1) + y_p = np.arange(-N*r1, N*r1, 3*r1) + x_p, y_p = np.meshgrid(x_p, y_p) + xy_p_even = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] + # ODD ROWS + x_p = np.arange(-(N-0.5)*r1*ca, (N+1.5)*r1*ca, ca*2*r1) + y_p = np.arange(-(N-2+sa)*r1, (N+1+sa)*r1, 3*r1) + x_p, y_p = np.meshgrid(x_p, y_p) + xy_p_odd = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] + pts = xy_p_even + xy_p_odd + # Smath.pike surface + thickness = 0.1 + fn = 6 + edge_points = [[r1*math.cos(i * 2*math.pi/fn), r1*math.sin(i * 2*math.pi/fn)] for i in range(fn+1)] + surface_points = [[0.25,0,0.75], [-0.25,0,0.75], [0,0.25,0.75], [0,-0.25,0.75], [0,0,2]] + edge_wire = Workplane('XY').polyline(edge_points) + be = Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=2, NbPtsOnCur=20, NbIter=2, Anisotromath.pie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9) + # Pattern on sphere + def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use with each() + return be.rotate((0,0,0),(0,0,1), 30).translate(pos).val() + plate_3 = Workplane('XY').pushPoints(pts).each(face) + plate_3 = plate_3.translate((0,4*11,0)) + + # Gyroïd, all edges are splines on different workplanes. + thickness = 0.1 + edge_points = [[[3.54, 3.54], [1.77, 0.0], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [-1.77, 0.0], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]]] + plane_list = ['XZ', 'XY', 'YZ', 'XZ', 'YZ', 'XY'] + offset_list = [-3.54, 3.54, 3.54, 3.54, -3.54, -3.54] + edge_wire = Workplane(plane_list[0]).workplane(offset=-offset_list[0]).spline(edge_points[0]) + for i in range(len(edge_points)-1): + edge_wire = edge_wire.add(Workplane(plane_list[i+1]).workplane(offset=-offset_list[i+1]).spline(edge_points[i+1])) + surface_points = [[0,0,0]] + plate_4 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness) + plate_4 = plate_4.translate((0,5*12,0)) + From 676149821dc76aa6009b655aeba5cd7decf9c0ae Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Thu, 26 Dec 2019 10:07:58 +0100 Subject: [PATCH 21/70] Delete TestInterpPlate.py --- tests/TestInterpPlate.py | 96 ---------------------------------------- 1 file changed, 96 deletions(-) delete mode 100644 tests/TestInterpPlate.py diff --git a/tests/TestInterpPlate.py b/tests/TestInterpPlate.py deleted file mode 100644 index 3d53f889e..000000000 --- a/tests/TestInterpPlate.py +++ /dev/null @@ -1,96 +0,0 @@ -""" - Tests interpPlate functionality -""" - -import cadquery as cq - -class TestinterpPlate(BaseTest): - - def plate_0(self): - # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. - thickness = 0 - edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] - surface_points = [[5.,5.,5.]] - plate_0 = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness) - - return plate_0 - - - def plate_1(self): - # Plate with 5 sides and 2 bumps, one side is not co-planar with the other sides - thickness = 0.1 - edge_points = [[-7.,-7.,0.], [-3.,-10.,3.], [7.,-7.,0.], [7.,7.,0.], [-7.,7.,0.]] - edge_wire = cq.Workplane('XY').polyline([(-7.,-7.), (7.,-7.), (7.,7.), (-7.,7.)]) - #edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(0, 45, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) - edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(0, 45, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) # In CadQuery Sept-2019 it worked with rotate=cq.Vector(0, 45, 0). In CadQuery Dec-2019 rotate=cq.Vector(45, 0, 0) only closes the wire. - surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] - plate_1 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) - - return plate_1 - - - def plate_2(self): - # Embossed star, need to change optional parameters to obtain nice looking result. - r1=3. - r2=10. - fn=6 - thickness = 0.1 - edge_points = [[r1*cos(i * pi/fn), r1*sin(i * pi/fn)] if i%2==0 else [r2*cos(i * pi/fn), r2*sin(i * pi/fn)] for i in range(2*fn+1)] - edge_wire = cq.Workplane('XY').polyline(edge_points) - r2=4.5 - surface_points = [[r2*cos(i * pi/fn), r2*sin(i * pi/fn), 1.] for i in range(2*fn)] + [[0.,0.,-2.]] - plate_2 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=49) - plate_2 = plate_2.translate((0,2*12,0)) - - return plate_2 - - - def plate_3(self): - # Points on hexagonal pattern coordinates, use of pushpoints. - r1 = 1. - N = 3 - ca = cos(30. * pi/180.) - sa = sin(30. * pi/180.) - # EVEN ROWS - x_p = np.arange(-N*r1, N*r1, ca*2*r1) - y_p = np.arange(-N*r1, N*r1, 3*r1) - x_p, y_p = np.meshgrid(x_p, y_p) - xy_p_even = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] - # ODD ROWS - x_p = np.arange(-(N-0.5)*r1*ca, (N+1.5)*r1*ca, ca*2*r1) - y_p = np.arange(-(N-2+sa)*r1, (N+1+sa)*r1, 3*r1) - x_p, y_p = np.meshgrid(x_p, y_p) - xy_p_odd = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] - pts = xy_p_even + xy_p_odd - # Spike surface - thickness = 0.1 - fn = 6 - edge_points = [[r1*cos(i * 2*pi/fn), r1*sin(i * 2*pi/fn)] for i in range(fn+1)] - surface_points = [[0.25,0,0.75], [-0.25,0,0.75], [0,0.25,0.75], [0,-0.25,0.75], [0,0,2]] - edge_wire = cq.Workplane('XY').polyline(edge_points) - be = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=2, NbPtsOnCur=20, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9) - # Pattern on sphere - def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use with each() - return be.rotate((0,0,0),(0,0,1), 30).translate(pos).val() - plate_3 = cq.Workplane('XY').pushPoints(pts).each(face) - plate_3 = plate_3.translate((0,4*11,0)) - - return plate_3 - - - def plate_4(self): - # Gyroïd, all edges are splines on different workplanes. - thickness = 0.1 - edge_points = [[[3.54, 3.54], [1.77, 0.0], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [-1.77, 0.0], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]]] - plane_list = ['XZ', 'XY', 'YZ', 'XZ', 'YZ', 'XY'] - offset_list = [-3.54, 3.54, 3.54, 3.54, -3.54, -3.54] - edge_wire = cq.Workplane(plane_list[0]).workplane(offset=-offset_list[0]).spline(edge_points[0]) - for i in range(len(edge_points)-1): - edge_wire = edge_wire.add(cq.Workplane(plane_list[i+1]).workplane(offset=-offset_list[i+1]).spline(edge_points[i+1])) - surface_points = [[0,0,0]] - plate_4 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) - plate_4 = plate_4.translate((0,5*12,0)) - - return plate_4 - - From 12c4df63060efd4117f02d78f015a67586269c4c Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Thu, 26 Dec 2019 10:08:07 +0100 Subject: [PATCH 22/70] Delete TestinterpPlate.py --- tests/TestinterpPlate.py | 96 ---------------------------------------- 1 file changed, 96 deletions(-) delete mode 100644 tests/TestinterpPlate.py diff --git a/tests/TestinterpPlate.py b/tests/TestinterpPlate.py deleted file mode 100644 index 3cfefa0b2..000000000 --- a/tests/TestinterpPlate.py +++ /dev/null @@ -1,96 +0,0 @@ -""" - Tests interpPlate functionality -""" - -import cadquery as cq - -class TestinterpPlate(BaseTest): - - def plate_0(self): - # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. - thickness = 0 - edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] - surface_points = [[5.,5.,5.]] - plate_0 = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness) - - return plate_0 - - - def plate_1(self): - # Plate with 5 sides and 2 bumps, one side is not co-planar with the other sides - thickness = 0.1 - edge_points = [[-7.,-7.,0.], [-3.,-10.,3.], [7.,-7.,0.], [7.,7.,0.], [-7.,7.,0.]] - edge_wire = cq.Workplane('XY').polyline([(-7.,-7.), (7.,-7.), (7.,7.), (-7.,7.)]) - #edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) - edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) # In CadQuery Sept-2019 it worked with rotate=cq.Vector(0, 45, 0). In CadQuery Dec-2019 rotate=cq.Vector(45, 0, 0) only closes the wire. - surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] - plate_1 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) - - return plate_1 - - - def plate_2(self): - # Embossed star, need to change optional parameters to obtain nice looking result. - r1=3. - r2=10. - fn=6 - thickness = 0.1 - edge_points = [[r1*cos(i * pi/fn), r1*sin(i * pi/fn)] if i%2==0 else [r2*cos(i * pi/fn), r2*sin(i * pi/fn)] for i in range(2*fn+1)] - edge_wire = cq.Workplane('XY').polyline(edge_points) - r2=4.5 - surface_points = [[r2*cos(i * pi/fn), r2*sin(i * pi/fn), 1.] for i in range(2*fn)] + [[0.,0.,-2.]] - plate_2 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=49) - plate_2 = plate_2.translate((0,2*12,0)) - - return plate_2 - - - def plate_3(self): - # Points on hexagonal pattern coordinates, use of pushpoints. - r1 = 1. - N = 3 - ca = cos(30. * pi/180.) - sa = sin(30. * pi/180.) - # EVEN ROWS - x_p = np.arange(-N*r1, N*r1, ca*2*r1) - y_p = np.arange(-N*r1, N*r1, 3*r1) - x_p, y_p = np.meshgrid(x_p, y_p) - xy_p_even = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] - # ODD ROWS - x_p = np.arange(-(N-0.5)*r1*ca, (N+1.5)*r1*ca, ca*2*r1) - y_p = np.arange(-(N-2+sa)*r1, (N+1+sa)*r1, 3*r1) - x_p, y_p = np.meshgrid(x_p, y_p) - xy_p_odd = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] - pts = xy_p_even + xy_p_odd - # Spike surface - thickness = 0.1 - fn = 6 - edge_points = [[r1*cos(i * 2*pi/fn), r1*sin(i * 2*pi/fn)] for i in range(fn+1)] - surface_points = [[0.25,0,0.75], [-0.25,0,0.75], [0,0.25,0.75], [0,-0.25,0.75], [0,0,2]] - edge_wire = cq.Workplane('XY').polyline(edge_points) - be = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=2, NbPtsOnCur=20, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9) - # Pattern on sphere - def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use with each() - return be.rotate((0,0,0),(0,0,1), 30).translate(pos).val() - plate_3 = cq.Workplane('XY').pushPoints(pts).each(face) - plate_3 = plate_3.translate((0,4*11,0)) - - return plate_3 - - - def plate_4(self): - # Gyroïd, all edges are splines on different workplanes. - thickness = 0.1 - edge_points = [[[3.54, 3.54], [1.77, 0.0], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [-1.77, 0.0], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]]] - plane_list = ['XZ', 'XY', 'YZ', 'XZ', 'YZ', 'XY'] - offset_list = [-3.54, 3.54, 3.54, 3.54, -3.54, -3.54] - edge_wire = cq.Workplane(plane_list[0]).workplane(offset=-offset_list[0]).spline(edge_points[0]) - for i in range(len(edge_points)-1): - edge_wire = edge_wire.add(cq.Workplane(plane_list[i+1]).workplane(offset=-offset_list[i+1]).spline(edge_points[i+1])) - surface_points = [[0,0,0]] - plate_4 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) - plate_4 = plate_4.translate((0,5*12,0)) - - return plate_4 - - From 4a8c8dcd9851519616c5d395c53f8c8b0b516a50 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Thu, 26 Dec 2019 10:08:36 +0100 Subject: [PATCH 23/70] Add files via upload From 4e55bbf12cb8e4c7b909c4e723a074d7103d5d2a Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Thu, 26 Dec 2019 10:17:07 +0100 Subject: [PATCH 24/70] Delete test_interpPlate.py --- tests/test_interpPlate.py | 96 --------------------------------------- 1 file changed, 96 deletions(-) delete mode 100644 tests/test_interpPlate.py diff --git a/tests/test_interpPlate.py b/tests/test_interpPlate.py deleted file mode 100644 index 3cfefa0b2..000000000 --- a/tests/test_interpPlate.py +++ /dev/null @@ -1,96 +0,0 @@ -""" - Tests interpPlate functionality -""" - -import cadquery as cq - -class TestinterpPlate(BaseTest): - - def plate_0(self): - # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. - thickness = 0 - edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] - surface_points = [[5.,5.,5.]] - plate_0 = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness) - - return plate_0 - - - def plate_1(self): - # Plate with 5 sides and 2 bumps, one side is not co-planar with the other sides - thickness = 0.1 - edge_points = [[-7.,-7.,0.], [-3.,-10.,3.], [7.,-7.,0.], [7.,7.,0.], [-7.,7.,0.]] - edge_wire = cq.Workplane('XY').polyline([(-7.,-7.), (7.,-7.), (7.,7.), (-7.,7.)]) - #edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) - edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) # In CadQuery Sept-2019 it worked with rotate=cq.Vector(0, 45, 0). In CadQuery Dec-2019 rotate=cq.Vector(45, 0, 0) only closes the wire. - surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] - plate_1 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) - - return plate_1 - - - def plate_2(self): - # Embossed star, need to change optional parameters to obtain nice looking result. - r1=3. - r2=10. - fn=6 - thickness = 0.1 - edge_points = [[r1*cos(i * pi/fn), r1*sin(i * pi/fn)] if i%2==0 else [r2*cos(i * pi/fn), r2*sin(i * pi/fn)] for i in range(2*fn+1)] - edge_wire = cq.Workplane('XY').polyline(edge_points) - r2=4.5 - surface_points = [[r2*cos(i * pi/fn), r2*sin(i * pi/fn), 1.] for i in range(2*fn)] + [[0.,0.,-2.]] - plate_2 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=49) - plate_2 = plate_2.translate((0,2*12,0)) - - return plate_2 - - - def plate_3(self): - # Points on hexagonal pattern coordinates, use of pushpoints. - r1 = 1. - N = 3 - ca = cos(30. * pi/180.) - sa = sin(30. * pi/180.) - # EVEN ROWS - x_p = np.arange(-N*r1, N*r1, ca*2*r1) - y_p = np.arange(-N*r1, N*r1, 3*r1) - x_p, y_p = np.meshgrid(x_p, y_p) - xy_p_even = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] - # ODD ROWS - x_p = np.arange(-(N-0.5)*r1*ca, (N+1.5)*r1*ca, ca*2*r1) - y_p = np.arange(-(N-2+sa)*r1, (N+1+sa)*r1, 3*r1) - x_p, y_p = np.meshgrid(x_p, y_p) - xy_p_odd = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] - pts = xy_p_even + xy_p_odd - # Spike surface - thickness = 0.1 - fn = 6 - edge_points = [[r1*cos(i * 2*pi/fn), r1*sin(i * 2*pi/fn)] for i in range(fn+1)] - surface_points = [[0.25,0,0.75], [-0.25,0,0.75], [0,0.25,0.75], [0,-0.25,0.75], [0,0,2]] - edge_wire = cq.Workplane('XY').polyline(edge_points) - be = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=2, NbPtsOnCur=20, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9) - # Pattern on sphere - def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use with each() - return be.rotate((0,0,0),(0,0,1), 30).translate(pos).val() - plate_3 = cq.Workplane('XY').pushPoints(pts).each(face) - plate_3 = plate_3.translate((0,4*11,0)) - - return plate_3 - - - def plate_4(self): - # Gyroïd, all edges are splines on different workplanes. - thickness = 0.1 - edge_points = [[[3.54, 3.54], [1.77, 0.0], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [-1.77, 0.0], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]]] - plane_list = ['XZ', 'XY', 'YZ', 'XZ', 'YZ', 'XY'] - offset_list = [-3.54, 3.54, 3.54, 3.54, -3.54, -3.54] - edge_wire = cq.Workplane(plane_list[0]).workplane(offset=-offset_list[0]).spline(edge_points[0]) - for i in range(len(edge_points)-1): - edge_wire = edge_wire.add(cq.Workplane(plane_list[i+1]).workplane(offset=-offset_list[i+1]).spline(edge_points[i+1])) - surface_points = [[0,0,0]] - plate_4 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) - plate_4 = plate_4.translate((0,5*12,0)) - - return plate_4 - - From 05e58876521d4d664df0f875973298490a332559 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Thu, 26 Dec 2019 10:27:47 +0100 Subject: [PATCH 25/70] Update test_cadquery.py --- tests/test_cadquery.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index 8586ecd8d..78ba7ec5b 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2176,6 +2176,7 @@ def test_assembleEdges(self): edge_wire = edge_wire.add(Workplane(plane_list[i+1]).workplane(offset=-offset_list[i+1]).spline(edge_points[i+1])) edge_wire = [o.vals()[0] for o in edge_wire.all()] edge_wire = Wire.assembleEdges(edge_wire) + def test_interpPlate(self): # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. @@ -2202,8 +2203,7 @@ def test_interpPlate(self): edge_wire = Workplane('XY').polyline(edge_points) r2=4.5 surface_points = [[r2*math.cos(i * math.pi/fn), r2*math.sin(i * math.pi/fn), 1.] for i in range(2*fn)] + [[0.,0.,-2.]] - plate_2 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotromath.pie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=49) - plate_2 = plate_2.translate((0,2*12,0)) + plate_2 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=49) # Points on hexagonal pattern coordinates, use of pushpoints. r1 = 1. @@ -2232,7 +2232,6 @@ def test_interpPlate(self): def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use with each() return be.rotate((0,0,0),(0,0,1), 30).translate(pos).val() plate_3 = Workplane('XY').pushPoints(pts).each(face) - plate_3 = plate_3.translate((0,4*11,0)) # Gyroïd, all edges are splines on different workplanes. thickness = 0.1 @@ -2244,5 +2243,3 @@ def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use edge_wire = edge_wire.add(Workplane(plane_list[i+1]).workplane(offset=-offset_list[i+1]).spline(edge_points[i+1])) surface_points = [[0,0,0]] plate_4 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness) - plate_4 = plate_4.translate((0,5*12,0)) - From a84228e0b5e63b4f651d9fa97ec492cc2247e592 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Thu, 26 Dec 2019 10:29:58 +0100 Subject: [PATCH 26/70] Update test_cadquery.py --- tests/test_cadquery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index 78ba7ec5b..53aaee636 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2227,7 +2227,7 @@ def test_interpPlate(self): edge_points = [[r1*math.cos(i * 2*math.pi/fn), r1*math.sin(i * 2*math.pi/fn)] for i in range(fn+1)] surface_points = [[0.25,0,0.75], [-0.25,0,0.75], [0,0.25,0.75], [0,-0.25,0.75], [0,0,2]] edge_wire = Workplane('XY').polyline(edge_points) - be = Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=2, NbPtsOnCur=20, NbIter=2, Anisotromath.pie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9) + be = Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=2, NbPtsOnCur=20, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9) # Pattern on sphere def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use with each() return be.rotate((0,0,0),(0,0,1), 30).translate(pos).val() From 8eda7889b79f0a990b2b63c85d35df16a9a3e2d5 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Thu, 26 Dec 2019 10:46:59 +0100 Subject: [PATCH 27/70] Update test_cadquery.py --- tests/test_cadquery.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index 53aaee636..6272f92c8 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2178,7 +2178,8 @@ def test_assembleEdges(self): edge_wire = Wire.assembleEdges(edge_wire) def test_interpPlate(self): - + + import numpy as np # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. thickness = 0 edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] From fd64195177404278ab33c10d2f0d5d9f0099d040 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Thu, 26 Dec 2019 10:50:08 +0100 Subject: [PATCH 28/70] Update test_cadquery.py --- tests/test_cadquery.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index 6272f92c8..f212350fb 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2179,7 +2179,6 @@ def test_assembleEdges(self): def test_interpPlate(self): - import numpy as np # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. thickness = 0 edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] @@ -2211,17 +2210,7 @@ def test_interpPlate(self): N = 3 ca = math.cos(30. * math.pi/180.) sa = math.sin(30. * math.pi/180.) - # EVEN ROWS - x_p = np.arange(-N*r1, N*r1, ca*2*r1) - y_p = np.arange(-N*r1, N*r1, 3*r1) - x_p, y_p = np.meshgrid(x_p, y_p) - xy_p_even = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] - # ODD ROWS - x_p = np.arange(-(N-0.5)*r1*ca, (N+1.5)*r1*ca, ca*2*r1) - y_p = np.arange(-(N-2+sa)*r1, (N+1+sa)*r1, 3*r1) - x_p, y_p = np.meshgrid(x_p, y_p) - xy_p_odd = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] - pts = xy_p_even + xy_p_odd + pts = [(-3.0, -3.0), (-1.2679491924311226, -3.0), (0.46410161513775483, -3.0), (2.196152422706632, -3.0), (-3.0, 0.0), (-1.2679491924311226, 0.0), (0.46410161513775483, 0.0), (2.196152422706632, 0.0), (-2.165063509461097, -1.5), (-0.4330127018922194, -1.5), (1.299038105676658, -1.5), (3.031088913245535, -1.5), (-2.165063509461097, 1.5), (-0.4330127018922194, 1.5), (1.299038105676658, 1.5), (3.031088913245535, 1.5)] # Smath.pike surface thickness = 0.1 fn = 6 From 132016be9005791ed309d300779970eec1d08a0a Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sun, 29 Dec 2019 10:04:44 +0100 Subject: [PATCH 29/70] Added: asserts in tests --- tests/test_cadquery.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index f212350fb..23357b4d7 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2148,6 +2148,8 @@ def test_assembleEdges(self): edge_wire = edge_wire.add(Workplane('YZ').workplane().transformed(offset=Vector(0, 0, -7), rotate=Vector(0, 45, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) edge_wire = [o.vals()[0] for o in edge_wire.all()] edge_wire = Wire.assembleEdges(edge_wire) + self.assertTrue(edge_wire.isValid()) + self.assertEqual(len(edge_wire.Wires()), 1) # Embossed star, need to change optional parameters to obtain nice looking result. r1=3. @@ -2157,6 +2159,8 @@ def test_assembleEdges(self): edge_wire = Workplane('XY').polyline(edge_points) edge_wire = [o.vals()[0] for o in edge_wire.all()] edge_wire = Wire.assembleEdges(edge_wire) + self.assertTrue(edge_wire.isValid()) + self.assertEqual(len(edge_wire.Wires()), 1) # Points on hexagonal pattern coordinates, use of pushpoints. r1 = 1. @@ -2166,6 +2170,8 @@ def test_assembleEdges(self): edge_wire = Workplane('XY').polyline(edge_points) edge_wire = [o.vals()[0] for o in edge_wire.all()] edge_wire = Wire.assembleEdges(edge_wire) + self.assertTrue(edge_wire.isValid()) + self.assertEqual(len(edge_wire.Wires()), 1) # Gyroïd, all edges are splines on different workplanes. edge_points = [[[3.54, 3.54], [1.77, 0.0], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [-1.77, 0.0], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]]] @@ -2176,15 +2182,21 @@ def test_assembleEdges(self): edge_wire = edge_wire.add(Workplane(plane_list[i+1]).workplane(offset=-offset_list[i+1]).spline(edge_points[i+1])) edge_wire = [o.vals()[0] for o in edge_wire.all()] edge_wire = Wire.assembleEdges(edge_wire) + self.assertTrue(edge_wire.isValid()) + self.assertEqual(len(edge_wire.Wires()), 1) def test_interpPlate(self): + decimal_places = 9 + # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. thickness = 0 edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] surface_points = [[5.,5.,5.]] plate_0 = Workplane('XY').interpPlate(edge_points, surface_points, thickness) - + self.assertTrue(plate_0.val().isValid()) + self.assertAlmostEqual(plate_0.val().Area(), 141.218823892, decimal_places) + # Plate with 5 sides and 2 bumps, one side is not co-planar with the other sides thickness = 0.1 edge_points = [[-7.,-7.,0.], [-3.,-10.,3.], [7.,-7.,0.], [7.,7.,0.], [-7.,7.,0.]] @@ -2193,6 +2205,8 @@ def test_interpPlate(self): edge_wire = edge_wire.add(Workplane('YZ').workplane().transformed(offset=Vector(0, 0, -7), rotate=Vector(45, 0, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) # In CadQuery Sept-2019 it worked with rotate=Vector(0, 45, 0). In CadQuery Dec-2019 rotate=Vector(45, 0, 0) only closes the wire. surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] plate_1 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness) + self.assertTrue(plate_1.val().isValid()) + self.assertAlmostEqual(plate_1.val().Volume(), 26.124970206, decimal_places) # Embossed star, need to change optional parameters to obtain nice looking result. r1=3. @@ -2203,7 +2217,9 @@ def test_interpPlate(self): edge_wire = Workplane('XY').polyline(edge_points) r2=4.5 surface_points = [[r2*math.cos(i * math.pi/fn), r2*math.sin(i * math.pi/fn), 1.] for i in range(2*fn)] + [[0.,0.,-2.]] - plate_2 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=49) + plate_2 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=49) + self.assertTrue(plate_2.val().isValid()) + self.assertAlmostEqual(plate_2.val().Volume(), 10.956054314, decimal_places) # Points on hexagonal pattern coordinates, use of pushpoints. r1 = 1. @@ -2222,6 +2238,8 @@ def test_interpPlate(self): def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use with each() return be.rotate((0,0,0),(0,0,1), 30).translate(pos).val() plate_3 = Workplane('XY').pushPoints(pts).each(face) + self.assertTrue(plate_3.val().isValid()) + self.assertAlmostEqual(plate_3.val().Volume(), 0.459661032, decimal_places) # Gyroïd, all edges are splines on different workplanes. thickness = 0.1 @@ -2233,3 +2251,6 @@ def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use edge_wire = edge_wire.add(Workplane(plane_list[i+1]).workplane(offset=-offset_list[i+1]).spline(edge_points[i+1])) surface_points = [[0,0,0]] plate_4 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness) + self.assertTrue(plate_4.val().isValid()) + self.assertAlmostEqual(plate_4.val().Volume(), 7.760559490, decimal_places) + From cbd59acfaa38f2eee7f1a908e1cb6f5405e2eaa1 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sun, 29 Dec 2019 14:22:41 +0100 Subject: [PATCH 30/70] Update test_cadquery.py --- tests/test_cadquery.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index 23357b4d7..570b27327 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2190,12 +2190,12 @@ def test_interpPlate(self): decimal_places = 9 # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. - thickness = 0 - edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] - surface_points = [[5.,5.,5.]] - plate_0 = Workplane('XY').interpPlate(edge_points, surface_points, thickness) - self.assertTrue(plate_0.val().isValid()) - self.assertAlmostEqual(plate_0.val().Area(), 141.218823892, decimal_places) + #thickness = 0 + #edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] + #surface_points = [[5.,5.,5.]] + #plate_0 = Workplane('XY').interpPlate(edge_points, surface_points, thickness) + #self.assertTrue(plate_0.val().isValid()) + #self.assertAlmostEqual(plate_0.val().Area(), 141.218823892, decimal_places) # Plate with 5 sides and 2 bumps, one side is not co-planar with the other sides thickness = 0.1 From 50e652b8c2dea58ed668e5a793d45e288907e1f1 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sun, 29 Dec 2019 14:35:24 +0100 Subject: [PATCH 31/70] Update test_cadquery.py --- tests/test_cadquery.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index 570b27327..33c46df87 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2190,10 +2190,10 @@ def test_interpPlate(self): decimal_places = 9 # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. - #thickness = 0 - #edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] - #surface_points = [[5.,5.,5.]] - #plate_0 = Workplane('XY').interpPlate(edge_points, surface_points, thickness) + thickness = 0 + edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] + surface_points = [[5.,5.,5.]] + plate_0 = Workplane('XY').interpPlate(edge_points, surface_points, thickness) #self.assertTrue(plate_0.val().isValid()) #self.assertAlmostEqual(plate_0.val().Area(), 141.218823892, decimal_places) @@ -2201,12 +2201,12 @@ def test_interpPlate(self): thickness = 0.1 edge_points = [[-7.,-7.,0.], [-3.,-10.,3.], [7.,-7.,0.], [7.,7.,0.], [-7.,7.,0.]] edge_wire = Workplane('XY').polyline([(-7.,-7.), (7.,-7.), (7.,7.), (-7.,7.)]) - #edge_wire = edge_wire.add(Workplane('YZ').workplane().transformed(offset=Vector(0, 0, -7), rotate=Vector(45, 0, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) + edge_wire = edge_wire.add(Workplane('YZ').workplane().transformed(offset=Vector(0, 0, -7), rotate=Vector(45, 0, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) edge_wire = edge_wire.add(Workplane('YZ').workplane().transformed(offset=Vector(0, 0, -7), rotate=Vector(45, 0, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) # In CadQuery Sept-2019 it worked with rotate=Vector(0, 45, 0). In CadQuery Dec-2019 rotate=Vector(45, 0, 0) only closes the wire. surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] plate_1 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness) - self.assertTrue(plate_1.val().isValid()) - self.assertAlmostEqual(plate_1.val().Volume(), 26.124970206, decimal_places) + #self.assertTrue(plate_1.val().isValid()) + #self.assertAlmostEqual(plate_1.val().Volume(), 26.124970206, decimal_places) # Embossed star, need to change optional parameters to obtain nice looking result. r1=3. From 3d198a75cdd1db7ca52ae50c312ad61e56036225 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sun, 29 Dec 2019 14:45:14 +0100 Subject: [PATCH 32/70] Update test_cadquery.py --- tests/test_cadquery.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index 33c46df87..6921df9a6 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2202,7 +2202,6 @@ def test_interpPlate(self): edge_points = [[-7.,-7.,0.], [-3.,-10.,3.], [7.,-7.,0.], [7.,7.,0.], [-7.,7.,0.]] edge_wire = Workplane('XY').polyline([(-7.,-7.), (7.,-7.), (7.,7.), (-7.,7.)]) edge_wire = edge_wire.add(Workplane('YZ').workplane().transformed(offset=Vector(0, 0, -7), rotate=Vector(45, 0, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) - edge_wire = edge_wire.add(Workplane('YZ').workplane().transformed(offset=Vector(0, 0, -7), rotate=Vector(45, 0, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) # In CadQuery Sept-2019 it worked with rotate=Vector(0, 45, 0). In CadQuery Dec-2019 rotate=Vector(45, 0, 0) only closes the wire. surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] plate_1 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness) #self.assertTrue(plate_1.val().isValid()) From 23be0d9e8d13ebfe31ce676b1cc7818ebb23b591 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sun, 29 Dec 2019 14:46:17 +0100 Subject: [PATCH 33/70] Update test_cadquery.py --- tests/test_cadquery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index 6921df9a6..eddbd7517 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2201,7 +2201,7 @@ def test_interpPlate(self): thickness = 0.1 edge_points = [[-7.,-7.,0.], [-3.,-10.,3.], [7.,-7.,0.], [7.,7.,0.], [-7.,7.,0.]] edge_wire = Workplane('XY').polyline([(-7.,-7.), (7.,-7.), (7.,7.), (-7.,7.)]) - edge_wire = edge_wire.add(Workplane('YZ').workplane().transformed(offset=Vector(0, 0, -7), rotate=Vector(45, 0, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) + edge_wire = edge_wire.add(Workplane('YZ').workplane().transformed(offset=Vector(0, 0, -7), rotate=Vector(45, 0, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] plate_1 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness) #self.assertTrue(plate_1.val().isValid()) From fcf30948775b36deb9dfe4336cade364919da01a Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Mon, 30 Dec 2019 09:52:28 +0100 Subject: [PATCH 34/70] Update test_cadquery.py --- tests/test_cadquery.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index eddbd7517..7d4a81b18 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2187,15 +2187,15 @@ def test_assembleEdges(self): def test_interpPlate(self): - decimal_places = 9 + decimal_places = 1 # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. thickness = 0 edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] surface_points = [[5.,5.,5.]] plate_0 = Workplane('XY').interpPlate(edge_points, surface_points, thickness) - #self.assertTrue(plate_0.val().isValid()) - #self.assertAlmostEqual(plate_0.val().Area(), 141.218823892, decimal_places) + self.assertTrue(plate_0.val().isValid()) + self.assertAlmostEqual(plate_0.val().Area(), 141.218823892, decimal_places) # Plate with 5 sides and 2 bumps, one side is not co-planar with the other sides thickness = 0.1 @@ -2204,8 +2204,8 @@ def test_interpPlate(self): edge_wire = edge_wire.add(Workplane('YZ').workplane().transformed(offset=Vector(0, 0, -7), rotate=Vector(45, 0, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] plate_1 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness) - #self.assertTrue(plate_1.val().isValid()) - #self.assertAlmostEqual(plate_1.val().Volume(), 26.124970206, decimal_places) + self.assertTrue(plate_1.val().isValid()) + self.assertAlmostEqual(plate_1.val().Volume(), 26.124970206, decimal_places) # Embossed star, need to change optional parameters to obtain nice looking result. r1=3. From 9980ecdd7c6f97b15412295516555872e2661bc9 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Mon, 30 Dec 2019 10:09:45 +0100 Subject: [PATCH 35/70] Update test_cadquery.py --- tests/test_cadquery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index 7d4a81b18..3c080ae95 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2187,7 +2187,7 @@ def test_assembleEdges(self): def test_interpPlate(self): - decimal_places = 1 + decimal_places = 0 # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. thickness = 0 From 40c152a1ddbead5d828a60b523caf690f503e58a Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Sun, 5 Jan 2020 08:46:04 +0100 Subject: [PATCH 36/70] Update shapes.py --- cadquery/occ_impl/shapes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index fe5ca85d0..fe4f8826a 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -1252,7 +1252,7 @@ def interpPlate(cls, surf_edges, surf_pts, thickness, Degree=3, NbPtsOnCur=15, N continuity = GeomAbs_C0 # Fixed, changing to anything else crashes. face = Face.makeNSidedSurface(edges, pts_array, continuity, Degree, NbPtsOnCur, NbIter, Anisotropie, Tol2d, Tol3d, TolAng, TolCurv, MaxDeg, MaxSegments) - if thickness>0: # Thicken surface + if abs(thickness)>0: # Thicken surface, abs() because negative values are allowed (sets direction of thickening) solid = BRepOffset_MakeOffset() solid.Initialize(face.wrapped, thickness, 1.e-5, BRepOffset_Skin, False, False, GeomAbs_Intersection, True) #The last True is important to make solid solid.MakeOffsetShape() From 09e84bff933310a2b4217d222346fbc4324c2b60 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Mon, 6 Jan 2020 14:46:59 +0100 Subject: [PATCH 37/70] assembleEdges moved to shapes.py --- cadquery/cq.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/cadquery/cq.py b/cadquery/cq.py index fc1509fbc..68e2e296e 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -2728,21 +2728,21 @@ def _sweep(self, path, multisection=False, makeSolid=True, isFrenet=False, return Compound.makeCompound(toFuse) - def interpPlate(self, surf_edges, surf_pts=[], thickness=0, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9): + def interpPlate(self, surf_edges, surf_pts=[], thickness=0, combine=True, clean=True, degree=3, nbPtsOnCur=15, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=9): """ Returns a plate surface that is 'thickness' thick, enclosed by 'surf_edge_pts' points, and going through 'surf_pts' points. - + :param surf_edges :type 1 surf_edges: list of [x,y,z] float ordered coordinates - :type 2 surf_edges: list of unordered CadQuery wires + :type 2 surf_edges: list of ordered or unordered CadQuery wires :param surf_pts = [] (uses only edges if []) :type surf_pts: list of [x,y,z] float coordinates :param thickness = 0 (returns 2D surface if 0) - :type thickness: float >= 0 + :type thickness: float (may be negative or positive depending on thicknening direction) :param combine: should the results be combined with other solids on the stack (and each other)? :type combine: true to combine shapes, false otherwise. - :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 Degree = 3 (OCCT default) :type Degree: Integer >= 2 :param NbPtsOnCur = 15 (OCCT default) @@ -2764,20 +2764,14 @@ def interpPlate(self, surf_edges, surf_pts=[], thickness=0, combine=True, clean= :param MaxSegments = 9 (OCCT default) :type MaxSegments: Integer >= 2 (?) """ - + # If thickness is 0, only a 2D surface will be returned. if thickness==0: combine=False - - # If a list of wires is provided, make a closed wire - if not isinstance(surf_edges, list): - surf_edges = [o.vals()[0] for o in surf_edges.all()] - surf_edges = Wire.assembleEdges(surf_edges) - surf_edges = surf_edges.wrapped - + # Creates interpolated plate def _makeplate(pnt): - return Solid.interpPlate(surf_edges, surf_pts, thickness, Degree, NbPtsOnCur, NbIter, Anisotropie, Tol2d, Tol3d, TolAng, TolCurv, MaxDeg, MaxSegments) + return Solid.interpPlate(surf_edges, surf_pts, thickness, degree, nbPtsOnCur, nbIter, anisotropy, tol2d, tol3d, tolAng, tolCurv, maxDeg, maxSegments) plates = self.eachpoint(_makeplate, True) @@ -2785,7 +2779,7 @@ def _makeplate(pnt): if not combine: return plates else: - # combine everything --> CRASHES, probably OCC 6 CAD kernel issue, better use pushpoints through each() instead + # combine everything --> CRASHES when using pushpoints directly with interpPlate, probably OCC 6 CAD kernel issue, better use pushpoints through each() instead. return self.union(plates, clean=clean) def box(self, length, width, height, centered=(True, True, True), combine=True, clean=True): From 6ba1d6d1fe94102fb6b3548acf53848c521bec8c Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Mon, 6 Jan 2020 14:47:47 +0100 Subject: [PATCH 38/70] call to assembleEdges moved into interpPlate --- cadquery/occ_impl/shapes.py | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index fe4f8826a..573974e3b 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -1203,14 +1203,14 @@ class Solid(Shape, Mixin3D): def interpPlate(cls, surf_edges, surf_pts, thickness, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9): """ Returns a plate surface that is 'thickness' thick, enclosed by 'surf_edge_pts' points, and going through 'surf_pts' points. - + :param surf_edges :type 1 surf_edges: list of [x,y,z] float ordered coordinates - :type 2 surf_edges: list of unordered CadQuery wires + :type 2 surf_edges: list of ordered or unordered CadQuery wires :param surf_pts = [] (uses only edges if []) :type surf_pts: list of [x,y,z] float coordinates :param thickness = 0 (returns 2D surface if 0) - :type thickness: float >= 0 + :type thickness: float (may be negative or positive depending on thicknening direction) :param Degree = 3 (OCCT default) :type Degree: Integer >= 2 :param NbPtsOnCur = 15 (OCCT default) @@ -1232,34 +1232,41 @@ def interpPlate(cls, surf_edges, surf_pts, thickness, Degree=3, NbPtsOnCur=15, N :param MaxSegments = 9 (OCCT default) :type MaxSegments: Integer >= 2 (?) """ - - # POINTS CONSTRAINTS: List of (x,y,z) points provided + + # POINTS CONSTRAINTS: list of (x,y,z) points, optional. pts_array = [gp_Pnt(*pt) for pt in surf_pts] - - # EDGE CONSTRAINTS: build closed polygon - if isinstance(surf_edges, list): # List of (x,y,z) points provided + + # EDGE CONSTRAINTS + # If a list of wires is provided, make a closed wire + if not isinstance(surf_edges, list): + surf_edges = [o.vals()[0] for o in surf_edges.all()] + surf_edges = Wire.assembleEdges(surf_edges) + w = surf_edges.wrapped + + # If a list of (x,y,z) points provided, build closed polygon + if isinstance(surf_edges, list): e_array = [Vector(*e) for e in surf_edges] wire_builder = BRepBuilderAPI_MakePolygon() for e in e_array: # Create polygon from edges wire_builder.Add(e.toPnt()) wire_builder.Close() w = wire_builder.Wire() - else: # Closed wires provided - w = surf_edges + edges = [i for i in TopologyExplorer(w).edges()] - + # MAKE SURFACE continuity = GeomAbs_C0 # Fixed, changing to anything else crashes. - face = Face.makeNSidedSurface(edges, pts_array, continuity, Degree, NbPtsOnCur, NbIter, Anisotropie, Tol2d, Tol3d, TolAng, TolCurv, MaxDeg, MaxSegments) - - if abs(thickness)>0: # Thicken surface, abs() because negative values are allowed (sets direction of thickening) + face = Face.makeNSidedSurface(edges, pts_array, continuity, Degree, NbPtsOnCur, NbIter, Anisotropie, Tol2d, Tol3d, TolAng, TolCurv, MaxDeg, MaxSegments) + + # THICKEN SURFACE + if abs(thickness)>0: # abs() because negative values are allowed to set direction of thickening solid = BRepOffset_MakeOffset() solid.Initialize(face.wrapped, thickness, 1.e-5, BRepOffset_Skin, False, False, GeomAbs_Intersection, True) #The last True is important to make solid solid.MakeOffsetShape() return cls(solid.Shape()) else: # Return 2D surface only return face - + @classmethod def isSolid(cls, obj): """ From 5f553cda3ec16203737cd4d01585f37d1f4e24c6 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Mon, 6 Jan 2020 14:48:27 +0100 Subject: [PATCH 39/70] Capitalization of variables changes to lower --- tests/test_cadquery.py | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index 3c080ae95..02781bb7c 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2148,8 +2148,6 @@ def test_assembleEdges(self): edge_wire = edge_wire.add(Workplane('YZ').workplane().transformed(offset=Vector(0, 0, -7), rotate=Vector(0, 45, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) edge_wire = [o.vals()[0] for o in edge_wire.all()] edge_wire = Wire.assembleEdges(edge_wire) - self.assertTrue(edge_wire.isValid()) - self.assertEqual(len(edge_wire.Wires()), 1) # Embossed star, need to change optional parameters to obtain nice looking result. r1=3. @@ -2159,8 +2157,6 @@ def test_assembleEdges(self): edge_wire = Workplane('XY').polyline(edge_points) edge_wire = [o.vals()[0] for o in edge_wire.all()] edge_wire = Wire.assembleEdges(edge_wire) - self.assertTrue(edge_wire.isValid()) - self.assertEqual(len(edge_wire.Wires()), 1) # Points on hexagonal pattern coordinates, use of pushpoints. r1 = 1. @@ -2170,8 +2166,6 @@ def test_assembleEdges(self): edge_wire = Workplane('XY').polyline(edge_points) edge_wire = [o.vals()[0] for o in edge_wire.all()] edge_wire = Wire.assembleEdges(edge_wire) - self.assertTrue(edge_wire.isValid()) - self.assertEqual(len(edge_wire.Wires()), 1) # Gyroïd, all edges are splines on different workplanes. edge_points = [[[3.54, 3.54], [1.77, 0.0], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [-1.77, 0.0], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]]] @@ -2182,13 +2176,11 @@ def test_assembleEdges(self): edge_wire = edge_wire.add(Workplane(plane_list[i+1]).workplane(offset=-offset_list[i+1]).spline(edge_points[i+1])) edge_wire = [o.vals()[0] for o in edge_wire.all()] edge_wire = Wire.assembleEdges(edge_wire) - self.assertTrue(edge_wire.isValid()) - self.assertEqual(len(edge_wire.Wires()), 1) - + def test_interpPlate(self): - - decimal_places = 0 - + + decimal_places = 9 + # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. thickness = 0 edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] @@ -2196,17 +2188,18 @@ def test_interpPlate(self): plate_0 = Workplane('XY').interpPlate(edge_points, surface_points, thickness) self.assertTrue(plate_0.val().isValid()) self.assertAlmostEqual(plate_0.val().Area(), 141.218823892, decimal_places) - + # Plate with 5 sides and 2 bumps, one side is not co-planar with the other sides thickness = 0.1 edge_points = [[-7.,-7.,0.], [-3.,-10.,3.], [7.,-7.,0.], [7.,7.,0.], [-7.,7.,0.]] edge_wire = Workplane('XY').polyline([(-7.,-7.), (7.,-7.), (7.,7.), (-7.,7.)]) - edge_wire = edge_wire.add(Workplane('YZ').workplane().transformed(offset=Vector(0, 0, -7), rotate=Vector(45, 0, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) + #edge_wire = edge_wire.add(Workplane('YZ').workplane().transformed(offset=Vector(0, 0, -7), rotate=Vector(45, 0, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) + edge_wire = edge_wire.add(Workplane('YZ').workplane().transformed(offset=Vector(0, 0, -7), rotate=Vector(45, 0, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) # In CadQuery Sept-2019 it worked with rotate=Vector(0, 45, 0). In CadQuery Dec-2019 rotate=Vector(45, 0, 0) only closes the wire. surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] plate_1 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness) self.assertTrue(plate_1.val().isValid()) - self.assertAlmostEqual(plate_1.val().Volume(), 26.124970206, decimal_places) - + self.assertAlmostEqual(plate_1.val().Volume(), 26.124970206, decimal_places) + # Embossed star, need to change optional parameters to obtain nice looking result. r1=3. r2=10. @@ -2216,10 +2209,10 @@ def test_interpPlate(self): edge_wire = Workplane('XY').polyline(edge_points) r2=4.5 surface_points = [[r2*math.cos(i * math.pi/fn), r2*math.sin(i * math.pi/fn), 1.] for i in range(2*fn)] + [[0.,0.,-2.]] - plate_2 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=49) + plate_2 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, degree=3, nbPtsOnCur=15, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=49) self.assertTrue(plate_2.val().isValid()) - self.assertAlmostEqual(plate_2.val().Volume(), 10.956054314, decimal_places) - + self.assertAlmostEqual(plate_2.val().Volume(), 10.956054314, decimal_places) + # Points on hexagonal pattern coordinates, use of pushpoints. r1 = 1. N = 3 @@ -2232,14 +2225,14 @@ def test_interpPlate(self): edge_points = [[r1*math.cos(i * 2*math.pi/fn), r1*math.sin(i * 2*math.pi/fn)] for i in range(fn+1)] surface_points = [[0.25,0,0.75], [-0.25,0,0.75], [0,0.25,0.75], [0,-0.25,0.75], [0,0,2]] edge_wire = Workplane('XY').polyline(edge_points) - be = Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=2, NbPtsOnCur=20, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9) + be = Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, degree=2, nbPtsOnCur=20, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=9) # Pattern on sphere def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use with each() return be.rotate((0,0,0),(0,0,1), 30).translate(pos).val() plate_3 = Workplane('XY').pushPoints(pts).each(face) self.assertTrue(plate_3.val().isValid()) - self.assertAlmostEqual(plate_3.val().Volume(), 0.459661032, decimal_places) - + self.assertAlmostEqual(plate_3.val().Volume(), 0.459661032, decimal_places) + # Gyroïd, all edges are splines on different workplanes. thickness = 0.1 edge_points = [[[3.54, 3.54], [1.77, 0.0], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [-1.77, 0.0], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]]] @@ -2251,5 +2244,4 @@ def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use surface_points = [[0,0,0]] plate_4 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness) self.assertTrue(plate_4.val().isValid()) - self.assertAlmostEqual(plate_4.val().Volume(), 7.760559490, decimal_places) - + self.assertAlmostEqual(plate_4.val().Volume(), 7.760559490, decimal_places) From 737ddd6beafdf768988988f474114bfb1d5ae8f5 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Mon, 6 Jan 2020 14:48:52 +0100 Subject: [PATCH 40/70] Capitalization of variables changes to lower --- examples/Ex101_InterpPlate.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/examples/Ex101_InterpPlate.py b/examples/Ex101_InterpPlate.py index 97260bfa4..2faa676e2 100644 --- a/examples/Ex101_InterpPlate.py +++ b/examples/Ex101_InterpPlate.py @@ -1,5 +1,4 @@ -import numpy as np -from numpy import sin, cos, pi, sqrt +from math import sin, cos, pi, sqrt import cadquery as cq # TEST_1 @@ -8,6 +7,7 @@ edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] surface_points = [[5.,5.,5.]] plate_0 = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness) +print("plate_0.val().Volume() = ", plate_0.val().Volume()) plate_0 = plate_0.translate((0,6*12,0)) show_object(plate_0) @@ -21,6 +21,7 @@ surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] plate_1 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) #plate_1 = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness) # list of (x,y,z) points instead of wires for edges +print("plate_1.val().Volume() = ", plate_1.val().Volume()) show_object(plate_1) # EXAMPLE 2 @@ -33,8 +34,9 @@ edge_wire = cq.Workplane('XY').polyline(edge_points) r2=4.5 surface_points = [[r2*cos(i * pi/fn), r2*sin(i * pi/fn), 1.] for i in range(2*fn)] + [[0.,0.,-2.]] -plate_2 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=49) +plate_2 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, degree=3, nbPtsOnCur=15, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=49) #plate_2 = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=49) # list of (x,y,z) points instead of wires for edges +print("plate_2.val().Volume() = ", plate_2.val().Volume()) plate_2 = plate_2.translate((0,2*12,0)) show_object(plate_2) @@ -45,28 +47,20 @@ ca = cos(30. * pi/180.) sa = sin(30. * pi/180.) # EVEN ROWS -x_p = np.arange(-N*r1, N*r1, ca*2*r1) -y_p = np.arange(-N*r1, N*r1, 3*r1) -x_p, y_p = np.meshgrid(x_p, y_p) -xy_p_even = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] -# ODD ROWS -x_p = np.arange(-(N-0.5)*r1*ca, (N+1.5)*r1*ca, ca*2*r1) -y_p = np.arange(-(N-2+sa)*r1, (N+1+sa)*r1, 3*r1) -x_p, y_p = np.meshgrid(x_p, y_p) -xy_p_odd = [(x,y) for x,y in zip(x_p.flatten(), y_p.flatten())] -pts = xy_p_even + xy_p_odd +pts = [(-3.0, -3.0), (-1.2679491924311226, -3.0), (0.46410161513775483, -3.0), (2.196152422706632, -3.0), (-3.0, 0.0), (-1.2679491924311226, 0.0), (0.46410161513775483, 0.0), (2.196152422706632, 0.0), (-2.165063509461097, -1.5), (-0.4330127018922194, -1.5), (1.299038105676658, -1.5), (3.031088913245535, -1.5), (-2.165063509461097, 1.5), (-0.4330127018922194, 1.5), (1.299038105676658, 1.5), (3.031088913245535, 1.5)] # Spike surface thickness = 0.1 fn = 6 edge_points = [[r1*cos(i * 2*pi/fn), r1*sin(i * 2*pi/fn)] for i in range(fn+1)] surface_points = [[0.25,0,0.75], [-0.25,0,0.75], [0,0.25,0.75], [0,-0.25,0.75], [0,0,2]] edge_wire = cq.Workplane('XY').polyline(edge_points) -be = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, Degree=2, NbPtsOnCur=20, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9) +be = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, degree=2, nbPtsOnCur=20, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=9) #be = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness, combine=True, clean=True, Degree=2, NbPtsOnCur=20, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9) # list of (x,y,z) points instead of wires for edges # Pattern on sphere def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use with each() return be.rotate((0,0,0),(0,0,1), 30).translate(pos).val() plate_3 = cq.Workplane('XY').pushPoints(pts).each(face) +print("plate_3.val().Volume() = ", plate_3.val().Volume()) plate_3 = plate_3.translate((0,4*11,0)) show_object(plate_3) @@ -81,5 +75,6 @@ def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use edge_wire = edge_wire.add(cq.Workplane(plane_list[i+1]).workplane(offset=-offset_list[i+1]).spline(edge_points[i+1])) surface_points = [[0,0,0]] plate_4 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) +print("plate_4.val().Volume() = ", plate_4.val().Volume()) plate_4 = plate_4.translate((0,5*12,0)) show_object(plate_4) From a165473c95559174e1bf784fe4bd5fab2ff7c4c0 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Mon, 6 Jan 2020 15:22:24 +0100 Subject: [PATCH 41/70] set decimal_places to 0 --- tests/test_cadquery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index 02781bb7c..dc090fed7 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2179,7 +2179,7 @@ def test_assembleEdges(self): def test_interpPlate(self): - decimal_places = 9 + decimal_places = 0 # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. thickness = 0 From 95a34b89aea1e4f3268fa84d86086b78df6b3758 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Mon, 6 Jan 2020 16:41:25 +0100 Subject: [PATCH 42/70] arguments set to lowercase --- cadquery/occ_impl/shapes.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index 573974e3b..b66ecc543 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -978,10 +978,10 @@ def innerWires(self): return [w for w in self.Wires() if not w.isSame(outer)] @classmethod - def makeNSidedSurface(cls, edges, points, continuity=GeomAbs_C0, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9): + def makeNSidedSurface(cls, edges, points, continuity=GeomAbs_C0, degree=3, nbPtsOnCur=15, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=9): """ Returns a surface enclosed by a closed polygon defined by 'edges' and going through 'points'. - :param points + :param points :type points: list of gp_Pnt :param edges :type edges: list of TopologyExplorer().edges() @@ -1008,8 +1008,8 @@ def makeNSidedSurface(cls, edges, points, continuity=GeomAbs_C0, Degree=3, NbPts :param MaxSegments = 9 (OCCT default) :type MaxSegments: Integer >= 2 (?) """ - - n_sided = BRepOffsetAPI_MakeFilling(Degree, NbPtsOnCur, NbIter, Anisotropie, Tol2d, Tol3d, TolAng, TolCurv, MaxDeg, MaxSegments) + + n_sided = BRepOffsetAPI_MakeFilling(degree, nbPtsOnCur, nbIter, anisotropy, tol2d, tol3d, tolAng, tolCurv, maxDeg, maxSegments) for edg in edges: n_sided.Add(edg, continuity) for pt in points: @@ -1200,7 +1200,7 @@ class Solid(Shape, Mixin3D): """ @classmethod - def interpPlate(cls, surf_edges, surf_pts, thickness, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9): + def interpPlate(cls, surf_edges, surf_pts, thickness, degree=3, nbPtsOnCur=15, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=9): """ Returns a plate surface that is 'thickness' thick, enclosed by 'surf_edge_pts' points, and going through 'surf_pts' points. @@ -1256,7 +1256,7 @@ def interpPlate(cls, surf_edges, surf_pts, thickness, Degree=3, NbPtsOnCur=15, N # MAKE SURFACE continuity = GeomAbs_C0 # Fixed, changing to anything else crashes. - face = Face.makeNSidedSurface(edges, pts_array, continuity, Degree, NbPtsOnCur, NbIter, Anisotropie, Tol2d, Tol3d, TolAng, TolCurv, MaxDeg, MaxSegments) + face = Face.makeNSidedSurface(edges, pts_array, continuity, degree, nbPtsOnCur, nbIter, anisotropy, tol2d, tol3d, tolAng, tolCurv, maxDeg, maxSegments) # THICKEN SURFACE if abs(thickness)>0: # abs() because negative values are allowed to set direction of thickening From 1c6d69ac2b0a12829b31f717f18caa4d764de783 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Tue, 7 Jan 2020 07:14:42 +0100 Subject: [PATCH 43/70] Test different decimal_places values --- tests/test_cadquery.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index dc090fed7..ffdbc873f 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2187,7 +2187,7 @@ def test_interpPlate(self): surface_points = [[5.,5.,5.]] plate_0 = Workplane('XY').interpPlate(edge_points, surface_points, thickness) self.assertTrue(plate_0.val().isValid()) - self.assertAlmostEqual(plate_0.val().Area(), 141.218823892, decimal_places) + self.assertAlmostEqual(plate_0.val().Area(), 141.218823892, 1) # Plate with 5 sides and 2 bumps, one side is not co-planar with the other sides thickness = 0.1 @@ -2198,7 +2198,7 @@ def test_interpPlate(self): surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] plate_1 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness) self.assertTrue(plate_1.val().isValid()) - self.assertAlmostEqual(plate_1.val().Volume(), 26.124970206, decimal_places) + self.assertAlmostEqual(plate_1.val().Volume(), 26.124970206, 3) # Embossed star, need to change optional parameters to obtain nice looking result. r1=3. @@ -2211,7 +2211,7 @@ def test_interpPlate(self): surface_points = [[r2*math.cos(i * math.pi/fn), r2*math.sin(i * math.pi/fn), 1.] for i in range(2*fn)] + [[0.,0.,-2.]] plate_2 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, degree=3, nbPtsOnCur=15, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=49) self.assertTrue(plate_2.val().isValid()) - self.assertAlmostEqual(plate_2.val().Volume(), 10.956054314, decimal_places) + self.assertAlmostEqual(plate_2.val().Volume(), 10.956054314, 1) # Points on hexagonal pattern coordinates, use of pushpoints. r1 = 1. @@ -2231,7 +2231,7 @@ def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use return be.rotate((0,0,0),(0,0,1), 30).translate(pos).val() plate_3 = Workplane('XY').pushPoints(pts).each(face) self.assertTrue(plate_3.val().isValid()) - self.assertAlmostEqual(plate_3.val().Volume(), 0.459661032, decimal_places) + self.assertAlmostEqual(plate_3.val().Volume(), 0.459661032, 3) # Gyroïd, all edges are splines on different workplanes. thickness = 0.1 @@ -2244,4 +2244,4 @@ def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use surface_points = [[0,0,0]] plate_4 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness) self.assertTrue(plate_4.val().isValid()) - self.assertAlmostEqual(plate_4.val().Volume(), 7.760559490, decimal_places) + self.assertAlmostEqual(plate_4.val().Volume(), 7.760559490, 3) From e14976dd116b23b091525c323988e02bdca2d02f Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Tue, 7 Jan 2020 07:34:51 +0100 Subject: [PATCH 44/70] Test different decimal_places values --- tests/test_cadquery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index ffdbc873f..4b2bb27be 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2211,7 +2211,7 @@ def test_interpPlate(self): surface_points = [[r2*math.cos(i * math.pi/fn), r2*math.sin(i * math.pi/fn), 1.] for i in range(2*fn)] + [[0.,0.,-2.]] plate_2 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, degree=3, nbPtsOnCur=15, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=49) self.assertTrue(plate_2.val().isValid()) - self.assertAlmostEqual(plate_2.val().Volume(), 10.956054314, 1) + self.assertAlmostEqual(plate_2.val().Volume(), 10.956054314, 0) # Points on hexagonal pattern coordinates, use of pushpoints. r1 = 1. From fac69ed6f00df9b653607e01c030fd49b7669060 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Tue, 7 Jan 2020 07:48:50 +0100 Subject: [PATCH 45/70] Test different decimal_places values --- tests/test_cadquery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index 4b2bb27be..6301fe90f 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2231,7 +2231,7 @@ def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use return be.rotate((0,0,0),(0,0,1), 30).translate(pos).val() plate_3 = Workplane('XY').pushPoints(pts).each(face) self.assertTrue(plate_3.val().isValid()) - self.assertAlmostEqual(plate_3.val().Volume(), 0.459661032, 3) + self.assertAlmostEqual(plate_3.val().Volume(), 0.459661032, 1) # Gyroïd, all edges are splines on different workplanes. thickness = 0.1 From 415ffc554901ea777ced56e797d42cd32f4978d6 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Tue, 7 Jan 2020 08:02:23 +0100 Subject: [PATCH 46/70] Final decimal places values set --- tests/test_cadquery.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index 6301fe90f..7a379c9b8 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2179,8 +2179,6 @@ def test_assembleEdges(self): def test_interpPlate(self): - decimal_places = 0 - # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. thickness = 0 edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] From 93da71869ef31d92f7ff5e7b5ad21a435c0ba7cb Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Tue, 7 Jan 2020 08:03:01 +0100 Subject: [PATCH 47/70] 'TestAssembleEdges' not needed anymore --- tests/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/__init__.py b/tests/__init__.py index 2c0ca15d2..bae4439d7 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -59,5 +59,4 @@ def assertTupleAlmostEquals(self, expected, actual, places): 'TestImporters', 'TestJupyter', 'TestWorkplanes', - 'TestAssembleEdges', ] From d4f7db7b13c752fda8a373c1ff3802922032cbd7 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Tue, 7 Jan 2020 08:41:28 +0100 Subject: [PATCH 48/70] Docstring added --- tests/test_cadquery.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index 7a379c9b8..5ae1b0ae5 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2178,6 +2178,10 @@ def test_assembleEdges(self): edge_wire = Wire.assembleEdges(edge_wire) def test_interpPlate(self): + """ + Tests the interpPlate() functionnalites + Numerical values of Areas and Volumes were obtained with the Area() and Volume() functions on a Linux machine under Debian 10 with python 3.7. + """ # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. thickness = 0 From 25fe17cc372e2797ff17d2c8034dc5ddbf1c26ba Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 15:10:13 +0100 Subject: [PATCH 49/70] Add files via upload Corrected for pushpoints() use --- cadquery/cq.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cadquery/cq.py b/cadquery/cq.py index 68e2e296e..b279b5299 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -2730,7 +2730,7 @@ def _sweep(self, path, multisection=False, makeSolid=True, isFrenet=False, def interpPlate(self, surf_edges, surf_pts=[], thickness=0, combine=True, clean=True, degree=3, nbPtsOnCur=15, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=9): """ - Returns a plate surface that is 'thickness' thick, enclosed by 'surf_edge_pts' points, and going through 'surf_pts' points. + Returns a plate surface that is 'thickness' thick, enclosed by 'surf_edge_pts' points, and going through 'surf_pts' points. Using pushpoints directly with interpPlate and combine=True, can be very ressources intensive depending on the complexity of the shape. In this case set combine=False. :param surf_edges :type 1 surf_edges: list of [x,y,z] float ordered coordinates @@ -2771,7 +2771,7 @@ def interpPlate(self, surf_edges, surf_pts=[], thickness=0, combine=True, clean= # Creates interpolated plate def _makeplate(pnt): - return Solid.interpPlate(surf_edges, surf_pts, thickness, degree, nbPtsOnCur, nbIter, anisotropy, tol2d, tol3d, tolAng, tolCurv, maxDeg, maxSegments) + return Solid.interpPlate(surf_edges, surf_pts, thickness, degree, nbPtsOnCur, nbIter, anisotropy, tol2d, tol3d, tolAng, tolCurv, maxDeg, maxSegments).translate(pnt) plates = self.eachpoint(_makeplate, True) @@ -2779,7 +2779,6 @@ def _makeplate(pnt): if not combine: return plates else: - # combine everything --> CRASHES when using pushpoints directly with interpPlate, probably OCC 6 CAD kernel issue, better use pushpoints through each() instead. return self.union(plates, clean=clean) def box(self, length, width, height, centered=(True, True, True), combine=True, clean=True): From 26d7c819915b6ea66712e2873744a670a1e5d22f Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 15:10:41 +0100 Subject: [PATCH 50/70] Add files via upload Corrected for pushpoints() use --- cadquery/occ_impl/shapes.py | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index b66ecc543..b9255688b 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -88,6 +88,9 @@ from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs, STEPControl_Controller +from OCC.Core.Interface import Interface_Static_SetCVal +from OCC.Core.Interface import Interface_Static_SetIVal from OCC.Core.BRepMesh import BRepMesh_IncrementalMesh from OCC.Core.StlAPI import StlAPI_Writer @@ -110,10 +113,10 @@ from OCC.Core.BRepClass3d import BRepClass3d_SolidClassifier -from OCC.Core.GeomAbs import GeomAbs_C0 -from OCC.Extend.TopologyUtils import TopologyExplorer, WireExplorer -from OCC.Core.GeomAbs import GeomAbs_Intersection -from OCC.Core.BRepOffsetAPI import BRepOffsetAPI_MakeFilling +from OCC.Core.GeomAbs import GeomAbs_C0 +from OCC.Extend.TopologyUtils import TopologyExplorer, WireExplorer +from OCC.Core.GeomAbs import GeomAbs_Intersection +from OCC.Core.BRepOffsetAPI import BRepOffsetAPI_MakeFilling from OCC.Core.BRepOffset import BRepOffset_MakeOffset, BRepOffset_Skin from OCC.Core.ShapeFix import ShapeFix_Wire @@ -262,12 +265,25 @@ def exportStl(self, fileName, precision=1e-5): return writer.Write(self.wrapped, fileName) def exportStep(self, fileName): - + """ + STEPControl_AsIs translates an Open CASCADE shape to its highest possible STEP representation. + STEPControl_ManifoldSolidBrep translates an Open CASCADE shape to a STEP manifold_solid_brep or brep_with_voids entity. NO + STEPControl_FacetedBrep translates an Open CASCADE shape into a STEP faceted_brep entity. NO + STEPControl_ShellBasedSurfaceModel translates an Open CASCADE shape into a STEP shell_based_surface_model entity. SAME SIZE + STEPControl_GeometricCurveSet translates an Open CASCADE shape into a STEP geometric_curve_set entity. LINES ONLY + surface_curve_mode: + 0: write without pcurves (2 times smaller STEP file) + 1 (Default): write with pcurves + """ + c = STEPControl_Controller() + c.Init() + Interface_Static_SetCVal("write.step.schema", "AP214") + Interface_Static_SetIVal('write.surfacecurve.mode', 0) writer = STEPControl_Writer() writer.Transfer(self.wrapped, STEPControl_AsIs) - + return writer.Write(fileName) - + def exportBrep(self, fileName): """ Export given shape to a BREP file @@ -1232,7 +1248,7 @@ def interpPlate(cls, surf_edges, surf_pts, thickness, degree=3, nbPtsOnCur=15, n :param MaxSegments = 9 (OCCT default) :type MaxSegments: Integer >= 2 (?) """ - + # POINTS CONSTRAINTS: list of (x,y,z) points, optional. pts_array = [gp_Pnt(*pt) for pt in surf_pts] From 4db4fc64047a4e54c70f83220d77240bff6dd890 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 15:11:03 +0100 Subject: [PATCH 51/70] Add files via upload Corrected for pushpoints() use --- examples/Ex101_InterpPlate.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/examples/Ex101_InterpPlate.py b/examples/Ex101_InterpPlate.py index 2faa676e2..d8596719c 100644 --- a/examples/Ex101_InterpPlate.py +++ b/examples/Ex101_InterpPlate.py @@ -47,19 +47,14 @@ ca = cos(30. * pi/180.) sa = sin(30. * pi/180.) # EVEN ROWS -pts = [(-3.0, -3.0), (-1.2679491924311226, -3.0), (0.46410161513775483, -3.0), (2.196152422706632, -3.0), (-3.0, 0.0), (-1.2679491924311226, 0.0), (0.46410161513775483, 0.0), (2.196152422706632, 0.0), (-2.165063509461097, -1.5), (-0.4330127018922194, -1.5), (1.299038105676658, -1.5), (3.031088913245535, -1.5), (-2.165063509461097, 1.5), (-0.4330127018922194, 1.5), (1.299038105676658, 1.5), (3.031088913245535, 1.5)] +pts = [(-3.0, -3.0), (-1.267949, -3.0), (0.464102, -3.0), (2.196152, -3.0), (-3.0, 0.0), (-1.267949, 0.0), (0.464102, 0.0), (2.196152, 0.0), (-2.133974, -1.5), (-0.401923, -1.5), (1.330127, -1.5), (3.062178, -1.5), (-2.133975, 1.5), (-0.401924, 1.5), (1.330127, 1.5), (3.062178, 1.5)] # Spike surface thickness = 0.1 fn = 6 -edge_points = [[r1*cos(i * 2*pi/fn), r1*sin(i * 2*pi/fn)] for i in range(fn+1)] -surface_points = [[0.25,0,0.75], [-0.25,0,0.75], [0,0.25,0.75], [0,-0.25,0.75], [0,0,2]] +edge_points = [[r1*cos(i * 2*pi/fn + 30*pi/180), r1*sin(i * 2*pi/fn + 30*pi/180)] for i in range(fn+1)] +surface_points = [[r1/4*cos(i * 2*pi/fn + 30*pi/180), r1/4*sin(i * 2*pi/fn + 30*pi/180), 0.75] for i in range(fn+1)] + [[0,0,2]] edge_wire = cq.Workplane('XY').polyline(edge_points) -be = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, degree=2, nbPtsOnCur=20, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=9) -#be = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness, combine=True, clean=True, Degree=2, NbPtsOnCur=20, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=9) # list of (x,y,z) points instead of wires for edges -# Pattern on sphere -def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use with each() - return be.rotate((0,0,0),(0,0,1), 30).translate(pos).val() -plate_3 = cq.Workplane('XY').pushPoints(pts).each(face) +plate_3 = cq.Workplane('XY').pushPoints(pts).interpPlate(edge_wire, surface_points, thickness, combine=False, clean=False, degree=2, nbPtsOnCur=20, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=9) print("plate_3.val().Volume() = ", plate_3.val().Volume()) plate_3 = plate_3.translate((0,4*11,0)) show_object(plate_3) From 8085955ca31dafec4d5ec127434a7023e1b04a00 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 15:11:24 +0100 Subject: [PATCH 52/70] Add files via upload Corrected for pushpoints() use --- tests/test_cadquery.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index 5ae1b0ae5..1748e3bba 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2220,20 +2220,17 @@ def test_interpPlate(self): N = 3 ca = math.cos(30. * math.pi/180.) sa = math.sin(30. * math.pi/180.) - pts = [(-3.0, -3.0), (-1.2679491924311226, -3.0), (0.46410161513775483, -3.0), (2.196152422706632, -3.0), (-3.0, 0.0), (-1.2679491924311226, 0.0), (0.46410161513775483, 0.0), (2.196152422706632, 0.0), (-2.165063509461097, -1.5), (-0.4330127018922194, -1.5), (1.299038105676658, -1.5), (3.031088913245535, -1.5), (-2.165063509461097, 1.5), (-0.4330127018922194, 1.5), (1.299038105676658, 1.5), (3.031088913245535, 1.5)] - # Smath.pike surface + # EVEN ROWS + pts = [(-3.0, -3.0), (-1.267949, -3.0), (0.464102, -3.0), (2.196152, -3.0), (-3.0, 0.0), (-1.267949, 0.0), (0.464102, 0.0), (2.196152, 0.0), (-2.133974, -1.5), (-0.401923, -1.5), (1.330127, -1.5), (3.062178, -1.5), (-2.133975, 1.5), (-0.401924, 1.5), (1.330127, 1.5), (3.062178, 1.5)] + # Spike surface thickness = 0.1 fn = 6 - edge_points = [[r1*math.cos(i * 2*math.pi/fn), r1*math.sin(i * 2*math.pi/fn)] for i in range(fn+1)] - surface_points = [[0.25,0,0.75], [-0.25,0,0.75], [0,0.25,0.75], [0,-0.25,0.75], [0,0,2]] + edge_points = [[r1*math.cos(i * 2*math.pi/fn + 30*math.pi/180), r1*math.sin(i * 2*math.pi/fn + 30*math.pi/180)] for i in range(fn+1)] + surface_points = [[r1/4*math.cos(i * 2*math.pi/fn + 30*math.pi/180), r1/4*math.sin(i * 2*math.pi/fn + 30*math.pi/180), 0.75] for i in range(fn+1)] + [[0,0,2]] edge_wire = Workplane('XY').polyline(edge_points) - be = Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, degree=2, nbPtsOnCur=20, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=9) - # Pattern on sphere - def face(pos): # If pushpoints is used directly with interpPlate --> crash! Use with each() - return be.rotate((0,0,0),(0,0,1), 30).translate(pos).val() - plate_3 = Workplane('XY').pushPoints(pts).each(face) + plate_3 = Workplane('XY').pushPoints(pts).interpPlate(edge_wire, surface_points, thickness, combine=False, clean=False, degree=2, nbPtsOnCur=20, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=9) self.assertTrue(plate_3.val().isValid()) - self.assertAlmostEqual(plate_3.val().Volume(), 0.459661032, 1) + self.assertAlmostEqual(plate_3.val().Volume(), 0.45893954685189414, 1) # Gyroïd, all edges are splines on different workplanes. thickness = 0.1 From 52ed6ed4de31f77491f974adbd67d8ed8d5c6729 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 15:29:52 +0100 Subject: [PATCH 53/70] Add files via upload Enable pushpoints() use --- cadquery/cq.py | 512 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 358 insertions(+), 154 deletions(-) diff --git a/cadquery/cq.py b/cadquery/cq.py index b279b5299..1da693899 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -18,8 +18,20 @@ """ import math -from . import Vector, Plane, Shape, Edge, Wire, Face, Solid, Compound, \ - sortWiresByBuildOrder, selectors, exporters +from copy import copy +from . import ( + Vector, + Plane, + Shape, + Edge, + Wire, + Face, + Solid, + Compound, + sortWiresByBuildOrder, + selectors, + exporters, +) class CQContext(object): @@ -31,7 +43,9 @@ class CQContext(object): """ def __init__(self): - self.pendingWires = [] # a list of wires that have been created and need to be extruded + self.pendingWires = ( + [] + ) # a list of wires that have been created and need to be extruded # a list of created pending edges that need to be joined into wires self.pendingEdges = [] # a reference to the first point for a set of edges. @@ -58,6 +72,7 @@ def __init__(self, obj): self.objects = [] self.ctx = CQContext() self.parent = None + self._tag = None if obj: # guarded because sometimes None for internal use self.objects.append(obj) @@ -81,6 +96,17 @@ def newObject(self, objlist): r.objects = list(objlist) return r + def tag(self, name): + """ + Tags the current CQ object for later reference. + + :param name: the name to tag this object with + :type name: string + :returns: self, a cq object with tag applied + """ + self._tag = name + return self + def _collectProperty(self, propName): """ Collects all of the values for propName, @@ -99,8 +125,12 @@ def _collectProperty(self, propName): # tricky-- if an object is a compound of solids, # do not return all of the solids underneath-- typically # then we'll keep joining to ourself - if propName == 'Solids' and isinstance(o, Solid) and o.ShapeType() == 'Compound': - for i in getattr(o, 'Compounds')(): + if ( + propName == "Solids" + and isinstance(o, Solid) + and o.ShapeType() == "Compound" + ): + for i in getattr(o, "Compounds")(): all[i.hashCode()] = i else: if hasattr(o, propName): @@ -249,6 +279,22 @@ def val(self): """ return self.objects[0] + def _getTagged(self, name): + """ + Search the parent chain for a an object with tag == name. + + :param name: the tag to search for + :type name: string + :returns: the first CQ object in the parent chain with tag == name + :raises: ValueError if no object tagged name in the chain + """ + if self._tag == name: + return self + if self.parent is None: + raise ValueError("No CQ object named {} in chain".format(name)) + else: + return self.parent._getTagged(name) + def toOCC(self): """ Directly returns the wrapped FreeCAD object to cut down on the amount of boiler plate code @@ -259,8 +305,9 @@ def toOCC(self): return self.objects[0].wrapped - def workplane(self, offset=0.0, invert=False, centerOption='CenterOfMass', - origin=None): + def workplane( + self, offset=0.0, invert=False, centerOption="CenterOfMass", origin=None + ): """ Creates a new 2-D workplane, located relative to the first face on the stack. @@ -310,6 +357,7 @@ def workplane(self, offset=0.0, invert=False, centerOption='CenterOfMass', For now you can work around by creating a workplane and then offsetting the center afterwards. """ + def _isCoPlanar(f0, f1): """Test if two faces are on the same plane.""" p0 = f0.Center() @@ -318,9 +366,11 @@ def _isCoPlanar(f0, f1): n1 = f1.normalAt() # test normals (direction of planes) - if not ((abs(n0.x - n1.x) < self.ctx.tolerance) or - (abs(n0.y - n1.y) < self.ctx.tolerance) or - (abs(n0.z - n1.z) < self.ctx.tolerance)): + if not ( + (abs(n0.x - n1.x) < self.ctx.tolerance) + or (abs(n0.y - n1.y) < self.ctx.tolerance) + or (abs(n0.z - n1.z) < self.ctx.tolerance) + ): return False # test if p1 is on the plane of f0 (offset of planes) @@ -339,22 +389,23 @@ def _computeXdir(normal): xd = Vector(1, 0, 0) return xd - if centerOption not in {'CenterOfMass', 'ProjectedOrigin', 'CenterOfBoundBox'}: - raise ValueError('Undefined centerOption value provided.') + if centerOption not in {"CenterOfMass", "ProjectedOrigin", "CenterOfBoundBox"}: + raise ValueError("Undefined centerOption value provided.") if len(self.objects) > 1: # are all objects 'PLANE'? - if not all(o.geomType() in ('PLANE', 'CIRCLE') for o in self.objects): + if not all(o.geomType() in ("PLANE", "CIRCLE") for o in self.objects): raise ValueError( - "If multiple objects selected, they all must be planar faces.") + "If multiple objects selected, they all must be planar faces." + ) # are all faces co-planar with each other? if not all(_isCoPlanar(self.objects[0], f) for f in self.objects[1:]): raise ValueError("Selected faces must be co-planar.") - if centerOption in {'CenterOfMass', 'ProjectedOrigin'}: + if centerOption in {"CenterOfMass", "ProjectedOrigin"}: center = Shape.CombinedCenter(self.objects) - elif centerOption == 'CenterOfBoundBox': + elif centerOption == "CenterOfBoundBox": center = Shape.CombinedCenterOfBoundBox(self.objects) normal = self.objects[0].normalAt() @@ -364,26 +415,27 @@ def _computeXdir(normal): obj = self.objects[0] if isinstance(obj, Face): - if centerOption in {'CenterOfMass', 'ProjectedOrigin'}: + if centerOption in {"CenterOfMass", "ProjectedOrigin"}: center = obj.Center() - elif centerOption == 'CenterOfBoundBox': + elif centerOption == "CenterOfBoundBox": center = obj.CenterOfBoundBox() normal = obj.normalAt(center) xDir = _computeXdir(normal) else: - if hasattr(obj, 'Center'): - if centerOption in {'CenterOfMass', 'ProjectedOrigin'}: + if hasattr(obj, "Center"): + if centerOption in {"CenterOfMass", "ProjectedOrigin"}: center = obj.Center() - elif centerOption == 'CenterOfBoundBox': + elif centerOption == "CenterOfBoundBox": center = obj.CenterOfBoundBox() normal = self.plane.zDir xDir = self.plane.xDir else: raise ValueError( - "Needs a face or a vertex or point on a work plane") + "Needs a face or a vertex or point on a work plane" + ) # update center to projected origin if desired - if centerOption == 'ProjectedOrigin': + if centerOption == "ProjectedOrigin": if origin is None: origin = self.plane.origin elif isinstance(origin, tuple): @@ -407,6 +459,31 @@ def _computeXdir(normal): # a new workplane has the center of the workplane on the stack return s + def copyWorkplane(self, obj): + """ + Copies the workplane from obj. + + :param obj: an object to copy the workplane from + :type obj: a CQ object + :returns: a CQ object with obj's workplane + """ + out = Workplane(obj.plane) + out.parent = self + out.ctx = self.ctx + return out + + def workplaneFromTagged(self, name): + """ + Copies the workplane from a tagged parent. + + :param name: tag to search for + :type name: string + :returns: a CQ object with name's workplane + """ + tagged = self._getTagged(name) + out = self.copyWorkplane(tagged) + return out + def first(self): """ Return the first item on the stack @@ -459,9 +536,7 @@ def _findType(self, types, searchStack=True, searchParents=True): return rv[0] if searchParents and self.parent is not None: - return self.parent._findType(types, - searchStack=True, - searchParents=True) + return self.parent._findType(types, searchStack=True, searchParents=True) return None @@ -501,20 +576,23 @@ def findFace(self, searchStack=True, searchParents=True): return self._findType(Face, searchStack, searchParents) - def _selectObjects(self, objType, selector=None): + def _selectObjects(self, objType, selector=None, tag=None): """ Filters objects of the selected type with the specified selector,and returns results :param objType: the type of object we are searching for :type objType: string: (Vertex|Edge|Wire|Solid|Shell|Compound|CompSolid) + :param tag: if set, search the tagged CQ object instead of self + :type tag: string :return: a CQ object with the selected objects on the stack. **Implementation Note**: This is the base implementation of the vertices,edges,faces, solids,shells, and other similar selector methods. It is a useful extension point for plugin developers to make other selector methods. """ + cq_obj = self._getTagged(tag) if tag else self # A single list of all faces from all objects on the stack - toReturn = self._collectProperty(objType) + toReturn = cq_obj._collectProperty(objType) if selector is not None: if isinstance(selector, str) or isinstance(selector, str): @@ -525,7 +603,7 @@ def _selectObjects(self, objType, selector=None): return self.newObject(toReturn) - def vertices(self, selector=None): + def vertices(self, selector=None, tag=None): """ Select the vertices of objects on the stack, optionally filtering the selection. If there are multiple objects on the stack, the vertices of all objects are collected and a list of @@ -533,6 +611,8 @@ def vertices(self, selector=None): :param selector: :type selector: None, a Selector object, or a string selector expression. + :param tag: if set, search the tagged CQ object instead of self + :type tag: string :return: a CQ object who's stack contains the *distinct* vertices of *all* objects on the current stack, after being filtered by the selector, if provided @@ -554,9 +634,9 @@ def vertices(self, selector=None): :py:class:`StringSyntaxSelector` """ - return self._selectObjects('Vertices', selector) + return self._selectObjects("Vertices", selector, tag) - def faces(self, selector=None): + def faces(self, selector=None, tag=None): """ Select the faces of objects on the stack, optionally filtering the selection. If there are multiple objects on the stack, the faces of all objects are collected and a list of all the @@ -564,6 +644,8 @@ def faces(self, selector=None): :param selector: A selector :type selector: None, a Selector object, or a string selector expression. + :param tag: if set, search the tagged CQ object instead of self + :type tag: string :return: a CQ object who's stack contains all of the *distinct* faces of *all* objects on the current stack, filtered by the provided selector. @@ -586,9 +668,9 @@ def faces(self, selector=None): See more about selectors HERE """ - return self._selectObjects('Faces', selector) + return self._selectObjects("Faces", selector, tag) - def edges(self, selector=None): + def edges(self, selector=None, tag=None): """ Select the edges of objects on the stack, optionally filtering the selection. If there are multiple objects on the stack, the edges of all objects are collected and a list of all the @@ -596,6 +678,8 @@ def edges(self, selector=None): :param selector: A selector :type selector: None, a Selector object, or a string selector expression. + :param tag: if set, search the tagged CQ object instead of self + :type tag: string :return: a CQ object who's stack contains all of the *distinct* edges of *all* objects on the current stack, filtered by the provided selector. @@ -617,9 +701,9 @@ def edges(self, selector=None): See more about selectors HERE """ - return self._selectObjects('Edges', selector) + return self._selectObjects("Edges", selector, tag) - def wires(self, selector=None): + def wires(self, selector=None, tag=None): """ Select the wires of objects on the stack, optionally filtering the selection. If there are multiple objects on the stack, the wires of all objects are collected and a list of all the @@ -627,6 +711,8 @@ def wires(self, selector=None): :param selector: A selector :type selector: None, a Selector object, or a string selector expression. + :param tag: if set, search the tagged CQ object instead of self + :type tag: string :return: a CQ object who's stack contains all of the *distinct* wires of *all* objects on the current stack, filtered by the provided selector. @@ -640,9 +726,9 @@ def wires(self, selector=None): See more about selectors HERE """ - return self._selectObjects('Wires', selector) + return self._selectObjects("Wires", selector, tag) - def solids(self, selector=None): + def solids(self, selector=None, tag=None): """ Select the solids of objects on the stack, optionally filtering the selection. If there are multiple objects on the stack, the solids of all objects are collected and a list of all the @@ -650,6 +736,8 @@ def solids(self, selector=None): :param selector: A selector :type selector: None, a Selector object, or a string selector expression. + :param tag: if set, search the tagged CQ object instead of self + :type tag: string :return: a CQ object who's stack contains all of the *distinct* solids of *all* objects on the current stack, filtered by the provided selector. @@ -666,9 +754,9 @@ def solids(self, selector=None): See more about selectors HERE """ - return self._selectObjects('Solids', selector) + return self._selectObjects("Solids", selector, tag) - def shells(self, selector=None): + def shells(self, selector=None, tag=None): """ Select the shells of objects on the stack, optionally filtering the selection. If there are multiple objects on the stack, the shells of all objects are collected and a list of all the @@ -676,6 +764,8 @@ def shells(self, selector=None): :param selector: A selector :type selector: None, a Selector object, or a string selector expression. + :param tag: if set, search the tagged CQ object instead of self + :type tag: string :return: a CQ object who's stack contains all of the *distinct* solids of *all* objects on the current stack, filtered by the provided selector. @@ -686,9 +776,9 @@ def shells(self, selector=None): See more about selectors HERE """ - return self._selectObjects('Shells', selector) + return self._selectObjects("Shells", selector, tag) - def compounds(self, selector=None): + def compounds(self, selector=None, tag=None): """ Select compounds on the stack, optionally filtering the selection. If there are multiple objects on the stack, they are collected and a list of all the distinct compounds @@ -696,6 +786,8 @@ def compounds(self, selector=None): :param selector: A selector :type selector: None, a Selector object, or a string selector expression. + :param tag: if set, search the tagged CQ object instead of self + :type tag: string :return: a CQ object who's stack contains all of the *distinct* solids of *all* objects on the current stack, filtered by the provided selector. @@ -704,7 +796,7 @@ def compounds(self, selector=None): See more about selectors HERE """ - return self._selectObjects('Compounds', selector) + return self._selectObjects("Compounds", selector, tag) def toSvg(self, opts=None): """ @@ -777,8 +869,9 @@ def rotate(self, axisStartPoint, axisEndPoint, angleDegrees): :type angleDegrees: float :returns: a CQ object """ - return self.newObject([o.rotate(axisStartPoint, axisEndPoint, angleDegrees) - for o in self.objects]) + return self.newObject( + [o.rotate(axisStartPoint, axisEndPoint, angleDegrees) for o in self.objects] + ) def mirror(self, mirrorPlane="XY", basePointVector=(0, 0, 0)): """ @@ -789,8 +882,7 @@ def mirror(self, mirrorPlane="XY", basePointVector=(0, 0, 0)): :param basePointVector: the base point to mirror about :type basePointVector: tuple """ - newS = self.newObject( - [self.objects[0].mirror(mirrorPlane, basePointVector)]) + newS = self.newObject([self.objects[0].mirror(mirrorPlane, basePointVector)]) return newS.first() def translate(self, vec): @@ -943,7 +1035,7 @@ class Workplane(CQ): :py:meth:`CQ.workplane` """ - FOR_CONSTRUCTION = 'ForConstruction' + FOR_CONSTRUCTION = "ForConstruction" def __init__(self, inPlane, origin=(0, 0, 0), obj=None): """ @@ -967,7 +1059,7 @@ def __init__(self, inPlane, origin=(0, 0, 0), obj=None): the *current point* is on the origin. """ - if inPlane.__class__.__name__ == 'Plane': + if inPlane.__class__.__name__ == "Plane": tmpPlane = inPlane elif isinstance(inPlane, str) or isinstance(inPlane, str): tmpPlane = Plane.named(inPlane, origin) @@ -976,15 +1068,16 @@ def __init__(self, inPlane, origin=(0, 0, 0), obj=None): if tmpPlane is None: raise ValueError( - 'Provided value {} is not a valid work plane'.format(inPlane)) + "Provided value {} is not a valid work plane".format(inPlane) + ) self.obj = obj self.plane = tmpPlane - self.firstPoint = None # Changed so that workplane has the center as the first item on the stack self.objects = [self.plane.origin] self.parent = None self.ctx = CQContext() + self._tag = None def transformed(self, rotate=(0, 0, 0), offset=(0, 0, 0)): """ @@ -999,10 +1092,10 @@ def transformed(self, rotate=(0, 0, 0), offset=(0, 0, 0)): """ # old api accepted a vector, so we'll check for that. - if rotate.__class__.__name__ == 'Vector': + if rotate.__class__.__name__ == "Vector": rotate = rotate.toTuple() - if offset.__class__.__name__ == 'Vector': + if offset.__class__.__name__ == "Vector": offset = offset.toTuple() p = self.plane.rotated(rotate) @@ -1026,7 +1119,7 @@ def newObject(self, objlist): # copy the current state to the new object ns = Workplane("XY") - ns.plane = self.plane + ns.plane = copy(self.plane) ns.parent = self ns.objects = list(objlist) ns.ctx = self.ctx @@ -1060,8 +1153,7 @@ def _findFromPoint(self, useLocalCoords=False): elif isinstance(obj, Vector): p = obj else: - raise RuntimeError( - "Cannot convert object type '%s' to vector " % type(obj)) + raise RuntimeError("Cannot convert object type '%s' to vector " % type(obj)) if useLocalCoords: return self.plane.toLocalCoords(p) @@ -1194,8 +1286,9 @@ def center(self, x, y): The result is a cube with a round boss on the corner """ "Shift local coordinates to the specified location, according to current coordinates" - self.plane.setOrigin2d(x, y) - n = self.newObject([self.plane.origin]) + new_origin = self.plane.toWorldCoords((x, y)) + n = self.newObject([new_origin]) + n.plane.setOrigin2d(x, y) return n def lineTo(self, x, y, forConstruction=False): @@ -1367,28 +1460,35 @@ def _makeslot(pnt): :return: A CQ object representing a slot """ - radius = diameter/2 + radius = diameter / 2 - p1 = pnt + Vector((-length/2) + radius, diameter/2) + p1 = pnt + Vector((-length / 2) + radius, diameter / 2) p2 = p1 + Vector(length - diameter, 0) p3 = p1 + Vector(length - diameter, -diameter) p4 = p1 + Vector(0, -diameter) arc1 = p2 + Vector(radius, -radius) arc2 = p4 + Vector(-radius, radius) - edges=[(Edge.makeLine(p1,p2))] + edges = [(Edge.makeLine(p1, p2))] edges.append(Edge.makeThreePointArc(p2, arc1, p3)) edges.append(Edge.makeLine(p3, p4)) edges.append(Edge.makeThreePointArc(p4, arc2, p1)) slot = Wire.assembleEdges(edges) - return slot.rotate(pnt, pnt + Vector(0,0,1), angle) + return slot.rotate(pnt, pnt + Vector(0, 0, 1), angle) return self.eachpoint(_makeslot, True) - def spline(self, listOfXYTuple, tangents=None, periodic=False, - forConstruction=False, includeCurrent=False, makeWire=False): + def spline( + self, + listOfXYTuple, + tangents=None, + periodic=False, + forConstruction=False, + includeCurrent=False, + makeWire=False, + ): """ Create a spline interpolated through the provided points. @@ -1435,8 +1535,7 @@ def spline(self, listOfXYTuple, tangents=None, periodic=False, if tangents: t1, t2 = tangents - tangents = (self.plane.toWorldCoords(t1), - self.plane.toWorldCoords(t2)) + tangents = (self.plane.toWorldCoords(t1), self.plane.toWorldCoords(t2)) e = Edge.makeSpline(allPoints, tangents=tangents, periodic=periodic) @@ -1464,9 +1563,9 @@ def parametricCurve(self, func, N=400, start=0, stop=1): """ - allPoints = [func(start+stop*t/N) for t in range(N+1)] + allPoints = [func(start + stop * t / N) for t in range(N + 1)] - return self.spline(allPoints,includeCurrent=False,makeWire=True) + return self.spline(allPoints, includeCurrent=False, makeWire=True) def threePointArc(self, point1, point2, forConstruction=False): """ @@ -1516,10 +1615,16 @@ def sagittaArc(self, endPoint, sag, forConstruction=False): midPoint = endPoint.add(startPoint).multiply(0.5) sagVector = endPoint.sub(startPoint).normalized().multiply(abs(sag)) - if(sag > 0): - sagVector.x, sagVector.y = -sagVector.y, sagVector.x # Rotate sagVector +90 deg + if sag > 0: + sagVector.x, sagVector.y = ( + -sagVector.y, + sagVector.x, + ) # Rotate sagVector +90 deg else: - sagVector.x, sagVector.y = sagVector.y, -sagVector.x # Rotate sagVector -90 deg + sagVector.x, sagVector.y = ( + sagVector.y, + -sagVector.x, + ) # Rotate sagVector -90 deg sagPoint = midPoint.add(sagVector) @@ -1545,7 +1650,7 @@ def radiusArc(self, endPoint, radius, forConstruction=False): # Calculate the sagitta from the radius length = endPoint.sub(startPoint).Length / 2.0 try: - sag = abs(radius) - math.sqrt(radius**2 - length**2) + sag = abs(radius) - math.sqrt(radius ** 2 - length ** 2) except ValueError: raise ValueError("Arc radius is not large enough to reach the end point.") @@ -1580,8 +1685,7 @@ def rotateAndCopy(self, matrix): # attempt to consolidate wires together. consolidated = n.consolidateWires() - rotatedWires = self.plane.rotateShapes( - consolidated.wires().vals(), matrix) + rotatedWires = self.plane.rotateShapes(consolidated.wires().vals(), matrix) for w in rotatedWires: consolidated.objects.append(w) @@ -1616,8 +1720,7 @@ def mirrorY(self): # attempt to consolidate wires together. consolidated = n.consolidateWires() - mirroredWires = self.plane.mirrorInPlane(consolidated.wires().vals(), - 'Y') + mirroredWires = self.plane.mirrorInPlane(consolidated.wires().vals(), "Y") for w in mirroredWires: consolidated.objects.append(w) @@ -1646,8 +1749,7 @@ def mirrorX(self): # attempt to consolidate wires together. consolidated = n.consolidateWires() - mirroredWires = self.plane.mirrorInPlane(consolidated.wires().vals(), - 'X') + mirroredWires = self.plane.mirrorInPlane(consolidated.wires().vals(), "X") for w in mirroredWires: consolidated.objects.append(w) @@ -1859,6 +1961,7 @@ def rect(self, xLen, yLen, centered=True, forConstruction=False): better way to handle forConstruction project points not in the workplane plane onto the workplane plane """ + def makeRectangleWire(pnt): # Here pnt is in local coordinates due to useLocalCoords=True # (xc,yc,zc) = pnt.toTuple() @@ -1909,6 +2012,7 @@ def circle(self, radius, forConstruction=False): project points not in the workplane plane onto the workplane plane """ + def makeCircleWire(obj): cir = Wire.makeCircle(radius, obj, Vector(0, 0, 1)) cir.forConstruction = forConstruction @@ -1927,19 +2031,25 @@ def polygon(self, nSides, diameter, forConstruction=False): :param diameter: the size of the circle the polygon is inscribed into :return: a polygon wire """ + def _makePolygon(center): # pnt is a vector in local coordinates angle = 2.0 * math.pi / nSides pnts = [] for i in range(nSides + 1): - pnts.append(center + Vector((diameter / 2.0 * math.cos(angle * i)), - (diameter / 2.0 * math.sin(angle * i)), 0)) + pnts.append( + center + + Vector( + (diameter / 2.0 * math.cos(angle * i)), + (diameter / 2.0 * math.sin(angle * i)), + 0, + ) + ) return Wire.makePolygon(pnts, forConstruction) return self.eachpoint(_makePolygon, True) - def polyline(self, listOfXYTuple, forConstruction=False, - includeCurrent=False): + def polyline(self, listOfXYTuple, forConstruction=False, includeCurrent=False): """ Create a polyline from a list of points @@ -2089,11 +2199,11 @@ def _makeCbore(center): boreDir = Vector(0, 0, -1) # first make the hole hole = Solid.makeCylinder( - diameter / 2.0, depth, center, boreDir) # local coordianates! + diameter / 2.0, depth, center, boreDir + ) # local coordianates! # add the counter bore - cbore = Solid.makeCylinder( - cboreDiameter / 2.0, cboreDepth, center, boreDir) + cbore = Solid.makeCylinder(cboreDiameter / 2.0, cboreDepth, center, boreDir) r = hole.fuse(cbore) return r @@ -2142,7 +2252,8 @@ def _makeCsk(center): # first make the hole hole = Solid.makeCylinder( - diameter / 2.0, depth, center, boreDir) # local coords! + diameter / 2.0, depth, center, boreDir + ) # local coords! r = cskDiameter / 2.0 h = r / math.tan(math.radians(cskAngle / 2.0)) csk = Solid.makeCone(r, 0.0, h, center, boreDir) @@ -2191,7 +2302,8 @@ def _makeHole(center): boreDir = Vector(0, 0, -1) # first make the hole hole = Solid.makeCylinder( - diameter / 2.0, depth, center, boreDir) # local coordinates! + diameter / 2.0, depth, center, boreDir + ) # local coordinates! return hole return self.cutEach(_makeHole, True, clean) @@ -2235,8 +2347,9 @@ def twistExtrude(self, distance, angleDegrees, combine=True, clean=True): # are multiple sets r = None for ws in wireSets: - thisObj = Solid.extrudeLinearWithRotation(ws[0], ws[1:], self.plane.origin, - eDir, angleDegrees) + thisObj = Solid.extrudeLinearWithRotation( + ws[0], ws[1:], self.plane.origin, eDir, angleDegrees + ) if r is None: r = thisObj else: @@ -2277,7 +2390,8 @@ def extrude(self, distance, combine=True, clean=True, both=False, taper=None): selected may not be planar """ r = self._extrude( - distance, both=both, taper=taper) # 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) @@ -2287,7 +2401,9 @@ def extrude(self, distance, combine=True, clean=True, both=False, taper=None): newS = newS.clean() return newS - def revolve(self, angleDegrees=360.0, axisStart=None, axisEnd=None, combine=True, clean=True): + def revolve( + self, angleDegrees=360.0, axisStart=None, axisEnd=None, combine=True, clean=True + ): """ Use all un-revolved wires in the parent chain to create a solid. @@ -2344,8 +2460,17 @@ def revolve(self, angleDegrees=360.0, axisStart=None, axisEnd=None, combine=True newS = newS.clean() return newS - def sweep(self, path, multisection=False, sweepAlongWires=None, makeSolid=True, isFrenet=False, - combine=True, clean=True, transition='right'): + def sweep( + self, + path, + multisection=False, + sweepAlongWires=None, + makeSolid=True, + isFrenet=False, + combine=True, + clean=True, + transition="right", + ): """ Use all un-extruded wires in the parent chain to create a swept solid. @@ -2360,22 +2485,27 @@ def sweep(self, path, multisection=False, sweepAlongWires=None, makeSolid=True, Possible values are {'transformed','round', 'right'} (default: 'right'). :return: a CQ object with the resulting solid selected. """ - + if not sweepAlongWires is None: - multisection=sweepAlongWires - + multisection = sweepAlongWires + from warnings import warn - warn('sweepAlongWires keyword argument is is depracated and will '\ - 'be removed in the next version; use multisection instead', - DeprecationWarning) - r = self._sweep(path.wire(), multisection, makeSolid, isFrenet, - transition) # returns a Solid (or a compound if there were multiple) + warn( + "sweepAlongWires keyword argument is is depracated and will " + "be removed in the next version; use multisection instead", + DeprecationWarning, + ) + + r = self._sweep( + path.wire(), multisection, makeSolid, isFrenet, transition + ) # returns a Solid (or a compound if there were multiple) if combine: newS = self._combineWithBase(r) else: newS = self.newObject([r]) - if clean: newS = newS.clean() + if clean: + newS = newS.clean() return newS def _combineWithBase(self, obj): @@ -2442,7 +2572,8 @@ def union(self, toUnion=None, clean=True): solids = toUnion.solids().vals() if len(solids) < 1: raise ValueError( - "CQ object must have at least one solid on the stack to union!") + "CQ object must have at least one solid on the stack to union!" + ) newS = solids.pop(0) for s in solids: newS = newS.fuse(s) @@ -2492,7 +2623,7 @@ def cut(self, toCut, clean=True): if clean: newS = newS.clean() - + return self.newObject([newS]) def intersect(self, toIntersect, clean=True): @@ -2522,8 +2653,9 @@ def intersect(self, toIntersect, clean=True): newS = solidRef.intersect(solidToIntersect) - if clean: newS = newS.clean() - + if clean: + newS = newS.clean() + return self.newObject([newS]) def cutBlind(self, distanceToCut, clean=True, taper=None): @@ -2580,19 +2712,18 @@ def cutThruAll(self, clean=True, taper=0): solidRef = self.findSolid() faceRef = self.findFace() - #if no faces on the stack take the nearest face parallel to the plane zDir + # if no faces on the stack take the nearest face parallel to the plane zDir if not faceRef: - #first select all with faces with good orietation + # first select all with faces with good orietation sel = selectors.PerpendicularDirSelector(self.plane.zDir) faces = sel.filter(solidRef.Faces()) - #then select the closest + # then select the closest sel = selectors.NearestToPointSelector(self.plane.origin.toTuple()) faceRef = sel.filter(faces)[0] rv = [] for solid in solidRef.Solids(): - s = solid.dprism(faceRef, wires, thruAll=True, additive=False, - taper=-taper) + s = solid.dprism(faceRef, wires, thruAll=True, additive=False, taper=-taper) if clean: s = s.clean() @@ -2635,9 +2766,8 @@ def _extrude(self, distance, both=False, taper=None): # group wires together into faces based on which ones are inside the others # result is a list of lists - - wireSets = sortWiresByBuildOrder( - list(self.ctx.pendingWires), []) + + wireSets = sortWiresByBuildOrder(list(self.ctx.pendingWires), []) # now all of the wires have been used to create an extrusion self.ctx.pendingWires = [] @@ -2655,18 +2785,17 @@ def _extrude(self, distance, both=False, taper=None): toFuse = [] if taper: - for ws in wireSets: - thisObj = Solid.extrudeLinear(ws[0], [], eDir, taper) - toFuse.append(thisObj) + 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) + 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 both: + thisObj = Solid.extrudeLinear(ws[0], ws[1:], eDir.multiply(-1.0)) + toFuse.append(thisObj) return Compound.makeCompound(toFuse) @@ -2685,8 +2814,7 @@ def _revolve(self, angleDegrees, axisStart, axisEnd): This method is a utility method, primarily for plugin and internal use. """ # We have to gather the wires to be revolved - wireSets = sortWiresByBuildOrder( - list(self.ctx.pendingWires)) + wireSets = sortWiresByBuildOrder(list(self.ctx.pendingWires)) # Mark that all of the wires have been used to create a revolution self.ctx.pendingWires = [] @@ -2694,14 +2822,19 @@ def _revolve(self, angleDegrees, axisStart, axisEnd): # Revolve the wires, make a compound out of them and then fuse them toFuse = [] for ws in wireSets: - thisObj = Solid.revolve( - ws[0], ws[1:], angleDegrees, axisStart, axisEnd) + thisObj = Solid.revolve(ws[0], ws[1:], angleDegrees, axisStart, axisEnd) toFuse.append(thisObj) return Compound.makeCompound(toFuse) - def _sweep(self, path, multisection=False, makeSolid=True, isFrenet=False, - transition='right'): + def _sweep( + self, + path, + multisection=False, + makeSolid=True, + isFrenet=False, + transition="right", + ): """ Makes a swept solid from an existing set of pending wires. @@ -2716,19 +2849,37 @@ def _sweep(self, path, multisection=False, makeSolid=True, isFrenet=False, if not multisection: wireSets = sortWiresByBuildOrder(list(self.ctx.pendingWires)) for ws in wireSets: - thisObj = Solid.sweep(ws[0], ws[1:], path.val(), makeSolid, - isFrenet, transition) + thisObj = Solid.sweep( + ws[0], ws[1:], path.val(), makeSolid, isFrenet, transition + ) toFuse.append(thisObj) else: sections = self.ctx.pendingWires thisObj = Solid.sweep_multi(sections, path.val(), makeSolid, isFrenet) toFuse.append(thisObj) - + self.ctx.pendingWires = [] return Compound.makeCompound(toFuse) - def interpPlate(self, surf_edges, surf_pts=[], thickness=0, combine=True, clean=True, degree=3, nbPtsOnCur=15, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=9): + def interpPlate( + self, + surf_edges, + surf_pts=[], + thickness=0, + combine=True, + clean=True, + degree=3, + nbPtsOnCur=15, + nbIter=2, + anisotropy=False, + tol2d=0.00001, + tol3d=0.0001, + tolAng=0.01, + tolCurv=0.1, + maxDeg=8, + maxSegments=9 + ): """ Returns a plate surface that is 'thickness' thick, enclosed by 'surf_edge_pts' points, and going through 'surf_pts' points. Using pushpoints directly with interpPlate and combine=True, can be very ressources intensive depending on the complexity of the shape. In this case set combine=False. @@ -2781,7 +2932,15 @@ def _makeplate(pnt): else: return self.union(plates, clean=clean) - def box(self, length, width, height, centered=(True, True, True), combine=True, clean=True): + def box( + self, + length, + width, + height, + centered=(True, True, True), + combine=True, + clean=True, + ): """ Return a 3d box with specified dimensions for each object on the stack. @@ -2826,14 +2985,14 @@ def box(self, length, width, height, centered=(True, True, True), combine=True, def _makebox(pnt): - #(xp,yp,zp) = self.plane.toLocalCoords(pnt) + # (xp,yp,zp) = self.plane.toLocalCoords(pnt) (xp, yp, zp) = pnt.toTuple() if centered[0]: - xp -= (length / 2.0) + xp -= length / 2.0 if centered[1]: - yp -= (width / 2.0) + yp -= width / 2.0 if centered[2]: - zp -= (height / 2.0) + zp -= height / 2.0 return Solid.makeBox(length, width, height, Vector(xp, yp, zp)) @@ -2846,8 +3005,17 @@ def _makebox(pnt): # combine everything return self.union(boxes, clean=clean) - def sphere(self, radius, direct=(0, 0, 1), angle1=-90, angle2=90, angle3=360, - centered=(True, True, True), combine=True, clean=True): + def sphere( + self, + radius, + direct=(0, 0, 1), + angle1=-90, + angle2=90, + angle3=360, + centered=(True, True, True), + combine=True, + clean=True, + ): """ Returns a 3D sphere with the specified radius for each point on the stack @@ -2904,7 +3072,9 @@ def _makesphere(pnt): if not centered[2]: zp += radius - return Solid.makeSphere(radius, Vector(xp, yp, zp), direct, angle1, angle2, angle3) + return Solid.makeSphere( + radius, Vector(xp, yp, zp), direct, angle1, angle2, angle3 + ) # We want a sphere for each point on the workplane spheres = self.eachpoint(_makesphere, True) @@ -2915,8 +3085,21 @@ def _makesphere(pnt): else: return self.union(spheres, clean=clean) - def wedge(self, dx, dy, dz, xmin, zmin, xmax, zmax, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1), - centered=(True, True, True), combine=True, clean=True): + def wedge( + self, + dx, + dy, + dz, + xmin, + zmin, + xmax, + zmax, + pnt=Vector(0, 0, 0), + dir=Vector(0, 0, 1), + centered=(True, True, True), + combine=True, + clean=True, + ): """ :param dx: Distance along the X axis :param dy: Distance along the Y axis @@ -2954,15 +3137,17 @@ def _makewedge(pnt): (xp, yp, zp) = pnt.toTuple() if not centered[0]: - xp += dx / 2. + xp += dx / 2.0 if not centered[1]: - yp += dy / 2. + yp += dy / 2.0 if not centered[2]: - zp += dx / 2. + zp += dx / 2.0 - return Solid.makeWedge(dx, dy, dz, xmin, zmin, xmax, zmax, Vector(xp, yp, zp), dir) + return Solid.makeWedge( + dx, dy, dz, xmin, zmin, xmax, zmax, Vector(xp, yp, zp), dir + ) # We want a wedge for each point on the workplane wedges = self.eachpoint(_makewedge) @@ -2998,11 +3183,23 @@ def clean(self): cleanObjects = [obj.clean() for obj in self.objects] except AttributeError: raise AttributeError( - "%s object doesn't support `clean()` method!" % obj.ShapeType()) + "%s object doesn't support `clean()` method!" % obj.ShapeType() + ) return self.newObject(cleanObjects) - def text(self, txt, fontsize, distance, cut=True, combine=False, clean=True, - font="Arial", kind='regular',halign='center',valign='center'): + def text( + self, + txt, + fontsize, + distance, + cut=True, + combine=False, + clean=True, + font="Arial", + kind="regular", + halign="center", + valign="center", + ): """ Create a 3D text @@ -3029,8 +3226,16 @@ def text(self, txt, fontsize, distance, cut=True, combine=False, clean=True, and the resulting solid becomes the new context solid. """ - r = Compound.makeText(txt,fontsize,distance,font=font,kind=kind, - halign=halign, valign=valign, position=self.plane) + r = Compound.makeText( + txt, + fontsize, + distance, + font=font, + kind=kind, + halign=halign, + valign=valign, + position=self.plane, + ) if cut: newS = self._cutFromBase(r) @@ -3048,7 +3253,6 @@ def _repr_html_(self): """ if type(self.objects[0]) is Vector: - return '< {} >'.format(self.__repr__()[1:-1]) + return "< {} >".format(self.__repr__()[1:-1]) else: return Compound.makeCompound(self.objects)._repr_html_() - From b6bc8c4dc18c1f7b61076a28b0a6f3d5ce3fd1cd Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 15:30:17 +0100 Subject: [PATCH 54/70] Add files via upload Enable pushpoints() use --- cadquery/occ_impl/shapes.py | 817 ++++++++++++++++++++---------------- 1 file changed, 446 insertions(+), 371 deletions(-) diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index b9255688b..736755a80 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -3,84 +3,104 @@ import OCC.Core.TopAbs as ta # Tolopolgy type enum import OCC.Core.GeomAbs as ga # Geometry type enum -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) +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, +) # collection of pints (used for spline construction) from OCC.Core.TColgp import TColgp_HArray1OfPnt from OCC.Core.BRepAdaptor import BRepAdaptor_Curve, BRepAdaptor_Surface -from OCC.Core.BRepBuilderAPI import (BRepBuilderAPI_MakeVertex, - BRepBuilderAPI_MakeEdge, - BRepBuilderAPI_MakeFace, - BRepBuilderAPI_MakePolygon, - BRepBuilderAPI_MakeWire, - BRepBuilderAPI_Sewing, - BRepBuilderAPI_MakeSolid, - BRepBuilderAPI_Copy, - BRepBuilderAPI_GTransform, - BRepBuilderAPI_Transform, - BRepBuilderAPI_Transformed, - BRepBuilderAPI_RightCorner, - BRepBuilderAPI_RoundCorner) +from OCC.Core.BRepBuilderAPI import ( + BRepBuilderAPI_MakeVertex, + BRepBuilderAPI_MakeEdge, + BRepBuilderAPI_MakeFace, + BRepBuilderAPI_MakePolygon, + BRepBuilderAPI_MakeWire, + BRepBuilderAPI_Sewing, + BRepBuilderAPI_MakeSolid, + BRepBuilderAPI_Copy, + BRepBuilderAPI_GTransform, + BRepBuilderAPI_Transform, + BRepBuilderAPI_Transformed, + BRepBuilderAPI_RightCorner, + BRepBuilderAPI_RoundCorner, +) + # properties used to store mass calculation result from OCC.Core.GProp import GProp_GProps -from OCC.Core.BRepGProp import BRepGProp_Face, \ - brepgprop_LinearProperties, \ - brepgprop_SurfaceProperties, \ - brepgprop_VolumeProperties # used for mass calculation +from OCC.Core.BRepGProp import ( + BRepGProp_Face, + brepgprop_LinearProperties, + brepgprop_SurfaceProperties, + brepgprop_VolumeProperties, +) # used for mass calculation from OCC.Core.BRepLProp import BRepLProp_CLProps # local curve properties -from OCC.Core.BRepPrimAPI import (BRepPrimAPI_MakeBox, - BRepPrimAPI_MakeCone, - BRepPrimAPI_MakeCylinder, - BRepPrimAPI_MakeTorus, - BRepPrimAPI_MakeWedge, - BRepPrimAPI_MakePrism, - BRepPrimAPI_MakeRevol, - BRepPrimAPI_MakeSphere) +from OCC.Core.BRepPrimAPI import ( + BRepPrimAPI_MakeBox, + BRepPrimAPI_MakeCone, + BRepPrimAPI_MakeCylinder, + BRepPrimAPI_MakeTorus, + BRepPrimAPI_MakeWedge, + BRepPrimAPI_MakePrism, + BRepPrimAPI_MakeRevol, + BRepPrimAPI_MakeSphere, +) from OCC.Core.TopExp import TopExp_Explorer # Toplogy explorer -from OCC.Core.BRepTools import (breptools_UVBounds, - breptools_OuterWire) +from OCC.Core.BRepTools import breptools_UVBounds, breptools_OuterWire + # used for getting underlying geoetry -- is this equvalent to brep adaptor? from OCC.Core.BRep import BRep_Tool, BRep_Tool_Degenerated -from OCC.Core.TopoDS import (topods_Vertex, # downcasting functions - topods_Edge, - topods_Wire, - topods_Face, - topods_Shell, - topods_Compound, - topods_Solid) +from OCC.Core.TopoDS import ( + topods_Vertex, # downcasting functions + topods_Edge, + topods_Wire, + topods_Face, + topods_Shell, + topods_Compound, + topods_Solid, +) -from OCC.Core.TopoDS import (TopoDS_Compound, - TopoDS_Builder) +from OCC.Core.TopoDS import TopoDS_Compound, TopoDS_Builder from OCC.Core.GC import GC_MakeArcOfCircle # geometry construction from OCC.Core.GCE2d import GCE2d_MakeSegment -from OCC.Core.GeomAPI import (GeomAPI_Interpolate, - GeomAPI_ProjectPointOnSurf) +from OCC.Core.GeomAPI import GeomAPI_Interpolate, GeomAPI_ProjectPointOnSurf from OCC.Core.BRepFill import brepfill_Shell, brepfill_Face -from OCC.Core.BRepAlgoAPI import (BRepAlgoAPI_Common, - BRepAlgoAPI_Fuse, - BRepAlgoAPI_Cut) +from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Common, BRepAlgoAPI_Fuse, BRepAlgoAPI_Cut from OCC.Core.Geom import Geom_ConicalSurface, Geom_CylindricalSurface from OCC.Core.Geom2d import Geom2d_Line from OCC.Core.BRepLib import breplib_BuildCurves3d -from OCC.Core.BRepOffsetAPI import (BRepOffsetAPI_ThruSections, - BRepOffsetAPI_MakePipeShell, - BRepOffsetAPI_MakeThickSolid) +from OCC.Core.BRepOffsetAPI import ( + BRepOffsetAPI_ThruSections, + BRepOffsetAPI_MakePipeShell, + BRepOffsetAPI_MakeThickSolid, +) -from OCC.Core.BRepFilletAPI import (BRepFilletAPI_MakeChamfer, - BRepFilletAPI_MakeFillet) +from OCC.Core.BRepFilletAPI import BRepFilletAPI_MakeChamfer, BRepFilletAPI_MakeFillet -from OCC.Core.TopTools import (TopTools_IndexedDataMapOfShapeListOfShape, - TopTools_ListOfShape) +from OCC.Core.TopTools import ( + TopTools_IndexedDataMapOfShapeListOfShape, + TopTools_ListOfShape, +) from OCC.Core.TopExp import topexp_MapShapesAndAncestors @@ -104,10 +124,7 @@ from OCC.Core.BRepCheck import BRepCheck_Analyzer -from OCC.Core.Addons import (text_to_brep, - Font_FA_Regular, - Font_FA_Italic, - Font_FA_Bold) +from OCC.Core.Addons import text_to_brep, Font_FA_Regular, Font_FA_Italic, Font_FA_Bold from OCC.Core.BRepFeat import BRepFeat_MakeDPrism @@ -126,75 +143,81 @@ import warnings TOLERANCE = 1e-6 -DEG2RAD = 2 * pi / 360. +DEG2RAD = 2 * pi / 360.0 HASH_CODE_MAX = 2147483647 # max 32bit signed int, required by OCC.Core.HashCode -shape_LUT = \ - {ta.TopAbs_VERTEX: 'Vertex', - ta.TopAbs_EDGE: 'Edge', - ta.TopAbs_WIRE: 'Wire', - ta.TopAbs_FACE: 'Face', - ta.TopAbs_SHELL: 'Shell', - ta.TopAbs_SOLID: 'Solid', - ta.TopAbs_COMPOUND: 'Compound'} - -shape_properties_LUT = \ - {ta.TopAbs_VERTEX: None, - ta.TopAbs_EDGE: brepgprop_LinearProperties, - ta.TopAbs_WIRE: brepgprop_LinearProperties, - ta.TopAbs_FACE: brepgprop_SurfaceProperties, - ta.TopAbs_SHELL: brepgprop_SurfaceProperties, - ta.TopAbs_SOLID: brepgprop_VolumeProperties, - ta.TopAbs_COMPOUND: brepgprop_VolumeProperties} +shape_LUT = { + ta.TopAbs_VERTEX: "Vertex", + ta.TopAbs_EDGE: "Edge", + ta.TopAbs_WIRE: "Wire", + ta.TopAbs_FACE: "Face", + ta.TopAbs_SHELL: "Shell", + ta.TopAbs_SOLID: "Solid", + ta.TopAbs_COMPOUND: "Compound", +} + +shape_properties_LUT = { + ta.TopAbs_VERTEX: None, + ta.TopAbs_EDGE: brepgprop_LinearProperties, + ta.TopAbs_WIRE: brepgprop_LinearProperties, + ta.TopAbs_FACE: brepgprop_SurfaceProperties, + ta.TopAbs_SHELL: brepgprop_SurfaceProperties, + ta.TopAbs_SOLID: brepgprop_VolumeProperties, + ta.TopAbs_COMPOUND: brepgprop_VolumeProperties, +} inverse_shape_LUT = {v: k for k, v in shape_LUT.items()} -downcast_LUT = \ - {ta.TopAbs_VERTEX: topods_Vertex, - ta.TopAbs_EDGE: topods_Edge, - ta.TopAbs_WIRE: topods_Wire, - ta.TopAbs_FACE: topods_Face, - ta.TopAbs_SHELL: topods_Shell, - ta.TopAbs_SOLID: topods_Solid, - ta.TopAbs_COMPOUND: topods_Compound} - -geom_LUT = \ - {ta.TopAbs_VERTEX: 'Vertex', - ta.TopAbs_EDGE: BRepAdaptor_Curve, - ta.TopAbs_WIRE: 'Wire', - ta.TopAbs_FACE: BRepAdaptor_Surface, - ta.TopAbs_SHELL: 'Shell', - ta.TopAbs_SOLID: 'Solid', - ta.TopAbs_COMPOUND: 'Compound'} - -geom_LUT_FACE = \ - {ga.GeomAbs_Plane : 'PLANE', - ga.GeomAbs_Cylinder : 'CYLINDER', - ga.GeomAbs_Cone : 'CONE', - ga.GeomAbs_Sphere : 'SPHERE', - ga.GeomAbs_Torus : 'TORUS', - ga.GeomAbs_BezierSurface : 'BEZIER', - ga.GeomAbs_BSplineSurface : 'BSPLINE', - ga.GeomAbs_SurfaceOfRevolution : 'REVOLUTION', - ga.GeomAbs_SurfaceOfExtrusion : 'EXTRUSION', - ga.GeomAbs_OffsetSurface : 'OFFSET', - ga.GeomAbs_OtherSurface : 'OTHER'} - -geom_LUT_EDGE = \ - {ga.GeomAbs_Line : 'LINE', - ga.GeomAbs_Circle : 'CIRCLE', - ga.GeomAbs_Ellipse : 'ELLIPSE', - ga.GeomAbs_Hyperbola : 'HYPERBOLA', - ga.GeomAbs_Parabola : 'PARABOLA', - ga.GeomAbs_BezierCurve : 'BEZIER', - ga.GeomAbs_BSplineCurve : 'BSPLINE', - ga.GeomAbs_OtherCurve : 'OTHER'} +downcast_LUT = { + ta.TopAbs_VERTEX: topods_Vertex, + ta.TopAbs_EDGE: topods_Edge, + ta.TopAbs_WIRE: topods_Wire, + ta.TopAbs_FACE: topods_Face, + ta.TopAbs_SHELL: topods_Shell, + ta.TopAbs_SOLID: topods_Solid, + ta.TopAbs_COMPOUND: topods_Compound, +} + +geom_LUT = { + ta.TopAbs_VERTEX: "Vertex", + ta.TopAbs_EDGE: BRepAdaptor_Curve, + ta.TopAbs_WIRE: "Wire", + ta.TopAbs_FACE: BRepAdaptor_Surface, + ta.TopAbs_SHELL: "Shell", + ta.TopAbs_SOLID: "Solid", + ta.TopAbs_COMPOUND: "Compound", +} + +geom_LUT_FACE = { + ga.GeomAbs_Plane: "PLANE", + ga.GeomAbs_Cylinder: "CYLINDER", + ga.GeomAbs_Cone: "CONE", + ga.GeomAbs_Sphere: "SPHERE", + ga.GeomAbs_Torus: "TORUS", + ga.GeomAbs_BezierSurface: "BEZIER", + ga.GeomAbs_BSplineSurface: "BSPLINE", + ga.GeomAbs_SurfaceOfRevolution: "REVOLUTION", + ga.GeomAbs_SurfaceOfExtrusion: "EXTRUSION", + ga.GeomAbs_OffsetSurface: "OFFSET", + ga.GeomAbs_OtherSurface: "OTHER", +} + +geom_LUT_EDGE = { + ga.GeomAbs_Line: "LINE", + ga.GeomAbs_Circle: "CIRCLE", + ga.GeomAbs_Ellipse: "ELLIPSE", + ga.GeomAbs_Hyperbola: "HYPERBOLA", + ga.GeomAbs_Parabola: "PARABOLA", + ga.GeomAbs_BezierCurve: "BEZIER", + ga.GeomAbs_BSplineCurve: "BSPLINE", + ga.GeomAbs_OtherCurve: "OTHER", +} def downcast(topods_obj): - ''' + """ Downcasts a TopoDS object to suitable specialized type - ''' + """ return downcast_LUT[topods_obj.ShapeType()](topods_obj) @@ -215,46 +238,46 @@ def __init__(self, obj): def clean(self): """Experimental clean using ShapeUpgrade""" - upgrader = ShapeUpgrade_UnifySameDomain( - self.wrapped, True, True, True) + upgrader = ShapeUpgrade_UnifySameDomain(self.wrapped, True, True, True) upgrader.Build() return self.cast(upgrader.Shape()) - + def fix(self): """Try to fix shape if not valid""" if not BRepCheck_Analyzer(self.wrapped).IsValid(): sf = ShapeFix_Shape(self.wrapped) sf.Perform() fixed = downcast(sf.Shape()) - + return self.cast(fixed) - + return self @classmethod def cast(cls, obj, forConstruction=False): "Returns the right type of wrapper, given a FreeCAD object" - + tr = None # define the shape lookup table for casting - constructor_LUT = {ta.TopAbs_VERTEX: Vertex, - ta.TopAbs_EDGE: Edge, - ta.TopAbs_WIRE: Wire, - ta.TopAbs_FACE: Face, - ta.TopAbs_SHELL: Shell, - ta.TopAbs_SOLID: Solid, - ta.TopAbs_COMPOUND: Compound} + constructor_LUT = { + ta.TopAbs_VERTEX: Vertex, + ta.TopAbs_EDGE: Edge, + ta.TopAbs_WIRE: Wire, + ta.TopAbs_FACE: Face, + ta.TopAbs_SHELL: Shell, + ta.TopAbs_SOLID: Solid, + ta.TopAbs_COMPOUND: Compound, + } t = obj.ShapeType() # NB downcast is nedded to handly TopoDS_Shape types tr = constructor_LUT[t](downcast(obj)) tr.forConstruction = forConstruction - return tr - + def exportStl(self, fileName, precision=1e-5): mesh = BRepMesh_IncrementalMesh(self.wrapped, precision, True) @@ -290,7 +313,7 @@ def exportBrep(self, fileName): """ return breptools_Write(self.wrapped, fileName) - + def geomType(self): """ Gets the underlying geometry type @@ -324,7 +347,7 @@ def geomType(self): return geom_LUT_EDGE[tr(self.wrapped).GetType()] else: return geom_LUT_FACE[tr(self.wrapped).GetType()] - + def hashCode(self): return self.wrapped.HashCode(HASH_CODE_MAX) @@ -356,8 +379,7 @@ def mirror(self, mirrorPlane="XY", basePointVector=(0, 0, 0)): basePointVector = Vector(basePointVector) T = gp_Trsf() - T.SetMirror(gp_Ax2(gp_Pnt(*basePointVector.toTuple()), - mirrorPlaneNormalVector)) + T.SetMirror(gp_Ax2(gp_Pnt(*basePointVector.toTuple()), mirrorPlaneNormalVector)) return self._apply_transform(T) @@ -365,15 +387,14 @@ def mirror(self, mirrorPlane="XY", basePointVector=(0, 0, 0)): def _center_of_mass(shape): Properties = GProp_GProps() - brepgprop_VolumeProperties(shape, - Properties) + brepgprop_VolumeProperties(shape, Properties) return Vector(Properties.CentreOfMass()) def Center(self): - ''' + """ Center of mass - ''' + """ return Shape.centerOfMass(self) @@ -388,14 +409,15 @@ def CombinedCenter(objects): :param objects: a list of objects with mass """ total_mass = sum(Shape.computeMass(o) for o in objects) - weighted_centers = [Shape.centerOfMass(o).multiply( - Shape.computeMass(o)) for o in objects] + weighted_centers = [ + Shape.centerOfMass(o).multiply(Shape.computeMass(o)) for o in objects + ] sum_wc = weighted_centers[0] for wc in weighted_centers[1:]: sum_wc = sum_wc.add(wc) - return Vector(sum_wc.multiply(1. / total_mass)) + return Vector(sum_wc.multiply(1.0 / total_mass)) @staticmethod def computeMass(obj): @@ -443,7 +465,7 @@ def CombinedCenterOfBoundBox(objects, tolerance=0.1): for wc in weighted_centers[1:]: sum_wc = sum_wc.add(wc) - return Vector(sum_wc.multiply(1. / total_mass)) + return Vector(sum_wc.multiply(1.0 / total_mass)) def Closed(self): return self.wrapped.Closed() @@ -466,30 +488,29 @@ def _entities(self, topo_type): def Vertices(self): - return [Vertex(i) for i in self._entities('Vertex')] + return [Vertex(i) for i in self._entities("Vertex")] def Edges(self): - return [Edge(i) for i in self._entities('Edge') if not BRep_Tool_Degenerated(i)] + return [Edge(i) for i in self._entities("Edge") if not BRep_Tool_Degenerated(i)] def Compounds(self): - return [Compound(i) for i in self._entities('Compound')] + return [Compound(i) for i in self._entities("Compound")] def Wires(self): - return [Wire(i) for i in self._entities('Wire')] + return [Wire(i) for i in self._entities("Wire")] def Faces(self): - return [Face(i) for i in self._entities('Face')] + return [Face(i) for i in self._entities("Face")] def Shells(self): - return [Shell(i) for i in self._entities('Shell')] + return [Shell(i) for i in self._entities("Shell")] def Solids(self): - return [Solid(i) for i in self._entities('Solid')] + return [Solid(i) for i in self._entities("Solid")] def Area(self): Properties = GProp_GProps() - brepgprop_SurfaceProperties(self.wrapped, - Properties) + brepgprop_SurfaceProperties(self.wrapped, Properties) return Properties.Mass() @@ -499,9 +520,7 @@ def Volume(self): def _apply_transform(self, T): - return Shape.cast(BRepBuilderAPI_Transform(self.wrapped, - T, - True).Shape()) + return Shape.cast(BRepBuilderAPI_Transform(self.wrapped, T, True).Shape()) def rotate(self, startVector, endVector, angleDegrees): """ @@ -518,9 +537,10 @@ def rotate(self, startVector, endVector, angleDegrees): endVector = Vector(endVector) T = gp_Trsf() - T.SetRotation(gp_Ax1(startVector.toPnt(), - (endVector - startVector).toDir()), - angleDegrees * DEG2RAD) + T.SetRotation( + gp_Ax1(startVector.toPnt(), (endVector - startVector).toDir()), + angleDegrees * DEG2RAD, + ) return self._apply_transform(T) @@ -537,8 +557,7 @@ def translate(self, vector): def scale(self, factor): T = gp_Trsf() - T.SetScale(gp_Pnt(), - factor) + T.SetScale(gp_Pnt(), factor) return self._apply_transform(T) @@ -553,8 +572,9 @@ def transformShape(self, tMatrix): with all objects keeping their type """ - r = Shape.cast(BRepBuilderAPI_Transform(self.wrapped, - tMatrix.wrapped.Trsf()).Shape()) + r = Shape.cast( + BRepBuilderAPI_Transform(self.wrapped, tMatrix.wrapped.Trsf()).Shape() + ) r.forConstruction = self.forConstruction return r @@ -572,9 +592,9 @@ def transformGeometry(self, tMatrix): If your transformation is only translation and rotation, it is safer to use transformShape, which doesnt change the underlying type of the geometry, but cannot handle skew transformations """ - r = Shape.cast(BRepBuilderAPI_GTransform(self.wrapped, - tMatrix.wrapped, - True).Shape()) + r = Shape.cast( + BRepBuilderAPI_GTransform(self.wrapped, tMatrix.wrapped, True).Shape() + ) r.forConstruction = self.forConstruction return r @@ -586,8 +606,7 @@ def cut(self, toCut): """ Remove a shape from another one """ - return Shape.cast(BRepAlgoAPI_Cut(self.wrapped, - toCut.wrapped).Shape()) + return Shape.cast(BRepAlgoAPI_Cut(self.wrapped, toCut.wrapped).Shape()) def fuse(self, toFuse): """ @@ -606,8 +625,7 @@ def intersect(self, toIntersect): """ Construct shape intersection """ - return Shape.cast(BRepAlgoAPI_Common(self.wrapped, - toIntersect.wrapped).Shape()) + return Shape.cast(BRepAlgoAPI_Common(self.wrapped, toIntersect.wrapped).Shape()) def _repr_html_(self): """ @@ -615,6 +633,7 @@ def _repr_html_(self): """ from .jupyter_tools import x3d_display + return x3d_display(self.wrapped, export_edges=True) @@ -635,9 +654,7 @@ def __init__(self, obj, forConstruction=False): def toTuple(self): geom_point = BRep_Tool.Pnt(self.wrapped) - return (geom_point.X(), - geom_point.Y(), - geom_point.Z()) + return (geom_point.X(), geom_point.Y(), geom_point.Z()) def Center(self): """ @@ -648,12 +665,10 @@ def Center(self): @classmethod def makeVertex(cls, x, y, z): - return cls(BRepBuilderAPI_MakeVertex(gp_Pnt(x, y, z) - ).Vertex()) + return cls(BRepBuilderAPI_MakeVertex(gp_Pnt(x, y, z)).Vertex()) class Mixin1D(object): - def Length(self): Properties = GProp_GProps() @@ -714,8 +729,8 @@ def tangentAt(self, locationParam=0.5): curve = self._geomAdaptor() umin, umax = curve.FirstParameter(), curve.LastParameter() - umid = (1-locationParam)*umin + locationParam*umax - + umid = (1 - locationParam) * umin + locationParam * umax + curve_props = BRepLProp_CLProps(curve, 2, curve.Tolerance()) curve_props.SetParameter(umid) @@ -728,35 +743,32 @@ def tangentAt(self, locationParam=0.5): def Center(self): Properties = GProp_GProps() - brepgprop_LinearProperties(self.wrapped, - Properties) + brepgprop_LinearProperties(self.wrapped, Properties) return Vector(Properties.CentreOfMass()) @classmethod - def makeCircle(cls, radius, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1), angle1=360.0, angle2=360): + def makeCircle( + cls, radius, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1), angle1=360.0, angle2=360 + ): """ """ pnt = Vector(pnt) dir = Vector(dir) - circle_gp = gp_Circ(gp_Ax2(pnt.toPnt(), - dir.toDir()), - radius) + circle_gp = gp_Circ(gp_Ax2(pnt.toPnt(), dir.toDir()), radius) if angle1 == angle2: # full circle case return cls(BRepBuilderAPI_MakeEdge(circle_gp).Edge()) else: # arc case - circle_geom = GC_MakeArcOfCircle(circle_gp, - angle1 * DEG2RAD, - angle2 * DEG2RAD, - True).Value() + circle_geom = GC_MakeArcOfCircle( + circle_gp, angle1 * DEG2RAD, angle2 * DEG2RAD, True + ).Value() return cls(BRepBuilderAPI_MakeEdge(circle_geom).Edge()) @classmethod - def makeSpline(cls, listOfVector, tangents=None, periodic=False, - tol = 1e-6): + def makeSpline(cls, listOfVector, tangents=None, periodic=False, tol=1e-6): """ Interpolate a spline through the provided points. :param cls: @@ -768,12 +780,12 @@ def makeSpline(cls, listOfVector, tangents=None, periodic=False, """ pnts = TColgp_HArray1OfPnt(1, len(listOfVector)) for ix, v in enumerate(listOfVector): - pnts.SetValue(ix+1, v.toPnt()) + pnts.SetValue(ix + 1, v.toPnt()) spline_builder = GeomAPI_Interpolate(pnts, periodic, tol) if tangents: - v1,v2 = tangents - spline_builder.Load(v1.wrapped,v2.wrapped) + v1, v2 = tangents + spline_builder.Load(v1.wrapped, v2.wrapped) spline_builder.Perform() spline_geom = spline_builder.Curve() @@ -790,9 +802,7 @@ def makeThreePointArc(cls, v1, v2, v3): :param v3: end vector :return: an edge object through the three points """ - circle_geom = GC_MakeArcOfCircle(v1.toPnt(), - v2.toPnt(), - v3.toPnt()).Value() + circle_geom = GC_MakeArcOfCircle(v1.toPnt(), v2.toPnt(), v3.toPnt()).Value() return cls(BRepBuilderAPI_MakeEdge(circle_geom).Edge()) @@ -804,8 +814,7 @@ def makeLine(cls, v1, v2): :param v2: Vector that represents the second point :return: A linear edge between the two provided points """ - return cls(BRepBuilderAPI_MakeEdge(v1.toPnt(), - v2.toPnt()).Edge()) + return cls(BRepBuilderAPI_MakeEdge(v1.toPnt(), v2.toPnt()).Edge()) class Wire(Shape, Mixin1D): @@ -843,17 +852,23 @@ def assembleEdges(cls, listOfEdges): :BRepBuilderAPI_NonManifoldWire = 3 """ wire_builder = BRepBuilderAPI_MakeWire() - + edges_list = TopTools_ListOfShape() for e in listOfEdges: - edges_list.Append(e.wrapped) + edges_list.Append(e.wrapped) wire_builder.Add(edges_list) - if wire_builder.Error()!=0: - w1 = 'BRepBuilderAPI_MakeWire::IsDone(): returns true if this algorithm contains a valid wire. IsDone returns false if: there are no edges in the wire, or the last edge which you tried to add was not connectable = '+ str(wire_builder.IsDone()) - w2 = 'BRepBuilderAPI_MakeWire::Error(): returns the construction status. BRepBuilderAPI_WireDone if the wire is built, or another value of the BRepBuilderAPI_WireError enumeration indicating why the construction failed = ' + str(wire_builder.Error()) + if wire_builder.Error() != 0: + w1 = ( + "BRepBuilderAPI_MakeWire::IsDone(): returns true if this algorithm contains a valid wire. IsDone returns false if: there are no edges in the wire, or the last edge which you tried to add was not connectable = " + + str(wire_builder.IsDone()) + ) + w2 = ( + "BRepBuilderAPI_MakeWire::Error(): returns the construction status. BRepBuilderAPI_WireDone if the wire is built, or another value of the BRepBuilderAPI_WireError enumeration indicating why the construction failed = " + + str(wire_builder.Error()) + ) warnings.warn(w1) warnings.warn(w2) - + return cls(wire_builder.Wire()) @classmethod @@ -884,8 +899,16 @@ def makePolygon(cls, listOfVertices, forConstruction=False): return w @classmethod - def makeHelix(cls, pitch, height, radius, center=Vector(0, 0, 0), - dir=Vector(0, 0, 1), angle=360.0, lefthand=False): + def makeHelix( + cls, + pitch, + height, + radius, + center=Vector(0, 0, 0), + dir=Vector(0, 0, 1), + angle=360.0, + lefthand=False, + ): """ Make a helix with a given pitch, height and radius By default a cylindrical surface is used to create the helix. If @@ -893,13 +916,14 @@ def makeHelix(cls, pitch, height, radius, center=Vector(0, 0, 0), """ # 1. build underlying cylindrical/conical surface - if angle == 360.: - geom_surf = Geom_CylindricalSurface(gp_Ax3(center.toPnt(), dir.toDir()), - radius) + if angle == 360.0: + geom_surf = Geom_CylindricalSurface( + gp_Ax3(center.toPnt(), dir.toDir()), radius + ) else: - geom_surf = Geom_ConicalSurface(gp_Ax3(center.toPnt(), dir.toDir()), - angle * DEG2RAD, - radius) + geom_surf = Geom_ConicalSurface( + gp_Ax3(center.toPnt(), dir.toDir()), angle * DEG2RAD, radius + ) # 2. construct an semgent in the u,v domain if lefthand: @@ -909,8 +933,8 @@ def makeHelix(cls, pitch, height, radius, center=Vector(0, 0, 0), # 3. put it together into a wire n_turns = height / pitch - u_start = geom_line.Value(0.) - u_stop = geom_line.Value(sqrt(n_turns * ((2 * pi)**2 + pitch**2))) + u_start = geom_line.Value(0.0) + u_stop = geom_line.Value(sqrt(n_turns * ((2 * pi) ** 2 + pitch ** 2))) geom_seg = GCE2d_MakeSegment(u_start, u_stop).Value() e = BRepBuilderAPI_MakeEdge(geom_seg, geom_surf).Edge() @@ -964,8 +988,7 @@ def normalAt(self, locationVector=None): v = 0.5 * (v0 + v1) else: # project point on surface - projector = GeomAPI_ProjectPointOnSurf(locationVector.toPnt(), - surface) + projector = GeomAPI_ProjectPointOnSurf(locationVector.toPnt(), surface) u, v = projector.LowerDistanceParameters() @@ -978,19 +1001,18 @@ def normalAt(self, locationVector=None): def Center(self): Properties = GProp_GProps() - brepgprop_SurfaceProperties(self.wrapped, - Properties) + brepgprop_SurfaceProperties(self.wrapped, Properties) return Vector(Properties.CentreOfMass()) - + def outerWire(self): - + return self.cast(breptools_OuterWire(self.wrapped)) - + def innerWires(self): - + outer = self.outerWire() - + return [w for w in self.Wires() if not w.isSame(outer)] @classmethod @@ -1041,11 +1063,11 @@ def makePlane(cls, length, width, basePnt=(0, 0, 0), dir=(0, 0, 1)): pln_geom = gp_Pln(basePnt.toPnt(), dir.toDir()) - return cls(BRepBuilderAPI_MakeFace(pln_geom, - -width * 0.5, - width * 0.5, - -length * 0.5, - length * 0.5).Face()) + return cls( + BRepBuilderAPI_MakeFace( + pln_geom, -width * 0.5, width * 0.5, -length * 0.5, length * 0.5 + ).Face() + ) @classmethod def makeRuledSurface(cls, edgeOrWire1, edgeOrWire2, dist=None): @@ -1056,26 +1078,24 @@ def makeRuledSurface(cls, edgeOrWire1, edgeOrWire2, dist=None): """ if isinstance(edgeOrWire1, Wire): - return cls.cast(brepfill_Shell(edgeOrWire1.wrapped, - edgeOrWire1.wrapped)) + return cls.cast(brepfill_Shell(edgeOrWire1.wrapped, edgeOrWire1.wrapped)) else: - return cls.cast(brepfill_Face(edgeOrWire1.wrapped, - edgeOrWire1.wrapped)) + return cls.cast(brepfill_Face(edgeOrWire1.wrapped, edgeOrWire1.wrapped)) @classmethod def makeFromWires(cls, outerWire, innerWires=[]): - ''' + """ Makes a planar face from one or more wires - ''' - - face_builder = BRepBuilderAPI_MakeFace(outerWire.wrapped,True) + """ + + face_builder = BRepBuilderAPI_MakeFace(outerWire.wrapped, True) for w in innerWires: face_builder.Add(w.wrapped) - + face_builder.Build() face = face_builder.Shape() - + return cls.cast(face).fix() @@ -1093,18 +1113,17 @@ def makeShell(cls, listOfFaces): shell_builder.Add(face.wrapped) shell_builder.Perform() - + return cls.cast(shell_builder.SewedShape()) class Mixin3D(object): - def tessellate(self, tolerance): tess = Tesselator(self.wrapped) tess.Compute(compute_edges=True, mesh_quality=tolerance) vertices = [] - indexes = [] + indexes = [] # add vertices for i_vert in range(tess.ObjGetVertexCount()): @@ -1145,10 +1164,9 @@ def chamfer(self, length, length2, edgeList): # make a edge --> faces mapping edge_face_map = TopTools_IndexedDataMapOfShapeListOfShape() - topexp_MapShapesAndAncestors(self.wrapped, - ta.TopAbs_EDGE, - ta.TopAbs_FACE, - edge_face_map) + topexp_MapShapesAndAncestors( + self.wrapped, ta.TopAbs_EDGE, ta.TopAbs_FACE, edge_face_map + ) # note: we prefer 'length' word to 'radius' as opposed to FreeCAD's API chamfer_builder = BRepFilletAPI_MakeChamfer(self.wrapped) @@ -1162,10 +1180,9 @@ def chamfer(self, length, length2, edgeList): for e in nativeEdges: face = edge_face_map.FindFromKey(e).First() - chamfer_builder.Add(d1, - d2, - e, - topods_Face(face)) # NB: edge_face_map return a generic TopoDS_Shape + chamfer_builder.Add( + d1, d2, e, topods_Face(face) + ) # NB: edge_face_map return a generic TopoDS_Shape return self.__class__(chamfer_builder.Shape()) def shell(self, faceList, thickness, tolerance=0.0001): @@ -1182,10 +1199,9 @@ def shell(self, faceList, thickness, tolerance=0.0001): for f in faceList: occ_faces_list.Append(f.wrapped) - shell_builder = BRepOffsetAPI_MakeThickSolid(self.wrapped, - occ_faces_list, - thickness, - tolerance) + shell_builder = BRepOffsetAPI_MakeThickSolid( + self.wrapped, occ_faces_list, thickness, tolerance + ) shell_builder.Build() @@ -1206,15 +1222,14 @@ def isInside(self, point, tolerance=1.0e-6): solid_classifier = BRepClass3d_SolidClassifier(self.wrapped) solid_classifier.Perform(gp_Pnt(*point), tolerance) - return (solid_classifier.State() == ta.TopAbs_IN or - solid_classifier.IsOnAFace()) + return solid_classifier.State() == ta.TopAbs_IN or solid_classifier.IsOnAFace() class Solid(Shape, Mixin3D): """ a single solid """ - + @classmethod def interpPlate(cls, surf_edges, surf_pts, thickness, degree=3, nbPtsOnCur=15, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=9): """ @@ -1288,15 +1303,16 @@ def isSolid(cls, obj): """ Returns true if the object is a FreeCAD solid, false otherwise """ - if hasattr(obj, 'ShapeType'): - if obj.ShapeType == 'Solid' or \ - (obj.ShapeType == 'Compound' and len(obj.Solids) > 0): + if hasattr(obj, "ShapeType"): + if obj.ShapeType == "Solid" or ( + obj.ShapeType == "Compound" and len(obj.Solids) > 0 + ): return True return False - + @classmethod def makeSolid(cls, shell): - + return cls(BRepBuilderAPI_MakeSolid(shell.wrapped).Solid()) @classmethod @@ -1305,53 +1321,77 @@ def makeBox(cls, length, width, height, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1) makeBox(length,width,height,[pnt,dir]) -- Make a box located in pnt with the dimensions (length,width,height) By default pnt=Vector(0,0,0) and dir=Vector(0,0,1)' """ - return cls(BRepPrimAPI_MakeBox(gp_Ax2(pnt.toPnt(), - dir.toDir()), - length, - width, - height).Shape()) + return cls( + BRepPrimAPI_MakeBox( + gp_Ax2(pnt.toPnt(), dir.toDir()), length, width, height + ).Shape() + ) @classmethod - def makeCone(cls, radius1, radius2, height, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1), angleDegrees=360): + def makeCone( + cls, + radius1, + radius2, + height, + pnt=Vector(0, 0, 0), + dir=Vector(0, 0, 1), + angleDegrees=360, + ): """ Make a cone with given radii and height By default pnt=Vector(0,0,0), dir=Vector(0,0,1) and angle=360' """ - return cls(BRepPrimAPI_MakeCone(gp_Ax2(pnt.toPnt(), - dir.toDir()), - radius1, - radius2, - height, - angleDegrees * DEG2RAD).Shape()) + return cls( + BRepPrimAPI_MakeCone( + gp_Ax2(pnt.toPnt(), dir.toDir()), + radius1, + radius2, + height, + angleDegrees * DEG2RAD, + ).Shape() + ) @classmethod - def makeCylinder(cls, radius, height, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1), angleDegrees=360): + def makeCylinder( + cls, radius, height, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1), angleDegrees=360 + ): """ makeCylinder(radius,height,[pnt,dir,angle]) -- Make a cylinder with a given radius and height By default pnt=Vector(0,0,0),dir=Vector(0,0,1) and angle=360' """ - return cls(BRepPrimAPI_MakeCylinder(gp_Ax2(pnt.toPnt(), - dir.toDir()), - radius, - height, - angleDegrees * DEG2RAD).Shape()) + return cls( + BRepPrimAPI_MakeCylinder( + gp_Ax2(pnt.toPnt(), dir.toDir()), radius, height, angleDegrees * DEG2RAD + ).Shape() + ) @classmethod - def makeTorus(cls, radius1, radius2, pnt=None, dir=None, angleDegrees1=None, angleDegrees2=None): + def makeTorus( + cls, + radius1, + radius2, + pnt=None, + dir=None, + angleDegrees1=None, + angleDegrees2=None, + ): """ makeTorus(radius1,radius2,[pnt,dir,angle1,angle2,angle]) -- Make a torus with agiven radii and angles By default pnt=Vector(0,0,0),dir=Vector(0,0,1),angle1=0 ,angle1=360 and angle=360' """ - return cls(BRepPrimAPI_MakeTorus(gp_Ax2(pnt.toPnt(), - dir.toDir()), - radius1, - radius2, - angleDegrees1 * DEG2RAD, - angleDegrees2 * DEG2RAD).Shape()) + return cls( + BRepPrimAPI_MakeTorus( + gp_Ax2(pnt.toPnt(), dir.toDir()), + radius1, + radius2, + angleDegrees1 * DEG2RAD, + angleDegrees2 * DEG2RAD, + ).Shape() + ) @classmethod def makeLoft(cls, listOfWire, ruled=False): @@ -1373,35 +1413,52 @@ def makeLoft(cls, listOfWire, ruled=False): return cls(loft_builder.Shape()) @classmethod - def makeWedge(cls, dx, dy, dz, xmin, zmin, xmax, zmax, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1)): + def makeWedge( + cls, + dx, + dy, + dz, + xmin, + zmin, + xmax, + zmax, + pnt=Vector(0, 0, 0), + dir=Vector(0, 0, 1), + ): """ Make a wedge located in pnt By default pnt=Vector(0,0,0) and dir=Vector(0,0,1) """ - return cls(BRepPrimAPI_MakeWedge( - gp_Ax2(pnt.toPnt(), - dir.toDir()), - dx, - dy, - dz, - xmin, - zmin, - xmax, - zmax).Solid()) + return cls( + BRepPrimAPI_MakeWedge( + gp_Ax2(pnt.toPnt(), dir.toDir()), dx, dy, dz, xmin, zmin, xmax, zmax + ).Solid() + ) @classmethod - def makeSphere(cls, radius, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1), angleDegrees1=0, angleDegrees2=90, angleDegrees3=360): + def makeSphere( + cls, + radius, + pnt=Vector(0, 0, 0), + dir=Vector(0, 0, 1), + angleDegrees1=0, + angleDegrees2=90, + angleDegrees3=360, + ): """ Make a sphere with a given radius By default pnt=Vector(0,0,0), dir=Vector(0,0,1), angle1=0, angle2=90 and angle3=360 """ - return cls(BRepPrimAPI_MakeSphere(gp_Ax2(pnt.toPnt(), - dir.toDir()), - radius, - angleDegrees1 * DEG2RAD, - angleDegrees2 * DEG2RAD, - angleDegrees3 * DEG2RAD).Shape()) + return cls( + BRepPrimAPI_MakeSphere( + gp_Ax2(pnt.toPnt(), dir.toDir()), + radius, + angleDegrees1 * DEG2RAD, + angleDegrees2 * DEG2RAD, + angleDegrees3 * DEG2RAD, + ).Shape() + ) @classmethod def _extrudeAuxSpine(cls, wire, spine, auxSpine): @@ -1416,7 +1473,9 @@ def _extrudeAuxSpine(cls, wire, spine, auxSpine): return extrude_builder.Shape() @classmethod - def extrudeLinearWithRotation(cls, outerWire, innerWires, vecCenter, vecNormal, angleDegrees): + def extrudeLinearWithRotation( + cls, outerWire, innerWires, vecCenter, vecNormal, angleDegrees + ): """ Creates a 'twisted prism' by extruding, while simultaneously rotating around the extrusion vector. @@ -1439,26 +1498,25 @@ def extrudeLinearWithRotation(cls, outerWire, innerWires, vecCenter, vecNormal, """ # make straight spine straight_spine_e = Edge.makeLine(vecCenter, vecCenter.add(vecNormal)) - straight_spine_w = Wire.combine([straight_spine_e, ]).wrapped + straight_spine_w = Wire.combine([straight_spine_e,]).wrapped # make an auxliliary spine - pitch = 360. / angleDegrees * vecNormal.Length + pitch = 360.0 / angleDegrees * vecNormal.Length radius = 1 - aux_spine_w = Wire.makeHelix(pitch, - vecNormal.Length, - radius, - center=vecCenter, - dir=vecNormal).wrapped + aux_spine_w = Wire.makeHelix( + pitch, vecNormal.Length, radius, center=vecCenter, dir=vecNormal + ).wrapped # extrude the outer wire - outer_solid = cls._extrudeAuxSpine(outerWire.wrapped, - straight_spine_w, - aux_spine_w) + outer_solid = cls._extrudeAuxSpine( + outerWire.wrapped, straight_spine_w, aux_spine_w + ) # extrude inner wires - inner_solids = [cls._extrudeAuxSpine(w.wrapped, - straight_spine_w. - aux_spine_w) for w in innerWires] + inner_solids = [ + cls._extrudeAuxSpine(w.wrapped, straight_spine_w.aux_spine_w) + for w in innerWires + ] # combine the inner solids into compund inner_comp = Compound._makeCompound(inner_solids) @@ -1491,21 +1549,19 @@ def extrudeLinear(cls, outerWire, innerWires, vecNormal, taper=0): reliable. """ - if taper==0: + if taper == 0: face = Face.makeFromWires(outerWire, innerWires) - prism_builder = BRepPrimAPI_MakePrism( - face.wrapped, vecNormal.wrapped, True) + 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) + 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): """ @@ -1537,20 +1593,28 @@ def revolve(cls, outerWire, innerWires, angleDegrees, axisStart, axisEnd): v1 = Vector(axisStart) v2 = Vector(axisEnd) v2 = v2 - v1 - revol_builder = BRepPrimAPI_MakeRevol(face.wrapped, - gp_Ax1(v1.toPnt(), v2.toDir()), - angleDegrees * DEG2RAD, - True) + revol_builder = BRepPrimAPI_MakeRevol( + face.wrapped, gp_Ax1(v1.toPnt(), v2.toDir()), angleDegrees * DEG2RAD, True + ) return cls(revol_builder.Shape()) - _transModeDict = {'transformed' : BRepBuilderAPI_Transformed, - 'round' : BRepBuilderAPI_RoundCorner, - 'right' : BRepBuilderAPI_RightCorner} + _transModeDict = { + "transformed": BRepBuilderAPI_Transformed, + "round": BRepBuilderAPI_RoundCorner, + "right": BRepBuilderAPI_RightCorner, + } @classmethod - def sweep(cls, outerWire, innerWires, path, makeSolid=True, isFrenet=False, - transitionMode='transformed'): + def sweep( + cls, + outerWire, + innerWires, + path, + makeSolid=True, + isFrenet=False, + transitionMode="transformed", + ): """ Attempt to sweep the list of wires into a prismatic solid along the provided path @@ -1564,11 +1628,11 @@ def sweep(cls, outerWire, innerWires, path, makeSolid=True, isFrenet=False, Possible values are {'transformed','round', 'right'} (default: 'right'). :return: a Solid object """ - if path.ShapeType() == 'Edge': - path = Wire.assembleEdges([path, ]) + if path.ShapeType() == "Edge": + path = Wire.assembleEdges([path,]) shapes = [] - for w in [outerWire]+innerWires: + for w in [outerWire] + innerWires: builder = BRepOffsetAPI_MakePipeShell(path.wrapped) builder.SetMode(isFrenet) builder.SetTransitionMode(cls._transModeDict[transitionMode]) @@ -1580,10 +1644,10 @@ def sweep(cls, outerWire, innerWires, path, makeSolid=True, isFrenet=False, shapes.append(cls(builder.Shape())) - rv,inner_shapes = shapes[0],shapes[1:] + rv, inner_shapes = shapes[0], shapes[1:] if inner_shapes: - inner_shapes = reduce(lambda a,b: a.fuse(b),inner_shapes) + inner_shapes = reduce(lambda a, b: a.fuse(b), inner_shapes) rv = rv.cut(inner_shapes) return rv @@ -1597,8 +1661,8 @@ def sweep_multi(cls, profiles, path, makeSolid=True, isFrenet=False): :param path: The wire to sweep the face resulting from the wires over :return: a Solid object """ - if path.ShapeType() == 'Edge': - path = Wire.assembleEdges([path, ]) + if path.ShapeType() == "Edge": + path = Wire.assembleEdges([path,]) builder = BRepOffsetAPI_MakePipeShell(path.wrapped) @@ -1613,8 +1677,7 @@ def sweep_multi(cls, profiles, path, makeSolid=True, isFrenet=False): return cls(builder.Shape()) - def dprism(self, basis, profiles, depth=None, taper=0, thruAll=True, - additive=True): + def dprism(self, basis, profiles, depth=None, taper=0, thruAll=True, additive=True): """ Make a prismatic feature (additive or subtractive) @@ -1629,13 +1692,10 @@ def dprism(self, basis, profiles, depth=None, taper=0, thruAll=True, shape = self.wrapped basis = basis.wrapped for p in sorted_profiles: - face = Face.makeFromWires(p[0],p[1:]) - feat = BRepFeat_MakeDPrism(shape, - face.wrapped, - basis, - taper*DEG2RAD, - additive, - False) + face = Face.makeFromWires(p[0], p[1:]) + feat = BRepFeat_MakeDPrism( + shape, face.wrapped, basis, taper * DEG2RAD, additive, False + ) if thruAll: feat.PerformThruAll() @@ -1646,21 +1706,22 @@ def dprism(self, basis, profiles, depth=None, taper=0, thruAll=True, return self.__class__(shape) + class Compound(Shape, Mixin3D): """ a collection of disconnected solids """ - + @staticmethod def _makeCompound(listOfShapes): - + comp = TopoDS_Compound() comp_builder = TopoDS_Builder() comp_builder.MakeCompound(comp) for s in listOfShapes: comp_builder.Add(comp, s) - + return comp @classmethod @@ -1672,39 +1733,51 @@ def makeCompound(cls, listOfShapes): return cls(cls._makeCompound((s.wrapped for s in listOfShapes))) @classmethod - def makeText(cls, text, size, height, font="Arial", kind='regular', - halign='center', valign='center',position=Plane.XY()): + def makeText( + cls, + text, + size, + height, + font="Arial", + kind="regular", + halign="center", + valign="center", + position=Plane.XY(), + ): """ Create a 3D text """ - font_kind = {'regular' : Font_FA_Regular, - 'bold' : Font_FA_Bold, - 'italic' : Font_FA_Italic}[kind] + font_kind = { + "regular": Font_FA_Regular, + "bold": Font_FA_Bold, + "italic": Font_FA_Italic, + }[kind] text_flat = Shape(text_to_brep(text, font, font_kind, size, False)) bb = text_flat.BoundingBox() - + t = Vector() - - if halign == 'center': - t.x = -bb.xlen/2 - elif halign == 'right': + + if halign == "center": + t.x = -bb.xlen / 2 + elif halign == "right": t.x = -bb.xlen - - if valign == 'center': - t.y = -bb.ylen/2 - elif valign == 'top': + + if valign == "center": + t.y = -bb.ylen / 2 + elif valign == "top": t.y = -bb.ylen - + text_flat = text_flat.translate(t) - - vecNormal = text_flat.Faces()[0].normalAt()*height + + vecNormal = text_flat.Faces()[0].normalAt() * height text_3d = BRepPrimAPI_MakePrism(text_flat.wrapped, vecNormal.wrapped) return cls(text_3d.Shape()).transformShape(position.rG) + def sortWiresByBuildOrder(wireList, result={}): """Tries to determine how wires should be combined into faces. @@ -1724,12 +1797,14 @@ def sortWiresByBuildOrder(wireList, result={}): # check if we have something to sort at all if len(wireList) < 2: - return [wireList, ] + return [ + wireList, + ] # make a Face, NB: this might return a compound of faces faces = Face.makeFromWires(wireList[0], wireList[1:]) - - rv = [] + + rv = [] for face in faces.Faces(): rv.append([face.outerWire(),] + face.innerWires()) From 0d0166852f4fd84b6802d7231d28dea9eb1eef3e Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 15:30:40 +0100 Subject: [PATCH 55/70] Add files via upload Enable pushpoints() use From 1b2bcb7c148795a794f69c48245878f9aa58d4ee Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 15:31:01 +0100 Subject: [PATCH 56/70] Add files via upload Enable pushpoints() use --- tests/test_cadquery.py | 1670 ++++++++++++++++++++++++++++------------ 1 file changed, 1161 insertions(+), 509 deletions(-) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index 1748e3bba..85cc0ab4e 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -3,7 +3,7 @@ """ # system modules -import math,os.path,time,tempfile +import math, os.path, time, tempfile from random import choice from random import random from random import randrange @@ -11,7 +11,14 @@ # my modules from cadquery import * from cadquery import exporters -from tests import BaseTest, writeStringToFile, makeUnitCube, readFileAsString, makeUnitSquareWire, makeCube +from tests import ( + BaseTest, + writeStringToFile, + makeUnitCube, + readFileAsString, + makeUnitSquareWire, + makeCube, +) # where unit test output will be saved OUTDIR = tempfile.gettempdir() @@ -45,7 +52,6 @@ class TestCadQuery(BaseTest): - def tearDown(self): """ Update summary with data from this test. @@ -61,12 +67,15 @@ def tearDown(self): existingSummary = readFileAsString(SUMMARY_FILE) svgText = readFileAsString(svgFile) svgText = svgText.replace( - '', "") + '', "" + ) # now write data into the file # the content we are replacing it with also includes the marker, so it can be replaced again - existingSummary = existingSummary.replace("", TEST_RESULT_TEMPLATE % ( - dict(svg=svgText, name=self._testMethodName))) + existingSummary = existingSummary.replace( + "", + TEST_RESULT_TEMPLATE % (dict(svg=svgText, name=self._testMethodName)), + ) writeStringToFile(existingSummary, SUMMARY_FILE) @@ -82,25 +91,27 @@ def testToOCC(self): """ Tests to make sure that a CadQuery object is converted correctly to a OCC object. """ - r = Workplane('XY').rect(5, 5).extrude(5) + r = Workplane("XY").rect(5, 5).extrude(5) r = r.toOCC() import OCC.Core as OCC + self.assertEqual(type(r), OCC.TopoDS.TopoDS_Compound) def testToSVG(self): """ Tests to make sure that a CadQuery object is converted correctly to SVG """ - r = Workplane('XY').rect(5, 5).extrude(5) + r = Workplane("XY").rect(5, 5).extrude(5) r_str = r.toSvg() # Make sure that a couple of sections from the SVG output make sense self.assertTrue(r_str.index('path d="M') > 0) - self.assertTrue(r_str.index( - 'line x1="30" y1="-30" x2="58" y2="-15" stroke-width="3"') > 0) + self.assertTrue( + r_str.index('line x1="30" y1="-30" x2="58" y2="-15" stroke-width="3"') > 0 + ) def testCubePlugin(self): """ @@ -126,8 +137,13 @@ def _singleCube(pnt): Workplane.makeCubes = makeCubes # call it - result = Workplane("XY").box(6.0, 8.0, 0.5).faces( - ">Z").rect(4.0, 4.0, forConstruction=True).vertices() + result = ( + Workplane("XY") + .box(6.0, 8.0, 0.5) + .faces(">Z") + .rect(4.0, 4.0, forConstruction=True) + .vertices() + ) result = result.makeCubes(1.0) result = result.combineSolids() self.saveModel(result) @@ -143,7 +159,6 @@ def testCylinderPlugin(self): """ def cylinders(self, radius, height): - def _cyl(pnt): # inner function to build a cylinder return Solid.makeCylinder(radius, height, pnt) @@ -151,11 +166,16 @@ def _cyl(pnt): # combine all the cylinders into a single compound r = self.eachpoint(_cyl, True).combineSolids() return r + Workplane.cyl = cylinders # now test. here we want weird workplane to see if the objects are transformed right - s = Workplane(Plane(Vector((0, 0, 0)), Vector((1, -1, 0)), Vector((1, 1, 0)))).rect(2.0, 3.0, forConstruction=True).vertices() \ + s = ( + Workplane(Plane(Vector((0, 0, 0)), Vector((1, -1, 0)), Vector((1, 1, 0)))) + .rect(2.0, 3.0, forConstruction=True) + .vertices() .cyl(0.25, 0.5) + ) self.assertEqual(4, s.solids().size()) self.saveModel(s) @@ -168,22 +188,35 @@ def testPolygonPlugin(self): """ def rPoly(self, nSides, diameter): - def _makePolygon(center): # pnt is a vector in local coordinates angle = 2.0 * math.pi / nSides pnts = [] for i in range(nSides + 1): - pnts.append(center + Vector((diameter / 2.0 * math.cos(angle * i)), - (diameter / 2.0 * math.sin(angle * i)), 0)) + pnts.append( + center + + Vector( + (diameter / 2.0 * math.cos(angle * i)), + (diameter / 2.0 * math.sin(angle * i)), + 0, + ) + ) return Wire.makePolygon(pnts) return self.eachpoint(_makePolygon, True) Workplane.rPoly = rPoly - s = Workplane("XY").box(4.0, 4.0, 0.25).faces(">Z").workplane().rect(2.0, 2.0, forConstruction=True).vertices()\ - .rPoly(5, 0.5).cutThruAll() + s = ( + Workplane("XY") + .box(4.0, 4.0, 0.25) + .faces(">Z") + .workplane() + .rect(2.0, 2.0, forConstruction=True) + .vertices() + .rPoly(5, 0.5) + .cutThruAll() + ) # 6 base sides, 4 pentagons, 5 sides each = 26 self.assertEqual(26, s.faces().size()) @@ -195,8 +228,7 @@ def testPointList(self): """ c = CQ(makeUnitCube()) - s = c.faces(">Z").workplane().pushPoints( - [(-0.3, 0.3), (0.3, 0.3), (0, 0)]) + s = c.faces(">Z").workplane().pushPoints([(-0.3, 0.3), (0.3, 0.3), (0, 0)]) self.assertEqual(3, s.size()) # TODO: is the ability to iterate over points with circle really worth it? # maybe we should just require using all() and a loop for this. the semantics and @@ -209,7 +241,7 @@ def testPointList(self): def callback_fn(pnt): self.assertEqual((0.0, 0.0), (pnt.x, pnt.y)) - r = Workplane('XY') + r = Workplane("XY") r.objects = [] r.eachpoint(callback_fn) @@ -237,10 +269,8 @@ def testRotate(self): """Test solid rotation at the CQ object level.""" box = Workplane("XY").box(1, 1, 5) box.rotate((0, 0, 0), (1, 0, 0), 90) - startPoint = box.faces("Z").circle(1.5)\ - .workplane(offset=3.0).rect(0.75, 0.5).loft(combine=True) + s = ( + Workplane("front") + .box(4.0, 4.0, 0.25) + .faces(">Z") + .circle(1.5) + .workplane(offset=3.0) + .rect(0.75, 0.5) + .loft(combine=True) + ) self.saveModel(s) - #self.assertEqual(1,s.solids().size() ) - #self.assertEqual(8,s.faces().size() ) + # self.assertEqual(1,s.solids().size() ) + # self.assertEqual(8,s.faces().size() ) def testRevolveCylinder(self): """ @@ -319,56 +355,81 @@ def testRevolveCylinder(self): angle_degrees = 360.0 # Test revolve without any options for making a cylinder - result = Workplane("XY").rect( - rectangle_width, rectangle_length, False).revolve() + result = ( + Workplane("XY").rect(rectangle_width, rectangle_length, False).revolve() + ) self.assertEqual(3, result.faces().size()) self.assertEqual(2, result.vertices().size()) self.assertEqual(3, result.edges().size()) # Test revolve when only setting the angle to revolve through - result = Workplane("XY").rect( - rectangle_width, rectangle_length, False).revolve(angle_degrees) + result = ( + Workplane("XY") + .rect(rectangle_width, rectangle_length, False) + .revolve(angle_degrees) + ) self.assertEqual(3, result.faces().size()) self.assertEqual(2, result.vertices().size()) self.assertEqual(3, result.edges().size()) - result = Workplane("XY").rect( - rectangle_width, rectangle_length, False).revolve(270.0) + result = ( + Workplane("XY") + .rect(rectangle_width, rectangle_length, False) + .revolve(270.0) + ) self.assertEqual(5, result.faces().size()) self.assertEqual(6, result.vertices().size()) self.assertEqual(9, result.edges().size()) # Test when passing revolve the angle and the axis of revolution's start point - result = Workplane("XY").rect( - rectangle_width, rectangle_length).revolve(angle_degrees, (-5, -5)) + result = ( + Workplane("XY") + .rect(rectangle_width, rectangle_length) + .revolve(angle_degrees, (-5, -5)) + ) self.assertEqual(3, result.faces().size()) self.assertEqual(2, result.vertices().size()) self.assertEqual(3, result.edges().size()) - result = Workplane("XY").rect( - rectangle_width, rectangle_length).revolve(270.0, (-5, -5)) + result = ( + Workplane("XY") + .rect(rectangle_width, rectangle_length) + .revolve(270.0, (-5, -5)) + ) self.assertEqual(5, result.faces().size()) self.assertEqual(6, result.vertices().size()) self.assertEqual(9, result.edges().size()) # Test when passing revolve the angle and both the start and ends of the axis of revolution - result = Workplane("XY").rect(rectangle_width, rectangle_length).revolve( - angle_degrees, (-5, -5), (-5, 5)) + result = ( + Workplane("XY") + .rect(rectangle_width, rectangle_length) + .revolve(angle_degrees, (-5, -5), (-5, 5)) + ) self.assertEqual(3, result.faces().size()) self.assertEqual(2, result.vertices().size()) self.assertEqual(3, result.edges().size()) - result = Workplane("XY").rect( - rectangle_width, rectangle_length).revolve(270.0, (-5, -5), (-5, 5)) + result = ( + Workplane("XY") + .rect(rectangle_width, rectangle_length) + .revolve(270.0, (-5, -5), (-5, 5)) + ) self.assertEqual(5, result.faces().size()) self.assertEqual(6, result.vertices().size()) self.assertEqual(9, result.edges().size()) # Testing all of the above without combine - result = Workplane("XY").rect(rectangle_width, rectangle_length).revolve( - angle_degrees, (-5, -5), (-5, 5), False) + result = ( + Workplane("XY") + .rect(rectangle_width, rectangle_length) + .revolve(angle_degrees, (-5, -5), (-5, 5), False) + ) self.assertEqual(3, result.faces().size()) self.assertEqual(2, result.vertices().size()) self.assertEqual(3, result.edges().size()) - result = Workplane("XY").rect(rectangle_width, rectangle_length).revolve( - 270.0, (-5, -5), (-5, 5), False) + result = ( + Workplane("XY") + .rect(rectangle_width, rectangle_length) + .revolve(270.0, (-5, -5), (-5, 5), False) + ) self.assertEqual(5, result.faces().size()) self.assertEqual(6, result.vertices().size()) self.assertEqual(9, result.edges().size()) @@ -384,8 +445,11 @@ def testRevolveDonut(self): rectangle_length = 10.0 angle_degrees = 360.0 - result = Workplane("XY").rect(rectangle_width, rectangle_length, True)\ + result = ( + Workplane("XY") + .rect(rectangle_width, rectangle_length, True) .revolve(angle_degrees, (20, 0), (20, 10)) + ) self.assertEqual(4, result.faces().size()) self.assertEqual(4, result.vertices().size()) self.assertEqual(6, result.edges().size()) @@ -404,18 +468,13 @@ def testSpline(self): """ Tests construction of splines """ - pts = [ - (0, 0), - (0, 1), - (1, 2), - (2, 4) - ] + pts = [(0, 0), (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() + path_closed = Workplane("XZ").spline(pts, periodic=True).val() self.assertTrue(path_closed.IsClosed()) # attempt to build a valid face @@ -429,24 +488,19 @@ def testSpline(self): self.assertFalse(f.isValid()) # Spline with explicit tangents - path_const = Workplane("XZ").spline(pts,tangents=((0,1),(1,0))).val() + 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)) - + # test include current - path1 = Workplane("XZ").spline(pts[1:],includeCurrent=True).val() - self.assertAlmostEqual(path.Length(),path1.Length()) + path1 = Workplane("XZ").spline(pts[1:], includeCurrent=True).val() + self.assertAlmostEqual(path.Length(), path1.Length()) def testSweep(self): """ Tests the operation of sweeping a wire(s) along a path """ - pts = [ - (0, 0), - (0, 1), - (1, 2), - (2, 4) - ] + pts = [(0, 0), (0, 1), (1, 2), (2, 4)] # Spline path path = Workplane("XZ").spline(pts) @@ -467,8 +521,7 @@ def testSweep(self): self.assertEqual(3, result.edges().size()) # Test with makeSolid False and isFrenet True - result = Workplane("XY").circle(1.0).sweep( - path, makeSolid=False, isFrenet=True) + result = Workplane("XY").circle(1.0).sweep(path, makeSolid=False, isFrenet=True) self.assertEqual(1, result.faces().size()) self.assertEqual(3, result.edges().size()) @@ -481,7 +534,7 @@ def testSweep(self): path = Workplane("XZ").polyline(pts) # Test defaults - result = Workplane("XY").circle(0.1).sweep(path,transition='transformed') + result = Workplane("XY").circle(0.1).sweep(path, transition="transformed") self.assertEqual(5, result.faces().size()) self.assertEqual(7, result.edges().size()) @@ -489,24 +542,40 @@ def testSweep(self): path = Workplane("XZ").polyline(pts) # Test defaults - result = Workplane("XY").circle(0.2).circle(0.1).sweep(path,transition='transformed') + result = ( + Workplane("XY") + .circle(0.2) + .circle(0.1) + .sweep(path, transition="transformed") + ) self.assertEqual(8, result.faces().size()) self.assertEqual(14, result.edges().size()) # Polyline path and different transition settings - for t in ('transformed','right','round'): + for t in ("transformed", "right", "round"): path = Workplane("XZ").polyline(pts) - result = Workplane("XY").circle(0.2).rect(0.2,0.1).rect(0.1,0.2)\ - .sweep(path,transition=t) + result = ( + Workplane("XY") + .circle(0.2) + .rect(0.2, 0.1) + .rect(0.1, 0.2) + .sweep(path, transition=t) + ) self.assertTrue(result.solids().val().isValid()) # Polyline path and multiple inner profiles path = Workplane("XZ").polyline(pts) # Test defaults - result = Workplane("XY").circle(0.2).rect(0.2,0.1).rect(0.1,0.2)\ - .circle(0.1).sweep(path) + result = ( + Workplane("XY") + .circle(0.2) + .rect(0.2, 0.1) + .rect(0.1, 0.2) + .circle(0.1) + .sweep(path) + ) self.assertTrue(result.solids().val().isValid()) # Arc path @@ -526,36 +595,79 @@ def testMultisectionSweep(self): path = Workplane("XZ").moveTo(-10, 0).lineTo(10, 0) # Sweep a circle from diameter 2.0 to diameter 1.0 to diameter 2.0 along X axis length 10.0 + 10.0 - defaultSweep = Workplane("YZ").workplane(offset=-10.0).circle(2.0). \ - workplane(offset=10.0).circle(1.0). \ - workplane(offset=10.0).circle(2.0).sweep(path, multisection=True) + defaultSweep = ( + Workplane("YZ") + .workplane(offset=-10.0) + .circle(2.0) + .workplane(offset=10.0) + .circle(1.0) + .workplane(offset=10.0) + .circle(2.0) + .sweep(path, multisection=True) + ) # We can sweep thrue different shapes - recttocircleSweep = Workplane("YZ").workplane(offset=-10.0).rect(2.0, 2.0). \ - workplane(offset=8.0).circle(1.0).workplane(offset=4.0).circle(1.0). \ - workplane(offset=8.0).rect(2.0, 2.0).sweep(path, multisection=True) - - circletorectSweep = Workplane("YZ").workplane(offset=-10.0).circle(1.0). \ - workplane(offset=7.0).rect(2.0, 2.0).workplane(offset=6.0).rect(2.0, 2.0). \ - workplane(offset=7.0).circle(1.0).sweep(path, multisection=True) + recttocircleSweep = ( + Workplane("YZ") + .workplane(offset=-10.0) + .rect(2.0, 2.0) + .workplane(offset=8.0) + .circle(1.0) + .workplane(offset=4.0) + .circle(1.0) + .workplane(offset=8.0) + .rect(2.0, 2.0) + .sweep(path, multisection=True) + ) + + circletorectSweep = ( + Workplane("YZ") + .workplane(offset=-10.0) + .circle(1.0) + .workplane(offset=7.0) + .rect(2.0, 2.0) + .workplane(offset=6.0) + .rect(2.0, 2.0) + .workplane(offset=7.0) + .circle(1.0) + .sweep(path, multisection=True) + ) # Placement of the Shape is important otherwise could produce unexpected shape - specialSweep = Workplane("YZ").circle(1.0).workplane(offset=10.0).rect(2.0, 2.0). \ - sweep(path, multisection=True) + specialSweep = ( + Workplane("YZ") + .circle(1.0) + .workplane(offset=10.0) + .rect(2.0, 2.0) + .sweep(path, multisection=True) + ) # Switch to an arc for the path : line l=5.0 then half circle r=4.0 then line l=5.0 - path = Workplane("XZ").moveTo(-5, 4).lineTo(0, 4). \ - threePointArc((4, 0), (0, -4)).lineTo(-5, -4) + path = ( + Workplane("XZ") + .moveTo(-5, 4) + .lineTo(0, 4) + .threePointArc((4, 0), (0, -4)) + .lineTo(-5, -4) + ) # Placement of different shapes should follow the path # cylinder r=1.5 along first line # then sweep allong arc from r=1.5 to r=1.0 # then cylinder r=1.0 along last line - arcSweep = Workplane("YZ").workplane(offset=-5).moveTo(0, 4).circle(1.5). \ - workplane(offset=5).circle(1.5). \ - moveTo(0, -8).circle(1.0). \ - workplane(offset=-5).circle(1.0). \ - sweep(path, multisection=True) + arcSweep = ( + Workplane("YZ") + .workplane(offset=-5) + .moveTo(0, 4) + .circle(1.5) + .workplane(offset=5) + .circle(1.5) + .moveTo(0, -8) + .circle(1.0) + .workplane(offset=-5) + .circle(1.0) + .sweep(path, multisection=True) + ) # Test and saveModel self.assertEqual(1, defaultSweep.solids().size()) @@ -569,7 +681,7 @@ def testTwistExtrude(self): """ Tests extrusion while twisting through an angle. """ - profile = Workplane('XY').rect(10, 10) + profile = Workplane("XY").rect(10, 10) r = profile.twistExtrude(10, 45, False) self.assertEqual(6, r.faces().size()) @@ -578,7 +690,7 @@ def testTwistExtrudeCombine(self): """ Tests extrusion while twisting through an angle, combining with other solids. """ - profile = Workplane('XY').rect(10, 10) + profile = Workplane("XY").rect(10, 10) r = profile.twistExtrude(10, 45) self.assertEqual(6, r.faces().size()) @@ -586,9 +698,16 @@ def testTwistExtrudeCombine(self): def testRectArray(self): NUMX = 3 NUMY = 3 - s = Workplane("XY").box(40, 40, 5, centered=(True, True, True)).faces( - ">Z").workplane().rarray(8.0, 8.0, NUMX, NUMY, True).circle(2.0).extrude(2.0) - #s = Workplane("XY").box(40,40,5,centered=(True,True,True)).faces(">Z").workplane().circle(2.0).extrude(2.0) + s = ( + Workplane("XY") + .box(40, 40, 5, centered=(True, True, True)) + .faces(">Z") + .workplane() + .rarray(8.0, 8.0, NUMX, NUMY, True) + .circle(2.0) + .extrude(2.0) + ) + # s = Workplane("XY").box(40,40,5,centered=(True,True,True)).faces(">Z").workplane().circle(2.0).extrude(2.0) self.saveModel(s) # 6 faces for the box, 2 faces for each cylinder self.assertEqual(6 + NUMX * NUMY * 2, s.faces().size()) @@ -623,8 +742,14 @@ def testPolarArray(self): self.assertAlmostEqual(radius, s.objects[0].y) def testNestedCircle(self): - s = Workplane("XY").box(40, 40, 5).pushPoints( - [(10, 0), (0, 10)]).circle(4).circle(2).extrude(4) + s = ( + Workplane("XY") + .box(40, 40, 5) + .pushPoints([(10, 0), (0, 10)]) + .circle(4) + .circle(2) + .extrude(4) + ) self.saveModel(s) self.assertEqual(14, s.faces().size()) @@ -651,8 +776,13 @@ def testLegoBrick(self): # build the brick s = Workplane("XY").box(total_length, total_width, H) # make the base s = s.faces("Z").workplane().rarray(P, P, lbumps, wbumps, True).circle( - bumpDiam / 2.0).extrude(1.8) # make the bumps on the top + s = ( + s.faces(">Z") + .workplane() + .rarray(P, P, lbumps, wbumps, True) + .circle(bumpDiam / 2.0) + .extrude(1.8) + ) # make the bumps on the top # add posts on the bottom. posts are different diameter depending on geometry # solid studs for 1 bump, tubes for multiple, none for 1x1 @@ -660,32 +790,40 @@ def testLegoBrick(self): tmp = s.faces(" 1 and wbumps > 1: - tmp = tmp.rarray(P, P, lbumps - 1, wbumps - 1, center=True).circle( - postDiam / 2.0).circle(bumpDiam / 2.0).extrude(H - t) + tmp = ( + tmp.rarray(P, P, lbumps - 1, wbumps - 1, center=True) + .circle(postDiam / 2.0) + .circle(bumpDiam / 2.0) + .extrude(H - t) + ) elif lbumps > 1: - tmp = tmp.rarray(P, P, lbumps - 1, 1, - center=True).circle(t).extrude(H - t) + tmp = tmp.rarray(P, P, lbumps - 1, 1, center=True).circle(t).extrude(H - t) elif wbumps > 1: - tmp = tmp.rarray(P, P, 1, wbumps - 1, - center=True).circle(t).extrude(H - t) + tmp = tmp.rarray(P, P, 1, wbumps - 1, center=True).circle(t).extrude(H - t) self.saveModel(s) def testAngledHoles(self): - s = Workplane("front").box(4.0, 4.0, 0.25).faces(">Z").workplane().transformed(offset=Vector(0, -1.5, 1.0), rotate=Vector(60, 0, 0))\ - .rect(1.5, 1.5, forConstruction=True).vertices().hole(0.25) + s = ( + Workplane("front") + .box(4.0, 4.0, 0.25) + .faces(">Z") + .workplane() + .transformed(offset=Vector(0, -1.5, 1.0), rotate=Vector(60, 0, 0)) + .rect(1.5, 1.5, forConstruction=True) + .vertices() + .hole(0.25) + ) self.saveModel(s) self.assertEqual(10, s.faces().size()) def testTranslateSolid(self): c = CQ(makeUnitCube()) - self.assertAlmostEqual(0.0, c.faces( - "Z').workplane().circle(0.125).extrude( - 0.5, True) # make a boss, not updating the original + r = ( + c.faces(">Z").workplane().circle(0.125).extrude(0.5, True) + ) # make a boss, not updating the original self.assertEqual(8, r.faces().size()) # just the boss faces self.assertEqual(6, c.faces().size()) # original is not modified @@ -737,9 +876,14 @@ def testSimpleWorkplane(self): A simple square part with a hole in it """ s = Workplane(Plane.XY()) - r = s.rect(2.0, 2.0).extrude(0.5)\ - .faces(">Z").workplane()\ - .circle(0.25).cutBlind(-1.0) + r = ( + s.rect(2.0, 2.0) + .extrude(0.5) + .faces(">Z") + .workplane() + .circle(0.25) + .cutBlind(-1.0) + ) self.saveModel(r) self.assertEqual(7, r.faces().size()) @@ -749,13 +893,12 @@ def testMultiFaceWorkplane(self): Test Creation of workplane from multiple co-planar face selection. """ - s = Workplane('XY').box(1, 1, 1).faces( - '>Z').rect(1, 0.5).cutBlind(-0.2) + s = Workplane("XY").box(1, 1, 1).faces(">Z").rect(1, 0.5).cutBlind(-0.2) - w = s.faces('>Z').workplane() + w = s.faces(">Z").workplane() o = w.objects[0] # origin of the workplane - self.assertAlmostEqual(o.x, 0., 3) - self.assertAlmostEqual(o.y, 0., 3) + self.assertAlmostEqual(o.x, 0.0, 3) + self.assertAlmostEqual(o.y, 0.0, 3) self.assertAlmostEqual(o.z, 0.5, 3) def testTriangularPrism(self): @@ -779,8 +922,13 @@ def testConstructionWire(self): also tests using a workplane plane other than XY """ s = Workplane(Plane.YZ()) - r = s.rect(2.0, 2.0).rect( - 1.3, 1.3, forConstruction=True).vertices().circle(0.125).extrude(0.5) + r = ( + s.rect(2.0, 2.0) + .rect(1.3, 1.3, forConstruction=True) + .vertices() + .circle(0.125) + .extrude(0.5) + ) self.saveModel(r) # 10 faces-- 6 plus 4 holes, the vertices of the second rect. self.assertEqual(10, r.faces().size()) @@ -798,8 +946,13 @@ def testTwoWorkplanes(self): # r = s.rect(2.0,2.0).rect(1.3,1.3,forConstruction=True).vertices() # for c in r.all(): # c.circle(0.125).extrude(0.5,True) - r = s.rect(2.0, 2.0).rect( - 1.3, 1.3, forConstruction=True).vertices().circle(0.125).extrude(0.5) + r = ( + s.rect(2.0, 2.0) + .rect(1.3, 1.3, forConstruction=True) + .vertices() + .circle(0.125) + .extrude(0.5) + ) # side hole, blind deep 1.9 t = r.faces(">Y").workplane().circle(0.125).cutBlind(-1.9) @@ -829,43 +982,45 @@ def testIntersect(self): resS = currentS.intersect(toIntersect.val()) self.assertEqual(6, resS.faces().size()) - self.assertAlmostEqual(resS.val().Volume(),0.5) + self.assertAlmostEqual(resS.val().Volume(), 0.5) resS = currentS.intersect(toIntersect) self.assertEqual(6, resS.faces().size()) - self.assertAlmostEqual(resS.val().Volume(),0.5) + self.assertAlmostEqual(resS.val().Volume(), 0.5) def testBoundingBox(self): """ Tests the boudingbox center of a model """ - result0 = (Workplane("XY") - .moveTo(10, 0) - .lineTo(5, 0) - .threePointArc((3.9393, 0.4393), (3.5, 1.5)) - .threePointArc((3.0607, 2.5607), (2, 3)) - .lineTo(1.5, 3) - .threePointArc((0.4393, 3.4393), (0, 4.5)) - .lineTo(0, 13.5) - .threePointArc((0.4393, 14.5607), (1.5, 15)) - .lineTo(28, 15) - .lineTo(28, 13.5) - .lineTo(24, 13.5) - .lineTo(24, 11.5) - .lineTo(27, 11.5) - .lineTo(27, 10) - .lineTo(22, 10) - .lineTo(22, 13.2) - .lineTo(14.5, 13.2) - .lineTo(14.5, 10) - .lineTo(12.5, 10) - .lineTo(12.5, 13.2) - .lineTo(5.5, 13.2) - .lineTo(5.5, 2) - .threePointArc((5.793, 1.293), (6.5, 1)) - .lineTo(10, 1) - .close()) + result0 = ( + Workplane("XY") + .moveTo(10, 0) + .lineTo(5, 0) + .threePointArc((3.9393, 0.4393), (3.5, 1.5)) + .threePointArc((3.0607, 2.5607), (2, 3)) + .lineTo(1.5, 3) + .threePointArc((0.4393, 3.4393), (0, 4.5)) + .lineTo(0, 13.5) + .threePointArc((0.4393, 14.5607), (1.5, 15)) + .lineTo(28, 15) + .lineTo(28, 13.5) + .lineTo(24, 13.5) + .lineTo(24, 11.5) + .lineTo(27, 11.5) + .lineTo(27, 10) + .lineTo(22, 10) + .lineTo(22, 13.2) + .lineTo(14.5, 13.2) + .lineTo(14.5, 10) + .lineTo(12.5, 10) + .lineTo(12.5, 13.2) + .lineTo(5.5, 13.2) + .lineTo(5.5, 2) + .threePointArc((5.793, 1.293), (6.5, 1)) + .lineTo(10, 1) + .close() + ) result = result0.extrude(100) bb_center = result.val().BoundingBox().center self.saveModel(result) @@ -888,9 +1043,13 @@ def testCutThroughAll(self): """ # base block s = Workplane(Plane.XY()) - r = s.rect(2.0, 2.0).rect( - 1.3, 1.3, forConstruction=True).vertices().circle(0.125).extrude(0.5) - + r = ( + s.rect(2.0, 2.0) + .rect(1.3, 1.3, forConstruction=True) + .vertices() + .circle(0.125) + .extrude(0.5) + ) # thru all without explicit face selection t = r.circle(0.5).cutThruAll() @@ -907,31 +1066,47 @@ def testCutToFaceOffsetNOTIMPLEMENTEDYET(self): """ # base block s = Workplane(Plane.XY()) - r = s.rect(2.0, 2.0).rect( - 1.3, 1.3, forConstruction=True).vertices().circle(0.125).extrude(0.5) + r = ( + s.rect(2.0, 2.0) + .rect(1.3, 1.3, forConstruction=True) + .vertices() + .circle(0.125) + .extrude(0.5) + ) # side hole, up to 0.1 from the last face try: - t = r.faces(">Y").workplane().circle( - 0.125).cutToOffsetFromFace(r.faces().mminDist(Dir.Y), 0.1) + t = ( + r.faces(">Y") + .workplane() + .circle(0.125) + .cutToOffsetFromFace(r.faces().mminDist(Dir.Y), 0.1) + ) # should end up being a blind hole self.assertEqual(10, t.faces().size()) - t.first().val().exportStep('c:/temp/testCutToFace.STEP') + t.first().val().exportStep("c:/temp/testCutToFace.STEP") except: pass # Not Implemented Yet def testWorkplaneOnExistingSolid(self): "Tests extruding on an existing solid" - c = CQ(makeUnitCube()).faces(">Z").workplane().circle( - 0.25).circle(0.125).extrude(0.25) + c = ( + CQ(makeUnitCube()) + .faces(">Z") + .workplane() + .circle(0.25) + .circle(0.125) + .extrude(0.25) + ) self.saveModel(c) self.assertEqual(10, c.faces().size()) def testWorkplaneCenterMove(self): # this workplane is centered at x=0.5,y=0.5, the center of the upper face - s = Workplane("XY").box(1, 1, 1).faces(">Z").workplane( - ).center(-0.5, -0.5) # move the center to the corner + s = ( + Workplane("XY").box(1, 1, 1).faces(">Z").workplane().center(-0.5, -0.5) + ) # move the center to the corner t = s.circle(0.25).extrude(0.2) # make a boss self.assertEqual(9, t.faces().size()) @@ -946,7 +1121,7 @@ def testBasicLines(self): # most users dont understand what a wire is, they are just drawing r = s.lineTo(1.0, 0).lineTo(0, 1.0).close().wire().extrude(0.25) - r.val().exportStep(os.path.join(OUTDIR, 'testBasicLinesStep1.STEP')) + r.val().exportStep(os.path.join(OUTDIR, "testBasicLinesStep1.STEP")) # no faces on the original workplane self.assertEqual(0, s.faces().size()) @@ -956,12 +1131,12 @@ def testBasicLines(self): # now add a circle through a side face r1 = r.faces("+XY").workplane().circle(0.08).cutThruAll() self.assertEqual(6, r1.faces().size()) - r1.val().exportStep(os.path.join(OUTDIR, 'testBasicLinesXY.STEP')) + r1.val().exportStep(os.path.join(OUTDIR, "testBasicLinesXY.STEP")) # now add a circle through a top r2 = r1.faces("+Z").workplane().circle(0.08).cutThruAll() self.assertEqual(9, r2.faces().size()) - r2.val().exportStep(os.path.join(OUTDIR, 'testBasicLinesZ.STEP')) + r2.val().exportStep(os.path.join(OUTDIR, "testBasicLinesZ.STEP")) self.saveModel(r2) @@ -970,17 +1145,19 @@ def test2DDrawing(self): Draw things like 2D lines and arcs, should be expanded later to include all 2D constructs """ s = Workplane(Plane.XY()) - r = s.lineTo(1.0, 0.0) \ - .lineTo(1.0, 1.0) \ - .threePointArc((1.0, 1.5), (0.0, 1.0)) \ - .lineTo(0.0, 0.0) \ - .moveTo(1.0, 0.0) \ - .lineTo(2.0, 0.0) \ - .lineTo(2.0, 2.0) \ - .threePointArc((2.0, 2.5), (0.0, 2.0)) \ - .lineTo(-2.0, 2.0) \ - .lineTo(-2.0, 0.0) \ - .close() + r = ( + s.lineTo(1.0, 0.0) + .lineTo(1.0, 1.0) + .threePointArc((1.0, 1.5), (0.0, 1.0)) + .lineTo(0.0, 0.0) + .moveTo(1.0, 0.0) + .lineTo(2.0, 0.0) + .lineTo(2.0, 2.0) + .threePointArc((2.0, 2.5), (0.0, 2.0)) + .lineTo(-2.0, 2.0) + .lineTo(-2.0, 0.0) + .close() + ) self.assertEqual(1, r.wires().size()) @@ -1004,12 +1181,19 @@ def test2DDrawing(self): self.assertEqual(1, r.wire().size()) self.assertEqual(4, r.edges().size()) - self.assertEqual((1.0, 1.0), - (r.vertices(selectors.NearestToPointSelector((0.0, 0.0, 0.0))) - .first().val().X, - r.vertices( - selectors.NearestToPointSelector((0.0, 0.0, 0.0))) - .first().val().Y)) + self.assertEqual( + (1.0, 1.0), + ( + r.vertices(selectors.NearestToPointSelector((0.0, 0.0, 0.0))) + .first() + .val() + .X, + r.vertices(selectors.NearestToPointSelector((0.0, 0.0, 0.0))) + .first() + .val() + .Y, + ), + ) # Test the sagittaArc and radiusArc functions a1 = Workplane(Plane.YZ()).threePointArc((5, 1), (10, 0)) @@ -1017,13 +1201,13 @@ def test2DDrawing(self): a3 = Workplane(Plane.YZ()).threePointArc((6, 2), (12, 0)) a4 = Workplane(Plane.YZ()).radiusArc((12, 0), -10) - assert(a1.edges().first().val().geomType() == "CIRCLE") - assert(a2.edges().first().val().geomType() == "CIRCLE") - assert(a3.edges().first().val().geomType() == "CIRCLE") - assert(a4.edges().first().val().geomType() == "CIRCLE") + assert a1.edges().first().val().geomType() == "CIRCLE" + assert a2.edges().first().val().geomType() == "CIRCLE" + assert a3.edges().first().val().geomType() == "CIRCLE" + assert a4.edges().first().val().geomType() == "CIRCLE" - assert(a1.edges().first().val().Length() == a2.edges().first().val().Length()) - assert(a3.edges().first().val().Length() == a4.edges().first().val().Length()) + assert a1.edges().first().val().Length() == a2.edges().first().val().Length() + assert a3.edges().first().val().Length() == a4.edges().first().val().Length() def testPolarLines(self): """ @@ -1032,11 +1216,13 @@ def testPolarLines(self): # Test the PolarLine* functions s = Workplane(Plane.XY()) - r = s.polarLine(10, 45) \ - .polarLineTo(10, -45) \ - .polarLine(10, -180) \ - .polarLine(-10, -90) \ + r = ( + s.polarLine(10, 45) + .polarLineTo(10, -45) + .polarLine(10, -180) + .polarLine(-10, -90) .close() + ) # a single wire, 5 edges self.assertEqual(1, r.wires().size()) @@ -1046,12 +1232,12 @@ def testLargestDimension(self): """ Tests the largestDimension function when no solids are on the stack and when there are """ - r = Workplane('XY').box(1, 1, 1) + r = Workplane("XY").box(1, 1, 1) dim = r.largestDimension() self.assertAlmostEqual(8.7, dim, 1) - r = Workplane('XY') + r = Workplane("XY") dim = r.largestDimension() self.assertEqual(-1, dim) @@ -1067,12 +1253,19 @@ def testOccBottle(self): s = Workplane(Plane.XY()) # draw half the profile of the bottle - p = s.center(-L / 2.0, 0).vLine(w / 2.0).threePointArc((L / 2.0, w / 2.0 + t), (L, w / 2.0)).vLine(-w / 2.0).mirrorX()\ + p = ( + s.center(-L / 2.0, 0) + .vLine(w / 2.0) + .threePointArc((L / 2.0, w / 2.0 + t), (L, w / 2.0)) + .vLine(-w / 2.0) + .mirrorX() .extrude(30.0, True) + ) # make the neck p.faces(">Z").workplane().circle(3.0).extrude( - 2.0, True) # .edges().fillet(0.05) + 2.0, True + ) # .edges().fillet(0.05) # make a shell p.faces(">Z").shell(0.3) @@ -1090,7 +1283,7 @@ def testSplineShape(self): (1.5, 1.0), (1.0, 1.25), (0.5, 1.0), - (0, 1.0) + (0, 1.0), ] r = s.lineTo(3.0, 0).lineTo(3.0, 1.0).spline(sPnts).close() r = r.extrude(0.5) @@ -1100,8 +1293,13 @@ def testSimpleMirror(self): """ Tests a simple mirroring operation """ - s = Workplane("XY").lineTo(2, 2).threePointArc((3, 1), (2, 0)) \ - .mirrorX().extrude(0.25) + s = ( + Workplane("XY") + .lineTo(2, 2) + .threePointArc((3, 1), (2, 0)) + .mirrorX() + .extrude(0.25) + ) self.assertEqual(6, s.faces().size()) self.saveModel(s) @@ -1123,7 +1321,7 @@ def testUnorderedMirror(self): (r / 2, s / 2), (r / 2 - t, s / 2), (r / 2 - t, r / 2 - 1.5 * t), - (t / 2, 0) + (t / 2, 0), ] r = Workplane("XY").polyline(points).mirrorX() @@ -1131,13 +1329,12 @@ def testUnorderedMirror(self): self.assertEqual(1, r.wires().size()) self.assertEqual(18, r.edges().size()) - # try the same with includeCurrent=True - r = Workplane("XY").polyline(points[1:],includeCurrent=True).mirrorX() + # try the same with includeCurrent=True + r = Workplane("XY").polyline(points[1:], includeCurrent=True).mirrorX() self.assertEqual(1, r.wires().size()) self.assertEqual(18, r.edges().size()) - def testChainedMirror(self): """ Tests whether or not calling mirrorX().mirrorY() works correctly @@ -1145,23 +1342,22 @@ def testChainedMirror(self): r = 20 s = 7 t = 1.5 - + points = [ - (0, 0), - (0, t/2), - (r/2-1.5*t, r/2-t), - (s/2, r/2-t), - (s/2, r/2), - (r/2, r/2), - (r/2, s/2), - (r/2-t, s/2), - (r/2-t, r/2-1.5*t), - (t/2, 0) + (0, 0), + (0, t / 2), + (r / 2 - 1.5 * t, r / 2 - t), + (s / 2, r / 2 - t), + (s / 2, r / 2), + (r / 2, r / 2), + (r / 2, s / 2), + (r / 2 - t, s / 2), + (r / 2 - t, r / 2 - 1.5 * t), + (t / 2, 0), ] - - r = Workplane("XY").polyline(points).mirrorX().mirrorY() \ - .extrude(1).faces('>Z') - + + r = Workplane("XY").polyline(points).mirrorX().mirrorY().extrude(1).faces(">Z") + self.assertEquals(1, r.wires().size()) self.assertEquals(32, r.edges().size()) @@ -1191,7 +1387,7 @@ def testIbeam(self): (t / 2.0, (t - H / 2.0)), (W / 2.0, (t - H / 2.0)), (W / 2.0, H / -2.0), - (0, H / -2.0) + (0, H / -2.0), ] r = s.polyline(pts).mirrorY() # these other forms also work res = r.extrude(L) @@ -1210,8 +1406,15 @@ def testFillet(self): """ Tests filleting edges on a solid """ - c = CQ(makeUnitCube()).faces(">Z").workplane().circle( - 0.25).extrude(0.25, True).edges("|Z").fillet(0.2) + c = ( + CQ(makeUnitCube()) + .faces(">Z") + .workplane() + .circle(0.25) + .extrude(0.25, True) + .edges("|Z") + .fillet(0.2) + ) self.saveModel(c) self.assertEqual(12, c.faces().size()) @@ -1241,8 +1444,7 @@ def testChamferCylinder(self): """ Test chamfer API with a cylinder shape """ - cylinder = Workplane("XY").circle( - 1).extrude(1).faces(">Z").chamfer(0.1) + cylinder = Workplane("XY").circle(1).extrude(1).faces(">Z").chamfer(0.1) self.saveModel(cylinder) self.assertEqual(4, cylinder.faces().size()) @@ -1251,21 +1453,15 @@ def testCounterBores(self): Tests making a set of counterbored holes in a face """ c = CQ(makeCube(3.0)) - pnts = [ - (-1.0, -1.0), (0.0, 0.0), (1.0, 1.0) - ] - c = c.faces(">Z").workplane().pushPoints( - pnts).cboreHole(0.1, 0.25, 0.25, 0.75) + pnts = [(-1.0, -1.0), (0.0, 0.0), (1.0, 1.0)] + c = c.faces(">Z").workplane().pushPoints(pnts).cboreHole(0.1, 0.25, 0.25, 0.75) self.assertEqual(18, c.faces().size()) self.saveModel(c) # Tests the case where the depth of the cboreHole is not specified c2 = CQ(makeCube(3.0)) - pnts = [ - (-1.0, -1.0), (0.0, 0.0), (1.0, 1.0) - ] - c2 = c2.faces(">Z").workplane().pushPoints( - pnts).cboreHole(0.1, 0.25, 0.25) + pnts = [(-1.0, -1.0), (0.0, 0.0), (1.0, 1.0)] + c2 = c2.faces(">Z").workplane().pushPoints(pnts).cboreHole(0.1, 0.25, 0.25) self.assertEqual(15, c2.faces().size()) def testCounterSinks(self): @@ -1273,8 +1469,15 @@ def testCounterSinks(self): Tests countersinks """ s = Workplane(Plane.XY()) - result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\ - .rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None) + result = ( + s.rect(2.0, 4.0) + .extrude(0.5) + .faces(">Z") + .workplane() + .rect(1.5, 3.5, forConstruction=True) + .vertices() + .cskHole(0.125, 0.25, 82, depth=None) + ) self.saveModel(result) def testSplitKeepingHalf(self): @@ -1283,8 +1486,7 @@ def testSplitKeepingHalf(self): """ # drill a hole in the side - c = CQ(makeUnitCube()).faces( - ">Z").workplane().circle(0.25).cutThruAll() + c = CQ(makeUnitCube()).faces(">Z").workplane().circle(0.25).cutThruAll() self.assertEqual(7, c.faces().size()) @@ -1299,13 +1501,11 @@ def testSplitKeepingBoth(self): """ # drill a hole in the side - c = CQ(makeUnitCube()).faces( - ">Z").workplane().circle(0.25).cutThruAll() + c = CQ(makeUnitCube()).faces(">Z").workplane().circle(0.25).cutThruAll() self.assertEqual(7, c.faces().size()) # now cut it in half sideways - result = c.faces( - ">Y").workplane(-0.5).split(keepTop=True, keepBottom=True) + result = c.faces(">Y").workplane(-0.5).split(keepTop=True, keepBottom=True) # stack will have both halves, original will be unchanged # two solids are on the stack, eac @@ -1318,13 +1518,11 @@ def testSplitKeepingBottom(self): Tests splitting a solid improperly """ # Drill a hole in the side - c = CQ(makeUnitCube()).faces( - ">Z").workplane().circle(0.25).cutThruAll() + c = CQ(makeUnitCube()).faces(">Z").workplane().circle(0.25).cutThruAll() self.assertEqual(7, c.faces().size()) # Now cut it in half sideways - result = c.faces( - ">Y").workplane(-0.5).split(keepTop=False, keepBottom=True) + result = c.faces(">Y").workplane(-0.5).split(keepTop=False, keepBottom=True) # stack will have both halves, original will be unchanged # one solid is on the stack @@ -1370,22 +1568,37 @@ def testBoxPointList(self): """ Tests creating an array of boxes """ - s = Workplane("XY").rect(4.0, 4.0, forConstruction=True).vertices().box( - 0.25, 0.25, 0.25, combine=True) + s = ( + Workplane("XY") + .rect(4.0, 4.0, forConstruction=True) + .vertices() + .box(0.25, 0.25, 0.25, combine=True) + ) # 1 object, 4 solids because the object is a compound self.assertEqual(4, s.solids().size()) self.assertEqual(1, s.size()) self.saveModel(s) - s = Workplane("XY").rect(4.0, 4.0, forConstruction=True).vertices().box( - 0.25, 0.25, 0.25, combine=False) + s = ( + Workplane("XY") + .rect(4.0, 4.0, forConstruction=True) + .vertices() + .box(0.25, 0.25, 0.25, combine=False) + ) # 4 objects, 4 solids, because each is a separate solid self.assertEqual(4, s.size()) self.assertEqual(4, s.solids().size()) def testBoxCombine(self): - s = Workplane("XY").box(4, 4, 0.5).faces(">Z").workplane().rect( - 3, 3, forConstruction=True).vertices().box(0.25, 0.25, 0.25, combine=True) + s = ( + Workplane("XY") + .box(4, 4, 0.5) + .faces(">Z") + .workplane() + .rect(3, 3, forConstruction=True) + .vertices() + .box(0.25, 0.25, 0.25, combine=True) + ) self.saveModel(s) self.assertEqual(1, s.solids().size()) # we should have one big solid @@ -1394,27 +1607,36 @@ def testBoxCombine(self): def testSphereDefaults(self): s = Workplane("XY").sphere(10) - self.saveModel(s) # Until FreeCAD fixes their sphere operation + self.saveModel(s) # Until FreeCAD fixes their sphere operation self.assertEqual(1, s.solids().size()) self.assertEqual(1, s.faces().size()) def testSphereCustom(self): - s = Workplane("XY").sphere(10, angle1=0, angle2=90, - angle3=360, centered=(False, False, False)) + s = Workplane("XY").sphere( + 10, angle1=0, angle2=90, angle3=360, centered=(False, False, False) + ) self.saveModel(s) self.assertEqual(1, s.solids().size()) self.assertEqual(2, s.faces().size()) def testSpherePointList(self): - s = Workplane("XY").rect( - 4.0, 4.0, forConstruction=True).vertices().sphere(0.25, combine=False) + s = ( + Workplane("XY") + .rect(4.0, 4.0, forConstruction=True) + .vertices() + .sphere(0.25, combine=False) + ) # self.saveModel(s) # Until FreeCAD fixes their sphere operation self.assertEqual(4, s.solids().size()) self.assertEqual(4, s.faces().size()) def testSphereCombine(self): - s = Workplane("XY").rect( - 4.0, 4.0, forConstruction=True).vertices().sphere(2.25, combine=True) + s = ( + Workplane("XY") + .rect(4.0, 4.0, forConstruction=True) + .vertices() + .sphere(2.25, combine=True) + ) # self.saveModel(s) # Until FreeCAD fixes their sphere operation self.assertEqual(1, s.solids().size()) self.assertEqual(4, s.faces().size()) @@ -1427,62 +1649,111 @@ def testWedgeDefaults(self): self.assertEqual(5, s.vertices().size()) def testWedgeCentering(self): - s = Workplane("XY").wedge(10, 10, 10, 5, 5, 5, 5, centered=(False, False, False)) + s = Workplane("XY").wedge( + 10, 10, 10, 5, 5, 5, 5, centered=(False, False, False) + ) # self.saveModel(s) self.assertEqual(1, s.solids().size()) self.assertEqual(5, s.faces().size()) self.assertEqual(5, s.vertices().size()) def testWedgePointList(self): - s = Workplane("XY").rect( - 4.0, 4.0, forConstruction=True).vertices().wedge(10, 10, 10, 5, 5, 5, 5, combine=False) - #self.saveModel(s) + s = ( + Workplane("XY") + .rect(4.0, 4.0, forConstruction=True) + .vertices() + .wedge(10, 10, 10, 5, 5, 5, 5, combine=False) + ) + # self.saveModel(s) self.assertEqual(4, s.solids().size()) self.assertEqual(20, s.faces().size()) self.assertEqual(20, s.vertices().size()) def testWedgeCombined(self): - s = Workplane("XY").rect( - 4.0, 4.0, forConstruction=True).vertices().wedge(10, 10, 10, 5, 5, 5, 5, combine=True) + s = ( + Workplane("XY") + .rect(4.0, 4.0, forConstruction=True) + .vertices() + .wedge(10, 10, 10, 5, 5, 5, 5, combine=True) + ) # self.saveModel(s) self.assertEqual(1, s.solids().size()) self.assertEqual(12, s.faces().size()) self.assertEqual(16, s.vertices().size()) def testQuickStartXY(self): - s = Workplane(Plane.XY()).box(2, 4, 0.5).faces(">Z").workplane().rect(1.5, 3.5, forConstruction=True)\ - .vertices().cskHole(0.125, 0.25, 82, depth=None) + s = ( + Workplane(Plane.XY()) + .box(2, 4, 0.5) + .faces(">Z") + .workplane() + .rect(1.5, 3.5, forConstruction=True) + .vertices() + .cskHole(0.125, 0.25, 82, depth=None) + ) self.assertEqual(1, s.solids().size()) self.assertEqual(14, s.faces().size()) self.saveModel(s) def testQuickStartYZ(self): - s = Workplane(Plane.YZ()).box(2, 4, 0.5).faces(">X").workplane().rect(1.5, 3.5, forConstruction=True)\ - .vertices().cskHole(0.125, 0.25, 82, depth=None) + s = ( + Workplane(Plane.YZ()) + .box(2, 4, 0.5) + .faces(">X") + .workplane() + .rect(1.5, 3.5, forConstruction=True) + .vertices() + .cskHole(0.125, 0.25, 82, depth=None) + ) self.assertEqual(1, s.solids().size()) self.assertEqual(14, s.faces().size()) self.saveModel(s) def testQuickStartXZ(self): - s = Workplane(Plane.XZ()).box(2, 4, 0.5).faces(">Y").workplane().rect(1.5, 3.5, forConstruction=True)\ - .vertices().cskHole(0.125, 0.25, 82, depth=None) + s = ( + Workplane(Plane.XZ()) + .box(2, 4, 0.5) + .faces(">Y") + .workplane() + .rect(1.5, 3.5, forConstruction=True) + .vertices() + .cskHole(0.125, 0.25, 82, depth=None) + ) self.assertEqual(1, s.solids().size()) self.assertEqual(14, s.faces().size()) self.saveModel(s) def testDoubleTwistedLoft(self): - s = Workplane("XY").polygon(8, 20.0).workplane(offset=4.0).transformed( - rotate=Vector(0, 0, 15.0)).polygon(8, 20).loft() - s2 = Workplane("XY").polygon(8, 20.0).workplane( - offset=-4.0).transformed(rotate=Vector(0, 0, 15.0)).polygon(8, 20).loft() + s = ( + Workplane("XY") + .polygon(8, 20.0) + .workplane(offset=4.0) + .transformed(rotate=Vector(0, 0, 15.0)) + .polygon(8, 20) + .loft() + ) + s2 = ( + Workplane("XY") + .polygon(8, 20.0) + .workplane(offset=-4.0) + .transformed(rotate=Vector(0, 0, 15.0)) + .polygon(8, 20) + .loft() + ) # self.assertEquals(10,s.faces().size()) # self.assertEquals(1,s.solids().size()) s3 = s.combineSolids(s2) self.saveModel(s3) def testTwistedLoft(self): - s = Workplane("XY").polygon(8, 20.0).workplane(offset=4.0).transformed( - rotate=Vector(0, 0, 15.0)).polygon(8, 20).loft() + s = ( + Workplane("XY") + .polygon(8, 20.0) + .workplane(offset=4.0) + .transformed(rotate=Vector(0, 0, 15.0)) + .polygon(8, 20) + .loft() + ) self.assertEqual(10, s.faces().size()) self.assertEqual(1, s.solids().size()) self.saveModel(s) @@ -1507,13 +1778,12 @@ def testUnions(self): toUnion = s.rect(1.0, 1.0).extrude(1.0) resS = currentS.union(toUnion) - - self.assertEqual(11,resS.faces().size()) + + self.assertEqual(11, resS.faces().size()) def testCombine(self): s = Workplane(Plane.XY()) - objects1 = s.rect(2.0, 2.0).extrude(0.5).faces( - '>Z').rect(1.0, 1.0).extrude(0.5) + objects1 = s.rect(2.0, 2.0).extrude(0.5).faces(">Z").rect(1.0, 1.0).extrude(0.5) objects1.combine() @@ -1544,33 +1814,64 @@ def testClean(self): # make a cube with a splitter edge on one of the faces # autosimplify should remove the splitter - s = Workplane("XY").moveTo(0, 0).line(5, 0).line(5, 0).line(0, 10).\ - line(-10, 0).close().extrude(10) + s = ( + Workplane("XY") + .moveTo(0, 0) + .line(5, 0) + .line(5, 0) + .line(0, 10) + .line(-10, 0) + .close() + .extrude(10) + ) self.assertEqual(6, s.faces().size()) # test removal of splitter caused by union operation - s = Workplane("XY").box(10, 10, 10).union( - Workplane("XY").box(20, 10, 10)) + s = Workplane("XY").box(10, 10, 10).union(Workplane("XY").box(20, 10, 10)) self.assertEqual(6, s.faces().size()) # test removal of splitter caused by extrude+combine operation - s = Workplane("XY").box(10, 10, 10).faces(">Y").\ - workplane().rect(5, 10, 5).extrude(20) + s = ( + Workplane("XY") + .box(10, 10, 10) + .faces(">Y") + .workplane() + .rect(5, 10, 5) + .extrude(20) + ) self.assertEqual(10, s.faces().size()) # test removal of splitter caused by double hole operation - s = Workplane("XY").box(10, 10, 10).faces(">Z").workplane().\ - hole(3, 5).faces(">Z").workplane().hole(3, 10) + s = ( + Workplane("XY") + .box(10, 10, 10) + .faces(">Z") + .workplane() + .hole(3, 5) + .faces(">Z") + .workplane() + .hole(3, 10) + ) self.assertEqual(7, s.faces().size()) # test removal of splitter caused by cutThruAll - s = Workplane("XY").box(10, 10, 10).faces(">Y").workplane().\ - rect(10, 5).cutBlind(-5).faces(">Z").workplane().\ - center(0, 2.5).rect(5, 5).cutThruAll() + s = ( + Workplane("XY") + .box(10, 10, 10) + .faces(">Y") + .workplane() + .rect(10, 5) + .cutBlind(-5) + .faces(">Z") + .workplane() + .center(0, 2.5) + .rect(5, 5) + .cutThruAll() + ) self.assertEqual(18, s.faces().size()) @@ -1584,16 +1885,33 @@ def testNoClean(self): Test the case when clean is disabled. """ # test disabling autoSimplify - s = Workplane("XY").moveTo(0, 0).line(5, 0).line(5, 0).line(0, 10).\ - line(-10, 0).close().extrude(10, clean=False) + s = ( + Workplane("XY") + .moveTo(0, 0) + .line(5, 0) + .line(5, 0) + .line(0, 10) + .line(-10, 0) + .close() + .extrude(10, clean=False) + ) self.assertEqual(7, s.faces().size()) - s = Workplane("XY").box(10, 10, 10).\ - union(Workplane("XY").box(20, 10, 10), clean=False) + s = ( + Workplane("XY") + .box(10, 10, 10) + .union(Workplane("XY").box(20, 10, 10), clean=False) + ) self.assertEqual(14, s.faces().size()) - s = Workplane("XY").box(10, 10, 10).faces(">Y").\ - workplane().rect(5, 10, 5).extrude(20, clean=False) + s = ( + Workplane("XY") + .box(10, 10, 10) + .faces(">Y") + .workplane() + .rect(5, 10, 5) + .extrude(20, clean=False) + ) self.assertEqual(12, s.faces().size()) @@ -1601,8 +1919,17 @@ def testExplicitClean(self): """ Test running of `clean()` method explicitly. """ - s = Workplane("XY").moveTo(0, 0).line(5, 0).line(5, 0).line(0, 10).\ - line(-10, 0).close().extrude(10, clean=False).clean() + s = ( + Workplane("XY") + .moveTo(0, 0) + .line(5, 0) + .line(5, 0) + .line(0, 10) + .line(-10, 0) + .close() + .extrude(10, clean=False) + .clean() + ) self.assertEqual(6, s.faces().size()) def testPlanes(self): @@ -1611,62 +1938,132 @@ def testPlanes(self): """ # ZX plane s = Workplane(Plane.ZX()) - result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\ - .rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None) + result = ( + s.rect(2.0, 4.0) + .extrude(0.5) + .faces(">Z") + .workplane() + .rect(1.5, 3.5, forConstruction=True) + .vertices() + .cskHole(0.125, 0.25, 82, depth=None) + ) self.saveModel(result) # YX plane s = Workplane(Plane.YX()) - result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\ - .rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None) + result = ( + s.rect(2.0, 4.0) + .extrude(0.5) + .faces(">Z") + .workplane() + .rect(1.5, 3.5, forConstruction=True) + .vertices() + .cskHole(0.125, 0.25, 82, depth=None) + ) self.saveModel(result) # YX plane s = Workplane(Plane.YX()) - result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\ - .rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None) + result = ( + s.rect(2.0, 4.0) + .extrude(0.5) + .faces(">Z") + .workplane() + .rect(1.5, 3.5, forConstruction=True) + .vertices() + .cskHole(0.125, 0.25, 82, depth=None) + ) self.saveModel(result) # ZY plane s = Workplane(Plane.ZY()) - result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\ - .rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None) + result = ( + s.rect(2.0, 4.0) + .extrude(0.5) + .faces(">Z") + .workplane() + .rect(1.5, 3.5, forConstruction=True) + .vertices() + .cskHole(0.125, 0.25, 82, depth=None) + ) self.saveModel(result) # front plane s = Workplane(Plane.front()) - result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\ - .rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None) + result = ( + s.rect(2.0, 4.0) + .extrude(0.5) + .faces(">Z") + .workplane() + .rect(1.5, 3.5, forConstruction=True) + .vertices() + .cskHole(0.125, 0.25, 82, depth=None) + ) self.saveModel(result) # back plane s = Workplane(Plane.back()) - result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\ - .rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None) + result = ( + s.rect(2.0, 4.0) + .extrude(0.5) + .faces(">Z") + .workplane() + .rect(1.5, 3.5, forConstruction=True) + .vertices() + .cskHole(0.125, 0.25, 82, depth=None) + ) self.saveModel(result) # left plane s = Workplane(Plane.left()) - result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\ - .rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None) + result = ( + s.rect(2.0, 4.0) + .extrude(0.5) + .faces(">Z") + .workplane() + .rect(1.5, 3.5, forConstruction=True) + .vertices() + .cskHole(0.125, 0.25, 82, depth=None) + ) self.saveModel(result) # right plane s = Workplane(Plane.right()) - result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\ - .rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None) + result = ( + s.rect(2.0, 4.0) + .extrude(0.5) + .faces(">Z") + .workplane() + .rect(1.5, 3.5, forConstruction=True) + .vertices() + .cskHole(0.125, 0.25, 82, depth=None) + ) self.saveModel(result) # top plane s = Workplane(Plane.top()) - result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\ - .rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None) + result = ( + s.rect(2.0, 4.0) + .extrude(0.5) + .faces(">Z") + .workplane() + .rect(1.5, 3.5, forConstruction=True) + .vertices() + .cskHole(0.125, 0.25, 82, depth=None) + ) self.saveModel(result) # bottom plane s = Workplane(Plane.bottom()) - result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\ - .rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None) + result = ( + s.rect(2.0, 4.0) + .extrude(0.5) + .faces(">Z") + .workplane() + .rect(1.5, 3.5, forConstruction=True) + .vertices() + .cskHole(0.125, 0.25, 82, depth=None) + ) self.saveModel(result) def testIsInside(self): @@ -1715,8 +2112,14 @@ def build(): h = 10.0 t = 1.0 s1 = Workplane("XY").circle(bd).workplane(offset=h).circle(td).loft() - s2 = Workplane("XY").workplane(offset=t).circle( - bd - (2.0 * t)).workplane(offset=(h - t)).circle(td - (2.0 * t)).loft() + s2 = ( + Workplane("XY") + .workplane(offset=t) + .circle(bd - (2.0 * t)) + .workplane(offset=(h - t)) + .circle(td - (2.0 * t)) + .loft() + ) s3 = s1.cut(s2) self.saveModel(s3) @@ -1756,57 +2159,90 @@ def testEnclosure(self): p_lipHeight = 1.0 # outer shell - oshell = Workplane("XY").rect(p_outerWidth, p_outerLength).extrude( - p_outerHeight + p_lipHeight) + oshell = ( + Workplane("XY") + .rect(p_outerWidth, p_outerLength) + .extrude(p_outerHeight + p_lipHeight) + ) # weird geometry happens if we make the fillets in the wrong order if p_sideRadius > p_topAndBottomRadius: - oshell = oshell.edges("|Z").fillet(p_sideRadius)\ - .edges("#Z").fillet(p_topAndBottomRadius) + oshell = ( + oshell.edges("|Z") + .fillet(p_sideRadius) + .edges("#Z") + .fillet(p_topAndBottomRadius) + ) else: - oshell = oshell.edges("#Z").fillet(p_topAndBottomRadius)\ - .edges("|Z").fillet(p_sideRadius) + oshell = ( + oshell.edges("#Z") + .fillet(p_topAndBottomRadius) + .edges("|Z") + .fillet(p_sideRadius) + ) # inner shell - ishell = oshell.faces("Z").workplane(-p_thickness)\ - .rect(POSTWIDTH, POSTLENGTH, forConstruction=True)\ - .vertices()\ - .circle(p_screwpostOD / 2.0)\ - .circle(p_screwpostID / 2.0)\ + POSTWIDTH = p_outerWidth - 2.0 * p_screwpostInset + POSTLENGTH = p_outerLength - 2.0 * p_screwpostInset + + box = ( + box.faces(">Z") + .workplane(-p_thickness) + .rect(POSTWIDTH, POSTLENGTH, forConstruction=True) + .vertices() + .circle(p_screwpostOD / 2.0) + .circle(p_screwpostID / 2.0) .extrude((-1.0) * (p_outerHeight + p_lipHeight - p_thickness), True) + ) # split lid into top and bottom parts - (lid, bottom) = box.faces(">Z").workplane(-p_thickness - - p_lipHeight).split(keepTop=True, keepBottom=True).all() # splits into two solids + (lid, bottom) = ( + box.faces(">Z") + .workplane(-p_thickness - p_lipHeight) + .split(keepTop=True, keepBottom=True) + .all() + ) # splits into two solids # translate the lid, and subtract the bottom from it to produce the lid inset lowerLid = lid.translate((0, 0, -p_lipHeight)) cutlip = lowerLid.cut(bottom).translate( - (p_outerWidth + p_thickness, 0, p_thickness - p_outerHeight + p_lipHeight)) + (p_outerWidth + p_thickness, 0, p_thickness - p_outerHeight + p_lipHeight) + ) # compute centers for counterbore/countersink or counterbore - topOfLidCenters = cutlip.faces(">Z").workplane().rect( - POSTWIDTH, POSTLENGTH, forConstruction=True).vertices() + topOfLidCenters = ( + cutlip.faces(">Z") + .workplane() + .rect(POSTWIDTH, POSTLENGTH, forConstruction=True) + .vertices() + ) # add holes of the desired type if p_boreDiameter > 0 and p_boreDepth > 0: topOfLid = topOfLidCenters.cboreHole( - p_screwpostID, p_boreDiameter, p_boreDepth, (2.0) * p_thickness) + p_screwpostID, p_boreDiameter, p_boreDepth, (2.0) * p_thickness + ) elif p_countersinkDiameter > 0 and p_countersinkAngle > 0: topOfLid = topOfLidCenters.cskHole( - p_screwpostID, p_countersinkDiameter, p_countersinkAngle, (2.0) * p_thickness) + p_screwpostID, + p_countersinkDiameter, + p_countersinkAngle, + (2.0) * p_thickness, + ) else: topOfLid = topOfLidCenters.hole(p_screwpostID, (2.0) * p_thickness) @@ -1823,9 +2259,9 @@ def testExtrude(self): """ Test extrude """ - r = 1. - h = 1. - decimal_places = 9. + r = 1.0 + h = 1.0 + decimal_places = 9.0 # extrude in one direction s = Workplane("XY").circle(r).extrude(h, both=False) @@ -1836,9 +2272,7 @@ def testExtrude(self): # 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) + self.assertTupleAlmostEquals(delta.toTuple(), (0.0, 0.0, h), decimal_places) # extrude symmetrically s = Workplane("XY").circle(r).extrude(h, both=True) @@ -1849,14 +2283,14 @@ def testExtrude(self): # 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., 2. * h), - decimal_places) + self.assertTupleAlmostEquals( + delta.toTuple(), (0.0, 0.0, 2.0 * h), decimal_places + ) def testTaperedExtrudeCutBlind(self): - h = 1. - r = 1. + h = 1.0 + r = 1.0 t = 5 # extrude with a positive taper @@ -1882,10 +2316,17 @@ def testTaperedExtrudeCutBlind(self): 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]') + 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) @@ -1896,7 +2337,13 @@ def testClose(self): # Close when endPoint and startPoint coincide. # Create a double half-circle - b = Workplane(Plane.XY()).sagittaArc((10, 0), 2).sagittaArc((0, 0), 2).close().extrude(2) + b = ( + Workplane(Plane.XY()) + .sagittaArc((10, 0), 2) + .sagittaArc((0, 0), 2) + .close() + .extrude(2) + ) # The b shape shall have twice the volume of the a shape. self.assertAlmostEqual(a.val().Volume() * 2.0, b.val().Volume()) @@ -1906,46 +2353,80 @@ def testClose(self): length = 10.0 width = 5.0 - obj1 = Workplane('XY', origin=(0, 0, -thickness / 2)) \ - .moveTo(length / 2, 0).threePointArc((0, width / 2), (-length / 2, 0)) \ - .threePointArc((0, -width / 2), (length / 2, 0)) \ - .close().extrude(thickness) + obj1 = ( + Workplane("XY", origin=(0, 0, -thickness / 2)) + .moveTo(length / 2, 0) + .threePointArc((0, width / 2), (-length / 2, 0)) + .threePointArc((0, -width / 2), (length / 2, 0)) + .close() + .extrude(thickness) + ) - os_x = 8.0 # Offset in X + os_x = 8.0 # Offset in X os_y = -19.5 # Offset in Y - obj2 = Workplane('YZ', origin=(os_x, os_y, -thickness / 2)) \ - .moveTo(os_x + length / 2, os_y).sagittaArc((os_x -length / 2, os_y), width / 2) \ - .sagittaArc((os_x + length / 2, os_y), width / 2) \ - .close().extrude(thickness) + obj2 = ( + Workplane("YZ", origin=(os_x, os_y, -thickness / 2)) + .moveTo(os_x + length / 2, os_y) + .sagittaArc((os_x - length / 2, os_y), width / 2) + .sagittaArc((os_x + length / 2, os_y), width / 2) + .close() + .extrude(thickness) + ) # The obj1 shape shall have the same volume as the obj2 shape. self.assertAlmostEqual(obj1.val().Volume(), obj2.val().Volume()) def testText(self): - box = Workplane("XY" ).box(4, 4, 0.5) - - obj1 = box.faces('>Z').workplane()\ - .text('CQ 2.0',0.5,-.05,cut=True,halign='left',valign='bottom', font='Sans') - - #combined object should have smaller volume - self.assertGreater(box.val().Volume(),obj1.val().Volume()) - - obj2 = box.faces('>Z').workplane()\ - .text('CQ 2.0',0.5,.05,cut=False,combine=True, font='Sans') - - #combined object should have bigger volume - self.assertLess(box.val().Volume(),obj2.val().Volume()) - - #verify that the number of top faces is correct (NB: this is font specific) - self.assertEqual(len(obj2.faces('>Z').vals()),5) - - obj3 = box.faces('>Z').workplane()\ - .text('CQ 2.0',0.5,.05,cut=False,combine=False,halign='right',valign='top', font='Sans') - - #verify that the number of solids is correct - self.assertEqual(len(obj3.solids().vals()),5) + box = Workplane("XY").box(4, 4, 0.5) + + obj1 = ( + box.faces(">Z") + .workplane() + .text( + "CQ 2.0", + 0.5, + -0.05, + cut=True, + halign="left", + valign="bottom", + font="Sans", + ) + ) + + # combined object should have smaller volume + self.assertGreater(box.val().Volume(), obj1.val().Volume()) + + obj2 = ( + box.faces(">Z") + .workplane() + .text("CQ 2.0", 0.5, 0.05, cut=False, combine=True, font="Sans") + ) + + # combined object should have bigger volume + self.assertLess(box.val().Volume(), obj2.val().Volume()) + + # verify that the number of top faces is correct (NB: this is font specific) + self.assertEqual(len(obj2.faces(">Z").vals()), 5) + + obj3 = ( + box.faces(">Z") + .workplane() + .text( + "CQ 2.0", + 0.5, + 0.05, + cut=False, + combine=False, + halign="right", + valign="top", + font="Sans", + ) + ) + + # verify that the number of solids is correct + self.assertEqual(len(obj3.solids().vals()), 5) def testParametricCurve(self): @@ -1954,83 +2435,87 @@ def testParametricCurve(self): k = 4 r = 1 - func = lambda t: ( r*(k+1)*cos(t) - r* cos((k+1)*t), - r*(k+1)*sin(t) - r* sin((k+1)*t)) + func = lambda t: ( + r * (k + 1) * cos(t) - r * cos((k + 1) * t), + r * (k + 1) * sin(t) - r * sin((k + 1) * t), + ) - res_open = Workplane('XY').parametricCurve(func).extrude(3) + res_open = Workplane("XY").parametricCurve(func).extrude(3) - #open profile generates an invalid solid + # open profile generates an invalid solid self.assertFalse(res_open.solids().val().isValid()) - res_closed = Workplane('XY').parametricCurve(func,start=0,stop=2*pi)\ - .extrude(3) + res_closed = ( + Workplane("XY").parametricCurve(func, start=0, stop=2 * pi).extrude(3) + ) - #closed profile will generate a valid solid with 3 faces + # closed profile will generate a valid solid with 3 faces self.assertTrue(res_closed.solids().val().isValid()) - self.assertEqual(len(res_closed.faces().vals()),3) - + self.assertEqual(len(res_closed.faces().vals()), 3) + def testMakeShellSolid(self): - c0 = math.sqrt(2)/4 - vertices = [[c0, -c0, c0], [c0, c0, -c0], [-c0, c0, c0], [-c0, -c0, -c0]] + c0 = math.sqrt(2) / 4 + vertices = [[c0, -c0, c0], [c0, c0, -c0], [-c0, c0, c0], [-c0, -c0, -c0]] faces_ixs = [[0, 1, 2, 0], [1, 0, 3, 1], [2, 3, 0, 2], [3, 2, 1, 3]] - + faces = [] for ixs in faces_ixs: lines = [] - for v1,v2 in zip(ixs,ixs[1:]): - lines.append(Edge.makeLine(Vector(*vertices[v1]), - Vector(*vertices[v2]))) + for v1, v2 in zip(ixs, ixs[1:]): + lines.append( + Edge.makeLine(Vector(*vertices[v1]), Vector(*vertices[v2])) + ) wire = Wire.combine(lines) faces.append(Face.makeFromWires(wire)) - + shell = Shell.makeShell(faces) solid = Solid.makeSolid(shell) - + self.assertTrue(shell.isValid()) self.assertTrue(solid.isValid()) - - self.assertEqual(len(solid.Vertices()),4) - self.assertEqual(len(solid.Faces()),4) + + self.assertEqual(len(solid.Vertices()), 4) + self.assertEqual(len(solid.Faces()), 4) def testIsInsideSolid(self): # test solid - model = Workplane('XY').box(10,10,10) - solid = model.val() # get first object on stack + model = Workplane("XY").box(10, 10, 10) + solid = model.val() # get first object on stack - self.assertTrue(solid.isInside((0,0,0))) - self.assertFalse(solid.isInside((10,10,10))) - self.assertTrue(solid.isInside((Vector(3,3,3)))) - self.assertFalse(solid.isInside((Vector(30.0,30.0,30.0)))) + self.assertTrue(solid.isInside((0, 0, 0))) + self.assertFalse(solid.isInside((10, 10, 10))) + self.assertTrue(solid.isInside((Vector(3, 3, 3)))) + self.assertFalse(solid.isInside((Vector(30.0, 30.0, 30.0)))) - self.assertTrue(solid.isInside((0,0,4.99), tolerance=0.1)) - self.assertTrue(solid.isInside((0,0,5))) # check point on surface - self.assertTrue(solid.isInside((0,0,5.01), tolerance=0.1)) - self.assertFalse(solid.isInside((0,0,5.1), tolerance=0.1)) + self.assertTrue(solid.isInside((0, 0, 4.99), tolerance=0.1)) + self.assertTrue(solid.isInside((0, 0, 5))) # check point on surface + self.assertTrue(solid.isInside((0, 0, 5.01), tolerance=0.1)) + self.assertFalse(solid.isInside((0, 0, 5.1), tolerance=0.1)) # test compound solid - model = Workplane('XY').box(10,10,10) - model = model.moveTo(50,50).box(10,10,10) + model = Workplane("XY").box(10, 10, 10) + model = model.moveTo(50, 50).box(10, 10, 10) solid = model.val() - self.assertTrue(solid.isInside((0,0,0))) - self.assertTrue(solid.isInside((50,50,0))) - self.assertFalse(solid.isInside((50,56,0))) + self.assertTrue(solid.isInside((0, 0, 0))) + self.assertTrue(solid.isInside((50, 50, 0))) + self.assertFalse(solid.isInside((50, 56, 0))) # make sure raises on non solid - model = Workplane('XY').rect(10,10) + model = Workplane("XY").rect(10, 10) solid = model.val() with self.assertRaises(AttributeError): - solid.isInside((0,0,0)) + solid.isInside((0, 0, 0)) # test solid with an internal void - void = Workplane('XY').box(10,10,10) - model = Workplane('XY').box(100,100,100).cut(void) + void = Workplane("XY").box(10, 10, 10) + model = Workplane("XY").box(100, 100, 100).cut(void) solid = model.val() - self.assertFalse(solid.isInside((0,0,0))) - self.assertTrue(solid.isInside((40,40,40))) - self.assertFalse(solid.isInside((55,55,55))) + self.assertFalse(solid.isInside((0, 0, 0))) + self.assertTrue(solid.isInside((40, 40, 40))) + self.assertFalse(solid.isInside((55, 55, 55))) def testWorkplaneCenterOptions(self): """ @@ -2038,145 +2523,312 @@ def testWorkplaneCenterOptions(self): """ decimal_places = 9 - pts = [(0,0),(90,0),(90,30),(30,30),(30,60),(0.0,60)] + pts = [(0, 0), (90, 0), (90, 30), (30, 30), (30, 60), (0.0, 60)] r = Workplane("XY").polyline(pts).close().extrude(10.0) - origin = r.faces(">Z").workplane(centerOption='ProjectedOrigin') \ - .plane.origin.toTuple() + origin = ( + r.faces(">Z") + .workplane(centerOption="ProjectedOrigin") + .plane.origin.toTuple() + ) self.assertTupleAlmostEquals(origin, (0.0, 0.0, 10.0), decimal_places) - origin = r.faces(">Z").workplane(centerOption='CenterOfMass') \ - .plane.origin.toTuple() + origin = ( + r.faces(">Z").workplane(centerOption="CenterOfMass").plane.origin.toTuple() + ) self.assertTupleAlmostEquals(origin, (37.5, 22.5, 10.0), decimal_places) - origin = r.faces(">Z").workplane(centerOption='CenterOfBoundBox') \ - .plane.origin.toTuple() + origin = ( + r.faces(">Z") + .workplane(centerOption="CenterOfBoundBox") + .plane.origin.toTuple() + ) self.assertTupleAlmostEquals(origin, (45.0, 30.0, 10.0), decimal_places) - origin = r.faces(">Z").workplane(centerOption='ProjectedOrigin',origin=(30,10,20)) \ - .plane.origin.toTuple() + origin = ( + r.faces(">Z") + .workplane(centerOption="ProjectedOrigin", origin=(30, 10, 20)) + .plane.origin.toTuple() + ) self.assertTupleAlmostEquals(origin, (30.0, 10.0, 10.0), decimal_places) - origin = r.faces(">Z").workplane(centerOption='ProjectedOrigin',origin=Vector(30,10,20)) \ - .plane.origin.toTuple() + origin = ( + r.faces(">Z") + .workplane(centerOption="ProjectedOrigin", origin=Vector(30, 10, 20)) + .plane.origin.toTuple() + ) self.assertTupleAlmostEquals(origin, (30.0, 10.0, 10.0), decimal_places) with self.assertRaises(ValueError): - origin = r.faces(">Z").workplane(centerOption='undefined') + origin = r.faces(">Z").workplane(centerOption="undefined") # test case where plane origin is shifted with center call - r = r.faces(">Z").workplane(centerOption='ProjectedOrigin').center(30,0) \ - .hole(90) - - origin = r.faces(">Z").workplane(centerOption='ProjectedOrigin') \ - .plane.origin.toTuple() + r = ( + r.faces(">Z") + .workplane(centerOption="ProjectedOrigin") + .center(30, 0) + .hole(90) + ) + + origin = ( + r.faces(">Z") + .workplane(centerOption="ProjectedOrigin") + .plane.origin.toTuple() + ) self.assertTupleAlmostEquals(origin, (30.0, 0.0, 10.0), decimal_places) - origin = r.faces(">Z").workplane(centerOption='ProjectedOrigin', origin=(0,0,0)) \ - .plane.origin.toTuple() + origin = ( + r.faces(">Z") + .workplane(centerOption="ProjectedOrigin", origin=(0, 0, 0)) + .plane.origin.toTuple() + ) self.assertTupleAlmostEquals(origin, (0.0, 0.0, 10.0), decimal_places) # make sure projection works in all directions r = Workplane("YZ").polyline(pts).close().extrude(10.0) - origin = r.faces(">X").workplane(centerOption='ProjectedOrigin') \ - .plane.origin.toTuple() + origin = ( + r.faces(">X") + .workplane(centerOption="ProjectedOrigin") + .plane.origin.toTuple() + ) self.assertTupleAlmostEquals(origin, (10.0, 0.0, 0.0), decimal_places) - origin = r.faces(">X").workplane(centerOption='CenterOfMass') \ - .plane.origin.toTuple() + origin = ( + r.faces(">X").workplane(centerOption="CenterOfMass").plane.origin.toTuple() + ) self.assertTupleAlmostEquals(origin, (10.0, 37.5, 22.5), decimal_places) - origin = r.faces(">X").workplane(centerOption='CenterOfBoundBox') \ - .plane.origin.toTuple() + origin = ( + r.faces(">X") + .workplane(centerOption="CenterOfBoundBox") + .plane.origin.toTuple() + ) self.assertTupleAlmostEquals(origin, (10.0, 45.0, 30.0), decimal_places) r = Workplane("XZ").polyline(pts).close().extrude(10.0) - origin = r.faces("Z").workplane().slot2D(4,1,0).cutThruAll() + box = Workplane("XY").box(5, 5, 1) + result = box.faces(">Z").workplane().slot2D(4, 1, 0).cutThruAll() self.assertAlmostEqual(result.val().Volume(), 21.214601837, decimal_places) - result = box.faces(">Z").workplane().slot2D(4,1,0).cutBlind(-0.5) + result = box.faces(">Z").workplane().slot2D(4, 1, 0).cutBlind(-0.5) self.assertAlmostEqual(result.val().Volume(), 23.107300918, decimal_places) # Test to see if slot is rotated correctly - result = Workplane("XY").slot2D(4,1,45).extrude(1) + result = Workplane("XY").slot2D(4, 1, 45).extrude(1) point = result.faces(">Z").edges(">X").first().val().startPoint().toTuple() - self.assertTupleAlmostEquals(point, (0.707106781, 1.414213562, 1.0), decimal_places) + self.assertTupleAlmostEquals( + point, (0.707106781, 1.414213562, 1.0), decimal_places + ) def test_assembleEdges(self): # Plate with 5 sides and 2 bumps, one side is not co-planar with the other sides # Passes an open wire to assembleEdges so that IsDone is true but Error returns 2 to test the warning functionality. - edge_points = [[-7.,-7.,0.], [-3.,-10.,3.], [7.,-7.,0.], [7.,7.,0.], [-7.,7.,0.]] - edge_wire = Workplane('XY').polyline([(-7.,-7.), (7.,-7.), (7.,7.), (-7.,7.)]) - edge_wire = edge_wire.add(Workplane('YZ').workplane().transformed(offset=Vector(0, 0, -7), rotate=Vector(0, 45, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) + edge_points = [ + [-7.0, -7.0, 0.0], + [-3.0, -10.0, 3.0], + [7.0, -7.0, 0.0], + [7.0, 7.0, 0.0], + [-7.0, 7.0, 0.0], + ] + edge_wire = Workplane("XY").polyline( + [(-7.0, -7.0), (7.0, -7.0), (7.0, 7.0), (-7.0, 7.0)] + ) + edge_wire = edge_wire.add( + Workplane("YZ") + .workplane() + .transformed(offset=Vector(0, 0, -7), rotate=Vector(0, 45, 0)) + .spline([(-7.0, 0.0), (3, -3), (7.0, 0.0)]) + ) edge_wire = [o.vals()[0] for o in edge_wire.all()] edge_wire = Wire.assembleEdges(edge_wire) - + # Embossed star, need to change optional parameters to obtain nice looking result. - r1=3. - r2=10. - fn=6 - edge_points = [[r1*math.cos(i * math.pi/fn), r1*math.sin(i * math.pi/fn)] if i%2==0 else [r2*math.cos(i * math.pi/fn), r2*math.sin(i * math.pi/fn)] for i in range(2*fn+1)] - edge_wire = Workplane('XY').polyline(edge_points) + r1 = 3.0 + r2 = 10.0 + fn = 6 + edge_points = [ + [r1 * math.cos(i * math.pi / fn), r1 * math.sin(i * math.pi / fn)] + if i % 2 == 0 + else [r2 * math.cos(i * math.pi / fn), r2 * math.sin(i * math.pi / fn)] + for i in range(2 * fn + 1) + ] + edge_wire = Workplane("XY").polyline(edge_points) edge_wire = [o.vals()[0] for o in edge_wire.all()] edge_wire = Wire.assembleEdges(edge_wire) - + # Points on hexagonal pattern coordinates, use of pushpoints. - r1 = 1. + r1 = 1.0 fn = 6 - edge_points = [[r1*math.cos(i * 2*math.pi/fn), r1*math.sin(i * 2*math.pi/fn)] for i in range(fn+1)] - surface_points = [[0.25,0,0.75], [-0.25,0,0.75], [0,0.25,0.75], [0,-0.25,0.75], [0,0,2]] - edge_wire = Workplane('XY').polyline(edge_points) + edge_points = [ + [r1 * math.cos(i * 2 * math.pi / fn), r1 * math.sin(i * 2 * math.pi / fn)] + for i in range(fn + 1) + ] + surface_points = [ + [0.25, 0, 0.75], + [-0.25, 0, 0.75], + [0, 0.25, 0.75], + [0, -0.25, 0.75], + [0, 0, 2], + ] + edge_wire = Workplane("XY").polyline(edge_points) edge_wire = [o.vals()[0] for o in edge_wire.all()] edge_wire = Wire.assembleEdges(edge_wire) - + # Gyroïd, all edges are splines on different workplanes. - edge_points = [[[3.54, 3.54], [1.77, 0.0], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [-1.77, 0.0], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]]] - plane_list = ['XZ', 'XY', 'YZ', 'XZ', 'YZ', 'XY'] - offset_list = [-3.54, 3.54, 3.54, 3.54, -3.54, -3.54] - edge_wire = Workplane(plane_list[0]).workplane(offset=-offset_list[0]).spline(edge_points[0]) - for i in range(len(edge_points)-1): - edge_wire = edge_wire.add(Workplane(plane_list[i+1]).workplane(offset=-offset_list[i+1]).spline(edge_points[i+1])) + edge_points = [ + [[3.54, 3.54], [1.77, 0.0], [3.54, -3.54]], + [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], + [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], + [[-3.54, -3.54], [-1.77, 0.0], [-3.54, 3.54]], + [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]], + [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]], + ] + plane_list = ["XZ", "XY", "YZ", "XZ", "YZ", "XY"] + offset_list = [-3.54, 3.54, 3.54, 3.54, -3.54, -3.54] + edge_wire = ( + Workplane(plane_list[0]) + .workplane(offset=-offset_list[0]) + .spline(edge_points[0]) + ) + for i in range(len(edge_points) - 1): + edge_wire = edge_wire.add( + Workplane(plane_list[i + 1]) + .workplane(offset=-offset_list[i + 1]) + .spline(edge_points[i + 1]) + ) edge_wire = [o.vals()[0] for o in edge_wire.all()] edge_wire = Wire.assembleEdges(edge_wire) + def testTag(self): + + # test tagging + result = ( + Workplane("XY") + .pushPoints([(-2, 0), (2, 0)]) + .box(1, 1, 1, combine=False) + .tag("2 solids") + .union(Workplane("XY").box(6, 1, 1)) + ) + self.assertEqual(len(result.objects), 1) + result = result._getTagged("2 solids") + self.assertEqual(len(result.objects), 2) + + def testCopyWorkplane(self): + + obj0 = Workplane("XY").box(1, 1, 10).faces(">Z").workplane() + obj1 = Workplane("XY").copyWorkplane(obj0).box(1, 1, 1) + self.assertTupleAlmostEquals((0, 0, 5), obj1.val().Center().toTuple(), 9) + + def testWorkplaneFromTagged(self): + + # create a flat, wide base. Extrude one object 4 units high, another + # object ontop of it 6 units high. Go back to base plane. Extrude an + # object 11 units high. Assert that top face is 11 units high. + result = ( + Workplane("XY") + .box(10, 10, 1, centered=(True, True, False)) + .faces(">Z") + .workplane() + .tag("base") + .center(3, 0) + .rect(2, 2) + .extrude(4) + .faces(">Z") + .workplane() + .circle(1) + .extrude(6) + .workplaneFromTagged("base") + .center(-3, 0) + .circle(1) + .extrude(11) + ) + self.assertTupleAlmostEquals( + result.faces(">Z").val().Center().toTuple(), (-3, 0, 12), 9 + ) + + def testTagSelectors(self): + + result0 = Workplane("XY").box(1, 1, 1).tag("box").sphere(1) + # result is currently a sphere + self.assertEqual(1, result0.faces().size()) + # a box has 8 vertices + self.assertEqual(8, result0.vertices(tag="box").size()) + # 6 faces + self.assertEqual(6, result0.faces(tag="box").size()) + # 12 edges + self.assertEqual(12, result0.edges(tag="box").size()) + # 6 wires + self.assertEqual(6, result0.wires(tag="box").size()) + + # create two solids, tag them, join to one solid + result1 = ( + Workplane("XY") + .pushPoints([(1, 0), (-1, 0)]) + .box(1, 1, 1) + .tag("boxes") + .sphere(1) + ) + self.assertEqual(1, result1.solids().size()) + self.assertEqual(2, result1.solids(tag="boxes").size()) + self.assertEqual(1, result1.shells().size()) + self.assertEqual(2, result1.shells(tag="boxes").size()) + + # create 4 individual objects, tag it, then combine to one compound + result2 = ( + Workplane("XY") + .rect(4, 4) + .vertices() + .box(1, 1, 1, combine=False) + .tag("4 objs") + ) + result2 = result2.newObject([Compound.makeCompound(result2.objects)]) + self.assertEqual(1, result2.compounds().size()) + self.assertEqual(0, result2.compounds(tag="4 objs").size()) + def test_interpPlate(self): """ Tests the interpPlate() functionnalites From 809d127c80e07c65d007a3a55b4c9a80eb2e1eaa Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 16:47:04 +0100 Subject: [PATCH 57/70] Add files via upload PEP8 formatting --- cadquery/cq.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cadquery/cq.py b/cadquery/cq.py index 1da693899..27ca7ebfc 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -2862,6 +2862,8 @@ def _sweep( return Compound.makeCompound(toFuse) + + def interpPlate( self, surf_edges, @@ -2917,8 +2919,8 @@ def interpPlate( """ # If thickness is 0, only a 2D surface will be returned. - if thickness==0: - combine=False + if thickness == 0: + combine = False # Creates interpolated plate def _makeplate(pnt): @@ -2931,7 +2933,6 @@ def _makeplate(pnt): return plates else: return self.union(plates, clean=clean) - def box( self, length, From 969be405df7bc1793c67e5fe2d07538d9be26d1b Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 16:47:28 +0100 Subject: [PATCH 58/70] Add files via upload PEP8 formatting --- cadquery/occ_impl/shapes.py | 38 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index 736755a80..54ba9e2dc 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -108,9 +108,6 @@ from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs -from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs, STEPControl_Controller -from OCC.Core.Interface import Interface_Static_SetCVal -from OCC.Core.Interface import Interface_Static_SetIVal from OCC.Core.BRepMesh import BRepMesh_IncrementalMesh from OCC.Core.StlAPI import StlAPI_Writer @@ -288,25 +285,12 @@ def exportStl(self, fileName, precision=1e-5): return writer.Write(self.wrapped, fileName) def exportStep(self, fileName): - """ - STEPControl_AsIs translates an Open CASCADE shape to its highest possible STEP representation. - STEPControl_ManifoldSolidBrep translates an Open CASCADE shape to a STEP manifold_solid_brep or brep_with_voids entity. NO - STEPControl_FacetedBrep translates an Open CASCADE shape into a STEP faceted_brep entity. NO - STEPControl_ShellBasedSurfaceModel translates an Open CASCADE shape into a STEP shell_based_surface_model entity. SAME SIZE - STEPControl_GeometricCurveSet translates an Open CASCADE shape into a STEP geometric_curve_set entity. LINES ONLY - surface_curve_mode: - 0: write without pcurves (2 times smaller STEP file) - 1 (Default): write with pcurves - """ - c = STEPControl_Controller() - c.Init() - Interface_Static_SetCVal("write.step.schema", "AP214") - Interface_Static_SetIVal('write.surfacecurve.mode', 0) + writer = STEPControl_Writer() writer.Transfer(self.wrapped, STEPControl_AsIs) - + return writer.Write(fileName) - + def exportBrep(self, fileName): """ Export given shape to a BREP file @@ -1263,7 +1247,7 @@ def interpPlate(cls, surf_edges, surf_pts, thickness, degree=3, nbPtsOnCur=15, n :param MaxSegments = 9 (OCCT default) :type MaxSegments: Integer >= 2 (?) """ - + # POINTS CONSTRAINTS: list of (x,y,z) points, optional. pts_array = [gp_Pnt(*pt) for pt in surf_pts] @@ -1278,7 +1262,7 @@ def interpPlate(cls, surf_edges, surf_pts, thickness, degree=3, nbPtsOnCur=15, n if isinstance(surf_edges, list): e_array = [Vector(*e) for e in surf_edges] wire_builder = BRepBuilderAPI_MakePolygon() - for e in e_array: # Create polygon from edges + for e in e_array: # Create polygon from edges wire_builder.Add(e.toPnt()) wire_builder.Close() w = wire_builder.Wire() @@ -1286,16 +1270,18 @@ def interpPlate(cls, surf_edges, surf_pts, thickness, degree=3, nbPtsOnCur=15, n edges = [i for i in TopologyExplorer(w).edges()] # MAKE SURFACE - continuity = GeomAbs_C0 # Fixed, changing to anything else crashes. - face = Face.makeNSidedSurface(edges, pts_array, continuity, degree, nbPtsOnCur, nbIter, anisotropy, tol2d, tol3d, tolAng, tolCurv, maxDeg, maxSegments) + continuity = GeomAbs_C0 # Fixed, changing to anything else crashes. + face = Face.makeNSidedSurface(edges, pts_array, continuity, degree, nbPtsOnCur, + nbIter, anisotropy, tol2d, tol3d, tolAng, tolCurv, maxDeg, maxSegments) # THICKEN SURFACE - if abs(thickness)>0: # abs() because negative values are allowed to set direction of thickening + if abs(thickness) > 0: # abs() because negative values are allowed to set direction of thickening solid = BRepOffset_MakeOffset() - solid.Initialize(face.wrapped, thickness, 1.e-5, BRepOffset_Skin, False, False, GeomAbs_Intersection, True) #The last True is important to make solid + solid.Initialize(face.wrapped, thickness, 1.e-5, BRepOffset_Skin, False, False, + GeomAbs_Intersection, True) # The last True is important to make solid solid.MakeOffsetShape() return cls(solid.Shape()) - else: # Return 2D surface only + else: # Return 2D surface only return face @classmethod From 4f85c77fe6cb14c1cf8930dbef119bba4dfce8cb Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 16:48:09 +0100 Subject: [PATCH 59/70] Add files via upload PEP8 formatting --- examples/Ex101_InterpPlate.py | 85 ++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/examples/Ex101_InterpPlate.py b/examples/Ex101_InterpPlate.py index d8596719c..ce1ecb947 100644 --- a/examples/Ex101_InterpPlate.py +++ b/examples/Ex101_InterpPlate.py @@ -4,40 +4,48 @@ # TEST_1 # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. thickness = 0 -edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] -surface_points = [[5.,5.,5.]] -plate_0 = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness) +edge_points = [[0., 0., 0.], [0., 10., 0.], [0., 10., 10.], [0., 0., 10.]] +surface_points = [[5., 5., 5.]] +plate_0 = cq.Workplane("XY").interpPlate( + edge_points, surface_points, thickness) print("plate_0.val().Volume() = ", plate_0.val().Volume()) -plate_0 = plate_0.translate((0,6*12,0)) +plate_0 = plate_0.translate((0, 6*12, 0)) show_object(plate_0) # EXAMPLE 1 # Plate with 5 sides and 2 bumps, one side is not co-planar with the other sides thickness = 0.1 -edge_points = [[-7.,-7.,0.], [-3.,-10.,3.], [7.,-7.,0.], [7.,7.,0.], [-7.,7.,0.]] -edge_wire = cq.Workplane('XY').polyline([(-7.,-7.), (7.,-7.), (7.,7.), (-7.,7.)]) -#edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) -edge_wire = edge_wire.add(cq.Workplane('YZ').workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) # In CadQuery Sept-2019 it worked with rotate=cq.Vector(0, 45, 0). In CadQuery Dec-2019 rotate=cq.Vector(45, 0, 0) only closes the wire. -surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] -plate_1 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) -#plate_1 = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness) # list of (x,y,z) points instead of wires for edges +edge_points = [[-7., -7., 0.], [-3., -10., 3.], + [7., -7., 0.], [7., 7., 0.], [-7., 7., 0.]] +edge_wire = cq.Workplane("XY").polyline( + [(-7., -7.), (7., -7.), (7., 7.), (-7., 7.)]) +#edge_wire = edge_wire.add(cq.Workplane("YZ").workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) +# In CadQuery Sept-2019 it worked with rotate=cq.Vector(0, 45, 0). In CadQuery Dec-2019 rotate=cq.Vector(45, 0, 0) only closes the wire. +edge_wire = edge_wire.add(cq.Workplane("YZ").workplane().transformed(offset=cq.Vector( + 0, 0, -7), rotate=cq.Vector(45, 0, 0)).spline([(-7., 0.), (3, -3), (7., 0.)])) +surface_points = [[-3., -3., -3.], [3., 3., 3.]] +plate_1 = cq.Workplane("XY").interpPlate(edge_wire, surface_points, thickness) +# plate_1 = cq.Workplane("XY").interpPlate(edge_points, surface_points, thickness) # list of (x,y,z) points instead of wires for edges print("plate_1.val().Volume() = ", plate_1.val().Volume()) show_object(plate_1) # EXAMPLE 2 # Embossed star, need to change optional parameters to obtain nice looking result. -r1=3. -r2=10. -fn=6 +r1 = 3. +r2 = 10. +fn = 6 thickness = 0.1 -edge_points = [[r1*cos(i * pi/fn), r1*sin(i * pi/fn)] if i%2==0 else [r2*cos(i * pi/fn), r2*sin(i * pi/fn)] for i in range(2*fn+1)] -edge_wire = cq.Workplane('XY').polyline(edge_points) -r2=4.5 -surface_points = [[r2*cos(i * pi/fn), r2*sin(i * pi/fn), 1.] for i in range(2*fn)] + [[0.,0.,-2.]] -plate_2 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, degree=3, nbPtsOnCur=15, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=49) -#plate_2 = cq.Workplane('XY').interpPlate(edge_points, surface_points, thickness, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=49) # list of (x,y,z) points instead of wires for edges +edge_points = [[r1*cos(i * pi/fn), r1*sin(i * pi/fn)] if i % 2 == + 0 else [r2*cos(i * pi/fn), r2*sin(i * pi/fn)] for i in range(2*fn+1)] +edge_wire = cq.Workplane("XY").polyline(edge_points) +r2 = 4.5 +surface_points = [[r2*cos(i * pi/fn), r2*sin(i * pi/fn), 1.] + for i in range(2*fn)] + [[0., 0., -2.]] +plate_2 = cq.Workplane("XY").interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, degree=3, nbPtsOnCur=15, + nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=49) +# plate_2 = cq.Workplane("XY").interpPlate(edge_points, surface_points, thickness, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=49) # list of (x,y,z) points instead of wires for edges print("plate_2.val().Volume() = ", plate_2.val().Volume()) -plate_2 = plate_2.translate((0,2*12,0)) +plate_2 = plate_2.translate((0, 2*12, 0)) show_object(plate_2) # EXAMPLE 3 @@ -47,29 +55,36 @@ ca = cos(30. * pi/180.) sa = sin(30. * pi/180.) # EVEN ROWS -pts = [(-3.0, -3.0), (-1.267949, -3.0), (0.464102, -3.0), (2.196152, -3.0), (-3.0, 0.0), (-1.267949, 0.0), (0.464102, 0.0), (2.196152, 0.0), (-2.133974, -1.5), (-0.401923, -1.5), (1.330127, -1.5), (3.062178, -1.5), (-2.133975, 1.5), (-0.401924, 1.5), (1.330127, 1.5), (3.062178, 1.5)] +pts = [(-3.0, -3.0), (-1.267949, -3.0), (0.464102, -3.0), (2.196152, -3.0), (-3.0, 0.0), (-1.267949, 0.0), (0.464102, 0.0), (2.196152, 0.0), + (-2.133974, -1.5), (-0.401923, -1.5), (1.330127, -1.5), (3.062178, -1.5), (-2.133975, 1.5), (-0.401924, 1.5), (1.330127, 1.5), (3.062178, 1.5)] # Spike surface thickness = 0.1 fn = 6 -edge_points = [[r1*cos(i * 2*pi/fn + 30*pi/180), r1*sin(i * 2*pi/fn + 30*pi/180)] for i in range(fn+1)] -surface_points = [[r1/4*cos(i * 2*pi/fn + 30*pi/180), r1/4*sin(i * 2*pi/fn + 30*pi/180), 0.75] for i in range(fn+1)] + [[0,0,2]] -edge_wire = cq.Workplane('XY').polyline(edge_points) -plate_3 = cq.Workplane('XY').pushPoints(pts).interpPlate(edge_wire, surface_points, thickness, combine=False, clean=False, degree=2, nbPtsOnCur=20, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=9) +edge_points = [[r1*cos(i * 2*pi/fn + 30*pi/180), r1 * + sin(i * 2*pi/fn + 30*pi/180)] for i in range(fn+1)] +surface_points = [[r1/4*cos(i * 2*pi/fn + 30*pi/180), r1/4*sin( + i * 2*pi/fn + 30*pi/180), 0.75] for i in range(fn+1)] + [[0, 0, 2]] +edge_wire = cq.Workplane("XY").polyline(edge_points) +plate_3 = cq.Workplane("XY").pushPoints(pts).interpPlate(edge_wire, surface_points, thickness, combine=False, clean=False, degree=2, + nbPtsOnCur=20, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=9) print("plate_3.val().Volume() = ", plate_3.val().Volume()) -plate_3 = plate_3.translate((0,4*11,0)) +plate_3 = plate_3.translate((0, 4*11, 0)) show_object(plate_3) # EXAMPLE 4 # Gyroïd, all edges are splines on different workplanes. thickness = 0.1 -edge_points = [[[3.54, 3.54], [1.77, 0.0], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [-1.77, 0.0], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]]] -plane_list = ['XZ', 'XY', 'YZ', 'XZ', 'YZ', 'XY'] -offset_list = [-3.54, 3.54, 3.54, 3.54, -3.54, -3.54] -edge_wire = cq.Workplane(plane_list[0]).workplane(offset=-offset_list[0]).spline(edge_points[0]) +edge_points = [[[3.54, 3.54], [1.77, 0.0], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], + [[-3.54, -3.54], [-1.77, 0.0], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]]] +plane_list = ["XZ", "XY", "YZ", "XZ", "YZ", "XY"] +offset_list = [-3.54, 3.54, 3.54, 3.54, -3.54, -3.54] +edge_wire = cq.Workplane(plane_list[0]).workplane( + offset=-offset_list[0]).spline(edge_points[0]) for i in range(len(edge_points)-1): - edge_wire = edge_wire.add(cq.Workplane(plane_list[i+1]).workplane(offset=-offset_list[i+1]).spline(edge_points[i+1])) -surface_points = [[0,0,0]] -plate_4 = cq.Workplane('XY').interpPlate(edge_wire, surface_points, thickness) + edge_wire = edge_wire.add(cq.Workplane( + plane_list[i+1]).workplane(offset=-offset_list[i+1]).spline(edge_points[i+1])) +surface_points = [[0, 0, 0]] +plate_4 = cq.Workplane("XY").interpPlate(edge_wire, surface_points, thickness) print("plate_4.val().Volume() = ", plate_4.val().Volume()) -plate_4 = plate_4.translate((0,5*12,0)) +plate_4 = plate_4.translate((0, 5*12, 0)) show_object(plate_4) From 36c8023075ff1aec15c25dc22bd9d1aa0fdfe03b Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 17:26:40 +0100 Subject: [PATCH 60/70] Add files via upload Black formatting --- cadquery/cq.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/cadquery/cq.py b/cadquery/cq.py index 27ca7ebfc..458b54976 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -2880,7 +2880,7 @@ def interpPlate( tolAng=0.01, tolCurv=0.1, maxDeg=8, - maxSegments=9 + maxSegments=9, ): """ Returns a plate surface that is 'thickness' thick, enclosed by 'surf_edge_pts' points, and going through 'surf_pts' points. Using pushpoints directly with interpPlate and combine=True, can be very ressources intensive depending on the complexity of the shape. In this case set combine=False. @@ -2924,7 +2924,21 @@ def interpPlate( # Creates interpolated plate def _makeplate(pnt): - return Solid.interpPlate(surf_edges, surf_pts, thickness, degree, nbPtsOnCur, nbIter, anisotropy, tol2d, tol3d, tolAng, tolCurv, maxDeg, maxSegments).translate(pnt) + return Solid.interpPlate( + surf_edges, + surf_pts, + thickness, + degree, + nbPtsOnCur, + nbIter, + anisotropy, + tol2d, + tol3d, + tolAng, + tolCurv, + maxDeg, + maxSegments, + ).translate(pnt) plates = self.eachpoint(_makeplate, True) From 7812f36b8a11620425359e4639263ae2e64c2e95 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 17:26:59 +0100 Subject: [PATCH 61/70] Add files via upload Black formatting --- cadquery/occ_impl/shapes.py | 75 +++++++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 7 deletions(-) diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index 54ba9e2dc..19eb04fb9 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -1000,7 +1000,22 @@ def innerWires(self): return [w for w in self.Wires() if not w.isSame(outer)] @classmethod - def makeNSidedSurface(cls, edges, points, continuity=GeomAbs_C0, degree=3, nbPtsOnCur=15, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=9): + def makeNSidedSurface( + cls, + edges, + points, + continuity=GeomAbs_C0, + degree=3, + nbPtsOnCur=15, + nbIter=2, + anisotropy=False, + tol2d=0.00001, + tol3d=0.0001, + tolAng=0.01, + tolCurv=0.1, + maxDeg=8, + maxSegments=9, + ): """ Returns a surface enclosed by a closed polygon defined by 'edges' and going through 'points'. :param points @@ -1031,7 +1046,17 @@ def makeNSidedSurface(cls, edges, points, continuity=GeomAbs_C0, degree=3, nbPts :type MaxSegments: Integer >= 2 (?) """ - n_sided = BRepOffsetAPI_MakeFilling(degree, nbPtsOnCur, nbIter, anisotropy, tol2d, tol3d, tolAng, tolCurv, maxDeg, maxSegments) + n_sided = BRepOffsetAPI_MakeFilling( + degree, + nbPtsOnCur, + nbIter, anisotropy, + tol2d, + tol3d, + tolAng, + tolCurv, + maxDeg, + maxSegments, + ) for edg in edges: n_sided.Add(edg, continuity) for pt in points: @@ -1215,7 +1240,21 @@ class Solid(Shape, Mixin3D): """ @classmethod - def interpPlate(cls, surf_edges, surf_pts, thickness, degree=3, nbPtsOnCur=15, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=9): + def interpPlate( + cls, + surf_edges, + surf_pts, thickness, + degree=3, + nbPtsOnCur=15, + nbIter=2, + anisotropy=False, + tol2d=0.00001, + tol3d=0.0001, + tolAng=0.01, + tolCurv=0.1, + maxDeg=8, + maxSegments=9, + ): """ Returns a plate surface that is 'thickness' thick, enclosed by 'surf_edge_pts' points, and going through 'surf_pts' points. @@ -1271,14 +1310,36 @@ def interpPlate(cls, surf_edges, surf_pts, thickness, degree=3, nbPtsOnCur=15, n # MAKE SURFACE continuity = GeomAbs_C0 # Fixed, changing to anything else crashes. - face = Face.makeNSidedSurface(edges, pts_array, continuity, degree, nbPtsOnCur, - nbIter, anisotropy, tol2d, tol3d, tolAng, tolCurv, maxDeg, maxSegments) + face = Face.makeNSidedSurface( + edges, + pts_array, + continuity, + degree, + nbPtsOnCur, + nbIter, + anisotropy, + tol2d, + tol3d, + tolAng, + tolCurv, + maxDeg, + maxSegments, + ) # THICKEN SURFACE if abs(thickness) > 0: # abs() because negative values are allowed to set direction of thickening solid = BRepOffset_MakeOffset() - solid.Initialize(face.wrapped, thickness, 1.e-5, BRepOffset_Skin, False, False, - GeomAbs_Intersection, True) # The last True is important to make solid + + solid.Initialize( + face.wrapped, + thickness, 1.e-5, + BRepOffset_Skin, + False, + False, + GeomAbs_Intersection, + True, + ) # The last True is important to make solid + solid.MakeOffsetShape() return cls(solid.Shape()) else: # Return 2D surface only From 84ae4ea0a39a9710e259773d8287a336aab0184d Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 17:27:18 +0100 Subject: [PATCH 62/70] Add files via upload Black formatting --- examples/Ex101_InterpPlate.py | 167 +++++++++++++++++++++++++--------- 1 file changed, 126 insertions(+), 41 deletions(-) diff --git a/examples/Ex101_InterpPlate.py b/examples/Ex101_InterpPlate.py index ce1ecb947..aa7db4cd1 100644 --- a/examples/Ex101_InterpPlate.py +++ b/examples/Ex101_InterpPlate.py @@ -4,26 +4,35 @@ # TEST_1 # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. thickness = 0 -edge_points = [[0., 0., 0.], [0., 10., 0.], [0., 10., 10.], [0., 0., 10.]] -surface_points = [[5., 5., 5.]] -plate_0 = cq.Workplane("XY").interpPlate( - edge_points, surface_points, thickness) +edge_points = [[0.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 10.0, 10.0], [0.0, 0.0, 10.0]] +surface_points = [[5.0, 5.0, 5.0]] +plate_0 = cq.Workplane("XY").interpPlate(edge_points, surface_points, thickness) print("plate_0.val().Volume() = ", plate_0.val().Volume()) -plate_0 = plate_0.translate((0, 6*12, 0)) +plate_0 = plate_0.translate((0, 6 * 12, 0)) show_object(plate_0) # EXAMPLE 1 # Plate with 5 sides and 2 bumps, one side is not co-planar with the other sides thickness = 0.1 -edge_points = [[-7., -7., 0.], [-3., -10., 3.], - [7., -7., 0.], [7., 7., 0.], [-7., 7., 0.]] +edge_points = [ + [-7.0, -7.0, 0.0], + [-3.0, -10.0, 3.0], + [7.0, -7.0, 0.0], + [7.0, 7.0, 0.0], + [-7.0, 7.0, 0.0], +] edge_wire = cq.Workplane("XY").polyline( - [(-7., -7.), (7., -7.), (7., 7.), (-7., 7.)]) -#edge_wire = edge_wire.add(cq.Workplane("YZ").workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) + [(-7.0, -7.0), (7.0, -7.0), (7.0, 7.0), (-7.0, 7.0)] +) +# edge_wire = edge_wire.add(cq.Workplane("YZ").workplane().transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) # In CadQuery Sept-2019 it worked with rotate=cq.Vector(0, 45, 0). In CadQuery Dec-2019 rotate=cq.Vector(45, 0, 0) only closes the wire. -edge_wire = edge_wire.add(cq.Workplane("YZ").workplane().transformed(offset=cq.Vector( - 0, 0, -7), rotate=cq.Vector(45, 0, 0)).spline([(-7., 0.), (3, -3), (7., 0.)])) -surface_points = [[-3., -3., -3.], [3., 3., 3.]] +edge_wire = edge_wire.add( + cq.Workplane("YZ") + .workplane() + .transformed(offset=cq.Vector(0, 0, -7), rotate=cq.Vector(45, 0, 0)) + .spline([(-7.0, 0.0), (3, -3), (7.0, 0.0)]) +) +surface_points = [[-3.0, -3.0, -3.0], [3.0, 3.0, 3.0]] plate_1 = cq.Workplane("XY").interpPlate(edge_wire, surface_points, thickness) # plate_1 = cq.Workplane("XY").interpPlate(edge_points, surface_points, thickness) # list of (x,y,z) points instead of wires for edges print("plate_1.val().Volume() = ", plate_1.val().Volume()) @@ -31,60 +40,136 @@ # EXAMPLE 2 # Embossed star, need to change optional parameters to obtain nice looking result. -r1 = 3. -r2 = 10. +r1 = 3.0 +r2 = 10.0 fn = 6 thickness = 0.1 -edge_points = [[r1*cos(i * pi/fn), r1*sin(i * pi/fn)] if i % 2 == - 0 else [r2*cos(i * pi/fn), r2*sin(i * pi/fn)] for i in range(2*fn+1)] +edge_points = [ + [r1 * cos(i * pi / fn), r1 * sin(i * pi / fn)] + if i % 2 == 0 + else [r2 * cos(i * pi / fn), r2 * sin(i * pi / fn)] + for i in range(2 * fn + 1) +] edge_wire = cq.Workplane("XY").polyline(edge_points) r2 = 4.5 -surface_points = [[r2*cos(i * pi/fn), r2*sin(i * pi/fn), 1.] - for i in range(2*fn)] + [[0., 0., -2.]] -plate_2 = cq.Workplane("XY").interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, degree=3, nbPtsOnCur=15, - nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=49) +surface_points = [ + [r2 * cos(i * pi / fn), r2 * sin(i * pi / fn), 1.0] for i in range(2 * fn) +] + [[0.0, 0.0, -2.0]] +plate_2 = cq.Workplane("XY").interpPlate( + edge_wire, + surface_points, + thickness, + combine=True, + clean=True, + degree=3, + nbPtsOnCur=15, + nbIter=2, + anisotropy=False, + tol2d=0.00001, + tol3d=0.0001, + tolAng=0.01, + tolCurv=0.1, + maxDeg=8, + maxSegments=49, +) # plate_2 = cq.Workplane("XY").interpPlate(edge_points, surface_points, thickness, combine=True, clean=True, Degree=3, NbPtsOnCur=15, NbIter=2, Anisotropie=False, Tol2d=0.00001, Tol3d=0.0001, TolAng=0.01, TolCurv=0.1, MaxDeg=8, MaxSegments=49) # list of (x,y,z) points instead of wires for edges print("plate_2.val().Volume() = ", plate_2.val().Volume()) -plate_2 = plate_2.translate((0, 2*12, 0)) +plate_2 = plate_2.translate((0, 2 * 12, 0)) show_object(plate_2) # EXAMPLE 3 # Points on hexagonal pattern coordinates, use of pushpoints. -r1 = 1. +r1 = 1.0 N = 3 -ca = cos(30. * pi/180.) -sa = sin(30. * pi/180.) +ca = cos(30.0 * pi / 180.0) +sa = sin(30.0 * pi / 180.0) # EVEN ROWS -pts = [(-3.0, -3.0), (-1.267949, -3.0), (0.464102, -3.0), (2.196152, -3.0), (-3.0, 0.0), (-1.267949, 0.0), (0.464102, 0.0), (2.196152, 0.0), - (-2.133974, -1.5), (-0.401923, -1.5), (1.330127, -1.5), (3.062178, -1.5), (-2.133975, 1.5), (-0.401924, 1.5), (1.330127, 1.5), (3.062178, 1.5)] +pts = [ + (-3.0, -3.0), + (-1.267949, -3.0), + (0.464102, -3.0), + (2.196152, -3.0), + (-3.0, 0.0), + (-1.267949, 0.0), + (0.464102, 0.0), + (2.196152, 0.0), + (-2.133974, -1.5), + (-0.401923, -1.5), + (1.330127, -1.5), + (3.062178, -1.5), + (-2.133975, 1.5), + (-0.401924, 1.5), + (1.330127, 1.5), + (3.062178, 1.5), +] # Spike surface thickness = 0.1 fn = 6 -edge_points = [[r1*cos(i * 2*pi/fn + 30*pi/180), r1 * - sin(i * 2*pi/fn + 30*pi/180)] for i in range(fn+1)] -surface_points = [[r1/4*cos(i * 2*pi/fn + 30*pi/180), r1/4*sin( - i * 2*pi/fn + 30*pi/180), 0.75] for i in range(fn+1)] + [[0, 0, 2]] +edge_points = [ + [ + r1 * cos(i * 2 * pi / fn + 30 * pi / 180), + r1 * sin(i * 2 * pi / fn + 30 * pi / 180), + ] + for i in range(fn + 1) +] +surface_points = [ + [ + r1 / 4 * cos(i * 2 * pi / fn + 30 * pi / 180), + r1 / 4 * sin(i * 2 * pi / fn + 30 * pi / 180), + 0.75, + ] + for i in range(fn + 1) +] + [[0, 0, 2]] edge_wire = cq.Workplane("XY").polyline(edge_points) -plate_3 = cq.Workplane("XY").pushPoints(pts).interpPlate(edge_wire, surface_points, thickness, combine=False, clean=False, degree=2, - nbPtsOnCur=20, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=9) +plate_3 = ( + cq.Workplane("XY") + .pushPoints(pts) + .interpPlate( + edge_wire, + surface_points, + thickness, + combine=False, + clean=False, + degree=2, + nbPtsOnCur=20, + nbIter=2, + anisotropy=False, + tol2d=0.00001, + tol3d=0.0001, + tolAng=0.01, + tolCurv=0.1, + maxDeg=8, + maxSegments=9, + ) +) print("plate_3.val().Volume() = ", plate_3.val().Volume()) -plate_3 = plate_3.translate((0, 4*11, 0)) +plate_3 = plate_3.translate((0, 4 * 11, 0)) show_object(plate_3) # EXAMPLE 4 # Gyroïd, all edges are splines on different workplanes. thickness = 0.1 -edge_points = [[[3.54, 3.54], [1.77, 0.0], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], - [[-3.54, -3.54], [-1.77, 0.0], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]]] +edge_points = [ + [[3.54, 3.54], [1.77, 0.0], [3.54, -3.54]], + [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], + [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], + [[-3.54, -3.54], [-1.77, 0.0], [-3.54, 3.54]], + [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]], + [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]], +] plane_list = ["XZ", "XY", "YZ", "XZ", "YZ", "XY"] offset_list = [-3.54, 3.54, 3.54, 3.54, -3.54, -3.54] -edge_wire = cq.Workplane(plane_list[0]).workplane( - offset=-offset_list[0]).spline(edge_points[0]) -for i in range(len(edge_points)-1): - edge_wire = edge_wire.add(cq.Workplane( - plane_list[i+1]).workplane(offset=-offset_list[i+1]).spline(edge_points[i+1])) +edge_wire = ( + cq.Workplane(plane_list[0]).workplane(offset=-offset_list[0]).spline(edge_points[0]) +) +for i in range(len(edge_points) - 1): + edge_wire = edge_wire.add( + cq.Workplane(plane_list[i + 1]) + .workplane(offset=-offset_list[i + 1]) + .spline(edge_points[i + 1]) + ) surface_points = [[0, 0, 0]] plate_4 = cq.Workplane("XY").interpPlate(edge_wire, surface_points, thickness) print("plate_4.val().Volume() = ", plate_4.val().Volume()) -plate_4 = plate_4.translate((0, 5*12, 0)) +plate_4 = plate_4.translate((0, 5 * 12, 0)) show_object(plate_4) From 52ff375bbea39e10733f5cd1580acc7d72ceaeda Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 17:27:36 +0100 Subject: [PATCH 63/70] Add files via upload Black formatting --- tests/test_cadquery.py | 269 ++++++++++++++++++----------------------- 1 file changed, 116 insertions(+), 153 deletions(-) diff --git a/tests/test_cadquery.py b/tests/test_cadquery.py index 941685777..f32e0ab54 100644 --- a/tests/test_cadquery.py +++ b/tests/test_cadquery.py @@ -2837,72 +2837,47 @@ def test_interpPlate(self): # example from PythonOCC core_geometry_geomplate.py, use of thickness = 0 returns 2D surface. thickness = 0 - edge_points = [[0.,0.,0.], [0.,10.,0.], [0.,10.,10.], [0.,0.,10.]] - surface_points = [[5.,5.,5.]] - plate_0 = Workplane('XY').interpPlate(edge_points, surface_points, thickness) + edge_points = [ + [0.0, 0.0, 0.0], + [0.0, 10.0, 0.0], + [0.0, 10.0, 10.0], + [0.0, 0.0, 10.0], + ] + surface_points = [[5.0, 5.0, 5.0]] + plate_0 = Workplane("XY").interpPlate(edge_points, surface_points, thickness) self.assertTrue(plate_0.val().isValid()) self.assertAlmostEqual(plate_0.val().Area(), 141.218823892, 1) # Plate with 5 sides and 2 bumps, one side is not co-planar with the other sides thickness = 0.1 - edge_points = [[-7.,-7.,0.], [-3.,-10.,3.], [7.,-7.,0.], [7.,7.,0.], [-7.,7.,0.]] - edge_wire = Workplane('XY').polyline([(-7.,-7.), (7.,-7.), (7.,7.), (-7.,7.)]) - #edge_wire = edge_wire.add(Workplane('YZ').workplane().transformed(offset=Vector(0, 0, -7), rotate=Vector(45, 0, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) - edge_wire = edge_wire.add(Workplane('YZ').workplane().transformed(offset=Vector(0, 0, -7), rotate=Vector(45, 0, 0)).spline([(-7.,0.), (3,-3), (7.,0.)])) # In CadQuery Sept-2019 it worked with rotate=Vector(0, 45, 0). In CadQuery Dec-2019 rotate=Vector(45, 0, 0) only closes the wire. - surface_points = [[-3.,-3.,-3.], [3.,3.,3.]] - plate_1 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness) + edge_points = [ + [-7.0, -7.0, 0.0], + [-3.0, -10.0, 3.0], + [7.0, -7.0, 0.0], + [7.0, 7.0, 0.0], + [-7.0, 7.0, 0.0], + ] + edge_wire = Workplane("XY").polyline( + [(-7.0, -7.0), (7.0, -7.0), (7.0, 7.0), (-7.0, 7.0)] + ) + # edge_wire = edge_wire.add(Workplane('YZ').workplane().transformed(offset=Vector(0, 0, -7), rotate=Vector(45, 0, 0)).polyline([(-7.,0.), (3,-3), (7.,0.)])) + # In CadQuery Sept-2019 it worked with rotate=Vector(0, 45, 0). In CadQuery Dec-2019 rotate=Vector(45, 0, 0) only closes the wire. + edge_wire = edge_wire.add( + Workplane("YZ") + .workplane() + .transformed(offset=Vector(0, 0, -7), rotate=Vector(45, 0, 0)) + .spline([(-7.0, 0.0), (3, -3), (7.0, 0.0)]) + ) + surface_points = [[-3.0, -3.0, -3.0], [3.0, 3.0, 3.0]] + plate_1 = Workplane("XY").interpPlate(edge_wire, surface_points, thickness) self.assertTrue(plate_1.val().isValid()) self.assertAlmostEqual(plate_1.val().Volume(), 26.124970206, 3) - # Embossed star, need to change optional parameters to obtain nice looking result. - r1=3. - r2=10. - fn=6 - thickness = 0.1 - edge_points = [[r1*math.cos(i * math.pi/fn), r1*math.sin(i * math.pi/fn)] if i%2==0 else [r2*math.cos(i * math.pi/fn), r2*math.sin(i * math.pi/fn)] for i in range(2*fn+1)] - edge_wire = Workplane('XY').polyline(edge_points) - r2=4.5 - surface_points = [[r2*math.cos(i * math.pi/fn), r2*math.sin(i * math.pi/fn), 1.] for i in range(2*fn)] + [[0.,0.,-2.]] - plate_2 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness, combine=True, clean=True, degree=3, nbPtsOnCur=15, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=49) - self.assertTrue(plate_2.val().isValid()) - self.assertAlmostEqual(plate_2.val().Volume(), 10.956054314, 0) - - # Points on hexagonal pattern coordinates, use of pushpoints. - r1 = 1. - N = 3 - ca = math.cos(30. * math.pi/180.) - sa = math.sin(30. * math.pi/180.) - # EVEN ROWS - pts = [(-3.0, -3.0), (-1.267949, -3.0), (0.464102, -3.0), (2.196152, -3.0), (-3.0, 0.0), (-1.267949, 0.0), (0.464102, 0.0), (2.196152, 0.0), (-2.133974, -1.5), (-0.401923, -1.5), (1.330127, -1.5), (3.062178, -1.5), (-2.133975, 1.5), (-0.401924, 1.5), (1.330127, 1.5), (3.062178, 1.5)] - # Spike surface - thickness = 0.1 - fn = 6 - edge_points = [[r1*math.cos(i * 2*math.pi/fn + 30*math.pi/180), r1*math.sin(i * 2*math.pi/fn + 30*math.pi/180)] for i in range(fn+1)] - surface_points = [[r1/4*math.cos(i * 2*math.pi/fn + 30*math.pi/180), r1/4*math.sin(i * 2*math.pi/fn + 30*math.pi/180), 0.75] for i in range(fn+1)] + [[0,0,2]] - edge_wire = Workplane('XY').polyline(edge_points) - plate_3 = Workplane('XY').pushPoints(pts).interpPlate(edge_wire, surface_points, thickness, combine=False, clean=False, degree=2, nbPtsOnCur=20, nbIter=2, anisotropy=False, tol2d=0.00001, tol3d=0.0001, tolAng=0.01, tolCurv=0.1, maxDeg=8, maxSegments=9) - self.assertTrue(plate_3.val().isValid()) - self.assertAlmostEqual(plate_3.val().Volume(), 0.45893954685189414, 1) - - # Gyroïd, all edges are splines on different workplanes. - thickness = 0.1 - edge_points = [[[3.54, 3.54], [1.77, 0.0], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], [[-3.54, -3.54], [-1.77, 0.0], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]], [[3.54, 3.54], [0.0, 1.77], [-3.54, 3.54]]] - plane_list = ['XZ', 'XY', 'YZ', 'XZ', 'YZ', 'XY'] - offset_list = [-3.54, 3.54, 3.54, 3.54, -3.54, -3.54] - edge_wire = Workplane(plane_list[0]).workplane(offset=-offset_list[0]).spline(edge_points[0]) - for i in range(len(edge_points)-1): - edge_wire = edge_wire.add(Workplane(plane_list[i+1]).workplane(offset=-offset_list[i+1]).spline(edge_points[i+1])) - surface_points = [[0,0,0]] - plate_4 = Workplane('XY').interpPlate(edge_wire, surface_points, thickness) - self.assertTrue(plate_4.val().isValid()) - self.assertAlmostEqual(plate_4.val().Volume(), 7.760559490, 3) - edge_wire = [o.vals()[0] for o in edge_wire.all()] - edge_wire = Wire.assembleEdges(edge_wire) - # Embossed star, need to change optional parameters to obtain nice looking result. r1 = 3.0 r2 = 10.0 fn = 6 + thickness = 0.1 edge_points = [ [r1 * math.cos(i * math.pi / fn), r1 * math.sin(i * math.pi / fn)] if i % 2 == 0 @@ -2910,28 +2885,100 @@ def test_interpPlate(self): for i in range(2 * fn + 1) ] edge_wire = Workplane("XY").polyline(edge_points) - edge_wire = [o.vals()[0] for o in edge_wire.all()] - edge_wire = Wire.assembleEdges(edge_wire) + r2 = 4.5 + surface_points = [ + [r2 * math.cos(i * math.pi / fn), r2 * math.sin(i * math.pi / fn), 1.0] + for i in range(2 * fn) + ] + [[0.0, 0.0, -2.0]] + plate_2 = Workplane("XY").interpPlate( + edge_wire, + surface_points, + thickness, + combine=True, + clean=True, + degree=3, + nbPtsOnCur=15, + nbIter=2, + anisotropy=False, + tol2d=0.00001, + tol3d=0.0001, + tolAng=0.01, + tolCurv=0.1, + maxDeg=8, + maxSegments=49, + ) + self.assertTrue(plate_2.val().isValid()) + self.assertAlmostEqual(plate_2.val().Volume(), 10.956054314, 0) # Points on hexagonal pattern coordinates, use of pushpoints. r1 = 1.0 + N = 3 + ca = math.cos(30.0 * math.pi / 180.0) + sa = math.sin(30.0 * math.pi / 180.0) + # EVEN ROWS + pts = [ + (-3.0, -3.0), + (-1.267949, -3.0), + (0.464102, -3.0), + (2.196152, -3.0), + (-3.0, 0.0), + (-1.267949, 0.0), + (0.464102, 0.0), + (2.196152, 0.0), + (-2.133974, -1.5), + (-0.401923, -1.5), + (1.330127, -1.5), + (3.062178, -1.5), + (-2.133975, 1.5), + (-0.401924, 1.5), + (1.330127, 1.5), + (3.062178, 1.5), + ] + # Spike surface + thickness = 0.1 fn = 6 edge_points = [ - [r1 * math.cos(i * 2 * math.pi / fn), r1 * math.sin(i * 2 * math.pi / fn)] + [ + r1 * math.cos(i * 2 * math.pi / fn + 30 * math.pi / 180), + r1 * math.sin(i * 2 * math.pi / fn + 30 * math.pi / 180), + ] for i in range(fn + 1) ] surface_points = [ - [0.25, 0, 0.75], - [-0.25, 0, 0.75], - [0, 0.25, 0.75], - [0, -0.25, 0.75], - [0, 0, 2], - ] + [ + r1 / 4 * math.cos(i * 2 * math.pi / fn + 30 * math.pi / 180), + r1 / 4 * math.sin(i * 2 * math.pi / fn + 30 * math.pi / 180), + 0.75, + ] + for i in range(fn + 1) + ] + [[0, 0, 2]] edge_wire = Workplane("XY").polyline(edge_points) - edge_wire = [o.vals()[0] for o in edge_wire.all()] - edge_wire = Wire.assembleEdges(edge_wire) + plate_3 = ( + Workplane("XY") + .pushPoints(pts) + .interpPlate( + edge_wire, + surface_points, + thickness, + combine=False, + clean=False, + degree=2, + nbPtsOnCur=20, + nbIter=2, + anisotropy=False, + tol2d=0.00001, + tol3d=0.0001, + tolAng=0.01, + tolCurv=0.1, + maxDeg=8, + maxSegments=9, + ) + ) + self.assertTrue(plate_3.val().isValid()) + self.assertAlmostEqual(plate_3.val().Volume(), 0.45893954685189414, 1) # Gyroïd, all edges are splines on different workplanes. + thickness = 0.1 edge_points = [ [[3.54, 3.54], [1.77, 0.0], [3.54, -3.54]], [[-3.54, -3.54], [0.0, -1.77], [3.54, -3.54]], @@ -2953,91 +3000,7 @@ def test_interpPlate(self): .workplane(offset=-offset_list[i + 1]) .spline(edge_points[i + 1]) ) - edge_wire = [o.vals()[0] for o in edge_wire.all()] - edge_wire = Wire.assembleEdges(edge_wire) - - def testTag(self): - - # test tagging - result = ( - Workplane("XY") - .pushPoints([(-2, 0), (2, 0)]) - .box(1, 1, 1, combine=False) - .tag("2 solids") - .union(Workplane("XY").box(6, 1, 1)) - ) - self.assertEqual(len(result.objects), 1) - result = result._getTagged("2 solids") - self.assertEqual(len(result.objects), 2) - - def testCopyWorkplane(self): - - obj0 = Workplane("XY").box(1, 1, 10).faces(">Z").workplane() - obj1 = Workplane("XY").copyWorkplane(obj0).box(1, 1, 1) - self.assertTupleAlmostEquals((0, 0, 5), obj1.val().Center().toTuple(), 9) - - def testWorkplaneFromTagged(self): - - # create a flat, wide base. Extrude one object 4 units high, another - # object ontop of it 6 units high. Go back to base plane. Extrude an - # object 11 units high. Assert that top face is 11 units high. - result = ( - Workplane("XY") - .box(10, 10, 1, centered=(True, True, False)) - .faces(">Z") - .workplane() - .tag("base") - .center(3, 0) - .rect(2, 2) - .extrude(4) - .faces(">Z") - .workplane() - .circle(1) - .extrude(6) - .workplaneFromTagged("base") - .center(-3, 0) - .circle(1) - .extrude(11) - ) - self.assertTupleAlmostEquals( - result.faces(">Z").val().Center().toTuple(), (-3, 0, 12), 9 - ) - - def testTagSelectors(self): - - result0 = Workplane("XY").box(1, 1, 1).tag("box").sphere(1) - # result is currently a sphere - self.assertEqual(1, result0.faces().size()) - # a box has 8 vertices - self.assertEqual(8, result0.vertices(tag="box").size()) - # 6 faces - self.assertEqual(6, result0.faces(tag="box").size()) - # 12 edges - self.assertEqual(12, result0.edges(tag="box").size()) - # 6 wires - self.assertEqual(6, result0.wires(tag="box").size()) - - # create two solids, tag them, join to one solid - result1 = ( - Workplane("XY") - .pushPoints([(1, 0), (-1, 0)]) - .box(1, 1, 1) - .tag("boxes") - .sphere(1) - ) - self.assertEqual(1, result1.solids().size()) - self.assertEqual(2, result1.solids(tag="boxes").size()) - self.assertEqual(1, result1.shells().size()) - self.assertEqual(2, result1.shells(tag="boxes").size()) - - # create 4 individual objects, tag it, then combine to one compound - result2 = ( - Workplane("XY") - .rect(4, 4) - .vertices() - .box(1, 1, 1, combine=False) - .tag("4 objs") - ) - result2 = result2.newObject([Compound.makeCompound(result2.objects)]) - self.assertEqual(1, result2.compounds().size()) - self.assertEqual(0, result2.compounds(tag="4 objs").size()) + surface_points = [[0, 0, 0]] + plate_4 = Workplane("XY").interpPlate(edge_wire, surface_points, thickness) + self.assertTrue(plate_4.val().isValid()) + self.assertAlmostEqual(plate_4.val().Volume(), 7.760559490, 3) From 2532ed1373f98ef7d52ee59b1e33f528380a63fa Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 18:17:31 +0100 Subject: [PATCH 64/70] Update __init__.py --- tests/__init__.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index 5f18be127..42ac9093f 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -50,13 +50,13 @@ def assertTupleAlmostEquals(self, expected, actual, places): __all__ = [ - 'TestCadObjects', - 'TestCadQuery', - 'TestCQGI', - 'TestCQSelectors', - 'TestCQSelectors', - 'TestExporters', - 'TestImporters', - 'TestJupyter', - 'TestWorkplanes', + "TestCadObjects", + "TestCadQuery", + "TestCQGI", + "TestCQSelectors", + "TestCQSelectors", + "TestExporters", + "TestImporters", + "TestJupyter", + "TestWorkplanes", ] From 05ae2ce06d65493d28753babf7f9e83aaf2859b6 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 18:18:16 +0100 Subject: [PATCH 65/70] Black formatting --- cadquery/cq.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cadquery/cq.py b/cadquery/cq.py index 458b54976..700552458 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -2862,8 +2862,6 @@ def _sweep( return Compound.makeCompound(toFuse) - - def interpPlate( self, surf_edges, @@ -2947,6 +2945,7 @@ def _makeplate(pnt): return plates else: return self.union(plates, clean=clean) + def box( self, length, From 2f7169af0f94155ea3ba778004256a292a37a3fd Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 18:18:40 +0100 Subject: [PATCH 66/70] Black formatting --- cadquery/occ_impl/shapes.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cadquery/occ_impl/shapes.py b/cadquery/occ_impl/shapes.py index 19eb04fb9..0119f1eac 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -1049,7 +1049,8 @@ def makeNSidedSurface( n_sided = BRepOffsetAPI_MakeFilling( degree, nbPtsOnCur, - nbIter, anisotropy, + nbIter, + anisotropy, tol2d, tol3d, tolAng, @@ -1327,19 +1328,20 @@ def interpPlate( ) # THICKEN SURFACE - if abs(thickness) > 0: # abs() because negative values are allowed to set direction of thickening + if ( + abs(thickness) > 0 + ): # abs() because negative values are allowed to set direction of thickening solid = BRepOffset_MakeOffset() - solid.Initialize( face.wrapped, - thickness, 1.e-5, + thickness, + 1.e-5, BRepOffset_Skin, False, False, GeomAbs_Intersection, True, ) # The last True is important to make solid - solid.MakeOffsetShape() return cls(solid.Shape()) else: # Return 2D surface only From 3f35402dd913a724d3a325e6f078ceadef082068 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 18:19:00 +0100 Subject: [PATCH 67/70] Black formatting From 19140498f83d5883d4106d016e9ac468b77fb87a Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 18:19:18 +0100 Subject: [PATCH 68/70] Black formatting From c17bc3576029cf6869003de053b9359cc8c1f209 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 19:21:56 +0100 Subject: [PATCH 69/70] Black formatting --- cadquery/cq.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cadquery/cq.py b/cadquery/cq.py index 700552458..4fee00e61 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -2867,7 +2867,7 @@ def interpPlate( surf_edges, surf_pts=[], thickness=0, - combine=True, + combine=False, clean=True, degree=3, nbPtsOnCur=15, @@ -2945,7 +2945,7 @@ def _makeplate(pnt): return plates else: return self.union(plates, clean=clean) - + def box( self, length, From 970855472e206ee33c2e6673d02d0b6543b9b4f9 Mon Sep 17 00:00:00 2001 From: Bruno Agostini Date: Wed, 12 Feb 2020 19:22:18 +0100 Subject: [PATCH 70/70] Black formatting --- 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 0119f1eac..72c4e7b9c 100644 --- a/cadquery/occ_impl/shapes.py +++ b/cadquery/occ_impl/shapes.py @@ -1244,7 +1244,8 @@ class Solid(Shape, Mixin3D): def interpPlate( cls, surf_edges, - surf_pts, thickness, + surf_pts, + thickness, degree=3, nbPtsOnCur=15, nbIter=2, @@ -1335,7 +1336,7 @@ def interpPlate( solid.Initialize( face.wrapped, thickness, - 1.e-5, + 1.0e-5, BRepOffset_Skin, False, False,