|
2 | 2 |
|
3 | 3 | import math
|
4 | 4 | import random
|
| 5 | +from typing import Tuple, List, Union |
| 6 | +from pyglet.math import Vec2, Vec3 |
5 | 7 | from arcade.types import Point, Vector
|
6 | 8 |
|
7 | 9 | _PRECISION = 2
|
|
11 | 13 | "round_fast",
|
12 | 14 | "clamp",
|
13 | 15 | "lerp",
|
14 |
| - "lerp_vec", |
| 16 | + "lerp_2d", |
| 17 | + "lerp_3d", |
15 | 18 | "lerp_angle",
|
16 | 19 | "rand_in_rect",
|
17 | 20 | "rand_in_circle",
|
|
25 | 28 | "rotate_point",
|
26 | 29 | "get_angle_degrees",
|
27 | 30 | "get_angle_radians",
|
| 31 | + "quaternion_rotation" |
28 | 32 | ]
|
29 | 33 |
|
30 | 34 |
|
@@ -64,13 +68,25 @@ def lerp(v1: float, v2: float, u: float) -> float:
|
64 | 68 | return v1 + ((v2 - v1) * u)
|
65 | 69 |
|
66 | 70 |
|
67 |
| -def lerp_vec(v1: Vector, v2: Vector, u: float) -> Vector: |
| 71 | +V_2D = Union[Vec2, Tuple[float, float], List[float]] |
| 72 | +V_3D = Union[Vec3, Tuple[float, float, float], List[float]] |
| 73 | + |
| 74 | + |
| 75 | +def lerp_2d(v1: V_2D, v2: V_2D, u: float) -> Tuple[float, float]: |
68 | 76 | return (
|
69 | 77 | lerp(v1[0], v2[0], u),
|
70 | 78 | lerp(v1[1], v2[1], u)
|
71 | 79 | )
|
72 | 80 |
|
73 | 81 |
|
| 82 | +def lerp_3d(v1: V_3D, v2: V_3D, u: float) -> Tuple[float, float, float]: |
| 83 | + return ( |
| 84 | + lerp(v1[0], v2[0], u), |
| 85 | + lerp(v1[1], v2[1], u), |
| 86 | + lerp(v1[2], v2[2], u) |
| 87 | + ) |
| 88 | + |
| 89 | + |
74 | 90 | def lerp_angle(start_angle: float, end_angle: float, u: float) -> float:
|
75 | 91 | """
|
76 | 92 | Linearly interpolate between two angles in degrees,
|
@@ -160,7 +176,7 @@ def rand_on_line(pos1: Point, pos2: Point) -> Point:
|
160 | 176 | :return: A random point on the line
|
161 | 177 | """
|
162 | 178 | u = random.uniform(0.0, 1.0)
|
163 |
| - return lerp_vec(pos1, pos2, u) |
| 179 | + return lerp_2d(pos1, pos2, u) |
164 | 180 |
|
165 | 181 |
|
166 | 182 | def rand_angle_360_deg() -> float:
|
@@ -354,3 +370,33 @@ def get_angle_radians(x1: float, y1: float, x2: float, y2: float) -> float:
|
354 | 370 | x_diff = x2 - x1
|
355 | 371 | y_diff = y2 - y1
|
356 | 372 | return math.atan2(x_diff, y_diff)
|
| 373 | + |
| 374 | + |
| 375 | +def quaternion_rotation(axis: Tuple[float, float, float], |
| 376 | + vector: Tuple[float, float, float], |
| 377 | + angle: float) -> Tuple[float, float, float]: |
| 378 | + """ |
| 379 | + Rotate a 3-dimensional vector of any length clockwise around a 3-dimensional unit length vector. |
| 380 | +
|
| 381 | + This method of vector rotation is immune to rotation-lock, however it takes a little more effort |
| 382 | + to find the axis of rotation rather than 3 angles of rotation. |
| 383 | + Ref: https://danceswithcode.net/engineeringnotes/quaternions/quaternions.html. |
| 384 | +
|
| 385 | + :param axis: The unit length vector that will be rotated around |
| 386 | + :param vector: The 3-dimensional vector to be rotated |
| 387 | + :param angle: The angle in degrees to rotate the vector clock-wise by |
| 388 | + :return: A rotated 3-dimension vector with the same length as the argument vector. |
| 389 | + """ |
| 390 | + _rotation_rads = -math.radians(angle) |
| 391 | + p1, p2, p3 = vector |
| 392 | + _c2, _s2 = math.cos(_rotation_rads / 2.0), math.sin(_rotation_rads / 2.0) |
| 393 | + |
| 394 | + q0, q1, q2, q3 = _c2, _s2 * axis[0], _s2 * axis[1], _s2 * axis[2] |
| 395 | + q0_2, q1_2, q2_2, q3_2 = q0 ** 2, q1 ** 2, q2 ** 2, q3 ** 2 |
| 396 | + q01, q02, q03, q12, q13, q23 = q0 * q1, q0 * q2, q0 * q3, q1 * q2, q1 * q3, q2 * q3 |
| 397 | + |
| 398 | + _x = p1 * (q0_2 + q1_2 - q2_2 - q3_2) + 2.0 * (p2 * (q12 - q03) + p3 * (q02 + q13)) |
| 399 | + _y = p2 * (q0_2 - q1_2 + q2_2 - q3_2) + 2.0 * (p1 * (q03 + q12) + p3 * (q23 - q01)) |
| 400 | + _z = p3 * (q0_2 - q1_2 - q2_2 + q3_2) + 2.0 * (p1 * (q13 - q02) + p2 * (q01 + q23)) |
| 401 | + |
| 402 | + return _x, _y, _z |
0 commit comments