From 2ce0041354fa1217b0bdbcc3a2dbc10b060c2976 Mon Sep 17 00:00:00 2001 From: Jeremy Wright Date: Tue, 7 Apr 2015 14:19:45 -0400 Subject: [PATCH] Fixed the Solid.makeSphere implementation and added CQ.sphere that mirrors the CQ.box high-level functionality. --- cadquery/CQ.py | 62 +++++++++++++++++++++++++++++++++ cadquery/freecad_impl/shapes.py | 4 +-- changes.md | 2 ++ tests/TestCadQuery.py | 24 +++++++++++++ 4 files changed, 90 insertions(+), 2 deletions(-) diff --git a/cadquery/CQ.py b/cadquery/CQ.py index e98854b..a898e32 100644 --- a/cadquery/CQ.py +++ b/cadquery/CQ.py @@ -2271,3 +2271,65 @@ class Workplane(CQ): #combine everything return self.union(boxes) + def sphere(self, radius, direct=(0, 0, 1), angle1=-90, angle2=90, angle3=360, centered=(True, True, True), combine=True): + """ + Returns a 3D sphere with the specified radius for each point on the stack + + :param radius: The radius of the sphere + :type radius: float > 0 + :param direct: The direction axis for the creation of the sphere + :type direct: A three-tuple + :param angle1: The first angle to sweep the sphere arc through + :type angle1: float > 0 + :param angle2: The second angle to sweep the sphere arc through + :type angle2: float > 0 + :param angle3: The third angle to sweep the sphere arc through + :type angle3: float > 0 + :param centered: A three-tuple of booleans that determines whether the sphere is centered on each axis origin + :param combine: Whether the results should be combined with other solids on the stack (and each other) + :type combine: true to combine shapes, false otherwise + :return: A sphere object for each point on the stack + + Centered is a tuple that describes whether the sphere should be centered on the x,y, and z axes. If true, + the sphere is centered on the respective axis relative to the workplane origin, if false, the workplane center + will represent the lower bound of the resulting sphere + + One sphere is created for each item on the current stack. If no items are on the stack, one box 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 spheres produced fused onto it + otherwise, the result is the combination of all the produced boxes + + If combine is false, the result will be a list of the spheres produced + """ + + # Convert the direction tuple to a vector, if needed + if isinstance(direct, tuple): + direct = Vector(direct) + + def _makesphere(pnt): + """ + Inner function that is used to create a sphere for each point/object on the workplane + :param pnt: The center point for the sphere + :return: A CQ Solid object representing a sphere + """ + (xp, yp, zp) = pnt.toTuple() + + if centered[0]: + xp = xp - radius + if centered[1]: + yp = yp - radius + if centered[2]: + zp = zp - radius + + 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) + + # If we don't need to combine everything, just return the created spheres + if not combine: + return spheres + else: + return self.union(spheres) diff --git a/cadquery/freecad_impl/shapes.py b/cadquery/freecad_impl/shapes.py index be88361..b58a67b 100644 --- a/cadquery/freecad_impl/shapes.py +++ b/cadquery/freecad_impl/shapes.py @@ -638,13 +638,13 @@ class Solid(Shape): FreeCADPart.makeWedge(xmin, ymin, zmin, z2min, x2min, xmax, ymax, zmax, z2max, x2max, pnt, dir)) @classmethod - def makeSphere(cls, radius, pnt=None, angleDegrees1=None, angleDegrees2=None, angleDegrees3=None): + def makeSphere(cls, radius, pnt=None, dir=None, angleDegrees1=None, angleDegrees2=None, angleDegrees3=None): """ 'makeSphere(radius,[pnt, dir, angle1,angle2,angle3]) -- Make a sphere with a giv en radius\nBy default pnt=Vector(0,0,0), dir=Vector(0,0,1), angle1=0, angle2=90 and angle3=360' """ - return Solid(FreeCADPart.makeSphere(radius, pnt, angleDegrees1, angleDegrees2, angleDegrees3)) + return Shape.cast(FreeCADPart.makeSphere(radius, pnt.wrapped, dir.wrapped, angleDegrees1, angleDegrees2, angleDegrees3)) @classmethod def extrudeLinearWithRotation(cls, outerWire, innerWires, vecCenter, vecNormal, angleDegrees): diff --git a/changes.md b/changes.md index 030666f..73d73e0 100644 --- a/changes.md +++ b/changes.md @@ -34,3 +34,5 @@ v0.1.8 v0.1.9 (Unreleased) ----- * Added license badge in changes.md + * Fixed Solid.makeSphere implementation + * Added CQ.sphere operation that mirrors CQ.box diff --git a/tests/TestCadQuery.py b/tests/TestCadQuery.py index 46eb308..822bb3d 100644 --- a/tests/TestCadQuery.py +++ b/tests/TestCadQuery.py @@ -750,6 +750,30 @@ class TestCadQuery(BaseTest): self.assertEquals(1,s.solids().size()) # we should have one big solid self.assertEquals(26,s.faces().size()) # should have 26 faces. 6 for the box, and 4x5 for the smaller cubes + def testSphereDefaults(self): + s = Workplane("XY").sphere(10) + self.saveModel(s) + self.assertEquals(1, s.solids().size()) + self.assertEquals(1, s.faces().size()) + + def testSphereCustom(self): + s = Workplane("XY").sphere(10, angle1=0, angle2=90, angle3=360, centered=(False, False, False)) + self.saveModel(s) + self.assertEquals(1, s.solids().size()) + self.assertEquals(2, s.faces().size()) + + def testSpherePointList(self): + s = Workplane("XY").rect(4.0, 4.0, forConstruction=True).vertices().sphere(0.25, combine=False) + self.saveModel(s) + self.assertEquals(4, s.solids().size()) + self.assertEquals(4, s.faces().size()) + + def testSphereCombine(self): + s = Workplane("XY").rect(4.0, 4.0, forConstruction=True).vertices().sphere(0.25, combine=True) + self.saveModel(s) + self.assertEquals(1, s.solids().size()) + self.assertEquals(4, s.faces().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)