Merge branch '3b1b:master' into master

This commit is contained in:
YishiMichael
2022-03-30 22:02:23 +08:00
committed by GitHub
4 changed files with 58 additions and 23 deletions

View File

@ -3,6 +3,7 @@ from __future__ import annotations
import moderngl import moderngl
from colour import Color from colour import Color
import OpenGL.GL as gl import OpenGL.GL as gl
import math
import itertools as it import itertools as it
@ -27,13 +28,14 @@ class CameraFrame(Mobject):
CONFIG = { CONFIG = {
"frame_shape": (FRAME_WIDTH, FRAME_HEIGHT), "frame_shape": (FRAME_WIDTH, FRAME_HEIGHT),
"center_point": ORIGIN, "center_point": ORIGIN,
"focal_distance": 2, "focal_dist_to_height": 2,
} }
def init_uniforms(self) -> None: def init_uniforms(self) -> None:
super().init_uniforms() super().init_uniforms()
# As a quaternion # As a quaternion
self.uniforms["orientation"] = Rotation.identity().as_quat() self.uniforms["orientation"] = Rotation.identity().as_quat()
self.uniforms["focal_dist_to_height"] = self.focal_dist_to_height
def init_points(self) -> None: def init_points(self) -> None:
self.set_points([ORIGIN, LEFT, RIGHT, DOWN, UP]) self.set_points([ORIGIN, LEFT, RIGHT, DOWN, UP])
@ -56,7 +58,7 @@ class CameraFrame(Mobject):
return self return self
def get_euler_angles(self): def get_euler_angles(self):
return self.get_orientation().as_euler("xzy") return self.get_orientation().as_euler("zxz")[::-1]
def get_inverse_camera_rotation_matrix(self): def get_inverse_camera_rotation_matrix(self):
return self.get_orientation().as_matrix().T return self.get_orientation().as_matrix().T
@ -73,11 +75,11 @@ class CameraFrame(Mobject):
gamma: float | None = None, gamma: float | None = None,
units: float = RADIANS units: float = RADIANS
): ):
eulers = self.get_euler_angles() # phi, theta, gamma eulers = self.get_euler_angles() # theta, phi, gamma
for i, var in enumerate([phi, theta, gamma]): for i, var in enumerate([theta, phi, gamma]):
if var is not None: if var is not None:
eulers[i] = var * units eulers[i] = var * units
self.set_orientation(Rotation.from_euler('xzy', eulers)) self.set_orientation(Rotation.from_euler("zxz", eulers[::-1]))
return self return self
def reorient( def reorient(
@ -114,6 +116,14 @@ class CameraFrame(Mobject):
self.rotate(dgamma, self.get_inverse_camera_rotation_matrix()[2]) self.rotate(dgamma, self.get_inverse_camera_rotation_matrix()[2])
return self return self
def set_focal_distance(self, focal_distance: float):
self.uniforms["focal_dist_to_height"] = focal_distance / self.get_height()
return self
def set_field_of_view(self, field_of_view: float):
self.uniforms["focal_dist_to_height"] = 2 * math.tan(field_of_view / 2)
return self
def get_shape(self): def get_shape(self):
return (self.get_width(), self.get_height()) return (self.get_width(), self.get_height())
@ -130,7 +140,10 @@ class CameraFrame(Mobject):
return points[4, 1] - points[3, 1] return points[4, 1] - points[3, 1]
def get_focal_distance(self) -> float: def get_focal_distance(self) -> float:
return self.focal_distance * self.get_height() return self.uniforms["focal_dist_to_height"] * self.get_height()
def get_field_of_view(self) -> float:
return 2 * math.atan(self.uniforms["focal_dist_to_height"] / 2)
def get_implied_camera_location(self) -> np.ndarray: def get_implied_camera_location(self) -> np.ndarray:
to_camera = self.get_inverse_camera_rotation_matrix()[2] to_camera = self.get_inverse_camera_rotation_matrix()[2]

View File

@ -206,6 +206,13 @@ class Cube(SGroup):
return Square3D(resolution=self.square_resolution) return Square3D(resolution=self.square_resolution)
class Prism(Cube):
def __init__(self, width: float = 3.0, height: float = 2.0, depth: float = 1.0, **kwargs):
super().__init__(**kwargs)
for dim, value in enumerate([width, height, depth]):
self.rescale_to_fit(value, dim, stretch=True)
class VCube(VGroup): class VCube(VGroup):
CONFIG = { CONFIG = {
"fill_color": BLUE_D, "fill_color": BLUE_D,
@ -213,18 +220,25 @@ class VCube(VGroup):
"stroke_width": 0, "stroke_width": 0,
"gloss": 0.5, "gloss": 0.5,
"shadow": 0.5, "shadow": 0.5,
"joint_type": "round",
} }
def __init__(self, side_length: int = 2, **kwargs): def __init__(self, side_length: float = 2.0, **kwargs):
super().__init__(**kwargs)
face = Square(side_length=side_length) face = Square(side_length=side_length)
face.get_triangulation() super().__init__(*Cube.square_to_cube_faces(face), **kwargs)
self.add(*Cube.square_to_cube_faces(face))
self.init_colors() self.init_colors()
self.set_joint_type(self.joint_type)
self.apply_depth_test() self.apply_depth_test()
self.refresh_unit_normal() self.refresh_unit_normal()
class VPrism(VCube):
def __init__(self, width: float = 3.0, height: float = 2.0, depth: float = 1.0, **kwargs):
super().__init__(**kwargs)
for dim, value in enumerate([width, height, depth]):
self.rescale_to_fit(value, dim, stretch=True)
class Dodecahedron(VGroup): class Dodecahedron(VGroup):
CONFIG = { CONFIG = {
"fill_color": BLUE_E, "fill_color": BLUE_E,
@ -272,20 +286,9 @@ class Dodecahedron(VGroup):
# self.add(pentagon2.copy().apply_matrix(matrix, about_point=ORIGIN)) # self.add(pentagon2.copy().apply_matrix(matrix, about_point=ORIGIN))
class Prism(Cube):
CONFIG = {
"dimensions": [3, 2, 1]
}
def init_points(self) -> None:
Cube.init_points(self)
for dim, value in enumerate(self.dimensions):
self.rescale_to_fit(value, dim, stretch=True)
class Prismify(VGroup): class Prismify(VGroup):
CONFIG = { CONFIG = {
"apply_depth_test": True "apply_depth_test": True,
} }
def __init__(self, vmobject, depth=1.0, direction=IN, **kwargs): def __init__(self, vmobject, depth=1.0, direction=IN, **kwargs):

View File

@ -341,6 +341,14 @@ class VMobject(Mobject):
def get_flat_stroke(self) -> bool: def get_flat_stroke(self) -> bool:
return self.flat_stroke return self.flat_stroke
def set_joint_type(self, joint_type: str, recurse: bool = True):
for mob in self.get_family(recurse):
mob.joint_type = joint_type
return self
def get_joint_type(self) -> str:
return self.joint_type
# Points # Points
def set_anchors_and_handles( def set_anchors_and_handles(
self, self,

View File

@ -4,11 +4,13 @@ import math
import operator as op import operator as op
from functools import reduce from functools import reduce
from typing import Callable, Iterable, Sequence from typing import Callable, Iterable, Sequence
import platform
import numpy as np import numpy as np
import numpy.typing as npt import numpy.typing as npt
from mapbox_earcut import triangulate_float32 as earcut from mapbox_earcut import triangulate_float32 as earcut
from scipy.spatial.transform import Rotation from scipy.spatial.transform import Rotation
from tqdm import tqdm as ProgressDisplay
from manimlib.constants import RIGHT from manimlib.constants import RIGHT
from manimlib.constants import DOWN from manimlib.constants import DOWN
@ -414,7 +416,16 @@ def earclip_triangulation(verts: np.ndarray, ring_ends: list[int]) -> list:
)) ))
chilren = [[] for i in rings] chilren = [[] for i in rings]
for idx, i in enumerate(rings_sorted): ringenum = ProgressDisplay(
enumerate(rings_sorted),
total=len(rings),
leave=False,
ascii=True if platform.system() == 'Windows' else None,
dynamic_ncols=True,
desc="SVG Triangulation",
delay=3,
)
for idx, i in ringenum:
for j in rings_sorted[:idx][::-1]: for j in rings_sorted[:idx][::-1]:
if is_in_fast(i, j): if is_in_fast(i, j):
chilren[j].append(i) chilren[j].append(i)