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.window import *
|
||||
|
||||
from manimlib.animation.animation import *
|
||||
from manimlib.animation.composition import *
|
||||
from manimlib.animation.creation import *
|
||||
|
@ -20,17 +22,16 @@ from manimlib.animation.update import *
|
|||
|
||||
from manimlib.camera.camera import *
|
||||
|
||||
from manimlib.window import *
|
||||
|
||||
from manimlib.mobject.boolean_ops import *
|
||||
from manimlib.mobject.coordinate_systems import *
|
||||
from manimlib.mobject.changing import *
|
||||
from manimlib.mobject.coordinate_systems import *
|
||||
from manimlib.mobject.frame import *
|
||||
from manimlib.mobject.functions import *
|
||||
from manimlib.mobject.geometry import *
|
||||
from manimlib.mobject.interactive import *
|
||||
from manimlib.mobject.matrix import *
|
||||
from manimlib.mobject.mobject import *
|
||||
from manimlib.mobject.mobject_update_utils import *
|
||||
from manimlib.mobject.number_line import *
|
||||
from manimlib.mobject.numbers 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.text_mobject 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.point_cloud_mobject import *
|
||||
from manimlib.mobject.types.surface 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.vector_field import *
|
||||
|
||||
from manimlib.scene.scene import *
|
||||
from manimlib.scene.interactive_scene import *
|
||||
from manimlib.scene.scene import *
|
||||
from manimlib.scene.three_d_scene import *
|
||||
|
||||
from manimlib.utils.bezier import *
|
||||
|
@ -62,9 +62,9 @@ from manimlib.utils.config_ops import *
|
|||
from manimlib.utils.customization import *
|
||||
from manimlib.utils.debug import *
|
||||
from manimlib.utils.directories import *
|
||||
from manimlib.utils.file_ops import *
|
||||
from manimlib.utils.images import *
|
||||
from manimlib.utils.iterables import *
|
||||
from manimlib.utils.file_ops import *
|
||||
from manimlib.utils.paths import *
|
||||
from manimlib.utils.rate_functions import *
|
||||
from manimlib.utils.simple_functions import *
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#!/usr/bin/env python
|
||||
import manimlib.config
|
||||
import manimlib.logger
|
||||
import manimlib.extract_scene
|
||||
import manimlib.utils.init_config
|
||||
from manimlib import __version__
|
||||
import manimlib.config
|
||||
import manimlib.extract_scene
|
||||
import manimlib.logger
|
||||
import manimlib.utils.init_config
|
||||
|
||||
|
||||
def main():
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from copy import deepcopy
|
||||
from typing import Callable
|
||||
|
||||
from manimlib.mobject.mobject import _AnimationBuilder
|
||||
from manimlib.mobject.mobject import Mobject
|
||||
|
@ -12,6 +11,8 @@ from manimlib.utils.simple_functions import clip
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Callable
|
||||
|
||||
from manimlib.scene.scene import Scene
|
||||
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
from __future__ import annotations
|
||||
|
||||
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.utils.bezier import integer_interpolate
|
||||
from manimlib.utils.bezier import interpolate
|
||||
|
@ -15,8 +15,10 @@ from manimlib.utils.simple_functions import clip
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from manimlib.scene.scene import Scene
|
||||
from typing import Callable
|
||||
|
||||
from manimlib.mobject.mobject import Mobject
|
||||
from manimlib.scene.scene import Scene
|
||||
|
||||
|
||||
DEFAULT_LAGGED_START_LAG_RATIO = 0.05
|
||||
|
|
|
@ -4,17 +4,17 @@ import numpy as np
|
|||
|
||||
from manimlib.animation.animation import Animation
|
||||
from manimlib.animation.transform import Transform
|
||||
from manimlib.mobject.mobject import Group
|
||||
from manimlib.constants import ORIGIN
|
||||
from manimlib.mobject.mobject import Group
|
||||
from manimlib.utils.bezier import interpolate
|
||||
from manimlib.utils.rate_functions import there_and_back
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from manimlib.scene.scene import Scene
|
||||
from manimlib.mobject.mobject import Mobject
|
||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||
from manimlib.scene.scene import Scene
|
||||
|
||||
|
||||
DEFAULT_FADE_LAG_RATIO = 0
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from manimlib.constants import PI
|
||||
from manimlib.animation.transform import Transform
|
||||
from manimlib.constants import PI
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import numpy as np
|
||||
from manimlib.mobject.mobject import Mobject
|
||||
|
||||
from manimlib.mobject.geometry import Arrow
|
||||
from manimlib.mobject.mobject import Mobject
|
||||
|
||||
|
||||
class GrowFromPoint(Transform):
|
||||
|
|
|
@ -1,40 +1,44 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from typing import Union, Sequence
|
||||
|
||||
import numpy as np
|
||||
|
||||
from manimlib.constants import *
|
||||
from manimlib.animation.animation import Animation
|
||||
from manimlib.animation.movement import Homotopy
|
||||
from manimlib.animation.composition import AnimationGroup
|
||||
from manimlib.animation.composition import Succession
|
||||
from manimlib.animation.creation import ShowCreation
|
||||
from manimlib.animation.creation import ShowPartial
|
||||
from manimlib.animation.fading import FadeOut
|
||||
from manimlib.animation.fading import FadeIn
|
||||
from manimlib.animation.movement import Homotopy
|
||||
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 Dot
|
||||
from manimlib.mobject.geometry import Line
|
||||
from manimlib.mobject.shape_matchers import SurroundingRectangle
|
||||
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.geometry import Line
|
||||
from manimlib.utils.bezier import interpolate
|
||||
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 squish_rate_func
|
||||
from manimlib.utils.rate_functions import there_and_back
|
||||
from manimlib.utils.rate_functions import wiggle
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import colour
|
||||
from colour import Color
|
||||
from typing import Union
|
||||
|
||||
from manimlib.mobject.mobject import Mobject
|
||||
ManimColor = Union[str, colour.Color, Sequence[float]]
|
||||
|
||||
ManimColor = Union[str, Color]
|
||||
|
||||
|
||||
class FocusOn(Transform):
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Callable, Sequence
|
||||
|
||||
from manimlib.animation.animation import Animation
|
||||
from manimlib.utils.rate_functions import linear
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Callable, Sequence
|
||||
|
||||
import numpy as np
|
||||
|
||||
from manimlib.mobject.mobject import Mobject
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Callable
|
||||
|
||||
from manimlib.animation.animation import Animation
|
||||
from manimlib.mobject.numbers import DecimalNumber
|
||||
from manimlib.utils.bezier import interpolate
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Callable
|
||||
|
||||
|
||||
class ChangingDecimal(Animation):
|
||||
CONFIG = {
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from manimlib.animation.animation import Animation
|
||||
from manimlib.constants import OUT
|
||||
from manimlib.constants import PI
|
||||
from manimlib.constants import TAU
|
||||
from manimlib.constants import ORIGIN
|
||||
from manimlib.constants import ORIGIN, OUT
|
||||
from manimlib.constants import PI, TAU
|
||||
from manimlib.utils.rate_functions import linear
|
||||
from manimlib.utils.rate_functions import smooth
|
||||
|
||||
|
@ -12,6 +10,7 @@ from typing import TYPE_CHECKING
|
|||
|
||||
if TYPE_CHECKING:
|
||||
import numpy as np
|
||||
|
||||
from manimlib.mobject.mobject import Mobject
|
||||
|
||||
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
|
||||
from manimlib.animation.composition import LaggedStart
|
||||
from manimlib.animation.transform import Restore
|
||||
from manimlib.constants import WHITE
|
||||
from manimlib.constants import BLACK
|
||||
from manimlib.constants import BLACK, WHITE
|
||||
from manimlib.mobject.geometry import Circle
|
||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||
from manimlib.utils.config_ops import digest_config
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import numpy as np
|
||||
|
||||
|
||||
class Broadcast(LaggedStart):
|
||||
CONFIG = {
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import inspect
|
||||
from typing import Callable, Union, Sequence
|
||||
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
|
||||
from manimlib.animation.animation import Animation
|
||||
from manimlib.constants import DEFAULT_POINTWISE_FUNCTION_RUN_TIME
|
||||
from manimlib.constants import OUT
|
||||
from manimlib.constants import DEGREES
|
||||
from manimlib.constants import OUT
|
||||
from manimlib.mobject.mobject import Group
|
||||
from manimlib.mobject.mobject import Mobject
|
||||
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
|
||||
|
||||
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
|
||||
ManimColor = Union[str, colour.Color, Sequence[float]]
|
||||
|
||||
ManimColor = Union[str, Color]
|
||||
|
||||
|
||||
class Transform(Animation):
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import operator as op
|
||||
from typing import Callable
|
||||
|
||||
from manimlib.animation.animation import Animation
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Callable
|
||||
|
||||
from manimlib.mobject.mobject import Mobject
|
||||
|
||||
|
||||
|
@ -47,10 +46,7 @@ class MaintainPositionRelativeTo(Animation):
|
|||
**kwargs
|
||||
):
|
||||
self.tracked_mobject = tracked_mobject
|
||||
self.diff = op.sub(
|
||||
mobject.get_center(),
|
||||
tracked_mobject.get_center(),
|
||||
)
|
||||
self.diff = mobject.get_center() - tracked_mobject.get_center()
|
||||
super().__init__(mobject, **kwargs)
|
||||
|
||||
def interpolate_mobject(self, alpha: float) -> None:
|
||||
|
|
|
@ -1,19 +1,23 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import moderngl
|
||||
from colour import Color
|
||||
import OpenGL.GL as gl
|
||||
import itertools as it
|
||||
import math
|
||||
|
||||
import itertools as it
|
||||
|
||||
import moderngl
|
||||
import numpy as np
|
||||
from scipy.spatial.transform import Rotation
|
||||
import OpenGL.GL as gl
|
||||
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 Point
|
||||
from manimlib.utils.color import color_to_rgba
|
||||
from manimlib.utils.config_ops import digest_config
|
||||
from manimlib.utils.simple_functions import fdiv
|
||||
from manimlib.utils.space_ops import normalize
|
||||
|
@ -189,10 +193,9 @@ class Camera(object):
|
|||
def __init__(self, ctx: moderngl.Context | None = None, **kwargs):
|
||||
digest_config(self, kwargs, locals())
|
||||
self.rgb_max_val: float = np.iinfo(self.pixel_array_dtype).max
|
||||
self.background_rgba: list[float] = [
|
||||
*Color(self.background_color).get_rgb(),
|
||||
self.background_opacity
|
||||
]
|
||||
self.background_rgba: list[float] = list(color_to_rgba(
|
||||
self.background_color, self.background_opacity
|
||||
))
|
||||
self.init_frame()
|
||||
self.init_context(ctx)
|
||||
self.init_shaders()
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import argparse
|
||||
import colour
|
||||
import inspect
|
||||
from contextlib import contextmanager
|
||||
import importlib
|
||||
import inspect
|
||||
import os
|
||||
from screeninfo import get_monitors
|
||||
import sys
|
||||
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.init_config import init_customization
|
||||
from manimlib.logger import log
|
||||
|
||||
|
||||
__config_file__ = "custom_config.yml"
|
||||
|
@ -117,16 +117,19 @@ def parse_cli():
|
|||
)
|
||||
parser.add_argument(
|
||||
"-n", "--start_at_animation_number",
|
||||
help="Start rendering not from the first animation, but"
|
||||
"from another, specified by its index. If you pass"
|
||||
"in two comma separated values, e.g. \"3,6\", it will end"
|
||||
help="Start rendering not from the first animation, but "
|
||||
"from another, specified by its index. If you pass "
|
||||
"in two comma separated values, e.g. \"3,6\", it will end "
|
||||
"the rendering at the second value",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-e", "--embed", metavar="LINENO",
|
||||
help="Takes a line number as an argument, and results"
|
||||
"in the scene being called as if the line `self.embed()`"
|
||||
"was inserted into the scene code at that line number."
|
||||
"-e", "--embed",
|
||||
nargs="?",
|
||||
const="",
|
||||
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(
|
||||
"-r", "--resolution",
|
||||
|
@ -185,22 +188,70 @@ def get_module(file_name):
|
|||
return module
|
||||
|
||||
|
||||
def get_indent(line: str):
|
||||
return len(line) - len(line.lstrip())
|
||||
|
||||
|
||||
@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:
|
||||
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:
|
||||
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:
|
||||
os.remove(alt_file)
|
||||
with open(file_name, 'w') as fp:
|
||||
fp.writelines(lines)
|
||||
|
||||
|
||||
def get_custom_config():
|
||||
|
@ -296,10 +347,10 @@ def get_configuration(args):
|
|||
"quiet": args.quiet,
|
||||
}
|
||||
|
||||
if args.embed is None:
|
||||
module = get_module(args.file)
|
||||
else:
|
||||
with insert_embed_line(args.file, int(args.embed)) as alt_file:
|
||||
module = get_module(args.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)
|
||||
|
||||
config = {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import numpy as np
|
||||
|
||||
|
||||
# Sizes relevant to default camera frame
|
||||
ASPECT_RATIO = 16.0 / 9.0
|
||||
FRAME_HEIGHT = 8.0
|
||||
|
@ -74,6 +75,7 @@ DEFAULT_STROKE_WIDTH = 4
|
|||
# For keyboard interactions
|
||||
CTRL_SYMBOL = 65508
|
||||
SHIFT_SYMBOL = 65505
|
||||
COMMAND_SYMBOL = 65517
|
||||
DELETE_SYMBOL = 65288
|
||||
ARROW_SYMBOLS = list(range(65361, 65365))
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ from __future__ import annotations
|
|||
|
||||
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_type import EventType
|
||||
|
||||
|
||||
class EventDispatcher(object):
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Callable, TYPE_CHECKING
|
||||
from typing import 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.mobject.mobject import Mobject
|
||||
|
||||
|
||||
class EventListner(object):
|
||||
def __init__(
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import copy
|
||||
import inspect
|
||||
import sys
|
||||
import copy
|
||||
|
||||
from manimlib.scene.scene import Scene
|
||||
from manimlib.config import get_custom_config
|
||||
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):
|
||||
exec(get_custom_config()["universal_import_line"])
|
||||
self.embed()
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import logging
|
||||
|
||||
from rich.logging import RichHandler
|
||||
|
||||
__all__ = ["log"]
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Callable
|
||||
|
||||
import numpy as np
|
||||
|
||||
from manimlib.constants import BLUE_D
|
||||
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.constants import BLUE_B, BLUE_D, BLUE_E, GREY_BROWN, WHITE
|
||||
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 VMobject
|
||||
from manimlib.utils.rate_functions import smooth
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Callable
|
||||
|
||||
|
||||
class AnimatedBoundary(VGroup):
|
||||
CONFIG = {
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
import numbers
|
||||
from abc import abstractmethod
|
||||
from typing import Type, TypeVar, Union, Callable, Iterable, Sequence
|
||||
|
||||
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.geometry import Arrow
|
||||
from manimlib.mobject.geometry import Line
|
||||
from manimlib.mobject.geometry import DashedLine
|
||||
from manimlib.mobject.geometry import Line
|
||||
from manimlib.mobject.geometry import Rectangle
|
||||
from manimlib.mobject.number_line import NumberLine
|
||||
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
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import colour
|
||||
from colour import Color
|
||||
from typing import Callable, Iterable, Sequence, Type, TypeVar, Union
|
||||
|
||||
from manimlib.mobject.mobject import Mobject
|
||||
|
||||
T = TypeVar("T", bound=Mobject)
|
||||
ManimColor = Union[str, colour.Color, Sequence[float]]
|
||||
ManimColor = Union[str, Color]
|
||||
|
||||
|
||||
EPSILON = 1e-8
|
||||
|
||||
|
||||
class CoordinateSystem():
|
||||
class CoordinateSystem(ABC):
|
||||
"""
|
||||
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.utils.config_ops import digest_config
|
||||
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Callable, Sequence
|
||||
|
||||
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.utils.config_ops import digest_config
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Callable, Sequence
|
||||
|
||||
|
||||
class ParametricCurve(VMobject):
|
||||
CONFIG = {
|
||||
|
|
|
@ -2,23 +2,24 @@ from __future__ import annotations
|
|||
|
||||
import math
|
||||
import numbers
|
||||
from typing import Sequence, Union
|
||||
|
||||
import colour
|
||||
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.types.vectorized_mobject import DashedVMobject
|
||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||
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.iterables import adjacent_n_tuples
|
||||
from manimlib.utils.iterables import adjacent_pairs
|
||||
from manimlib.utils.simple_functions import fdiv
|
||||
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_of_vector
|
||||
from manimlib.utils.space_ops import compass_directions
|
||||
from manimlib.utils.space_ops import find_intersection
|
||||
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 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
|
||||
|
@ -716,8 +723,8 @@ class Arrow(Line):
|
|||
|
||||
def set_stroke(
|
||||
self,
|
||||
color: ManimColor | None = None,
|
||||
width: float | None = None,
|
||||
color: ManimColor | Iterable[ManimColor] | None = None,
|
||||
width: float | Iterable[float] | None = None,
|
||||
*args, **kwargs
|
||||
):
|
||||
super().set_stroke(color=color, width=width, *args, **kwargs)
|
||||
|
|
|
@ -1,22 +1,32 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Callable
|
||||
|
||||
import numpy as np
|
||||
from pyglet.window import key as PygletWindowKeys
|
||||
|
||||
from manimlib.constants import FRAME_HEIGHT, FRAME_WIDTH
|
||||
from manimlib.constants import LEFT, RIGHT, UP, DOWN, ORIGIN
|
||||
from manimlib.constants import SMALL_BUFF, MED_SMALL_BUFF, MED_LARGE_BUFF
|
||||
from manimlib.constants import BLACK, GREY_A, GREY_C, RED, GREEN, BLUE, WHITE
|
||||
from manimlib.mobject.mobject import Mobject, Group
|
||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||
from manimlib.mobject.geometry import Dot, Line, Square, Rectangle, RoundedRectangle, Circle
|
||||
from manimlib.constants import DOWN, LEFT, ORIGIN, RIGHT, UP
|
||||
from manimlib.constants import MED_LARGE_BUFF, MED_SMALL_BUFF, SMALL_BUFF
|
||||
from manimlib.constants import BLACK, BLUE, GREEN, GREY_A, GREY_C, RED, WHITE
|
||||
from manimlib.mobject.mobject import Group
|
||||
from manimlib.mobject.mobject import Mobject
|
||||
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.types.vectorized_mobject import VGroup
|
||||
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.space_ops import get_norm, 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_closest_point_on_line
|
||||
from manimlib.utils.space_ops import get_norm
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Callable
|
||||
|
||||
|
||||
# Interactive Mobjects
|
||||
|
@ -336,7 +346,7 @@ class ColorSliders(Group):
|
|||
g = self.g_slider.get_value() / 255
|
||||
b = self.b_slider.get_value() / 255
|
||||
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:
|
||||
rgba = self.get_value()
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import itertools as it
|
||||
from typing import Union, Sequence
|
||||
|
||||
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 Integer
|
||||
from manimlib.mobject.shape_matchers import BackgroundRectangle
|
||||
|
@ -18,9 +18,14 @@ from manimlib.mobject.types.vectorized_mobject import VMobject
|
|||
from typing import 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
|
||||
ManimColor = Union[str, colour.Color, Sequence[float]]
|
||||
|
||||
ManimColor = Union[str, Color]
|
||||
|
||||
|
||||
VECTOR_LABEL_SCALE_FACTOR = 0.8
|
||||
|
|
|
@ -1,50 +1,61 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import sys
|
||||
import random
|
||||
import itertools as it
|
||||
from functools import wraps
|
||||
from typing import Iterable, Callable, Union, Sequence
|
||||
import pickle
|
||||
import itertools as it
|
||||
import os
|
||||
import pickle
|
||||
import random
|
||||
import sys
|
||||
|
||||
import colour
|
||||
import moderngl
|
||||
import numbers
|
||||
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_to_rgb
|
||||
from manimlib.utils.color import get_colormap_list
|
||||
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.iterables import batch_by_property
|
||||
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_preserving_order
|
||||
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 interpolate
|
||||
from manimlib.utils.paths import straight_path
|
||||
from manimlib.utils.simple_functions import get_parameters
|
||||
from manimlib.utils.space_ops import angle_of_vector
|
||||
from manimlib.utils.space_ops import get_norm
|
||||
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
|
||||
|
||||
TimeBasedUpdater = Callable[["Mobject", float], None]
|
||||
NonTimeUpdater = Callable[["Mobject"], None]
|
||||
Updater = Union[TimeBasedUpdater, NonTimeUpdater]
|
||||
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
|
||||
|
||||
TimeBasedUpdater = Callable[["Mobject", float], None]
|
||||
NonTimeUpdater = Callable[["Mobject"], None]
|
||||
Updater = Union[TimeBasedUpdater, NonTimeUpdater]
|
||||
ManimColor = Union[str, Color]
|
||||
|
||||
|
||||
class Mobject(object):
|
||||
|
@ -84,7 +95,8 @@ class Mobject(object):
|
|||
self.locked_data_keys: set[str] = set()
|
||||
self.needs_new_bounding_box: bool = True
|
||||
self._is_animating: bool = False
|
||||
self._is_movable: bool = False
|
||||
self.saved_state = None
|
||||
self.target = None
|
||||
|
||||
self.init_data()
|
||||
self.init_uniforms()
|
||||
|
@ -136,8 +148,10 @@ class Mobject(object):
|
|||
return self
|
||||
|
||||
def set_uniforms(self, uniforms: dict):
|
||||
for key in uniforms:
|
||||
self.uniforms[key] = uniforms[key] # Copy?
|
||||
for key, value in uniforms.items():
|
||||
if isinstance(value, np.ndarray):
|
||||
value = value.copy()
|
||||
self.uniforms[key] = value
|
||||
return self
|
||||
|
||||
@property
|
||||
|
@ -460,66 +474,94 @@ class Mobject(object):
|
|||
self.assemble_family()
|
||||
return self
|
||||
|
||||
# Creating new Mobjects from this one
|
||||
# Copying and serialization
|
||||
|
||||
def replicate(self, n: int) -> Group:
|
||||
return self.get_group_class()(
|
||||
*(self.copy() for x in range(n))
|
||||
)
|
||||
def stash_mobject_pointers(func):
|
||||
@wraps(func)
|
||||
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):
|
||||
"""
|
||||
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
|
||||
@stash_mobject_pointers
|
||||
def serialize(self):
|
||||
return pickle.dumps(self)
|
||||
|
||||
# Copying
|
||||
def deserialize(self, data: bytes):
|
||||
self.become(pickle.loads(data))
|
||||
return self
|
||||
|
||||
def copy(self):
|
||||
self.parents = []
|
||||
def deepcopy(self):
|
||||
try:
|
||||
# Often faster than deepcopy
|
||||
return pickle.loads(pickle.dumps(self))
|
||||
except AttributeError:
|
||||
return copy.deepcopy(self)
|
||||
|
||||
def deepcopy(self):
|
||||
# This used to be different from copy, so is now just here for backward compatibility
|
||||
return self.copy()
|
||||
@stash_mobject_pointers
|
||||
def copy(self, deep: bool = False):
|
||||
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):
|
||||
# TODO, remove now pointless use_deepcopy arg
|
||||
self.target = None # Prevent exponential explosion
|
||||
self.target = self.copy()
|
||||
self.target = self.copy(deep=use_deepcopy)
|
||||
self.target.saved_state = self.saved_state
|
||||
return self.target
|
||||
|
||||
def save_state(self, use_deepcopy: bool = False):
|
||||
# TODO, remove now pointless use_deepcopy arg
|
||||
if hasattr(self, "saved_state"):
|
||||
# Prevent exponential growth of data
|
||||
self.saved_state = None
|
||||
self.saved_state = self.copy()
|
||||
self.saved_state = self.copy(deep=use_deepcopy)
|
||||
self.saved_state.target = self.target
|
||||
return 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")
|
||||
self.become(self.saved_state)
|
||||
return self
|
||||
|
||||
def save_to_file(self, file_path):
|
||||
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
|
||||
def save_to_file(self, file_path: str, supress_overwrite_warning: bool = False):
|
||||
with open(file_path, "wb") as fp:
|
||||
pickle.dump(self, fp)
|
||||
fp.write(self.serialize())
|
||||
log.info(f"Saved mobject to {file_path}")
|
||||
return self
|
||||
|
||||
|
@ -532,6 +574,39 @@ class Mobject(object):
|
|||
mobject = pickle.load(fp)
|
||||
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
|
||||
|
||||
def init_updaters(self):
|
||||
|
@ -634,21 +709,13 @@ class Mobject(object):
|
|||
# Check if mark as static or not for camera
|
||||
|
||||
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:
|
||||
for mob in self.get_family(recurse):
|
||||
mob._is_animating = is_animating
|
||||
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
|
||||
|
||||
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
|
||||
respect to that point.
|
||||
"""
|
||||
if isinstance(scale_factor, Iterable):
|
||||
scale_factor = np.array(scale_factor).clip(min=min_scale_factor)
|
||||
else:
|
||||
if isinstance(scale_factor, numbers.Number):
|
||||
scale_factor = max(scale_factor, min_scale_factor)
|
||||
else:
|
||||
scale_factor = np.array(scale_factor).clip(min=min_scale_factor)
|
||||
self.apply_points_function(
|
||||
lambda points: scale_factor * points,
|
||||
about_point=about_point,
|
||||
|
@ -1064,8 +1131,8 @@ class Mobject(object):
|
|||
|
||||
def set_rgba_array_by_color(
|
||||
self,
|
||||
color: ManimColor | None = None,
|
||||
opacity: float | None = None,
|
||||
color: ManimColor | Iterable[ManimColor] | None = None,
|
||||
opacity: float | Iterable[float] | None = None,
|
||||
name: str = "rgbas",
|
||||
recurse: bool = True
|
||||
):
|
||||
|
@ -1087,7 +1154,12 @@ class Mobject(object):
|
|||
mob.data[name][:, 3] = resize_array(opacities, size)
|
||||
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)
|
||||
# Recurse to submobjects differently from how set_rgba_array_by_color
|
||||
# in case they implement set_color differently
|
||||
|
@ -1096,7 +1168,11 @@ class Mobject(object):
|
|||
submob.set_color(color, recurse=True)
|
||||
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)
|
||||
if recurse:
|
||||
for submob in self.submobjects:
|
||||
|
@ -1203,7 +1279,7 @@ class Mobject(object):
|
|||
bb = self.get_bounding_box()
|
||||
return np.array([
|
||||
[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:
|
||||
|
@ -1519,18 +1595,6 @@ class Mobject(object):
|
|||
"""
|
||||
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
|
||||
|
||||
def lock_data(self, keys: Iterable[str]):
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import inspect
|
||||
from typing import Callable
|
||||
|
||||
from manimlib.constants import DEGREES
|
||||
from manimlib.constants import RIGHT
|
||||
|
@ -11,7 +10,10 @@ from manimlib.utils.simple_functions import clip
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Callable
|
||||
|
||||
import numpy as np
|
||||
|
||||
from manimlib.animation.animation import Animation
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
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.numbers import DecimalNumber
|
||||
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.simple_functions import fdiv
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Iterable, Sequence
|
||||
|
||||
|
||||
class NumberLine(Line):
|
||||
CONFIG = {
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
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.text_mobject import Text
|
||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||
|
||||
T = TypeVar("T", bound=VMobject)
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Type, TypeVar
|
||||
|
||||
T = TypeVar("T", bound=VMobject)
|
||||
|
||||
|
||||
class DecimalNumber(VMobject):
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Iterable, Union, Sequence
|
||||
import colour
|
||||
|
||||
from manimlib.constants import *
|
||||
from manimlib.constants import BLUE, BLUE_E, GREEN_E, GREY_B, GREY_D, MAROON_B, YELLOW
|
||||
from manimlib.constants import DOWN, LEFT, RIGHT, UP
|
||||
from manimlib.constants import MED_LARGE_BUFF, MED_SMALL_BUFF, SMALL_BUFF
|
||||
from manimlib.mobject.geometry import Line
|
||||
from manimlib.mobject.geometry import Rectangle
|
||||
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.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
|
||||
|
||||
|
|
|
@ -1,20 +1,25 @@
|
|||
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 Rectangle
|
||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||
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.customization import get_customization
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Union, Sequence
|
||||
from typing import Union
|
||||
|
||||
from manimlib.mobject.mobject import Mobject
|
||||
ManimColor = Union[str, Color, Sequence[float]]
|
||||
|
||||
ManimColor = Union[str, Color]
|
||||
|
||||
|
||||
class SurroundingRectangle(Rectangle):
|
||||
|
|
|
@ -2,27 +2,32 @@ from __future__ import annotations
|
|||
|
||||
import math
|
||||
import copy
|
||||
from typing import Iterable
|
||||
|
||||
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.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 Tex
|
||||
from manimlib.mobject.svg.tex_mobject import TexText
|
||||
from manimlib.mobject.svg.text_mobject import Text
|
||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||
from manimlib.utils.config_ops import digest_config
|
||||
from manimlib.utils.iterables import listify
|
||||
from manimlib.utils.space_ops import get_norm
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from manimlib.mobject.mobject import Mobject
|
||||
from typing import Iterable
|
||||
|
||||
from manimlib.animation.animation import Animation
|
||||
from manimlib.mobject.mobject import Mobject
|
||||
|
||||
|
||||
class Brace(SingleStringTex):
|
||||
CONFIG = {
|
||||
|
@ -113,8 +118,8 @@ class BraceLabel(VMobject):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
obj: list[VMobject] | Mobject,
|
||||
text: Iterable[str] | str,
|
||||
obj: VMobject | list[VMobject],
|
||||
text: str | Iterable[str],
|
||||
brace_direction: np.ndarray = DOWN,
|
||||
**kwargs
|
||||
) -> None:
|
||||
|
@ -124,12 +129,8 @@ class BraceLabel(VMobject):
|
|||
obj = VMobject(*obj)
|
||||
self.brace = Brace(obj, brace_direction, **kwargs)
|
||||
|
||||
if isinstance(text, Iterable):
|
||||
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 = self.label_constructor(*listify(text), **kwargs)
|
||||
self.label.scale(self.label_scale)
|
||||
|
||||
self.brace.put_at_tip(self.label, buff=self.label_buff)
|
||||
self.set_submobjects([self.brace, self.label])
|
||||
|
@ -137,11 +138,11 @@ class BraceLabel(VMobject):
|
|||
def creation_anim(
|
||||
self,
|
||||
label_anim: Animation = FadeIn,
|
||||
brace_anim: Animation=GrowFromCenter
|
||||
brace_anim: Animation = GrowFromCenter
|
||||
) -> AnimationGroup:
|
||||
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):
|
||||
obj = VMobject(*obj)
|
||||
self.brace = Brace(obj, self.brace_direction, **kwargs)
|
||||
|
@ -158,7 +159,7 @@ class BraceLabel(VMobject):
|
|||
self.submobjects[1] = self.label
|
||||
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.change_label(*text)
|
||||
return self
|
||||
|
|
|
@ -20,8 +20,8 @@ from manimlib.utils.config_ops import digest_config
|
|||
from manimlib.utils.rate_functions import linear
|
||||
from manimlib.utils.space_ops import angle_of_vector
|
||||
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 rotate_vector
|
||||
|
||||
|
||||
class Checkmark(TexText):
|
||||
|
|
|
@ -1,27 +1,24 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import hashlib
|
||||
import itertools as it
|
||||
from typing import Callable
|
||||
import os
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
import svgelements as se
|
||||
import numpy as np
|
||||
import svgelements as se
|
||||
|
||||
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 Line
|
||||
from manimlib.mobject.geometry import Polygon
|
||||
from manimlib.mobject.geometry import Polyline
|
||||
from manimlib.mobject.geometry import Rectangle
|
||||
from manimlib.mobject.geometry import RoundedRectangle
|
||||
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.images import get_full_vector_image_path
|
||||
from manimlib.utils.iterables import hash_obj
|
||||
from manimlib.logger import log
|
||||
|
||||
|
||||
SVG_HASH_TO_MOB_MAP: dict[int, VMobject] = {}
|
||||
|
@ -199,9 +196,9 @@ class SVGMobject(VMobject):
|
|||
) -> VMobject:
|
||||
mob.set_style(
|
||||
stroke_width=shape.stroke_width,
|
||||
stroke_color=shape.stroke.hex,
|
||||
stroke_color=shape.stroke.hexrgb,
|
||||
stroke_opacity=shape.stroke.opacity,
|
||||
fill_color=shape.fill.hex,
|
||||
fill_color=shape.fill.hexrgb,
|
||||
fill_opacity=shape.fill.opacity
|
||||
)
|
||||
return mob
|
||||
|
|
|
@ -1,21 +1,28 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Iterable, Sequence, Union
|
||||
from functools import reduce
|
||||
import operator as op
|
||||
import colour
|
||||
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.svg.svg_mobject import SVGMobject
|
||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||
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 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
|
||||
|
|
|
@ -2,19 +2,23 @@ from __future__ import annotations
|
|||
|
||||
import math
|
||||
|
||||
from manimlib.constants import *
|
||||
from manimlib.mobject.types.surface import Surface
|
||||
import numpy as np
|
||||
|
||||
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 Surface
|
||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||
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 Square
|
||||
from manimlib.utils.bezier import interpolate
|
||||
from manimlib.utils.config_ops import digest_config
|
||||
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 z_to_vector
|
||||
from manimlib.utils.space_ops import compass_directions
|
||||
|
||||
|
||||
class SurfaceMesh(VGroup):
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
import moderngl
|
||||
import numpy as np
|
||||
|
||||
from manimlib.constants import GREY_C
|
||||
from manimlib.constants import YELLOW
|
||||
from manimlib.constants import GREY_C, YELLOW
|
||||
from manimlib.constants import ORIGIN
|
||||
from manimlib.mobject.types.point_cloud_mobject import PMobject
|
||||
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_GLOW_DOT_RADIUS = 0.2
|
||||
|
|
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
|||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
from manimlib.constants import *
|
||||
from manimlib.constants import DL, DR, UL, UR
|
||||
from manimlib.mobject.mobject import Mobject
|
||||
from manimlib.utils.bezier import inverse_interpolate
|
||||
from manimlib.utils.images import get_full_raster_image_path
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Callable, Sequence, Union
|
||||
|
||||
import colour
|
||||
import numpy.typing as npt
|
||||
|
||||
from manimlib.constants import *
|
||||
from manimlib.constants import BLACK
|
||||
from manimlib.constants import ORIGIN
|
||||
from manimlib.mobject.mobject import Mobject
|
||||
from manimlib.utils.color import color_gradient
|
||||
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_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):
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Iterable, Callable
|
||||
|
||||
import moderngl
|
||||
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.utils.bezier import integer_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
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Callable, Iterable
|
||||
|
||||
import numpy.typing as npt
|
||||
|
||||
from manimlib.camera.camera import Camera
|
||||
|
||||
|
||||
|
|
|
@ -1,30 +1,36 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import operator as op
|
||||
from functools import reduce
|
||||
from functools import wraps
|
||||
import itertools as it
|
||||
from functools import reduce, wraps
|
||||
from typing import Iterable, Sequence, Callable, Union
|
||||
import operator as op
|
||||
|
||||
import colour
|
||||
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 Point
|
||||
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_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 inverse_interpolate
|
||||
from manimlib.utils.bezier import integer_interpolate
|
||||
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.iterables import listify
|
||||
from manimlib.utils.iterables import make_even
|
||||
from manimlib.utils.iterables import resize_array
|
||||
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 cross2d
|
||||
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.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):
|
||||
|
@ -130,8 +143,8 @@ class VMobject(Mobject):
|
|||
|
||||
def set_fill(
|
||||
self,
|
||||
color: ManimColor | None = None,
|
||||
opacity: float | None = None,
|
||||
color: ManimColor | Iterable[ManimColor] | None = None,
|
||||
opacity: float | Iterable[float] | None = None,
|
||||
recurse: bool = True
|
||||
):
|
||||
self.set_rgba_array_by_color(color, opacity, 'fill_rgba', recurse)
|
||||
|
@ -139,9 +152,9 @@ class VMobject(Mobject):
|
|||
|
||||
def set_stroke(
|
||||
self,
|
||||
color: ManimColor | None = None,
|
||||
width: float | npt.ArrayLike | None = None,
|
||||
opacity: float | None = None,
|
||||
color: ManimColor | Iterable[ManimColor] | None = None,
|
||||
width: float | Iterable[float] | None = None,
|
||||
opacity: float | Iterable[float] | None = None,
|
||||
background: bool | None = None,
|
||||
recurse: bool = True
|
||||
):
|
||||
|
@ -162,8 +175,8 @@ class VMobject(Mobject):
|
|||
|
||||
def set_backstroke(
|
||||
self,
|
||||
color: ManimColor = BLACK,
|
||||
width: float | npt.ArrayLike = 3,
|
||||
color: ManimColor | Iterable[ManimColor] = BLACK,
|
||||
width: float | Iterable[float] = 3,
|
||||
background: bool = True
|
||||
):
|
||||
self.set_stroke(color, width, background=background)
|
||||
|
@ -177,13 +190,13 @@ class VMobject(Mobject):
|
|||
|
||||
def set_style(
|
||||
self,
|
||||
fill_color: ManimColor | None = None,
|
||||
fill_opacity: float | None = None,
|
||||
fill_color: ManimColor | Iterable[ManimColor] | None = None,
|
||||
fill_opacity: float | Iterable[float] | None = None,
|
||||
fill_rgba: npt.ArrayLike | None = None,
|
||||
stroke_color: ManimColor | None = None,
|
||||
stroke_opacity: float | None = None,
|
||||
stroke_color: ManimColor | Iterable[ManimColor] | None = None,
|
||||
stroke_opacity: float | Iterable[float] | 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,
|
||||
reflectiveness: float | None = None,
|
||||
gloss: float | None = None,
|
||||
|
@ -247,12 +260,21 @@ class VMobject(Mobject):
|
|||
sm1.match_style(sm2)
|
||||
return self
|
||||
|
||||
def set_color(self, color: ManimColor, recurse: bool = True):
|
||||
self.set_fill(color, recurse=recurse)
|
||||
self.set_stroke(color, recurse=recurse)
|
||||
def set_color(
|
||||
self,
|
||||
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
|
||||
|
||||
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_stroke(opacity=opacity, recurse=recurse)
|
||||
return self
|
||||
|
@ -1174,3 +1196,24 @@ class DashedVMobject(VMobject):
|
|||
# Family is already taken care of by get_subcurve
|
||||
# implementation
|
||||
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
|
||||
|
||||
import itertools as it
|
||||
import random
|
||||
from typing import Sequence, TypeVar, Callable, Iterable
|
||||
|
||||
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.indication import VShowPassingFlash
|
||||
from manimlib.mobject.geometry import Arrow
|
||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||
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 inverse_interpolate
|
||||
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 merge_dicts_recursively
|
||||
from manimlib.utils.rate_functions import linear
|
||||
from manimlib.utils.simple_functions import sigmoid
|
||||
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
|
||||
|
||||
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.mobject import Mobject
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
|
@ -299,7 +302,7 @@ class AnimatedStreamLines(VGroup):
|
|||
**self.line_anim_config,
|
||||
)
|
||||
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_updater(lambda m, dt: m.update(dt))
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from functools import reduce
|
||||
import random
|
||||
|
||||
from manimlib.constants import *
|
||||
# from manimlib.for_3b1b_videos.pi_creature import PiCreature
|
||||
|
|
|
@ -1,41 +1,43 @@
|
|||
import numpy as np
|
||||
import itertools as it
|
||||
import numpy as np
|
||||
import pyperclip
|
||||
import os
|
||||
import platform
|
||||
|
||||
from manimlib.animation.fading import FadeIn
|
||||
from manimlib.constants import MANIM_COLORS, WHITE, YELLOW
|
||||
from manimlib.constants import ORIGIN, UP, DOWN, LEFT, RIGHT, DL, UL, UR, DR
|
||||
from manimlib.constants import ARROW_SYMBOLS, CTRL_SYMBOL, DELETE_SYMBOL, SHIFT_SYMBOL
|
||||
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 SHIFT_SYMBOL, DELETE_SYMBOL, ARROW_SYMBOLS
|
||||
from manimlib.constants import SHIFT_MODIFIER, COMMAND_MODIFIER
|
||||
from manimlib.mobject.mobject import Mobject
|
||||
from manimlib.constants import MANIM_COLORS, WHITE, GREY_C
|
||||
from manimlib.mobject.geometry import Rectangle
|
||||
from manimlib.mobject.geometry import Square
|
||||
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.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.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.utils.tex_file_writing import LatexError
|
||||
from manimlib.utils.family_ops import extract_mobject_family_members
|
||||
from manimlib.utils.space_ops import get_norm
|
||||
from manimlib.logger import log
|
||||
from manimlib.utils.tex_file_writing import LatexError
|
||||
|
||||
|
||||
SELECT_KEY = 's'
|
||||
GRAB_KEY = 'g'
|
||||
HORIZONTAL_GRAB_KEY = 'h'
|
||||
VERTICAL_GRAB_KEY = 'v'
|
||||
X_GRAB_KEY = 'h'
|
||||
Y_GRAB_KEY = 'v'
|
||||
GRAB_KEYS = [GRAB_KEY, X_GRAB_KEY, Y_GRAB_KEY]
|
||||
RESIZE_KEY = 't'
|
||||
COLOR_KEY = 'c'
|
||||
CURSOR_LOCATION_KEY = 'l'
|
||||
|
||||
|
||||
# Note, a lot of the functionality here is still buggy and very much a work in progress.
|
||||
|
||||
|
||||
class InteractiveScene(Scene):
|
||||
"""
|
||||
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
|
||||
colors = MANIM_COLORS
|
||||
selection_nudge_size = 0.05
|
||||
cursor_location_config = dict(
|
||||
font_size=14,
|
||||
fill_color=GREY_C,
|
||||
num_decimal_places=3,
|
||||
)
|
||||
|
||||
def setup(self):
|
||||
self.selection = Group()
|
||||
self.selection_highlight = Group()
|
||||
self.selection_rectangle = self.get_selection_rectangle()
|
||||
self.color_palette = self.get_color_palette()
|
||||
self.cursor_location_label = self.get_cursor_location_label()
|
||||
self.unselectables = [
|
||||
self.selection,
|
||||
self.selection_highlight,
|
||||
self.selection_rectangle,
|
||||
self.cursor_location_label,
|
||||
self.camera.frame
|
||||
]
|
||||
self.saved_selection_state = []
|
||||
self.select_top_level_mobs = True
|
||||
self.regenerate_selection_search_set()
|
||||
|
||||
self.is_selecting = False
|
||||
self.is_grabbing = False
|
||||
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):
|
||||
self.select_top_level_mobs = not self.select_top_level_mobs
|
||||
self.refresh_selection_scope()
|
||||
self.regenerate_selection_search_set()
|
||||
|
||||
def get_selection_search_set(self):
|
||||
mobs = [m for m in self.mobjects if m not in self.unselectables]
|
||||
def get_selection_search_set(self) -> list[Mobject]:
|
||||
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:
|
||||
return mobs
|
||||
self.selection_search_set = selectable
|
||||
else:
|
||||
return [
|
||||
self.selection_search_set = [
|
||||
submob
|
||||
for mob in mobs
|
||||
for mob in selectable
|
||||
for submob in mob.family_members_with_points()
|
||||
]
|
||||
|
||||
|
@ -116,37 +184,7 @@ class InteractiveScene(Scene):
|
|||
)
|
||||
self.refresh_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 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):
|
||||
def get_corner_dots(self, mobject: Mobject) -> Mobject:
|
||||
dots = DotCloud(**self.corner_dot_config)
|
||||
radius = self.corner_dot_config["radius"]
|
||||
if mobject.get_depth() < 1e-2:
|
||||
|
@ -159,9 +197,11 @@ class InteractiveScene(Scene):
|
|||
]))
|
||||
return dots
|
||||
|
||||
def get_highlight(self, mobject):
|
||||
if isinstance(mobject, VMobject) and mobject.has_points():
|
||||
return self.get_stroke_highlight(mobject)
|
||||
def get_highlight(self, mobject: Mobject) -> Mobject:
|
||||
if isinstance(mobject, VMobject) and mobject.has_points() and not self.select_top_level_mobs:
|
||||
result = VHighlight(mobject)
|
||||
result.add_updater(lambda m: m.replace(mobject))
|
||||
return result
|
||||
else:
|
||||
return self.get_corner_dots(mobject)
|
||||
|
||||
|
@ -171,40 +211,54 @@ class InteractiveScene(Scene):
|
|||
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):
|
||||
mobs = list(filter(lambda m: m not in self.unselectables, mobjects))
|
||||
self.selection.add(*mobjects)
|
||||
mobs = list(filter(
|
||||
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.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):
|
||||
for mob in mobjects:
|
||||
if mob in self.selection:
|
||||
self.selection.remove(mob)
|
||||
mob.set_animating_status(False)
|
||||
else:
|
||||
self.add_to_selection(mob)
|
||||
self.refresh_selection_highlight()
|
||||
|
||||
def clear_selection(self):
|
||||
for mob in self.selection:
|
||||
mob.set_animating_status(False)
|
||||
self.selection.set_submobjects([])
|
||||
self.selection_highlight.set_submobjects([])
|
||||
self.refresh_static_mobjects()
|
||||
|
||||
def add(self, *new_mobjects: Mobject):
|
||||
for mob in new_mobjects:
|
||||
mob.make_movable()
|
||||
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):
|
||||
ids = map(id, self.selection)
|
||||
|
@ -218,11 +272,11 @@ class InteractiveScene(Scene):
|
|||
mobs = map(self.id_to_mobject, ids)
|
||||
mob_copies = [m.copy() for m in mobs if m is not None]
|
||||
self.clear_selection()
|
||||
self.add_to_selection(*mob_copies)
|
||||
self.play(*(
|
||||
FadeIn(mc, run_time=0.5, scale=1.5)
|
||||
for mc in mob_copies
|
||||
))
|
||||
self.add_to_selection(*mob_copies)
|
||||
return
|
||||
except ValueError:
|
||||
pass
|
||||
|
@ -242,41 +296,27 @@ class InteractiveScene(Scene):
|
|||
self.remove(*self.selection)
|
||||
self.clear_selection()
|
||||
|
||||
def saved_selection_to_file(self):
|
||||
directory = self.file_writer.get_saved_mobject_directory()
|
||||
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)
|
||||
def restore_state(self, mobject_states: list[tuple[Mobject, Mobject]]):
|
||||
super().restore_state(mobject_states)
|
||||
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):
|
||||
center = self.selection.get_center()
|
||||
mp = self.mouse_point.get_center()
|
||||
|
@ -286,136 +326,173 @@ class InteractiveScene(Scene):
|
|||
self.scale_about_point = center
|
||||
self.scale_ref_vect = mp - self.scale_about_point
|
||||
self.scale_ref_width = self.selection.get_width()
|
||||
self.scale_ref_height = self.selection.get_height()
|
||||
|
||||
# Event handlers
|
||||
def toggle_color_palette(self):
|
||||
if len(self.selection) == 0:
|
||||
return
|
||||
if self.color_palette not in self.mobjects:
|
||||
self.save_state()
|
||||
self.add(self.color_palette)
|
||||
else:
|
||||
self.remove(self.color_palette)
|
||||
|
||||
def group_selection(self):
|
||||
group = self.get_group(*self.selection)
|
||||
self.add(group)
|
||||
self.clear_selection()
|
||||
self.add_to_selection(group)
|
||||
|
||||
def ungroup_selection(self):
|
||||
pieces = []
|
||||
for mob in list(self.selection):
|
||||
self.remove(mob)
|
||||
pieces.extend(list(mob))
|
||||
self.clear_selection()
|
||||
self.add(*pieces)
|
||||
self.add_to_selection(*pieces)
|
||||
|
||||
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)
|
||||
# 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
|
||||
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)
|
||||
# Show color palette
|
||||
elif char == COLOR_KEY and modifiers == 0:
|
||||
if len(self.selection) == 0:
|
||||
return
|
||||
if self.color_palette not in self.mobjects:
|
||||
self.add(self.color_palette)
|
||||
else:
|
||||
self.remove(self.color_palette)
|
||||
# Command + c -> Copy mobject ids to clipboard
|
||||
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()
|
||||
# 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)
|
||||
self.add(group)
|
||||
self.clear_selection()
|
||||
self.add_to_selection(group)
|
||||
# Command + shift + g -> Ungroup the selection
|
||||
self.group_selection()
|
||||
elif char == "g" and modifiers == COMMAND_MODIFIER | SHIFT_MODIFIER:
|
||||
pieces = []
|
||||
for mob in list(self.selection):
|
||||
self.remove(mob)
|
||||
pieces.extend(list(mob))
|
||||
self.clear_selection()
|
||||
self.add(*pieces)
|
||||
self.add_to_selection(*pieces)
|
||||
# Command + t -> Toggle selection mode
|
||||
self.ungroup_selection()
|
||||
elif char == "t" and modifiers == COMMAND_MODIFIER:
|
||||
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:
|
||||
self.saved_selection_to_file()
|
||||
# Keyboard movements
|
||||
self.save_selection_to_file()
|
||||
elif symbol in ARROW_SYMBOLS:
|
||||
nudge = self.selection_nudge_size
|
||||
if (modifiers & SHIFT_MODIFIER):
|
||||
nudge *= 10
|
||||
vect = [LEFT, UP, RIGHT, DOWN][ARROW_SYMBOLS.index(symbol)]
|
||||
self.selection.shift(nudge * vect)
|
||||
self.nudge_selection(
|
||||
vect=[LEFT, UP, RIGHT, DOWN][ARROW_SYMBOLS.index(symbol)],
|
||||
large=(modifiers & SHIFT_MODIFIER),
|
||||
)
|
||||
|
||||
# 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:
|
||||
super().on_key_release(symbol, modifiers)
|
||||
if chr(symbol) == SELECT_KEY:
|
||||
self.is_selecting = False
|
||||
self.remove(self.selection_rectangle)
|
||||
for mob in reversed(self.get_selection_search_set()):
|
||||
if mob.is_movable() and self.selection_rectangle.is_touching(mob):
|
||||
self.add_to_selection(mob)
|
||||
self.gather_new_selection()
|
||||
if chr(symbol) in GRAB_KEYS:
|
||||
self.is_grabbing = False
|
||||
elif chr(symbol) == CURSOR_LOCATION_KEY:
|
||||
self.remove(self.cursor_location_label)
|
||||
elif symbol == SHIFT_SYMBOL and self.window.is_key_pressed(ord(RESIZE_KEY)):
|
||||
self.prepare_resizing(about_corner=False)
|
||||
|
||||
elif symbol == SHIFT_SYMBOL:
|
||||
if self.window.is_key_pressed(ord(RESIZE_KEY)):
|
||||
self.prepare_resizing(about_corner=False)
|
||||
# Mouse actions
|
||||
def handle_grabbing(self, point: np.ndarray):
|
||||
diff = point - self.mouse_to_selection
|
||||
if self.window.is_key_pressed(ord(GRAB_KEY)):
|
||||
self.selection.move_to(diff)
|
||||
elif self.window.is_key_pressed(ord(X_GRAB_KEY)):
|
||||
self.selection.set_x(diff[0])
|
||||
elif self.window.is_key_pressed(ord(Y_GRAB_KEY)):
|
||||
self.selection.set_y(diff[1])
|
||||
|
||||
def on_mouse_motion(self, point: np.ndarray, d_point: np.ndarray) -> None:
|
||||
super().on_mouse_motion(point, d_point)
|
||||
# Move selection
|
||||
if self.window.is_key_pressed(ord("g")):
|
||||
self.selection.move_to(point - self.mouse_to_selection)
|
||||
# Move selection restricted to horizontal
|
||||
elif self.window.is_key_pressed(ord("h")):
|
||||
self.selection.set_x((point - self.mouse_to_selection)[0])
|
||||
# Move selection restricted to vertical
|
||||
elif self.window.is_key_pressed(ord("v")):
|
||||
self.selection.set_y((point - self.mouse_to_selection)[1])
|
||||
# Scale selection
|
||||
elif self.window.is_key_pressed(ord("t")):
|
||||
# TODO, allow for scaling about the opposite corner
|
||||
vect = point - self.scale_about_point
|
||||
def handle_resizing(self, point: np.ndarray):
|
||||
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)
|
||||
self.selection.set_width(
|
||||
scalar * self.scale_ref_width,
|
||||
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:
|
||||
super().on_mouse_release(point, button, mods)
|
||||
if self.color_palette in self.mobjects:
|
||||
# Search through all mobject on the screne, not just the palette
|
||||
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)
|
||||
self.choose_color(point)
|
||||
elif self.window.is_key_pressed(SHIFT_SYMBOL):
|
||||
mob = self.point_to_mobject(point)
|
||||
if mob is not None:
|
||||
self.toggle_from_selection(mob)
|
||||
self.toggle_clicked_mobject_from_selection(point)
|
||||
else:
|
||||
self.clear_selection()
|
||||
|
|
|
@ -2,10 +2,11 @@ from manimlib.animation.animation import Animation
|
|||
from manimlib.animation.transform import MoveToTarget
|
||||
from manimlib.animation.transform import Transform
|
||||
from manimlib.animation.update import UpdateFromFunc
|
||||
from manimlib.constants import *
|
||||
from manimlib.scene.scene import Scene
|
||||
from manimlib.constants import DOWN, RIGHT
|
||||
from manimlib.constants import MED_LARGE_BUFF, SMALL_BUFF
|
||||
from manimlib.mobject.probability import SampleSpace
|
||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||
from manimlib.scene.scene import Scene
|
||||
|
||||
|
||||
class SampleSpaceScene(Scene):
|
||||
|
|
|
@ -1,40 +1,42 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
import random
|
||||
import inspect
|
||||
import platform
|
||||
from functools import wraps
|
||||
from typing import Iterable, Callable
|
||||
import inspect
|
||||
import os
|
||||
import platform
|
||||
import random
|
||||
import time
|
||||
|
||||
from tqdm import tqdm as ProgressDisplay
|
||||
import numpy as np
|
||||
from tqdm import tqdm as ProgressDisplay
|
||||
|
||||
from manimlib.animation.animation import prepare_animation
|
||||
from manimlib.animation.transform import MoveToTarget
|
||||
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 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 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 VMobject
|
||||
from manimlib.scene.scene_file_writer import SceneFileWriter
|
||||
from manimlib.utils.config_ops import digest_config
|
||||
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.event_handler.event_type import EventType
|
||||
from manimlib.event_handler import EVENT_DISPATCHER
|
||||
from manimlib.logger import log
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Callable, Iterable
|
||||
|
||||
from PIL.Image import Image
|
||||
|
||||
from manimlib.animation.animation import Animation
|
||||
|
||||
|
||||
|
@ -43,7 +45,6 @@ FRAME_SHIFT_KEY = 'f'
|
|||
ZOOM_KEY = 'z'
|
||||
RESET_FRAME_KEY = 'r'
|
||||
QUIT_KEY = 'q'
|
||||
EMBED_KEY = 'e'
|
||||
|
||||
|
||||
class Scene(object):
|
||||
|
@ -62,6 +63,7 @@ class Scene(object):
|
|||
"presenter_mode": False,
|
||||
"linger_after_completion": True,
|
||||
"pan_sensitivity": 3,
|
||||
"max_num_saved_states": 20,
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
@ -71,12 +73,15 @@ class Scene(object):
|
|||
self.window = Window(scene=self, **self.window_config)
|
||||
self.camera_config["ctx"] = self.window.ctx
|
||||
self.camera_config["frame_rate"] = 30 # Where's that 30 from?
|
||||
self.undo_stack = []
|
||||
self.redo_stack = []
|
||||
else:
|
||||
self.window = None
|
||||
|
||||
self.camera: Camera = self.camera_class(**self.camera_config)
|
||||
self.file_writer = SceneFileWriter(self, **self.file_writer_config)
|
||||
self.mobjects: list[Mobject] = [self.camera.frame]
|
||||
self.id_to_mobject_map: dict[int, Mobject] = dict()
|
||||
self.num_plays: int = 0
|
||||
self.time: float = 0
|
||||
self.skip_time: float = 0
|
||||
|
@ -88,12 +93,16 @@ class Scene(object):
|
|||
self.mouse_point = Point()
|
||||
self.mouse_drag_point = Point()
|
||||
self.hold_on_wait = self.presenter_mode
|
||||
self.inside_embed = False
|
||||
|
||||
# Much nicer to work with deterministic scenes
|
||||
if self.random_seed is not None:
|
||||
random.seed(self.random_seed)
|
||||
np.random.seed(self.random_seed)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__class__.__name__
|
||||
|
||||
def run(self) -> None:
|
||||
self.virtual_animation_start_time: float = 0
|
||||
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:
|
||||
if not self.preview:
|
||||
# If the scene is just being
|
||||
# written, ignore embed calls
|
||||
# Ignore embed calls when there is no preview
|
||||
return
|
||||
self.inside_embed = True
|
||||
self.stop_skipping()
|
||||
self.linger_after_completion = False
|
||||
self.update_frame()
|
||||
|
||||
# Save scene state at the point of embedding
|
||||
self.save_state()
|
||||
|
||||
from IPython.terminal.embed import InteractiveShellEmbed
|
||||
shell = InteractiveShellEmbed()
|
||||
# Have the frame update after each command
|
||||
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 of the caller as the local namespace
|
||||
# once embedded, and add a few custom shortcuts
|
||||
# Configure and launch embedded IPython terminal
|
||||
from IPython.terminal import embed, pt_inputhooks
|
||||
shell = embed.InteractiveShellEmbed.instance()
|
||||
|
||||
# Use the locals namespace of the caller
|
||||
local_ns = inspect.currentframe().f_back.f_locals
|
||||
local_ns["touch"] = self.interact
|
||||
local_ns["i2g"] = self.ids_to_group
|
||||
for term in ("play", "wait", "add", "remove", "clear", "save_state", "restore"):
|
||||
local_ns[term] = getattr(self, term)
|
||||
log.info("Tips: Now the embed iPython terminal is open. But you can't interact with"
|
||||
" the window directly. To do so, you need to type `touch()` or `self.interact()`")
|
||||
exec(get_custom_config()["universal_import_line"])
|
||||
# Add a few custom shortcuts
|
||||
local_ns.update({
|
||||
name: getattr(self, name)
|
||||
for name in [
|
||||
"play", "wait", "add", "remove", "clear",
|
||||
"save_state", "undo", "redo", "i2g", "i2m"
|
||||
]
|
||||
})
|
||||
|
||||
# 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)
|
||||
|
||||
self.inside_embed = False
|
||||
# End scene when exiting an embed
|
||||
if close_scene_on_exit:
|
||||
raise EndSceneEarlyException()
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__class__.__name__
|
||||
|
||||
# Only these methods should touch the camera
|
||||
|
||||
def get_image(self) -> Image:
|
||||
return self.camera.get_image()
|
||||
|
||||
|
@ -207,6 +233,7 @@ class Scene(object):
|
|||
self.file_writer.write_frame(self.camera)
|
||||
|
||||
# Related to updating
|
||||
|
||||
def update_mobjects(self, dt: float) -> None:
|
||||
for mobject in self.mobjects:
|
||||
mobject.update(dt)
|
||||
|
@ -225,6 +252,7 @@ class Scene(object):
|
|||
])
|
||||
|
||||
# Related to time
|
||||
|
||||
def get_time(self) -> float:
|
||||
return self.time
|
||||
|
||||
|
@ -232,6 +260,7 @@ class Scene(object):
|
|||
self.time += dt
|
||||
|
||||
# Related to internal mobject organization
|
||||
|
||||
def get_top_level_mobjects(self) -> list[Mobject]:
|
||||
# Return only those which are not in the family
|
||||
# of another mobject from the scene
|
||||
|
@ -256,6 +285,11 @@ class Scene(object):
|
|||
"""
|
||||
self.remove(*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
|
||||
|
||||
def add_mobjects_among(self, values: Iterable):
|
||||
|
@ -319,11 +353,7 @@ class Scene(object):
|
|||
return Group(*mobjects)
|
||||
|
||||
def id_to_mobject(self, id_value):
|
||||
for mob in self.mobjects:
|
||||
for sm in mob.get_family():
|
||||
if id(sm) == id_value:
|
||||
return sm
|
||||
return None
|
||||
return self.id_to_mobject_map[id_value]
|
||||
|
||||
def ids_to_group(self, *id_values):
|
||||
return self.get_group(*filter(
|
||||
|
@ -331,7 +361,14 @@ class Scene(object):
|
|||
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
|
||||
|
||||
def update_skipping_status(self) -> None:
|
||||
if self.start_at_animation_number is not None:
|
||||
if self.num_plays == self.start_at_animation_number:
|
||||
|
@ -347,6 +384,7 @@ class Scene(object):
|
|||
self.skip_animations = False
|
||||
|
||||
# Methods associated with running animations
|
||||
|
||||
def get_time_progression(
|
||||
self,
|
||||
run_time: float,
|
||||
|
@ -470,6 +508,8 @@ class Scene(object):
|
|||
def handle_play_like_call(func):
|
||||
@wraps(func)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
if self.inside_embed:
|
||||
self.save_state()
|
||||
self.update_skipping_status()
|
||||
should_write = not self.skip_animations
|
||||
if should_write:
|
||||
|
@ -591,24 +631,39 @@ class Scene(object):
|
|||
self.file_writer.add_sound(sound_file, time, gain, gain_to_background)
|
||||
|
||||
# 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:
|
||||
self.saved_state = [
|
||||
(mob, mob.copy())
|
||||
for mob in self.mobjects
|
||||
]
|
||||
if not self.preview:
|
||||
return
|
||||
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:
|
||||
if not hasattr(self, "saved_state"):
|
||||
raise Exception("Trying to restore scene without having saved")
|
||||
self.mobjects = []
|
||||
for mob, mob_state in self.saved_state:
|
||||
mob.become(mob_state)
|
||||
self.mobjects.append(mob)
|
||||
def undo(self):
|
||||
if self.undo_stack:
|
||||
self.redo_stack.append(self.get_state())
|
||||
self.restore_state(self.undo_stack.pop())
|
||||
self.refresh_static_mobjects()
|
||||
|
||||
def save_mobect(self, mobject: Mobject, file_name: str):
|
||||
directory = self.file_writer.get_saved_mobject_directory()
|
||||
path = os.path.join(directory, file_name)
|
||||
mobject.save_to_file(path)
|
||||
def redo(self):
|
||||
if self.redo_stack:
|
||||
self.undo_stack.append(self.get_state())
|
||||
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):
|
||||
if os.path.exists(file_name):
|
||||
|
@ -730,15 +785,16 @@ class Scene(object):
|
|||
|
||||
if char == RESET_FRAME_KEY:
|
||||
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
|
||||
elif char == QUIT_KEY and modifiers == COMMAND_MODIFIER:
|
||||
self.quit_interaction = True
|
||||
# Space or right arrow
|
||||
elif char == " " or symbol == ARROW_SYMBOLS[2]:
|
||||
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:
|
||||
self.camera.reset_pixel_shape(width, height)
|
||||
|
|
|
@ -1,30 +1,32 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import platform
|
||||
import shutil
|
||||
import subprocess as sp
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
from pydub import AudioSegment
|
||||
from tqdm import tqdm as ProgressDisplay
|
||||
|
||||
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.file_ops import guarantee_existence
|
||||
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 guarantee_existence
|
||||
from manimlib.utils.sounds import get_full_sound_file_path
|
||||
from manimlib.logger import log
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from manimlib.scene.scene import Scene
|
||||
from manimlib.camera.camera import Camera
|
||||
from PIL.Image import Image
|
||||
|
||||
from manimlib.camera.camera import Camera
|
||||
from manimlib.scene.scene import Scene
|
||||
|
||||
|
||||
class SceneFileWriter(object):
|
||||
CONFIG = {
|
||||
|
@ -60,7 +62,7 @@ class SceneFileWriter(object):
|
|||
|
||||
# Output directories and files
|
||||
def init_output_directories(self) -> None:
|
||||
out_dir = self.output_directory
|
||||
out_dir = self.output_directory or ""
|
||||
if self.mirror_module_path:
|
||||
module_dir = self.get_default_module_directory()
|
||||
out_dir = os.path.join(out_dir, module_dir)
|
||||
|
@ -127,6 +129,36 @@ class SceneFileWriter(object):
|
|||
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
|
||||
def init_audio(self) -> None:
|
||||
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 ApplyPointwiseFunction
|
||||
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 NumberPlane
|
||||
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 get_norm
|
||||
|
||||
|
||||
X_COLOR = GREEN_C
|
||||
Y_COLOR = RED_C
|
||||
Z_COLOR = BLUE_D
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import os
|
||||
import re
|
||||
import copy
|
||||
from typing import Iterable
|
||||
|
||||
import moderngl
|
||||
import numpy as np
|
||||
|
@ -11,6 +10,12 @@ import numpy as np
|
|||
from manimlib.utils.directories import get_shader_dir
|
||||
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
|
||||
# the same shader will be organized and
|
||||
# clumped together based on keeping track
|
||||
|
|
|
@ -1,19 +1,26 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Iterable, Callable, TypeVar, Sequence
|
||||
|
||||
from scipy import linalg
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
from scipy import linalg
|
||||
|
||||
from manimlib.utils.simple_functions import choose
|
||||
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
|
||||
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
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
CLOSED_THRESHOLD = 0.001
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def bezier(
|
||||
points: Iterable[float | np.ndarray]
|
||||
|
|
|
@ -1,15 +1,24 @@
|
|||
import random
|
||||
from __future__ import annotations
|
||||
|
||||
from colour import Color
|
||||
from colour import hex2rgb
|
||||
from colour import rgb2hex
|
||||
import numpy as np
|
||||
|
||||
from manimlib.constants import WHITE
|
||||
from manimlib.constants import COLORMAP_3B1B
|
||||
from manimlib.constants import WHITE
|
||||
from manimlib.utils.bezier import interpolate
|
||||
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):
|
||||
return hex_to_rgb(color)
|
||||
elif isinstance(color, Color):
|
||||
|
@ -18,55 +27,48 @@ def color_to_rgb(color):
|
|||
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])
|
||||
|
||||
|
||||
def rgb_to_color(rgb):
|
||||
def rgb_to_color(rgb: Iterable[float]) -> Color:
|
||||
try:
|
||||
return Color(rgb=rgb)
|
||||
return Color(rgb=tuple(rgb))
|
||||
except ValueError:
|
||||
return Color(WHITE)
|
||||
|
||||
|
||||
def rgba_to_color(rgba):
|
||||
return rgb_to_color(rgba[:3])
|
||||
def rgba_to_color(rgba: Iterable[float]) -> Color:
|
||||
return rgb_to_color(tuple(rgba)[:3])
|
||||
|
||||
|
||||
def rgb_to_hex(rgb):
|
||||
return "#" + "".join(
|
||||
hex(int_x // 16)[2] + hex(int_x % 16)[2]
|
||||
for x in rgb
|
||||
for int_x in [int(255 * x)]
|
||||
)
|
||||
def rgb_to_hex(rgb: Iterable[float]) -> str:
|
||||
return rgb2hex(rgb, force_long=True).upper()
|
||||
|
||||
|
||||
def hex_to_rgb(hex_code):
|
||||
hex_part = hex_code[1:]
|
||||
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 hex_to_rgb(hex_code: str) -> np.ndarray:
|
||||
return np.array(hex2rgb(hex_code))
|
||||
|
||||
|
||||
def invert_color(color):
|
||||
def invert_color(color: ManimColor) -> 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')
|
||||
|
||||
|
||||
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)
|
||||
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:
|
||||
return reference_colors[0]
|
||||
return []
|
||||
rgbs = list(map(color_to_rgb, reference_colors))
|
||||
alphas = np.linspace(0, (len(rgbs) - 1), length_of_output)
|
||||
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)
|
||||
return rgb_to_color(rgb)
|
||||
|
||||
|
||||
def average_color(*colors):
|
||||
def average_color(*colors: ManimColor) -> Color:
|
||||
rgbs = np.array(list(map(color_to_rgb, colors)))
|
||||
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()
|
||||
curr_rgb = color_to_rgb(color)
|
||||
new_rgb = interpolate(
|
||||
curr_rgb, np.ones(len(curr_rgb)), 0.5
|
||||
)
|
||||
return Color(rgb=new_rgb)
|
||||
return average_color(color, Color(WHITE))
|
||||
|
||||
|
||||
def random_color():
|
||||
return Color(rgb=(random.random() for i in range(3)))
|
||||
|
||||
|
||||
def get_colormap_list(map_name="viridis", n_colors=9):
|
||||
def get_colormap_list(
|
||||
map_name: str = "viridis",
|
||||
n_colors: int = 9
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
Options for map_name:
|
||||
3b1b_colormap
|
||||
|
|
|
@ -4,6 +4,7 @@ import tempfile
|
|||
from manimlib.config import get_custom_config
|
||||
from manimlib.config import get_manim_dir
|
||||
|
||||
|
||||
CUSTOMIZATION = {}
|
||||
|
||||
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
import numpy as np
|
||||
from typing import Callable
|
||||
import time
|
||||
|
||||
from manimlib.constants import BLACK
|
||||
from manimlib.logger import log
|
||||
from manimlib.mobject.numbers import Integer
|
||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||
from manimlib.logger import log
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Callable
|
||||
|
||||
from manimlib.mobject.mobject import Mobject
|
||||
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ from __future__ import annotations
|
|||
|
||||
import os
|
||||
|
||||
from manimlib.utils.file_ops import guarantee_existence
|
||||
from manimlib.utils.customization import get_customization
|
||||
from manimlib.utils.file_ops import guarantee_existence
|
||||
|
||||
|
||||
def get_directories() -> dict[str, str]:
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Iterable
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Iterable
|
||||
|
||||
from manimlib.mobject.mobject import Mobject
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import Iterable
|
||||
|
||||
import numpy as np
|
||||
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:
|
||||
# This could conceivably be smarter about handling existing differing extensions
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
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_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:
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import inspect
|
||||
import os
|
||||
import yaml
|
||||
import inspect
|
||||
import importlib
|
||||
from typing import Any
|
||||
|
||||
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.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:
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from typing import Callable
|
||||
|
||||
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 rotation_matrix_transpose
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Callable
|
||||
|
||||
|
||||
STRAIGHT_PATH_THRESHOLD = 0.01
|
||||
|
||||
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
from typing import Callable
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
|
||||
from manimlib.utils.bezier import bezier
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Callable
|
||||
|
||||
|
||||
def linear(t: float) -> float:
|
||||
return t
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import inspect
|
||||
import numpy as np
|
||||
import math
|
||||
from functools import lru_cache
|
||||
import inspect
|
||||
import math
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
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.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(
|
||||
sound_file_name,
|
||||
directories=[get_sound_dir()],
|
||||
|
|
|
@ -1,25 +1,27 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from functools import reduce
|
||||
import math
|
||||
import operator as op
|
||||
from functools import reduce
|
||||
from typing import Callable, Iterable, Sequence
|
||||
import platform
|
||||
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
from mapbox_earcut import triangulate_float32 as earcut
|
||||
import numpy as np
|
||||
from scipy.spatial.transform import Rotation
|
||||
from tqdm import tqdm as ProgressDisplay
|
||||
|
||||
from manimlib.constants import RIGHT
|
||||
from manimlib.constants import DOWN
|
||||
from manimlib.constants import OUT
|
||||
from manimlib.constants import PI
|
||||
from manimlib.constants import TAU
|
||||
from manimlib.constants import DOWN, OUT, RIGHT
|
||||
from manimlib.constants import PI, TAU
|
||||
from manimlib.utils.iterables import adjacent_pairs
|
||||
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]:
|
||||
return [
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
import sys
|
||||
import os
|
||||
import hashlib
|
||||
from contextlib import contextmanager
|
||||
from __future__ import annotations
|
||||
|
||||
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_manim_dir
|
||||
from manimlib.logger import log
|
||||
from manimlib.utils.directories import get_tex_dir
|
||||
|
||||
|
||||
SAVED_TEX_CONFIG = {}
|
||||
|
||||
|
||||
def get_tex_config():
|
||||
def get_tex_config() -> dict[str, str]:
|
||||
"""
|
||||
Returns a dict which should look something like this:
|
||||
{
|
||||
|
@ -37,13 +39,13 @@ def get_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
|
||||
hasher = hashlib.sha256(tex_file_content.encode())
|
||||
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(
|
||||
get_tex_dir(), tex_hash(tex_file_content) + ".svg"
|
||||
)
|
||||
|
@ -53,7 +55,7 @@ def tex_to_svg_file(tex_file_content):
|
|||
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")
|
||||
with open(tex_file, "w", encoding="utf-8") as outfile:
|
||||
outfile.write(tex_file_content)
|
||||
|
@ -69,7 +71,7 @@ def tex_to_svg(tex_file_content, svg_file):
|
|||
return svg_file
|
||||
|
||||
|
||||
def tex_to_dvi(tex_file):
|
||||
def tex_to_dvi(tex_file: str) -> str:
|
||||
tex_config = get_tex_config()
|
||||
program = tex_config["executable"]
|
||||
file_type = tex_config["intermediate_filetype"]
|
||||
|
@ -96,7 +98,7 @@ def tex_to_dvi(tex_file):
|
|||
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
|
||||
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
|
||||
@contextmanager
|
||||
def display_during_execution(message):
|
||||
def display_during_execution(message: str) -> None:
|
||||
# Only show top line
|
||||
to_print = message.split("\n")[0]
|
||||
max_characters = os.get_terminal_size().columns - 1
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
|
||||
import moderngl_window as mglw
|
||||
from moderngl_window.context.pyglet.window import Window as PygletWindow
|
||||
from moderngl_window.timers.clock import Timer
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
colour
|
||||
numpy
|
||||
Pillow
|
||||
scipy
|
||||
sympy
|
||||
tqdm
|
||||
ipython
|
||||
isosurfaces
|
||||
manimpango>=0.4.0.post0,<0.5.0
|
||||
mapbox-earcut
|
||||
matplotlib
|
||||
moderngl
|
||||
moderngl_window
|
||||
skia-pathops
|
||||
numpy
|
||||
Pillow
|
||||
pydub
|
||||
pygments
|
||||
PyOpenGL
|
||||
pyperclip
|
||||
pyyaml
|
||||
rich
|
||||
scipy
|
||||
screeninfo
|
||||
validators
|
||||
ipython
|
||||
PyOpenGL
|
||||
manimpango>=0.4.0.post0,<0.5.0
|
||||
isosurfaces
|
||||
skia-pathops
|
||||
svgelements
|
||||
sympy
|
||||
tqdm
|
||||
validators
|
||||
|
|
23
setup.cfg
23
setup.cfg
|
@ -30,27 +30,28 @@ packages = find:
|
|||
include_package_data = True
|
||||
install_requires =
|
||||
colour
|
||||
numpy
|
||||
Pillow
|
||||
scipy
|
||||
sympy
|
||||
tqdm
|
||||
ipython
|
||||
isosurfaces
|
||||
manimpango>=0.4.0.post0,<0.5.0
|
||||
mapbox-earcut
|
||||
matplotlib
|
||||
moderngl
|
||||
moderngl_window
|
||||
skia-pathops
|
||||
numpy
|
||||
Pillow
|
||||
pydub
|
||||
pygments
|
||||
PyOpenGL
|
||||
pyperclip
|
||||
pyyaml
|
||||
rich
|
||||
scipy
|
||||
screeninfo
|
||||
validators
|
||||
ipython
|
||||
PyOpenGL
|
||||
manimpango>=0.4.0.post0,<0.5.0
|
||||
isosurfaces
|
||||
skia-pathops
|
||||
svgelements
|
||||
sympy
|
||||
tqdm
|
||||
validators
|
||||
|
||||
[options.entry_points]
|
||||
console_scripts =
|
||||
|
|
Loading…
Add table
Reference in a new issue