00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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)
00034
00035 params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
00036 precision = params.GetInt("precision")
00037
00038
00039
00040
00041 def vec(edge):
00042 "vec(edge) or vec(line) -- returns a vector from an edge or a Part.line"
00043
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
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
00108
00109
00110
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
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
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
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
00162 if (pt1 in [pt3,pt4]):
00163 return [pt1]
00164 elif (pt2 in [pt3,pt4]):
00165 return [pt2]
00166
00167
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 []
00189 else :
00190 return []
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
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
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
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 :
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
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
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 []
00292 else :
00293
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
00365
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
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
00384
00385
00386
00387
00388
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 = []
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 = []
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 :
00433 olEdges = sortEdges(lEdges, result[3].Vertexes[result[2]])
00434 return olEdges
00435
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
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
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
00714 v1 = findIntersection(curr,prev,True,True)[0]
00715 else:
00716 v1 = curr.Vertexes[0].Point
00717 if next:
00718
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
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
00913
00914 sharedhedges = []
00915 for k,v in lut.iteritems():
00916 if len(v) == 2:
00917 sharedhedges.append(k)
00918
00919
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
00928
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
00936
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
00958
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
00972
00973 for f in faceset:
00974 if not f.hashCode() in treated:
00975 newfaces.append(f)
00976
00977
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
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
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
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
01026 vx = vec(base.Edges[0])
01027 vy = vec(base.Edges[1])
01028
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
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
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
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
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
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
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
01399
01400
01401
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
01412
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
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"
01434
01435
01436
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
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
01451
01452
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
01463
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
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"
01485
01486
01487
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
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
01511
01512
01513
01514
01515
01516
01517 r1 = circle1.Curve.Radius
01518 r2 = circle2.Curve.Radius
01519 r3 = circle3.Curve.Radius
01520 outerSoddy = outerSoddyCircle(circle1, circle2, circle3)
01521
01522
01523 innerSoddy = innerSoddyCircle(circle1, circle2, circle3)
01524
01525
01526 circles = []
01527 if outerSoddy:
01528 circles.append(outerSoddy)
01529 if innerSoddy:
01530 circles.append(innerSoddy)
01531 return circles
01532
01533
01534
01535
01536
01537
01538
01539 else:
01540
01541 return None
01542
01543 else:
01544 print "debug: circleFrom3CircleTangents bad parameters!\n"
01545
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
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
01615 perpCenDir = cenDir.cross(Vector(0,0,1)); perpCenDir.normalize()
01616
01617
01618 p1 = Vector.add(circle1.Curve.Center, fcvec.scale(perpCenDir, circle1.Curve.Radius))
01619
01620 centers = []
01621
01622
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
01629 if circle1.Curve.Radius != circle2.Curve.Radius:
01630
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
01666 dist = fcvec.dist(cen1, circle2.Curve.Center)
01667 cenDir = cen1.sub(circle2.Curve.Center); cenDir.normalize()
01668
01669
01670 perpCenDir = cenDir.cross(Vector(0,0,1)); perpCenDir.normalize()
01671
01672
01673
01674
01675
01676
01677
01678 k1 = (dist + (r1^2 - r2^2) / dist) / 2.0
01679
01680
01681 K = Vector.add(cen1, fcvec.scale(cenDir, k1))
01682
01683
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
01717 return None
01718
01719 int = findIntersection(radicalAxis12, radicalAxis23, True, True)
01720
01721 if int:
01722 return int
01723 else:
01724
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
01751
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