00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 import FreeCAD, FreeCADGui, Part, math
00026 from FreeCAD import Vector
00027 from draftlibs import fcvec
00028
00029 __title__="FreeCAD Working Plane utility"
00030 __author__ = "Ken Cline"
00031 __url__ = "http://free-cad.sourceforge.net"
00032
00033 '''
00034 This module provides a class called plane to assist in selecting and maintaining a working plane.
00035 '''
00036
00037
00038 class plane:
00039 '''A WorkPlane object'''
00040
00041 def __init__(self):
00042
00043 self.doc = None
00044
00045 self.weak = True
00046
00047 self.u = None
00048 self.v = None
00049 self.axis = None
00050 self.position = None
00051
00052 self.stored = None
00053
00054 def __repr__(self):
00055 return "Workplane x="+str(fcvec.rounded(self.u))+" y="+str(fcvec.rounded(self.v))+" z="+str(fcvec.rounded(self.axis))
00056
00057 def offsetToPoint(self, p, direction=None):
00058 '''
00059 Return the signed distance from p to the plane, such
00060 that p + offsetToPoint(p)*direction lies on the plane.
00061 direction defaults to -plane.axis
00062 '''
00063
00064 '''
00065 A picture will help explain the computation:
00066
00067 p
00068 //|
00069 / / |
00070 / / |
00071 / / |
00072 / / |
00073 -------------------- plane -----c-----x-----a--------
00074
00075 Here p is the specified point,
00076 c is a point (in this case plane.position) on the plane
00077 x is the intercept on the plane from p in the specified direction, and
00078 a is the perpendicular intercept on the plane (i.e. along plane.axis)
00079
00080 Using vertival bars to denote the length operator,
00081 |ap| = |cp| * cos(apc) = |xp| * cos(apx)
00082 so
00083 |xp| = |cp| * cos(apc) / cos(apx)
00084 = (cp . axis) / (direction . axis)
00085 '''
00086 if direction == None: direction = self.axis
00087 return self.axis.dot(self.position.sub(p))/self.axis.dot(direction)
00088
00089 def projectPoint(self, p, direction=None):
00090 '''project point onto plane, default direction is orthogonal'''
00091 if not direction: direction = self.axis
00092 t = Vector(direction)
00093 t.multiply(self.offsetToPoint(p, direction))
00094 return p.add(t)
00095
00096 def alignToPointAndAxis(self, point, axis, offset, upvec=None):
00097 self.doc = FreeCAD.ActiveDocument
00098 self.axis = axis;
00099 self.axis.normalize()
00100 if (fcvec.equals(axis, Vector(1,0,0))):
00101 self.u = Vector(0,1,0)
00102 self.v = Vector(0,0,1)
00103 elif (fcvec.equals(axis, Vector(-1,0,0))):
00104 self.u = Vector(0,-1,0)
00105 self.v = Vector(0,0,1)
00106 elif upvec:
00107 self.v = upvec
00108 self.v.normalize()
00109 self.u = self.v.cross(self.axis)
00110 else:
00111 self.v = axis.cross(Vector(1,0,0))
00112 self.v.normalize()
00113 self.u = fcvec.rotate(self.v, -math.pi/2, self.axis)
00114 offsetVector = Vector(axis); offsetVector.multiply(offset)
00115 self.position = point.add(offsetVector)
00116 self.weak = False
00117
00118
00119
00120 def alignToCurve(self, shape, offset):
00121 if shape.ShapeType == 'Edge':
00122
00123 return False
00124 elif shape.ShapeType == 'Wire':
00125
00126 return False
00127 else:
00128 return False
00129
00130 def alignToFace(self, shape, offset=0):
00131
00132 if shape.ShapeType == 'Face':
00133
00134 self.alignToPointAndAxis(shape.Faces[0].CenterOfMass, shape.Faces[0].normalAt(0,0), offset)
00135 return True
00136 else:
00137 return False
00138
00139 def alignToSelection(self, offset):
00140 '''If selection uniquely defines a plane, align working plane to it. Return success (bool)'''
00141 sex = FreeCADGui.Selection.getSelectionEx(FreeCAD.ActiveDocument.Name)
00142 if len(sex) == 0:
00143 return False
00144 elif len(sex) == 1:
00145 if not sex[0].Object.isDerivedFrom("Part::Shape"):
00146 return False
00147 return self.alignToCurve(sex[0].Object.Shape, offset) \
00148 or self.alignToFace(sex[0].Object.Shape, offset) \
00149 or (len(sex[0].SubObjects) == 1 and self.alignToFace(sex[0].SubObjects[0], offset))
00150 else:
00151
00152 return False
00153
00154 def setup(self, direction, point, upvec=None):
00155 '''If working plane is undefined, define it!'''
00156 if self.weak:
00157 self.alignToPointAndAxis(point, direction, 0, upvec)
00158 self.weak = True
00159
00160 def reset(self):
00161 self.doc = None
00162 self.weak = True
00163
00164 def getRotation(self):
00165 "returns a placement describing the working plane orientation ONLY"
00166 m = fcvec.getPlaneRotation(self.u,self.v,self.axis)
00167 return FreeCAD.Placement(m)
00168
00169 def getPlacement(self):
00170 "returns the placement of the working plane"
00171 m = FreeCAD.Matrix(
00172 self.u.x,self.v.x,self.axis.x,self.position.x,
00173 self.u.y,self.v.y,self.axis.y,self.position.y,
00174 self.u.z,self.v.z,self.axis.z,self.position.z,
00175 0.0,0.0,0.0,1.0)
00176 return FreeCAD.Placement(m)
00177
00178 def save(self):
00179 "stores the current plane state"
00180 self.stored = [self.u,self.v,self.axis,self.position,self.weak]
00181
00182 def restore(self):
00183 "restores a previously saved plane state, if exists"
00184 if self.stored:
00185 self.u = self.stored[0]
00186 self.v = self.stored[1]
00187 self.axis = self.stored[2]
00188 self.position = self.stored[3]
00189 self.weak = self.stored[4]
00190 self.stored = None
00191
00192 def getLocalCoords(self,point):
00193 "returns the coordinates of a given point on the working plane"
00194 xv = fcvec.project(point,self.u)
00195 x = xv.Length
00196 if xv.getAngle(self.u) > 1:
00197 x = -x
00198 yv = fcvec.project(point,self.v)
00199 y = yv.Length
00200 if yv.getAngle(self.v) > 1:
00201 y = -y
00202 zv = fcvec.project(point,self.axis)
00203 z = zv.Length
00204 if zv.getAngle(self.axis) > 1:
00205 z = -z
00206 return Vector(x,y,z)
00207
00208 def getGlobalCoords(self,point):
00209 "returns the global coordinates of the given point, taken relatively to this working plane"
00210 vx = fcvec.scale(self.u,point.x)
00211 vy = fcvec.scale(self.v,point.y)
00212 vz = fcvec.scale(self.axis,point.z)
00213 return (vx.add(vy)).add(vz)
00214
00215 def getClosestAxis(self,point):
00216 "returns which of the workingplane axes is closest from the given vector"
00217 ax = point.getAngle(self.u)
00218 ay = point.getAngle(self.v)
00219 az = point.getAngle(self.axis)
00220 bx = point.getAngle(fcvec.neg(self.u))
00221 by = point.getAngle(fcvec.neg(self.v))
00222 bz = point.getAngle(fcvec.neg(self.axis))
00223 b = min(ax,ay,az)
00224 if b in [ax,bx]:
00225 return "x"
00226 elif b in [ay,by]:
00227 return "y"
00228 elif b in [az,bz]:
00229 return "z"
00230 else:
00231 return None
00232
00233 def getPlacementFromPoints(points):
00234 "returns a placement from a list of 3 or 4 vectors"
00235 pl = plane()
00236 pl.position = points[0]
00237 pl.u = (points[1].sub(points[0]).normalize())
00238 pl.v = (points[2].sub(points[0]).normalize())
00239 if len(points) == 4:
00240 pl.axis = (points[3].sub(points[0]).normalize())
00241 else:
00242 pl.axis = ((pl.u).cross(pl.v)).normalize()
00243 p = pl.getPlacement()
00244 del pl
00245 return p