| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | """Provides various functions to work with circles.""" |
| | |
| | |
| | |
| |
|
| | import math |
| | import lazy_loader.lazy_loader as lz |
| |
|
| | import FreeCAD as App |
| | import DraftVecUtils |
| |
|
| | from draftgeoutils.general import geomType, edg, vec, NORM, v1 |
| | from draftgeoutils.geometry import mirror, findDistance |
| | from draftgeoutils.edges import findMidpoint |
| | from draftgeoutils.intersections import findIntersection, angleBisection |
| |
|
| | |
| | Part = lz.LazyLoader("Part", globals(), "Part") |
| |
|
| | |
| | |
| |
|
| |
|
| | def findClosestCircle(point, circles): |
| | """Return the circle with closest center to a given point.""" |
| | dist = 1000000 |
| | closest = None |
| | for c in circles: |
| | if c.Center.sub(point).Length < dist: |
| | dist = c.Center.sub(point).Length |
| | closest = c |
| | return closest |
| |
|
| |
|
| | def getCircleFromSpline(edge): |
| | """Return a circle-based edge from a bspline-based edge. |
| | |
| | Return None if the edge is not a BSplineCurve. |
| | """ |
| | if geomType(edge) != "BSplineCurve" or len(edge.Vertexes) != 1: |
| | return None |
| |
|
| | |
| | p1 = edge.Curve.value(0) |
| | p2 = edge.Curve.value(math.pi / 2) |
| | |
| | t1 = edge.Curve.tangent(0)[0] |
| | t2 = edge.Curve.tangent(math.pi / 2)[0] |
| | |
| | n = p1.cross(p2) |
| | if DraftVecUtils.isNull(n): |
| | return None |
| |
|
| | |
| | r1 = DraftVecUtils.rotate(t1, math.pi / 2, n) |
| | r2 = DraftVecUtils.rotate(t2, math.pi / 2, n) |
| | |
| | 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) |
| | |
| | return circle |
| |
|
| |
|
| | def circlefrom1Line2Points(edge, p1, p2): |
| | """Return a list of circles created from an edge and two points. |
| | |
| | It calculates up to 2 possible centers. |
| | """ |
| | p1_p2 = edg(p1, p2) |
| | s = findIntersection(edge, p1_p2, True, True) |
| | if not s: |
| | return None |
| |
|
| | s = s[0] |
| | v1 = p1.sub(s) |
| | v2 = p2.sub(s) |
| | projectedDist = math.sqrt(abs(v1.dot(v2))) |
| | edgeDir = vec(edge) |
| | edgeDir.normalize() |
| |
|
| | projectedCen1 = App.Vector.add(s, App.Vector(edgeDir).multiply(projectedDist)) |
| | projectedCen2 = App.Vector.add(s, App.Vector(edgeDir).multiply(-projectedDist)) |
| | perpEdgeDir = edgeDir.cross(App.Vector(0, 0, 1)) |
| | perpCen1 = App.Vector.add(projectedCen1, perpEdgeDir) |
| | perpCen2 = App.Vector.add(projectedCen2, perpEdgeDir) |
| |
|
| | mid = findMidpoint(p1_p2) |
| | x = DraftVecUtils.crossproduct(vec(p1_p2)) |
| | x.normalize() |
| | perp_mid = App.Vector.add(mid, x) |
| | cen1 = findIntersection(edg(projectedCen1, perpCen1), edg(mid, perp_mid), True, True) |
| | cen2 = findIntersection(edg(projectedCen2, perpCen2), edg(mid, perp_mid), True, True) |
| |
|
| | circles = [] |
| | if cen1: |
| | radius = DraftVecUtils.dist(projectedCen1, cen1[0]) |
| | circles.append(Part.Circle(cen1[0], NORM, radius)) |
| |
|
| | if cen2: |
| | radius = DraftVecUtils.dist(projectedCen2, cen2[0]) |
| | circles.append(Part.Circle(cen2[0], NORM, radius)) |
| |
|
| | if circles: |
| | return circles |
| | else: |
| | return None |
| |
|
| |
|
| | def circlefrom2Lines1Point(edge1, edge2, point): |
| | """Return a list of circles from two edges and one point.""" |
| | bis = angleBisection(edge1, edge2) |
| | if not bis: |
| | return None |
| |
|
| | mirrPoint = mirror(point, bis) |
| | return circlefrom1Line2Points(edge1, point, mirrPoint) |
| |
|
| |
|
| | def circleFrom2LinesRadius(edge1, edge2, radius): |
| | """Return a list of circles from two edges and one radius. |
| | |
| | It calculates 4 centers. |
| | """ |
| | intsec = findIntersection(edge1, edge2, True, True) |
| | if not intsec: |
| | return None |
| |
|
| | intsec = intsec[0] |
| | bis12 = angleBisection(edge1, edge2) |
| | bis21 = Part.LineSegment( |
| | bis12.Vertexes[0].Point, DraftVecUtils.rotate(vec(bis12), math.pi / 2.0) |
| | ) |
| | ang12 = abs(DraftVecUtils.angle(vec(edge1), vec(edge2))) |
| | ang21 = math.pi - ang12 |
| | dist12 = radius / math.sin(ang12 * 0.5) |
| | dist21 = radius / math.sin(ang21 * 0.5) |
| |
|
| | circles = [] |
| | cen = App.Vector.add(intsec, vec(bis12).multiply(dist12)) |
| | circles.append(Part.Circle(cen, NORM, radius)) |
| |
|
| | cen = App.Vector.add(intsec, vec(bis12).multiply(-dist12)) |
| | circles.append(Part.Circle(cen, NORM, radius)) |
| |
|
| | cen = App.Vector.add(intsec, vec(bis21).multiply(dist21)) |
| | circles.append(Part.Circle(cen, NORM, radius)) |
| |
|
| | cen = App.Vector.add(intsec, vec(bis21).multiply(-dist21)) |
| | circles.append(Part.Circle(cen, NORM, radius)) |
| |
|
| | return circles |
| |
|
| |
|
| | def circleFrom3LineTangents(edge1, edge2, edge3): |
| | """Return a list of circles from three edges. |
| | |
| | It calculates up to 6 possible centers. |
| | """ |
| |
|
| | def rot(ed): |
| | geo = Part.LineSegment(v1(ed), v1(ed).add(DraftVecUtils.rotate(vec(ed), math.pi / 2))) |
| | return geo.toShape() |
| |
|
| | bis12 = angleBisection(edge1, edge2) |
| | bis23 = angleBisection(edge2, edge3) |
| | bis31 = angleBisection(edge3, edge1) |
| | intersections = [] |
| |
|
| | intsec = findIntersection(bis12, bis23, True, True) |
| | if intsec: |
| | radius = findDistance(intsec[0], edge1).Length |
| | intersections.append(Part.Circle(intsec[0], NORM, radius)) |
| |
|
| | intsec = findIntersection(bis23, bis31, True, True) |
| | if intsec: |
| | radius = findDistance(intsec[0], edge1).Length |
| | intersections.append(Part.Circle(intsec[0], NORM, radius)) |
| |
|
| | intsec = findIntersection(bis31, bis12, True, True) |
| | if intsec: |
| | radius = findDistance(intsec[0], edge1).Length |
| | intersections.append(Part.Circle(intsec[0], NORM, radius)) |
| |
|
| | intsec = findIntersection(rot(bis12), rot(bis23), True, True) |
| | if intsec: |
| | radius = findDistance(intsec[0], edge1).Length |
| | intersections.append(Part.Circle(intsec[0], NORM, radius)) |
| |
|
| | intsec = findIntersection(rot(bis23), rot(bis31), True, True) |
| | if intsec: |
| | radius = findDistance(intsec[0], edge1).Length |
| | intersections.append(Part.Circle(intsec[0], NORM, radius)) |
| |
|
| | intsec = findIntersection(rot(bis31), rot(bis12), True, True) |
| | if intsec: |
| | radius = findDistance(intsec[0], edge1).Length |
| | intersections.append(Part.Circle(intsec[0], NORM, radius)) |
| |
|
| | circles = [] |
| | for intsec in intersections: |
| | exists = False |
| | for cir in circles: |
| | if DraftVecUtils.equals(cir.Center, intsec.Center): |
| | exists = True |
| | break |
| | if not exists: |
| | circles.append(intsec) |
| |
|
| | if circles: |
| | return circles |
| | else: |
| | return None |
| |
|
| |
|
| | def circleFromPointLineRadius(point, edge, radius): |
| | """Return a list of circles from one point, one edge, and one radius. |
| | |
| | It calculates up to 2 possible centers. |
| | """ |
| | dist = findDistance(point, edge, False) |
| | center1 = None |
| | center2 = None |
| |
|
| | if dist.Length == 0: |
| | segment = vec(edge) |
| | perpVec = DraftVecUtils.crossproduct(segment) |
| | perpVec.normalize() |
| | normPoint_c1 = App.Vector(perpVec).multiply(radius) |
| | normPoint_c2 = App.Vector(perpVec).multiply(-radius) |
| | center1 = point.add(normPoint_c1) |
| | center2 = point.add(normPoint_c2) |
| | elif dist.Length > 2 * radius: |
| | return None |
| | elif dist.Length == 2 * radius: |
| | normPoint = point.add(findDistance(point, edge, False)) |
| | dummy = (normPoint.sub(point)).multiply(0.5) |
| | cen = point.add(dummy) |
| | circ = Part.Circle(cen, NORM, radius) |
| | if circ: |
| | return [circ] |
| | else: |
| | return None |
| | else: |
| | normPoint = point.add(findDistance(point, edge, False)) |
| | normDist = DraftVecUtils.dist(normPoint, point) |
| | dist = math.sqrt(radius**2 - (radius - normDist) ** 2) |
| | centerNormVec = DraftVecUtils.scaleTo(point.sub(normPoint), radius) |
| | edgeDir = edge.Vertexes[0].Point.sub(normPoint) |
| | edgeDir.normalize() |
| | center1 = centerNormVec.add(normPoint.add(App.Vector(edgeDir).multiply(dist))) |
| | center2 = centerNormVec.add(normPoint.add(App.Vector(edgeDir).multiply(-dist))) |
| |
|
| | circles = [] |
| | if center1: |
| | circ = Part.Circle(center1, NORM, radius) |
| | if circ: |
| | circles.append(circ) |
| |
|
| | if center2: |
| | circ = Part.Circle(center2, NORM, radius) |
| | if circ: |
| | circles.append(circ) |
| |
|
| | if circles: |
| | return circles |
| | else: |
| | return None |
| |
|
| |
|
| | def circleFrom2PointsRadius(p1, p2, radius): |
| | """Return a list of circles from two points, and one radius. |
| | |
| | The two points must not be equal. |
| | |
| | It calculates up to 2 possible centers. |
| | """ |
| | if DraftVecUtils.equals(p1, p2): |
| | return None |
| |
|
| | p1_p2 = Part.LineSegment(p1, p2).toShape() |
| | dist_p1p2 = DraftVecUtils.dist(p1, p2) |
| | mid = findMidpoint(p1_p2) |
| |
|
| | if dist_p1p2 == 2 * radius: |
| | circle = Part.Circle(mid, NORM, radius) |
| | if circle: |
| | return [circle] |
| | else: |
| | return None |
| |
|
| | _dir = vec(p1_p2) |
| | _dir.normalize() |
| | perpDir = _dir.cross(App.Vector(0, 0, 1)) |
| | perpDir.normalize() |
| | dist = math.sqrt(radius**2 - (dist_p1p2 / 2.0) ** 2) |
| | cen1 = App.Vector.add(mid, App.Vector(perpDir).multiply(dist)) |
| | cen2 = App.Vector.add(mid, App.Vector(perpDir).multiply(-dist)) |
| |
|
| | circles = [] |
| | if cen1: |
| | circles.append(Part.Circle(cen1, NORM, radius)) |
| | if cen2: |
| | circles.append(Part.Circle(cen2, NORM, radius)) |
| |
|
| | if circles: |
| | return circles |
| | else: |
| | 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() |
| |
|
| | |
| | perpCenDir = cenDir.cross(App.Vector(0, 0, 1)) |
| | perpCenDir.normalize() |
| |
|
| | |
| | p1 = App.Vector.add(circle1.Curve.Center, App.Vector(perpCenDir).multiply(circle1.Curve.Radius)) |
| |
|
| | centers = [] |
| | |
| | |
| | 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) |
| |
|
| | |
| | |
| | if circle1.Curve.Radius != circle2.Curve.Radius: |
| | |
| | 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 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 = DraftVecUtils.dist(cen1, circle2.Curve.Center) |
| | cenDir = cen1.sub(circle2.Curve.Center) |
| | cenDir.normalize() |
| |
|
| | |
| | perpCenDir = cenDir.cross(App.Vector(0, 0, 1)) |
| | perpCenDir.normalize() |
| |
|
| | |
| | |
| | |
| | |
| | |
| |
|
| | k1 = (dist + (r1**2 - r2**2) / dist) / 2.0 |
| | |
| |
|
| | K = App.Vector.add(cen1, cenDir.multiply(k1)) |
| |
|
| | |
| | |
| | K_ = App.Vector.add(K, perpCenDir) |
| |
|
| | |
| | |
| | |
| |
|
| | origin = App.Vector(0, 0, 0) |
| | radicalAxis = Part.LineSegment(K, App.Vector.add(origin, perpCenDir)) |
| |
|
| | if radicalAxis: |
| | return radicalAxis |
| | else: |
| | return None |
| |
|
| |
|
| | 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: |
| | |
| | return None |
| |
|
| | intersect = findIntersection(radicalAxis12, radicalAxis23, True, True) |
| |
|
| | if intersect: |
| | return intersect |
| | else: |
| | |
| | return None |
| |
|
| |
|
| | |
| |
|