Clean up geom.py
Remove Plane.setOrigin3d() and replace with Plane.origin = ...
This commit is contained in:
parent
cc82031e51
commit
37890d132b
|
@ -943,7 +943,7 @@ class Workplane(CQ):
|
|||
offset = offset.toTuple()
|
||||
|
||||
p = self.plane.rotated(rotate)
|
||||
p.setOrigin3d(self.plane.toWorldCoords(offset))
|
||||
p.origin = self.plane.toWorldCoords(offset)
|
||||
ns = self.newObject([p.origin])
|
||||
ns.plane = p
|
||||
|
||||
|
|
|
@ -22,16 +22,19 @@ import cadquery
|
|||
import FreeCAD
|
||||
import Part as FreeCADPart
|
||||
|
||||
|
||||
def sortWiresByBuildOrder(wireList, plane, result=[]):
|
||||
"""
|
||||
Tries to determine how wires should be combined into faces.
|
||||
"""Tries to determine how wires should be combined into faces.
|
||||
|
||||
Assume:
|
||||
The wires make up one or more faces, which could have 'holes'
|
||||
Outer wires are listed ahead of inner wires
|
||||
there are no wires inside wires inside wires ( IE, islands -- we can deal with that later on )
|
||||
there are no wires inside wires inside wires
|
||||
( IE, islands -- we can deal with that later on )
|
||||
none of the wires are construction wires
|
||||
Compute:
|
||||
one or more sets of wires, with the outer wire listed first, and inner ones
|
||||
one or more sets of wires, with the outer wire listed first, and inner
|
||||
ones
|
||||
Returns, list of lists.
|
||||
"""
|
||||
result = []
|
||||
|
@ -49,6 +52,7 @@ def sortWiresByBuildOrder(wireList,plane,result=[]):
|
|||
|
||||
return result
|
||||
|
||||
|
||||
class Vector(object):
|
||||
"""Create a 3-dimensional vector
|
||||
|
||||
|
@ -60,15 +64,8 @@ class Vector(object):
|
|||
* a vector ( in which case it is copied )
|
||||
* a 3-tuple
|
||||
* three float values, x, y, and z
|
||||
|
||||
FreeCAD's vector implementation has a dumb
|
||||
implementation for multiply and add-- they modify the existing
|
||||
value and return a copy as well.
|
||||
|
||||
This vector is immutable-- all mutations return a copy!
|
||||
"""
|
||||
def __init__(self, *args):
|
||||
|
||||
if len(args) == 3:
|
||||
fV = FreeCAD.Base.Vector(args[0], args[1], args[2])
|
||||
elif len(args) == 1:
|
||||
|
@ -85,11 +82,27 @@ class Vector(object):
|
|||
else:
|
||||
raise ValueError("Expected three floats, FreeCAD Vector, or 3-tuple")
|
||||
|
||||
self.wrapped = fV
|
||||
self.Length = fV.Length
|
||||
self.x = fV.x
|
||||
self.y = fV.y
|
||||
self.z = fV.z
|
||||
self._wrapped = fV
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
return self.wrapped.x
|
||||
|
||||
@property
|
||||
def y(self):
|
||||
return self.wrapped.y
|
||||
|
||||
@property
|
||||
def z(self):
|
||||
return self.wrapped.z
|
||||
|
||||
@property
|
||||
def Length(self):
|
||||
return self.wrapped.Length
|
||||
|
||||
@property
|
||||
def wrapped(self):
|
||||
return self._wrapped
|
||||
|
||||
def toTuple(self):
|
||||
return (self.x, self.y, self.z)
|
||||
|
@ -108,31 +121,23 @@ class Vector(object):
|
|||
return Vector(self.wrapped.add(v.wrapped))
|
||||
|
||||
def multiply(self, scale):
|
||||
"""
|
||||
Return self multiplied by the provided scalar
|
||||
|
||||
Note: FreeCAD has a bug here, where the
|
||||
base is also modified
|
||||
"""
|
||||
tmp = FreeCAD.Base.Vector(self.wrapped)
|
||||
return Vector( tmp.multiply(scale))
|
||||
"""Return a copy multiplied by the provided scalar"""
|
||||
tmp_fc_vector = FreeCAD.Base.Vector(self.wrapped)
|
||||
return Vector(tmp_fc_vector.multiply(scale))
|
||||
|
||||
def normalize(self):
|
||||
"""
|
||||
Return normalized version this vector.
|
||||
|
||||
Note: FreeCAD has a bug here, where the
|
||||
base is also modified
|
||||
"""
|
||||
tmp = FreeCAD.Base.Vector(self.wrapped)
|
||||
tmp.normalize()
|
||||
return Vector( tmp )
|
||||
"""Return a normalized version of this vector"""
|
||||
tmp_fc_vector = FreeCAD.Base.Vector(self.wrapped)
|
||||
tmp_fc_vector.normalize()
|
||||
return Vector(tmp_fc_vector)
|
||||
|
||||
def Center(self):
|
||||
"""
|
||||
"""Return the vector itself
|
||||
|
||||
The center of myself is myself.
|
||||
Provided so that vectors, vertexes, and other shapes all support a common interface,
|
||||
when Center() is requested for all objects on the stack
|
||||
Provided so that vectors, vertexes, and other shapes all support a
|
||||
common interface, when Center() is requested for all objects on the
|
||||
stack.
|
||||
"""
|
||||
return self
|
||||
|
||||
|
@ -157,44 +162,26 @@ class Vector(object):
|
|||
def __add__(self, v):
|
||||
return self.add(v)
|
||||
|
||||
def __len__(self):
|
||||
return self.Length
|
||||
|
||||
def __repr__(self):
|
||||
return self.wrapped.__repr__()
|
||||
|
||||
def __str__(self):
|
||||
return self.wrapped.__str__()
|
||||
|
||||
def __len__(self,other):
|
||||
return self.wrapped.__len__(other)
|
||||
|
||||
def __lt__(self,other):
|
||||
return self.wrapped.__lt__(other)
|
||||
|
||||
def __gt__(self,other):
|
||||
return self.wrapped.__gt__(other)
|
||||
|
||||
def __ne__(self, other):
|
||||
return self.wrapped.__ne__(other)
|
||||
|
||||
def __le__(self,other):
|
||||
return self.wrapped.__le__(other)
|
||||
|
||||
def __ge__(self,other):
|
||||
return self.wrapped.__ge__(other)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.wrapped.__eq__(other)
|
||||
|
||||
|
||||
class Matrix:
|
||||
"""
|
||||
A 3d , 4x4 transformation matrix.
|
||||
"""A 3d , 4x4 transformation matrix.
|
||||
|
||||
Used to move geometry in space.
|
||||
"""
|
||||
def __init__(self, matrix=None):
|
||||
if matrix == None:
|
||||
if matrix is None:
|
||||
self.wrapped = FreeCAD.Base.Matrix()
|
||||
else:
|
||||
self.wrapped = matrix
|
||||
|
@ -206,29 +193,30 @@ class Matrix:
|
|||
self.wrapped.rotateY(angle)
|
||||
|
||||
|
||||
class Plane:
|
||||
"""
|
||||
A 2d coordinate system in space, with the x-y axes on the a plane, and a particular point as the origin.
|
||||
class Plane(object):
|
||||
"""A 2D coordinate system in space
|
||||
|
||||
A plane allows the use of 2-d coordinates, which are later converted to global, 3d coordinates when
|
||||
the operations are complete.
|
||||
A 2D coordinate system in space, with the x-y axes on the plane, and a
|
||||
particular point as the origin.
|
||||
|
||||
Frequently, it is not necessary to create work planes, as they can be created automatically from faces.
|
||||
A plane allows the use of 2-d coordinates, which are later converted to
|
||||
global, 3d coordinates when the operations are complete.
|
||||
|
||||
Frequently, it is not necessary to create work planes, as they can be
|
||||
created automatically from faces.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def named(cls, stdName, origin=(0, 0, 0)):
|
||||
"""
|
||||
Create a predefined Plane based on the conventional names.
|
||||
"""Create a predefined Plane based on the conventional names.
|
||||
|
||||
:param stdName: one of (XY|YZ|ZX|XZ|YX|ZY|front|back|left|right|top|bottom
|
||||
:param stdName: one of (XY|YZ|ZX|XZ|YX|ZY|front|back|left|right|top|bottom)
|
||||
:type stdName: string
|
||||
:param origin: the desired origin, specified in global coordinates
|
||||
:type origin: 3-tuple of the origin of the new plane, in global coorindates.
|
||||
|
||||
Available named planes are as follows. Direction references refer to the global
|
||||
directions
|
||||
Available named planes are as follows. Direction references refer to
|
||||
the global directions.
|
||||
|
||||
=========== ======= ======= ======
|
||||
Name xDir yDir zDir
|
||||
|
@ -250,32 +238,37 @@ class Plane:
|
|||
|
||||
namedPlanes = {
|
||||
# origin, xDir, normal
|
||||
'XY' : Plane(Vector(origin),Vector((1,0,0)),Vector((0,0,1))),
|
||||
'YZ' : Plane(Vector(origin),Vector((0,1,0)),Vector((1,0,0))),
|
||||
'XY': Plane(origin, (1, 0, 0), (0, 0, 1)),
|
||||
'YZ': Plane(origin, (0, 1, 0), (1, 0, 0)),
|
||||
'ZX': Plane(origin, (0, 0, 1), (0, 1, 0)),
|
||||
'XZ' : Plane(Vector(origin),Vector((1,0,0)),Vector((0,-1,0))),
|
||||
'XZ': Plane(origin, (1, 0, 0), (0, -1, 0)),
|
||||
'YX': Plane(origin, (0, 1, 0), (0, 0, -1)),
|
||||
'ZY': Plane(origin, (0, 0, 1), (-1, 0, 0)),
|
||||
'front': Plane(Vector(origin),Vector((1,0,0)),Vector((0,0,1))),
|
||||
'back': Plane(Vector(origin),Vector((-1,0,0)),Vector((0,0,-1))),
|
||||
'left': Plane(Vector(origin),Vector((0,0,1)),Vector((-1,0,0))),
|
||||
'right': Plane(Vector(origin),Vector((0,0,-1)),Vector((1,0,0))),
|
||||
'top': Plane(Vector(origin),Vector((1,0,0)),Vector((0,1,0))),
|
||||
'bottom': Plane(Vector(origin),Vector((1,0,0)),Vector((0,-1,0)))
|
||||
'front': Plane(origin, (1, 0, 0), (0, 0, 1)),
|
||||
'back': Plane(origin, (-1, 0, 0), (0, 0, -1)),
|
||||
'left': Plane(origin, (0, 0, 1), (-1, 0, 0)),
|
||||
'right': Plane(origin, (0, 0, -1), (1, 0, 0)),
|
||||
'top': Plane(origin, (1, 0, 0), (0, 1, 0)),
|
||||
'bottom': Plane(origin, (1, 0, 0), (0, -1, 0))
|
||||
}
|
||||
|
||||
if namedPlanes.has_key(stdName):
|
||||
try:
|
||||
return namedPlanes[stdName]
|
||||
else:
|
||||
raise ValueError("Supported names are %s " % str(namedPlanes.keys()) )
|
||||
except KeyError:
|
||||
raise ValueError('Supported names are {}'.format(
|
||||
namedPlanes.keys()))
|
||||
|
||||
@classmethod
|
||||
def XY(cls, origin=(0, 0, 0), xDir=Vector(1, 0, 0)):
|
||||
return Plane.named('XY',origin)
|
||||
plane = Plane.named('XY', origin)
|
||||
plane._setPlaneDir(xDir)
|
||||
return plane
|
||||
|
||||
@classmethod
|
||||
def YZ(cls,origin=(0,0,0),xDir=Vector(1,0,0)):
|
||||
return Plane.named('YZ',origin)
|
||||
def YZ(cls, origin=(0, 0, 0), xDir=Vector(0, 1, 0)):
|
||||
plane = Plane.named('YZ', origin)
|
||||
plane._setPlaneDir(xDir)
|
||||
return plane
|
||||
|
||||
@classmethod
|
||||
def ZX(cls, origin=(0, 0, 0), xDir=Vector(0, 0, 1)):
|
||||
|
@ -285,7 +278,9 @@ class Plane:
|
|||
|
||||
@classmethod
|
||||
def XZ(cls, origin=(0, 0, 0), xDir=Vector(1, 0, 0)):
|
||||
return Plane.named('XZ',origin)
|
||||
plane = Plane.named('XZ', origin)
|
||||
plane._setPlaneDir(xDir)
|
||||
return plane
|
||||
|
||||
@classmethod
|
||||
def YX(cls, origin=(0, 0, 0), xDir=Vector(0, 1, 0)):
|
||||
|
@ -301,31 +296,42 @@ class Plane:
|
|||
|
||||
@classmethod
|
||||
def front(cls, origin=(0, 0, 0), xDir=Vector(1, 0, 0)):
|
||||
return Plane.named('front',origin)
|
||||
plane = Plane.named('front', origin)
|
||||
plane._setPlaneDir(xDir)
|
||||
return plane
|
||||
|
||||
@classmethod
|
||||
def back(cls,origin=(0,0,0),xDir=Vector(1,0,0)):
|
||||
return Plane.named('back',origin)
|
||||
def back(cls, origin=(0, 0, 0), xDir=Vector(-1, 0, 0)):
|
||||
plane = Plane.named('back', origin)
|
||||
plane._setPlaneDir(xDir)
|
||||
return plane
|
||||
|
||||
@classmethod
|
||||
def left(cls,origin=(0,0,0),xDir=Vector(1,0,0)):
|
||||
return Plane.named('left',origin)
|
||||
def left(cls, origin=(0, 0, 0), xDir=Vector(0, 0, 1)):
|
||||
plane = Plane.named('left', origin)
|
||||
plane._setPlaneDir(xDir)
|
||||
return plane
|
||||
|
||||
@classmethod
|
||||
def right(cls,origin=(0,0,0),xDir=Vector(1,0,0)):
|
||||
return Plane.named('right',origin)
|
||||
def right(cls, origin=(0, 0, 0), xDir=Vector(0, 0, -1)):
|
||||
plane = Plane.named('right', origin)
|
||||
plane._setPlaneDir(xDir)
|
||||
return plane
|
||||
|
||||
@classmethod
|
||||
def top(cls, origin=(0, 0, 0), xDir=Vector(1, 0, 0)):
|
||||
return Plane.named('top',origin)
|
||||
plane = Plane.named('top', origin)
|
||||
plane._setPlaneDir(xDir)
|
||||
return plane
|
||||
|
||||
@classmethod
|
||||
def bottom(cls, origin=(0, 0, 0), xDir=Vector(1, 0, 0)):
|
||||
return Plane.named('bottom',origin)
|
||||
plane = Plane.named('bottom', origin)
|
||||
plane._setPlaneDir(xDir)
|
||||
return plane
|
||||
|
||||
def __init__(self, origin, xDir, normal):
|
||||
"""
|
||||
Create a Plane with an arbitrary orientation
|
||||
"""Create a Plane with an arbitrary orientation
|
||||
|
||||
TODO: project x and y vectors so they work even if not orthogonal
|
||||
:param origin: the origin
|
||||
|
@ -336,7 +342,6 @@ class Plane:
|
|||
:type normal: a FreeCAD Vector
|
||||
:raises: ValueError if the specified xDir is not orthogonal to the provided normal.
|
||||
:return: a plane in the global space, with the xDirection of the plane in the specified direction.
|
||||
|
||||
"""
|
||||
normal = Vector(normal)
|
||||
if (normal.Length == 0.0):
|
||||
|
@ -347,45 +352,45 @@ class Plane:
|
|||
raise ValueError('xDir should be non null')
|
||||
self._setPlaneDir(xDir)
|
||||
|
||||
#stupid freeCAD!!!!! multiply has a bug that changes the original also!
|
||||
self.invZDir = self.zDir.multiply(-1.0)
|
||||
|
||||
self.setOrigin3d(origin)
|
||||
self.origin = origin
|
||||
|
||||
@property
|
||||
def origin(self):
|
||||
return self._origin
|
||||
|
||||
def setOrigin3d(self,originVector):
|
||||
"""
|
||||
Move the origin of the plane, leaving its orientation and xDirection unchanged.
|
||||
:param originVector: the new center of the plane, *global* coordinates
|
||||
:type originVector: a FreeCAD Vector.
|
||||
:return: void
|
||||
|
||||
"""
|
||||
self.origin = Vector(originVector)
|
||||
@origin.setter
|
||||
def origin(self, value):
|
||||
self._origin = Vector(value)
|
||||
self._calcTransforms()
|
||||
|
||||
def setOrigin2d(self, x, y):
|
||||
"""
|
||||
Set a new origin based of the plane. The plane's orientation and xDrection are unaffected.
|
||||
"""Set a new origin in the plane itself
|
||||
|
||||
Set a new origin in the plane itself. The plane's orientation and
|
||||
xDrection are unaffected.
|
||||
|
||||
:param float x: offset in the x direction
|
||||
:param float y: offset in the y direction
|
||||
:return: void
|
||||
|
||||
the new coordinates are specified in terms of the current 2-d system. As an example::
|
||||
The new coordinates are specified in terms of the current 2-d system.
|
||||
As an example:
|
||||
p = Plane.XY()
|
||||
p.setOrigin2d(2, 2)
|
||||
p.setOrigin2d(2, 2)
|
||||
|
||||
results in a plane with its origin at (x,y)=(4,4) in global coordinates. The both operations were relative to
|
||||
local coordinates of the plane.
|
||||
|
||||
results in a plane with its origin at (x, y) = (4, 4) in global
|
||||
coordinates. Both operations were relative to local coordinates of the
|
||||
plane.
|
||||
"""
|
||||
self.setOrigin3d(self.toWorldCoords((x,y)))
|
||||
self.origin = self.toWorldCoords((x, y))
|
||||
|
||||
def isWireInside(self, baseWire, testWire):
|
||||
"""
|
||||
Determine if testWire is inside baseWire, after both wires are projected into the current plane
|
||||
"""Determine if testWire is inside baseWire
|
||||
|
||||
Determine if testWire is inside baseWire, after both wires are projected
|
||||
into the current plane.
|
||||
|
||||
:param baseWire: a reference wire
|
||||
:type baseWire: a FreeCAD wire
|
||||
|
@ -393,44 +398,46 @@ class Plane:
|
|||
:type testWire: a FreeCAD wire
|
||||
:return: True if testWire is inside baseWire, otherwise False
|
||||
|
||||
If either wire does not lie in the current plane, it is projected into the plane first.
|
||||
If either wire does not lie in the current plane, it is projected into
|
||||
the plane first.
|
||||
|
||||
*WARNING*: This method is not 100% reliable. It uses bounding box tests, but needs
|
||||
more work to check for cases when curves are complex.
|
||||
*WARNING*: This method is not 100% reliable. It uses bounding box
|
||||
tests, but needs more work to check for cases when curves are complex.
|
||||
|
||||
Future Enhancements:
|
||||
* Discretizing points along each curve to provide a more reliable test
|
||||
|
||||
* Discretizing points along each curve to provide a more reliable
|
||||
test.
|
||||
"""
|
||||
# TODO: also use a set of points along the wire to test as well.
|
||||
#TODO: would it be more efficient to create objects in the local coordinate system, and then transform to global
|
||||
# TODO: would it be more efficient to create objects in the local
|
||||
# coordinate system, and then transform to global
|
||||
# coordinates upon extrusion?
|
||||
|
||||
tBaseWire = baseWire.transformGeometry(self.fG)
|
||||
tTestWire = testWire.transformGeometry(self.fG)
|
||||
|
||||
#these bounding boxes will have z=0, since we transformed them into the space of the plane
|
||||
# These bounding boxes will have z=0, since we transformed them into the
|
||||
# space of the plane.
|
||||
bb = tBaseWire.BoundingBox()
|
||||
tb = tTestWire.BoundingBox()
|
||||
|
||||
# findOutsideBox actually inspects both ways, here we only want to
|
||||
# know if one is inside the other
|
||||
x = BoundBox.findOutsideBox2D(bb,tb)
|
||||
return x == bb
|
||||
return bb == BoundBox.findOutsideBox2D(bb, tb)
|
||||
|
||||
def toLocalCoords(self, obj):
|
||||
"""
|
||||
Project the provided coordinates onto this plane.
|
||||
"""Project the provided coordinates onto this plane
|
||||
|
||||
:param obj: an object or vector to convert
|
||||
:type vector: a vector or shape
|
||||
:return: an object of the same type as the input, but converted to local coordinates
|
||||
:return: an object of the same type, but converted to local coordinates
|
||||
|
||||
|
||||
Most of the time, the z-coordinate returned will be zero, because most operations
|
||||
based on a plane are all 2-d. Occasionally, though, 3-d points outside of the current plane are transformed.
|
||||
One such example is :py:meth:`Workplane.box`, where 3-d corners of a box are transformed to orient the box in space
|
||||
correctly.
|
||||
Most of the time, the z-coordinate returned will be zero, because most
|
||||
operations based on a plane are all 2-d. Occasionally, though, 3-d
|
||||
points outside of the current plane are transformed. One such example is
|
||||
:py:meth:`Workplane.box`, where 3-d corners of a box are transformed to
|
||||
orient the box in space correctly.
|
||||
|
||||
"""
|
||||
if isinstance(obj, Vector):
|
||||
|
@ -438,96 +445,89 @@ class Plane:
|
|||
elif isinstance(obj, cadquery.Shape):
|
||||
return obj.transformShape(self.rG)
|
||||
else:
|
||||
raise ValueError("Dont know how to convert type %s to local coordinates" % str(type(obj)))
|
||||
|
||||
raise ValueError(
|
||||
"Don't know how to convert type {} to local coordinates".format(
|
||||
type(obj)))
|
||||
|
||||
def toWorldCoords(self, tuplePoint):
|
||||
"""
|
||||
Convert a point in local coordinates to global coordinates.
|
||||
"""Convert a point in local coordinates to global coordinates
|
||||
|
||||
:param tuplePoint: point in local coordinates to convert
|
||||
:type tuplePoint: a 2 or three tuple of float. the third value is taken to be zero if not supplied
|
||||
:param tuplePoint: point in local coordinates to convert.
|
||||
:type tuplePoint: a 2 or three tuple of float. The third value is taken to be zero if not supplied.
|
||||
:return: a Vector in global coordinates
|
||||
|
||||
|
||||
"""
|
||||
if isinstance(tuplePoint, Vector):
|
||||
v = tuplePoint
|
||||
elif len(tuplePoint) == 2:
|
||||
v = Vector(tuplePoint[0], tuplePoint[1], 0)
|
||||
else:
|
||||
v = Vector(tuplePoint[0],tuplePoint[1],tuplePoint[2])
|
||||
v = Vector(tuplePoint)
|
||||
return Vector(self.rG.multiply(v.wrapped))
|
||||
|
||||
|
||||
def rotated(self, rotate=(0, 0, 0)):
|
||||
"""
|
||||
returns a copy of this plane, rotated about the specified axes, as measured from horizontal
|
||||
"""Returns a copy of this plane, rotated about the specified axes
|
||||
|
||||
Since the z axis is always normal the plane, rotating around Z will always produce a plane
|
||||
that is parallel to this one
|
||||
Since the z axis is always normal the plane, rotating around Z will
|
||||
always produce a plane that is parallel to this one.
|
||||
|
||||
the origin of the workplane is unaffected by the rotation.
|
||||
The origin of the workplane is unaffected by the rotation.
|
||||
|
||||
rotations are done in order x,y,z. if you need a different order, manually chain together multiple .rotate()
|
||||
commands
|
||||
Rotations are done in order x, y, z. If you need a different order,
|
||||
manually chain together multiple rotate() commands.
|
||||
|
||||
:param rotate: Vector [xDegrees, yDegrees, zDegrees]
|
||||
:return: a copy of this plane rotated as requested
|
||||
:return: a copy of this plane rotated as requested.
|
||||
"""
|
||||
|
||||
if rotate.__class__.__name__ != 'Vector':
|
||||
rotate = Vector(rotate)
|
||||
#convert to radians
|
||||
# Convert to radians.
|
||||
rotate = rotate.multiply(math.pi / 180.0)
|
||||
|
||||
#compute rotation matrix
|
||||
# Compute rotation matrix.
|
||||
m = FreeCAD.Base.Matrix()
|
||||
m.rotateX(rotate.x)
|
||||
m.rotateY(rotate.y)
|
||||
m.rotateZ(rotate.z)
|
||||
|
||||
#compute the new plane
|
||||
# Compute the new plane.
|
||||
newXdir = Vector(m.multiply(self.xDir.wrapped))
|
||||
newZdir = Vector(m.multiply(self.zDir.wrapped))
|
||||
|
||||
newP= Plane(self.origin,newXdir,newZdir)
|
||||
return newP
|
||||
return Plane(self.origin, newXdir, newZdir)
|
||||
|
||||
def rotateShapes(self, listOfShapes, rotationMatrix):
|
||||
"""
|
||||
rotate the listOfShapes by the rotationMatrix supplied.
|
||||
"""Rotate the listOfShapes by the supplied rotationMatrix
|
||||
|
||||
@param listOfShapes is a list of shape objects
|
||||
@param rotationMatrix is a geom.Matrix object.
|
||||
returns a list of shape objects rotated according to the rotationMatrix
|
||||
returns a list of shape objects rotated according to the rotationMatrix.
|
||||
"""
|
||||
|
||||
#compute rotation matrix ( global --> local --> rotate --> global )
|
||||
# Compute rotation matrix (global --> local --> rotate --> global).
|
||||
# rm = self.plane.fG.multiply(matrix).multiply(self.plane.rG)
|
||||
rm = self.computeTransform(rotationMatrix)
|
||||
# rm = self.computeTransform(rotationMatrix)
|
||||
|
||||
|
||||
#There might be a better way, but to do this rotation takes 3 steps
|
||||
#transform geometry to local coordinates
|
||||
#then rotate about x
|
||||
#then transform back to global coordinates
|
||||
# There might be a better way, but to do this rotation takes 3 steps:
|
||||
# - transform geometry to local coordinates
|
||||
# - then rotate about x
|
||||
# - then transform back to global coordinates.
|
||||
|
||||
resultWires = []
|
||||
for w in listOfShapes:
|
||||
mirrored = w.transformGeometry(rotationMatrix.wrapped)
|
||||
|
||||
# If the first vertex of the second wire is not coincident with the first or last vertices of the first wire
|
||||
# we have to fix the wire so that it will mirror correctly
|
||||
if (mirrored.wrapped.Vertexes[0].X == w.wrapped.Vertexes[0].X and
|
||||
# If the first vertex of the second wire is not coincident with the
|
||||
# first or last vertices of the first wire we have to fix the wire
|
||||
# so that it will mirror correctly.
|
||||
if ((mirrored.wrapped.Vertexes[0].X == w.wrapped.Vertexes[0].X and
|
||||
mirrored.wrapped.Vertexes[0].Y == w.wrapped.Vertexes[0].Y and
|
||||
mirrored.wrapped.Vertexes[0].Z == w.wrapped.Vertexes[0].Z) or \
|
||||
mirrored.wrapped.Vertexes[0].Z == w.wrapped.Vertexes[0].Z) or
|
||||
(mirrored.wrapped.Vertexes[0].X == w.wrapped.Vertexes[-1].X and
|
||||
mirrored.wrapped.Vertexes[0].Y == w.wrapped.Vertexes[-1].Y and
|
||||
mirrored.wrapped.Vertexes[0].Z == w.wrapped.Vertexes[-1].Z):
|
||||
mirrored.wrapped.Vertexes[0].Z == w.wrapped.Vertexes[-1].Z)):
|
||||
|
||||
resultWires.append(mirrored)
|
||||
else:
|
||||
# Make sure that our mirrored edges meet up and are ordered properly
|
||||
# Make sure that our mirrored edges meet up and are ordered
|
||||
# properly.
|
||||
aEdges = w.wrapped.Edges
|
||||
aEdges.extend(mirrored.wrapped.Edges)
|
||||
comp = FreeCADPart.Compound(aEdges)
|
||||
|
@ -541,39 +541,43 @@ class Plane:
|
|||
"""Set the vectors parallel to the plane, i.e. xDir and yDir"""
|
||||
if (self.zDir.dot(xDir) > 1e-5):
|
||||
raise ValueError('xDir must be parralel to the plane')
|
||||
xDir = Vector(xDir)
|
||||
self.xDir = xDir.normalize()
|
||||
self.yDir = self.zDir.cross(self.xDir).normalize()
|
||||
|
||||
def _calcTransforms(self):
|
||||
"""
|
||||
Computes transformation martrices to convert between local and global coordinates
|
||||
"""Computes transformation matrices to convert between coordinates
|
||||
|
||||
Computes transformation matrices to convert between local and global
|
||||
coordinates.
|
||||
"""
|
||||
# r is the forward transformation matrix from world to local coordinates
|
||||
#ok i will be really honest-- i cannot understand exactly why this works
|
||||
# ok i will be really honest, i cannot understand exactly why this works
|
||||
# something bout the order of the translation and the rotation.
|
||||
# the double-inverting is strange, and I don't understand it.
|
||||
r = FreeCAD.Base.Matrix()
|
||||
|
||||
#forward transform must rotate and adjust for origin
|
||||
# Forward transform must rotate and adjust for origin.
|
||||
(r.A11, r.A12, r.A13) = (self.xDir.x, self.xDir.y, self.xDir.z)
|
||||
(r.A21, r.A22, r.A23) = (self.yDir.x, self.yDir.y, self.yDir.z)
|
||||
(r.A31, r.A32, r.A33) = (self.zDir.x, self.zDir.y, self.zDir.z)
|
||||
|
||||
invR = r.inverse()
|
||||
(invR.A14,invR.A24,invR.A34) = (self.origin.x,self.origin.y,self.origin.z)
|
||||
invR.A14 = self.origin.x
|
||||
invR.A24 = self.origin.y
|
||||
invR.A34 = self.origin.z
|
||||
|
||||
( self.rG,self.fG ) = ( invR,invR.inverse() )
|
||||
self.rG = invR
|
||||
self.fG = invR.inverse()
|
||||
|
||||
def computeTransform(self, tMatrix):
|
||||
"""
|
||||
Computes the 2-d projection of the supplied matrix
|
||||
"""
|
||||
"""Computes the 2-d projection of the supplied matrix"""
|
||||
|
||||
return Matrix(self.fG.multiply(tMatrix.wrapped).multiply(self.rG))
|
||||
|
||||
rm = self.fG.multiply(tMatrix.wrapped).multiply(self.rG)
|
||||
return Matrix(rm)
|
||||
|
||||
class BoundBox(object):
|
||||
"A BoundingBox for an object or set of objects. Wraps the FreeCAD one"
|
||||
"""A BoundingBox for an object or set of objects. Wraps the FreeCAD one"""
|
||||
def __init__(self, bb):
|
||||
self.wrapped = bb
|
||||
self.xmin = bb.XMin
|
||||
|
@ -589,54 +593,53 @@ class BoundBox(object):
|
|||
self.DiagonalLength = bb.DiagonalLength
|
||||
|
||||
def add(self, obj):
|
||||
"""
|
||||
returns a modified (expanded) bounding box
|
||||
"""Returns a modified (expanded) bounding box
|
||||
|
||||
obj can be one of several things:
|
||||
1. a 3-tuple corresponding to x,y, and z amounts to add
|
||||
2. a vector, containing the x,y,z values to add
|
||||
3. another bounding box, where a new box will be created that encloses both
|
||||
3. another bounding box, where a new box will be created that
|
||||
encloses both.
|
||||
|
||||
this bounding box is not changed
|
||||
This bounding box is not changed.
|
||||
"""
|
||||
tmp = FreeCAD.Base.BoundBox(self.wrapped)
|
||||
if type(obj) is tuple:
|
||||
if isinstance(obj, tuple):
|
||||
tmp.add(obj[0], obj[1], obj[2])
|
||||
elif type(obj) is Vector:
|
||||
elif isinstance(obj, Vector):
|
||||
tmp.add(obj.fV)
|
||||
elif type(obj) is BoundBox:
|
||||
elif isinstance(obj, BoundBox):
|
||||
tmp.add(obj.wrapped)
|
||||
|
||||
return BoundBox(tmp)
|
||||
|
||||
@classmethod
|
||||
def findOutsideBox2D(cls, b1, b2):
|
||||
"""
|
||||
compares bounding boxes. returns none if neither is inside the other. returns
|
||||
the outer one if either is outside the other
|
||||
"""Compares bounding boxes
|
||||
|
||||
BoundBox.isInside works in 3d, but this is a 2d bounding box, so it doesnt work correctly
|
||||
plus, there was all kinds of rounding error in the built-in implementation i do not understand.
|
||||
Here we assume that the b
|
||||
Compares bounding boxes. Returns none if neither is inside the other.
|
||||
Returns the outer one if either is outside the other.
|
||||
|
||||
BoundBox.isInside works in 3d, but this is a 2d bounding box, so it
|
||||
doesn't work correctly plus, there was all kinds of rounding error in
|
||||
the built-in implementation i do not understand.
|
||||
"""
|
||||
bb1 = b1.wrapped
|
||||
bb2 = b2.wrapped
|
||||
if bb1.XMin < bb2.XMin and\
|
||||
bb1.XMax > bb2.XMax and\
|
||||
bb1.YMin < bb2.YMin and\
|
||||
bb1.YMax > bb2.YMax:
|
||||
fc_bb1 = b1.wrapped
|
||||
fc_bb2 = b2.wrapped
|
||||
if (fc_bb1.XMin < fc_bb2.XMin and
|
||||
fc_bb1.XMax > fc_bb2.XMax and
|
||||
fc_bb1.YMin < fc_bb2.YMin and
|
||||
fc_bb1.YMax > fc_bb2.YMax):
|
||||
return b1
|
||||
|
||||
if bb2.XMin < bb1.XMin and\
|
||||
bb2.XMax > bb1.XMax and\
|
||||
bb2.YMin < bb1.YMin and\
|
||||
bb2.YMax > bb1.YMax:
|
||||
if (fc_bb2.XMin < fc_bb1.XMin and
|
||||
fc_bb2.XMax > fc_bb1.XMax and
|
||||
fc_bb2.YMin < fc_bb1.YMin and
|
||||
fc_bb2.YMax > fc_bb1.YMax):
|
||||
return b2
|
||||
|
||||
return None
|
||||
|
||||
def isInside(self, anotherBox):
|
||||
"""
|
||||
is the provided bounding box inside this one?
|
||||
"""
|
||||
"""Is the provided bounding box inside this one?"""
|
||||
return self.wrapped.isInside(anotherBox.wrapped)
|
||||
|
|
Loading…
Reference in New Issue
Block a user