Compare commits

...

190 commits

Author SHA1 Message Date
F. Muenkel
733a1fa3d3
[Experimental] Fix image test (#4618)
* Fix test_image.py

* Make test_invert_image use the full range of possible RGB values
2026-04-06 14:32:10 -04:00
F. Muenkel
bc05b06e1a
[Experimental] Fix ruff linter issues (#4621)
* Fix ruff linter issues

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-03-03 14:31:22 -03:00
JasonGrace2282
f565d48e5d
Test math in test_new_rendering 2026-03-01 12:51:50 -05:00
Francisco Manríquez Novoa
64511b98e4 Merge branch 'main' into experimental 2026-02-27 23:56:31 -03:00
Francisco Manríquez Novoa
6b3c191dd1 Fix graph animations for remove_vertices and remove_edges (TODO: add_vertices animation is still broken) 2026-02-23 00:08:49 -03:00
Francisco Manríquez Novoa
d3fa15fba1 Merge branch 'main' into experimental 2026-02-20 22:52:30 -03:00
Francisco Manríquez Novoa
7df4abd334 Merge with main 2026-02-20 22:48:07 -03:00
F. Muenkel
8b24a433ae
[EXPERIMENTAL] Fix test_graph.py tests (#4569)
* Fix some graph tests

* Fix two more tests

* Ensure temporary visuals get removed from scene

* Fix wrong import

* Remove redundant hasattr

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-02-19 00:51:18 -03:00
Aarush Deshpande
6fb7fa4b2f
[EXPERIMENTAL] Remove _on_finish parameter from Animation (#4595)
* Remove _on_finish parameter from Animation

* Rewrite GenericGraph._add_vertices_animation()

---------

Co-authored-by: Francisco Manríquez Novoa <francisco.manriquezn@usm.cl>
2026-02-19 00:10:19 -03:00
Francisco Manríquez Novoa
40c166153d Fix 6 tests in test_matrix.py due to wrong import of VGroup 2026-02-15 23:05:32 -03:00
Francisco Manríquez Novoa
3481d59baa Fix test_mobject.py::test_mobject_add 2026-02-15 22:57:49 -03:00
Francisco Manríquez Novoa
33884470e4 Fix various tests and reveal underlying issues by using get_fill_color() instead of fill_color and related 2026-02-14 15:59:38 -03:00
Francisco Manríquez Novoa
0ec0c5cb76 Fix test_multi_animation_override_exception 2026-02-13 19:27:21 -03:00
Francisco Manríquez Novoa
d3d7a814c1
Remove _Uniforms, _Data and some shader logic from Mobjects (#4590) 2026-02-13 18:35:28 -03:00
Henrik Skov Midtiby
f6611bac46
[Experimental] Synchronize axes in interactive show diff (#4570)
* Synchronize axes in the show_diff interactive view

* Modify show_diff layout to include test name in plot

* Fix issue where the difference displayed was wrong

This occured when the expected color was black but the generated color was something different.

* Also show number of non matching pixels in the show diff gui

* Implemented suggestions from Chopan50
2026-02-13 18:34:57 -03:00
Federico Burna
8b5d48d424
[EXPERIMENTAL] Make play_logic and file_writer tests pass + Manager fixes (#4559)
* Fix embed dt calculation, Arrow stroke_width bug, and shader compatibility

* Restored lost fixes from b78ccba: Arrow stroke_width, Shaders AA, Manager interaction

* fix(core): resolve recursion in set_fill, ManimColor opacity error, and restore add_sound

* Fix(Experimental): Restore functionality and stabilize architecture

- Fix Arrow stroke_width initialization (float vs list bug) and restore scaling logic.
- Implement 'save_last_frame' logic by integrating it with skipping mechanism.
- Refactor Manager.post_construct to ensure correct exit and image saving behavior.
- Fix transparency issues in OpenGLRenderer (background_opacity and shader alpha).
- Update shader code to use modern 'texture()' syntax instead of 'texture2D()'.
- Fix VMobject recursion error in set_fill and ManimColor initialization.
- Restore audio connectivity in Scene.add_sound.
- Update tests to align with new Manager(Scene) architecture and document pending TODOs.
- Detailed rationale in CHANGES_SUMMARY.md.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-02-13 11:16:40 -03:00
Henrik Skov Midtiby
84870ed0a9
Removed unused type ignore statements and ignored all files with type errors (#4583) 2026-02-13 11:08:53 -03:00
Francisco Manríquez Novoa
97d3f3e774 Fix tests in animation/test_updaters.py 2026-02-13 10:58:52 -03:00
Francisco Manríquez Novoa
4504dd11df Unindent remapping logic in pointwise_become_partial 2026-02-13 10:34:32 -03:00
Francisco Manríquez Novoa
dec8b93ca8 Typo in test_vectorized_mobject.py 2026-02-13 02:51:01 -03:00
Francisco Manríquez Novoa
98967c9fe5 Fix all tests in test_vectorized_mobject.py and allow iterables in OpenGLVGroup 2026-02-13 02:48:57 -03:00
Francisco Manríquez Novoa
c76c868213 Fix all tests in test_vectorized_mobject.py and allow iterables in OpenGLVGroup 2026-02-13 02:47:14 -03:00
Francisco Manríquez Novoa
186ad964bb
[Experimental] Use OpenGLMobject as Mobject and similar imports everywhere (#4578)
* Use 'OpenGLMobject as Mobject' and similar imports everywhere

* Remove commented Mobject imports

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Revert accidental renaming of OpenGLMobject to Mobject and duplicated .set_default() - now the example scene works again

* Fix CurvesAsSubmobjects and DashedVMobject imports

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Temporarily solve issue in _add_intrinsic_animation_overrides()

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-02-12 19:59:50 -03:00
Francisco Manríquez Novoa
071a724cef Merge branch 'exp_replace_openglmob_imports' of https://github.com/chopan050/manim into exp_replace_openglmob_imports 2026-02-12 19:51:12 -03:00
Francisco Manríquez Novoa
06c989fbe1 Merge branch 'experimental' of https://github.com/ManimCommunity/manim into exp_replace_openglmob_imports 2026-02-12 19:50:56 -03:00
Francisco Manríquez Novoa
1f60d48740 Temporarily solve issue in _add_intrinsic_animation_overrides() 2026-02-12 19:50:48 -03:00
Francisco Manríquez Novoa
22a284ed5b Add more typings for MyAnimation in test_composition.py 2026-02-09 18:15:04 -03:00
Miguel Ángel Rodríguez Vidal
74b9925b11
test: fix test_composition/test_animationgroup_calls_finish (#4581)
The function implements the class MyAnimation which inherits form
Animation but did not implement the function interpolate_submobject,
which by default raises a NotImplementedError exception
2026-02-09 18:00:41 -03:00
Henrik Skov Midtiby
05c8e37ac7
[Experimental] Added GraphicalDeviationError exception (#4576)
* Added the GraphicalDeviationError exception

* Adjusted error message

* Add a note about __module__ = "builtins"
2026-02-09 21:59:16 +01:00
pre-commit-ci[bot]
669f1447bb [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-02-09 17:42:20 +00:00
Francisco Manríquez Novoa
41f40657c9 Merge branch 'experimental' of https://github.com/ManimCommunity/manim into exp_replace_openglmob_imports 2026-02-09 14:41:57 -03:00
Francisco Manríquez Novoa
2995ad4040 Fix wrong import of FileWriter and reference to nonexisting 'found' in module_ops 2026-02-09 14:34:29 -03:00
pre-commit-ci[bot]
f60c54581d [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-02-09 17:29:09 +00:00
Francisco Manríquez Novoa
92c489fedb Merge branch 'main' into experimental 2026-02-09 14:28:41 -03:00
Francisco Manríquez Novoa
ef7bcd1d2a Fix CurvesAsSubmobjects and DashedVMobject imports 2026-02-09 10:15:12 -03:00
Henrik Skov Midtiby
8603510d13
Marked tests as slow if they take more than 10 seconds to run on my computer. (#4577)
pytest reported these durations for the slowest tests.

669.59s call     tests/test_graphical_units/test_coordinate_systems.py::test_gradient_line_graph_x_axis
648.22s call     tests/test_graphical_units/test_coordinate_systems.py::test_gradient_line_graph_y_axis
192.45s call     tests/test_graphical_units/test_threed.py::test_Torus
53.81s call     tests/test_graphical_units/test_threed.py::test_Sphere
24.88s call     tests/module/mobject/mobject/test_mobject.py::test_mobject_dimensions_nested_mobjects
19.33s call     tests/test_graphical_units/test_coordinate_systems.py::test_plot_surface
14.16s call     tests/test_graphical_units/test_coordinate_systems.py::test_plot_surface_colorscale
10.49s call     tests/test_graphical_units/test_probability.py::test_default_chart
7.87s call     tests/test_graphical_units/test_numbers.py::test_set_value_with_updaters
6.57s call     tests/module/mobject/test_matrix.py::TestMatrix::test_get_columns[2x2]
6.30s call     tests/module/mobject/test_matrix.py::TestMatrix::test_get_columns[1x3]
5.64s call     tests/test_graphical_units/test_tables.py::test_Table
5.54s call     tests/test_graphical_units/test_axes.py::test_plot[False]
5.54s call     tests/module/mobject/test_matrix.py::TestMatrix::test_matrix_init_valid[2x2_numpy]
5.25s call     tests/test_graphical_units/test_img_and_svg.py::test_WeightSVG
2026-02-08 23:44:30 -03:00
Francisco Manríquez Novoa
89ff65200c Merge branch 'exp_replace_openglmob_imports' of https://github.com/chopan050/manim into exp_replace_openglmob_imports 2026-02-08 21:30:13 -03:00
Francisco Manríquez Novoa
fbe41c1902 Revert accidental renaming of OpenGLMobject to Mobject and duplicated .set_default() - now the example scene works again 2026-02-08 21:29:53 -03:00
pre-commit-ci[bot]
7dbb19b926 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-02-09 00:16:54 +00:00
Francisco Manríquez Novoa
60a36ee028 Remove commented Mobject imports 2026-02-08 21:16:17 -03:00
Francisco Manríquez Novoa
88b84eeada Use 'OpenGLMobject as Mobject' and similar imports everywhere 2026-02-08 20:56:24 -03:00
JasonGrace2282
9e07cbad72
Report invalid configuration value upon manager initialization
Also fixes the variance of Manager[T]
2026-02-08 11:47:57 -05:00
JasonGrace2282
45aef8e556
Fix a test that was missing a method 2026-02-08 11:41:32 -05:00
Francisco Manríquez Novoa
bd816db5f7
Fix Mobject become() to use move_to() without copying self center 2026-02-04 23:40:36 -03:00
Francisco Manríquez Novoa
c5954f88a8 Add docs and 'copy' parameter to OpenGLMobject methods get_bounding_box() and get_center() 2026-02-04 23:15:59 -03:00
Henrik Skov Midtiby
61b8cfd52f
Improve the output generated by --show_diff (#4554)
* Improve the output generated by --show_diff

Do not only look at the red color channel values.

* Remove comment

* Code cleanup

* Use named colors as suggested by Chopan50

* Use the actual shape

* Made the code easier to read.

Solution by Chopan50
2026-02-03 07:14:46 -03:00
Francisco Manríquez Novoa
b579779ce7 Rewrite SpiralIn to avoid Mobject.restore(), Mobject.rotate() and setting final_position and initial_position 2026-02-02 23:54:25 -03:00
Francisco Manríquez Novoa
3531ab2bc5 Allow using Surfaces 2026-02-02 23:52:34 -03:00
Henrik Skov Midtiby
f9586c88eb
[EXPERIMENTAL] Ensure that Dot3D is derived from OpenGLVMobject (#4561) 2026-02-02 01:08:18 -03:00
Henrik Skov Midtiby
0223143739
Use get_bounding_box_point instead of get_critical_point (#4560) 2026-02-02 01:05:48 -03:00
Francisco Manríquez Novoa
715b61168e Revert broken change from main in OpenGLMobject.get_opacity() 2026-02-01 19:44:53 -03:00
Henrik Skov Midtiby
d1b7d20c65
[Experimental] Fix errors in the table tests (#4549)
* Rename parameter n_rows and n_cols of OpenGLMobject::arrange_in_grid to rows and cols

This is to match the Mobject::arrange_in_grid method signature

* Make OpenGLMobject::arrange_in_grid handle buff values similar to Mobject::arrange_in_grid

* Use Mobject::arrange_in_grid from the main branch

Also removed some type ignore statements

* Use the same default opacities as VMobject in the main branch

Also removed some type ignore statements and added strict=False to two zip statements.

* Deal with opacities later.
2026-02-01 18:11:29 -03:00
Francisco Manríquez Novoa
a2c04954b8
[EXPERIMENTAL] Fix ManimBanner animations by modifying Mobject.become(), Mobject.get_center(), and ManimBanner.expand() (#4556)
* Fix ManimBanner animations by modifying Mobject.become(), Mobject.get_center(), and ManimBanner.expand()

* Add ManimBanner animations to test_new_rendering.py
2026-02-01 17:37:05 -03:00
F. Muenkel
53882628c0
Fix a few vectorized mobject tests (#4546) 2026-01-30 10:16:05 -05:00
Federico Burna
d83bd04661
Fix wait animation system (#4544)
Implement the `Wait` animation
2026-01-29 14:05:46 -05:00
Francisco Manríquez Novoa
dcc6f4b901 Merge branch 'experimental' of https://github.com/ManimCommunity/manim into experimental 2026-01-29 15:22:33 -03:00
Francisco Manríquez Novoa
8dbc427a92 Revert Transform.begin() to fix growing animations - use SpinInFromNothing in test_new_rendering 2026-01-29 15:22:24 -03:00
Henrik Skov Midtiby
31fc6e7649
Fix failing test where the same mobject was included in a scene twice. (#4548)
Fix `tests/module/animation/test_transform.py::test_no_duplicate_references`
2026-01-29 11:13:19 -05:00
F. Muenkel
771f0152f6
Fix a few more tests related to scene (#4540) 2026-01-26 12:21:54 -05:00
Henrik Skov Midtiby
6156d861f2
Experimental - Hide type errors (#4543) 2026-01-26 12:20:58 -05:00
JasonGrace2282
172ea97d70
fix outdated docstrings regarding manager rendering 2026-01-22 23:04:01 -05:00
Henrik Skov Midtiby
6d61818c7e
Prevent creation of a 'moderngl.InvalidObject' (#4539)
Prevent creation of a 'moderngl.InvalidObject' when accessing renderer resources after `.render()` was called.

---------

Co-authored-by: JasonGrace2282 <aarush.deshpande@gmail.com>
2026-01-22 22:50:23 -05:00
Henrik Skov Midtiby
df21b2dc31
Import MethodWithArgs, CHOOSE_NUMBER_MESSAGE and INVALID_NUMBER_MESSAGE (#4538)
* Fix error when calling 'manim basic.py'

* Fix error when running "uv run manim basic.py"

NameError: name 'MethodWithArgs' is not defined

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Import Shader, Mesh, FullScreenQuad, OpenGLTexturedSurface and OpenGLSurfaceMeshFix

To handle some missing imports

# Conflicts:
#	example_scenes/opengl.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-01-22 08:49:16 +01:00
Francisco Manríquez Novoa
fe3e505556 Merge main into experimental 2026-01-22 02:46:02 -03:00
JasonGrace2282
04140dae18
Fix typo on changelog 2026-01-11 09:42:20 -05:00
F. Muenkel
36bb003745
[Experimental] Fix a few tests that failed due to incorrect imports (#4379)
* Fix incorrect import in test

* Fix docstrings and a few type annotations

* Fix test_ipython_magic.py by fixing import errors

* Fix test for SurroundingRectangle
2025-08-09 10:57:08 -04:00
Theo Barollet
3618b4677f
fixing window resolution in several tests (#4187)
* fixing window resolution in several tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Theo Barollet <theo.barollet@proton.me>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-03-13 07:06:29 -04:00
Francisco Manríquez
9aef19f482 Move validate_run_time to Scene and fix some tests in test_animation.py 2024-12-17 13:18:23 -03:00
JasonGrace2282
5be4c13148
Remove missed cairo import 2024-12-16 20:46:31 -05:00
JasonGrace2282
7de544da1c
Merge branch 'main' of https://github.com/JasonGrace2282/manim into experimental 2024-12-16 20:41:35 -05:00
JasonGrace2282
620766e6dd
Merge branch 'main' of https://github.com/JasonGrace2282/manim into experimental 2024-12-16 20:34:16 -05:00
Francisco Manríquez Novoa
19980e3c70
[EXPERIMENTAL] Remove ThreeDScene and rewrite Camera internals (#4059)
Co-authored-by: Aarush Deshpande <110117391+JasonGrace2282@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-12-16 22:18:51 -03:00
Aarush Deshpande
3911643720
[Experimental] Start Migration Guide (#4060)
Covers the Manager, ThreeDScene, Camera, and Animations

Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>

---------

Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>
2024-12-16 11:58:05 -05:00
JasonGrace2282
f9e6090af2
Add missing AnimationProtocol method 2024-12-16 09:55:32 -05:00
JasonGrace2282
d98497690c
Simplify Manager creation 2024-12-13 18:12:34 -05:00
Francisco Manríquez Novoa
9f72377c9d
Improve rendering time by rewriting STD140BufferFormat._write_padded() (#4058)
The original implementation of `STD140BufferFormat._write_padded()` used `np.pad` which is slow.
This new implementation avoids that by creating a new array of zeroes instead. In this way, this method now takes 60x less time, and the total rendering time is almost halved.
2024-12-13 18:12:01 -05:00
Francisco Manríquez Novoa
9360129365
Fix color initialization in OpenGLVMobject.__init__() to prevent crashes in subclasses (#4056)
* Change order of statements in `OpenGLVMobject.__init__()`
2024-12-13 14:08:42 -05:00
pre-commit-ci[bot]
9fcf94f2cb [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2024-12-01 02:38:54 +00:00
JasonGrace2282
89cb646049
Fix circular import issue 2024-11-30 21:38:29 -05:00
JasonGrace2282
497debad8e
Merge branch 'main' of https://github.com/ManimCommunity/manim into experimental 2024-11-30 21:35:36 -05:00
JasonGrace2282
23a9d40bd7
Write to movie 2024-11-30 20:23:55 -05:00
Tristan Schulz
2726a4d60d
Fix Updaters with Hacky solution 2024-11-27 09:56:32 +01:00
Tristan Schulz
4ea40d2f58
Fixing AnimationGroup remover behavior 2024-11-27 07:29:32 +01:00
Tristan Schulz
85db7ac248
Fixing test_vectorized_mobject to account for OpenGLMobject 2024-11-26 09:00:05 +01:00
Tristan Schulz
b94eb15229
Fixing test_vectorized_mobject to use OpenGLVMobject an port add function assertions for automated checking 2024-11-26 08:02:25 +01:00
Tristan Schulz
91a6bf0658
Cone doesn't initialize OpenglVMobject 2024-11-26 07:14:00 +01:00
Tristan Schulz
0dc1547bee
Fixing broken rendering when no window is present 2024-11-26 06:38:57 +01:00
Tristan Schulz
0426552ec9
Whops 2024-11-26 06:13:47 +01:00
Tristan Schulz
b51d3fe200
Fixing another stray global variable 2024-11-25 13:22:11 +01:00
Tristan Schulz
e18fdd2e74
Formatting shaders 2024-11-25 13:12:11 +01:00
Tristan Schulz
b830b7d63e
Fixed fix_in_frame refactored glsl files and deleted useless 2024-11-25 13:04:47 +01:00
JasonGrace2282
97fedbca13
Add a release method to OpenGLRenderer 2024-11-07 16:31:17 -05:00
Francisco Manríquez Novoa
4e6af02ceb
Experimental: fix 11 tests (#3979)
* Fix tests and verify Animation run time inside new run_time property setter

* test_boolean_ops

* Add run_time validation from main branch

* Fix 2 tests in tests/module/mobject/mobject/test_mobject.py

* Fix 2 tests in tests/module/mobject/text/test_text_mobject.py

* Manager in test_file_writer.py
2024-10-29 22:08:30 -03:00
JasonGrace2282
7631878ab4
Add docs for groups 2024-10-24 22:14:34 -04:00
JasonGrace2282
088d6be3de
remove debugging content 2024-10-24 22:02:33 -04:00
JasonGrace2282
d7fa8f051c
Merge branch 'main' of https://github.com/ManimCommunity/manim into experimental 2024-10-24 22:01:19 -04:00
JasonGrace2282
2304541dad
rename sections feature to groups 2024-10-24 21:56:31 -04:00
JasonGrace2282
bfaa9003ea
Clean up the diff a little bit 2024-09-08 16:06:08 -04:00
JasonGrace2282
f41b9251eb
Revert changes to i18n 2024-09-08 15:33:58 -04:00
JasonGrace2282
11f179224b
Better typing and doc strings
- Adds a `MobjectAnimation` protocol for use in
  `turn_animation_into_updater`
- Adds doc strings to `AnimationProtocol`
- More explanations on how sections API works
2024-09-08 13:19:17 -04:00
JasonGrace2282
867ce8da98
Merge branch 'main' of https://github.com/ManimCommunity/manim into experimental 2024-09-08 10:55:04 -04:00
JasonGrace2282
db26fe6e5e
Use descriptors to bind SceneSections 2024-09-02 12:39:53 -04:00
JasonGrace2282
560ca7fe85
Drop support for python 3.9 2024-09-02 11:51:28 -04:00
OliverStrait
9f0eafb6b8
Little patch on memoryleaks in test scenarios. (#3928)
Co-authored-by: Oliver Strait <strait.devoliver@gmail.com>
2024-09-02 09:49:57 -04:00
pre-commit-ci[bot]
38fdaec214 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2024-09-01 18:51:09 +00:00
JasonGrace2282
793e853ae8
Merge branch 'main' of https://github.com/ManimCommunity/manim into experimental 2024-09-01 14:50:45 -04:00
JasonGrace2282
157ed6e4ea
Merge branch 'main' of https://github.com/ManimCommunity/manim into experimental 2024-08-31 11:52:56 -04:00
Aarush Deshpande
3f431a12f7
Note changed families in some places, and fix colors (#3894) 2024-08-31 11:46:41 -04:00
JasonGrace2282
7e8c5c8144
Merge branch 'main' of https://github.com/ManimCommunity/manim into experimental 2024-08-03 14:46:28 -04:00
Aarush Deshpande
7844c848f0
Implement sections for scenes (#3883)
Reimplement the ManimCE sections API, as well as generic fixes for `Tex`.
2024-08-02 20:08:44 -04:00
Aarush Deshpande
d09b03bb45
Fix MathTex on experimental (#3871)
* Fix MathTex
2024-07-31 19:54:40 -04:00
adeshpande
424cb27c6a
Cleanup legacy code in experimental (#3874)
* Remove data/uniforms
* Clean up some manager code.
2024-07-24 21:13:46 -04:00
JasonGrace2282
53f637b865
Merge branch 'main' of https://github.com/ManimCommunity/manim into experimental 2024-07-21 11:09:56 -04:00
JasonGrace2282
ce752bf57a
Fix window being internally None after hitting quit
When hitting escape, the window becomes null and calls to window.swap_buffer fail
2024-07-19 10:12:15 -04:00
JasonGrace2282
7a634dda95
Fixes for pre-commit 2024-07-15 10:05:55 -04:00
pre-commit-ci[bot]
51cf463cbe [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2024-07-15 13:58:13 +00:00
JasonGrace2282
d6ea8412d6
Merge branch 'main' of https://github.com/ManimCommunity/manim into experimental 2024-07-15 09:56:17 -04:00
adeshpande
5dcab4c4a5
Bring in main's FileWriter into experimental (#3821)
* pyproject.toml: update manimpango version (#3405)

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Added docs for functions in `mobject_update_utils` (#3325)

* Added docs for functions in mobject_update_utils

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Updated docstring of always_shift

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Added period to sentence.

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Updated parameter description in always_redraw

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Update always_rotate description

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Finished parameters in always_redraw

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Changed comment in always_shift

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* update always_shift description

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* used normalize from manim.utils.space_ops

* fixed indentation in always_redraw

* added type-hints

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Fix tests to run on Cairo 1.18.0 (#3416)

* Add a script to build and install cairo

* Update gui tests for cairo 1.18.0

* update script to set env vars

* Make the script run with plain python

* Prefer the recently built one in pkg-config

* Skip the built if it's windows

* CI: build and install latest cairo

* CI: only run when cache is missed

* Disable compiling tests while building cairo

* update poetry lock file

* Display the cairo version when running pytest

* fixup

* tests: skip graphical test when cairo is old

* fix the path to find the pkgconfig files on linux

* set the LD_LIBRARY_PATH too

only then it'll work on linux

* fixup

* small fixup

* Move the script inside `.github/scripts` folder

* Make the minimum cairo version a constant

* Seperate setting env vars to a sperate step

this seem to have broken when cache is hit

* Fix: Fixed a bug in regards to empty inputs in AddTextLetterByLetter class.  (#3404)

* Misc: Just a class to test out some functions

* Fix: Fixed a bug in AddTextLetterByLetter class

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix: Adjusted changes according to Ben's comments

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix: Removed imports

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Feat: Adjusted changes to AddTextLetterByLetter

* Feat: Added test_creation

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Introduce new workflow creating a downloadable version of the documentation (#3417)

* Revert "rtd: enable htmlzip build (#3355)"

This reverts commit 571f79be2c.

* use python3.11 to build docs

* upgrade python version used in release publish workflow

* new workflow for building downloadable docs

* change event trigger for testing

* sudo apt

* rename release job; build html in poetry env

* set GITHUB_PATH instead of PATH

* introduce additional step

* use correct binary path

* forgot microtype

* fonts-roboto + actually compress files correctly

* fix asset path

* Update .github/workflows/release-publish-documentation.yml

Co-authored-by: Naveen M K <naveen521kk@gmail.com>

* pull_request -> workflow_dispatch

* Update .github/workflows/release-publish-documentation.yml

---------

Co-authored-by: Naveen M K <naveen521kk@gmail.com>

* Fix incorrect submobject count of multi-part Tex/MathTex mobjects by stopping them from adding empty submobjects (#3423)

* do not add a VectorizedPoint as a submobject if SingleStringMathTex renders to empty SVG

* test new behavior

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update tests/module/mobject/text/test_texmobject.py

* Update tests/module/mobject/text/test_texmobject.py

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* CI: fix caching of cairo (#3419)

I forgot to change the path after moving around the file.

* Fix CSV reader adding empty lists in rendering summary (#3430)

* Fix CSV reader adding empty files

Fixes issue #3311

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Fix None check order in _tree_layout (#3421)

* Fix None check order in _tree_layout

* add tests to test_graph.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Bump teatimeguest/setup-texlive-action from 2 to 3 (#3431)

Bumps [teatimeguest/setup-texlive-action](https://github.com/teatimeguest/setup-texlive-action) from 2 to 3.
- [Release notes](https://github.com/teatimeguest/setup-texlive-action/releases)
- [Commits](https://github.com/teatimeguest/setup-texlive-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: teatimeguest/setup-texlive-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* bump dependencies -- see #3241 (#3433)

* Fix Typing (#3086)

* first draft of color class + starting library conversion

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* changed everything to Manim color todo: figure out circular dependency in utils

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* first working draft of new color version

* resolving conflicts

* resolving conflicts

* resolving conflicts

* resolving conflicts

* resolving conflicts

* changed default internal value of ManimColor to np.ndarray[float]

* starting to fix tests

* fixed more tests and changed precision of manim color

* removed premature color conversion

* fixed some more tests

* final test changes

* fix doctests

* fix for 3.8

* fixing ManimColor string representation

* removing some unneccesary conversions

* moved community constants to manim_colors.py and added more color standards

* Added typing.py and typed bezier.py, core.py, constants.py  fully

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fixed codeql complaints

* add type ignore for np.allclose

* fixed import in three_dimensions

* added ignore for F401 back again in flake

* added typings to coordinate_systems.py

* Few improvements to `graphing/coordinate_systems.py`

* added some typings to mobject/geometry/line.py

* updated typings for mobject/geometry/line.py

* Add missing imports to `line.py`

* added typings to three_dimensions.py

* Use `FunctionOverride` for animation overrides

Fix type signature of `set_color_by_gradient`

* Remove `TYPE_CHECKING` check

Doc is failing

* Revert "Remove `TYPE_CHECKING` check"

Fails due to circular import

* Use `Self` in `coordinate_systems.py`

* Typehinted mobject.py and updated manim.typing.py

* Typed VMobject

* Type-hinted manim.mobject.geometry

* math.cos->np.cos, etc & fixed incorrect typehints

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix missing annotations import

* TypeAlias fix in typing.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add ignore errors again to mypy because commits are not possible like this

* Fix last typing issues

* Update docs

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Only type check manim

* Try fixing pre-commit

* fix merge

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix compat

* Fix compat again

* Fix imports compat

* Use union syntax

* Use union syntax

* Fix reduce_across_dimension

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Various test and merge fixes

* Doc fixes

* Last doc fix

* Revert usage of np over math

* Bump numpy version

* Remove obsolete duplicate example

* Fixed Incorrect Typehint in manim.constants

* Fix docstring typo

* More fixes

Use mypy.ini instead of .mypy.ini
Fix more docstrings
Improve types in utils and constants

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* docs fixes

* Add internal aliases

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix compat

* line lengths in .rst file, formatting, typos

* add docstring for space_ops:cross2d

* add some more arrow tip typings (in a non-circular import causing way)

* yes, this can be deleted

* fix formatting of example

* added docstring to bezier::inverse_interpolation

* added docstring + test for bezier::match_interpolate

* some improvements in coordinate_systems

* Vector -> Vector3

* replaced np.ndarray with more appropriate type hints

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Apply feedback

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* revert to previous (new) version

* fix doctest

* fix ReST errors

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Alex Lembcke <alex.lembcke@gmail.com>
Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>
Co-authored-by: JasonGrace2282 <aarush.deshpande@gmail.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* fix: issue with ImageMobject bounding box (#3340)

* fix: fix an issue with ImageMobject bounding box

A missing point resulted in smaller bounding box causing issues it to be
smaller when the object is rotated. Added the missing fourth point to
ImageMobject points and altered call from camera. Filled in docstring
that used to propagate from superclass, saying that ImageMobject has no
points.

* add a test to check that rotating an image to and from doesn't change it

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Václav Blažej <vaclav.blazej@warwick.ac.uk>
Co-authored-by: Naveen M K <naveen521kk@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* chore(deps): add Python 3.12 support (#3395)

* chore(deps): add Python 3.11 and 3.12 support


chore(deps): update lock file


chore(deps): remove colour


fix(deps): force NumPy version


fix(deps): relax constraints


chore(deps): update lock file

* fix(deps): make poetry happy

* fix(ci): skia pathops on 3.12

* fix(test): doctest skip

* disable python 3.8 pipeline

* removed get_parameters, replaced by direct call to inspect

* black

---------

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Added ability to remove non-svg LaTeX files (#3322)

* Added ability to remove latex junk (default True)

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fixed tests (hopefully), and whitelisted .tex

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* reverted weird changes from merge

* See previous commit message

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fixed logs-too-long test

* Fixed log output

* Fixed typo ;)

* deleted unused variable

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* moved latex deletion to tex_file_writing.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* removed changes in scene files

* Added caching based on LaTeX expression .svg

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Deleted unused function in delete_old_tex

* make if condition more readable

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* cleaned up svg file check

* changed blacklist -> whitelist for file endings

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Reverted docstring change

* Updated delete_non_svg files docstring

* Changed list to a set

* Update manim/_config/utils.py

* Update manim/cli/render/global_options.py

* added one test for the no_latex_cleanup config option

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>
Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>

* feat: DecimalNumber() - added spacing between values and unit (#3366)

* feat: DecimalNumber() - added spacing between values and unit

* Update manim/mobject/text/numbers.py

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Update manim/mobject/text/numbers.py

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Update manim/mobject/text/numbers.py

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Update manim/mobject/text/numbers.py

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Update manim/mobject/text/numbers.py

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Update manim/mobject/text/numbers.py

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Update manim/mobject/text/numbers.py

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

---------

Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Add option to run examples directly with binder (#3427)

* Add option to run examples directly with binder

The minified JS is from
https://github.com/naveen521kk/manim-binder

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* slight style changes

* update the js file to fix on chrome

Signed-off-by: Naveen M K <naveen521kk@gmail.com>

* show the run button as an cursor

* make the video to be 100% of the width

* Update manim/utils/docbuild/manim_directive.py

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Add a "Make interactive" button instead of "Run" button

Clicking on the "Make interactive" button show the code-editor and "run" button

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* update margin for run interactive button

---------

Signed-off-by: Naveen M K <naveen521kk@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Prepare v0.18.0 (#3439)

* generated changelog and bumped version

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* changed some PR descriptions in the changelog

* fix some docbuild warnings

* fixed a reference that became ambiguous

* copyedit pass of changelog

* some more changelog polishing

* bump release date

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* updated release date

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Fixed wrong path in action building downloadable docs (#3450)

* fixed wrong path in action building downloadable docs

* fix second occurrence of wrong path

* Allow accessing ghost vectors in :class:`.LinearTransformationScene` (#3435)

* Fix CSV reader adding empty files

Fixes issue #3311

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added LinearTransformationScene.ghost_vectors

* Added test and prevented empty VGroups as ghost vectors

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fixed typo in example

* Added ability to join together multiple renders

* Revert "Added ability to join together multiple renders" (wrong branch)

This reverts commit dee29c390f.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Add type hints to `_config` (#3440)

* Add type hints to `_config`

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix call issues

* Fix wrong value being used

* Fix test

* Fix wrong value being set

* lint

* Few type fixes

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Fix Idicate docs typo (#3461)

* Update indication.py (#3477)

reading docs, im sure oppising isnt a word

* Optimized `get_unit_normal()` and replaced `np.cross()` with custom `cross()` in `manim.utils.space_ops` (#3494)

* Added cross and optimized get_unit_normal in manim.utils.space_ops

* Added missing border case to new get_unit_normal where one vector is nonzero

* Updated test_threed.py::test_Sphere test data

* Update dependency constraints, fix deprecation warnings (#3376)

* WIP: Update metadata

* Finish removing upper bounds

Drop requests dependency, use urllib instead
order depencencies

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix issues on 3.12

* Order dev dependencies

* Update most dev deps, update lint config

* Add missing import

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* trigger CI

* More deprecation fixes

* Missing argument

* Deprecation fixes, again

* Use older xdist to fix test flakyness

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* fix 360° to 180° in quickstart tutorial (#3498)

* Update Docker base image to python3.12-slim (#3458) (#3459)

* Update Docker base image to python3.12-slim (#3458)

* Update docker/Dockerfile

---------

Co-authored-by: Melody Griesen <jvgriese@ncsu.edu>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* fix line_join to joint_type in example_scenes/basic.py (#3510)

* fix typo in docstring for DtUpdater example: line -> square (#3509)

* Implement caching of fonts list to improve runtime performance (#3316)

* Implement caching of fonts list to improve runtime performance

* Fix small use_svg_cache kwargs error

* replaced font list with LRU cache

* Removed deprecated new command (#3512)

Co-authored-by: Naveen M K <naveen521kk@gmail.com>

* Added `cap_style` feature to `VMobject` (#3516)

* Added cap_style feature to VMobject

* Added an example to `set_cap_style` method

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Unsplitted line 2501

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added graphical test for cap_style

* Added vmobject_cap_styles.npz for testing cap_styles

* Removed # noqa comments from vectorized_mobject.py

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* feat(cli): optionally hide version splash (#3329)

* feat(cli): optionally hide version splash

As discussed in #3326, this PR proposes a new optional flag to hide the version splash when manim command in launched. Additionally, the splash print is now inly executed when the CLI is executed, not on module import.

After looking at the current documentation, it does not seem to change anything. I only saw that you documented a version splash for when the CLI is used, but not when the module is imported. So removing it should not break the api docs.

In the future, users can still have version information with `import manim; print(manim.__version__)`.

Closes #3326

* chore(tests): make tests pass

---------

Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>

* Reformatting the `--save_sections` output to have the format `<Scene>_<SecNum>_<SecName><extension>` (#3499)

* Worked on issue 3471, fixing rendered file names to inherit section name

* Modified file name to include section number and name

* Modified tests for file names to include number and name, in order to pass

---------

Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>

* Explain ``.Transform`` vs ``.ReplacementTransform`` in quickstart examples (#3500)

* Explained ReplacementTransform vs Transform

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added section explaining Transform vs ReplacementTransform

* Added a->b->c example

* Clarified explanation

* Fixed Typo

* Fixed missing colon

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>

* Fix formatting building blocks (#3515)

* Fix formatting building blocks

* Fix formatting building blocks

---------

Co-authored-by: Jason Grace <110117391+JasonGrace2282@users.noreply.github.com>

* Bump jupyter-server from 2.9.1 to 2.11.2 (#3497)

Bumps [jupyter-server](https://github.com/jupyter-server/jupyter_server) from 2.9.1 to 2.11.2.
- [Release notes](https://github.com/jupyter-server/jupyter_server/releases)
- [Changelog](https://github.com/jupyter-server/jupyter_server/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jupyter-server/jupyter_server/compare/v2.9.1...v2.11.2)

---
updated-dependencies:
- dependency-name: jupyter-server
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Account for dtype in the pixel array so the maximum value stays correct in the invert function (#3493)

* fix(lib): fix

This fixes an issue where the `invert` argument would only work for `uint8` dtypes. Now the `max` value is updated according to the pixel array dtype.

Maybe we should add unit tests for that, but haven't found an obvious place to put unit tests.

* chore(ci): add basic test

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix(ci): wrong attr name

* Update tests/module/mobject/test_image.py

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Added `grid_lines` attribute to `Rectangle` to add individual styling to the grid lines (#3428)

* Added 'grid_line_stroke_width' parameter in Rectangle

* Added 'grid_lines' (VGroup) attribute to 'Rectangle' class

---------

Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>

* Fix rectangle grid properties (#3082) (#3513)

* Import  for both vertical and horizontal gridlines in

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Fix animations with zero runtime length to give a useful error instead of a broken pipe (#3491)

* Fix animation group not erroring when instantiated with an empty list

* Move error messages into Animation.begin()

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update manim/animation/animation.py

* Update manim/animation/composition.py

* Update manim/animation/animation.py

Co-authored-by: Jason Grace <110117391+JasonGrace2282@users.noreply.github.com>

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Jason Grace <110117391+JasonGrace2282@users.noreply.github.com>

* fixed the stroke width issue with single color in streamlines (#3436)

* fixed the stroke width issue with single color in streamlines

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added test for streamlines

* Added test for streamlines

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: MrDiver <mrdiverlp@gmail.com>

* Add Documentation to `.to_edge` and `to_corner` (#3408)

* Added docstrings and example renders to Mobject.to_corner() and Mobject.to_edge

* Added docstrings and example renders to Mobject.to_corner() and Mobject.to_edge

* Update manim/mobject/mobject.py

* Update manim/mobject/mobject.py

* Update manim/mobject/mobject.py

* Update manim/mobject/mobject.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update manim/mobject/mobject.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Adding the ability to pass lists and generators to .play() (#3365)

* adding the ability to pass lists and generators to .play()

* fix for _AnimationBuilder

* Changed handling of generators to accept lists of generators and normal arguments at the same time

* Animation group handles generators

* Refactored into own function for reusability

* Fix typing

* Fix typing

---------

Co-authored-by: Jason Grace <110117391+JasonGrace2282@users.noreply.github.com>

* follow-up to #3491, made errors more consistent. fixes #3527

* chore(docs): add some words about Cairo 1.18 (#3530)

* chore(docs): add some words about Cairo 1.18

Closes #3521

* fix(docs): typo

* Update testing.rst

* Update testing.rst

* Fix formatting of ``MoveAlongPath`` docs (#3541)

* Remove wag method from Mobject

* Fixed MoveAlongPath

* Revert remove wag

Created a new branch with the wrong base, sorry ;)

* Fixed Animate Type-hint (#3543)

* Remove wag method from Mobject (#3539)

Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>

* Fix typo of `get_y_axis_label` docstring (#3547)

* Finish TODO's in ``contributing/typings.rst`` (#3545)

* Updated typing docs

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added link for protocols

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added object vs Any

* Fix Typo

* Rephrase TypeVar

Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>

* Compare between tuple vs list

Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>

* typing -> collections.abc

Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>

* typing -> collections.abc

Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>

* change method to attr

Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>

* clarify object typehint

Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>

* Fix code typo

Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>

* Added if TYPE_CHECKING section

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix reST for inline code

* Elaborate on if TYPE_CHECKING

Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>

* functions -> collections

Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>

* Fix use of `Mobject`'s deprecated `get_*()` and `set_*()` methods in Cairo tests (#3549)

* Fix Deprecation warnings in cairo tests

* Fix animation/specialized.py

* add note in docstring of ManimColor about class constructors (#3554)

* Added support for Manim type aliases in Sphinx docs + Added new TypeAliases (#3484)

* Updated manim.typing and included TypeAliases in docs.source.conf

* Added Vector2 and reorganized manim_type_aliases

* Fixed __all__ exports for __all__ of manim

* Update manim/cli/render/global_options.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Draft of new typing docs and new autotyping directive

* Changed vertical bars to Unions

* Updated poetry.lock

* Created custom file parser for manim.typing

* Got reST parser going

* Updated autotyping and parsing

* Update parsing

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added code_block toggle

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added typings to directives

* Renamed Tuple to tuple in manim.typings

* Added missing docs for type aliases

* Fixed exponent typo in ManimInt

* Hyperlinks to types work - removed Module Attributes section

* Removed Unused Import

Remove ``import re``

* Added freeglut-devel to workflows for Linux

Hopefully (?) fix the GLU import error

* Fix package name

* Add support for Type Aliases section in every module - Renaming of Vector types

* Add/fix docs for directive, parser and others

* Fixed alias typo in module_parsing

* Fix decode/import bugs, fix minor details in docs

* Added missing docs for utils.docbuild and utils.testing

* Sort alphabetically entries in utilities_misc.rst

* Address review comments, add notes about Vector and hyperlinks inside definition blocks

---------

Co-authored-by: MrDiver <mrdiverlp@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: JasonGrace2282 <aarush.deshpande@gmail.com>

* Improve documentation section about contributing to docs (#3555)

* Improve section in docs about contributing to docs

* Add note about doc build command depending on the OS

* Improve section in docs about contributing to docs

* Add note about doc build command depending on the OS

* Fix wrong toctree path in docs/source/contributing/docs.rst

* Add helpful hints to `VGroup.add()` error message (#3561)

* Improve VGroup creation error message

* Use .__name__ for the type

Co-authored-by: Jason Grace <110117391+JasonGrace2282@users.noreply.github.com>

---------

Co-authored-by: Jason Grace <110117391+JasonGrace2282@users.noreply.github.com>

* exception add if new_rings is none (#3574)

* exception add if new_rings is none

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Fix typing of `Animation` (#3568)

* Add 'to be used in the future' TODOs to ManimFrame (#3553)

* Refactor `TexTemplate` (#3520)

* Refactor `TexTemplate`

* Add tests, refactor some things

* Fixed Some tests

* Move typing imports

* Fix remaining tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: JasonGrace2282 <aarush.deshpande@gmail.com>
Co-authored-by: Jason Grace <110117391+JasonGrace2282@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Bump github/codeql-action from 2 to 3 (#3567)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump actions/upload-artifact from 3 to 4 (#3566)

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump actions/setup-python from 4 to 5 (#3565)

Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* updated several packages (pillow, jupyterlab, notebook, jupyterlab-lsp, jinja2, gitpython) (#3593)

* Removed -s / --save_last_frame flag from CLI arguments (#3528)

* Remove -s flag

* Make help text more verbose

* fix write_subcaption_file error when using opengl renderer (#3546)

* fix write_subcaption_file error when using opengl renderer

* Update manim/scene/scene_file_writer.py

---------

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Update docker.rst to use bash from the PATH (#3582)

* fix typo in value_tracker.py (#3594)

* fix `get_arc_center()` returning reference of point (#3599)

* Add ref_class (#3598)

* Fix typehint (#3592)

* Update ci.yml (#3611)

* fix type hint of indication.py (#3613)

* Revert vector type aliases to NumPy ndarrays (#3595)

* Improve handling of specified font name (#3429)

Co-authored-by: Jason Grace <110117391+JasonGrace2282@users.noreply.github.com>
Co-authored-by: JasonGrace2282 <aarush.deshpande@gmail.com>

The proposed fix does two things :

* If the specified font is 'sans-serif' : change it to 'sans' as this is the name used in the list of fonts
* if the font name is not in the list of fonts, automatically check if the capitalized version of the font exists in the list of fonts. If not, print a warning to the user.

* Remove support for dynamic plugin imports (#3524)

* Remove call to deprecated `pkg_resources`

* Remove support for dynamic plugin imports, update plugin utilities

* fix affected tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* more fixes

* Last fix

* Fix import

* Update docs

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Jason Villanueva <a@jsonvillanueva.com>

* Run poetry lock --no-update (#3621)

* Update jupyter.rst (#3630)

Pinpoint IPython==8.21.0 for Google Colab, because more recent versions are incompatible with their runtime.

* Fix Vector3 -> Vector3D in contributing docs (#3639)

* Bump black from 23.12.1 to 24.3.0 (#3649)

Bumps [black](https://github.com/psf/black) from 23.12.1 to 24.3.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/23.12.1...24.3.0)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump cryptography from 42.0.0 to 42.0.4 (#3629)

Bumps [cryptography](https://github.com/pyca/cryptography) from 42.0.0 to 42.0.4.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/42.0.0...42.0.4)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Code Cleanup: removing unused imports and global variables (#3620)

* Remove unused import

* More security fixes

* Remove unused global variable

* More fixes

* Revert change (actual fix would require some rewrite)

* Add exception for edge case to satisfy warning

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Stuff

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Fixing the behavior of `.become` to not modify target mobject via side effects fix color linking (#3508)

* Copied ndarray for rgbas when interpolating

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* changing .become to copy the target mobject

* change tests and test data to reflect .become new behavior

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update tests/test_graphical_units/test_mobjects.py

* removed unused copy_submobject kwarg

* added doctests and improved documentation

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Added some examples for `Mobject`/`VMobject` methods (#3641)

* Add examples to mobject+vmobject methods

* Add missing import

* Separate whitespace to point_from_proportion

* Fixes!

* Changed example of Mobject.get_color

* Remove unneccessary import

* Add in import

* Fix typehint of `Vector` direction parameter (#3640)

* Fix typehint of Vector

* Change from Vector to Point in typehint

In `TipableVMobject._pointify` it converts a 3D
list of the form [x, y, z] to a Vector3D. Therefore
the direction parameter can take lists, not just numpy arrays.

* Fix bug in :class:`.VMobjectFromSVGPath` (#3677)

* Fixes #3676

* Update manim/mobject/svg/svg_mobject.py

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* Fixed problem and added test

---------

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* Flake8 rule C901 is about McCabe code complexity (#3673)

* Flake8 rule C901 is about McCabe code complexity

It is not about flake8-comprehensions.

* max-complexity = 29

* Fix broken link to Poetry's installation guide in the first time contributors page (#3692)

* Fix minor grammatical errors found in the index page of the documentation (#3690)

* Fix some minor grammatical errors in the index page of the docs

* Fix grammar

* Undo uneccessary change in phrasing

* fix(LICENSE): update year (#3689)

* Remove deprecated parameters and animations (#3688)

* Remove deprecated parameters/animations

* Remove test

* Remove test data

* Attempted fix for windows cp1252 encoding failure (#3687)

* Attempt to fix windows test

* Revert "Attempt to fix windows test"

This reverts commit e31c2077cd.

* try a different fix

* maybe both fixes together?

* try adding in CI

* Update ci.yml

* Update logger_utils.py

* maybe needs a dash?

* try utf8 again

* Remove legacy_windows

* try changing test

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Try decoding after capturing bytes output

* Nicer fix

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix typo (#3696)

* Docs: fix out-dated CLI option in Manim's Output Settings (#3674)

* Docs: fix out-dated CLI option in Manim's Output Settings

* Docs: more fluent English

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* Docs: break lines

* Docs: more fluent English

* Docs: remove a space

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

---------

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* only do actions if try succeeded (#3694)

* Mention pixi in installation guide (#3678)

* Mention pixi in installation guide

* Update docs/source/installation/conda.rst

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Add note

---------

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Fix successive calls of :meth:`.LinearTransformationScene.apply_matrix` (#3675)

* docs: improve installation FAQ's

* I have potentially resolved the issue when in LinearTransformationScene between two animations of transforming space we invoke the self.wait()

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* added another solutions in comments, added tests and removed wrong files from git

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* yeah , i forgot to save the file xd

* fixed the test, removed the comments my in changed file

* fix test and speed up test time for test_apply_matrix

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fixed the test, removed the comments my in changed file

* fixed the test

* Revert "docs: improve installation FAQ's"

This reverts commit e53a1c8d6f.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: JasonGrace2282 <aarush.deshpande@gmail.com>
Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* Bump actions/cache from 3 to 4 (#3607)

Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* Bump FedericoCarboni/setup-ffmpeg from 2 to 3 (#3608)

Bumps [FedericoCarboni/setup-ffmpeg](https://github.com/federicocarboni/setup-ffmpeg) from 2 to 3.
- [Release notes](https://github.com/federicocarboni/setup-ffmpeg/releases)
- [Commits](https://github.com/federicocarboni/setup-ffmpeg/compare/v2...v3)

---
updated-dependencies:
- dependency-name: FedericoCarboni/setup-ffmpeg
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump ssciwr/setup-mesa-dist-win from 1 to 2 (#3609)

Bumps [ssciwr/setup-mesa-dist-win](https://github.com/ssciwr/setup-mesa-dist-win) from 1 to 2.
- [Release notes](https://github.com/ssciwr/setup-mesa-dist-win/releases)
- [Commits](https://github.com/ssciwr/setup-mesa-dist-win/compare/v1...v2)

---
updated-dependencies:
- dependency-name: ssciwr/setup-mesa-dist-win
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* docs: update typing guidelines (#3704)

* Update typing guidelines

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix formatting

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update documentation and typings for `ParametricFunction` (#3703)

* Update documentation and typings for ParametricFunction

* Use manim tyings

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* fix typings

* a few doc fixes

* Update manim/mobject/graphing/functions.py

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* update typings

* remove extraneous line

* update example code

* add line back for comptibility

* import TYPE_CHECKING

---------

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* fix(copyright): automate copyright updating for docs (#3708)

* Fix some typehints in mobject.py (#3668)

* refactor(mobject): fix some typehints

* Move typing_extensions import under `if TYPE_CHECKING`
* Change from using `def animate(self: T ,...) -> T` to `def
  animate(self, ...) -> Self` as stated in PEP 673
* Fix incorrect usage of `T` in a method

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* move updaters type alias into TYPE_CHECKING

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Bump idna from 3.6 to 3.7 (#3693)

Bumps [idna](https://github.com/kjd/idna) from 3.6 to 3.7.
- [Release notes](https://github.com/kjd/idna/releases)
- [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.rst)
- [Commits](https://github.com/kjd/idna/compare/v3.6...v3.7)

---
updated-dependencies:
- dependency-name: idna
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump pillow from 10.2.0 to 10.3.0 (#3672)

Bumps [pillow](https://github.com/python-pillow/Pillow) from 10.2.0 to 10.3.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/10.2.0...10.3.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Fix typo (#3721)

* Fixed `Mobject.put_start_and_end_on` with same start and end point (#3718)

* fix put_start_and_end_on() at the same point

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* [pre-commit.ci] pre-commit autoupdate (#3332)

* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/pre-commit/pre-commit-hooks: v4.4.0 → v4.6.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.4.0...v4.6.0)
- [github.com/pycqa/isort: 5.12.0 → 5.13.2](https://github.com/pycqa/isort/compare/5.12.0...5.13.2)
- [github.com/asottile/pyupgrade: v3.10.1 → v3.15.2](https://github.com/asottile/pyupgrade/compare/v3.10.1...v3.15.2)
- [github.com/psf/black: 23.7.0 → 24.4.0](https://github.com/psf/black/compare/23.7.0...24.4.0)
- [github.com/asottile/blacken-docs: 1.15.0 → 1.16.0](https://github.com/asottile/blacken-docs/compare/1.15.0...1.16.0)
- [github.com/PyCQA/flake8: 6.1.0 → 7.0.0](https://github.com/PyCQA/flake8/compare/6.1.0...7.0.0)
- [github.com/pre-commit/mirrors-mypy: v1.5.1 → v1.9.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.5.1...v1.9.0)
- [github.com/codespell-project/codespell: v2.2.5 → v2.2.6](https://github.com/codespell-project/codespell/compare/v2.2.5...v2.2.6)

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* make smoothererstep readable again, avoid overlong line

* zoom_value more readable

* fix blacken-docs touching .github

* fix codespell setup, remove unnecessary file, fix some typos

* flake8: ignore E704, triggered by overload

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update docs/source/tutorials/quickstart.rst

* more flake fixes

* try to make blacken-docs happy

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* fix(autoaliasattr): search for type aliases under if TYPE_CHECKING (#3671)

* build(deps): read-the-docs sphinx (#3720)

* Fix issue where SpiralIn doesn't show elements. (#3589)

* Set SpiralIn to use fill_opacity 1 if not set

* Create SpiralIn control data

* Create test for SpiralIn

* Fix spiralin to separate fill and stroke opacity

* resolve opacity issue

* fix test data

---------

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Clean Graph layouts and increase flexibility (#3434)

* allow user-defined layout functions for Graph
+ fixup type annotations

* only pass relevant args

* write tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* change_layout forward root_vertex and partitions
- deduplicated layout code in __init__ and change_layout
- fixed change_layout backwards compatibility

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* add test for change_layout

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix copy/paste error

* fix

* fixup types for CodeQL

* static type the Layout Names

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix dynamic union type for Python 3.9

* add example scenes to LayoutFunction protocol documentation

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Replace references to np.ndarray with standard Manim types

* Label NxGraph as a TypeAlias

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Follow-up to graph layout cleanup: improvements for tests and typing (#3728)

* suggestions from review on #3434

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Update coordinate_systems.py (#3730)

small change

* build(ci): change from macos-latest to macos-13 (#3729)

* Add ``--preview_command`` cli flag (#3615)

* Add preview_command cli flag

* Edit help for --preview_command

* Change back from subprocess.run

* Remove old comment

* Bug with timg stopped happening with sp.run

* Fix docstring

* Revert "Fix docstring"

This reverts commit d2c00fc24dc46586f994237f1d2758528b78d6a3.

* Actually fix docstring

* Change help for option

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

---------

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* AnimationGroup: optimized interpolate() and fixed alpha bug on finish() (#3542)

* Optimized AnimationGroup computation of start-end times with lag ratio

* Added extra comment for init_run_time

* Added full path to imports in composition.py

* Optimized AnimationGroup.interpolate

* Fixed final bugs

* Removed accidental print

* Final fix to AnimationGroup.interpolate

* Fixed animations being skipped unintentionally

* Addressed requested changes

---------

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Fixed ```get_anchors()``` Return Type Inconsistency (#3214)

* changed return type of get_anchors()

* Ensured consistency with OpenGLVMobject

* Fixed CodeQl, updated docstring

* Update manim/mobject/types/vectorized_mobject.py

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Update manim/mobject/opengl/opengl_vectorized_mobject.py

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* fixed typo, t -> e

* fixed doctest

---------

Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>
Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>

* fixed [""] being set as loaded plugins (#3734)

* Prepare new release: v0.18.1 (#3719)

* add note about changelog in changelog.rst

* bump version

* Update CITATION.cff

* feat: Add three animations that together simulate a typing animation (#3612)

* feat: Add animations that together simulate typing

AddTextLetterByLetterWithCursor

RemoveTextLetterByLetterWithCursor

Blink

* Revert "feat: Add animations that together simulate typing"

This reverts commit 5fe256880d.

* Revert "Revert "feat: Add animations that together simulate typing""

This reverts commit 6a8244a157.

* Add new animations to __all__

* Temporarily remove docs example

* Modify "Blink" and add docstring examples back in

To avoid 0-second animations, which fail docstring test

* Address requested changes

Fix imports
Remove redundant constructor arguments
Improve names

* Shorten names

* Fix release documentation building (#3737)

* [pre-commit.ci] pre-commit autoupdate (#3739)

updates:
- [github.com/psf/black: 24.4.0 → 24.4.2](https://github.com/psf/black/compare/24.4.0...24.4.2)
- [github.com/pre-commit/mirrors-mypy: v1.9.0 → v1.10.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.9.0...v1.10.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Fixes #3744 (#3745)

Co-authored-by: Andrzej Nagórko <>

* Bump tqdm from 4.66.1 to 4.66.3 (#3746)

Bumps [tqdm](https://github.com/tqdm/tqdm) from 4.66.1 to 4.66.3.
- [Release notes](https://github.com/tqdm/tqdm/releases)
- [Commits](https://github.com/tqdm/tqdm/compare/v4.66.1...v4.66.3)

---
updated-dependencies:
- dependency-name: tqdm
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump jinja2 from 3.1.3 to 3.1.4 (#3750)

Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.3 to 3.1.4.
- [Release notes](https://github.com/pallets/jinja/releases)
- [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/jinja/compare/3.1.3...3.1.4)

---
updated-dependencies:
- dependency-name: jinja2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Add typehints to `manim.utils.iterables` (#3751)

* typehint iterables

* organize typing hints

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* remove any

* Add overloads for tuplify

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Remove example

* feedback

* Make TypeVars accessible at runtime

* Add hints for zip

Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>

* typing -> collections.abc

Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>

* try to make mypy happy

* zip[tuple[T, ...]] instead of zip[T]

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>

* Let `SceneFileWriter` access `ffmpeg` via `av` instead of via external process (#3501)

* added av as a dependency

* make partial movie files use av instead of piping to external ffmpeg

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* opengl rendering: use av for movie files

* no need to check for ffmpeg executable

* refactor: *_movie_pipe -> *_partial_movie_stream

* improve (oneline) documentation

* pass more options to partial movie file rendering

* move ffmpeg verbosity settings to config; renamed option dict

* replaced call to ffmpeg in combine_files by using av

Co-authored-by: Jérome Eertmans <jeertmans@icloud.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* there was one examples saved as a gif?

* chore(deps): re-order av

* chore(lib): simplify `write_frame` method

Reduces the overall code complexity

* chore(lib): add audio

* fix(lib): same issue for conversion

* fix(lib): webm export

* fix(lib): transparent export

Though the output video is weird

* try(lib): fix gif + TODOs

* chore(deps): lower dep crit

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* feat(lib): add support for GIF

* fix(ci): rewrite tests

* fix

* chore(ci): prevent calling concat on empty list

* add missing dot

* fix(ci): update frame comparison ?

* fix(log): add handler to libav logger

* chore: add TODO

* fix(lib): concat issue

* Revert "fix(ci): update frame comparison ?"

This reverts commit 904cfb46ae.

* fix(ci): make it pass tests

* chore(lib/docs/ci): remove FFMPEG entirely

This removes any reference to FFMPEG, except in translation files

* added av as a dependency

* make partial movie files use av instead of piping to external ffmpeg

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* opengl rendering: use av for movie files

* no need to check for ffmpeg executable

* refactor: *_movie_pipe -> *_partial_movie_stream

* improve (oneline) documentation

* pass more options to partial movie file rendering

* move ffmpeg verbosity settings to config; renamed option dict

* replaced call to ffmpeg in combine_files by using av

Co-authored-by: Jérome Eertmans <jeertmans@icloud.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* there was one examples saved as a gif?

* chore(deps): re-order av

* chore(lib): simplify `write_frame` method

Reduces the overall code complexity

* chore(lib): add audio

* fix(lib): same issue for conversion

* fix(lib): webm export

* fix(lib): transparent export

Though the output video is weird

* try(lib): fix gif + TODOs

* chore(deps): lower dep crit

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* feat(lib): add support for GIF

* fix(ci): rewrite tests

* fix

* chore(ci): prevent calling concat on empty list

* add missing dot

* fix(ci): update frame comparison ?

* fix(log): add handler to libav logger

* chore: add TODO

* fix(lib): concat issue

* Revert "fix(ci): update frame comparison ?"

This reverts commit 904cfb46ae.

* fix(ci): make it pass tests

* chore(lib/docs/ci): remove FFMPEG entirely

This removes any reference to FFMPEG, except in translation files

* chore(deps): update lockfile

* chore(lib): rewrite ffprobe

* fix typo

* slightly more aggressive removal of ffmpeg in docs; minor language changes

* fix gif output stream dimensions

* minor style change

* fix encoding of (transparent) mov files

* fixed metadata / comment

* set frame rate for --format=gif in output_stream

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* more video tests for different render settings, also test pix_fmt

* improve default bitrate setting via crf

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* parametrized format/transparency rendering test

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* context managers for (some) av.open

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update manim/utils/commands.py

Co-authored-by: Jérome Eertmans <jeertmans@icloud.com>

* fixed segfault

* update test data involving implicit functions (output improved!)

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* explicity set pix_fmt for transparent webms

* special-special case extracting frame from vp9-encoded file with transparency

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix transparent gifs, more special casing in parametrized video format test

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* run tests on macos-latest again

* removed old control data

* Revert "run tests on macos-latest again"

This reverts commit f50efa4b88.

* added sound to codec test; fixed issue with sound track in gif (disabled) and webm (now via opus)

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* manual wav -> ogg transcoding

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fixed f-string

* refactored codec test, split out gif

* check for non-zero audio samples

* more cleanup

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* remove ffmpeg from readthedocs apt_packages

* round up run_time if positive and shorter than current frame rate

* added more run_time tests

* black

* improve implementation of test

* removed some unused imports

* improve wording of logged warning

Co-authored-by: Jérome Eertmans <jeertmans@icloud.com>

* move run_time checks from Animation.begin to Scene.get_run_time

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* remove unused import

* flake: PT012

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Jérome Eertmans <jeertmans@icloud.com>

* Use --py39-plus in pre-commit (#3761)

* Use --py39-plus in pre-commit

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix indication.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Optimized `manim.utils.bezier.is_closed()` (#3768)

* Optimized manim.utils.bezier.is_closed()

* oops, that shouldn't have been there

* Slightly optimized is_closed() even more

* Added doctest for is_closed()

* Created and optimized Bézier splitting functions such as `partial_bezier_points()` in `manim.utils.bezier` (#3766)

* Optimized manim.utils.partial_bezier_points()

* Added split_bezier, subdivide_bezier and bezier_remap, and tests

* Use bezier_remap() in VMobject and OpenGLVMobject()

* Note that partial_bezier_points is similar to calling split_bezier twice

* Bump requests to 2.32.0 (#3776)

updated-dependencies:
- dependency-name: requests
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Fix assertions and improve error messages when adding submobjects (#3756)

* Optimized AnimationGroup computation of start-end times with lag ratio

* Added extra comment for init_run_time

* Added full path to imports in composition.py

* Optimized AnimationGroup.interpolate

* Fixed final bugs

* Removed accidental print

* Final fix to AnimationGroup.interpolate

* Fixed animations being skipped unintentionally

* Fix and improve Mobject assertions when adding submobjects

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update examples in Mobject.add() and OpenGLMobject.add() docstrings

* overriden -> overridden

* Joined string in OpenGLMobject error message

* Address requested changes

* OpenGLVMObjects -> OpenGLVMobjects

* Use tuplify in VGroup.__setitem__()

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Add pyproject for ruff formatting (#3777)

* Add pyproject for ruff

* add black config back

* Make only formatting

* rearrange isort to undo diff

* poetry lock

* Feedback

* style

Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>

---------

Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>

* pre-commit change to ruff (#3779)

* pre-commit change to ruff

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fixes

* astral-sh ruff bump

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Ignore Ruff format in git blame (#3781)

* Fixed `there_and_back_with_pause()` rate function behaviour with different `pause_ratio` values (#3778)

Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>

* Optimize VMobject methods which append to points (#3765)

* Add `@` shorthand for `CoordinateSystem` methods `coords_to_point` (`c2p`) and `point_to_coords` (`p2c`) (#3754)

* Add shorthand for axes

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add spacing

Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>

* Convert CoordinateSystem example, and add to NumberLine

* Add doctest for NumberLine

* Add test

* Fix typehint for c2p

Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>
Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>

* [pre-commit.ci] pre-commit autoupdate (#3784)

* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.4 → v0.4.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.4...v0.4.5)
- [github.com/codespell-project/codespell: v2.2.6 → v2.3.0](https://github.com/codespell-project/codespell/compare/v2.2.6...v2.3.0)

* Fix typo

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: JasonGrace2282 <aarush.deshpande@gmail.com>

* [pre-commit.ci] pre-commit autoupdate (#3794)

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.5 → v0.4.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.5...v0.4.7)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Add Ruff Lint (#3780)

Adds Ruff Linting to CI, and replaces isort in the pre-commit config with Ruff's isort rules.

Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>

* Replace Pyupgrade with Ruff rule (#3795)

* Add config for pyupgrade

* Fix pyupgrade errors

* Unsafe-fixes

* Nicer way of formatting

Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Revert "Nicer way of formatting"

This reverts commit 48013f4a30.

---------

Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Bump tornado from 6.4 to 6.4.1 (#3796)

Bumps [tornado](https://github.com/tornadoweb/tornado) from 6.4 to 6.4.1.
- [Changelog](https://github.com/tornadoweb/tornado/blob/master/docs/releases.rst)
- [Commits](https://github.com/tornadoweb/tornado/compare/v6.4.0...v6.4.1)

---
updated-dependencies:
- dependency-name: tornado
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Update opengl_vectorized_mobject.py (#3790)

The "insert_n_curves_to_point_list" function requires the "points" argument to be a numpy array, since it calls the "get_bezier_tuples_from_points" function, which requires "points" to be a numpy array because it has the "return points.reshape((-1, nppc, 3))" statement. Ordinary lists do not have a "reshape" method.

So we need to convert "sp1" and "sp2" to numpy arrays before calling the "insert_n_curves_to_point_list" function.

Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>

* [pre-commit.ci] pre-commit autoupdate (#3801)

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.7 → v0.4.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.7...v0.4.8)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Add typings to `OpenGLMobject` (#3803)

* Add typings to OpenGLMobject

* Import typing_extensions

* Add explicit returns to inner functions in .arrange_in_grid()

* Add quotes to parameters in ValueError

* Add some more typings

* Address requested changes

* Type apply_over_attr_arrays with TypeVar

* Fix use of TypeVar

* Add Vector3D typing in set_x, set_y and set_z

* fix: importing manim should not trigger pygments.styles.get_all_styles (#3797)

* fix: importing manim should not trigger pygments.styles.get_all_styles

Removed the Code.styles_list attribute.

Rewrote the documentation to say that a list of all styles can be generated by calling list(pygments.styles.get_all_styles()).

The example in the docstring of Code was rewritten to use an explicit code style name.

* fix: small change to documentation

* Added potential class method to get available code styles.

* Adding typehints to newly-added attributes.

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* Removing unnecessary lines.

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

---------

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* [pre-commit.ci] pre-commit autoupdate (#3809)

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.8 → v0.4.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.8...v0.4.9)
- [github.com/PyCQA/flake8: 7.0.0 → 7.1.0](https://github.com/PyCQA/flake8/compare/7.0.0...7.1.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Bump urllib3 from 2.2.1 to 2.2.2 (#3810)

Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.2.1 to 2.2.2.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.2.1...2.2.2)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Update macos packages (#3812)

* Fixed infinite loop in OpenGL `BackgroundRectangle.get_color()` (#3732)

Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>

* docs(contributing): add manim.typing guide (#3669)

* docs: add manim.typing guide

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add colors

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add another example for when to typehint as Vector

* Add docs for images+functions

* write Beziers

* Improve based on feedback

* type -> Type

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>

* Implement partial movie files

* Sort of combining working

* Fix `DiGraph` edges not fading correctly on `FadeIn` and `FadeOut` (#3786)

* Make `Line::set_points_by_ends` behavior consistent with constructor

* Use `Line::set_points_by_ends` in edge updaters

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Undo unnecessary change to Graph

* Update manim/mobject/geometry/line.py

Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>

* fix segfault

* Fix upside down video

* fix some bugs with wait()

* Fix CLI flags

* Fix FileWriter not rendering for the correct amount of time in video

* Progress on removing cairo

* Avoid printing caching message if write-to-movie is false

* Fix Manim directive

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* [pre-commit.ci] pre-commit autoupdate (#3823)

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.9 → v0.4.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.9...v0.4.10)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Implement progressbar

* hji

* feat(autoaliasattr): Implement documentation of TypeVar's (#3818)

* feat(autoaliasattr): Implement Documentation of TypeVar's

* Feedback

---------

Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>

* Fixed `Arrow3D.put_start_and_end_on()` to use the actual end of the arrow (#3706)

* my test is not passing, i need to add a little bit of docs. except that everything is fine. Issue is solved!

* fixed the issue #3655

* removed comments

* fix: 3706 original issue, without adding unnecessary dot
added: i added self.height parameter in Cone class
my tests passes

* Changes that way how end point of Arrow3D is calculated.

* I've improved the methods get_start and get_end for the Cone class, and get_end for the Arrow3D class to ensure they return accurate geometrical points after transformations. Additionally, I've included unit tests to verify the correctness of these methods for the Cone class.

* Finished! Replaced VMobject by VectorizedPoint as Ben suggested while ago

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>

* Optimized `manim.utils.bezier.get_smooth_cubic_bezier_handle_points()` (#3767)

* Optimized manim.utils.get_smooth_cubic_bezier_handle_points()

* Fixed typo in docstring regarding vector u

* Add tests for get_smooth_cubic_bezier_handle_points

* Fix backreference in test docstrings

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

---------

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* Implement no progressbar if write_to_movie=False

* Fix docker profile (#3827)

* Doc: add docstrings to Brace (#3715)

* Add docstrings to `Brace` methods

* Add full NumPy format docstring for the `Brace` methods

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* feedback

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: JasonGrace2282 <aarush.deshpande@gmail.com>
Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Some docs

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* [pre-commit.ci] pre-commit autoupdate (#3834)

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.10 → v0.5.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.10...v0.5.0)
- [github.com/pre-commit/mirrors-mypy: v1.10.0 → v1.10.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.10.0...v1.10.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Bump docker/build-push-action from 5 to 6 (#3835)

Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* bugfixes + speed + linting

* Deprecate opengl fixture

* Implement basic plugins

* Add docstring with doctest

* Add pydantic to deps

* remove all pycairo references

* Cleanup other stuff

Adjust default values for config, clean up Scene

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* [pre-commit.ci] pre-commit autoupdate (#3844)

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.5.0 → v0.5.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.0...v0.5.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Bump certifi from 2024.2.2 to 2024.7.4 (#3841)

Bumps [certifi](https://github.com/certifi/python-certifi) from 2024.2.2 to 2024.7.4.
- [Commits](https://github.com/certifi/python-certifi/compare/2024.02.02...2024.07.04)

---
updated-dependencies:
- dependency-name: certifi
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* more plugin stuff

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* kinda working Rotate?

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Working Rotation?

* Better test scene

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* change interpolate_mobject -> interpolate

* README.md

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* undo i18n changes

* Remove deprecated quadratic Bézier functions

* Add docstring back to Rotate

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add missing type annotations

* Add manim prefix to imports in rotation.py

* Fix imports of AnimationProtocol and OpenGLMobject in __init__.py

* Modify Scene.replace() docstring

* Remove Point(PMobject) and only use Point(Mobject)

* Remove Point from point_cloud_mobject.__all__

* Fix Ruff errors and comment out submob.refresh_triangulation() in OpenGLVMobject.change_anchor_mode()

* Update OpenGLVMobject.insert_n_curves_to_point_list()

* Fix incorrect version number in plugin section in docs (#3849)

* Bump zipp from 3.18.2 to 3.19.1 (#3847)

Bumps [zipp](https://github.com/jaraco/zipp) from 3.18.2 to 3.19.1.
- [Release notes](https://github.com/jaraco/zipp/releases)
- [Changelog](https://github.com/jaraco/zipp/blob/main/NEWS.rst)
- [Commits](https://github.com/jaraco/zipp/compare/v3.18.2...v3.19.1)

---
updated-dependencies:
- dependency-name: zipp
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Fix commands.py to use Manager(SceneClass) instead of SceneClass()

* Reimplement buffers, add typehints, and start caching

Also started cleaning up Scene and Manager

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix ci

* Rename assemble_family -> note_changed_family to match 3b1b

* patch typehints due to lack of Proxy[T] in python typing

* Rename Image aliases to PixelArray, refactor imports to avoid circular imports, and refactor TypeVar R definition to allow defining class OpenGLMobject(Generic[R])

* Rename typing.Image type aliases to PixelArray to avoid conflict with PIL.Image (#3851)

* Revert some import refactor to pass tests

* Use get_bezier_tuples_from_points(points) in OpenGLVMobject.insert_n_curves_to_point_list()

* Progress on rewriting frame stuff

* Change from tempconfig to a config fixture in tests (#3853)

* Implement changes to fixtures

* Change tests to use the config fixture

* fix merge conflicts

* Fix :attr:`.ManimConfig.format` not updating movie file extension (#3839)

* Fix config.format not updating config.movie_file_extension

* Add test

* Rewrite `manim.utils.bezier.get_quadratic_approximation_of_cubic()` to produce curves which can be animated smoothly (#3829)

* Rewrite get_quadratic_approximation_of_cubic() and add test

* Move test_get... to end of file

* Complete docstring for get_quadratic...()

* Progress towards a working png file-writer

* Log execution time of scene rendering in the Manim Checkhealth command (#3855)

* Log execution time of scene rendering in the Manim Checkhealth command

* Use timeit.timeit instead of time.time for more reliable profiling

* Optimize `VMobject.pointwise_become_partial()` (#3760)

* Optimize VMobject.pointwise_become_partial()

* selftransformation -> self

* Small factorization of nppc

* Update macos.rst (#3857)

* Update macos.rst

As of July/2024, brew installs Manim and its dependencies.
Guideline for installing dependencies with brew, and attempt to install using pip3 will no longer work either venv or not.
Now homebrew team manages python resources as "System-wide" only with Brew installed resources. 
Hence, to give first time installer just leave a single line install command would be the simplest option we have.

* Update docs/source/installation/macos.rst

---------

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* VGroup -> OpenGLVGroup + typehint changes

* Remove file that appeared somehow

* Delete opengl tests

* Make manager generic in its scene for autocomplete

* Undo deletion of manim/mobject/opengl

* progress on tests

* Convert scene.render() -> manager.render()

* fix opengl context not found in tests

* use keyword argument when creating scene

This makes it less changes for subclasses to "just work"

* Change Scene() -> Manager(Scene)

* Change to use OpenGL* for some classes

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Naveen M K <naveen521kk@gmail.com>
Co-authored-by: Naveen M K <naveen521kk@gmail.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Immanuel-Alvaro-Bhirawa <127812163+Immanuel-Alvaro-Bhirawa@users.noreply.github.com>
Co-authored-by: Nikhil Iyer <iyer.h.nikhil@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Harald Schilly <harald.schilly@gmail.com>
Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>
Co-authored-by: Alex Lembcke <alex.lembcke@gmail.com>
Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>
Co-authored-by: Václav Blažej <6208643+vaclavblazej@users.noreply.github.com>
Co-authored-by: Václav Blažej <vaclav.blazej@warwick.ac.uk>
Co-authored-by: Jérome Eertmans <jeertmans@icloud.com>
Co-authored-by: Uwe Zimmermann <uwe.zimmermann@sciencetronics.com>
Co-authored-by: Lawrence Qupty <80665382+Lawqup@users.noreply.github.com>
Co-authored-by: JosephD <46393716+jcep@users.noreply.github.com>
Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>
Co-authored-by: szchixy <szchixy@outlook.com>
Co-authored-by: Melody Griesen <pikablue107@gmail.com>
Co-authored-by: Melody Griesen <jvgriese@ncsu.edu>
Co-authored-by: yuan <yuan_xin_yu@hotmail.com>
Co-authored-by: Benjamín Ubilla <118409119+MathItYT@users.noreply.github.com>
Co-authored-by: Doaa Muhammad <126016494+doaamuham@users.noreply.github.com>
Co-authored-by: Robin Dimasin <robindimasin@gmail.com>
Co-authored-by: Paul Uhlenbruck <48606747+pauluhlenbruck@users.noreply.github.com>
Co-authored-by: Yash Mundada <F20210001@pilani.bits-pilani.ac.in>
Co-authored-by: TheMathematicFanatic <63360493+TheMathematicFanatic@users.noreply.github.com>
Co-authored-by: Václav Volhejn <8401624+vvolhejn@users.noreply.github.com>
Co-authored-by: Hydromel Victor Doledji <victorvaddely@gmail.com>
Co-authored-by: Dan Davison <dandavison7@gmail.com>
Co-authored-by: Greg Rupp <gmrupp@gmail.com>
Co-authored-by: NotWearingPants <26556598+NotWearingPants@users.noreply.github.com>
Co-authored-by: Sparsh Goenka <43041139+sparshg@users.noreply.github.com>
Co-authored-by: Said Taghadouini <84044788+staghado@users.noreply.github.com>
Co-authored-by: Jason Villanueva <a@jsonvillanueva.com>
Co-authored-by: Abulafia <44573666+abul4fia@users.noreply.github.com>
Co-authored-by: Christian Clauss <cclauss@me.com>
Co-authored-by: Chin Zhe Ning <108804868+biinnnggggg@users.noreply.github.com>
Co-authored-by: HairlessVillager <64526732+HairlessVillager@users.noreply.github.com>
Co-authored-by: Pavel Zwerschke <pavelzw@gmail.com>
Co-authored-by: Sir James Clark Maxwell <71722499+SirJamesClarkMaxwell@users.noreply.github.com>
Co-authored-by: Daniel Zhu <danielfangzhu@gmail.com>
Co-authored-by: Stefano Ottolenghi <stejey@gmail.com>
Co-authored-by: MontroyJosh <122334909+MontroyJosh@users.noreply.github.com>
Co-authored-by: Greg Rupp <greg.rupp@66degrees.com>
Co-authored-by: Amirreza A <45117218+amrear@users.noreply.github.com>
Co-authored-by: Jinchu Li <63861808+JinchuLi2002@users.noreply.github.com>
Co-authored-by: VPC <111203113+VinhPhmCng@users.noreply.github.com>
Co-authored-by: anagorko <3418166+anagorko@users.noreply.github.com>
Co-authored-by: jkjkil4 <52841865+jkjkil4@users.noreply.github.com>
Co-authored-by: yang-tsao <caoyang2005@outlook.com>
Co-authored-by: Eddie Ruiz <eduardo.j.ruiz@gmail.com>
Co-authored-by: Cameron Burdgick <156892808+camburd2@users.noreply.github.com>
Co-authored-by: Francisco Manríquez <francisco.manriquezn@usm.cl>
Co-authored-by: CJ Lee <changjoon.lee@arenne.net>
2024-07-14 21:12:04 -04:00
adeshpande
08264dcf76
Merge with main (#3815)
* fix a typo in deep_dive.rst (#3377)

* Several GitHub actions updates (#3397)

* Bump docker/login-action from 2 to 3

Bumps [docker/login-action](https://github.com/docker/login-action) from 2 to 3.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump docker/setup-buildx-action from 2 to 3

Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2 to 3.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump docker/build-push-action from 4 to 5

Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4 to 5.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump docker/setup-qemu-action from 2 to 3

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 2 to 3.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump actions/checkout from 3 to 4

Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* rtd: enable htmlzip build (#3355)

also, bump python to 3.11
fixes https://github.com/ManimCommunity/manim/issues/3342

* fix(docs): Remove extra curly bracket in LaTeX math expression to fix issue #3330 (#3389)

* Bug fix: Use np.isclose for float equality in number line elongated ticks (#3392)

* use np.isclose for float equality in number line elongated ticks

* use offsets relative to x_min to tell if we need to elongate a tick

* forgot to subtract to create list of offsets

* add test for elongated ticks float equality

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Remove unused import

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Updated several dependencies (#3399)

* updated lockfile

* ran poetry update again

* pyproject.toml: update manimpango version (#3405)

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Added docs for functions in `mobject_update_utils` (#3325)

* Added docs for functions in mobject_update_utils

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Updated docstring of always_shift

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Added period to sentence.

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Updated parameter description in always_redraw

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Update always_rotate description

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Finished parameters in always_redraw

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Changed comment in always_shift

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* update always_shift description

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* used normalize from manim.utils.space_ops

* fixed indentation in always_redraw

* added type-hints

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Fix tests to run on Cairo 1.18.0 (#3416)

* Add a script to build and install cairo

* Update gui tests for cairo 1.18.0

* update script to set env vars

* Make the script run with plain python

* Prefer the recently built one in pkg-config

* Skip the built if it's windows

* CI: build and install latest cairo

* CI: only run when cache is missed

* Disable compiling tests while building cairo

* update poetry lock file

* Display the cairo version when running pytest

* fixup

* tests: skip graphical test when cairo is old

* fix the path to find the pkgconfig files on linux

* set the LD_LIBRARY_PATH too

only then it'll work on linux

* fixup

* small fixup

* Move the script inside `.github/scripts` folder

* Make the minimum cairo version a constant

* Seperate setting env vars to a sperate step

this seem to have broken when cache is hit

* Fix: Fixed a bug in regards to empty inputs in AddTextLetterByLetter class.  (#3404)

* Misc: Just a class to test out some functions

* Fix: Fixed a bug in AddTextLetterByLetter class

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix: Adjusted changes according to Ben's comments

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix: Removed imports

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Feat: Adjusted changes to AddTextLetterByLetter

* Feat: Added test_creation

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Introduce new workflow creating a downloadable version of the documentation (#3417)

* Revert "rtd: enable htmlzip build (#3355)"

This reverts commit 571f79be2c.

* use python3.11 to build docs

* upgrade python version used in release publish workflow

* new workflow for building downloadable docs

* change event trigger for testing

* sudo apt

* rename release job; build html in poetry env

* set GITHUB_PATH instead of PATH

* introduce additional step

* use correct binary path

* forgot microtype

* fonts-roboto + actually compress files correctly

* fix asset path

* Update .github/workflows/release-publish-documentation.yml

Co-authored-by: Naveen M K <naveen521kk@gmail.com>

* pull_request -> workflow_dispatch

* Update .github/workflows/release-publish-documentation.yml

---------

Co-authored-by: Naveen M K <naveen521kk@gmail.com>

* Fix incorrect submobject count of multi-part Tex/MathTex mobjects by stopping them from adding empty submobjects (#3423)

* do not add a VectorizedPoint as a submobject if SingleStringMathTex renders to empty SVG

* test new behavior

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update tests/module/mobject/text/test_texmobject.py

* Update tests/module/mobject/text/test_texmobject.py

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* CI: fix caching of cairo (#3419)

I forgot to change the path after moving around the file.

* Fix CSV reader adding empty lists in rendering summary (#3430)

* Fix CSV reader adding empty files

Fixes issue #3311

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Fix None check order in _tree_layout (#3421)

* Fix None check order in _tree_layout

* add tests to test_graph.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Bump teatimeguest/setup-texlive-action from 2 to 3 (#3431)

Bumps [teatimeguest/setup-texlive-action](https://github.com/teatimeguest/setup-texlive-action) from 2 to 3.
- [Release notes](https://github.com/teatimeguest/setup-texlive-action/releases)
- [Commits](https://github.com/teatimeguest/setup-texlive-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: teatimeguest/setup-texlive-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* bump dependencies -- see #3241 (#3433)

* Fix Typing (#3086)

* first draft of color class + starting library conversion

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* changed everything to Manim color todo: figure out circular dependency in utils

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* first working draft of new color version

* resolving conflicts

* resolving conflicts

* resolving conflicts

* resolving conflicts

* resolving conflicts

* changed default internal value of ManimColor to np.ndarray[float]

* starting to fix tests

* fixed more tests and changed precision of manim color

* removed premature color conversion

* fixed some more tests

* final test changes

* fix doctests

* fix for 3.8

* fixing ManimColor string representation

* removing some unneccesary conversions

* moved community constants to manim_colors.py and added more color standards

* Added typing.py and typed bezier.py, core.py, constants.py  fully

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fixed codeql complaints

* add type ignore for np.allclose

* fixed import in three_dimensions

* added ignore for F401 back again in flake

* added typings to coordinate_systems.py

* Few improvements to `graphing/coordinate_systems.py`

* added some typings to mobject/geometry/line.py

* updated typings for mobject/geometry/line.py

* Add missing imports to `line.py`

* added typings to three_dimensions.py

* Use `FunctionOverride` for animation overrides

Fix type signature of `set_color_by_gradient`

* Remove `TYPE_CHECKING` check

Doc is failing

* Revert "Remove `TYPE_CHECKING` check"

Fails due to circular import

* Use `Self` in `coordinate_systems.py`

* Typehinted mobject.py and updated manim.typing.py

* Typed VMobject

* Type-hinted manim.mobject.geometry

* math.cos->np.cos, etc & fixed incorrect typehints

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix missing annotations import

* TypeAlias fix in typing.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add ignore errors again to mypy because commits are not possible like this

* Fix last typing issues

* Update docs

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Only type check manim

* Try fixing pre-commit

* fix merge

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix compat

* Fix compat again

* Fix imports compat

* Use union syntax

* Use union syntax

* Fix reduce_across_dimension

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Various test and merge fixes

* Doc fixes

* Last doc fix

* Revert usage of np over math

* Bump numpy version

* Remove obsolete duplicate example

* Fixed Incorrect Typehint in manim.constants

* Fix docstring typo

* More fixes

Use mypy.ini instead of .mypy.ini
Fix more docstrings
Improve types in utils and constants

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* docs fixes

* Add internal aliases

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix compat

* line lengths in .rst file, formatting, typos

* add docstring for space_ops:cross2d

* add some more arrow tip typings (in a non-circular import causing way)

* yes, this can be deleted

* fix formatting of example

* added docstring to bezier::inverse_interpolation

* added docstring + test for bezier::match_interpolate

* some improvements in coordinate_systems

* Vector -> Vector3

* replaced np.ndarray with more appropriate type hints

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Apply feedback

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* revert to previous (new) version

* fix doctest

* fix ReST errors

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Alex Lembcke <alex.lembcke@gmail.com>
Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>
Co-authored-by: JasonGrace2282 <aarush.deshpande@gmail.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* fix: issue with ImageMobject bounding box (#3340)

* fix: fix an issue with ImageMobject bounding box

A missing point resulted in smaller bounding box causing issues it to be
smaller when the object is rotated. Added the missing fourth point to
ImageMobject points and altered call from camera. Filled in docstring
that used to propagate from superclass, saying that ImageMobject has no
points.

* add a test to check that rotating an image to and from doesn't change it

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Václav Blažej <vaclav.blazej@warwick.ac.uk>
Co-authored-by: Naveen M K <naveen521kk@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* chore(deps): add Python 3.12 support (#3395)

* chore(deps): add Python 3.11 and 3.12 support


chore(deps): update lock file


chore(deps): remove colour


fix(deps): force NumPy version


fix(deps): relax constraints


chore(deps): update lock file

* fix(deps): make poetry happy

* fix(ci): skia pathops on 3.12

* fix(test): doctest skip

* disable python 3.8 pipeline

* removed get_parameters, replaced by direct call to inspect

* black

---------

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Added ability to remove non-svg LaTeX files (#3322)

* Added ability to remove latex junk (default True)

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fixed tests (hopefully), and whitelisted .tex

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* reverted weird changes from merge

* See previous commit message

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fixed logs-too-long test

* Fixed log output

* Fixed typo ;)

* deleted unused variable

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* moved latex deletion to tex_file_writing.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* removed changes in scene files

* Added caching based on LaTeX expression .svg

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Deleted unused function in delete_old_tex

* make if condition more readable

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* cleaned up svg file check

* changed blacklist -> whitelist for file endings

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Reverted docstring change

* Updated delete_non_svg files docstring

* Changed list to a set

* Update manim/_config/utils.py

* Update manim/cli/render/global_options.py

* added one test for the no_latex_cleanup config option

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>
Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>

* feat: DecimalNumber() - added spacing between values and unit (#3366)

* feat: DecimalNumber() - added spacing between values and unit

* Update manim/mobject/text/numbers.py

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Update manim/mobject/text/numbers.py

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Update manim/mobject/text/numbers.py

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Update manim/mobject/text/numbers.py

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Update manim/mobject/text/numbers.py

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Update manim/mobject/text/numbers.py

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Update manim/mobject/text/numbers.py

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

---------

Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Add option to run examples directly with binder (#3427)

* Add option to run examples directly with binder

The minified JS is from
https://github.com/naveen521kk/manim-binder

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* slight style changes

* update the js file to fix on chrome

Signed-off-by: Naveen M K <naveen521kk@gmail.com>

* show the run button as an cursor

* make the video to be 100% of the width

* Update manim/utils/docbuild/manim_directive.py

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Add a "Make interactive" button instead of "Run" button

Clicking on the "Make interactive" button show the code-editor and "run" button

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* update margin for run interactive button

---------

Signed-off-by: Naveen M K <naveen521kk@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Prepare v0.18.0 (#3439)

* generated changelog and bumped version

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* changed some PR descriptions in the changelog

* fix some docbuild warnings

* fixed a reference that became ambiguous

* copyedit pass of changelog

* some more changelog polishing

* bump release date

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* updated release date

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Fixed wrong path in action building downloadable docs (#3450)

* fixed wrong path in action building downloadable docs

* fix second occurrence of wrong path

* Allow accessing ghost vectors in :class:`.LinearTransformationScene` (#3435)

* Fix CSV reader adding empty files

Fixes issue #3311

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added LinearTransformationScene.ghost_vectors

* Added test and prevented empty VGroups as ghost vectors

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fixed typo in example

* Added ability to join together multiple renders

* Revert "Added ability to join together multiple renders" (wrong branch)

This reverts commit dee29c390f.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Add type hints to `_config` (#3440)

* Add type hints to `_config`

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix call issues

* Fix wrong value being used

* Fix test

* Fix wrong value being set

* lint

* Few type fixes

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Fix Idicate docs typo (#3461)

* Update indication.py (#3477)

reading docs, im sure oppising isnt a word

* Optimized `get_unit_normal()` and replaced `np.cross()` with custom `cross()` in `manim.utils.space_ops` (#3494)

* Added cross and optimized get_unit_normal in manim.utils.space_ops

* Added missing border case to new get_unit_normal where one vector is nonzero

* Updated test_threed.py::test_Sphere test data

* Update dependency constraints, fix deprecation warnings (#3376)

* WIP: Update metadata

* Finish removing upper bounds

Drop requests dependency, use urllib instead
order depencencies

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix issues on 3.12

* Order dev dependencies

* Update most dev deps, update lint config

* Add missing import

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* trigger CI

* More deprecation fixes

* Missing argument

* Deprecation fixes, again

* Use older xdist to fix test flakyness

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* fix 360° to 180° in quickstart tutorial (#3498)

* Update Docker base image to python3.12-slim (#3458) (#3459)

* Update Docker base image to python3.12-slim (#3458)

* Update docker/Dockerfile

---------

Co-authored-by: Melody Griesen <jvgriese@ncsu.edu>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* fix line_join to joint_type in example_scenes/basic.py (#3510)

* fix typo in docstring for DtUpdater example: line -> square (#3509)

* Implement caching of fonts list to improve runtime performance (#3316)

* Implement caching of fonts list to improve runtime performance

* Fix small use_svg_cache kwargs error

* replaced font list with LRU cache

* Removed deprecated new command (#3512)

Co-authored-by: Naveen M K <naveen521kk@gmail.com>

* Added `cap_style` feature to `VMobject` (#3516)

* Added cap_style feature to VMobject

* Added an example to `set_cap_style` method

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Unsplitted line 2501

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added graphical test for cap_style

* Added vmobject_cap_styles.npz for testing cap_styles

* Removed # noqa comments from vectorized_mobject.py

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* feat(cli): optionally hide version splash (#3329)

* feat(cli): optionally hide version splash

As discussed in #3326, this PR proposes a new optional flag to hide the version splash when manim command in launched. Additionally, the splash print is now inly executed when the CLI is executed, not on module import.

After looking at the current documentation, it does not seem to change anything. I only saw that you documented a version splash for when the CLI is used, but not when the module is imported. So removing it should not break the api docs.

In the future, users can still have version information with `import manim; print(manim.__version__)`.

Closes #3326

* chore(tests): make tests pass

---------

Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>

* Reformatting the `--save_sections` output to have the format `<Scene>_<SecNum>_<SecName><extension>` (#3499)

* Worked on issue 3471, fixing rendered file names to inherit section name

* Modified file name to include section number and name

* Modified tests for file names to include number and name, in order to pass

---------

Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>

* Explain ``.Transform`` vs ``.ReplacementTransform`` in quickstart examples (#3500)

* Explained ReplacementTransform vs Transform

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added section explaining Transform vs ReplacementTransform

* Added a->b->c example

* Clarified explanation

* Fixed Typo

* Fixed missing colon

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>

* Fix formatting building blocks (#3515)

* Fix formatting building blocks

* Fix formatting building blocks

---------

Co-authored-by: Jason Grace <110117391+JasonGrace2282@users.noreply.github.com>

* Bump jupyter-server from 2.9.1 to 2.11.2 (#3497)

Bumps [jupyter-server](https://github.com/jupyter-server/jupyter_server) from 2.9.1 to 2.11.2.
- [Release notes](https://github.com/jupyter-server/jupyter_server/releases)
- [Changelog](https://github.com/jupyter-server/jupyter_server/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jupyter-server/jupyter_server/compare/v2.9.1...v2.11.2)

---
updated-dependencies:
- dependency-name: jupyter-server
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Account for dtype in the pixel array so the maximum value stays correct in the invert function (#3493)

* fix(lib): fix

This fixes an issue where the `invert` argument would only work for `uint8` dtypes. Now the `max` value is updated according to the pixel array dtype.

Maybe we should add unit tests for that, but haven't found an obvious place to put unit tests.

* chore(ci): add basic test

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix(ci): wrong attr name

* Update tests/module/mobject/test_image.py

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Added `grid_lines` attribute to `Rectangle` to add individual styling to the grid lines (#3428)

* Added 'grid_line_stroke_width' parameter in Rectangle

* Added 'grid_lines' (VGroup) attribute to 'Rectangle' class

---------

Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>

* Fix rectangle grid properties (#3082) (#3513)

* Import  for both vertical and horizontal gridlines in

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Fix animations with zero runtime length to give a useful error instead of a broken pipe (#3491)

* Fix animation group not erroring when instantiated with an empty list

* Move error messages into Animation.begin()

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update manim/animation/animation.py

* Update manim/animation/composition.py

* Update manim/animation/animation.py

Co-authored-by: Jason Grace <110117391+JasonGrace2282@users.noreply.github.com>

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Jason Grace <110117391+JasonGrace2282@users.noreply.github.com>

* fixed the stroke width issue with single color in streamlines (#3436)

* fixed the stroke width issue with single color in streamlines

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added test for streamlines

* Added test for streamlines

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: MrDiver <mrdiverlp@gmail.com>

* Add Documentation to `.to_edge` and `to_corner` (#3408)

* Added docstrings and example renders to Mobject.to_corner() and Mobject.to_edge

* Added docstrings and example renders to Mobject.to_corner() and Mobject.to_edge

* Update manim/mobject/mobject.py

* Update manim/mobject/mobject.py

* Update manim/mobject/mobject.py

* Update manim/mobject/mobject.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update manim/mobject/mobject.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Adding the ability to pass lists and generators to .play() (#3365)

* adding the ability to pass lists and generators to .play()

* fix for _AnimationBuilder

* Changed handling of generators to accept lists of generators and normal arguments at the same time

* Animation group handles generators

* Refactored into own function for reusability

* Fix typing

* Fix typing

---------

Co-authored-by: Jason Grace <110117391+JasonGrace2282@users.noreply.github.com>

* follow-up to #3491, made errors more consistent. fixes #3527

* chore(docs): add some words about Cairo 1.18 (#3530)

* chore(docs): add some words about Cairo 1.18

Closes #3521

* fix(docs): typo

* Update testing.rst

* Update testing.rst

* Fix formatting of ``MoveAlongPath`` docs (#3541)

* Remove wag method from Mobject

* Fixed MoveAlongPath

* Revert remove wag

Created a new branch with the wrong base, sorry ;)

* Fixed Animate Type-hint (#3543)

* Remove wag method from Mobject (#3539)

Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>

* Fix typo of `get_y_axis_label` docstring (#3547)

* Finish TODO's in ``contributing/typings.rst`` (#3545)

* Updated typing docs

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added link for protocols

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added object vs Any

* Fix Typo

* Rephrase TypeVar

Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>

* Compare between tuple vs list

Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>

* typing -> collections.abc

Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>

* typing -> collections.abc

Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>

* change method to attr

Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>

* clarify object typehint

Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>

* Fix code typo

Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>

* Added if TYPE_CHECKING section

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix reST for inline code

* Elaborate on if TYPE_CHECKING

Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>

* functions -> collections

Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>

* Fix use of `Mobject`'s deprecated `get_*()` and `set_*()` methods in Cairo tests (#3549)

* Fix Deprecation warnings in cairo tests

* Fix animation/specialized.py

* add note in docstring of ManimColor about class constructors (#3554)

* Added support for Manim type aliases in Sphinx docs + Added new TypeAliases (#3484)

* Updated manim.typing and included TypeAliases in docs.source.conf

* Added Vector2 and reorganized manim_type_aliases

* Fixed __all__ exports for __all__ of manim

* Update manim/cli/render/global_options.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Draft of new typing docs and new autotyping directive

* Changed vertical bars to Unions

* Updated poetry.lock

* Created custom file parser for manim.typing

* Got reST parser going

* Updated autotyping and parsing

* Update parsing

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added code_block toggle

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added typings to directives

* Renamed Tuple to tuple in manim.typings

* Added missing docs for type aliases

* Fixed exponent typo in ManimInt

* Hyperlinks to types work - removed Module Attributes section

* Removed Unused Import

Remove ``import re``

* Added freeglut-devel to workflows for Linux

Hopefully (?) fix the GLU import error

* Fix package name

* Add support for Type Aliases section in every module - Renaming of Vector types

* Add/fix docs for directive, parser and others

* Fixed alias typo in module_parsing

* Fix decode/import bugs, fix minor details in docs

* Added missing docs for utils.docbuild and utils.testing

* Sort alphabetically entries in utilities_misc.rst

* Address review comments, add notes about Vector and hyperlinks inside definition blocks

---------

Co-authored-by: MrDiver <mrdiverlp@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: JasonGrace2282 <aarush.deshpande@gmail.com>

* Improve documentation section about contributing to docs (#3555)

* Improve section in docs about contributing to docs

* Add note about doc build command depending on the OS

* Improve section in docs about contributing to docs

* Add note about doc build command depending on the OS

* Fix wrong toctree path in docs/source/contributing/docs.rst

* Add helpful hints to `VGroup.add()` error message (#3561)

* Improve VGroup creation error message

* Use .__name__ for the type

Co-authored-by: Jason Grace <110117391+JasonGrace2282@users.noreply.github.com>

---------

Co-authored-by: Jason Grace <110117391+JasonGrace2282@users.noreply.github.com>

* exception add if new_rings is none (#3574)

* exception add if new_rings is none

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Fix typing of `Animation` (#3568)

* Add 'to be used in the future' TODOs to ManimFrame (#3553)

* Refactor `TexTemplate` (#3520)

* Refactor `TexTemplate`

* Add tests, refactor some things

* Fixed Some tests

* Move typing imports

* Fix remaining tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: JasonGrace2282 <aarush.deshpande@gmail.com>
Co-authored-by: Jason Grace <110117391+JasonGrace2282@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Bump github/codeql-action from 2 to 3 (#3567)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump actions/upload-artifact from 3 to 4 (#3566)

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump actions/setup-python from 4 to 5 (#3565)

Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* updated several packages (pillow, jupyterlab, notebook, jupyterlab-lsp, jinja2, gitpython) (#3593)

* Removed -s / --save_last_frame flag from CLI arguments (#3528)

* Remove -s flag

* Make help text more verbose

* fix write_subcaption_file error when using opengl renderer (#3546)

* fix write_subcaption_file error when using opengl renderer

* Update manim/scene/scene_file_writer.py

---------

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Update docker.rst to use bash from the PATH (#3582)

* fix typo in value_tracker.py (#3594)

* fix `get_arc_center()` returning reference of point (#3599)

* Add ref_class (#3598)

* Fix typehint (#3592)

* Update ci.yml (#3611)

* fix type hint of indication.py (#3613)

* Revert vector type aliases to NumPy ndarrays (#3595)

* Improve handling of specified font name (#3429)

Co-authored-by: Jason Grace <110117391+JasonGrace2282@users.noreply.github.com>
Co-authored-by: JasonGrace2282 <aarush.deshpande@gmail.com>

The proposed fix does two things :

* If the specified font is 'sans-serif' : change it to 'sans' as this is the name used in the list of fonts
* if the font name is not in the list of fonts, automatically check if the capitalized version of the font exists in the list of fonts. If not, print a warning to the user.

* Remove support for dynamic plugin imports (#3524)

* Remove call to deprecated `pkg_resources`

* Remove support for dynamic plugin imports, update plugin utilities

* fix affected tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* more fixes

* Last fix

* Fix import

* Update docs

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Jason Villanueva <a@jsonvillanueva.com>

* Run poetry lock --no-update (#3621)

* Update jupyter.rst (#3630)

Pinpoint IPython==8.21.0 for Google Colab, because more recent versions are incompatible with their runtime.

* Fix Vector3 -> Vector3D in contributing docs (#3639)

* Bump black from 23.12.1 to 24.3.0 (#3649)

Bumps [black](https://github.com/psf/black) from 23.12.1 to 24.3.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/23.12.1...24.3.0)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump cryptography from 42.0.0 to 42.0.4 (#3629)

Bumps [cryptography](https://github.com/pyca/cryptography) from 42.0.0 to 42.0.4.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/42.0.0...42.0.4)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Code Cleanup: removing unused imports and global variables (#3620)

* Remove unused import

* More security fixes

* Remove unused global variable

* More fixes

* Revert change (actual fix would require some rewrite)

* Add exception for edge case to satisfy warning

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Stuff

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Fixing the behavior of `.become` to not modify target mobject via side effects fix color linking (#3508)

* Copied ndarray for rgbas when interpolating

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* changing .become to copy the target mobject

* change tests and test data to reflect .become new behavior

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update tests/test_graphical_units/test_mobjects.py

* removed unused copy_submobject kwarg

* added doctests and improved documentation

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Added some examples for `Mobject`/`VMobject` methods (#3641)

* Add examples to mobject+vmobject methods

* Add missing import

* Separate whitespace to point_from_proportion

* Fixes!

* Changed example of Mobject.get_color

* Remove unneccessary import

* Add in import

* Fix typehint of `Vector` direction parameter (#3640)

* Fix typehint of Vector

* Change from Vector to Point in typehint

In `TipableVMobject._pointify` it converts a 3D
list of the form [x, y, z] to a Vector3D. Therefore
the direction parameter can take lists, not just numpy arrays.

* Fix bug in :class:`.VMobjectFromSVGPath` (#3677)

* Fixes #3676

* Update manim/mobject/svg/svg_mobject.py

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* Fixed problem and added test

---------

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* Flake8 rule C901 is about McCabe code complexity (#3673)

* Flake8 rule C901 is about McCabe code complexity

It is not about flake8-comprehensions.

* max-complexity = 29

* Fix broken link to Poetry's installation guide in the first time contributors page (#3692)

* Fix minor grammatical errors found in the index page of the documentation (#3690)

* Fix some minor grammatical errors in the index page of the docs

* Fix grammar

* Undo uneccessary change in phrasing

* fix(LICENSE): update year (#3689)

* Remove deprecated parameters and animations (#3688)

* Remove deprecated parameters/animations

* Remove test

* Remove test data

* Attempted fix for windows cp1252 encoding failure (#3687)

* Attempt to fix windows test

* Revert "Attempt to fix windows test"

This reverts commit e31c2077cd.

* try a different fix

* maybe both fixes together?

* try adding in CI

* Update ci.yml

* Update logger_utils.py

* maybe needs a dash?

* try utf8 again

* Remove legacy_windows

* try changing test

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Try decoding after capturing bytes output

* Nicer fix

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix typo (#3696)

* Docs: fix out-dated CLI option in Manim's Output Settings (#3674)

* Docs: fix out-dated CLI option in Manim's Output Settings

* Docs: more fluent English

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* Docs: break lines

* Docs: more fluent English

* Docs: remove a space

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

---------

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* only do actions if try succeeded (#3694)

* Mention pixi in installation guide (#3678)

* Mention pixi in installation guide

* Update docs/source/installation/conda.rst

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Add note

---------

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Fix successive calls of :meth:`.LinearTransformationScene.apply_matrix` (#3675)

* docs: improve installation FAQ's

* I have potentially resolved the issue when in LinearTransformationScene between two animations of transforming space we invoke the self.wait()

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* added another solutions in comments, added tests and removed wrong files from git

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* yeah , i forgot to save the file xd

* fixed the test, removed the comments my in changed file

* fix test and speed up test time for test_apply_matrix

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fixed the test, removed the comments my in changed file

* fixed the test

* Revert "docs: improve installation FAQ's"

This reverts commit e53a1c8d6f.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: JasonGrace2282 <aarush.deshpande@gmail.com>
Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* Bump actions/cache from 3 to 4 (#3607)

Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* Bump FedericoCarboni/setup-ffmpeg from 2 to 3 (#3608)

Bumps [FedericoCarboni/setup-ffmpeg](https://github.com/federicocarboni/setup-ffmpeg) from 2 to 3.
- [Release notes](https://github.com/federicocarboni/setup-ffmpeg/releases)
- [Commits](https://github.com/federicocarboni/setup-ffmpeg/compare/v2...v3)

---
updated-dependencies:
- dependency-name: FedericoCarboni/setup-ffmpeg
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump ssciwr/setup-mesa-dist-win from 1 to 2 (#3609)

Bumps [ssciwr/setup-mesa-dist-win](https://github.com/ssciwr/setup-mesa-dist-win) from 1 to 2.
- [Release notes](https://github.com/ssciwr/setup-mesa-dist-win/releases)
- [Commits](https://github.com/ssciwr/setup-mesa-dist-win/compare/v1...v2)

---
updated-dependencies:
- dependency-name: ssciwr/setup-mesa-dist-win
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* docs: update typing guidelines (#3704)

* Update typing guidelines

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix formatting

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update documentation and typings for `ParametricFunction` (#3703)

* Update documentation and typings for ParametricFunction

* Use manim tyings

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* fix typings

* a few doc fixes

* Update manim/mobject/graphing/functions.py

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* update typings

* remove extraneous line

* update example code

* add line back for comptibility

* import TYPE_CHECKING

---------

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* fix(copyright): automate copyright updating for docs (#3708)

* Fix some typehints in mobject.py (#3668)

* refactor(mobject): fix some typehints

* Move typing_extensions import under `if TYPE_CHECKING`
* Change from using `def animate(self: T ,...) -> T` to `def
  animate(self, ...) -> Self` as stated in PEP 673
* Fix incorrect usage of `T` in a method

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* move updaters type alias into TYPE_CHECKING

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Bump idna from 3.6 to 3.7 (#3693)

Bumps [idna](https://github.com/kjd/idna) from 3.6 to 3.7.
- [Release notes](https://github.com/kjd/idna/releases)
- [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.rst)
- [Commits](https://github.com/kjd/idna/compare/v3.6...v3.7)

---
updated-dependencies:
- dependency-name: idna
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump pillow from 10.2.0 to 10.3.0 (#3672)

Bumps [pillow](https://github.com/python-pillow/Pillow) from 10.2.0 to 10.3.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/10.2.0...10.3.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Fix typo (#3721)

* Fixed `Mobject.put_start_and_end_on` with same start and end point (#3718)

* fix put_start_and_end_on() at the same point

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* [pre-commit.ci] pre-commit autoupdate (#3332)

* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/pre-commit/pre-commit-hooks: v4.4.0 → v4.6.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.4.0...v4.6.0)
- [github.com/pycqa/isort: 5.12.0 → 5.13.2](https://github.com/pycqa/isort/compare/5.12.0...5.13.2)
- [github.com/asottile/pyupgrade: v3.10.1 → v3.15.2](https://github.com/asottile/pyupgrade/compare/v3.10.1...v3.15.2)
- [github.com/psf/black: 23.7.0 → 24.4.0](https://github.com/psf/black/compare/23.7.0...24.4.0)
- [github.com/asottile/blacken-docs: 1.15.0 → 1.16.0](https://github.com/asottile/blacken-docs/compare/1.15.0...1.16.0)
- [github.com/PyCQA/flake8: 6.1.0 → 7.0.0](https://github.com/PyCQA/flake8/compare/6.1.0...7.0.0)
- [github.com/pre-commit/mirrors-mypy: v1.5.1 → v1.9.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.5.1...v1.9.0)
- [github.com/codespell-project/codespell: v2.2.5 → v2.2.6](https://github.com/codespell-project/codespell/compare/v2.2.5...v2.2.6)

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* make smoothererstep readable again, avoid overlong line

* zoom_value more readable

* fix blacken-docs touching .github

* fix codespell setup, remove unnecessary file, fix some typos

* flake8: ignore E704, triggered by overload

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update docs/source/tutorials/quickstart.rst

* more flake fixes

* try to make blacken-docs happy

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* fix(autoaliasattr): search for type aliases under if TYPE_CHECKING (#3671)

* build(deps): read-the-docs sphinx (#3720)

* Fix issue where SpiralIn doesn't show elements. (#3589)

* Set SpiralIn to use fill_opacity 1 if not set

* Create SpiralIn control data

* Create test for SpiralIn

* Fix spiralin to separate fill and stroke opacity

* resolve opacity issue

* fix test data

---------

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Clean Graph layouts and increase flexibility (#3434)

* allow user-defined layout functions for Graph
+ fixup type annotations

* only pass relevant args

* write tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* change_layout forward root_vertex and partitions
- deduplicated layout code in __init__ and change_layout
- fixed change_layout backwards compatibility

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* add test for change_layout

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix copy/paste error

* fix

* fixup types for CodeQL

* static type the Layout Names

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix dynamic union type for Python 3.9

* add example scenes to LayoutFunction protocol documentation

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Replace references to np.ndarray with standard Manim types

* Label NxGraph as a TypeAlias

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Follow-up to graph layout cleanup: improvements for tests and typing (#3728)

* suggestions from review on #3434

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Update coordinate_systems.py (#3730)

small change

* build(ci): change from macos-latest to macos-13 (#3729)

* Add ``--preview_command`` cli flag (#3615)

* Add preview_command cli flag

* Edit help for --preview_command

* Change back from subprocess.run

* Remove old comment

* Bug with timg stopped happening with sp.run

* Fix docstring

* Revert "Fix docstring"

This reverts commit d2c00fc24dc46586f994237f1d2758528b78d6a3.

* Actually fix docstring

* Change help for option

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

---------

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* AnimationGroup: optimized interpolate() and fixed alpha bug on finish() (#3542)

* Optimized AnimationGroup computation of start-end times with lag ratio

* Added extra comment for init_run_time

* Added full path to imports in composition.py

* Optimized AnimationGroup.interpolate

* Fixed final bugs

* Removed accidental print

* Final fix to AnimationGroup.interpolate

* Fixed animations being skipped unintentionally

* Addressed requested changes

---------

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Fixed ```get_anchors()``` Return Type Inconsistency (#3214)

* changed return type of get_anchors()

* Ensured consistency with OpenGLVMobject

* Fixed CodeQl, updated docstring

* Update manim/mobject/types/vectorized_mobject.py

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* Update manim/mobject/opengl/opengl_vectorized_mobject.py

Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>

* fixed typo, t -> e

* fixed doctest

---------

Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>
Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>

* fixed [""] being set as loaded plugins (#3734)

* Prepare new release: v0.18.1 (#3719)

* add note about changelog in changelog.rst

* bump version

* Update CITATION.cff

* feat: Add three animations that together simulate a typing animation (#3612)

* feat: Add animations that together simulate typing

AddTextLetterByLetterWithCursor

RemoveTextLetterByLetterWithCursor

Blink

* Revert "feat: Add animations that together simulate typing"

This reverts commit 5fe256880d.

* Revert "Revert "feat: Add animations that together simulate typing""

This reverts commit 6a8244a157.

* Add new animations to __all__

* Temporarily remove docs example

* Modify "Blink" and add docstring examples back in

To avoid 0-second animations, which fail docstring test

* Address requested changes

Fix imports
Remove redundant constructor arguments
Improve names

* Shorten names

* Fix release documentation building (#3737)

* [pre-commit.ci] pre-commit autoupdate (#3739)

updates:
- [github.com/psf/black: 24.4.0 → 24.4.2](https://github.com/psf/black/compare/24.4.0...24.4.2)
- [github.com/pre-commit/mirrors-mypy: v1.9.0 → v1.10.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.9.0...v1.10.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Fixes #3744 (#3745)

Co-authored-by: Andrzej Nagórko <>

* Bump tqdm from 4.66.1 to 4.66.3 (#3746)

Bumps [tqdm](https://github.com/tqdm/tqdm) from 4.66.1 to 4.66.3.
- [Release notes](https://github.com/tqdm/tqdm/releases)
- [Commits](https://github.com/tqdm/tqdm/compare/v4.66.1...v4.66.3)

---
updated-dependencies:
- dependency-name: tqdm
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump jinja2 from 3.1.3 to 3.1.4 (#3750)

Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.3 to 3.1.4.
- [Release notes](https://github.com/pallets/jinja/releases)
- [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/jinja/compare/3.1.3...3.1.4)

---
updated-dependencies:
- dependency-name: jinja2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Add typehints to `manim.utils.iterables` (#3751)

* typehint iterables

* organize typing hints

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* remove any

* Add overloads for tuplify

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Remove example

* feedback

* Make TypeVars accessible at runtime

* Add hints for zip

Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>

* typing -> collections.abc

Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>

* try to make mypy happy

* zip[tuple[T, ...]] instead of zip[T]

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>

* Let `SceneFileWriter` access `ffmpeg` via `av` instead of via external process (#3501)

* added av as a dependency

* make partial movie files use av instead of piping to external ffmpeg

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* opengl rendering: use av for movie files

* no need to check for ffmpeg executable

* refactor: *_movie_pipe -> *_partial_movie_stream

* improve (oneline) documentation

* pass more options to partial movie file rendering

* move ffmpeg verbosity settings to config; renamed option dict

* replaced call to ffmpeg in combine_files by using av

Co-authored-by: Jérome Eertmans <jeertmans@icloud.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* there was one examples saved as a gif?

* chore(deps): re-order av

* chore(lib): simplify `write_frame` method

Reduces the overall code complexity

* chore(lib): add audio

* fix(lib): same issue for conversion

* fix(lib): webm export

* fix(lib): transparent export

Though the output video is weird

* try(lib): fix gif + TODOs

* chore(deps): lower dep crit

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* feat(lib): add support for GIF

* fix(ci): rewrite tests

* fix

* chore(ci): prevent calling concat on empty list

* add missing dot

* fix(ci): update frame comparison ?

* fix(log): add handler to libav logger

* chore: add TODO

* fix(lib): concat issue

* Revert "fix(ci): update frame comparison ?"

This reverts commit 904cfb46ae.

* fix(ci): make it pass tests

* chore(lib/docs/ci): remove FFMPEG entirely

This removes any reference to FFMPEG, except in translation files

* added av as a dependency

* make partial movie files use av instead of piping to external ffmpeg

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* opengl rendering: use av for movie files

* no need to check for ffmpeg executable

* refactor: *_movie_pipe -> *_partial_movie_stream

* improve (oneline) documentation

* pass more options to partial movie file rendering

* move ffmpeg verbosity settings to config; renamed option dict

* replaced call to ffmpeg in combine_files by using av

Co-authored-by: Jérome Eertmans <jeertmans@icloud.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* there was one examples saved as a gif?

* chore(deps): re-order av

* chore(lib): simplify `write_frame` method

Reduces the overall code complexity

* chore(lib): add audio

* fix(lib): same issue for conversion

* fix(lib): webm export

* fix(lib): transparent export

Though the output video is weird

* try(lib): fix gif + TODOs

* chore(deps): lower dep crit

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* feat(lib): add support for GIF

* fix(ci): rewrite tests

* fix

* chore(ci): prevent calling concat on empty list

* add missing dot

* fix(ci): update frame comparison ?

* fix(log): add handler to libav logger

* chore: add TODO

* fix(lib): concat issue

* Revert "fix(ci): update frame comparison ?"

This reverts commit 904cfb46ae.

* fix(ci): make it pass tests

* chore(lib/docs/ci): remove FFMPEG entirely

This removes any reference to FFMPEG, except in translation files

* chore(deps): update lockfile

* chore(lib): rewrite ffprobe

* fix typo

* slightly more aggressive removal of ffmpeg in docs; minor language changes

* fix gif output stream dimensions

* minor style change

* fix encoding of (transparent) mov files

* fixed metadata / comment

* set frame rate for --format=gif in output_stream

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* more video tests for different render settings, also test pix_fmt

* improve default bitrate setting via crf

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* parametrized format/transparency rendering test

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* context managers for (some) av.open

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update manim/utils/commands.py

Co-authored-by: Jérome Eertmans <jeertmans@icloud.com>

* fixed segfault

* update test data involving implicit functions (output improved!)

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* explicity set pix_fmt for transparent webms

* special-special case extracting frame from vp9-encoded file with transparency

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix transparent gifs, more special casing in parametrized video format test

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* run tests on macos-latest again

* removed old control data

* Revert "run tests on macos-latest again"

This reverts commit f50efa4b88.

* added sound to codec test; fixed issue with sound track in gif (disabled) and webm (now via opus)

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* manual wav -> ogg transcoding

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fixed f-string

* refactored codec test, split out gif

* check for non-zero audio samples

* more cleanup

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* remove ffmpeg from readthedocs apt_packages

* round up run_time if positive and shorter than current frame rate

* added more run_time tests

* black

* improve implementation of test

* removed some unused imports

* improve wording of logged warning

Co-authored-by: Jérome Eertmans <jeertmans@icloud.com>

* move run_time checks from Animation.begin to Scene.get_run_time

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* remove unused import

* flake: PT012

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Jérome Eertmans <jeertmans@icloud.com>

* Use --py39-plus in pre-commit (#3761)

* Use --py39-plus in pre-commit

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix indication.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Optimized `manim.utils.bezier.is_closed()` (#3768)

* Optimized manim.utils.bezier.is_closed()

* oops, that shouldn't have been there

* Slightly optimized is_closed() even more

* Added doctest for is_closed()

* Created and optimized Bézier splitting functions such as `partial_bezier_points()` in `manim.utils.bezier` (#3766)

* Optimized manim.utils.partial_bezier_points()

* Added split_bezier, subdivide_bezier and bezier_remap, and tests

* Use bezier_remap() in VMobject and OpenGLVMobject()

* Note that partial_bezier_points is similar to calling split_bezier twice

* Bump requests to 2.32.0 (#3776)

updated-dependencies:
- dependency-name: requests
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Fix assertions and improve error messages when adding submobjects (#3756)

* Optimized AnimationGroup computation of start-end times with lag ratio

* Added extra comment for init_run_time

* Added full path to imports in composition.py

* Optimized AnimationGroup.interpolate

* Fixed final bugs

* Removed accidental print

* Final fix to AnimationGroup.interpolate

* Fixed animations being skipped unintentionally

* Fix and improve Mobject assertions when adding submobjects

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update examples in Mobject.add() and OpenGLMobject.add() docstrings

* overriden -> overridden

* Joined string in OpenGLMobject error message

* Address requested changes

* OpenGLVMObjects -> OpenGLVMobjects

* Use tuplify in VGroup.__setitem__()

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Add pyproject for ruff formatting (#3777)

* Add pyproject for ruff

* add black config back

* Make only formatting

* rearrange isort to undo diff

* poetry lock

* Feedback

* style

Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>

---------

Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>

* pre-commit change to ruff (#3779)

* pre-commit change to ruff

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fixes

* astral-sh ruff bump

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Ignore Ruff format in git blame (#3781)

* Fixed `there_and_back_with_pause()` rate function behaviour with different `pause_ratio` values (#3778)

Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>

* Optimize VMobject methods which append to points (#3765)

* Add `@` shorthand for `CoordinateSystem` methods `coords_to_point` (`c2p`) and `point_to_coords` (`p2c`) (#3754)

* Add shorthand for axes

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add spacing

Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>

* Convert CoordinateSystem example, and add to NumberLine

* Add doctest for NumberLine

* Add test

* Fix typehint for c2p

Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>
Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>

* [pre-commit.ci] pre-commit autoupdate (#3784)

* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.4 → v0.4.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.4...v0.4.5)
- [github.com/codespell-project/codespell: v2.2.6 → v2.3.0](https://github.com/codespell-project/codespell/compare/v2.2.6...v2.3.0)

* Fix typo

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: JasonGrace2282 <aarush.deshpande@gmail.com>

* [pre-commit.ci] pre-commit autoupdate (#3794)

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.5 → v0.4.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.5...v0.4.7)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Add Ruff Lint (#3780)

Adds Ruff Linting to CI, and replaces isort in the pre-commit config with Ruff's isort rules.

Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>

* Replace Pyupgrade with Ruff rule (#3795)

* Add config for pyupgrade

* Fix pyupgrade errors

* Unsafe-fixes

* Nicer way of formatting

Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Revert "Nicer way of formatting"

This reverts commit 48013f4a30.

---------

Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Bump tornado from 6.4 to 6.4.1 (#3796)

Bumps [tornado](https://github.com/tornadoweb/tornado) from 6.4 to 6.4.1.
- [Changelog](https://github.com/tornadoweb/tornado/blob/master/docs/releases.rst)
- [Commits](https://github.com/tornadoweb/tornado/compare/v6.4.0...v6.4.1)

---
updated-dependencies:
- dependency-name: tornado
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Update opengl_vectorized_mobject.py (#3790)

The "insert_n_curves_to_point_list" function requires the "points" argument to be a numpy array, since it calls the "get_bezier_tuples_from_points" function, which requires "points" to be a numpy array because it has the "return points.reshape((-1, nppc, 3))" statement. Ordinary lists do not have a "reshape" method.

So we need to convert "sp1" and "sp2" to numpy arrays before calling the "insert_n_curves_to_point_list" function.

Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>

* [pre-commit.ci] pre-commit autoupdate (#3801)

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.7 → v0.4.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.7...v0.4.8)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Add typings to `OpenGLMobject` (#3803)

* Add typings to OpenGLMobject

* Import typing_extensions

* Add explicit returns to inner functions in .arrange_in_grid()

* Add quotes to parameters in ValueError

* Add some more typings

* Address requested changes

* Type apply_over_attr_arrays with TypeVar

* Fix use of TypeVar

* Add Vector3D typing in set_x, set_y and set_z

* fix: importing manim should not trigger pygments.styles.get_all_styles (#3797)

* fix: importing manim should not trigger pygments.styles.get_all_styles

Removed the Code.styles_list attribute.

Rewrote the documentation to say that a list of all styles can be generated by calling list(pygments.styles.get_all_styles()).

The example in the docstring of Code was rewritten to use an explicit code style name.

* fix: small change to documentation

* Added potential class method to get available code styles.

* Adding typehints to newly-added attributes.

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* Removing unnecessary lines.

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

---------

Co-authored-by: adeshpande <110117391+JasonGrace2282@users.noreply.github.com>

* [pre-commit.ci] pre-commit autoupdate (#3809)

updates:
- [github.com/astral-sh/ruff-pre-commit: v0.4.8 → v0.4.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.8...v0.4.9)
- [github.com/PyCQA/flake8: 7.0.0 → 7.1.0](https://github.com/PyCQA/flake8/compare/7.0.0...7.1.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* Bump urllib3 from 2.2.1 to 2.2.2 (#3810)

Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.2.1 to 2.2.2.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.2.1...2.2.2)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Update macos packages (#3812)

* Fixed infinite loop in OpenGL `BackgroundRectangle.get_color()` (#3732)

Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>

* Some fixes for interpolation

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Naveen M K <naveen521kk@gmail.com>
Co-authored-by: Holly and Prosper <45610532+rongpenl@users.noreply.github.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Naveen M K <naveen521kk@gmail.com>
Co-authored-by: Anousheh Moonen <141789878+u7481690@users.noreply.github.com>
Co-authored-by: Narahari Rao <narahari387@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Immanuel-Alvaro-Bhirawa <127812163+Immanuel-Alvaro-Bhirawa@users.noreply.github.com>
Co-authored-by: Nikhil Iyer <iyer.h.nikhil@gmail.com>
Co-authored-by: Harald Schilly <harald.schilly@gmail.com>
Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>
Co-authored-by: Alex Lembcke <alex.lembcke@gmail.com>
Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com>
Co-authored-by: Václav Blažej <6208643+vaclavblazej@users.noreply.github.com>
Co-authored-by: Václav Blažej <vaclav.blazej@warwick.ac.uk>
Co-authored-by: Jérome Eertmans <jeertmans@icloud.com>
Co-authored-by: Uwe Zimmermann <uwe.zimmermann@sciencetronics.com>
Co-authored-by: Lawrence Qupty <80665382+Lawqup@users.noreply.github.com>
Co-authored-by: JosephD <46393716+jcep@users.noreply.github.com>
Co-authored-by: Francisco Manríquez Novoa <49853152+chopan050@users.noreply.github.com>
Co-authored-by: szchixy <szchixy@outlook.com>
Co-authored-by: Melody Griesen <pikablue107@gmail.com>
Co-authored-by: Melody Griesen <jvgriese@ncsu.edu>
Co-authored-by: yuan <yuan_xin_yu@hotmail.com>
Co-authored-by: Benjamín Ubilla <118409119+MathItYT@users.noreply.github.com>
Co-authored-by: Doaa Muhammad <126016494+doaamuham@users.noreply.github.com>
Co-authored-by: Robin Dimasin <robindimasin@gmail.com>
Co-authored-by: Paul Uhlenbruck <48606747+pauluhlenbruck@users.noreply.github.com>
Co-authored-by: Yash Mundada <F20210001@pilani.bits-pilani.ac.in>
Co-authored-by: TheMathematicFanatic <63360493+TheMathematicFanatic@users.noreply.github.com>
Co-authored-by: Václav Volhejn <8401624+vvolhejn@users.noreply.github.com>
Co-authored-by: Hydromel Victor Doledji <victorvaddely@gmail.com>
Co-authored-by: Dan Davison <dandavison7@gmail.com>
Co-authored-by: Greg Rupp <gmrupp@gmail.com>
Co-authored-by: NotWearingPants <26556598+NotWearingPants@users.noreply.github.com>
Co-authored-by: Sparsh Goenka <43041139+sparshg@users.noreply.github.com>
Co-authored-by: Said Taghadouini <84044788+staghado@users.noreply.github.com>
Co-authored-by: Jason Villanueva <a@jsonvillanueva.com>
Co-authored-by: Abulafia <44573666+abul4fia@users.noreply.github.com>
Co-authored-by: Christian Clauss <cclauss@me.com>
Co-authored-by: Chin Zhe Ning <108804868+biinnnggggg@users.noreply.github.com>
Co-authored-by: HairlessVillager <64526732+HairlessVillager@users.noreply.github.com>
Co-authored-by: Pavel Zwerschke <pavelzw@gmail.com>
Co-authored-by: Sir James Clark Maxwell <71722499+SirJamesClarkMaxwell@users.noreply.github.com>
Co-authored-by: Daniel Zhu <danielfangzhu@gmail.com>
Co-authored-by: Stefano Ottolenghi <stejey@gmail.com>
Co-authored-by: MontroyJosh <122334909+MontroyJosh@users.noreply.github.com>
Co-authored-by: Greg Rupp <greg.rupp@66degrees.com>
Co-authored-by: Amirreza A <45117218+amrear@users.noreply.github.com>
Co-authored-by: Jinchu Li <63861808+JinchuLi2002@users.noreply.github.com>
Co-authored-by: VPC <111203113+VinhPhmCng@users.noreply.github.com>
Co-authored-by: anagorko <3418166+anagorko@users.noreply.github.com>
Co-authored-by: jkjkil4 <52841865+jkjkil4@users.noreply.github.com>
Co-authored-by: yang-tsao <caoyang2005@outlook.com>
Co-authored-by: Eddie Ruiz <eduardo.j.ruiz@gmail.com>
Co-authored-by: Cameron Burdgick <156892808+camburd2@users.noreply.github.com>
2024-06-25 14:51:37 -04:00
Tristan Schulz
e550c5586c Merge branch 'experimental' of github.com:ManimCommunity/manim into experimental 2024-06-21 19:05:15 +02:00
Tristan Schulz
557784ad2f Merge branch 'main' of github.com:MrDiver/manim into experimental 2024-06-21 19:03:13 +02:00
adeshpande
69ddb0e8d5
Rewrite Animation Loop (#3785)
* Initial restructuring

* Revamp exceptions

* Got render loop working

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Implement self.wait

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add note

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>
2024-06-21 19:00:39 +02:00
adeshpande
22844a23ce
Add rendering loop to experimental (#3707)
* try adding a render manager

* Add a test scene

* Got keys working

* yay its a triangle

* Cursed!

* Allow support for self.add

* Get it working

* change test scene

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Separate Animation and Scene using SceneBuffer

* Update subclasses

This is likely to be a very bug-prone commit

* Fix bugs with not clearing buffer

* remove useless clear in scene.py'

* fixes

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add Scene.replace functionality

* Make :class:`.Animation` explicitly implement `AnimationProtocol`

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix a bug with animation restructuring

* fix succession

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix typo in render_manager

* Added window independent resolution rendering

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Whops ?

* Who needs window names anyway

* fixed transform animation

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Remove printing of animation buffer

* Lint, remove unused imports

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Removed useless reading from the gpu

* Fixed broken interp function in opengl_vectorized mobject which used non-existing opacity

* Fixed TracedPath

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Whops

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: = <=>
Co-authored-by: Tristan Schulz <mrdiverlp@gmail.com>
2024-05-02 19:12:09 +02:00
Benjamin Hackl
c6dfc1583b
Disable rendering doc examples for experimental branch (#3475)
* add MANIM_SKIP_EXAMPLES env variable, disable rendering for this branch

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* try using predefined env variable

* what

* env vars don't work

* skip based on PR numbers

* Apply suggestions from code review

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-12-16 14:25:18 +01:00
Francisco Manríquez Novoa
9b98fe5af2
[EXPERIMENTAL] Mobject shader cleanup + Scene changes + Undo orientation input in vertex shader + Transparency Fix (#3474)
* Updated Scene.remove

* Updated Scene.replace

* When shader file is missing, log its absolute path

* Undo adding 'orientation' in vert.glsl, revert to 'unit_normal' in OpenGLVMobject

* Added removed docstring to Scene.replace

* Removed and rewrote multiple OpenGL(V)Mobject methods

* Fixed OpenGLVMobject.get_style()

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fixing transparency and generator bug

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: MrDiver <mrdiverlp@gmail.com>
2023-11-27 01:10:52 +01:00
Jason G. Villanueva
eb1ff92ccd
Add some comments to the code, run black for formatting 2023-10-24 21:06:01 -07:00
Jason G. Villanueva
393c2a15d4 Small byte_offset bugfix for vec2 2023-10-24 05:16:03 -07:00
Jason G. Villanueva
985141f5ea Implemented a rudimentary UniformBufferObject compatible with the STD140 memory layout 2023-10-24 04:41:19 -07:00
Jason G. Villanueva
feff6ba8bc Added YishiMichael's suggestion on missed declaration 2023-10-03 13:14:34 -07:00
Jason G. Villanueva
d98ba1cf51 Progress thus far on uniform block/buffer/layouts 2023-10-03 12:58:38 -07:00
MrDiver
4fb492c821 Merge branch 'experimental' of github.com:jsonvillanueva/manim into experimental 2023-09-15 00:43:49 +02:00
MrDiver
e50b82fea7 add comment 2023-09-15 00:43:38 +02:00
pre-commit-ci[bot]
bffbc22794 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2023-09-14 22:39:25 +00:00
MrDiver
7f4b287ac5 some formatting and removing unused variables 2023-09-15 00:38:39 +02:00
MrDiver
7e97df490d experimental transparent stroke shadnig 2023-09-15 00:34:31 +02:00
MrDiver
8537fd6eaa Fixing svg mobject constructor 2023-09-15 00:32:12 +02:00
MrDiver
17f0204934 Initializing Window before gl context 2023-09-15 00:26:42 +02:00
MrDiver
df97462ca4 Fix logo .set deprecated usage 2023-09-15 00:25:41 +02:00
pre-commit-ci[bot]
95f97b7dc6 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2023-09-12 21:44:37 +00:00
Tristan Schulz
d8f261d414 Make depth_testing default 2023-09-12 23:43:14 +02:00
Tristan Schulz
115bcf968f Fixed affecting colors bug returns copy instead 2023-09-12 23:35:03 +02:00
Tristan Schulz
7a7ed422fc Working Blending and Transparency in one direction 2023-09-12 23:18:38 +02:00
Tristan Schulz
46ba43cc97 Almost working blending, stroke broken 2023-09-12 22:36:32 +02:00
Tristan Schulz
ab44633082 Working Shading in 3D many many tries 2023-09-12 18:25:34 +02:00
Tristan Schulz
389c1a1587 Working Shading in 3D many many tries 2023-09-12 18:21:24 +02:00
pre-commit-ci[bot]
603e7128ef [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2023-09-12 13:27:43 +00:00
Tristan Schulz
388604b3f7 Submobject rendering and further abstraction - WIP for stroke early drop 2023-09-12 05:08:11 +02:00
pre-commit-ci[bot]
d9dd887cb1 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2023-09-11 01:44:01 +00:00
MrDiver
f82ec48e95 Merge 2023-09-11 03:42:54 +02:00
MrDiver
f73b35d86c Working render to numpy array 2023-09-11 03:41:32 +02:00
pre-commit-ci[bot]
b7fe84d6c8 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2023-09-11 00:26:09 +00:00
MrDiver
5a7da79631 Working window rendering with events 2023-09-11 02:25:29 +02:00
MrDiver
9a94781cf1 change dtype to f1 float 1 byte 2023-09-11 01:29:00 +02:00
MrDiver
1a821d7013 nothing rendering again
Co-authored-by: Jason Villanueva <a@jsonvillanueva.com>
2023-09-11 01:17:49 +02:00
MrDiver
d418acc865 Basic Rendering Vmobject works!
Co-authored-by: Jason Villanueva <a@jsonvillanueva.com>
2023-09-10 23:45:42 +02:00
MrDiver
7dea0446d0 Fixed frame_width and height usage, fix ibo to be i4
Co-authored-by: Jason Villanueva <a@jsonvillanueva.com>
2023-09-10 22:51:36 +02:00
MrDiver
1e57feccdb Something is rendering now but the relative manim units don't work Co-authored-by: Jason Villanueva <a@jsonvillanueva.com> 2023-09-10 22:12:53 +02:00
MrDiver
5d8ea6a4a3 Merge 3320-fix-antialiasing-opengl into experimental 2023-09-10 14:30:47 +02:00
pre-commit-ci[bot]
76694e528b [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2023-09-10 12:05:56 +00:00
Tristan Schulz
9904627b19 wip: copy data functions to opengl_renderer, preparing uniforms and shaders 2023-09-10 14:05:00 +02:00
pre-commit-ci[bot]
aa18dc44d1 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2023-09-04 02:41:14 +00:00
Jason G. Villanueva
c2d1a406f1 small color fix openglvmobject 2023-09-03 19:32:20 -07:00
Jason G. Villanueva
520577dde3 Merge branch 'main' of github.com:ManimCommunity/manim into experimental 2023-09-03 18:13:24 -07:00
Jason G. Villanueva
1d4f0d9665 changed Camera back to Renderer
Co-authored-by: Tristan Schulz <tristan.schulz@stud.tu-darmstadt.de>
2023-09-03 17:24:12 -07:00
Jason G. Villanueva
56e9914916 Initial Renderer abstraction 2023-09-03 13:48:42 -07:00
pre-commit-ci[bot]
4231751e27 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2023-01-05 14:21:23 +00:00
Benjamin Hackl
30dd07eee7 fix decorator issues in OpenGL(V)Mobject 2023-01-05 15:19:04 +01:00
Benjamin Hackl
db7274e325 fix a bunch of imports 2023-01-05 15:18:23 +01:00
Benjamin Hackl
5ae747e879 opengl_renderer.py -> camera.py 2023-01-05 15:17:15 +01:00
Benjamin Hackl
b411dbd298 remove scenes depending on cairo camera subclasses 2023-01-05 15:15:55 +01:00
Benjamin Hackl
9c40838fcb camera.py -> cairo_camera.py; delete other cairo camera subclasses 2023-01-05 14:56:27 +01:00
pre-commit-ci[bot]
f90c9a2e95 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2023-01-04 18:53:19 +00:00
MrDiver
068919ef12 Merge branch 'experimental' of github.com:ManimCommunity/manim into experimental 2023-01-04 19:51:54 +01:00
MrDiver
d169cd556f addded relative import support for shaders and first working square (fill is broken) 2023-01-04 19:50:38 +01:00
pre-commit-ci[bot]
eda8bb0e06 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2023-01-04 02:51:41 +00:00
MrDiver
6151860418 tried to fix shaders added warning in camera if uniform does not exist 2023-01-04 03:50:45 +01:00
pre-commit-ci[bot]
67e1ca98c8 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2023-01-03 18:11:15 +00:00
Benjamin Hackl
3888d20c6c rendering a black screen works :-) 2023-01-03 19:08:54 +01:00
Benjamin Hackl
5569c9f3d2 Merge branch 'experimental' of github.com:ManimCommunity/manim into experimental 2023-01-03 17:26:15 +01:00
Benjamin Hackl
178778dce8 fix imports, again 2023-01-03 17:26:06 +01:00
Benjamin Hackl
ed9e797b65 removed renderer argument from scene class call 2023-01-03 17:09:30 +01:00
Benjamin Hackl
e77e6c8766 removed a bunch of config.renderer comparisons 2023-01-03 17:07:29 +01:00
pre-commit-ci[bot]
be45dcafa4 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2023-01-03 14:15:45 +00:00
Benjamin Hackl
5ec1f255dd Merge branch 'experimental' of github.com:ManimCommunity/manim into experimental 2023-01-03 15:14:39 +01:00
Benjamin Hackl
9f55388fea fix imports: CLI works again (rendering doesn't yet) 2023-01-03 15:14:30 +01:00
pre-commit-ci[bot]
120db6fcea [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2023-01-03 12:29:36 +00:00
Benjamin Hackl
334f5d7877 added ipython as dependency 2023-01-03 13:27:49 +01:00
Benjamin Hackl
59ab3925b7 removed checkpoint feature (for now) 2023-01-03 13:27:35 +01:00
Benjamin Hackl
31cebcced1 replace manimlib import 2023-01-03 13:21:19 +01:00
Tristan Schulz
8b345d9a07
Porting and drastical rewrite to reach compatibility with 3b1b/manim again (#3107)
* ported functionality of Mobject from 3b1b to OpenGLMobject

* ported functionality of VMobject from 3b1b to OpenVGLMobject

* first working render

* first step to dump old scene structure

* copied scene without adapting
2023-01-03 12:46:56 +01:00
259 changed files with 8731 additions and 15174 deletions

1
.clang-format Normal file
View file

@ -0,0 +1 @@
BasedOnStyle: Microsoft

View file

@ -34,7 +34,7 @@ Manim is an animation engine for explanatory math videos. It's used to create pr
## Installation
> [!CAUTION]
> [!WARNING]
> These instructions are for the community version _only_. Trying to use these instructions to install [3b1b/manim](https://github.com/3b1b/manim) or instructions there to install this version will cause problems. Read [this](https://docs.manim.community/en/stable/faq/installation.html#why-are-there-different-versions-of-manim) and decide which version you wish to install, then only follow the instructions for your desired version.
Manim requires a few dependencies that must be installed prior to using it. If you
@ -71,7 +71,7 @@ In order to view the output of this scene, save the code in a file called `examp
manim -p -ql example.py SquareToCircle
```
You should see your native video player program pop up and play a simple scene in which a square is transformed into a circle. You may find some more simple examples within this
You should see a window pop up and play a simple scene in which a square is transformed into a circle. You may find some more simple examples within this
[GitHub repository](example_scenes). You can also visit the [official gallery](https://docs.manim.community/en/stable/examples.html) for more advanced examples.
Manim also ships with a `%%manim` IPython magic which allows to use it conveniently in JupyterLab (as well as classic Jupyter) notebooks. See the
@ -84,7 +84,8 @@ The general usage of Manim is as follows:
![manim-illustration](https://raw.githubusercontent.com/ManimCommunity/manim/main/docs/source/_static/command.png)
The `-p` flag in the command above is for previewing, meaning the video file will automatically open when it is done rendering. The `-ql` flag is for a faster rendering at a lower quality.
The `-p` flag in the command above is for previewing, meaning a window will show up to render it in real time.
The `-ql` flag is for a faster rendering at a lower quality.
Some other useful flags include:

View file

@ -19,7 +19,7 @@ msgid "Bases: :py:class:`manim.mobject.three_d.three_dimensions.Cylinder`"
msgstr ""
#: ../../../manim/mobject/three_d/three_dimensions.py:docstring of manim.mobject.three_d.three_dimensions.Line3D:1
msgid "A cylindrical line, for use in ThreeDScene."
msgid "A cylindrical line."
msgstr ""
#: ../../../manim/mobject/three_d/three_dimensions.py:docstring of manim.mobject.three_d.three_dimensions.Line3D:4

View file

@ -35,7 +35,7 @@ msgid "A spherical dot."
msgstr ""
#: ../../source/reference/manim.mobject.three_d.three_dimensions.rst:40:<autosummary>:1
msgid "A cylindrical line, for use in ThreeDScene."
msgid "A cylindrical line."
msgstr ""
#: ../../source/reference/manim.mobject.three_d.three_dimensions.rst:40:<autosummary>:1

View file

@ -1,97 +0,0 @@
msgid ""
msgstr ""
"Project-Id-Version: Manim \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: ../../source/reference/manim.scene.three_d_scene.SpecialThreeDScene.rst:2
msgid "SpecialThreeDScene"
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.SpecialThreeDScene.rst:4
msgid "Qualified name: ``manim.scene.three\\_d\\_scene.SpecialThreeDScene``"
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.SpecialThreeDScene:1
msgid "Bases: :py:class:`manim.scene.three_d_scene.ThreeDScene`"
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.SpecialThreeDScene:1
msgid "An extension of :class:`ThreeDScene` with more settings."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.SpecialThreeDScene:3
msgid "It has some extra configuration for axes, spheres, and an override for low quality rendering. Further key differences are:"
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.SpecialThreeDScene:7
msgid "The camera shades applicable 3DMobjects by default, except if rendering in low quality."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.SpecialThreeDScene:9
msgid "Some default params for Spheres and Axes have been added."
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.SpecialThreeDScene.rst:14
msgid "Methods"
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.SpecialThreeDScene.rst:23:<autosummary>:1
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.SpecialThreeDScene.get_axes:1
msgid "Return a set of 3D axes."
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.SpecialThreeDScene.rst:23:<autosummary>:1
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.SpecialThreeDScene.get_default_camera_position:1
msgid "Returns the default_angled_camera position."
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.SpecialThreeDScene.rst:23:<autosummary>:1
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.SpecialThreeDScene.get_sphere:1
msgid "Returns a sphere with the passed keyword arguments as properties."
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.SpecialThreeDScene.rst:23:<autosummary>:1
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.SpecialThreeDScene.set_camera_to_default_position:1
msgid "Sets the camera to its default position."
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.SpecialThreeDScene.rst:25
msgid "Attributes"
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.SpecialThreeDScene.get_axes:0
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.SpecialThreeDScene.get_default_camera_position:0
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.SpecialThreeDScene.get_sphere:0
msgid "Returns"
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.SpecialThreeDScene.get_axes:3
msgid "A set of 3D axes."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.SpecialThreeDScene.get_axes:0
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.SpecialThreeDScene.get_default_camera_position:0
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.SpecialThreeDScene.get_sphere:0
msgid "Return type"
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.SpecialThreeDScene.get_default_camera_position:3
msgid "Dictionary of phi, theta, focal_distance, and gamma."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.SpecialThreeDScene.get_sphere:0
msgid "Parameters"
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.SpecialThreeDScene.get_sphere:3
msgid "Any valid parameter of :class:`~.Sphere` or :class:`~.Surface`."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.SpecialThreeDScene.get_sphere:5
msgid "The sphere object."
msgstr ""

View file

@ -1,210 +0,0 @@
msgid ""
msgstr ""
"Project-Id-Version: Manim \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: ../../source/reference/manim.scene.three_d_scene.ThreeDScene.rst:2
msgid "ThreeDScene"
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.ThreeDScene.rst:4
msgid "Qualified name: ``manim.scene.three\\_d\\_scene.ThreeDScene``"
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene:1
msgid "Bases: :py:class:`manim.scene.scene.Scene`"
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene:1
msgid "This is a Scene, with special configurations and properties that make it suitable for Three Dimensional Scenes."
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.ThreeDScene.rst:14
msgid "Methods"
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.ThreeDScene.rst:31:<autosummary>:1
msgid "This method is used to prevent the rotation and movement of mobjects as the camera moves around."
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.ThreeDScene.rst:31:<autosummary>:1
msgid "This method is used to prevent the rotation and tilting of mobjects as the camera moves around."
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.ThreeDScene.rst:31:<autosummary>:1
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.begin_3dillusion_camera_rotation:1
msgid "This method creates a 3D camera rotation illusion around the current camera orientation."
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.ThreeDScene.rst:31:<autosummary>:1
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.begin_ambient_camera_rotation:1
msgid "This method begins an ambient rotation of the camera about the Z_AXIS, in the anticlockwise direction"
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.ThreeDScene.rst:31:<autosummary>:1
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.get_moving_mobjects:1
msgid "This method returns a list of all of the Mobjects in the Scene that are moving, that are also in the animations passed."
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.ThreeDScene.rst:31:<autosummary>:1
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.move_camera:1
msgid "This method animates the movement of the camera to the given spherical coordinates."
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.ThreeDScene.rst:31:<autosummary>:1
msgid "This method undoes what add_fixed_in_frame_mobjects does."
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.ThreeDScene.rst:31:<autosummary>:1
msgid "This method \"unfixes\" the orientation of the mobjects passed, meaning they will no longer be at the same angle relative to the camera."
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.ThreeDScene.rst:31:<autosummary>:1
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.set_camera_orientation:1
msgid "This method sets the orientation of the camera in the scene."
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.ThreeDScene.rst:31:<autosummary>:1
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.set_to_default_angled_camera_orientation:1
msgid "This method sets the default_angled_camera_orientation to the keyword arguments passed, and sets the camera to that orientation."
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.ThreeDScene.rst:31:<autosummary>:1
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.stop_3dillusion_camera_rotation:1
msgid "This method stops all illusion camera rotations."
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.ThreeDScene.rst:31:<autosummary>:1
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.stop_ambient_camera_rotation:1
msgid "This method stops all ambient camera rotation."
msgstr ""
#: ../../source/reference/manim.scene.three_d_scene.ThreeDScene.rst:33
msgid "Attributes"
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.add_fixed_in_frame_mobjects:1
msgid "This method is used to prevent the rotation and movement of mobjects as the camera moves around. The mobject is essentially overlaid, and is not impacted by the camera's movement in any way."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.add_fixed_in_frame_mobjects:0
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.add_fixed_orientation_mobjects:0
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.begin_3dillusion_camera_rotation:0
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.begin_ambient_camera_rotation:0
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.get_moving_mobjects:0
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.move_camera:0
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.remove_fixed_in_frame_mobjects:0
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.remove_fixed_orientation_mobjects:0
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.set_camera_orientation:0
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.set_to_default_angled_camera_orientation:0
msgid "Parameters"
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.add_fixed_in_frame_mobjects:6
msgid "The Mobjects whose orientation must be fixed."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.add_fixed_orientation_mobjects:1
msgid "This method is used to prevent the rotation and tilting of mobjects as the camera moves around. The mobject can still move in the x,y,z directions, but will always be at the angle (relative to the camera) that it was at when it was passed through this method.)"
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.add_fixed_orientation_mobjects:7
msgid "The Mobject(s) whose orientation must be fixed."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.add_fixed_orientation_mobjects:9
msgid "Some valid kwargs are use_static_center_func : bool center_func : function"
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.add_fixed_orientation_mobjects:11
msgid "Some valid kwargs are"
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.add_fixed_orientation_mobjects:11
msgid "use_static_center_func : bool center_func : function"
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.begin_3dillusion_camera_rotation:4
msgid "The rate at which the camera rotation illusion should operate."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.begin_3dillusion_camera_rotation:5
msgid "The polar angle the camera should move around. Defaults to the current phi angle."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.begin_3dillusion_camera_rotation:7
msgid "The azimutal angle the camera should move around. Defaults to the current theta angle."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.begin_ambient_camera_rotation:4
msgid "The rate at which the camera should rotate about the Z_AXIS. Negative rate means clockwise rotation."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.begin_ambient_camera_rotation:7
msgid "one of 3 options: [\"theta\", \"phi\", \"gamma\"]. defaults to theta."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.get_moving_mobjects:4
msgid "The animations whose mobjects will be checked."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.move_camera:4
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.set_camera_orientation:3
msgid "The polar angle i.e the angle between Z_AXIS and Camera through ORIGIN in radians."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.move_camera:6
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.set_camera_orientation:5
msgid "The azimuthal angle i.e the angle that spins the camera around the Z_AXIS."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.move_camera:8
msgid "The radial focal_distance between ORIGIN and Camera."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.move_camera:10
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.set_camera_orientation:9
msgid "The rotation of the camera about the vector from the ORIGIN to the Camera."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.move_camera:12
msgid "The zoom factor of the camera."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.move_camera:14
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.set_camera_orientation:13
msgid "The new center of the camera frame in cartesian coordinates."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.move_camera:16
msgid "Any other animations to be played at the same time."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.remove_fixed_in_frame_mobjects:1
msgid "This method undoes what add_fixed_in_frame_mobjects does. It allows the mobject to be affected by the movement of the camera."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.remove_fixed_in_frame_mobjects:5
msgid "The Mobjects whose position and orientation must be unfixed."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.remove_fixed_orientation_mobjects:1
msgid "This method \"unfixes\" the orientation of the mobjects passed, meaning they will no longer be at the same angle relative to the camera. This only makes sense if the mobject was passed through add_fixed_orientation_mobjects first."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.remove_fixed_orientation_mobjects:6
msgid "The Mobjects whose orientation must be unfixed."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.set_camera_orientation:7
msgid "The focal_distance of the Camera."
msgstr ""
#: ../../../manim/scene/three_d_scene.py:docstring of manim.scene.three_d_scene.ThreeDScene.set_camera_orientation:11
msgid "The zoom factor of the scene."
msgstr ""

View file

@ -7,6 +7,7 @@ This page contains a list of changes made between releases.
.. toctree::
:maxdepth: 1
changelog/experimental
changelog/0.20.1-changelog
changelog/0.20.0-changelog
changelog/0.19.2-changelog

View file

@ -0,0 +1,97 @@
# Migrating from v0.19.0 to v0.20.0
This constitutes a list of all the changes needed to migrate your code
to work with the latest version of Manim
## Manager
If you ever used `Scene.render`, you must replace it with {class}`.Manager`.
Original code:
```py
scene = SceneClass()
scene.render()
```
should be changed to:
```py
with Manager(SceneClass) as manager:
manager.render()
```
If you are a plugin author that subclasses `Scene` and changed `Scene.render`, you should migrate
your code to use the specific public methods on {class}`.Manager` instead.
## ThreeDScene and Camera
`ThreeDScene` has been completely removed, and all of its functionality has been replaced
with methods on {class}`.Camera`, which can be accessed via {attr}`.Scene.camera`.
For example, the following code
```py
class MyScene(ThreeDScene):
def construct(self):
t = Text("Hello")
self.add_fixed_in_frame_mobjects(t)
self.begin_ambient_camera_rotation()
self.wait(3)
```
should be changed to
```py
# change ThreeDScene -> Scene
class MyScene(Scene):
def construct(self):
t = Text("Hello")
# add_fixed_in_frame_mobjects() no longer exists.
# Now you must use Mobject.fix_in_frame() manually for each Mobject.
t.fix_in_frame()
self.add(t)
# access the method on the camera
self.camera.begin_ambient_rotation()
self.add(self.camera)
self.wait(3)
```
## Animation
`Animation.interpolate_mobject` has been combined into `Animation.interpolate`.
Methods `Animation._setup_scene` and `Animation.clean_up_from_scene` have been removed
in favor of `Animation.begin` and `Animation.finish`. If you need to access the scene,
you can use a simple buffer to communicate. Note that this buffer cannot access
methods on the {class}`.Scene`, but can only do basic actions like {meth}`.Scene.add`,
{meth}`.Scene.remove`, and {meth}`.Scene.replace`.
For example, the following code:
```py
class MyAnimation(Animation):
def begin(self) -> None:
self._sqrs = VGroup(Square())
def _setup_scene(self, scene: Scene) -> None:
scene.add(self._sqrs)
self.scene = scene
def interpolate_mobject(self, alpha: float) -> None:
sqr = Square().move_to((alpha, 0, 0))
self._sqrs.add(sqr)
self.scene.add(sqr)
def clean_up_from_scene(self, scene: Scene) -> None:
scene.remove(self._sqrs)
```
should be changed to
```py
class MyAnimation(Animation):
def begin(self) -> None:
self._sqrs = VGroup(Square())
self.buffer.add(self._sqrs)
def interpolate(self, alpha: float) -> None:
sqr = Square().move_to((alpha, 0, 0))
self._sqrs.add(sqr)
self.buffer.add(sqr)
# tell the scene to empty the buffer
self.apply_buffer = True
def finish(self) -> None:
self.buffer.remove(self._sqrs)
```

View file

@ -24,8 +24,8 @@ to the bottom of the file:
.. code-block:: python
with tempconfig({"quality": "medium_quality", "disable_caching": True}):
scene = SceneName()
scene.render()
manager = Manager(SceneName)
manager.render()
Where ``SceneName`` is the name of the scene you want to run. You can then run the
file directly, and can thus follow the instructions for most profilers.
@ -58,8 +58,8 @@ to ``square_to_circle.py``:
with tempconfig({"quality": "medium_quality", "disable_caching": True}):
scene = SquareToCircle()
scene.render()
manager = Manager(SquareToCircle)
manager.render()
Now run the following in the terminal:

View file

@ -213,11 +213,11 @@ The decorator can be used with or without parentheses. **By default, the test on
circle = Circle()
scene.play(Animation(circle))
You can also specify, when needed, which base scene you need (ThreeDScene, for example) :
You can also specify, when needed, which base scene you need (VectorScene, for example) :
.. code:: python
@frames_comparison(last_frame=False, base_scene=ThreeDScene)
@frames_comparison(last_frame=False, base_scene=VectorScene)
def test_circle(scene):
circle = Circle()
scene.play(Animation(circle))

View file

@ -597,25 +597,25 @@ Special Camera Settings
.. manim:: FixedInFrameMObjectTest
:save_last_frame:
:ref_classes: ThreeDScene
:ref_methods: ThreeDScene.set_camera_orientation ThreeDScene.add_fixed_in_frame_mobjects
:ref_classes: Scene
:ref_methods: Camera.set_orientation OpenGLMobject.fix_in_frame
class FixedInFrameMObjectTest(ThreeDScene):
class FixedInFrameMObjectTest(Scene):
def construct(self):
axes = ThreeDAxes()
self.set_camera_orientation(phi=75 * DEGREES, theta=-45 * DEGREES)
self.camera.set_orientation(theta=-45 * DEGREES, phi=75 * DEGREES)
text3d = Text("This is a 3D text")
self.add_fixed_in_frame_mobjects(text3d)
text3d.fix_in_frame()
text3d.to_corner(UL)
self.add(axes)
self.wait()
.. manim:: ThreeDLightSourcePosition
:save_last_frame:
:ref_classes: ThreeDScene ThreeDAxes Surface
:ref_methods: ThreeDScene.set_camera_orientation
:ref_classes: Scene ThreeDAxes Surface
:ref_methods: Camera.set_orientation
class ThreeDLightSourcePosition(ThreeDScene):
class ThreeDLightSourcePosition(Scene):
def construct(self):
axes = ThreeDAxes()
sphere = Surface(
@ -626,49 +626,57 @@ Special Camera Settings
]), v_range=[0, TAU], u_range=[-PI / 2, PI / 2],
checkerboard_colors=[RED_D, RED_E], resolution=(15, 32)
)
self.renderer.camera.light_source.move_to(3*IN) # changes the source of the light
self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)
# TODO: implement light source
self.camera.light_source.move_to(3*IN) # changes the source of the light
self.camera.set_orientation(theta=30 * DEGREES, phi=75 * DEGREES)
self.add(axes, sphere)
.. manim:: ThreeDCameraRotation
:ref_classes: ThreeDScene ThreeDAxes
:ref_methods: ThreeDScene.begin_ambient_camera_rotation ThreeDScene.stop_ambient_camera_rotation
:ref_classes: Circle Scene ThreeDAxes
:ref_methods: Camera.begin_ambient_rotation Camera.stop_ambient_rotation
class ThreeDCameraRotation(ThreeDScene):
class ThreeDCameraRotation(Scene):
def construct(self):
axes = ThreeDAxes()
circle = Circle()
self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)
self.camera.set_orientation(theta=30 * DEGREES, phi=75 * DEGREES)
self.add(circle,axes)
self.begin_ambient_camera_rotation(rate=0.1)
self.camera.begin_ambient_rotation(rate=0.1)
self.add(self.camera)
self.wait()
self.stop_ambient_camera_rotation()
self.move_camera(phi=75 * DEGREES, theta=30 * DEGREES)
self.camera.stop_ambient_rotation()
self.play(
self.camera.animate.set_orientation(
theta=30 * DEGREES, phi=75 * DEGREES
),
)
self.wait()
.. manim:: ThreeDCameraIllusionRotation
:ref_classes: ThreeDScene ThreeDAxes
:ref_methods: ThreeDScene.begin_3dillusion_camera_rotation ThreeDScene.stop_3dillusion_camera_rotation
.. manim:: ThreeDCameraPrecession
:ref_classes: Circle Scene ThreeDAxes
:ref_methods: Camera.begin_precession Camera.stop_precession
class ThreeDCameraIllusionRotation(ThreeDScene):
class ThreeDCameraPrecession(Scene):
def construct(self):
axes = ThreeDAxes()
circle = Circle()
self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)
self.camera.set_orientation(theta=30 * DEGREES, phi=75 * DEGREES)
self.add(circle,axes)
self.begin_3dillusion_camera_rotation(rate=2)
self.camera.begin_precession(rate=2)
self.add(self.camera)
self.wait(PI/2)
self.stop_3dillusion_camera_rotation()
self.camera.stop_precession()
.. manim:: ThreeDSurfacePlot
:save_last_frame:
:ref_classes: ThreeDScene Surface
:ref_classes: Scene Surface
:ref_methods: Camera.set_orientation
class ThreeDSurfacePlot(ThreeDScene):
class ThreeDSurfacePlot(Scene):
def construct(self):
resolution_fa = 24
self.set_camera_orientation(phi=75 * DEGREES, theta=-30 * DEGREES)
self.camera.set_orientation(theta=-30 * DEGREES, phi=75 * DEGREES)
def param_gauss(u, v):
x = u

View file

@ -357,10 +357,10 @@ A list of all config options
'log_dir', 'log_to_file', 'max_files_cached', 'media_dir', 'media_width',
'movie_file_extension', 'notify_outdated_version', 'output_file', 'partial_movie_dir',
'pixel_height', 'pixel_width', 'plugins', 'preview',
'progress_bar', 'quality', 'right_side', 'save_as_gif', 'save_last_frame',
'save_pngs', 'scene_names', 'show_in_file_browser', 'sound', 'tex_dir',
'progress_bar', 'quality', 'right_side', 'save_last_frame',
'scene_names', 'show_in_file_browser', 'sound', 'tex_dir',
'tex_template', 'tex_template_file', 'text_dir', 'top', 'transparent',
'upto_animation_number', 'use_opengl_renderer', 'verbosity', 'video_dir',
'upto_animation_number', 'verbosity', 'video_dir',
'window_position', 'window_monitor', 'window_size', 'write_all', 'write_to_movie',
'enable_wireframe', 'force_window']

View file

@ -1,11 +1,11 @@
A deep dive into Manim's internals
==================================
**Author:** `Benjamin Hackl <https://benjamin-hackl.at>`__
**Authors:** `Benjamin Hackl <https://benjamin-hackl.at>`__ and `Aarush Deshpande <https://github.com/JasonGrace2282>`__
.. admonition:: Disclaimer
This guide reflects the state of the library as of version ``v0.16.0``
This guide reflects the state of the library as of version ``v0.20.0``
and primarily treats the Cairo renderer. The situation in the latest
version of Manim might be different; in case of substantial deviations
we will add a note below.
@ -84,7 +84,7 @@ discussing the contents of the following chapters on a very high level.
to prepare a scene for rendering; right until the point where the user-overridden
``construct`` method is ran. This includes a brief discussion on using Manim's CLI
versus other means of rendering (e.g., via Jupyter notebooks, or in your Python
script by calling the :meth:`.Scene.render` method yourself).
script by calling the :meth:`.Manager.render` method yourself).
- `Mobject Initialization`_: For the second chapter we dive into creating and handling
Mobjects, the basic elements that should be displayed in our scene.
We discuss the :class:`.Mobject` base class, how there are essentially
@ -107,6 +107,25 @@ discussing the contents of the following chapters on a very high level.
:meth:`.Scene.construct` has been run, the library combines the partial movie
files to one video.
.. hint::
As we move forward, try to keep in mind the responsibilities of every
class we introduce. We'll talk more about them in detail, but here's a brief
overview
* :class:`.Scene` is responsible for managing the classes :class:`Mobject`, :class:`.Animation`,
and :class:`.Camera`.
* :class:`.Manager` is responsible for coordinating the :class:`.Scene`, :class:`.Renderer`,
and :class:`.FileWriter`.
* :class:`.FileWriter` is responsible for writing frames and partial movie files, as well
as combining them all into a final movie file.
* :class:`.Renderer` is an abstract class which has to be subclassed.
It's job is to take information related to the :class:`.Camera`, and the mobjects
on the :class:`.Scene` at a certain frame, and to return the pixels in a frame.
And with that, let us get *in medias res*.
Preliminaries
@ -123,8 +142,8 @@ like
::
with tempconfig({"quality": "medium_quality", "preview": True}):
scene = ToyExample()
scene.render()
manager = Manager(ToyExample)
manager.render()
or whether you are rendering the code in a Jupyter notebook, you are still telling your
python interpreter to import the library. The usual pattern used to do this is
@ -202,8 +221,8 @@ have created a file ``toy_example.py`` which looks like this::
self.play(FadeOut(blue_circle, small_dot))
with tempconfig({"quality": "medium_quality", "preview": True}):
scene = ToyExample()
scene.render()
manager = Manager(ToyExample)
manager.render()
With such a file, the desired scene is rendered by simply running this Python
script via ``python toy_example.py``. Then, as described above, the library
@ -218,10 +237,10 @@ dictionary, and upon leaving the context the original version of the
configuration is restored. TL;DR: it provides a fancy way of temporarily setting
configuration options.
Inside the context manager, two things happen: an actual ``ToyExample``-scene
object is instantiated, and the ``render`` method is called. Every way of using
Inside the context manager, two things happen: a :class:`.Manager` is created for
the ``ToyExample``-scene, and the ``render`` method is called. Every way of using
Manim ultimately does something along of these lines, the library always instantiates
the scene object and then calls its ``render`` method. To illustrate that this
the manager of the scene object and then calls its ``render`` method. To illustrate that this
really is the case, let us briefly look at the two most common ways of rendering
scenes:
@ -243,54 +262,75 @@ and the code creating the scene class and calling its render method is located
`here <https://github.com/ManimCommunity/manim/blob/ac1ee9a683ce8b92233407351c681f7d71a4f2db/manim/utils/ipython_magic.py#L137-L138>`__.
Now that we know that either way, a :class:`.Scene` object is created, let us investigate
what Manim does when that happens. When instantiating our scene object
Now that we know that either way, a :class:`.Manager` for a :class:`.Scene` object is created, let us investigate
what Manim does when that happens. When instantiating our manager
::
scene = ToyExample()
manager = Manager(ToyExample)
the ``Scene.__init__`` method is called, given that we did not implement our own initialization
method. Inspecting the corresponding code (see
`here <https://github.com/ManimCommunity/manim/blob/main/manim/scene/scene.py>`__)
reveals that ``Scene.__init__`` first sets several attributes of the scene objects that do not
depend on any configuration options set in ``config``. Then the scene inspects the value of
``config.renderer``, and based on its value, either instantiates a ``CairoRenderer`` or an
``OpenGLRenderer`` object and assigns it to its ``renderer`` attribute.
The :meth:`.Manager.__init__` method is called. Looking at the source code (`here <https://github.com/ManimCommunity/manim/blob/experimental/manim/manager.py>`__),
we see that the :meth:`.Scene.__init__` method is called,
given that we did not implement our own initialization
method. Inspecting the corresponding code (see `here <https://github.com/ManimCommunity/manim/blob/main/manim/scene/scene.py>`__)
reveals that :class:`Scene.__init__` first sets several attributes of the scene objects that do not
depend on any configuration options set in ``config``. It then initializes it's :class:`.Camera`.
The purpose of a :class:`.Camera` is to keep track of what you can see in the scene. Think of it
as a pair of eyes, that limit how far you can look sideways and vertically.
The scene then asks its renderer to initialize the scene by calling
The :class:`.Scene` also sets up :attr:`.Scene.mobjects`. This attribute keeps track of all the :class:`.Mobject`
that have been added to the scene.
::
The :class:`.Manager` then continues on to create a :class:`.Window`, which is the popopen interactive window,
and creates the renderer::
self.renderer.init_scene(self)
self.renderer = self.create_renderer()
self.renderer.use_window()
Inspecting both the default Cairo renderer and the OpenGL renderer shows that the ``init_scene``
method effectively makes the renderer instantiate a :class:`.SceneFileWriter` object, which
basically is Manim's interface to ``libav`` (FFMPEG) and actually writes the movie file. The Cairo
renderer (see the implementation `here <https://github.com/ManimCommunity/manim/blob/main/manim/renderer/cairo_renderer.py>`__) does not require any further initialization. The OpenGL renderer
does some additional setup to enable the realtime rendering preview window, which we do not go
into detail further here.
If you hover over :attr:`.Manager.renderer`, you might see that the type is a :class:`.RendererProtocol`.
A :class:`~typing.Protocol` is a contract for a class. It says that whatever the class is, it will implement
the methods defined inside the protocol. In this case, it means that the renderer will have all the methods
defined in :class:`.RendererProtocol`.
.. warning::
.. note::
Currently, there is a lot of interplay between a scene and its renderer. This is a flaw
in Manim's current architecture, and we are working on reducing this interdependency to
achieve a less convoluted code flow.
The point of using :class:`~typing.Protocol` is so that in the future, plugins
can swap out the renderer with their own version - either for speed, or for a different
behavior.
After the renderer has been instantiated and initialized its file writer, the scene populates
further initial attributes (notable mention: the ``mobjects`` attribute which keeps track
of the mobjects that have been added to the scene). It is then done with its instantiation
and ready to be rendered.
For the rest of this article to take a concrete example, we'll use :class:`.OpenGLRenderer`.
Finally, the :class:`.Manager` creates a :class:`.FileWriter`. This is the object that actually
writes the partial movie files.
The rest of this article is concerned with the last line in our toy example script::
scene.render()
manager.render()
This is where the actual magic happens.
.. note::
TODO TO REVIEWERS - Replace this link with the proper permanent link
Inspecting the `implementation of the render method <https://github.com/ManimCommunity/manim/blob/df1a60421ea1119cbbbd143ef288d294851baaac/manim/scene/scene.py#L211>`__
reveals that there are several hooks that can be used for pre- or postprocessing
a scene. Unsurprisingly, :meth:`.Scene.render` describes the full *render cycle*
we see that there are two passes of rendering.
.. note::
As of the experimental branch at June 30th, 2024, two pass rendering
does not exist. This will proceed to explain the single pass rendering system.
Looking around, we find that there are several hooks that can be used for pre- or postprocessing
a scene (check out :meth:`.Manager._setup`, and :meth:`.Manager._tear_down`).
.. note::
You might notice :attr:`.Manager.virtual_animation_start_time` and :attr:`.Manager.real_animation_start_time`
when looking through :meth:`.Manager._setup`. These will be explained later.
Unsurprisingly, :meth:`.Manager.render` describes the full *render cycle*
of a scene. During this life cycle, there are three custom methods whose base
implementation is empty and that can be overwritten to suit your purposes. In
the order they are called, these customizable methods are:
@ -308,14 +348,14 @@ the order they are called, these customizable methods are:
Python scripts).
After these three methods are run, the animations have been fully rendered,
and Manim calls :meth:`.CairoRenderer.scene_finished` to gracefully
and Manim calls :meth:`.Manager.tear_down` to gracefully
complete the rendering process. This checks whether any animations have been
played -- and if so, it tells the :class:`.SceneFileWriter` to close the output
file. If not, Manim assumes that a static image should be output
which it then renders using the same strategy by calling the render loop
(see below) once.
**Back in our toy example,** the call to :meth:`.Scene.render` first
**Back in our toy example,** the call to :meth:`.Manager.render` first
triggers :meth:`.Scene.setup` (which only consists of ``pass``), followed by
a call of :meth:`.Scene.construct`. At this point, our *animation script*
is run, starting with the initialization of ``orange_square``.
@ -348,16 +388,12 @@ of :class:`.Mobject`, you will find that not too much happens in there:
- and finally, ``init_colors`` is called.
Digging deeper, you will find that :meth:`.Mobject.reset_points` simply
sets the ``points`` attribute of the mobject to an empty NumPy vector,
sets the ``points`` attribute of the mobject to an empty NumPy array,
while the other two methods, :meth:`.Mobject.generate_points` and
:meth:`.Mobject.init_colors` are just implemented as ``pass``.
This makes sense: :class:`.Mobject` is not supposed to be used as
an *actual* object that is displayed on screen; in fact the camera
(which we will discuss later in more detail; it is the class that is,
for the Cairo renderer, responsible for "taking a picture" of the
current scene) does not process "pure" :class:`Mobjects <.Mobject>`
in any way, they *cannot* even appear in the rendered output.
an *actual* object that is displayed on screen.
This is where different types of mobjects come into play. Roughly
speaking, the Cairo renderer setup knows three different types of
@ -376,24 +412,24 @@ mobjects that can be rendered:
As just mentioned, :class:`VMobjects <.VMobject>` represent vectorized
mobjects. To render a :class:`.VMobject`, the camera looks at the
``points`` attribute of a :class:`.VMobject` and divides it into sets
of four points each. Each of these sets is then used to construct a
cubic Bézier curve with the first and last entry describing the
end points of the curve ("anchors"), and the second and third entry
describing the control points in between ("handles").
:attr:`~.VMobject.points` attribute of a :class:`.VMobject` and divides it into sets
of three points each. Each of these sets is then used to construct a
quadratic Bézier curve with the first and last entry describing the
end points of the curve ("anchors"), and the second entry
describing the control points in between ("handle").
.. hint::
To learn more about Bézier curves, take a look at the excellent
online textbook `A Primer on Bézier curves <https://pomax.github.io/bezierinfo/>`__
by `Pomax <https://twitter.com/TheRealPomax>`__ -- there is a playground representing
cubic Bézier curves `in §1 <https://pomax.github.io/bezierinfo/#introduction>`__,
quadratic Bézier curves `in §1 <https://pomax.github.io/bezierinfo/#introduction>`__,
the red and yellow points are "anchors", and the green and blue
points are "handles".
In contrast to :class:`.Mobject`, :class:`.VMobject` can be displayed
on screen (even though, technically, it is still considered a base class).
To illustrate how points are processed, consider the following short example
of a :class:`.VMobject` with 8 points (and thus made out of 8/4 = 2 cubic
of a :class:`.VMobject` with 6 points (and thus made out of 6/3 = 2 cubic
Bézier curves). The resulting :class:`.VMobject` is drawn in green.
The handles are drawn as red dots with a line to their closest anchor.
@ -430,6 +466,7 @@ The handles are drawn as red dots with a line to their closest anchor.
.. warning::
Manually setting the points of your :class:`.VMobject` is usually
discouraged; there are specialized methods that can take care of
that for you -- but it might be relevant when implementing your own,
@ -561,59 +598,12 @@ is not a "flat" list of mobjects, but a list of mobjects which
might contain mobjects themselves, and so on.
Stepping through the code in :meth:`.Scene.add`, we see that first
it is checked whether we are currently using the OpenGL renderer
(which we are not) -- adding mobjects to the scene works slightly
different (and actually easier!) for the OpenGL renderer. Then, the
code branch for the Cairo renderer is entered and the list of so-called
foreground mobjects (which are rendered on top of all other mobjects)
is added to the list of passed mobjects. This is to ensure that the
foreground mobjects will stay above of the other mobjects, even after
adding the new ones. In our case, the list of foreground mobjects
is actually empty, and nothing changes.
we remove all the mobjects that are being added -- this is to make
sure we don't add a :class:`.Mobject` twice! After that, we can safely
add it to :attr:`.Scene.mobjects`.
Next, :meth:`.Scene.restructure_mobjects` is called with the list
of mobjects to be added as the ``to_remove`` argument, which might
sound odd at first. Practically, this ensures that mobjects are not
added twice, as mentioned above: if they were present in the scene
``Scene.mobjects`` list before (even if they were contained as a
child of some other mobject), they are first removed from the list.
The way :meth:`.Scene.restructure_mobjects` works is rather aggressive:
It always operates on a given list of mobjects; in the ``add`` method
two different lists occur: the default one, ``Scene.mobjects`` (no extra
keyword argument is passed), and ``Scene.moving_mobjects`` (which we will
discuss later in more detail). It iterates through all of the members of
the list, and checks whether any of the mobjects passed in ``to_remove``
are contained as children (in any nesting level). If so, **their parent
mobject is deconstructed** and their siblings are inserted directly
one level higher. Consider the following example::
>>> from manim import Scene, Square, Circle, Group
>>> test_scene = Scene()
>>> mob1 = Square()
>>> mob2 = Circle()
>>> mob_group = Group(mob1, mob2)
>>> test_scene.add(mob_group)
<manim.scene.scene.Scene object at ...>
>>> test_scene.mobjects
[Group]
>>> test_scene.restructure_mobjects(to_remove=[mob1])
<manim.scene.scene.Scene object at ...>
>>> test_scene.mobjects
[Circle]
Note that the group is disbanded and the circle moves into the
root layer of mobjects in ``test_scene.mobjects``.
After the mobject list is "restructured", the mobject to be added
are simply appended to ``Scene.mobjects``. In our toy example,
the ``Scene.mobjects`` list is actually empty, so the
``restructure_mobjects`` method does not actually do anything. The
``orange_square`` is simply added to ``Scene.mobjects``, and as
the aforementioned ``Scene.moving_mobjects`` list is, at this point,
also still empty, nothing happens and :meth:`.Scene.add` returns.
We will hear more about the ``moving_mobject`` list when we discuss
the render loop. Before we do that, let us look at the next line
We will hear more from :class:`.Scene` soon.
Before we do that, let us look at the next line
of code in our toy example, which includes the initialization of
an animation class,
::
@ -642,11 +632,11 @@ the run time of animations is also fixed and known beforehand.
The initialization of animations actually is not very exciting,
:meth:`.Animation.__init__` merely sets some attributes derived
from the passed keyword arguments and additionally ensures that
the ``Animation.starting_mobject`` and ``Animation.mobject``
the :attr:`~Animation.starting_mobject` and :attr:`~.Animation.mobject`
attributes are populated. Once the animation is played, the
``starting_mobject`` attribute holds an unmodified copy of the
:attr:`~.Animation.starting_mobject` attribute holds an unmodified copy of the
mobject the animation is attached to; during the initialization
it is set to a placeholder mobject. The ``mobject`` attribute
it is set to a placeholder mobject. The :attr:`~.Animation.mobject` attribute
is set to the mobject the animation is attached to.
Animations have a few special methods which are called during the
@ -681,77 +671,80 @@ animation (like its ``run_time``, the ``rate_func``, etc.) are
processed there -- and then the animation object is fully
initialized and ready to be played.
The Animation Buffer
^^^^^^^^^^^^^^^^^^^^
There's an attribute of animations that we have glossed
over, and that is :attr:`.Animation.buffer`, of type :class:`.SceneBuffer`.
The :attr:`~.Animation.buffer` is the animations way of communicating
with what happens on the scene. If you want to modify
the scene during the interpolation stage (outside of :meth:`~.Animation.begin` or :meth:`~.Animation.finish`),
the attribute :attr:`.Animation.apply_buffer` is what tells the scene that the buffer
should be processed.
For example, an animation that adds a circle to the scene every frame might look like this
.. code-block:: python
class CircleAnimation(Animation):
def begin(self) -> None:
self.circles = VGroup()
def interpolate(self, alpha: float) -> None:
# create and arrange the circles
self.circles.add(Circle())
self.circles().arrange()
# add the new circle to the scene
self.buffer.add(self.circles[-1])
# make sure the scene actually realizes something changed
self.apply_buffer = True
Every time the :class:`.Scene` applies the buffer, it gets emptied out
for use the next time.
The ``play`` call: preparing to enter Manim's render loop
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
We are finally there, the render loop is in our reach. Let us
walk through the code that is run when :meth:`.Scene.play` is called.
.. hint::
.. note::
Recall that this article is specifically about the Cairo renderer.
Up to here, things were more or less the same for the OpenGL renderer
as well; while some base mobjects might be different, the control flow
and lifecycle of mobjects is still more or less the same. There are more
substantial differences when it comes to the rendering loop.
In the future, control will not be passed to the Manager.
Instead, the Scene will keep track of every animation and
at the very end, the Manager will render everything.
As you will see when inspecting the method, :meth:`.Scene.play` almost
immediately passes over to the ``play`` method of the renderer,
in our case :class:`.CairoRenderer.play`. The one thing :meth:`.Scene.play`
takes care of is the management of subcaptions that you might have
passed to it (see the the documentation of :meth:`.Scene.play` and
:meth:`.Scene.add_subcaption` for more information).
immediately passes over to the :class:`~.Manager._play` method of the :class:`.Manager`.
The one thing :meth:`.Scene.play` does before that is preparing the animations.
Whenever :attr:`.Mobject.animate` is called, it creates a new object called a
:class:`._AnimationBuilder`. We have to make sure to convert that into an actual
animation by calling it's :meth:`._AnimationBuilder.build` method.
We also have to update the animations with the correct rate functions, lag ratios,
and run time.
.. note::
Methods in :class:`.Manager` starting with an underscore ``_`` are intended to be
private, and are not guaranteed to be stable across versions of Manim. The :class:`.Manager`
class provides some "public" methods (methods not prefixed with ``_``) that can be overridden to
change the behavior of the program.
.. warning::
As has been said before, the communication between scene and renderer
is not in a very clean state at this point, so the following paragraphs
might be confusing if you don't run a debugger and step through the
code yourself a bit.
Inside :meth:`.CairoRenderer.play`, the renderer first checks whether
it may skip rendering of the current play call. This might happen, for example,
when ``-s`` is passed to the CLI (i.e., only the last frame should be rendered),
or when the ``-n`` flag is passed and the current play call is outside of the
specified render bounds. The "skipping status" is updated in form of the
call to :meth:`.CairoRenderer.update_skipping_status`.
Next, the renderer asks the scene to process the animations in the play
call so that renderer obtains all of the information it needs. To
be more concrete, :meth:`.Scene.compile_animation_data` is called,
which then takes care of several things:
- The method processes all animations and the keyword arguments passed
to the initial :meth:`.Scene.play` call. In particular, this means
that it makes sure all arguments passed to the play call are actually
animations (or ``.animate`` syntax calls, which are also assembled to
be actual :class:`.Animation`-objects at that point). It also propagates
any animation-related keyword arguments (like ``run_time``,
or ``rate_func``) passed to :class:`.Scene.play` to each individual
animation. The processed animations are then stored in the ``animations``
attribute of the scene (which the renderer later reads...).
- It adds all mobjects to which the animations that are played are
bound to to the scene (provided the animation is not an mobject-introducing
animation -- for these, the addition to the scene happens later).
- In case the played animation is a :class:`.Wait` animation (this is the
case in a :meth:`.Scene.wait` call), the method checks whether a static
image should be rendered, or whether the render loop should be processed
as usual (see :meth:`.Scene.should_update_mobjects` for the exact conditions,
basically it checks whether there are any time-dependent updater functions
and so on).
- Finally, the method determines the total run time of the play call (which
at this point is computed as the maximum of the run times of the passed
animations). This is stored in the ``duration`` attribute of the scene.
Subcaptions and audio is still in progress
After the animation data has been compiled by the scene, the renderer
continues to prepare for entering the render loop. It now checks the
skipping status which has been determined before. If the renderer can
skip this play call, it does so: it sets the current play call hash (which
we will get back to in a moment) to ``None`` and increases the time of the
renderer by the determined animation run time.
After the :class:`.Scene` has done all the processing of animations,
it hands out control to the :class:`.Manager`. The :class:`.Manager`
then updates the skipping status of the :class:`.Scene`. This makes sure
that if ``-s`` or ``-n`` is used for sections, the scene does the correct
thing.
Otherwise, the renderer checks whether or not Manim's caching system should
The next important line is::
self._write_hashed_movie_file()
Here, the :class:`.Manager` checks whether or not Manim's caching system should
be used. The idea of the caching system is simple: for every play call, a
hash value is computed, which is then stored and upon re-rendering the scene,
the hash is generated again and checked against the stored value. If it is the
@ -761,8 +754,8 @@ to learn more, the :func:`.get_hash_from_play_call` function in the
:mod:`.utils.hashing` module is essentially the entry point to the caching
mechanism.
In the event that the animation has to be rendered, the renderer asks
its :class:`.SceneFileWriter` to open an output container. The process
In the event that the animation has to be rendered, the manager asks
its :class:`.FileWriter` to open an output container. The process
is started by a call to ``libav`` and opens a container to which rendered
raw frames can be written. As long as the output is open, the container
can be accessed via the ``output_container`` attribute of the file writer.
@ -770,31 +763,18 @@ With the writing process in place, the renderer then asks the scene
to "begin" the animations.
First, it literally *begins* all of the animations by calling their
setup methods (:meth:`.Animation._setup_scene`, :meth:`.Animation.begin`).
setup methods (:meth:`.Animation.begin`).
In doing so, the mobjects that are newly introduced by an animation
(like via :class:`.Create` etc.) are added to the scene. Furthermore, the
animation suspends updater functions being called on its mobject, and
it sets its mobject to the state that corresponds to the first frame
of the animation.
After this has happened for all animations in the current ``play`` call,
the Cairo renderer determines which of the scene's mobjects can be
painted statically to the background, and which ones have to be
redrawn every frame. It does so by calling
:meth:`.Scene.get_moving_and_static_mobjects`, and the resulting
partition of mobjects is stored in the corresponding ``moving_mobjects``
and ``static_mobjects`` attributes.
.. note::
.. NOTE::
Implementation of figuring out which mobjects have to be redrawn
is still in progress.
The mechanism that determines static and moving mobjects is
specific for the Cairo renderer, the OpenGL renderer works differently.
Basically, moving mobjects are determined by checking whether they,
any of their children, or any of the mobjects "below" them (in the
sense of the order in which mobjects are processed in the scene)
either have an update function attached, or whether they appear
in one of the current animations. See the implementation of
:meth:`.Scene.get_moving_mobjects` for more details.
Up to this very point, we did not actually render any (partial)
image or movie files from the scene yet. This is, however, about to change.
@ -835,68 +815,28 @@ Time to render some frames.
The render loop (for real this time)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Now we get to the meat of rendering, which happens in :meth:`.Manager._progress_through_animations`.
As mentioned above, due to the mechanism that determines static and moving
mobjects in the scene, the renderer knows which mobjects it can paint
statically to the background of the scene. Practically, this means that
it partially renders a scene (to produce a background image), and then
when iterating through the time progression of the animation only the
"moving mobjects" are re-painted on top of the static background.
The renderer calls :meth:`.CairoRenderer.save_static_frame_data`, which
first checks whether there are currently any static mobjects, and if there
are, it updates the frame (only with the static mobjects; more about how
exactly this works in a moment) and then saves a NumPy array representing
the rendered frame in the ``static_image`` attribute. In our toy example,
there are no static mobjects, and so the ``static_image`` attribute is
simply set to ``None``.
Next, the renderer asks the scene whether the current animation is
a "frozen frame" animation, which would mean that the renderer actually
does not have to repaint the moving mobjects in every frame of the time
progression. It can then just take the latest static frame, and display it
throughout the animation.
.. NOTE::
An animation is considered a "frozen frame" animation if only a
static :class:`.Wait` animation is played. See the description
of :meth:`.Scene.compile_animation_data` above, or the
implementation of :meth:`.Scene.should_update_mobjects` for
more details.
If this is not the case (just as in our toy example), the renderer
then calls the :meth:`.Scene.play_internal` method, which is the
integral part of the render loop (in which the library steps through
the time progression of the animation and renders the corresponding
frames).
Within :meth:`.Scene.play_internal`, the following steps are performed:
- The scene determines the run time of the animations by calling
:meth:`.Scene.get_run_time`. This method basically takes the maximum
- The manager determines the run time of the animations by calling
:meth:`.Manager._calc_run_time`. This method basically takes the maximum
``run_time`` attribute of all of the animations passed to the
:meth:`.Scene.play` call.
- Then the *time progression* is constructed via the (internal)
:meth:`.Scene._get_animation_time_progression` method, which wraps
the actual :meth:`.Scene.get_time_progression` method. The time
progression is a ``tqdm`` `progress bar object <https://tqdm.github.io>`__
for an iterator over ``np.arange(0, run_time, 1 / config.frame_rate)``. In
- Then, the progressbar is created by :meth:`.Manager._create_progressbar`,
which returns a ``tqdm`` `progress bar object <https://tqdm.github.io>`__
object (from the ``tqdm`` library), or a fake progressbar if
:attr:`.ManimConfig.write_to_movie` is ``False``.
- Then the *time progression* is constructed via
:meth:`.Manager._calc_time_progression` method, which returns
``np.arange(0, run_time, 1 / config.frame_rate)``. In
other words, the time progression holds the time stamps (relative to the
current animations, so starting at 0 and ending at the total animation run time,
with the step size determined by the render frame rate) of the timeline where
a new animation frame should be rendered.
- Then the scene iterates over the time progression: for each time stamp ``t``,
:meth:`.Scene.update_to_time` is called, which ...
- ... first computes the time passed since the last update (which might be 0,
especially for the initial call) and references it as ``dt``,
- then (in the order in which the animations are passed to :meth:`.Scene.play`)
calls :meth:`.Animation.update_mobjects` to trigger all updater functions that
are attached to the respective animation except for the "main mobject" of
the animation (that is, for example, for :class:`.Transform` the unmodified
copies of start and target mobject -- see :meth:`.Animation.get_all_mobjects_to_update`
for more details),
we find the time difference between the current and previous frame (AKA ``dt``).
We then update the animations in the scene using ``dt`` by
- iterating over each animation
- next, we update the animations mobjects
- then the relative time progression with respect to the current animation
is computed (``alpha = t / animation.run_time``), which is then used to
update the state of the animation with a call to :meth:`.Animation.interpolate`.
@ -904,62 +844,29 @@ Within :meth:`.Scene.play_internal`, the following steps are performed:
of all mobjects in the scene, all meshes, and finally those attached to
the scene itself are run.
After updating the animations, we pass ``dt`` to :meth:`.Manager._update_frame` which...
- ... updates the total time passed
- Updates all the mobjects by calling :meth:`.Scene._update_mobjects`. This in turn
iterates over all the mobjects on the screen and updates them.
- After that, the current state of the scene is computed by :meth:`.Scene.get_state`,
which returns a :class:`.SceneState`.
- The state is then passed into :meth:`.Manager._render_frame`, which gets
the renderer to create the pixels. With :class:`.OpenGLRenderer`, this
also updates the window. :meth:`~.Manager._render_frame` also checks if it should write a frame,
and if so, writes a frame via the :class:`.FileWriter`.
- Finally, it uses a concept of virtual time vs real time to see
if the right amount of time has passed in the window. The virtual
time is the amount of time that is supposed to have passed (that is, ``t``).
The real time is how much time has actually passed in the window
(current time - start time of play). If the animations are progressing
faster than they would in real life, it will slow down the window by calling
:meth:`~.Manager._update_frame` with ``dt=0`` until that's no longer the case.
This is to make sure that animations never go too fast: it doesn't do anything if
animations are too slow!
At this point, the internal (Python) state of all mobjects has been updated
to match the currently processed timestamp. If rendering should not be skipped,
then it is now time to *take a picture*!
.. NOTE::
The update of the internal state (iteration over the time progression) happens
*always* once :meth:`.Scene.play_internal` is entered. This ensures that even
if frames do not need to be rendered (because, e.g., the ``-n`` CLI flag has
been passed, something has been cached, or because we might be in a *Section*
with skipped rendering), updater functions still run correctly, and the state
of the first frame that *is* rendered is kept consistent.
To render an image, the scene calls the corresponding method of its renderer,
:meth:`.CairoRenderer.render` and passes just the list of *moving mobjects* (remember,
the *static mobjects* are assumed to have already been painted statically to
the background of the scene). All of the hard work then happens when the renderer
updates its current frame via a call to :meth:`.CairoRenderer.update_frame`:
First, the renderer prepares its :class:`.Camera` by checking whether the renderer
has a ``static_image`` different from ``None`` stored already. If so, it sets the
image as the *background image* of the camera via :meth:`.Camera.set_frame_to_background`,
and otherwise it just resets the camera via :meth:`.Camera.reset`. The camera is then
asked to capture the scene with a call to :meth:`.Camera.capture_mobjects`.
Things get a bit technical here, and at some point it is more efficient to
delve into the implementation -- but here is a summary of what happens once the
camera is asked to capture the scene:
- First, a flat list of mobjects is created (so submobjects get extracted from
their parents). This list is then processed in groups of the same type of
mobjects (e.g., a batch of vectorized mobjects, followed by a batch of image mobjects,
followed by more vectorized mobjects, etc. -- in many cases there will just be
one batch of vectorized mobjects).
- Depending on the type of the currently processed batch, the camera uses dedicated
*display functions* to convert the :class:`.Mobject` Python object to
a NumPy array stored in the camera's ``pixel_array`` attribute.
The most important example in that context is the display function for
vectorized mobjects, :meth:`.Camera.display_multiple_vectorized_mobjects`,
or the more particular (in case you did not add a background image to your
:class:`.VMobject`), :meth:`.Camera.display_multiple_non_background_colored_vmobjects`.
This method first gets the current Cairo context, and then, for every (vectorized)
mobject in the batch, calls :meth:`.Camera.display_vectorized`. There,
the actual background stroke, fill, and then stroke of the mobject is
drawn onto the context. See :meth:`.Camera.apply_stroke` and
:meth:`.Camera.set_cairo_context_color` for more details -- but it does not get
much deeper than that, in the latter method the actual Bézier curves
determined by the points of the mobject are drawn; this is where the low-level
interaction with Cairo happens.
After all batches have been processed, the camera has an image representation
of the Scene at the current time stamp in form of a NumPy array stored in its
``pixel_array`` attribute. The renderer then takes this array and passes it to
its :class:`.SceneFileWriter`. This concludes one iteration of the render loop,
and once the time progression has been processed completely, a final bit
of cleanup is performed before the :meth:`.Scene.play_internal` call is completed.
to match the currently processed timestamp.
A TL;DR for the render loop, in the context of our toy example, reads as follows:
@ -968,23 +875,20 @@ A TL;DR for the render loop, in the context of our toy example, reads as follows
medium render quality, the frame rate is 30 frames per second, and so the time
progression with steps ``[0, 1/30, 2/30, ..., 89/30]`` is created.
- In the internal render loop, each of these time stamps is processed:
there are no updater functions, so effectively the scene updates the
there are no updater functions, so effectively the manager updates the
state of the transformation animation to the desired time stamp (for example,
at time stamp ``t = 45/30``, the animation is completed to a rate of
``alpha = 0.5``).
- Then the scene asks the renderer to do its job. The renderer asks its camera
to capture the scene, the only mobject that needs to be processed at this point
is the main mobject attached to the transformation; the camera converts the
current state of the mobject to entries in a NumPy array. The renderer passes
this array to the file writer.
- Then the manager asks the renderer to do its job. The renderer then produces
the pixels, which are then fed into the :class:`.FileWriter`.
- At the end of the loop, 90 frames have been passed to the file writer.
Completing the render loop
^^^^^^^^^^^^^^^^^^^^^^^^^^
The last few steps in the :meth:`.Scene.play_internal` call are not too
The last few steps in the :meth:`.Manager._play` call are not too
exciting: for every animation, the corresponding :meth:`.Animation.finish`
and :meth:`.Animation.clean_up_from_scene` methods are called.
method is called.
.. NOTE::
@ -999,10 +903,6 @@ and :meth:`.Animation.clean_up_from_scene` methods are called.
would be slightly longer than 1 second. We decided against this at some point.
In the end, the time progression is closed (which completes the displayed progress bar)
in the terminal. With the closing of the time progression, the
:meth:`.Scene.play_internal` call is completed, and we return to the renderer,
which now orders the :class:`.SceneFileWriter` to close the output container that has
been opened for this animation: a partial movie file is written.
This pretty much concludes the walkthrough of a :class:`.Scene.play` call,
and actually there is not too much more to say for our toy example either: at
@ -1025,5 +925,4 @@ which triggers the combination of the partial movie files into the final product
And there you go! This is a more or less detailed description of how Manim works
under the hood. While we did not discuss every single line of code in detail
in this walkthrough, it should still give you a fairly good idea of how the general
structural design of the library and at least the Cairo rendering flow in particular
looks like.
structural design of the library looks like.

View file

@ -39,12 +39,8 @@ Cameras
.. inheritance-diagram::
manim.camera.camera
manim.camera.mapping_camera
manim.camera.moving_camera
manim.camera.multi_camera
manim.camera.three_d_camera
:parts: 1
:top-classes: manim.camera.camera.Camera, manim.mobject.mobject.Mobject
:top-classes: manim.camera.camera.Camera, manim.mobject.opengl.opengl_mobject.OpenGLMobject
Mobjects
********

View file

@ -297,9 +297,9 @@ Creating a custom animation
Even though Manim has many built-in animations, you will find times when you need to smoothly animate from one state of a :class:`~.Mobject` to another.
If you find yourself in that situation, then you can define your own custom animation.
You start by extending the :class:`~.Animation` class and overriding its :meth:`~.Animation.interpolate_mobject`.
The :meth:`~.Animation.interpolate_mobject` method receives alpha as a parameter that starts at 0 and changes throughout the animation.
So, you just have to manipulate self.mobject inside Animation according to the alpha value in its interpolate_mobject method.
You start by extending the :class:`~.Animation` class and overriding its :meth:`~.Animation.interpolate`.
The :meth:`~.Animation.interpolate` method receives alpha as a parameter that starts at 0 and changes throughout the animation.
So, you just have to manipulate self.mobject inside Animation according to the alpha value in its interpolate method.
Then you get all the benefits of :class:`~.Animation` such as playing it for different run times or using different rate functions.
Let's say you start with a number and want to create a :class:`~.Transform` animation that transforms it to a target number.
@ -312,11 +312,11 @@ The class can have a constructor with three arguments, a :class:`~.DecimalNumber
The constructor will pass the :class:`~.DecimalNumber` Mobject to the super constructor (in this case, the :class:`~.Animation` constructor) and will set start and end.
The only thing that you need to do is to define how you want it to look at every step of the animation.
Manim provides you with the alpha value in the :meth:`~.Animation.interpolate_mobject` method based on frame rate of video, rate function, and run time of animation played.
Manim provides you with the alpha value in the :meth:`~.Animation.interpolate` method based on frame rate of video, rate function, and run time of animation played.
The alpha parameter holds a value between 0 and 1 representing the step of the currently playing animation.
For example, 0 means the beginning of the animation, 0.5 means halfway through the animation, and 1 means the end of the animation.
In the case of the ``Count`` animation, you just have to figure out a way to determine the number to display at the given alpha value and then set that value in the :meth:`~.Animation.interpolate_mobject` method of the ``Count`` animation.
In the case of the ``Count`` animation, you just have to figure out a way to determine the number to display at the given alpha value and then set that value in the :meth:`~.Animation.interpolate` method of the ``Count`` animation.
Suppose you are starting at 50 and incrementing until the :class:`~.DecimalNumber` reaches 100 at the end of the animation.
* If alpha is 0, you want the value to be 50.
@ -338,7 +338,7 @@ Once you have defined your ``Count`` animation, you can play it in your :class:`
.. manim:: CountingScene
:ref_classes: Animation DecimalNumber
:ref_methods: Animation.interpolate_mobject Scene.play
:ref_methods: Animation.interpolate Scene.play
class Count(Animation):
def __init__(self, number: DecimalNumber, start: float, end: float, **kwargs) -> None:
@ -348,7 +348,7 @@ Once you have defined your ``Count`` animation, you can play it in your :class:`
self.start = start
self.end = end
def interpolate_mobject(self, alpha: float) -> None:
def interpolate(self, alpha: float) -> None:
# Set value of DecimalNumber according to alpha
value = self.start + (self.rate_func(alpha) * (self.end - self.start))
self.mobject.set_value(value)

View file

@ -266,6 +266,44 @@ You can also skip rendering all animations belonging to a section like this:
Groups
******
Sections are a powerful tool to organize your animations into different parts. However, sometimes it's
more useful to look at bigger parts of your animations. *Groups* are effectively sections of sections.
The syntax is fairly simple::
class MyScene(Scene):
# enable groups
groups_api = True
@group
def introduction(self) -> None:
self.play(Write(Text("Hello World!")))
self.next_section(...)
self.play(Write(Text("This is a group!")))
self.next_section(...)
@group
def main_part(self) -> None:
self.play(Write(Text("This is the main part!")))
self.next_section(...)
self.play(Write(Text("This is a group as well!")))
self.next_section(...)
@group
def conclusion(self) -> None:
self.play(FadeOut(*self.mobjects))
You can then play specific groups by using the ``--groups`` flag::
manim --groups introduction,conclusion scene.py
Note that they must be separated by commas and without spaces.
Alternatively, you can set it on Manim's global ``config`` variable::
config.groups = ["introduction", "conclusion"]
Some command line flags
***********************

9
example_scenes/bench.py Normal file
View file

@ -0,0 +1,9 @@
from manim import *
class Test(Scene):
def construct(scene):
scene.camera.set_euler_angles(phi=75 * DEGREES, theta=-45 * DEGREES)
text = Tex("This is a 3D tex").fix_in_frame()
scene.add(text)
scene.wait()

View file

@ -0,0 +1,148 @@
import time
import numpy as np
# import pyglet
from pyglet.gl import Config
from pyglet.window import Window
import manim.utils.color.manim_colors as col
from manim._config import tempconfig
from manim.animation.creation import DrawBorderThenFill
from manim.camera.camera import Camera
from manim.constants import LEFT, OUT, RIGHT, UP
from manim.mobject.geometry.arc import Circle
from manim.mobject.geometry.polygram import Square
from manim.mobject.logo import ManimBanner
from manim.mobject.text.numbers import DecimalNumber
from manim.renderer.opengl_renderer import OpenGLRenderer
if __name__ == "__main__":
with tempconfig({"renderer": "opengl"}):
win = Window(
width=1920,
height=1080,
fullscreen=True,
vsync=True,
config=Config(double_buffer=True, samples=0),
)
renderer = OpenGLRenderer(1920, 1080, background_color=col.GRAY)
# vm = OpenGLVMobject([col.RED, col.GREEN])
vm = (
Circle(
radius=1,
stroke_color=col.YELLOW,
)
.shift(3 * RIGHT + OUT)
.set_opacity(0.6)
)
vm2 = Square(stroke_color=col.GREEN, fill_opacity=0, stroke_opacity=1).move_to(
(0, 0, -0.5)
)
vm3 = ManimBanner().set_opacity(0.6)
vm4 = (
Circle(0.5, col.GREEN)
.set_opacity(0.6)
.shift(OUT)
.set_fill(col.BLUE, opacity=0.2)
)
# vm.set_points_as_corners([[-1920/2, 0, 0], [1920/2, 0, 0], [0, 1080/2, 0]])
# print(vm.color)
# print(vm.fill_color)
# print(vm.stroke_color)
clock_mobject = DecimalNumber(0.0).shift(4 * LEFT + 2.5 * UP)
clock_mobject.fix_in_frame()
camera = Camera()
camera.save_state()
# renderer.init_camera(camera)
# renderer.render(camera, [vm, vm2])
# image = renderer.get_pixels()
# print(image.shape)
# Image.fromarray(image, "RGBA").show()
# exit(0)
renderer.use_window()
# clock = pyglet.clock.get_default()
def update_circle(dt):
vm.move_to((np.sin(dt) * 4, np.cos(dt) * 4, -1))
def p2m(x, y, z):
from manim._config import config
return (
config.frame_width * (x / config.pixel_width - 0.5),
config.frame_height * (y / config.pixel_height - 0.5),
z,
)
@win.event
def on_close():
win.close()
@win.event
def on_mouse_motion(x, y, dx, dy):
# vm.move_to((14.2222 * (x / 1920 - 0.5), 8 * (y / 1080 - 0.5), 0))
# camera.move_to(p2m(x,y,camera.get_center()[2]))
from scipy.spatial.transform import Rotation
camera.set_orientation(
Rotation.from_rotvec(
(-UP * (x / 1920 - 0.5) + RIGHT * (y / 1080 - 0.5)) * 2 * 3.1415
)
)
# vm.set_color(col.RED.interpolate(col.GREEN,x/1920))
# print(x,y)
@win.event
def on_draw():
# dt = clock.update_time()
renderer.render(camera, [vm2, vm3, vm4, clock_mobject, vm])
# update_circle(counter)
@win.event
def on_resize(width, height):
super(Window, win).on_resize(width, height)
# pyglet.app.run()
has_started = False
is_finished = False
run_time = 5
new_vm = Square(fill_color=col.GREEN, stroke_color=col.BLUE).shift(
2.5 * RIGHT - UP + 2 * OUT
)
animation = DrawBorderThenFill(vm3, run_time=run_time)
real_time = 0
virtual_time = 0
start_timestamp = time.time()
dt = 1 / 30
while True:
# pyglet.app.platform_event_loop.step()
win.switch_to()
if not has_started:
animation.begin()
has_started = True
real_time = time.time() - start_timestamp
while virtual_time < real_time:
virtual_time += dt
if not is_finished:
if virtual_time >= run_time:
animation.finish()
buffer = str(animation.buffer)
print(f"buffer = {buffer}")
has_finished = True
else:
animation.update_mobjects(dt)
animation.interpolate(virtual_time / run_time)
# update_circle(virtual_time)
clock_mobject.set_value(virtual_time)
win.dispatch_event("on_draw")
win.dispatch_events()
win.flip()

View file

@ -1,8 +1,12 @@
from pathlib import Path
from manim.opengl import *
import manim.utils.opengl as opengl
from manim import *
from manim.opengl import *
from manim.mobject.opengl.opengl_surface import OpenGLTexturedSurface
from manim.mobject.opengl.opengl_three_dimensions import OpenGLSurfaceMesh
from manim.mobject.opengl.shader import FullScreenQuad, Mesh, Shader
# 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.

114
example_scenes/test_new.py Normal file
View file

@ -0,0 +1,114 @@
import numpy as np
import pyglet
from pyglet.gl import Config
from pyglet.window import Window
import manim.utils.color.manim_colors as col
from manim._config import tempconfig
from manim.camera.camera import Camera
from manim.constants import OUT, RIGHT, UP
from manim.mobject.geometry.arc import Circle
from manim.mobject.geometry.polygram import Square
from manim.mobject.logo import ManimBanner
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject
from manim.mobject.text.numbers import DecimalNumber
from manim.renderer.opengl_renderer import OpenGLRenderer
if __name__ == "__main__":
with tempconfig({"renderer": "opengl"}):
win = Window(
width=1920,
height=1080,
vsync=True,
config=Config(double_buffer=True, samples=0),
)
renderer = OpenGLRenderer(1920, 1080, background_color=col.GRAY)
# vm = OpenGLVMobject([col.RED, col.GREEN])
vm = (
Circle(
radius=1,
stroke_color=col.YELLOW,
)
.shift(RIGHT)
.set_opacity(0.5)
)
vm2 = Square(stroke_color=col.GREEN, fill_opacity=0, stroke_opacity=1).move_to(
(0, 0, -0.5)
)
vm3 = ManimBanner().set_opacity(1.0)
vm4 = (
Circle(0.5, col.GREEN)
.set_opacity(0.6)
.shift(OUT)
.set_fill(col.BLUE, opacity=0.2)
)
# vm.set_points_as_corners([[-1920/2, 0, 0], [1920/2, 0, 0], [0, 1080/2, 0]])
# print(vm.color)
# print(vm.fill_color)
# print(vm.stroke_color)
camera = Camera()
camera.save_state()
renderer.init_camera(camera)
# renderer.render(camera, [vm, vm2])
# image = renderer.get_pixels()
# print(image.shape)
# Image.fromarray(image, "RGBA").show()
# exit(0)
renderer.use_window()
clock = pyglet.clock.get_default()
def update_circle(dt):
vm.move_to((np.sin(dt) * 4, np.cos(dt) * 4, -1))
def p2m(x, y, z):
from manim._config import config
return (
config.frame_width * (x / config.pixel_width - 0.5),
config.frame_height * (y / config.pixel_height - 0.5),
z,
)
@win.event
def on_close():
win.close()
@win.event
def on_mouse_motion(x, y, dx, dy):
# vm.move_to((14.2222 * (x / 1920 - 0.5), 8 * (y / 1080 - 0.5), 0))
# camera.move_to(p2m(x,y,camera.get_center()[2]))
from scipy.spatial.transform import Rotation
camera.set_orientation(
Rotation.from_rotvec(
(-UP * (x / 1920 - 0.5) + RIGHT * (y / 1080 - 0.5)) * 2 * 3.1415
)
)
# vm.set_color(col.RED.interpolate(col.GREEN,x/1920))
# print(x,y)
@win.event
def on_draw():
dt = clock.update_time()
fps: OpenGLVMobject = DecimalNumber(dt)
fps.fix_in_frame()
renderer.render(camera, [vm, vm2, vm3, vm4, fps])
# update_circle(counter)
@win.event
def on_resize(width, height):
super(Window, win).on_resize(width, height)
pyglet.app.run()
# while True:
# pyglet.clock.tick()
# pyglet.app.platform_event_loop.step()
# win.switch_to()
# counter += 0.01
# update_circle(counter)
# win.dispatch_event("on_draw")
# win.dispatch_events()
# win.flip()

View file

@ -0,0 +1,96 @@
from manim import *
class Test(Scene):
groups_api = True
@group
def first_section(self) -> None:
line = Line()
line.add_updater(lambda m, dt: m.rotate(PI * dt))
line.to_edge(LEFT)
self.add(line)
square = Square()
tex = Tex(
"Hello, ",
"world",
r" $e^{i\theta}$",
stroke_color=RED,
fill_color=BLUE,
stroke_width=2,
).to_edge(RIGHT)
tex.set_color_by_tex("world", GREEN)
self.add(tex)
self.play(Create(tex), Rotate(square, PI / 2))
self.wait(1)
self.play(FadeOut(square))
@group
def three_mobjects(self) -> None:
hexagon = RegularPolygon(6)
circle = Circle()
star = Star()
VGroup(hexagon, circle, star).arrange()
self.play(
Succession(
Create(hexagon),
DrawBorderThenFill(circle),
SpinInFromNothing(star),
)
)
self.play(FadeOut(VGroup(hexagon, circle, star)))
@group
def manim_banner(self) -> None:
banner = ManimBanner().scale(0.5)
self.play(banner.create())
self.play(banner.expand())
self.wait(1)
self.play(Unwrite(banner))
@group
def graph(self):
vertices = [1, 2, 3]
edges = [(1, 2), (2, 3), (3, 1)]
graph = Graph(vertices, edges, layout="circular")
self.play(Create(graph))
self.play(
graph.animate.add_vertices(
4,
5,
vertex_config={4: {"fill_color": RED}, 5: {"fill_color": RED}},
positions={4: [2, 1, 0], 5: [2, -1, 0]},
)
)
self.play( # TODO: this animation is currently broken
graph.animate.add_edges(
(2, 4),
(3, 5),
(4, 5),
edge_config={
(2, 4): {"stroke_color": GREEN},
(3, 5): {"stroke_color": GREEN},
(4, 5): {"stroke_color": YELLOW},
},
)
)
self.wait(1)
self.play(graph.animate.remove_vertices(1))
self.play(graph.animate.remove_edges((4, 5)))
self.play(Uncreate(graph))
if __name__ == "__main__":
with (
tempconfig(
{
"preview": True,
"write_to_movie": False,
"disable_caching": True,
"frame_rate": 60,
"disable_caching_warning": True,
}
),
Manager(Test) as manager,
):
manager.render()

View file

@ -17,98 +17,91 @@ except PackageNotFoundError:
# Importing the config module should be the first thing we do, since other
# modules depend on the global config dict for initialization.
from ._config import *
from manim._config import *
# many scripts depend on this -> has to be loaded first
from .utils.commands import *
from manim.utils.commands import *
# isort: on
import numpy as np
from .animation.animation import *
from .animation.changing import *
from .animation.composition import *
from .animation.creation import *
from .animation.fading import *
from .animation.growing import *
from .animation.indication import *
from .animation.movement import *
from .animation.numbers import *
from .animation.rotation import *
from .animation.specialized import *
from .animation.speedmodifier import *
from .animation.transform import *
from .animation.transform_matching_parts import *
from .animation.updaters.mobject_update_utils import *
from .animation.updaters.update import *
from .camera.camera import *
from .camera.mapping_camera import *
from .camera.moving_camera import *
from .camera.multi_camera import *
from .camera.three_d_camera import *
from .constants import *
from .mobject.frame import *
from .mobject.geometry.arc import *
from .mobject.geometry.boolean_ops import *
from .mobject.geometry.labeled import *
from .mobject.geometry.line import *
from .mobject.geometry.polygram import *
from .mobject.geometry.shape_matchers import *
from .mobject.geometry.tips import *
from .mobject.graph import *
from .mobject.graphing.coordinate_systems import *
from .mobject.graphing.functions import *
from .mobject.graphing.number_line import *
from .mobject.graphing.probability import *
from .mobject.graphing.scale import *
from .mobject.logo import *
from .mobject.matrix import *
from .mobject.mobject import *
from .mobject.opengl.dot_cloud import *
from .mobject.opengl.opengl_point_cloud_mobject import *
from .mobject.svg.brace import *
from .mobject.svg.svg_mobject import *
from .mobject.table import *
from .mobject.text.code_mobject import *
from .mobject.text.numbers import *
from .mobject.text.tex_mobject import *
from .mobject.text.text_mobject import *
from .mobject.three_d.polyhedra import *
from .mobject.three_d.three_d_utils import *
from .mobject.three_d.three_dimensions import *
from .mobject.types.image_mobject import *
from .mobject.types.point_cloud_mobject import *
from .mobject.types.vectorized_mobject import *
from .mobject.value_tracker import *
from .mobject.vector_field import *
from .renderer.cairo_renderer import *
from .scene.moving_camera_scene import *
from .scene.scene import *
from .scene.scene_file_writer import *
from .scene.section import *
from .scene.three_d_scene import *
from .scene.vector_space_scene import *
from .scene.zoomed_scene import *
from .utils import color, rate_functions, unit
from .utils.bezier import *
from .utils.color import *
from .utils.config_ops import *
from .utils.debug import *
from .utils.file_ops import *
from .utils.images import *
from .utils.iterables import *
from .utils.paths import *
from .utils.rate_functions import *
from .utils.simple_functions import *
from .utils.sounds import *
from .utils.space_ops import *
from .utils.tex import *
from .utils.tex_templates import *
from manim.animation.animation import *
from manim.animation.changing import *
from manim.animation.composition import *
from manim.animation.creation import *
from manim.animation.fading import *
from manim.animation.growing import *
from manim.animation.indication import *
from manim.animation.movement import *
from manim.animation.numbers import *
from manim.animation.rotation import *
from manim.animation.specialized import *
from manim.animation.speedmodifier import *
from manim.animation.transform import *
from manim.animation.transform_matching_parts import *
from manim.animation.updaters.mobject_update_utils import *
from manim.animation.updaters.update import *
from manim.constants import *
from manim.file_writer import *
from manim.manager import *
from manim.mobject.frame import *
from manim.mobject.geometry.arc import *
from manim.mobject.geometry.boolean_ops import *
from manim.mobject.geometry.labeled import *
from manim.mobject.geometry.line import *
from manim.mobject.geometry.polygram import *
from manim.mobject.geometry.shape_matchers import *
from manim.mobject.geometry.tips import *
from manim.mobject.graph import *
from manim.mobject.graphing.coordinate_systems import *
from manim.mobject.graphing.functions import *
from manim.mobject.graphing.number_line import *
from manim.mobject.graphing.probability import *
from manim.mobject.graphing.scale import *
from manim.mobject.logo import *
from manim.mobject.matrix import *
from manim.mobject.mobject import *
from manim.mobject.opengl.dot_cloud import *
from manim.mobject.opengl.opengl_point_cloud_mobject import *
from manim.mobject.opengl.opengl_vectorized_mobject import *
from manim.mobject.svg.brace import *
from manim.mobject.svg.svg_mobject import *
from manim.mobject.table import *
from manim.mobject.text.code_mobject import *
from manim.mobject.text.numbers import *
from manim.mobject.text.tex_mobject import *
from manim.mobject.text.text_mobject import *
from manim.mobject.three_d.polyhedra import *
from manim.mobject.three_d.three_d_utils import *
from manim.mobject.three_d.three_dimensions import *
from manim.mobject.types.image_mobject import *
from manim.mobject.types.point_cloud_mobject import *
from manim.mobject.types.vectorized_mobject import *
from manim.mobject.value_tracker import *
from manim.mobject.vector_field import *
from manim.scene.scene import *
from manim.scene.sections import *
from manim.scene.vector_space_scene import *
from manim.utils import color, rate_functions, unit
from manim.utils.bezier import *
from manim.utils.color import *
from manim.utils.config_ops import *
from manim.utils.debug import *
from manim.utils.file_ops import *
from manim.utils.images import *
from manim.utils.iterables import *
from manim.utils.paths import *
from manim.utils.rate_functions import *
from manim.utils.simple_functions import *
from manim.utils.sounds import *
from manim.utils.space_ops import *
from manim.utils.tex import *
from manim.utils.tex_templates import *
try:
from IPython import get_ipython
from .utils.ipython_magic import ManimMagic
from manim.utils.ipython_magic import ManimMagic
except ImportError:
pass
else:
@ -116,4 +109,4 @@ else:
if ipy is not None:
ipy.register_magics(ManimMagic)
from .plugins import *
from manim.plugins import *

View file

@ -7,18 +7,18 @@
# Each of the following will be set to True if the corresponding CLI flag
# is present when executing manim. If the flag is not present, they will
# be set to the value found here. For example, running manim with the -w
# flag will set WRITE_TO_MOVIE to True. However, since the default value
# of WRITE_TO_MOVIE defined in this file is also True, running manim
# without the -w value will also output a movie file. To change that, set
# WRITE_TO_MOVIE = False so that running manim without the -w flag will not
# generate a movie file. Note all of the following accept boolean values.
# be set to the value found here. For example, running manim with the --format=mp4
# flag will set FORMAT to mp4. However, since the default value
# of FORMAT defined in this file is also mp4, running manim
# without the --format=mp4 value will also output an mp4 movie file. To change that, set
# FORMAT = webm so that running manim without the --format=mp4 flag will not
# generate an mp4 movie file.
# --notify_outdated_version
notify_outdated_version = True
# -w, --write_to_movie
write_to_movie = True
write_to_movie = False
format = mp4
@ -29,15 +29,9 @@ save_last_frame = False
# -a, --write_all
write_all = False
# -g, --save_pngs
save_pngs = False
# -0, --zero_pad
zero_pad = 4
# -i, --save_as_gif
save_as_gif = False
# --save_sections
save_sections = False
@ -94,7 +88,7 @@ text_dir = {media_dir}/texts
partial_movie_dir = {video_dir}/partial_movie_files/{scene_name}
# --renderer [cairo|opengl]
renderer = cairo
renderer = opengl
# --enable_gui
enable_gui = False
@ -121,12 +115,6 @@ window_monitor = 0
# --force_window
force_window = False
# --use_projection_fill_shaders
use_projection_fill_shaders = False
# --use_projection_stroke_shaders
use_projection_stroke_shaders = False
movie_file_extension = .mp4
# These now override the --quality option.

View file

@ -20,7 +20,7 @@ import logging
import os
import re
import sys
from collections.abc import Iterator, Mapping, MutableMapping
from collections.abc import Iterator, Mapping, MutableMapping, Sequence
from pathlib import Path
from typing import TYPE_CHECKING, Any, ClassVar, NoReturn
@ -276,6 +276,7 @@ class ManimConfig(MutableMapping):
"frame_x_radius",
"frame_y_radius",
"from_animation_number",
"groups",
"images_dir",
"input_file",
"media_embed",
@ -294,10 +295,8 @@ class ManimConfig(MutableMapping):
"preview",
"progress_bar",
"quality",
"save_as_gif",
"save_sections",
"save_last_frame",
"save_pngs",
"scene_names",
"seed",
"show_in_file_browser",
@ -309,8 +308,6 @@ class ManimConfig(MutableMapping):
"renderer",
"enable_gui",
"gui_location",
"use_projection_fill_shaders",
"use_projection_stroke_shaders",
"verbosity",
"video_dir",
"sections_dir",
@ -329,6 +326,22 @@ class ManimConfig(MutableMapping):
def __init__(self) -> None:
self._d: dict[str, Any | None] = dict.fromkeys(self._OPTS)
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(
"Output format set as webm, this can be slower than other formats",
)
if not self.preview and not self.write_to_movie:
logger.warning(
"preview and write_to_movie disabled, this is a dry run. Try passing -p or -w."
)
elif self.preview and self.write_to_movie:
logger.warning(
"Both preview and write_to_movie enabled, this can be slower than just previewing."
)
# behave like a dict
def __iter__(self) -> Iterator[str]:
return iter(self._d)
@ -579,8 +592,6 @@ class ManimConfig(MutableMapping):
"write_to_movie",
"save_last_frame",
"write_all",
"save_pngs",
"save_as_gif",
"save_sections",
"preview",
"show_in_file_browser",
@ -591,8 +602,6 @@ class ManimConfig(MutableMapping):
"custom_folders",
"enable_gui",
"fullscreen",
"use_projection_fill_shaders",
"use_projection_stroke_shaders",
"enable_wireframe",
"force_window",
"no_latex_cleanup",
@ -700,6 +709,8 @@ class ManimConfig(MutableMapping):
if quality:
self.quality = _determine_quality(quality)
self.groups = parser["CLI"].get("groups", fallback="", raw=True) or []
return self
def digest_args(self, args: argparse.Namespace) -> Self:
@ -754,8 +765,6 @@ class ManimConfig(MutableMapping):
"show_in_file_browser",
"write_to_movie",
"save_last_frame",
"save_pngs",
"save_as_gif",
"save_sections",
"write_all",
"disable_caching",
@ -769,8 +778,6 @@ class ManimConfig(MutableMapping):
"background_color",
"enable_gui",
"fullscreen",
"use_projection_fill_shaders",
"use_projection_stroke_shaders",
"zero_pad",
"enable_wireframe",
"force_window",
@ -943,6 +950,7 @@ class ManimConfig(MutableMapping):
def notify_outdated_version(self, value: bool) -> None:
self._set_boolean("notify_outdated_version", value)
# TODO: Rename to write_to_file
@property
def write_to_movie(self) -> bool:
"""Whether to render the scene to a movie file (-w)."""
@ -970,24 +978,6 @@ class ManimConfig(MutableMapping):
def write_all(self, value: bool) -> None:
self._set_boolean("write_all", value)
@property
def save_pngs(self) -> bool:
"""Whether to save all frames in the scene as images files (-g)."""
return self._d["save_pngs"]
@save_pngs.setter
def save_pngs(self, value: bool) -> None:
self._set_boolean("save_pngs", value)
@property
def save_as_gif(self) -> bool:
"""Whether to save the rendered scene in .gif format (-i)."""
return self._d["save_as_gif"]
@save_as_gif.setter
def save_as_gif(self, value: bool) -> None:
self._set_boolean("save_as_gif", value)
@property
def save_sections(self) -> bool:
"""Whether to save single videos for each section in addition to the movie file."""
@ -1059,10 +1049,6 @@ class ManimConfig(MutableMapping):
[None, "png", "gif", "mp4", "mov", "webm"],
)
self.resolve_movie_file_extension(self.transparent)
if self.format == "webm":
logger.warning(
"Output format set as webm, this can be slower than other formats",
)
@property
def ffmpeg_loglevel(self) -> str:
@ -1218,6 +1204,24 @@ class ManimConfig(MutableMapping):
def upto_animation_number(self, value: int) -> None:
self._set_pos_number("upto_animation_number", value, True)
@property
def groups(self) -> tuple[str, ...]:
"""The name of the groups to play.
If not passed, it will play all groups. Otherwise,
it will play only the groups passed in.
"""
return self._d["groups"] # type: ignore[misc]
@groups.setter
def groups(self, value: str | Sequence[str]) -> None:
if isinstance(value, str):
self._set_str("groups", value.replace(" ", "").split(","))
else:
if not all(isinstance(v, str) for v in value):
raise ValueError("groups must be a string or a sequence of strings")
self._d["groups"] = tuple(value)
@property
def max_files_cached(self) -> int:
"""Maximum number of files cached. Use -1 for infinity (no flag)."""
@ -1478,24 +1482,6 @@ class ManimConfig(MutableMapping):
def fullscreen(self, value: bool) -> None:
self._set_boolean("fullscreen", value)
@property
def use_projection_fill_shaders(self) -> bool:
"""Use shaders for OpenGLVMobject fill which are compatible with transformation matrices."""
return self._d["use_projection_fill_shaders"]
@use_projection_fill_shaders.setter
def use_projection_fill_shaders(self, value: bool) -> None:
self._set_boolean("use_projection_fill_shaders", value)
@property
def use_projection_stroke_shaders(self) -> bool:
"""Use shaders for OpenGLVMobject stroke which are compatible with transformation matrices."""
return self._d["use_projection_stroke_shaders"]
@use_projection_stroke_shaders.setter
def use_projection_stroke_shaders(self, value: bool) -> None:
self._set_boolean("use_projection_stroke_shaders", value)
@property
def zero_pad(self) -> int:
"""PNG zero padding. A number between 0 (no zero padding) and 9 (9 columns minimum)."""

View file

@ -2,32 +2,45 @@
from __future__ import annotations
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
from .. import config, logger
from ..constants import RendererType
from ..mobject import mobject
from ..mobject.mobject import Group, Mobject
from ..mobject.opengl import opengl_mobject
from ..utils.rate_functions import linear, smooth
__all__ = ["Animation", "Wait", "Add", "override_animation"]
from collections.abc import Callable, Iterable, Sequence
from copy import deepcopy
from functools import partialmethod
from typing import TYPE_CHECKING, Any, Self
from typing import TYPE_CHECKING, Any, Self, assert_never, cast, overload
import numpy as np
from typing_extensions import TypeVar
from manim.mobject.opengl.opengl_mobject import (
OpenGLGroup as Group,
)
from manim.mobject.opengl.opengl_mobject import (
OpenGLMobject as Mobject,
)
from manim.mobject.opengl.opengl_mobject import (
_AnimationBuilder,
)
from .. import logger
from ..utils.rate_functions import linear, smooth
from .protocol import AnimationProtocol, MobjectAnimation
from .scene_buffer import SceneBuffer, SceneOperation
if TYPE_CHECKING:
from typing import Self
from manim.scene.scene import Scene
M = TypeVar("M", bound=Mobject)
__all__ = ["Animation", "Wait", "override_animation"]
DEFAULT_ANIMATION_RUN_TIME: float = 1.0
DEFAULT_ANIMATION_LAG_RATIO: float = 0.0
class Animation:
class Animation(AnimationProtocol):
"""An animation.
Animations have a fixed time span.
@ -69,9 +82,9 @@ class Animation:
.. NOTE::
In the current implementation of this class, the specified rate function is applied
within :meth:`.Animation.interpolate_mobject` call as part of the call to
within :meth:`.Animation.interpolate` call as part of the call to
:meth:`.Animation.interpolate_submobject`. For subclasses of :class:`.Animation`
that are implemented by overriding :meth:`interpolate_mobject`, the rate function
that are implemented by overriding :meth:`interpolate`, the rate function
has to be applied manually (e.g., by passing ``self.rate_func(alpha)`` instead
of just ``alpha``).
@ -127,35 +140,31 @@ class Animation:
def __init__(
self,
mobject: Mobject | OpenGLMobject | None,
mobject: Mobject | None,
lag_ratio: float = DEFAULT_ANIMATION_LAG_RATIO,
run_time: float = DEFAULT_ANIMATION_RUN_TIME,
rate_func: Callable[[float], float] = smooth,
reverse_rate_function: bool = False,
name: str = None,
remover: bool = False, # remove a mobject from the screen?
name: str = "",
remover: bool = False, # remove a mobject from the screen at end of animation
suspend_mobject_updating: bool = True,
introducer: bool = False,
*,
_on_finish: Callable[[], None] = lambda _: None,
use_override: bool = True, # included here to avoid TypeError if passed from a subclass' constructor
) -> None:
self._typecheck_input(mobject)
self.run_time: float = run_time
self.rate_func: Callable[[float], float] = rate_func
self.reverse_rate_function: bool = reverse_rate_function
self.name: str | None = name
self.name: str = name
self.remover: bool = remover
self.introducer: bool = introducer
self.suspend_mobject_updating: bool = suspend_mobject_updating
self.lag_ratio: float = lag_ratio
self._on_finish: Callable[[Scene], None] = _on_finish
if config["renderer"] == RendererType.OPENGL:
self.starting_mobject: OpenGLMobject = OpenGLMobject()
self.mobject: OpenGLMobject = (
mobject if mobject is not None else OpenGLMobject()
)
else:
self.buffer = SceneBuffer()
self.apply_buffer = False # ask scene to apply buffer
self.starting_mobject: Mobject = Mobject()
self.mobject: Mobject = mobject if mobject is not None else Mobject()
@ -183,7 +192,7 @@ class Animation:
def _typecheck_input(self, mobject: Mobject | None) -> None:
if mobject is None:
logger.debug("Animation with empty mobject")
elif not isinstance(mobject, (Mobject, OpenGLMobject)):
elif not isinstance(mobject, Mobject):
raise TypeError("Animation only works on Mobjects")
def __str__(self) -> str:
@ -194,6 +203,17 @@ class Animation:
def __repr__(self) -> str:
return str(self)
def update_rate_info(
self,
run_time: float | None = None,
rate_func: Callable[[float], float] | None = None,
lag_ratio: float | None = None,
):
self.run_time = run_time or self.run_time
self.rate_func = rate_func or self.rate_func
self.lag_ratio = lag_ratio or self.lag_ratio
return self
def begin(self) -> None:
"""Begin the animation.
@ -213,10 +233,12 @@ class Animation:
self.mobject.suspend_updating()
self.interpolate(0)
# TODO: Figure out a way to check
# if self.mobject in scene.get_mobject_family
if self.introducer:
self.buffer.add(self.mobject)
def finish(self) -> None:
# TODO: begin and finish should require a scene as parameter.
# That way Animation.clean_up_from_screen and Scene.add_mobjects_from_animations
# could be removed as they fulfill basically the same purpose.
"""Finish the animation.
This method gets called when the animation is over.
@ -226,45 +248,14 @@ class Animation:
if self.suspend_mobject_updating and self.mobject is not None:
self.mobject.resume_updating()
def clean_up_from_scene(self, scene: Scene) -> None:
"""Clean up the :class:`~.Scene` after finishing the animation.
if self.remover:
self.buffer.remove(self.mobject)
This includes to :meth:`~.Scene.remove` the Animation's
:class:`~.Mobject` if the animation is a remover.
Parameters
----------
scene
The scene the animation should be cleaned up from.
"""
self._on_finish(scene)
if self.is_remover():
scene.remove(self.mobject)
def _setup_scene(self, scene: Scene) -> None:
"""Setup up the :class:`~.Scene` before starting the animation.
This includes to :meth:`~.Scene.add` the Animation's
:class:`~.Mobject` if the animation is an introducer.
Parameters
----------
scene
The scene the animation should be cleaned up from.
"""
if scene is None:
return
if (
self.is_introducer()
and self.mobject not in scene.get_mobject_family_members()
):
scene.add(self.mobject)
def create_starting_mobject(self) -> Mobject | OpenGLMobject:
def create_starting_mobject(self) -> Mobject:
# Keep track of where the mobject starts
return self.mobject.copy()
def get_all_mobjects(self) -> Sequence[Mobject | OpenGLMobject]:
def get_all_mobjects(self) -> Sequence[Mobject]:
"""Get all mobjects involved in the animation.
Ordering must match the ordering of arguments to interpolate_submobject
@ -277,14 +268,7 @@ class Animation:
return self.mobject, self.starting_mobject
def get_all_families_zipped(self) -> Iterable[tuple]:
if config["renderer"] == RendererType.OPENGL:
return zip(
*(mob.get_family() for mob in self.get_all_mobjects()), strict=False
)
return zip(
*(mob.family_members_with_points() for mob in self.get_all_mobjects()),
strict=False,
)
return zip(*(mob.get_family() for mob in self.get_all_mobjects()), strict=False)
def update_mobjects(self, dt: float) -> None:
"""
@ -297,7 +281,24 @@ class Animation:
for mob in self.get_all_mobjects_to_update():
mob.update(dt)
def get_all_mobjects_to_update(self) -> list[Mobject]:
def process_subanimation_buffer(self, buffer: SceneBuffer):
"""
This is used in animations that are proxies around
other animations, like :class:`.AnimationGroup`
"""
for op, args, kwargs in buffer:
match op:
case SceneOperation.ADD:
self.buffer.add(*args, **kwargs)
case SceneOperation.REMOVE:
self.buffer.remove(*args, **kwargs)
case SceneOperation.REPLACE:
self.buffer.replace(*args, **kwargs)
case _:
assert_never(op)
buffer.clear()
def get_all_mobjects_to_update(self) -> Sequence[Mobject]:
"""Get all mobjects to be updated during the animation.
Returns
@ -308,9 +309,9 @@ class Animation:
# The surrounding scene typically handles
# updating of self.mobject. Besides, in
# most cases its updating is suspended anyway
return list(filter(lambda m: m is not self.mobject, self.get_all_mobjects()))
return [m for m in self.get_all_mobjects() if m is not self.mobject]
def copy(self) -> Animation:
def copy(self) -> Self:
"""Create a copy of the animation.
Returns
@ -324,19 +325,6 @@ class Animation:
# TODO: stop using alpha as parameter name in different meanings.
def interpolate(self, alpha: float) -> None:
"""Set the animation progress.
This method gets called for every frame during an animation.
Parameters
----------
alpha
The relative time to set the animation to, 0 meaning the start, 1 meaning
the end.
"""
self.interpolate_mobject(alpha)
def interpolate_mobject(self, alpha: float) -> None:
"""Interpolates the mobject of the :class:`Animation` based on alpha value.
Parameters
@ -346,10 +334,10 @@ class Animation:
is completed. For example, alpha-values of 0, 0.5, and 1 correspond
to the animation being completed 0%, 50%, and 100%, respectively.
"""
families = list(self.get_all_families_zipped())
families = tuple(self.get_all_families_zipped())
for i, mobs in enumerate(families):
sub_alpha = self.get_sub_alpha(alpha, i, len(families))
self.interpolate_submobject(*mobs, sub_alpha)
self.interpolate_submobject(*mobs, sub_alpha) # type: ignore[call-arg]
def interpolate_submobject(
self,
@ -358,8 +346,7 @@ class Animation:
# target_copy: Mobject, #Todo: fix - signature of interpolate_submobject differs in Transform().
alpha: float,
) -> Animation:
# Typically implemented by subclass
pass
raise NotImplementedError("Implement in subclass")
def get_sub_alpha(self, alpha: float, index: int, num_submobjects: int) -> float:
"""Get the animation progress of any submobjects subanimation.
@ -385,13 +372,14 @@ class Animation:
full_length = (num_submobjects - 1) * lag_ratio + 1
value = alpha * full_length
lower = index * lag_ratio
raw_sub_alpha = np.clip((value - lower), 0, 1)
if self.reverse_rate_function:
return self.rate_func(1 - (value - lower))
return self.rate_func(1 - raw_sub_alpha)
else:
return self.rate_func(value - lower)
return self.rate_func(raw_sub_alpha)
# Getters and setters
def set_run_time(self, run_time: float) -> Animation:
def set_run_time(self, run_time: float) -> Self:
"""Set the run time of the animation.
Parameters
@ -426,7 +414,7 @@ class Animation:
def set_rate_func(
self,
rate_func: Callable[[float], float],
) -> Animation:
) -> Self:
"""Set the rate function of the animation.
Parameters
@ -455,7 +443,7 @@ class Animation:
"""
return self.rate_func
def set_name(self, name: str) -> Animation:
def set_name(self, name: str) -> Self:
"""Set the name of the animation.
Parameters
@ -471,26 +459,6 @@ class Animation:
self.name = name
return self
def is_remover(self) -> bool:
"""Test if the animation is a remover.
Returns
-------
bool
``True`` if the animation is a remover, ``False`` otherwise.
"""
return self.remover
def is_introducer(self) -> bool:
"""Test if the animation is an introducer.
Returns
-------
bool
``True`` if the animation is an introducer, ``False`` otherwise.
"""
return self.introducer
@classmethod
def __init_subclass__(cls, **kwargs) -> None:
super().__init_subclass__(**kwargs)
@ -540,9 +508,19 @@ class Animation:
cls.__init__ = cls._original__init__
@overload
def prepare_animation(anim: MobjectAnimation[M]) -> MobjectAnimation[M]: ...
@overload
def prepare_animation(
anim: Animation | mobject._AnimationBuilder | opengl_mobject._AnimationBuilder,
) -> Animation:
anim: AnimationProtocol | _AnimationBuilder | Mobject,
) -> AnimationProtocol: ...
def prepare_animation(
anim: AnimationProtocol | _AnimationBuilder | Mobject,
) -> AnimationProtocol:
r"""Returns either an unchanged animation, or the animation built
from a passed animation factory.
@ -569,16 +547,17 @@ def prepare_animation(
TypeError: Object 42 cannot be converted to an animation
"""
if isinstance(anim, mobject._AnimationBuilder):
if isinstance(anim, _AnimationBuilder):
return anim.build()
if isinstance(anim, opengl_mobject._AnimationBuilder):
return anim.build()
if isinstance(anim, Animation):
return anim
raise TypeError(f"Object {anim} cannot be converted to an animation")
# if it has these three methods it probably is an AnimationProtocol
# but we don't use isinstance because it's slow
try:
for method in ("begin", "finish", "update_mobjects"):
getattr(anim, method)
return cast(AnimationProtocol, anim)
except AttributeError:
raise TypeError(f"Object {anim} cannot be converted to an animation") from None
class Wait(Animation):
@ -615,12 +594,9 @@ class Wait(Animation):
if stop_condition and frozen_frame:
raise ValueError("A static Wait animation cannot have a stop condition.")
self.duration: float = run_time
self.stop_condition = stop_condition
self.is_static_wait: bool = frozen_frame
self.is_static_wait: bool = bool(frozen_frame)
super().__init__(None, run_time=run_time, rate_func=rate_func, **kwargs)
# quick fix to work in opengl setting:
self.mobject.shader_wrapper_list = []
def begin(self) -> None:
pass
@ -628,9 +604,6 @@ class Wait(Animation):
def finish(self) -> None:
pass
def clean_up_from_scene(self, scene: Scene) -> None:
pass
def update_mobjects(self, dt: float) -> None:
pass
@ -760,9 +733,10 @@ def override_animation(
self.play(FadeIn(MySquare()))
"""
_F = TypeVar("_F", bound=Callable)
def decorator(func):
func._override_animation = animation_class
def decorator(func: _F) -> _F:
func._override_animation = animation_class # type: ignore[attr-defined]
return func
return decorator

View file

@ -4,12 +4,13 @@ from __future__ import annotations
__all__ = ["AnimatedBoundary", "TracedPath"]
from collections.abc import Callable, Sequence
from typing import Any, Self
from typing import TYPE_CHECKING, Any, Self
import numpy as np
from manim.mobject.mobject import Mobject
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.types.vectorized_mobject import VGroup, VMobject
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVGroup as VGroup
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject as VMobject
from manim.utils.color import (
BLUE_B,
BLUE_D,
@ -20,6 +21,11 @@ from manim.utils.color import (
)
from manim.utils.rate_functions import RateFunction, smooth
if TYPE_CHECKING:
from collections.abc import Callable, Sequence
import numpy.typing as npt
class AnimatedBoundary(VGroup):
"""Boundary of a :class:`.VMobject` with animated color change.
@ -62,7 +68,7 @@ class AnimatedBoundary(VGroup):
]
self.add(*self.boundary_copies)
self.total_time = 0.0
self.add_updater(lambda m, dt: self.update_boundary_copies(dt))
self.add_updater(lambda _, dt: self.update_boundary_copies(dt))
def update_boundary_copies(self, dt: float) -> None:
# Not actual time, but something which passes at
@ -102,7 +108,7 @@ class AnimatedBoundary(VGroup):
return self
class TracedPath(VMobject, metaclass=ConvertToOpenGL):
class TracedPath(VMobject):
"""Traces the path of a point returned by a function call.
Parameters
@ -146,22 +152,31 @@ class TracedPath(VMobject, metaclass=ConvertToOpenGL):
def __init__(
self,
traced_point_func: Callable,
traced_point_func: Callable[
[], npt.NDArray[npt.float]
], # TODO: Replace with Callable[[], Point3D]
stroke_width: float = 2,
stroke_color: ParsableManimColor | None = WHITE,
dissipating_time: float | None = None,
fill_opacity: float = 0.0,
**kwargs: Any,
) -> None:
super().__init__(stroke_color=stroke_color, stroke_width=stroke_width, **kwargs)
):
super().__init__(
stroke_color=stroke_color,
stroke_width=stroke_width,
fill_opacity=fill_opacity,
**kwargs,
)
self.traced_point_func = traced_point_func
self.dissipating_time = dissipating_time
self.time = 1.0 if self.dissipating_time else None
self.add_updater(self.update_path)
def update_path(self, mob: Mobject, dt: float) -> None:
def update_path(self, _mob: Mobject, dt: float) -> None:
new_point = self.traced_point_func()
if not self.has_points():
self.start_new_path(new_point)
if not np.allclose(self.get_end(), new_point):
self.add_line_to(new_point)
if self.dissipating_time:
assert self.time is not None

View file

@ -7,19 +7,19 @@ from typing import TYPE_CHECKING, Any
import numpy as np
from manim._config import config
from manim.animation.animation import Animation, prepare_animation
from manim.constants import RendererType
from manim.mobject.mobject import Group, Mobject
from manim.mobject.opengl.opengl_mobject import OpenGLGroup, OpenGLMobject
from manim.scene.scene import Scene
from manim.mobject.opengl.opengl_mobject import (
OpenGLGroup as Group,
)
from manim.mobject.opengl.opengl_mobject import (
OpenGLMobject as Mobject,
)
from manim.utils.iterables import remove_list_redundancies
from manim.utils.parameter_parsing import flatten_iterable_parameters
from manim.utils.rate_functions import linear
if TYPE_CHECKING:
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVGroup
from manim.mobject.types.vectorized_mobject import VGroup
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVGroup as VGroup
__all__ = ["AnimationGroup", "Succession", "LaggedStart", "LaggedStartMap"]
@ -54,24 +54,20 @@ class AnimationGroup(Animation):
def __init__(
self,
*animations: Animation | Iterable[Animation],
group: Group | VGroup | OpenGLGroup | OpenGLVGroup | None = None,
group: Group | VGroup | None = None,
run_time: float | None = None,
rate_func: Callable[[float], float] = linear,
lag_ratio: float = 0,
**kwargs: Any,
):
arg_anim = flatten_iterable_parameters(animations)
self.animations = [prepare_animation(anim) for anim in arg_anim]
self.rate_func = rate_func
if group is None:
mobjects = remove_list_redundancies(
[anim.mobject for anim in self.animations if not anim.is_introducer()],
[anim.mobject for anim in self.animations if not anim.introducer],
)
if config["renderer"] == RendererType.OPENGL:
self.group: Group | VGroup | OpenGLGroup | OpenGLVGroup = OpenGLGroup(
*mobjects
)
else:
self.group = Group(*mobjects)
else:
self.group = group
@ -80,7 +76,7 @@ class AnimationGroup(Animation):
)
self.run_time: float = self.init_run_time(run_time)
def get_all_mobjects(self) -> Sequence[Mobject | OpenGLMobject]:
def get_all_mobjects(self) -> Sequence[Mobject]:
return list(self.group)
def begin(self) -> None:
@ -89,30 +85,30 @@ class AnimationGroup(Animation):
f"Trying to play {self} without animations, this is not supported. "
"Please add at least one subanimation."
)
for anim in self.animations:
if self.introducer:
anim.introducer = True
anim.begin()
self.process_subanimation_buffer(anim.buffer)
self.anim_group_time = 0.0
if self.suspend_mobject_updating:
self.group.suspend_updating()
for anim in self.animations:
anim.begin()
def _setup_scene(self, scene: Scene) -> None:
for anim in self.animations:
anim._setup_scene(scene)
def finish(self) -> None:
for anim in self.animations:
anim.finish()
self.anims_begun[:] = True
self.anims_finished[:] = True
if self.suspend_mobject_updating:
self.group.resume_updating()
def clean_up_from_scene(self, scene: Scene) -> None:
self._on_finish(scene)
for anim in self.animations:
if self.remover:
anim.remover = self.remover
anim.clean_up_from_scene(scene)
anim.remover = True
anim.finish()
self.process_subanimation_buffer(anim.buffer)
if self.suspend_mobject_updating:
self.group.resume_updating()
def update_mobjects(self, dt: float) -> None:
for anim in self.anims_with_timings["anim"][
@ -251,16 +247,6 @@ class Succession(AnimationGroup):
if self.active_animation:
self.active_animation.update_mobjects(dt)
def _setup_scene(self, scene: Scene | None) -> None:
if scene is None:
return
if self.is_introducer():
for anim in self.animations:
if not anim.is_introducer() and anim.mobject is not None:
scene.add(anim.mobject)
self.scene = scene
def update_active_animation(self, index: int) -> None:
self.active_index = index
if index >= len(self.animations):
@ -269,8 +255,9 @@ class Succession(AnimationGroup):
self.active_end_time: float | None = None
else:
self.active_animation = self.animations[index]
self.active_animation._setup_scene(self.scene)
self.active_animation.begin()
self.process_subanimation_buffer(self.active_animation.buffer)
self.apply_buffer = True
self.active_start_time = self.anims_with_timings[index]["start"]
self.active_end_time = self.anims_with_timings[index]["end"]
@ -281,6 +268,7 @@ class Succession(AnimationGroup):
"""
if self.active_animation is not None:
self.active_animation.finish()
self.process_subanimation_buffer(self.active_animation.buffer)
self.update_active_animation(self.active_index + 1)
def interpolate(self, alpha: float) -> None:

View file

@ -82,19 +82,26 @@ from typing import TYPE_CHECKING
import numpy as np
if TYPE_CHECKING:
from typing import Any, Self
from manim.mobject.text.text_mobject import Text
from manim.scene.scene import Scene
from manim.constants import RIGHT, TAU
from manim.mobject.opengl.opengl_surface import OpenGLSurface
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject as VMobject
from manim.utils.color import ManimColor
from manim.utils.space_ops import rotate_vector
from .. import config
from ..animation.animation import Animation
from ..animation.composition import Succession
from ..mobject.mobject import Group, Mobject
from ..mobject.types.vectorized_mobject import VMobject
from ..mobject.opengl.opengl_mobject import (
OpenGLGroup as Group,
)
from ..mobject.opengl.opengl_mobject import (
OpenGLMobject as Mobject,
)
from ..utils.bezier import integer_interpolate
from ..utils.rate_functions import double_smooth, linear
@ -113,11 +120,7 @@ class ShowPartial(Animation):
"""
def __init__(
self,
mobject: VMobject | OpenGLVMobject | OpenGLSurface | None,
**kwargs,
):
def __init__(self, mobject: VMobject | OpenGLSurface | None, **kwargs: Any):
pointwise = getattr(mobject, "pointwise_become_partial", None)
if not callable(pointwise):
raise TypeError(f"{self.__class__.__name__} only works for VMobjects.")
@ -128,10 +131,11 @@ class ShowPartial(Animation):
submobject: Mobject,
starting_submobject: Mobject,
alpha: float,
) -> None:
) -> Self:
submobject.pointwise_become_partial(
starting_submobject, *self._get_bounds(alpha)
)
return self
def _get_bounds(self, alpha: float) -> tuple[float, float]:
raise NotImplementedError("Please use Create or ShowPassingFlash")
@ -166,7 +170,7 @@ class Create(ShowPartial):
def __init__(
self,
mobject: VMobject | OpenGLVMobject | OpenGLSurface,
mobject: VMobject | OpenGLSurface,
lag_ratio: float = 1.0,
introducer: bool = True,
**kwargs,
@ -196,7 +200,7 @@ class Uncreate(Create):
def __init__(
self,
mobject: VMobject | OpenGLVMobject,
mobject: VMobject,
reverse_rate_function: bool = True,
remover: bool = True,
**kwargs,
@ -224,11 +228,13 @@ class DrawBorderThenFill(Animation):
def __init__(
self,
vmobject: VMobject | OpenGLVMobject,
vmobject: VMobject,
run_time: float = 2,
rate_func: Callable[[float], float] = double_smooth,
stroke_width: float = 2,
stroke_color: str = None,
stroke_color: ManimColor | None = None,
draw_border_animation_config: dict = {}, # what does this dict accept?
fill_animation_config: dict = {},
introducer: bool = True,
**kwargs,
) -> None:
@ -244,13 +250,15 @@ class DrawBorderThenFill(Animation):
self.stroke_color = stroke_color
self.outline = self.get_outline()
def _typecheck_input(self, vmobject: VMobject | OpenGLVMobject) -> None:
if not isinstance(vmobject, (VMobject, OpenGLVMobject)):
def _typecheck_input(self, vmobject: VMobject) -> None:
if not isinstance(vmobject, VMobject):
raise TypeError(
f"{self.__class__.__name__} only works for vectorized Mobjects"
)
def begin(self) -> None:
# this self.get_outline() has to be called
# before super().begin(), for whatever reason
self.outline = self.get_outline()
super().begin()
@ -261,7 +269,7 @@ class DrawBorderThenFill(Animation):
sm.set_stroke(color=self.get_stroke_color(sm), width=self.stroke_width)
return outline
def get_stroke_color(self, vmobject: VMobject | OpenGLVMobject) -> ManimColor:
def get_stroke_color(self, vmobject: VMobject) -> ManimColor:
if self.stroke_color:
return self.stroke_color
elif vmobject.get_stroke_width() > 0:
@ -275,9 +283,9 @@ class DrawBorderThenFill(Animation):
self,
submobject: Mobject,
starting_submobject: Mobject,
outline,
outline: Mobject,
alpha: float,
) -> None: # Fixme: not matching the parent class? What is outline doing here?
) -> None:
index: int
subalpha: float
index, subalpha = integer_interpolate(0, 2, alpha)
@ -317,13 +325,13 @@ class Write(DrawBorderThenFill):
def __init__(
self,
vmobject: VMobject | OpenGLVMobject,
vmobject: VMobject,
rate_func: Callable[[float], float] = linear,
reverse: bool = False,
run_time: float | None = None,
lag_ratio: float | None = None,
**kwargs,
) -> None:
run_time: float | None = kwargs.pop("run_time", None)
lag_ratio: float | None = kwargs.pop("lag_ratio", None)
run_time, lag_ratio = self._set_default_config_from_length(
vmobject,
run_time,
@ -343,7 +351,7 @@ class Write(DrawBorderThenFill):
def _set_default_config_from_length(
self,
vmobject: VMobject | OpenGLVMobject,
vmobject: VMobject,
run_time: float | None,
lag_ratio: float | None,
) -> tuple[float, float]:
@ -354,18 +362,15 @@ class Write(DrawBorderThenFill):
lag_ratio = min(4.0 / max(1.0, length), 0.2)
return run_time, lag_ratio
def reverse_submobjects(self) -> None:
self.mobject.invert(recursive=True)
def begin(self) -> None:
if self.reverse:
self.reverse_submobjects()
self.mobject.reverse_submobjects(recursive=True)
super().begin()
def finish(self) -> None:
super().finish()
if self.reverse:
self.reverse_submobjects()
self.mobject.reverse_submobjects(recursive=True)
class Unwrite(Write):
@ -452,28 +457,33 @@ class SpiralIn(Animation):
self,
shapes: Mobject,
scale_factor: float = 8,
fade_in_fraction=0.3,
**kwargs,
fade_in_fraction: float = 0.3,
**kwargs: Any,
) -> None:
self.shapes = shapes.copy()
self.scale_factor = scale_factor
self.shape_center = shapes.get_center()
self.fade_in_fraction = fade_in_fraction
for shape in shapes:
shape.final_position = shape.get_center()
shape.initial_position = (
shape.final_position
+ (shape.final_position - self.shape_center) * self.scale_factor
)
shape.move_to(shape.initial_position)
shape.save_state()
self.final_positions = [shape.get_center() for shape in shapes]
self.initial_positions = [
final_pos + (final_pos - self.shape_center) * self.scale_factor
for final_pos in self.final_positions
]
super().__init__(shapes, introducer=True, **kwargs)
def interpolate_mobject(self, alpha: float) -> None:
def interpolate(self, alpha: float) -> None:
alpha = self.rate_func(alpha)
for original_shape, shape in zip(self.shapes, self.mobject, strict=True):
shape.restore()
for i, shape in enumerate(self.mobject):
initial_pos = self.initial_positions[i]
final_pos = self.final_positions[i]
# Avoid shape.rotate() in order to preserve the bounding box of the shape.
# Instead, rotate the vector itself to calculate the current shape position.
vector = initial_pos - self.shape_center + (final_pos - initial_pos) * alpha
vector = rotate_vector(vector, TAU * alpha)
shape.move_to(self.shape_center + vector)
original_shape = self.shapes[i]
fill_opacity = original_shape.get_fill_opacity()
stroke_opacity = original_shape.get_stroke_opacity()
new_fill_opacity = min(
@ -482,9 +492,6 @@ class SpiralIn(Animation):
new_stroke_opacity = min(
stroke_opacity, alpha * stroke_opacity / self.fade_in_fraction
)
shape.shift((shape.final_position - shape.initial_position) * alpha)
shape.rotate(TAU * alpha, about_point=self.shape_center)
shape.rotate(-TAU * alpha, about_point=shape.get_center_of_mass())
shape.set_fill(opacity=new_fill_opacity)
shape.set_stroke(opacity=new_stroke_opacity)
@ -524,7 +531,7 @@ class ShowIncreasingSubsets(Animation):
**kwargs,
)
def interpolate_mobject(self, alpha: float) -> None:
def interpolate(self, alpha: float) -> None:
n_submobs = len(self.all_submobs)
value = (
1 - self.rate_func(alpha)

View file

@ -23,12 +23,15 @@ from typing import Any
import numpy as np
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
from manim.mobject.opengl.opengl_mobject import (
OpenGLGroup as Group,
)
from manim.mobject.opengl.opengl_mobject import (
OpenGLMobject as Mobject,
)
from ..animation.transform import Transform
from ..constants import ORIGIN
from ..mobject.mobject import Group, Mobject
from ..scene.scene import Scene
class _Fade(Transform):
@ -64,7 +67,7 @@ class _Fade(Transform):
self.point_target = False
if shift is None:
if target_position is not None:
if isinstance(target_position, (Mobject, OpenGLMobject)):
if isinstance(target_position, Mobject):
target_position = target_position.get_center()
shift = target_position - mobject.get_center()
self.point_target = True
@ -74,12 +77,12 @@ class _Fade(Transform):
self.scale_factor = scale
super().__init__(mobject, **kwargs)
def _create_faded_mobject(self, fadeIn: bool) -> Mobject:
def _create_faded_mobject(self, fade_in: bool) -> Mobject:
"""Create a faded, shifted and scaled copy of the mobject.
Parameters
----------
fadeIn
fade_in
Whether the faded mobject is used to fade in.
Returns
@ -89,7 +92,7 @@ class _Fade(Transform):
"""
faded_mobject: Mobject = self.mobject.copy() # type: ignore[assignment]
faded_mobject.fade(1)
direction_modifier = -1 if fadeIn and not self.point_target else 1
direction_modifier = -1 if fade_in and not self.point_target else 1
faded_mobject.shift(self.shift_vector * direction_modifier)
faded_mobject.scale(self.scale_factor)
return faded_mobject
@ -137,10 +140,10 @@ class FadeIn(_Fade):
super().__init__(*mobjects, introducer=True, **kwargs)
def create_target(self) -> Mobject:
return self.mobject # type: ignore[return-value]
return self.mobject
def create_starting_mobject(self) -> Mobject:
return self._create_faded_mobject(fadeIn=True)
return self._create_faded_mobject(fade_in=True)
class FadeOut(_Fade):
@ -185,8 +188,8 @@ class FadeOut(_Fade):
super().__init__(*mobjects, remover=True, **kwargs)
def create_target(self) -> Mobject:
return self._create_faded_mobject(fadeIn=False)
return self._create_faded_mobject(fade_in=False)
def clean_up_from_scene(self, scene: Scene) -> None:
super().clean_up_from_scene(scene)
def begin(self) -> None:
super().begin()
self.interpolate(0)

View file

@ -39,12 +39,10 @@ from ..utils.paths import spiral_path
if TYPE_CHECKING:
from manim.mobject.geometry.line import Arrow
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
from manim.mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
from manim.typing import Point3DLike, Vector3DLike
from manim.utils.color import ParsableManimColor
from ..mobject.mobject import Mobject
class GrowFromPoint(Transform):
"""Introduce an :class:`~.Mobject` by growing it from a point.
@ -87,10 +85,10 @@ class GrowFromPoint(Transform):
self.point_color = point_color
super().__init__(mobject, introducer=True, **kwargs)
def create_target(self) -> Mobject | OpenGLMobject:
def create_target(self) -> Mobject:
return self.mobject
def create_starting_mobject(self) -> Mobject | OpenGLMobject:
def create_starting_mobject(self) -> Mobject:
start = super().create_starting_mobject()
start.scale(0)
start.move_to(self.point)
@ -169,7 +167,7 @@ class GrowFromEdge(GrowFromPoint):
point_color: ParsableManimColor | None = None,
**kwargs: Any,
):
point = mobject.get_critical_point(edge)
point = mobject.get_bounding_box_point(edge)
super().__init__(mobject, point, point_color=point_color, **kwargs)
@ -203,7 +201,7 @@ class GrowArrow(GrowFromPoint):
point = arrow.get_start()
super().__init__(arrow, point, point_color=point_color, **kwargs)
def create_starting_mobject(self) -> Mobject | OpenGLMobject:
def create_starting_mobject(self) -> Mobject:
start_arrow = self.mobject.copy()
start_arrow.scale(0, scale_tips=True, about_point=self.point)
if self.point_color:

View file

@ -48,8 +48,7 @@ from manim.mobject.geometry.arc import Circle, Dot
from manim.mobject.geometry.line import Line
from manim.mobject.geometry.polygram import Rectangle
from manim.mobject.geometry.shape_matchers import SurroundingRectangle
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
from manim.scene.scene import Scene
from manim.mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
from .. import config
from ..animation.animation import Animation
@ -60,8 +59,12 @@ from ..animation.movement import Homotopy
from ..animation.transform import Transform
from ..animation.updaters.update import UpdateFromFunc
from ..constants import *
from ..mobject.mobject import Mobject
from ..mobject.types.vectorized_mobject import VGroup, VMobject
from ..mobject.opengl.opengl_vectorized_mobject import (
OpenGLVGroup as VGroup,
)
from ..mobject.opengl.opengl_vectorized_mobject import (
OpenGLVMobject as VMobject,
)
from ..typing import Point3D, Point3DLike, Vector3DLike
from ..utils.bezier import interpolate, inverse_interpolate
from ..utils.color import GREY, PURE_YELLOW, ParsableManimColor
@ -161,7 +164,7 @@ class Indicate(Transform):
self.scale_factor = scale_factor
super().__init__(mobject, rate_func=rate_func, **kwargs)
def create_target(self) -> Mobject | OpenGLMobject:
def create_target(self) -> Mobject:
target = self.mobject.copy()
target.scale(self.scale_factor)
target.set_color(self.color)
@ -319,8 +322,8 @@ class ShowPassingFlash(ShowPartial):
lower = max(lower, 0)
return (lower, upper)
def clean_up_from_scene(self, scene: Scene) -> None:
super().clean_up_from_scene(scene)
def finish(self) -> None:
super().finish()
for submob, start in self.get_all_families_zipped():
submob.pointwise_become_partial(start, 0, 1)
@ -407,6 +410,7 @@ class ApplyWave(Homotopy):
time_width: float = 1,
ripples: int = 1,
run_time: float = 2,
introducer: bool = True,
**kwargs: Any,
):
x_min = mobject.get_left()[0]
@ -482,7 +486,9 @@ class ApplyWave(Homotopy):
return_value: tuple[float, float, float] = np.array([x, y, z]) + nudge
return return_value
super().__init__(homotopy, mobject, run_time=run_time, **kwargs)
super().__init__(
homotopy, mobject, run_time=run_time, introducer=introducer, **kwargs
)
class Wiggle(Animation):
@ -568,6 +574,7 @@ class Wiggle(Animation):
return self
# TODO: get rid of this if condition madness
class Circumscribe(Succession):
r"""Draw a temporary line surrounding the mobject.

View file

@ -82,9 +82,7 @@ class Homotopy(Animation):
**kwargs: Any,
):
self.homotopy = homotopy
self.apply_function_kwargs = (
apply_function_kwargs if apply_function_kwargs is not None else {}
)
self.apply_function_kwargs = apply_function_kwargs or {}
super().__init__(mobject, run_time=run_time, **kwargs)
def function_at_time_t(self, t: float) -> MappingFunction:
@ -100,7 +98,7 @@ class Homotopy(Animation):
starting_submobject: Mobject,
alpha: float,
) -> Self:
submobject.points = starting_submobject.points
submobject.match_points(starting_submobject)
submobject.apply_function(
self.function_at_time_t(alpha),
**self.apply_function_kwargs,
@ -161,7 +159,7 @@ class PhaseFlow(Animation):
**kwargs,
)
def interpolate_mobject(self, alpha: float) -> None:
def interpolate(self, alpha: float) -> None:
if hasattr(self, "last_alpha"):
dt = self.virtual_time * (
self.rate_func(alpha) - self.rate_func(self.last_alpha)
@ -197,6 +195,6 @@ class MoveAlongPath(Animation):
mobject, suspend_mobject_updating=suspend_mobject_updating, **kwargs
)
def interpolate_mobject(self, alpha: float) -> None:
def interpolate(self, alpha: float) -> None:
point = self.path.point_from_proportion(self.rate_func(alpha))
self.mobject.move_to(point)

View file

@ -67,7 +67,7 @@ class ChangingDecimal(Animation):
if not isinstance(decimal_mob, DecimalNumber):
raise TypeError("ChangingDecimal can only take in a DecimalNumber")
def interpolate_mobject(self, alpha: float) -> None:
def interpolate(self, alpha: float) -> None:
self.mobject.set_value(self.number_update_func(self.rate_func(alpha))) # type: ignore[attr-defined]

View file

@ -0,0 +1,82 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Protocol
from typing_extensions import TypeVar
if TYPE_CHECKING:
from manim.mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
from manim.utils.rate_functions import RateFunction
from .scene_buffer import SceneBuffer
M = TypeVar("M", bound="Mobject", default="Mobject")
__all__ = ("AnimationProtocol",)
class AnimationProtocol(Protocol):
"""A protocol that all animations must implement."""
buffer: SceneBuffer
"""The interface to the scene. This can be used to add, remove, or replace mobjects on the scene."""
apply_buffer: bool
"""Normally, the buffer is only applied at the beginning and end of an animation.
To apply it mid animation, set :attr:`apply_buffer` to ``True``."""
def begin(self) -> object:
"""Called before the animation starts.
This is where all setup for the animation should be done, such
as creating copies/targets of the mobject to animate, etc.
"""
def finish(self) -> object:
"""Called after the animation finishes.
This is where all cleanup should happen, such as removing
mobjects from the scene, etc.
"""
def interpolate(self, alpha: float) -> object:
"""This is called every frame of the animation.
This method should update the animation to the given ``alpha`` value.
Parameters
----------
alpha : a value in the interval :math:`[0, 1]` representing the proportion of the animation that has passed.
"""
def get_run_time(self) -> float:
"""Compute and return the run time of the animation."""
raise NotImplementedError
def update_rate_info(
self,
run_time: float | None,
rate_func: RateFunction | None,
lag_ratio: float | None,
) -> object:
"""Update the rate information for the animation.
If any value is ``None``, it should not update
the animation's corresponding attribute.
"""
def update_mobjects(self, dt: float) -> object:
"""Update the mobjects during the animation.
This method is called every frame of the animation
"""
class MobjectAnimation(AnimationProtocol, Protocol[M]):
mobject: M
"""The mobject that is being animated."""
suspend_mobject_updating: bool
"""Whether to suspend updating the mobject during the animation."""

View file

@ -4,18 +4,16 @@ from __future__ import annotations
__all__ = ["Rotating", "Rotate"]
from collections.abc import Callable
from typing import TYPE_CHECKING, Any
from ..animation.animation import Animation
from ..animation.transform import Transform
from ..constants import OUT, PI, TAU
from ..utils.rate_functions import linear
from manim.animation.animation import Animation
from manim.constants import ORIGIN, OUT, PI, TAU
from manim.utils.rate_functions import RateFunction, linear
if TYPE_CHECKING:
from ..mobject.mobject import Mobject
from ..mobject.opengl.opengl_mobject import OpenGLMobject
from ..typing import Point3DLike, Vector3DLike
from manim.mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
from manim.typing import Point3DLike, Vector3DLike
from manim.utils.rate_functions import RateFunction
class Rotating(Animation):
@ -92,18 +90,30 @@ class Rotating(Animation):
axis: Vector3DLike = OUT,
about_point: Point3DLike | None = None,
about_edge: Vector3DLike | None = None,
run_time: float = 5,
rate_func: Callable[[float], float] = linear,
rate_func: RateFunction = linear,
suspend_mobject_updating: bool = False,
**kwargs: Any,
) -> None:
):
super().__init__(
mobject,
rate_func=rate_func,
suspend_mobject_updating=suspend_mobject_updating,
**kwargs,
)
self.angle = angle
self.axis = axis
self.about_point = about_point
self.about_edge = about_edge
super().__init__(mobject, run_time=run_time, rate_func=rate_func, **kwargs)
def interpolate_mobject(self, alpha: float) -> None:
self.mobject.become(self.starting_mobject)
def interpolate(self, alpha: float) -> None:
pairs = zip(
self.mobject.family_members_with_points(),
self.starting_mobject.family_members_with_points(),
strict=True,
)
for sm1, sm2 in pairs:
sm1.points[:] = sm2.points
self.mobject.rotate(
self.rate_func(alpha) * self.angle,
axis=self.axis,
@ -112,7 +122,7 @@ class Rotating(Animation):
)
class Rotate(Transform):
class Rotate(Rotating):
"""Animation that rotates a Mobject.
Parameters
@ -157,28 +167,16 @@ class Rotate(Transform):
mobject: Mobject,
angle: float = PI,
axis: Vector3DLike = OUT,
about_point: Point3DLike | None = None,
about_edge: Vector3DLike | None = None,
run_time: float = 1,
about_edge: Vector3DLike = ORIGIN,
**kwargs: Any,
) -> None:
if "path_arc" not in kwargs:
kwargs["path_arc"] = angle
if "path_arc_axis" not in kwargs:
kwargs["path_arc_axis"] = axis
self.angle = angle
self.axis = axis
self.about_edge = about_edge
self.about_point = about_point
if self.about_point is None:
self.about_point = mobject.get_center()
super().__init__(mobject, path_arc_centers=self.about_point, **kwargs)
def create_target(self) -> Mobject | OpenGLMobject:
target = self.mobject.copy()
target.rotate(
self.angle,
axis=self.axis,
about_point=self.about_point,
about_edge=self.about_edge,
):
super().__init__(
mobject,
angle,
axis,
run_time=run_time,
about_edge=about_edge,
introducer=True,
**kwargs,
)
return target

View file

@ -0,0 +1,79 @@
from __future__ import annotations
from collections.abc import Iterator, Sequence
from enum import Enum
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from manim.mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
__all__ = ["SceneBuffer", "SceneOperation"]
class SceneOperation(Enum):
ADD = "add"
REMOVE = "remove"
REPLACE = "replace"
class SceneBuffer:
"""
A "buffer" between :class:`.Scene` and :class:`.Animation`
Operations an animation wants to do on :class:`.Scene` should be
done here (eg. :meth:`.Scene.add`, :meth:`.Scene.remove`). The
scene will then apply these changes at specific points (namely
at the beginning and end of animations)
It is the scenes job to clear the buffer in between the beginning
and end of animations.
To iterate over the operations, simply iterate over the buffer.
Example
-------
.. code-block:: pycon
>>> buffer = SceneBuffer()
>>> buffer.add(Square())
>>> buffer.remove(Circle())
>>> buffer.replace(Square(), Circle(), flag=True)
>>> for operation in buffer:
... print(operation)
(SceneOperation.ADD, (Square(),), {})
(SceneOperation.REMOVE, (Circle(),), {})
(SceneOperation.REPLACE, (Square(), Circle()), {"flag": True})
"""
def __init__(self) -> None:
self.operations: list[
tuple[SceneOperation, Sequence[Mobject], dict[str, Any]]
] = []
def add(self, *mobs: Mobject, **kwargs: Any) -> None:
"""Add mobjects to the scene."""
self.operations.append((SceneOperation.ADD, mobs, kwargs))
def remove(self, *mobs: Mobject, **kwargs: Any) -> None:
"""Remove mobjects from the scene."""
self.operations.append((SceneOperation.REMOVE, mobs, kwargs))
def replace(self, mob: Mobject, *replacements: Mobject, **kwargs: Any) -> None:
"""Replace a ``mob`` with ``replacements`` on the scene."""
self.operations.append((SceneOperation.REPLACE, (mob, *replacements), kwargs))
def clear(self) -> None:
"""Clear the buffer."""
self.operations.clear()
def __str__(self) -> str:
operations = self.operations
return f"{type(self).__name__}({operations=})"
__repr__ = __str__
def __iter__(
self,
) -> Iterator[tuple[SceneOperation, Sequence[Mobject], dict[str, Any]]]:
return iter(self.operations)

View file

@ -12,10 +12,10 @@ from numpy import piecewise
from ..animation.animation import Animation, Wait, prepare_animation
from ..animation.composition import AnimationGroup
from ..mobject.mobject import Mobject, _AnimationBuilder
from ..scene.scene import Scene
if TYPE_CHECKING:
from ..mobject.mobject import Updater
from .protocol import MobjectAnimation
__all__ = ["ChangeSpeed"]
@ -102,7 +102,7 @@ class ChangeSpeed(Animation):
affects_speed_updaters: bool = True,
**kwargs,
) -> None:
if issubclass(type(anim), AnimationGroup):
if isinstance(anim, AnimationGroup):
self.anim = type(anim)(
*map(self.setup, anim.animations),
group=anim.group,
@ -209,11 +209,11 @@ class ChangeSpeed(Animation):
super().__init__(
self.anim.mobject,
rate_func=self.rate_func,
run_time=scaled_total_time * self.anim.run_time,
run_time=scaled_total_time * self.anim.get_run_time(),
**kwargs,
)
def setup(self, anim):
def setup(self, anim: MobjectAnimation):
if type(anim) is Wait:
anim.interpolate = types.MethodType(
lambda self, alpha: self.rate_func(alpha), anim
@ -282,15 +282,11 @@ class ChangeSpeed(Animation):
def update_mobjects(self, dt: float) -> None:
self.anim.update_mobjects(dt)
def begin(self) -> None:
self.anim.begin()
self.process_subanimation_buffer(self.anim.buffer)
def finish(self) -> None:
ChangeSpeed.is_changing_dt = False
self.anim.finish()
def begin(self) -> None:
self.anim.begin()
def clean_up_from_scene(self, scene: Scene) -> None:
self.anim.clean_up_from_scene(scene)
def _setup_scene(self, scene) -> None:
self.anim._setup_scene(scene)
self.process_subanimation_buffer(self.anim.buffer)

View file

@ -2,6 +2,8 @@
from __future__ import annotations
from manim.typing import PathFuncType
__all__ = [
"Transform",
"ReplacementTransform",
@ -28,30 +30,31 @@ __all__ = [
import inspect
import types
from collections.abc import Callable, Iterable, Sequence
from collections.abc import Callable, Sequence
from typing import TYPE_CHECKING, Any
import numpy as np
from manim.data_structures import MethodWithArgs
from manim.mobject.opengl.opengl_mobject import OpenGLGroup, OpenGLMobject
from manim.mobject.opengl.opengl_mobject import (
OpenGLGroup as Group,
)
from manim.mobject.opengl.opengl_mobject import (
OpenGLMobject as Mobject,
)
from .. import config
from ..animation.animation import Animation
from ..constants import (
DEFAULT_POINTWISE_FUNCTION_RUN_TIME,
DEGREES,
ORIGIN,
OUT,
RendererType,
)
from ..mobject.mobject import Group, Mobject
from ..utils.paths import path_along_arc, path_along_circles
from ..utils.rate_functions import smooth, squish_rate_func
if TYPE_CHECKING:
from ..scene.scene import Scene
from ..typing import Point3DLike, Point3DLike_Array
from manim.typing import Point3DLike, Point3DLike_Array
class Transform(Animation):
@ -179,46 +182,33 @@ class Transform(Animation):
@property
def path_func(
self,
) -> Callable[
[Iterable[np.ndarray], Iterable[np.ndarray], float],
Iterable[np.ndarray],
]:
) -> PathFuncType:
return self._path_func
@path_func.setter
def path_func(
self,
path_func: Callable[
[Iterable[np.ndarray], Iterable[np.ndarray], float],
Iterable[np.ndarray],
],
path_func: PathFuncType,
) -> None:
if path_func is not None:
self._path_func = path_func
def begin(self) -> None:
# Use a copy of target_mobject for the align_data
# call so that the actual target_mobject stays
# preserved.
self.target_mobject = self.create_target()
self.target_copy = self.target_mobject.copy()
# Note, this potentially changes the structure
# of both mobject and target_mobject
if config.renderer == RendererType.OPENGL:
self.mobject.align_data_and_family(self.target_copy)
else:
self.mobject.align_data(self.target_copy)
super().begin()
def create_target(self) -> Mobject | OpenGLMobject:
def create_target(self) -> Mobject:
# Has no meaningful effect here, but may be useful
# in subclasses
return self.target_mobject
def clean_up_from_scene(self, scene: Scene) -> None:
super().clean_up_from_scene(scene)
def finish(self) -> None:
super().finish()
if self.replace_mobject_with_target_in_scene:
scene.replace(self.mobject, self.target_mobject)
self.buffer.replace(self.mobject, self.target_mobject)
def get_all_mobjects(self) -> Sequence[Mobject]:
return [
@ -228,15 +218,13 @@ class Transform(Animation):
self.target_copy,
]
def get_all_families_zipped(self) -> Iterable[tuple]: # more precise typing?
def get_all_families_zipped(self) -> zip[tuple[Mobject, Mobject, Mobject]]:
mobs = [
self.mobject,
self.starting_mobject,
self.target_copy,
]
if config.renderer == RendererType.OPENGL:
return zip(*(mob.get_family() for mob in mobs), strict=True)
return zip(*(mob.family_members_with_points() for mob in mobs), strict=True)
def interpolate_submobject(
self,
@ -483,7 +471,7 @@ class ApplyMethod(Transform):
"Whoops, looks like you accidentally invoked "
"the method you want to animate",
)
assert isinstance(method.__self__, (Mobject, OpenGLMobject))
assert isinstance(method.__self__, Mobject)
def create_target(self) -> Mobject:
method = self.method
@ -627,7 +615,7 @@ class ApplyFunction(Transform):
def create_target(self) -> Any:
target = self.function(self.mobject.copy())
if not isinstance(target, (Mobject, OpenGLMobject)):
if not isinstance(target, Mobject):
raise TypeError(
"Functions passed to ApplyFunction must return object of type Mobject",
)
@ -692,6 +680,7 @@ class ApplyComplexFunction(ApplyMethod):
super().__init__(method, function, **kwargs)
def _init_path_func(self) -> None:
# TODO: this seems broken?
func1 = self.function(complex(1))
self.path_arc = np.log(func1).imag
super()._init_path_func()
@ -840,9 +829,6 @@ class FadeTransform(Transform):
self.stretch = stretch
self.dim_to_match = dim_to_match
mobject.save_state()
if config.renderer == RendererType.OPENGL:
group = OpenGLGroup(mobject, target_mobject.copy())
else:
group = Group(mobject, target_mobject.copy())
super().__init__(group, **kwargs)
@ -884,11 +870,11 @@ class FadeTransform(Transform):
def get_all_families_zipped(self):
return Animation.get_all_families_zipped(self)
def clean_up_from_scene(self, scene):
Animation.clean_up_from_scene(self, scene)
scene.remove(self.mobject)
def finish(self):
Animation.finish(self) # TODO: is this really needed over super()?
self.buffer.remove(self.mobject)
self.mobject[0].restore()
scene.add(self.to_add_on_completion)
self.buffer.add(self.to_add_on_completion)
class FadeTransformPieces(FadeTransform):

View file

@ -4,24 +4,28 @@ from __future__ import annotations
__all__ = ["TransformMatchingShapes", "TransformMatchingTex"]
from typing import TYPE_CHECKING
import numpy as np
from manim.mobject.opengl.opengl_mobject import OpenGLGroup, OpenGLMobject
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVGroup, OpenGLVMobject
from manim.mobject.opengl.opengl_mobject import (
OpenGLGroup as Group,
)
from manim.mobject.opengl.opengl_mobject import (
OpenGLMobject as Mobject,
)
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVGroup as VGroup,
)
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVMobject as VMobject,
)
from .._config import config
from ..constants import RendererType
from ..mobject.mobject import Group, Mobject
from ..mobject.types.vectorized_mobject import VGroup, VMobject
from .composition import AnimationGroup
from .fading import FadeIn, FadeOut
from .transform import FadeTransformPieces, Transform
if TYPE_CHECKING:
from ..scene.scene import Scene
class TransformMatchingAbstractBase(AnimationGroup):
"""Abstract base class for transformations that keep track of matching parts.
@ -76,14 +80,7 @@ class TransformMatchingAbstractBase(AnimationGroup):
key_map: dict | None = None,
**kwargs,
):
if isinstance(mobject, OpenGLVMobject):
group_type = OpenGLVGroup
elif isinstance(mobject, OpenGLMobject):
group_type = OpenGLGroup
elif isinstance(mobject, VMobject):
group_type = VGroup
else:
group_type = Group
group_type = VGroup if isinstance(mobject, VMobject) else Group
source_map = self.get_shape_map(mobject)
target_map = self.get_shape_map(target_mobject)
@ -146,19 +143,20 @@ class TransformMatchingAbstractBase(AnimationGroup):
key = self.get_mobject_key(sm)
if key not in shape_map:
if config["renderer"] == RendererType.OPENGL:
shape_map[key] = OpenGLVGroup()
shape_map[key] = VGroup()
else:
shape_map[key] = VGroup()
shape_map[key].add(sm)
return shape_map
def clean_up_from_scene(self, scene: Scene) -> None:
def finish(self) -> None:
super().finish()
# Interpolate all animations back to 0 to ensure source mobjects remain unchanged.
for anim in self.animations:
anim.interpolate(0)
scene.remove(self.mobject)
scene.remove(*self.to_remove)
scene.add(self.to_add)
self.buffer.remove(self.mobject)
self.buffer.remove(*self.to_remove)
self.buffer.add(self.to_add)
@staticmethod
def get_mobject_parts(mobject: Mobject):
@ -282,7 +280,7 @@ class TransformMatchingTex(TransformMatchingAbstractBase):
@staticmethod
def get_mobject_parts(mobject: Mobject) -> list[Mobject]:
if isinstance(mobject, (Group, VGroup, OpenGLGroup, OpenGLVGroup)):
if isinstance(mobject, (Group, VGroup)):
return [
p
for s in mobject.submobjects

View file

@ -3,12 +3,9 @@
from __future__ import annotations
__all__ = [
"assert_is_mobject_method",
"always",
"f_always",
"always_redraw",
"always_shift",
"always_rotate",
"turn_animation_into_updater",
"cycle_animation",
]
@ -16,41 +13,56 @@ __all__ = [
import inspect
from collections.abc import Callable
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any, TypeVar, cast
import numpy as np
from manim.constants import DEGREES, RIGHT
from manim.mobject.mobject import Mobject
from manim.opengl import OpenGLMobject
from manim.utils.space_ops import normalize
from manim.mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
if TYPE_CHECKING:
from manim.animation.animation import Animation
import types
from typing import Concatenate
from typing_extensions import ParamSpec, TypeIs
from manim.animation.protocol import MobjectAnimation
P = ParamSpec("P")
def assert_is_mobject_method(method: Callable) -> None:
assert inspect.ismethod(method)
mobject = method.__self__
assert isinstance(mobject, (Mobject, OpenGLMobject))
M = TypeVar("M", bound=Mobject)
def always(method: Callable, *args, **kwargs) -> Mobject:
assert_is_mobject_method(method)
mobject = method.__self__
# TODO: figure out how to typehint as MethodType[Mobject] to avoid the cast
# madness in always/f_always
def is_mobject_method(method: Callable[..., Any]) -> TypeIs[types.MethodType]:
return inspect.ismethod(method) and isinstance(method.__self__, Mobject)
def always(
method: Callable[Concatenate[M, P], object], *args: P.args, **kwargs: P.kwargs
) -> M:
if not is_mobject_method(method):
raise ValueError("always must take a method of a Mobject")
mobject = cast(M, method.__self__)
func = method.__func__
mobject.add_updater(lambda m: func(m, *args, **kwargs))
return mobject
def f_always(method: Callable[[Mobject], None], *arg_generators, **kwargs) -> Mobject:
def f_always(
method: Callable[Concatenate[M, ...], None],
*arg_generators: Callable[[], object],
**kwargs,
) -> M:
"""
More functional version of always, where instead
of taking in args, it takes in functions which output
the relevant arguments.
"""
assert_is_mobject_method(method)
mobject = method.__self__
if not is_mobject_method(method):
raise ValueError("f_always must take a method of a Mobject")
mobject = cast(M, method.__self__)
func = method.__func__
def updater(mob):
@ -61,7 +73,7 @@ def f_always(method: Callable[[Mobject], None], *arg_generators, **kwargs) -> Mo
return mobject
def always_redraw(func: Callable[[], Mobject]) -> Mobject:
def always_redraw(func: Callable[[], M]) -> M:
"""Redraw the mobject constructed by a function every frame.
This function returns a mobject with an attached updater that
@ -76,7 +88,6 @@ def always_redraw(func: Callable[[], Mobject]) -> Mobject:
Examples
--------
.. manim:: TangentAnimation
class TangentAnimation(Scene):
@ -106,81 +117,11 @@ def always_redraw(func: Callable[[], Mobject]) -> Mobject:
return mob
def always_shift(
mobject: Mobject, direction: np.ndarray[np.float64] = RIGHT, rate: float = 0.1
) -> Mobject:
"""A mobject which is continuously shifted along some direction
at a certain rate.
Parameters
----------
mobject
The mobject to shift.
direction
The direction to shift. The vector is normalized, the specified magnitude
is not relevant.
rate
Length in Manim units which the mobject travels in one
second along the specified direction.
Examples
--------
.. manim:: ShiftingSquare
class ShiftingSquare(Scene):
def construct(self):
sq = Square().set_fill(opacity=1)
tri = Triangle()
VGroup(sq, tri).arrange(LEFT)
# construct a square which is continuously
# shifted to the right
always_shift(sq, RIGHT, rate=5)
self.add(sq)
self.play(tri.animate.set_fill(opacity=1))
"""
mobject.add_updater(lambda m, dt: m.shift(dt * rate * normalize(direction)))
return mobject
def always_rotate(mobject: Mobject, rate: float = 20 * DEGREES, **kwargs) -> Mobject:
"""A mobject which is continuously rotated at a certain rate.
Parameters
----------
mobject
The mobject to be rotated.
rate
The angle which the mobject is rotated by
over one second.
kwags
Further arguments to be passed to :meth:`.Mobject.rotate`.
Examples
--------
.. manim:: SpinningTriangle
class SpinningTriangle(Scene):
def construct(self):
tri = Triangle().set_fill(opacity=1).set_z_index(2)
sq = Square().to_edge(LEFT)
# will keep spinning while there is an animation going on
always_rotate(tri, rate=2*PI, about_point=ORIGIN)
self.add(tri, sq)
self.play(sq.animate.to_edge(RIGHT), rate_func=linear, run_time=1)
"""
mobject.add_updater(lambda m, dt: m.rotate(dt * rate, **kwargs))
return mobject
def turn_animation_into_updater(
animation: Animation, cycle: bool = False, delay: float = 0, **kwargs
) -> Mobject:
animation: MobjectAnimation[M],
cycle: bool = False,
delay: float = 0,
) -> M:
"""
Add an updater to the animation's mobject which applies
the interpolation and update functions of the animation
@ -209,10 +150,12 @@ def turn_animation_into_updater(
mobject = animation.mobject
animation.suspend_mobject_updating = False
animation.begin()
animation.total_time = -delay
def update(m: Mobject, dt: float):
if animation.total_time >= 0:
total_time = -delay
def update(m: M, dt: float):
nonlocal total_time
if total_time >= 0:
run_time = animation.get_run_time()
# handle zero/negative runtime safely
@ -224,7 +167,7 @@ def turn_animation_into_updater(
m.remove_updater(update)
return
time_ratio = animation.total_time / run_time
time_ratio = total_time / run_time
if cycle:
alpha = time_ratio % 1
else:
@ -235,11 +178,11 @@ def turn_animation_into_updater(
return
animation.interpolate(alpha)
animation.update_mobjects(dt)
animation.total_time += dt
total_time += dt
mobject.add_updater(update)
return mobject
def cycle_animation(animation: Animation, **kwargs) -> Mobject:
def cycle_animation(animation: MobjectAnimation[M], **kwargs) -> M:
return turn_animation_into_updater(animation, cycle=True, **kwargs)

View file

@ -12,7 +12,7 @@ from typing import TYPE_CHECKING, Any
from manim.animation.animation import Animation
if TYPE_CHECKING:
from manim.mobject.mobject import Mobject
from manim.mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
class UpdateFromFunc(Animation):
@ -25,22 +25,22 @@ class UpdateFromFunc(Animation):
def __init__(
self,
mobject: Mobject,
update_function: Callable[[Mobject], Any],
update_function: Callable[[Mobject], object],
suspend_mobject_updating: bool = False,
**kwargs: Any,
) -> None:
self.update_function = update_function
super().__init__(
mobject, suspend_mobject_updating=suspend_mobject_updating, **kwargs
)
self.update_function = update_function
def interpolate_mobject(self, alpha: float) -> None:
self.update_function(self.mobject) # type: ignore[arg-type]
def interpolate(self, alpha: float) -> None:
self.update_function(self.mobject)
class UpdateFromAlphaFunc(UpdateFromFunc):
def interpolate_mobject(self, alpha: float) -> None:
self.update_function(self.mobject, self.rate_func(alpha)) # type: ignore[call-arg, arg-type]
def interpolate(self, alpha: float) -> None:
self.update_function(self.mobject, self.rate_func(alpha)) # type: ignore[call-arg]
class MaintainPositionRelativeTo(Animation):
@ -54,7 +54,7 @@ class MaintainPositionRelativeTo(Animation):
)
super().__init__(mobject, **kwargs)
def interpolate_mobject(self, alpha: float) -> None:
def interpolate(self, alpha: float) -> None:
target = self.tracked_mobject.get_center()
location = self.mobject.get_center()
self.mobject.shift(target - location + self.diff)

File diff suppressed because it is too large Load diff

View file

@ -1,170 +0,0 @@
"""A camera module that supports spatial mapping between objects for distortion effects."""
from __future__ import annotations
__all__ = ["MappingCamera", "OldMultiCamera", "SplitScreenCamera"]
import math
import numpy as np
from ..camera.camera import Camera
from ..mobject.types.vectorized_mobject import VMobject
from ..utils.config_ops import DictAsObject
# TODO: Add an attribute to mobjects under which they can specify that they should just
# map their centers but remain otherwise undistorted (useful for labels, etc.)
class MappingCamera(Camera):
"""Parameters
----------
mapping_func : callable
Function to map 3D points to new 3D points (identity by default).
min_num_curves : int
Minimum number of curves for VMobjects to avoid visual glitches.
allow_object_intrusion : bool
If True, modifies original mobjects; else works on copies.
kwargs : dict
Additional arguments passed to Camera base class.
"""
def __init__(
self,
mapping_func=lambda p: p,
min_num_curves=50,
allow_object_intrusion=False,
**kwargs,
):
self.mapping_func = mapping_func
self.min_num_curves = min_num_curves
self.allow_object_intrusion = allow_object_intrusion
super().__init__(**kwargs)
def points_to_pixel_coords(self, mobject, points):
# Map points with custom function before converting to pixels
return super().points_to_pixel_coords(
mobject,
np.apply_along_axis(self.mapping_func, 1, points),
)
def capture_mobjects(self, mobjects, **kwargs):
"""Capture mobjects for rendering after applying the spatial mapping.
Copies mobjects unless intrusion is allowed, and ensures
vector objects have enough curves for smooth distortion.
"""
mobjects = self.get_mobjects_to_display(mobjects, **kwargs)
if self.allow_object_intrusion:
mobject_copies = mobjects
else:
mobject_copies = [mobject.copy() for mobject in mobjects]
for mobject in mobject_copies:
if (
isinstance(mobject, VMobject)
and 0 < mobject.get_num_curves() < self.min_num_curves
):
mobject.insert_n_curves(self.min_num_curves)
super().capture_mobjects(
mobject_copies,
include_submobjects=False,
excluded_mobjects=None,
)
# Note: This allows layering of multiple cameras onto the same portion of the pixel array,
# the later cameras overwriting the former
#
# TODO: Add optional separator borders between cameras (or perhaps peel this off into a
# CameraPlusOverlay class)
# TODO, the classes below should likely be deleted
class OldMultiCamera(Camera):
"""Parameters
----------
cameras_with_start_positions : tuple
Tuples of (Camera, (start_y, start_x)) indicating camera and
its pixel offset on the final frame.
"""
def __init__(self, *cameras_with_start_positions, **kwargs):
self.shifted_cameras = [
DictAsObject(
{
"camera": camera_with_start_positions[0],
"start_x": camera_with_start_positions[1][1],
"start_y": camera_with_start_positions[1][0],
"end_x": camera_with_start_positions[1][1]
+ camera_with_start_positions[0].pixel_width,
"end_y": camera_with_start_positions[1][0]
+ camera_with_start_positions[0].pixel_height,
},
)
for camera_with_start_positions in cameras_with_start_positions
]
super().__init__(**kwargs)
def capture_mobjects(self, mobjects, **kwargs):
for shifted_camera in self.shifted_cameras:
shifted_camera.camera.capture_mobjects(mobjects, **kwargs)
self.pixel_array[
shifted_camera.start_y : shifted_camera.end_y,
shifted_camera.start_x : shifted_camera.end_x,
] = shifted_camera.camera.pixel_array
def set_background(self, pixel_array, **kwargs):
for shifted_camera in self.shifted_cameras:
shifted_camera.camera.set_background(
pixel_array[
shifted_camera.start_y : shifted_camera.end_y,
shifted_camera.start_x : shifted_camera.end_x,
],
**kwargs,
)
def set_pixel_array(self, pixel_array, **kwargs):
super().set_pixel_array(pixel_array, **kwargs)
for shifted_camera in self.shifted_cameras:
shifted_camera.camera.set_pixel_array(
pixel_array[
shifted_camera.start_y : shifted_camera.end_y,
shifted_camera.start_x : shifted_camera.end_x,
],
**kwargs,
)
def init_background(self):
super().init_background()
for shifted_camera in self.shifted_cameras:
shifted_camera.camera.init_background()
# A OldMultiCamera which, when called with two full-size cameras, initializes itself
# as a split screen, also taking care to resize each individual camera within it
class SplitScreenCamera(OldMultiCamera):
"""Initializes a split screen camera setup with two side-by-side cameras.
Parameters
----------
left_camera : Camera
right_camera : Camera
kwargs : dict
"""
def __init__(self, left_camera, right_camera, **kwargs):
Camera.__init__(self, **kwargs) # to set attributes such as pixel_width
self.left_camera = left_camera
self.right_camera = right_camera
half_width = math.ceil(self.pixel_width / 2)
for camera in [self.left_camera, self.right_camera]:
camera.reset_pixel_shape(camera.pixel_height, half_width)
super().__init__(
(left_camera, (0, 0)),
(right_camera, (0, half_width)),
)

View file

@ -1,274 +0,0 @@
"""Defines the MovingCamera class, a camera that can pan and zoom through a scene.
.. SEEALSO::
:mod:`.moving_camera_scene`
"""
from __future__ import annotations
__all__ = ["MovingCamera"]
from collections.abc import Iterable
from typing import Any
from cairo import Context
from manim.typing import PixelArray, Point3D, Point3DLike
from .. import config
from ..camera.camera import Camera
from ..constants import DOWN, LEFT, RIGHT, UP
from ..mobject.frame import ScreenRectangle
from ..mobject.mobject import Mobject
from ..utils.color import WHITE, ManimColor
class MovingCamera(Camera):
"""A camera that follows and matches the size and position of its 'frame', a Rectangle (or similar Mobject).
The frame defines the region of space the camera displays and can move or resize dynamically.
.. SEEALSO::
:class:`.MovingCameraScene`
"""
def __init__(
self,
frame: Mobject | None = None,
fixed_dimension: int = 0, # width
default_frame_stroke_color: ManimColor = WHITE,
default_frame_stroke_width: int = 0,
**kwargs: Any,
):
"""Frame is a Mobject, (should almost certainly be a rectangle)
determining which region of space the camera displays
"""
self.fixed_dimension = fixed_dimension
self.default_frame_stroke_color = default_frame_stroke_color
self.default_frame_stroke_width = default_frame_stroke_width
if frame is None:
frame = ScreenRectangle(height=config["frame_height"])
frame.set_stroke(
self.default_frame_stroke_color,
self.default_frame_stroke_width,
)
self.frame = frame
super().__init__(**kwargs)
# TODO, make these work for a rotated frame
@property
def frame_height(self) -> float:
"""Returns the height of the frame.
Returns
-------
float
The height of the frame.
"""
return self.frame.height
@frame_height.setter
def frame_height(self, frame_height: float) -> None:
"""Sets the height of the frame in MUnits.
Parameters
----------
frame_height
The new frame_height.
"""
self.frame.stretch_to_fit_height(frame_height)
@property
def frame_width(self) -> float:
"""Returns the width of the frame
Returns
-------
float
The width of the frame.
"""
return self.frame.width
@frame_width.setter
def frame_width(self, frame_width: float) -> None:
"""Sets the width of the frame in MUnits.
Parameters
----------
frame_width
The new frame_width.
"""
self.frame.stretch_to_fit_width(frame_width)
@property
def frame_center(self) -> Point3D:
"""Returns the centerpoint of the frame in cartesian coordinates.
Returns
-------
np.array
The cartesian coordinates of the center of the frame.
"""
return self.frame.get_center()
@frame_center.setter
def frame_center(self, frame_center: Point3DLike | Mobject) -> None:
"""Sets the centerpoint of the frame.
Parameters
----------
frame_center
The point to which the frame must be moved.
If is of type mobject, the frame will be moved to
the center of that mobject.
"""
self.frame.move_to(frame_center)
def capture_mobjects(self, mobjects: Iterable[Mobject], **kwargs: Any) -> None:
# self.reset_frame_center()
# self.realign_frame_shape()
super().capture_mobjects(mobjects, **kwargs)
def get_cached_cairo_context(self, pixel_array: PixelArray) -> None:
"""Since the frame can be moving around, the cairo
context used for updating should be regenerated
at each frame. So no caching.
"""
return None
def cache_cairo_context(self, pixel_array: PixelArray, ctx: Context) -> None:
"""Since the frame can be moving around, the cairo
context used for updating should be regenerated
at each frame. So no caching.
"""
pass
# def reset_frame_center(self):
# self.frame_center = self.frame.get_center()
# def realign_frame_shape(self):
# height, width = self.frame_shape
# if self.fixed_dimension == 0:
# self.frame_shape = (height, self.frame.width
# else:
# self.frame_shape = (self.frame.height, width)
# self.resize_frame_shape(fixed_dimension=self.fixed_dimension)
def get_mobjects_indicating_movement(self) -> list[Mobject]:
"""Returns all mobjects whose movement implies that the camera
should think of all other mobjects on the screen as moving
Returns
-------
list[Mobject]
"""
return [self.frame]
def auto_zoom(
self,
mobjects: Iterable[Mobject],
margin: float = 0,
only_mobjects_in_frame: bool = False,
animate: bool = True,
) -> Mobject:
"""Zooms on to a given array of mobjects (or a singular mobject)
and automatically resizes to frame all the mobjects.
.. NOTE::
This method only works when 2D-objects in the XY-plane are considered, it
will not work correctly when the camera has been rotated.
Parameters
----------
mobjects
The mobject or array of mobjects that the camera will focus on.
margin
The width of the margin that is added to the frame (optional, 0 by default).
only_mobjects_in_frame
If set to ``True``, only allows focusing on mobjects that are already in frame.
animate
If set to ``False``, applies the changes instead of returning the corresponding animation
Returns
-------
Union[_AnimationBuilder, ScreenRectangle]
_AnimationBuilder that zooms the camera view to a given list of mobjects
or ScreenRectangle with position and size updated to zoomed position.
"""
(
scene_critical_x_left,
scene_critical_x_right,
scene_critical_y_up,
scene_critical_y_down,
) = self._get_bounding_box(mobjects, only_mobjects_in_frame)
# calculate center x and y
x = (scene_critical_x_left + scene_critical_x_right) / 2
y = (scene_critical_y_up + scene_critical_y_down) / 2
# calculate proposed width and height of zoomed scene
new_width = abs(scene_critical_x_left - scene_critical_x_right)
new_height = abs(scene_critical_y_up - scene_critical_y_down)
m_target = self.frame.animate if animate else self.frame
# zoom to fit all mobjects along the side that has the largest size
if new_width / self.frame.width > new_height / self.frame.height:
return m_target.set_x(x).set_y(y).set(width=new_width + margin)
else:
return m_target.set_x(x).set_y(y).set(height=new_height + margin)
def _get_bounding_box(
self, mobjects: Iterable[Mobject], only_mobjects_in_frame: bool
) -> tuple[float, float, float, float]:
bounding_box_located = False
scene_critical_x_left: float = 0
scene_critical_x_right: float = 1
scene_critical_y_up: float = 1
scene_critical_y_down: float = 0
for m in mobjects:
if (m == self.frame) or (
only_mobjects_in_frame and not self.is_in_frame(m)
):
# detected camera frame, should not be used to calculate final position of camera
continue
# initialize scene critical points with first mobjects critical points
if not bounding_box_located:
scene_critical_x_left = m.get_critical_point(LEFT)[0]
scene_critical_x_right = m.get_critical_point(RIGHT)[0]
scene_critical_y_up = m.get_critical_point(UP)[1]
scene_critical_y_down = m.get_critical_point(DOWN)[1]
bounding_box_located = True
else:
if m.get_critical_point(LEFT)[0] < scene_critical_x_left:
scene_critical_x_left = m.get_critical_point(LEFT)[0]
if m.get_critical_point(RIGHT)[0] > scene_critical_x_right:
scene_critical_x_right = m.get_critical_point(RIGHT)[0]
if m.get_critical_point(UP)[1] > scene_critical_y_up:
scene_critical_y_up = m.get_critical_point(UP)[1]
if m.get_critical_point(DOWN)[1] < scene_critical_y_down:
scene_critical_y_down = m.get_critical_point(DOWN)[1]
if not bounding_box_located:
raise Exception(
"Could not determine bounding box of the mobjects given to 'auto_zoom'."
)
return (
scene_critical_x_left,
scene_critical_x_right,
scene_critical_y_up,
scene_critical_y_down,
)

View file

@ -1,107 +0,0 @@
"""A camera supporting multiple perspectives."""
from __future__ import annotations
__all__ = ["MultiCamera"]
from collections.abc import Iterable
from typing import Any, Self
from manim.mobject.mobject import Mobject
from manim.mobject.types.image_mobject import ImageMobjectFromCamera
from ..camera.moving_camera import MovingCamera
from ..utils.iterables import list_difference_update
class MultiCamera(MovingCamera):
"""Camera Object that allows for multiple perspectives."""
def __init__(
self,
image_mobjects_from_cameras: Iterable[ImageMobjectFromCamera] | None = None,
allow_cameras_to_capture_their_own_display: bool = False,
**kwargs: Any,
) -> None:
"""Initialises the MultiCamera
Parameters
----------
image_mobjects_from_cameras
kwargs
Any valid keyword arguments of MovingCamera.
"""
self.image_mobjects_from_cameras: list[ImageMobjectFromCamera] = []
if image_mobjects_from_cameras is not None:
for imfc in image_mobjects_from_cameras:
self.add_image_mobject_from_camera(imfc)
self.allow_cameras_to_capture_their_own_display = (
allow_cameras_to_capture_their_own_display
)
super().__init__(**kwargs)
def add_image_mobject_from_camera(
self, image_mobject_from_camera: ImageMobjectFromCamera
) -> None:
"""Adds an ImageMobject that's been obtained from the camera
into the list ``self.image_mobject_from_cameras``
Parameters
----------
image_mobject_from_camera
The ImageMobject to add to self.image_mobject_from_cameras
"""
# A silly method to have right now, but maybe there are things
# we want to guarantee about any imfc's added later.
imfc = image_mobject_from_camera
assert isinstance(imfc.camera, MovingCamera)
self.image_mobjects_from_cameras.append(imfc)
def update_sub_cameras(self) -> None:
"""Reshape sub_camera pixel_arrays"""
for imfc in self.image_mobjects_from_cameras:
pixel_height, pixel_width = self.pixel_array.shape[:2]
# imfc.camera.frame_shape = (
# imfc.camera.frame.height,
# imfc.camera.frame.width,
# )
imfc.camera.reset_pixel_shape(
int(pixel_height * imfc.height / self.frame_height),
int(pixel_width * imfc.width / self.frame_width),
)
def reset(self) -> Self:
"""Resets the MultiCamera.
Returns
-------
MultiCamera
The reset MultiCamera
"""
for imfc in self.image_mobjects_from_cameras:
imfc.camera.reset()
super().reset()
return self
def capture_mobjects(self, mobjects: Iterable[Mobject], **kwargs: Any) -> None:
self.update_sub_cameras()
for imfc in self.image_mobjects_from_cameras:
to_add = list(mobjects)
if not self.allow_cameras_to_capture_their_own_display:
to_add = list_difference_update(to_add, imfc.get_family())
imfc.camera.capture_mobjects(to_add, **kwargs)
super().capture_mobjects(mobjects, **kwargs)
def get_mobjects_indicating_movement(self) -> list[Mobject]:
"""Returns all mobjects whose movement implies that the camera
should think of all other mobjects on the screen as moving
Returns
-------
list
"""
return [self.frame] + [
imfc.camera.frame for imfc in self.image_mobjects_from_cameras
]

View file

@ -1,459 +0,0 @@
"""A camera that can be positioned and oriented in three-dimensional space."""
from __future__ import annotations
__all__ = ["ThreeDCamera"]
from collections.abc import Callable, Iterable
from typing import Any
import numpy as np
from manim.mobject.mobject import Mobject
from manim.mobject.three_d.three_d_utils import (
get_3d_vmob_end_corner,
get_3d_vmob_end_corner_unit_normal,
get_3d_vmob_start_corner,
get_3d_vmob_start_corner_unit_normal,
)
from manim.mobject.types.vectorized_mobject import VMobject
from manim.mobject.value_tracker import ValueTracker
from manim.typing import (
FloatRGBA_Array,
MatrixMN,
Point3D,
Point3D_Array,
Point3DLike,
)
from .. import config
from ..camera.camera import Camera
from ..constants import *
from ..mobject.types.point_cloud_mobject import Point
from ..utils.color import get_shaded_rgb
from ..utils.family import extract_mobject_family_members
from ..utils.space_ops import rotation_about_z, rotation_matrix
class ThreeDCamera(Camera):
def __init__(
self,
focal_distance: float = 20.0,
shading_factor: float = 0.2,
default_distance: float = 5.0,
light_source_start_point: Point3DLike = 9 * DOWN + 7 * LEFT + 10 * OUT,
should_apply_shading: bool = True,
exponential_projection: bool = False,
phi: float = 0,
theta: float = -90 * DEGREES,
gamma: float = 0,
zoom: float = 1,
**kwargs: Any,
):
"""Initializes the ThreeDCamera
Parameters
----------
*kwargs
Any keyword argument of Camera.
"""
self._frame_center = Point(kwargs.get("frame_center", ORIGIN), stroke_width=0)
super().__init__(**kwargs)
self.focal_distance = focal_distance
self.phi = phi
self.theta = theta
self.gamma = gamma
self.zoom = zoom
self.shading_factor = shading_factor
self.default_distance = default_distance
self.light_source_start_point = light_source_start_point
self.light_source = Point(self.light_source_start_point)
self.should_apply_shading = should_apply_shading
self.exponential_projection = exponential_projection
self.max_allowable_norm = 3 * config["frame_width"]
self.phi_tracker = ValueTracker(self.phi)
self.theta_tracker = ValueTracker(self.theta)
self.focal_distance_tracker = ValueTracker(self.focal_distance)
self.gamma_tracker = ValueTracker(self.gamma)
self.zoom_tracker = ValueTracker(self.zoom)
self.fixed_orientation_mobjects: dict[Mobject, Callable[[], Point3D]] = {}
self.fixed_in_frame_mobjects: set[Mobject] = set()
self.reset_rotation_matrix()
@property
def frame_center(self) -> Point3D:
return self._frame_center.points[0]
@frame_center.setter
def frame_center(self, point: Point3DLike) -> None:
self._frame_center.move_to(point)
def capture_mobjects(self, mobjects: Iterable[Mobject], **kwargs: Any) -> None:
self.reset_rotation_matrix()
super().capture_mobjects(mobjects, **kwargs)
def get_value_trackers(self) -> list[ValueTracker]:
"""A list of :class:`ValueTrackers <.ValueTracker>` of phi, theta, focal_distance,
gamma and zoom.
Returns
-------
list
list of ValueTracker objects
"""
return [
self.phi_tracker,
self.theta_tracker,
self.focal_distance_tracker,
self.gamma_tracker,
self.zoom_tracker,
]
def modified_rgbas(
self, vmobject: VMobject, rgbas: FloatRGBA_Array
) -> FloatRGBA_Array:
if not self.should_apply_shading:
return rgbas
if vmobject.shade_in_3d and (vmobject.get_num_points() > 0):
light_source_point = self.light_source.points[0]
if len(rgbas) < 2:
shaded_rgbas = rgbas.repeat(2, axis=0)
else:
shaded_rgbas = np.array(rgbas[:2])
shaded_rgbas[0, :3] = get_shaded_rgb(
shaded_rgbas[0, :3],
get_3d_vmob_start_corner(vmobject),
get_3d_vmob_start_corner_unit_normal(vmobject),
light_source_point,
)
shaded_rgbas[1, :3] = get_shaded_rgb(
shaded_rgbas[1, :3],
get_3d_vmob_end_corner(vmobject),
get_3d_vmob_end_corner_unit_normal(vmobject),
light_source_point,
)
return shaded_rgbas
return rgbas
def get_stroke_rgbas(
self,
vmobject: VMobject,
background: bool = False,
) -> FloatRGBA_Array: # NOTE : DocStrings From parent
return self.modified_rgbas(vmobject, vmobject.get_stroke_rgbas(background))
def get_fill_rgbas(
self, vmobject: VMobject
) -> FloatRGBA_Array: # NOTE : DocStrings From parent
return self.modified_rgbas(vmobject, vmobject.get_fill_rgbas())
def get_mobjects_to_display(
self, *args: Any, **kwargs: Any
) -> list[Mobject]: # NOTE : DocStrings From parent
mobjects = super().get_mobjects_to_display(*args, **kwargs)
rot_matrix = self.get_rotation_matrix()
def z_key(mob: Mobject) -> float:
if not (hasattr(mob, "shade_in_3d") and mob.shade_in_3d):
return np.inf # type: ignore[no-any-return]
# Assign a number to a three dimensional mobjects
# based on how close it is to the camera
distance: float = np.dot(mob.get_z_index_reference_point(), rot_matrix.T)[2]
return distance
return sorted(mobjects, key=z_key)
def get_phi(self) -> float:
"""Returns the Polar angle (the angle off Z_AXIS) phi.
Returns
-------
float
The Polar angle in radians.
"""
return self.phi_tracker.get_value()
def get_theta(self) -> float:
"""Returns the Azimuthal i.e the angle that spins the camera around the Z_AXIS.
Returns
-------
float
The Azimuthal angle in radians.
"""
return self.theta_tracker.get_value()
def get_focal_distance(self) -> float:
"""Returns focal_distance of the Camera.
Returns
-------
float
The focal_distance of the Camera in MUnits.
"""
return self.focal_distance_tracker.get_value()
def get_gamma(self) -> float:
"""Returns the rotation of the camera about the vector from the ORIGIN to the Camera.
Returns
-------
float
The angle of rotation of the camera about the vector
from the ORIGIN to the Camera in radians
"""
return self.gamma_tracker.get_value()
def get_zoom(self) -> float:
"""Returns the zoom amount of the camera.
Returns
-------
float
The zoom amount of the camera.
"""
return self.zoom_tracker.get_value()
def set_phi(self, value: float) -> None:
"""Sets the polar angle i.e the angle between Z_AXIS and Camera through ORIGIN in radians.
Parameters
----------
value
The new value of the polar angle in radians.
"""
self.phi_tracker.set_value(value)
def set_theta(self, value: float) -> None:
"""Sets the azimuthal angle i.e the angle that spins the camera around Z_AXIS in radians.
Parameters
----------
value
The new value of the azimuthal angle in radians.
"""
self.theta_tracker.set_value(value)
def set_focal_distance(self, value: float) -> None:
"""Sets the focal_distance of the Camera.
Parameters
----------
value
The focal_distance of the Camera.
"""
self.focal_distance_tracker.set_value(value)
def set_gamma(self, value: float) -> None:
"""Sets the angle of rotation of the camera about the vector from the ORIGIN to the Camera.
Parameters
----------
value
The new angle of rotation of the camera.
"""
self.gamma_tracker.set_value(value)
def set_zoom(self, value: float) -> None:
"""Sets the zoom amount of the camera.
Parameters
----------
value
The zoom amount of the camera.
"""
self.zoom_tracker.set_value(value)
def reset_rotation_matrix(self) -> None:
"""Sets the value of self.rotation_matrix to
the matrix corresponding to the current position of the camera
"""
self.rotation_matrix = self.generate_rotation_matrix()
def get_rotation_matrix(self) -> MatrixMN:
"""Returns the matrix corresponding to the current position of the camera.
Returns
-------
np.array
The matrix corresponding to the current position of the camera.
"""
return self.rotation_matrix
def generate_rotation_matrix(self) -> MatrixMN:
"""Generates a rotation matrix based off the current position of the camera.
Returns
-------
np.array
The matrix corresponding to the current position of the camera.
"""
phi = self.get_phi()
theta = self.get_theta()
gamma = self.get_gamma()
matrices = [
rotation_about_z(-theta - 90 * DEGREES),
rotation_matrix(-phi, RIGHT),
rotation_about_z(gamma),
]
result = np.identity(3)
for matrix in matrices:
result = np.dot(matrix, result)
return result
def project_points(self, points: Point3D_Array) -> Point3D_Array:
"""Applies the current rotation_matrix as a projection
matrix to the passed array of points.
Parameters
----------
points
The list of points to project.
Returns
-------
np.array
The points after projecting.
"""
frame_center = self.frame_center
focal_distance = self.get_focal_distance()
zoom = self.get_zoom()
rot_matrix = self.get_rotation_matrix()
points = points - frame_center
points = np.dot(points, rot_matrix.T)
zs = points[:, 2]
for i in 0, 1:
if self.exponential_projection:
# Proper projection would involve multiplying
# x and y by d / (d-z). But for points with high
# z value that causes weird artifacts, and applying
# the exponential helps smooth it out.
factor = np.exp(zs / focal_distance)
lt0 = zs < 0
factor[lt0] = focal_distance / (focal_distance - zs[lt0])
else:
factor = focal_distance / (focal_distance - zs)
factor[(focal_distance - zs) < 0] = 10**6
points[:, i] *= factor * zoom
return points
def project_point(self, point: Point3D) -> Point3D:
"""Applies the current rotation_matrix as a projection
matrix to the passed point.
Parameters
----------
point
The point to project.
Returns
-------
np.array
The point after projection.
"""
return self.project_points(point.reshape((1, 3)))[0, :]
def transform_points_pre_display(
self,
mobject: Mobject,
points: Point3D_Array,
) -> Point3D_Array: # TODO: Write Docstrings for this Method.
points = super().transform_points_pre_display(mobject, points)
fixed_orientation = mobject in self.fixed_orientation_mobjects
fixed_in_frame = mobject in self.fixed_in_frame_mobjects
if fixed_in_frame:
return points
if fixed_orientation:
center_func = self.fixed_orientation_mobjects[mobject]
center = center_func()
new_center = self.project_point(center)
return points + (new_center - center)
else:
return self.project_points(points)
def add_fixed_orientation_mobjects(
self,
*mobjects: Mobject,
use_static_center_func: bool = False,
center_func: Callable[[], Point3D] | None = None,
) -> None:
"""This method allows the mobject to have a fixed orientation,
even when the camera moves around.
E.G If it was passed through this method, facing the camera, it
will continue to face the camera even as the camera moves.
Highly useful when adding labels to graphs and the like.
Parameters
----------
*mobjects
The mobject whose orientation must be fixed.
use_static_center_func
Whether or not to use the function that takes the mobject's
center as centerpoint, by default False
center_func
The function which returns the centerpoint
with respect to which the mobject will be oriented, by default None
"""
# This prevents the computation of mobject.get_center
# every single time a projection happens
def get_static_center_func(mobject: Mobject) -> Callable[[], Point3D]:
point = mobject.get_center()
return lambda: point
for mobject in mobjects:
if center_func:
func = center_func
elif use_static_center_func:
func = get_static_center_func(mobject)
else:
func = mobject.get_center
for submob in mobject.get_family():
self.fixed_orientation_mobjects[submob] = func
def add_fixed_in_frame_mobjects(self, *mobjects: Mobject) -> None:
"""This method allows the mobject to have a fixed position,
even when the camera moves around.
E.G If it was passed through this method, at the top of the frame, it
will continue to be displayed at the top of the frame.
Highly useful when displaying Titles or formulae or the like.
Parameters
----------
**mobjects
The mobject to fix in frame.
"""
for mobject in extract_mobject_family_members(mobjects):
self.fixed_in_frame_mobjects.add(mobject)
def remove_fixed_orientation_mobjects(self, *mobjects: Mobject) -> None:
"""If a mobject was fixed in its orientation by passing it through
:meth:`.add_fixed_orientation_mobjects`, then this undoes that fixing.
The Mobject will no longer have a fixed orientation.
Parameters
----------
mobjects
The mobjects whose orientation need not be fixed any longer.
"""
for mobject in extract_mobject_family_members(mobjects):
if mobject in self.fixed_orientation_mobjects:
del self.fixed_orientation_mobjects[mobject]
def remove_fixed_in_frame_mobjects(self, *mobjects: Mobject) -> None:
"""If a mobject was fixed in frame by passing it through
:meth:`.add_fixed_in_frame_mobjects`, then this undoes that fixing.
The Mobject will no longer be fixed in frame.
Parameters
----------
mobjects
The mobjects which need not be fixed in frame any longer.
"""
for mobject in extract_mobject_family_members(mobjects):
if mobject in self.fixed_in_frame_mobjects:
self.fixed_in_frame_mobjects.remove(mobject)

View file

@ -84,7 +84,9 @@ def checkhealth() -> None:
self.execution_time = timeit.timeit(self._inner_construct, number=1)
with mn.tempconfig({"preview": True, "disable_caching": True}):
scene = CheckHealthDemo()
scene.render()
with mn.Manager(CheckHealthDemo) as manager:
manager.render()
click.echo(f"Scene rendered in {scene.execution_time:.2f} seconds.")
click.echo(
f"Scene rendered in {manager.scene.execution_time:.2f} seconds."
)

View file

@ -31,7 +31,8 @@ from manim.cli.render.ease_of_access_options import ease_of_access_options
from manim.cli.render.global_options import global_options
from manim.cli.render.output_options import output_options
from manim.cli.render.render_options import render_options
from manim.constants import EPILOG, RendererType
from manim.constants import EPILOG
from manim.manager import Manager
from manim.utils.module_ops import scene_classes_from_file
__all__ = ["render"]
@ -75,14 +76,6 @@ def render(**kwargs: Any) -> ClickArgs | dict[str, Any]:
SCENES is an optional list of scenes in the file.
"""
if kwargs["save_as_gif"]:
logger.warning("--save_as_gif is deprecated, please use --format=gif instead!")
kwargs["format"] = "gif"
if kwargs["save_pngs"]:
logger.warning("--save_pngs is deprecated, please use --format=png instead!")
kwargs["format"] = "png"
if kwargs["show_in_file_browser"]:
logger.warning(
"The short form of show_in_file_browser is deprecated and will be moved to support --format.",
@ -94,35 +87,10 @@ def render(**kwargs: Any) -> ClickArgs | dict[str, Any]:
config.digest_args(click_args)
file = Path(config.input_file)
if config.renderer == RendererType.OPENGL:
from manim.renderer.opengl_renderer import OpenGLRenderer
try:
renderer = OpenGLRenderer()
keep_running = True
while keep_running:
for SceneClass in scene_classes_from_file(file):
with tempconfig({}):
scene = SceneClass(renderer)
rerun = scene.render()
if rerun or config["write_all"]:
renderer.num_plays = 0
continue
else:
keep_running = False
break
if config["write_all"]:
keep_running = False
except Exception:
error_console.print_exception()
sys.exit(1)
else:
for SceneClass in scene_classes_from_file(file):
try:
with tempconfig({}):
scene = SceneClass()
scene.render()
with tempconfig({}), Manager(SceneClass) as manager:
manager.render()
except Exception:
error_console.print_exception()
sys.exit(1)

View file

@ -12,6 +12,7 @@ if TYPE_CHECKING:
__all__ = ["global_options"]
logger = logging.getLogger("manim")

View file

@ -21,10 +21,11 @@ output_options = option_group(
help="Zero padding for PNG file names.",
),
option(
"-w",
"--write_to_movie",
is_flag=True,
default=None,
help="Write the video rendered with opengl to a file.",
help="Write the video to a file.",
),
option(
"--media_dir",

View file

@ -7,7 +7,7 @@ from typing import TYPE_CHECKING
from cloup import Choice, option, option_group
from manim.constants import QUALITIES, RendererType
from manim.constants import QUALITIES
if TYPE_CHECKING:
from click import Context, Option
@ -16,6 +16,8 @@ __all__ = ["render_options"]
logger = logging.getLogger("manim")
__all__ = ["render_options"]
def validate_scene_range(
ctx: Context, param: Option, value: str | None
@ -112,6 +114,13 @@ render_options = option_group(
"renders all scenes after n_0.",
default=None,
),
option(
"-g",
"--groups",
callback=lambda ctx, param, value: value.split(","),
help="Render only the specified groups.",
default=[],
),
option(
"-a",
"--write_all",
@ -165,29 +174,6 @@ render_options = option_group(
default=None,
help="Render at this frame rate.",
),
option(
"--renderer",
type=Choice(
[renderer_type.value for renderer_type in RendererType],
case_sensitive=False,
),
help="Select a renderer for your Scene.",
default="cairo",
),
option(
"-g",
"--save_pngs",
is_flag=True,
default=None,
help="Save each frame as png (Deprecated).",
),
option(
"-i",
"--save_as_gif",
default=None,
is_flag=True,
help="Save as a gif (Deprecated).",
),
option(
"--save_sections",
default=None,
@ -200,16 +186,4 @@ render_options = option_group(
is_flag=True,
help="Render scenes with alpha channel.",
),
option(
"--use_projection_fill_shaders",
is_flag=True,
help="Use shaders for OpenGLVMobject fill which are compatible with transformation matrices.",
default=None,
),
option(
"--use_projection_stroke_shaders",
is_flag=True,
help="Use shaders for OpenGLVMobject stroke which are compatible with transformation matrices.",
default=None,
),
)

View file

@ -67,12 +67,11 @@ __all__ = [
"PI",
"TAU",
"DEGREES",
"RADIANS",
"QUALITIES",
"DEFAULT_QUALITY",
"EPILOG",
"CONTEXT_SETTINGS",
"SHIFT_VALUE",
"CTRL_VALUE",
"RendererType",
"LineJointType",
"CapStyleType",
@ -194,6 +193,9 @@ TAU = 2 * PI
DEGREES = TAU / 360
"""The exchange rate between radians and degrees."""
RADIANS: float = 1.0
"""Just a default to select for camera."""
class QualityDict(TypedDict):
flag: str | None
@ -245,8 +247,6 @@ QUALITIES: dict[str, QualityDict] = {
DEFAULT_QUALITY = "high_quality"
EPILOG = "Made with <3 by Manim Community developers."
SHIFT_VALUE = 65505
CTRL_VALUE = 65507
CONTEXT_SETTINGS = Context.settings(
align_option_groups=True,

View file

@ -0,0 +1,7 @@
from __future__ import annotations
from manim.event_handler.event_dispatcher import EventDispatcher
# This is supposed to be a Singleton
# i.e., during runtime there should be only one object of Event Dispatcher
EVENT_DISPATCHER = EventDispatcher()

View file

@ -0,0 +1,93 @@
from __future__ import annotations
from typing import Any, Self
import numpy as np
from manim.event_handler.event_listener import EventListener
from manim.event_handler.event_type import EventType
class EventDispatcher:
def __init__(self) -> None:
self.event_listeners: dict[EventType, list[EventListener]] = {
event_type: [] for event_type in EventType
}
self.mouse_point = np.array((0.0, 0.0, 0.0))
self.mouse_drag_point = np.array((0.0, 0.0, 0.0))
self.pressed_keys: set[int] = set()
self.draggable_object_listeners: list[EventListener] = []
def add_listener(self, event_listener: EventListener) -> Self:
assert isinstance(event_listener, EventListener)
self.event_listeners[event_listener.event_type].append(event_listener)
return self
def remove_listener(self, event_listener: EventListener) -> Self:
assert isinstance(event_listener, EventListener)
try:
while event_listener in self.event_listeners[event_listener.event_type]:
self.event_listeners[event_listener.event_type].remove(event_listener)
except Exception:
# raise ValueError("Handler is not handling this event, so cannot remove it.")
pass
return self
def dispatch(self, event_type: EventType, **event_data: Any) -> bool | None:
if event_type == EventType.MouseMotionEvent:
self.mouse_point = event_data["point"]
elif event_type == EventType.MouseDragEvent:
self.mouse_drag_point = event_data["point"]
elif event_type == EventType.KeyPressEvent:
self.pressed_keys.add(event_data["symbol"]) # Modifiers?
elif event_type == EventType.KeyReleaseEvent:
self.pressed_keys.difference_update({event_data["symbol"]}) # Modifiers?
elif event_type == EventType.MousePressEvent:
self.draggable_object_listeners = [
listener
for listener in self.event_listeners[EventType.MouseDragEvent]
if listener.mobject.is_point_touching(self.mouse_point)
]
elif event_type == EventType.MouseReleaseEvent:
self.draggable_object_listeners = []
propagate_event = None
if event_type == EventType.MouseDragEvent:
for listener in self.draggable_object_listeners:
assert isinstance(listener, EventListener)
propagate_event = listener.callback(listener.mobject, event_data)
if propagate_event is not None and propagate_event is False:
return propagate_event
elif event_type.value.startswith("mouse"):
for listener in self.event_listeners[event_type]:
if listener.mobject.is_point_touching(self.mouse_point):
propagate_event = listener.callback(listener.mobject, event_data)
if propagate_event is not None and propagate_event is False:
return propagate_event
elif event_type.value.startswith("key"):
for listener in self.event_listeners[event_type]:
propagate_event = listener.callback(listener.mobject, event_data)
if propagate_event is not None and propagate_event is False:
return propagate_event
return propagate_event
def get_listeners_count(self) -> int:
return sum([len(value) for key, value in self.event_listeners.items()])
def get_mouse_point(self) -> np.ndarray:
return self.mouse_point
def get_mouse_drag_point(self) -> np.ndarray:
return self.mouse_drag_point
def is_key_pressed(self, symbol: int) -> bool:
return symbol in self.pressed_keys
__iadd__ = add_listener
__isub__ = remove_listener
__call__ = dispatch
__len__ = get_listeners_count

View file

@ -0,0 +1,34 @@
from __future__ import annotations
import contextlib
from collections.abc import Callable
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import Any
from manim.event_handler.event_type import EventType
from manim.mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
class EventListener:
def __init__(
self,
mobject: Mobject,
event_type: EventType,
event_callback: Callable[[Mobject, dict[str, str]], None],
) -> None:
self.mobject = mobject
self.event_type = event_type
self.callback = event_callback
def __eq__(self, other: Any) -> bool:
return_val = False
if isinstance(other, EventListener):
with contextlib.suppress(Exception):
return_val = (
self.callback == other.callback
and self.mobject == other.mobject
and self.event_type == other.event_type
)
return return_val

View file

@ -0,0 +1,13 @@
from __future__ import annotations
from enum import Enum
class EventType(Enum):
MouseMotionEvent = "mouse_motion_event"
MousePressEvent = "mouse_press_event"
MouseReleaseEvent = "mouse_release_event"
MouseDragEvent = "mouse_drag_event"
MouseScrollEvent = "mouse_scroll_event"
KeyPressEvent = "key_press_event"
KeyReleaseEvent = "key_release_event"

View file

@ -0,0 +1,14 @@
from __future__ import annotations
from typing import Protocol
class WindowProtocol(Protocol):
@property
def is_closing(self) -> bool: ...
def swap_buffers(self) -> object: ...
def close(self) -> object: ...
def clear(self) -> object: ...

View file

@ -0,0 +1,4 @@
from __future__ import annotations
from .file_writer import FileWriter
from .sections import *

View file

@ -2,7 +2,7 @@
from __future__ import annotations
__all__ = ["SceneFileWriter"]
__all__ = ["FileWriter"]
import json
import shutil
@ -10,7 +10,7 @@ import warnings
from fractions import Fraction
from pathlib import Path
from queue import Queue
from tempfile import NamedTemporaryFile, _TemporaryFileWrapper
from tempfile import NamedTemporaryFile
from threading import Thread
from typing import TYPE_CHECKING, Any
@ -31,11 +31,11 @@ with warnings.catch_warnings():
from pydub import AudioSegment
from manim import __version__
from .. import config, logger
from .._config.logger_utils import set_file_logger
from ..constants import RendererType
from ..utils.file_ops import (
from manim._config import config, logger
from manim._config.logger_utils import set_file_logger
from manim.file_writer.protocols import FileWriterProtocol
from manim.file_writer.sections import DefaultSectionType, Section
from manim.utils.file_ops import (
add_extension_if_not_present,
add_version_before_extension,
guarantee_existence,
@ -44,16 +44,13 @@ from ..utils.file_ops import (
modify_atime,
write_to_movie,
)
from ..utils.sounds import get_full_sound_file_path
from .section import DefaultSectionType, Section
from manim.utils.sounds import get_full_sound_file_path
if TYPE_CHECKING:
from av.container.output import OutputContainer
from av.stream import Stream
from manim.renderer.cairo_renderer import CairoRenderer
from manim.renderer.opengl_renderer import OpenGLRenderer
from manim.typing import PixelArray, StrPath
from manim.typing import PixelArray, StrOrBytesPath, StrPath
def to_av_frame_rate(fps: float) -> Fraction:
@ -74,7 +71,9 @@ def to_av_frame_rate(fps: float) -> Fraction:
def convert_audio(
input_path: Path, output_path: Path | _TemporaryFileWrapper[bytes], codec_name: str
input_path: StrOrBytesPath,
output_path: StrOrBytesPath,
codec_name: str,
) -> None:
with (
av.open(input_path) as input_audio,
@ -90,8 +89,8 @@ def convert_audio(
output_audio.mux(packet)
class SceneFileWriter:
"""SceneFileWriter is the object that actually writes the animations
class FileWriter(FileWriterProtocol):
"""FileWriter is the object that actually writes the animations
played, into video files, using FFMPEG.
This is mostly for Manim's internal use. You will rarely, if ever,
have to use the methods for this class, unless tinkering with the very
@ -120,16 +119,11 @@ class SceneFileWriter:
force_output_as_scene_name = False
def __init__(
self,
renderer: CairoRenderer | OpenGLRenderer,
scene_name: str,
**kwargs: Any,
) -> None:
self.renderer = renderer
def __init__(self, scene_name: str) -> None:
self.init_output_directories(scene_name)
self.init_audio()
self.frame_count = 0
self.num_plays = 0
self.partial_movie_files: list[str | None] = []
self.subcaptions: list[srt.Subtitle] = []
self.sections: list[Section] = []
@ -139,6 +133,10 @@ class SceneFileWriter:
name="autocreated", type_=DefaultSectionType.NORMAL, skip_animations=False
)
@classmethod
def use_output_as_scene_name(cls) -> None:
cls.force_output_as_scene_name = True
def init_output_directories(self, scene_name: str) -> None:
"""Initialise output directories.
@ -154,7 +152,7 @@ class SceneFileWriter:
module_name = config.get_dir("input_file").stem if config["input_file"] else ""
if SceneFileWriter.force_output_as_scene_name:
if self.force_output_as_scene_name:
self.output_name = Path(scene_name)
elif config["output_file"] and not config["write_all"]:
self.output_name = config.get_dir("output_file")
@ -247,11 +245,8 @@ class SceneFileWriter:
)
def add_partial_movie_file(self, hash_animation: str | None) -> None:
"""Adds a new partial movie file path to ``scene.partial_movie_files``
and current section from a hash.
This method will compute the path from the hash. In addition to that it
adds the new animation to the current section.
"""Adds a new partial movie file path to `scene.partial_movie_files` and current section from a hash.
This method will compute the path from the hash. In addition to that it adds the new animation to the current section.
Parameters
----------
@ -269,7 +264,7 @@ class SceneFileWriter:
else:
new_partial_movie_file = str(
self.partial_movie_directory
/ f"{hash_animation}{config['movie_file_extension']}"
/ f"{hash_animation}{config.movie_file_extension}"
)
self.partial_movie_files.append(new_partial_movie_file)
self.sections[-1].partial_movie_files.append(new_partial_movie_file)
@ -301,8 +296,8 @@ class SceneFileWriter:
:class:`str`
The name of the directory.
"""
pixel_height = config["pixel_height"]
frame_rate = config["frame_rate"]
pixel_height = config.pixel_height
frame_rate = config.frame_rate
return f"{pixel_height}p{frame_rate}"
# Sound
@ -338,7 +333,7 @@ class SceneFileWriter:
self.includes_sound = True
self.create_audio_segment()
segment = self.audio_segment
curr_end = segment.duration_seconds
curr_end: float = segment.duration_seconds
if time is None:
time = curr_end
if time < 0:
@ -425,6 +420,7 @@ class SceneFileWriter:
"""
if write_to_movie() and allow_write:
self.close_partial_movie_stream()
self.num_plays += 1
def listen_and_write(self) -> None:
"""For internal use only: blocks until new frame is available on the queue."""
@ -450,9 +446,7 @@ class SceneFileWriter:
for packet in self.video_stream.encode(av_frame):
self.video_container.mux(packet)
def write_frame(
self, frame_or_renderer: PixelArray | OpenGLRenderer, num_frames: int = 1
) -> None:
def write_frame(self, frame: PixelArray, num_frames: int = 1) -> None:
"""Used internally by Manim to write a frame to the FFMPEG input buffer.
Parameters
@ -463,34 +457,18 @@ class SceneFileWriter:
The number of times to write frame.
"""
if write_to_movie():
if isinstance(frame_or_renderer, np.ndarray):
frame = frame_or_renderer
else:
frame = (
frame_or_renderer.get_frame()
if config.renderer == RendererType.OPENGL
else frame_or_renderer
)
msg = (num_frames, frame)
self.queue.put(msg)
if is_png_format() and not config["dry_run"]:
if isinstance(frame_or_renderer, np.ndarray):
image = Image.fromarray(frame_or_renderer)
else:
image = (
frame_or_renderer.get_image()
if config.renderer == RendererType.OPENGL
else Image.fromarray(frame_or_renderer)
)
if is_png_format() and not config.dry_run:
image = Image.fromarray(frame)
target_dir = self.image_file_path.parent / self.image_file_path.stem
extension = self.image_file_path.suffix
self.output_image(
image,
target_dir,
extension,
config["zero_pad"],
config.zero_pad,
)
def output_image(
@ -502,20 +480,19 @@ class SceneFileWriter:
image.save(f"{target_dir}{self.frame_count}{ext}")
self.frame_count += 1
def save_image(self, image: Image.Image) -> None:
"""This method saves the image passed to it in the default image directory.
def save_image(self, image: PixelArray) -> None:
"""Saves an image in the default image directory.
Parameters
----------
image
The pixel array of the image to save.
"""
if config["dry_run"]:
return
if not config["output_file"]:
self.image_file_path = add_version_before_extension(self.image_file_path)
image.save(self.image_file_path)
image_processed = Image.fromarray(image)
image_processed.save(self.image_file_path)
self.print_file_ready_message(self.image_file_path)
def finish(self) -> None:
@ -544,7 +521,7 @@ class SceneFileWriter:
the video stream of a partial movie file.
"""
if file_path is None:
file_path = self.partial_movie_files[self.renderer.num_plays]
file_path = self.partial_movie_files[self.num_plays]
self.partial_movie_file_path = file_path
fps = to_av_frame_rate(config.frame_rate)
@ -599,7 +576,7 @@ class SceneFileWriter:
self.video_container.close()
logger.info(
f"Animation {self.renderer.num_plays} : Partial movie file written in %(path)s",
f"Animation {self.num_plays} : Partial movie file written in %(path)s",
{"path": f"'{self.partial_movie_file_path}'"},
)
@ -710,7 +687,7 @@ class SceneFileWriter:
output_container.mux(packet)
else:
output_stream = output_container.add_stream_from_template(
output_stream = output_container.add_stream(
template=partial_movies_stream,
)
if config.transparent and config.movie_file_extension == ".webm":
@ -802,12 +779,8 @@ class SceneFileWriter:
output_container = av.open(
str(temp_file_path), mode="w", options=av_options
)
output_video_stream = output_container.add_stream_from_template(
template=video_stream
)
output_audio_stream = output_container.add_stream_from_template(
template=audio_stream
)
output_video_stream = output_container.add_stream(template=video_stream)
output_audio_stream = output_container.add_stream(template=audio_stream)
for packet in video_input.demux(video_stream):
# We need to skip the "flushing" packets that `demux` generates.
@ -861,9 +834,9 @@ class SceneFileWriter:
for file_name in self.partial_movie_directory.iterdir()
if file_name != "partial_movie_file_list.txt"
]
if len(cached_partial_movies) > config["max_files_cached"]:
if len(cached_partial_movies) > config.max_files_cached:
number_files_to_delete = (
len(cached_partial_movies) - config["max_files_cached"]
len(cached_partial_movies) - config.max_files_cached
)
oldest_files_to_delete = sorted(
cached_partial_movies,
@ -872,7 +845,7 @@ class SceneFileWriter:
for file_to_delete in oldest_files_to_delete:
file_to_delete.unlink()
logger.info(
f"The partial movie directory is full (> {config['max_files_cached']} files). Therefore, manim has removed the {number_files_to_delete} oldest file(s)."
f"The partial movie directory is full (> {config.max_files_cached} files). Therefore, manim has removed the {number_files_to_delete} oldest file(s)."
" You can change this behaviour by changing max_files_cached in config.",
)
@ -900,5 +873,5 @@ class SceneFileWriter:
def print_file_ready_message(self, file_path: StrPath) -> None:
"""Prints the "File Ready" message to STDOUT."""
config["output_file"] = file_path
logger.info("\nFile ready at %(file_path)s\n", {"file_path": f"'{file_path}'"})
config.output_file = str(file_path)
logger.info(f"\nFile ready at {str(file_path)!r}\n")

View file

@ -0,0 +1,33 @@
from __future__ import annotations
from typing import Protocol
from manim.typing import PixelArray
class FileWriterProtocol(Protocol):
"""Protocol for a file writer.
This is mainly useful for testing purposes, to create
a mock file writer. However, it can be used in plugins.
"""
num_plays: int
def __init__(self, scene_name: str) -> None: ...
def begin_animation(self, allow_write: bool = False) -> object: ...
def end_animation(self, allow_write: bool = False) -> object: ...
def is_already_cached(self, hash_invocation: str) -> bool: ...
def add_partial_movie_file(self, hash_animation: str) -> object: ...
def write_frame(self, frame: PixelArray) -> object: ...
def next_section(self, name: str, type_: str, skip_animations: bool) -> object: ...
def finish(self) -> None: ...
def save_image(self, image: PixelArray) -> object: ...

482
manim/manager.py Normal file
View file

@ -0,0 +1,482 @@
from __future__ import annotations
__all__ = ["Manager"]
import contextlib
import platform
import time
import warnings
from collections.abc import Iterable, Iterator, Sequence
from typing import TYPE_CHECKING, Generic, TypeVar
import numpy as np
from manim import config, logger
from manim.animation.animation import Wait
from manim.event_handler.window import WindowProtocol
from manim.file_writer import FileWriter
from manim.renderer.opengl_renderer import OpenGLRenderer
from manim.renderer.opengl_renderer_window import Window
from manim.scene.scene import Scene, SceneState
from manim.utils.exceptions import EndSceneEarlyException
from manim.utils.hashing import get_hash_from_play_call
from manim.utils.progressbar import (
ExperimentalProgressBarWarning,
NullProgressBar,
ProgressBar,
ProgressBarProtocol,
)
if TYPE_CHECKING:
from types import TracebackType
from typing import Any, Self
import numpy.typing as npt
from manim.animation.protocol import AnimationProtocol
from manim.file_writer.protocols import FileWriterProtocol
from manim.renderer.renderer import RendererProtocol
SceneT = TypeVar("SceneT", bound=Scene)
class Manager(Generic[SceneT]):
"""
The Brain of Manim
.. admonition:: Warning for Developers
Only methods of this class that are not prefixed with an
underscore (``_``) are stable. If you override any of the
``_`` methods, consider pinning your version of Manim.
Usage
-----
.. code-block:: python
class Manimation(Scene):
def construct(self):
self.play(FadeIn(Circle()))
# make sure to use it as a context manager
# to ensure proper resource cleanup
with Manager(Manimation) as manager:
manager.render()
"""
def __init__(self, scene_cls: type[SceneT]) -> None:
config._warn_about_config_options()
self.scene: SceneT = scene_cls(manager=self)
if not isinstance(self.scene, Scene):
raise ValueError(f"{self.scene!r} is not an instance of Scene")
self.time = 0.0
# Initialize window, if applicable
self.window = self.create_window()
# this must be done AFTER instantiating a window
self.renderer = self.create_renderer()
self.file_writer = self.create_file_writer()
self._write_files = config.write_to_movie
# internal state
self._skipping = config.save_last_frame
def __str__(self) -> str:
return f"{self.__class__.__name__}({self.scene!r}) at time {self.time:.2f}s"
def __enter__(self) -> Self:
return self
def __exit__(
self,
exc_type: type[BaseException] | None,
exc_value: BaseException | None,
traceback: TracebackType | None,
) -> None:
self.release()
# keep these as instance methods so subclasses
# have access to everything
def create_renderer(self) -> RendererProtocol:
"""Create and return a renderer instance.
This can be overridden in subclasses (plugins), if more processing
is needed.
Returns
-------
An instance of a renderer
"""
renderer = OpenGLRenderer(
background_color=config.background_color,
background_opacity=config.background_opacity,
)
if config.preview:
renderer.use_window()
return renderer
def create_window(self) -> WindowProtocol | None:
"""Create and return a window instance.
This can be overridden in subclasses (plugins), if more
processing is needed.
Returns
-------
A window if previewing, else None
"""
return Window() if config.preview else None
def create_file_writer(self) -> FileWriterProtocol:
"""Create and return a file writer instance.
This can be overridden in subclasses (plugins), if more
processing is needed.
Returns
-------
A file writer satisfying :class:`.FileWriterProtocol`
"""
return FileWriter(scene_name=self.scene.get_default_scene_name())
def setup(self) -> None:
"""Set up processes and manager"""
self.scene.setup()
# these are used for making sure it feels like the correct
# amount of time has passed in the window instead of rendering
# at full speed
# See the docstring of :meth:`_wait_for_animation_time`
self.virtual_animation_start_time = 0.0
self.real_animation_start_time = time.perf_counter()
def render(self) -> None:
"""
Entry point to running a Manim class
Example
-------
.. code-block:: python
class MyScene(Scene):
def construct(self):
self.play(Create(Circle()))
with tempconfig({"preview": True}), Manager(MyScene) as manager:
manager.render()
"""
self._render_first_pass()
self._render_second_pass()
def _render_first_pass(self) -> None:
"""
Temporarily use the normal single pass
rendering system
"""
self.setup()
with contextlib.suppress(EndSceneEarlyException):
self.construct()
self.post_construct()
self._interact()
self.tear_down()
def construct(self) -> None:
if not self.scene.groups_api:
self.scene.construct()
return
for group in self.scene.find_groups():
if not config.groups or group.name in config.groups:
group()
elif group.name not in config.groups:
with self.no_render():
group()
def _render_second_pass(self) -> None:
"""
In the future, this method could be used
for two pass rendering
"""
...
def release(self) -> None:
self.renderer.release()
def post_construct(self) -> None:
"""Run post-construct hooks, and clean up the file writer."""
should_write_image = config.save_last_frame or (
config.write_to_movie and not self.file_writer.num_plays
)
if self.file_writer.num_plays:
self.file_writer.finish()
# otherwise no animations were played
if should_write_image:
self.render_state(write_frame=False)
# FIXME: for some reason the OpenGLRenderer does not give out the
# correct frame values here
frame = self.renderer.get_pixels()
self.file_writer.save_image(frame)
self._write_files = False
def tear_down(self) -> None:
"""Tear down the scene and the window."""
self.scene.tear_down()
if self.window is not None:
self.window.close()
self.window = None
def _interact(self) -> None:
"""Live interaction with the Window"""
if self.window is None:
return
logger.info(
"\nTips: Using the keys `d`, `f`, or `z` "
"you can interact with the scene. "
"Press `command + q` or `esc` to quit"
)
last_time = time.perf_counter()
while not self.window.is_closing:
current_time = time.perf_counter()
dt = current_time - last_time
last_time = current_time
self._update_frame(dt)
@contextlib.contextmanager
def no_render(self) -> Iterator[None]:
"""Context manager to temporarily disable rendering.
Usage
-----
.. code-block:: python
with manager.no_render():
manager._play(FadeIn(Circle()))
"""
self._skipping = True
yield
self._skipping = False
# ----------------------------------#
# Animation Pipeline #
# ----------------------------------#
def _update_frame(
self, dt: float, *, write_frame: bool | None = None, run_updaters: bool = True
) -> None:
"""Update the current frame by ``dt``
Parameters
----------
dt : the time in between frames
write_frame : Whether to write the result to the output stream (videos ONLY).
Default value checks :attr:`_write_files` to see if it should be written.
"""
self.time += dt
if run_updaters:
self.scene._update_mobjects(dt)
self.scene.time = self.time
if self.window is not None:
if not self._skipping:
self.window.clear()
# if it's closing, then any subsequent methods will
# raise an error because the internal C window pointer is nullptr.
if self.window.is_closing:
raise EndSceneEarlyException()
if not self._skipping:
self.render_state(write_frame=write_frame)
self._wait_for_animation_time()
def _wait_for_animation_time(self) -> None:
"""Wait for the real time to catch up to the "virtual" animation time.
Animations can render faster than real time, so we have to
slow the window down for the correct amount of time, such
as during a wait animation.
"""
if self.window is None:
return
self.window.swap_buffers()
if self._skipping:
return
vt = self.time - self.virtual_animation_start_time
rt = time.perf_counter() - self.real_animation_start_time
# we can't sleep because we still need to poll for events,
# e.g. hitting Escape or close
while rt < vt:
if self.window.is_closing:
raise EndSceneEarlyException()
# make sure to poll for events
self.window.swap_buffers()
rt = time.perf_counter() - self.real_animation_start_time
def _play(self, *animations: AnimationProtocol) -> None:
"""Play a bunch of animations"""
self.scene.pre_play()
if self.window is not None:
self.real_animation_start_time = time.perf_counter()
self.virtual_animation_start_time = self.time
self._write_hashed_movie_file(animations)
self.scene.begin_animations(animations)
self._progress_through_animations(animations)
self.scene.finish_animations(animations)
self.scene.post_play()
self.file_writer.end_animation(allow_write=self._write_files)
def _write_hashed_movie_file(self, animations: Sequence[AnimationProtocol]) -> None:
"""Compute the hash of a self.play call, and write it to a file
Essentially, a series of methods that need to be called to successfully
render a frame.
"""
if not config.write_to_movie or self._skipping:
return
if config.disable_caching:
if not config.disable_caching_warning:
logger.info("Caching disabled...")
hash_current_play = f"uncached_{self.file_writer.num_plays:05}"
else:
hash_current_play = get_hash_from_play_call(
self.scene,
self.scene.camera,
animations,
self.scene.mobjects,
)
if self.file_writer.is_already_cached(hash_current_play):
logger.info(
f"Animation {self.file_writer.num_plays} : Using cached data (hash : {hash_current_play})"
)
# TODO: think about how to skip
raise NotImplementedError(
"Skipping cached animations is not implemented yet"
)
self.file_writer.add_partial_movie_file(hash_current_play)
self.file_writer.begin_animation(allow_write=self._write_files)
def _create_progressbar(
self, total: float, description: str, **kwargs: Any
) -> contextlib.AbstractContextManager[ProgressBarProtocol]:
"""Create a progressbar"""
if not config.progress_bar:
return contextlib.nullcontext(NullProgressBar())
with warnings.catch_warnings():
if config.verbosity != "DEBUG":
# Note: update when rich/notebook tqdm is no longer experimental
warnings.simplefilter("ignore", category=ExperimentalProgressBarWarning)
return ProgressBar(
total=total,
unit="frames",
desc=description % {"num": self.file_writer.num_plays},
ascii=True if platform.system() == "Windows" else None,
leave=config.progress_bar == "leave",
disable=config.progress_bar == "none",
**kwargs,
)
def _progress_through_animations(
self, animations: Sequence[AnimationProtocol]
) -> None:
last_t = 0.0
run_time = _calc_runtime(animations)
progression = _calc_time_progression(run_time)
with self._create_progressbar(
progression.shape[0],
f"Animation %(num)d: {animations[0]}{', etc.' if len(animations) > 1 else ''}",
) as progress:
if self._skipping:
self.scene._update_animations(animations, run_time, run_time)
self._update_frame(run_time, run_updaters=False)
return
for t in progression:
dt, last_t = t - last_t, t
self.scene._update_animations(animations, t, dt)
run_updaters = not self.scene.is_current_animation_frozen_frame(
animations
)
self._update_frame(dt, run_updaters=run_updaters)
for anim in animations:
if (
isinstance(anim, Wait)
and anim.stop_condition is not None
and anim.stop_condition()
):
return
progress.update(1)
# -------------------------#
# Rendering #
# -------------------------#
def render_state(self, write_frame: bool | None = None) -> None:
"""Render the current state of the scene.
Any extra kwargs are passed to :meth:`_render_frame`.
"""
state = self.scene.get_state()
self._render_frame(state, write_frame=write_frame)
def _render_frame(
self, state: SceneState, *, write_frame: bool | None = None
) -> None:
"""Renders a frame based on a state, and writes it to the file writers stream.
This is used for writing a single frame. Any extra kwargs are passed to :meth:`write_frame`.
.. warning::
This method will not work if :meth:`.FileWriter.begin_animation` and
: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
if should_write:
self.write_frame()
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)
def _calc_runtime(animations: Iterable[AnimationProtocol]) -> float:
"""Calculate the runtime of an iterable of animations.
.. warning::
If animations is a generator, this will consume the generator.
"""
return max(animation.get_run_time() for animation in animations)

View file

@ -50,8 +50,8 @@ from typing import TYPE_CHECKING, Any, Self, cast
import numpy as np
from manim.constants import *
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.types.vectorized_mobject import VGroup, VMobject
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVGroup as VGroup
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject as VMobject
from manim.utils.color import BLACK, BLUE, RED, WHITE, ParsableManimColor
from manim.utils.iterables import adjacent_pairs
from manim.utils.space_ops import (
@ -68,7 +68,7 @@ if TYPE_CHECKING:
import manim.mobject.geometry.tips as tips
from manim.mobject.geometry.line import Line
from manim.mobject.mobject import Mobject
from manim.mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
from manim.mobject.text.tex_mobject import SingleStringMathTex, Tex
from manim.mobject.text.text_mobject import Text
from manim.typing import (
@ -79,7 +79,7 @@ if TYPE_CHECKING:
)
class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
class TipableVMobject(VMobject):
"""Meant for shared functionality between Arc and Line.
Functionality can be classified broadly into these groups:
@ -1093,7 +1093,7 @@ class Annulus(Circle):
self.generate_points()
class CubicBezier(VMobject, metaclass=ConvertToOpenGL):
class CubicBezier(VMobject):
"""A cubic Bézier curve.
Example
@ -1128,7 +1128,7 @@ class CubicBezier(VMobject, metaclass=ConvertToOpenGL):
self.add_cubic_bezier_curve(start_anchor, start_handle, end_handle, end_anchor)
class ArcPolygon(VMobject, metaclass=ConvertToOpenGL):
class ArcPolygon(VMobject):
"""A generalized polygon allowing for points to be connected with arcs.
This version tries to stick close to the way :class:`Polygon` is used. Points
@ -1249,7 +1249,7 @@ class ArcPolygon(VMobject, metaclass=ConvertToOpenGL):
self.arcs = arcs
class ArcPolygonFromArcs(VMobject, metaclass=ConvertToOpenGL):
class ArcPolygonFromArcs(VMobject):
"""A generalized polygon allowing for points to be connected with arcs.
This version takes in pre-defined arcs to generate the arcpolygon and introduces

View file

@ -8,19 +8,16 @@ import numpy as np
from pathops import Path as SkiaPath
from pathops import PathVerb, difference, intersection, union, xor
from manim import config
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.types.vectorized_mobject import VMobject
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject as VMobject
if TYPE_CHECKING:
from manim.typing import Point2DLike_Array, Point3D_Array, Point3DLike_Array
from ...constants import RendererType
__all__ = ["Union", "Intersection", "Difference", "Exclusion"]
class _BooleanOps(VMobject, metaclass=ConvertToOpenGL):
class _BooleanOps(VMobject):
"""This class contains some helper functions which
helps to convert to and from skia objects and manim
objects (:class:`~.VMobject`).
@ -84,8 +81,6 @@ class _BooleanOps(VMobject, metaclass=ConvertToOpenGL):
if len(points) == 0: # what? No points so return empty path
return path
# In OpenGL it's quadratic beizer curves while on Cairo it's cubic...
if config.renderer == RendererType.OPENGL:
subpaths = vmobject.get_subpaths_from_points(points)
for subpath in subpaths:
quads = vmobject.get_bezier_tuples_from_points(subpath)
@ -95,18 +90,6 @@ class _BooleanOps(VMobject, metaclass=ConvertToOpenGL):
path.quadTo(*p1[:2], *p2[:2])
if vmobject.consider_points_equals(subpath[0], subpath[-1]):
path.close()
elif config.renderer == RendererType.CAIRO:
subpaths = vmobject.gen_subpaths_from_points_2d(points) # type: ignore[assignment]
for subpath in subpaths:
quads = vmobject.gen_cubic_bezier_tuples_from_points(subpath)
start = subpath[0]
path.moveTo(*start[:2])
for _p0, p1, p2, p3 in quads:
path.cubicTo(*p1[:2], *p2[:2], *p3[:2])
if vmobject.consider_points_equals_2d(subpath[0], subpath[-1]):
path.close()
return path
def _convert_skia_path_to_vmobject(self, path: SkiaPath) -> VMobject:

View file

@ -15,9 +15,9 @@ from manim.mobject.geometry.shape_matchers import (
BackgroundRectangle,
SurroundingRectangle,
)
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVGroup as VGroup
from manim.mobject.text.tex_mobject import MathTex, Tex
from manim.mobject.text.text_mobject import Text
from manim.mobject.types.vectorized_mobject import VGroup
from manim.utils.color import WHITE
from manim.utils.polylabel import polylabel

View file

@ -18,14 +18,15 @@ from typing import TYPE_CHECKING, Any, Literal
import numpy as np
from manim import config
from manim.constants import *
from manim.mobject.geometry.arc import Arc, ArcBetweenPoints, Dot, TipableVMobject
from manim.mobject.geometry.tips import ArrowTriangleFilledTip
from manim.mobject.mobject import Mobject
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
from manim.mobject.types.vectorized_mobject import DashedVMobject, VGroup, VMobject
from manim.mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLDashedVMobject as DashedVMobject,
)
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVGroup as VGroup
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject as VMobject
from manim.utils.color import WHITE
from manim.utils.space_ops import angle_of_vector, line_intersection, normalize
@ -187,7 +188,7 @@ class Line(TipableVMobject):
direction
The direction.
"""
if isinstance(mob_or_point, (Mobject, OpenGLMobject)):
if isinstance(mob_or_point, Mobject):
mob = mob_or_point
if direction is None:
return mob.get_center()
@ -458,7 +459,7 @@ class TangentLine(Line):
self.scale(self.length / self.get_length())
class Elbow(VMobject, metaclass=ConvertToOpenGL):
class Elbow(VMobject):
"""Two lines that create a right angle about each other: L-shape.
Parameters
@ -599,7 +600,7 @@ class Arrow(Line):
super().__init__(*args, buff=buff, stroke_width=stroke_width, **kwargs) # type: ignore[misc]
# TODO, should this be affected when
# Arrow.set_stroke is called?
self.initial_stroke_width = self.stroke_width
self.initial_stroke_width = stroke_width
self.add_tip(tip_shape=tip_shape)
self._set_stroke_width_from_length()
@ -688,19 +689,12 @@ class Arrow(Line):
def _set_stroke_width_from_length(self) -> Self:
"""Sets stroke width based on length."""
max_ratio = self.max_stroke_width_to_length_ratio
if config.renderer == RendererType.OPENGL:
# Mypy does not recognize that the self object in this case
# is a OpenGLVMobject and that the set_stroke method is
# defined here:
# mobject/opengl/opengl_vectorized_mobject.py#L248
self.set_stroke( # type: ignore[call-arg]
width=min(self.initial_stroke_width, max_ratio * self.get_length()),
recurse=False,
)
else:
self.set_stroke(
width=min(self.initial_stroke_width, max_ratio * self.get_length()),
family=False,
width=min(
self.initial_stroke_width,
[max_ratio * self.get_length()] * len(self.initial_stroke_width),
),
recurse=False,
)
return self
@ -863,7 +857,7 @@ class DoubleArrow(Arrow):
self.add_tip(at_start=True, tip_shape=tip_shape_start)
class Angle(VMobject, metaclass=ConvertToOpenGL):
class Angle(VMobject):
"""A circular arc or elbow-type mobject representing an angle of two lines.
Parameters

View file

@ -24,8 +24,12 @@ import numpy as np
from manim.constants import *
from manim.mobject.geometry.arc import ArcBetweenPoints
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.types.vectorized_mobject import VGroup, VMobject
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVGroup as VGroup,
)
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVMobject as VMobject,
)
from manim.utils.color import BLUE, WHITE, ParsableManimColor
from manim.utils.iterables import adjacent_n_tuples, adjacent_pairs
from manim.utils.qhull import QuickHull
@ -45,7 +49,7 @@ if TYPE_CHECKING:
from manim.utils.color import ParsableManimColor
class Polygram(VMobject, metaclass=ConvertToOpenGL):
class Polygram(VMobject):
"""A generalized :class:`Polygon`, allowing for disconnected sets of edges.
Parameters
@ -743,7 +747,7 @@ class RoundedRectangle(Rectangle):
self.round_corners(self.corner_radius)
class Cutout(VMobject, metaclass=ConvertToOpenGL):
class Cutout(VMobject):
"""A shape with smaller cutouts.
Parameters

View file

@ -17,9 +17,8 @@ from manim.constants import (
)
from manim.mobject.geometry.line import Line
from manim.mobject.geometry.polygram import RoundedRectangle
from manim.mobject.mobject import Mobject
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
from manim.mobject.types.vectorized_mobject import VGroup
from manim.mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVGroup as VGroup
from manim.utils.color import BLACK, PURE_YELLOW, RED, ParsableManimColor
@ -55,12 +54,10 @@ class SurroundingRectangle(RoundedRectangle):
corner_radius: float = 0.0,
**kwargs: Any,
) -> None:
from manim.mobject.mobject import Group
from manim.mobject.opengl.opengl_mobject import OpenGLGroup as Group
if not all(isinstance(mob, (Mobject, OpenGLMobject)) for mob in mobjects):
raise TypeError(
"Expected all inputs for parameter mobjects to be a Mobjects"
)
if not all(isinstance(mob, Mobject) for mob in mobjects):
raise TypeError("Expected all inputs for parameter mobjects to be Mobjects")
if isinstance(buff, tuple):
buff_x = buff[0]
@ -127,7 +124,7 @@ class BackgroundRectangle(SurroundingRectangle):
buff=buff,
**kwargs,
)
self.original_fill_opacity: float = self.fill_opacity
self.original_fill_opacity: float = self.get_fill_opacity()
def pointwise_become_partial(self, mobject: Mobject, a: Any, b: float) -> Self:
self.set_fill(opacity=b * self.original_fill_opacity)

View file

@ -20,15 +20,14 @@ import numpy as np
from manim.constants import *
from manim.mobject.geometry.arc import Circle
from manim.mobject.geometry.polygram import Square, Triangle
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.types.vectorized_mobject import VMobject
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject as VMobject
from manim.utils.space_ops import angle_of_vector
if TYPE_CHECKING:
from manim.typing import Point3D, Vector3D
class ArrowTip(VMobject, metaclass=ConvertToOpenGL):
class ArrowTip(VMobject):
r"""Base class for arrow tips.
.. seealso::

View file

@ -18,7 +18,6 @@ import numpy as np
if TYPE_CHECKING:
from typing import TypeAlias
from manim.scene.scene import Scene
from manim.typing import Point3D, Point3DLike
NxGraph: TypeAlias = nx.classes.graph.Graph | nx.classes.digraph.DiGraph
@ -27,11 +26,12 @@ from manim.animation.composition import AnimationGroup
from manim.animation.creation import Create, Uncreate
from manim.mobject.geometry.arc import Dot, LabeledDot
from manim.mobject.geometry.line import Line
from manim.mobject.mobject import Mobject, override_animate
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
from manim.mobject.opengl.opengl_mobject import (
override_animate,
)
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVGroup as VGroup
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject as VMobject
from manim.mobject.text.tex_mobject import MathTex
from manim.mobject.types.vectorized_mobject import VMobject
from manim.utils.color import BLACK
@ -476,7 +476,7 @@ def _determine_graph_layout(
) from e
class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
class GenericGraph(VMobject):
"""Abstract base class for graphs (that is, a collection of vertices
connected with edges).
@ -569,10 +569,10 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
layout: LayoutName | dict[Hashable, Point3DLike] | LayoutFunction = "spring",
layout_scale: float | tuple[float, float, float] = 2,
layout_config: dict | None = None,
vertex_type: type[Mobject] = Dot,
vertex_type: type[VMobject] = Dot,
vertex_config: dict | None = None,
vertex_mobjects: dict | None = None,
edge_type: type[Mobject] = Line,
edge_type: type[VMobject] = Line,
partitions: Sequence[Sequence[Hashable]] | None = None,
root_vertex: Hashable | None = None,
edge_config: dict | None = None,
@ -664,12 +664,12 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
raise NotImplementedError("To be implemented in concrete subclasses")
def _populate_edge_dict(
self, edges: list[tuple[Hashable, Hashable]], edge_type: type[Mobject]
self, edges: list[tuple[Hashable, Hashable]], edge_type: type[VMobject]
):
"""Helper method for populating the edges of the graph."""
raise NotImplementedError("To be implemented in concrete subclasses")
def __getitem__(self: Graph, v: Hashable) -> Mobject:
def __getitem__(self: Graph, v: Hashable) -> VMobject:
return self.vertices[v]
def _create_vertex(
@ -678,10 +678,10 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
position: Point3DLike | None = None,
label: bool = False,
label_fill_color: str = BLACK,
vertex_type: type[Mobject] = Dot,
vertex_type: type[VMobject] = Dot,
vertex_config: dict | None = None,
vertex_mobject: dict | None = None,
) -> tuple[Hashable, Point3D, dict, Mobject]:
) -> tuple[Hashable, Point3D, dict, VMobject]:
np_position: Point3D = (
self.get_center() if position is None else np.asarray(position)
)
@ -698,7 +698,7 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
label = MathTex(vertex, color=label_fill_color)
elif vertex in self._labels:
label = self._labels[vertex]
elif not isinstance(label, (Mobject, OpenGLMobject)):
elif not isinstance(label, VMobject):
label = None
base_vertex_config = copy(self.default_vertex_config)
@ -722,8 +722,8 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
vertex: Hashable,
position: Point3DLike,
vertex_config: dict,
vertex_mobject: Mobject,
) -> Mobject:
vertex_mobject: VMobject,
) -> VMobject:
if vertex in self.vertices:
raise ValueError(
f"Vertex identifier '{vertex}' is already used for a vertex in this graph.",
@ -749,10 +749,10 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
position: Point3DLike | None = None,
label: bool = False,
label_fill_color: str = BLACK,
vertex_type: type[Mobject] = Dot,
vertex_type: type[VMobject] = Dot,
vertex_config: dict | None = None,
vertex_mobject: dict | None = None,
) -> Mobject:
) -> VMobject:
"""Add a vertex to the graph.
Parameters
@ -767,7 +767,7 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
Controls whether or not the vertex is labeled. If ``False`` (the default),
the vertex is not labeled; if ``True`` it is labeled using its
names (as specified in ``vertex``) via :class:`~.MathTex`. Alternatively,
any :class:`~.Mobject` can be passed to be used as the label.
any :class:`~.VMobject` can be passed to be used as the label.
label_fill_color
Sets the fill color of the default labels generated when ``labels``
is set to ``True``. Has no effect for other values of ``label``.
@ -798,10 +798,10 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
positions: dict | None = None,
labels: bool = False,
label_fill_color: str = BLACK,
vertex_type: type[Mobject] = Dot,
vertex_type: type[VMobject] = Dot,
vertex_config: dict | None = None,
vertex_mobjects: dict | None = None,
) -> Iterable[tuple[Hashable, Point3D, dict, Mobject]]:
) -> Iterable[tuple[Hashable, Point3D, dict, VMobject]]:
if positions is None:
positions = {}
if vertex_mobjects is None:
@ -852,10 +852,10 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
positions: dict | None = None,
labels: bool = False,
label_fill_color: str = BLACK,
vertex_type: type[Mobject] = Dot,
vertex_type: type[VMobject] = Dot,
vertex_config: dict | None = None,
vertex_mobjects: dict | None = None,
):
) -> VGroup:
"""Add a list of vertices to the graph.
Parameters
@ -870,7 +870,7 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
Controls whether or not the vertex is labeled. If ``False`` (the default),
the vertex is not labeled; if ``True`` it is labeled using its
names (as specified in ``vertex``) via :class:`~.MathTex`. Alternatively,
any :class:`~.Mobject` can be passed to be used as the label.
any :class:`~.VMobject` can be passed to be used as the label.
label_fill_color
Sets the fill color of the default labels generated when ``labels``
is set to ``True``. Has no effect for other values of ``labels``.
@ -884,7 +884,7 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
values are mobjects that should be used as vertices. Overrides
all other vertex customization options.
"""
return [
return VGroup(
self._add_created_vertex(*v)
for v in self._create_vertices(
*vertices,
@ -895,29 +895,30 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
vertex_config=vertex_config,
vertex_mobjects=vertex_mobjects,
)
]
@override_animate(add_vertices)
def _add_vertices_animation(self, *args, anim_args=None, **kwargs):
if anim_args is None:
anim_args = {}
animation = anim_args.pop("animation", Create)
vertex_mobjects = self._create_vertices(*args, **kwargs)
def on_finish(scene: Scene):
for v in vertex_mobjects:
scene.remove(v[-1])
self._add_created_vertex(*v)
return AnimationGroup(
*(animation(v[-1], **anim_args) for v in vertex_mobjects),
group=self,
_on_finish=on_finish,
)
def _remove_vertex(self, vertex):
@override_animate(add_vertices)
def _add_vertices_animation(
self,
*vertices: Hashable,
anim_args: dict[str, Any] | None = None,
**kwargs: Any,
) -> AnimationGroup:
# Use introducer=False to prevent re-adding the vertices when animating them
base_anim_args = {"animation": Create, "introducer": False}
if anim_args is not None:
base_anim_args.update(anim_args)
animation = base_anim_args.pop("animation")
vertex_mobjects = self.add_vertices(*vertices, **kwargs)
return AnimationGroup(
*(
animation(vertex_mobject, **base_anim_args)
for vertex_mobject in vertex_mobjects
),
)
def _remove_vertex(self, vertex: Hashable) -> VGroup:
"""Remove a vertex (as well as all incident edges) from the graph.
Parameters
@ -951,9 +952,9 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
to_remove.append(self.vertices.pop(vertex))
self.remove(*to_remove)
return self.get_group_class()(*to_remove)
return VGroup(*to_remove)
def remove_vertices(self, *vertices):
def remove_vertices(self, *vertices: Hashable) -> VGroup:
"""Remove several vertices from the graph.
Parameters
@ -967,7 +968,8 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
::
>>> G = Graph([1, 2, 3], [(1, 2), (2, 3)])
>>> removed = G.remove_vertices(2, 3); removed
>>> removed = G.remove_vertices(2, 3)
>>> removed
VGroup(Line, Line, Dot, Dot)
>>> G
Undirected graph on 1 vertices and 0 edges
@ -976,26 +978,32 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
mobjects = []
for v in vertices:
mobjects.extend(self._remove_vertex(v).submobjects)
return self.get_group_class()(*mobjects)
return VGroup(*mobjects)
@override_animate(remove_vertices)
def _remove_vertices_animation(self, *vertices, anim_args=None):
if anim_args is None:
anim_args = {}
def _remove_vertices_animation(
self, *vertices: Hashable, anim_args: dict[str, Any] | None = None
) -> AnimationGroup:
base_anim_args = {"animation": Uncreate}
if anim_args is not None:
base_anim_args.update(anim_args)
animation = base_anim_args.pop("animation")
animation = anim_args.pop("animation", Uncreate)
mobjects = self.remove_vertices(*vertices)
vertex_and_edge_mobjects = self.remove_vertices(*vertices)
return AnimationGroup(
*(animation(mobj, **anim_args) for mobj in mobjects), group=self
*(
animation(vertex_or_edge_mobject, **anim_args)
for vertex_or_edge_mobject in vertex_and_edge_mobjects
),
introducer=True, # Reintroduce vertices and edges temporarily to animate them
)
def _add_edge(
self,
edge: tuple[Hashable, Hashable],
edge_type: type[Mobject] = Line,
edge_type: type[VMobject] = Line,
edge_config: dict | None = None,
):
) -> VGroup:
"""Add a new edge to the graph.
Parameters
@ -1039,15 +1047,17 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
self.add(edge_mobject)
added_mobjects.append(edge_mobject)
return self.get_group_class()(*added_mobjects)
return VGroup(*added_mobjects)
def add_edges(
self,
*edges: tuple[Hashable, Hashable],
edge_type: type[Mobject] = Line,
edge_config: dict | None = None,
**kwargs,
):
edge_type: type[VMobject] = Line,
edge_config: dict[str, Any]
| dict[tuple[Hashable, Hashable], dict[str, Any]]
| None = None,
**kwargs: Any,
) -> VGroup:
"""Add new edges to the graph.
Parameters
@ -1100,20 +1110,33 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
),
added_vertices,
)
return self.get_group_class()(*added_mobjects)
return VGroup(*added_mobjects)
@override_animate(add_edges)
def _add_edges_animation(self, *args, anim_args=None, **kwargs):
if anim_args is None:
anim_args = {}
animation = anim_args.pop("animation", Create)
def _add_edges_animation(
self,
*edges: tuple[Hashable, Hashable],
anim_args: dict[str, Any] | None = None,
**kwargs: Any,
) -> AnimationGroup:
# TODO: the animation is broken with introducer=False, but not passing it
# disbands the graph upon re-adding the edges and vertices. Fix this
mobjects = self.add_edges(*args, **kwargs)
# Use introducer=False to prevent re-adding the edges and vertices when animating
base_anim_args = {"animation": Create, "introducer": False}
if anim_args is not None:
base_anim_args.update(anim_args)
animation = base_anim_args.pop("animation")
edge_and_vertex_mobjects = self.add_edges(*edges, **kwargs)
return AnimationGroup(
*(animation(mobj, **anim_args) for mobj in mobjects), group=self
*(
animation(edge_or_vertex_mobject, **base_anim_args)
for edge_or_vertex_mobject in edge_and_vertex_mobjects
)
)
def _remove_edge(self, edge: tuple[Hashable]):
def _remove_edge(self, edge: tuple[Hashable]) -> VMobject:
"""Remove an edge from the graph.
Parameters
@ -1125,7 +1148,7 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
Returns
-------
Mobject
VMobject
The removed edge.
"""
@ -1140,7 +1163,7 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
self.remove(edge_mobject)
return edge_mobject
def remove_edges(self, *edges: tuple[Hashable]):
def remove_edges(self, *edges: tuple[Hashable]) -> VGroup:
"""Remove several edges from the graph.
Parameters
@ -1155,17 +1178,25 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
"""
edge_mobjects = [self._remove_edge(edge) for edge in edges]
return self.get_group_class()(*edge_mobjects)
return VGroup(*edge_mobjects)
@override_animate(remove_edges)
def _remove_edges_animation(self, *edges, anim_args=None):
if anim_args is None:
anim_args = {}
def _remove_edges_animation(
self, *edges: tuple[Hashable, Hashable], anim_args: dict[str, Any] | None = None
) -> AnimationGroup:
base_anim_args = {"animation": Uncreate}
if anim_args is not None:
base_anim_args.update(anim_args)
animation = base_anim_args.pop("animation")
animation = anim_args.pop("animation", Uncreate)
mobjects = self.remove_edges(*edges)
return AnimationGroup(*(animation(mobj, **anim_args) for mobj in mobjects))
edge_and_vertex_mobjects = self.remove_edges(*edges)
return AnimationGroup(
*(
animation(edge_or_vertex_mobject, **anim_args)
for edge_or_vertex_mobject in edge_and_vertex_mobjects
),
introducer=True, # Reintroduce edges and vertices temporarily to animate them
)
@classmethod
def from_networkx(
@ -1537,7 +1568,7 @@ class Graph(GenericGraph):
return nx.Graph()
def _populate_edge_dict(
self, edges: list[tuple[Hashable, Hashable]], edge_type: type[Mobject]
self, edges: list[tuple[Hashable, Hashable]], edge_type: type[VMobject]
):
self.edges = {
(u, v): edge_type(
@ -1562,6 +1593,9 @@ class Graph(GenericGraph):
def __repr__(self: Graph) -> str:
return f"Undirected graph on {len(self.vertices)} vertices and {len(self.edges)} edges"
def __str__(self: Graph) -> str:
return self.__repr__()
class DiGraph(GenericGraph):
"""A directed graph.
@ -1744,7 +1778,7 @@ class DiGraph(GenericGraph):
return nx.DiGraph()
def _populate_edge_dict(
self, edges: list[tuple[Hashable, Hashable]], edge_type: type[Mobject]
self, edges: list[tuple[Hashable, Hashable]], edge_type: type[VMobject]
):
self.edges = {
(u, v): edge_type(
@ -1767,7 +1801,7 @@ class DiGraph(GenericGraph):
"""
for (u, v), edge in graph.edges.items():
tip = edge.pop_tips()[0]
# Passing the Mobject instead of the vertex makes the tip
# Passing the VMobject instead of the vertex makes the tip
# stop on the bounding box of the vertex.
edge.set_points_by_ends(
graph[u],
@ -1779,3 +1813,6 @@ class DiGraph(GenericGraph):
def __repr__(self: DiGraph) -> str:
return f"Directed graph on {len(self.vertices)} vertices and {len(self.edges)} edges"
def __str__(self: DiGraph) -> str:
return self.__repr__()

View file

@ -26,17 +26,22 @@ from manim.mobject.geometry.polygram import Polygon, Rectangle, RegularPolygon
from manim.mobject.graphing.functions import ImplicitFunction, ParametricFunction
from manim.mobject.graphing.number_line import NumberLine
from manim.mobject.graphing.scale import LinearBase
from manim.mobject.mobject import Mobject
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
from manim.mobject.opengl.opengl_surface import OpenGLSurface
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVDict as VDict,
)
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVectorizedPoint as VectorizedPoint,
)
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVGroup as VGroup,
)
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVMobject as VMobject,
)
from manim.mobject.text.tex_mobject import MathTex
from manim.mobject.three_d.three_dimensions import Surface
from manim.mobject.types.vectorized_mobject import (
VDict,
VectorizedPoint,
VGroup,
VMobject,
)
from manim.utils.color import (
BLACK,
BLUE,
@ -55,7 +60,6 @@ from manim.utils.simple_functions import binary_search
from manim.utils.space_ops import angle_of_vector
if TYPE_CHECKING:
from manim.mobject.mobject import Mobject
from manim.typing import (
ManimFloat,
Point2D,
@ -975,10 +979,10 @@ class CoordinateSystem:
.. manim:: PlotSurfaceExample
:save_last_frame:
class PlotSurfaceExample(ThreeDScene):
class PlotSurfaceExample(Scene):
def construct(self):
resolution_fa = 16
self.set_camera_orientation(phi=75 * DEGREES, theta=-60 * DEGREES)
self.camera.set_orientation(theta=-60 * DEGREES, phi=75 * DEGREES)
axes = ThreeDAxes(x_range=(-3, 3, 1), y_range=(-3, 3, 1), z_range=(-5, 5, 1))
def param_trig(u, v):
x = u
@ -994,21 +998,8 @@ class CoordinateSystem:
)
self.add(axes, trig_plane)
"""
if config.renderer == RendererType.CAIRO:
if config.renderer == RendererType.OPENGL:
surface = Surface(
lambda u, v: self.c2p(u, v, function(u, v)),
u_range=u_range,
v_range=v_range,
**kwargs,
)
if colorscale:
surface.set_fill_by_value(
axes=self.copy(),
colorscale=colorscale,
axis=colorscale_axis,
)
elif config.renderer == RendererType.OPENGL:
surface = OpenGLSurface(
lambda u, v: self.c2p(u, v, function(u, v)),
u_range=u_range,
v_range=v_range,
@ -1017,6 +1008,9 @@ class CoordinateSystem:
colorscale_axis=colorscale_axis,
**kwargs,
)
elif config.renderer == RendererType.CAIRO:
# TODO: CairoSurface?
raise NotImplementedError
return surface
@ -1873,7 +1867,7 @@ class CoordinateSystem:
def _origin_shift(axis_range: Sequence[float]) -> float: ...
class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
class Axes(VGroup, CoordinateSystem):
"""Creates a set of axes.
Parameters
@ -2512,6 +2506,7 @@ class ThreeDAxes(Axes):
self.z_axis = z_axis
if config.renderer == RendererType.CAIRO:
# TODO: check in how far these methods are supported by new VMobject class
self._add_3d_pieces()
self._set_axis_shading()
@ -2573,11 +2568,11 @@ class ThreeDAxes(Axes):
.. manim:: GetYAxisLabelExample
:save_last_frame:
class GetYAxisLabelExample(ThreeDScene):
class GetYAxisLabelExample(Scene):
def construct(self):
ax = ThreeDAxes()
lab = ax.get_y_axis_label(Tex("$y$-label"))
self.set_camera_orientation(phi=2*PI/5, theta=PI/5)
self.camera.set_orientation(theta=PI/5, phi=2*PI/5)
self.add(ax, lab)
"""
positioned_label = self._get_axis_label(
@ -2623,11 +2618,11 @@ class ThreeDAxes(Axes):
.. manim:: GetZAxisLabelExample
:save_last_frame:
class GetZAxisLabelExample(ThreeDScene):
class GetZAxisLabelExample(Scene):
def construct(self):
ax = ThreeDAxes()
lab = ax.get_z_axis_label(Tex("$z$-label"))
self.set_camera_orientation(phi=2*PI/5, theta=PI/5)
self.camera.set_orientation(theta=PI/5, phi=2*PI/5)
self.add(ax, lab)
"""
positioned_label = self._get_axis_label(
@ -2674,9 +2669,9 @@ class ThreeDAxes(Axes):
.. manim:: GetAxisLabelsExample
:save_last_frame:
class GetAxisLabelsExample(ThreeDScene):
class GetAxisLabelsExample(Scene):
def construct(self):
self.set_camera_orientation(phi=2*PI/5, theta=PI/5)
self.camera.set_orientation(theta=PI/5, phi=2*PI/5)
axes = ThreeDAxes()
labels = axes.get_axis_labels(
Text("x-axis").scale(0.7), Text("y-axis").scale(0.45), Text("z-axis").scale(0.45)

View file

@ -13,8 +13,7 @@ from isosurfaces import plot_isoline
from manim import config
from manim.mobject.graphing.scale import LinearBase, _ScaleBase
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.types.vectorized_mobject import VMobject
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject as VMobject
if TYPE_CHECKING:
from typing import Any, Self
@ -25,7 +24,7 @@ if TYPE_CHECKING:
from manim.utils.color import PURE_YELLOW
class ParametricFunction(VMobject, metaclass=ConvertToOpenGL):
class ParametricFunction(VMobject):
"""A parametric curve.
Parameters
@ -66,7 +65,7 @@ class ParametricFunction(VMobject, metaclass=ConvertToOpenGL):
.. manim:: ThreeDParametricSpring
:save_last_frame:
class ThreeDParametricSpring(ThreeDScene):
class ThreeDParametricSpring(Scene):
def construct(self):
curve1 = ParametricFunction(
lambda u: (
@ -77,7 +76,7 @@ class ParametricFunction(VMobject, metaclass=ConvertToOpenGL):
).set_shade_in_3d(True)
axes = ThreeDAxes()
self.add(axes, curve1)
self.set_camera_orientation(phi=80 * DEGREES, theta=-60 * DEGREES)
self.camera.set_orientation(theta=-60 * DEGREES, phi=80 * DEGREES)
self.wait()
.. attention::
@ -237,7 +236,7 @@ class FunctionGraph(ParametricFunction):
return self.parametric_function(x)
class ImplicitFunction(VMobject, metaclass=ConvertToOpenGL):
class ImplicitFunction(VMobject):
def __init__(
self,
func: Callable[[float, float], float],

View file

@ -2,9 +2,6 @@
from __future__ import annotations
from manim.mobject.mobject import Mobject
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject
__all__ = ["NumberLine", "UnitInterval"]
@ -23,10 +20,16 @@ from manim import config
from manim.constants import *
from manim.mobject.geometry.line import Line
from manim.mobject.graphing.scale import LinearBase, _ScaleBase
from manim.mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVGroup as VGroup,
)
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVMobject as VMobject,
)
from manim.mobject.text.numbers import DecimalNumber, Integer
from manim.mobject.text.tex_mobject import MathTex, Tex
from manim.mobject.text.text_mobject import Text
from manim.mobject.types.vectorized_mobject import VGroup, VMobject
from manim.utils.bezier import interpolate
from manim.utils.config_ops import merge_dicts_recursively
from manim.utils.space_ops import normalize
@ -651,7 +654,7 @@ class NumberLine(Line):
:class:`~.VMobject`
The label.
"""
if isinstance(label_tex, (VMobject, OpenGLVMobject)):
if isinstance(label_tex, VMobject):
return label_tex
if label_constructor is None:
label_constructor = self.label_constructor

View file

@ -14,10 +14,14 @@ from manim import config, logger
from manim.constants import *
from manim.mobject.geometry.polygram import Rectangle
from manim.mobject.graphing.coordinate_systems import Axes
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVGroup as VGroup,
)
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVMobject as VMobject,
)
from manim.mobject.svg.brace import Brace
from manim.mobject.text.tex_mobject import MathTex, Tex
from manim.mobject.types.vectorized_mobject import VGroup, VMobject
from manim.typing import Vector3D
from manim.utils.color import (
BLUE_E,
@ -144,7 +148,7 @@ class SampleSpace(Rectangle):
def get_subdivision_braces_and_labels(
self,
parts: VGroup,
labels: list[str | VMobject | OpenGLVMobject],
labels: list[str | VMobject],
direction: Vector3D,
buff: float = SMALL_BUFF,
min_num_quads: int = 1,
@ -153,7 +157,7 @@ class SampleSpace(Rectangle):
braces = VGroup()
for label, part in zip(labels, parts, strict=False):
brace = Brace(part, direction, min_num_quads=min_num_quads, buff=buff)
if isinstance(label, (VMobject, OpenGLVMobject)):
if isinstance(label, VMobject):
label_mob = label
else:
label_mob = MathTex(label)
@ -174,7 +178,7 @@ class SampleSpace(Rectangle):
def get_side_braces_and_labels(
self,
labels: list[str | VMobject | OpenGLVMobject],
labels: list[str | VMobject],
direction: Vector3D = LEFT,
**kwargs: Any,
) -> VGroup:
@ -185,14 +189,14 @@ class SampleSpace(Rectangle):
)
def get_top_braces_and_labels(
self, labels: list[str | VMobject | OpenGLVMobject], **kwargs: Any
self, labels: list[str | VMobject], **kwargs: Any
) -> VGroup:
assert hasattr(self, "vertical_parts")
parts = self.vertical_parts
return self.get_subdivision_braces_and_labels(parts, labels, UP, **kwargs)
def get_bottom_braces_and_labels(
self, labels: list[str | VMobject | OpenGLVMobject], **kwargs: Any
self, labels: list[str | VMobject], **kwargs: Any
) -> VGroup:
assert hasattr(self, "vertical_parts")
parts = self.vertical_parts

View file

@ -13,7 +13,9 @@ from manim.mobject.text.numbers import Integer
if TYPE_CHECKING:
from collections.abc import Callable
from manim.mobject.types.vectorized_mobject import VMobject
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVMobject as VMobject,
)
class _ScaleBase:

View file

@ -11,7 +11,12 @@ import svgelements as se
from manim.animation.updaters.update import UpdateFromAlphaFunc
from manim.mobject.geometry.arc import Circle
from manim.mobject.geometry.polygram import Square, Triangle
from manim.mobject.mobject import Mobject
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVGroup as VGroup,
)
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVMobject as VMobject,
)
from manim.typing import Vector3D
from .. import constants as cst
@ -20,7 +25,6 @@ from ..animation.composition import AnimationGroup, Succession
from ..animation.creation import Create, SpiralIn
from ..animation.fading import FadeIn
from ..mobject.svg.svg_mobject import VMobjectFromSVGPath
from ..mobject.types.vectorized_mobject import VGroup
from ..utils.rate_functions import ease_in_out_cubic, smooth
MANIM_SVG_PATHS: list[se.Path] = [
@ -153,7 +157,7 @@ class ManimBanner(VGroup):
self.scale_factor = 1.0
self.M = VMobjectFromSVGPath(MANIM_SVG_PATHS[0]).flip(cst.RIGHT).center()
self.M.set(stroke_width=0).scale(
self.M.set_stroke(width=0).scale(
7 * cst.DEFAULT_FONT_SIZE * cst.SCALE_FACTOR_PER_FONT_POINT
)
self.M.set_fill(color=self.font_color, opacity=1).shift(
@ -170,7 +174,7 @@ class ManimBanner(VGroup):
anim = VGroup()
for ind, path in enumerate(MANIM_SVG_PATHS[1:]):
tex = VMobjectFromSVGPath(path).flip(cst.RIGHT).center()
tex.set(stroke_width=0).scale(
tex.set_stroke(width=0).scale(
cst.DEFAULT_FONT_SIZE * cst.SCALE_FACTOR_PER_FONT_POINT
)
if ind > 0:
@ -265,7 +269,7 @@ class ManimBanner(VGroup):
)
"""
if direction not in ["left", "right", "center"]:
if direction.lower() not in {"left", "right", "center"}:
raise ValueError("direction must be 'left', 'right' or 'center'.")
m_shape_offset = 6.25 * self.scale_factor
@ -292,7 +296,7 @@ class ManimBanner(VGroup):
elif direction == "left":
left_group.shift(-vector)
def slide_and_uncover(mob: Mobject, alpha: float) -> None:
def slide_and_uncover(mob: VMobject, alpha: float) -> None:
shift(alpha * (m_shape_offset + shape_sliding_overshoot) * cst.RIGHT)
# Add letters when they are covered
@ -305,11 +309,11 @@ class ManimBanner(VGroup):
if alpha == 1:
self.remove(*[self.anim])
self.add_to_back(self.anim)
mob.shapes.set_z_index(0)
mob.shapes.set_z(0)
mob.shapes.save_state()
mob.M.save_state()
def slide_back(mob: Mobject, alpha: float) -> None:
def slide_back(mob: VMobject, alpha: float) -> None:
if alpha == 0:
m_clone.set_opacity(1)
m_clone.move_to(mob.anim[-1])
@ -327,11 +331,13 @@ class ManimBanner(VGroup):
slide_and_uncover,
run_time=run_time * 2 / 3,
rate_func=ease_in_out_cubic,
introducer=True,
),
UpdateFromAlphaFunc(
self,
slide_back,
run_time=run_time * 1 / 3,
rate_func=smooth,
introducer=True,
),
)

View file

@ -45,13 +45,17 @@ from typing import Any, Self
import numpy as np
from manim.mobject.mobject import Mobject
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVGroup as VGroup,
)
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVMobject as VMobject,
)
from manim.mobject.text.numbers import DecimalNumber, Integer
from manim.mobject.text.tex_mobject import MathTex, Tex
from ..constants import *
from ..mobject.types.vectorized_mobject import VGroup, VMobject
# TO DO : The following two functions are not used in this file.
# Not sure if we should keep it or not.
@ -72,7 +76,7 @@ def matrix_to_mobject(matrix: np.ndarray) -> MathTex:
return MathTex(matrix_to_tex_string(matrix))
class Matrix(VMobject, metaclass=ConvertToOpenGL):
class Matrix(VMobject):
r"""A mobject that displays a matrix on the screen.
Parameters

View file

@ -21,12 +21,12 @@ from typing import TYPE_CHECKING, Any
import numpy as np
from manim import config, logger
from manim.constants import *
from manim.data_structures import MethodWithArgs
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from .. import config, logger
from ..constants import *
from ..utils.color import (
from manim.mobject.opengl.opengl_mobject import InvisibleMobject
from manim.utils.color import (
BLACK,
PURE_YELLOW,
WHITE,
@ -35,14 +35,15 @@ from ..utils.color import (
color_gradient,
interpolate_color,
)
from ..utils.exceptions import MultiAnimationOverrideException
from ..utils.iterables import list_update, remove_list_redundancies
from ..utils.paths import straight_path
from ..utils.space_ops import angle_between_vectors, normalize, rotation_matrix
from manim.utils.exceptions import MultiAnimationOverrideException
from manim.utils.iterables import list_update, remove_list_redundancies
from manim.utils.paths import straight_path
from manim.utils.space_ops import angle_between_vectors, normalize, rotation_matrix
if TYPE_CHECKING:
from typing import Self, TypeAlias
from manim.animation.animation import Animation
from manim.typing import (
FunctionOverride,
MappingFunction,
@ -56,8 +57,6 @@ if TYPE_CHECKING:
Vector3DLike,
)
from ..animation.animation import Animation
TimeBasedUpdater: TypeAlias = Callable[["Mobject", float], object]
NonTimeBasedUpdater: TypeAlias = Callable[["Mobject"], object]
Updater: TypeAlias = NonTimeBasedUpdater | TimeBasedUpdater
@ -151,7 +150,7 @@ class Mobject:
return self._assert_valid_submobjects_internal(submobjects, Mobject)
def _assert_valid_submobjects_internal(
self, submobjects: list[Mobject], mob_class: type[Mobject]
self, submobjects: Iterable[Mobject], mob_class: type[Mobject]
) -> Self:
for i, submob in enumerate(submobjects):
if not isinstance(submob, mob_class):
@ -269,10 +268,12 @@ class Mobject:
>>> from manim import Square, GREEN
>>> Square.set_default(color=GREEN, fill_opacity=0.25)
>>> s = Square(); s.color, s.fill_opacity
>>> s = Square()
>>> s.color, s.fill_opacity
(ManimColor('#83C167'), 0.25)
>>> Square.set_default()
>>> s = Square(); s.color, s.fill_opacity
>>> s = Square()
>>> s.color, s.fill_opacity
(ManimColor('#FFFFFF'), 0.0)
.. manim:: ChangedDefaultTextcolor
@ -716,13 +717,10 @@ class Mobject:
# Add automatic compatibility layer
# between properties and get_* and set_*
# methods.
#
# In python 3.9+ we could change this
# logic to use str.remove_prefix instead.
if attr.startswith("get_"):
# Remove the "get_" prefix
to_get = attr[4:]
to_get = attr.removeprefix("get_")
def getter(self):
warnings.warn(
@ -864,7 +862,7 @@ class Mobject:
def get_image(self, camera=None) -> PixelArray:
if camera is None:
from ..camera.camera import Camera
from manim.camera.cairo_camera import CairoCamera as Camera
camera = Camera()
camera.capture_mobject(self)
@ -981,6 +979,22 @@ class Mobject:
"dt" in inspect.signature(updater).parameters for updater in self.updaters
)
@property
def has_updaters(self) -> bool:
"""Test if ``self`` has an updater.
Returns
-------
:class:`bool`
``True`` if at least one updater is added, ``False`` otherwise.
See Also
--------
:meth:`get_updaters`
"""
return len(self.updaters) > 0
def get_updaters(self) -> list[Updater]:
"""Return all updaters.
@ -1454,6 +1468,7 @@ class Mobject:
about_point,
about_edge,
)
self.note_changed_family()
return self
def apply_function_to_position(self, function: MappingFunction) -> Self:
@ -2489,6 +2504,24 @@ class Mobject:
all_mobjects = [self] + list(it.chain(*sub_families))
return remove_list_redundancies(all_mobjects)
def get_ancestors(self, extended: bool = False) -> list[Self]:
"""Returns parents, grandparents, etc.
Base Mobject implementation returns empty list as it doesn't
track parent relationships. OpenGLMobject overrides this.
Parameters
----------
extended
If True, includes ancestors of all family members.
Returns
-------
list[Self]
List of ancestor mobjects. Empty for base Mobject.
"""
return []
def family_members_with_points(self) -> list[Self]:
"""Filters the list of family members (generated by :meth:`.get_family`) to include only mobjects with points.
@ -3297,7 +3330,7 @@ class Mobject:
return self
class Group(Mobject, metaclass=ConvertToOpenGL):
class Group(Mobject, InvisibleMobject, metaclass=ConvertToOpenGL):
"""Groups together multiple :class:`Mobjects <.Mobject>`.
Notes
@ -3312,6 +3345,45 @@ class Group(Mobject, metaclass=ConvertToOpenGL):
self.add(*mobjects)
class Point(Mobject, InvisibleMobject, metaclass=ConvertToOpenGL):
def __init__(
self,
location: np.ndarray = ORIGIN,
artificial_width: float = 1e-6,
artificial_height: float = 1e-6,
**kwargs,
):
self.artificial_width = artificial_width
self.artificial_height = artificial_height
super().__init__(**kwargs)
self.set_location(location)
@property
def width(self):
return self.artificial_width
@property
def height(self):
return self.artificial_height
# TODO: properties vs. getter methods?
def get_width(self):
return self.artificial_width
def get_height(self):
return self.artificial_height
def get_location(self):
return self.points[0].copy()
def get_bounding_box_point(self, *args, **kwargs):
return self.get_location()
def set_location(self, new_loc):
self.set_points(np.array(new_loc, ndmin=2, dtype=float))
class _AnimationBuilder:
def __init__(self, mobject) -> None:
self.mobject = mobject

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,6 @@ __all__ = ["OpenGLPMobject", "OpenGLPGroup", "OpenGLPMPoint"]
from typing import TYPE_CHECKING
import moderngl
import numpy as np
from manim.constants import *
@ -18,7 +17,6 @@ from manim.utils.color import (
color_gradient,
color_to_rgba,
)
from manim.utils.config_ops import _Uniforms
from manim.utils.iterables import resize_with_interpolation
if TYPE_CHECKING:
@ -35,25 +33,17 @@ __all__ = ["OpenGLPMobject", "OpenGLPGroup", "OpenGLPMPoint"]
class OpenGLPMobject(OpenGLMobject):
shader_folder = "true_dot"
# Scale for consistency with cairo units
OPENGL_POINT_RADIUS_SCALE_FACTOR = 0.01
shader_dtype = [
("point", np.float32, (3,)),
("color", np.float32, (4,)),
]
point_radius = _Uniforms()
def __init__(
self,
stroke_width: float = 2.0,
color: ParsableManimColor = PURE_YELLOW,
render_primitive: int = moderngl.POINTS,
**kwargs,
):
self.stroke_width = stroke_width
super().__init__(color=color, render_primitive=render_primitive, **kwargs)
super().__init__(color=color, **kwargs)
self.point_radius = (
self.stroke_width * OpenGLPMobject.OPENGL_POINT_RADIUS_SCALE_FACTOR
)
@ -148,21 +138,26 @@ class OpenGLPMobject(OpenGLMobject):
def filter_out(self, condition):
for mob in self.family_members_with_points():
to_keep = ~np.apply_along_axis(condition, 1, mob.points)
for key in mob.data:
mob.data[key] = mob.data[key][to_keep]
for attr_name in mob.get_array_attrs():
array = getattr(mob, attr_name)
filtered_array = array[to_keep]
setattr(mob, attr_name, filtered_array)
return self
def sort_points(self, function=lambda p: p[0]):
"""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:
mob.data[key] = mob.data[key][indices]
for attr_name in mob.get_array_attrs():
array = getattr(mob, attr_name)
sorted_array = array[indices]
setattr(mob, attr_name, sorted_array)
return self
def ingest_submobjects(self):
for key in self.data:
self.data[key] = np.vstack([sm.data[key] for sm in self.get_family()])
for attr_name in self.get_array_attrs():
submob_arrays = [getattr(sm, attr_name) for sm in self.get_family()]
setattr(self, attr_name, np.vstack(submob_arrays))
return self
def point_from_proportion(self, alpha):
@ -172,16 +167,12 @@ class OpenGLPMobject(OpenGLMobject):
def pointwise_become_partial(self, pmobject, a, b):
lower_index = int(a * pmobject.get_num_points())
upper_index = int(b * pmobject.get_num_points())
for key in self.data:
self.data[key] = pmobject.data[key][lower_index:upper_index]
for attr_name in self.get_array_attrs():
pmob_array = getattr(pmobject, attr_name)
partial_pmob_array = pmob_array[lower_index:upper_index]
setattr(self, attr_name, partial_pmob_array)
return self
def get_shader_data(self):
shader_data = np.zeros(len(self.points), dtype=self.shader_dtype)
self.read_data_to_shader(shader_data, "point", "points")
self.read_data_to_shader(shader_data, "color", "rgbas")
return shader_data
@staticmethod
def get_mobject_type_class():
return OpenGLPMobject

View file

@ -4,7 +4,6 @@ from collections.abc import Iterable
from pathlib import Path
from typing import TYPE_CHECKING
import moderngl
import numpy as np
from PIL import Image
@ -12,7 +11,6 @@ from manim.constants import *
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
from manim.utils.bezier import integer_interpolate, interpolate
from manim.utils.color import *
from manim.utils.config_ops import _Data, _Uniforms
from manim.utils.images import change_to_rgba_array, get_full_raster_image_path
from manim.utils.iterables import listify
from manim.utils.space_ops import normalize_along_axis
@ -25,6 +23,7 @@ if TYPE_CHECKING:
__all__ = ["OpenGLSurface", "OpenGLTexturedSurface"]
# TODO: Those will not work in the current state we will have to think about a different method to render these with shaders in our current pipeline
class OpenGLSurface(OpenGLMobject):
r"""Creates a Surface.
@ -57,14 +56,6 @@ class OpenGLSurface(OpenGLMobject):
to 1 being fully opaque. Defaults to 1.
"""
shader_dtype = [
("point", np.float32, (3,)),
("du_point", np.float32, (3,)),
("dv_point", np.float32, (3,)),
("color", np.float32, (4,)),
]
shader_folder = "surface"
def __init__(
self,
uv_func=None,
@ -85,9 +76,7 @@ class OpenGLSurface(OpenGLMobject):
# For du and dv steps. Much smaller and numerical error
# can crop up in the shaders.
epsilon=1e-5,
render_primitive=moderngl.TRIANGLES,
depth_test=True,
shader_folder=None,
**kwargs: Any,
):
self.passed_uv_func = uv_func
@ -111,8 +100,6 @@ class OpenGLSurface(OpenGLMobject):
opacity=opacity,
gloss=gloss,
shadow=shadow,
shader_folder=shader_folder if shader_folder is not None else "surface",
render_primitive=render_primitive,
depth_test=depth_test,
**kwargs,
)
@ -252,106 +239,6 @@ class OpenGLSurface(OpenGLMobject):
tri_is[k::3] = tri_is[k::3][indices]
return self
# For shaders
def get_shader_data(self):
"""Called by parent Mobject to calculate and return
the shader data.
Returns
-------
shader_dtype
An array containing the shader data (vertices and
color of each vertex)
"""
s_points, du_points, dv_points = self.get_surface_points_and_nudged_points()
shader_data = np.zeros(len(s_points), dtype=self.shader_dtype)
if "points" not in self.locked_data_keys:
shader_data["point"] = s_points
shader_data["du_point"] = du_points
shader_data["dv_point"] = dv_points
if self.colorscale:
if not hasattr(self, "color_by_val"):
self.color_by_val = self._get_color_by_value(s_points)
shader_data["color"] = self.color_by_val
else:
self.fill_in_shader_color_info(shader_data)
return shader_data
def fill_in_shader_color_info(self, shader_data):
"""Fills in the shader color data when the surface
is all one color.
Parameters
----------
shader_data
The vertices of the surface.
Returns
-------
shader_dtype
An array containing the shader data (vertices and
color of each vertex)
"""
self.read_data_to_shader(shader_data, "color", "rgbas")
return shader_data
def _get_color_by_value(self, s_points):
"""Matches each vertex to a color associated to it's z-value.
Parameters
----------
s_points
The vertices of the surface.
Returns
-------
List
A list of colors matching the vertex inputs.
"""
if type(self.colorscale[0]) in (list, tuple):
new_colors, pivots = [
[i for i, j in self.colorscale],
[j for i, j in self.colorscale],
]
else:
new_colors = self.colorscale
pivot_min = self.axes.z_range[0]
pivot_max = self.axes.z_range[1]
pivot_frequency = (pivot_max - pivot_min) / (len(new_colors) - 1)
pivots = np.arange(
start=pivot_min,
stop=pivot_max + pivot_frequency,
step=pivot_frequency,
)
return_colors = []
for point in s_points:
axis_value = self.axes.point_to_coords(point)[self.colorscale_axis]
if axis_value <= pivots[0]:
return_colors.append(color_to_rgba(new_colors[0], self.opacity))
elif axis_value >= pivots[-1]:
return_colors.append(color_to_rgba(new_colors[-1], self.opacity))
else:
for i, pivot in enumerate(pivots):
if pivot > axis_value:
color_index = (axis_value - pivots[i - 1]) / (
pivots[i] - pivots[i - 1]
)
color_index = max(min(color_index, 1), 0)
temp_color = interpolate_color(
new_colors[i - 1],
new_colors[i],
color_index,
)
break
return_colors.append(color_to_rgba(temp_color, self.opacity))
return return_colors
def get_shader_vert_indices(self):
return self.get_triangle_indices()
class OpenGLSurfaceGroup(OpenGLSurface):
def __init__(self, *parametric_surfaces, resolution=None, **kwargs):
@ -364,29 +251,14 @@ class OpenGLSurfaceGroup(OpenGLSurface):
class OpenGLTexturedSurface(OpenGLSurface):
shader_dtype = [
("point", np.float32, (3,)),
("du_point", np.float32, (3,)),
("dv_point", np.float32, (3,)),
("im_coords", np.float32, (2,)),
("opacity", np.float32, (1,)),
]
shader_folder = "textured_surface"
im_coords = _Data()
opacity = _Data()
num_textures = _Uniforms()
def __init__(
self,
uv_surface: OpenGLSurface,
image_file: str | Path | npt.NDArray,
dark_image_file: str | Path = None,
image_mode: str | Iterable[str] = "RGBA",
shader_folder: str | Path = None,
**kwargs,
):
self.uniforms = {}
if not isinstance(uv_surface, OpenGLSurface):
raise Exception("uv_surface must be of type OpenGLSurface")
if isinstance(image_file, np.ndarray):
@ -396,7 +268,8 @@ class OpenGLTexturedSurface(OpenGLSurface):
if isinstance(image_mode, (str, Path)):
image_mode = [image_mode] * 2
image_mode_light, image_mode_dark = image_mode
texture_paths = {
# TODO: move to renderer
_texture_paths = {
"LightTexture": self.get_image_from_file(
image_file,
image_mode_light,
@ -415,7 +288,7 @@ class OpenGLTexturedSurface(OpenGLSurface):
self.v_range = uv_surface.v_range
self.resolution = uv_surface.resolution
self.gloss = self.uv_surface.gloss
super().__init__(texture_paths=texture_paths, **kwargs)
super().__init__(**kwargs)
def get_image_from_file(
self,

File diff suppressed because it is too large Load diff

View file

@ -21,8 +21,8 @@ if TYPE_CHECKING:
from manim.typing import MatrixMN, Point3D
from .. import config
from ..utils import opengl
from ... import config
from ...utils import opengl
SHADER_FOLDER = Path(__file__).parent / "shaders"
shader_program_cache: dict[str, moderngl.Program] = {}

View file

@ -12,8 +12,8 @@ import svgelements as se
from manim._config import config
from manim.mobject.geometry.arc import Arc
from manim.mobject.geometry.line import Line
from manim.mobject.mobject import Mobject
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject as VMobject
from manim.mobject.text.tex_mobject import MathTex, SingleStringMathTex, Tex
from manim.mobject.text.text_mobject import Text
@ -22,7 +22,6 @@ from ...animation.composition import AnimationGroup
from ...animation.fading import FadeIn
from ...animation.growing import GrowFromCenter
from ...constants import *
from ...mobject.types.vectorized_mobject import VMobject
from ...utils.color import BLACK
from ..svg.svg_mobject import VMobjectFromSVGPath
@ -204,7 +203,7 @@ class Brace(VMobjectFromSVGPath):
return vect / np.linalg.norm(vect)
class BraceLabel(VMobject, metaclass=ConvertToOpenGL):
class BraceLabel(VMobject):
"""Create a brace with a label attached.
Parameters

View file

@ -11,7 +11,8 @@ import numpy as np
import svgelements as se
from manim import config, logger
from manim.utils.color import ManimColor, ParsableManimColor
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVGroup as VGroup
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject as VMobject
from ...constants import RIGHT
from ...utils.bezier import get_quadratic_approximation_of_cubic
@ -20,8 +21,6 @@ from ...utils.iterables import hash_obj
from ..geometry.arc import Circle
from ..geometry.line import Line
from ..geometry.polygram import Polygon, Rectangle, RoundedRectangle
from ..opengl.opengl_compatibility import ConvertToOpenGL
from ..types.vectorized_mobject import VGroup, VMobject
__all__ = ["SVGMobject", "VMobjectFromSVGPath"]
@ -33,7 +32,7 @@ def _convert_point_to_3d(x: float, y: float) -> np.ndarray:
return np.array([x, y, 0.0])
class SVGMobject(VMobject, metaclass=ConvertToOpenGL):
class SVGMobject(VMobject):
"""A vectorized mobject created from importing an SVG file.
Parameters
@ -100,36 +99,21 @@ class SVGMobject(VMobject, metaclass=ConvertToOpenGL):
should_center: bool = True,
height: float | None = 2,
width: float | None = None,
color: ParsableManimColor | None = None,
opacity: float | None = None,
fill_color: ParsableManimColor | None = None,
fill_opacity: float | None = None,
stroke_color: ParsableManimColor | None = None,
stroke_opacity: float | None = None,
stroke_width: float | None = None,
svg_default: dict | None = None,
path_string_config: dict | None = None,
use_svg_cache: bool = True,
**kwargs: Any,
):
super().__init__(color=None, stroke_color=None, fill_color=None, **kwargs)
super().__init__(**kwargs)
# process keyword arguments
self.file_name = Path(file_name) if file_name is not None else None
self.should_center = should_center
self.svg_height = height
self.svg_width = width
self.color = ManimColor(color)
self.opacity = opacity
self.fill_color = fill_color
self.fill_opacity = fill_opacity # type: ignore[assignment]
self.stroke_color = stroke_color
self.stroke_opacity = stroke_opacity # type: ignore[assignment]
self.stroke_width = stroke_width # type: ignore[assignment]
self.id_to_vgroup_dict: dict[str, VGroup] = {}
if self.stroke_width is None:
self.stroke_width = 0
if svg_default is None:
svg_default = {
@ -137,24 +121,20 @@ class SVGMobject(VMobject, metaclass=ConvertToOpenGL):
"opacity": None,
"fill_color": None,
"fill_opacity": None,
"stroke_width": 0,
"stroke_width": [0],
"stroke_color": None,
"stroke_opacity": None,
}
self.svg_default = svg_default
if path_string_config is None:
path_string_config = {}
self.path_string_config = path_string_config
self.path_string_config = path_string_config or {}
self.init_svg_mobject(use_svg_cache=use_svg_cache)
self.set_style(
fill_color=fill_color,
fill_opacity=fill_opacity,
stroke_color=stroke_color,
stroke_opacity=stroke_opacity,
stroke_width=stroke_width,
fill_color=self.fill_color,
stroke_color=self.stroke_color,
stroke_width=self.stroke_width,
)
self.move_into_position()
@ -497,7 +477,7 @@ class SVGMobject(VMobject, metaclass=ConvertToOpenGL):
self.set(width=self.svg_width)
class VMobjectFromSVGPath(VMobject, metaclass=ConvertToOpenGL):
class VMobjectFromSVGPath(VMobject):
"""A vectorized mobject representing an SVG path.
.. note::
@ -547,7 +527,6 @@ class VMobjectFromSVGPath(VMobject, metaclass=ConvertToOpenGL):
self.handle_commands()
if config.renderer == "opengl":
if self.should_subdivide_sharp_curves:
# For a healthy triangulation later
self.subdivide_sharp_curves()

View file

@ -70,6 +70,12 @@ from collections.abc import Callable, Iterable, Sequence
from manim.mobject.geometry.line import Line
from manim.mobject.geometry.polygram import Polygon
from manim.mobject.geometry.shape_matchers import BackgroundRectangle
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVGroup as VGroup,
)
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVMobject as VMobject,
)
from manim.mobject.text.numbers import DecimalNumber, Integer
from manim.mobject.text.tex_mobject import MathTex
from manim.mobject.text.text_mobject import Paragraph
@ -78,9 +84,7 @@ from ..animation.animation import Animation
from ..animation.composition import AnimationGroup
from ..animation.creation import Create, Write
from ..animation.fading import FadeIn
from ..mobject.types.vectorized_mobject import VGroup, VMobject
from ..utils.color import BLACK, PURE_YELLOW, ManimColor, ParsableManimColor
from .utils import get_vectorized_mobject_class
class Table(VGroup):
@ -323,8 +327,7 @@ class Table(VGroup):
mob_table.insert(0, col_labels)
else:
# Placeholder to use arrange_in_grid if top_left_entry is not set.
# Import OpenGLVMobject to work with --renderer=opengl
dummy_mobject = get_vectorized_mobject_class()()
dummy_mobject = VMobject()
col_labels = [dummy_mobject] + self.col_labels
mob_table.insert(0, col_labels)
else:

View file

@ -19,13 +19,17 @@ from pygments.styles import get_all_styles
from manim.constants import *
from manim.mobject.geometry.arc import Dot
from manim.mobject.geometry.shape_matchers import SurroundingRectangle
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.types.vectorized_mobject import VGroup, VMobject
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVGroup as VGroup,
)
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVMobject as VMobject,
)
from manim.typing import StrPath
from manim.utils.color import WHITE, ManimColor
class Code(VMobject, metaclass=ConvertToOpenGL):
class Code(VMobject):
"""A highlighted source code listing.
Examples

View file

@ -10,17 +10,16 @@ import numpy as np
from manim import config
from manim.constants import *
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject as VMobject
from manim.mobject.text.tex_mobject import MathTex, SingleStringMathTex, Tex
from manim.mobject.text.text_mobject import Text
from manim.mobject.types.vectorized_mobject import VMobject
from manim.mobject.value_tracker import ValueTracker
from manim.typing import Vector3DLike
string_to_mob_map: dict[str, SingleStringMathTex] = {}
class DecimalNumber(VMobject, metaclass=ConvertToOpenGL):
class DecimalNumber(VMobject):
r"""An mobject representing a decimal number.
Parameters
@ -154,6 +153,8 @@ class DecimalNumber(VMobject, metaclass=ConvertToOpenGL):
def _set_submobjects_from_number(self, number: float) -> None:
self.number = number
# the self.add below will recalculate the family,
# no need to do it here.
self.submobjects = []
num_string = self._get_num_string(number)
@ -341,7 +342,7 @@ class Integer(DecimalNumber):
return int(np.round(super().get_value()))
class Variable(VMobject, metaclass=ConvertToOpenGL):
class Variable(VMobject):
"""A class for displaying text that shows "label = value" with
the value continuously updated from a :class:`~.ValueTracker`.

View file

@ -33,13 +33,12 @@ from typing import Any, Self
from manim import config, logger
from manim.constants import *
from manim.mobject.geometry.line import Line
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVGroup as VGroup
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject as VMobject
from manim.mobject.svg.svg_mobject import SVGMobject
from manim.mobject.types.vectorized_mobject import VGroup, VMobject
from manim.utils.tex import TexTemplate
from manim.utils.tex_file_writing import tex_to_svg_file
from ..opengl.opengl_compatibility import ConvertToOpenGL
MATHTEX_SUBSTRING = "substring"
@ -67,15 +66,12 @@ class SingleStringMathTex(SVGMobject):
color: ParsableManimColor | None = None,
**kwargs: Any,
):
if color is None:
color = VMobject().color
self._font_size = font_size
self.organize_left_to_right = organize_left_to_right
self.tex_environment = tex_environment
if tex_template is None:
tex_template = config["tex_template"]
self.tex_template: TexTemplate = tex_template
tex_template = config.tex_template
self.tex_template = tex_template
self.tex_string = tex_string
file_name = tex_to_svg_file(
@ -312,7 +308,7 @@ class MathTex(SingleStringMathTex):
# Save the original tex_string
self.tex_string = self.arg_separator.join(self.tex_strings)
self._break_up_by_substrings()
except ValueError as compilation_error:
except ValueError:
if self.brace_notation_split_occurred:
logger.error(
dedent(
@ -326,7 +322,7 @@ class MathTex(SingleStringMathTex):
""",
),
)
raise compilation_error
raise
self.set_color_by_tex_to_color_map(self.tex_to_color_map)
if self.organize_left_to_right:
@ -534,6 +530,12 @@ class MathTex(SingleStringMathTex):
)
new_submobjects.append(self.id_to_vgroup_dict["root"])
self.submobjects = new_submobjects
# 5 hours of work went into this line
# and it's still not perfect
# July 18, 2024
self.note_changed_family()
return self
def get_part_by_tex(self, tex: str, **kwargs: Any) -> VGroup | None:
@ -595,9 +597,10 @@ class MathTex(SingleStringMathTex):
def sort_alphabetically(self) -> None:
self.submobjects.sort(key=lambda m: m.get_tex_string())
self.note_changed_family()
class MathTexPart(VMobject, metaclass=ConvertToOpenGL):
class MathTexPart(VMobject):
tex_string: str
def __repr__(self) -> str:

View file

@ -70,14 +70,16 @@ from manimpango import MarkupUtils, PangoUtils, TextSetting
from manim import config, logger
from manim.constants import *
from manim.mobject.geometry.arc import Dot
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVGroup as VGroup,
)
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVMobject as VMobject,
)
from manim.mobject.svg.svg_mobject import SVGMobject
from manim.mobject.types.vectorized_mobject import VGroup, VMobject
from manim.typing import Point3D
from manim.utils.color import ManimColor, ParsableManimColor, color_gradient
if TYPE_CHECKING:
from typing import Self
from manim.typing import Point3D
TEXT_MOB_SCALE_FACTOR = 0.05
@ -510,7 +512,7 @@ class Text(SVGMobject):
else:
self.line_spacing = self._font_size + self._font_size * self.line_spacing
parsed_color: ManimColor = ManimColor(color) if color else VMobject().color
parsed_color = ManimColor(color)
file_name = self._text2svg(parsed_color.to_hex())
PangoUtils.remove_last_M(file_name)
super().__init__(
@ -526,6 +528,7 @@ class Text(SVGMobject):
self.text = text
if self.disable_ligatures:
self.submobjects = [*self._gen_chars()]
self.note_changed_family()
self.chars = self.get_group_class()(*self.submobjects)
self.text = text_without_tabs.replace(" ", "").replace("\n", "")
nppc = self.n_points_per_curve
@ -588,6 +591,11 @@ class Text(SVGMobject):
# anti-aliasing
if height is None and width is None:
self.scale(TEXT_MOB_SCALE_FACTOR)
# Just a temporary hack to get better triangulation
# See pr #1552 for details
for i in self.submobjects:
i.insert_n_curves(len(i.get_all_points()))
self.initial_height = self.height
def __repr__(self) -> str:
@ -827,13 +835,6 @@ class Text(SVGMobject):
return svg_file
def init_colors(self, propagate_colors: bool = True) -> Self:
if config.renderer == RendererType.OPENGL:
super().init_colors()
elif config.renderer == RendererType.CAIRO:
super().init_colors(propagate_colors=propagate_colors)
return self
class MarkupText(SVGMobject):
r"""Display (non-LaTeX) text rendered using `Pango <https://pango.org/>`_.
@ -1208,7 +1209,7 @@ class MarkupText(SVGMobject):
else:
self.line_spacing = self._font_size + self._font_size * self.line_spacing
parsed_color: ManimColor = ManimColor(color) if color else VMobject().color
parsed_color = ManimColor(color)
file_name = self._text2svg(parsed_color)
PangoUtils.remove_last_M(file_name)

View file

@ -9,12 +9,12 @@ import numpy as np
from manim.mobject.geometry.polygram import Polygon
from manim.mobject.graph import Graph
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVGroup as VGroup
from manim.mobject.three_d.three_dimensions import Dot3D
from manim.mobject.types.vectorized_mobject import VGroup
from manim.utils.qhull import QuickHull
if TYPE_CHECKING:
from manim.mobject.mobject import Mobject
from manim.mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
from manim.typing import Point3D, Point3DLike_Array
__all__ = [
@ -52,9 +52,9 @@ class Polyhedron(VGroup):
.. manim:: SquarePyramidScene
:save_last_frame:
class SquarePyramidScene(ThreeDScene):
class SquarePyramidScene(Scene):
def construct(self):
self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)
self.camera.set_orientation(theta=30 * DEGREES, phi=75 * DEGREES)
vertex_coords = [
[1, 1, 0],
[1, -1, 0],
@ -86,9 +86,9 @@ class Polyhedron(VGroup):
.. manim:: PolyhedronSubMobjects
:save_last_frame:
class PolyhedronSubMobjects(ThreeDScene):
class PolyhedronSubMobjects(Scene):
def construct(self):
self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)
self.camera.set_orientation(theta=30 * DEGREES, phi=75 * DEGREES)
octahedron = Octahedron(edge_length = 3)
octahedron.graph[0].set_color(RED)
octahedron.faces[2].set_color(YELLOW)
@ -173,9 +173,9 @@ class Tetrahedron(Polyhedron):
.. manim:: TetrahedronScene
:save_last_frame:
class TetrahedronScene(ThreeDScene):
class TetrahedronScene(Scene):
def construct(self):
self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)
self.camera.set_orientation(theta=30 * DEGREES, phi=75 * DEGREES)
obj = Tetrahedron()
self.add(obj)
"""
@ -208,9 +208,9 @@ class Octahedron(Polyhedron):
.. manim:: OctahedronScene
:save_last_frame:
class OctahedronScene(ThreeDScene):
class OctahedronScene(Scene):
def construct(self):
self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)
self.camera.set_orientation(theta=30 * DEGREES, phi=75 * DEGREES)
obj = Octahedron()
self.add(obj)
"""
@ -254,9 +254,9 @@ class Icosahedron(Polyhedron):
.. manim:: IcosahedronScene
:save_last_frame:
class IcosahedronScene(ThreeDScene):
class IcosahedronScene(Scene):
def construct(self):
self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)
self.camera.set_orientation(theta=30 * DEGREES, phi=75 * DEGREES)
obj = Icosahedron()
self.add(obj)
"""
@ -319,9 +319,9 @@ class Dodecahedron(Polyhedron):
.. manim:: DodecahedronScene
:save_last_frame:
class DodecahedronScene(ThreeDScene):
class DodecahedronScene(Scene):
def construct(self):
self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)
self.camera.set_orientation(theta=30 * DEGREES, phi=75 * DEGREES)
obj = Dodecahedron()
self.add(obj)
"""
@ -389,9 +389,9 @@ class ConvexHull3D(Polyhedron):
:save_last_frame:
:quality: high
class ConvexHull3DExample(ThreeDScene):
class ConvexHull3DExample(Scene):
def construct(self):
self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)
self.camera.set_orientation(theta=30 * DEGREES, phi=75 * DEGREES)
points = [
[ 1.93192757, 0.44134585, -1.52407061],
[-0.93302521, 1.23206983, 0.64117067],

View file

@ -22,10 +22,11 @@ from manim.constants import ORIGIN, UP
from manim.utils.space_ops import get_unit_normal
if TYPE_CHECKING:
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVMobject as VMobject,
)
from manim.typing import Point3D, Vector3D
from ..types.vectorized_mobject import VMobject
def get_3d_vmob_gradient_start_and_end_points(
vmob: VMobject,

View file

@ -25,10 +25,16 @@ from manim import config, logger
from manim.constants import *
from manim.mobject.geometry.arc import Circle
from manim.mobject.geometry.polygram import Square
from manim.mobject.mobject import *
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
from manim.mobject.types.vectorized_mobject import VectorizedPoint, VGroup, VMobject
from manim.mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVectorizedPoint as VectorizedPoint,
)
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVGroup as VGroup,
)
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVMobject as VMobject,
)
from manim.utils.color import (
BLUE,
BLUE_D,
@ -46,7 +52,7 @@ if TYPE_CHECKING:
from manim.typing import Point3D, Point3DLike, Vector3D, Vector3DLike
class ThreeDVMobject(VMobject, metaclass=ConvertToOpenGL):
class ThreeDVMobject(VMobject):
u_index: int
v_index: int
u1: float
@ -58,7 +64,7 @@ class ThreeDVMobject(VMobject, metaclass=ConvertToOpenGL):
super().__init__(shade_in_3d=shade_in_3d, **kwargs)
class Surface(VGroup, metaclass=ConvertToOpenGL):
class Surface(VGroup):
"""Creates a Parametric Surface using a checkerboard pattern.
Parameters
@ -94,7 +100,7 @@ class Surface(VGroup, metaclass=ConvertToOpenGL):
.. manim:: ParaSurface
:save_last_frame:
class ParaSurface(ThreeDScene):
class ParaSurface(Scene):
def func(self, u, v):
return np.array([np.cos(u) * np.cos(v), np.cos(u) * np.sin(v), u])
@ -106,7 +112,7 @@ class Surface(VGroup, metaclass=ConvertToOpenGL):
v_range=[0, TAU],
resolution=8,
)
self.set_camera_orientation(theta=70 * DEGREES, phi=75 * DEGREES)
self.camera.set_orientation(theta=70 * DEGREES, phi=75 * DEGREES)
self.add(axes, surface)
"""
@ -125,6 +131,7 @@ class Surface(VGroup, metaclass=ConvertToOpenGL):
],
stroke_color: ParsableManimColor = LIGHT_GREY,
stroke_width: float = 0.5,
stroke_opacity: float = 1.0, # TODO: placed temporarily to have a stroke_opacity
should_make_jagged: bool = False,
pre_function_handle_to_anchor_scale_factor: float = 0.00001,
**kwargs: Any,
@ -136,6 +143,7 @@ class Surface(VGroup, metaclass=ConvertToOpenGL):
fill_opacity=fill_opacity,
stroke_color=stroke_color,
stroke_width=stroke_width,
stroke_opacity=stroke_opacity,
**kwargs,
)
self.resolution = resolution
@ -193,12 +201,8 @@ class Surface(VGroup, metaclass=ConvertToOpenGL):
face.u2 = u2
face.v1 = v1
face.v2 = v2
faces.set_fill(color=self.fill_color, opacity=self.fill_opacity)
faces.set_stroke(
color=self.stroke_color,
width=self.stroke_width,
opacity=self.stroke_opacity,
)
faces.set_fill(color=self.fill_color)
faces.set_stroke(color=self.stroke_color, width=self.stroke_width)
self.add(*faces)
if self.checkerboard_colors:
self.set_fill_by_checkerboard(*self.checkerboard_colors)
@ -262,16 +266,18 @@ class Surface(VGroup, metaclass=ConvertToOpenGL):
.. manim:: FillByValueExample
:save_last_frame:
class FillByValueExample(ThreeDScene):
class FillByValueExample(Scene):
def construct(self):
resolution_fa = 8
self.set_camera_orientation(phi=75 * DEGREES, theta=-160 * DEGREES)
self.camera.set_orientation(theta=-160 * DEGREES, phi=75 * DEGREES)
axes = ThreeDAxes(x_range=(0, 5, 1), y_range=(0, 5, 1), z_range=(-1, 1, 0.5))
def param_surface(u, v):
x = u
y = v
z = np.sin(x) * np.cos(y)
return z
surface_plane = Surface(
lambda u, v: axes.c2p(u, v, param_surface(u, v)),
resolution=(resolution_fa, resolution_fa),
@ -337,11 +343,7 @@ class Surface(VGroup, metaclass=ConvertToOpenGL):
new_colors[i],
color_index,
)
if config.renderer == RendererType.OPENGL:
assert isinstance(mob, OpenGLMobject)
mob.set_color(mob_color, recurse=False)
elif config.renderer == RendererType.CAIRO:
mob.set_color(mob_color, family=False)
break
return self
@ -373,9 +375,9 @@ class Sphere(Surface):
.. manim:: ExampleSphere
:save_last_frame:
class ExampleSphere(ThreeDScene):
class ExampleSphere(Scene):
def construct(self):
self.set_camera_orientation(phi=PI / 6, theta=PI / 6)
self.camera.set_orientation(theta=PI / 6, phi=PI / 6)
sphere1 = Sphere(
center=(3, 0, 0),
radius=1,
@ -457,9 +459,9 @@ class Dot3D(Sphere):
.. manim:: Dot3DExample
:save_last_frame:
class Dot3DExample(ThreeDScene):
class Dot3DExample(Scene):
def construct(self):
self.set_camera_orientation(phi=75*DEGREES, theta=-45*DEGREES)
self.camera.set_orientation(theta=-45*DEGREES, phi=75*DEGREES)
axes = ThreeDAxes()
dot_1 = Dot3D(point=axes.coords_to_point(0, 0, 1), color=RED)
@ -501,9 +503,9 @@ class Cube(VGroup):
.. manim:: CubeExample
:save_last_frame:
class CubeExample(ThreeDScene):
class CubeExample(Scene):
def construct(self):
self.set_camera_orientation(phi=75*DEGREES, theta=-45*DEGREES)
self.camera.set_orientation(theta=-45*DEGREES, phi=75*DEGREES)
axes = ThreeDAxes()
cube = Cube(side_length=3, fill_opacity=0.7, fill_color=BLUE)
@ -559,9 +561,9 @@ class Prism(Cube):
.. manim:: ExamplePrism
:save_last_frame:
class ExamplePrism(ThreeDScene):
class ExamplePrism(Scene):
def construct(self):
self.set_camera_orientation(phi=60 * DEGREES, theta=150 * DEGREES)
self.camera.set_orientation(theta=150 * DEGREES, phi=60 * DEGREES)
prismSmall = Prism(dimensions=[1, 2, 3]).rotate(PI / 2)
prismLarge = Prism(dimensions=[1.5, 3, 4.5]).move_to([2, 0, 0])
self.add(prismSmall, prismLarge)
@ -612,11 +614,11 @@ class Cone(Surface):
.. manim:: ExampleCone
:save_last_frame:
class ExampleCone(ThreeDScene):
class ExampleCone(Scene):
def construct(self):
axes = ThreeDAxes()
cone = Cone(direction=X_AXIS+Y_AXIS+2*Z_AXIS, resolution=8)
self.set_camera_orientation(phi=5*PI/11, theta=PI/9)
self.camera.set_orientation(theta=PI/9, phi=5*PI/11)
self.add(axes, cone)
"""
@ -647,8 +649,7 @@ class Cone(Surface):
self._current_phi = 0
self.base_circle = Circle(
radius=base_radius,
color=self.fill_color,
fill_opacity=self.fill_opacity,
color=self.get_fill_colors(),
stroke_width=0,
)
self.base_circle.shift(height * IN)
@ -774,11 +775,11 @@ class Cylinder(Surface):
.. manim:: ExampleCylinder
:save_last_frame:
class ExampleCylinder(ThreeDScene):
class ExampleCylinder(Scene):
def construct(self):
axes = ThreeDAxes()
cylinder = Cylinder(radius=2, height=3)
self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)
self.camera.set_orientation(theta=30 * DEGREES, phi=75 * DEGREES)
self.add(axes, cylinder)
"""
@ -829,14 +830,14 @@ class Cylinder(Surface):
def add_bases(self) -> None:
"""Adds the end caps of the cylinder."""
opacity: float
if config.renderer == RendererType.OPENGL:
assert isinstance(self, OpenGLMobject)
if config.renderer == RendererType.CAIRO:
# TODO: Surface should be made a separate mobject type
# (like it is for OpenGL) for the Cairo renderer too,
# to make them have the same interface.
raise NotImplementedError
color = self.color
opacity = self.opacity
elif config.renderer == RendererType.CAIRO:
color = self.fill_color
opacity = self.fill_opacity
self.base_top = Circle(
radius=self.radius,
@ -911,7 +912,7 @@ class Cylinder(Surface):
class Line3D(Cylinder):
"""A cylindrical line, for use in ThreeDScene.
"""A cylindrical line.
Parameters
----------
@ -935,11 +936,11 @@ class Line3D(Cylinder):
.. manim:: ExampleLine3D
:save_last_frame:
class ExampleLine3D(ThreeDScene):
class ExampleLine3D(Scene):
def construct(self):
axes = ThreeDAxes()
line = Line3D(start=np.array([0, 0, 0]), end=np.array([2, 2, 2]))
self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)
self.camera.set_orientation(theta=30 * DEGREES, phi=75 * DEGREES)
self.add(axes, line)
"""
@ -1017,7 +1018,7 @@ class Line3D(Cylinder):
:class:`numpy.array`
Center of the :class:`Mobjects <.Mobject>` or point, or edge if direction is given.
"""
if isinstance(mob_or_point, (Mobject, OpenGLMobject)):
if isinstance(mob_or_point, Mobject):
mob = mob_or_point
if direction is None:
return mob.get_center()
@ -1077,9 +1078,9 @@ class Line3D(Cylinder):
.. manim:: ParallelLineExample
:save_last_frame:
class ParallelLineExample(ThreeDScene):
class ParallelLineExample(Scene):
def construct(self):
self.set_camera_orientation(PI / 3, -PI / 4)
self.camera.set_orientation(theta=-PI / 4, phi=PI / 3)
ax = ThreeDAxes((-5, 5), (-5, 5), (-5, 5), 10, 10, 10)
line1 = Line3D(RIGHT * 2, UP + OUT, color=RED)
line2 = Line3D.parallel_to(line1, color=YELLOW)
@ -1125,9 +1126,9 @@ class Line3D(Cylinder):
.. manim:: PerpLineExample
:save_last_frame:
class PerpLineExample(ThreeDScene):
class PerpLineExample(Scene):
def construct(self):
self.set_camera_orientation(PI / 3, -PI / 4)
self.camera.set_orientation(theta=-PI / 4, phi=PI / 3)
ax = ThreeDAxes((-5, 5), (-5, 5), (-5, 5), 10, 10, 10)
line1 = Line3D(RIGHT * 2, UP + OUT, color=RED)
line2 = Line3D.perpendicular_to(line1, color=BLUE)
@ -1173,7 +1174,7 @@ class Arrow3D(Line3D):
.. manim:: ExampleArrow3D
:save_last_frame:
class ExampleArrow3D(ThreeDScene):
class ExampleArrow3D(Scene):
def construct(self):
axes = ThreeDAxes()
arrow = Arrow3D(
@ -1181,7 +1182,7 @@ class Arrow3D(Line3D):
end=np.array([2, 2, 2]),
resolution=8
)
self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)
self.camera.set_orientation(theta=30 * DEGREES, phi=75 * DEGREES)
self.add(axes, arrow)
"""
@ -1249,11 +1250,11 @@ class Torus(Surface):
.. manim :: ExampleTorus
:save_last_frame:
class ExampleTorus(ThreeDScene):
class ExampleTorus(Scene):
def construct(self):
axes = ThreeDAxes()
torus = Torus()
self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)
self.camera.set_orientation(theta=30 * DEGREES, phi=75 * DEGREES)
self.add(axes, torus)
"""

View file

@ -12,11 +12,10 @@ from PIL import Image
from PIL.Image import Resampling
from manim.mobject.geometry.shape_matchers import SurroundingRectangle
from manim.mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
from ... import config
from ...camera.moving_camera import MovingCamera
from ...constants import *
from ...mobject.mobject import Mobject
from ...utils.bezier import interpolate
from ...utils.color import (
WHITE,
@ -27,8 +26,6 @@ from ...utils.color import (
)
from ...utils.images import change_to_rgba_array, get_full_raster_image_path
__all__ = ["ImageMobject", "ImageMobjectFromCamera"]
if TYPE_CHECKING:
from typing import Self
@ -36,8 +33,6 @@ if TYPE_CHECKING:
from manim.typing import PixelArray, StrPath
from ...camera.moving_camera import MovingCamera
class AbstractImageMobject(Mobject):
"""
@ -217,6 +212,10 @@ class ImageMobject(AbstractImageMobject):
"""A simple getter method."""
return self.pixel_array
def init_colors(self) -> None:
"""Override base init_colors to avoid overwriting image pixels during init."""
return None
def set_color( # type: ignore[override]
self,
color: ParsableManimColor = YELLOW_C,

View file

@ -9,11 +9,10 @@ from typing import TYPE_CHECKING, Any
import numpy as np
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.opengl.opengl_point_cloud_mobject import OpenGLPMobject
from ...constants import *
from ...mobject.mobject import Mobject
from ...mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
from ...utils.bezier import interpolate
from ...utils.color import (
BLACK,
@ -27,7 +26,7 @@ from ...utils.color import (
)
from ...utils.iterables import stretch_array_to_length
__all__ = ["PMobject", "Mobject1D", "Mobject2D", "PGroup", "PointCloudDot", "Point"]
__all__ = ["PMobject", "Mobject1D", "Mobject2D", "PGroup", "PointCloudDot"]
if TYPE_CHECKING:
from typing import Self
@ -44,7 +43,7 @@ if TYPE_CHECKING:
)
class PMobject(Mobject, metaclass=ConvertToOpenGL):
class PMobject(Mobject):
"""A disc made of a cloud of Dots
Examples
@ -202,6 +201,7 @@ class PMobject(Mobject, metaclass=ConvertToOpenGL):
for attr, array in zip(attrs, arrays, strict=True):
setattr(self, attr, array)
self.submobjects = []
self.note_changed_family()
return self
def get_color(self) -> ManimColor:
@ -225,7 +225,7 @@ class PMobject(Mobject, metaclass=ConvertToOpenGL):
def get_point_mobject(self, center: Point3DLike | None = None) -> Point:
if center is None:
center = self.get_center()
return Point(center)
return PMobject().set_points([center])
def interpolate_color(
self, mobject1: Mobject, mobject2: Mobject, alpha: float
@ -249,7 +249,7 @@ class PMobject(Mobject, metaclass=ConvertToOpenGL):
# TODO, Make the two implementations below non-redundant
class Mobject1D(PMobject, metaclass=ConvertToOpenGL):
class Mobject1D(PMobject):
def __init__(self, density: int = DEFAULT_POINT_DENSITY_1D, **kwargs: Any) -> None:
self.density = density
self.epsilon = 1.0 / self.density
@ -273,7 +273,7 @@ class Mobject1D(PMobject, metaclass=ConvertToOpenGL):
self.add_points(points, color=color)
class Mobject2D(PMobject, metaclass=ConvertToOpenGL):
class Mobject2D(PMobject):
def __init__(self, density: int = DEFAULT_POINT_DENSITY_2D, **kwargs: Any) -> None:
self.density = density
self.epsilon = 1.0 / self.density
@ -388,39 +388,3 @@ class PointCloudDot(Mobject1D):
]
),
)
class Point(PMobject):
"""A mobject representing a point.
Examples
--------
.. manim:: ExamplePoint
:save_last_frame:
class ExamplePoint(Scene):
def construct(self):
colorList = [RED, GREEN, BLUE, YELLOW]
for i in range(200):
point = Point(location=[0.63 * np.random.randint(-4, 4), 0.37 * np.random.randint(-4, 4), 0], color=np.random.choice(colorList))
self.add(point)
for i in range(200):
point = Point(location=[0.37 * np.random.randint(-4, 4), 0.63 * np.random.randint(-4, 4), 0], color=np.random.choice(colorList))
self.add(point)
self.add(point)
"""
def __init__(
self, location: Point3DLike = ORIGIN, color: ManimColor = BLACK, **kwargs: Any
) -> None:
self.location = location
super().__init__(color=color, **kwargs)
def init_points(self) -> None:
self.reset_points()
self.generate_points()
self.set_points([self.location])
def generate_points(self) -> None:
self.add_points(np.array([self.location]))

View file

@ -38,6 +38,7 @@ from manim.utils.bezier import (
proportions_along_bezier_curve_for_point,
)
from manim.utils.color import BLACK, WHITE, ManimColor, ParsableManimColor
from manim.utils.deprecation import deprecated
from manim.utils.iterables import (
make_even,
resize_array,
@ -128,8 +129,6 @@ class VMobject(Mobject):
cap_style: CapStyleType = CapStyleType.AUTO,
**kwargs: Any,
):
self.fill_opacity = fill_opacity
self.stroke_opacity = stroke_opacity
self.stroke_width = stroke_width
if background_stroke_color is not None:
self.background_stroke_color: ManimColor = ManimColor(
@ -161,14 +160,19 @@ class VMobject(Mobject):
self.submobjects: list[VMobject]
# TODO: Find where color overwrites are happening and remove the color doubling
# if "color" in kwargs:
# fill_color = kwargs["color"]
# stroke_color = kwargs["color"]
if "color" in kwargs:
fill_color = kwargs["color"]
stroke_color = kwargs["color"]
if fill_color is not None:
self.fill_color = ManimColor.parse(fill_color)
if stroke_color is not None:
self.stroke_color = ManimColor.parse(stroke_color)
if fill_opacity is not None:
self.fill_color = self.fill_color.opacity(fill_opacity)
if stroke_opacity is not None:
self.stroke_color = self.stroke_color.opacity(stroke_opacity)
def _assert_valid_submobjects(self, submobjects: Iterable[VMobject]) -> Self:
return self._assert_valid_submobjects_internal(submobjects, VMobject)
@ -188,13 +192,11 @@ class VMobject(Mobject):
def init_colors(self, propagate_colors: bool = True) -> Self:
self.set_fill(
color=self.fill_color,
opacity=self.fill_opacity,
family=propagate_colors,
)
self.set_stroke(
color=self.stroke_color,
width=self.stroke_width,
opacity=self.stroke_opacity,
family=propagate_colors,
)
self.set_background_stroke(
@ -318,10 +320,8 @@ class VMobject(Mobject):
if family:
for submobject in self.submobjects:
submobject.set_fill(color, opacity, family)
self.update_rgbas_array("fill_rgbas", color, opacity)
self.fill_rgbas: FloatRGBA_Array
if opacity is not None:
self.fill_opacity = opacity
array_name = "fill_rgbas"
self.update_rgbas_array(array_name, color, opacity)
return self
def set_stroke(
@ -773,6 +773,18 @@ class VMobject(Mobject):
self.points: Point3D_Array = np.array(points)
return self
def set_z(self, z: float) -> Self:
self.points[..., -1] = z
return self
@deprecated(
since="0.18.2",
until="0.19.0",
message="OpenGL has no concept of z_index. Use set_z instead",
)
def set_z_index(self, z: float) -> Self:
return self.set_z(z)
def resize_points(
self,
new_length: int,
@ -2152,11 +2164,8 @@ class VGroup(VMobject, metaclass=ConvertToOpenGL):
f"submobject{'s' if len(self.submobjects) > 0 else ''}"
)
def add(
self,
*vmobjects: VMobject | Iterable[VMobject],
) -> Self:
"""Checks if all passed elements are an instance, or iterables of VMobject and then adds them to submobjects
def add(self, *vmobjects: OpenGLVMobject) -> Self:
"""Checks if all passed elements are an instance of VMobject and then add them to submobjects
Parameters
----------
@ -2474,7 +2483,7 @@ class VDict(VMobject, metaclass=ConvertToOpenGL):
my_dict.remove("square")
"""
if key not in self.submob_dict:
raise KeyError(f"The given key '{key!s}' is not present in the VDict")
raise KeyError(f"The given key {key!r} is not present in the VDict")
super().remove(self.submob_dict[key])
del self.submob_dict[key]
return self
@ -2679,6 +2688,7 @@ class VectorizedPoint(VMobject, metaclass=ConvertToOpenGL):
self.set_points(np.array([new_loc]))
# TODO: Move somewhere to match opengl
class CurvesAsSubmobjects(VGroup):
"""Convert a curve's elements to submobjects.
@ -2698,9 +2708,9 @@ class CurvesAsSubmobjects(VGroup):
def __init__(self, vmobject: VMobject, **kwargs) -> None:
super().__init__(**kwargs)
tuples = vmobject.get_cubic_bezier_tuples()
tuples = vmobject.get_bezier_tuples()
for tup in tuples:
part = VMobject()
part = OpenGLVMobject()
part.set_points(tup)
part.match_style(vmobject)
self.add(part)

View file

@ -1,107 +0,0 @@
"""Utilities for working with mobjects."""
from __future__ import annotations
__all__ = [
"get_mobject_class",
"get_point_mobject_class",
"get_vectorized_mobject_class",
]
from .._config import config
from ..constants import RendererType
from .mobject import Mobject
from .opengl.opengl_mobject import OpenGLMobject
from .opengl.opengl_point_cloud_mobject import OpenGLPMobject
from .opengl.opengl_vectorized_mobject import OpenGLVMobject
from .types.point_cloud_mobject import PMobject
from .types.vectorized_mobject import VMobject
def get_mobject_class() -> type:
"""Gets the base mobject class, depending on the currently active renderer.
.. NOTE::
This method is intended to be used in the code base of Manim itself
or in plugins where code should work independent of the selected
renderer.
Examples
--------
The function has to be explicitly imported. We test that
the name of the returned class is one of the known mobject
base classes::
>>> from manim.mobject.utils import get_mobject_class
>>> get_mobject_class().__name__ in ['Mobject', 'OpenGLMobject']
True
"""
if config.renderer == RendererType.CAIRO:
return Mobject
if config.renderer == RendererType.OPENGL:
return OpenGLMobject
raise NotImplementedError(
"Base mobjects are not implemented for the active renderer."
)
def get_vectorized_mobject_class() -> type:
"""Gets the vectorized mobject class, depending on the currently
active renderer.
.. NOTE::
This method is intended to be used in the code base of Manim itself
or in plugins where code should work independent of the selected
renderer.
Examples
--------
The function has to be explicitly imported. We test that
the name of the returned class is one of the known mobject
base classes::
>>> from manim.mobject.utils import get_vectorized_mobject_class
>>> get_vectorized_mobject_class().__name__ in ['VMobject', 'OpenGLVMobject']
True
"""
if config.renderer == RendererType.CAIRO:
return VMobject
if config.renderer == RendererType.OPENGL:
return OpenGLVMobject
raise NotImplementedError(
"Vectorized mobjects are not implemented for the active renderer."
)
def get_point_mobject_class() -> type:
"""Gets the point cloud mobject class, depending on the currently
active renderer.
.. NOTE::
This method is intended to be used in the code base of Manim itself
or in plugins where code should work independent of the selected
renderer.
Examples
--------
The function has to be explicitly imported. We test that
the name of the returned class is one of the known mobject
base classes::
>>> from manim.mobject.utils import get_point_mobject_class
>>> get_point_mobject_class().__name__ in ['PMobject', 'OpenGLPMobject']
True
"""
if config.renderer == RendererType.CAIRO:
return PMobject
if config.renderer == RendererType.OPENGL:
return OpenGLPMobject
raise NotImplementedError(
"Point cloud mobjects are not implemented for the active renderer."
)

View file

@ -8,8 +8,7 @@ from typing import TYPE_CHECKING, Any
import numpy as np
from manim.mobject.mobject import Mobject
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
from manim.utils.paths import straight_path
if TYPE_CHECKING:
@ -18,7 +17,7 @@ if TYPE_CHECKING:
from manim.typing import PathFuncType
class ValueTracker(Mobject, metaclass=ConvertToOpenGL):
class ValueTracker(Mobject):
"""A mobject that can be used for tracking (real-valued) parameters.
Useful for animating parameter changes.

View file

@ -20,15 +20,19 @@ from PIL import Image
from manim.animation.updaters.update import UpdateFromAlphaFunc
from manim.mobject.geometry.line import Vector
from manim.mobject.graphing.coordinate_systems import CoordinateSystem
from manim.mobject.opengl.opengl_mobject import OpenGLMobject as Mobject
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVGroup as VGroup,
)
from manim.mobject.opengl.opengl_vectorized_mobject import (
OpenGLVMobject as VMobject,
)
from .. import config
from ..animation.composition import AnimationGroup, Succession
from ..animation.creation import Create
from ..animation.indication import ShowPassingFlash
from ..constants import OUT, RIGHT, UP, RendererType
from ..mobject.mobject import Mobject
from ..mobject.types.vectorized_mobject import VGroup
from ..mobject.utils import get_vectorized_mobject_class
from ..utils.bezier import interpolate, inverse_interpolate
from ..utils.color import (
BLUE_E,
@ -835,7 +839,7 @@ class StreamLines(VectorField):
step = max_steps
if not step:
continue
line = get_vectorized_mobject_class()()
line = VMobject()
line.duration = step * dt
step = max(1, int(len(points) / self.max_anchors_per_line))
line.set_points_smoothly(points[::step])

View file

@ -1,18 +0,0 @@
from __future__ import annotations
import contextlib
with contextlib.suppress(ImportError):
from dearpygui import dearpygui as dpg
from manim.mobject.opengl.dot_cloud import *
from manim.mobject.opengl.opengl_image_mobject import *
from manim.mobject.opengl.opengl_mobject import *
from manim.mobject.opengl.opengl_point_cloud_mobject import *
from manim.mobject.opengl.opengl_surface import *
from manim.mobject.opengl.opengl_three_dimensions import *
from manim.mobject.opengl.opengl_vectorized_mobject import *
from ..renderer.shader import *
from ..utils.opengl import *

Some files were not shown because too many files have changed in this diff Show more