From feb559bedad7268a96d834ccb258b5e639947147 Mon Sep 17 00:00:00 2001 From: Youbao Zhang Date: Wed, 30 Dec 2015 14:08:49 +0000 Subject: [PATCH 1/5] 1. Add a mirror function into CQ class(wrap) and Shape class(implement); 2. To get precise BoundBox, add tessellate(0.000001) in the BoundBox function implement...... --- cadquery/CQ.py | 13 +++++++++++++ cadquery/freecad_impl/shapes.py | 14 ++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/cadquery/CQ.py b/cadquery/CQ.py index 8d21173..7e87fb2 100644 --- a/cadquery/CQ.py +++ b/cadquery/CQ.py @@ -742,6 +742,19 @@ def rotate(self, axisStartPoint, axisEndPoint, angleDegrees): return self.newObject([o.rotate(axisStartPoint, axisEndPoint, angleDegrees) for o in self.objects]) + def mirror(self, mirrorPlane="XY", basePointVector=(0, 0, 0)): + """ + Mirror a single CQ object. This operation is the same as in the FreeCAD PartWB's mirroring + + :param mirrorPlane: the plane to mirror about + :type mirrorPlane: string, one of "XY", "YX", "XZ", "ZX", "YZ", "ZY" the planes + :param basePointVector: the base point to mirror about + :type basePointVector: tuple + """ + newS = self.newObject([self.objects[0].mirror(mirrorPlane, basePointVector)]) + return newS.first() + + def translate(self, vec): """ Returns a copy of all of the items on the stack moved by the specified translation vector. diff --git a/cadquery/freecad_impl/shapes.py b/cadquery/freecad_impl/shapes.py index 27b97a6..3134333 100644 --- a/cadquery/freecad_impl/shapes.py +++ b/cadquery/freecad_impl/shapes.py @@ -182,8 +182,22 @@ def isValid(self): return self.wrapped.isValid() def BoundingBox(self): + self.wrapped.tessellate(0.000001) return BoundBox(self.wrapped.BoundBox) + def mirror(self, mirrorPlane="XY", basePointVector=(0, 0, 0)): + if mirrorPlane == "XY" or mirrorPlane== "YX": + mirrorPlaneNormalVector = FreeCAD.Base.Vector(0, 0, 1) + elif mirrorPlane == "XZ" or mirrorPlane == "ZX": + mirrorPlaneNormalVector = FreeCAD.Base.Vector(0, 1, 0) + elif mirrorPlane == "YZ" or mirrorPlane == "ZY": + mirrorPlaneNormalVector = FreeCAD.Base.Vector(1, 0, 0) + + if type(basePointVector) == tuple: + basePointVector = Vector(basePointVector) + + return Shape.cast(self.wrapped.mirror(basePointVector.wrapped, mirrorPlaneNormalVector)) + def Center(self): # A Part.Shape object doesn't have the CenterOfMass function, but it's wrapped Solid(s) does if isinstance(self.wrapped, FreeCADPart.Shape): From 3186b68b058069a7f7a667fcd11c565a3568b284 Mon Sep 17 00:00:00 2001 From: Youbao Zhang Date: Wed, 30 Dec 2015 14:08:49 +0000 Subject: [PATCH 2/5] 1. Add a mirror function into CQ class(wrap) and Shape class(implement); 2. To get precise BoundBox, add tessellate(0.000001) in the BoundBox function implement...... --- cadquery/cq.py | 13 +++++++++++++ cadquery/freecad_impl/shapes.py | 14 ++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/cadquery/cq.py b/cadquery/cq.py index 59a1180..7293813 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -742,6 +742,19 @@ def rotate(self, axisStartPoint, axisEndPoint, angleDegrees): return self.newObject([o.rotate(axisStartPoint, axisEndPoint, angleDegrees) for o in self.objects]) + def mirror(self, mirrorPlane="XY", basePointVector=(0, 0, 0)): + """ + Mirror a single CQ object. This operation is the same as in the FreeCAD PartWB's mirroring + + :param mirrorPlane: the plane to mirror about + :type mirrorPlane: string, one of "XY", "YX", "XZ", "ZX", "YZ", "ZY" the planes + :param basePointVector: the base point to mirror about + :type basePointVector: tuple + """ + newS = self.newObject([self.objects[0].mirror(mirrorPlane, basePointVector)]) + return newS.first() + + def translate(self, vec): """ Returns a copy of all of the items on the stack moved by the specified translation vector. diff --git a/cadquery/freecad_impl/shapes.py b/cadquery/freecad_impl/shapes.py index 76af1c1..12d410b 100644 --- a/cadquery/freecad_impl/shapes.py +++ b/cadquery/freecad_impl/shapes.py @@ -185,8 +185,22 @@ def isValid(self): return self.wrapped.isValid() def BoundingBox(self): + self.wrapped.tessellate(0.000001) return BoundBox(self.wrapped.BoundBox) + def mirror(self, mirrorPlane="XY", basePointVector=(0, 0, 0)): + if mirrorPlane == "XY" or mirrorPlane== "YX": + mirrorPlaneNormalVector = FreeCAD.Base.Vector(0, 0, 1) + elif mirrorPlane == "XZ" or mirrorPlane == "ZX": + mirrorPlaneNormalVector = FreeCAD.Base.Vector(0, 1, 0) + elif mirrorPlane == "YZ" or mirrorPlane == "ZY": + mirrorPlaneNormalVector = FreeCAD.Base.Vector(1, 0, 0) + + if type(basePointVector) == tuple: + basePointVector = Vector(basePointVector) + + return Shape.cast(self.wrapped.mirror(basePointVector.wrapped, mirrorPlaneNormalVector)) + def Center(self): # A Part.Shape object doesn't have the CenterOfMass function, but it's wrapped Solid(s) does if isinstance(self.wrapped, FreeCADPart.Shape): From 7225d84905403b1c5926dcba02c709ae133356ac Mon Sep 17 00:00:00 2001 From: Simon Huskier Date: Wed, 20 Jan 2016 16:56:48 +0800 Subject: [PATCH 3/5] 1. Add a mirroring example into doc/example.rst; 2. Add a tolerance parameter into BoundingBox function, and default it as 0.1, otherwise, it's very slow when compile the docs with sphinx-build, don't know why; 3. Add a testBoundingBox function into TestCadQuery.py file... --- cadquery/freecad_impl/shapes.py | 18 +++++----- doc/examples.rst | 58 +++++++++++++++++++++++++++++++++ tests/TestCadQuery.py | 37 +++++++++++++++++++++ 3 files changed, 104 insertions(+), 9 deletions(-) diff --git a/cadquery/freecad_impl/shapes.py b/cadquery/freecad_impl/shapes.py index 12d410b..16814ba 100644 --- a/cadquery/freecad_impl/shapes.py +++ b/cadquery/freecad_impl/shapes.py @@ -184,22 +184,22 @@ def isEqual(self, other): def isValid(self): return self.wrapped.isValid() - def BoundingBox(self): - self.wrapped.tessellate(0.000001) + def BoundingBox(self, tolerance=0.1): + self.wrapped.tessellate(tolerance) return BoundBox(self.wrapped.BoundBox) def mirror(self, mirrorPlane="XY", basePointVector=(0, 0, 0)): - if mirrorPlane == "XY" or mirrorPlane== "YX": - mirrorPlaneNormalVector = FreeCAD.Base.Vector(0, 0, 1) - elif mirrorPlane == "XZ" or mirrorPlane == "ZX": - mirrorPlaneNormalVector = FreeCAD.Base.Vector(0, 1, 0) - elif mirrorPlane == "YZ" or mirrorPlane == "ZY": - mirrorPlaneNormalVector = FreeCAD.Base.Vector(1, 0, 0) + if mirrorPlane == "XY" or mirrorPlane== "YX": + mirrorPlaneNormalVector = FreeCAD.Base.Vector(0, 0, 1) + elif mirrorPlane == "XZ" or mirrorPlane == "ZX": + mirrorPlaneNormalVector = FreeCAD.Base.Vector(0, 1, 0) + elif mirrorPlane == "YZ" or mirrorPlane == "ZY": + mirrorPlaneNormalVector = FreeCAD.Base.Vector(1, 0, 0) if type(basePointVector) == tuple: basePointVector = Vector(basePointVector) - return Shape.cast(self.wrapped.mirror(basePointVector.wrapped, mirrorPlaneNormalVector)) + return Shape.cast(self.wrapped.mirror(basePointVector.wrapped, mirrorPlaneNormalVector)) def Center(self): # A Part.Shape object doesn't have the CenterOfMass function, but it's wrapped Solid(s) does diff --git a/doc/examples.rst b/doc/examples.rst index 0705da5..06c79aa 100644 --- a/doc/examples.rst +++ b/doc/examples.rst @@ -312,6 +312,64 @@ introduce horizontal and vertical lines, which make for slightly easier coding. * :py:meth:`Workplane` * :py:meth:`Workplane.extrude` +Mirroring 3D Objects +----------------------------- + +.. cq_plot:: + + result0 = (cadquery.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) + + result = result.rotate((0, 0, 0),(1, 0, 0), 90) + + result = result.translate(result.val().BoundingBox().center.multiply(-1)) + + mirXY_neg = result.mirror(mirrorPlane="XY", basePointVector=(0, 0, -30)) + mirXY_pos = result.mirror(mirrorPlane="XY", basePointVector=(0, 0, 30)) + mirZY_neg = result.mirror(mirrorPlane="ZY", basePointVector=(-30,0,0)) + mirZY_pos = result.mirror(mirrorPlane="ZY", basePointVector=(30,0,0)) + + result = result.union(mirXY_neg).union(mirXY_pos).union(mirZY_neg).union(mirZY_pos) + + build_object(result) + +.. topic:: Api References + + .. hlist:: + :columns: 2 + + * :py:meth:`Workplane.moveTo` + * :py:meth:`Workplane.lineTo` + * :py:meth:`Workplane.threePointArc` + * :py:meth:`Workplane.extrude` + * :py:meth:`Workplane.mirror` + * :py:meth:`Workplane.union` + * :py:meth:`CQ.rotate` Creating Workplanes on Faces ----------------------------- diff --git a/tests/TestCadQuery.py b/tests/TestCadQuery.py index 01e7753..f8b9b2a 100644 --- a/tests/TestCadQuery.py +++ b/tests/TestCadQuery.py @@ -550,6 +550,43 @@ def testCut(self): self.assertEqual(10,currentS.faces().size()) + 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()) + result = result0.extrude(100) + bb_center = result.val().BoundingBox().center + self.saveModel(result) + self.assertAlmostEqual(14.0, bb_center.x, 3) + self.assertAlmostEqual(7.5, bb_center.y, 3) + self.assertAlmostEqual(50.0, bb_center.z, 3) + def testCutThroughAll(self): """ Tests a model that uses more than one workplane From 9d9b6d310e67dc50616cb81f7ede2500fd4cddcf Mon Sep 17 00:00:00 2001 From: Simon Huskier Date: Wed, 20 Jan 2016 17:01:14 +0800 Subject: [PATCH 4/5] Small format fix for function BoundingBox in shapes.py...... --- cadquery/freecad_impl/shapes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cadquery/freecad_impl/shapes.py b/cadquery/freecad_impl/shapes.py index 16814ba..a78443c 100644 --- a/cadquery/freecad_impl/shapes.py +++ b/cadquery/freecad_impl/shapes.py @@ -185,7 +185,7 @@ def isValid(self): return self.wrapped.isValid() def BoundingBox(self, tolerance=0.1): - self.wrapped.tessellate(tolerance) + self.wrapped.tessellate(tolerance) return BoundBox(self.wrapped.BoundBox) def mirror(self, mirrorPlane="XY", basePointVector=(0, 0, 0)): From 8c8432410bbe611e2321657a325c121683f67869 Mon Sep 17 00:00:00 2001 From: Simon Huskier Date: Wed, 20 Jan 2016 17:08:32 +0800 Subject: [PATCH 5/5] Small format fix for function mirror in Shapes.py file...... --- cadquery/freecad_impl/shapes.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cadquery/freecad_impl/shapes.py b/cadquery/freecad_impl/shapes.py index a78443c..0ceae22 100644 --- a/cadquery/freecad_impl/shapes.py +++ b/cadquery/freecad_impl/shapes.py @@ -189,17 +189,17 @@ def BoundingBox(self, tolerance=0.1): return BoundBox(self.wrapped.BoundBox) def mirror(self, mirrorPlane="XY", basePointVector=(0, 0, 0)): - if mirrorPlane == "XY" or mirrorPlane== "YX": - mirrorPlaneNormalVector = FreeCAD.Base.Vector(0, 0, 1) - elif mirrorPlane == "XZ" or mirrorPlane == "ZX": - mirrorPlaneNormalVector = FreeCAD.Base.Vector(0, 1, 0) - elif mirrorPlane == "YZ" or mirrorPlane == "ZY": - mirrorPlaneNormalVector = FreeCAD.Base.Vector(1, 0, 0) + if mirrorPlane == "XY" or mirrorPlane== "YX": + mirrorPlaneNormalVector = FreeCAD.Base.Vector(0, 0, 1) + elif mirrorPlane == "XZ" or mirrorPlane == "ZX": + mirrorPlaneNormalVector = FreeCAD.Base.Vector(0, 1, 0) + elif mirrorPlane == "YZ" or mirrorPlane == "ZY": + mirrorPlaneNormalVector = FreeCAD.Base.Vector(1, 0, 0) if type(basePointVector) == tuple: basePointVector = Vector(basePointVector) - return Shape.cast(self.wrapped.mirror(basePointVector.wrapped, mirrorPlaneNormalVector)) + return Shape.cast(self.wrapped.mirror(basePointVector.wrapped, mirrorPlaneNormalVector)) def Center(self): # A Part.Shape object doesn't have the CenterOfMass function, but it's wrapped Solid(s) does