Refactor so that view matrix incorporates frame scaling

This commit is contained in:
Grant Sanderson 2023-01-30 14:15:39 -08:00
parent b85c3bd478
commit 277c471c90
8 changed files with 45 additions and 36 deletions

View file

@ -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(),
)

View file

@ -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]

View file

@ -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(

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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)

View file

@ -13,6 +13,7 @@ pydub
pygments
PyOpenGL
pyperclip
pyrr
pyyaml
rich
scipy