rendering a black screen works :-)

This commit is contained in:
Benjamin Hackl 2023-01-03 19:07:51 +01:00
commit 3888d20c6c
6 changed files with 520 additions and 694 deletions

View file

@ -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.

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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))