mirror of
https://github.com/ManimCommunity/manim.git
synced 2026-06-22 10:01:47 +00:00
Merge branch 'main' of https://github.com/ManimCommunity/manim into experimental
This commit is contained in:
commit
793e853ae8
64 changed files with 185 additions and 374 deletions
|
|
@ -12,11 +12,6 @@ repos:
|
|||
- id: end-of-file-fixer
|
||||
- id: check-toml
|
||||
name: Validate Poetry
|
||||
- repo: https://github.com/pre-commit/pygrep-hooks
|
||||
rev: v1.10.0
|
||||
hooks:
|
||||
- id: python-check-blanket-noqa
|
||||
name: Precision flake ignores
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.6.2
|
||||
hooks:
|
||||
|
|
@ -26,17 +21,6 @@ repos:
|
|||
args: [--exit-non-zero-on-fix]
|
||||
- id: ruff-format
|
||||
types: [python]
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 7.1.1
|
||||
hooks:
|
||||
- id: flake8
|
||||
additional_dependencies:
|
||||
[
|
||||
flake8-docstrings==1.6.0,
|
||||
flake8-pytest-style==1.5.0,
|
||||
flake8-rst-docstrings==0.2.3,
|
||||
flake8-simplify==0.14.1,
|
||||
]
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v1.11.2
|
||||
hooks:
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ def FrenchCursive(*tex_strings, **kwargs):
|
|||
|
||||
class TexFontTemplateManual(Scene):
|
||||
"""An example scene that uses a manually defined TexTemplate() object to create
|
||||
LaTeX output in French Cursive font"""
|
||||
LaTeX output in French Cursive font
|
||||
"""
|
||||
|
||||
def construct(self):
|
||||
self.add(Tex("Tex Font Example").to_edge(UL))
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from pathlib import Path
|
|||
|
||||
import manim.utils.opengl as opengl
|
||||
from manim import *
|
||||
from manim.opengl import * # type: ignore
|
||||
from manim.opengl import *
|
||||
|
||||
# Copied from https://3b1b.github.io/manim/getting_started/example_scenes.html#surfaceexample.
|
||||
# Lines that do not yet work with the Community Version are commented.
|
||||
|
|
|
|||
|
|
@ -323,7 +323,6 @@ class ManimConfig(MutableMapping):
|
|||
|
||||
def _warn_about_config_options(self) -> None:
|
||||
"""Warns about incorrect config options, or permutations of config options."""
|
||||
|
||||
logger = logging.getLogger("manim")
|
||||
if self.format == "webm":
|
||||
logger.warning(
|
||||
|
|
@ -385,7 +384,6 @@ class ManimConfig(MutableMapping):
|
|||
:meth:`~ManimConfig.digest_parser`
|
||||
|
||||
"""
|
||||
|
||||
if isinstance(obj, ManimConfig):
|
||||
self._d.update(obj._d)
|
||||
if obj.tex_template:
|
||||
|
|
|
|||
|
|
@ -623,7 +623,6 @@ def override_animation(
|
|||
self.play(FadeIn(MySquare()))
|
||||
|
||||
"""
|
||||
|
||||
_F = TypeVar("_F", bound=Callable)
|
||||
|
||||
def decorator(func: _F) -> _F:
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ class _Fade(Transform):
|
|||
|
||||
|
||||
class FadeIn(_Fade):
|
||||
"""Fade in :class:`~.OpenGLMobject` s.
|
||||
r"""Fade in :class:`~.OpenGLMobject` s.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -123,7 +123,7 @@ class FadeIn(_Fade):
|
|||
dot = Dot(UP * 2 + LEFT)
|
||||
self.add(dot)
|
||||
tex = Tex(
|
||||
"FadeIn with ", "shift ", " or target\\_position", " and scale"
|
||||
"FadeIn with ", "shift ", r" or target\_position", " and scale"
|
||||
).scale(1)
|
||||
animations = [
|
||||
FadeIn(tex[0]),
|
||||
|
|
@ -146,7 +146,7 @@ class FadeIn(_Fade):
|
|||
|
||||
|
||||
class FadeOut(_Fade):
|
||||
"""Fade out :class:`~.OpenGLMobject` s.
|
||||
r"""Fade out :class:`~.OpenGLMobject` s.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ class Flash(AnimationGroup):
|
|||
|
||||
|
||||
class ShowPassingFlash(ShowPartial):
|
||||
"""Show only a sliver of the VMobject each frame.
|
||||
r"""Show only a sliver of the VMobject each frame.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -289,7 +289,7 @@ class ShowPassingFlash(ShowPartial):
|
|||
self.add(p, lbl)
|
||||
p = p.copy().set_color(BLUE)
|
||||
for time_width in [0.2, 0.5, 1, 2]:
|
||||
lbl.become(Tex(r"\\texttt{time\\_width={{%.1f}}}"%time_width))
|
||||
lbl.become(Tex(r"\texttt{time\_width={{%.1f}}}"%time_width))
|
||||
self.play(ShowPassingFlash(
|
||||
p.copy().set_color(BLUE),
|
||||
run_time=2,
|
||||
|
|
@ -554,7 +554,7 @@ class Wiggle(Animation):
|
|||
|
||||
# TODO: get rid of this if condition madness
|
||||
class Circumscribe(Succession):
|
||||
"""Draw a temporary line surrounding the mobject.
|
||||
r"""Draw a temporary line surrounding the mobject.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -585,7 +585,7 @@ class Circumscribe(Succession):
|
|||
|
||||
class UsingCircumscribe(Scene):
|
||||
def construct(self):
|
||||
lbl = Tex(r"Circum-\\\\scribe").scale(2)
|
||||
lbl = Tex(r"Circum-\\scribe").scale(2)
|
||||
self.add(lbl)
|
||||
self.play(Circumscribe(lbl))
|
||||
self.play(Circumscribe(lbl, Circle))
|
||||
|
|
|
|||
|
|
@ -91,9 +91,7 @@ class ComplexHomotopy(Homotopy):
|
|||
def __init__(
|
||||
self, complex_homotopy: Callable[[complex], float], mobject: Mobject, **kwargs
|
||||
) -> None:
|
||||
"""
|
||||
Complex Homotopy a function Cx[0, 1] to C
|
||||
"""
|
||||
"""Complex Homotopy a function Cx[0, 1] to C"""
|
||||
|
||||
def homotopy(
|
||||
x: float,
|
||||
|
|
|
|||
|
|
@ -294,9 +294,7 @@ class ReplacementTransform(Transform):
|
|||
|
||||
|
||||
class TransformFromCopy(Transform):
|
||||
"""
|
||||
Performs a reversed Transform
|
||||
"""
|
||||
"""Performs a reversed Transform"""
|
||||
|
||||
def __init__(
|
||||
self, mobject: OpenGLMobject, target_mobject: OpenGLMobject, **kwargs
|
||||
|
|
|
|||
|
|
@ -41,13 +41,6 @@ class Camera(OpenGLMobject):
|
|||
def get_orientation(self):
|
||||
return Rotation.from_quat(self.orientation)
|
||||
|
||||
def to_default_state(self):
|
||||
self.center()
|
||||
self.set_height(config.frame_width)
|
||||
self.set_width(config.frame_height)
|
||||
self.set_orientation(Rotation.identity())
|
||||
return self
|
||||
|
||||
def get_euler_angles(self):
|
||||
return self.get_orientation().as_euler("zxz")[::-1]
|
||||
|
||||
|
|
@ -63,8 +56,8 @@ class Camera(OpenGLMobject):
|
|||
def get_inverse_camera_rotation_matrix(self):
|
||||
return self.get_orientation().as_matrix().T
|
||||
|
||||
def rotate(self, angle: float, axis: np.ndarray = OUT, **kwargs): # type: ignore
|
||||
rot = Rotation.from_rotvec(axis * normalize(axis)) # type: ignore
|
||||
def rotate(self, angle: float, axis: np.ndarray = OUT, **kwargs):
|
||||
rot = Rotation.from_rotvec(axis * normalize(axis))
|
||||
self.set_orientation(rot * self.get_orientation())
|
||||
|
||||
def set_euler_angles(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
"""Auxiliary module for the checkhealth subcommand, contains
|
||||
the actual check implementations."""
|
||||
the actual check implementations.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ __all__ = ["render"]
|
|||
@cloup.argument("scene_names", required=False, nargs=-1)
|
||||
@global_options
|
||||
@output_options
|
||||
@render_options # type: ignore
|
||||
@render_options
|
||||
@ease_of_access_options
|
||||
def render(
|
||||
**args,
|
||||
|
|
@ -53,7 +53,6 @@ def render(
|
|||
|
||||
SCENES is an optional list of scenes in the file.
|
||||
"""
|
||||
|
||||
if args["show_in_file_browser"]:
|
||||
logger.warning(
|
||||
"The short form of show_in_file_browser is deprecated and will be moved to support --format.",
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ render_options = option_group(
|
|||
"--quality",
|
||||
default=None,
|
||||
type=Choice(
|
||||
list(reversed([q["flag"] for q in QUALITIES.values() if q["flag"]])), # type: ignore
|
||||
reversed([q["flag"] for q in QUALITIES.values() if q["flag"]]), # type: ignore[arg-type]
|
||||
case_sensitive=False,
|
||||
),
|
||||
help="Render quality at the follow resolution framerates, respectively: "
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
"""
|
||||
Constant definitions.
|
||||
"""
|
||||
"""Constant definitions."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
|
|||
|
|
@ -246,15 +246,11 @@ class FileWriter(FileWriterProtocol):
|
|||
|
||||
# Sound
|
||||
def init_audio(self) -> None:
|
||||
"""
|
||||
Preps the writer for adding audio to the movie.
|
||||
"""
|
||||
"""Preps the writer for adding audio to the movie."""
|
||||
self.includes_sound = False
|
||||
|
||||
def create_audio_segment(self) -> None:
|
||||
"""
|
||||
Creates an empty, silent, Audio Segment.
|
||||
"""
|
||||
"""Creates an empty, silent, Audio Segment."""
|
||||
self.audio_segment = AudioSegment.silent()
|
||||
|
||||
def add_audio_segment(
|
||||
|
|
@ -390,9 +386,7 @@ class FileWriter(FileWriterProtocol):
|
|||
self.num_plays += 1
|
||||
|
||||
def listen_and_write(self) -> None:
|
||||
"""
|
||||
For internal use only: blocks until new frame is available on the queue.
|
||||
"""
|
||||
"""For internal use only: blocks until new frame is available on the queue."""
|
||||
while True:
|
||||
num_frames, frame_data = self.queue.get()
|
||||
if frame_data is None:
|
||||
|
|
@ -794,7 +788,6 @@ class FileWriter(FileWriterProtocol):
|
|||
|
||||
def combine_to_section_videos(self) -> None:
|
||||
"""Concatenate partial movie files for each section."""
|
||||
|
||||
self.finish_last_section()
|
||||
sections_index: list[dict[str, Any]] = []
|
||||
for section in self.sections:
|
||||
|
|
@ -855,6 +848,5 @@ class FileWriter(FileWriterProtocol):
|
|||
|
||||
def print_file_ready_message(self, file_path: str | Path) -> None:
|
||||
"""Prints the "File Ready" message to STDOUT."""
|
||||
|
||||
config.output_file = str(file_path)
|
||||
logger.info(f"\nFile ready at {str(file_path)!r}\n")
|
||||
|
|
|
|||
|
|
@ -129,7 +129,6 @@ class Manager(Generic[Scene_co]):
|
|||
|
||||
def setup(self) -> None:
|
||||
"""Set up processes and manager"""
|
||||
|
||||
self.scene.setup()
|
||||
|
||||
# these are used for making sure it feels like the correct
|
||||
|
|
@ -212,7 +211,6 @@ class Manager(Generic[Scene_co]):
|
|||
|
||||
def tear_down(self) -> None:
|
||||
"""Tear down the scene and the window."""
|
||||
|
||||
self.scene.tear_down()
|
||||
|
||||
if self.window is not None:
|
||||
|
|
@ -221,7 +219,6 @@ class Manager(Generic[Scene_co]):
|
|||
|
||||
def _interact(self) -> None:
|
||||
"""Live interaction with the Window"""
|
||||
|
||||
if self.window is None:
|
||||
return
|
||||
logger.info(
|
||||
|
|
@ -272,7 +269,6 @@ class Manager(Generic[Scene_co]):
|
|||
slow the window down for the correct amount of time, such
|
||||
as during a wait animation.
|
||||
"""
|
||||
|
||||
if self.window is None:
|
||||
return
|
||||
|
||||
|
|
@ -294,7 +290,6 @@ class Manager(Generic[Scene_co]):
|
|||
|
||||
def _play(self, *animations: AnimationProtocol) -> None:
|
||||
"""Play a bunch of animations"""
|
||||
|
||||
self.scene.pre_play()
|
||||
|
||||
if self.window is not None:
|
||||
|
|
@ -347,7 +342,6 @@ class Manager(Generic[Scene_co]):
|
|||
self, total: float, description: str, **kwargs: Any
|
||||
) -> contextlib.AbstractContextManager[ProgressBarProtocol]:
|
||||
"""Create a progressbar"""
|
||||
|
||||
if not config.progress_bar:
|
||||
return contextlib.nullcontext(NullProgressBar())
|
||||
|
||||
|
|
@ -450,7 +444,6 @@ class Manager(Generic[Scene_co]):
|
|||
:meth:`.FileWriter.add_partial_movie_file` have not been called. Do NOT
|
||||
use this to write a single frame!
|
||||
"""
|
||||
|
||||
self.renderer.render(state)
|
||||
|
||||
should_write = write_frame if write_frame is not None else self._write_files
|
||||
|
|
@ -459,14 +452,12 @@ class Manager(Generic[Scene_co]):
|
|||
|
||||
def write_frame(self) -> None:
|
||||
"""Take a frame from the renderer and write it in the file writer."""
|
||||
|
||||
frame = self.renderer.get_pixels()
|
||||
self.file_writer.write_frame(frame)
|
||||
|
||||
|
||||
def _calc_time_progression(run_time: float) -> npt.NDArray[np.float64]:
|
||||
"""Compute the time values at which to evaluate the animation"""
|
||||
|
||||
return np.arange(0, run_time, 1 / config.frame_rate)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ class ScreenRectangle(Rectangle):
|
|||
When set, the width is stretched to accommodate
|
||||
the new aspect ratio.
|
||||
"""
|
||||
|
||||
return self.width / self.height
|
||||
|
||||
@aspect_ratio.setter
|
||||
|
|
|
|||
|
|
@ -247,7 +247,8 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|||
|
||||
def get_tip(self):
|
||||
"""Returns the TipableVMobject instance's (first) tip,
|
||||
otherwise throws an exception."""
|
||||
otherwise throws an exception.
|
||||
"""
|
||||
tips = self.get_tips()
|
||||
if len(tips) == 0:
|
||||
raise Exception("tip not found")
|
||||
|
|
@ -572,7 +573,6 @@ class Circle(Arc):
|
|||
group = Group(group1, group2, group3).arrange(buff=1)
|
||||
self.add(group)
|
||||
"""
|
||||
|
||||
# Ignores dim_to_match and stretch; result will always be a circle
|
||||
# TODO: Perhaps create an ellipse class to handle single-dimension stretching
|
||||
|
||||
|
|
@ -612,7 +612,6 @@ class Circle(Arc):
|
|||
self.add(circle, s1, s2)
|
||||
|
||||
"""
|
||||
|
||||
start_angle = angle_of_vector(self.points[0] - self.get_center())
|
||||
proportion = (angle - start_angle) / TAU
|
||||
proportion -= np.floor(proportion)
|
||||
|
|
|
|||
|
|
@ -197,7 +197,6 @@ class Line(TipableVMobject):
|
|||
point
|
||||
The point to which the line is projected.
|
||||
"""
|
||||
|
||||
start = self.get_start()
|
||||
end = self.get_end()
|
||||
unit_vect = normalize(end - start)
|
||||
|
|
@ -283,7 +282,6 @@ class DashedLine(Line):
|
|||
>>> DashedLine()._calculate_num_dashes()
|
||||
20
|
||||
"""
|
||||
|
||||
# Minimum number of dashes has to be 2
|
||||
return max(
|
||||
2,
|
||||
|
|
@ -300,7 +298,6 @@ class DashedLine(Line):
|
|||
>>> DashedLine().get_start()
|
||||
array([-1., 0., 0.])
|
||||
"""
|
||||
|
||||
if len(self.submobjects) > 0:
|
||||
return self.submobjects[0].get_start()
|
||||
else:
|
||||
|
|
@ -316,7 +313,6 @@ class DashedLine(Line):
|
|||
>>> DashedLine().get_end()
|
||||
array([1., 0., 0.])
|
||||
"""
|
||||
|
||||
if len(self.submobjects) > 0:
|
||||
return self.submobjects[-1].get_end()
|
||||
else:
|
||||
|
|
@ -332,7 +328,6 @@ class DashedLine(Line):
|
|||
>>> DashedLine().get_first_handle()
|
||||
array([-0.98333333, 0. , 0. ])
|
||||
"""
|
||||
|
||||
return self.submobjects[0].points[1]
|
||||
|
||||
def get_last_handle(self) -> Point3D:
|
||||
|
|
@ -345,7 +340,6 @@ class DashedLine(Line):
|
|||
>>> DashedLine().get_last_handle()
|
||||
array([0.98333333, 0. , 0. ])
|
||||
"""
|
||||
|
||||
return self.submobjects[-1].points[-2]
|
||||
|
||||
|
||||
|
|
@ -606,7 +600,6 @@ class Arrow(Line):
|
|||
>>> np.round(Arrow().get_normal_vector()) + 0. # add 0. to avoid negative 0 in output
|
||||
array([ 0., 0., -1.])
|
||||
"""
|
||||
|
||||
p0, p1, p2 = self.tip.get_start_anchors()[:3]
|
||||
return normalize(np.cross(p2 - p1, p1 - p0))
|
||||
|
||||
|
|
@ -626,7 +619,6 @@ class Arrow(Line):
|
|||
>>> Arrow().get_default_tip_length()
|
||||
0.35
|
||||
"""
|
||||
|
||||
max_ratio = self.max_tip_length_to_length_ratio
|
||||
return min(self.tip_length, max_ratio * self.get_length())
|
||||
|
||||
|
|
@ -720,7 +712,6 @@ class Vector(Arrow):
|
|||
|
||||
self.add(plane, vec_1, vec_2, label_1, label_2)
|
||||
"""
|
||||
|
||||
# avoiding circular imports
|
||||
from ..matrix import Matrix
|
||||
|
||||
|
|
@ -1020,11 +1011,10 @@ class Angle(VMobject, metaclass=ConvertToOpenGL):
|
|||
>>> angle.get_lines()
|
||||
VGroup(Line, Line)
|
||||
"""
|
||||
|
||||
return VGroup(*self.lines)
|
||||
|
||||
def get_value(self, degrees: bool = False) -> float:
|
||||
"""Get the value of an angle of the :class:`Angle` class.
|
||||
r"""Get the value of an angle of the :class:`Angle` class.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -1054,12 +1044,11 @@ class Angle(VMobject, metaclass=ConvertToOpenGL):
|
|||
|
||||
self.add(line1, line2, angle, value)
|
||||
"""
|
||||
|
||||
return self.angle_value / DEGREES if degrees else self.angle_value
|
||||
|
||||
@staticmethod
|
||||
def from_three_points(A: Point3D, B: Point3D, C: Point3D, **kwargs) -> Angle:
|
||||
"""The angle between the lines AB and BC.
|
||||
r"""The angle between the lines AB and BC.
|
||||
|
||||
This constructs the angle :math:`\\angle ABC`.
|
||||
|
||||
|
|
|
|||
|
|
@ -104,7 +104,6 @@ class Polygram(OpenGLVMobject):
|
|||
[-1., -1., 0.],
|
||||
[ 1., -1., 0.]])
|
||||
"""
|
||||
|
||||
return self.get_start_anchors()
|
||||
|
||||
def get_vertex_groups(self) -> np.ndarray[Point3D_Array]:
|
||||
|
|
@ -129,7 +128,6 @@ class Polygram(OpenGLVMobject):
|
|||
[-1., 1., 0.],
|
||||
[-2., 0., 0.]]])
|
||||
"""
|
||||
|
||||
vertex_groups = []
|
||||
|
||||
group = []
|
||||
|
|
@ -204,7 +202,6 @@ class Polygram(OpenGLVMobject):
|
|||
shapes.arrange(RIGHT)
|
||||
self.add(shapes)
|
||||
"""
|
||||
|
||||
if radius == 0:
|
||||
return self
|
||||
|
||||
|
|
|
|||
|
|
@ -334,7 +334,6 @@ class CoordinateSystem:
|
|||
)
|
||||
self.add(ax, y_label)
|
||||
"""
|
||||
|
||||
return self._get_axis_label(
|
||||
label, self.get_y_axis(), edge, direction, buff=buff, **kwargs
|
||||
)
|
||||
|
|
@ -367,7 +366,6 @@ class CoordinateSystem:
|
|||
:class:`~.Mobject`
|
||||
The positioned label along the given axis.
|
||||
"""
|
||||
|
||||
label = self.x_axis._create_label_tex(label)
|
||||
label.next_to(axis.get_edge_center(edge), direction=direction, buff=buff)
|
||||
label.shift_onto_screen(buff=MED_SMALL_BUFF)
|
||||
|
|
@ -421,7 +419,6 @@ class CoordinateSystem:
|
|||
x_dict = dict(zip(x_pos, x_vals))
|
||||
ax.add_coordinates(x_dict)
|
||||
"""
|
||||
|
||||
self.coordinate_labels = VGroup()
|
||||
# if nothing is passed to axes_numbers, produce axes with default labelling
|
||||
if not axes_numbers:
|
||||
|
|
@ -502,7 +499,6 @@ class CoordinateSystem:
|
|||
:meth:`~.CoordinateSystem.get_vertical_line`
|
||||
:meth:`~.CoordinateSystem.get_horizontal_line`
|
||||
"""
|
||||
|
||||
line_config = line_config if line_config is not None else {}
|
||||
|
||||
if color is None:
|
||||
|
|
@ -579,7 +575,6 @@ class CoordinateSystem:
|
|||
|
||||
self.add(ax, line, dot)
|
||||
"""
|
||||
|
||||
return self.get_line_from_axis_to_point(1, point, **kwargs)
|
||||
|
||||
def get_lines_to_point(self, point: Sequence[float], **kwargs) -> VGroup:
|
||||
|
|
@ -616,7 +611,6 @@ class CoordinateSystem:
|
|||
lines_2 = ax.get_lines_to_point(circ.get_corner(DL), color=BLUE_B)
|
||||
self.add(ax, lines_1, lines_2, circ)
|
||||
"""
|
||||
|
||||
return VGroup(
|
||||
self.get_horizontal_line(point, **kwargs),
|
||||
self.get_vertical_line(point, **kwargs),
|
||||
|
|
@ -709,7 +703,6 @@ class CoordinateSystem:
|
|||
|
||||
self.add(axes, curves)
|
||||
"""
|
||||
|
||||
t_range = np.array(self.x_range, dtype=float)
|
||||
if x_range is not None:
|
||||
t_range[: len(x_range)] = x_range
|
||||
|
|
@ -1036,7 +1029,6 @@ class CoordinateSystem:
|
|||
|
||||
self.add(ax, curve, sq)
|
||||
"""
|
||||
|
||||
if hasattr(graph, "underlying_function"):
|
||||
return graph.function(x)
|
||||
else:
|
||||
|
|
@ -1092,7 +1084,7 @@ class CoordinateSystem:
|
|||
dot: bool = False,
|
||||
dot_config: dict[str, Any] | None = None,
|
||||
) -> Mobject:
|
||||
"""Creates a properly positioned label for the passed graph, with an optional dot.
|
||||
r"""Creates a properly positioned label for the passed graph, with an optional dot.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -1129,7 +1121,7 @@ class CoordinateSystem:
|
|||
sin = ax.plot(lambda x: np.sin(x), color=PURPLE_B)
|
||||
label = ax.get_graph_label(
|
||||
graph=sin,
|
||||
label= MathTex(r"\\frac{\\pi}{2}"),
|
||||
label= MathTex(r"\frac{\pi}{2}"),
|
||||
x_val=PI / 2,
|
||||
dot=True,
|
||||
direction=UR,
|
||||
|
|
@ -1137,7 +1129,6 @@ class CoordinateSystem:
|
|||
|
||||
self.add(ax, sin, label)
|
||||
"""
|
||||
|
||||
if dot_config is None:
|
||||
dot_config = {}
|
||||
if color is None:
|
||||
|
|
@ -1257,7 +1248,6 @@ class CoordinateSystem:
|
|||
ax, bounding_line, quadratic, rects_right, rects_left, bounded_rects
|
||||
)
|
||||
"""
|
||||
|
||||
# setting up x_range, overwrite user's third input
|
||||
if x_range is None:
|
||||
if bounded_graph is None:
|
||||
|
|
@ -1447,7 +1437,6 @@ class CoordinateSystem:
|
|||
ax.angle_of_tangent(x=3, graph=curve)
|
||||
# 1.4056476493802699
|
||||
"""
|
||||
|
||||
p0 = np.array([*self.input_to_graph_coords(x, graph)])
|
||||
p1 = np.array([*self.input_to_graph_coords(x + dx, graph)])
|
||||
return angle_of_vector(p1 - p0)
|
||||
|
|
@ -1479,7 +1468,6 @@ class CoordinateSystem:
|
|||
ax.slope_of_tangent(x=-2, graph=curve)
|
||||
# -3.5000000259052038
|
||||
"""
|
||||
|
||||
return np.tan(self.angle_of_tangent(x, graph, **kwargs))
|
||||
|
||||
def plot_derivative_graph(
|
||||
|
|
@ -1755,7 +1743,6 @@ class CoordinateSystem:
|
|||
|
||||
self.add(ax, curve, lines)
|
||||
"""
|
||||
|
||||
x_range = x_range if x_range is not None else self.x_range
|
||||
|
||||
return VGroup(
|
||||
|
|
@ -1817,7 +1804,6 @@ class CoordinateSystem:
|
|||
t_label = axes.get_T_label(x_val=4, graph=func, label=Tex("x-value"))
|
||||
self.add(axes, func, t_label)
|
||||
"""
|
||||
|
||||
T_label_group = VGroup()
|
||||
triangle = RegularPolygon(n=3, start_angle=np.pi / 2, stroke_width=0).set_fill(
|
||||
color=triangle_color,
|
||||
|
|
@ -2009,7 +1995,6 @@ class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
|
|||
)
|
||||
)
|
||||
"""
|
||||
|
||||
for default_config, passed_config in zip(default_configs, passed_configs):
|
||||
if passed_config is not None:
|
||||
update_dict_recursively(default_config, passed_config)
|
||||
|
|
@ -2254,7 +2239,6 @@ class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
|
|||
)
|
||||
self.add(ax, labels)
|
||||
"""
|
||||
|
||||
self.axis_labels = VGroup(
|
||||
self.get_x_axis_label(x_label),
|
||||
self.get_y_axis_label(y_label),
|
||||
|
|
@ -2542,7 +2526,6 @@ class ThreeDAxes(Axes):
|
|||
self.set_camera_orientation(phi=2*PI/5, theta=PI/5)
|
||||
self.add(ax, lab)
|
||||
"""
|
||||
|
||||
positioned_label = self._get_axis_label(
|
||||
label, self.get_y_axis(), edge, direction, buff=buff, **kwargs
|
||||
)
|
||||
|
|
@ -2593,7 +2576,6 @@ class ThreeDAxes(Axes):
|
|||
self.set_camera_orientation(phi=2*PI/5, theta=PI/5)
|
||||
self.add(ax, lab)
|
||||
"""
|
||||
|
||||
positioned_label = self._get_axis_label(
|
||||
label, self.get_z_axis(), edge, direction, buff=buff, **kwargs
|
||||
)
|
||||
|
|
@ -2647,7 +2629,6 @@ class ThreeDAxes(Axes):
|
|||
)
|
||||
self.add(axes, labels)
|
||||
"""
|
||||
|
||||
self.axis_labels = VGroup(
|
||||
self.get_x_axis_label(x_label),
|
||||
self.get_y_axis_label(y_label),
|
||||
|
|
@ -2871,7 +2852,6 @@ class NumberPlane(Axes):
|
|||
The first (i.e the non-faded lines parallel to `axis_parallel_to`) and second
|
||||
(i.e the faded lines parallel to `axis_parallel_to`) sets of lines, respectively.
|
||||
"""
|
||||
|
||||
line = Line(axis_parallel_to.get_start(), axis_parallel_to.get_end())
|
||||
if ratio_faded_lines == 0: # don't show faded lines
|
||||
ratio_faded_lines = 1 # i.e. set ratio to 1
|
||||
|
|
@ -3383,7 +3363,6 @@ class ComplexPlane(NumberPlane):
|
|||
np.ndarray
|
||||
The point on the plane.
|
||||
"""
|
||||
|
||||
number = complex(number)
|
||||
return self.coords_to_point(number.real, number.imag)
|
||||
|
||||
|
|
@ -3404,7 +3383,6 @@ class ComplexPlane(NumberPlane):
|
|||
complex
|
||||
A complex number consisting of real and imaginary components.
|
||||
"""
|
||||
|
||||
x, y = self.point_to_coords(point)
|
||||
return complex(x, y)
|
||||
|
||||
|
|
@ -3442,7 +3420,6 @@ class ComplexPlane(NumberPlane):
|
|||
:class:`~.VGroup`
|
||||
A :class:`~.VGroup` containing the positioned label mobjects.
|
||||
"""
|
||||
|
||||
# TODO: Make this work the same as coord_sys.add_coordinates()
|
||||
if len(numbers) == 0:
|
||||
numbers = self._get_default_coordinate_values()
|
||||
|
|
@ -3473,6 +3450,5 @@ class ComplexPlane(NumberPlane):
|
|||
kwargs
|
||||
Additional arguments to be passed to :meth:`~.NumberLine.get_number_mobject`, i.e. :class:`~.DecimalNumber`.
|
||||
"""
|
||||
|
||||
self.add(self.get_coordinate_labels(*numbers, **kwargs))
|
||||
return self
|
||||
|
|
|
|||
|
|
@ -277,7 +277,8 @@ class NumberLine(Line):
|
|||
|
||||
def add_ticks(self):
|
||||
"""Adds ticks to the number line. Ticks can be accessed after creation
|
||||
via ``self.ticks``."""
|
||||
via ``self.ticks``.
|
||||
"""
|
||||
ticks = VGroup()
|
||||
elongated_tick_size = self.tick_size * self.longer_tick_multiple
|
||||
elongated_tick_offsets = self.numbers_with_elongated_ticks - self.x_min
|
||||
|
|
@ -580,7 +581,6 @@ class NumberLine(Line):
|
|||
AttributeError
|
||||
If the label does not have a ``font_size`` attribute, an ``AttributeError`` is raised.
|
||||
"""
|
||||
|
||||
direction = self.label_direction if direction is None else direction
|
||||
buff = self.line_to_number_buff if buff is None else buff
|
||||
font_size = self.font_size if font_size is None else font_size
|
||||
|
|
|
|||
|
|
@ -319,7 +319,6 @@ class BarChart(Axes):
|
|||
Primarily used when the bars are initialized with ``self._add_bars``
|
||||
or updated via ``self.change_bar_values``.
|
||||
"""
|
||||
|
||||
self.bars.set_color_by_gradient(*self.bar_colors)
|
||||
|
||||
def _add_x_axis_labels(self):
|
||||
|
|
@ -329,7 +328,6 @@ class BarChart(Axes):
|
|||
|
||||
UP for negative values and DOWN for positive values.
|
||||
"""
|
||||
|
||||
val_range = np.arange(
|
||||
0.5, len(self.bar_names), 1
|
||||
) # 0.5 shifted so that labels are centered, not on ticks
|
||||
|
|
@ -369,7 +367,6 @@ class BarChart(Axes):
|
|||
Rectangle
|
||||
A positioned rectangle representing a bar on the chart.
|
||||
"""
|
||||
|
||||
# bar measurements relative to the axis
|
||||
|
||||
# distance from between the y-axis and the top of the bar
|
||||
|
|
@ -432,7 +429,6 @@ class BarChart(Axes):
|
|||
|
||||
self.add(chart, c_bar_lbls)
|
||||
"""
|
||||
|
||||
bar_labels = VGroup()
|
||||
for bar, value in zip(self.bars, self.values):
|
||||
bar_lbl = label_constructor(str(value))
|
||||
|
|
@ -480,7 +476,6 @@ class BarChart(Axes):
|
|||
chart.change_bar_values(list(reversed(values)))
|
||||
self.add(chart.get_bar_labels(font_size=24))
|
||||
"""
|
||||
|
||||
for i, (bar, value) in enumerate(zip(self.bars, values)):
|
||||
chart_val = self.values[i]
|
||||
|
||||
|
|
|
|||
|
|
@ -90,7 +90,6 @@ class LinearBase(_ScaleBase):
|
|||
scale_factor
|
||||
The slope of the linear function, by default 1.0
|
||||
"""
|
||||
|
||||
super().__init__()
|
||||
self.scale_factor = scale_factor
|
||||
|
||||
|
|
@ -177,7 +176,6 @@ class LogBase(_ScaleBase):
|
|||
base_config
|
||||
Additional arguments to be passed to :class:`~.Integer`.
|
||||
"""
|
||||
|
||||
# uses `format` syntax to control the number of decimal places.
|
||||
tex_labels = [
|
||||
Integer(
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ def matrix_to_mobject(matrix):
|
|||
|
||||
|
||||
class Matrix(VMobject, metaclass=ConvertToOpenGL):
|
||||
"""A mobject that displays a matrix on the screen.
|
||||
r"""A mobject that displays a matrix on the screen.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -118,22 +118,22 @@ class Matrix(VMobject, metaclass=ConvertToOpenGL):
|
|||
|
||||
class MatrixExamples(Scene):
|
||||
def construct(self):
|
||||
m0 = Matrix([[2, "\\pi"], [-1, 1]])
|
||||
m0 = Matrix([[2, r"\pi"], [-1, 1]])
|
||||
m1 = Matrix([[2, 0, 4], [-1, 1, 5]],
|
||||
v_buff=1.3,
|
||||
h_buff=0.8,
|
||||
bracket_h_buff=SMALL_BUFF,
|
||||
bracket_v_buff=SMALL_BUFF,
|
||||
left_bracket="\\{",
|
||||
right_bracket="\\}")
|
||||
left_bracket=r"\{",
|
||||
right_bracket=r"\}")
|
||||
m1.add(SurroundingRectangle(m1.get_columns()[1]))
|
||||
m2 = Matrix([[2, 1], [-1, 3]],
|
||||
element_alignment_corner=UL,
|
||||
left_bracket="(",
|
||||
right_bracket=")")
|
||||
m3 = Matrix([[2, 1], [-1, 3]],
|
||||
left_bracket="\\\\langle",
|
||||
right_bracket="\\\\rangle")
|
||||
left_bracket=r"\langle",
|
||||
right_bracket=r"\rangle")
|
||||
m4 = Matrix([[2, 1], [-1, 3]],
|
||||
).set_column_colors(RED, GREEN)
|
||||
m5 = Matrix([[2, 1], [-1, 3]],
|
||||
|
|
@ -241,7 +241,6 @@ class Matrix(VMobject, metaclass=ConvertToOpenGL):
|
|||
:class:`Matrix`
|
||||
The current matrix object (self).
|
||||
"""
|
||||
|
||||
# Height per row of LaTeX array with default settings
|
||||
BRACKET_HEIGHT = 0.5977
|
||||
|
||||
|
|
@ -280,7 +279,7 @@ class Matrix(VMobject, metaclass=ConvertToOpenGL):
|
|||
return self
|
||||
|
||||
def get_columns(self):
|
||||
"""Return columns of the matrix as VGroups.
|
||||
r"""Return columns of the matrix as VGroups.
|
||||
|
||||
Returns
|
||||
--------
|
||||
|
|
@ -299,7 +298,6 @@ class Matrix(VMobject, metaclass=ConvertToOpenGL):
|
|||
m0.add(SurroundingRectangle(m0.get_columns()[1]))
|
||||
self.add(m0)
|
||||
"""
|
||||
|
||||
return VGroup(
|
||||
*(
|
||||
VGroup(*(row[i] for row in self.mob_matrix))
|
||||
|
|
@ -308,7 +306,7 @@ class Matrix(VMobject, metaclass=ConvertToOpenGL):
|
|||
)
|
||||
|
||||
def set_column_colors(self, *colors: str):
|
||||
"""Set individual colors for each columns of the matrix.
|
||||
r"""Set individual colors for each columns of the matrix.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -338,7 +336,7 @@ class Matrix(VMobject, metaclass=ConvertToOpenGL):
|
|||
return self
|
||||
|
||||
def get_rows(self):
|
||||
"""Return rows of the matrix as VGroups.
|
||||
r"""Return rows of the matrix as VGroups.
|
||||
|
||||
Returns
|
||||
--------
|
||||
|
|
@ -360,7 +358,7 @@ class Matrix(VMobject, metaclass=ConvertToOpenGL):
|
|||
return VGroup(*(VGroup(*row) for row in self.mob_matrix))
|
||||
|
||||
def set_row_colors(self, *colors: str):
|
||||
"""Set individual colors for each row of the matrix.
|
||||
r"""Set individual colors for each row of the matrix.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -438,7 +436,7 @@ class Matrix(VMobject, metaclass=ConvertToOpenGL):
|
|||
return self.elements
|
||||
|
||||
def get_brackets(self):
|
||||
"""Return the bracket mobjects.
|
||||
r"""Return the bracket mobjects.
|
||||
|
||||
Returns
|
||||
--------
|
||||
|
|
@ -464,7 +462,7 @@ class Matrix(VMobject, metaclass=ConvertToOpenGL):
|
|||
|
||||
|
||||
class DecimalMatrix(Matrix):
|
||||
"""A mobject that displays a matrix with decimal entries on the screen.
|
||||
r"""A mobject that displays a matrix with decimal entries on the screen.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
|
@ -544,7 +542,7 @@ class IntegerMatrix(Matrix):
|
|||
|
||||
|
||||
class MobjectMatrix(Matrix):
|
||||
"""A mobject that displays a matrix of mobject entries on the screen.
|
||||
r"""A mobject that displays a matrix of mobject entries on the screen.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
|
|
|||
|
|
@ -666,7 +666,6 @@ class Mobject:
|
|||
>>> mob.foo
|
||||
0
|
||||
"""
|
||||
|
||||
for attr, value in kwargs.items():
|
||||
setattr(self, attr, value)
|
||||
|
||||
|
|
@ -745,7 +744,6 @@ class Mobject:
|
|||
:meth:`length_over_dim`
|
||||
|
||||
"""
|
||||
|
||||
# Get the length across the X dimension
|
||||
return self.length_over_dim(0)
|
||||
|
||||
|
|
@ -782,7 +780,6 @@ class Mobject:
|
|||
:meth:`length_over_dim`
|
||||
|
||||
"""
|
||||
|
||||
# Get the length across the Y dimension
|
||||
return self.length_over_dim(1)
|
||||
|
||||
|
|
@ -803,7 +800,6 @@ class Mobject:
|
|||
:meth:`length_over_dim`
|
||||
|
||||
"""
|
||||
|
||||
# Get the length across the Z dimension
|
||||
return self.length_over_dim(2)
|
||||
|
||||
|
|
@ -835,7 +831,8 @@ class Mobject:
|
|||
|
||||
def save_image(self, name: str | None = None) -> None:
|
||||
"""Saves an image of only this :class:`Mobject` at its position to a png
|
||||
file."""
|
||||
file.
|
||||
"""
|
||||
self.get_image().save(
|
||||
Path(config.get_dir("video_dir")).joinpath((name or str(self)) + ".png"),
|
||||
)
|
||||
|
|
@ -1026,7 +1023,6 @@ class Mobject:
|
|||
:meth:`remove_updater`
|
||||
:class:`~.UpdateFromFunc`
|
||||
"""
|
||||
|
||||
if index is None:
|
||||
self.updaters.append(update_function)
|
||||
else:
|
||||
|
|
@ -1116,7 +1112,6 @@ class Mobject:
|
|||
:meth:`clear_updaters`
|
||||
|
||||
"""
|
||||
|
||||
self.clear_updaters()
|
||||
for updater in mobject.get_updaters():
|
||||
self.add_updater(updater)
|
||||
|
|
@ -1142,7 +1137,6 @@ class Mobject:
|
|||
:meth:`add_updater`
|
||||
|
||||
"""
|
||||
|
||||
self.updating_suspended = True
|
||||
if recursive:
|
||||
for submob in self.submobjects:
|
||||
|
|
@ -1217,7 +1211,6 @@ class Mobject:
|
|||
--------
|
||||
:meth:`move_to`
|
||||
"""
|
||||
|
||||
total_vector = reduce(op.add, vectors)
|
||||
for mob in self.family_members_with_points():
|
||||
mob.points = mob.points.astype("float")
|
||||
|
|
@ -1617,7 +1610,6 @@ class Mobject:
|
|||
>>> sq.height
|
||||
5.0
|
||||
"""
|
||||
|
||||
return self.rescale_to_fit(width, 0, stretch=False, **kwargs)
|
||||
|
||||
def stretch_to_fit_width(self, width: float, **kwargs) -> Self:
|
||||
|
|
@ -1643,7 +1635,6 @@ class Mobject:
|
|||
>>> sq.height
|
||||
2.0
|
||||
"""
|
||||
|
||||
return self.rescale_to_fit(width, 0, stretch=True, **kwargs)
|
||||
|
||||
def scale_to_fit_height(self, height: float, **kwargs) -> Self:
|
||||
|
|
@ -1669,7 +1660,6 @@ class Mobject:
|
|||
>>> sq.width
|
||||
5.0
|
||||
"""
|
||||
|
||||
return self.rescale_to_fit(height, 1, stretch=False, **kwargs)
|
||||
|
||||
def stretch_to_fit_height(self, height: float, **kwargs) -> Self:
|
||||
|
|
@ -1695,17 +1685,14 @@ class Mobject:
|
|||
>>> sq.width
|
||||
2.0
|
||||
"""
|
||||
|
||||
return self.rescale_to_fit(height, 1, stretch=True, **kwargs)
|
||||
|
||||
def scale_to_fit_depth(self, depth: float, **kwargs) -> Self:
|
||||
"""Scales the :class:`~.Mobject` to fit a depth while keeping width/height proportional."""
|
||||
|
||||
return self.rescale_to_fit(depth, 2, stretch=False, **kwargs)
|
||||
|
||||
def stretch_to_fit_depth(self, depth: float, **kwargs) -> Self:
|
||||
"""Stretches the :class:`~.Mobject` to fit a depth, not keeping width/height proportional."""
|
||||
|
||||
return self.rescale_to_fit(depth, 2, stretch=True, **kwargs)
|
||||
|
||||
def set_coord(self, value, dim: int, direction: Vector3D = ORIGIN) -> Self:
|
||||
|
|
@ -1832,7 +1819,6 @@ class Mobject:
|
|||
:class:`~.BackgroundRectangle`
|
||||
|
||||
"""
|
||||
|
||||
# TODO, this does not behave well when the mobject has points,
|
||||
# since it gets displayed on top
|
||||
from manim.mobject.geometry.shape_matchers import BackgroundRectangle
|
||||
|
|
|
|||
|
|
@ -187,7 +187,8 @@ class OpenGLTipableVMobject(OpenGLVMobject):
|
|||
|
||||
def get_tip(self):
|
||||
"""Returns the TipableVMobject instance's (first) tip,
|
||||
otherwise throws an exception."""
|
||||
otherwise throws an exception.
|
||||
"""
|
||||
tips = self.get_tips()
|
||||
if len(tips) == 0:
|
||||
raise Exception("tip not found")
|
||||
|
|
@ -516,9 +517,7 @@ class OpenGLLine(OpenGLTipableVMobject):
|
|||
return angle_of_vector(self.get_vector())
|
||||
|
||||
def get_projection(self, point):
|
||||
"""
|
||||
Return projection of a point onto the line
|
||||
"""
|
||||
"""Return projection of a point onto the line"""
|
||||
unit_vect = self.get_unit_vector()
|
||||
start = self.get_start()
|
||||
return start + np.dot(point - start, unit_vect) * unit_vect
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ from manim.event_handler.event_listener import EventListener
|
|||
from manim.event_handler.event_type import EventType
|
||||
from manim.utils.bezier import integer_interpolate, interpolate
|
||||
from manim.utils.color import *
|
||||
from manim.utils.deprecation import deprecated
|
||||
|
||||
# from ..utils.iterables import batch_by_property
|
||||
from manim.utils.iterables import (
|
||||
|
|
@ -261,17 +260,29 @@ class OpenGLMobject:
|
|||
def init_colors(self):
|
||||
"""Initializes the colors.
|
||||
|
||||
Gets called upon creation"""
|
||||
Gets called upon creation
|
||||
"""
|
||||
self.set_color(self.color, self.opacity)
|
||||
|
||||
def init_points(self):
|
||||
"""Initializes :attr:`points` and therefore the shape.
|
||||
|
||||
Gets called upon creation. This is an empty method that can be implemented by
|
||||
subclasses."""
|
||||
subclasses.
|
||||
"""
|
||||
# Typically implemented in subclass, unless purposefully left blank
|
||||
pass
|
||||
|
||||
def set_data(self, data: dict[str, Any]) -> Self:
|
||||
for key in data:
|
||||
self.data[key] = data[key].copy()
|
||||
return self
|
||||
|
||||
def set_uniforms(self, uniforms: dict[str, Any]) -> Self:
|
||||
for key in uniforms:
|
||||
self.uniforms[key] = uniforms[key] # Copy?
|
||||
return self
|
||||
|
||||
# https://github.com/python/typing/issues/802
|
||||
# so we hack around it by doing | Self
|
||||
# but this causes issues in Scene.play which only
|
||||
|
|
@ -486,7 +497,6 @@ class OpenGLMobject:
|
|||
:meth:`length_over_dim`
|
||||
|
||||
"""
|
||||
|
||||
# Get the length across the X dimension
|
||||
return self.length_over_dim(0)
|
||||
|
||||
|
|
@ -524,7 +534,6 @@ class OpenGLMobject:
|
|||
:meth:`length_over_dim`
|
||||
|
||||
"""
|
||||
|
||||
# Get the length across the Y dimension
|
||||
return self.length_over_dim(1)
|
||||
|
||||
|
|
@ -545,7 +554,6 @@ class OpenGLMobject:
|
|||
:meth:`length_over_dim`
|
||||
|
||||
"""
|
||||
|
||||
# Get the length across the Z dimension
|
||||
return self.length_over_dim(2)
|
||||
|
||||
|
|
@ -1664,7 +1672,7 @@ class OpenGLMobject:
|
|||
return self
|
||||
|
||||
def _handle_scale_side_effects(self, scale_factor: float | np.ndarray) -> None:
|
||||
"""In case subclasses, such as DecimalNumber, need to make
|
||||
r"""In case subclasses, such as DecimalNumber, need to make
|
||||
any other changes when the size gets altered by scaling.
|
||||
This method can be overridden in subclasses.
|
||||
|
||||
|
|
@ -2798,10 +2806,6 @@ class OpenGLMobject:
|
|||
caller_name = sys._getframe(1).f_code.co_name
|
||||
raise Exception(message.format(caller_name))
|
||||
|
||||
@deprecated(
|
||||
since="v0.17.2",
|
||||
message="The usage of this method is discouraged please set attributes directly",
|
||||
)
|
||||
def set(self, **kwargs) -> Self:
|
||||
"""Sets attributes.
|
||||
|
||||
|
|
@ -2830,7 +2834,6 @@ class OpenGLMobject:
|
|||
|
||||
|
||||
"""
|
||||
|
||||
for attr, value in kwargs.items():
|
||||
setattr(self, attr, value)
|
||||
|
||||
|
|
|
|||
|
|
@ -65,9 +65,7 @@ class OpenGLPMobject(OpenGLMobject):
|
|||
return self
|
||||
|
||||
def thin_out(self, factor=5):
|
||||
"""
|
||||
Removes all but every nth point for n = factor
|
||||
"""
|
||||
"""Removes all but every nth point for n = factor"""
|
||||
for mob in self.family_members_with_points():
|
||||
num_points = mob.get_num_points()
|
||||
|
||||
|
|
@ -126,9 +124,7 @@ class OpenGLPMobject(OpenGLMobject):
|
|||
return self
|
||||
|
||||
def sort_points(self, function=lambda p: p[0]):
|
||||
"""
|
||||
function is any map from R^3 to R
|
||||
"""
|
||||
"""function is any map from R^3 to R"""
|
||||
for mob in self.family_members_with_points():
|
||||
indices = np.argsort(np.apply_along_axis(function, 1, mob.points))
|
||||
for key in mob.data:
|
||||
|
|
|
|||
|
|
@ -414,9 +414,7 @@ class OpenGLVMobject(OpenGLMobject):
|
|||
self.append_points(new_points)
|
||||
|
||||
def add_cubic_bezier_curve_to(self, handle1, handle2, anchor):
|
||||
"""
|
||||
Add cubic bezier curve to the path.
|
||||
"""
|
||||
"""Add cubic bezier curve to the path."""
|
||||
self.throw_error_if_no_points()
|
||||
quadratic_approx = get_quadratic_approximation_of_cubic(
|
||||
self.get_last_point(),
|
||||
|
|
@ -767,7 +765,6 @@ class OpenGLVMobject(OpenGLMobject):
|
|||
length : :class:`float`
|
||||
The length of the nth curve.
|
||||
"""
|
||||
|
||||
if sample_points is None:
|
||||
sample_points = 10
|
||||
|
||||
|
|
@ -814,7 +811,6 @@ class OpenGLVMobject(OpenGLMobject):
|
|||
length : :class:`float`
|
||||
The length of the nth curve.
|
||||
"""
|
||||
|
||||
_, length = self.get_nth_curve_function_with_length(n, sample_points)
|
||||
|
||||
return length
|
||||
|
|
@ -829,7 +825,6 @@ class OpenGLVMobject(OpenGLMobject):
|
|||
Iterable[Callable[[float], np.ndarray]]
|
||||
The functions for the curves.
|
||||
"""
|
||||
|
||||
num_curves = self.get_num_curves()
|
||||
|
||||
for n in range(num_curves):
|
||||
|
|
@ -879,7 +874,6 @@ class OpenGLVMobject(OpenGLMobject):
|
|||
Iterable[Tuple[Callable[[float], np.ndarray], float]]
|
||||
The functions and lengths of the curves.
|
||||
"""
|
||||
|
||||
num_curves = self.get_num_curves()
|
||||
|
||||
for n in range(num_curves):
|
||||
|
|
@ -905,7 +899,6 @@ class OpenGLVMobject(OpenGLMobject):
|
|||
:exc:`Exception`
|
||||
If the :class:`OpenGLVMobject` has no points.
|
||||
"""
|
||||
|
||||
if alpha < 0 or alpha > 1:
|
||||
raise ValueError(f"Alpha {alpha} not between 0 and 1.")
|
||||
|
||||
|
|
@ -1067,7 +1060,6 @@ class OpenGLVMobject(OpenGLMobject):
|
|||
float
|
||||
The length of the :class:`OpenGLVMobject`.
|
||||
"""
|
||||
|
||||
if n_sample_points is None:
|
||||
n_sample_points = 4 * self.get_num_curves() + 1
|
||||
points = np.array(
|
||||
|
|
@ -1238,7 +1230,6 @@ class OpenGLVMobject(OpenGLMobject):
|
|||
np.ndarray
|
||||
Points generated.
|
||||
"""
|
||||
|
||||
if len(points) == 1:
|
||||
nppc = self.n_points_per_curve
|
||||
return np.repeat(points, nppc * n, 0)
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ from ..utils.color import BLACK, YELLOW, ManimColor, ParsableManimColor
|
|||
|
||||
|
||||
class Table(VGroup):
|
||||
"""A mobject that displays a table on the screen.
|
||||
r"""A mobject that displays a table on the screen.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -682,7 +682,6 @@ class Table(VGroup):
|
|||
item.set_color(random_bright_color())
|
||||
self.add(table)
|
||||
"""
|
||||
|
||||
return VGroup(*self.row_labels)
|
||||
|
||||
def get_col_labels(self) -> VGroup:
|
||||
|
|
@ -711,7 +710,6 @@ class Table(VGroup):
|
|||
item.set_color(random_bright_color())
|
||||
self.add(table)
|
||||
"""
|
||||
|
||||
return VGroup(*self.col_labels)
|
||||
|
||||
def get_labels(self) -> VGroup:
|
||||
|
|
@ -1066,7 +1064,7 @@ class MobjectTable(Table):
|
|||
|
||||
|
||||
class IntegerTable(Table):
|
||||
"""A specialized :class:`~.Table` mobject for use with :class:`~.Integer`.
|
||||
r"""A specialized :class:`~.Table` mobject for use with :class:`~.Integer`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
|
@ -1080,14 +1078,14 @@ class IntegerTable(Table):
|
|||
[[0,30,45,60,90],
|
||||
[90,60,45,30,0]],
|
||||
col_labels=[
|
||||
MathTex("\\\\frac{\\sqrt{0}}{2}"),
|
||||
MathTex("\\\\frac{\\sqrt{1}}{2}"),
|
||||
MathTex("\\\\frac{\\sqrt{2}}{2}"),
|
||||
MathTex("\\\\frac{\\sqrt{3}}{2}"),
|
||||
MathTex("\\\\frac{\\sqrt{4}}{2}")],
|
||||
row_labels=[MathTex("\\sin"), MathTex("\\cos")],
|
||||
MathTex(r"\frac{\sqrt{0}}{2}"),
|
||||
MathTex(r"\frac{\sqrt{1}}{2}"),
|
||||
MathTex(r"\frac{\sqrt{2}}{2}"),
|
||||
MathTex(r"\frac{\sqrt{3}}{2}"),
|
||||
MathTex(r"\frac{\sqrt{4}}{2}")],
|
||||
row_labels=[MathTex(r"\sin"), MathTex(r"\cos")],
|
||||
h_buff=1,
|
||||
element_to_mobject_config={"unit": "^{\\circ}"})
|
||||
element_to_mobject_config={"unit": r"^{\circ}"})
|
||||
self.add(t0)
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ __all__ = ["DecimalNumber", "Integer", "Variable"]
|
|||
|
||||
|
||||
class DecimalNumber(VMobject, metaclass=ConvertToOpenGL):
|
||||
"""An mobject representing a decimal number.
|
||||
r"""An mobject representing a decimal number.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ r"""Mobjects representing text rendered using LaTeX.
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from manim.utils.color import ManimColor
|
||||
from manim.utils.color import BLACK, ManimColor, ParsableManimColor
|
||||
|
||||
__all__ = [
|
||||
"SingleStringMathTex",
|
||||
|
|
@ -62,6 +62,7 @@ class SingleStringMathTex(SVGMobject):
|
|||
tex_environment: str = "align*",
|
||||
tex_template: TexTemplate | None = None,
|
||||
font_size: float = DEFAULT_FONT_SIZE,
|
||||
color: ParsableManimColor | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
self._font_size = font_size
|
||||
|
|
@ -83,6 +84,7 @@ class SingleStringMathTex(SVGMobject):
|
|||
should_center=should_center,
|
||||
stroke_width=stroke_width,
|
||||
height=height,
|
||||
color=color,
|
||||
path_string_config={
|
||||
"should_subdivide_sharp_curves": True,
|
||||
"should_remove_null_curves": True,
|
||||
|
|
@ -186,7 +188,6 @@ class SingleStringMathTex(SVGMobject):
|
|||
This is important when the braces in the TeX code are spread over
|
||||
multiple arguments as in, e.g., ``MathTex(r"e^{i", r"\tau} = 1")``.
|
||||
"""
|
||||
|
||||
# "\{" does not count (it's a brace literal), but "\\{" counts (it's a new line and then brace)
|
||||
num_lefts = tex.count("{") - tex.count("\\{") + tex.count("\\\\{")
|
||||
num_rights = tex.count("}") - tex.count("\\}") + tex.count("\\\\}")
|
||||
|
|
@ -205,6 +206,19 @@ class SingleStringMathTex(SVGMobject):
|
|||
def get_tex_string(self):
|
||||
return self.tex_string
|
||||
|
||||
def init_colors(self, propagate_colors=True):
|
||||
for submobject in self.submobjects:
|
||||
# needed to preserve original (non-black)
|
||||
# TeX colors of individual submobjects
|
||||
if submobject.color != BLACK:
|
||||
continue
|
||||
submobject.color = self.color
|
||||
if config.renderer == RendererType.OPENGL:
|
||||
submobject.init_colors()
|
||||
elif config.renderer == RendererType.CAIRO:
|
||||
submobject.init_colors(propagate_colors=propagate_colors)
|
||||
return self
|
||||
|
||||
|
||||
class MathTex(SingleStringMathTex):
|
||||
r"""A string compiled with LaTeX in math mode.
|
||||
|
|
@ -424,6 +438,10 @@ class MathTex(SingleStringMathTex):
|
|||
class Tex(MathTex):
|
||||
r"""A string compiled with LaTeX in normal mode.
|
||||
|
||||
The color can be set using
|
||||
the ``color`` argument. Any parts of the ``tex_string`` that are colored by the
|
||||
TeX commands ``\color`` or ``\textcolor`` will retain their original color.
|
||||
|
||||
Tests
|
||||
-----
|
||||
|
||||
|
|
|
|||
|
|
@ -670,7 +670,8 @@ class Text(SVGMobject):
|
|||
)
|
||||
def _set_color_by_t2g(self, t2g=None):
|
||||
"""Sets gradient colors for specified
|
||||
strings. Behaves similarly to ``set_color_by_t2c``."""
|
||||
strings. Behaves similarly to ``set_color_by_t2c``.
|
||||
"""
|
||||
t2g = t2g if t2g else self.t2g
|
||||
for word, gradient in list(t2g.items()):
|
||||
for start, end in self._find_indexes(word, self.text):
|
||||
|
|
@ -1411,7 +1412,8 @@ class MarkupText(SVGMobject):
|
|||
"""Counts characters that will be displayed.
|
||||
|
||||
This is needed for partial coloring or gradients, because space
|
||||
counts to the text's `len`, but has no corresponding character."""
|
||||
counts to the text's `len`, but has no corresponding character.
|
||||
"""
|
||||
count = 0
|
||||
level = 0
|
||||
# temporarily replace HTML entities by single char
|
||||
|
|
@ -1546,7 +1548,6 @@ def register_font(font_file: str | Path):
|
|||
This method is available for macOS for ``ManimPango>=v0.2.3``. Using this
|
||||
method with previous releases will raise an :class:`AttributeError` on macOS.
|
||||
"""
|
||||
|
||||
input_folder = Path(config.input_file).parent.resolve()
|
||||
possible_paths = [
|
||||
Path(font_file),
|
||||
|
|
|
|||
|
|
@ -142,9 +142,7 @@ class PMobject(Mobject, metaclass=ConvertToOpenGL):
|
|||
return self
|
||||
|
||||
def thin_out(self, factor=5):
|
||||
"""
|
||||
Removes all but every nth point for n = factor
|
||||
"""
|
||||
"""Removes all but every nth point for n = factor"""
|
||||
for mob in self.family_members_with_points():
|
||||
num_points = self.get_num_points()
|
||||
mob.apply_over_attr_arrays(
|
||||
|
|
@ -153,9 +151,7 @@ class PMobject(Mobject, metaclass=ConvertToOpenGL):
|
|||
return self
|
||||
|
||||
def sort_points(self, function=lambda p: p[0]):
|
||||
"""
|
||||
Function is any map from R^3 to R
|
||||
"""
|
||||
"""Function is any map from R^3 to R"""
|
||||
for mob in self.family_members_with_points():
|
||||
indices = np.argsort(np.apply_along_axis(function, 1, mob.points))
|
||||
mob.apply_over_attr_arrays(lambda arr, idx=indices: arr[idx])
|
||||
|
|
|
|||
|
|
@ -589,7 +589,6 @@ class VMobject(Mobject):
|
|||
:meth:`~.VMobject.set_sheen`
|
||||
:meth:`~.VMobject.rotate_sheen_direction`
|
||||
"""
|
||||
|
||||
direction = np.array(direction)
|
||||
if family:
|
||||
for submob in self.get_family():
|
||||
|
|
@ -655,7 +654,6 @@ class VMobject(Mobject):
|
|||
circle = Circle(fill_opacity=1).set_sheen(-0.3, DR)
|
||||
self.add(circle)
|
||||
"""
|
||||
|
||||
if family:
|
||||
for submob in self.submobjects:
|
||||
submob.set_sheen(factor, direction, family)
|
||||
|
|
@ -1393,7 +1391,6 @@ class VMobject(Mobject):
|
|||
length : :class:`float`
|
||||
The length of the nth curve.
|
||||
"""
|
||||
|
||||
_, length = self.get_nth_curve_function_with_length(n, sample_points)
|
||||
|
||||
return length
|
||||
|
|
@ -1419,7 +1416,6 @@ class VMobject(Mobject):
|
|||
length : :class:`float`
|
||||
The length of the nth curve.
|
||||
"""
|
||||
|
||||
curve = self.get_nth_curve_function(n)
|
||||
norms = self.get_nth_curve_length_pieces(n, sample_points=sample_points)
|
||||
length = np.sum(norms)
|
||||
|
|
@ -1447,7 +1443,6 @@ class VMobject(Mobject):
|
|||
Generator[Callable[[float], Point3D]]
|
||||
The functions for the curves.
|
||||
"""
|
||||
|
||||
num_curves = self.get_num_curves()
|
||||
|
||||
for n in range(num_curves):
|
||||
|
|
@ -1468,7 +1463,6 @@ class VMobject(Mobject):
|
|||
Generator[tuple[Callable[[float], Point3D], float]]
|
||||
The functions and lengths of the curves.
|
||||
"""
|
||||
|
||||
num_curves = self.get_num_curves()
|
||||
|
||||
for n in range(num_curves):
|
||||
|
|
@ -1510,7 +1504,6 @@ class VMobject(Mobject):
|
|||
line.point_from_proportion(proportion)
|
||||
))
|
||||
"""
|
||||
|
||||
if alpha < 0 or alpha > 1:
|
||||
raise ValueError(f"Alpha {alpha} not between 0 and 1.")
|
||||
|
||||
|
|
@ -1662,7 +1655,6 @@ class VMobject(Mobject):
|
|||
float
|
||||
The length of the :class:`VMobject`.
|
||||
"""
|
||||
|
||||
return sum(
|
||||
length
|
||||
for _, length in self.get_curve_functions_with_lengths(
|
||||
|
|
@ -1782,7 +1774,6 @@ class VMobject(Mobject):
|
|||
-------
|
||||
Points generated.
|
||||
"""
|
||||
|
||||
if len(points) == 1:
|
||||
nppcc = self.n_points_per_cubic_curve
|
||||
return np.repeat(points, nppcc * n, 0)
|
||||
|
|
|
|||
|
|
@ -165,7 +165,8 @@ class ComplexValueTracker(ValueTracker):
|
|||
"""Get the current value of this value tracker as a complex number.
|
||||
|
||||
The value is internally stored as a points array [a, b, 0]. This can be accessed directly
|
||||
to represent the value geometrically, see the usage example."""
|
||||
to represent the value geometrically, see the usage example.
|
||||
"""
|
||||
return complex(*self.points[0, :2])
|
||||
|
||||
def set_value(self, z):
|
||||
|
|
|
|||
|
|
@ -352,7 +352,6 @@ class VectorField(VGroup):
|
|||
This vector field.
|
||||
|
||||
"""
|
||||
|
||||
self.stop_submobject_movement()
|
||||
self.submob_movement_updater = lambda mob, dt: mob.nudge_submobjects(
|
||||
dt * speed,
|
||||
|
|
@ -950,7 +949,6 @@ class StreamLines(VectorField):
|
|||
self.wait(stream_lines.virtual_time / stream_lines.flow_speed)
|
||||
|
||||
"""
|
||||
|
||||
for line in self.stream_lines:
|
||||
run_time = line.duration / flow_speed
|
||||
line.anim = line_animation_class(
|
||||
|
|
@ -1010,7 +1008,6 @@ class StreamLines(VectorField):
|
|||
self.play(stream_lines.end_animation())
|
||||
|
||||
"""
|
||||
|
||||
if self.flow_animation is None:
|
||||
raise ValueError("You have to start the animation before fading it out.")
|
||||
|
||||
|
|
|
|||
|
|
@ -140,13 +140,10 @@ class Scene:
|
|||
)
|
||||
|
||||
def tear_down(self) -> None:
|
||||
"""
|
||||
This method is used to clean up scenes
|
||||
"""
|
||||
"""This method is used to clean up scenes"""
|
||||
|
||||
def find_sections(self) -> list[SceneSection]:
|
||||
"""Find all sections in a :class:`.Scene`"""
|
||||
|
||||
sections: list[SceneSection] = [
|
||||
bound
|
||||
for _, bound in inspect.getmembers(
|
||||
|
|
|
|||
|
|
@ -86,7 +86,6 @@ class ThreeDScene(Scene):
|
|||
The new center of the camera frame in cartesian coordinates.
|
||||
|
||||
"""
|
||||
|
||||
if phi is not None:
|
||||
self.renderer.camera.set_phi(phi)
|
||||
if theta is not None:
|
||||
|
|
@ -139,9 +138,7 @@ class ThreeDScene(Scene):
|
|||
raise ValueError("Invalid ambient rotation angle.") from e
|
||||
|
||||
def stop_ambient_camera_rotation(self, about="theta"):
|
||||
"""
|
||||
This method stops all ambient camera rotation.
|
||||
"""
|
||||
"""This method stops all ambient camera rotation."""
|
||||
about: str = about.lower()
|
||||
try:
|
||||
if config.renderer == RendererType.CAIRO:
|
||||
|
|
@ -205,9 +202,7 @@ class ThreeDScene(Scene):
|
|||
self.add(self.renderer.camera.phi_tracker)
|
||||
|
||||
def stop_3dillusion_camera_rotation(self):
|
||||
"""
|
||||
This method stops all illusion camera rotations.
|
||||
"""
|
||||
"""This method stops all illusion camera rotations."""
|
||||
self.renderer.camera.theta_tracker.clear_updaters()
|
||||
self.remove(self.renderer.camera.theta_tracker)
|
||||
self.renderer.camera.phi_tracker.clear_updaters()
|
||||
|
|
@ -546,7 +541,5 @@ class SpecialThreeDScene(ThreeDScene):
|
|||
return self.default_angled_camera_position
|
||||
|
||||
def set_camera_to_default_position(self):
|
||||
"""
|
||||
Sets the camera to its default position.
|
||||
"""
|
||||
"""Sets the camera to its default position."""
|
||||
self.set_camera_orientation(**self.default_angled_camera_position)
|
||||
|
|
|
|||
|
|
@ -1002,7 +1002,6 @@ class LinearTransformationScene(VectorScene):
|
|||
Animation
|
||||
The animation of the movement.
|
||||
"""
|
||||
|
||||
v_pieces = [piece for piece in pieces if isinstance(piece, VMobject)]
|
||||
start = VGroup(*v_pieces)
|
||||
target = VGroup(*(mob.target for mob in v_pieces))
|
||||
|
|
@ -1093,7 +1092,6 @@ class LinearTransformationScene(VectorScene):
|
|||
**kwargs
|
||||
Any valid keyword argument of self.apply_transposed_matrix()
|
||||
"""
|
||||
|
||||
self.apply_transposed_matrix(np.array(matrix).T, **kwargs)
|
||||
|
||||
def apply_inverse(self, matrix: np.ndarray | list | tuple, **kwargs):
|
||||
|
|
|
|||
|
|
@ -542,7 +542,6 @@ def split_bezier(points: BezierPoints, t: float) -> Point3D_Array:
|
|||
:class:`~.Point3D_Array`
|
||||
An array containing the control points defining the two Bézier curves.
|
||||
"""
|
||||
|
||||
points = np.asarray(points)
|
||||
N, dim = points.shape
|
||||
degree = N - 1
|
||||
|
|
@ -1115,7 +1114,7 @@ def match_interpolate(
|
|||
return interpolate(
|
||||
new_start,
|
||||
new_end,
|
||||
old_alpha, # type: ignore
|
||||
old_alpha, # type: ignore[arg-type]
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -1931,7 +1930,7 @@ def proportions_along_bezier_curve_for_point(
|
|||
# Roots will be none, but in this specific instance, we don't need to consider that.
|
||||
continue
|
||||
bezier_polynom = np.polynomial.Polynomial(terms[::-1])
|
||||
polynom_roots = bezier_polynom.roots() # type: ignore
|
||||
polynom_roots = bezier_polynom.roots()
|
||||
if len(polynom_roots) > 0:
|
||||
polynom_roots = np.around(polynom_roots, int(np.log10(1 / round_to)))
|
||||
roots.append(polynom_roots)
|
||||
|
|
@ -1939,7 +1938,7 @@ def proportions_along_bezier_curve_for_point(
|
|||
roots = [[root for root in rootlist if root.imag == 0] for rootlist in roots]
|
||||
# Get common roots
|
||||
# arg-type: ignore
|
||||
roots = reduce(np.intersect1d, roots) # type: ignore
|
||||
roots = reduce(np.intersect1d, roots)
|
||||
result = np.asarray([r.real for r in roots if 0 <= r.real <= 1])
|
||||
return result
|
||||
|
||||
|
|
@ -1971,7 +1970,6 @@ def point_lies_on_bezier(
|
|||
bool
|
||||
Whether the point lies on the curve.
|
||||
"""
|
||||
|
||||
roots = proportions_along_bezier_curve_for_point(point, control_points, round_to)
|
||||
|
||||
return len(roots) > 0
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ def handle_caching_play(func: Callable[..., None]):
|
|||
The play like function that has to be written to the video file stream.
|
||||
Take the same parameters as `scene.play`.
|
||||
"""
|
||||
|
||||
# NOTE : This is only kept for OpenGL renderer.
|
||||
# The play logic of the cairo renderer as been refactored and does not need this function anymore.
|
||||
# When OpenGL renderer will have a proper testing system,
|
||||
|
|
|
|||
|
|
@ -163,9 +163,9 @@ class ManimColor:
|
|||
length = len(value)
|
||||
if all(isinstance(x, float) for x in value):
|
||||
if length == 3:
|
||||
self._internal_value = ManimColor._internal_from_rgb(value, alpha) # type: ignore
|
||||
self._internal_value = ManimColor._internal_from_rgb(value, alpha) # type: ignore[arg-type]
|
||||
elif length == 4:
|
||||
self._internal_value = ManimColor._internal_from_rgba(value) # type: ignore
|
||||
self._internal_value = ManimColor._internal_from_rgba(value) # type: ignore[arg-type]
|
||||
else:
|
||||
raise ValueError(
|
||||
f"ManimColor only accepts lists/tuples/arrays of length 3 or 4, not {length}"
|
||||
|
|
@ -173,11 +173,11 @@ class ManimColor:
|
|||
else:
|
||||
if length == 3:
|
||||
self._internal_value = ManimColor._internal_from_int_rgb(
|
||||
value, # type: ignore
|
||||
value, # type: ignore[arg-type]
|
||||
alpha,
|
||||
)
|
||||
elif length == 4:
|
||||
self._internal_value = ManimColor._internal_from_int_rgba(value) # type: ignore
|
||||
self._internal_value = ManimColor._internal_from_int_rgba(value) # type: ignore[arg-type]
|
||||
else:
|
||||
raise ValueError(
|
||||
f"ManimColor only accepts lists/tuples/arrays of length 3 or 4, not {length}"
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ def index_labels(
|
|||
|
||||
self.add(text, indices)
|
||||
"""
|
||||
|
||||
labels = VGroup()
|
||||
for n, submob in enumerate(mobject):
|
||||
label = Integer(n, **kwargs)
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ def smart_replace(base: str, alias: str, substitution: str) -> str:
|
|||
str
|
||||
The new string after the alias substitution.
|
||||
"""
|
||||
|
||||
occurrences = []
|
||||
len_alias = len(alias)
|
||||
len_base = len(base)
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ from typing import TYPE_CHECKING, Any
|
|||
|
||||
import jinja2
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import Directive, directives # type: ignore
|
||||
from docutils.parsers.rst import Directive, directives
|
||||
from docutils.statemachine import StringList
|
||||
|
||||
from manim import QUALITIES
|
||||
|
|
|
|||
|
|
@ -192,7 +192,6 @@ def rotate_vector(
|
|||
ValueError
|
||||
If vector is not of dimension 2 or 3.
|
||||
"""
|
||||
|
||||
if len(vector) > 3:
|
||||
raise ValueError("Vector must have the correct dimensions.")
|
||||
if len(vector) == 2:
|
||||
|
|
@ -248,9 +247,7 @@ def rotation_matrix(
|
|||
axis: np.ndarray,
|
||||
homogeneous: bool = False,
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
Rotation in R^3 about a specified axis of rotation.
|
||||
"""
|
||||
"""Rotation in R^3 about a specified axis of rotation."""
|
||||
inhomogeneous_rotation_matrix = Rotation.from_rotvec(
|
||||
angle * normalize(np.array(axis))
|
||||
).as_matrix()
|
||||
|
|
@ -356,7 +353,6 @@ def angle_between_vectors(v1: np.ndarray, v2: np.ndarray) -> float:
|
|||
float
|
||||
The angle between the vectors.
|
||||
"""
|
||||
|
||||
return 2 * np.arctan2(
|
||||
np.linalg.norm(normalize(v1) - normalize(v2)),
|
||||
np.linalg.norm(normalize(v1) + normalize(v2)),
|
||||
|
|
@ -493,7 +489,6 @@ def regular_vertices(
|
|||
start_angle : :class:`float`
|
||||
The angle the vertices start at.
|
||||
"""
|
||||
|
||||
if start_angle is None:
|
||||
start_angle = 0 if n % 2 == 0 else TAU / 4
|
||||
|
||||
|
|
@ -734,7 +729,6 @@ def earclip_triangulation(verts: np.ndarray, ring_ends: list) -> list:
|
|||
list
|
||||
A list of indices giving a triangulation of a polygon.
|
||||
"""
|
||||
|
||||
rings = [list(range(e0, e1)) for e0, e1 in zip([0, *ring_ends], ring_ends)]
|
||||
|
||||
def is_in(point, ring_id):
|
||||
|
|
|
|||
|
|
@ -188,7 +188,6 @@ def _make_test_comparing_frames(
|
|||
Callable[[], None]
|
||||
The pytest test.
|
||||
"""
|
||||
|
||||
if is_set_test_data_test:
|
||||
frames_tester = _ControlDataWriter(file_path, size_frame=size_frame)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -181,7 +181,6 @@ def _texcode_for_environment(environment: str) -> tuple[str, str]:
|
|||
A pair of strings representing the opening and closing of the tex environment, e.g.
|
||||
``\begin{tabular}{cccl}`` and ``\end{tabular}``
|
||||
"""
|
||||
|
||||
environment.removeprefix(r"\begin").removeprefix("{")
|
||||
|
||||
# The \begin command takes everything and closes with a brace
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ def tex_to_svg_file(
|
|||
environment: str | None = None,
|
||||
tex_template: TexTemplate | None = None,
|
||||
):
|
||||
"""Takes a tex expression and returns the svg version of the compiled tex
|
||||
r"""Takes a tex expression and returns the svg version of the compiled tex
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -76,7 +76,7 @@ def generate_tex_file(
|
|||
environment: str | None = None,
|
||||
tex_template: TexTemplate | None = None,
|
||||
) -> Path:
|
||||
"""Takes a tex expression (and an optional tex environment),
|
||||
r"""Takes a tex expression (and an optional tex environment),
|
||||
and returns a fully formed tex file ready for compilation.
|
||||
|
||||
Parameters
|
||||
|
|
@ -271,7 +271,6 @@ def delete_nonsvg_files(additional_endings: Iterable[str] = ()) -> None:
|
|||
additional_endings
|
||||
Additional endings to whitelist
|
||||
"""
|
||||
|
||||
tex_dir = config.get_dir("tex_dir")
|
||||
file_suffix_whitelist = {".svg", ".tex", *additional_endings}
|
||||
|
||||
|
|
|
|||
91
poetry.lock
generated
91
poetry.lock
generated
|
|
@ -1008,13 +1008,13 @@ testing = ["hatch", "pre-commit", "pytest", "tox"]
|
|||
|
||||
[[package]]
|
||||
name = "executing"
|
||||
version = "2.0.1"
|
||||
version = "2.1.0"
|
||||
description = "Get the currently executing AST node of a frame, and other information"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"},
|
||||
{file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"},
|
||||
{file = "executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf"},
|
||||
{file = "executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
|
|
@ -1050,37 +1050,6 @@ docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1
|
|||
testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"]
|
||||
typing = ["typing-extensions (>=4.8)"]
|
||||
|
||||
[[package]]
|
||||
name = "flake8"
|
||||
version = "6.1.0"
|
||||
description = "the modular source code checker: pep8 pyflakes and co"
|
||||
optional = false
|
||||
python-versions = ">=3.8.1"
|
||||
files = [
|
||||
{file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"},
|
||||
{file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
mccabe = ">=0.7.0,<0.8.0"
|
||||
pycodestyle = ">=2.11.0,<2.12.0"
|
||||
pyflakes = ">=3.1.0,<3.2.0"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-docstrings"
|
||||
version = "1.7.0"
|
||||
description = "Extension for flake8 which uses pydocstyle to check docstrings"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"},
|
||||
{file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
flake8 = ">=3"
|
||||
pydocstyle = ">=2.1"
|
||||
|
||||
[[package]]
|
||||
name = "fonttools"
|
||||
version = "4.53.1"
|
||||
|
|
@ -2159,17 +2128,6 @@ files = [
|
|||
[package.dependencies]
|
||||
traitlets = "*"
|
||||
|
||||
[[package]]
|
||||
name = "mccabe"
|
||||
version = "0.7.0"
|
||||
description = "McCabe checker, plugin for flake8"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
|
||||
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mdit-py-plugins"
|
||||
version = "0.4.1"
|
||||
|
|
@ -2889,17 +2847,6 @@ files = [
|
|||
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pycodestyle"
|
||||
version = "2.11.1"
|
||||
description = "Python style guide checker"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"},
|
||||
{file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pycparser"
|
||||
version = "2.22"
|
||||
|
|
@ -3031,23 +2978,6 @@ files = [
|
|||
[package.dependencies]
|
||||
typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
|
||||
|
||||
[[package]]
|
||||
name = "pydocstyle"
|
||||
version = "6.3.0"
|
||||
description = "Python docstring style checker"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"},
|
||||
{file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
snowballstemmer = ">=2.2.0"
|
||||
|
||||
[package.extras]
|
||||
toml = ["tomli (>=1.2.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "pydub"
|
||||
version = "0.25.1"
|
||||
|
|
@ -3059,17 +2989,6 @@ files = [
|
|||
{file = "pydub-0.25.1.tar.gz", hash = "sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyflakes"
|
||||
version = "3.1.0"
|
||||
description = "passive checker of Python programs"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"},
|
||||
{file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygithub"
|
||||
version = "2.4.0"
|
||||
|
|
@ -4634,4 +4553,4 @@ jupyterlab = ["jupyterlab", "notebook"]
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.10,<3.13"
|
||||
content-hash = "d5fe13e0bc943b0145374cbd92156bffbce1198bfbfd2de31203a9fdef8c8d66"
|
||||
content-hash = "bbb339bd2fb2a6161e6accdb7369cf9a0652a280c8ef27a98fddd261a0ff5e7f"
|
||||
|
|
|
|||
|
|
@ -62,8 +62,6 @@ jupyterlab = ["jupyterlab", "notebook"]
|
|||
gui = ["dearpygui"]
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
flake8 = "^6.1.0"
|
||||
flake8-docstrings = "^1.7.0"
|
||||
furo = "^2023.09.10"
|
||||
gitpython = "^3"
|
||||
isort = "^5.12.0"
|
||||
|
|
@ -133,9 +131,11 @@ select = [
|
|||
"A",
|
||||
"B",
|
||||
"C4",
|
||||
"D",
|
||||
"E",
|
||||
"F",
|
||||
"I",
|
||||
"PGH",
|
||||
"PT",
|
||||
"SIM",
|
||||
"UP",
|
||||
|
|
@ -147,14 +147,24 @@ ignore = [
|
|||
# No function calls in defaults
|
||||
# ignored because np.array() and straight_path()
|
||||
"B008",
|
||||
# docstring ignores - mostly stylistic
|
||||
"D1",
|
||||
"D203",
|
||||
"D205",
|
||||
"D212",
|
||||
"D4",
|
||||
# due to the import * used in manim
|
||||
"F403",
|
||||
"F405",
|
||||
# generic type: ignore
|
||||
"PGH003",
|
||||
# fixtures not returning anything should have leading underscore
|
||||
"PT004",
|
||||
# Exception too broad (this would require lots of changes + re.escape) for little benefit
|
||||
"PT011",
|
||||
# as recommended by https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
|
||||
"D206",
|
||||
"D300",
|
||||
"E111",
|
||||
"E114",
|
||||
"E117",
|
||||
|
|
@ -192,9 +202,8 @@ required-imports = ["from __future__ import annotations"]
|
|||
fixture-parentheses = false
|
||||
mark-parentheses = false
|
||||
|
||||
[tool.ruff.lint.pydocstyle]
|
||||
convention = "numpy"
|
||||
|
||||
[tool.ruff.format]
|
||||
docstring-code-format = true
|
||||
|
||||
[tool.codespell]
|
||||
write-changes = true
|
||||
ignore-words-list = ["medias", "nam"]
|
||||
|
|
|
|||
|
|
@ -217,7 +217,6 @@ def main(token, prior, tag, additional, outfile):
|
|||
|
||||
ADDITIONAL includes additional PR(s) that have not been recognized automatically.
|
||||
"""
|
||||
|
||||
lst_release, cur_release = prior, tag
|
||||
|
||||
github = Github(token)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ from manim.mobject.text.numbers import Integer
|
|||
|
||||
def test_unit_vector():
|
||||
"""Check if the magnitude of unit vector along
|
||||
the NumberLine is equal to its unit_size."""
|
||||
the NumberLine is equal to its unit_size.
|
||||
"""
|
||||
axis1 = NumberLine(unit_size=0.4)
|
||||
axis2 = NumberLine(x_range=[-2, 5], length=12)
|
||||
for axis in (axis1, axis2):
|
||||
|
|
@ -17,7 +18,8 @@ def test_unit_vector():
|
|||
|
||||
def test_decimal_determined_by_step():
|
||||
"""Checks that step size is considered when determining the number of decimal
|
||||
places."""
|
||||
places.
|
||||
"""
|
||||
axis = NumberLine(x_range=[-2, 2, 0.5])
|
||||
expected_decimal_places = 1
|
||||
actual_decimal_places = axis.decimal_number_config["num_decimal_places"]
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ from manim import RED, DecimalNumber, Integer
|
|||
|
||||
def test_font_size():
|
||||
"""Test that DecimalNumber returns the correct font_size value
|
||||
after being scaled."""
|
||||
after being scaled.
|
||||
"""
|
||||
num = DecimalNumber(0).scale(0.3)
|
||||
|
||||
assert round(num.font_size, 5) == 14.4
|
||||
|
|
@ -39,7 +40,8 @@ def test_set_value_size():
|
|||
|
||||
def test_color_when_number_of_digits_changes():
|
||||
"""Test that all digits of an Integer are colored correctly when
|
||||
the number of digits changes."""
|
||||
the number of digits changes.
|
||||
"""
|
||||
mob = Integer(color=RED)
|
||||
mob.set_value(42)
|
||||
assert all(
|
||||
|
|
|
|||
|
|
@ -123,7 +123,8 @@ def test_tex_size():
|
|||
|
||||
def test_font_size():
|
||||
"""Test that tex_mobject classes return
|
||||
the correct font_size value after being scaled."""
|
||||
the correct font_size value after being scaled.
|
||||
"""
|
||||
string = MathTex(0).scale(0.3)
|
||||
|
||||
assert round(string.font_size, 5) == 14.4
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ from manim.mobject.text.text_mobject import MarkupText, Text
|
|||
|
||||
def test_font_size():
|
||||
"""Test that Text and MarkupText return the
|
||||
correct font_size value after being scaled."""
|
||||
correct font_size value after being scaled.
|
||||
"""
|
||||
text_string = Text("0").scale(0.3)
|
||||
markuptext_string = MarkupText("0").scale(0.3)
|
||||
|
||||
|
|
|
|||
|
|
@ -165,7 +165,6 @@ class Top:
|
|||
@deprecated(since="0.8.0", message="This method is useless.")
|
||||
def mid_func(self):
|
||||
"""Middle function in Top."""
|
||||
|
||||
pass
|
||||
|
||||
@deprecated(until="1.4.0", replacement="Top.NewNested")
|
||||
|
|
@ -180,7 +179,6 @@ class Top:
|
|||
@deprecated(since="1.0.0", until="12/25/2025")
|
||||
def nested_func(self):
|
||||
"""Nested function in Top.NewNested."""
|
||||
|
||||
pass
|
||||
|
||||
class Bottom:
|
||||
|
|
|
|||
|
|
@ -58,7 +58,6 @@ class MyScene(Scene):
|
|||
|
||||
def test_transparent(config):
|
||||
"""Test the 'transparent' config option."""
|
||||
|
||||
config.verbosity = "ERROR"
|
||||
config.dry_run = True
|
||||
|
||||
|
|
@ -89,7 +88,6 @@ def test_transparent_by_background_opacity(config, dry_run):
|
|||
|
||||
def test_background_color(config):
|
||||
"""Test the 'background_color' config option."""
|
||||
|
||||
config.background_color = WHITE
|
||||
config.verbosity = "ERROR"
|
||||
config.dry_run = True
|
||||
|
|
@ -217,7 +215,6 @@ def test_temporary_dry_run(config):
|
|||
|
||||
def test_dry_run_with_png_format(config, dry_run):
|
||||
"""Test that there are no exceptions when running a png without output"""
|
||||
|
||||
config.write_to_movie = False
|
||||
config.disable_caching = True
|
||||
assert config.dry_run is True
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ __module_test__ = "tex_mobject"
|
|||
@frames_comparison
|
||||
def test_color_inheritance(scene):
|
||||
"""Test that Text and MarkupText correctly inherit colour from
|
||||
their parent class."""
|
||||
|
||||
their parent class.
|
||||
"""
|
||||
VMobject.set_default(color=RED)
|
||||
tex = Tex("test color inheritance")
|
||||
mathtex = MathTex("test color inheritance")
|
||||
|
|
@ -24,3 +24,33 @@ def test_set_opacity_by_tex(scene):
|
|||
tex = MathTex("f(x) = y", substrings_to_isolate=["f(x)"])
|
||||
tex.set_opacity_by_tex("f(x)", 0.2, 0.5)
|
||||
scene.add(tex)
|
||||
|
||||
|
||||
def test_preserve_tex_color():
|
||||
"""Test that Tex preserves original tex colors."""
|
||||
template = TexTemplate(preamble=r"\usepackage{xcolor}")
|
||||
Tex.set_default(tex_template=template)
|
||||
|
||||
txt = Tex(r"\textcolor{red}{Hello} World")
|
||||
assert len(txt[0].submobjects) == 10
|
||||
assert all(char.fill_color.to_hex() == "#FF0000" for char in txt[0][:5]) # "Hello"
|
||||
assert all(
|
||||
char.fill_color.to_hex() == WHITE.to_hex() for char in txt[0][-5:]
|
||||
) # "World"
|
||||
|
||||
txt = Tex(r"\textcolor{red}{Hello} World", color=BLUE)
|
||||
assert len(txt[0].submobjects) == 10
|
||||
assert all(char.fill_color.to_hex() == "#FF0000" for char in txt[0][:5]) # "Hello"
|
||||
assert all(
|
||||
char.fill_color.to_hex() == BLUE.to_hex() for char in txt[0][-5:]
|
||||
) # "World"
|
||||
|
||||
Tex.set_default(color=GREEN)
|
||||
txt = Tex(r"\textcolor{red}{Hello} World")
|
||||
assert len(txt[0].submobjects) == 10
|
||||
assert all(char.fill_color.to_hex() == "#FF0000" for char in txt[0][:5]) # "Hello"
|
||||
assert all(
|
||||
char.fill_color.to_hex() == GREEN.to_hex() for char in txt[0][-5:]
|
||||
) # "World"
|
||||
|
||||
Tex.set_default()
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ def test_Text2Color():
|
|||
|
||||
def test_text_color_inheritance():
|
||||
"""Test that Text and MarkupText correctly inherit colour from
|
||||
their parent class."""
|
||||
their parent class.
|
||||
"""
|
||||
VMobject.set_default(color=RED)
|
||||
# set both to a singular font so that the tests agree.
|
||||
text = Text("test_color_inheritance", font="Sans")
|
||||
|
|
|
|||
|
|
@ -66,7 +66,6 @@ def logs_comparison(
|
|||
Callable[[Any], Any]
|
||||
The test wrapped with which we are going to make the comparison.
|
||||
"""
|
||||
|
||||
control_data_file = Path(control_data_file)
|
||||
log_path_from_media_dir = Path(log_path_from_media_dir)
|
||||
|
||||
|
|
|
|||
|
|
@ -90,7 +90,6 @@ def video_comparison(
|
|||
--------
|
||||
tests/helpers/video_utils.py : create control data
|
||||
"""
|
||||
|
||||
control_data_file = Path(control_data_file)
|
||||
scene_path_from_media_dir = Path(scene_path_from_media_dir)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue