mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
Basic preview window
This commit is contained in:
parent
2cf21fd0ad
commit
960e918e61
3 changed files with 233 additions and 95 deletions
|
@ -26,11 +26,12 @@ class CameraFrame(Mobject):
|
||||||
"center": ORIGIN,
|
"center": ORIGIN,
|
||||||
}
|
}
|
||||||
|
|
||||||
def generate_points(self):
|
def init_points(self):
|
||||||
self.points = np.array([UL, UR, DR, DL])
|
self.points = np.array([UL, UR, DR, DL])
|
||||||
self.set_width(self.width, stretch=True)
|
self.set_width(self.width, stretch=True)
|
||||||
self.set_height(self.height, stretch=True)
|
self.set_height(self.height, stretch=True)
|
||||||
self.move_to(self.center)
|
self.move_to(self.center)
|
||||||
|
self.save_state()
|
||||||
|
|
||||||
|
|
||||||
class Camera(object):
|
class Camera(object):
|
||||||
|
@ -43,7 +44,7 @@ class Camera(object):
|
||||||
},
|
},
|
||||||
"pixel_height": DEFAULT_PIXEL_HEIGHT,
|
"pixel_height": DEFAULT_PIXEL_HEIGHT,
|
||||||
"pixel_width": DEFAULT_PIXEL_WIDTH,
|
"pixel_width": DEFAULT_PIXEL_WIDTH,
|
||||||
"frame_rate": DEFAULT_FRAME_RATE,
|
"frame_rate": DEFAULT_FRAME_RATE, # TODO, move this elsewhere
|
||||||
# Note: frame height and width will be resized to match
|
# Note: frame height and width will be resized to match
|
||||||
# the pixel aspect ratio
|
# the pixel aspect ratio
|
||||||
"background_color": BLACK,
|
"background_color": BLACK,
|
||||||
|
@ -55,37 +56,37 @@ class Camera(object):
|
||||||
"n_channels": 4,
|
"n_channels": 4,
|
||||||
"pixel_array_dtype": 'uint8',
|
"pixel_array_dtype": 'uint8',
|
||||||
"line_width_multiple": 0.01,
|
"line_width_multiple": 0.01,
|
||||||
"background_fbo": None,
|
"window": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, background=None, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
digest_config(self, kwargs, locals())
|
digest_config(self, kwargs, locals())
|
||||||
|
self.background_fbo = None
|
||||||
self.rgb_max_val = np.iinfo(self.pixel_array_dtype).max
|
self.rgb_max_val = np.iinfo(self.pixel_array_dtype).max
|
||||||
self.init_frame()
|
self.init_frame()
|
||||||
self.init_context()
|
self.init_context()
|
||||||
self.init_frame_buffer()
|
|
||||||
self.init_shaders()
|
self.init_shaders()
|
||||||
|
|
||||||
def init_frame(self):
|
def init_frame(self):
|
||||||
self.frame = CameraFrame(**self.frame_config)
|
self.frame = CameraFrame(**self.frame_config)
|
||||||
|
|
||||||
def init_context(self):
|
def init_context(self):
|
||||||
# TODO, context with a window?
|
if self.window is not None:
|
||||||
ctx = moderngl.create_standalone_context()
|
self.ctx = self.window.ctx
|
||||||
ctx.enable(moderngl.BLEND)
|
self.fbo = self.window.ctx.detect_framebuffer()
|
||||||
ctx.blend_func = (
|
else:
|
||||||
|
self.ctx = moderngl.create_standalone_context()
|
||||||
|
self.fbo = self.get_fbo()
|
||||||
|
self.fbo.use()
|
||||||
|
# self.clear()
|
||||||
|
|
||||||
|
self.ctx.enable(moderngl.BLEND)
|
||||||
|
self.ctx.blend_func = (
|
||||||
moderngl.SRC_ALPHA, moderngl.ONE_MINUS_SRC_ALPHA,
|
moderngl.SRC_ALPHA, moderngl.ONE_MINUS_SRC_ALPHA,
|
||||||
moderngl.ONE, moderngl.ONE
|
moderngl.ONE, moderngl.ONE
|
||||||
)
|
)
|
||||||
self.ctx = ctx
|
|
||||||
|
|
||||||
# Methods associated with the frame buffer
|
# Methods associated with the frame buffer
|
||||||
def init_frame_buffer(self):
|
|
||||||
# TODO, account for live window
|
|
||||||
self.fbo = self.get_fbo()
|
|
||||||
self.fbo.use()
|
|
||||||
self.clear()
|
|
||||||
|
|
||||||
def get_fbo(self):
|
def get_fbo(self):
|
||||||
return self.ctx.simple_framebuffer(self.get_pixel_shape())
|
return self.ctx.simple_framebuffer(self.get_pixel_shape())
|
||||||
|
|
||||||
|
@ -109,24 +110,28 @@ class Camera(object):
|
||||||
self.set_frame_width(frame_width)
|
self.set_frame_width(frame_width)
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
if self.background_fbo:
|
if self.window:
|
||||||
self.ctx.copy_framebuffer(self.fbo, self.background_fbo)
|
self.window.clear()
|
||||||
else:
|
|
||||||
rgba = (*Color(self.background_color).get_rgb(), self.background_opacity)
|
rgba = (*Color(self.background_color).get_rgb(), self.background_opacity)
|
||||||
self.fbo.clear(*rgba)
|
self.fbo.clear(*rgba)
|
||||||
|
|
||||||
def lock_state_as_background(self):
|
def lock_state_as_background(self):
|
||||||
self.background_fbo = self.get_fbo()
|
# TODO, somehow do this by creating a Texture
|
||||||
self.ctx.copy_framebuffer(self.background_fbo, self.fbo)
|
# and adding it to the queue like an image mobject
|
||||||
|
pass
|
||||||
|
|
||||||
def unlock_background(self):
|
def unlock_background(self):
|
||||||
|
pass # TODO
|
||||||
self.background_fbo = None
|
self.background_fbo = None
|
||||||
|
|
||||||
def reset_pixel_shape(self, new_height, new_width):
|
def reset_pixel_shape(self, new_width, new_height):
|
||||||
self.pixel_width = new_width
|
self.pixel_width = new_width
|
||||||
self.pixel_height = new_height
|
self.pixel_height = new_height
|
||||||
|
|
||||||
self.fbo.release()
|
self.fbo.release()
|
||||||
self.init_frame_buffer()
|
self.init_frame_buffer()
|
||||||
|
self.refresh_shader_uniforms()
|
||||||
|
|
||||||
# Various ways to read from the fbo
|
# Various ways to read from the fbo
|
||||||
def get_raw_fbo_data(self, dtype='f1'):
|
def get_raw_fbo_data(self, dtype='f1'):
|
||||||
|
@ -173,6 +178,9 @@ class Camera(object):
|
||||||
def get_frame_width(self):
|
def get_frame_width(self):
|
||||||
return self.frame.get_width()
|
return self.frame.get_width()
|
||||||
|
|
||||||
|
def get_frame_shape(self):
|
||||||
|
return (self.get_frame_width(), self.get_frame_height())
|
||||||
|
|
||||||
def get_frame_center(self):
|
def get_frame_center(self):
|
||||||
return self.frame.get_center()
|
return self.frame.get_center()
|
||||||
|
|
||||||
|
@ -185,6 +193,21 @@ class Camera(object):
|
||||||
def set_frame_center(self, center):
|
def set_frame_center(self, center):
|
||||||
self.frame.move_to(center)
|
self.frame.move_to(center)
|
||||||
|
|
||||||
|
def pixel_coords_to_space_coords(self, px, py, relative=False):
|
||||||
|
pw, ph = self.get_pixel_shape()
|
||||||
|
fw, fh = self.get_frame_shape()
|
||||||
|
fc = self.get_frame_center()
|
||||||
|
if relative:
|
||||||
|
return 2 * np.array([px / pw, py / ph, 0])
|
||||||
|
else:
|
||||||
|
# Only scale wrt one axis
|
||||||
|
scale = fh / ph
|
||||||
|
return np.array([
|
||||||
|
scale * (px - pw / 2) - fc[0],
|
||||||
|
scale * (py - py / 2) - fc[0],
|
||||||
|
-fc[2] / 2,
|
||||||
|
])
|
||||||
|
|
||||||
# TODO, account for 3d
|
# TODO, account for 3d
|
||||||
def is_in_frame(self, mobject):
|
def is_in_frame(self, mobject):
|
||||||
fc = self.get_frame_center()
|
fc = self.get_frame_center()
|
||||||
|
@ -225,7 +248,10 @@ class Camera(object):
|
||||||
render_primative = info_group[0]["render_primative"]
|
render_primative = info_group[0]["render_primative"]
|
||||||
self.render_from_shader(shader, data, render_primative)
|
self.render_from_shader(shader, data, render_primative)
|
||||||
|
|
||||||
# Shader stuff
|
if self.window:
|
||||||
|
self.window.swap_buffers()
|
||||||
|
|
||||||
|
# Shaders
|
||||||
def init_shaders(self):
|
def init_shaders(self):
|
||||||
self.id_to_shader = {}
|
self.id_to_shader = {}
|
||||||
|
|
||||||
|
@ -271,52 +297,23 @@ class Camera(object):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def set_shader_uniforms(self, shader):
|
def set_shader_uniforms(self, shader):
|
||||||
# TODO, think about how uniforms come from mobjects
|
# TODO, think about how uniforms come from mobjects as well.
|
||||||
# as well.
|
|
||||||
fw = self.get_frame_width()
|
|
||||||
fh = self.get_frame_height()
|
fh = self.get_frame_height()
|
||||||
|
fc = self.get_frame_center()
|
||||||
|
pw, ph = self.get_pixel_shape()
|
||||||
|
|
||||||
shader['scale'].value = fh / 2
|
shader['scale'].value = fh / 2 # Scale based on frame size
|
||||||
shader['aspect_ratio'].value = fw / fh
|
shader['aspect_ratio'].value = (pw / ph) # AR based on pixel shape
|
||||||
shader['anti_alias_width'].value = ANTI_ALIAS_WIDTH
|
shader['anti_alias_width'].value = ANTI_ALIAS_WIDTH
|
||||||
|
shader['frame_center'].value = tuple(fc)
|
||||||
|
|
||||||
|
def refresh_shader_uniforms(self):
|
||||||
|
for sid, shader in self.id_to_shader.items():
|
||||||
|
self.set_shader_uniforms(shader)
|
||||||
|
|
||||||
def render_from_shader(self, shader, data, render_primative):
|
def render_from_shader(self, shader, data, render_primative):
|
||||||
vbo = shader.ctx.buffer(data.tobytes())
|
if len(data) == 0:
|
||||||
vao = shader.ctx.simple_vertex_array(shader, vbo, *data.dtype.names)
|
return
|
||||||
|
vbo = self.ctx.buffer(data.tobytes())
|
||||||
|
vao = self.ctx.simple_vertex_array(shader, vbo, *data.dtype.names)
|
||||||
vao.render(render_primative)
|
vao.render(render_primative)
|
||||||
|
|
||||||
|
|
||||||
def get_vmob_shader(ctx, type):
|
|
||||||
vert_file = f"quadratic_bezier_{type}_vert.glsl"
|
|
||||||
geom_file = f"quadratic_bezier_{type}_geom.glsl"
|
|
||||||
frag_file = f"quadratic_bezier_{type}_frag.glsl"
|
|
||||||
|
|
||||||
shader = ctx.program(
|
|
||||||
vertex_shader=get_code_from_file(vert_file),
|
|
||||||
geometry_shader=get_code_from_file(geom_file),
|
|
||||||
fragment_shader=get_code_from_file(frag_file),
|
|
||||||
)
|
|
||||||
set_shader_uniforms(shader)
|
|
||||||
return shader
|
|
||||||
|
|
||||||
|
|
||||||
def get_stroke_shader(ctx):
|
|
||||||
return get_vmob_shader(ctx, "stroke")
|
|
||||||
|
|
||||||
|
|
||||||
def get_fill_shader(ctx):
|
|
||||||
return get_vmob_shader(ctx, "fill")
|
|
||||||
|
|
||||||
|
|
||||||
def render_vmob_stroke(shader, vmobs):
|
|
||||||
assert(len(vmobs) > 0)
|
|
||||||
data_arrays = [vmob.get_stroke_shader_data() for vmob in vmobs]
|
|
||||||
data = join_arrays(*data_arrays)
|
|
||||||
send_data_to_shader(shader, data)
|
|
||||||
|
|
||||||
|
|
||||||
def render_vmob_fill(shader, vmobs):
|
|
||||||
assert(len(vmobs) > 0)
|
|
||||||
data_arrays = [vmob.get_fill_shader_data() for vmob in vmobs]
|
|
||||||
data = join_arrays(*data_arrays)
|
|
||||||
send_data_to_shader(shader, data)
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import platform
|
||||||
|
|
||||||
from tqdm import tqdm as ProgressDisplay
|
from tqdm import tqdm as ProgressDisplay
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import time
|
||||||
|
|
||||||
from manimlib.animation.animation import Animation
|
from manimlib.animation.animation import Animation
|
||||||
from manimlib.animation.transform import MoveToTarget
|
from manimlib.animation.transform import MoveToTarget
|
||||||
|
@ -15,11 +16,13 @@ from manimlib.mobject.mobject import Mobject
|
||||||
from manimlib.scene.scene_file_writer import SceneFileWriter
|
from manimlib.scene.scene_file_writer import SceneFileWriter
|
||||||
from manimlib.utils.family_ops import extract_mobject_family_members
|
from manimlib.utils.family_ops import extract_mobject_family_members
|
||||||
from manimlib.utils.family_ops import restructure_list_to_exclude_certain_family_members
|
from manimlib.utils.family_ops import restructure_list_to_exclude_certain_family_members
|
||||||
|
from manimlib.window import Window
|
||||||
|
|
||||||
|
|
||||||
class Scene(Container):
|
class Scene(Container):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"camera_class": Camera, # TODO, there should be only one camera
|
"window_config": {},
|
||||||
|
"camera_class": Camera,
|
||||||
"camera_config": {},
|
"camera_config": {},
|
||||||
"file_writer_config": {},
|
"file_writer_config": {},
|
||||||
"skip_animations": False,
|
"skip_animations": False,
|
||||||
|
@ -28,29 +31,37 @@ class Scene(Container):
|
||||||
"start_at_animation_number": None,
|
"start_at_animation_number": None,
|
||||||
"end_at_animation_number": None,
|
"end_at_animation_number": None,
|
||||||
"leave_progress_bars": False,
|
"leave_progress_bars": False,
|
||||||
|
"preview": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
Container.__init__(self, **kwargs)
|
Container.__init__(self, **kwargs)
|
||||||
|
if self.preview:
|
||||||
|
self.window = Window(self, **self.window_config)
|
||||||
|
self.camera_config["window"] = self.window
|
||||||
|
|
||||||
self.camera = self.camera_class(**self.camera_config)
|
self.camera = self.camera_class(**self.camera_config)
|
||||||
self.file_writer = SceneFileWriter(self, **self.file_writer_config)
|
self.file_writer = SceneFileWriter(self, **self.file_writer_config)
|
||||||
|
|
||||||
self.mobjects = []
|
self.mobjects = []
|
||||||
self.num_plays = 0
|
self.num_plays = 0
|
||||||
self.time = 0
|
self.time = 0
|
||||||
self.original_skipping_status = self.skip_animations
|
self.original_skipping_status = self.skip_animations
|
||||||
|
self.time_of_last_frame = time.time()
|
||||||
if self.random_seed is not None:
|
if self.random_seed is not None:
|
||||||
random.seed(self.random_seed)
|
random.seed(self.random_seed)
|
||||||
np.random.seed(self.random_seed)
|
np.random.seed(self.random_seed)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
self.setup()
|
self.setup()
|
||||||
try:
|
try:
|
||||||
self.construct()
|
self.construct()
|
||||||
except EndSceneEarlyException:
|
except EndSceneEarlyException:
|
||||||
pass
|
pass
|
||||||
self.tear_down()
|
self.tear_down()
|
||||||
self.file_writer.finish()
|
|
||||||
self.print_end_message()
|
# Is this what we want?
|
||||||
|
if self.preview:
|
||||||
|
self.update_until_closed()
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
"""
|
"""
|
||||||
|
@ -61,10 +72,20 @@ class Scene(Container):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def construct(self):
|
def construct(self):
|
||||||
pass # To be implemented in subclasses
|
# To be implemented in subclasses
|
||||||
|
pass
|
||||||
|
|
||||||
def tear_down(self):
|
def tear_down(self):
|
||||||
pass
|
self.file_writer.finish()
|
||||||
|
self.print_end_message()
|
||||||
|
|
||||||
|
def update_until_closed(self):
|
||||||
|
while not self.window.is_closing:
|
||||||
|
now = time.time()
|
||||||
|
self.update_frame()
|
||||||
|
time.sleep(
|
||||||
|
max(1 / 30 - (time.time() - now), 0)
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.__class__.__name__
|
return self.__class__.__name__
|
||||||
|
@ -91,9 +112,6 @@ class Scene(Container):
|
||||||
return [getattr(self, key) for key in keys]
|
return [getattr(self, key) for key in keys]
|
||||||
|
|
||||||
# Only these methods should touch the camera
|
# Only these methods should touch the camera
|
||||||
def set_camera(self, camera):
|
|
||||||
self.camera = camera
|
|
||||||
|
|
||||||
def get_image(self):
|
def get_image(self):
|
||||||
return self.camera.get_image()
|
return self.camera.get_image()
|
||||||
|
|
||||||
|
@ -103,15 +121,23 @@ class Scene(Container):
|
||||||
if mobjects is None:
|
if mobjects is None:
|
||||||
mobjects = self.mobjects
|
mobjects = self.mobjects
|
||||||
|
|
||||||
|
## REMOVE, this is just temporary while camera.lock_background doesn't work
|
||||||
|
mobjects = self.mobjects
|
||||||
|
##
|
||||||
|
|
||||||
self.camera.clear()
|
self.camera.clear()
|
||||||
self.camera.capture_mobjects(mobjects, excluded_mobjects=excluded_mobjects)
|
self.camera.capture_mobjects(mobjects, excluded_mobjects=excluded_mobjects)
|
||||||
|
|
||||||
def write_frame(self):
|
def emit_frame(self, dt):
|
||||||
self.increment_time(1.0 / self.camera.frame_rate)
|
self.increment_time(dt)
|
||||||
if self.skip_animations:
|
if not self.skip_animations:
|
||||||
return
|
self.file_writer.write_frame(self.camera)
|
||||||
data = self.camera.get_raw_fbo_data()
|
|
||||||
self.file_writer.write_frame(data)
|
if self.preview:
|
||||||
|
min_time_between_frames = 1 / self.camera.frame_rate
|
||||||
|
time_since_last = time.time() - self.time_of_last_frame
|
||||||
|
time.sleep(max(0, min_time_between_frames - time_since_last))
|
||||||
|
self.time_of_last_frame = time.time()
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
|
@ -238,7 +264,7 @@ class Scene(Container):
|
||||||
]))
|
]))
|
||||||
return time_progression
|
return time_progression
|
||||||
|
|
||||||
def compile_play_args_to_animation_list(self, *args, **kwargs):
|
def anims_from_play_args(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Each arg can either be an animation, or a mobject method
|
Each arg can either be an animation, or a mobject method
|
||||||
followed by that methods arguments (and potentially follow
|
followed by that methods arguments (and potentially follow
|
||||||
|
@ -314,13 +340,16 @@ class Scene(Container):
|
||||||
self.skip_animations = True
|
self.skip_animations = True
|
||||||
raise EndSceneEarlyException()
|
raise EndSceneEarlyException()
|
||||||
|
|
||||||
|
# Methods associated with running animations
|
||||||
def handle_play_like_call(func):
|
def handle_play_like_call(func):
|
||||||
def wrapper(self, *args, **kwargs):
|
def wrapper(self, *args, **kwargs):
|
||||||
self.update_skipping_status()
|
self.update_skipping_status()
|
||||||
allow_write = not self.skip_animations
|
allow_write = not self.skip_animations
|
||||||
self.file_writer.begin_animation(allow_write)
|
if allow_write:
|
||||||
|
self.file_writer.begin_animation()
|
||||||
func(self, *args, **kwargs)
|
func(self, *args, **kwargs)
|
||||||
self.file_writer.end_animation(allow_write)
|
if allow_write:
|
||||||
|
self.file_writer.end_animation()
|
||||||
self.num_plays += 1
|
self.num_plays += 1
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
@ -352,7 +381,7 @@ class Scene(Container):
|
||||||
animation.interpolate(alpha)
|
animation.interpolate(alpha)
|
||||||
self.update_mobjects(dt)
|
self.update_mobjects(dt)
|
||||||
self.update_frame(moving_mobjects)
|
self.update_frame(moving_mobjects)
|
||||||
self.write_frame()
|
self.emit_frame(dt)
|
||||||
self.camera.unlock_background()
|
self.camera.unlock_background()
|
||||||
|
|
||||||
def finish_animations(self, animations):
|
def finish_animations(self, animations):
|
||||||
|
@ -373,16 +402,11 @@ class Scene(Container):
|
||||||
if len(args) == 0:
|
if len(args) == 0:
|
||||||
warnings.warn("Called Scene.play with no animations")
|
warnings.warn("Called Scene.play with no animations")
|
||||||
return
|
return
|
||||||
animations = self.compile_play_args_to_animation_list(
|
animations = self.anims_from_play_args(*args, **kwargs)
|
||||||
*args, **kwargs
|
|
||||||
)
|
|
||||||
self.begin_animations(animations)
|
self.begin_animations(animations)
|
||||||
self.progress_through_animations(animations)
|
self.progress_through_animations(animations)
|
||||||
self.finish_animations(animations)
|
self.finish_animations(animations)
|
||||||
|
|
||||||
def idle_stream(self):
|
|
||||||
self.file_writer.idle_stream()
|
|
||||||
|
|
||||||
def clean_up_animations(self, *animations):
|
def clean_up_animations(self, *animations):
|
||||||
for animation in animations:
|
for animation in animations:
|
||||||
animation.clean_up_from_scene(self)
|
animation.clean_up_from_scene(self)
|
||||||
|
@ -423,7 +447,7 @@ class Scene(Container):
|
||||||
last_t = t
|
last_t = t
|
||||||
self.update_mobjects(dt)
|
self.update_mobjects(dt)
|
||||||
self.update_frame()
|
self.update_frame()
|
||||||
self.write_frame()
|
self.emit_frame(dt)
|
||||||
if stop_condition is not None and stop_condition():
|
if stop_condition is not None and stop_condition():
|
||||||
time_progression.close()
|
time_progression.close()
|
||||||
break
|
break
|
||||||
|
@ -435,7 +459,7 @@ class Scene(Container):
|
||||||
dt = 1 / self.camera.frame_rate
|
dt = 1 / self.camera.frame_rate
|
||||||
n_frames = int(duration / dt)
|
n_frames = int(duration / dt)
|
||||||
for n in range(n_frames):
|
for n in range(n_frames):
|
||||||
self.write_frame()
|
self.emit_frame(dt)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def wait_until(self, stop_condition, max_time=60):
|
def wait_until(self, stop_condition, max_time=60):
|
||||||
|
@ -461,6 +485,44 @@ class Scene(Container):
|
||||||
self.update_frame(ignore_skipping=True)
|
self.update_frame(ignore_skipping=True)
|
||||||
self.get_image().show()
|
self.get_image().show()
|
||||||
|
|
||||||
|
# Event handling
|
||||||
|
def on_mouse_motion(self, point, d_point):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_mouse_drag(self, point, d_point, buttons, modifiers):
|
||||||
|
self.camera.frame.shift(-d_point)
|
||||||
|
self.camera.refresh_shader_uniforms()
|
||||||
|
|
||||||
|
def on_mouse_press(self, point, button, mods):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_mouse_release(self, point, button, mods):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_mouse_scroll(self, point, offset):
|
||||||
|
self.camera.frame.scale(1 + np.arctan(offset[1]))
|
||||||
|
self.camera.refresh_shader_uniforms()
|
||||||
|
|
||||||
|
def on_key_release(self, symbol, modifiers):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_key_press(self, symbol, modifiers):
|
||||||
|
if chr(symbol) == "r":
|
||||||
|
self.camera.frame.restore()
|
||||||
|
self.camera.refresh_shader_uniforms()
|
||||||
|
|
||||||
|
def on_resize(self, width: int, height: int):
|
||||||
|
self.camera.reset_pixel_shape(width, height)
|
||||||
|
|
||||||
|
def on_show(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_hide(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_close(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class EndSceneEarlyException(Exception):
|
class EndSceneEarlyException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
79
manimlib/window.py
Normal file
79
manimlib/window.py
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import moderngl_window as mglw
|
||||||
|
from moderngl_window.context.pyglet.window import Window as PygletWindow
|
||||||
|
|
||||||
|
from manimlib.constants import DEFAULT_PIXEL_WIDTH
|
||||||
|
from manimlib.constants import DEFAULT_PIXEL_HEIGHT
|
||||||
|
|
||||||
|
|
||||||
|
class Window(PygletWindow):
|
||||||
|
title = ""
|
||||||
|
size = (DEFAULT_PIXEL_WIDTH, DEFAULT_PIXEL_HEIGHT)
|
||||||
|
fullscreen = False
|
||||||
|
resizable = True
|
||||||
|
gl_version = (3, 3)
|
||||||
|
aspect_ratio = None
|
||||||
|
vsync = True
|
||||||
|
samples = 1
|
||||||
|
cursor = True
|
||||||
|
|
||||||
|
def __init__(self, scene, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.scene = scene
|
||||||
|
# self.print_context_info()
|
||||||
|
mglw.activate_context(window=self)
|
||||||
|
|
||||||
|
# Delegate event handling to scene
|
||||||
|
def pixel_coords_to_space_coords(self, px, py, relative=False):
|
||||||
|
return self.scene.camera.pixel_coords_to_space_coords(px, py, relative)
|
||||||
|
|
||||||
|
def on_mouse_motion(self, x, y, dx, dy):
|
||||||
|
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, y, dx, dy, buttons, modifiers):
|
||||||
|
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) # Do a conversion?
|
||||||
|
|
||||||
|
def on_mouse_press(self, x: int, y: int, button, mods):
|
||||||
|
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, mods):
|
||||||
|
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, y, x_offset: float, y_offset: float):
|
||||||
|
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_release(self, symbol, modifiers):
|
||||||
|
super().on_key_release(symbol, modifiers)
|
||||||
|
self.scene.on_key_release(symbol, modifiers)
|
||||||
|
|
||||||
|
def on_key_press(self, symbol, modifiers):
|
||||||
|
super().on_key_press(symbol, modifiers)
|
||||||
|
self.scene.on_key_press(symbol, modifiers)
|
||||||
|
|
||||||
|
def on_resize(self, width: int, height: int):
|
||||||
|
super().on_resize(width, height)
|
||||||
|
self.scene.on_resize(width, height)
|
||||||
|
|
||||||
|
def on_show(self):
|
||||||
|
super().on_show()
|
||||||
|
self.scene.on_show()
|
||||||
|
|
||||||
|
def on_hide(self):
|
||||||
|
super().on_hide()
|
||||||
|
self.scene.on_hide()
|
||||||
|
|
||||||
|
def on_close(self):
|
||||||
|
super().on_close()
|
||||||
|
self.scene.on_close()
|
Loading…
Add table
Reference in a new issue