1 # SPDX-FileCopyrightText: 2019-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 from mathutils
import *
8 def IsSamePoint(v31
, v32
, limitDistance
):
9 if (v31
- v32
).magnitude
< limitDistance
: return True
17 p1
= Vector((0, 0, 0))
18 p2
= Vector((1, 0, 0))
19 p3
= Vector((0, 1, 0))
21 return Plane(p1
, p2
, p3
)
24 # plane equation: (p - position).dot(normal) = 0
25 def __init__(self
, P1
, P2
, P3
):
26 self
.normal
= (P2
- P1
).cross(P3
- P1
)
27 self
.normal
.normalize()
32 def CalcIntersectionPointLineSegment(self
, PL1
, PL2
):
35 try: rvPar
= ((self
.position
- PL1
).dot(self
.normal
)) / (DL
.dot(self
.normal
))
41 def CalcNormalParameter(self
, vector
):
42 return (vector
- self
.position
).dot(self
.normal
)
45 def CalcProjection(self
, vector
):
46 normalParameter
= self
.CalcNormalParameter(vector
)
48 rvv3
= vector
- (self
.normal
* normalParameter
)
50 return [normalParameter
, rvv3
]
54 # http://geomalgorithms.com/a07-_distance.html
55 def CalcClosestPointLineSegments(v3P0
, v3P1
, v3Q0
, v3Q1
):
67 try: parP
= (b
* e
- c
* d
) / (a
* c
- b
* b
)
70 try: parQ
= (a
* e
- b
* d
) / (a
* c
- b
* b
)
77 def CalcIntersectionPointLineSegments(v3P0
, v3P1
, v3Q0
, v3Q1
, limitDistance
):
78 rvList
= CalcClosestPointLineSegments(v3P0
, v3P1
, v3Q0
, v3Q1
)
79 if rvList
is None: return None
83 if parP
< 0.0: return None
84 if parP
> 1.0: return None
87 if parQ
< 0.0: return None
88 if parQ
> 1.0: return None
91 pointP
= v3P0
+ ((v3P1
- v3P0
) * parP
)
92 pointQ
= v3Q0
+ ((v3Q1
- v3Q0
) * parQ
)
93 if not IsSamePoint(pointP
, pointQ
, limitDistance
): return None
95 return [parP
, parQ
, pointP
, pointQ
]
98 def CalcIntersectionPointsLineSegmentsPOV(v3P0
, v3P1
, v3Q0
, v3Q1
, v3POV
):
99 planeQ
= Plane(v3POV
, v3Q0
, v3Q1
)
100 parP
= planeQ
.CalcIntersectionPointLineSegment(v3P0
, v3P1
)
101 if parP
is None: return None
102 if parP
< 0.0: return None
103 if parP
> 1.0: return None
105 planeP
= Plane(v3POV
, v3P0
, v3P1
)
106 parQ
= planeP
.CalcIntersectionPointLineSegment(v3Q0
, v3Q1
)
107 if parQ
is None: return None
108 if parQ
< 0.0: return None
109 if parQ
> 1.0: return None
114 def CalcIntersectionPointsLineSegmentsDIR(v3P0
, v3P1
, v3Q0
, v3Q1
, v3DIR
):
116 planeQ
= Plane(v3POV
, v3Q0
, v3Q1
)
117 parP
= planeQ
.CalcIntersectionPointLineSegment(v3P0
, v3P1
)
118 if parP
is None: return None
119 if parP
< 0.0: return None
120 if parP
> 1.0: return None
123 planeP
= Plane(v3POV
, v3P0
, v3P1
)
124 parQ
= planeP
.CalcIntersectionPointLineSegment(v3Q0
, v3Q1
)
125 if parQ
is None: return None
126 if parQ
< 0.0: return None
127 if parQ
> 1.0: return None
133 def CalcRotationMatrix(v3From
, v3To
):
134 cross
= v3From
.cross(v3To
)
136 try: angle
= v3From
.angle(v3To
)
137 except: return Matrix
.Identity(4)
139 return Matrix
.Rotation(angle
, 4, cross
) # normalize axis?
142 def subdivide_cubic_bezier(p1
, p2
, p3
, p4
, t
):
143 p12
= (p2
- p1
) * t
+ p1
144 p23
= (p3
- p2
) * t
+ p2
145 p34
= (p4
- p3
) * t
+ p3
146 p123
= (p23
- p12
) * t
+ p12
147 p234
= (p34
- p23
) * t
+ p23
148 p1234
= (p234
- p123
) * t
+ p123
149 return [p12
, p123
, p1234
, p234
, p34
]