From 969aa82f0442c4693b053991bb6db7bc5068ac37 Mon Sep 17 00:00:00 2001 From: Sunkisser <40634339+sunkisser@users.noreply.github.com> Date: Sat, 26 Mar 2022 23:43:11 +0000 Subject: [PATCH 1/9] user feedback for SVGs that take a while --- manimlib/utils/space_ops.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/manimlib/utils/space_ops.py b/manimlib/utils/space_ops.py index 4dde08d5..bd9064e2 100644 --- a/manimlib/utils/space_ops.py +++ b/manimlib/utils/space_ops.py @@ -17,6 +17,7 @@ from manimlib.constants import PI from manimlib.constants import TAU from manimlib.utils.iterables import adjacent_pairs from manimlib.utils.simple_functions import clip +from manimlib.logger import log def cross(v1: np.ndarray, v2: np.ndarray) -> list[np.ndarray]: @@ -415,6 +416,8 @@ def earclip_triangulation(verts: np.ndarray, ring_ends: list[int]) -> list: chilren = [[] for i in rings] for idx, i in enumerate(rings_sorted): + if len(rings_sorted) > 100 and (not idx%100 or idx+1==len(rings_sorted)): + log.info(f"SVG triangulation: {idx}/{len(rings_sorted)}") for j in rings_sorted[:idx][::-1]: if is_in_fast(i, j): chilren[j].append(i) From a3e4246938ece17ed8c0183a573b12ea1f890dfd Mon Sep 17 00:00:00 2001 From: Sunkisser <40634339+sunkisser@users.noreply.github.com> Date: Sun, 27 Mar 2022 21:59:49 +0000 Subject: [PATCH 2/9] use log.debug and display idx+1 --- manimlib/utils/space_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manimlib/utils/space_ops.py b/manimlib/utils/space_ops.py index bd9064e2..38cb78e8 100644 --- a/manimlib/utils/space_ops.py +++ b/manimlib/utils/space_ops.py @@ -417,7 +417,7 @@ def earclip_triangulation(verts: np.ndarray, ring_ends: list[int]) -> list: chilren = [[] for i in rings] for idx, i in enumerate(rings_sorted): if len(rings_sorted) > 100 and (not idx%100 or idx+1==len(rings_sorted)): - log.info(f"SVG triangulation: {idx}/{len(rings_sorted)}") + log.debug(f"SVG triangulation: {idx+1}/{len(rings_sorted)}") for j in rings_sorted[:idx][::-1]: if is_in_fast(i, j): chilren[j].append(i) From e11c5def6337168c3c19951fd0a2f5c15e864672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B9=A4=E7=BF=94=E4=B8=87=E9=87=8C?= Date: Mon, 28 Mar 2022 08:05:41 +0800 Subject: [PATCH 3/9] style: some style fixes --- manimlib/utils/space_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manimlib/utils/space_ops.py b/manimlib/utils/space_ops.py index 38cb78e8..fc7f39e5 100644 --- a/manimlib/utils/space_ops.py +++ b/manimlib/utils/space_ops.py @@ -416,7 +416,7 @@ def earclip_triangulation(verts: np.ndarray, ring_ends: list[int]) -> list: chilren = [[] for i in rings] for idx, i in enumerate(rings_sorted): - if len(rings_sorted) > 100 and (not idx%100 or idx+1==len(rings_sorted)): + if len(rings_sorted) > 100 and (idx%100 == 0 or idx+1 == len(rings_sorted)): log.debug(f"SVG triangulation: {idx+1}/{len(rings_sorted)}") for j in rings_sorted[:idx][::-1]: if is_in_fast(i, j): From cfba6c431f8a333c766b79b1472e207815534031 Mon Sep 17 00:00:00 2001 From: Sunkisser <40634339+sunkisser@users.noreply.github.com> Date: Mon, 28 Mar 2022 02:55:21 +0000 Subject: [PATCH 4/9] revert previous changes - we will refactor using tqdm --- manimlib/utils/space_ops.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/manimlib/utils/space_ops.py b/manimlib/utils/space_ops.py index fc7f39e5..4dde08d5 100644 --- a/manimlib/utils/space_ops.py +++ b/manimlib/utils/space_ops.py @@ -17,7 +17,6 @@ from manimlib.constants import PI from manimlib.constants import TAU from manimlib.utils.iterables import adjacent_pairs from manimlib.utils.simple_functions import clip -from manimlib.logger import log def cross(v1: np.ndarray, v2: np.ndarray) -> list[np.ndarray]: @@ -416,8 +415,6 @@ def earclip_triangulation(verts: np.ndarray, ring_ends: list[int]) -> list: chilren = [[] for i in rings] for idx, i in enumerate(rings_sorted): - if len(rings_sorted) > 100 and (idx%100 == 0 or idx+1 == len(rings_sorted)): - log.debug(f"SVG triangulation: {idx+1}/{len(rings_sorted)}") for j in rings_sorted[:idx][::-1]: if is_in_fast(i, j): chilren[j].append(i) From c4ea79410732a375ea124365752717490193b4fc Mon Sep 17 00:00:00 2001 From: Sunkisser <40634339+sunkisser@users.noreply.github.com> Date: Mon, 28 Mar 2022 03:30:10 +0000 Subject: [PATCH 5/9] use tqdm to display progress bar for long running SVG triangulations --- manimlib/utils/space_ops.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/manimlib/utils/space_ops.py b/manimlib/utils/space_ops.py index 4dde08d5..d570d04d 100644 --- a/manimlib/utils/space_ops.py +++ b/manimlib/utils/space_ops.py @@ -4,11 +4,13 @@ import math import operator as op from functools import reduce from typing import Callable, Iterable, Sequence +import platform import numpy as np import numpy.typing as npt from mapbox_earcut import triangulate_float32 as earcut from scipy.spatial.transform import Rotation +from tqdm import tqdm as ProgressDisplay from manimlib.constants import RIGHT 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] - 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]: if is_in_fast(i, j): chilren[j].append(i) From a0ba9c8b30dd3dd1eeb9e3af45d9b8bd0d7e24bb Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Tue, 29 Mar 2022 19:21:12 -0700 Subject: [PATCH 6/9] Fix CameraFrame.get_euler_angles to match conventions with set_euler_angles --- manimlib/camera/camera.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/manimlib/camera/camera.py b/manimlib/camera/camera.py index 647f28a9..47b9b073 100644 --- a/manimlib/camera/camera.py +++ b/manimlib/camera/camera.py @@ -56,7 +56,7 @@ class CameraFrame(Mobject): return 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): return self.get_orientation().as_matrix().T @@ -73,11 +73,11 @@ class CameraFrame(Mobject): gamma: float | None = None, units: float = RADIANS ): - eulers = self.get_euler_angles() # phi, theta, gamma - for i, var in enumerate([phi, theta, gamma]): + eulers = self.get_euler_angles() # theta, phi, gamma + for i, var in enumerate([theta, phi, gamma]): if var is not None: eulers[i] = var * units - self.set_orientation(Rotation.from_euler('xzy', eulers)) + self.set_orientation(Rotation.from_euler("zxz", eulers[::-1])) return self def reorient( From 0610f331a4f7a126a3aae34f8a2a86eabcb692f4 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Tue, 29 Mar 2022 20:20:41 -0700 Subject: [PATCH 7/9] Add get/set field_of_view for camera frame --- manimlib/camera/camera.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/manimlib/camera/camera.py b/manimlib/camera/camera.py index 47b9b073..40037a3d 100644 --- a/manimlib/camera/camera.py +++ b/manimlib/camera/camera.py @@ -3,6 +3,7 @@ from __future__ import annotations import moderngl from colour import Color import OpenGL.GL as gl +import math import itertools as it @@ -27,13 +28,14 @@ class CameraFrame(Mobject): CONFIG = { "frame_shape": (FRAME_WIDTH, FRAME_HEIGHT), "center_point": ORIGIN, - "focal_distance": 2, + "focal_dist_to_height": 2, } def init_uniforms(self) -> None: super().init_uniforms() # As a quaternion self.uniforms["orientation"] = Rotation.identity().as_quat() + self.uniforms["focal_dist_to_height"] = self.focal_dist_to_height def init_points(self) -> None: self.set_points([ORIGIN, LEFT, RIGHT, DOWN, UP]) @@ -114,6 +116,14 @@ class CameraFrame(Mobject): self.rotate(dgamma, self.get_inverse_camera_rotation_matrix()[2]) 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): return (self.get_width(), self.get_height()) @@ -130,7 +140,10 @@ class CameraFrame(Mobject): return points[4, 1] - points[3, 1] 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: to_camera = self.get_inverse_camera_rotation_matrix()[2] From 2a7a7ac5189a14170f883533137e8a2ae09aac41 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Tue, 29 Mar 2022 20:28:48 -0700 Subject: [PATCH 8/9] Add getter and setter for joint_type --- manimlib/mobject/types/vectorized_mobject.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/manimlib/mobject/types/vectorized_mobject.py b/manimlib/mobject/types/vectorized_mobject.py index 3d27a07f..f5b47859 100644 --- a/manimlib/mobject/types/vectorized_mobject.py +++ b/manimlib/mobject/types/vectorized_mobject.py @@ -341,6 +341,14 @@ class VMobject(Mobject): def get_flat_stroke(self) -> bool: 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 def set_anchors_and_handles( self, From 0f8d7ed59751d42d5011813ba5694ecb506082f7 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Tue, 29 Mar 2022 20:34:14 -0700 Subject: [PATCH 9/9] Add VPrism, and refactor VCube --- manimlib/mobject/three_dimensions.py | 35 +++++++++++++++------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/manimlib/mobject/three_dimensions.py b/manimlib/mobject/three_dimensions.py index 329732b1..c6c2d946 100644 --- a/manimlib/mobject/three_dimensions.py +++ b/manimlib/mobject/three_dimensions.py @@ -206,6 +206,13 @@ class Cube(SGroup): 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): CONFIG = { "fill_color": BLUE_D, @@ -213,18 +220,25 @@ class VCube(VGroup): "stroke_width": 0, "gloss": 0.5, "shadow": 0.5, + "joint_type": "round", } - def __init__(self, side_length: int = 2, **kwargs): - super().__init__(**kwargs) + def __init__(self, side_length: float = 2.0, **kwargs): face = Square(side_length=side_length) - face.get_triangulation() - self.add(*Cube.square_to_cube_faces(face)) + super().__init__(*Cube.square_to_cube_faces(face), **kwargs) self.init_colors() + self.set_joint_type(self.joint_type) self.apply_depth_test() 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): CONFIG = { "fill_color": BLUE_E, @@ -272,20 +286,9 @@ class Dodecahedron(VGroup): # 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): CONFIG = { - "apply_depth_test": True + "apply_depth_test": True, } def __init__(self, vmobject, depth=1.0, direction=IN, **kwargs):