diff --git a/manimlib/camera/camera.py b/manimlib/camera/camera.py index f8f02c28..2b54752f 100644 --- a/manimlib/camera/camera.py +++ b/manimlib/camera/camera.py @@ -231,12 +231,12 @@ class Camera(object): cam_pos = self.frame.get_implied_camera_location() self.uniforms.update( + view=tuple(view_matrix.T.flatten()), + focal_distance=frame.get_focal_distance() / frame.get_scale(), frame_shape=frame.get_shape(), pixel_size=self.get_pixel_size(), - view=tuple(view_matrix.T.flatten()), camera_position=tuple(cam_pos), light_position=tuple(light_pos), - focal_distance=frame.get_focal_distance(), ) diff --git a/manimlib/camera/camera_frame.py b/manimlib/camera/camera_frame.py index 5f46e253..0c4f9143 100644 --- a/manimlib/camera/camera_frame.py +++ b/manimlib/camera/camera_frame.py @@ -4,6 +4,7 @@ import math import numpy as np from scipy.spatial.transform import Rotation +from pyrr import Matrix44 from manimlib.constants import DEGREES, RADIANS from manimlib.constants import FRAME_SHAPE @@ -76,6 +77,9 @@ class CameraFrame(Mobject): def get_gamma(self): return self.get_euler_angles()[2] + def get_scale(self): + return self.get_height() / FRAME_SHAPE[1] + def get_inverse_camera_rotation_matrix(self): return self.get_orientation().as_matrix().T @@ -84,13 +88,14 @@ class CameraFrame(Mobject): Returns a 4x4 for the affine transformation mapping a point into the camera's internal coordinate system """ - result = self.view_matrix - result[:] = np.identity(4) - result[:3, 3] = -self.get_center() - rotation = np.identity(4) - rotation[:3, :3] = self.get_inverse_camera_rotation_matrix() - result[:] = np.dot(rotation, result) - return result + shift = Matrix44.from_translation(-self.get_center()).T + rotation = Matrix44.from_quaternion(self.uniforms["orientation"]).T + self.view_matrix[:] = np.dot(rotation, shift) + self.view_matrix[:3, :3] /= self.get_scale() + return self.view_matrix + + def get_inv_view_matrix(self): + return np.linalg.inv(self.get_view_matrix()) def rotate(self, angle: float, axis: np.ndarray = OUT, **kwargs): rot = Rotation.from_rotvec(angle * normalize(axis)) @@ -160,6 +165,10 @@ class CameraFrame(Mobject): def get_shape(self): return (self.get_width(), self.get_height()) + def get_aspect_ratio(self): + width, height = self.get_shape() + return width / height + def get_center(self) -> np.ndarray: # Assumes first point is at the center return self.get_points()[0] @@ -183,6 +192,12 @@ class CameraFrame(Mobject): dist = self.get_focal_distance() return self.get_center() + dist * to_camera - def to_fixed_frame_point(self, point: Vect3): + def to_fixed_frame_point(self, point: Vect3, relative: bool = False): view = self.get_view_matrix() - return np.dot([*point, 1], view.T)[:3] + point4d = [*point, 0 if relative else 1] + return np.dot(point4d, view.T)[:3] + + def from_fixed_frame_point(self, point: Vect3, relative: bool = False): + inv_view = self.get_inv_view_matrix() + point4d = [*point, 0 if relative else 1] + return np.dot(point4d, inv_view.T)[:3] diff --git a/manimlib/scene/scene.py b/manimlib/scene/scene.py index cfb907e6..aee5873e 100644 --- a/manimlib/scene/scene.py +++ b/manimlib/scene/scene.py @@ -804,6 +804,7 @@ class Scene(object): point: Vect3, d_point: Vect3 ) -> None: + assert(self.window is not None) self.mouse_point.move_to(point) event_data = {"point": point, "d_point": d_point} @@ -814,13 +815,13 @@ class Scene(object): frame = self.camera.frame # Handle perspective changes if self.window.is_key_pressed(ord(PAN_3D_KEY)): - ff_d_point = np.dot(d_point, frame.get_view_matrix()[:3, :3].T) + ff_d_point = frame.to_fixed_frame_point(d_point, relative=True) ff_d_point *= self.pan_sensitivity frame.increment_theta(-ff_d_point[0]) frame.increment_phi(ff_d_point[1]) # Handle frame movements elif self.window.is_key_pressed(ord(FRAME_SHIFT_KEY)): - frame.shift(-d_point) + frame.shift(-d_point / self.frame.get_scale()) def on_mouse_drag( self, @@ -830,7 +831,7 @@ class Scene(object): modifiers: int ) -> None: self.mouse_drag_point.move_to(point) - self.frame.shift(-d_point) + self.frame.shift(-d_point / self.frame.get_scale()) event_data = {"point": point, "d_point": d_point, "buttons": buttons, "modifiers": modifiers} propagate_event = EVENT_DISPATCHER.dispatch(EventType.MouseDragEvent, **event_data) @@ -871,7 +872,7 @@ class Scene(object): return frame = self.camera.frame - ff_offset = offset * FRAME_HEIGHT / frame.get_height() + ff_offset = offset / frame.get_scale() frame.scale(1 - ff_offset[1], about_point=point) def on_key_release( diff --git a/manimlib/shaders/inserts/get_gl_Position.glsl b/manimlib/shaders/inserts/get_gl_Position.glsl index 719125ba..7789ae64 100644 --- a/manimlib/shaders/inserts/get_gl_Position.glsl +++ b/manimlib/shaders/inserts/get_gl_Position.glsl @@ -1,20 +1,17 @@ uniform float is_fixed_in_frame; uniform mat4 view; -uniform vec2 frame_shape; uniform float focal_distance; -const vec2 DEFAULT_FRAME_SHAPE = vec2(8.0 * 16.0 / 9.0, 8.0); +const float DEFAULT_FRAME_HEIGHT = 8.0; +const float DEFAULT_FRAME_WIDTH = DEFAULT_FRAME_HEIGHT * 16.0 / 9.0; vec4 get_gl_Position(vec3 point){ - bool is_fixed = bool(is_fixed_in_frame); vec4 result = vec4(point, 1.0); - if(!is_fixed){ + if(!bool(is_fixed_in_frame)){ result = view * result; } - - vec2 shape = is_fixed ? DEFAULT_FRAME_SHAPE : frame_shape; - result.x *= 2.0 / shape.x; - result.y *= 2.0 / shape.y; + result.x *= 2.0 / DEFAULT_FRAME_WIDTH; + result.y *= 2.0 / DEFAULT_FRAME_HEIGHT; result.z /= focal_distance; result.w = 1.0 - result.z; // Flip and scale to prevent premature clipping diff --git a/manimlib/shaders/mandelbrot_fractal/frag.glsl b/manimlib/shaders/mandelbrot_fractal/frag.glsl index 10e62f39..8b00a8ba 100644 --- a/manimlib/shaders/mandelbrot_fractal/frag.glsl +++ b/manimlib/shaders/mandelbrot_fractal/frag.glsl @@ -15,8 +15,6 @@ uniform vec3 color6; uniform vec3 color7; uniform vec3 color8; -uniform vec2 frame_shape; - in vec3 xyz_coords; out vec4 frag_color; diff --git a/manimlib/shaders/newton_fractal/frag.glsl b/manimlib/shaders/newton_fractal/frag.glsl index 64d7d2fa..3323a215 100644 --- a/manimlib/shaders/newton_fractal/frag.glsl +++ b/manimlib/shaders/newton_fractal/frag.glsl @@ -26,8 +26,6 @@ uniform float saturation_factor; uniform float black_for_cycles; uniform float is_parameter_space; -uniform vec2 frame_shape; - in vec3 xyz_coords; out vec4 frag_color; diff --git a/manimlib/window.py b/manimlib/window.py index 178250c1..6ae4c24b 100644 --- a/manimlib/window.py +++ b/manimlib/window.py @@ -7,6 +7,7 @@ from moderngl_window.context.pyglet.window import Window as PygletWindow from moderngl_window.timers.clock import Timer from screeninfo import get_monitors +from manimlib.constants import FRAME_SHAPE from manimlib.utils.customization import get_customization from typing import TYPE_CHECKING @@ -78,16 +79,14 @@ class Window(PygletWindow): relative: bool = False ) -> np.ndarray: pixel_shape = np.array(self.size) - frame_shape = np.array(self.scene.frame.get_shape()) + frame_shape = np.array(FRAME_SHAPE) + frame = self.scene.frame - coords = (frame_shape / pixel_shape) * np.array([px, py]) - view = self.scene.frame.get_view_matrix() - - if relative: - return np.dot([*coords, 0], view[:3, :3]) - - coords -= 0.5 * frame_shape - return np.dot([*coords, 0, 1], np.linalg.inv(view).T)[:3] + coords = np.zeros(3) + coords[:2] = (frame_shape / pixel_shape) * np.array([px, py]) + if not relative: + coords[:2] -= 0.5 * frame_shape + return frame.from_fixed_frame_point(coords, relative) def on_mouse_motion(self, x: int, y: int, dx: int, dy: int) -> None: super().on_mouse_motion(x, y, dx, dy) diff --git a/requirements.txt b/requirements.txt index 0030fd2a..319bacfe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,6 +13,7 @@ pydub pygments PyOpenGL pyperclip +pyrr pyyaml rich scipy