mirror of
https://github.com/ManimCommunity/manim.git
synced 2026-06-22 10:01:47 +00:00
rendering a black screen works :-)
This commit is contained in:
parent
5569c9f3d2
commit
3888d20c6c
6 changed files with 520 additions and 694 deletions
|
|
@ -183,6 +183,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.
|
||||
|
||||
|
|
|
|||
|
|
@ -295,12 +295,7 @@ class OpenGLCamera:
|
|||
|
||||
def get_raw_fbo_data(self, dtype: str = "f1") -> bytes:
|
||||
# Copy blocks from the fbo_msaa to the drawn fbo using Blit
|
||||
pw, ph = (self.pixel_width, self.pixel_height)
|
||||
gl.glBindFrameBuffer(gl.GL_READ_FRAMEBUFFER, self.fbo_msaa.glo)
|
||||
gl.glBindFrameBuffer(gl.GL_DRAW_FRAMEBUFFER, self.fbo.glo)
|
||||
gl.glBlitFramebuffer(
|
||||
0, 0, pw, ph, 0, 0, pw, ph, gl.GL_COLOR_BUFFER_BIT, gl.GL_LINEAR
|
||||
)
|
||||
self.ctx.copy_framebuffer(self.fbo_msaa, self.fbo)
|
||||
return self.fbo.read(
|
||||
viewport=self.fbo.viewport,
|
||||
components=self.n_channels,
|
||||
|
|
|
|||
|
|
@ -1,21 +1,31 @@
|
|||
from __future__ import annotations
|
||||
|
||||
|
||||
import moderngl_window as mglw
|
||||
import numpy as np
|
||||
|
||||
from moderngl_window.context.pyglet.window import Window as PygletWindow
|
||||
from moderngl_window.timers.clock import Timer
|
||||
from screeninfo import get_monitors
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from .. import __version__, config
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import manim.scene as m_scene
|
||||
|
||||
|
||||
class Window(PygletWindow):
|
||||
fullscreen = False
|
||||
resizable = True
|
||||
gl_version = (3, 3)
|
||||
vsync = True
|
||||
cursor = True
|
||||
fullscreen: bool = False
|
||||
resizable: bool = True
|
||||
gl_version: tuple[int, int] = (3, 3)
|
||||
vsync: bool = True
|
||||
cursor: bool = True
|
||||
|
||||
def __init__(self, scene: m_scene.Scene, size=config.window_size):
|
||||
# TODO: remove size argument from window init,
|
||||
# move size computation below to config
|
||||
|
||||
def __init__(self, renderer, size=config.window_size, **kwargs):
|
||||
monitors = get_monitors()
|
||||
mon_index = config.window_monitor
|
||||
monitor = monitors[min(mon_index, len(monitors) - 1)]
|
||||
|
|
@ -38,71 +48,33 @@ class Window(PygletWindow):
|
|||
|
||||
super().__init__(size=size)
|
||||
|
||||
self.title = f"Manim Community {__version__}"
|
||||
self.scene = scene
|
||||
self.pressed_keys = set()
|
||||
self.title = self.title = f"Manim Community {__version__} - {str(scene)}"
|
||||
self.size = size
|
||||
self.renderer = renderer
|
||||
|
||||
mglw.activate_context(window=self)
|
||||
self.timer = Timer()
|
||||
self.config = mglw.WindowConfig(ctx=self.ctx, wnd=self, timer=self.timer)
|
||||
self.timer.start()
|
||||
|
||||
self.swap_buffers()
|
||||
|
||||
initial_position = self.find_initial_position(size, monitor)
|
||||
# No idea why, but when self.position is set once
|
||||
# it sometimes doesn't actually change the position
|
||||
# to the specified tuple on the rhs, but doing it
|
||||
# twice seems to make it work. ¯\_(ツ)_/¯
|
||||
initial_position = self.find_initial_position(size)
|
||||
self.position = initial_position
|
||||
self.position = initial_position
|
||||
|
||||
# Delegate event handling to scene.
|
||||
def on_mouse_motion(self, x, y, dx, dy):
|
||||
super().on_mouse_motion(x, y, dx, dy)
|
||||
point = self.renderer.pixel_coords_to_space_coords(x, y)
|
||||
d_point = self.renderer.pixel_coords_to_space_coords(dx, dy, relative=True)
|
||||
self.renderer.scene.on_mouse_motion(point, d_point)
|
||||
|
||||
def on_mouse_scroll(self, x, y, x_offset: float, y_offset: float):
|
||||
super().on_mouse_scroll(x, y, x_offset, y_offset)
|
||||
point = self.renderer.pixel_coords_to_space_coords(x, y)
|
||||
offset = self.renderer.pixel_coords_to_space_coords(
|
||||
x_offset,
|
||||
y_offset,
|
||||
relative=True,
|
||||
)
|
||||
self.renderer.scene.on_mouse_scroll(point, offset)
|
||||
|
||||
def on_key_press(self, symbol, modifiers):
|
||||
self.renderer.pressed_keys.add(symbol)
|
||||
super().on_key_press(symbol, modifiers)
|
||||
self.renderer.scene.on_key_press(symbol, modifiers)
|
||||
|
||||
def on_key_release(self, symbol, modifiers):
|
||||
if symbol in self.renderer.pressed_keys:
|
||||
self.renderer.pressed_keys.remove(symbol)
|
||||
super().on_key_release(symbol, modifiers)
|
||||
self.renderer.scene.on_key_release(symbol, modifiers)
|
||||
|
||||
def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
|
||||
super().on_mouse_drag(x, y, dx, dy, buttons, modifiers)
|
||||
point = self.renderer.pixel_coords_to_space_coords(x, y)
|
||||
d_point = self.renderer.pixel_coords_to_space_coords(dx, dy, relative=True)
|
||||
self.renderer.scene.on_mouse_drag(point, d_point, buttons, modifiers)
|
||||
|
||||
def find_initial_position(self, size, monitor):
|
||||
def find_initial_position(self, size: tuple[int, int]) -> tuple[int, int]:
|
||||
custom_position = config.window_position
|
||||
monitors = get_monitors()
|
||||
mon_index = config.window_monitor
|
||||
monitor = monitors[min(mon_index, len(monitors) - 1)]
|
||||
window_width, window_height = size
|
||||
# Position might be specified with a string of the form
|
||||
# x,y for integers x and y
|
||||
if len(custom_position) == 1:
|
||||
raise ValueError(
|
||||
"window_position must specify both Y and X positions (Y/X -> UR). Also accepts LEFT/RIGHT/ORIGIN/UP/DOWN.",
|
||||
)
|
||||
# in the form Y/X (UR)
|
||||
if custom_position in ["LEFT", "RIGHT"]:
|
||||
custom_position = "O" + custom_position[0]
|
||||
elif custom_position in ["UP", "DOWN"]:
|
||||
custom_position = custom_position[0] + "O"
|
||||
elif custom_position == "ORIGIN":
|
||||
custom_position = "O" * 2
|
||||
elif "," in custom_position:
|
||||
if "," in custom_position:
|
||||
return tuple(map(int, custom_position.split(",")))
|
||||
|
||||
# Alternatively, it might be specified with a string like
|
||||
|
|
@ -110,18 +82,80 @@ class Window(PygletWindow):
|
|||
char_to_n = {"L": 0, "U": 0, "O": 1, "R": 2, "D": 2}
|
||||
width_diff = monitor.width - window_width
|
||||
height_diff = monitor.height - window_height
|
||||
|
||||
return (
|
||||
monitor.x + char_to_n[custom_position[1]] * width_diff // 2,
|
||||
-monitor.y + char_to_n[custom_position[0]] * height_diff // 2,
|
||||
)
|
||||
|
||||
def on_mouse_press(self, x, y, button, modifiers):
|
||||
super().on_mouse_press(x, y, button, modifiers)
|
||||
point = self.renderer.pixel_coords_to_space_coords(x, y)
|
||||
mouse_button_map = {
|
||||
1: "LEFT",
|
||||
2: "MOUSE",
|
||||
4: "RIGHT",
|
||||
}
|
||||
self.renderer.scene.on_mouse_press(point, mouse_button_map[button], modifiers)
|
||||
# Delegate event handling to scene
|
||||
def pixel_coords_to_space_coords(
|
||||
self, px: int, py: int, relative: bool = False
|
||||
) -> np.ndarray:
|
||||
pw, ph = self.size
|
||||
fw, fh = self.scene.camera.get_frame_shape()
|
||||
fc = self.scene.camera.get_frame_center()
|
||||
if relative:
|
||||
return np.array([px / pw, py / ph, 0])
|
||||
else:
|
||||
return np.array(
|
||||
[fc[0] + px * fw / pw - fw / 2, fc[1] + py * fh / ph - fh / 2, 0]
|
||||
)
|
||||
|
||||
def on_mouse_motion(self, x: int, y: int, dx: int, dy: int) -> None:
|
||||
super().on_mouse_motion(x, y, dx, dy)
|
||||
point = self.pixel_coords_to_space_coords(x, y)
|
||||
d_point = self.pixel_coords_to_space_coords(dx, dy, relative=True)
|
||||
self.scene.on_mouse_motion(point, d_point)
|
||||
|
||||
def on_mouse_drag(
|
||||
self, x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int
|
||||
) -> None:
|
||||
super().on_mouse_drag(x, y, dx, dy, buttons, modifiers)
|
||||
point = self.pixel_coords_to_space_coords(x, y)
|
||||
d_point = self.pixel_coords_to_space_coords(dx, dy, relative=True)
|
||||
self.scene.on_mouse_drag(point, d_point, buttons, modifiers)
|
||||
|
||||
def on_mouse_press(self, x: int, y: int, button: int, mods: int) -> None:
|
||||
super().on_mouse_press(x, y, button, mods)
|
||||
point = self.pixel_coords_to_space_coords(x, y)
|
||||
self.scene.on_mouse_press(point, button, mods)
|
||||
|
||||
def on_mouse_release(self, x: int, y: int, button: int, mods: int) -> None:
|
||||
super().on_mouse_release(x, y, button, mods)
|
||||
point = self.pixel_coords_to_space_coords(x, y)
|
||||
self.scene.on_mouse_release(point, button, mods)
|
||||
|
||||
def on_mouse_scroll(self, x: int, y: int, x_offset: float, y_offset: float) -> None:
|
||||
super().on_mouse_scroll(x, y, x_offset, y_offset)
|
||||
point = self.pixel_coords_to_space_coords(x, y)
|
||||
offset = self.pixel_coords_to_space_coords(x_offset, y_offset, relative=True)
|
||||
self.scene.on_mouse_scroll(point, offset)
|
||||
|
||||
def on_key_press(self, symbol: int, modifiers: int) -> None:
|
||||
self.pressed_keys.add(symbol) # Modifiers?
|
||||
super().on_key_press(symbol, modifiers)
|
||||
self.scene.on_key_press(symbol, modifiers)
|
||||
|
||||
def on_key_release(self, symbol: int, modifiers: int) -> None:
|
||||
self.pressed_keys.difference_update({symbol}) # Modifiers?
|
||||
super().on_key_release(symbol, modifiers)
|
||||
self.scene.on_key_release(symbol, modifiers)
|
||||
|
||||
def on_resize(self, width: int, height: int) -> None:
|
||||
super().on_resize(width, height)
|
||||
self.scene.on_resize(width, height)
|
||||
|
||||
def on_show(self) -> None:
|
||||
super().on_show()
|
||||
self.scene.on_show()
|
||||
|
||||
def on_hide(self) -> None:
|
||||
super().on_hide()
|
||||
self.scene.on_hide()
|
||||
|
||||
def on_close(self) -> None:
|
||||
super().on_close()
|
||||
self.scene.on_close()
|
||||
|
||||
def is_key_pressed(self, symbol: int) -> bool:
|
||||
return symbol in self.pressed_keys
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ from tqdm import tqdm as ProgressDisplay
|
|||
|
||||
from manim._config import logger as log
|
||||
from manim.animation.animation import prepare_animation
|
||||
from manim.camera.camera import Camera
|
||||
from manim.renderer.opengl_renderer import OpenGLCamera as Camera
|
||||
from manim.constants import DEFAULT_WAIT_TIME
|
||||
from manim.event_handler import EVENT_DISPATCHER
|
||||
from manim.event_handler.event_type import EventType
|
||||
|
|
@ -101,7 +101,7 @@ class Scene:
|
|||
|
||||
# Core state of the scene
|
||||
self.camera: Camera = Camera(**self.camera_config)
|
||||
self.file_writer = SceneFileWriter(self, **self.file_writer_config)
|
||||
self.file_writer = SceneFileWriter(self, str(self), **self.file_writer_config)
|
||||
self.mobjects: list[Mobject] = [self.camera.frame]
|
||||
self.id_to_mobject_map: dict[int, Mobject] = {}
|
||||
self.num_plays: int = 0
|
||||
|
|
@ -147,6 +147,8 @@ class Scene:
|
|||
self.file_writer.ended_with_interrupt = True
|
||||
self.tear_down()
|
||||
|
||||
render = run
|
||||
|
||||
def setup(self) -> None:
|
||||
"""
|
||||
This is meant to be implement by any scenes which
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -18,6 +18,7 @@ __all__ = [
|
|||
"ensure_executable",
|
||||
]
|
||||
|
||||
import numpy as np
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
|
|
@ -292,3 +293,35 @@ def copy_template_files(
|
|||
copyfile(template_scene_path, Path.resolve(project_dir / "main.py"))
|
||||
console.print("\n\t[green]copied[/green] [blue]main.py[/blue]\n")
|
||||
add_import_statement(Path.resolve(project_dir / "main.py"))
|
||||
|
||||
|
||||
def get_sorted_integer_files(
|
||||
directory: str,
|
||||
min_index: float = 0,
|
||||
max_index: float = np.inf,
|
||||
remove_non_integer_files: bool = False,
|
||||
remove_indices_greater_than: float | None = None,
|
||||
extension: str | None = None,
|
||||
) -> list[str]:
|
||||
indexed_files = []
|
||||
for file in os.listdir(directory):
|
||||
if "." in file:
|
||||
index_str = file[: file.index(".")]
|
||||
else:
|
||||
index_str = file
|
||||
|
||||
full_path = os.path.join(directory, file)
|
||||
if index_str.isdigit():
|
||||
index = int(index_str)
|
||||
if remove_indices_greater_than is not None:
|
||||
if index > remove_indices_greater_than:
|
||||
os.remove(full_path)
|
||||
continue
|
||||
if extension is not None and not file.endswith(extension):
|
||||
continue
|
||||
if index >= min_index and index < max_index:
|
||||
indexed_files.append((index, file))
|
||||
elif remove_non_integer_files:
|
||||
os.remove(full_path)
|
||||
indexed_files.sort(key=lambda p: p[0])
|
||||
return list(map(lambda p: os.path.join(directory, p[1]), indexed_files))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue