WorkingPlane.py

Go to the documentation of this file.
00001 #***************************************************************************
00002 #*                                                                         *
00003 #*   Copyright (c) 2009, 2010                                              *
00004 #*   Ken Cline <cline@frii.com>                                            *
00005 #*                                                                         *
00006 #*   This program is free software; you can redistribute it and/or modify  *
00007 #*   it under the terms of the GNU General Public License (GPL)            *
00008 #*   as published by the Free Software Foundation; either version 2 of     *
00009 #*   the License, or (at your option) any later version.                   *
00010 #*   for detail see the LICENCE text file.                                 *
00011 #*                                                                         *
00012 #*   This program is distributed in the hope that it will be useful,       *
00013 #*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00014 #*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00015 #*   GNU Library General Public License for more details.                  *
00016 #*                                                                         *
00017 #*   You should have received a copy of the GNU Library General Public     *
00018 #*   License along with this program; if not, write to the Free Software   *
00019 #*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
00020 #*   USA                                                                   *
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                 # keep track of active document.  Reset view when doc changes.
00043                 self.doc = None
00044                 # self.weak is true if the plane has been defined by self.setup or has been reset
00045                 self.weak = True
00046                 # u, v axes and position define plane, perpendicular axis is handy, though redundant.
00047                 self.u = None
00048                 self.v = None
00049                 self.axis = None
00050                 self.position = None
00051                 # a placeholder for a stored state
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                 # FreeCAD.Console.PrintMessage("(position = " + str(self.position) + ")\n")
00118                 # FreeCAD.Console.PrintMessage("Current workplane: x="+str(fcvec.rounded(self.u))+" y="+str(fcvec.rounded(self.v))+" z="+str(fcvec.rounded(self.axis))+"\n")
00119 
00120         def alignToCurve(self, shape, offset):
00121                 if shape.ShapeType == 'Edge':
00122                         #??? TODO: process curve here.  look at shape.edges[0].Curve
00123                         return False
00124                 elif shape.ShapeType == 'Wire':
00125                         #??? TODO: determine if edges define a plane
00126                         return False
00127                 else:
00128                         return False
00129 
00130         def alignToFace(self, shape, offset=0):
00131                 # Set face to the unique selected face, if found
00132                 if shape.ShapeType == 'Face':
00133                         #we should really use face.tangentAt to get u and v here, and implement alignToUVPoint
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                         # len(sex) > 2, look for point and line, three points, etc.
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

Generated on Wed Nov 23 19:01:12 2011 for FreeCAD by  doxygen 1.6.1