| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | """Provides various functions to calculate intersections of shapes.""" |
| | |
| | |
| | |
| |
|
| | import lazy_loader.lazy_loader as lz |
| |
|
| | import FreeCAD as App |
| | import DraftVecUtils |
| |
|
| | from draftgeoutils.general import precision, vec, geomType, isPtOnEdge |
| | from draftgeoutils.edges import findMidpoint |
| |
|
| | |
| | Part = lz.LazyLoader("Part", globals(), "Part") |
| |
|
| | |
| | |
| |
|
| |
|
| | def findIntersection( |
| | edge1, edge2, infinite1=False, infinite2=False, ex1=False, ex2=False, dts=True, findAll=False |
| | ): |
| | """Return a list containing the intersection points of 2 edges. |
| | |
| | You can also feed 4 points instead of `edge1` and `edge2`. |
| | If `dts` is used, `Shape.section()` is used. |
| | |
| | Parameters |
| | ---------- |
| | edge1 |
| | Part.Edge, Circle, Line -> the first edge. |
| | Base::Vector3 -> the starting point of the first line. In which case |
| | `infinite1` must also be a point. |
| | edge2 |
| | Part.Edge, Circle, Line -> the second edge. |
| | Base::Vector3 -> the ending point of the second line. In which case |
| | `infinite2` must also be a point. |
| | the second edge. In case of a point, `infinite2` must also be a point. |
| | infinite1 |
| | bool, optional -> whether `edge1` should be continued to infinity. |
| | Default to `False`. |
| | Base::Vector3 -> if `edge1` is a point, must also be a point. |
| | infinite2 |
| | bool, optional -> whether `edge2` should be continued to infinity. |
| | Default to `False`. |
| | Base::Vector3 -> if `edge2` is a point, must also be a point. |
| | ex1: bool, optional |
| | In case `edge1` is a point, indicate whether the line should be |
| | continued to infinity. Default to `False` |
| | ex2: bool, optional |
| | In case `edge2` is a point, indicate whether the line should be |
| | continued to infinity. Default to `False` |
| | dts: bool, optional |
| | NOT_DOCUMENTED. Default to `True` |
| | findAll: bool, optional |
| | In case either `edge1` or `edge2` is a circle, indicates whether |
| | to find all intersection points. Default to `False` |
| | |
| | Returns |
| | ------- |
| | list |
| | A list of intersection points |
| | """ |
| |
|
| | def getLineIntersections(pt1, pt2, pt3, pt4, infinite1, infinite2): |
| | if pt1: |
| | |
| | if pt1 in [pt3, pt4]: |
| | return [pt1] |
| | elif pt2 in [pt3, pt4]: |
| | return [pt2] |
| | norm1 = pt2.sub(pt1).cross(pt3.sub(pt1)) |
| | norm2 = pt2.sub(pt4).cross(pt3.sub(pt4)) |
| |
|
| | if not DraftVecUtils.isNull(norm1): |
| | try: |
| | norm1.normalize() |
| | except Part.OCCError: |
| | return [] |
| |
|
| | if not DraftVecUtils.isNull(norm2): |
| | try: |
| | norm2.normalize() |
| | except Part.OCCError: |
| | return [] |
| |
|
| | if DraftVecUtils.isNull(norm1.cross(norm2)): |
| | vec1 = pt2.sub(pt1) |
| | vec2 = pt4.sub(pt3) |
| | if DraftVecUtils.isNull(vec1) or DraftVecUtils.isNull(vec2): |
| | return [] |
| | try: |
| | vec1.normalize() |
| | vec2.normalize() |
| | except Part.OCCError: |
| | return [] |
| | norm3 = vec1.cross(vec2) |
| | denom = norm3.x + norm3.y + norm3.z |
| | if not DraftVecUtils.isNull(norm3) and denom != 0: |
| | k = ( |
| | (pt3.z - pt1.z) * (vec2.x - vec2.y) |
| | + (pt3.y - pt1.y) * (vec2.z - vec2.x) |
| | + (pt3.x - pt1.x) * (vec2.y - vec2.z) |
| | ) / denom |
| | vec1.scale(k, k, k) |
| | intp = pt1.add(vec1) |
| |
|
| | if infinite1 is False and not isPtOnEdge(intp, edge1): |
| | return [] |
| |
|
| | if infinite2 is False and not isPtOnEdge(intp, edge2): |
| | return [] |
| |
|
| | return [intp] |
| | else: |
| | return [] |
| | else: |
| | return [] |
| |
|
| | tol = pow(10, -precision()) |
| |
|
| | |
| | if ( |
| | isinstance(edge1, Part.Edge) |
| | and isinstance(edge2, Part.Edge) |
| | and (not infinite1) |
| | and (not infinite2) |
| | ): |
| | bb1 = edge1.BoundBox |
| | bb1.enlarge(tol) |
| | if not bb1.intersect(edge2.BoundBox): |
| | return [] |
| |
|
| | |
| | if ( |
| | dts |
| | and ( |
| | (isinstance(edge1, Part.Edge) and isinstance(edge2, (Part.Edge, Part.Face))) |
| | or (isinstance(edge1, (Part.Edge, Part.Face)) and isinstance(edge2, Part.Edge)) |
| | ) |
| | and (not infinite1) |
| | and (not infinite2) |
| | ): |
| | return [v.Point for v in edge1.section((edge2), tol).Vertexes] |
| |
|
| | pt1 = None |
| |
|
| | if isinstance(edge1, App.Vector) and isinstance(edge2, App.Vector): |
| | |
| | pt1 = edge1 |
| | pt2 = edge2 |
| | pt3 = infinite1 |
| | pt4 = infinite2 |
| | infinite1 = ex1 |
| | infinite2 = ex2 |
| | return getLineIntersections(pt1, pt2, pt3, pt4, infinite1, infinite2) |
| |
|
| | elif (geomType(edge1) == "Line") and (geomType(edge2) == "Line"): |
| | |
| | pt1, pt2, pt3, pt4 = [ |
| | edge1.Vertexes[0].Point, |
| | edge1.Vertexes[1].Point, |
| | edge2.Vertexes[0].Point, |
| | edge2.Vertexes[1].Point, |
| | ] |
| | return getLineIntersections(pt1, pt2, pt3, pt4, infinite1, infinite2) |
| |
|
| | elif ( |
| | (geomType(edge1) == "Circle") |
| | and (geomType(edge2) == "Line") |
| | or (geomType(edge1) == "Line") |
| | and (geomType(edge2) == "Circle") |
| | ): |
| |
|
| | |
| | edges = [edge1, edge2] |
| | for edge in edges: |
| | if geomType(edge) == "Line": |
| | line = edge |
| | else: |
| | arc = edge |
| |
|
| | dirVec = vec(line) |
| | dirVec.normalize() |
| | pt1 = line.Vertexes[0].Point |
| | pt2 = line.Vertexes[1].Point |
| | pt3 = arc.Vertexes[0].Point |
| | pt4 = arc.Vertexes[-1].Point |
| | center = arc.Curve.Center |
| |
|
| | int = [] |
| | |
| | if DraftVecUtils.equals(pt1, pt3) or DraftVecUtils.equals(pt1, pt4): |
| | if findAll: |
| | int.append(pt1) |
| | else: |
| | return [pt1] |
| | elif pt2 in [pt3, pt4]: |
| | if findAll: |
| | int.append(pt2) |
| | else: |
| | return [pt2] |
| |
|
| | if DraftVecUtils.isNull(pt1.sub(center).cross(pt2.sub(center)).cross(arc.Curve.Axis)): |
| | |
| |
|
| | dOnLine = center.sub(pt1).dot(dirVec) |
| | onLine = App.Vector(dirVec) |
| | onLine.scale(dOnLine, dOnLine, dOnLine) |
| | toLine = pt1.sub(center).add(onLine) |
| |
|
| | if toLine.Length < arc.Curve.Radius: |
| | dOnLine = (arc.Curve.Radius**2 - toLine.Length**2) ** (0.5) |
| | onLine = App.Vector(dirVec) |
| | onLine.scale(dOnLine, dOnLine, dOnLine) |
| | int += [center.add(toLine).add(onLine)] |
| | onLine = App.Vector(dirVec) |
| | onLine.scale(-dOnLine, -dOnLine, -dOnLine) |
| | int += [center.add(toLine).add(onLine)] |
| | elif round(toLine.Length - arc.Curve.Radius, precision()) == 0: |
| | int = [center.add(toLine)] |
| | else: |
| | return [] |
| |
|
| | else: |
| | |
| | if dirVec.dot(arc.Curve.Axis) != 0: |
| | toPlane = App.Vector(arc.Curve.Axis) |
| | toPlane.normalize() |
| | d = pt1.dot(toPlane) |
| | if not d: |
| | return [] |
| | dToPlane = center.sub(pt1).dot(toPlane) |
| | toPlane = App.Vector(pt1) |
| | toPlane.scale(dToPlane / d, dToPlane / d, dToPlane / d) |
| | ptOnPlane = toPlane.add(pt1) |
| | if round(ptOnPlane.sub(center).Length - arc.Curve.Radius, precision()) == 0: |
| | int = [ptOnPlane] |
| | else: |
| | return [] |
| | else: |
| | return [] |
| |
|
| | if infinite1 is False: |
| | for i in range(len(int) - 1, -1, -1): |
| | if not isPtOnEdge(int[i], edge1): |
| | del int[i] |
| | if infinite2 is False: |
| | for i in range(len(int) - 1, -1, -1): |
| | if not isPtOnEdge(int[i], edge2): |
| | del int[i] |
| | return int |
| |
|
| | elif (geomType(edge1) == "Circle") and (geomType(edge2) == "Circle"): |
| | |
| | cent1, cent2 = edge1.Curve.Center, edge2.Curve.Center |
| | rad1, rad2 = edge1.Curve.Radius, edge2.Curve.Radius |
| | axis1, axis2 = edge1.Curve.Axis, edge2.Curve.Axis |
| | c2c = cent2.sub(cent1) |
| |
|
| | if cent1.sub(cent2).Length == 0: |
| | |
| | return [] |
| |
|
| | if DraftVecUtils.isNull(axis1.cross(axis2)): |
| | if round(c2c.dot(axis1), precision()) == 0: |
| | |
| | dc2c = c2c.Length |
| | if not DraftVecUtils.isNull(c2c): |
| | c2c.normalize() |
| | if ( |
| | round(rad1 + rad2 - dc2c, precision()) < 0 |
| | or round(rad1 - dc2c - rad2, precision()) > 0 |
| | or round(rad2 - dc2c - rad1, precision()) > 0 |
| | ): |
| | return [] |
| | else: |
| | norm = c2c.cross(axis1) |
| | if not DraftVecUtils.isNull(norm): |
| | norm.normalize() |
| | if DraftVecUtils.isNull(norm): |
| | x = 0 |
| | else: |
| | x = (dc2c**2 + rad1**2 - rad2**2) / (2 * dc2c) |
| | y = abs(rad1**2 - x**2) ** (0.5) |
| | c2c.scale(x, x, x) |
| | if round(y, precision()) != 0: |
| | norm.scale(y, y, y) |
| | int = [cent1.add(c2c).add(norm)] |
| | int += [cent1.add(c2c).sub(norm)] |
| | else: |
| | int = [cent1.add(c2c)] |
| | else: |
| | return [] |
| | else: |
| | |
| | axis1.normalize() |
| | axis2.normalize() |
| | U = axis1.cross(axis2) |
| | V = axis1.cross(U) |
| | dToPlane = c2c.dot(axis2) |
| | d = V.add(cent1).dot(axis2) |
| | V.scale(dToPlane / d, dToPlane / d, dToPlane / d) |
| | PtOn2Planes = V.add(cent1) |
| | planeIntersectionVector = U.add(PtOn2Planes) |
| | intTemp = findIntersection(planeIntersectionVector, edge1, True, True) |
| | int = [] |
| | for pt in intTemp: |
| | if round(pt.sub(cent2).Length - rad2, precision()) == 0: |
| | int += [pt] |
| |
|
| | if infinite1 is False: |
| | for i in range(len(int) - 1, -1, -1): |
| | if not isPtOnEdge(int[i], edge1): |
| | del int[i] |
| | if infinite2 is False: |
| | for i in range(len(int) - 1, -1, -1): |
| | if not isPtOnEdge(int[i], edge2): |
| | del int[i] |
| |
|
| | return int |
| | else: |
| | print( |
| | "DraftGeomUtils: Unsupported curve type: " |
| | "(" + str(edge1.Curve) + ", " + str(edge2.Curve) + ")" |
| | ) |
| | return [] |
| |
|
| |
|
| | def wiresIntersect(wire1, wire2): |
| | """Return True if some of the edges of the wires are intersecting. |
| | |
| | Otherwise return `False`. |
| | """ |
| | for e1 in wire1.Edges: |
| | for e2 in wire2.Edges: |
| | if findIntersection(e1, e2, dts=False): |
| | return True |
| | return False |
| |
|
| |
|
| | def connect(edges, closed=False, wireNedge=False): |
| | """Connect the edges in the given list by their intersections.""" |
| |
|
| | inters_list = [] |
| | for i, curr in enumerate(edges): |
| | if i > 0: |
| | prev = edges[i - 1] |
| | elif closed: |
| | prev = edges[-1] |
| | else: |
| | inters_list.append(None) |
| | continue |
| |
|
| | curr_inters_list = findIntersection(prev, curr, True, True) |
| | if len(curr_inters_list) == 0: |
| | inters_list.append(None) |
| | elif len(curr_inters_list) == 1: |
| | inters_list.append(curr_inters_list[0]) |
| | else: |
| | inters = curr_inters_list[ |
| | DraftVecUtils.closest(curr.Vertexes[0].Point, curr_inters_list) |
| | ] |
| | inters_list.append(inters) |
| |
|
| | new_edges_full = [] |
| | new_edges = [] |
| |
|
| | for i, curr in enumerate(edges): |
| | curr_sta = inters_list[i] |
| | if i < (len(edges) - 1): |
| | curr_end = inters_list[i + 1] |
| | elif closed: |
| | curr_end = inters_list[0] |
| | else: |
| | curr_end = None |
| |
|
| | if curr_sta is None: |
| | curr_sta = curr.Vertexes[0].Point |
| | if i > 0: |
| | prev = edges[i - 1] |
| | elif closed: |
| | prev = edges[-1] |
| | else: |
| | prev = None |
| | if prev is not None: |
| | prev_end = prev.Vertexes[-1].Point |
| | new_edges_full.append(Part.LineSegment(prev_end, curr_sta).toShape()) |
| |
|
| | if curr_end is None: |
| | curr_end = curr.Vertexes[-1].Point |
| |
|
| | if curr_sta != curr_end: |
| | if geomType(curr) == "Line": |
| | n = Part.LineSegment(curr_sta, curr_end).toShape() |
| | new_edges.append(n) |
| | new_edges_full.append(n) |
| |
|
| | elif geomType(curr) == "Circle": |
| | n = Part.Arc(curr_sta, findMidpoint(curr), curr_end).toShape() |
| | new_edges.append(n) |
| | new_edges_full.append(n) |
| |
|
| | try: |
| | wire = Part.Wire(new_edges_full) |
| |
|
| | |
| | |
| | if wireNedge: |
| | return (wire, new_edges_full, new_edges) |
| | else: |
| | return wire |
| |
|
| | except Part.OCCError: |
| | print("DraftGeomUtils.connect: unable to connect edges") |
| | for edge in new_edges: |
| | print(edge.Curve, " ", edge.Vertexes[0].Point, " ", edge.Vertexes[-1].Point) |
| | 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() |
| |
|
| |
|
| | |
| |
|