2
2
3
3
from __future__ import annotations
4
4
5
- from manim .typing import Point3D_Array , Vector
5
+ from manim .typing import Point3D_Array , Vector , Vector3
6
6
7
7
__all__ = [
8
8
"quaternion_mult" ,
@@ -53,6 +53,16 @@ def norm_squared(v: float) -> float:
53
53
return np .dot (v , v )
54
54
55
55
56
+ def cross (v1 : Vector3 , v2 : Vector3 ) -> Vector3 :
57
+ return np .array (
58
+ [
59
+ v1 [1 ] * v2 [2 ] - v1 [2 ] * v2 [1 ],
60
+ v1 [2 ] * v2 [0 ] - v1 [0 ] * v2 [2 ],
61
+ v1 [0 ] * v2 [1 ] - v1 [1 ] * v2 [0 ],
62
+ ]
63
+ )
64
+
65
+
56
66
# Quaternions
57
67
# TODO, implement quaternion type
58
68
@@ -273,12 +283,12 @@ def z_to_vector(vector: np.ndarray) -> np.ndarray:
273
283
(normalized) vector provided as an argument
274
284
"""
275
285
axis_z = normalize (vector )
276
- axis_y = normalize (np . cross (axis_z , RIGHT ))
277
- axis_x = np . cross (axis_y , axis_z )
286
+ axis_y = normalize (cross (axis_z , RIGHT ))
287
+ axis_x = cross (axis_y , axis_z )
278
288
if np .linalg .norm (axis_y ) == 0 :
279
289
# the vector passed just so happened to be in the x direction.
280
- axis_x = normalize (np . cross (UP , axis_z ))
281
- axis_y = - np . cross (axis_x , axis_z )
290
+ axis_x = normalize (cross (UP , axis_z ))
291
+ axis_y = - cross (axis_x , axis_z )
282
292
283
293
return np .array ([axis_x , axis_y , axis_z ]).T
284
294
@@ -359,7 +369,7 @@ def normalize_along_axis(array: np.ndarray, axis: np.ndarray) -> np.ndarray:
359
369
return array
360
370
361
371
362
- def get_unit_normal (v1 : np . ndarray , v2 : np . ndarray , tol : float = 1e-6 ) -> np . ndarray :
372
+ def get_unit_normal (v1 : Vector3 , v2 : Vector3 , tol : float = 1e-6 ) -> Vector3 :
363
373
"""Gets the unit normal of the vectors.
364
374
365
375
Parameters
@@ -376,16 +386,37 @@ def get_unit_normal(v1: np.ndarray, v2: np.ndarray, tol: float = 1e-6) -> np.nda
376
386
np.ndarray
377
387
The normal of the two vectors.
378
388
"""
379
- v1 , v2 = (normalize (i ) for i in (v1 , v2 ))
380
- cp = np .cross (v1 , v2 )
381
- cp_norm = np .linalg .norm (cp )
382
- if cp_norm < tol :
383
- # Vectors align, so find a normal to them in the plane shared with the z-axis
384
- cp = np .cross (np .cross (v1 , OUT ), v1 )
385
- cp_norm = np .linalg .norm (cp )
386
- if cp_norm < tol :
389
+ # Instead of normalizing v1 and v2, just divide by the greatest
390
+ # of all their absolute components, which is just enough
391
+ div1 , div2 = max (np .abs (v1 )), max (np .abs (v2 ))
392
+ if div1 == 0.0 :
393
+ if div2 == 0.0 :
387
394
return DOWN
388
- return normalize (cp )
395
+ u = v2 / div2
396
+ elif div2 == 0.0 :
397
+ u = v1 / div1
398
+ else :
399
+ # Normal scenario: v1 and v2 are both non-null
400
+ u1 , u2 = v1 / div1 , v2 / div2
401
+ cp = cross (u1 , u2 )
402
+ cp_norm = np .sqrt (norm_squared (cp ))
403
+ if cp_norm > tol :
404
+ return cp / cp_norm
405
+ # Otherwise, v1 and v2 were aligned
406
+ u = u1
407
+
408
+ # If you are here, you have an "unique", non-zero, unit-ish vector u
409
+ # If it's also too aligned to the Z axis, just return DOWN
410
+ if abs (u [0 ]) < tol and abs (u [1 ]) < tol :
411
+ return DOWN
412
+ # Otherwise rotate u in the plane it shares with the Z axis,
413
+ # 90° TOWARDS the Z axis. This is done via (u x [0, 0, 1]) x u,
414
+ # which gives [-xz, -yz, x²+y²] (slightly scaled as well)
415
+ cp = np .array ([- u [0 ] * u [2 ], - u [1 ] * u [2 ], u [0 ] * u [0 ] + u [1 ] * u [1 ]])
416
+ cp_norm = np .sqrt (norm_squared (cp ))
417
+ # Because the norm(u) == 0 case was filtered in the beginning,
418
+ # there is no need to check if the norm of cp is 0
419
+ return cp / cp_norm
389
420
390
421
391
422
###
@@ -529,8 +560,8 @@ def line_intersection(
529
560
np .pad (np .array (i )[:, :2 ], ((0 , 0 ), (0 , 1 )), constant_values = 1 )
530
561
for i in (line1 , line2 )
531
562
)
532
- line1 , line2 = (np . cross (* i ) for i in padded )
533
- x , y , z = np . cross (line1 , line2 )
563
+ line1 , line2 = (cross (* i ) for i in padded )
564
+ x , y , z = cross (line1 , line2 )
534
565
535
566
if z == 0 :
536
567
raise ValueError (
@@ -558,7 +589,7 @@ def find_intersection(
558
589
result = []
559
590
560
591
for p0 , v0 , p1 , v1 in zip (* [p0s , v0s , p1s , v1s ]):
561
- normal = np . cross (v1 , np . cross (v0 , v1 ))
592
+ normal = cross (v1 , cross (v0 , v1 ))
562
593
denom = max (np .dot (v0 , normal ), threshold )
563
594
result += [p0 + np .dot (p1 - p0 , normal ) / denom * v0 ]
564
595
return result
@@ -814,6 +845,6 @@ def perpendicular_bisector(
814
845
"""
815
846
p1 = line [0 ]
816
847
p2 = line [1 ]
817
- direction = np . cross (p1 - p2 , norm_vector )
848
+ direction = cross (p1 - p2 , norm_vector )
818
849
m = midpoint (p1 , p2 )
819
850
return [m + direction , m - direction ]
0 commit comments