From ae65dc3579a3a6c2f6d4162af34673f59bcd09bf Mon Sep 17 00:00:00 2001 From: Michael Greminger Date: Sat, 1 Jun 2019 18:54:56 -0500 Subject: [PATCH] Differentiated projected plane origin and global origin for creating workplanes Added ProjectedGlobalOrigin as centerOption for worplane constructor to differentiate between projecting the global origin and the current plane origin when creating new workplanes. Also updated projectToPlane method of Vector to only accept plane objects and it no longer does in inplace modification. Updated test converage for ProjectedOrigin and ProjectedGlobalOrigin options. --- cadquery/cq.py | 18 +++++++++++++----- cadquery/occ_impl/geom.py | 25 ++++++------------------- tests/TestCadObjects.py | 13 ------------- tests/TestCadQuery.py | 19 ++++++++++++++++--- 4 files changed, 35 insertions(+), 40 deletions(-) diff --git a/cadquery/cq.py b/cadquery/cq.py index 30ee9ae1..0cf41694 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -285,7 +285,11 @@ class CQ(object): face/faces, if a face/faces was selected. If a vertex was selected, the origin will be at the vertex, and located on the face. The centerOption paramter sets how the center is defined. - Options are 'CenterOfMass', 'CenterOfBoundBox', or 'ProjectedOrigin'. + Options are 'CenterOfMass', 'CenterOfBoundBox', 'ProjectedOrigin', or + 'ProjectedGlobalOrigin'. 'CenterOfMass' and 'CenterOfBoundBox' are + in relation to the selected face/faces. 'ProjectedOrigin' uses the + current origin that may have been updated by calls to transformed or + center. 'ProjectedGlobalOrigin' projects the global (0,0,0) origin. * The Z direction will be normal to the plane of the face,computed at the center point. * The X direction will be parallel to the x-y plane. If the workplane is parallel to @@ -344,10 +348,12 @@ class CQ(object): if not all(_isCoPlanar(self.objects[0], f) for f in self.objects[1:]): raise ValueError("Selected faces must be co-planar.") - if centerOption == 'CenterOfMass' or centerOption == 'ProjectedOrigin': + if centerOption in {'CenterOfMass', 'ProjectedOrigin', 'ProjectedGlobalOrigin'}: center = Shape.CombinedCenter(self.objects) elif centerOption == 'CenterOfBoundBox': center = Shape.CombinedCenterOfBoundBox(self.objects) + else: + raise ValueError('Undefined value passed to centerOption') normal = self.objects[0].normalAt() xDir = _computeXdir(normal) @@ -356,7 +362,7 @@ class CQ(object): obj = self.objects[0] if isinstance(obj, Face): - if centerOption == 'CenterOfMass' or centerOption == 'ProjectedOrigin': + if centerOption in {'CenterOfMass', 'ProjectedOrigin', 'ProjectedGlobalOrigin'}: center = obj.Center() elif centerOption == 'CenterOfBoundBox': center = obj.CenterOfBoundBox() @@ -364,7 +370,7 @@ class CQ(object): xDir = _computeXdir(normal) else: if hasattr(obj, 'Center'): - if centerOption == 'CenterOfMass' or centerOption == 'ProjectedOrigin': + if centerOption in {'CenterOfMass', 'ProjectedOrigin', 'ProjectedGlobalOrigin'}: center = obj.Center() elif centerOption == 'CenterOfBoundBox': center = obj.CenterOfBoundBox() @@ -376,7 +382,9 @@ class CQ(object): # update center to projected origin if desired if centerOption == 'ProjectedOrigin': - center = Vector(0, 0, 0).projectToPlane(center, normal) + center = self.plane.origin.projectToPlane(Plane(center, xDir, normal)) + elif centerOption == 'ProjectedGlobalOrigin': + center = Vector(0,0,0).projectToPlane(Plane(center, xDir, normal)) # invert if requested if invert: diff --git a/cadquery/occ_impl/geom.py b/cadquery/occ_impl/geom.py index e8d54afc..804fdf06 100644 --- a/cadquery/occ_impl/geom.py +++ b/cadquery/occ_impl/geom.py @@ -143,31 +143,18 @@ class Vector(object): raise NotImplementedError( "Have not needed this yet, but FreeCAD supports it!") - def projectToPlane(self, *args): + def projectToPlane(self, plane): """ Vector is projected onto the plane provided as input. - :param args: Plane object or base and normal vectors that define the plane + :param args: Plane object - This method modifies the vector in place and returns the new vector. + Returns the projected vector. """ - if len(args) == 2: - base = args[0] - normal = args[1] - elif len(args) == 1: - base = args[0].origin - normal = args[0].zDir - else: - raise TypeError("Expected 1 or 2 arguments consisting of 1 Plane object \ -or a base Vector and a normal Vector object.") + base = plane.origin + normal = plane.zDir - result = self-normal*(((self-base).dot(normal))/normal.Length**2) - - self.x = result.x - self.y = result.y - self.z = result.z - - return self + return self-normal*(((self-base).dot(normal))/normal.Length**2) def __neg__(self): return self * -1 diff --git a/tests/TestCadObjects.py b/tests/TestCadObjects.py index 09efaa9b..53c432fe 100644 --- a/tests/TestCadObjects.py +++ b/tests/TestCadObjects.py @@ -161,24 +161,11 @@ class TestCadObjects(BaseTest): base = Vector(5, 7, 9) x_dir = Vector(1, 0, 0) - # test passing plane defined by base and normal - point = Vector(10, 11, 12).projectToPlane(base, normal) - self.assertTupleAlmostEquals(point.toTuple(), (59/7, 55/7, 51/7), - decimal_places) - - point = Vector(10, 11, 12).projectToPlane(base, normal.normalized()) - self.assertTupleAlmostEquals(point.toTuple(), (59/7, 55/7, 51/7), - decimal_places) - # test passing Plane object point = Vector(10, 11, 12).projectToPlane(Plane(base, x_dir, normal)) self.assertTupleAlmostEquals(point.toTuple(), (59/7, 55/7, 51/7), decimal_places) - # test wrong number of input arguments - with self.assertRaises(TypeError): - Vector(10,11.12).projectToPlane(1,1,1) - def testMatrixCreationAndAccess(self): def matrix_vals(m): return [[m[r,c] for c in range(4)] for r in range(4)] diff --git a/tests/TestCadQuery.py b/tests/TestCadQuery.py index 9efc2402..681a938b 100644 --- a/tests/TestCadQuery.py +++ b/tests/TestCadQuery.py @@ -1924,6 +1924,22 @@ class TestCadQuery(BaseTest): .plane.origin.toTuple() self.assertTupleAlmostEquals(origin, (45.0, 30.0, 10.0), decimal_places) + # 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() + self.assertTupleAlmostEquals(origin, (30.0, 0.0, 10.0), decimal_places) + + origin = r.faces(">Z").workplane(centerOption='ProjectedGlobalOrigin') \ + .plane.origin.toTuple() + self.assertTupleAlmostEquals(origin, (0.0, 0.0, 10.0), decimal_places) + + with self.assertRaises(ValueError): + origin = r.faces(">Z").workplane(centerOption='undefined') + + # make sure projection works in all directions r = Workplane("YZ").polyline(pts).close().extrude(10.0) origin = r.faces(">X").workplane(centerOption='ProjectedOrigin') \ @@ -1951,6 +1967,3 @@ class TestCadQuery(BaseTest): origin = r.faces("