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.
This commit is contained in:
Michael Greminger
2019-06-01 18:54:56 -05:00
parent 37611308ff
commit ae65dc3579
4 changed files with 35 additions and 40 deletions

View File

@ -285,7 +285,11 @@ class CQ(object):
face/faces, if a face/faces was selected. If a vertex was face/faces, if a face/faces was selected. If a vertex was
selected, the origin will be at the vertex, and located selected, the origin will be at the vertex, and located
on the face. The centerOption paramter sets how the center is defined. 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 * The Z direction will be normal to the plane of the face,computed
at the center point. at the center point.
* The X direction will be parallel to the x-y plane. If the workplane is parallel to * 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:]): if not all(_isCoPlanar(self.objects[0], f) for f in self.objects[1:]):
raise ValueError("Selected faces must be co-planar.") 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) center = Shape.CombinedCenter(self.objects)
elif centerOption == 'CenterOfBoundBox': elif centerOption == 'CenterOfBoundBox':
center = Shape.CombinedCenterOfBoundBox(self.objects) center = Shape.CombinedCenterOfBoundBox(self.objects)
else:
raise ValueError('Undefined value passed to centerOption')
normal = self.objects[0].normalAt() normal = self.objects[0].normalAt()
xDir = _computeXdir(normal) xDir = _computeXdir(normal)
@ -356,7 +362,7 @@ class CQ(object):
obj = self.objects[0] obj = self.objects[0]
if isinstance(obj, Face): if isinstance(obj, Face):
if centerOption == 'CenterOfMass' or centerOption == 'ProjectedOrigin': if centerOption in {'CenterOfMass', 'ProjectedOrigin', 'ProjectedGlobalOrigin'}:
center = obj.Center() center = obj.Center()
elif centerOption == 'CenterOfBoundBox': elif centerOption == 'CenterOfBoundBox':
center = obj.CenterOfBoundBox() center = obj.CenterOfBoundBox()
@ -364,7 +370,7 @@ class CQ(object):
xDir = _computeXdir(normal) xDir = _computeXdir(normal)
else: else:
if hasattr(obj, 'Center'): if hasattr(obj, 'Center'):
if centerOption == 'CenterOfMass' or centerOption == 'ProjectedOrigin': if centerOption in {'CenterOfMass', 'ProjectedOrigin', 'ProjectedGlobalOrigin'}:
center = obj.Center() center = obj.Center()
elif centerOption == 'CenterOfBoundBox': elif centerOption == 'CenterOfBoundBox':
center = obj.CenterOfBoundBox() center = obj.CenterOfBoundBox()
@ -376,7 +382,9 @@ class CQ(object):
# update center to projected origin if desired # update center to projected origin if desired
if centerOption == 'ProjectedOrigin': 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 # invert if requested
if invert: if invert:

View File

@ -143,31 +143,18 @@ class Vector(object):
raise NotImplementedError( raise NotImplementedError(
"Have not needed this yet, but FreeCAD supports it!") "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. 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 = plane.origin
base = args[0] normal = plane.zDir
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.")
result = self-normal*(((self-base).dot(normal))/normal.Length**2) return self-normal*(((self-base).dot(normal))/normal.Length**2)
self.x = result.x
self.y = result.y
self.z = result.z
return self
def __neg__(self): def __neg__(self):
return self * -1 return self * -1

View File

@ -161,24 +161,11 @@ class TestCadObjects(BaseTest):
base = Vector(5, 7, 9) base = Vector(5, 7, 9)
x_dir = Vector(1, 0, 0) 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 # test passing Plane object
point = Vector(10, 11, 12).projectToPlane(Plane(base, x_dir, normal)) point = Vector(10, 11, 12).projectToPlane(Plane(base, x_dir, normal))
self.assertTupleAlmostEquals(point.toTuple(), (59/7, 55/7, 51/7), self.assertTupleAlmostEquals(point.toTuple(), (59/7, 55/7, 51/7),
decimal_places) decimal_places)
# test wrong number of input arguments
with self.assertRaises(TypeError):
Vector(10,11.12).projectToPlane(1,1,1)
def testMatrixCreationAndAccess(self): def testMatrixCreationAndAccess(self):
def matrix_vals(m): def matrix_vals(m):
return [[m[r,c] for c in range(4)] for r in range(4)] return [[m[r,c] for c in range(4)] for r in range(4)]

View File

@ -1924,6 +1924,22 @@ class TestCadQuery(BaseTest):
.plane.origin.toTuple() .plane.origin.toTuple()
self.assertTupleAlmostEquals(origin, (45.0, 30.0, 10.0), decimal_places) 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) r = Workplane("YZ").polyline(pts).close().extrude(10.0)
origin = r.faces(">X").workplane(centerOption='ProjectedOrigin') \ origin = r.faces(">X").workplane(centerOption='ProjectedOrigin') \
@ -1951,6 +1967,3 @@ class TestCadQuery(BaseTest):
origin = r.faces("<Y").workplane(centerOption='CenterOfBoundBox') \ origin = r.faces("<Y").workplane(centerOption='CenterOfBoundBox') \
.plane.origin.toTuple() .plane.origin.toTuple()
self.assertTupleAlmostEquals(origin, (45.0, -10.0, 30.0), decimal_places) self.assertTupleAlmostEquals(origin, (45.0, -10.0, 30.0), decimal_places)