def circleInversion(circle, circle2): """Circle inversion of a circle, inverting the center point. Returns the new circle created from the inverted center of circle2. """ if geomType(circle) != "Circle" or geomType(circle2) != "Circle": print("debug: circleInversion bad parameters! Must be circles.") return None cen1 = circle.Curve.Center cen2 = circle2.Curve.Center rad2 = circle2.Curve.Radius if DraftVecUtils.equals(cen1, cen2): return None invCen2 = pointInversion(circle, cen2) pointOnCircle2 = App.Vector.add(cen2, App.Vector(rad2, 0, 0)) invPointOnCircle2 = pointInversion(circle, pointOnCircle2) return Part.Circle(invCen2, NORM, DraftVecUtils.dist(invCen2, invPointOnCircle2))
def findRadicalCenter(circle1, circle2, circle3): """Calculate the radical center of three circles. It is also called the power center. It is the intersection point of the three radical axes of the pairs of circles. http://en.wikipedia.org/wiki/Power_center_(geometry) http://mathworld.wolfram.com/RadicalCenter.html See Also -------- findRadicalAxis """ if (geomType(circle1) != "Circle" or geomType(circle2) != "Circle" or geomType(circle3) != "Circle"): print("debug: findRadicalCenter bad parameters! Must be circles.") return None radicalAxis12 = findRadicalAxis(circle1, circle2) radicalAxis23 = findRadicalAxis(circle2, circle3) if not radicalAxis12 or not radicalAxis23: # No radical center could be calculated. return None intersect = findIntersection(radicalAxis12, radicalAxis23, True, True) if intersect: return intersect else: # No radical center could be calculated. return None
def circleFrom1tan2pt(tan1, p1, p2): if (geomType(tan1) == "Line") and isinstance( p1, FreeCAD.Vector) and isinstance(p2, FreeCAD.Vector): return circlefrom1Line2Points(tan1, p1, p2) if (geomType(tan1) == "Line") and isinstance( p1, FreeCAD.Vector) and isinstance(p2, FreeCAD.Vector): return circlefrom1Circle2Points(tan1, p1, p2)
def findRadicalCenter(circle1, circle2, circle3): """ findRadicalCenter(circle1, circle2, circle3): Calculates the radical center (also called the power center) of three circles. It is the intersection point of the three radical axes of the pairs of circles. http://en.wikipedia.org/wiki/Power_center_(geometry) http://mathworld.wolfram.com/RadicalCenter.html @sa findRadicalAxis """ if (geomType(circle1) == "Circle") and (geomType(circle2) == "Circle"): radicalAxis12 = findRadicalAxis(circle1, circle2) radicalAxis23 = findRadicalAxis(circle1, circle2) if not radicalAxis12 or not radicalAxis23: # No radical center could be calculated. return None int = findIntersection(radicalAxis12, radicalAxis23, True, True) if int: return int else: # No radical center could be calculated. return None else: FreeCAD.Console.PrintMessage( "debug: findRadicalCenter bad parameters!\n") return None
def invert(shape): """Return an inverted copy of the edge or wire contained in the shape.""" if shape.ShapeType == "Wire": edges = [invert(edge) for edge in shape.OrderedEdges] edges.reverse() return Part.Wire(edges) elif shape.ShapeType == "Edge": if len(shape.Vertexes) == 1: return shape if geomType(shape) == "Line": return Part.LineSegment(shape.Vertexes[-1].Point, shape.Vertexes[0].Point).toShape() elif geomType(shape) == "Circle": mp = findMidpoint(shape) return Part.Arc(shape.Vertexes[-1].Point, mp, shape.Vertexes[0].Point).toShape() elif geomType(shape) in ["BSplineCurve", "BezierCurve"]: if isLine(shape.Curve): return Part.LineSegment(shape.Vertexes[-1].Point, shape.Vertexes[0].Point).toShape() print("DraftGeomUtils.invert: unable to invert", shape.Curve) return shape else: print("DraftGeomUtils.invert: unable to handle", shape.ShapeType) return shape
def offset(edge, vector, trim=False): """Return a copy of the edge at a certain vector offset. If the edge is an arc, the vector will be added at its first point and a complete circle will be returned. None if there is a problem. """ if (not isinstance(edge, Part.Shape) or not isinstance(vector, App.Vector)): return None if geomType(edge) == "Line": v1 = App.Vector.add(edge.Vertexes[0].Point, vector) v2 = App.Vector.add(edge.Vertexes[-1].Point, vector) return Part.LineSegment(v1, v2).toShape() elif geomType(edge) == "Circle": rad = edge.Vertexes[0].Point.sub(edge.Curve.Center) curve = Part.Circle(edge.Curve) curve.Radius = App.Vector.add(rad, vector).Length if trim: return Part.ArcOfCircle(curve, edge.FirstParameter, edge.LastParameter).toShape() else: return curve.toShape() else: return None
def angleBisection(edge1, edge2): """Return an edge that bisects the angle between the 2 straight edges.""" if geomType(edge1) != "Line" or geomType(edge2) != "Line": return None p1 = edge1.Vertexes[0].Point p2 = edge1.Vertexes[-1].Point p3 = edge2.Vertexes[0].Point p4 = edge2.Vertexes[-1].Point intersect = findIntersection(edge1, edge2, True, True) if intersect: line1Dir = p2.sub(p1) angleDiff = DraftVecUtils.angle(line1Dir, p4.sub(p3)) ang = angleDiff * 0.5 origin = intersect[0] line1Dir.normalize() direction = DraftVecUtils.rotate(line1Dir, ang) else: diff = p3.sub(p1) origin = p1.add(diff.multiply(0.5)) direction = p2.sub(p1) direction.normalize() return Part.LineSegment(origin, origin.add(direction)).toShape()
def circleInversion(circle, circle2): """ pointInversion(Circle, Circle) Circle inversion of a circle. """ if (geomType(circle) == "Circle") and (geomType(circle2) == "Circle"): cen1 = circle.Curve.Center rad1 = circle.Curve.Radius if DraftVecUtils.equals(cen1, point): return None invCen2 = Inversion(circle, circle2.Curve.Center) pointOnCircle2 = Vector.add(circle2.Curve.Center, Vector(circle2.Curve.Radius, 0, 0)) invPointOnCircle2 = Inversion(circle, pointOnCircle2) return Part.Circle(invCen2, norm, DraftVecUtils.dist(invCen2, invPointOnCircle2)) else: FreeCAD.Console.PrintMessage( "debug: circleInversion bad parameters!\n") return None
def findMidpoint(edge): """Return the midpoint of a straight line or circular edge.""" first = edge.Vertexes[0].Point last = edge.Vertexes[-1].Point if geomType(edge) == "Circle": center = edge.Curve.Center radius = edge.Curve.Radius if len(edge.Vertexes) == 1: # Circle dv = first.sub(center) dv = dv.negative() return center.add(dv) axis = edge.Curve.Axis chord = last.sub(first) perp = chord.cross(axis) perp.normalize() ray = first.sub(center) apothem = ray.dot(perp) sagitta = radius - apothem startpoint = FreeCAD.Vector.add(first, chord.multiply(0.5)) endpoint = DraftVecUtils.scaleTo(perp, sagitta) return FreeCAD.Vector.add(startpoint, endpoint) elif geomType(edge) == "Line": halfedge = (last.sub(first)).multiply(0.5) return FreeCAD.Vector.add(first, halfedge) else: return None
def findRadicalAxis(circle1, circle2): """Calculate the radical axis of two circles. On the radical axis (also called power line) of two circles any tangents drawn from a point on the axis to both circles have the same length. http://en.wikipedia.org/wiki/Radical_axis http://mathworld.wolfram.com/RadicalLine.html See Also -------- findRadicalCenter """ if (geomType(circle1) == "Circle") and (geomType(circle2) == "Circle"): print("debug: findRadicalAxis bad parameters! Must be circles.") return None if DraftVecUtils.equals(circle1.Curve.Center, circle2.Curve.Center): return None r1 = circle1.Curve.Radius r2 = circle1.Curve.Radius cen1 = circle1.Curve.Center # dist .. the distance from cen1 to cen2. dist = DraftVecUtils.dist(cen1, circle2.Curve.Center) cenDir = cen1.sub(circle2.Curve.Center) cenDir.normalize() # Get the perpedicular vector. perpCenDir = cenDir.cross(App.Vector(0, 0, 1)) perpCenDir.normalize() # J ... The radical center. # K ... The point where the cadical axis crosses the line of cen1->cen2. # k1 ... Distance from cen1 to K. # k2 ... Distance from cen2 to K. # dist = k1 + k2 k1 = (dist + (r1**2 - r2**2) / dist) / 2.0 # k2 = dist - k1 K = App.Vector.add(cen1, cenDir.multiply(k1)) # K_ .. A point somewhere between K and J; actually with a distance # of 1 unit from K. K_ = App.Vector.add(K, perpCenDir) # Original code didn't specify the value of origin nor dir, # so this is a guess: # radicalAxis = Part.LineSegment(K, Vector.add(origin, dir)) origin = App.Vector(0, 0, 0) radicalAxis = Part.LineSegment(K, App.Vector.add(origin, perpCenDir)) if radicalAxis: return radicalAxis else: return None
def findHomotheticCenterOfCircles(circle1, circle2): """Calculate the homothetic center(s) of two circles. http://en.wikipedia.org/wiki/Homothetic_center http://mathworld.wolfram.com/HomotheticCenter.html """ if (geomType(circle1) == "Circle") and (geomType(circle2) == "Circle"): if DraftVecUtils.equals(circle1.Curve.Center, circle2.Curve.Center): return None cen1_cen2 = Part.LineSegment(circle1.Curve.Center, circle2.Curve.Center).toShape() cenDir = vec(cen1_cen2) cenDir.normalize() # Get the perpedicular vector. perpCenDir = cenDir.cross(Vector(0, 0, 1)) perpCenDir.normalize() # Get point on first circle p1 = Vector.add(circle1.Curve.Center, Vector(perpCenDir).multiply(circle1.Curve.Radius)) centers = [] # Calculate inner homothetic center # Get point on second circle p2_inner = Vector.add( circle1.Curve.Center, Vector(perpCenDir).multiply(-circle1.Curve.Radius)) hCenterInner = DraftVecUtils.intersect(circle1.Curve.Center, circle2.Curve.Center, p1, p2_inner, True, True) if hCenterInner: centers.append(hCenterInner) # Calculate outer homothetic center (only exists of the circles have different radii) if circle1.Curve.Radius != circle2.Curve.Radius: # Get point on second circle p2_outer = Vector.add( circle1.Curve.Center, Vector(perpCenDir).multiply(circle1.Curve.Radius)) hCenterOuter = DraftVecUtils.intersect(circle1.Curve.Center, circle2.Curve.Center, p1, p2_outer, True, True) if hCenterOuter: centers.append(hCenterOuter) if len(centers): return centers else: return None else: FreeCAD.Console.PrintMessage( "debug: findHomotheticCenterOfCirclescleFrom3tan bad parameters!\n" ) return None
def findHomotheticCenterOfCircles(circle1, circle2): """Calculate the homothetic centers from two circles. Return None if the objects are not circles, or if they are concentric. http://en.wikipedia.org/wiki/Homothetic_center http://mathworld.wolfram.com/HomotheticCenter.html """ if (geomType(circle1) == "Circle" and geomType(circle2) == "Circle"): print("debug: findHomotheticCenterOfCircles bad parameters!") return None if DraftVecUtils.equals(circle1.Curve.Center, circle2.Curve.Center): return None cen1_cen2 = Part.LineSegment(circle1.Curve.Center, circle2.Curve.Center).toShape() cenDir = vec(cen1_cen2) cenDir.normalize() # Get the perpedicular vector. perpCenDir = cenDir.cross(App.Vector(0, 0, 1)) perpCenDir.normalize() # Get point on first circle p1 = App.Vector.add(circle1.Curve.Center, App.Vector(perpCenDir).multiply(circle1.Curve.Radius)) centers = [] # Calculate inner homothetic center # Get point on second circle p2_inner = App.Vector.add(circle1.Curve.Center, App.Vector(perpCenDir).multiply(-circle1.Curve.Radius)) hCenterInner = DraftVecUtils.intersect(circle1.Curve.Center, circle2.Curve.Center, p1, p2_inner, True, True) if hCenterInner: centers.append(hCenterInner) # Calculate outer homothetic center; it only exists if the circles # have different radii if circle1.Curve.Radius != circle2.Curve.Radius: # Get point on second circle p2_outer = App.Vector.add(circle1.Curve.Center, App.Vector(perpCenDir).multiply(circle1.Curve.Radius)) hCenterOuter = DraftVecUtils.intersect(circle1.Curve.Center, circle2.Curve.Center, p1, p2_outer, True, True) if hCenterOuter: centers.append(hCenterOuter) if centers: return centers else: return None
def cleanProjection(shape, tessellate=True, seglength=0.05): """Return a valid compound of edges, by recreating them. This is because the projection algorithm somehow creates wrong shapes. They display fine, but on loading the file the shape is invalid. Now with tanderson's fix to `ProjectionAlgos`, that isn't the case, but this function can be used for tessellating ellipses and splines for DXF output-DF. """ oldedges = shape.Edges newedges = [] for e in oldedges: try: if geomType(e) == "Line": newedges.append(e.Curve.toShape()) elif geomType(e) == "Circle": if len(e.Vertexes) > 1: mp = findMidpoint(e) a = Part.Arc(e.Vertexes[0].Point, mp, e.Vertexes[-1].Point).toShape() newedges.append(a) else: newedges.append(e.Curve.toShape()) elif geomType(e) == "Ellipse": if tessellate: newedges.append(Part.Wire(curvetowire(e, seglength))) else: if len(e.Vertexes) > 1: a = Part.Arc(e.Curve, e.FirstParameter, e.LastParameter).toShape() newedges.append(a) else: newedges.append(e.Curve.toShape()) elif (geomType(e) == "BSplineCurve" or geomType(e) == "BezierCurve"): if tessellate: newedges.append(Part.Wire(curvetowire(e, seglength))) else: if isLine(e.Curve): line = Part.LineSegment(e.Vertexes[0].Point, e.Vertexes[-1].Point).toShape() newedges.append(line) else: newedges.append(e.Curve.toShape(e.FirstParameter, e.LastParameter)) else: newedges.append(e) except Part.OCCError: print("Debug: error cleaning edge ", e) return Part.makeCompound(newedges)
def circleFrom1tan1pt1rad(tan1, p1, rad): """Circle from one tangent, one point, and radius. The tangents should be edges, and they may be either straight line edges or circular edges, so two combinations are possible. """ if geomType(tan1) == "Line" and isinstance(p1, FreeCAD.Vector): return circleFromPointLineRadius(p1, tan1, rad) elif geomType(tan1) == "Circle" and isinstance(p1, FreeCAD.Vector): return circleFromPointCircleRadius(p1, tan1, rad)
def circleFrom3CircleTangents(circle1, circle2, circle3): """Return the circle that is tangent to three other circles. This problem is called the 'Problem of Appollonius'. A special case is that of 'Soddy circles'. To Do ----- Currently not all possible solutions are found, only the Soddy circles. * Calc all 6 homothetic centers. * Create 3 lines from the inner and 4 from the outer h. center. * Calc. the 4 inversion poles of these lines for each circle. * Calc. the radical center of the 3 circles. * Calc. the intersection points (max. 8) of 4 lines (through each inversion pole and the radical center) with the circle. * This gives us all the tangent points. """ if (geomType(circle1) != "Circle" and geomType(circle2) != "Circle" and geomType(circle3) == "Circle"): print("debug: circleFrom3CircleTangents bad input! Must be circles") return None int12 = findIntersection(circle1, circle2, True, True) int23 = findIntersection(circle2, circle3, True, True) int31 = findIntersection(circle3, circle1, True, True) if int12 and int23 and int31: if len(int12) == 1 and len(int23) == 1 and len(int31) == 1: # If only one intersection with each circle, Soddy circle # r1 = circle1.Curve.Radius # r2 = circle2.Curve.Radius # r3 = circle3.Curve.Radius outerSoddy = outerSoddyCircle(circle1, circle2, circle3) # print(str(outerSoddy)) # Debug innerSoddy = innerSoddyCircle(circle1, circle2, circle3) # print(str(innerSoddy)) # Debug circles = [] if outerSoddy: circles.append(outerSoddy) if innerSoddy: circles.append(innerSoddy) return circles # Here the rest of the circles should be calculated # ... else: # Some circles are inside each other or an error has occurred. return None
def circleFrom3CircleTangents(circle1, circle2, circle3): """ http://en.wikipedia.org/wiki/Problem_of_Apollonius#Inversive_methods http://mathworld.wolfram.com/ApolloniusCircle.html http://mathworld.wolfram.com/ApolloniusProblem.html """ if (geomType(circle1) == "Circle") and (geomType(circle2) == "Circle") \ and (geomType(circle3) == "Circle"): int12 = findIntersection(circle1, circle2, True, True) int23 = findIntersection(circle2, circle3, True, True) int31 = findIntersection(circle3, circle1, True, True) if int12 and int23 and int31: if len(int12) == 1 and len(int23) == 1 and len(int31) == 1: # Only one intersection with each circle. # => "Soddy Circle" - 2 solutions. # http://en.wikipedia.org/wiki/Problem_of_Apollonius#Mutually_tangent_given_circles:_Soddy.27s_circles_and_Descartes.27_theorem # http://mathworld.wolfram.com/SoddyCircles.html # http://mathworld.wolfram.com/InnerSoddyCenter.html # http://mathworld.wolfram.com/OuterSoddyCenter.html r1 = circle1.Curve.Radius r2 = circle2.Curve.Radius r3 = circle3.Curve.Radius outerSoddy = outerSoddyCircle(circle1, circle2, circle3) # print(str(outerSoddy) + "\n") # Debug innerSoddy = innerSoddyCircle(circle1, circle2, circle3) # print(str(innerSoddy) + "\n") # Debug circles = [] if outerSoddy: circles.append(outerSoddy) if innerSoddy: circles.append(innerSoddy) return circles # @todo Calc all 6 homothetic centers. # @todo Create 3 lines from the inner and 4 from the outer h. center. # @todo Calc. the 4 inversion poles of these lines for each circle. # @todo Calc. the radical center of the 3 circles. # @todo Calc. the intersection points (max. 8) of 4 lines (through each inversion pole and the radical center) with the circle. # This gives us all the tangent points. else: # Some circles are inside each other or an error has occurred. return None else: print("debug: circleFrom3CircleTangents bad parameters!\n") # FreeCAD.Console.PrintMessage("debug: circleFrom3CircleTangents bad parameters!\n") return None
def outerSoddyCircle(circle1, circle2, circle3): """Compute the outer soddy circle for three tightly packed circles. Original Java code Copyright (rc) 2008 Werner Randelshofer Converted to python by Martin Buerbaum 2009 http://www.randelshofer.ch/treeviz/ Either Creative Commons Attribution 3.0, the MIT license, or the GNU Lesser General License LGPL. """ if (geomType(circle1) != "Circle" or geomType(circle2) != "Circle" or geomType(circle3) != "Circle"): print("debug: outerSoddyCircle bad parameters! Must be circles.") return None A = circle1.Curve.Center B = circle2.Curve.Center C = circle3.Curve.Center ra = circle1.Curve.Radius rb = circle2.Curve.Radius rc = circle3.Curve.Radius # Solution using Descartes' theorem, as described here: # http://en.wikipedia.org/wiki/Descartes%27_theorem k1 = 1 / ra k2 = 1 / rb k3 = 1 / rc k4 = abs(k1 + k2 + k3 - 2 * math.sqrt(k1 * k2 + k2 * k3 + k3 * k1)) q1 = (k1 + 0j) * (A.x + A.y * 1j) q2 = (k2 + 0j) * (B.x + B.y * 1j) q3 = (k3 + 0j) * (C.x + C.y * 1j) temp = ((q1 * q2) + (q2 * q3) + (q3 * q1)) q4 = q1 + q2 + q3 - ((2 + 0j) * cmath.sqrt(temp)) z = q4 / (k4 + 0j) # If the formula is not solvable, we return no circle. if not z or not (1 / k4): return None X = -z.real Y = -z.imag print("Outer Soddy circle: " + str(X) + " " + str(Y) + "\n") # Debug # The Radius of the outer soddy circle can also be calculated # with the following formula: # radiusOuter = abs(r1*r2*r3 / (r1*r2 + r1*r3 + r2*r3 - 2 * math.sqrt(r1*r2*r3 * (r1+r2+r3)))) circ = Part.Circle(App.Vector(X, Y, A.z), NORM, 1 / k4) return circ
def circleFrom1tan2pt(tan1, p1, p2): """Circle from one tangent and two points. The tangents should be edges, and they may be either straight line edges or circular edges, so two combinations are possible. """ if (geomType(tan1) == "Line" and isinstance(p1, FreeCAD.Vector) and isinstance(p2, FreeCAD.Vector)): return circlefrom1Line2Points(tan1, p1, p2) elif (geomType(tan1) == "Circle" and isinstance(p1, FreeCAD.Vector) and isinstance(p2, FreeCAD.Vector)): return circlefrom1Circle2Points(tan1, p1, p2)
def findRadicalAxis(circle1, circle2): """Calculate the radical axis of two circles. On the radical axis (also called power line) of two circles any tangents drawn from a point on the axis to both circles have the same length. http://en.wikipedia.org/wiki/Radical_axis http://mathworld.wolfram.com/RadicalLine.html @sa findRadicalCenter """ if (geomType(circle1) == "Circle") and (geomType(circle2) == "Circle"): if DraftVecUtils.equals(circle1.Curve.Center, circle2.Curve.Center): return None r1 = circle1.Curve.Radius r2 = circle1.Curve.Radius cen1 = circle1.Curve.Center # dist .. the distance from cen1 to cen2. dist = DraftVecUtils.dist(cen1, circle2.Curve.Center) cenDir = cen1.sub(circle2.Curve.Center) cenDir.normalize() # Get the perpedicular vector. perpCenDir = cenDir.cross(Vector(0, 0, 1)) perpCenDir.normalize() # J ... The radical center. # K ... The point where the cadical axis crosses the line of cen1->cen2. # k1 ... Distance from cen1 to K. # k2 ... Distance from cen2 to K. # dist = k1 + k2 k1 = (dist + (r1 ^ 2 - r2 ^ 2) / dist) / 2.0 #k2 = dist - k1 K = Vector.add(cen1, cenDir.multiply(k1)) # K_ .. A point somewhere between K and J (actually with a distance of 1 unit from K). K_ = Vector, add(K, perpCenDir) radicalAxis = Part.LineSegment(K, Vector.add(origin, dir)) if radicalAxis: return radicalAxis else: return None else: FreeCAD.Console.PrintMessage( "debug: findRadicalAxis bad parameters!\n") return None
def innerSoddyCircle(circle1, circle2, circle3): """Compute the inner soddy circle for three tightly packed circles.""" if (geomType(circle1) == "Circle") and (geomType(circle2) == "Circle") \ and (geomType(circle3) == "Circle"): # Original Java code Copyright (rc) 2008 Werner Randelshofer # Converted to python by Martin Buerbaum 2009 # http://www.randelshofer.ch/treeviz/ A = circle1.Curve.Center B = circle2.Curve.Center C = circle3.Curve.Center ra = circle1.Curve.Radius rb = circle2.Curve.Radius rc = circle3.Curve.Radius # Solution using Descartes' theorem, as described here: # http://en.wikipedia.org/wiki/Descartes%27_theorem k1 = 1 / ra k2 = 1 / rb k3 = 1 / rc k4 = abs(k1 + k2 + k3 + 2 * math.sqrt(k1 * k2 + k2 * k3 + k3 * k1)) q1 = (k1 + 0j) * (A.x + A.y * 1j) q2 = (k2 + 0j) * (B.x + B.y * 1j) q3 = (k3 + 0j) * (C.x + C.y * 1j) temp = ((q1 * q2) + (q2 * q3) + (q3 * q1)) q4 = q1 + q2 + q3 + ((2 + 0j) * cmath.sqrt(temp)) z = q4 / (k4 + 0j) # If the formula is not solvable, we return no circle. if (not z or not (1 / k4)): return None X = z.real Y = z.imag print("Outer Soddy circle: " + str(X) + " " + str(Y) + "\n") # Debug # The Radius of the inner soddy circle can also be calculated with the following formula: # radiusInner = abs(r1*r2*r3 / (r1*r2 + r1*r3 + r2*r3 + 2 * math.sqrt(r1*r2*r3 * (r1+r2+r3)))) circ = Part.Circle(Vector(X, Y, A.z), norm, 1 / k4) return circ else: print("debug: innerSoddyCircle bad parameters!\n") # FreeCAD.Console.PrintMessage("debug: innerSoddyCircle bad parameters!\n") return None
def isClockwise(edge, ref=None): """Return True if a circle-based edge has a clockwise direction.""" if not geomType(edge) == "Circle": return True v1 = edge.Curve.tangent(edge.ParameterRange[0])[0] if DraftVecUtils.isNull(v1): return True # we take an arbitrary other point on the edge that has little chances # to be aligned with the first one v2 = edge.Curve.tangent(edge.ParameterRange[0] + 0.01)[0] n = edge.Curve.Axis # if that axis points "the wrong way" from the reference, we invert it if not ref: ref = FreeCAD.Vector(0, 0, 1) if n.getAngle(ref) > math.pi / 2: n = n.negative() if DraftVecUtils.angle(v1, v2, n) < 0: return False if n.z < 0: return False return True
def isCubic(shape): """Return True if the shape is a parallelepiped (cuboid). A parallelepiped of cube-like shape has 8 vertices, 6 faces, 12 edges, and all angles are 90 degrees between its edges. """ # first we try fast methods if (len(shape.Vertexes) != 8 or len(shape.Faces) != 6 or len(shape.Edges) != 12): return False for e in shape.Edges: if geomType(e) != "Line": return False # if ok until now, let's do more advanced testing for f in shape.Faces: if len(f.Edges) != 4: return False for i in range(4): e1 = vec(f.Edges[i]) if i < 3: e2 = vec(f.Edges[i + 1]) else: e2 = vec(f.Edges[0]) rpi = [0.0, round(math.pi / 2, precision())] if round(e1.getAngle(e2), precision()) not in rpi: return False return True
def pointInversion(circle, point): """Return the circle inversion of a point. It will return `None` if the given point is equal to the center of the circle. """ if geomType(circle) != "Circle" or isinstance(point, App.Vector): print("debug: pointInversion bad parameters!") return None cen = circle.Curve.Center rad = circle.Curve.Radius if DraftVecUtils.equals(cen, point): return None # Inverse the distance of the point # dist(cen -> P) = r^2 / dist(cen -> invP) dist = DraftVecUtils.dist(point, cen) invDist = rad**2 / dist invPoint = App.Vector(0, 0, point.z) invPoint.x = cen.x + (point.x - cen.x) * invDist / dist invPoint.y = cen.y + (point.y - cen.y) * invDist / dist return invPoint
def pointInversion(circle, point): """Circle inversion of a point. pointInversion(Circle, Vector) Will calculate the inversed point an return it. If the given point is equal to the center of the circle "None" will be returned. See also: http://en.wikipedia.org/wiki/Inversive_geometry """ if (geomType(circle) == "Circle") and isinstance(point, FreeCAD.Vector): cen = circle.Curve.Center rad = circle.Curve.Radius if DraftVecUtils.equals(cen, point): return None # Inverse the distance of the point # dist(cen -> P) = r^2 / dist(cen -> invP) dist = DraftVecUtils.dist(point, cen) invDist = rad**2 / d invPoint = Vector(0, 0, point.z) invPoint.x = cen.x + (point.x - cen.x) * invDist / dist invPoint.y = cen.y + (point.y - cen.y) * invDist / dist return invPoint else: FreeCAD.Console.PrintMessage("debug: pointInversion bad parameters!\n") return None
def getCircleFromSpline(edge): """Return a circle-based edge from a bspline-based edge.""" if geomType(edge) != "BSplineCurve": return None if len(edge.Vertexes) != 1: return None # get 2 points p1 = edge.Curve.value(0) p2 = edge.Curve.value(math.pi / 2) # get 2 tangents t1 = edge.Curve.tangent(0)[0] t2 = edge.Curve.tangent(math.pi / 2)[0] # get normal n = p1.cross(p2) if DraftVecUtils.isNull(n): return None # get rays r1 = DraftVecUtils.rotate(t1, math.pi / 2, n) r2 = DraftVecUtils.rotate(t2, math.pi / 2, n) # get center (intersection of rays) i = findIntersection(p1, p1.add(r1), p2, p2.add(r2), True, True) if not i: return None c = i[0] r = (p1.sub(c)).Length circle = Part.makeCircle(r, c, n) #print(circle.Curve) return circle
def arcFromSpline(edge): """arcFromSpline(edge): turns the given edge into an arc, by taking its first point, midpoint and endpoint. Works best with bspline segments such as those from imported svg files. Use this only if you are sure your edge is really an arc...""" if geomType(edge) == "Line": print("This edge is straight, cannot build an arc on it") return None if len(edge.Vertexes) > 1: # 2-point arc p1 = edge.Vertexes[0].Point p2 = edge.Vertexes[-1].Point ml = edge.Length / 2 p3 = edge.valueAt(ml) try: return Part.Arc(p1, p3, p2).toShape() except: print("Couldn't make an arc out of this edge") return None else: # circle p1 = edge.Vertexes[0].Point ml = edge.Length / 2 p2 = edge.valueAt(ml) ray = p2.sub(p1) ray.scale(.5, .5, .5) center = p1.add(ray) radius = ray.Length try: return Part.makeCircle(radius, center) except: print("couldn't make a circle out of this edge")
def isCubic(shape): """isCubic(shape): verifies if a shape is cubic, that is, has 8 vertices, 6 faces, and all angles are 90 degrees.""" # first we try fast methods if len(shape.Vertexes) != 8: return False if len(shape.Faces) != 6: return False if len(shape.Edges) != 12: return False for e in shape.Edges: if geomType(e) != "Line": return False # if ok until now, let's do more advanced testing for f in shape.Faces: if len(f.Edges) != 4: return False for i in range(4): e1 = vec(f.Edges[i]) if i < 3: e2 = vec(f.Edges[i + 1]) else: e2 = vec(f.Edges[0]) rpi = [0.0, round(math.pi / 2, precision())] if not round(e1.getAngle(e2), precision()) in rpi: return False return True
def circleFrom2tan1rad(tan1, tan2, rad): """circleFrom2tan1rad(edge, edge, float)""" if (geomType(tan1) == "Line") and (geomType(tan2) == "Line"): return circleFrom2LinesRadius(tan1, tan2, rad) elif (geomType(tan1) == "Circle") and (geomType(tan2) == "Line"): return circleFromCircleLineRadius(tan1, tan2, rad) elif (geomType(tan1) == "Line") and (geomType(tan2) == "Circle"): return circleFromCircleLineRadius(tan2, tan1, rad) elif (geomType(tan1) == "Circle") and (geomType(tan2) == "Circle"): return circleFrom2CirclesRadius(tan1, tan2, rad)
def polarInversion(circle, edge): """Return the inversion pole of a line. The edge is the polar. The nearest point on the line is inversed. http://mathworld.wolfram.com/InversionPole.html """ if geomType(circle) != "Circle" or geomType(edge) != "Line": print("debug: circleInversionPole bad parameters! Must be a circle.") return None nearest = circle.Curve.Center.add( findDistance(circle.Curve.Center, edge, False)) if nearest: inversionPole = pointInversion(circle, nearest) if inversionPole: return inversionPole return None
def tessellateProjection(shape, seglen): """Returns projection with BSplines and Ellipses broken into line segments. Useful for exporting projected views to *dxf files.""" oldedges = shape.Edges newedges = [] for e in oldedges: try: if geomType(e) == "Line": newedges.append(e.Curve.toShape()) elif geomType(e) == "Circle": newedges.append(e.Curve.toShape()) elif geomType(e) == "Ellipse": newedges.append(Part.Wire(curvetosegment(e, seglen))) elif geomType(e) == "BSplineCurve": newedges.append(Part.Wire(curvetosegment(e, seglen))) else: newedges.append(e) except: print("Debug: error cleaning edge ", e) return Part.makeCompound(newedges)