Implemented :meth:.MovingCamera.auto_zoom for automatically zooming onto specified mobjects (#2162)

* feature request #1056

Added auto_zoom to moving_camera.
Fixed is_in_frame.

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

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

* Fixed indentation

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

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

* Removed faulty comment

Keeps failing automated test so i removed it.

* Update manim/camera/moving_camera.py

padding vs. margin :-)

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

* Update manim/camera/moving_camera.py

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

* Update manim/camera/moving_camera.py

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

* Update manim/camera/moving_camera.py

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

* Update manim/camera/moving_camera.py

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

* Tidied the code

snake_casing >  camelCasing :-)
or > |
and > &

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

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

* Update manim/camera/moving_camera.py

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

* Update tests/test_auto_zoom.py

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

* [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

* Update manim/camera/moving_camera.py

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

* fixed snake_casing and removed unused imports

* Update manim/camera/moving_camera.py

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

* remove above comment

* Update tests/test_auto_zoom.py

* Update tests/test_auto_zoom.py

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Darylgolden <darylgolden@gmail.com>
Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>
This commit is contained in:
Gustav-Rixon 2021-11-01 08:48:43 +01:00 committed by GitHub
commit 59d4ea9888
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 101 additions and 6 deletions

View file

@ -454,10 +454,10 @@ class Camera:
return not reduce(
op.or_,
[
mobject.get_right()[0] < fc[0] - fw,
mobject.get_bottom()[1] > fc[1] + fh,
mobject.get_left()[0] > fc[0] + fw,
mobject.get_top()[1] < fc[1] - fh,
mobject.get_right()[0] < fc[0] - fw / 2,
mobject.get_bottom()[1] > fc[1] + fh / 2,
mobject.get_left()[0] > fc[0] + fw / 2,
mobject.get_top()[1] < fc[1] - fh / 2,
],
)

View file

@ -8,10 +8,9 @@
__all__ = ["CameraFrame", "MovingCamera"]
from .. import config
from ..camera.camera import Camera
from ..constants import ORIGIN
from ..constants import DOWN, LEFT, ORIGIN, RIGHT, UP
from ..mobject.frame import ScreenRectangle
from ..mobject.types.vectorized_mobject import VGroup
from ..utils.color import WHITE
@ -173,3 +172,76 @@ class MovingCamera(Camera):
list
"""
return [self.frame]
def auto_zoom(self, mobjects, margin=0, only_mobjects_in_frame=False):
"""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.
Returns
-------
_AnimationBuilder
Returns an animation that zooms the camera view to a given
list of mobjects.
"""
scene_critical_x_left = None
scene_critical_x_right = None
scene_critical_y_up = None
scene_critical_y_down = None
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 scene_critical_x_left == None:
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]
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]
# 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)
# 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 self.frame.animate.set_x(x).set_y(y).set(width=new_width + margin)
else:
return self.frame.animate.set_x(x).set_y(y).set(height=new_height + margin)

23
tests/test_auto_zoom.py Normal file
View file

@ -0,0 +1,23 @@
from manim import *
def test_zoom():
s1 = Square()
s1.set_x(-10)
s2 = Square()
s2.set_x(10)
with tempconfig({"dry_run": True, "quality": "low_quality"}):
scene = MovingCameraScene()
scene.add(s1, s2)
scene.play(scene.camera.auto_zoom([s1, s2]))
assert (
scene.camera.frame_width
== abs(
s1.get_left()[0] - s2.get_right()[0],
)
and scene.camera.frame.get_center()[0]
== (abs(s1.get_center()[0] + s2.get_center()[0]) / 2)
)