mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
Account for Gimbal lock in panning
This commit is contained in:
parent
2b6ec2d95f
commit
b45c71d3c2
1 changed files with 19 additions and 9 deletions
|
@ -1,6 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
import warnings
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from scipy.spatial.transform import Rotation
|
from scipy.spatial.transform import Rotation
|
||||||
|
@ -9,8 +10,10 @@ from pyrr import Matrix44
|
||||||
from manimlib.constants import DEGREES, RADIANS
|
from manimlib.constants import DEGREES, RADIANS
|
||||||
from manimlib.constants import FRAME_SHAPE
|
from manimlib.constants import FRAME_SHAPE
|
||||||
from manimlib.constants import DOWN, LEFT, ORIGIN, OUT, RIGHT, UP
|
from manimlib.constants import DOWN, LEFT, ORIGIN, OUT, RIGHT, UP
|
||||||
|
from manimlib.constants import PI
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
from manimlib.utils.space_ops import normalize
|
from manimlib.utils.space_ops import normalize
|
||||||
|
from manimlib.utils.simple_functions import clip
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
@ -62,9 +65,16 @@ class CameraFrame(Mobject):
|
||||||
|
|
||||||
def get_euler_angles(self) -> np.ndarray:
|
def get_euler_angles(self) -> np.ndarray:
|
||||||
orientation = self.get_orientation()
|
orientation = self.get_orientation()
|
||||||
if all(orientation.as_quat() == [0, 0, 0, 1]):
|
if np.isclose(orientation.as_quat(), [0, 0, 0, 1]).all():
|
||||||
return np.zeros(3)
|
return np.zeros(3)
|
||||||
return orientation.as_euler(self.euler_axes)[::-1]
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter('ignore', UserWarning) # Ignore UserWarnings
|
||||||
|
angles = orientation.as_euler(self.euler_axes)[::-1]
|
||||||
|
# Handle Gimble lock case
|
||||||
|
if np.isclose(angles[1], 0, atol=1e-2):
|
||||||
|
angles[0] = angles[0] + angles[2]
|
||||||
|
angles[2] = 0
|
||||||
|
return angles
|
||||||
|
|
||||||
def get_theta(self):
|
def get_theta(self):
|
||||||
return self.get_euler_angles()[0]
|
return self.get_euler_angles()[0]
|
||||||
|
@ -134,16 +144,16 @@ class CameraFrame(Mobject):
|
||||||
|
|
||||||
def increment_euler_angles(
|
def increment_euler_angles(
|
||||||
self,
|
self,
|
||||||
dtheta: float | None = None,
|
dtheta: float = 0,
|
||||||
dphi: float | None = None,
|
dphi: float = 0,
|
||||||
dgamma: float | None = None,
|
dgamma: float = 0,
|
||||||
units: float = RADIANS
|
units: float = RADIANS
|
||||||
):
|
):
|
||||||
angles = self.get_euler_angles()
|
angles = self.get_euler_angles()
|
||||||
for i, value in enumerate([dtheta, dphi, dgamma]):
|
new_angles = angles + np.array([dtheta, dphi, dgamma]) * units
|
||||||
if value is not None:
|
new_angles[1] = clip(new_angles[1], 0, PI) # Limit range for phi
|
||||||
angles[i] += value * units
|
new_rot = Rotation.from_euler(self.euler_axes, new_angles[::-1])
|
||||||
self.set_euler_angles(*angles)
|
self.set_orientation(new_rot)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def set_euler_axes(self, seq: str):
|
def set_euler_axes(self, seq: str):
|
||||||
|
|
Loading…
Add table
Reference in a new issue