fcgeo.py

Go to the documentation of this file.
00001 #***************************************************************************
00002 #*                                                                         *
00003 #*   Copyright (c) 2009, 2010                                              *
00004 #*   Yorik van Havre <yorik@gmx.fr>, 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 __title__="FreeCAD Draft Workbench - Geometry library"
00025 __author__ = "Yorik van Havre, Jacques-Antoine Gaudin, Ken Cline"
00026 __url__ = ["http://free-cad.sourceforge.net"]
00027 
00028 "this file contains generic geometry functions for manipulating Part shapes"
00029 
00030 import FreeCAD, Part, fcvec, math, cmath, FreeCADGui
00031 from FreeCAD import Vector
00032 
00033 NORM = Vector(0,0,1) # provisory normal direction for all geometry ops.
00034 
00035 params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
00036 precision = params.GetInt("precision")
00037 
00038 # Generic functions *********************************************************
00039 
00040 
00041 def vec(edge):
00042         "vec(edge) or vec(line) -- returns a vector from an edge or a Part.line"
00043         # if edge is not straight, you'll get strange results!
00044         if isinstance(edge,Part.Shape):
00045                 return edge.Vertexes[-1].Point.sub(edge.Vertexes[0].Point)
00046         elif isinstance(edge,Part.Line):
00047                 return edge.EndPoint.sub(edge.StartPoint)
00048         else:
00049                 return None
00050 
00051 def edg(p1,p2):
00052         "edg(Vector,Vector) -- returns an edge from 2 vectors"
00053         if isinstance(p1,FreeCAD.Vector) and isinstance(p2,FreeCAD.Vector):
00054                 if fcvec.equals(p1,p2): return None
00055                 else: return Part.Line(p1,p2).toShape()
00056 
00057 def getVerts(shape):
00058         "getVerts(shape) -- returns a list containing vectors of each vertex of the shape"
00059         p = []
00060         for v in shape.Vertexes:
00061                 p.append(v.Point)
00062         return p
00063 
00064 def v1(edge):
00065         "v1(edge) -- returns the first point of an edge"
00066         return edge.Vertexes[0].Point
00067 
00068 def isNull(something):
00069         '''returns true if the given shape is null or the given placement is 0 or
00070         if the given vector is (0,0,0)'''
00071         if isinstance(something,Part.Shape):
00072                 return something.isNull()
00073         elif isinstance(something,FreeCAD.Vector):
00074                 if something == Vector(0,0,0):
00075                         return True
00076                 else:
00077                         return False
00078         elif isinstance(something,FreeCAD.Placement):
00079                 if (something.Base == Vector(0,0,0)) and (something.Rotation.Q == (0,0,0,1)):
00080                         return True
00081                 else:
00082                         return False
00083 
00084 def isPtOnEdge(pt,edge) :
00085         '''isPtOnEdge(Vector,edge) -- Tests if a point is on an edge'''
00086         if isinstance(edge.Curve,Part.Line) :
00087                 orig = edge.Vertexes[0].Point
00088                 if fcvec.isNull(pt.sub(orig).cross(vec(edge))) :
00089                         return pt.sub(orig).Length <= vec(edge).Length and pt.sub(orig).dot(vec(edge)) >= 0
00090                 else : 
00091                         return False
00092                 
00093         elif isinstance(edge.Curve,Part.Circle) :
00094                 center = edge.Curve.Center
00095                 axis   = edge.Curve.Axis ; axis.normalize()
00096                 radius = edge.Curve.Radius
00097                 if  round(pt.sub(center).dot(axis),precision) == 0 \
00098                 and round(pt.sub(center).Length - radius,precision) == 0 :
00099                         if len(edge.Vertexes) == 1 :
00100                                 return True # edge is a complete circle
00101                         else :
00102                                 begin = edge.Vertexes[0].Point
00103                                 end   = edge.Vertexes[-1].Point
00104                                 if fcvec.isNull(pt.sub(begin)) or fcvec.isNull(pt.sub(end)) :
00105                                         return True
00106                                 else :
00107                                         # newArc = Part.Arc(begin,pt,end)
00108                                         # return fcvec.isNull(newArc.Center.sub(center)) \
00109                                         #    and fcvec.isNull(newArc.Axis-axis) \
00110                                         #    and round(newArc.Radius-radius,precision) == 0
00111                                         angle1 = fcvec.angle(begin.sub(center))
00112                                         angle2 = fcvec.angle(end.sub(center))
00113                                         anglept = fcvec.angle(pt.sub(center))
00114                                         if (angle1 < anglept) and (anglept < angle2):
00115                                                 return True
00116         return False
00117 
00118 def hasCurves(shape):
00119         "checks if the given shape has curves"
00120         for e in shape.Edges:
00121                 if not isInstance(e.Curve,Part.Line):
00122                         return True
00123         return False
00124         
00125 # edge functions *****************************************************************
00126 
00127 def findEdge(anEdge,aList):
00128         '''findEdge(anEdge,aList): returns True if anEdge is found in aList of edges'''
00129         for e in range(len(aList)):
00130                 if str(anEdge.Curve) == str(aList[e].Curve):
00131                         if fcvec.equals(anEdge.Vertexes[0].Point,aList[e].Vertexes[0].Point):
00132                                 if fcvec.equals(anEdge.Vertexes[-1].Point,aList[e].Vertexes[-1].Point):
00133                                         return(e)
00134         return None
00135         
00136 
00137 def findIntersection(edge1,edge2,infinite1=False,infinite2=False,ex1=False,ex2=False) :
00138         '''findIntersection(edge1,edge2,infinite1=False,infinite2=False):
00139         returns a list containing the intersection point(s) of 2 edges.
00140         You can also feed 4 points instead of edge1 and edge2'''
00141 
00142         pt1 = None
00143         
00144         if isinstance(edge1,FreeCAD.Vector) and isinstance(edge2,FreeCAD.Vector):
00145                 # we got points directly
00146                 pt1 = edge1
00147                 pt2 = edge2
00148                 pt3 = infinite1
00149                 pt4 = infinite2
00150                 infinite1 = ex1
00151                 infinite2 = ex2
00152 
00153         elif isinstance(edge1.Curve,Part.Line) and isinstance(edge2.Curve,Part.Line) :  
00154                 # we have 2 edges       
00155                 pt1, pt2, pt3, pt4 = [edge1.Vertexes[0].Point,
00156                                       edge1.Vertexes[1].Point,
00157                                       edge2.Vertexes[0].Point,
00158                                       edge2.Vertexes[1].Point]
00159 
00160         if pt1:
00161                 # first check if we don't already have coincident endpoints
00162                 if (pt1 in [pt3,pt4]):
00163                         return [pt1]
00164                 elif (pt2 in [pt3,pt4]):
00165                         return [pt2]
00166                 
00167                 #we have 2 straight lines                 
00168                 if fcvec.isNull(pt2.sub(pt1).cross(pt3.sub(pt1)).cross(pt2.sub(pt4).cross(pt3.sub(pt4)))):
00169                         vec1 = pt2.sub(pt1) ; vec2 = pt4.sub(pt3)
00170                         if fcvec.isNull(vec1) or fcvec.isNull(vec2):
00171                                 return []
00172                         vec1.normalize()  ; vec2.normalize()
00173                         cross = vec1.cross(vec2)
00174                         if not fcvec.isNull(cross) :
00175                                 k = ((pt3.z-pt1.z)*(vec2.x-vec2.y)+(pt3.y-pt1.y)*(vec2.z-vec2.x)+ \
00176                                          (pt3.x-pt1.x)*(vec2.y-vec2.z))/(cross.x+cross.y+cross.z)
00177                                 vec1.scale(k,k,k)
00178                                 int = pt1.add(vec1)
00179                                 
00180                                 if  infinite1 == False and not isPtOnEdge(int,edge1) :
00181                                         return []
00182                                         
00183                                 if  infinite2 == False and not isPtOnEdge(int,edge2) :
00184                                         return []
00185                                         
00186                                 return [int]
00187                         else :
00188                                 return [] # Lines have same direction
00189                 else :
00190                         return [] # Lines aren't on same plane
00191                         
00192         elif isinstance(edge1.Curve,Part.Circle) and isinstance(edge2.Curve,Part.Line) \
00193           or isinstance(edge1.Curve,Part.Line)   and isinstance(edge2.Curve,Part.Circle) :
00194                 
00195                 # deals with an arc or circle and a line
00196                 
00197                 edges = [edge1,edge2]
00198                 for edge in edges :
00199                         if isinstance(edge.Curve,Part.Line) :
00200                                 line = edge
00201                         else :
00202                                 arc  = edge
00203                                 
00204                 dirVec = vec(line) ; dirVec.normalize()
00205                 pt1    = line.Vertexes[0].Point
00206                 pt2    = line.Vertexes[1].Point
00207                 pt3    = arc.Vertexes[0].Point
00208                 pt4    = arc.Vertexes[-1].Point
00209                 center = arc.Curve.Center
00210 
00211                 # first check for coincident endpoints
00212                 if (pt1 in [pt3,pt4]):
00213                         return [pt1]
00214                 elif (pt2 in [pt3,pt4]):
00215                         return [pt2]
00216                 
00217                 if fcvec.isNull(pt1.sub(center).cross(pt2.sub(center)).cross(arc.Curve.Axis)) :
00218                         # Line and Arc are on same plane
00219                         
00220                         dOnLine = center.sub(pt1).dot(dirVec)
00221                         onLine  = Vector(dirVec) ; onLine.scale(dOnLine,dOnLine,dOnLine)
00222                         toLine  = pt1.sub(center).add(onLine)
00223                         
00224                         if toLine.Length < arc.Curve.Radius :
00225                                 dOnLine = (arc.Curve.Radius**2 - toLine.Length**2)**(0.5)
00226                                 onLine  = Vector(dirVec) ; onLine.scale(dOnLine,dOnLine,dOnLine)
00227                                 int = [center.add(toLine).add(onLine)]
00228                                 onLine  = Vector(dirVec) ; onLine.scale(-dOnLine,-dOnLine,-dOnLine)
00229                                 int += [center.add(toLine).add(onLine)]
00230                         elif round(toLine.Length-arc.Curve.Radius,precision) == 0 :
00231                                 int = [center.add(toLine)]
00232                         else :
00233                                 return []
00234                                 
00235                 else : # Line isn't on Arc's plane
00236                         if dirVec.dot(arc.Curve.Axis) != 0 :
00237                                 toPlane  = Vector(arc.Curve.Axis) ; toPlane.normalize()
00238                                 d = vec1.dot(toPlane)
00239                                 dToPlane = center.sub(pt1).dot(toPlane)
00240                                 toPlane = Vector(vec1)
00241                                 toPlane.scale(dToPlane/d,dToPlane/d,dToPlane/d)
00242                                 ptOnPlane = toPlane.add(pt1)
00243                                 if round(ptOnPlane.sub(center).Length - arc.Curve.Radius,precision) == 0 :
00244                                         int = [ptOnPlane]
00245                                 else :
00246                                         return []
00247                         else :
00248                                 return []
00249                                 
00250                 if infinite1 == False :
00251                         for i in range(len(int)-1,-1,-1) :
00252                                 if not isPtOnEdge(int[i],edge1) :
00253                                         del int[i]
00254                 if infinite2 == False :
00255                         for i in range(len(int)-1,-1,-1) :
00256                                 if not isPtOnEdge(int[i],edge2) :
00257                                         del int[i]
00258                 return int
00259                 
00260         elif isinstance(edge1.Curve,Part.Circle) and isinstance(edge2.Curve,Part.Circle) :
00261                 
00262                 # deals with 2 arcs or circles
00263 
00264                 cent1, cent2 = edge1.Curve.Center, edge2.Curve.Center
00265                 rad1 , rad2  = edge1.Curve.Radius, edge2.Curve.Radius
00266                 axis1, axis2 = edge1.Curve.Axis  , edge2.Curve.Axis
00267                 c2c          = cent2.sub(cent1)
00268                 
00269                 if fcvec.isNull(axis1.cross(axis2)) :
00270                         if round(c2c.dot(axis1),precision) == 0 :
00271                                 # circles are on same plane
00272                                 dc2c = c2c.Length ;
00273                                 if not fcvec.isNull(c2c): c2c.normalize()
00274                                 if round(rad1+rad2-dc2c,precision) < 0 \
00275                                 or round(rad1-dc2c-rad2,precision) > 0 or round(rad2-dc2c-rad1,precision) > 0 :
00276                                         return []
00277                                 else :
00278                                         norm = c2c.cross(axis1)
00279                                         if not fcvec.isNull(norm): norm.normalize()
00280                                         if fcvec.isNull(norm): x = 0
00281                                         else: x = (dc2c**2 + rad1**2 - rad2**2)/(2*dc2c)
00282                                         y = abs(rad1**2 - x**2)**(0.5)
00283                                         c2c.scale(x,x,x)
00284                                         if round(y,precision) != 0 :
00285                                                 norm.scale(y,y,y)
00286                                                 int =  [cent1.add(c2c).add(norm)]
00287                                                 int += [cent1.add(c2c).sub(norm)]
00288                                         else :
00289                                                 int = [cent1.add(c2c)]
00290                         else :
00291                                 return [] # circles are on parallel planes
00292                 else :
00293                         # circles aren't on same plane
00294                         axis1.normalize() ; axis2.normalize()
00295                         U = axis1.cross(axis2)
00296                         V = axis1.cross(U)
00297                         dToPlane = c2c.dot(axis2)
00298                         d        = V.add(cent1).dot(axis2)
00299                         V.scale(dToPlane/d,dToPlane/d,dToPlane/d)
00300                         PtOn2Planes = V.add(cent1)
00301                         planeIntersectionVector = U.add(PtOn2Planes)
00302                         intTemp = findIntersection(planeIntersectionVector,edge1,True,True)
00303                         int = []
00304                         for pt in intTemp :
00305                                 if round(pt.sub(cent2).Length-rad2,precision) == 0 :
00306                                         int += [pt]
00307                                         
00308                 if infinite1 == False :
00309                         for i in range(len(int)-1,-1,-1) :
00310                                 if not isPtOnEdge(int[i],edge1) :
00311                                         del int[i]
00312                 if infinite2 == False :
00313                         for i in range(len(int)-1,-1,-1) :
00314                                 if not isPtOnEdge(int[i],edge2) :
00315                                         del int[i]
00316                 
00317                 return int
00318         else :
00319                 print "fcgeo: Unsupported curve type: (" + str(edge1.Curve) + ", " + str(edge2.Curve) + ")"
00320 
00321 
00322 def mirror (point, edge):
00323         "finds mirror point relative to an edge"
00324         normPoint = point.add(findDistance(point, edge, False))
00325         if normPoint:
00326                 normPoint_point = Vector.sub(point, normPoint)
00327                 normPoint_refl = fcvec.neg(normPoint_point)
00328                 refl = Vector.add(normPoint, normPoint_refl)
00329                 return refl
00330         else:
00331                 return None
00332 
00333 def findClosest(basepoint,pointslist):
00334         '''
00335         findClosest(vector,list)
00336         in a list of 3d points, finds the closest point to the base point.
00337         an index from the list is returned.
00338         '''
00339         if not pointslist: return None
00340         smallest = 100000
00341         for n in range(len(pointslist)):
00342                 new = basepoint.sub(pointslist[n]).Length
00343                 if new < smallest:
00344                         smallest = new
00345                         npoint = n
00346         return npoint
00347 
00348 def concatenate(shape):
00349         "concatenate(shape) -- turns several faces into one"
00350         edges = getBoundary(shape)
00351         edges = sortEdges(edges)
00352         try:
00353                 wire=Part.Wire(edges)
00354                 face=Part.Face(wire)
00355         except:
00356                 print "fcgeo: Couldn't join faces into one"
00357                 return(shape)
00358         else:
00359                 if not wire.isClosed(): return(wire)
00360                 else: return(face)
00361 
00362 def getBoundary(shape):
00363         "getBoundary(shape) -- this function returns the boundary edges of a group of faces"
00364         # make a lookup-table where we get the number of occurrences
00365         # to each edge in the fused face
00366         if isinstance(shape,list):
00367                 shape = Part.makeCompound(shape)
00368         lut={}
00369         for f in shape.Faces:
00370                 for e in f.Edges:
00371                         hc= e.hashCode()
00372                         if lut.has_key(hc): lut[hc]=lut[hc]+1
00373                         else: lut[hc]=1
00374         # filter out the edges shared by more than one sub-face
00375         bound=[]
00376         for e in shape.Edges:
00377                 if lut[e.hashCode()] == 1: bound.append(e)
00378         return bound
00379 
00380 def sortEdges(lEdges, aVertex=None):
00381         "an alternative, more accurate version of Part.__sortEdges__"
00382 
00383         #There is no reason to limit this to lines only because every non-closed edge always
00384         #has exactly two vertices (wmayer)
00385         #for e in lEdges:
00386         #        if not isinstance(e.Curve,Part.Line):
00387         #                print "Warning: sortedges cannot treat wired containing curves yet."
00388         #                return lEdges
00389         
00390         def isSameVertex(V1, V2):
00391                 ''' Test if vertexes have same coordinates with precision 10E(-precision)'''
00392                 if round(V1.X-V2.X,1)==0 and round(V1.Y-V2.Y,1)==0 and round(V1.Z-V2.Z,1)==0 :
00393                         return True
00394                 else :
00395                         return False
00396         
00397         def lookfor(aVertex, inEdges):
00398                 ''' Look for (aVertex, inEdges) returns count, the position of the instance
00399                         the position in the instance and the instance of the Edge'''
00400                 count = 0
00401                 linstances = [] #lists the instances of aVertex
00402                 for i in range(len(inEdges)) :
00403                         for j in range(2) :
00404                                 if isSameVertex(aVertex,inEdges[i].Vertexes[j-1]):
00405                                         instance = inEdges[i]
00406                                         count += 1
00407                                         linstances += [i,j-1,instance]
00408                 return [count]+linstances
00409         
00410         if (len(lEdges) < 2):
00411                 if aVertex == None:
00412                         return lEdges
00413                 else:
00414                         result = lookfor(aVertex,lEdges)
00415                         if result[0] != 0:
00416                                 if isSameVertex(aVertex,result[3].Vertexes[0]):
00417                                         return lEdges
00418                                 else:
00419                                         if isinstance(result[3].Curve,Part.Line):
00420                                                 return [Part.Line(aVertex.Point,result[3].Vertexes[0].Point).toShape()]
00421                                         elif isinstance(result[3].Curve,Part.Circle):
00422                                                 mp = findMidpoint(result[3])
00423                                                 return [Part.Arc(aVertex.Point,mp,result[3].Vertexes[0].Point).toShape()]
00424                                         else:
00425                                                 return lEdges
00426                                         
00427         olEdges = [] # ol stands for ordered list 
00428         if aVertex == None:
00429                 for i in range(len(lEdges)*2) :
00430                         if len(lEdges[i/2].Vertexes) > 1:
00431                                 result = lookfor(lEdges[i/2].Vertexes[i%2],lEdges)
00432                                 if result[0] == 1 :  # Have we found an end ?
00433                                         olEdges = sortEdges(lEdges, result[3].Vertexes[result[2]])
00434                                         return olEdges
00435                 # if the wire is closed there is no end so choose 1st Vertex
00436                 return sortEdges(lEdges, lEdges[0].Vertexes[0]) 
00437         else :
00438                 result = lookfor(aVertex,lEdges)
00439                 if result[0] != 0 :
00440                         del lEdges[result[1]]
00441                         next = sortEdges(lEdges, result[3].Vertexes[-((-result[2])^1)])
00442                         if isSameVertex(aVertex,result[3].Vertexes[0]):
00443                                 olEdges += [result[3]] + next
00444                         else:
00445                                 if isinstance(result[3].Curve,Part.Line):
00446                                         newedge = Part.Line(aVertex.Point,result[3].Vertexes[0].Point).toShape()
00447                                         olEdges += [newedge] + next
00448                                 elif isinstance(result[3].Curve,Part.Circle):
00449                                         mp = findMidpoint(result[3])
00450                                         newedge = Part.Arc(aVertex.Point,mp,result[3].Vertexes[0].Point).toShape()
00451                                         olEdges += [newedge] + next
00452                                 else:
00453                                         olEdges += [result[3]] + next                                        
00454                         return olEdges
00455                 else :
00456                         return []
00457 
00458                 
00459 def superWire(edgeslist,closed=False):
00460         '''superWire(edges,[closed]): forces a wire between edges that don't necessarily
00461         have coincident endpoints. If closed=True, wire will always be closed'''
00462         def median(v1,v2):
00463                 vd = v2.sub(v1)
00464                 vd.scale(.5,.5,.5)
00465                 return v1.add(vd)
00466         edges = sortEdges(edgeslist)
00467         print edges
00468         newedges = []
00469         for i in range(len(edges)):
00470                 curr = edges[i]
00471                 if i == 0:
00472                         if closed:
00473                                 prev = edges[-1]
00474                         else:
00475                                 prev = None
00476                 else:
00477                         prev = edges[i-1]
00478                 if i == (len(edges)-1):
00479                         if closed:
00480                                 next = edges[0]
00481                         else:
00482                                 next = None
00483                 else:
00484                         next = edges[i+1]
00485                 print i,prev,curr,next
00486                 if prev:
00487                         if curr.Vertexes[0].Point == prev.Vertexes[-1].Point:
00488                                 p1 = curr.Vertexes[0].Point
00489                         else:
00490                                 p1 = median(curr.Vertexes[0].Point,prev.Vertexes[-1].Point)
00491                 else:
00492                         p1 = curr.Vertexes[0].Point
00493                 if next:
00494                         if curr.Vertexes[-1].Point == next.Vertexes[0].Point:
00495                                 p2 = next.Vertexes[0].Point
00496                         else:
00497                                 p2 = median(curr.Vertexes[-1].Point,next.Vertexes[0].Point)
00498                 else:
00499                         p2 = curr.Vertexes[-1].Point
00500                 if isinstance(curr.Curve,Part.Line):
00501                         print "line",p1,p2
00502                         newedges.append(Part.Line(p1,p2).toShape())
00503                 elif isinstance(curr.Curve,Part.Circle):
00504                         p3 = findMidpoint(curr)
00505                         print "arc",p1,p3,p2
00506                         newedges.append(Part.Arc(p1,p3,p2).toShape())
00507                 else:
00508                         print "Cannot superWire edges that are not lines or arcs"
00509                         return None
00510         print newedges
00511         return Part.Wire(newedges)
00512 
00513 def findMidpoint(edge):
00514         "calculates the midpoint of an edge"
00515         first = edge.Vertexes[0].Point
00516         last = edge.Vertexes[-1].Point
00517         if isinstance(edge.Curve,Part.Circle):
00518                 center = edge.Curve.Center
00519                 radius = edge.Curve.Radius
00520                 if len(edge.Vertexes) == 1:
00521                         # Circle
00522                         dv = first.sub(center)
00523                         dv = fcvec.neg(dv)
00524                         return center.add(dv)
00525                 axis = edge.Curve.Axis
00526                 chord = last.sub(first)
00527                 perp = chord.cross(axis)
00528                 perp.normalize()
00529                 ray = first.sub(center)
00530                 apothem = ray.dot(perp)
00531                 sagitta = radius - apothem
00532                 startpoint = Vector.add(first, fcvec.scale(chord,0.5))
00533                 endpoint = fcvec.scaleTo(perp,sagitta)
00534                 return Vector.add(startpoint,endpoint)
00535 
00536         elif isinstance(edge.Curve,Part.Line):
00537                 halfedge = fcvec.scale(last.sub(first),.5)
00538                 return Vector.add(first,halfedge)
00539 
00540         else:
00541                 return None
00542 
00543 def complexity(obj):
00544         '''
00545         tests given object for shape complexity:
00546         1: line
00547         2: arc
00548         3: circle
00549         4: open wire with no arc
00550         5: closed wire
00551         6: wire with arcs
00552         7: faces
00553         8: faces with arcs
00554         '''
00555         shape = obj.Shape
00556         if shape.Faces:
00557                 for e in shape.Edges:
00558                         if (isinstance(e.Curve,Part.Circle)): return 8
00559                 return 7
00560         if shape.Wires:
00561                 for e in shape.Edges:
00562                         if (isinstance(e.Curve,Part.Circle)): return 6
00563                 for w in shape.Wires:
00564                         if w.isClosed(): return 5
00565                 return 4
00566         if (isinstance(shape.Edges[0].Curve,Part.Circle)):
00567                 if len(shape.Vertexes) == 1:
00568                         return 3
00569                 return 2
00570         return 1
00571 
00572 def findPerpendicular(point,edgeslist,force=None):
00573         '''
00574         findPerpendicular(vector,wire,[force]):
00575         finds the shortest perpendicular distance between a point and an edgeslist.
00576         If force is specified, only the edge[force] will be considered, and it will be
00577         considered infinite.
00578         The function will return a list [vector_from_point_to_closest_edge,edge_index]
00579         or None if no perpendicular vector could be found.
00580         '''
00581         if not isinstance(edgeslist,list):
00582                 try:
00583                         edgeslist = edgeslist.Edges
00584                 except:
00585                         return None
00586         if (force == None):
00587                 valid = None
00588                 for edge in edgeslist:
00589                         dist = findDistance(point,edge,strict=True)
00590                         if dist:
00591                                 if not valid: valid = [dist,edgeslist.index(edge)]
00592                                 else:
00593                                         if (dist.Length < valid[0].Length):
00594                                                 valid = [dist,edgeslist.index(edge)]
00595                 return valid
00596         else:
00597                 edge = edgeslist[force]
00598                 dist = findDistance(point,edge)
00599                 if dist: return [dist,force]
00600                 else: return None
00601         return None
00602 
00603 def offset(edge,vector):
00604         '''
00605         offset(edge,vector)
00606         returns a copy of the edge at a certain (vector) distance
00607         if the edge is an arc, the vector will be added at its first point
00608         and a complete circle will be returned
00609         '''
00610         if (not isinstance(edge,Part.Shape)) or (not isinstance(vector,FreeCAD.Vector)):
00611                 return None
00612         if isinstance(edge.Curve,Part.Line):
00613                 v1 = Vector.add(edge.Vertexes[0].Point, vector)
00614                 v2 = Vector.add(edge.Vertexes[-1].Point, vector)
00615                 return Part.Line(v1,v2).toShape()
00616         else:
00617                 rad = edge.Vertexes[0].Point.sub(edge.Curve.Center)
00618                 newrad = Vector.add(rad,vector).Length
00619                 return Part.Circle(edge.Curve.Center,NORM,newrad).toShape()
00620 
00621 def isReallyClosed(wire):
00622         "checks if a wire is really closed"
00623         if len(wire.Edges) == len(wire.Vertexes): return True
00624         v1 = wire.Vertexes[0].Point
00625         v2 = wire.Vertexes[-1].Point
00626         if fcvec.equals(v1,v2): return True
00627         return False
00628 
00629 def getNormal(shape):
00630         "finds the normal of a shape, if possible"
00631         n = Vector(0,0,1)
00632         if shape.ShapeType == "Face":
00633                 n = shape.normalAt(0.5,0.5)
00634         elif shape.ShapeType == "Edge":
00635                 if isinstance(shape.Curve,Part.Circle):
00636                         n = shape.Curve.Axis
00637         else:
00638                 for e in shape.Edges:
00639                         if isinstance(e.Curve,Part.Circle):
00640                                 n = e.Curve.Axis
00641                                 break
00642                         e1 = vec(shape.Edges[0])
00643                         for i in range(1,len(shape.Edges)):
00644                                 e2 = vec(shape.Edges[i])
00645                                 if 0.1 < abs(e1.getAngle(e2)) < 1.56:
00646                                         n = e1.cross(e2).normalize()
00647                                         break
00648         vdir = FreeCADGui.ActiveDocument.ActiveView.getViewDirection()
00649         if n.getAngle(vdir) < 0.78: n = fcvec.neg(n)
00650         return n
00651 
00652 def offsetWire(wire,dvec,bind=False,occ=False):
00653         '''
00654         offsetWire(wire,vector,[bind]): offsets the given wire along the
00655         given vector. The vector will be applied at the first vertex of
00656         the wire. If bind is True (and the shape is open), the original
00657         wire and the offsetted one are bound by 2 edges, forming a face.
00658         '''
00659         edges = sortEdges(wire.Edges)
00660         norm = getNormal(wire)
00661         closed = isReallyClosed(wire)
00662         nedges = []
00663         if occ:
00664                 l=abs(dvec.Length)
00665                 if not l: return None
00666                 if not wire.Wires:
00667                         wire = Part.Wire(edges)
00668                 try:
00669                         off = wire.makeOffset(l)
00670                 except:
00671                         return None
00672                 else:
00673                         return off
00674         for i in range(len(edges)):
00675                 curredge = edges[i]
00676                 delta = dvec
00677                 if i != 0:
00678                         angle = fcvec.angle(vec(edges[0]),vec(curredge),norm)
00679                         delta = fcvec.rotate(delta,angle,norm)
00680                 nedge = offset(curredge,delta)
00681                 nedges.append(nedge)
00682         nedges = connect(nedges,closed)
00683         if bind and not closed:
00684                 e1 = Part.Line(edges[0].Vertexes[0].Point,nedges[0].Vertexes[0].Point).toShape()
00685                 e2 = Part.Line(edges[-1].Vertexes[-1].Point,nedges[-1].Vertexes[-1].Point).toShape()
00686                 alledges = edges.extend(nedges)
00687                 alledges = alledges.extend([e1,e2])
00688                 w = Part.Wire(alledges)
00689                 return w
00690         else:
00691                 return nedges
00692 
00693 def connect(edges,closed=False):
00694         '''connects the edges in the given list by their intersections'''
00695         nedges = []
00696         for i in range(len(edges)):
00697                 curr = edges[i]
00698                 # print "fcgeo.connect edge ",i," : ",curr.Vertexes[0].Point,curr.Vertexes[-1].Point
00699                 if i > 0:
00700                         prev = edges[i-1]
00701                 else:
00702                         if closed:
00703                                 prev = edges[-1]
00704                         else:
00705                                 prev = None
00706                 if i < (len(edges)-1):
00707                         next = edges[i+1]
00708                 else:
00709                         if closed: next = edges[0]
00710                         else:
00711                                 next = None
00712                 if prev:
00713                         # print "debug: fcgeo.connect prev : ",prev.Vertexes[0].Point,prev.Vertexes[-1].Point
00714                         v1 = findIntersection(curr,prev,True,True)[0]
00715                 else:
00716                         v1 = curr.Vertexes[0].Point
00717                 if next:
00718                         # print "debug: fcgeo.connect next : ",next.Vertexes[0].Point,next.Vertexes[-1].Point
00719                         v2 = findIntersection(curr,next,True,True)[0]
00720                 else:
00721                         v2 = curr.Vertexes[-1].Point
00722                 if isinstance(curr.Curve,Part.Line):
00723                         if v1 != v2:
00724                                 nedges.append(Part.Line(v1,v2).toShape())
00725                 elif isinstance(curr.Curve,Part.Circle):
00726                         if v1 != v2:
00727                                 nedges.append(Part.Arc(v1,findMidPoint(curr),v2))
00728         return Part.Wire(nedges)
00729 
00730 def findDistance(point,edge,strict=False):
00731         '''
00732         findDistance(vector,edge,[strict]) - Returns a vector from the point to its
00733         closest point on the edge. If strict is True, the vector will be returned
00734         only if its endpoint lies on the edge.
00735         '''
00736         if isinstance(point, FreeCAD.Vector):
00737                 if isinstance(edge.Curve, Part.Line):
00738                         segment = vec(edge)
00739                         chord = edge.Vertexes[0].Point.sub(point)
00740                         norm = segment.cross(chord)
00741                         perp = segment.cross(norm)
00742                         dist = fcvec.project(chord,perp)
00743                         if not dist: return None
00744                         newpoint = point.add(dist)
00745                         if (dist.Length == 0):
00746                                 return None
00747                         if strict:
00748                                 s1 = newpoint.sub(edge.Vertexes[0].Point)
00749                                 s2 = newpoint.sub(edge.Vertexes[-1].Point)
00750                                 if (s1.Length <= segment.Length) and (s2.Length <= segment.Length):
00751                                         return dist
00752                                 else:
00753                                         return None
00754                         else: return dist
00755                 elif isinstance(edge.Curve, Part.Circle):
00756                         ve1 = edge.Vertexes[0].Point
00757                         if (len(edge.Vertexes) > 1):
00758                                 ve2 = edge.Vertexes[-1].Point
00759                         else:
00760                                 ve2 = None
00761                         center = edge.Curve.Center
00762                         segment = center.sub(point)
00763                         ratio = (segment.Length - edge.Curve.Radius) / segment.Length
00764                         dist = fcvec.scale(segment,ratio)
00765                         newpoint = Vector.add(point, dist)
00766                         if (dist.Length == 0):
00767                                 return None
00768                         if strict and ve2:
00769                                 ang1 = fcvec.angle(ve1.sub(center))
00770                                 ang2 = fcvec.angle(ve2.sub(center))
00771                                 angpt = fcvec.angle(newpoint.sub(center))
00772                                 if ((angpt <= ang2 and angpt >= ang1) or (angpt <= ang1 and angpt >= ang2)):
00773                                         return dist
00774                                 else:
00775                                         return None
00776                         else:
00777                                 return dist
00778                 else:
00779                         print "fcgeo: Couldn't project point"
00780                         return None
00781         else:
00782                 print "fcgeo: Couldn't project point"
00783                 return None
00784 
00785 
00786 def angleBisection(edge1, edge2):
00787         "angleBisection(edge,edge) - Returns an edge that bisects the angle between the 2 edges."
00788         if isinstance(edge1.Curve, Part.Line) and isinstance(edge2.Curve, Part.Line):
00789                 p1 = edge1.Vertexes[0].Point
00790                 p2 = edge1.Vertexes[-1].Point
00791                 p3 = edge2.Vertexes[0].Point
00792                 p4 = edge2.Vertexes[-1].Point
00793                 int = findIntersection(edge1, edge2, True, True)
00794                 if int:
00795                         line1Dir = p2.sub(p1)
00796                         angleDiff = fcvec.angle(line1Dir, p4.sub(p3))
00797                         ang = angleDiff * 0.5
00798                         origin = int[0]
00799                         line1Dir.normalize()
00800                         dir = fcvec.rotate(line1Dir, ang)
00801                         return Part.Line(origin,origin.add(dir)).toShape()
00802                 else:
00803                         diff = p3.sub(p1)
00804                         origin = p1.add(fcvec.scale(diff, 0.5))
00805                         dir = p2.sub(p1); dir.normalize()
00806                         return Part.Line(origin,origin.add(dir)).toShape()
00807         else:
00808                 return None
00809 
00810 def findClosestCircle(point,circles):
00811         "findClosestCircle(Vector, list of circles) -- returns the circle with closest center"
00812         dist = 1000000
00813         closest = None
00814         for c in circles:
00815                 if c.Center.sub(point).Length < dist:
00816                         dist = c.Center.sub(point).Length
00817                         closest = c
00818         return closest
00819 
00820 def isCoplanar(faces):
00821         "checks if all faces in the given list are coplanar"
00822         if len(faces) < 2:
00823                 return True
00824         base =faces[0].normalAt(.5,.5)
00825         for i in range(1,len(faces)):
00826                 normal = faces[i].normalAt(.5,.5)
00827                 if (normal.getAngle(base) > .0001) and (normal.getAngle(base) < 3.1415):
00828                         return False
00829         return True
00830 
00831 def findWires(edges):
00832         '''finds connected edges in the list, and returns a list of lists containing edges
00833         that can be connected'''
00834         def verts(shape):
00835                 return [shape.Vertexes[0].Point,shape.Vertexes[-1].Point]
00836         def group(shapes):
00837                 shapesIn = shapes[:]
00838                 shapesOut = [shapesIn.pop()]
00839                 changed = False
00840                 for s in shapesIn:
00841                         if len(s.Vertexes) < 2:
00842                                 continue
00843                         else:
00844                                 clean = True
00845                                 for v in verts(s):
00846                                         for i in range(len(shapesOut)):
00847                                                 if clean and (v in verts(shapesOut[i])):
00848                                                         shapesOut[i] = Part.Wire(shapesOut[i].Edges+s.Edges)
00849                                                         changed = True
00850                                                         clean = False
00851                                 if clean:
00852                                         shapesOut.append(s)
00853                 return(changed,shapesOut)
00854         working = True
00855         edgeSet = edges
00856         while working:
00857                 result = group(edgeSet)
00858                 working = result[0]
00859                 edgeSet = result[1]
00860         return result[1]
00861 
00862 def getTangent(edge,frompoint=None):
00863         '''
00864         returns the tangent to an edge. If from point is given, it is used to
00865         calculate the tangent (only useful for an arc of course).
00866         '''
00867         if isinstance(edge.Curve,Part.Line):
00868                 return vec(edge)
00869         elif isinstance(edge.Curve,Part.Circle):
00870                 if not frompoint:
00871                         v1 = edge.Vertexes[0].Point.sub(edge.Curve.Center)
00872                 else:
00873                         v1 = frompoint.sub(edge.Curve.Center)
00874                 return v1.cross(edge.Curve.Axis)
00875         return None
00876 
00877 def bind(w1,w2):
00878         '''bind(wire1,wire2): binds 2 wires by their endpoints and
00879         returns a face'''
00880         w3 = Part.Line(w1.Vertexes[0].Point,w2.Vertexes[0].Point).toShape()
00881         w4 = Part.Line(w1.Vertexes[-1].Point,w2.Vertexes[-1].Point).toShape()
00882         return Part.Face(Part.Wire(w1.Edges+[w3]+w2.Edges+[w4]))
00883 
00884 def cleanFaces(shape):
00885         "removes inner edges from coplanar faces"
00886         faceset = shape.Faces
00887         def find(hc):
00888                 "finds a face with the given hashcode"
00889                 for f in faceset:
00890                         if f.hashCode() == hc:
00891                                 return f
00892 
00893         def findNeighbour(hface,hfacelist):
00894                 "finds the first neighbour of a face in a list, and returns its index"
00895                 eset = []
00896                 for e in find(hface).Edges:
00897                         eset.append(e.hashCode())
00898                 for i in range(len(hfacelist)):
00899                         for ee in find(hfacelist[i]).Edges:
00900                                 if ee.hashCode() in eset:
00901                                         return i
00902                 return None
00903         
00904         # build lookup table
00905         lut = {}
00906         for face in faceset:
00907                 for edge in face.Edges:
00908                         if edge.hashCode() in lut:
00909                                 lut[edge.hashCode()].append(face.hashCode())
00910                         else:
00911                                 lut[edge.hashCode()] = [face.hashCode()]
00912         # print "lut:",lut
00913         # take edges shared by 2 faces
00914         sharedhedges = []
00915         for k,v in lut.iteritems():
00916                 if len(v) == 2:
00917                         sharedhedges.append(k)
00918         # print len(sharedhedges)," shared edges:",sharedhedges
00919         # find those with same normals
00920         targethedges = []
00921         for hedge in sharedhedges:
00922                 faces = lut[hedge]
00923                 n1 = find(faces[0]).normalAt(0.5,0.5)
00924                 n2 = find(faces[1]).normalAt(0.5,0.5)
00925                 if n1 == n2:
00926                         targethedges.append(hedge)
00927         # print len(targethedges)," target edges:",targethedges
00928         # get target faces
00929         hfaces = []
00930         for hedge in targethedges:
00931                 for f in lut[hedge]:
00932                         if not f in hfaces:
00933                                 hfaces.append(f)
00934 
00935         # print len(hfaces)," target faces:",hfaces
00936         # sort islands
00937         islands = [[hfaces.pop(0)]]
00938         currentisle = 0
00939         currentface = 0
00940         found = True
00941         while hfaces:
00942                 if not found:
00943                         if len(islands[currentisle]) > (currentface + 1):
00944                                 currentface += 1
00945                                 found = True
00946                         else:
00947                                 islands.append([hfaces.pop(0)])
00948                                 currentisle += 1
00949                                 currentface = 0
00950                                 found = True
00951                 else:
00952                         f = findNeighbour(islands[currentisle][currentface],hfaces)
00953                         if f != None:
00954                                 islands[currentisle].append(hfaces.pop(f))
00955                         else:
00956                                 found = False
00957         # print len(islands)," islands:",islands
00958         # make new faces from islands
00959         newfaces = []
00960         treated = []
00961         for isle in islands:
00962                 treated.extend(isle)
00963                 fset = []
00964                 for i in isle: fset.append(find(i))
00965                 bounds = getBoundary(fset)
00966                 shp = Part.Wire(sortEdges(bounds))
00967                 shp = Part.Face(shp)
00968                 if shp.normalAt(0.5,0.5) != find(isle[0]).normalAt(0.5,0.5):
00969                         shp.reverse()
00970                 newfaces.append(shp)
00971         # print "new faces:",newfaces
00972         # add remaining faces
00973         for f in faceset:
00974                 if not f.hashCode() in treated:
00975                         newfaces.append(f)
00976         # print "final faces"
00977         # finishing
00978         fshape = Part.makeShell(newfaces)
00979         if shape.isClosed():
00980                 fshape = Part.makeSolid(fshape)
00981         return fshape
00982 
00983 
00984 def isCubic(shape):
00985     '''isCubic(shape): verifies if a shape is cubic, that is, has
00986     8 vertices, 6 faces, and all angles are 90 degrees.'''
00987     # first we try fast methods
00988     if len(shape.Vertexes) != 8:
00989         return False
00990     if len(shape.Faces) != 6:
00991         return False
00992     if len(shape.Edges) != 12:
00993         return False
00994     for e in shape.Edges:
00995         if not isinstance(e.Curve,Part.Line):
00996             return False
00997     # if ok until now, let's do more advanced testing
00998     for f in shape.Faces:
00999         if len(f.Edges) != 4: return False
01000         for i in range(4):
01001             e1 = vec(f.Edges[i])
01002             if i < 3:
01003                 e2 = vec(f.Edges[i+1])
01004             else: e2 = vec(f.Edges[0])
01005             rpi = [0.0,round(math.pi/2,precision)]
01006             if not round(e1.getAngle(e2),precision) in rpi:
01007                 return False
01008     return True
01009 
01010 def getCubicDimensions(shape):
01011     '''getCubicDimensions(shape): returns a list containing the placement,
01012     the length, the width and the height of a cubic shape. If not cubic, nothing
01013     is returned. The placement point is the lowest corner of the shape.'''
01014     if not isCubic(shape): return None
01015     # determine lowest face, which will be our base
01016     z = [10,1000000000000]
01017     for i in range(len(shape.Faces)):
01018         if shape.Faces[i].CenterOfMass.z < z[1]:
01019             z = [i,shape.Faces[i].CenterOfMass.z]
01020     if z[0] > 5: return None
01021     base = shape.Faces[z[0]]
01022     basepoint = base.Edges[0].Vertexes[0].Point
01023     plpoint = base.CenterOfMass
01024     basenorm = base.normalAt(0.5,0.5)
01025     # getting length and width
01026     vx = vec(base.Edges[0])
01027     vy = vec(base.Edges[1])
01028     # getting rotations
01029     rotZ = fcvec.angle(vx)
01030     rotY = fcvec.angle(vx,FreeCAD.Vector(vx.x,vx.y,0))
01031     rotX = fcvec.angle(vy,FreeCAD.Vector(vy.x,vy.y,0))
01032     # getting height
01033     vz = None
01034     rpi = round(math.pi/2,precision)
01035     for i in range(1,6):
01036         for e in shape.Faces[i].Edges:
01037             if basepoint in [e.Vertexes[0].Point,e.Vertexes[1].Point]:
01038                 vtemp = vec(e)
01039                 # print vtemp
01040                 if round(vtemp.getAngle(vx),precision) == rpi:
01041                     if round(vtemp.getAngle(vy),precision) == rpi:
01042                         vz = vtemp
01043     if not vz: return None
01044     mat = FreeCAD.Matrix()
01045     mat.move(plpoint)
01046     mat.rotateX(rotX)
01047     mat.rotateY(rotY)
01048     mat.rotateZ(rotZ)
01049     return [FreeCAD.Placement(mat),round(vx.Length,precision),round(vy.Length,precision),round(vz.Length,precision)]
01050 
01051 def removeInterVertices(wire):
01052         '''removeInterVertices(wire) - remove unneeded vertices (those that
01053         are in the middle of a straight line) from a wire, returns a new wire.'''
01054         edges = sortEdges(wire.Edges)
01055         nverts = []
01056         def getvec(v1,v2):
01057                 if not abs(round(v1.getAngle(v2),precision) in [0,round(math.pi,precision)]):
01058                         nverts.append(edges[i].Vertexes[-1].Point)
01059         for i in range(len(edges)-1):
01060                 vA = vec(edges[i])
01061                 vB = vec(edges[i+1])
01062                 getvec(vA,vB)
01063         vA = vec(edges[-1])
01064         vB = vec(edges[0])
01065         getvec(vA,vB)
01066         if nverts:
01067                 if wire.isClosed():
01068                         nverts.append(nverts[0])
01069                 w = Part.makePolygon(nverts)
01070                 return w
01071         else:
01072                 return wire
01073 
01074 def arcFromSpline(edge):
01075         """arcFromSpline(edge): turns the given edge into an arc, by taking
01076         its first point, midpoint and endpoint. Works best with bspline
01077         segments such as those from imported svg files. Use this only
01078         if you are sure your edge is really an arc..."""
01079         if isinstance(edge.Curve,Part.Line):
01080                 print "This edge is straight, cannot build an arc on it"
01081                 return None
01082         if len(edge.Vertexes) > 1:
01083                 # 2-point arc
01084                 p1 = edge.Vertexes[0].Point
01085                 p2 = edge.Vertexes[-1].Point
01086                 ml = edge.Length/2
01087                 p3 = edge.valueAt(ml)
01088                 try:
01089                         return Part.Arc(p1,p3,p2).toShape()
01090                 except:
01091                         print "Couldn't make an arc out of this edge"
01092                         return None
01093         else:
01094                 # circle
01095                 p1 = edge.Vertexes[0].Point
01096                 ml = edge.Length/2
01097                 p2 = edge.valueAt(ml)
01098                 ray = p2.sub(p1)
01099                 ray.scale(.5,.5,.5)
01100                 center = p1.add(ray)
01101                 radius = ray.Length
01102                 try:
01103                         return Part.makeCircle(radius,center)
01104                 except:
01105                         print "couldn't make a circle out of this edge"
01106    
01107 # circle functions *********************************************************
01108 
01109 def getBoundaryAngles(angle,alist):
01110         '''returns the 2 closest angles from the list that
01111         encompass the given angle'''
01112         negs = True
01113         while negs:
01114                 negs = False
01115                 for i in range(len(alist)):
01116                         if alist[i] < 0:
01117                                 alist[i] = 2*math.pi + alist[i]
01118                                 negs = True
01119                 if angle < 0:
01120                         angle = 2*math.pi + angle
01121                         negs = True
01122         lower = None
01123         for a in alist:
01124                 if a < angle:
01125                         if lower == None:
01126                                 lower = a
01127                         else:
01128                                 if a > lower:
01129                                         lower = a
01130         if lower == None:
01131                 lower = 0
01132                 for a in alist:
01133                         if a > lower:
01134                                 lower = a
01135         higher = None
01136         for a in alist:
01137                 if a > angle:
01138                         if higher == None:
01139                                 higher = a
01140                         else:
01141                                 if a < higher:
01142                                         higher = a
01143         if higher == None:
01144                 higher = 2*math.pi
01145                 for a in alist:
01146                         if a < higher:
01147                                 higher = a
01148         return (lower,higher)
01149                         
01150 
01151 def circleFrom2tan1pt(tan1, tan2, point):
01152         "circleFrom2tan1pt(edge, edge, Vector)"
01153         if isinstance(tan1.Curve, Part.Line) and isinstance(tan2.Curve, Part.Line) and isinstance(point, FreeCAD.Vector):
01154                 return circlefrom2Lines1Point(tan1, tan2, point)
01155         elif isinstance(tan1.Curve, Part.Circle) and isinstance(tan2.Curve, Part.Line) and isinstance(point, FreeCAD.Vector):
01156                 return circlefromCircleLinePoint(tan1, tan2, point)
01157         elif isinstance(tan2.Curve, Part.Circle) and isinstance(tan1.Curve, Part.Line) and isinstance(point, FreeCAD.Vector):
01158                 return circlefromCircleLinePoint(tan2, tan1, point)
01159         elif isinstance(tan2.Curve, Part.Circle) and isinstance(tan1.Curve, Part.Circle) and isinstance(point, FreeCAD.Vector):
01160                 return circlefrom2Circles1Point(tan2, tan1, point)
01161 
01162 def circleFrom2tan1rad(tan1, tan2, rad):
01163         "circleFrom2tan1rad(edge, edge, float)"
01164         if isinstance(tan1.Curve, Part.Line) and isinstance(tan2.Curve, Part.Line):
01165                 return circleFrom2LinesRadius(tan1, tan2, rad)
01166         elif isinstance(tan1.Curve, Part.Circle) and isinstance(tan2.Curve, Part.Line):
01167                 return circleFromCircleLineRadius(tan1, tan2, rad)
01168         elif isinstance(tan1.Curve, Part.Line) and isinstance(tan2.Curve, Part.Circle):
01169                 return circleFromCircleLineRadius(tan2, tan1, rad)
01170         elif isinstance(tan1.Curve, Part.Circle) and isinstance(tan2.Curve, Part.Circle):
01171                 return circleFrom2CirclesRadius(tan1, tan2, rad)
01172 
01173 def circleFrom1tan2pt(tan1, p1, p2):
01174         if isinstance(tan1.Curve, Part.Line) and isinstance(p1, FreeCAD.Vector) and isinstance(p2, FreeCAD.Vector):
01175                 return circlefrom1Line2Points(tan1, p1, p2)
01176         if isinstance(tan1.Curve, Part.Line) and isinstance(p1, FreeCAD.Vector) and isinstance(p2, FreeCAD.Vector):
01177                 return circlefrom1Circle2Points(tan1, p1, p2)
01178 
01179 def circleFrom1tan1pt1rad(tan1, p1, rad):
01180         if isinstance(tan1.Curve, Part.Line) and isinstance(p1, FreeCAD.Vector):
01181                 return circleFromPointLineRadius(p1, tan1, rad)
01182         if isinstance(tan1.Curve, Part.Circle) and isinstance(p1, FreeCAD.Vector):
01183                 return circleFromPointCircleRadius(p1, tan1, rad)
01184 
01185 def circleFrom3tan(tan1, tan2, tan3):
01186         tan1IsLine = isinstance(tan1.Curve, Part.Line)
01187         tan2IsLine = isinstance(tan2.Curve, Part.Line)
01188         tan3IsLine = isinstance(tan3.Curve, Part.Line)
01189         tan1IsCircle = isinstance(tan1.Curve, Part.Circle)
01190         tan2IsCircle = isinstance(tan2.Curve, Part.Circle)
01191         tan3IsCircle = isinstance(tan3.Curve, Part.Circle)
01192         if tan1IsLine and tan2IsLine and tan3IsLine:
01193                 return circleFrom3LineTangents(tan1, tan2, tan3)
01194         elif tan1IsCircle and tan2IsCircle and tan3IsCircle:
01195                 return circleFrom3CircleTangents(tan1, tan2, tan3)
01196         elif (tan1IsCircle and tan2IsLine and tan3IsLine):
01197                 return circleFrom1Circle2Lines(tan1, tan2, tan3)
01198         elif (tan1IsLine and tan2IsCircle and tan3IsLine):
01199                 return circleFrom1Circle2Lines(tan2, tan1, tan3)
01200         elif (tan1IsLine and tan2IsLine and tan3IsCircle):
01201                 return circleFrom1Circle2Lines(tan3, tan1, tan2)
01202         elif (tan1IsLine and tan2IsCircle and tan3IsCircle):
01203                 return circleFrom2Circle1Lines(tan2, tan3, tan1)
01204         elif (tan1IsCircle and tan2IsLine and tan3IsCircle):
01205                 return circleFrom2Circle1Lines(tan1, tan3, tan2)
01206         elif (tan1IsCircle and tan2IsCircle and tan3IsLine):
01207                 return circleFrom2Circle1Lines(tan1, tan2, tan3)
01208 
01209 def circlefrom2Lines1Point(edge1, edge2, point):
01210         "circlefrom2Lines1Point(edge, edge, Vector)"
01211         bis = angleBisection(edge1, edge2)
01212         if not bis: return None
01213         mirrPoint = mirror(point, bis)
01214         return circlefrom1Line2Points(edge1, point, mirrPoint)
01215 
01216 def circlefrom1Line2Points(edge, p1, p2):
01217         "circlefrom1Line2Points(edge, Vector, Vector)"
01218         p1_p2 = edg(p1, p2)
01219         s = findIntersection(edge, p1_p2, True, True)
01220         if not s: return None
01221         s = s[0]
01222         v1 = p1.sub(s)
01223         v2 = p2.sub(s)
01224         projectedDist = math.sqrt(abs(v1.dot(v2)))
01225         edgeDir = vec(edge); edgeDir.normailze()
01226         projectedCen1 = Vector.add(s, fcvec.scale(edgeDir, projectedDist))
01227         projectedCen2 = Vector.add(s, fcvec.scale(edgeDir, -projectedDist))
01228         perpEdgeDir = edgeDir.cross(Vector(0,0,1))
01229         perpCen1 = Vector.add(projectedCen1, perpEdgeDir)
01230         perpCen2 = Vector.add(projectedCen2, perpEdgeDir)
01231         mid = findMidpoint(p1_p2)
01232         x = fcvec.crossproduct(vec(p1_p2)); x.normalize()
01233         perp_mid = Vector.add(mid, x)
01234         cen1 = findIntersection(edg(projectedCen1, perpCen1), edg(mid, perp_mid), True, True)
01235         cen2 = findIntersection(edg(projectedCen2, perpCen2), edg(mid, perp_mid), True, True)
01236         circles = []
01237         if cen1:
01238                 radius = fcvec.dist(projectedCen1, cen1[0])
01239                 circles.append(Part.Circle(cen1[0], NORM, radius))
01240         if cen2:
01241                 radius = fcvec.dist(projectedCen2, cen2[0])
01242                 circles.append(Part.Circle(cen2[0], NORM, radius))
01243 
01244         if circles: return circles
01245         else: return None
01246 
01247 def circleFrom2LinesRadius (edge1, edge2, radius):
01248         "circleFrom2LinesRadius(edge,edge,radius)"
01249         int = findIntersection(edge1, edge2, True, True)
01250         if not int: return None
01251         int = int[0]
01252         bis12 = angleBisection(edge1,edge2)
01253         bis21 = Part.Line(bis12.Vertexes[0].Point,fcvec.rotate(vec(bis12), math.pi/2.0))
01254         ang12 = abs(fcvec.angle(vec(edge1),vec(edge2)))
01255         ang21 = math.pi - ang12
01256         dist12 = radius / math.sin(ang12 * 0.5)
01257         dist21 = radius / math.sin(ang21 * 0.5)
01258         circles = []
01259         cen = Vector.add(int, fcvec.scale(vec(bis12), dist12))
01260         circles.append(Part.Circle(cen, NORM, radius))
01261         cen = Vector.add(int, fcvec.scale(vec(bis12), -dist12))
01262         circles.append(Part.Circle(cen, NORM, radius))
01263         cen = Vector.add(int, fcvec.scale(vec(bis21), dist21))
01264         circles.append(Part.Circle(cen, NORM, radius))
01265         cen = Vector.add(int, fcvec.scale(vec(bis21), -dist21))
01266         circles.append(Part.Circle(cen, NORM, radius))
01267         return circles
01268 
01269 def circleFrom3LineTangents (edge1, edge2, edge3):
01270         "circleFrom3LineTangents(edge,edge,edge)"
01271         def rot(ed):
01272                 return Part.Line(v1(ed),v1(ed).add(fcvec.rotate(vec(ed),math.pi/2))).toShape()
01273         bis12 = angleBisection(edge1,edge2)
01274         bis23 = angleBisection(edge2,edge3)
01275         bis31 = angleBisection(edge3,edge1)
01276         intersections = []
01277         int = findIntersection(bis12, bis23, True, True)        
01278         if int:
01279                 radius = findDistance(int[0],edge1).Length
01280                 intersections.append(Part.Circle(int[0],NORM,radius))
01281         int = findIntersection(bis23, bis31, True, True)
01282         if int:
01283                 radius = findDistance(int[0],edge1).Length
01284                 intersections.append(Part.Circle(int[0],NORM,radius))
01285         int = findIntersection(bis31, bis12, True, True)
01286         if int:
01287                 radius = findDistance(int[0],edge1).Length
01288                 intersections.append(Part.Circle(int[0],NORM,radius))
01289         int = findIntersection(rot(bis12), rot(bis23), True, True)
01290         if int:
01291                 radius = findDistance(int[0],edge1).Length
01292                 intersections.append(Part.Circle(int[0],NORM,radius))
01293         int = findIntersection(rot(bis23), rot(bis31), True, True)
01294         if int:
01295                 radius = findDistance(int[0],edge1).Length
01296                 intersections.append(Part.Circle(int[0],NORM,radius))
01297         int = findIntersection(rot(bis31), rot(bis12), True, True)
01298         if int:
01299                 radius = findDistance(int[0],edge1).Length
01300                 intersections.append(Part.Circle(int[0],NORM,radius))
01301         circles = []
01302         for int in intersections:
01303                 exists = False
01304                 for cir in circles:
01305                         if fcvec.equals(cir.Center, int.Center):
01306                                 exists = True
01307                                 break
01308                 if not exists:
01309                         circles.append(int)
01310         if circles:
01311                 return circles
01312         else:
01313                 return None
01314 
01315 def circleFromPointLineRadius (point, edge, radius):
01316         "circleFromPointLineRadius (point, edge, radius)"
01317         dist = findDistance(point, edge, False)
01318         center1 = None
01319         center2 = None
01320         if dist.Length == 0:
01321                 segment = vec(edge)
01322                 perpVec = fcvec.crossproduct(segment); perpVec.normalize()
01323                 normPoint_c1 = fcvec.scale(perpVec, radius)
01324                 normPoint_c2 = fcvec.scale(perpVec, -radius)
01325                 center1 = point.add(normPoint_c1)
01326                 center2 = point.add(normPoint_c2)
01327         elif dist.Length > 2 * radius:
01328                 return None
01329         elif dist.Length == 2 * radius:
01330                 normPoint = point.add(findDistance(point, edge, False))
01331                 dummy = fcvec.scale(normPoint.sub(point), 0.5)
01332                 cen = point.add(dummy)
01333                 circ = Part.Circle(cen, NORM, radius)
01334                 if circ:
01335                         return [circ]
01336                 else:
01337                         return None
01338         else:
01339                 normPoint = point.add(findDistance(point, edge, False))
01340                 normDist = fcvec.dist(normPoint, point)
01341                 dist = math.sqrt(radius**2 - (radius - normDist)**2)
01342                 centerNormVec = fcvec.scaleTo(point.sub(normPoint), radius)
01343                 edgeDir = edge.Vertexes[0].Point.sub(normPoint); edgeDir.normalize()
01344                 center1 = centerNormVec.add(normPoint.add(fcvec.scale(edgeDir, dist)))
01345                 center2 = centerNormVec.add(normPoint.add(fcvec.scale(edgeDir, -dist)))
01346         circles = []
01347         if center1:
01348                 circ = Part.Circle(center1, NORM, radius)
01349                 if circ:
01350                         circles.append(circ)
01351         if center2:
01352                 circ = Part.Circle(center2, NORM, radius)
01353                 if circ:
01354                         circles.append(circ)
01355 
01356         if len(circles):
01357                 return circles
01358         else:
01359                 return None
01360 
01361 def circleFrom2PointsRadius(p1, p2, radius):
01362         "circleFrom2PointsRadiust(Vector, Vector, radius)"
01363         if fcvec.equals(p1, p2): return None
01364 
01365         p1_p2 = Part.Line(p1, p2).toShape()
01366         dist_p1p2 = fcvec.dist(p1, p1)
01367         mid = findMidpoint(p1_p2)
01368         if dist_p1p2 == 2*radius:
01369                 circle = Part.Circle(mid, norm, radius)
01370                 if circle: return [circle]
01371                 else: return None
01372         dir = vec(p1_p2); dir.normalize()
01373         perpDir = dir.cross(Vector(0,0,1)); perpDir.normailze()
01374         dist = math.sqrt(radius**2 - (dist_p1p2 / 2.0)**2)
01375         cen1 = Vector.add(mid, fcvec.scale(perpDir, dist))
01376         cen2 = Vector.add(mid, fcvec.scale(perpDir, -dist))
01377         circles = []
01378         if cen1: circles.append(Part.Circle(cen1, norm, radius))
01379         if cen2: circles.append(Part.Circle(cen2, norm, radius))
01380         if circles: return circles
01381         else: return None
01382 
01383 
01384 
01385 #############################33 to include
01386 
01387 
01388 
01389 
01390 
01391 
01392 
01393 def outerSoddyCircle(circle1, circle2, circle3):
01394         '''
01395         Computes the outer soddy circle for three tightly packed circles.
01396         '''
01397         if isinstance(circle1.Curve, Part.Circle) and isinstance(circle2.Curve, Part.Circle) and isinstance(circle3.Curve, Part.Circle):
01398                 # Original Java code Copyright (rc) 2008 Werner Randelshofer
01399                 # Converted to python by Martin Buerbaum 2009
01400                 # http://www.randelshofer.ch/treeviz/
01401                 # Either Creative Commons Attribution 3.0, the MIT license, or the GNU Lesser General License LGPL.
01402 
01403                 A = circle1.Curve.Center
01404                 B = circle2.Curve.Center
01405                 C = circle3.Curve.Center
01406 
01407                 ra = circle1.Curve.Radius
01408                 rb = circle2.Curve.Radius
01409                 rc = circle3.Curve.Radius
01410 
01411                 # Solution using Descartes' theorem, as described here:
01412                 # http://en.wikipedia.org/wiki/Descartes%27_theorem
01413                 k1 = 1 / ra
01414                 k2 = 1 / rb
01415                 k3 = 1 / rc
01416                 k4 = abs(k1 + k2 + k3 - 2 * math.sqrt(k1 * k2 + k2 * k3 + k3 * k1))
01417 
01418                 q1 = (k1 + 0j) * (A.x + A.y * 1j)
01419                 q2 = (k2 + 0j) * (B.x + B.y * 1j)
01420                 q3 = (k3 + 0j) * (C.x + C.y * 1j)
01421 
01422                 temp = ((q1 * q2) + (q2 * q3) + (q3 * q1))
01423                 q4 = q1 + q2 + q3 - ((2 + 0j) * cmath.sqrt(temp) )
01424 
01425                 z = q4 / (k4 + 0j)
01426 
01427                 # If the formula is not solveable, we return no circle.
01428                 if (not z or not (1 / k4)):
01429                         return None
01430 
01431                 X = -z.real
01432                 Y = -z.imag
01433                 print "Outer Soddy circle: " + str(X) + " " + str(Y) + "\n"     # Debug
01434 
01435                 # The Radius of the outer soddy circle can also be calculated with the following formula:
01436                 # radiusOuter = abs(r1*r2*r3 / (r1*r2 + r1*r3 + r2*r3 - 2 * math.sqrt(r1*r2*r3 * (r1+r2+r3))))
01437                 circ = Part.Circle(Vector(X, Y, A.z), norm, 1 / k4)
01438                 return circ
01439 
01440         else:
01441                 print "debug: outerSoddyCircle bad parameters!\n"
01442                 # FreeCAD.Console.PrintMessage("debug: outerSoddyCircle bad parameters!\n")
01443                 return None
01444 
01445 def innerSoddyCircle(circle1, circle2, circle3):
01446         '''
01447         Computes the inner soddy circle for three tightly packed circles.
01448         '''
01449         if isinstance(circle1.Curve, Part.Circle) and isinstance(circle2.Curve, Part.Circle) and isinstance(circle3.Curve, Part.Circle):
01450                 # Original Java code Copyright (rc) 2008 Werner Randelshofer
01451                 # Converted to python by Martin Buerbaum 2009
01452                 # http://www.randelshofer.ch/treeviz/
01453 
01454                 A = circle1.Curve.Center
01455                 B = circle2.Curve.Center
01456                 C = circle3.Curve.Center
01457 
01458                 ra = circle1.Curve.Radius
01459                 rb = circle2.Curve.Radius
01460                 rc = circle3.Curve.Radius
01461 
01462                 # Solution using Descartes' theorem, as described here:
01463                 # http://en.wikipedia.org/wiki/Descartes%27_theorem
01464                 k1 = 1 / ra
01465                 k2 = 1 / rb
01466                 k3 = 1 / rc
01467                 k4 = abs(k1 + k2 + k3 + 2 * math.sqrt(k1 * k2 + k2 * k3 + k3 * k1))
01468 
01469                 q1 = (k1 + 0j) * (A.x + A.y * 1j)
01470                 q2 = (k2 + 0j) * (B.x + B.y * 1j)
01471                 q3 = (k3 + 0j) * (C.x + C.y * 1j)
01472 
01473                 temp = ((q1 * q2) + (q2 * q3) + (q3 * q1))
01474                 q4 = q1 + q2 + q3 + ((2 + 0j) * cmath.sqrt(temp) )
01475 
01476                 z = q4 / (k4 + 0j)
01477 
01478                 # If the formula is not solveable, we return no circle.
01479                 if (not z or not (1 / k4)):
01480                         return None
01481 
01482                 X = z.real
01483                 Y = z.imag
01484                 print "Outer Soddy circle: " + str(X) + " " + str(Y) + "\n"     # Debug
01485 
01486                 # The Radius of the inner soddy circle can also be calculated with the following formula:
01487                 # radiusInner = abs(r1*r2*r3 / (r1*r2 + r1*r3 + r2*r3 + 2 * math.sqrt(r1*r2*r3 * (r1+r2+r3))))
01488                 circ = Part.Circle(Vector(X, Y, A.z), norm, 1 / k4)
01489                 return circ
01490 
01491         else:
01492                 print "debug: innerSoddyCircle bad parameters!\n"
01493                 # FreeCAD.Console.PrintMessage("debug: innerSoddyCircle bad parameters!\n")
01494                 return None
01495  
01496 def circleFrom3CircleTangents(circle1, circle2, circle3):
01497         '''
01498         http://en.wikipedia.org/wiki/Problem_of_Apollonius#Inversive_methods
01499         http://mathworld.wolfram.com/ApolloniusCircle.html
01500         http://mathworld.wolfram.com/ApolloniusProblem.html
01501         '''
01502 
01503         if isinstance(circle1.Curve, Part.Circle) and isinstance(circle2.Curve, Part.Circle) and isinstance(circle3.Curve, Part.Circle):
01504                 int12 = findIntersection(circle1, circle2, True, True)
01505                 int23 = findIntersection(circle2, circle3, True, True)
01506                 int31 = findIntersection(circle3, circle1, True, True)
01507 
01508                 if int12 and int23 and int31:
01509                         if len(int12) == 1 and len(int23) == 1 and len(int31) == 1:
01510                                 # Only one intersection with each circle.
01511                                 # => "Soddy Circle" - 2 solutions.
01512                                 # http://en.wikipedia.org/wiki/Problem_of_Apollonius#Mutually_tangent_given_circles:_Soddy.27s_circles_and_Descartes.27_theorem
01513                                 # http://mathworld.wolfram.com/SoddyCircles.html
01514                                 # http://mathworld.wolfram.com/InnerSoddyCenter.html
01515                                 # http://mathworld.wolfram.com/OuterSoddyCenter.html
01516 
01517                                 r1 = circle1.Curve.Radius
01518                                 r2 = circle2.Curve.Radius
01519                                 r3 = circle3.Curve.Radius
01520                                 outerSoddy = outerSoddyCircle(circle1, circle2, circle3)
01521 #                               print str(outerSoddy) + "\n" # Debug
01522 
01523                                 innerSoddy = innerSoddyCircle(circle1, circle2, circle3)
01524 #                               print str(innerSoddy) + "\n" # Debug
01525 
01526                                 circles = []
01527                                 if outerSoddy:
01528                                         circles.append(outerSoddy)
01529                                 if innerSoddy:
01530                                         circles.append(innerSoddy)
01531                                 return circles
01532 
01533                         # @todo Calc all 6 homothetic centers.
01534                         # @todo Create 3 lines from the inner and 4 from the outer h. center.
01535                         # @todo Calc. the 4 inversion poles of these lines for each circle.
01536                         # @todo Calc. the radical center of the 3 circles.
01537                         # @todo Calc. the intersection points (max. 8) of 4 lines (trough each inversion pole and the radical center) with the circle.
01538                         #       This gives us all the tangent points.
01539                 else:
01540                         # Some circles are inside each other or an error has occured.
01541                         return None
01542 
01543         else:
01544                 print "debug: circleFrom3CircleTangents bad parameters!\n"
01545                 # FreeCAD.Console.PrintMessage("debug: circleFrom3CircleTangents bad parameters!\n")
01546                 return None
01547 
01548 
01549 def linearFromPoints (p1, p2):
01550         '''
01551         Calculate linear equation from points.
01552         Calculate the slope and offset parameters of the linear equation of a line defined by two points.
01553 
01554         Linear equation:
01555         y = m * x + b
01556         m = dy / dx
01557         m ... Slope
01558         b ... Offset (point where the line intersects the y axis)
01559         dx/dy ... Delta x and y. Using both as a vector results in a non-offset direction vector.
01560         '''
01561         if isinstance(p1, Vector) and isinstance(p2, Vector):
01562                 line = {}
01563                 line['dx'] = (p2.x - p1.x)
01564                 line['dy'] = (p2.y - p1.y)
01565                 line['slope'] = line['dy'] / line['dx']
01566                 line['offset'] = p1.y - slope * p1.x
01567                 return line
01568         else:
01569                 return None
01570 
01571 
01572 def determinant (mat,n):
01573         '''
01574         determinant(matrix,int) - Determinat function. Returns the determinant
01575         of a n-matrix. It recursively expands the minors.
01576         '''
01577         matTemp = [[0.0,0.0,0.0],[0.0,0.0,0.0],[0.0,0.0,0.0]]
01578         if (n > 1):
01579                 if n == 2:
01580                         d = mat[0][0] * mat[1][1] - mat[1][0] * mat[0][1]
01581                 else:
01582                         d = 0.0
01583                         for j1 in range(n):
01584                                 # Create minor
01585                                 for i in range(1, n):
01586                                         j2 = 0
01587                                         for j in range(n):
01588                                                 if j == j1:
01589                                                         continue
01590                                                 matTemp[i-1][j2] = mat[i][j]
01591                                                 j2 += 1
01592                                 d += (-1.0)**(1.0 + j1 + 1.0) * mat[0][j1] * determinant(matTemp, n-1)
01593                 return d
01594         else:
01595                 return 0
01596 
01597         
01598 def findHomotheticCenterOfCircles(circle1, circle2):
01599         '''
01600         findHomotheticCenterOfCircles(circle1, circle2)
01601         Calculates the homothetic center(s) of two circles.
01602 
01603         http://en.wikipedia.org/wiki/Homothetic_center
01604         http://mathworld.wolfram.com/HomotheticCenter.html
01605         '''
01606 
01607         if isinstance(circle1.Curve, Part.Circle) and isinstance(circle2.Curve, Part.Circle):
01608                 if fcvec.equals(circle1.Curve.Center, circle2.Curve.Center):
01609                         return None
01610 
01611                 cen1_cen2 = Part.Line(circle1.Curve.Center, circle2.Curve.Center).toShape()
01612                 cenDir = vec(cen1_cen2); cenDir.normalize()
01613 
01614                 # Get the perpedicular vector.
01615                 perpCenDir = cenDir.cross(Vector(0,0,1)); perpCenDir.normalize()
01616 
01617                 # Get point on first circle
01618                 p1 = Vector.add(circle1.Curve.Center, fcvec.scale(perpCenDir, circle1.Curve.Radius))
01619 
01620                 centers = []
01621                 # Calculate inner homothetic center
01622                 # Get point on second circle
01623                 p2_inner = Vector.add(circle1.Curve.Center, fcvec.scale(perpCenDir, -circle1.Curve.Radius))
01624                 hCenterInner = fcvec.intersect(circle1.Curve.Center, circle2.Curve.Center, p1, p2_inner, True, True)
01625                 if hCenterInner:
01626                         centers.append(hCenterInner)
01627 
01628                 # Calculate outer homothetic center (only exists of the circles have different radii)
01629                 if circle1.Curve.Radius != circle2.Curve.Radius:
01630                         # Get point on second circle
01631                         p2_outer = Vector.add(circle1.Curve.Center, fcvec.scale(perpCenDir, circle1.Curve.Radius))
01632                         hCenterOuter = fcvec.intersect(circle1.Curve.Center, circle2.Curve.Center, p1, p2_outer, True, True)
01633                         if hCenterOuter:
01634                                 centers.append(hCenterOuter)
01635 
01636                 if len(centers):
01637                         return centers
01638                 else:
01639                         return None
01640 
01641         else:
01642                 print "debug: findHomotheticCenterOfCircles bad parameters!\n"
01643                 FreeCAD.Console.PrintMessage("debug: findHomotheticCenterOfCirclescleFrom3tan bad parameters!\n")
01644                 return None
01645 
01646 
01647 def findRadicalAxis(circle1, circle2):
01648         '''
01649         Calculates the radical axis of two circles.
01650         On the radical axis (also called power line) of two circles any
01651         tangents drawn from a point on the axis to both circles have the same length.
01652 
01653         http://en.wikipedia.org/wiki/Radical_axis
01654         http://mathworld.wolfram.com/RadicalLine.html
01655 
01656         @sa findRadicalCenter
01657         '''
01658 
01659         if isinstance(circle1.Curve, Part.Circle) and isinstance(circle2.Curve, Part.Circle):
01660                 if fcvec.equals(circle1.Curve.Center, circle2.Curve.Center):
01661                         return None
01662                 r1 = circle1.Curve.Radius
01663                 r2 = circle1.Curve.Radius
01664                 cen1 = circle1.Curve.Center
01665                 # dist .. the distance from cen1 to cen2.
01666                 dist = fcvec.dist(cen1, circle2.Curve.Center)
01667                 cenDir = cen1.sub(circle2.Curve.Center); cenDir.normalize()
01668 
01669                 # Get the perpedicular vector.
01670                 perpCenDir = cenDir.cross(Vector(0,0,1)); perpCenDir.normalize()
01671 
01672                 # J ... The radical center.
01673                 # K ... The point where the cadical axis crosses the line of cen1->cen2.
01674                 # k1 ... Distance from cen1 to K.
01675                 # k2 ... Distance from cen2 to K.
01676                 # dist = k1 + k2
01677 
01678                 k1 = (dist + (r1^2 - r2^2) / dist) / 2.0
01679                 #k2 = dist - k1
01680 
01681                 K = Vector.add(cen1, fcvec.scale(cenDir, k1))
01682 
01683                 # K_ .. A point somewhere between K and J (actually with a distance of 1 unit from K).
01684                 K_ = Vector,add(K, perpCenDir)
01685 
01686                 radicalAxis = Part.Line(K, Vector.add(origin, dir))
01687 
01688                 if radicalAxis:
01689                         return radicalAxis
01690                 else:
01691                         return None
01692         else:
01693                 print "debug: findRadicalAxis bad parameters!\n"
01694                 FreeCAD.Console.PrintMessage("debug: findRadicalAxis bad parameters!\n")
01695                 return None
01696 
01697 
01698 
01699 def findRadicalCenter(circle1, circle2, circle3):
01700         '''
01701         findRadicalCenter(circle1, circle2, circle3):
01702         Calculates the radical center (also called the power center) of three circles.
01703         It is the intersection point of the three radical axes of the pairs of circles.
01704 
01705         http://en.wikipedia.org/wiki/Power_center_(geometry)
01706         http://mathworld.wolfram.com/RadicalCenter.html
01707 
01708         @sa findRadicalAxis
01709         '''
01710 
01711         if isinstance(circle1.Curve, Part.Circle) and isinstance(circle2.Curve, Part.Circle) and isinstance(circle2.Curve, Part.Circle):
01712                 radicalAxis12 = findRadicalAxis(circle1, circle2)
01713                 radicalAxis23 = findRadicalAxis(circle1, circle2)
01714 
01715                 if not radicalAxis12 or not radicalAxis23:
01716                         # No radical center could be calculated.
01717                         return None
01718 
01719                 int = findIntersection(radicalAxis12, radicalAxis23, True, True)
01720 
01721                 if int:
01722                         return int
01723                 else:
01724                         # No radical center could be calculated.
01725                         return None
01726         else:
01727                 print "debug: findRadicalCenter bad parameters!\n"
01728                 FreeCAD.Console.PrintMessage("debug: findRadicalCenter bad parameters!\n")
01729                 return None
01730 
01731 def pointInversion(circle, point):
01732         '''
01733         pointInversion(Circle, Vector)
01734 
01735         Circle inversion of a point.
01736         Will calculate the inversed point an return it.
01737         If the given point is equal to the center of the circle "None" will be returned.
01738 
01739         See also:
01740         http://en.wikipedia.org/wiki/Inversive_geometry
01741         '''
01742 
01743         if isinstance(circle.Curve, Part.Circle) and isinstance(point, FreeCAD.Vector):
01744                 cen = circle.Curve.Center
01745                 rad = circle.Curve.Radius
01746 
01747                 if fcvec.equals(cen, point):
01748                         return None
01749 
01750                 # Inverse the distance of the point
01751                 # dist(cen -> P) = r^2 / dist(cen -> invP)
01752 
01753                 dist = fcvec.dist(point, cen)
01754                 invDist = rad**2 / d
01755 
01756                 invPoint = Vector(0, 0, point.z)
01757                 invPoint.x = cen.x + (point.x - cen.x) * invDist / dist;
01758                 invPoint.y = cen.y + (point.y - cen.y) * invDist / dist;
01759 
01760                 return invPoint
01761 
01762         else:
01763                 print "debug: pointInversion bad parameters!\n"
01764                 FreeCAD.Console.PrintMessage("debug: pointInversion bad parameters!\n")
01765                 return None
01766 
01767 def polarInversion(circle, edge):
01768         '''
01769         polarInversion(circle, edge):
01770         Returns the inversion pole of a line.
01771         edge ... The polar.
01772         i.e. The nearest point on the line is inversed.
01773 
01774         http://mathworld.wolfram.com/InversionPole.html
01775         '''
01776 
01777         if isinstance(circle.Curve, Part.Circle) and isinstance(edge.Curve, Part.Line):
01778                 nearest = circle.Curve.Center.add(findDistance(circle.Curve.Center, edge, False))
01779                 if nearest:
01780                         inversionPole = pointInversion(circle, nearest)
01781                         if inversionPole:
01782                                 return inversionPole
01783 
01784         else:
01785                 print "debug: circleInversionPole bad parameters!\n"
01786                 FreeCAD.Console.PrintMessage("debug: circleInversionPole bad parameters!\n")
01787                 return None
01788 
01789 def circleInversion(circle, circle2):
01790         '''
01791         pointInversion(Circle, Circle)
01792 
01793         Circle inversion of a circle.
01794         '''
01795         if isinstance(circle.Curve, Part.Circle) and isinstance(circle2.Curve, Part.Circle):
01796                 cen1 = circle.Curve.Center
01797                 rad1 = circle.Curve.Radius
01798 
01799                 if fcvec.equals(cen1, point):
01800                         return None
01801 
01802                 invCen2 = Inversion(circle, circle2.Curve.Center)
01803 
01804                 pointOnCircle2 = Vector.add(circle2.Curve.Center, Vector(circle2.Curve.Radius, 0, 0))
01805                 invPointOnCircle2 = Inversion(circle, pointOnCircle2)
01806 
01807                 return Part.Circle(invCen2, norm, fcvec.dist(invCen2, invPointOnCircle2))
01808 
01809         else:
01810                 print "debug: circleInversion bad parameters!\n"
01811                 FreeCAD.Console.PrintMessage("debug: circleInversion bad parameters!\n")
01812                 return None
01813 

Generated on Wed Nov 23 19:00:11 2011 for FreeCAD by  doxygen 1.6.1