mirror of
https://github.com/3b1b/manim.git
synced 2025-09-01 00:48:45 +00:00
Merge branch 'master' into refactor
This commit is contained in:
commit
97edc2d6cf
69 changed files with 1193 additions and 708 deletions
|
@ -4,6 +4,8 @@ __version__ = pkg_resources.get_distribution("manimgl").version
|
||||||
|
|
||||||
from manimlib.constants import *
|
from manimlib.constants import *
|
||||||
|
|
||||||
|
from manimlib.window import *
|
||||||
|
|
||||||
from manimlib.animation.animation import *
|
from manimlib.animation.animation import *
|
||||||
from manimlib.animation.composition import *
|
from manimlib.animation.composition import *
|
||||||
from manimlib.animation.creation import *
|
from manimlib.animation.creation import *
|
||||||
|
@ -20,17 +22,16 @@ from manimlib.animation.update import *
|
||||||
|
|
||||||
from manimlib.camera.camera import *
|
from manimlib.camera.camera import *
|
||||||
|
|
||||||
from manimlib.window import *
|
|
||||||
|
|
||||||
from manimlib.mobject.boolean_ops import *
|
from manimlib.mobject.boolean_ops import *
|
||||||
from manimlib.mobject.coordinate_systems import *
|
|
||||||
from manimlib.mobject.changing import *
|
from manimlib.mobject.changing import *
|
||||||
|
from manimlib.mobject.coordinate_systems import *
|
||||||
from manimlib.mobject.frame import *
|
from manimlib.mobject.frame import *
|
||||||
from manimlib.mobject.functions import *
|
from manimlib.mobject.functions import *
|
||||||
from manimlib.mobject.geometry import *
|
from manimlib.mobject.geometry import *
|
||||||
from manimlib.mobject.interactive import *
|
from manimlib.mobject.interactive import *
|
||||||
from manimlib.mobject.matrix import *
|
from manimlib.mobject.matrix import *
|
||||||
from manimlib.mobject.mobject import *
|
from manimlib.mobject.mobject import *
|
||||||
|
from manimlib.mobject.mobject_update_utils import *
|
||||||
from manimlib.mobject.number_line import *
|
from manimlib.mobject.number_line import *
|
||||||
from manimlib.mobject.numbers import *
|
from manimlib.mobject.numbers import *
|
||||||
from manimlib.mobject.probability import *
|
from manimlib.mobject.probability import *
|
||||||
|
@ -43,17 +44,16 @@ from manimlib.mobject.svg.svg_mobject import *
|
||||||
from manimlib.mobject.svg.tex_mobject import *
|
from manimlib.mobject.svg.tex_mobject import *
|
||||||
from manimlib.mobject.svg.text_mobject import *
|
from manimlib.mobject.svg.text_mobject import *
|
||||||
from manimlib.mobject.three_dimensions import *
|
from manimlib.mobject.three_dimensions import *
|
||||||
|
from manimlib.mobject.types.dot_cloud import *
|
||||||
from manimlib.mobject.types.image_mobject import *
|
from manimlib.mobject.types.image_mobject import *
|
||||||
from manimlib.mobject.types.point_cloud_mobject import *
|
from manimlib.mobject.types.point_cloud_mobject import *
|
||||||
from manimlib.mobject.types.surface import *
|
from manimlib.mobject.types.surface import *
|
||||||
from manimlib.mobject.types.vectorized_mobject import *
|
from manimlib.mobject.types.vectorized_mobject import *
|
||||||
from manimlib.mobject.types.dot_cloud import *
|
|
||||||
from manimlib.mobject.mobject_update_utils import *
|
|
||||||
from manimlib.mobject.value_tracker import *
|
from manimlib.mobject.value_tracker import *
|
||||||
from manimlib.mobject.vector_field import *
|
from manimlib.mobject.vector_field import *
|
||||||
|
|
||||||
from manimlib.scene.scene import *
|
|
||||||
from manimlib.scene.interactive_scene import *
|
from manimlib.scene.interactive_scene import *
|
||||||
|
from manimlib.scene.scene import *
|
||||||
from manimlib.scene.three_d_scene import *
|
from manimlib.scene.three_d_scene import *
|
||||||
|
|
||||||
from manimlib.utils.bezier import *
|
from manimlib.utils.bezier import *
|
||||||
|
@ -62,9 +62,9 @@ from manimlib.utils.config_ops import *
|
||||||
from manimlib.utils.customization import *
|
from manimlib.utils.customization import *
|
||||||
from manimlib.utils.debug import *
|
from manimlib.utils.debug import *
|
||||||
from manimlib.utils.directories import *
|
from manimlib.utils.directories import *
|
||||||
|
from manimlib.utils.file_ops import *
|
||||||
from manimlib.utils.images import *
|
from manimlib.utils.images import *
|
||||||
from manimlib.utils.iterables import *
|
from manimlib.utils.iterables import *
|
||||||
from manimlib.utils.file_ops import *
|
|
||||||
from manimlib.utils.paths import *
|
from manimlib.utils.paths import *
|
||||||
from manimlib.utils.rate_functions import *
|
from manimlib.utils.rate_functions import *
|
||||||
from manimlib.utils.simple_functions import *
|
from manimlib.utils.simple_functions import *
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
import manimlib.config
|
|
||||||
import manimlib.logger
|
|
||||||
import manimlib.extract_scene
|
|
||||||
import manimlib.utils.init_config
|
|
||||||
from manimlib import __version__
|
from manimlib import __version__
|
||||||
|
import manimlib.config
|
||||||
|
import manimlib.extract_scene
|
||||||
|
import manimlib.logger
|
||||||
|
import manimlib.utils.init_config
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
from manimlib.mobject.mobject import _AnimationBuilder
|
from manimlib.mobject.mobject import _AnimationBuilder
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
|
@ -12,6 +11,8 @@ from manimlib.utils.simple_functions import clip
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
from manimlib.scene.scene import Scene
|
from manimlib.scene.scene import Scene
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
from manimlib.animation.animation import Animation, prepare_animation
|
from manimlib.animation.animation import Animation
|
||||||
|
from manimlib.animation.animation import prepare_animation
|
||||||
from manimlib.mobject.mobject import Group
|
from manimlib.mobject.mobject import Group
|
||||||
from manimlib.utils.bezier import integer_interpolate
|
from manimlib.utils.bezier import integer_interpolate
|
||||||
from manimlib.utils.bezier import interpolate
|
from manimlib.utils.bezier import interpolate
|
||||||
|
@ -15,8 +15,10 @@ from manimlib.utils.simple_functions import clip
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from manimlib.scene.scene import Scene
|
from typing import Callable
|
||||||
|
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
|
from manimlib.scene.scene import Scene
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_LAGGED_START_LAG_RATIO = 0.05
|
DEFAULT_LAGGED_START_LAG_RATIO = 0.05
|
||||||
|
|
|
@ -4,17 +4,17 @@ import numpy as np
|
||||||
|
|
||||||
from manimlib.animation.animation import Animation
|
from manimlib.animation.animation import Animation
|
||||||
from manimlib.animation.transform import Transform
|
from manimlib.animation.transform import Transform
|
||||||
from manimlib.mobject.mobject import Group
|
|
||||||
from manimlib.constants import ORIGIN
|
from manimlib.constants import ORIGIN
|
||||||
|
from manimlib.mobject.mobject import Group
|
||||||
from manimlib.utils.bezier import interpolate
|
from manimlib.utils.bezier import interpolate
|
||||||
from manimlib.utils.rate_functions import there_and_back
|
from manimlib.utils.rate_functions import there_and_back
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from manimlib.scene.scene import Scene
|
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||||
|
from manimlib.scene.scene import Scene
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_FADE_LAG_RATIO = 0
|
DEFAULT_FADE_LAG_RATIO = 0
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from manimlib.constants import PI
|
|
||||||
from manimlib.animation.transform import Transform
|
from manimlib.animation.transform import Transform
|
||||||
|
from manimlib.constants import PI
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from manimlib.mobject.mobject import Mobject
|
|
||||||
from manimlib.mobject.geometry import Arrow
|
from manimlib.mobject.geometry import Arrow
|
||||||
|
from manimlib.mobject.mobject import Mobject
|
||||||
|
|
||||||
|
|
||||||
class GrowFromPoint(Transform):
|
class GrowFromPoint(Transform):
|
||||||
|
|
|
@ -1,40 +1,44 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import math
|
import math
|
||||||
from typing import Union, Sequence
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from manimlib.constants import *
|
|
||||||
from manimlib.animation.animation import Animation
|
from manimlib.animation.animation import Animation
|
||||||
from manimlib.animation.movement import Homotopy
|
|
||||||
from manimlib.animation.composition import AnimationGroup
|
from manimlib.animation.composition import AnimationGroup
|
||||||
from manimlib.animation.composition import Succession
|
from manimlib.animation.composition import Succession
|
||||||
from manimlib.animation.creation import ShowCreation
|
from manimlib.animation.creation import ShowCreation
|
||||||
from manimlib.animation.creation import ShowPartial
|
from manimlib.animation.creation import ShowPartial
|
||||||
from manimlib.animation.fading import FadeOut
|
from manimlib.animation.fading import FadeOut
|
||||||
from manimlib.animation.fading import FadeIn
|
from manimlib.animation.fading import FadeIn
|
||||||
|
from manimlib.animation.movement import Homotopy
|
||||||
from manimlib.animation.transform import Transform
|
from manimlib.animation.transform import Transform
|
||||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
from manimlib.constants import ORIGIN, RIGHT, UP
|
||||||
|
from manimlib.constants import SMALL_BUFF
|
||||||
|
from manimlib.constants import TAU
|
||||||
|
from manimlib.constants import GREY, YELLOW
|
||||||
from manimlib.mobject.geometry import Circle
|
from manimlib.mobject.geometry import Circle
|
||||||
from manimlib.mobject.geometry import Dot
|
from manimlib.mobject.geometry import Dot
|
||||||
|
from manimlib.mobject.geometry import Line
|
||||||
from manimlib.mobject.shape_matchers import SurroundingRectangle
|
from manimlib.mobject.shape_matchers import SurroundingRectangle
|
||||||
from manimlib.mobject.shape_matchers import Underline
|
from manimlib.mobject.shape_matchers import Underline
|
||||||
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||||
from manimlib.mobject.geometry import Line
|
|
||||||
from manimlib.utils.bezier import interpolate
|
from manimlib.utils.bezier import interpolate
|
||||||
from manimlib.utils.config_ops import digest_config
|
from manimlib.utils.config_ops import digest_config
|
||||||
from manimlib.utils.rate_functions import there_and_back
|
|
||||||
from manimlib.utils.rate_functions import wiggle
|
|
||||||
from manimlib.utils.rate_functions import smooth
|
from manimlib.utils.rate_functions import smooth
|
||||||
from manimlib.utils.rate_functions import squish_rate_func
|
from manimlib.utils.rate_functions import squish_rate_func
|
||||||
|
from manimlib.utils.rate_functions import there_and_back
|
||||||
|
from manimlib.utils.rate_functions import wiggle
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import colour
|
from colour import Color
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
ManimColor = Union[str, colour.Color, Sequence[float]]
|
|
||||||
|
ManimColor = Union[str, Color]
|
||||||
|
|
||||||
|
|
||||||
class FocusOn(Transform):
|
class FocusOn(Transform):
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Callable, Sequence
|
|
||||||
|
|
||||||
from manimlib.animation.animation import Animation
|
from manimlib.animation.animation import Animation
|
||||||
from manimlib.utils.rate_functions import linear
|
from manimlib.utils.rate_functions import linear
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from typing import Callable, Sequence
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
from manimlib.animation.animation import Animation
|
from manimlib.animation.animation import Animation
|
||||||
from manimlib.mobject.numbers import DecimalNumber
|
from manimlib.mobject.numbers import DecimalNumber
|
||||||
from manimlib.utils.bezier import interpolate
|
from manimlib.utils.bezier import interpolate
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
|
||||||
class ChangingDecimal(Animation):
|
class ChangingDecimal(Animation):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from manimlib.animation.animation import Animation
|
from manimlib.animation.animation import Animation
|
||||||
from manimlib.constants import OUT
|
from manimlib.constants import ORIGIN, OUT
|
||||||
from manimlib.constants import PI
|
from manimlib.constants import PI, TAU
|
||||||
from manimlib.constants import TAU
|
|
||||||
from manimlib.constants import ORIGIN
|
|
||||||
from manimlib.utils.rate_functions import linear
|
from manimlib.utils.rate_functions import linear
|
||||||
from manimlib.utils.rate_functions import smooth
|
from manimlib.utils.rate_functions import smooth
|
||||||
|
|
||||||
|
@ -12,6 +10,7 @@ from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
from manimlib.animation.composition import LaggedStart
|
from manimlib.animation.composition import LaggedStart
|
||||||
from manimlib.animation.transform import Restore
|
from manimlib.animation.transform import Restore
|
||||||
from manimlib.constants import WHITE
|
from manimlib.constants import BLACK, WHITE
|
||||||
from manimlib.constants import BLACK
|
|
||||||
from manimlib.mobject.geometry import Circle
|
from manimlib.mobject.geometry import Circle
|
||||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||||
from manimlib.utils.config_ops import digest_config
|
from manimlib.utils.config_ops import digest_config
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
class Broadcast(LaggedStart):
|
class Broadcast(LaggedStart):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
from typing import Callable, Union, Sequence
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import numpy.typing as npt
|
|
||||||
|
|
||||||
from manimlib.animation.animation import Animation
|
from manimlib.animation.animation import Animation
|
||||||
from manimlib.constants import DEFAULT_POINTWISE_FUNCTION_RUN_TIME
|
from manimlib.constants import DEFAULT_POINTWISE_FUNCTION_RUN_TIME
|
||||||
from manimlib.constants import OUT
|
|
||||||
from manimlib.constants import DEGREES
|
from manimlib.constants import DEGREES
|
||||||
|
from manimlib.constants import OUT
|
||||||
from manimlib.mobject.mobject import Group
|
from manimlib.mobject.mobject import Group
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
from manimlib.utils.config_ops import digest_config
|
from manimlib.utils.config_ops import digest_config
|
||||||
|
@ -21,9 +19,14 @@ from manimlib.utils.rate_functions import squish_rate_func
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import colour
|
from colour import Color
|
||||||
|
from typing import Callable, Union
|
||||||
|
|
||||||
|
import numpy.typing as npt
|
||||||
|
|
||||||
from manimlib.scene.scene import Scene
|
from manimlib.scene.scene import Scene
|
||||||
ManimColor = Union[str, colour.Color, Sequence[float]]
|
|
||||||
|
ManimColor = Union[str, Color]
|
||||||
|
|
||||||
|
|
||||||
class Transform(Animation):
|
class Transform(Animation):
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import operator as op
|
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
from manimlib.animation.animation import Animation
|
from manimlib.animation.animation import Animation
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,10 +46,7 @@ class MaintainPositionRelativeTo(Animation):
|
||||||
**kwargs
|
**kwargs
|
||||||
):
|
):
|
||||||
self.tracked_mobject = tracked_mobject
|
self.tracked_mobject = tracked_mobject
|
||||||
self.diff = op.sub(
|
self.diff = mobject.get_center() - tracked_mobject.get_center()
|
||||||
mobject.get_center(),
|
|
||||||
tracked_mobject.get_center(),
|
|
||||||
)
|
|
||||||
super().__init__(mobject, **kwargs)
|
super().__init__(mobject, **kwargs)
|
||||||
|
|
||||||
def interpolate_mobject(self, alpha: float) -> None:
|
def interpolate_mobject(self, alpha: float) -> None:
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import moderngl
|
import itertools as it
|
||||||
from colour import Color
|
|
||||||
import OpenGL.GL as gl
|
|
||||||
import math
|
import math
|
||||||
|
|
||||||
import itertools as it
|
import moderngl
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from scipy.spatial.transform import Rotation
|
import OpenGL.GL as gl
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
from scipy.spatial.transform import Rotation
|
||||||
|
|
||||||
from manimlib.constants import *
|
from manimlib.constants import BLACK
|
||||||
|
from manimlib.constants import DEGREES, RADIANS
|
||||||
|
from manimlib.constants import DEFAULT_FRAME_RATE
|
||||||
|
from manimlib.constants import DEFAULT_PIXEL_HEIGHT, DEFAULT_PIXEL_WIDTH
|
||||||
|
from manimlib.constants import FRAME_HEIGHT, FRAME_WIDTH
|
||||||
|
from manimlib.constants import DOWN, LEFT, ORIGIN, OUT, RIGHT, UP
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
from manimlib.mobject.mobject import Point
|
from manimlib.mobject.mobject import Point
|
||||||
|
from manimlib.utils.color import color_to_rgba
|
||||||
from manimlib.utils.config_ops import digest_config
|
from manimlib.utils.config_ops import digest_config
|
||||||
from manimlib.utils.simple_functions import fdiv
|
from manimlib.utils.simple_functions import fdiv
|
||||||
from manimlib.utils.space_ops import normalize
|
from manimlib.utils.space_ops import normalize
|
||||||
|
@ -189,10 +193,9 @@ class Camera(object):
|
||||||
def __init__(self, ctx: moderngl.Context | None = None, **kwargs):
|
def __init__(self, ctx: moderngl.Context | None = None, **kwargs):
|
||||||
digest_config(self, kwargs, locals())
|
digest_config(self, kwargs, locals())
|
||||||
self.rgb_max_val: float = np.iinfo(self.pixel_array_dtype).max
|
self.rgb_max_val: float = np.iinfo(self.pixel_array_dtype).max
|
||||||
self.background_rgba: list[float] = [
|
self.background_rgba: list[float] = list(color_to_rgba(
|
||||||
*Color(self.background_color).get_rgb(),
|
self.background_color, self.background_opacity
|
||||||
self.background_opacity
|
))
|
||||||
]
|
|
||||||
self.init_frame()
|
self.init_frame()
|
||||||
self.init_context(ctx)
|
self.init_context(ctx)
|
||||||
self.init_shaders()
|
self.init_shaders()
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import argparse
|
import argparse
|
||||||
import colour
|
import colour
|
||||||
import inspect
|
from contextlib import contextmanager
|
||||||
import importlib
|
import importlib
|
||||||
|
import inspect
|
||||||
import os
|
import os
|
||||||
|
from screeninfo import get_monitors
|
||||||
import sys
|
import sys
|
||||||
import yaml
|
import yaml
|
||||||
from contextlib import contextmanager
|
|
||||||
from screeninfo import get_monitors
|
|
||||||
|
|
||||||
|
from manimlib.logger import log
|
||||||
from manimlib.utils.config_ops import merge_dicts_recursively
|
from manimlib.utils.config_ops import merge_dicts_recursively
|
||||||
from manimlib.utils.init_config import init_customization
|
from manimlib.utils.init_config import init_customization
|
||||||
from manimlib.logger import log
|
|
||||||
|
|
||||||
|
|
||||||
__config_file__ = "custom_config.yml"
|
__config_file__ = "custom_config.yml"
|
||||||
|
@ -123,10 +123,13 @@ def parse_cli():
|
||||||
"the rendering at the second value",
|
"the rendering at the second value",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-e", "--embed", metavar="LINENO",
|
"-e", "--embed",
|
||||||
help="Takes a line number as an argument, and results"
|
nargs="?",
|
||||||
"in the scene being called as if the line `self.embed()`"
|
const="",
|
||||||
"was inserted into the scene code at that line number."
|
help="Creates a new file where the line `self.embed` is inserted "
|
||||||
|
"into the Scenes construct method. "
|
||||||
|
"If a string is passed in, the line will be inserted below the "
|
||||||
|
"last line of code including that string."
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-r", "--resolution",
|
"-r", "--resolution",
|
||||||
|
@ -185,22 +188,70 @@ def get_module(file_name):
|
||||||
return module
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def get_indent(line: str):
|
||||||
|
return len(line) - len(line.lstrip())
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def insert_embed_line(file_name, lineno):
|
def insert_embed_line(file_name: str, scene_name: str, line_marker: str):
|
||||||
|
"""
|
||||||
|
This is hacky, but convenient. When user includes the argument "-e", it will try
|
||||||
|
to recreate a file that inserts the line `self.embed()` into the end of the scene's
|
||||||
|
construct method. If there is an argument passed in, it will insert the line after
|
||||||
|
the last line in the sourcefile which includes that string.
|
||||||
|
"""
|
||||||
with open(file_name, 'r') as fp:
|
with open(file_name, 'r') as fp:
|
||||||
lines = fp.readlines()
|
lines = fp.readlines()
|
||||||
line = lines[lineno - 1]
|
|
||||||
n_spaces = len(line) - len(line.lstrip())
|
|
||||||
lines.insert(lineno, " " * n_spaces + "self.embed()\n")
|
|
||||||
|
|
||||||
alt_file = file_name.replace(".py", "_inserted_embed.py")
|
|
||||||
with open(alt_file, 'w') as fp:
|
|
||||||
fp.writelines(lines)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
yield alt_file
|
scene_line_number = next(
|
||||||
|
i for i, line in enumerate(lines)
|
||||||
|
if line.startswith(f"class {scene_name}")
|
||||||
|
)
|
||||||
|
except StopIteration:
|
||||||
|
log.error(f"No scene {scene_name}")
|
||||||
|
|
||||||
|
prev_line_num = None
|
||||||
|
n_spaces = None
|
||||||
|
if len(line_marker) == 0:
|
||||||
|
# Find the end of the construct method
|
||||||
|
in_construct = False
|
||||||
|
for index in range(scene_line_number, len(lines) - 1):
|
||||||
|
line = lines[index]
|
||||||
|
if line.lstrip().startswith("def construct"):
|
||||||
|
in_construct = True
|
||||||
|
n_spaces = get_indent(line) + 4
|
||||||
|
elif in_construct:
|
||||||
|
if len(line.strip()) > 0 and get_indent(line) < n_spaces:
|
||||||
|
prev_line_num = index - 2
|
||||||
|
break
|
||||||
|
elif line_marker.isdigit():
|
||||||
|
# Treat the argument as a line number
|
||||||
|
prev_line_num = int(line_marker) - 1
|
||||||
|
elif len(line_marker) > 0:
|
||||||
|
# Treat the argument as a string
|
||||||
|
try:
|
||||||
|
prev_line_num = next(
|
||||||
|
i
|
||||||
|
for i in range(len(lines) - 1, scene_line_number, -1)
|
||||||
|
if line_marker in lines[i]
|
||||||
|
)
|
||||||
|
except StopIteration:
|
||||||
|
log.error(f"No lines matching {line_marker}")
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
# Insert and write new file
|
||||||
|
if n_spaces is None:
|
||||||
|
n_spaces = get_indent(lines[prev_line_num])
|
||||||
|
new_lines = list(lines)
|
||||||
|
new_lines.insert(prev_line_num + 1, " " * n_spaces + "self.embed()\n")
|
||||||
|
with open(file_name, 'w') as fp:
|
||||||
|
fp.writelines(new_lines)
|
||||||
|
try:
|
||||||
|
yield file_name
|
||||||
finally:
|
finally:
|
||||||
os.remove(alt_file)
|
with open(file_name, 'w') as fp:
|
||||||
|
fp.writelines(lines)
|
||||||
|
|
||||||
|
|
||||||
def get_custom_config():
|
def get_custom_config():
|
||||||
|
@ -296,10 +347,10 @@ def get_configuration(args):
|
||||||
"quiet": args.quiet,
|
"quiet": args.quiet,
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.embed is None:
|
|
||||||
module = get_module(args.file)
|
module = get_module(args.file)
|
||||||
else:
|
|
||||||
with insert_embed_line(args.file, int(args.embed)) as alt_file:
|
if args.embed is not None:
|
||||||
|
with insert_embed_line(args.file, args.scene_names[0], args.embed) as alt_file:
|
||||||
module = get_module(alt_file)
|
module = get_module(alt_file)
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
# Sizes relevant to default camera frame
|
# Sizes relevant to default camera frame
|
||||||
ASPECT_RATIO = 16.0 / 9.0
|
ASPECT_RATIO = 16.0 / 9.0
|
||||||
FRAME_HEIGHT = 8.0
|
FRAME_HEIGHT = 8.0
|
||||||
|
@ -74,6 +75,7 @@ DEFAULT_STROKE_WIDTH = 4
|
||||||
# For keyboard interactions
|
# For keyboard interactions
|
||||||
CTRL_SYMBOL = 65508
|
CTRL_SYMBOL = 65508
|
||||||
SHIFT_SYMBOL = 65505
|
SHIFT_SYMBOL = 65505
|
||||||
|
COMMAND_SYMBOL = 65517
|
||||||
DELETE_SYMBOL = 65288
|
DELETE_SYMBOL = 65288
|
||||||
ARROW_SYMBOLS = list(range(65361, 65365))
|
ARROW_SYMBOLS = list(range(65361, 65365))
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@ from __future__ import annotations
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from manimlib.event_handler.event_type import EventType
|
|
||||||
from manimlib.event_handler.event_listner import EventListner
|
from manimlib.event_handler.event_listner import EventListner
|
||||||
|
from manimlib.event_handler.event_type import EventType
|
||||||
|
|
||||||
|
|
||||||
class EventDispatcher(object):
|
class EventDispatcher(object):
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Callable, TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from manimlib.mobject.mobject import Mobject
|
from typing import Callable
|
||||||
|
|
||||||
from manimlib.event_handler.event_type import EventType
|
from manimlib.event_handler.event_type import EventType
|
||||||
|
from manimlib.mobject.mobject import Mobject
|
||||||
|
|
||||||
|
|
||||||
class EventListner(object):
|
class EventListner(object):
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
|
import copy
|
||||||
import inspect
|
import inspect
|
||||||
import sys
|
import sys
|
||||||
import copy
|
|
||||||
|
|
||||||
from manimlib.scene.scene import Scene
|
|
||||||
from manimlib.config import get_custom_config
|
from manimlib.config import get_custom_config
|
||||||
from manimlib.logger import log
|
from manimlib.logger import log
|
||||||
|
from manimlib.scene.interactive_scene import InteractiveScene
|
||||||
|
from manimlib.scene.scene import Scene
|
||||||
|
|
||||||
|
|
||||||
class BlankScene(Scene):
|
class BlankScene(InteractiveScene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
exec(get_custom_config()["universal_import_line"])
|
exec(get_custom_config()["universal_import_line"])
|
||||||
self.embed()
|
self.embed()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from rich.logging import RichHandler
|
from rich.logging import RichHandler
|
||||||
|
|
||||||
__all__ = ["log"]
|
__all__ = ["log"]
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from manimlib.constants import BLUE_D
|
from manimlib.constants import BLUE_B, BLUE_D, BLUE_E, GREY_BROWN, WHITE
|
||||||
from manimlib.constants import BLUE_B
|
|
||||||
from manimlib.constants import BLUE_E
|
|
||||||
from manimlib.constants import GREY_BROWN
|
|
||||||
from manimlib.constants import WHITE
|
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
|
||||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||||
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||||
from manimlib.utils.rate_functions import smooth
|
from manimlib.utils.rate_functions import smooth
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
|
||||||
class AnimatedBoundary(VGroup):
|
class AnimatedBoundary(VGroup):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
import numbers
|
import numbers
|
||||||
from abc import abstractmethod
|
|
||||||
from typing import Type, TypeVar, Union, Callable, Iterable, Sequence
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from manimlib.constants import *
|
from manimlib.constants import BLACK, BLUE, BLUE_D, GREEN, GREY_A, WHITE
|
||||||
|
from manimlib.constants import DEGREES, PI
|
||||||
|
from manimlib.constants import DL, DOWN, DR, LEFT, ORIGIN, OUT, RIGHT, UP
|
||||||
|
from manimlib.constants import FRAME_HEIGHT, FRAME_WIDTH
|
||||||
|
from manimlib.constants import FRAME_X_RADIUS, FRAME_Y_RADIUS
|
||||||
|
from manimlib.constants import MED_SMALL_BUFF, SMALL_BUFF
|
||||||
from manimlib.mobject.functions import ParametricCurve
|
from manimlib.mobject.functions import ParametricCurve
|
||||||
from manimlib.mobject.geometry import Arrow
|
from manimlib.mobject.geometry import Arrow
|
||||||
from manimlib.mobject.geometry import Line
|
|
||||||
from manimlib.mobject.geometry import DashedLine
|
from manimlib.mobject.geometry import DashedLine
|
||||||
|
from manimlib.mobject.geometry import Line
|
||||||
from manimlib.mobject.geometry import Rectangle
|
from manimlib.mobject.geometry import Rectangle
|
||||||
from manimlib.mobject.number_line import NumberLine
|
from manimlib.mobject.number_line import NumberLine
|
||||||
from manimlib.mobject.svg.tex_mobject import Tex
|
from manimlib.mobject.svg.tex_mobject import Tex
|
||||||
|
@ -25,16 +29,19 @@ from manimlib.utils.space_ops import rotate_vector
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import colour
|
from colour import Color
|
||||||
|
from typing import Callable, Iterable, Sequence, Type, TypeVar, Union
|
||||||
|
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
|
|
||||||
T = TypeVar("T", bound=Mobject)
|
T = TypeVar("T", bound=Mobject)
|
||||||
ManimColor = Union[str, colour.Color, Sequence[float]]
|
ManimColor = Union[str, Color]
|
||||||
|
|
||||||
|
|
||||||
EPSILON = 1e-8
|
EPSILON = 1e-8
|
||||||
|
|
||||||
|
|
||||||
class CoordinateSystem():
|
class CoordinateSystem(ABC):
|
||||||
"""
|
"""
|
||||||
Abstract class for Axes and NumberPlane
|
Abstract class for Axes and NumberPlane
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from manimlib.constants import *
|
from manimlib.constants import BLACK, GREY_E
|
||||||
|
from manimlib.constants import FRAME_HEIGHT
|
||||||
from manimlib.mobject.geometry import Rectangle
|
from manimlib.mobject.geometry import Rectangle
|
||||||
from manimlib.utils.config_ops import digest_config
|
from manimlib.utils.config_ops import digest_config
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Callable, Sequence
|
|
||||||
|
|
||||||
from isosurfaces import plot_isoline
|
from isosurfaces import plot_isoline
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
from manimlib.constants import *
|
from manimlib.constants import FRAME_X_RADIUS, FRAME_Y_RADIUS
|
||||||
|
from manimlib.constants import YELLOW
|
||||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||||
from manimlib.utils.config_ops import digest_config
|
from manimlib.utils.config_ops import digest_config
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import Callable, Sequence
|
||||||
|
|
||||||
|
|
||||||
class ParametricCurve(VMobject):
|
class ParametricCurve(VMobject):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
|
@ -2,23 +2,24 @@ from __future__ import annotations
|
||||||
|
|
||||||
import math
|
import math
|
||||||
import numbers
|
import numbers
|
||||||
from typing import Sequence, Union
|
|
||||||
|
|
||||||
import colour
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from manimlib.constants import *
|
from manimlib.constants import DL, DOWN, DR, LEFT, ORIGIN, OUT, RIGHT, UL, UP, UR
|
||||||
|
from manimlib.constants import GREY_A, RED, WHITE
|
||||||
|
from manimlib.constants import MED_SMALL_BUFF
|
||||||
|
from manimlib.constants import PI, TAU
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
|
from manimlib.mobject.types.vectorized_mobject import DashedVMobject
|
||||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||||
from manimlib.mobject.types.vectorized_mobject import DashedVMobject
|
|
||||||
from manimlib.utils.config_ops import digest_config
|
from manimlib.utils.config_ops import digest_config
|
||||||
from manimlib.utils.iterables import adjacent_n_tuples
|
from manimlib.utils.iterables import adjacent_n_tuples
|
||||||
from manimlib.utils.iterables import adjacent_pairs
|
from manimlib.utils.iterables import adjacent_pairs
|
||||||
from manimlib.utils.simple_functions import fdiv
|
|
||||||
from manimlib.utils.simple_functions import clip
|
from manimlib.utils.simple_functions import clip
|
||||||
from manimlib.utils.space_ops import angle_of_vector
|
from manimlib.utils.simple_functions import fdiv
|
||||||
from manimlib.utils.space_ops import angle_between_vectors
|
from manimlib.utils.space_ops import angle_between_vectors
|
||||||
|
from manimlib.utils.space_ops import angle_of_vector
|
||||||
from manimlib.utils.space_ops import compass_directions
|
from manimlib.utils.space_ops import compass_directions
|
||||||
from manimlib.utils.space_ops import find_intersection
|
from manimlib.utils.space_ops import find_intersection
|
||||||
from manimlib.utils.space_ops import get_norm
|
from manimlib.utils.space_ops import get_norm
|
||||||
|
@ -26,7 +27,13 @@ from manimlib.utils.space_ops import normalize
|
||||||
from manimlib.utils.space_ops import rotate_vector
|
from manimlib.utils.space_ops import rotate_vector
|
||||||
from manimlib.utils.space_ops import rotation_matrix_transpose
|
from manimlib.utils.space_ops import rotation_matrix_transpose
|
||||||
|
|
||||||
ManimColor = Union[str, colour.Color, Sequence[float]]
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from colour import Color
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
ManimColor = Union[str, Color]
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_DOT_RADIUS = 0.08
|
DEFAULT_DOT_RADIUS = 0.08
|
||||||
|
@ -716,8 +723,8 @@ class Arrow(Line):
|
||||||
|
|
||||||
def set_stroke(
|
def set_stroke(
|
||||||
self,
|
self,
|
||||||
color: ManimColor | None = None,
|
color: ManimColor | Iterable[ManimColor] | None = None,
|
||||||
width: float | None = None,
|
width: float | Iterable[float] | None = None,
|
||||||
*args, **kwargs
|
*args, **kwargs
|
||||||
):
|
):
|
||||||
super().set_stroke(color=color, width=width, *args, **kwargs)
|
super().set_stroke(color=color, width=width, *args, **kwargs)
|
||||||
|
|
|
@ -1,22 +1,32 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from pyglet.window import key as PygletWindowKeys
|
from pyglet.window import key as PygletWindowKeys
|
||||||
|
|
||||||
from manimlib.constants import FRAME_HEIGHT, FRAME_WIDTH
|
from manimlib.constants import FRAME_HEIGHT, FRAME_WIDTH
|
||||||
from manimlib.constants import LEFT, RIGHT, UP, DOWN, ORIGIN
|
from manimlib.constants import DOWN, LEFT, ORIGIN, RIGHT, UP
|
||||||
from manimlib.constants import SMALL_BUFF, MED_SMALL_BUFF, MED_LARGE_BUFF
|
from manimlib.constants import MED_LARGE_BUFF, MED_SMALL_BUFF, SMALL_BUFF
|
||||||
from manimlib.constants import BLACK, GREY_A, GREY_C, RED, GREEN, BLUE, WHITE
|
from manimlib.constants import BLACK, BLUE, GREEN, GREY_A, GREY_C, RED, WHITE
|
||||||
from manimlib.mobject.mobject import Mobject, Group
|
from manimlib.mobject.mobject import Group
|
||||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
from manimlib.mobject.mobject import Mobject
|
||||||
from manimlib.mobject.geometry import Dot, Line, Square, Rectangle, RoundedRectangle, Circle
|
from manimlib.mobject.geometry import Circle
|
||||||
|
from manimlib.mobject.geometry import Dot
|
||||||
|
from manimlib.mobject.geometry import Line
|
||||||
|
from manimlib.mobject.geometry import Rectangle
|
||||||
|
from manimlib.mobject.geometry import RoundedRectangle
|
||||||
|
from manimlib.mobject.geometry import Square
|
||||||
from manimlib.mobject.svg.text_mobject import Text
|
from manimlib.mobject.svg.text_mobject import Text
|
||||||
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||||
from manimlib.mobject.value_tracker import ValueTracker
|
from manimlib.mobject.value_tracker import ValueTracker
|
||||||
|
from manimlib.utils.color import rgb_to_hex
|
||||||
from manimlib.utils.config_ops import digest_config
|
from manimlib.utils.config_ops import digest_config
|
||||||
from manimlib.utils.space_ops import get_norm, get_closest_point_on_line
|
from manimlib.utils.space_ops import get_closest_point_on_line
|
||||||
from manimlib.utils.color import rgb_to_color, color_to_rgba, rgb_to_hex
|
from manimlib.utils.space_ops import get_norm
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
|
||||||
# Interactive Mobjects
|
# Interactive Mobjects
|
||||||
|
@ -336,7 +346,7 @@ class ColorSliders(Group):
|
||||||
g = self.g_slider.get_value() / 255
|
g = self.g_slider.get_value() / 255
|
||||||
b = self.b_slider.get_value() / 255
|
b = self.b_slider.get_value() / 255
|
||||||
alpha = self.a_slider.get_value()
|
alpha = self.a_slider.get_value()
|
||||||
return color_to_rgba(rgb_to_color((r, g, b)), alpha=alpha)
|
return np.array((r, g, b, alpha))
|
||||||
|
|
||||||
def get_picked_color(self) -> str:
|
def get_picked_color(self) -> str:
|
||||||
rgba = self.get_value()
|
rgba = self.get_value()
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import itertools as it
|
import itertools as it
|
||||||
from typing import Union, Sequence
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import numpy.typing as npt
|
|
||||||
|
|
||||||
from manimlib.constants import *
|
from manimlib.constants import DEFAULT_MOBJECT_TO_MOBJECT_BUFFER
|
||||||
|
from manimlib.constants import DOWN, LEFT, RIGHT, UP
|
||||||
|
from manimlib.constants import WHITE
|
||||||
from manimlib.mobject.numbers import DecimalNumber
|
from manimlib.mobject.numbers import DecimalNumber
|
||||||
from manimlib.mobject.numbers import Integer
|
from manimlib.mobject.numbers import Integer
|
||||||
from manimlib.mobject.shape_matchers import BackgroundRectangle
|
from manimlib.mobject.shape_matchers import BackgroundRectangle
|
||||||
|
@ -18,9 +18,14 @@ from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import colour
|
from colour import Color
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
import numpy.typing as npt
|
||||||
|
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
ManimColor = Union[str, colour.Color, Sequence[float]]
|
|
||||||
|
ManimColor = Union[str, Color]
|
||||||
|
|
||||||
|
|
||||||
VECTOR_LABEL_SCALE_FACTOR = 0.8
|
VECTOR_LABEL_SCALE_FACTOR = 0.8
|
||||||
|
|
|
@ -1,50 +1,61 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import sys
|
|
||||||
import random
|
|
||||||
import itertools as it
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import Iterable, Callable, Union, Sequence
|
import itertools as it
|
||||||
import pickle
|
|
||||||
import os
|
import os
|
||||||
|
import pickle
|
||||||
|
import random
|
||||||
|
import sys
|
||||||
|
|
||||||
import colour
|
|
||||||
import moderngl
|
import moderngl
|
||||||
|
import numbers
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import numpy.typing as npt
|
|
||||||
|
|
||||||
from manimlib.constants import *
|
from manimlib.constants import DEFAULT_MOBJECT_TO_EDGE_BUFFER
|
||||||
|
from manimlib.constants import DEFAULT_MOBJECT_TO_MOBJECT_BUFFER
|
||||||
|
from manimlib.constants import DOWN, IN, LEFT, ORIGIN, OUT, RIGHT, UP
|
||||||
|
from manimlib.constants import FRAME_X_RADIUS, FRAME_Y_RADIUS
|
||||||
|
from manimlib.constants import MED_SMALL_BUFF
|
||||||
|
from manimlib.constants import TAU
|
||||||
|
from manimlib.constants import WHITE
|
||||||
|
from manimlib.event_handler import EVENT_DISPATCHER
|
||||||
|
from manimlib.event_handler.event_listner import EventListner
|
||||||
|
from manimlib.event_handler.event_type import EventType
|
||||||
|
from manimlib.logger import log
|
||||||
|
from manimlib.shader_wrapper import get_colormap_code
|
||||||
|
from manimlib.shader_wrapper import ShaderWrapper
|
||||||
from manimlib.utils.color import color_gradient
|
from manimlib.utils.color import color_gradient
|
||||||
|
from manimlib.utils.color import color_to_rgb
|
||||||
from manimlib.utils.color import get_colormap_list
|
from manimlib.utils.color import get_colormap_list
|
||||||
from manimlib.utils.color import rgb_to_hex
|
from manimlib.utils.color import rgb_to_hex
|
||||||
from manimlib.utils.color import color_to_rgb
|
|
||||||
from manimlib.utils.config_ops import digest_config
|
from manimlib.utils.config_ops import digest_config
|
||||||
from manimlib.utils.iterables import batch_by_property
|
from manimlib.utils.iterables import batch_by_property
|
||||||
from manimlib.utils.iterables import list_update
|
from manimlib.utils.iterables import list_update
|
||||||
|
from manimlib.utils.iterables import listify
|
||||||
from manimlib.utils.iterables import resize_array
|
from manimlib.utils.iterables import resize_array
|
||||||
from manimlib.utils.iterables import resize_preserving_order
|
from manimlib.utils.iterables import resize_preserving_order
|
||||||
from manimlib.utils.iterables import resize_with_interpolation
|
from manimlib.utils.iterables import resize_with_interpolation
|
||||||
from manimlib.utils.iterables import listify
|
|
||||||
from manimlib.utils.bezier import interpolate
|
|
||||||
from manimlib.utils.bezier import integer_interpolate
|
from manimlib.utils.bezier import integer_interpolate
|
||||||
|
from manimlib.utils.bezier import interpolate
|
||||||
from manimlib.utils.paths import straight_path
|
from manimlib.utils.paths import straight_path
|
||||||
from manimlib.utils.simple_functions import get_parameters
|
from manimlib.utils.simple_functions import get_parameters
|
||||||
from manimlib.utils.space_ops import angle_of_vector
|
from manimlib.utils.space_ops import angle_of_vector
|
||||||
from manimlib.utils.space_ops import get_norm
|
from manimlib.utils.space_ops import get_norm
|
||||||
from manimlib.utils.space_ops import rotation_matrix_transpose
|
from manimlib.utils.space_ops import rotation_matrix_transpose
|
||||||
from manimlib.shader_wrapper import ShaderWrapper
|
|
||||||
from manimlib.shader_wrapper import get_colormap_code
|
|
||||||
from manimlib.event_handler import EVENT_DISPATCHER
|
|
||||||
from manimlib.event_handler.event_listner import EventListner
|
|
||||||
from manimlib.event_handler.event_type import EventType
|
|
||||||
from manimlib.logger import log
|
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from colour import Color
|
||||||
|
from typing import Callable, Iterable, Sequence, Union
|
||||||
|
|
||||||
|
import numpy.typing as npt
|
||||||
|
|
||||||
TimeBasedUpdater = Callable[["Mobject", float], None]
|
TimeBasedUpdater = Callable[["Mobject", float], None]
|
||||||
NonTimeUpdater = Callable[["Mobject"], None]
|
NonTimeUpdater = Callable[["Mobject"], None]
|
||||||
Updater = Union[TimeBasedUpdater, NonTimeUpdater]
|
Updater = Union[TimeBasedUpdater, NonTimeUpdater]
|
||||||
ManimColor = Union[str, colour.Color, Sequence[float]]
|
ManimColor = Union[str, Color]
|
||||||
|
|
||||||
|
|
||||||
class Mobject(object):
|
class Mobject(object):
|
||||||
|
@ -84,7 +95,8 @@ class Mobject(object):
|
||||||
self.locked_data_keys: set[str] = set()
|
self.locked_data_keys: set[str] = set()
|
||||||
self.needs_new_bounding_box: bool = True
|
self.needs_new_bounding_box: bool = True
|
||||||
self._is_animating: bool = False
|
self._is_animating: bool = False
|
||||||
self._is_movable: bool = False
|
self.saved_state = None
|
||||||
|
self.target = None
|
||||||
|
|
||||||
self.init_data()
|
self.init_data()
|
||||||
self.init_uniforms()
|
self.init_uniforms()
|
||||||
|
@ -136,8 +148,10 @@ class Mobject(object):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def set_uniforms(self, uniforms: dict):
|
def set_uniforms(self, uniforms: dict):
|
||||||
for key in uniforms:
|
for key, value in uniforms.items():
|
||||||
self.uniforms[key] = uniforms[key] # Copy?
|
if isinstance(value, np.ndarray):
|
||||||
|
value = value.copy()
|
||||||
|
self.uniforms[key] = value
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -460,66 +474,94 @@ class Mobject(object):
|
||||||
self.assemble_family()
|
self.assemble_family()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
# Creating new Mobjects from this one
|
# Copying and serialization
|
||||||
|
|
||||||
def replicate(self, n: int) -> Group:
|
def stash_mobject_pointers(func):
|
||||||
return self.get_group_class()(
|
@wraps(func)
|
||||||
*(self.copy() for x in range(n))
|
def wrapper(self, *args, **kwargs):
|
||||||
)
|
uncopied_attrs = ["parents", "target", "saved_state"]
|
||||||
|
stash = dict()
|
||||||
|
for attr in uncopied_attrs:
|
||||||
|
if hasattr(self, attr):
|
||||||
|
value = getattr(self, attr)
|
||||||
|
stash[attr] = value
|
||||||
|
null_value = [] if isinstance(value, list) else None
|
||||||
|
setattr(self, attr, null_value)
|
||||||
|
result = func(self, *args, **kwargs)
|
||||||
|
self.__dict__.update(stash)
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
def get_grid(self, n_rows: int, n_cols: int, height: float | None = None, **kwargs):
|
@stash_mobject_pointers
|
||||||
"""
|
def serialize(self):
|
||||||
Returns a new mobject containing multiple copies of this one
|
return pickle.dumps(self)
|
||||||
arranged in a grid
|
|
||||||
"""
|
|
||||||
grid = self.replicate(n_rows * n_cols)
|
|
||||||
grid.arrange_in_grid(n_rows, n_cols, **kwargs)
|
|
||||||
if height is not None:
|
|
||||||
grid.set_height(height)
|
|
||||||
return grid
|
|
||||||
|
|
||||||
# Copying
|
def deserialize(self, data: bytes):
|
||||||
|
self.become(pickle.loads(data))
|
||||||
|
return self
|
||||||
|
|
||||||
def copy(self):
|
def deepcopy(self):
|
||||||
self.parents = []
|
|
||||||
try:
|
try:
|
||||||
|
# Often faster than deepcopy
|
||||||
return pickle.loads(pickle.dumps(self))
|
return pickle.loads(pickle.dumps(self))
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return copy.deepcopy(self)
|
return copy.deepcopy(self)
|
||||||
|
|
||||||
def deepcopy(self):
|
@stash_mobject_pointers
|
||||||
# This used to be different from copy, so is now just here for backward compatibility
|
def copy(self, deep: bool = False):
|
||||||
return self.copy()
|
if deep:
|
||||||
|
return self.deepcopy()
|
||||||
|
|
||||||
|
result = copy.copy(self)
|
||||||
|
|
||||||
|
# The line above is only a shallow copy, so the internal
|
||||||
|
# data which are numpyu arrays or other mobjects still
|
||||||
|
# need to be further copied.
|
||||||
|
result.data = {
|
||||||
|
key: np.array(value)
|
||||||
|
for key, value in self.data.items()
|
||||||
|
}
|
||||||
|
result.uniforms = {
|
||||||
|
key: np.array(value)
|
||||||
|
for key, value in self.uniforms.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
result.submobjects = []
|
||||||
|
result.add(*(sm.copy() for sm in self.submobjects))
|
||||||
|
result.match_updaters(self)
|
||||||
|
|
||||||
|
family = self.get_family()
|
||||||
|
for attr, value in list(self.__dict__.items()):
|
||||||
|
if isinstance(value, Mobject) and value is not self:
|
||||||
|
if value in family:
|
||||||
|
setattr(result, attr, result.family[self.family.index(value)])
|
||||||
|
else:
|
||||||
|
setattr(result, attr, value.copy())
|
||||||
|
if isinstance(value, np.ndarray):
|
||||||
|
setattr(result, attr, value.copy())
|
||||||
|
if isinstance(value, ShaderWrapper):
|
||||||
|
setattr(result, attr, value.copy())
|
||||||
|
return result
|
||||||
|
|
||||||
def generate_target(self, use_deepcopy: bool = False):
|
def generate_target(self, use_deepcopy: bool = False):
|
||||||
# TODO, remove now pointless use_deepcopy arg
|
self.target = self.copy(deep=use_deepcopy)
|
||||||
self.target = None # Prevent exponential explosion
|
self.target.saved_state = self.saved_state
|
||||||
self.target = self.copy()
|
|
||||||
return self.target
|
return self.target
|
||||||
|
|
||||||
def save_state(self, use_deepcopy: bool = False):
|
def save_state(self, use_deepcopy: bool = False):
|
||||||
# TODO, remove now pointless use_deepcopy arg
|
self.saved_state = self.copy(deep=use_deepcopy)
|
||||||
if hasattr(self, "saved_state"):
|
self.saved_state.target = self.target
|
||||||
# Prevent exponential growth of data
|
|
||||||
self.saved_state = None
|
|
||||||
self.saved_state = self.copy()
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def restore(self):
|
def restore(self):
|
||||||
if not hasattr(self, "saved_state") or self.save_state is None:
|
if not hasattr(self, "saved_state") or self.saved_state is None:
|
||||||
raise Exception("Trying to restore without having saved")
|
raise Exception("Trying to restore without having saved")
|
||||||
self.become(self.saved_state)
|
self.become(self.saved_state)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def save_to_file(self, file_path):
|
def save_to_file(self, file_path: str, supress_overwrite_warning: bool = False):
|
||||||
if not file_path.endswith(".mob"):
|
|
||||||
file_path += ".mob"
|
|
||||||
if os.path.exists(file_path):
|
|
||||||
cont = input(f"{file_path} already exists. Overwrite (y/n)? ")
|
|
||||||
if cont != "y":
|
|
||||||
return
|
|
||||||
with open(file_path, "wb") as fp:
|
with open(file_path, "wb") as fp:
|
||||||
pickle.dump(self, fp)
|
fp.write(self.serialize())
|
||||||
log.info(f"Saved mobject to {file_path}")
|
log.info(f"Saved mobject to {file_path}")
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -532,6 +574,39 @@ class Mobject(object):
|
||||||
mobject = pickle.load(fp)
|
mobject = pickle.load(fp)
|
||||||
return mobject
|
return mobject
|
||||||
|
|
||||||
|
def become(self, mobject: Mobject):
|
||||||
|
"""
|
||||||
|
Edit all data and submobjects to be idential
|
||||||
|
to another mobject
|
||||||
|
"""
|
||||||
|
self.align_family(mobject)
|
||||||
|
for sm1, sm2 in zip(self.get_family(), mobject.get_family()):
|
||||||
|
sm1.set_data(sm2.data)
|
||||||
|
sm1.set_uniforms(sm2.uniforms)
|
||||||
|
sm1.shader_folder = sm2.shader_folder
|
||||||
|
sm1.texture_paths = sm2.texture_paths
|
||||||
|
sm1.depth_test = sm2.depth_test
|
||||||
|
sm1.render_primitive = sm2.render_primitive
|
||||||
|
self.refresh_bounding_box(recurse_down=True)
|
||||||
|
return self
|
||||||
|
|
||||||
|
# Creating new Mobjects from this one
|
||||||
|
|
||||||
|
def replicate(self, n: int) -> Group:
|
||||||
|
group_class = self.get_group_class()
|
||||||
|
return group_class(*(self.copy() for _ in range(n)))
|
||||||
|
|
||||||
|
def get_grid(self, n_rows: int, n_cols: int, height: float | None = None, **kwargs) -> Group:
|
||||||
|
"""
|
||||||
|
Returns a new mobject containing multiple copies of this one
|
||||||
|
arranged in a grid
|
||||||
|
"""
|
||||||
|
grid = self.replicate(n_rows * n_cols)
|
||||||
|
grid.arrange_in_grid(n_rows, n_cols, **kwargs)
|
||||||
|
if height is not None:
|
||||||
|
grid.set_height(height)
|
||||||
|
return grid
|
||||||
|
|
||||||
# Updating
|
# Updating
|
||||||
|
|
||||||
def init_updaters(self):
|
def init_updaters(self):
|
||||||
|
@ -634,21 +709,13 @@ class Mobject(object):
|
||||||
# Check if mark as static or not for camera
|
# Check if mark as static or not for camera
|
||||||
|
|
||||||
def is_changing(self) -> bool:
|
def is_changing(self) -> bool:
|
||||||
return self._is_animating or self.has_updaters or self._is_movable
|
return self._is_animating or self.has_updaters
|
||||||
|
|
||||||
def set_animating_status(self, is_animating: bool, recurse: bool = True) -> None:
|
def set_animating_status(self, is_animating: bool, recurse: bool = True) -> None:
|
||||||
for mob in self.get_family(recurse):
|
for mob in self.get_family(recurse):
|
||||||
mob._is_animating = is_animating
|
mob._is_animating = is_animating
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def make_movable(self, value: bool = True, recurse: bool = True) -> None:
|
|
||||||
for mob in self.get_family(recurse):
|
|
||||||
mob._is_movable = value
|
|
||||||
return self
|
|
||||||
|
|
||||||
def is_movable(self) -> bool:
|
|
||||||
return self._is_movable
|
|
||||||
|
|
||||||
# Transforming operations
|
# Transforming operations
|
||||||
|
|
||||||
def shift(self, vector: np.ndarray):
|
def shift(self, vector: np.ndarray):
|
||||||
|
@ -675,10 +742,10 @@ class Mobject(object):
|
||||||
Otherwise, if about_point is given a value, scaling is done with
|
Otherwise, if about_point is given a value, scaling is done with
|
||||||
respect to that point.
|
respect to that point.
|
||||||
"""
|
"""
|
||||||
if isinstance(scale_factor, Iterable):
|
if isinstance(scale_factor, numbers.Number):
|
||||||
scale_factor = np.array(scale_factor).clip(min=min_scale_factor)
|
|
||||||
else:
|
|
||||||
scale_factor = max(scale_factor, min_scale_factor)
|
scale_factor = max(scale_factor, min_scale_factor)
|
||||||
|
else:
|
||||||
|
scale_factor = np.array(scale_factor).clip(min=min_scale_factor)
|
||||||
self.apply_points_function(
|
self.apply_points_function(
|
||||||
lambda points: scale_factor * points,
|
lambda points: scale_factor * points,
|
||||||
about_point=about_point,
|
about_point=about_point,
|
||||||
|
@ -1064,8 +1131,8 @@ class Mobject(object):
|
||||||
|
|
||||||
def set_rgba_array_by_color(
|
def set_rgba_array_by_color(
|
||||||
self,
|
self,
|
||||||
color: ManimColor | None = None,
|
color: ManimColor | Iterable[ManimColor] | None = None,
|
||||||
opacity: float | None = None,
|
opacity: float | Iterable[float] | None = None,
|
||||||
name: str = "rgbas",
|
name: str = "rgbas",
|
||||||
recurse: bool = True
|
recurse: bool = True
|
||||||
):
|
):
|
||||||
|
@ -1087,7 +1154,12 @@ class Mobject(object):
|
||||||
mob.data[name][:, 3] = resize_array(opacities, size)
|
mob.data[name][:, 3] = resize_array(opacities, size)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def set_color(self, color: ManimColor, opacity: float | None = None, recurse: bool = True):
|
def set_color(
|
||||||
|
self,
|
||||||
|
color: ManimColor | Iterable[ManimColor] | None,
|
||||||
|
opacity: float | Iterable[float] | None = None,
|
||||||
|
recurse: bool = True
|
||||||
|
):
|
||||||
self.set_rgba_array_by_color(color, opacity, recurse=False)
|
self.set_rgba_array_by_color(color, opacity, recurse=False)
|
||||||
# Recurse to submobjects differently from how set_rgba_array_by_color
|
# Recurse to submobjects differently from how set_rgba_array_by_color
|
||||||
# in case they implement set_color differently
|
# in case they implement set_color differently
|
||||||
|
@ -1096,7 +1168,11 @@ class Mobject(object):
|
||||||
submob.set_color(color, recurse=True)
|
submob.set_color(color, recurse=True)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def set_opacity(self, opacity: float, recurse: bool = True):
|
def set_opacity(
|
||||||
|
self,
|
||||||
|
opacity: float | Iterable[float] | None,
|
||||||
|
recurse: bool = True
|
||||||
|
):
|
||||||
self.set_rgba_array_by_color(color=None, opacity=opacity, recurse=False)
|
self.set_rgba_array_by_color(color=None, opacity=opacity, recurse=False)
|
||||||
if recurse:
|
if recurse:
|
||||||
for submob in self.submobjects:
|
for submob in self.submobjects:
|
||||||
|
@ -1203,7 +1279,7 @@ class Mobject(object):
|
||||||
bb = self.get_bounding_box()
|
bb = self.get_bounding_box()
|
||||||
return np.array([
|
return np.array([
|
||||||
[bb[indices[-i + 1]][i] for i in range(3)]
|
[bb[indices[-i + 1]][i] for i in range(3)]
|
||||||
for indices in it.product(*3 * [[0, 2]])
|
for indices in it.product([0, 2], repeat=3)
|
||||||
])
|
])
|
||||||
|
|
||||||
def get_center(self) -> np.ndarray:
|
def get_center(self) -> np.ndarray:
|
||||||
|
@ -1519,18 +1595,6 @@ class Mobject(object):
|
||||||
"""
|
"""
|
||||||
pass # To implement in subclass
|
pass # To implement in subclass
|
||||||
|
|
||||||
def become(self, mobject: Mobject):
|
|
||||||
"""
|
|
||||||
Edit all data and submobjects to be idential
|
|
||||||
to another mobject
|
|
||||||
"""
|
|
||||||
self.align_family(mobject)
|
|
||||||
for sm1, sm2 in zip(self.get_family(), mobject.get_family()):
|
|
||||||
sm1.set_data(sm2.data)
|
|
||||||
sm1.set_uniforms(sm2.uniforms)
|
|
||||||
self.refresh_bounding_box(recurse_down=True)
|
|
||||||
return self
|
|
||||||
|
|
||||||
# Locking data
|
# Locking data
|
||||||
|
|
||||||
def lock_data(self, keys: Iterable[str]):
|
def lock_data(self, keys: Iterable[str]):
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
from manimlib.constants import DEGREES
|
from manimlib.constants import DEGREES
|
||||||
from manimlib.constants import RIGHT
|
from manimlib.constants import RIGHT
|
||||||
|
@ -11,7 +10,10 @@ from manimlib.utils.simple_functions import clip
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from manimlib.animation.animation import Animation
|
from manimlib.animation.animation import Animation
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Iterable, Sequence
|
import numpy as np
|
||||||
|
|
||||||
from manimlib.constants import *
|
from manimlib.constants import DOWN, LEFT, RIGHT, UP
|
||||||
|
from manimlib.constants import GREY_B
|
||||||
|
from manimlib.constants import MED_SMALL_BUFF
|
||||||
from manimlib.mobject.geometry import Line
|
from manimlib.mobject.geometry import Line
|
||||||
from manimlib.mobject.numbers import DecimalNumber
|
from manimlib.mobject.numbers import DecimalNumber
|
||||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||||
|
@ -12,6 +14,11 @@ from manimlib.utils.config_ops import digest_config
|
||||||
from manimlib.utils.config_ops import merge_dicts_recursively
|
from manimlib.utils.config_ops import merge_dicts_recursively
|
||||||
from manimlib.utils.simple_functions import fdiv
|
from manimlib.utils.simple_functions import fdiv
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import Iterable, Sequence
|
||||||
|
|
||||||
|
|
||||||
class NumberLine(Line):
|
class NumberLine(Line):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TypeVar, Type
|
import numpy as np
|
||||||
|
|
||||||
from manimlib.constants import *
|
from manimlib.constants import DOWN, LEFT, RIGHT, UP
|
||||||
from manimlib.mobject.svg.tex_mobject import SingleStringTex
|
from manimlib.mobject.svg.tex_mobject import SingleStringTex
|
||||||
from manimlib.mobject.svg.text_mobject import Text
|
from manimlib.mobject.svg.text_mobject import Text
|
||||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import Type, TypeVar
|
||||||
|
|
||||||
T = TypeVar("T", bound=VMobject)
|
T = TypeVar("T", bound=VMobject)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Iterable, Union, Sequence
|
from manimlib.constants import BLUE, BLUE_E, GREEN_E, GREY_B, GREY_D, MAROON_B, YELLOW
|
||||||
import colour
|
from manimlib.constants import DOWN, LEFT, RIGHT, UP
|
||||||
|
from manimlib.constants import MED_LARGE_BUFF, MED_SMALL_BUFF, SMALL_BUFF
|
||||||
from manimlib.constants import *
|
|
||||||
from manimlib.mobject.geometry import Line
|
from manimlib.mobject.geometry import Line
|
||||||
from manimlib.mobject.geometry import Rectangle
|
from manimlib.mobject.geometry import Rectangle
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
|
@ -14,7 +13,14 @@ from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||||
from manimlib.utils.color import color_gradient
|
from manimlib.utils.color import color_gradient
|
||||||
from manimlib.utils.iterables import listify
|
from manimlib.utils.iterables import listify
|
||||||
|
|
||||||
ManimColor = Union[str, colour.Color, Sequence[float]]
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from colour import Color
|
||||||
|
from typing import Iterable, Union
|
||||||
|
|
||||||
|
ManimColor = Union[str, Color]
|
||||||
|
|
||||||
|
|
||||||
EPSILON = 0.0001
|
EPSILON = 0.0001
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,25 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from manimlib.constants import *
|
from colour import Color
|
||||||
|
|
||||||
|
from manimlib.constants import BLACK, RED, YELLOW
|
||||||
|
from manimlib.constants import DL, DOWN, DR, LEFT, RIGHT, UL, UR
|
||||||
|
from manimlib.constants import SMALL_BUFF
|
||||||
from manimlib.mobject.geometry import Line
|
from manimlib.mobject.geometry import Line
|
||||||
from manimlib.mobject.geometry import Rectangle
|
from manimlib.mobject.geometry import Rectangle
|
||||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||||
from manimlib.utils.color import Color
|
|
||||||
from manimlib.utils.customization import get_customization
|
|
||||||
from manimlib.utils.config_ops import digest_config
|
from manimlib.utils.config_ops import digest_config
|
||||||
|
from manimlib.utils.customization import get_customization
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Union, Sequence
|
from typing import Union
|
||||||
|
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
ManimColor = Union[str, Color, Sequence[float]]
|
|
||||||
|
ManimColor = Union[str, Color]
|
||||||
|
|
||||||
|
|
||||||
class SurroundingRectangle(Rectangle):
|
class SurroundingRectangle(Rectangle):
|
||||||
|
|
|
@ -2,27 +2,32 @@ from __future__ import annotations
|
||||||
|
|
||||||
import math
|
import math
|
||||||
import copy
|
import copy
|
||||||
from typing import Iterable
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from manimlib.constants import *
|
from manimlib.constants import DEFAULT_MOBJECT_TO_MOBJECT_BUFFER, SMALL_BUFF
|
||||||
|
from manimlib.constants import DOWN, LEFT, ORIGIN, RIGHT, UP
|
||||||
|
from manimlib.constants import PI
|
||||||
|
from manimlib.animation.composition import AnimationGroup
|
||||||
from manimlib.animation.fading import FadeIn
|
from manimlib.animation.fading import FadeIn
|
||||||
from manimlib.animation.growing import GrowFromCenter
|
from manimlib.animation.growing import GrowFromCenter
|
||||||
from manimlib.animation.composition import AnimationGroup
|
|
||||||
from manimlib.mobject.svg.tex_mobject import Tex
|
|
||||||
from manimlib.mobject.svg.tex_mobject import SingleStringTex
|
from manimlib.mobject.svg.tex_mobject import SingleStringTex
|
||||||
|
from manimlib.mobject.svg.tex_mobject import Tex
|
||||||
from manimlib.mobject.svg.tex_mobject import TexText
|
from manimlib.mobject.svg.tex_mobject import TexText
|
||||||
from manimlib.mobject.svg.text_mobject import Text
|
from manimlib.mobject.svg.text_mobject import Text
|
||||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||||
from manimlib.utils.config_ops import digest_config
|
from manimlib.utils.config_ops import digest_config
|
||||||
|
from manimlib.utils.iterables import listify
|
||||||
from manimlib.utils.space_ops import get_norm
|
from manimlib.utils.space_ops import get_norm
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from manimlib.mobject.mobject import Mobject
|
from typing import Iterable
|
||||||
|
|
||||||
from manimlib.animation.animation import Animation
|
from manimlib.animation.animation import Animation
|
||||||
|
from manimlib.mobject.mobject import Mobject
|
||||||
|
|
||||||
|
|
||||||
class Brace(SingleStringTex):
|
class Brace(SingleStringTex):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
@ -113,8 +118,8 @@ class BraceLabel(VMobject):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
obj: list[VMobject] | Mobject,
|
obj: VMobject | list[VMobject],
|
||||||
text: Iterable[str] | str,
|
text: str | Iterable[str],
|
||||||
brace_direction: np.ndarray = DOWN,
|
brace_direction: np.ndarray = DOWN,
|
||||||
**kwargs
|
**kwargs
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -124,11 +129,7 @@ class BraceLabel(VMobject):
|
||||||
obj = VMobject(*obj)
|
obj = VMobject(*obj)
|
||||||
self.brace = Brace(obj, brace_direction, **kwargs)
|
self.brace = Brace(obj, brace_direction, **kwargs)
|
||||||
|
|
||||||
if isinstance(text, Iterable):
|
self.label = self.label_constructor(*listify(text), **kwargs)
|
||||||
self.label = self.label_constructor(*text, **kwargs)
|
|
||||||
else:
|
|
||||||
self.label = self.label_constructor(str(text))
|
|
||||||
if self.label_scale != 1:
|
|
||||||
self.label.scale(self.label_scale)
|
self.label.scale(self.label_scale)
|
||||||
|
|
||||||
self.brace.put_at_tip(self.label, buff=self.label_buff)
|
self.brace.put_at_tip(self.label, buff=self.label_buff)
|
||||||
|
@ -141,7 +142,7 @@ class BraceLabel(VMobject):
|
||||||
) -> AnimationGroup:
|
) -> AnimationGroup:
|
||||||
return AnimationGroup(brace_anim(self.brace), label_anim(self.label))
|
return AnimationGroup(brace_anim(self.brace), label_anim(self.label))
|
||||||
|
|
||||||
def shift_brace(self, obj: list[VMobject] | Mobject, **kwargs):
|
def shift_brace(self, obj: VMobject | list[VMobject], **kwargs):
|
||||||
if isinstance(obj, list):
|
if isinstance(obj, list):
|
||||||
obj = VMobject(*obj)
|
obj = VMobject(*obj)
|
||||||
self.brace = Brace(obj, self.brace_direction, **kwargs)
|
self.brace = Brace(obj, self.brace_direction, **kwargs)
|
||||||
|
@ -158,7 +159,7 @@ class BraceLabel(VMobject):
|
||||||
self.submobjects[1] = self.label
|
self.submobjects[1] = self.label
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def change_brace_label(self, obj: list[VMobject] | Mobject, *text: str):
|
def change_brace_label(self, obj: VMobject | list[VMobject], *text: str):
|
||||||
self.shift_brace(obj)
|
self.shift_brace(obj)
|
||||||
self.change_label(*text)
|
self.change_label(*text)
|
||||||
return self
|
return self
|
||||||
|
|
|
@ -20,8 +20,8 @@ from manimlib.utils.config_ops import digest_config
|
||||||
from manimlib.utils.rate_functions import linear
|
from manimlib.utils.rate_functions import linear
|
||||||
from manimlib.utils.space_ops import angle_of_vector
|
from manimlib.utils.space_ops import angle_of_vector
|
||||||
from manimlib.utils.space_ops import complex_to_R3
|
from manimlib.utils.space_ops import complex_to_R3
|
||||||
from manimlib.utils.space_ops import rotate_vector
|
|
||||||
from manimlib.utils.space_ops import midpoint
|
from manimlib.utils.space_ops import midpoint
|
||||||
|
from manimlib.utils.space_ops import rotate_vector
|
||||||
|
|
||||||
|
|
||||||
class Checkmark(TexText):
|
class Checkmark(TexText):
|
||||||
|
|
|
@ -1,27 +1,24 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import itertools as it
|
import os
|
||||||
from typing import Callable
|
|
||||||
from xml.etree import ElementTree as ET
|
from xml.etree import ElementTree as ET
|
||||||
|
|
||||||
import svgelements as se
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import svgelements as se
|
||||||
|
|
||||||
from manimlib.constants import RIGHT
|
from manimlib.constants import RIGHT
|
||||||
from manimlib.mobject.geometry import Line
|
from manimlib.logger import log
|
||||||
from manimlib.mobject.geometry import Circle
|
from manimlib.mobject.geometry import Circle
|
||||||
|
from manimlib.mobject.geometry import Line
|
||||||
from manimlib.mobject.geometry import Polygon
|
from manimlib.mobject.geometry import Polygon
|
||||||
from manimlib.mobject.geometry import Polyline
|
from manimlib.mobject.geometry import Polyline
|
||||||
from manimlib.mobject.geometry import Rectangle
|
from manimlib.mobject.geometry import Rectangle
|
||||||
from manimlib.mobject.geometry import RoundedRectangle
|
from manimlib.mobject.geometry import RoundedRectangle
|
||||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||||
from manimlib.utils.config_ops import digest_config
|
|
||||||
from manimlib.utils.directories import get_mobject_data_dir
|
from manimlib.utils.directories import get_mobject_data_dir
|
||||||
from manimlib.utils.images import get_full_vector_image_path
|
from manimlib.utils.images import get_full_vector_image_path
|
||||||
from manimlib.utils.iterables import hash_obj
|
from manimlib.utils.iterables import hash_obj
|
||||||
from manimlib.logger import log
|
|
||||||
|
|
||||||
|
|
||||||
SVG_HASH_TO_MOB_MAP: dict[int, VMobject] = {}
|
SVG_HASH_TO_MOB_MAP: dict[int, VMobject] = {}
|
||||||
|
@ -199,9 +196,9 @@ class SVGMobject(VMobject):
|
||||||
) -> VMobject:
|
) -> VMobject:
|
||||||
mob.set_style(
|
mob.set_style(
|
||||||
stroke_width=shape.stroke_width,
|
stroke_width=shape.stroke_width,
|
||||||
stroke_color=shape.stroke.hex,
|
stroke_color=shape.stroke.hexrgb,
|
||||||
stroke_opacity=shape.stroke.opacity,
|
stroke_opacity=shape.stroke.opacity,
|
||||||
fill_color=shape.fill.hex,
|
fill_color=shape.fill.hexrgb,
|
||||||
fill_opacity=shape.fill.opacity
|
fill_opacity=shape.fill.opacity
|
||||||
)
|
)
|
||||||
return mob
|
return mob
|
||||||
|
|
|
@ -1,21 +1,28 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Iterable, Sequence, Union
|
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
import operator as op
|
import operator as op
|
||||||
import colour
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from manimlib.constants import *
|
from manimlib.constants import BLACK, WHITE
|
||||||
|
from manimlib.constants import DOWN, LEFT, RIGHT, UP
|
||||||
|
from manimlib.constants import FRAME_WIDTH
|
||||||
|
from manimlib.constants import MED_LARGE_BUFF, MED_SMALL_BUFF, SMALL_BUFF
|
||||||
from manimlib.mobject.geometry import Line
|
from manimlib.mobject.geometry import Line
|
||||||
from manimlib.mobject.svg.svg_mobject import SVGMobject
|
from manimlib.mobject.svg.svg_mobject import SVGMobject
|
||||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||||
from manimlib.utils.config_ops import digest_config
|
from manimlib.utils.config_ops import digest_config
|
||||||
from manimlib.utils.tex_file_writing import tex_to_svg_file
|
|
||||||
from manimlib.utils.tex_file_writing import get_tex_config
|
|
||||||
from manimlib.utils.tex_file_writing import display_during_execution
|
from manimlib.utils.tex_file_writing import display_during_execution
|
||||||
|
from manimlib.utils.tex_file_writing import get_tex_config
|
||||||
|
from manimlib.utils.tex_file_writing import tex_to_svg_file
|
||||||
|
|
||||||
ManimColor = Union[str, colour.Color, Sequence[float]]
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from colour import Color
|
||||||
|
from typing import Iterable, Union
|
||||||
|
|
||||||
|
ManimColor = Union[str, Color]
|
||||||
|
|
||||||
|
|
||||||
SCALE_FACTOR_PER_FONT_POINT = 0.001
|
SCALE_FACTOR_PER_FONT_POINT = 0.001
|
||||||
|
|
|
@ -2,19 +2,23 @@ from __future__ import annotations
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
|
||||||
from manimlib.constants import *
|
import numpy as np
|
||||||
from manimlib.mobject.types.surface import Surface
|
|
||||||
|
from manimlib.constants import BLUE, BLUE_D, BLUE_E
|
||||||
|
from manimlib.constants import IN, ORIGIN, OUT, RIGHT
|
||||||
|
from manimlib.constants import PI, TAU
|
||||||
from manimlib.mobject.types.surface import SGroup
|
from manimlib.mobject.types.surface import SGroup
|
||||||
|
from manimlib.mobject.types.surface import Surface
|
||||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||||
from manimlib.mobject.geometry import Square
|
|
||||||
from manimlib.mobject.geometry import Polygon
|
from manimlib.mobject.geometry import Polygon
|
||||||
|
from manimlib.mobject.geometry import Square
|
||||||
from manimlib.utils.bezier import interpolate
|
from manimlib.utils.bezier import interpolate
|
||||||
from manimlib.utils.config_ops import digest_config
|
from manimlib.utils.config_ops import digest_config
|
||||||
from manimlib.utils.iterables import adjacent_pairs
|
from manimlib.utils.iterables import adjacent_pairs
|
||||||
|
from manimlib.utils.space_ops import compass_directions
|
||||||
from manimlib.utils.space_ops import get_norm
|
from manimlib.utils.space_ops import get_norm
|
||||||
from manimlib.utils.space_ops import z_to_vector
|
from manimlib.utils.space_ops import z_to_vector
|
||||||
from manimlib.utils.space_ops import compass_directions
|
|
||||||
|
|
||||||
|
|
||||||
class SurfaceMesh(VGroup):
|
class SurfaceMesh(VGroup):
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
import numpy.typing as npt
|
|
||||||
import moderngl
|
import moderngl
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
from manimlib.constants import GREY_C
|
from manimlib.constants import GREY_C, YELLOW
|
||||||
from manimlib.constants import YELLOW
|
|
||||||
from manimlib.constants import ORIGIN
|
from manimlib.constants import ORIGIN
|
||||||
from manimlib.mobject.types.point_cloud_mobject import PMobject
|
from manimlib.mobject.types.point_cloud_mobject import PMobject
|
||||||
from manimlib.utils.iterables import resize_preserving_order
|
from manimlib.utils.iterables import resize_preserving_order
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
import numpy.typing as npt
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_DOT_RADIUS = 0.05
|
DEFAULT_DOT_RADIUS = 0.05
|
||||||
DEFAULT_GLOW_DOT_RADIUS = 0.2
|
DEFAULT_GLOW_DOT_RADIUS = 0.2
|
||||||
|
|
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from manimlib.constants import *
|
from manimlib.constants import DL, DR, UL, UR
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
from manimlib.utils.bezier import inverse_interpolate
|
from manimlib.utils.bezier import inverse_interpolate
|
||||||
from manimlib.utils.images import get_full_raster_image_path
|
from manimlib.utils.images import get_full_raster_image_path
|
||||||
|
|
|
@ -1,19 +1,22 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Callable, Sequence, Union
|
from manimlib.constants import BLACK
|
||||||
|
from manimlib.constants import ORIGIN
|
||||||
import colour
|
|
||||||
import numpy.typing as npt
|
|
||||||
|
|
||||||
from manimlib.constants import *
|
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
from manimlib.utils.color import color_gradient
|
from manimlib.utils.color import color_gradient
|
||||||
from manimlib.utils.color import color_to_rgba
|
from manimlib.utils.color import color_to_rgba
|
||||||
from manimlib.utils.iterables import resize_with_interpolation
|
|
||||||
from manimlib.utils.iterables import resize_array
|
from manimlib.utils.iterables import resize_array
|
||||||
|
from manimlib.utils.iterables import resize_with_interpolation
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
ManimColor = Union[str, colour.Color, Sequence[float]]
|
if TYPE_CHECKING:
|
||||||
|
from colour import Color
|
||||||
|
from typing import Callable, Union
|
||||||
|
|
||||||
|
import numpy.typing as npt
|
||||||
|
|
||||||
|
ManimColor = Union[str, Color]
|
||||||
|
|
||||||
|
|
||||||
class PMobject(Mobject):
|
class PMobject(Mobject):
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Iterable, Callable
|
|
||||||
|
|
||||||
import moderngl
|
import moderngl
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import numpy.typing as npt
|
|
||||||
|
|
||||||
from manimlib.constants import *
|
from manimlib.constants import GREY
|
||||||
|
from manimlib.constants import OUT
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
from manimlib.utils.bezier import integer_interpolate
|
from manimlib.utils.bezier import integer_interpolate
|
||||||
from manimlib.utils.bezier import interpolate
|
from manimlib.utils.bezier import interpolate
|
||||||
|
@ -17,6 +15,10 @@ from manimlib.utils.space_ops import normalize_along_axis
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from typing import Callable, Iterable
|
||||||
|
|
||||||
|
import numpy.typing as npt
|
||||||
|
|
||||||
from manimlib.camera.camera import Camera
|
from manimlib.camera.camera import Camera
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,30 +1,36 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import operator as op
|
from functools import reduce
|
||||||
|
from functools import wraps
|
||||||
import itertools as it
|
import itertools as it
|
||||||
from functools import reduce, wraps
|
import operator as op
|
||||||
from typing import Iterable, Sequence, Callable, Union
|
|
||||||
|
|
||||||
import colour
|
|
||||||
import moderngl
|
import moderngl
|
||||||
import numpy.typing as npt
|
import numpy as np
|
||||||
|
|
||||||
from manimlib.constants import *
|
from manimlib.constants import GREY_C
|
||||||
|
from manimlib.constants import GREY_E
|
||||||
|
from manimlib.constants import BLACK, WHITE
|
||||||
|
from manimlib.constants import DEFAULT_STROKE_WIDTH
|
||||||
|
from manimlib.constants import DEGREES
|
||||||
|
from manimlib.constants import JOINT_TYPE_MAP
|
||||||
|
from manimlib.constants import ORIGIN, OUT, UP
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
from manimlib.mobject.mobject import Point
|
from manimlib.mobject.mobject import Point
|
||||||
from manimlib.utils.bezier import bezier
|
from manimlib.utils.bezier import bezier
|
||||||
from manimlib.utils.bezier import get_smooth_quadratic_bezier_handle_points
|
|
||||||
from manimlib.utils.bezier import get_smooth_cubic_bezier_handle_points
|
|
||||||
from manimlib.utils.bezier import get_quadratic_approximation_of_cubic
|
from manimlib.utils.bezier import get_quadratic_approximation_of_cubic
|
||||||
|
from manimlib.utils.bezier import get_smooth_cubic_bezier_handle_points
|
||||||
|
from manimlib.utils.bezier import get_smooth_quadratic_bezier_handle_points
|
||||||
|
from manimlib.utils.bezier import integer_interpolate
|
||||||
from manimlib.utils.bezier import interpolate
|
from manimlib.utils.bezier import interpolate
|
||||||
from manimlib.utils.bezier import inverse_interpolate
|
from manimlib.utils.bezier import inverse_interpolate
|
||||||
from manimlib.utils.bezier import integer_interpolate
|
|
||||||
from manimlib.utils.bezier import partial_quadratic_bezier_points
|
from manimlib.utils.bezier import partial_quadratic_bezier_points
|
||||||
|
from manimlib.utils.color import color_gradient
|
||||||
from manimlib.utils.color import rgb_to_hex
|
from manimlib.utils.color import rgb_to_hex
|
||||||
|
from manimlib.utils.iterables import listify
|
||||||
from manimlib.utils.iterables import make_even
|
from manimlib.utils.iterables import make_even
|
||||||
from manimlib.utils.iterables import resize_array
|
from manimlib.utils.iterables import resize_array
|
||||||
from manimlib.utils.iterables import resize_with_interpolation
|
from manimlib.utils.iterables import resize_with_interpolation
|
||||||
from manimlib.utils.iterables import listify
|
|
||||||
from manimlib.utils.space_ops import angle_between_vectors
|
from manimlib.utils.space_ops import angle_between_vectors
|
||||||
from manimlib.utils.space_ops import cross2d
|
from manimlib.utils.space_ops import cross2d
|
||||||
from manimlib.utils.space_ops import earclip_triangulation
|
from manimlib.utils.space_ops import earclip_triangulation
|
||||||
|
@ -33,8 +39,15 @@ from manimlib.utils.space_ops import get_unit_normal
|
||||||
from manimlib.utils.space_ops import z_to_vector
|
from manimlib.utils.space_ops import z_to_vector
|
||||||
from manimlib.shader_wrapper import ShaderWrapper
|
from manimlib.shader_wrapper import ShaderWrapper
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
ManimColor = Union[str, colour.Color, Sequence[float]]
|
if TYPE_CHECKING:
|
||||||
|
from colour import Color
|
||||||
|
from typing import Callable, Iterable, Sequence, Union
|
||||||
|
|
||||||
|
import numpy.typing as npt
|
||||||
|
|
||||||
|
ManimColor = Union[str, Color]
|
||||||
|
|
||||||
|
|
||||||
class VMobject(Mobject):
|
class VMobject(Mobject):
|
||||||
|
@ -130,8 +143,8 @@ class VMobject(Mobject):
|
||||||
|
|
||||||
def set_fill(
|
def set_fill(
|
||||||
self,
|
self,
|
||||||
color: ManimColor | None = None,
|
color: ManimColor | Iterable[ManimColor] | None = None,
|
||||||
opacity: float | None = None,
|
opacity: float | Iterable[float] | None = None,
|
||||||
recurse: bool = True
|
recurse: bool = True
|
||||||
):
|
):
|
||||||
self.set_rgba_array_by_color(color, opacity, 'fill_rgba', recurse)
|
self.set_rgba_array_by_color(color, opacity, 'fill_rgba', recurse)
|
||||||
|
@ -139,9 +152,9 @@ class VMobject(Mobject):
|
||||||
|
|
||||||
def set_stroke(
|
def set_stroke(
|
||||||
self,
|
self,
|
||||||
color: ManimColor | None = None,
|
color: ManimColor | Iterable[ManimColor] | None = None,
|
||||||
width: float | npt.ArrayLike | None = None,
|
width: float | Iterable[float] | None = None,
|
||||||
opacity: float | None = None,
|
opacity: float | Iterable[float] | None = None,
|
||||||
background: bool | None = None,
|
background: bool | None = None,
|
||||||
recurse: bool = True
|
recurse: bool = True
|
||||||
):
|
):
|
||||||
|
@ -162,8 +175,8 @@ class VMobject(Mobject):
|
||||||
|
|
||||||
def set_backstroke(
|
def set_backstroke(
|
||||||
self,
|
self,
|
||||||
color: ManimColor = BLACK,
|
color: ManimColor | Iterable[ManimColor] = BLACK,
|
||||||
width: float | npt.ArrayLike = 3,
|
width: float | Iterable[float] = 3,
|
||||||
background: bool = True
|
background: bool = True
|
||||||
):
|
):
|
||||||
self.set_stroke(color, width, background=background)
|
self.set_stroke(color, width, background=background)
|
||||||
|
@ -177,13 +190,13 @@ class VMobject(Mobject):
|
||||||
|
|
||||||
def set_style(
|
def set_style(
|
||||||
self,
|
self,
|
||||||
fill_color: ManimColor | None = None,
|
fill_color: ManimColor | Iterable[ManimColor] | None = None,
|
||||||
fill_opacity: float | None = None,
|
fill_opacity: float | Iterable[float] | None = None,
|
||||||
fill_rgba: npt.ArrayLike | None = None,
|
fill_rgba: npt.ArrayLike | None = None,
|
||||||
stroke_color: ManimColor | None = None,
|
stroke_color: ManimColor | Iterable[ManimColor] | None = None,
|
||||||
stroke_opacity: float | None = None,
|
stroke_opacity: float | Iterable[float] | None = None,
|
||||||
stroke_rgba: npt.ArrayLike | None = None,
|
stroke_rgba: npt.ArrayLike | None = None,
|
||||||
stroke_width: float | npt.ArrayLike | None = None,
|
stroke_width: float | Iterable[float] | None = None,
|
||||||
stroke_background: bool = True,
|
stroke_background: bool = True,
|
||||||
reflectiveness: float | None = None,
|
reflectiveness: float | None = None,
|
||||||
gloss: float | None = None,
|
gloss: float | None = None,
|
||||||
|
@ -247,12 +260,21 @@ class VMobject(Mobject):
|
||||||
sm1.match_style(sm2)
|
sm1.match_style(sm2)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def set_color(self, color: ManimColor, recurse: bool = True):
|
def set_color(
|
||||||
self.set_fill(color, recurse=recurse)
|
self,
|
||||||
self.set_stroke(color, recurse=recurse)
|
color: ManimColor | Iterable[ManimColor] | None,
|
||||||
|
opacity: float | Iterable[float] | None = None,
|
||||||
|
recurse: bool = True
|
||||||
|
):
|
||||||
|
self.set_fill(color, opacity=opacity, recurse=recurse)
|
||||||
|
self.set_stroke(color, opacity=opacity, recurse=recurse)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def set_opacity(self, opacity: float, recurse: bool = True):
|
def set_opacity(
|
||||||
|
self,
|
||||||
|
opacity: float | Iterable[float] | None,
|
||||||
|
recurse: bool = True
|
||||||
|
):
|
||||||
self.set_fill(opacity=opacity, recurse=recurse)
|
self.set_fill(opacity=opacity, recurse=recurse)
|
||||||
self.set_stroke(opacity=opacity, recurse=recurse)
|
self.set_stroke(opacity=opacity, recurse=recurse)
|
||||||
return self
|
return self
|
||||||
|
@ -1174,3 +1196,24 @@ class DashedVMobject(VMobject):
|
||||||
# Family is already taken care of by get_subcurve
|
# Family is already taken care of by get_subcurve
|
||||||
# implementation
|
# implementation
|
||||||
self.match_style(vmobject, recurse=False)
|
self.match_style(vmobject, recurse=False)
|
||||||
|
|
||||||
|
|
||||||
|
class VHighlight(VGroup):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
vmobject: VMobject,
|
||||||
|
n_layers: int = 3,
|
||||||
|
color_bounds: tuple[ManimColor] = (GREY_C, GREY_E),
|
||||||
|
max_stroke_width: float = 10.0,
|
||||||
|
):
|
||||||
|
outline = vmobject.replicate(n_layers)
|
||||||
|
outline.set_fill(opacity=0)
|
||||||
|
added_widths = np.linspace(0, max_stroke_width, n_layers + 1)[1:]
|
||||||
|
colors = color_gradient(color_bounds, n_layers)
|
||||||
|
for part, added_width, color in zip(reversed(outline), added_widths, colors):
|
||||||
|
for sm in part.family_members_with_points():
|
||||||
|
part.set_stroke(
|
||||||
|
width=sm.get_stroke_width() + added_width,
|
||||||
|
color=color,
|
||||||
|
)
|
||||||
|
super().__init__(*outline)
|
||||||
|
|
|
@ -1,23 +1,21 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import itertools as it
|
import itertools as it
|
||||||
import random
|
|
||||||
from typing import Sequence, TypeVar, Callable, Iterable
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import numpy.typing as npt
|
|
||||||
|
|
||||||
from manimlib.constants import *
|
from manimlib.constants import FRAME_HEIGHT, FRAME_WIDTH
|
||||||
|
from manimlib.constants import WHITE
|
||||||
from manimlib.animation.composition import AnimationGroup
|
from manimlib.animation.composition import AnimationGroup
|
||||||
from manimlib.animation.indication import VShowPassingFlash
|
from manimlib.animation.indication import VShowPassingFlash
|
||||||
from manimlib.mobject.geometry import Arrow
|
from manimlib.mobject.geometry import Arrow
|
||||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||||
from manimlib.utils.bezier import inverse_interpolate
|
|
||||||
from manimlib.utils.bezier import interpolate
|
from manimlib.utils.bezier import interpolate
|
||||||
|
from manimlib.utils.bezier import inverse_interpolate
|
||||||
from manimlib.utils.color import get_colormap_list
|
from manimlib.utils.color import get_colormap_list
|
||||||
from manimlib.utils.config_ops import merge_dicts_recursively
|
|
||||||
from manimlib.utils.config_ops import digest_config
|
from manimlib.utils.config_ops import digest_config
|
||||||
|
from manimlib.utils.config_ops import merge_dicts_recursively
|
||||||
from manimlib.utils.rate_functions import linear
|
from manimlib.utils.rate_functions import linear
|
||||||
from manimlib.utils.simple_functions import sigmoid
|
from manimlib.utils.simple_functions import sigmoid
|
||||||
from manimlib.utils.space_ops import get_norm
|
from manimlib.utils.space_ops import get_norm
|
||||||
|
@ -25,8 +23,13 @@ from manimlib.utils.space_ops import get_norm
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from manimlib.mobject.mobject import Mobject
|
from typing import Callable, Iterable, Sequence, TypeVar
|
||||||
|
|
||||||
|
import numpy.typing as npt
|
||||||
|
|
||||||
from manimlib.mobject.coordinate_systems import CoordinateSystem
|
from manimlib.mobject.coordinate_systems import CoordinateSystem
|
||||||
|
from manimlib.mobject.mobject import Mobject
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
|
@ -299,7 +302,7 @@ class AnimatedStreamLines(VGroup):
|
||||||
**self.line_anim_config,
|
**self.line_anim_config,
|
||||||
)
|
)
|
||||||
line.anim.begin()
|
line.anim.begin()
|
||||||
line.time = -self.lag_range * random.random()
|
line.time = -self.lag_range * np.random.random()
|
||||||
self.add(line.anim.mobject)
|
self.add(line.anim.mobject)
|
||||||
|
|
||||||
self.add_updater(lambda m, dt: m.update(dt))
|
self.add_updater(lambda m, dt: m.update(dt))
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
import random
|
||||||
|
|
||||||
from manimlib.constants import *
|
from manimlib.constants import *
|
||||||
# from manimlib.for_3b1b_videos.pi_creature import PiCreature
|
# from manimlib.for_3b1b_videos.pi_creature import PiCreature
|
||||||
|
|
|
@ -1,41 +1,43 @@
|
||||||
import numpy as np
|
|
||||||
import itertools as it
|
import itertools as it
|
||||||
|
import numpy as np
|
||||||
import pyperclip
|
import pyperclip
|
||||||
import os
|
|
||||||
import platform
|
|
||||||
|
|
||||||
from manimlib.animation.fading import FadeIn
|
from manimlib.animation.fading import FadeIn
|
||||||
from manimlib.constants import MANIM_COLORS, WHITE, YELLOW
|
from manimlib.constants import ARROW_SYMBOLS, CTRL_SYMBOL, DELETE_SYMBOL, SHIFT_SYMBOL
|
||||||
from manimlib.constants import ORIGIN, UP, DOWN, LEFT, RIGHT, DL, UL, UR, DR
|
from manimlib.constants import COMMAND_MODIFIER, SHIFT_MODIFIER
|
||||||
|
from manimlib.constants import DL, DOWN, DR, LEFT, ORIGIN, RIGHT, UL, UP, UR
|
||||||
from manimlib.constants import FRAME_WIDTH, SMALL_BUFF
|
from manimlib.constants import FRAME_WIDTH, SMALL_BUFF
|
||||||
from manimlib.constants import SHIFT_SYMBOL, DELETE_SYMBOL, ARROW_SYMBOLS
|
from manimlib.constants import MANIM_COLORS, WHITE, GREY_C
|
||||||
from manimlib.constants import SHIFT_MODIFIER, COMMAND_MODIFIER
|
|
||||||
from manimlib.mobject.mobject import Mobject
|
|
||||||
from manimlib.mobject.geometry import Rectangle
|
from manimlib.mobject.geometry import Rectangle
|
||||||
from manimlib.mobject.geometry import Square
|
from manimlib.mobject.geometry import Square
|
||||||
from manimlib.mobject.mobject import Group
|
from manimlib.mobject.mobject import Group
|
||||||
|
from manimlib.mobject.mobject import Mobject
|
||||||
|
from manimlib.mobject.numbers import DecimalNumber
|
||||||
from manimlib.mobject.svg.tex_mobject import Tex
|
from manimlib.mobject.svg.tex_mobject import Tex
|
||||||
from manimlib.mobject.svg.text_mobject import Text
|
from manimlib.mobject.svg.text_mobject import Text
|
||||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
|
||||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
|
||||||
from manimlib.mobject.types.dot_cloud import DotCloud
|
from manimlib.mobject.types.dot_cloud import DotCloud
|
||||||
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||||
|
from manimlib.mobject.types.vectorized_mobject import VHighlight
|
||||||
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||||
from manimlib.scene.scene import Scene
|
from manimlib.scene.scene import Scene
|
||||||
from manimlib.utils.tex_file_writing import LatexError
|
|
||||||
from manimlib.utils.family_ops import extract_mobject_family_members
|
from manimlib.utils.family_ops import extract_mobject_family_members
|
||||||
from manimlib.utils.space_ops import get_norm
|
from manimlib.utils.space_ops import get_norm
|
||||||
from manimlib.logger import log
|
from manimlib.utils.tex_file_writing import LatexError
|
||||||
|
|
||||||
|
|
||||||
SELECT_KEY = 's'
|
SELECT_KEY = 's'
|
||||||
GRAB_KEY = 'g'
|
GRAB_KEY = 'g'
|
||||||
HORIZONTAL_GRAB_KEY = 'h'
|
X_GRAB_KEY = 'h'
|
||||||
VERTICAL_GRAB_KEY = 'v'
|
Y_GRAB_KEY = 'v'
|
||||||
|
GRAB_KEYS = [GRAB_KEY, X_GRAB_KEY, Y_GRAB_KEY]
|
||||||
RESIZE_KEY = 't'
|
RESIZE_KEY = 't'
|
||||||
COLOR_KEY = 'c'
|
COLOR_KEY = 'c'
|
||||||
|
CURSOR_LOCATION_KEY = 'l'
|
||||||
|
|
||||||
|
|
||||||
# Note, a lot of the functionality here is still buggy and very much a work in progress.
|
# Note, a lot of the functionality here is still buggy and very much a work in progress.
|
||||||
|
|
||||||
|
|
||||||
class InteractiveScene(Scene):
|
class InteractiveScene(Scene):
|
||||||
"""
|
"""
|
||||||
To select mobjects on screen, hold ctrl and move the mouse to highlight a region,
|
To select mobjects on screen, hold ctrl and move the mouse to highlight a region,
|
||||||
|
@ -66,36 +68,102 @@ class InteractiveScene(Scene):
|
||||||
selection_rectangle_stroke_width = 1.0
|
selection_rectangle_stroke_width = 1.0
|
||||||
colors = MANIM_COLORS
|
colors = MANIM_COLORS
|
||||||
selection_nudge_size = 0.05
|
selection_nudge_size = 0.05
|
||||||
|
cursor_location_config = dict(
|
||||||
|
font_size=14,
|
||||||
|
fill_color=GREY_C,
|
||||||
|
num_decimal_places=3,
|
||||||
|
)
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
self.selection = Group()
|
self.selection = Group()
|
||||||
self.selection_highlight = Group()
|
self.selection_highlight = Group()
|
||||||
self.selection_rectangle = self.get_selection_rectangle()
|
self.selection_rectangle = self.get_selection_rectangle()
|
||||||
self.color_palette = self.get_color_palette()
|
self.color_palette = self.get_color_palette()
|
||||||
|
self.cursor_location_label = self.get_cursor_location_label()
|
||||||
self.unselectables = [
|
self.unselectables = [
|
||||||
self.selection,
|
self.selection,
|
||||||
self.selection_highlight,
|
self.selection_highlight,
|
||||||
self.selection_rectangle,
|
self.selection_rectangle,
|
||||||
|
self.cursor_location_label,
|
||||||
self.camera.frame
|
self.camera.frame
|
||||||
]
|
]
|
||||||
self.saved_selection_state = []
|
|
||||||
self.select_top_level_mobs = True
|
self.select_top_level_mobs = True
|
||||||
|
self.regenerate_selection_search_set()
|
||||||
|
|
||||||
self.is_selecting = False
|
self.is_selecting = False
|
||||||
|
self.is_grabbing = False
|
||||||
self.add(self.selection_highlight)
|
self.add(self.selection_highlight)
|
||||||
|
|
||||||
|
def get_selection_rectangle(self):
|
||||||
|
rect = Rectangle(
|
||||||
|
stroke_color=self.selection_rectangle_stroke_color,
|
||||||
|
stroke_width=self.selection_rectangle_stroke_width,
|
||||||
|
)
|
||||||
|
rect.fix_in_frame()
|
||||||
|
rect.fixed_corner = ORIGIN
|
||||||
|
rect.add_updater(self.update_selection_rectangle)
|
||||||
|
return rect
|
||||||
|
|
||||||
|
def update_selection_rectangle(self, rect):
|
||||||
|
p1 = rect.fixed_corner
|
||||||
|
p2 = self.mouse_point.get_center()
|
||||||
|
rect.set_points_as_corners([
|
||||||
|
p1, [p2[0], p1[1], 0],
|
||||||
|
p2, [p1[0], p2[1], 0],
|
||||||
|
p1,
|
||||||
|
])
|
||||||
|
return rect
|
||||||
|
|
||||||
|
def get_color_palette(self):
|
||||||
|
palette = VGroup(*(
|
||||||
|
Square(fill_color=color, fill_opacity=1, side_length=1)
|
||||||
|
for color in self.colors
|
||||||
|
))
|
||||||
|
palette.set_stroke(width=0)
|
||||||
|
palette.arrange(RIGHT, buff=0.5)
|
||||||
|
palette.set_width(FRAME_WIDTH - 0.5)
|
||||||
|
palette.to_edge(DOWN, buff=SMALL_BUFF)
|
||||||
|
palette.fix_in_frame()
|
||||||
|
return palette
|
||||||
|
|
||||||
|
def get_cursor_location_label(self):
|
||||||
|
decimals = VGroup(*(
|
||||||
|
DecimalNumber(**self.cursor_location_config)
|
||||||
|
for n in range(3)
|
||||||
|
))
|
||||||
|
|
||||||
|
def update_coords(decimals):
|
||||||
|
for mob, coord in zip(decimals, self.mouse_point.get_location()):
|
||||||
|
mob.set_value(coord)
|
||||||
|
decimals.arrange(RIGHT, buff=decimals.get_height())
|
||||||
|
decimals.to_corner(DR, buff=SMALL_BUFF)
|
||||||
|
decimals.fix_in_frame()
|
||||||
|
return decimals
|
||||||
|
|
||||||
|
decimals.add_updater(update_coords)
|
||||||
|
return decimals
|
||||||
|
|
||||||
|
# Related to selection
|
||||||
|
|
||||||
def toggle_selection_mode(self):
|
def toggle_selection_mode(self):
|
||||||
self.select_top_level_mobs = not self.select_top_level_mobs
|
self.select_top_level_mobs = not self.select_top_level_mobs
|
||||||
self.refresh_selection_scope()
|
self.refresh_selection_scope()
|
||||||
|
self.regenerate_selection_search_set()
|
||||||
|
|
||||||
def get_selection_search_set(self):
|
def get_selection_search_set(self) -> list[Mobject]:
|
||||||
mobs = [m for m in self.mobjects if m not in self.unselectables]
|
return self.selection_search_set
|
||||||
|
|
||||||
|
def regenerate_selection_search_set(self):
|
||||||
|
selectable = list(filter(
|
||||||
|
lambda m: m not in self.unselectables,
|
||||||
|
self.mobjects
|
||||||
|
))
|
||||||
if self.select_top_level_mobs:
|
if self.select_top_level_mobs:
|
||||||
return mobs
|
self.selection_search_set = selectable
|
||||||
else:
|
else:
|
||||||
return [
|
self.selection_search_set = [
|
||||||
submob
|
submob
|
||||||
for mob in mobs
|
for mob in selectable
|
||||||
for submob in mob.family_members_with_points()
|
for submob in mob.family_members_with_points()
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -116,37 +184,7 @@ class InteractiveScene(Scene):
|
||||||
)
|
)
|
||||||
self.refresh_selection_highlight()
|
self.refresh_selection_highlight()
|
||||||
|
|
||||||
def get_selection_rectangle(self):
|
def get_corner_dots(self, mobject: Mobject) -> Mobject:
|
||||||
rect = Rectangle(
|
|
||||||
stroke_color=self.selection_rectangle_stroke_color,
|
|
||||||
stroke_width=self.selection_rectangle_stroke_width,
|
|
||||||
)
|
|
||||||
rect.fix_in_frame()
|
|
||||||
rect.fixed_corner = ORIGIN
|
|
||||||
rect.add_updater(self.update_selection_rectangle)
|
|
||||||
return rect
|
|
||||||
|
|
||||||
def get_color_palette(self):
|
|
||||||
palette = VGroup(*(
|
|
||||||
Square(fill_color=color, fill_opacity=1, side_length=1)
|
|
||||||
for color in self.colors
|
|
||||||
))
|
|
||||||
palette.set_stroke(width=0)
|
|
||||||
palette.arrange(RIGHT, buff=0.5)
|
|
||||||
palette.set_width(FRAME_WIDTH - 0.5)
|
|
||||||
palette.to_edge(DOWN, buff=SMALL_BUFF)
|
|
||||||
palette.fix_in_frame()
|
|
||||||
return palette
|
|
||||||
|
|
||||||
def get_stroke_highlight(self, vmobject):
|
|
||||||
outline = vmobject.copy()
|
|
||||||
for sm, osm in zip(vmobject.get_family(), outline.get_family()):
|
|
||||||
osm.set_fill(opacity=0)
|
|
||||||
osm.set_stroke(YELLOW, width=sm.get_stroke_width() + 1.5)
|
|
||||||
outline.add_updater(lambda o: o.replace(vmobject))
|
|
||||||
return outline
|
|
||||||
|
|
||||||
def get_corner_dots(self, mobject):
|
|
||||||
dots = DotCloud(**self.corner_dot_config)
|
dots = DotCloud(**self.corner_dot_config)
|
||||||
radius = self.corner_dot_config["radius"]
|
radius = self.corner_dot_config["radius"]
|
||||||
if mobject.get_depth() < 1e-2:
|
if mobject.get_depth() < 1e-2:
|
||||||
|
@ -159,9 +197,11 @@ class InteractiveScene(Scene):
|
||||||
]))
|
]))
|
||||||
return dots
|
return dots
|
||||||
|
|
||||||
def get_highlight(self, mobject):
|
def get_highlight(self, mobject: Mobject) -> Mobject:
|
||||||
if isinstance(mobject, VMobject) and mobject.has_points():
|
if isinstance(mobject, VMobject) and mobject.has_points() and not self.select_top_level_mobs:
|
||||||
return self.get_stroke_highlight(mobject)
|
result = VHighlight(mobject)
|
||||||
|
result.add_updater(lambda m: m.replace(mobject))
|
||||||
|
return result
|
||||||
else:
|
else:
|
||||||
return self.get_corner_dots(mobject)
|
return self.get_corner_dots(mobject)
|
||||||
|
|
||||||
|
@ -171,40 +211,54 @@ class InteractiveScene(Scene):
|
||||||
for mob in self.selection
|
for mob in self.selection
|
||||||
])
|
])
|
||||||
|
|
||||||
def update_selection_rectangle(self, rect):
|
|
||||||
p1 = rect.fixed_corner
|
|
||||||
p2 = self.mouse_point.get_center()
|
|
||||||
rect.set_points_as_corners([
|
|
||||||
p1, [p2[0], p1[1], 0],
|
|
||||||
p2, [p1[0], p2[1], 0],
|
|
||||||
p1,
|
|
||||||
])
|
|
||||||
return rect
|
|
||||||
|
|
||||||
def add_to_selection(self, *mobjects):
|
def add_to_selection(self, *mobjects):
|
||||||
mobs = list(filter(lambda m: m not in self.unselectables, mobjects))
|
mobs = list(filter(
|
||||||
self.selection.add(*mobjects)
|
lambda m: m not in self.unselectables and m not in self.selection,
|
||||||
|
mobjects
|
||||||
|
))
|
||||||
|
if len(mobs) == 0:
|
||||||
|
return
|
||||||
|
self.selection.add(*mobs)
|
||||||
self.selection_highlight.add(*map(self.get_highlight, mobs))
|
self.selection_highlight.add(*map(self.get_highlight, mobs))
|
||||||
self.saved_selection_state = [(mob, mob.copy()) for mob in self.selection]
|
for mob in mobs:
|
||||||
|
mob.set_animating_status(True)
|
||||||
|
self.refresh_static_mobjects()
|
||||||
|
|
||||||
def toggle_from_selection(self, *mobjects):
|
def toggle_from_selection(self, *mobjects):
|
||||||
for mob in mobjects:
|
for mob in mobjects:
|
||||||
if mob in self.selection:
|
if mob in self.selection:
|
||||||
self.selection.remove(mob)
|
self.selection.remove(mob)
|
||||||
|
mob.set_animating_status(False)
|
||||||
else:
|
else:
|
||||||
self.add_to_selection(mob)
|
self.add_to_selection(mob)
|
||||||
self.refresh_selection_highlight()
|
self.refresh_selection_highlight()
|
||||||
|
|
||||||
def clear_selection(self):
|
def clear_selection(self):
|
||||||
|
for mob in self.selection:
|
||||||
|
mob.set_animating_status(False)
|
||||||
self.selection.set_submobjects([])
|
self.selection.set_submobjects([])
|
||||||
self.selection_highlight.set_submobjects([])
|
self.selection_highlight.set_submobjects([])
|
||||||
|
self.refresh_static_mobjects()
|
||||||
|
|
||||||
def add(self, *new_mobjects: Mobject):
|
def add(self, *new_mobjects: Mobject):
|
||||||
for mob in new_mobjects:
|
|
||||||
mob.make_movable()
|
|
||||||
super().add(*new_mobjects)
|
super().add(*new_mobjects)
|
||||||
|
self.regenerate_selection_search_set()
|
||||||
|
|
||||||
# Selection operations
|
def remove(self, *mobjects: Mobject):
|
||||||
|
super().remove(*mobjects)
|
||||||
|
self.regenerate_selection_search_set()
|
||||||
|
|
||||||
|
def disable_interaction(self, *mobjects: Mobject):
|
||||||
|
for mob in mobjects:
|
||||||
|
self.unselectables.append(mob)
|
||||||
|
self.regenerate_selection_search_set()
|
||||||
|
|
||||||
|
def enable_interaction(self, *mobjects: Mobject):
|
||||||
|
for mob in mobjects:
|
||||||
|
if mob in self.unselectables:
|
||||||
|
self.unselectables.remove(mob)
|
||||||
|
|
||||||
|
# Functions for keyboard actions
|
||||||
|
|
||||||
def copy_selection(self):
|
def copy_selection(self):
|
||||||
ids = map(id, self.selection)
|
ids = map(id, self.selection)
|
||||||
|
@ -218,11 +272,11 @@ class InteractiveScene(Scene):
|
||||||
mobs = map(self.id_to_mobject, ids)
|
mobs = map(self.id_to_mobject, ids)
|
||||||
mob_copies = [m.copy() for m in mobs if m is not None]
|
mob_copies = [m.copy() for m in mobs if m is not None]
|
||||||
self.clear_selection()
|
self.clear_selection()
|
||||||
self.add_to_selection(*mob_copies)
|
|
||||||
self.play(*(
|
self.play(*(
|
||||||
FadeIn(mc, run_time=0.5, scale=1.5)
|
FadeIn(mc, run_time=0.5, scale=1.5)
|
||||||
for mc in mob_copies
|
for mc in mob_copies
|
||||||
))
|
))
|
||||||
|
self.add_to_selection(*mob_copies)
|
||||||
return
|
return
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
@ -242,41 +296,27 @@ class InteractiveScene(Scene):
|
||||||
self.remove(*self.selection)
|
self.remove(*self.selection)
|
||||||
self.clear_selection()
|
self.clear_selection()
|
||||||
|
|
||||||
def saved_selection_to_file(self):
|
def restore_state(self, mobject_states: list[tuple[Mobject, Mobject]]):
|
||||||
directory = self.file_writer.get_saved_mobject_directory()
|
super().restore_state(mobject_states)
|
||||||
files = os.listdir(directory)
|
|
||||||
for mob in self.selection:
|
|
||||||
file_name = str(mob) + "_0.mob"
|
|
||||||
index = 0
|
|
||||||
while file_name in files:
|
|
||||||
file_name = file_name.replace(str(index), str(index + 1))
|
|
||||||
index += 1
|
|
||||||
if platform.system() == 'Darwin':
|
|
||||||
user_name = os.popen(f"""
|
|
||||||
osascript -e '
|
|
||||||
set chosenfile to (choose file name default name "{file_name}" default location "{directory}")
|
|
||||||
POSIX path of chosenfile'
|
|
||||||
""").read()
|
|
||||||
user_name = user_name.replace("\n", "")
|
|
||||||
else:
|
|
||||||
user_name = input(
|
|
||||||
f"Enter mobject file name (default is {file_name}): "
|
|
||||||
)
|
|
||||||
if user_name:
|
|
||||||
file_name = user_name
|
|
||||||
files.append(file_name)
|
|
||||||
self.save_mobect(mob, file_name)
|
|
||||||
|
|
||||||
def undo(self):
|
|
||||||
mobs = []
|
|
||||||
for mob, state in self.saved_selection_state:
|
|
||||||
mob.become(state)
|
|
||||||
mobs.append(mob)
|
|
||||||
if mob not in self.mobjects:
|
|
||||||
self.add(mob)
|
|
||||||
self.selection.set_submobjects(mobs)
|
|
||||||
self.refresh_selection_highlight()
|
self.refresh_selection_highlight()
|
||||||
|
|
||||||
|
def enable_selection(self):
|
||||||
|
self.is_selecting = True
|
||||||
|
self.add(self.selection_rectangle)
|
||||||
|
self.selection_rectangle.fixed_corner = self.mouse_point.get_center().copy()
|
||||||
|
|
||||||
|
def gather_new_selection(self):
|
||||||
|
self.is_selecting = False
|
||||||
|
self.remove(self.selection_rectangle)
|
||||||
|
for mob in reversed(self.get_selection_search_set()):
|
||||||
|
if self.selection_rectangle.is_touching(mob):
|
||||||
|
self.add_to_selection(mob)
|
||||||
|
|
||||||
|
def prepare_grab(self):
|
||||||
|
mp = self.mouse_point.get_center()
|
||||||
|
self.mouse_to_selection = mp - self.selection.get_center()
|
||||||
|
self.is_grabbing = True
|
||||||
|
|
||||||
def prepare_resizing(self, about_corner=False):
|
def prepare_resizing(self, about_corner=False):
|
||||||
center = self.selection.get_center()
|
center = self.selection.get_center()
|
||||||
mp = self.mouse_point.get_center()
|
mp = self.mouse_point.get_center()
|
||||||
|
@ -286,62 +326,24 @@ class InteractiveScene(Scene):
|
||||||
self.scale_about_point = center
|
self.scale_about_point = center
|
||||||
self.scale_ref_vect = mp - self.scale_about_point
|
self.scale_ref_vect = mp - self.scale_about_point
|
||||||
self.scale_ref_width = self.selection.get_width()
|
self.scale_ref_width = self.selection.get_width()
|
||||||
|
self.scale_ref_height = self.selection.get_height()
|
||||||
|
|
||||||
# Event handlers
|
def toggle_color_palette(self):
|
||||||
|
|
||||||
def on_key_press(self, symbol: int, modifiers: int) -> None:
|
|
||||||
super().on_key_press(symbol, modifiers)
|
|
||||||
char = chr(symbol)
|
|
||||||
# Enable selection
|
|
||||||
if char == SELECT_KEY and modifiers == 0:
|
|
||||||
self.is_selecting = True
|
|
||||||
self.add(self.selection_rectangle)
|
|
||||||
self.selection_rectangle.fixed_corner = self.mouse_point.get_center().copy()
|
|
||||||
# Prepare for move
|
|
||||||
elif char in [GRAB_KEY, HORIZONTAL_GRAB_KEY, VERTICAL_GRAB_KEY] and modifiers == 0:
|
|
||||||
mp = self.mouse_point.get_center()
|
|
||||||
self.mouse_to_selection = mp - self.selection.get_center()
|
|
||||||
# Prepare for resizing
|
|
||||||
elif char == RESIZE_KEY and modifiers in [0, SHIFT_MODIFIER]:
|
|
||||||
self.prepare_resizing(about_corner=(modifiers == SHIFT_MODIFIER))
|
|
||||||
elif symbol == SHIFT_SYMBOL:
|
|
||||||
if self.window.is_key_pressed(ord("t")):
|
|
||||||
self.prepare_resizing(about_corner=True)
|
|
||||||
# Show color palette
|
|
||||||
elif char == COLOR_KEY and modifiers == 0:
|
|
||||||
if len(self.selection) == 0:
|
if len(self.selection) == 0:
|
||||||
return
|
return
|
||||||
if self.color_palette not in self.mobjects:
|
if self.color_palette not in self.mobjects:
|
||||||
|
self.save_state()
|
||||||
self.add(self.color_palette)
|
self.add(self.color_palette)
|
||||||
else:
|
else:
|
||||||
self.remove(self.color_palette)
|
self.remove(self.color_palette)
|
||||||
# Command + c -> Copy mobject ids to clipboard
|
|
||||||
elif char == "c" and modifiers == COMMAND_MODIFIER:
|
def group_selection(self):
|
||||||
self.copy_selection()
|
|
||||||
# Command + v -> Paste
|
|
||||||
elif char == "v" and modifiers == COMMAND_MODIFIER:
|
|
||||||
self.paste_selection()
|
|
||||||
# Command + x -> Cut
|
|
||||||
elif char == "x" and modifiers == COMMAND_MODIFIER:
|
|
||||||
# TODO, this copy won't work, because once the objects are removed,
|
|
||||||
# they're not searched for in the pasting.
|
|
||||||
self.copy_selection()
|
|
||||||
self.delete_selection()
|
|
||||||
# Delete
|
|
||||||
elif symbol == DELETE_SYMBOL:
|
|
||||||
self.delete_selection()
|
|
||||||
# Command + a -> Select all
|
|
||||||
elif char == "a" and modifiers == COMMAND_MODIFIER:
|
|
||||||
self.clear_selection()
|
|
||||||
self.add_to_selection(*self.mobjects)
|
|
||||||
# Command + g -> Group selection
|
|
||||||
elif char == "g" and modifiers == COMMAND_MODIFIER:
|
|
||||||
group = self.get_group(*self.selection)
|
group = self.get_group(*self.selection)
|
||||||
self.add(group)
|
self.add(group)
|
||||||
self.clear_selection()
|
self.clear_selection()
|
||||||
self.add_to_selection(group)
|
self.add_to_selection(group)
|
||||||
# Command + shift + g -> Ungroup the selection
|
|
||||||
elif char == "g" and modifiers == COMMAND_MODIFIER | SHIFT_MODIFIER:
|
def ungroup_selection(self):
|
||||||
pieces = []
|
pieces = []
|
||||||
for mob in list(self.selection):
|
for mob in list(self.selection):
|
||||||
self.remove(mob)
|
self.remove(mob)
|
||||||
|
@ -349,73 +351,148 @@ class InteractiveScene(Scene):
|
||||||
self.clear_selection()
|
self.clear_selection()
|
||||||
self.add(*pieces)
|
self.add(*pieces)
|
||||||
self.add_to_selection(*pieces)
|
self.add_to_selection(*pieces)
|
||||||
# Command + t -> Toggle selection mode
|
|
||||||
|
def nudge_selection(self, vect: np.ndarray, large: bool = False):
|
||||||
|
nudge = self.selection_nudge_size
|
||||||
|
if large:
|
||||||
|
nudge *= 10
|
||||||
|
self.selection.shift(nudge * vect)
|
||||||
|
|
||||||
|
def save_selection_to_file(self):
|
||||||
|
if len(self.selection) == 1:
|
||||||
|
self.save_mobject_to_file(self.selection[0])
|
||||||
|
else:
|
||||||
|
self.save_mobject_to_file(self.selection)
|
||||||
|
|
||||||
|
def on_key_press(self, symbol: int, modifiers: int) -> None:
|
||||||
|
super().on_key_press(symbol, modifiers)
|
||||||
|
char = chr(symbol)
|
||||||
|
if char == SELECT_KEY and modifiers == 0:
|
||||||
|
self.enable_selection()
|
||||||
|
elif char in GRAB_KEYS and modifiers == 0:
|
||||||
|
self.prepare_grab()
|
||||||
|
elif char == RESIZE_KEY and modifiers in [0, SHIFT_MODIFIER]:
|
||||||
|
self.prepare_resizing(about_corner=(modifiers == SHIFT_MODIFIER))
|
||||||
|
elif symbol == SHIFT_SYMBOL:
|
||||||
|
if self.window.is_key_pressed(ord("t")):
|
||||||
|
self.prepare_resizing(about_corner=True)
|
||||||
|
elif char == COLOR_KEY and modifiers == 0:
|
||||||
|
self.toggle_color_palette()
|
||||||
|
elif char == CURSOR_LOCATION_KEY and modifiers == 0:
|
||||||
|
self.add(self.cursor_location_label)
|
||||||
|
elif char == "c" and modifiers == COMMAND_MODIFIER:
|
||||||
|
self.copy_selection()
|
||||||
|
elif char == "v" and modifiers == COMMAND_MODIFIER:
|
||||||
|
self.paste_selection()
|
||||||
|
elif char == "x" and modifiers == COMMAND_MODIFIER:
|
||||||
|
self.copy_selection()
|
||||||
|
self.delete_selection()
|
||||||
|
elif symbol == DELETE_SYMBOL:
|
||||||
|
self.delete_selection()
|
||||||
|
elif char == "a" and modifiers == COMMAND_MODIFIER:
|
||||||
|
self.clear_selection()
|
||||||
|
self.add_to_selection(*self.mobjects)
|
||||||
|
elif char == "g" and modifiers == COMMAND_MODIFIER:
|
||||||
|
self.group_selection()
|
||||||
|
elif char == "g" and modifiers == COMMAND_MODIFIER | SHIFT_MODIFIER:
|
||||||
|
self.ungroup_selection()
|
||||||
elif char == "t" and modifiers == COMMAND_MODIFIER:
|
elif char == "t" and modifiers == COMMAND_MODIFIER:
|
||||||
self.toggle_selection_mode()
|
self.toggle_selection_mode()
|
||||||
# Command + z -> Restore selection to original state
|
|
||||||
elif char == "z" and modifiers == COMMAND_MODIFIER:
|
|
||||||
self.undo()
|
|
||||||
# Command + s -> Save selections to file
|
|
||||||
elif char == "s" and modifiers == COMMAND_MODIFIER:
|
elif char == "s" and modifiers == COMMAND_MODIFIER:
|
||||||
self.saved_selection_to_file()
|
self.save_selection_to_file()
|
||||||
# Keyboard movements
|
|
||||||
elif symbol in ARROW_SYMBOLS:
|
elif symbol in ARROW_SYMBOLS:
|
||||||
nudge = self.selection_nudge_size
|
self.nudge_selection(
|
||||||
if (modifiers & SHIFT_MODIFIER):
|
vect=[LEFT, UP, RIGHT, DOWN][ARROW_SYMBOLS.index(symbol)],
|
||||||
nudge *= 10
|
large=(modifiers & SHIFT_MODIFIER),
|
||||||
vect = [LEFT, UP, RIGHT, DOWN][ARROW_SYMBOLS.index(symbol)]
|
)
|
||||||
self.selection.shift(nudge * vect)
|
|
||||||
|
# Conditions for saving state
|
||||||
|
if char in [GRAB_KEY, X_GRAB_KEY, Y_GRAB_KEY, RESIZE_KEY]:
|
||||||
|
self.save_state()
|
||||||
|
|
||||||
def on_key_release(self, symbol: int, modifiers: int) -> None:
|
def on_key_release(self, symbol: int, modifiers: int) -> None:
|
||||||
super().on_key_release(symbol, modifiers)
|
super().on_key_release(symbol, modifiers)
|
||||||
if chr(symbol) == SELECT_KEY:
|
if chr(symbol) == SELECT_KEY:
|
||||||
self.is_selecting = False
|
self.gather_new_selection()
|
||||||
self.remove(self.selection_rectangle)
|
if chr(symbol) in GRAB_KEYS:
|
||||||
for mob in reversed(self.get_selection_search_set()):
|
self.is_grabbing = False
|
||||||
if mob.is_movable() and self.selection_rectangle.is_touching(mob):
|
elif chr(symbol) == CURSOR_LOCATION_KEY:
|
||||||
self.add_to_selection(mob)
|
self.remove(self.cursor_location_label)
|
||||||
|
elif symbol == SHIFT_SYMBOL and self.window.is_key_pressed(ord(RESIZE_KEY)):
|
||||||
elif symbol == SHIFT_SYMBOL:
|
|
||||||
if self.window.is_key_pressed(ord(RESIZE_KEY)):
|
|
||||||
self.prepare_resizing(about_corner=False)
|
self.prepare_resizing(about_corner=False)
|
||||||
|
|
||||||
def on_mouse_motion(self, point: np.ndarray, d_point: np.ndarray) -> None:
|
# Mouse actions
|
||||||
super().on_mouse_motion(point, d_point)
|
def handle_grabbing(self, point: np.ndarray):
|
||||||
# Move selection
|
diff = point - self.mouse_to_selection
|
||||||
if self.window.is_key_pressed(ord("g")):
|
if self.window.is_key_pressed(ord(GRAB_KEY)):
|
||||||
self.selection.move_to(point - self.mouse_to_selection)
|
self.selection.move_to(diff)
|
||||||
# Move selection restricted to horizontal
|
elif self.window.is_key_pressed(ord(X_GRAB_KEY)):
|
||||||
elif self.window.is_key_pressed(ord("h")):
|
self.selection.set_x(diff[0])
|
||||||
self.selection.set_x((point - self.mouse_to_selection)[0])
|
elif self.window.is_key_pressed(ord(Y_GRAB_KEY)):
|
||||||
# Move selection restricted to vertical
|
self.selection.set_y(diff[1])
|
||||||
elif self.window.is_key_pressed(ord("v")):
|
|
||||||
self.selection.set_y((point - self.mouse_to_selection)[1])
|
def handle_resizing(self, point: np.ndarray):
|
||||||
# Scale selection
|
|
||||||
elif self.window.is_key_pressed(ord("t")):
|
|
||||||
# TODO, allow for scaling about the opposite corner
|
|
||||||
vect = point - self.scale_about_point
|
vect = point - self.scale_about_point
|
||||||
|
if self.window.is_key_pressed(CTRL_SYMBOL):
|
||||||
|
for i in (0, 1):
|
||||||
|
scalar = vect[i] / self.scale_ref_vect[i]
|
||||||
|
self.selection.rescale_to_fit(
|
||||||
|
scalar * [self.scale_ref_width, self.scale_ref_height][i],
|
||||||
|
dim=i,
|
||||||
|
about_point=self.scale_about_point,
|
||||||
|
stretch=True,
|
||||||
|
)
|
||||||
|
else:
|
||||||
scalar = get_norm(vect) / get_norm(self.scale_ref_vect)
|
scalar = get_norm(vect) / get_norm(self.scale_ref_vect)
|
||||||
self.selection.set_width(
|
self.selection.set_width(
|
||||||
scalar * self.scale_ref_width,
|
scalar * self.scale_ref_width,
|
||||||
about_point=self.scale_about_point
|
about_point=self.scale_about_point
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def handle_sweeping_selection(self, point: np.ndarray):
|
||||||
|
mob = self.point_to_mobject(
|
||||||
|
point, search_set=self.get_selection_search_set(),
|
||||||
|
buff=SMALL_BUFF
|
||||||
|
)
|
||||||
|
if mob is not None:
|
||||||
|
self.add_to_selection(mob)
|
||||||
|
|
||||||
|
def choose_color(self, point: np.ndarray):
|
||||||
|
# Search through all mobject on the screen, not just the palette
|
||||||
|
to_search = [
|
||||||
|
sm
|
||||||
|
for mobject in self.mobjects
|
||||||
|
for sm in mobject.family_members_with_points()
|
||||||
|
if mobject not in self.unselectables
|
||||||
|
]
|
||||||
|
mob = self.point_to_mobject(point, to_search)
|
||||||
|
if mob is not None:
|
||||||
|
self.selection.set_color(mob.get_color())
|
||||||
|
self.remove(self.color_palette)
|
||||||
|
|
||||||
|
def toggle_clicked_mobject_from_selection(self, point: np.ndarray):
|
||||||
|
mob = self.point_to_mobject(
|
||||||
|
point,
|
||||||
|
search_set=self.get_selection_search_set(),
|
||||||
|
buff=SMALL_BUFF
|
||||||
|
)
|
||||||
|
if mob is not None:
|
||||||
|
self.toggle_from_selection(mob)
|
||||||
|
|
||||||
|
def on_mouse_motion(self, point: np.ndarray, d_point: np.ndarray) -> None:
|
||||||
|
super().on_mouse_motion(point, d_point)
|
||||||
|
if self.is_grabbing:
|
||||||
|
self.handle_grabbing(point)
|
||||||
|
elif self.window.is_key_pressed(ord(RESIZE_KEY)):
|
||||||
|
self.handle_resizing(point)
|
||||||
|
elif self.window.is_key_pressed(ord(SELECT_KEY)) and self.window.is_key_pressed(SHIFT_SYMBOL):
|
||||||
|
self.handle_sweeping_selection(point)
|
||||||
|
|
||||||
def on_mouse_release(self, point: np.ndarray, button: int, mods: int) -> None:
|
def on_mouse_release(self, point: np.ndarray, button: int, mods: int) -> None:
|
||||||
super().on_mouse_release(point, button, mods)
|
super().on_mouse_release(point, button, mods)
|
||||||
if self.color_palette in self.mobjects:
|
if self.color_palette in self.mobjects:
|
||||||
# Search through all mobject on the screne, not just the palette
|
self.choose_color(point)
|
||||||
to_search = list(it.chain(*(
|
|
||||||
mobject.family_members_with_points()
|
|
||||||
for mobject in self.mobjects
|
|
||||||
if mobject not in self.unselectables
|
|
||||||
)))
|
|
||||||
mob = self.point_to_mobject(point, to_search)
|
|
||||||
if mob is not None:
|
|
||||||
self.selection.set_color(mob.get_fill_color())
|
|
||||||
self.remove(self.color_palette)
|
|
||||||
elif self.window.is_key_pressed(SHIFT_SYMBOL):
|
elif self.window.is_key_pressed(SHIFT_SYMBOL):
|
||||||
mob = self.point_to_mobject(point)
|
self.toggle_clicked_mobject_from_selection(point)
|
||||||
if mob is not None:
|
|
||||||
self.toggle_from_selection(mob)
|
|
||||||
else:
|
else:
|
||||||
self.clear_selection()
|
self.clear_selection()
|
||||||
|
|
|
@ -2,10 +2,11 @@ from manimlib.animation.animation import Animation
|
||||||
from manimlib.animation.transform import MoveToTarget
|
from manimlib.animation.transform import MoveToTarget
|
||||||
from manimlib.animation.transform import Transform
|
from manimlib.animation.transform import Transform
|
||||||
from manimlib.animation.update import UpdateFromFunc
|
from manimlib.animation.update import UpdateFromFunc
|
||||||
from manimlib.constants import *
|
from manimlib.constants import DOWN, RIGHT
|
||||||
from manimlib.scene.scene import Scene
|
from manimlib.constants import MED_LARGE_BUFF, SMALL_BUFF
|
||||||
from manimlib.mobject.probability import SampleSpace
|
from manimlib.mobject.probability import SampleSpace
|
||||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||||
|
from manimlib.scene.scene import Scene
|
||||||
|
|
||||||
|
|
||||||
class SampleSpaceScene(Scene):
|
class SampleSpaceScene(Scene):
|
||||||
|
|
|
@ -1,40 +1,42 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import time
|
|
||||||
import random
|
|
||||||
import inspect
|
|
||||||
import platform
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import Iterable, Callable
|
import inspect
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
|
||||||
from tqdm import tqdm as ProgressDisplay
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from tqdm import tqdm as ProgressDisplay
|
||||||
|
|
||||||
from manimlib.animation.animation import prepare_animation
|
from manimlib.animation.animation import prepare_animation
|
||||||
from manimlib.animation.transform import MoveToTarget
|
from manimlib.animation.transform import MoveToTarget
|
||||||
from manimlib.camera.camera import Camera
|
from manimlib.camera.camera import Camera
|
||||||
from manimlib.config import get_custom_config
|
|
||||||
from manimlib.constants import DEFAULT_WAIT_TIME
|
|
||||||
from manimlib.constants import ARROW_SYMBOLS
|
from manimlib.constants import ARROW_SYMBOLS
|
||||||
from manimlib.constants import SHIFT_MODIFIER, CTRL_MODIFIER, COMMAND_MODIFIER
|
from manimlib.constants import DEFAULT_WAIT_TIME
|
||||||
|
from manimlib.constants import COMMAND_MODIFIER
|
||||||
|
from manimlib.constants import SHIFT_MODIFIER
|
||||||
|
from manimlib.event_handler import EVENT_DISPATCHER
|
||||||
|
from manimlib.event_handler.event_type import EventType
|
||||||
|
from manimlib.logger import log
|
||||||
|
from manimlib.mobject.mobject import Group
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
from manimlib.mobject.mobject import Point
|
from manimlib.mobject.mobject import Point
|
||||||
from manimlib.mobject.mobject import Group
|
|
||||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
|
||||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||||
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||||
from manimlib.scene.scene_file_writer import SceneFileWriter
|
from manimlib.scene.scene_file_writer import SceneFileWriter
|
||||||
from manimlib.utils.config_ops import digest_config
|
from manimlib.utils.config_ops import digest_config
|
||||||
from manimlib.utils.family_ops import extract_mobject_family_members
|
from manimlib.utils.family_ops import extract_mobject_family_members
|
||||||
from manimlib.utils.family_ops import restructure_list_to_exclude_certain_family_members
|
from manimlib.utils.family_ops import restructure_list_to_exclude_certain_family_members
|
||||||
from manimlib.event_handler.event_type import EventType
|
|
||||||
from manimlib.event_handler import EVENT_DISPATCHER
|
|
||||||
from manimlib.logger import log
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from typing import Callable, Iterable
|
||||||
|
|
||||||
from PIL.Image import Image
|
from PIL.Image import Image
|
||||||
|
|
||||||
from manimlib.animation.animation import Animation
|
from manimlib.animation.animation import Animation
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,7 +45,6 @@ FRAME_SHIFT_KEY = 'f'
|
||||||
ZOOM_KEY = 'z'
|
ZOOM_KEY = 'z'
|
||||||
RESET_FRAME_KEY = 'r'
|
RESET_FRAME_KEY = 'r'
|
||||||
QUIT_KEY = 'q'
|
QUIT_KEY = 'q'
|
||||||
EMBED_KEY = 'e'
|
|
||||||
|
|
||||||
|
|
||||||
class Scene(object):
|
class Scene(object):
|
||||||
|
@ -62,6 +63,7 @@ class Scene(object):
|
||||||
"presenter_mode": False,
|
"presenter_mode": False,
|
||||||
"linger_after_completion": True,
|
"linger_after_completion": True,
|
||||||
"pan_sensitivity": 3,
|
"pan_sensitivity": 3,
|
||||||
|
"max_num_saved_states": 20,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
@ -71,12 +73,15 @@ class Scene(object):
|
||||||
self.window = Window(scene=self, **self.window_config)
|
self.window = Window(scene=self, **self.window_config)
|
||||||
self.camera_config["ctx"] = self.window.ctx
|
self.camera_config["ctx"] = self.window.ctx
|
||||||
self.camera_config["frame_rate"] = 30 # Where's that 30 from?
|
self.camera_config["frame_rate"] = 30 # Where's that 30 from?
|
||||||
|
self.undo_stack = []
|
||||||
|
self.redo_stack = []
|
||||||
else:
|
else:
|
||||||
self.window = None
|
self.window = None
|
||||||
|
|
||||||
self.camera: Camera = self.camera_class(**self.camera_config)
|
self.camera: Camera = self.camera_class(**self.camera_config)
|
||||||
self.file_writer = SceneFileWriter(self, **self.file_writer_config)
|
self.file_writer = SceneFileWriter(self, **self.file_writer_config)
|
||||||
self.mobjects: list[Mobject] = [self.camera.frame]
|
self.mobjects: list[Mobject] = [self.camera.frame]
|
||||||
|
self.id_to_mobject_map: dict[int, Mobject] = dict()
|
||||||
self.num_plays: int = 0
|
self.num_plays: int = 0
|
||||||
self.time: float = 0
|
self.time: float = 0
|
||||||
self.skip_time: float = 0
|
self.skip_time: float = 0
|
||||||
|
@ -88,12 +93,16 @@ class Scene(object):
|
||||||
self.mouse_point = Point()
|
self.mouse_point = Point()
|
||||||
self.mouse_drag_point = Point()
|
self.mouse_drag_point = Point()
|
||||||
self.hold_on_wait = self.presenter_mode
|
self.hold_on_wait = self.presenter_mode
|
||||||
|
self.inside_embed = False
|
||||||
|
|
||||||
# Much nicer to work with deterministic scenes
|
# Much nicer to work with deterministic scenes
|
||||||
if self.random_seed is not None:
|
if self.random_seed is not None:
|
||||||
random.seed(self.random_seed)
|
random.seed(self.random_seed)
|
||||||
np.random.seed(self.random_seed)
|
np.random.seed(self.random_seed)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.__class__.__name__
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
self.virtual_animation_start_time: float = 0
|
self.virtual_animation_start_time: float = 0
|
||||||
self.real_animation_start_time: float = time.time()
|
self.real_animation_start_time: float = time.time()
|
||||||
|
@ -143,40 +152,57 @@ class Scene(object):
|
||||||
|
|
||||||
def embed(self, close_scene_on_exit: bool = True) -> None:
|
def embed(self, close_scene_on_exit: bool = True) -> None:
|
||||||
if not self.preview:
|
if not self.preview:
|
||||||
# If the scene is just being
|
# Ignore embed calls when there is no preview
|
||||||
# written, ignore embed calls
|
|
||||||
return
|
return
|
||||||
|
self.inside_embed = True
|
||||||
self.stop_skipping()
|
self.stop_skipping()
|
||||||
self.linger_after_completion = False
|
self.linger_after_completion = False
|
||||||
self.update_frame()
|
self.update_frame()
|
||||||
|
|
||||||
# Save scene state at the point of embedding
|
|
||||||
self.save_state()
|
self.save_state()
|
||||||
|
|
||||||
from IPython.terminal.embed import InteractiveShellEmbed
|
# Configure and launch embedded IPython terminal
|
||||||
shell = InteractiveShellEmbed()
|
from IPython.terminal import embed, pt_inputhooks
|
||||||
# Have the frame update after each command
|
shell = embed.InteractiveShellEmbed.instance()
|
||||||
shell.events.register('post_run_cell', lambda *a, **kw: self.refresh_static_mobjects())
|
|
||||||
shell.events.register('post_run_cell', lambda *a, **kw: self.update_frame())
|
# Use the locals namespace of the caller
|
||||||
# Use the locals of the caller as the local namespace
|
|
||||||
# once embedded, and add a few custom shortcuts
|
|
||||||
local_ns = inspect.currentframe().f_back.f_locals
|
local_ns = inspect.currentframe().f_back.f_locals
|
||||||
local_ns["touch"] = self.interact
|
# Add a few custom shortcuts
|
||||||
local_ns["i2g"] = self.ids_to_group
|
local_ns.update({
|
||||||
for term in ("play", "wait", "add", "remove", "clear", "save_state", "restore"):
|
name: getattr(self, name)
|
||||||
local_ns[term] = getattr(self, term)
|
for name in [
|
||||||
log.info("Tips: Now the embed iPython terminal is open. But you can't interact with"
|
"play", "wait", "add", "remove", "clear",
|
||||||
" the window directly. To do so, you need to type `touch()` or `self.interact()`")
|
"save_state", "undo", "redo", "i2g", "i2m"
|
||||||
exec(get_custom_config()["universal_import_line"])
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
# Enables gui interactions during the embed
|
||||||
|
def inputhook(context):
|
||||||
|
while not context.input_is_ready():
|
||||||
|
if self.window.is_closing:
|
||||||
|
pass
|
||||||
|
# self.window.destroy()
|
||||||
|
else:
|
||||||
|
self.update_frame(dt=0)
|
||||||
|
|
||||||
|
pt_inputhooks.register("manim", inputhook)
|
||||||
|
shell.enable_gui("manim")
|
||||||
|
|
||||||
|
# Operation to run after each ipython command
|
||||||
|
def post_cell_func(*args, **kwargs):
|
||||||
|
self.refresh_static_mobjects()
|
||||||
|
|
||||||
|
shell.events.register("post_run_cell", post_cell_func)
|
||||||
|
|
||||||
|
# Launch shell, with stack_depth=2 indicating we should use caller globals/locals
|
||||||
shell(local_ns=local_ns, stack_depth=2)
|
shell(local_ns=local_ns, stack_depth=2)
|
||||||
|
|
||||||
|
self.inside_embed = False
|
||||||
# End scene when exiting an embed
|
# End scene when exiting an embed
|
||||||
if close_scene_on_exit:
|
if close_scene_on_exit:
|
||||||
raise EndSceneEarlyException()
|
raise EndSceneEarlyException()
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return self.__class__.__name__
|
|
||||||
|
|
||||||
# Only these methods should touch the camera
|
# Only these methods should touch the camera
|
||||||
|
|
||||||
def get_image(self) -> Image:
|
def get_image(self) -> Image:
|
||||||
return self.camera.get_image()
|
return self.camera.get_image()
|
||||||
|
|
||||||
|
@ -207,6 +233,7 @@ class Scene(object):
|
||||||
self.file_writer.write_frame(self.camera)
|
self.file_writer.write_frame(self.camera)
|
||||||
|
|
||||||
# Related to updating
|
# Related to updating
|
||||||
|
|
||||||
def update_mobjects(self, dt: float) -> None:
|
def update_mobjects(self, dt: float) -> None:
|
||||||
for mobject in self.mobjects:
|
for mobject in self.mobjects:
|
||||||
mobject.update(dt)
|
mobject.update(dt)
|
||||||
|
@ -225,6 +252,7 @@ class Scene(object):
|
||||||
])
|
])
|
||||||
|
|
||||||
# Related to time
|
# Related to time
|
||||||
|
|
||||||
def get_time(self) -> float:
|
def get_time(self) -> float:
|
||||||
return self.time
|
return self.time
|
||||||
|
|
||||||
|
@ -232,6 +260,7 @@ class Scene(object):
|
||||||
self.time += dt
|
self.time += dt
|
||||||
|
|
||||||
# Related to internal mobject organization
|
# Related to internal mobject organization
|
||||||
|
|
||||||
def get_top_level_mobjects(self) -> list[Mobject]:
|
def get_top_level_mobjects(self) -> list[Mobject]:
|
||||||
# Return only those which are not in the family
|
# Return only those which are not in the family
|
||||||
# of another mobject from the scene
|
# of another mobject from the scene
|
||||||
|
@ -256,6 +285,11 @@ class Scene(object):
|
||||||
"""
|
"""
|
||||||
self.remove(*new_mobjects)
|
self.remove(*new_mobjects)
|
||||||
self.mobjects += new_mobjects
|
self.mobjects += new_mobjects
|
||||||
|
self.id_to_mobject_map.update({
|
||||||
|
id(sm): sm
|
||||||
|
for m in new_mobjects
|
||||||
|
for sm in m.get_family()
|
||||||
|
})
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add_mobjects_among(self, values: Iterable):
|
def add_mobjects_among(self, values: Iterable):
|
||||||
|
@ -319,11 +353,7 @@ class Scene(object):
|
||||||
return Group(*mobjects)
|
return Group(*mobjects)
|
||||||
|
|
||||||
def id_to_mobject(self, id_value):
|
def id_to_mobject(self, id_value):
|
||||||
for mob in self.mobjects:
|
return self.id_to_mobject_map[id_value]
|
||||||
for sm in mob.get_family():
|
|
||||||
if id(sm) == id_value:
|
|
||||||
return sm
|
|
||||||
return None
|
|
||||||
|
|
||||||
def ids_to_group(self, *id_values):
|
def ids_to_group(self, *id_values):
|
||||||
return self.get_group(*filter(
|
return self.get_group(*filter(
|
||||||
|
@ -331,7 +361,14 @@ class Scene(object):
|
||||||
map(self.id_to_mobject, id_values)
|
map(self.id_to_mobject, id_values)
|
||||||
))
|
))
|
||||||
|
|
||||||
|
def i2g(self, *id_values):
|
||||||
|
return self.ids_to_group(*id_values)
|
||||||
|
|
||||||
|
def i2m(self, id_value):
|
||||||
|
return self.id_to_mobject(id_value)
|
||||||
|
|
||||||
# Related to skipping
|
# Related to skipping
|
||||||
|
|
||||||
def update_skipping_status(self) -> None:
|
def update_skipping_status(self) -> None:
|
||||||
if self.start_at_animation_number is not None:
|
if self.start_at_animation_number is not None:
|
||||||
if self.num_plays == self.start_at_animation_number:
|
if self.num_plays == self.start_at_animation_number:
|
||||||
|
@ -347,6 +384,7 @@ class Scene(object):
|
||||||
self.skip_animations = False
|
self.skip_animations = False
|
||||||
|
|
||||||
# Methods associated with running animations
|
# Methods associated with running animations
|
||||||
|
|
||||||
def get_time_progression(
|
def get_time_progression(
|
||||||
self,
|
self,
|
||||||
run_time: float,
|
run_time: float,
|
||||||
|
@ -470,6 +508,8 @@ class Scene(object):
|
||||||
def handle_play_like_call(func):
|
def handle_play_like_call(func):
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapper(self, *args, **kwargs):
|
def wrapper(self, *args, **kwargs):
|
||||||
|
if self.inside_embed:
|
||||||
|
self.save_state()
|
||||||
self.update_skipping_status()
|
self.update_skipping_status()
|
||||||
should_write = not self.skip_animations
|
should_write = not self.skip_animations
|
||||||
if should_write:
|
if should_write:
|
||||||
|
@ -591,24 +631,39 @@ class Scene(object):
|
||||||
self.file_writer.add_sound(sound_file, time, gain, gain_to_background)
|
self.file_writer.add_sound(sound_file, time, gain, gain_to_background)
|
||||||
|
|
||||||
# Helpers for interactive development
|
# Helpers for interactive development
|
||||||
|
|
||||||
|
def get_state(self) -> list[tuple[Mobject, Mobject]]:
|
||||||
|
return [(mob, mob.copy()) for mob in self.mobjects]
|
||||||
|
|
||||||
|
def restore_state(self, mobject_states: list[tuple[Mobject, Mobject]]):
|
||||||
|
self.mobjects = [mob.become(mob_copy) for mob, mob_copy in mobject_states]
|
||||||
|
|
||||||
def save_state(self) -> None:
|
def save_state(self) -> None:
|
||||||
self.saved_state = [
|
if not self.preview:
|
||||||
(mob, mob.copy())
|
return
|
||||||
for mob in self.mobjects
|
self.redo_stack = []
|
||||||
]
|
self.undo_stack.append(self.get_state())
|
||||||
|
if len(self.undo_stack) > self.max_num_saved_states:
|
||||||
|
self.undo_stack.pop(0)
|
||||||
|
|
||||||
def restore(self) -> None:
|
def undo(self):
|
||||||
if not hasattr(self, "saved_state"):
|
if self.undo_stack:
|
||||||
raise Exception("Trying to restore scene without having saved")
|
self.redo_stack.append(self.get_state())
|
||||||
self.mobjects = []
|
self.restore_state(self.undo_stack.pop())
|
||||||
for mob, mob_state in self.saved_state:
|
self.refresh_static_mobjects()
|
||||||
mob.become(mob_state)
|
|
||||||
self.mobjects.append(mob)
|
|
||||||
|
|
||||||
def save_mobect(self, mobject: Mobject, file_name: str):
|
def redo(self):
|
||||||
directory = self.file_writer.get_saved_mobject_directory()
|
if self.redo_stack:
|
||||||
path = os.path.join(directory, file_name)
|
self.undo_stack.append(self.get_state())
|
||||||
mobject.save_to_file(path)
|
self.restore_state(self.redo_stack.pop())
|
||||||
|
self.refresh_static_mobjects()
|
||||||
|
|
||||||
|
def save_mobject_to_file(self, mobject: Mobject, file_path: str | None = None) -> None:
|
||||||
|
if file_path is None:
|
||||||
|
file_path = self.file_writer.get_saved_mobject_path(mobject)
|
||||||
|
if file_path is None:
|
||||||
|
return
|
||||||
|
mobject.save_to_file(file_path)
|
||||||
|
|
||||||
def load_mobject(self, file_name):
|
def load_mobject(self, file_name):
|
||||||
if os.path.exists(file_name):
|
if os.path.exists(file_name):
|
||||||
|
@ -730,15 +785,16 @@ class Scene(object):
|
||||||
|
|
||||||
if char == RESET_FRAME_KEY:
|
if char == RESET_FRAME_KEY:
|
||||||
self.camera.frame.to_default_state()
|
self.camera.frame.to_default_state()
|
||||||
|
elif char == "z" and modifiers == COMMAND_MODIFIER:
|
||||||
|
self.undo()
|
||||||
|
elif char == "z" and modifiers == COMMAND_MODIFIER | SHIFT_MODIFIER:
|
||||||
|
self.redo()
|
||||||
# command + q
|
# command + q
|
||||||
elif char == QUIT_KEY and modifiers == COMMAND_MODIFIER:
|
elif char == QUIT_KEY and modifiers == COMMAND_MODIFIER:
|
||||||
self.quit_interaction = True
|
self.quit_interaction = True
|
||||||
# Space or right arrow
|
# Space or right arrow
|
||||||
elif char == " " or symbol == ARROW_SYMBOLS[2]:
|
elif char == " " or symbol == ARROW_SYMBOLS[2]:
|
||||||
self.hold_on_wait = False
|
self.hold_on_wait = False
|
||||||
# ctrl + shift + e
|
|
||||||
elif char == EMBED_KEY and modifiers == CTRL_MODIFIER | SHIFT_MODIFIER:
|
|
||||||
self.embed(close_scene_on_exit=False)
|
|
||||||
|
|
||||||
def on_resize(self, width: int, height: int) -> None:
|
def on_resize(self, width: int, height: int) -> None:
|
||||||
self.camera.reset_pixel_shape(width, height)
|
self.camera.reset_pixel_shape(width, height)
|
||||||
|
|
|
@ -1,30 +1,32 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import shutil
|
|
||||||
import platform
|
import platform
|
||||||
|
import shutil
|
||||||
import subprocess as sp
|
import subprocess as sp
|
||||||
|
import sys
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from pydub import AudioSegment
|
from pydub import AudioSegment
|
||||||
from tqdm import tqdm as ProgressDisplay
|
from tqdm import tqdm as ProgressDisplay
|
||||||
|
|
||||||
from manimlib.constants import FFMPEG_BIN
|
from manimlib.constants import FFMPEG_BIN
|
||||||
|
from manimlib.logger import log
|
||||||
|
from manimlib.mobject.mobject import Mobject
|
||||||
from manimlib.utils.config_ops import digest_config
|
from manimlib.utils.config_ops import digest_config
|
||||||
from manimlib.utils.file_ops import guarantee_existence
|
|
||||||
from manimlib.utils.file_ops import add_extension_if_not_present
|
from manimlib.utils.file_ops import add_extension_if_not_present
|
||||||
from manimlib.utils.file_ops import get_sorted_integer_files
|
from manimlib.utils.file_ops import get_sorted_integer_files
|
||||||
|
from manimlib.utils.file_ops import guarantee_existence
|
||||||
from manimlib.utils.sounds import get_full_sound_file_path
|
from manimlib.utils.sounds import get_full_sound_file_path
|
||||||
from manimlib.logger import log
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from manimlib.scene.scene import Scene
|
|
||||||
from manimlib.camera.camera import Camera
|
|
||||||
from PIL.Image import Image
|
from PIL.Image import Image
|
||||||
|
|
||||||
|
from manimlib.camera.camera import Camera
|
||||||
|
from manimlib.scene.scene import Scene
|
||||||
|
|
||||||
|
|
||||||
class SceneFileWriter(object):
|
class SceneFileWriter(object):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
@ -60,7 +62,7 @@ class SceneFileWriter(object):
|
||||||
|
|
||||||
# Output directories and files
|
# Output directories and files
|
||||||
def init_output_directories(self) -> None:
|
def init_output_directories(self) -> None:
|
||||||
out_dir = self.output_directory
|
out_dir = self.output_directory or ""
|
||||||
if self.mirror_module_path:
|
if self.mirror_module_path:
|
||||||
module_dir = self.get_default_module_directory()
|
module_dir = self.get_default_module_directory()
|
||||||
out_dir = os.path.join(out_dir, module_dir)
|
out_dir = os.path.join(out_dir, module_dir)
|
||||||
|
@ -127,6 +129,36 @@ class SceneFileWriter(object):
|
||||||
str(self.scene),
|
str(self.scene),
|
||||||
))
|
))
|
||||||
|
|
||||||
|
def get_saved_mobject_path(self, mobject: Mobject) -> str | None:
|
||||||
|
directory = self.get_saved_mobject_directory()
|
||||||
|
files = os.listdir(directory)
|
||||||
|
default_name = str(mobject) + "_0.mob"
|
||||||
|
index = 0
|
||||||
|
while default_name in files:
|
||||||
|
default_name = default_name.replace(str(index), str(index + 1))
|
||||||
|
index += 1
|
||||||
|
if platform.system() == 'Darwin':
|
||||||
|
cmds = [
|
||||||
|
"osascript", "-e",
|
||||||
|
f"""
|
||||||
|
set chosenfile to (choose file name default name "{default_name}" default location "{directory}")
|
||||||
|
POSIX path of chosenfile
|
||||||
|
""",
|
||||||
|
]
|
||||||
|
process = sp.Popen(cmds, stdout=sp.PIPE)
|
||||||
|
file_path = process.stdout.read().decode("utf-8").split("\n")[0]
|
||||||
|
if not file_path:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
user_name = input(f"Enter mobject file name (default is {default_name}): ")
|
||||||
|
file_path = os.path.join(directory, user_name or default_name)
|
||||||
|
if os.path.exists(file_path) or os.path.exists(file_path + ".mob"):
|
||||||
|
if input(f"{file_path} already exists. Overwrite (y/n)? ") != "y":
|
||||||
|
return
|
||||||
|
if not file_path.endswith(".mob"):
|
||||||
|
file_path = file_path + ".mob"
|
||||||
|
return file_path
|
||||||
|
|
||||||
# Sound
|
# Sound
|
||||||
def init_audio(self) -> None:
|
def init_audio(self) -> None:
|
||||||
self.includes_sound: bool = False
|
self.includes_sound: bool = False
|
||||||
|
|
|
@ -8,7 +8,10 @@ from manimlib.animation.growing import GrowArrow
|
||||||
from manimlib.animation.transform import ApplyFunction
|
from manimlib.animation.transform import ApplyFunction
|
||||||
from manimlib.animation.transform import ApplyPointwiseFunction
|
from manimlib.animation.transform import ApplyPointwiseFunction
|
||||||
from manimlib.animation.transform import Transform
|
from manimlib.animation.transform import Transform
|
||||||
from manimlib.constants import *
|
from manimlib.constants import BLACK, BLUE_D, GREEN_C, RED_C, GREY, WHITE, YELLOW
|
||||||
|
from manimlib.constants import DL, DOWN, ORIGIN, RIGHT, UP
|
||||||
|
from manimlib.constants import FRAME_WIDTH, FRAME_X_RADIUS, FRAME_Y_RADIUS
|
||||||
|
from manimlib.constants import SMALL_BUFF
|
||||||
from manimlib.mobject.coordinate_systems import Axes
|
from manimlib.mobject.coordinate_systems import Axes
|
||||||
from manimlib.mobject.coordinate_systems import NumberPlane
|
from manimlib.mobject.coordinate_systems import NumberPlane
|
||||||
from manimlib.mobject.geometry import Arrow
|
from manimlib.mobject.geometry import Arrow
|
||||||
|
@ -30,6 +33,7 @@ from manimlib.utils.rate_functions import rush_into
|
||||||
from manimlib.utils.space_ops import angle_of_vector
|
from manimlib.utils.space_ops import angle_of_vector
|
||||||
from manimlib.utils.space_ops import get_norm
|
from manimlib.utils.space_ops import get_norm
|
||||||
|
|
||||||
|
|
||||||
X_COLOR = GREEN_C
|
X_COLOR = GREEN_C
|
||||||
Y_COLOR = RED_C
|
Y_COLOR = RED_C
|
||||||
Z_COLOR = BLUE_D
|
Z_COLOR = BLUE_D
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import copy
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import copy
|
|
||||||
from typing import Iterable
|
|
||||||
|
|
||||||
import moderngl
|
import moderngl
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
@ -11,6 +10,12 @@ import numpy as np
|
||||||
from manimlib.utils.directories import get_shader_dir
|
from manimlib.utils.directories import get_shader_dir
|
||||||
from manimlib.utils.file_ops import find_file
|
from manimlib.utils.file_ops import find_file
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import Iterable
|
||||||
|
|
||||||
|
|
||||||
# Mobjects that should be rendered with
|
# Mobjects that should be rendered with
|
||||||
# the same shader will be organized and
|
# the same shader will be organized and
|
||||||
# clumped together based on keeping track
|
# clumped together based on keeping track
|
||||||
|
|
|
@ -1,19 +1,26 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Iterable, Callable, TypeVar, Sequence
|
|
||||||
|
|
||||||
from scipy import linalg
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from scipy import linalg
|
||||||
|
|
||||||
|
from manimlib.logger import log
|
||||||
|
from manimlib.utils.simple_functions import choose
|
||||||
|
from manimlib.utils.space_ops import cross2d
|
||||||
|
from manimlib.utils.space_ops import find_intersection
|
||||||
|
from manimlib.utils.space_ops import midpoint
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import Callable, Iterable, Sequence, TypeVar
|
||||||
|
|
||||||
import numpy.typing as npt
|
import numpy.typing as npt
|
||||||
|
|
||||||
from manimlib.utils.simple_functions import choose
|
T = TypeVar("T")
|
||||||
from manimlib.utils.space_ops import find_intersection
|
|
||||||
from manimlib.utils.space_ops import cross2d
|
|
||||||
from manimlib.utils.space_ops import midpoint
|
|
||||||
from manimlib.logger import log
|
|
||||||
|
|
||||||
CLOSED_THRESHOLD = 0.001
|
CLOSED_THRESHOLD = 0.001
|
||||||
T = TypeVar("T")
|
|
||||||
|
|
||||||
def bezier(
|
def bezier(
|
||||||
points: Iterable[float | np.ndarray]
|
points: Iterable[float | np.ndarray]
|
||||||
|
|
|
@ -1,15 +1,24 @@
|
||||||
import random
|
from __future__ import annotations
|
||||||
|
|
||||||
from colour import Color
|
from colour import Color
|
||||||
|
from colour import hex2rgb
|
||||||
|
from colour import rgb2hex
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from manimlib.constants import WHITE
|
|
||||||
from manimlib.constants import COLORMAP_3B1B
|
from manimlib.constants import COLORMAP_3B1B
|
||||||
|
from manimlib.constants import WHITE
|
||||||
from manimlib.utils.bezier import interpolate
|
from manimlib.utils.bezier import interpolate
|
||||||
from manimlib.utils.iterables import resize_with_interpolation
|
from manimlib.utils.iterables import resize_with_interpolation
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
def color_to_rgb(color):
|
if TYPE_CHECKING:
|
||||||
|
from typing import Iterable, Union
|
||||||
|
|
||||||
|
ManimColor = Union[str, Color]
|
||||||
|
|
||||||
|
|
||||||
|
def color_to_rgb(color: ManimColor) -> np.ndarray:
|
||||||
if isinstance(color, str):
|
if isinstance(color, str):
|
||||||
return hex_to_rgb(color)
|
return hex_to_rgb(color)
|
||||||
elif isinstance(color, Color):
|
elif isinstance(color, Color):
|
||||||
|
@ -18,55 +27,48 @@ def color_to_rgb(color):
|
||||||
raise Exception("Invalid color type")
|
raise Exception("Invalid color type")
|
||||||
|
|
||||||
|
|
||||||
def color_to_rgba(color, alpha=1):
|
def color_to_rgba(color: ManimColor, alpha: float = 1.0) -> np.ndarray:
|
||||||
return np.array([*color_to_rgb(color), alpha])
|
return np.array([*color_to_rgb(color), alpha])
|
||||||
|
|
||||||
|
|
||||||
def rgb_to_color(rgb):
|
def rgb_to_color(rgb: Iterable[float]) -> Color:
|
||||||
try:
|
try:
|
||||||
return Color(rgb=rgb)
|
return Color(rgb=tuple(rgb))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return Color(WHITE)
|
return Color(WHITE)
|
||||||
|
|
||||||
|
|
||||||
def rgba_to_color(rgba):
|
def rgba_to_color(rgba: Iterable[float]) -> Color:
|
||||||
return rgb_to_color(rgba[:3])
|
return rgb_to_color(tuple(rgba)[:3])
|
||||||
|
|
||||||
|
|
||||||
def rgb_to_hex(rgb):
|
def rgb_to_hex(rgb: Iterable[float]) -> str:
|
||||||
return "#" + "".join(
|
return rgb2hex(rgb, force_long=True).upper()
|
||||||
hex(int_x // 16)[2] + hex(int_x % 16)[2]
|
|
||||||
for x in rgb
|
|
||||||
for int_x in [int(255 * x)]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def hex_to_rgb(hex_code):
|
def hex_to_rgb(hex_code: str) -> np.ndarray:
|
||||||
hex_part = hex_code[1:]
|
return np.array(hex2rgb(hex_code))
|
||||||
if len(hex_part) == 3:
|
|
||||||
hex_part = "".join([2 * c for c in hex_part])
|
|
||||||
return np.array([
|
|
||||||
int(hex_part[i:i + 2], 16) / 255
|
|
||||||
for i in range(0, 6, 2)
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
def invert_color(color):
|
def invert_color(color: ManimColor) -> Color:
|
||||||
return rgb_to_color(1.0 - color_to_rgb(color))
|
return rgb_to_color(1.0 - color_to_rgb(color))
|
||||||
|
|
||||||
|
|
||||||
def color_to_int_rgb(color):
|
def color_to_int_rgb(color: ManimColor) -> np.ndarray:
|
||||||
return (255 * color_to_rgb(color)).astype('uint8')
|
return (255 * color_to_rgb(color)).astype('uint8')
|
||||||
|
|
||||||
|
|
||||||
def color_to_int_rgba(color, opacity=1.0):
|
def color_to_int_rgba(color: ManimColor, opacity: float = 1.0) -> np.ndarray:
|
||||||
alpha = int(255 * opacity)
|
alpha = int(255 * opacity)
|
||||||
return np.array([*color_to_int_rgb(color), alpha])
|
return np.array([*color_to_int_rgb(color), alpha])
|
||||||
|
|
||||||
|
|
||||||
def color_gradient(reference_colors, length_of_output):
|
def color_gradient(
|
||||||
|
reference_colors: Iterable[ManimColor],
|
||||||
|
length_of_output: int
|
||||||
|
) -> list[Color]:
|
||||||
if length_of_output == 0:
|
if length_of_output == 0:
|
||||||
return reference_colors[0]
|
return []
|
||||||
rgbs = list(map(color_to_rgb, reference_colors))
|
rgbs = list(map(color_to_rgb, reference_colors))
|
||||||
alphas = np.linspace(0, (len(rgbs) - 1), length_of_output)
|
alphas = np.linspace(0, (len(rgbs) - 1), length_of_output)
|
||||||
floors = alphas.astype('int')
|
floors = alphas.astype('int')
|
||||||
|
@ -80,30 +82,33 @@ def color_gradient(reference_colors, length_of_output):
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def interpolate_color(color1, color2, alpha):
|
def interpolate_color(
|
||||||
|
color1: ManimColor,
|
||||||
|
color2: ManimColor,
|
||||||
|
alpha: float
|
||||||
|
) -> Color:
|
||||||
rgb = interpolate(color_to_rgb(color1), color_to_rgb(color2), alpha)
|
rgb = interpolate(color_to_rgb(color1), color_to_rgb(color2), alpha)
|
||||||
return rgb_to_color(rgb)
|
return rgb_to_color(rgb)
|
||||||
|
|
||||||
|
|
||||||
def average_color(*colors):
|
def average_color(*colors: ManimColor) -> Color:
|
||||||
rgbs = np.array(list(map(color_to_rgb, colors)))
|
rgbs = np.array(list(map(color_to_rgb, colors)))
|
||||||
return rgb_to_color(rgbs.mean(0))
|
return rgb_to_color(rgbs.mean(0))
|
||||||
|
|
||||||
|
|
||||||
def random_bright_color():
|
def random_color() -> Color:
|
||||||
|
return Color(rgb=tuple(np.random.random(3)))
|
||||||
|
|
||||||
|
|
||||||
|
def random_bright_color() -> Color:
|
||||||
color = random_color()
|
color = random_color()
|
||||||
curr_rgb = color_to_rgb(color)
|
return average_color(color, Color(WHITE))
|
||||||
new_rgb = interpolate(
|
|
||||||
curr_rgb, np.ones(len(curr_rgb)), 0.5
|
|
||||||
)
|
|
||||||
return Color(rgb=new_rgb)
|
|
||||||
|
|
||||||
|
|
||||||
def random_color():
|
def get_colormap_list(
|
||||||
return Color(rgb=(random.random() for i in range(3)))
|
map_name: str = "viridis",
|
||||||
|
n_colors: int = 9
|
||||||
|
) -> np.ndarray:
|
||||||
def get_colormap_list(map_name="viridis", n_colors=9):
|
|
||||||
"""
|
"""
|
||||||
Options for map_name:
|
Options for map_name:
|
||||||
3b1b_colormap
|
3b1b_colormap
|
||||||
|
|
|
@ -4,6 +4,7 @@ import tempfile
|
||||||
from manimlib.config import get_custom_config
|
from manimlib.config import get_custom_config
|
||||||
from manimlib.config import get_manim_dir
|
from manimlib.config import get_manim_dir
|
||||||
|
|
||||||
|
|
||||||
CUSTOMIZATION = {}
|
CUSTOMIZATION = {}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import time
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from typing import Callable
|
import time
|
||||||
|
|
||||||
from manimlib.constants import BLACK
|
from manimlib.constants import BLACK
|
||||||
|
from manimlib.logger import log
|
||||||
from manimlib.mobject.numbers import Integer
|
from manimlib.mobject.numbers import Integer
|
||||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||||
from manimlib.logger import log
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@ from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from manimlib.utils.file_ops import guarantee_existence
|
|
||||||
from manimlib.utils.customization import get_customization
|
from manimlib.utils.customization import get_customization
|
||||||
|
from manimlib.utils.file_ops import guarantee_existence
|
||||||
|
|
||||||
|
|
||||||
def get_directories() -> dict[str, str]:
|
def get_directories() -> dict[str, str]:
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Iterable
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from typing import Iterable
|
||||||
|
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from typing import Iterable
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import validators
|
import validators
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import Iterable
|
||||||
|
|
||||||
|
|
||||||
def add_extension_if_not_present(file_name: str, extension: str) -> str:
|
def add_extension_if_not_present(file_name: str, extension: str) -> str:
|
||||||
# This could conceivably be smarter about handling existing differing extensions
|
# This could conceivably be smarter about handling existing differing extensions
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from typing import Iterable
|
|
||||||
|
|
||||||
from manimlib.utils.file_ops import find_file
|
|
||||||
from manimlib.utils.directories import get_raster_image_dir
|
from manimlib.utils.directories import get_raster_image_dir
|
||||||
from manimlib.utils.directories import get_vector_image_dir
|
from manimlib.utils.directories import get_vector_image_dir
|
||||||
|
from manimlib.utils.file_ops import find_file
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import Iterable
|
||||||
|
|
||||||
|
|
||||||
def get_full_raster_image_path(image_file_name: str) -> str:
|
def get_full_raster_image_path(image_file_name: str) -> str:
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import importlib
|
||||||
|
import inspect
|
||||||
import os
|
import os
|
||||||
import yaml
|
import yaml
|
||||||
import inspect
|
|
||||||
import importlib
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
from rich import box
|
from rich import box
|
||||||
|
from rich.console import Console
|
||||||
|
from rich.prompt import Confirm
|
||||||
|
from rich.prompt import Prompt
|
||||||
from rich.rule import Rule
|
from rich.rule import Rule
|
||||||
from rich.table import Table
|
from rich.table import Table
|
||||||
from rich.console import Console
|
|
||||||
from rich.prompt import Prompt, Confirm
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
def get_manim_dir() -> str:
|
def get_manim_dir() -> str:
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import math
|
import math
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
@ -8,6 +9,12 @@ from manimlib.utils.bezier import interpolate
|
||||||
from manimlib.utils.space_ops import get_norm
|
from manimlib.utils.space_ops import get_norm
|
||||||
from manimlib.utils.space_ops import rotation_matrix_transpose
|
from manimlib.utils.space_ops import rotation_matrix_transpose
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
|
||||||
STRAIGHT_PATH_THRESHOLD = 0.01
|
STRAIGHT_PATH_THRESHOLD = 0.01
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
from typing import Callable
|
from __future__ import annotations
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from manimlib.utils.bezier import bezier
|
from manimlib.utils.bezier import bezier
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
|
||||||
def linear(t: float) -> float:
|
def linear(t: float) -> float:
|
||||||
return t
|
return t
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import inspect
|
|
||||||
import numpy as np
|
|
||||||
import math
|
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
|
import inspect
|
||||||
|
import math
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
def sigmoid(x):
|
def sigmoid(x):
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
from manimlib.utils.file_ops import find_file
|
from __future__ import annotations
|
||||||
|
|
||||||
from manimlib.utils.directories import get_sound_dir
|
from manimlib.utils.directories import get_sound_dir
|
||||||
|
from manimlib.utils.file_ops import find_file
|
||||||
|
|
||||||
|
|
||||||
def get_full_sound_file_path(sound_file_name) -> str:
|
def get_full_sound_file_path(sound_file_name: str) -> str:
|
||||||
return find_file(
|
return find_file(
|
||||||
sound_file_name,
|
sound_file_name,
|
||||||
directories=[get_sound_dir()],
|
directories=[get_sound_dir()],
|
||||||
|
|
|
@ -1,25 +1,27 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from functools import reduce
|
||||||
import math
|
import math
|
||||||
import operator as op
|
import operator as op
|
||||||
from functools import reduce
|
|
||||||
from typing import Callable, Iterable, Sequence
|
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
import numpy.typing as npt
|
|
||||||
from mapbox_earcut import triangulate_float32 as earcut
|
from mapbox_earcut import triangulate_float32 as earcut
|
||||||
|
import numpy as np
|
||||||
from scipy.spatial.transform import Rotation
|
from scipy.spatial.transform import Rotation
|
||||||
from tqdm import tqdm as ProgressDisplay
|
from tqdm import tqdm as ProgressDisplay
|
||||||
|
|
||||||
from manimlib.constants import RIGHT
|
from manimlib.constants import DOWN, OUT, RIGHT
|
||||||
from manimlib.constants import DOWN
|
from manimlib.constants import PI, TAU
|
||||||
from manimlib.constants import OUT
|
|
||||||
from manimlib.constants import PI
|
|
||||||
from manimlib.constants import TAU
|
|
||||||
from manimlib.utils.iterables import adjacent_pairs
|
from manimlib.utils.iterables import adjacent_pairs
|
||||||
from manimlib.utils.simple_functions import clip
|
from manimlib.utils.simple_functions import clip
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import Callable, Iterable, Sequence
|
||||||
|
|
||||||
|
import numpy.typing as npt
|
||||||
|
|
||||||
|
|
||||||
def cross(v1: np.ndarray, v2: np.ndarray) -> list[np.ndarray]:
|
def cross(v1: np.ndarray, v2: np.ndarray) -> list[np.ndarray]:
|
||||||
return [
|
return [
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
import sys
|
from __future__ import annotations
|
||||||
import os
|
|
||||||
import hashlib
|
from contextlib import contextmanager
|
||||||
from contextlib import contextmanager
|
import hashlib
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
from manimlib.utils.directories import get_tex_dir
|
|
||||||
from manimlib.config import get_manim_dir
|
|
||||||
from manimlib.config import get_custom_config
|
from manimlib.config import get_custom_config
|
||||||
|
from manimlib.config import get_manim_dir
|
||||||
from manimlib.logger import log
|
from manimlib.logger import log
|
||||||
|
from manimlib.utils.directories import get_tex_dir
|
||||||
|
|
||||||
|
|
||||||
SAVED_TEX_CONFIG = {}
|
SAVED_TEX_CONFIG = {}
|
||||||
|
|
||||||
|
|
||||||
def get_tex_config():
|
def get_tex_config() -> dict[str, str]:
|
||||||
"""
|
"""
|
||||||
Returns a dict which should look something like this:
|
Returns a dict which should look something like this:
|
||||||
{
|
{
|
||||||
|
@ -37,13 +39,13 @@ def get_tex_config():
|
||||||
return SAVED_TEX_CONFIG
|
return SAVED_TEX_CONFIG
|
||||||
|
|
||||||
|
|
||||||
def tex_hash(tex_file_content):
|
def tex_hash(tex_file_content: str) -> int:
|
||||||
# Truncating at 16 bytes for cleanliness
|
# Truncating at 16 bytes for cleanliness
|
||||||
hasher = hashlib.sha256(tex_file_content.encode())
|
hasher = hashlib.sha256(tex_file_content.encode())
|
||||||
return hasher.hexdigest()[:16]
|
return hasher.hexdigest()[:16]
|
||||||
|
|
||||||
|
|
||||||
def tex_to_svg_file(tex_file_content):
|
def tex_to_svg_file(tex_file_content: str) -> str:
|
||||||
svg_file = os.path.join(
|
svg_file = os.path.join(
|
||||||
get_tex_dir(), tex_hash(tex_file_content) + ".svg"
|
get_tex_dir(), tex_hash(tex_file_content) + ".svg"
|
||||||
)
|
)
|
||||||
|
@ -53,7 +55,7 @@ def tex_to_svg_file(tex_file_content):
|
||||||
return svg_file
|
return svg_file
|
||||||
|
|
||||||
|
|
||||||
def tex_to_svg(tex_file_content, svg_file):
|
def tex_to_svg(tex_file_content: str, svg_file: str) -> str:
|
||||||
tex_file = svg_file.replace(".svg", ".tex")
|
tex_file = svg_file.replace(".svg", ".tex")
|
||||||
with open(tex_file, "w", encoding="utf-8") as outfile:
|
with open(tex_file, "w", encoding="utf-8") as outfile:
|
||||||
outfile.write(tex_file_content)
|
outfile.write(tex_file_content)
|
||||||
|
@ -69,7 +71,7 @@ def tex_to_svg(tex_file_content, svg_file):
|
||||||
return svg_file
|
return svg_file
|
||||||
|
|
||||||
|
|
||||||
def tex_to_dvi(tex_file):
|
def tex_to_dvi(tex_file: str) -> str:
|
||||||
tex_config = get_tex_config()
|
tex_config = get_tex_config()
|
||||||
program = tex_config["executable"]
|
program = tex_config["executable"]
|
||||||
file_type = tex_config["intermediate_filetype"]
|
file_type = tex_config["intermediate_filetype"]
|
||||||
|
@ -96,7 +98,7 @@ def tex_to_dvi(tex_file):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def dvi_to_svg(dvi_file, regen_if_exists=False):
|
def dvi_to_svg(dvi_file: str) -> str:
|
||||||
"""
|
"""
|
||||||
Converts a dvi, which potentially has multiple slides, into a
|
Converts a dvi, which potentially has multiple slides, into a
|
||||||
directory full of enumerated pngs corresponding with these slides.
|
directory full of enumerated pngs corresponding with these slides.
|
||||||
|
@ -123,7 +125,7 @@ def dvi_to_svg(dvi_file, regen_if_exists=False):
|
||||||
|
|
||||||
# TODO, perhaps this should live elsewhere
|
# TODO, perhaps this should live elsewhere
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def display_during_execution(message):
|
def display_during_execution(message: str) -> None:
|
||||||
# Only show top line
|
# Only show top line
|
||||||
to_print = message.split("\n")[0]
|
to_print = message.split("\n")[0]
|
||||||
max_characters = os.get_terminal_size().columns - 1
|
max_characters = os.get_terminal_size().columns - 1
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
import moderngl_window as mglw
|
import moderngl_window as mglw
|
||||||
from moderngl_window.context.pyglet.window import Window as PygletWindow
|
from moderngl_window.context.pyglet.window import Window as PygletWindow
|
||||||
from moderngl_window.timers.clock import Timer
|
from moderngl_window.timers.clock import Timer
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
colour
|
colour
|
||||||
numpy
|
ipython
|
||||||
Pillow
|
isosurfaces
|
||||||
scipy
|
manimpango>=0.4.0.post0,<0.5.0
|
||||||
sympy
|
|
||||||
tqdm
|
|
||||||
mapbox-earcut
|
mapbox-earcut
|
||||||
matplotlib
|
matplotlib
|
||||||
moderngl
|
moderngl
|
||||||
moderngl_window
|
moderngl_window
|
||||||
skia-pathops
|
numpy
|
||||||
|
Pillow
|
||||||
pydub
|
pydub
|
||||||
pygments
|
pygments
|
||||||
|
PyOpenGL
|
||||||
pyperclip
|
pyperclip
|
||||||
pyyaml
|
pyyaml
|
||||||
rich
|
rich
|
||||||
|
scipy
|
||||||
screeninfo
|
screeninfo
|
||||||
validators
|
skia-pathops
|
||||||
ipython
|
|
||||||
PyOpenGL
|
|
||||||
manimpango>=0.4.0.post0,<0.5.0
|
|
||||||
isosurfaces
|
|
||||||
svgelements
|
svgelements
|
||||||
|
sympy
|
||||||
|
tqdm
|
||||||
|
validators
|
||||||
|
|
23
setup.cfg
23
setup.cfg
|
@ -30,27 +30,28 @@ packages = find:
|
||||||
include_package_data = True
|
include_package_data = True
|
||||||
install_requires =
|
install_requires =
|
||||||
colour
|
colour
|
||||||
numpy
|
ipython
|
||||||
Pillow
|
isosurfaces
|
||||||
scipy
|
manimpango>=0.4.0.post0,<0.5.0
|
||||||
sympy
|
|
||||||
tqdm
|
|
||||||
mapbox-earcut
|
mapbox-earcut
|
||||||
matplotlib
|
matplotlib
|
||||||
moderngl
|
moderngl
|
||||||
moderngl_window
|
moderngl_window
|
||||||
skia-pathops
|
numpy
|
||||||
|
Pillow
|
||||||
pydub
|
pydub
|
||||||
pygments
|
pygments
|
||||||
|
PyOpenGL
|
||||||
|
pyperclip
|
||||||
pyyaml
|
pyyaml
|
||||||
rich
|
rich
|
||||||
|
scipy
|
||||||
screeninfo
|
screeninfo
|
||||||
validators
|
skia-pathops
|
||||||
ipython
|
|
||||||
PyOpenGL
|
|
||||||
manimpango>=0.4.0.post0,<0.5.0
|
|
||||||
isosurfaces
|
|
||||||
svgelements
|
svgelements
|
||||||
|
sympy
|
||||||
|
tqdm
|
||||||
|
validators
|
||||||
|
|
||||||
[options.entry_points]
|
[options.entry_points]
|
||||||
console_scripts =
|
console_scripts =
|
||||||
|
|
Loading…
Add table
Reference in a new issue