mirror of
https://github.com/3b1b/manim.git
synced 2025-07-28 20:43:56 +08:00
Merge branch '3b1b:master' into master
This commit is contained in:
@ -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]
|
||||||
|
@ -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):
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
|
Reference in New Issue
Block a user