# SPDX-License-Identifier: GPL-2.0-or-later from mathutils import * def IsSamePoint(v31, v32, limitDistance): if (v31 - v32).magnitude < limitDistance: return True return False class Plane: @staticmethod def XY(): p1 = Vector((0, 0, 0)) p2 = Vector((1, 0, 0)) p3 = Vector((0, 1, 0)) return Plane(p1, p2, p3) # plane equation: (p - position).dot(normal) = 0 def __init__(self, P1, P2, P3): self.normal = (P2 - P1).cross(P3 - P1) self.normal.normalize() self.position = P1 def CalcIntersectionPointLineSegment(self, PL1, PL2): DL = PL2 - PL1 try: rvPar = ((self.position - PL1).dot(self.normal)) / (DL.dot(self.normal)) except: return None return rvPar def CalcNormalParameter(self, vector): return (vector - self.position).dot(self.normal) def CalcProjection(self, vector): normalParameter = self.CalcNormalParameter(vector) rvv3 = vector - (self.normal * normalParameter) return [normalParameter, rvv3] # http://geomalgorithms.com/a07-_distance.html def CalcClosestPointLineSegments(v3P0, v3P1, v3Q0, v3Q1): u = v3P1 - v3P0 v = v3Q1 - v3Q0 w0 = v3P0 - v3Q0 a = u.dot(u) b = u.dot(v) c = v.dot(v) d = u.dot(w0) e = v.dot(w0) try: parP = (b * e - c * d) / (a * c - b * b) except: return None try: parQ = (a * e - b * d) / (a * c - b * b) except: return None return [parP, parQ] def CalcIntersectionPointLineSegments(v3P0, v3P1, v3Q0, v3Q1, limitDistance): rvList = CalcClosestPointLineSegments(v3P0, v3P1, v3Q0, v3Q1) if rvList is None: return None parP = rvList[0] if parP < 0.0: return None if parP > 1.0: return None parQ = rvList[1] if parQ < 0.0: return None if parQ > 1.0: return None pointP = v3P0 + ((v3P1 - v3P0) * parP) pointQ = v3Q0 + ((v3Q1 - v3Q0) * parQ) if not IsSamePoint(pointP, pointQ, limitDistance): return None return [parP, parQ, pointP, pointQ] def CalcIntersectionPointsLineSegmentsPOV(v3P0, v3P1, v3Q0, v3Q1, v3POV): planeQ = Plane(v3POV, v3Q0, v3Q1) parP = planeQ.CalcIntersectionPointLineSegment(v3P0, v3P1) if parP is None: return None if parP < 0.0: return None if parP > 1.0: return None planeP = Plane(v3POV, v3P0, v3P1) parQ = planeP.CalcIntersectionPointLineSegment(v3Q0, v3Q1) if parQ is None: return None if parQ < 0.0: return None if parQ > 1.0: return None return [parP, parQ] def CalcIntersectionPointsLineSegmentsDIR(v3P0, v3P1, v3Q0, v3Q1, v3DIR): v3POV = v3Q0 + v3DIR planeQ = Plane(v3POV, v3Q0, v3Q1) parP = planeQ.CalcIntersectionPointLineSegment(v3P0, v3P1) if parP is None: return None if parP < 0.0: return None if parP > 1.0: return None v3POV = v3P0 + v3DIR planeP = Plane(v3POV, v3P0, v3P1) parQ = planeP.CalcIntersectionPointLineSegment(v3Q0, v3Q1) if parQ is None: return None if parQ < 0.0: return None if parQ > 1.0: return None return [parP, parQ] def CalcRotationMatrix(v3From, v3To): cross = v3From.cross(v3To) try: angle = v3From.angle(v3To) except: return Matrix.Identity(4) return Matrix.Rotation(angle, 4, cross) # normalize axis? def subdivide_cubic_bezier(p1, p2, p3, p4, t): p12 = (p2 - p1) * t + p1 p23 = (p3 - p2) * t + p2 p34 = (p4 - p3) * t + p3 p123 = (p23 - p12) * t + p12 p234 = (p34 - p23) * t + p23 p1234 = (p234 - p123) * t + p123 return [p12, p123, p1234, p234, p34]