2022-02-15 14:55:35 +08:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2021-08-24 11:26:43 -07:00
|
|
|
import numpy as np
|
2020-02-11 19:51:19 -08:00
|
|
|
import moderngl_window as mglw
|
|
|
|
from moderngl_window.context.pyglet.window import Window as PygletWindow
|
2020-02-13 10:50:38 -08:00
|
|
|
from moderngl_window.timers.clock import Timer
|
2021-01-19 11:35:25 -08:00
|
|
|
from screeninfo import get_monitors
|
2020-02-11 19:51:19 -08:00
|
|
|
|
2020-02-13 10:50:38 -08:00
|
|
|
from manimlib.utils.config_ops import digest_config
|
2021-01-19 11:35:25 -08:00
|
|
|
from manimlib.utils.customization import get_customization
|
2020-02-11 19:51:19 -08:00
|
|
|
|
2022-02-15 14:55:35 +08:00
|
|
|
from typing import TYPE_CHECKING
|
2022-02-16 21:08:25 +08:00
|
|
|
|
2022-02-15 14:55:35 +08:00
|
|
|
if TYPE_CHECKING:
|
|
|
|
from manimlib.scene.scene import Scene
|
|
|
|
|
2020-02-11 19:51:19 -08:00
|
|
|
|
|
|
|
class Window(PygletWindow):
|
|
|
|
fullscreen = False
|
|
|
|
resizable = True
|
|
|
|
gl_version = (3, 3)
|
|
|
|
vsync = True
|
|
|
|
cursor = True
|
|
|
|
|
2022-02-15 14:55:35 +08:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
scene: Scene,
|
|
|
|
size: tuple[int, int] = (1280, 720),
|
|
|
|
**kwargs
|
|
|
|
):
|
2021-06-04 17:07:41 -07:00
|
|
|
super().__init__(size=size)
|
2021-01-02 20:47:51 -08:00
|
|
|
digest_config(self, kwargs)
|
2021-02-10 14:48:00 -08:00
|
|
|
|
2021-02-11 12:20:17 -08:00
|
|
|
self.scene = scene
|
|
|
|
self.pressed_keys = set()
|
|
|
|
self.title = str(scene)
|
|
|
|
self.size = size
|
|
|
|
|
2021-02-10 14:48:00 -08:00
|
|
|
mglw.activate_context(window=self)
|
|
|
|
self.timer = Timer()
|
|
|
|
self.config = mglw.WindowConfig(ctx=self.ctx, wnd=self, timer=self.timer)
|
|
|
|
self.timer.start()
|
|
|
|
|
2021-02-09 10:53:26 -08:00
|
|
|
# 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. ¯\_(ツ)_/¯
|
2021-02-12 13:34:33 -08:00
|
|
|
initial_position = self.find_initial_position(size)
|
2021-02-09 10:53:26 -08:00
|
|
|
self.position = initial_position
|
|
|
|
self.position = initial_position
|
2021-01-06 12:47:13 -08:00
|
|
|
|
2022-02-15 14:55:35 +08:00
|
|
|
def find_initial_position(self, size: tuple[int, int]) -> tuple[int, int]:
|
2021-01-19 11:35:25 -08:00
|
|
|
custom_position = get_customization()["window_position"]
|
2021-03-18 17:32:45 -07:00
|
|
|
monitors = get_monitors()
|
|
|
|
mon_index = get_customization()["window_monitor"]
|
|
|
|
monitor = monitors[min(mon_index, len(monitors) - 1)]
|
2021-02-12 13:34:33 -08:00
|
|
|
window_width, window_height = size
|
2021-01-19 11:35:25 -08:00
|
|
|
# Position might be specified with a string of the form
|
|
|
|
# x,y for integers x and y
|
|
|
|
if "," in custom_position:
|
|
|
|
return tuple(map(int, custom_position.split(",")))
|
|
|
|
|
|
|
|
# Alternatively, it might be specified with a string like
|
2021-08-07 22:25:26 +07:00
|
|
|
# UR, OO, DL, etc. specifying what corner it should go to
|
2021-01-19 11:35:25 -08:00
|
|
|
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 (
|
2021-02-09 10:53:26 -08:00
|
|
|
monitor.x + char_to_n[custom_position[1]] * width_diff // 2,
|
|
|
|
-monitor.y + char_to_n[custom_position[0]] * height_diff // 2,
|
2021-01-19 11:35:25 -08:00
|
|
|
)
|
|
|
|
|
2020-02-11 19:51:19 -08:00
|
|
|
# Delegate event handling to scene
|
2022-02-15 14:55:35 +08:00
|
|
|
def pixel_coords_to_space_coords(
|
|
|
|
self,
|
|
|
|
px: int,
|
|
|
|
py: int,
|
|
|
|
relative: bool = False
|
|
|
|
) -> np.ndarray:
|
2021-08-24 11:26:43 -07:00
|
|
|
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
|
|
|
|
])
|
2020-02-11 19:51:19 -08:00
|
|
|
|
2022-02-15 14:55:35 +08:00
|
|
|
def on_mouse_motion(self, x: int, y: int, dx: int, dy: int) -> None:
|
2020-02-11 19:51:19 -08:00
|
|
|
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)
|
|
|
|
|
2022-02-15 14:55:35 +08:00
|
|
|
def on_mouse_drag(self, x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int) -> None:
|
2020-02-11 19:51:19 -08:00
|
|
|
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)
|
2020-02-14 10:52:39 -08:00
|
|
|
self.scene.on_mouse_drag(point, d_point, buttons, modifiers)
|
2020-02-11 19:51:19 -08:00
|
|
|
|
2022-02-15 14:55:35 +08:00
|
|
|
def on_mouse_press(self, x: int, y: int, button: int, mods: int) -> None:
|
2020-02-11 19:51:19 -08:00
|
|
|
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)
|
|
|
|
|
2022-02-15 14:55:35 +08:00
|
|
|
def on_mouse_release(self, x: int, y: int, button: int, mods: int) -> None:
|
2020-02-11 19:51:19 -08:00
|
|
|
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)
|
|
|
|
|
2022-02-15 14:55:35 +08:00
|
|
|
def on_mouse_scroll(self, x: int, y: int, x_offset: float, y_offset: float) -> None:
|
2020-02-11 19:51:19 -08:00
|
|
|
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)
|
|
|
|
|
2022-02-15 14:55:35 +08:00
|
|
|
def on_key_press(self, symbol: int, modifiers: int) -> None:
|
2021-01-06 12:47:13 -08:00
|
|
|
self.pressed_keys.add(symbol) # Modifiers?
|
2020-02-11 19:51:19 -08:00
|
|
|
super().on_key_press(symbol, modifiers)
|
|
|
|
self.scene.on_key_press(symbol, modifiers)
|
|
|
|
|
2022-02-15 14:55:35 +08:00
|
|
|
def on_key_release(self, symbol: int, modifiers: int) -> None:
|
2021-01-06 12:47:13 -08:00
|
|
|
self.pressed_keys.difference_update({symbol}) # Modifiers?
|
|
|
|
super().on_key_release(symbol, modifiers)
|
|
|
|
self.scene.on_key_release(symbol, modifiers)
|
|
|
|
|
2022-02-15 14:55:35 +08:00
|
|
|
def on_resize(self, width: int, height: int) -> None:
|
2020-02-11 19:51:19 -08:00
|
|
|
super().on_resize(width, height)
|
|
|
|
self.scene.on_resize(width, height)
|
|
|
|
|
2022-02-15 14:55:35 +08:00
|
|
|
def on_show(self) -> None:
|
2020-02-11 19:51:19 -08:00
|
|
|
super().on_show()
|
|
|
|
self.scene.on_show()
|
|
|
|
|
2022-02-15 14:55:35 +08:00
|
|
|
def on_hide(self) -> None:
|
2020-02-11 19:51:19 -08:00
|
|
|
super().on_hide()
|
|
|
|
self.scene.on_hide()
|
|
|
|
|
2022-02-15 14:55:35 +08:00
|
|
|
def on_close(self) -> None:
|
2020-02-11 19:51:19 -08:00
|
|
|
super().on_close()
|
|
|
|
self.scene.on_close()
|
2021-01-06 12:47:13 -08:00
|
|
|
|
2022-02-15 14:55:35 +08:00
|
|
|
def is_key_pressed(self, symbol: int) -> bool:
|
2021-01-06 12:47:13 -08:00
|
|
|
return (symbol in self.pressed_keys)
|