From 8ce5dc7e84b09e5f90f3358a90ce8230c7571647 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 23 Jan 2023 11:53:30 -0800 Subject: [PATCH 01/14] Add DieFace to drawings.py --- manimlib/mobject/svg/drawings.py | 54 ++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/manimlib/mobject/svg/drawings.py b/manimlib/mobject/svg/drawings.py index ed7e7f9d..6c02175b 100644 --- a/manimlib/mobject/svg/drawings.py +++ b/manimlib/mobject/svg/drawings.py @@ -27,16 +27,19 @@ from manimlib.constants import OUT from manimlib.constants import PI from manimlib.constants import RED from manimlib.constants import RIGHT -from manimlib.constants import RIGHT from manimlib.constants import SMALL_BUFF from manimlib.constants import SMALL_BUFF from manimlib.constants import UP -from manimlib.constants import UP +from manimlib.constants import UL +from manimlib.constants import UR +from manimlib.constants import DL +from manimlib.constants import DR from manimlib.constants import WHITE from manimlib.constants import YELLOW from manimlib.mobject.boolean_ops import Difference from manimlib.mobject.geometry import Arc from manimlib.mobject.geometry import Circle +from manimlib.mobject.geometry import Dot from manimlib.mobject.geometry import Line from manimlib.mobject.geometry import Polygon from manimlib.mobject.geometry import Rectangle @@ -577,3 +580,50 @@ class Piano3D(VGroup): if piano_2d[i] in piano_2d.black_keys: key.shift(black_key_shift * OUT) key.set_color(BLACK) + + + +class DieFace(VGroup): + def __init__( + self, + value: int, + side_length: float = 1.0, + corner_radius: float = 0.15, + stroke_color: ManimColor = WHITE, + stroke_width: float = 2.0, + fill_color: ManimColor = GREY_E, + dot_radius: float = 0.08, + dot_color: ManimColor = BLUE_B, + dot_coalesce_factor: float = 0.5 + ): + dot = Dot(radius=dot_radius, fill_color=dot_color) + square = Square( + side_length=side_length, + stroke_color=stroke_color, + stroke_width=stroke_width, + fill_color=fill_color, + fill_opacity=1.0, + ) + square.round_corners(corner_radius) + + if not (1 <= value <= 6): + raise Exception("DieFace only accepts integer inputs between 1 and 6") + + edge_group = [ + (ORIGIN,), + (UL, DR), + (UL, ORIGIN, DR), + (UL, UR, DL, DR), + (UL, UR, ORIGIN, DL, DR), + (UL, UR, LEFT, RIGHT, DL, DR), + ][value - 1] + + arrangement = VGroup(*( + dot.copy().move_to(square.get_bounding_box_point(vect)) + for vect in edge_group + )) + arrangement.space_out_submobjects(dot_coalesce_factor) + + super().__init__(square, arrangement) + self.value = value + self.index = value From 1d4fcf020b324015678a8b26a28120ebfce06396 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 23 Jan 2023 11:54:01 -0800 Subject: [PATCH 02/14] Refer directly to fbo viewports in get_raw_fbo_data --- manimlib/camera/camera.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/manimlib/camera/camera.py b/manimlib/camera/camera.py index 727fdfc7..f9046f02 100644 --- a/manimlib/camera/camera.py +++ b/manimlib/camera/camera.py @@ -305,10 +305,13 @@ class Camera(object): def get_raw_fbo_data(self, dtype: str = 'f1') -> bytes: # Copy blocks from fbo into draw_fbo using Blit - pw, ph = (self.pixel_width, self.pixel_height) gl.glBindFramebuffer(gl.GL_READ_FRAMEBUFFER, self.fbo.glo) gl.glBindFramebuffer(gl.GL_DRAW_FRAMEBUFFER, self.draw_fbo.glo) - gl.glBlitFramebuffer(0, 0, pw, ph, 0, 0, pw, ph, gl.GL_COLOR_BUFFER_BIT, gl.GL_LINEAR) + gl.glBlitFramebuffer( + *self.fbo.viewport, + *self.draw_fbo.viewport, + gl.GL_COLOR_BUFFER_BIT, gl.GL_LINEAR + ) return self.draw_fbo.read( viewport=self.draw_fbo.viewport, components=self.n_channels, From e8b75941e0fc98d47061269b8f32b161f3cca245 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 23 Jan 2023 14:02:06 -0800 Subject: [PATCH 03/14] Get rid of pixel_width and pixel_height attrs on Camera --- manimlib/camera/camera.py | 18 ++++++------------ manimlib/scene/scene.py | 2 +- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/manimlib/camera/camera.py b/manimlib/camera/camera.py index f9046f02..8e3d843c 100644 --- a/manimlib/camera/camera.py +++ b/manimlib/camera/camera.py @@ -209,8 +209,7 @@ class Camera(object): samples: int = 0, ): self.background_image = background_image - self.pixel_width = pixel_width - self.pixel_height = pixel_height + self.default_pixel_shape = (pixel_width, pixel_height) self.fps = fps self.max_allowable_norm = max_allowable_norm self.image_mode = image_mode @@ -281,16 +280,14 @@ class Camera(object): ctx: moderngl.Context, samples: int = 0 ) -> moderngl.Framebuffer: - pw = self.pixel_width - ph = self.pixel_height return ctx.framebuffer( color_attachments=ctx.texture( - (pw, ph), + self.default_pixel_shape, components=self.n_channels, samples=samples, ), depth_attachment=ctx.depth_renderbuffer( - (pw, ph), + self.default_pixel_shape, samples=samples ) ) @@ -298,11 +295,6 @@ class Camera(object): def clear(self) -> None: self.fbo.clear(*self.background_rgba) - def reset_pixel_shape(self, new_width: int, new_height: int) -> None: - self.pixel_width = new_width - self.pixel_height = new_height - self.refresh_perspective_uniforms() - def get_raw_fbo_data(self, dtype: str = 'f1') -> bytes: # Copy blocks from fbo into draw_fbo using Blit gl.glBindFramebuffer(gl.GL_READ_FRAMEBUFFER, self.fbo.glo) @@ -345,9 +337,11 @@ class Camera(object): return texture # Getting camera attributes + def get_pixel_size(self): + return self.frame.get_shape()[0] / self.get_pixel_shape()[0] + def get_pixel_shape(self) -> tuple[int, int]: return self.fbo.viewport[2:4] - # return (self.pixel_width, self.pixel_height) def get_pixel_width(self) -> int: return self.get_pixel_shape()[0] diff --git a/manimlib/scene/scene.py b/manimlib/scene/scene.py index 37323b79..c8b1c155 100644 --- a/manimlib/scene/scene.py +++ b/manimlib/scene/scene.py @@ -901,7 +901,7 @@ class Scene(object): self.hold_on_wait = False def on_resize(self, width: int, height: int) -> None: - self.camera.reset_pixel_shape(width, height) + pass def on_show(self) -> None: pass From 8d729eef5a3f943511daec7ef7457f67ddd804fe Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 23 Jan 2023 14:41:17 -0800 Subject: [PATCH 04/14] Rename perspective to view_matrix --- manimlib/camera/camera.py | 11 +++++------ manimlib/shaders/inserts/get_gl_Position.glsl | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/manimlib/camera/camera.py b/manimlib/camera/camera.py index 8e3d843c..5596c9b1 100644 --- a/manimlib/camera/camera.py +++ b/manimlib/camera/camera.py @@ -39,7 +39,7 @@ class CameraFrame(Mobject): self.frame_shape = frame_shape self.center_point = center_point self.focal_dist_to_height = focal_dist_to_height - self.perspective_transform = np.identity(4) + self.view_matrix = np.identity(4) super().__init__(**kwargs) def init_uniforms(self) -> None: @@ -83,12 +83,12 @@ class CameraFrame(Mobject): def get_inverse_camera_rotation_matrix(self): return self.get_orientation().as_matrix().T - def get_perspective_transform(self): + def get_view_matrix(self): """ Returns a 4x4 for the affine transformation mapping a point into the camera's internal coordinate system """ - result = self.perspective_transform + result = self.view_matrix result[:] = np.identity(4) result[:3, 3] = -self.get_center() rotation = np.identity(4) @@ -499,8 +499,7 @@ class Camera(object): def refresh_perspective_uniforms(self) -> None: frame = self.frame - # Orient light - perspective_transform = frame.get_perspective_transform() + view_matrix = frame.get_view_matrix() light_pos = self.light_source.get_location() cam_pos = self.frame.get_implied_camera_location() frame_shape = frame.get_shape() @@ -508,7 +507,7 @@ class Camera(object): self.perspective_uniforms.update( frame_shape=frame_shape, pixel_size=frame_shape[0] / self.get_pixel_shape()[0], - perspective=tuple(perspective_transform.T.flatten()), + view=tuple(view_matrix.T.flatten()), camera_position=tuple(cam_pos), light_position=tuple(light_pos), focal_distance=frame.get_focal_distance(), diff --git a/manimlib/shaders/inserts/get_gl_Position.glsl b/manimlib/shaders/inserts/get_gl_Position.glsl index 0c872515..bc666272 100644 --- a/manimlib/shaders/inserts/get_gl_Position.glsl +++ b/manimlib/shaders/inserts/get_gl_Position.glsl @@ -1,5 +1,5 @@ uniform float is_fixed_in_frame; -uniform mat4 perspective; +uniform mat4 view; uniform vec2 frame_shape; uniform float focal_distance; @@ -9,7 +9,7 @@ vec4 get_gl_Position(vec3 point){ vec4 result = vec4(point, 1.0); vec2 shape = DEFAULT_FRAME_SHAPE; if(!bool(is_fixed_in_frame)){ - result = perspective * result; + result = view * result; shape = frame_shape; } From 03080a10a77bb30747ce97c997d0ee1bb5061b72 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 23 Jan 2023 15:05:10 -0800 Subject: [PATCH 05/14] Small style tweaks --- manimlib/config.py | 6 +++--- manimlib/shaders/inserts/get_gl_Position.glsl | 6 +++--- manimlib/shaders/quadratic_bezier_stroke/geom.glsl | 8 ++------ 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/manimlib/config.py b/manimlib/config.py index 17e203ab..9b1e033b 100644 --- a/manimlib/config.py +++ b/manimlib/config.py @@ -414,9 +414,9 @@ def get_window_config(args: Namespace, custom_config: dict, camera_config: dict) if not (args.full_screen or custom_config["full_screen"]): window_width //= 2 window_height = int(window_width / aspect_ratio) - return { - "size": (window_width, window_height), - } + return dict( + size=(window_width, window_height), + ) def get_camera_config(args: Namespace, custom_config: dict) -> dict: diff --git a/manimlib/shaders/inserts/get_gl_Position.glsl b/manimlib/shaders/inserts/get_gl_Position.glsl index bc666272..719125ba 100644 --- a/manimlib/shaders/inserts/get_gl_Position.glsl +++ b/manimlib/shaders/inserts/get_gl_Position.glsl @@ -6,13 +6,13 @@ uniform float focal_distance; const vec2 DEFAULT_FRAME_SHAPE = vec2(8.0 * 16.0 / 9.0, 8.0); vec4 get_gl_Position(vec3 point){ + bool is_fixed = bool(is_fixed_in_frame); vec4 result = vec4(point, 1.0); - vec2 shape = DEFAULT_FRAME_SHAPE; - if(!bool(is_fixed_in_frame)){ + if(!is_fixed){ result = view * result; - shape = frame_shape; } + vec2 shape = is_fixed ? DEFAULT_FRAME_SHAPE : frame_shape; result.x *= 2.0 / shape.x; result.y *= 2.0 / shape.y; result.z /= focal_distance; diff --git a/manimlib/shaders/quadratic_bezier_stroke/geom.glsl b/manimlib/shaders/quadratic_bezier_stroke/geom.glsl index 8e6570f2..bcf288b9 100644 --- a/manimlib/shaders/quadratic_bezier_stroke/geom.glsl +++ b/manimlib/shaders/quadratic_bezier_stroke/geom.glsl @@ -42,12 +42,8 @@ vec3 unit_normal = vec3(0.0, 0.0, 1.0); vec3 get_joint_unit_normal(vec4 joint_product){ - vec3 result; - if(joint_product.w < COS_THRESHOLD){ - result = joint_product.xyz; - }else{ - result = v_joint_product[1].xyz; - } + vec3 result = (joint_product.w < COS_THRESHOLD) ? + joint_product.xyz : v_joint_product[1].xyz; float norm = length(result); return (norm > 1e-5) ? result / norm : vec3(0.0, 0.0, 1.0); } From c13495deeb548cd72c975aa1fc161bcdde235abc Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 23 Jan 2023 17:03:46 -0800 Subject: [PATCH 06/14] Blit based on window's viewport, when in preview mode --- manimlib/camera/camera.py | 17 ++++++++++++----- manimlib/scene/scene.py | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/manimlib/camera/camera.py b/manimlib/camera/camera.py index 5596c9b1..6e53e6aa 100644 --- a/manimlib/camera/camera.py +++ b/manimlib/camera/camera.py @@ -26,6 +26,7 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: from manimlib.shader_wrapper import ShaderWrapper from manimlib.typing import ManimColor, Vect3 + from manimlib.window import Window from typing import Any, Iterable class CameraFrame(Mobject): @@ -187,7 +188,7 @@ class CameraFrame(Mobject): class Camera(object): def __init__( self, - ctx: moderngl.Context | None = None, + window: Window | None = None, background_image: str | None = None, frame_config: dict = dict(), pixel_width: int = DEFAULT_PIXEL_WIDTH, @@ -209,6 +210,7 @@ class Camera(object): samples: int = 0, ): self.background_image = background_image + self.window = window self.default_pixel_shape = (pixel_width, pixel_height) self.fps = fps self.max_allowable_norm = max_allowable_norm @@ -224,7 +226,7 @@ class Camera(object): )) self.perspective_uniforms = dict() self.init_frame(**frame_config) - self.init_context(ctx) + self.init_context(window) self.init_shaders() self.init_textures() self.init_light_source() @@ -237,11 +239,12 @@ class Camera(object): def init_frame(self, **config) -> None: self.frame = CameraFrame(**config) - def init_context(self, ctx: moderngl.Context | None = None) -> None: - if ctx is None: + def init_context(self, window: Window | None = None) -> None: + if window is None: ctx = moderngl.create_standalone_context() fbo = self.get_fbo(ctx, self.samples) else: + ctx = window.ctx fbo = ctx.detect_framebuffer() self.ctx = ctx self.fbo = fbo @@ -299,8 +302,12 @@ class Camera(object): # Copy blocks from fbo into draw_fbo using Blit gl.glBindFramebuffer(gl.GL_READ_FRAMEBUFFER, self.fbo.glo) gl.glBindFramebuffer(gl.GL_DRAW_FRAMEBUFFER, self.draw_fbo.glo) + if self.window is not None: + src_viewport = self.window.viewport + else: + src_viewport = self.fbo.viewport gl.glBlitFramebuffer( - *self.fbo.viewport, + *src_viewport, *self.draw_fbo.viewport, gl.GL_COLOR_BUFFER_BIT, gl.GL_LINEAR ) diff --git a/manimlib/scene/scene.py b/manimlib/scene/scene.py index c8b1c155..6a1ce206 100644 --- a/manimlib/scene/scene.py +++ b/manimlib/scene/scene.py @@ -101,7 +101,7 @@ class Scene(object): if self.preview: from manimlib.window import Window self.window = Window(scene=self, **self.window_config) - self.camera_config["ctx"] = self.window.ctx + self.camera_config["window"] = self.window self.camera_config["fps"] = 30 # Where's that 30 from? else: self.window = None From b0cca9e4b633408af4d467ac8c2d3c2a9a10eab9 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 23 Jan 2023 17:04:44 -0800 Subject: [PATCH 07/14] Camera pixel_shape should reflect the drawn fbo --- manimlib/camera/camera.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manimlib/camera/camera.py b/manimlib/camera/camera.py index 6e53e6aa..dc728c58 100644 --- a/manimlib/camera/camera.py +++ b/manimlib/camera/camera.py @@ -348,7 +348,7 @@ class Camera(object): return self.frame.get_shape()[0] / self.get_pixel_shape()[0] def get_pixel_shape(self) -> tuple[int, int]: - return self.fbo.viewport[2:4] + return self.draw_fbo.size def get_pixel_width(self) -> int: return self.get_pixel_shape()[0] From e2421a650cacd5cc1df8b741bd8430cbe8298204 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 23 Jan 2023 17:06:49 -0800 Subject: [PATCH 08/14] Don't disable clip plane --- manimlib/camera/camera.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/manimlib/camera/camera.py b/manimlib/camera/camera.py index dc728c58..ff2d990e 100644 --- a/manimlib/camera/camera.py +++ b/manimlib/camera/camera.py @@ -271,8 +271,6 @@ class Camera(object): def set_ctx_clip_plane(self, enable: bool = True) -> None: if enable: gl.glEnable(gl.GL_CLIP_DISTANCE0) - else: - gl.glDisable(gl.GL_CLIP_DISTANCE0) def init_light_source(self) -> None: self.light_source = Point(self.light_source_position) From 8a6deb40680ec38f939260d929f92a65636754e8 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 23 Jan 2023 17:10:18 -0800 Subject: [PATCH 09/14] Enable recording during a Scene embed --- manimlib/camera/camera.py | 5 ++--- manimlib/config.py | 1 + manimlib/scene/scene.py | 16 ++++++++++++++-- manimlib/scene/scene_file_writer.py | 19 +++++++++++++++++++ manimlib/window.py | 18 ++++++++++-------- 5 files changed, 46 insertions(+), 13 deletions(-) diff --git a/manimlib/camera/camera.py b/manimlib/camera/camera.py index ff2d990e..6f5f6b6a 100644 --- a/manimlib/camera/camera.py +++ b/manimlib/camera/camera.py @@ -507,11 +507,10 @@ class Camera(object): view_matrix = frame.get_view_matrix() light_pos = self.light_source.get_location() cam_pos = self.frame.get_implied_camera_location() - frame_shape = frame.get_shape() self.perspective_uniforms.update( - frame_shape=frame_shape, - pixel_size=frame_shape[0] / self.get_pixel_shape()[0], + frame_shape=frame.get_shape(), + pixel_size=self.get_pixel_size(), view=tuple(view_matrix.T.flatten()), camera_position=tuple(cam_pos), light_position=tuple(light_pos), diff --git a/manimlib/config.py b/manimlib/config.py index 9b1e033b..26ddb708 100644 --- a/manimlib/config.py +++ b/manimlib/config.py @@ -415,6 +415,7 @@ def get_window_config(args: Namespace, custom_config: dict, camera_config: dict) window_width //= 2 window_height = int(window_width / aspect_ratio) return dict( + full_size=(camera_config["pixel_width"], camera_config["pixel_height"]), size=(window_width, window_height), ) diff --git a/manimlib/scene/scene.py b/manimlib/scene/scene.py index 6a1ce206..0602d2e6 100644 --- a/manimlib/scene/scene.py +++ b/manimlib/scene/scene.py @@ -711,7 +711,7 @@ class Scene(object): self.restore_state(self.redo_stack.pop()) self.refresh_static_mobjects() - def checkpoint_paste(self, skip: bool = False): + def checkpoint_paste(self, skip: bool = False, record: bool = False): """ Used during interactive development to run (or re-run) a block of scene code. @@ -721,7 +721,7 @@ class Scene(object): was called on a block of code starting with that comment. """ shell = get_ipython() - if shell is None: + if shell is None or self.window is None: raise Exception( "Scene.checkpoint_paste cannot be called outside of " + "an ipython shell" @@ -738,8 +738,20 @@ class Scene(object): prev_skipping = self.skip_animations self.skip_animations = skip + if record: + # Resize window so rendering happens at the appropriate size + self.window.size = self.camera.get_pixel_shape() + self.window.swap_buffers() + self.update_frame() + self.file_writer.begin_insert() + shell.run_cell(pasted) + if record: + self.file_writer.end_insert() + # Put window back to how it started + self.window.to_default_position() + self.skip_animations = prev_skipping def checkpoint(self, key: str): diff --git a/manimlib/scene/scene_file_writer.py b/manimlib/scene/scene_file_writer.py index f99fdf78..d17f1dbd 100644 --- a/manimlib/scene/scene_file_writer.py +++ b/manimlib/scene/scene_file_writer.py @@ -288,6 +288,25 @@ class SceneFileWriter(object): ) self.set_progress_display_description() + def begin_insert(self): + # Begin writing process + self.write_to_movie = True + self.init_output_directories() + movie_path = self.get_movie_file_path() + folder, file = os.path.split(movie_path) + scene_name, ext = file.split(".") + n_inserts = len(list(filter( + lambda f: f.startswith(scene_name + "_insert"), + os.listdir(folder) + ))) + self.inserted_file_path = movie_path.replace(".", f"_insert_{n_inserts}.") + self.open_movie_pipe(self.inserted_file_path) + + def end_insert(self): + self.close_movie_pipe() + self.write_to_movie = False + self.print_file_ready_message(self.inserted_file_path) + def has_progress_display(self): return self.progress_display is not None diff --git a/manimlib/window.py b/manimlib/window.py index 4c3d3935..f5bdf09a 100644 --- a/manimlib/window.py +++ b/manimlib/window.py @@ -26,10 +26,14 @@ class Window(PygletWindow): self, scene: Scene, size: tuple[int, int] = (1280, 720), + full_size: tuple[int, int] = (1920, 1080), samples = 0 ): - super().__init__(size=size, samples=samples) + super().__init__(size=full_size, samples=samples) + self.full_size = full_size + self.default_size = size + self.default_position = self.find_initial_position(size) self.scene = scene self.pressed_keys = set() self.title = str(scene) @@ -40,13 +44,11 @@ class Window(PygletWindow): self.config = mglw.WindowConfig(ctx=self.ctx, wnd=self, timer=self.timer) self.timer.start() - # 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 + self.to_default_position() + + def to_default_position(self): + self.size = self.default_size + self.position = self.default_position def find_initial_position(self, size: tuple[int, int]) -> tuple[int, int]: custom_position = get_customization()["window_position"] From 1dda706335f758c040a8f5fe73e444d2d65c20fa Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Tue, 24 Jan 2023 12:04:43 -0800 Subject: [PATCH 10/14] Small cleanup --- manimlib/camera/camera.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/manimlib/camera/camera.py b/manimlib/camera/camera.py index 6f5f6b6a..e6f4a76a 100644 --- a/manimlib/camera/camera.py +++ b/manimlib/camera/camera.py @@ -241,20 +241,18 @@ class Camera(object): def init_context(self, window: Window | None = None) -> None: if window is None: - ctx = moderngl.create_standalone_context() - fbo = self.get_fbo(ctx, self.samples) + self.ctx = moderngl.create_standalone_context() + self.fbo = self.get_fbo(self.samples) else: - ctx = window.ctx - fbo = ctx.detect_framebuffer() - self.ctx = ctx - self.fbo = fbo + self.ctx = window.ctx + self.fbo = self.ctx.detect_framebuffer() self.fbo.use() self.set_ctx_blending() self.ctx.enable(moderngl.PROGRAM_POINT_SIZE) # This is the frame buffer we'll draw into when emitting frames - self.draw_fbo = self.get_fbo(ctx, 0) + self.draw_fbo = self.get_fbo(samples=0) def set_ctx_blending(self, enable: bool = True) -> None: if enable: @@ -278,16 +276,15 @@ class Camera(object): # Methods associated with the frame buffer def get_fbo( self, - ctx: moderngl.Context, samples: int = 0 ) -> moderngl.Framebuffer: - return ctx.framebuffer( - color_attachments=ctx.texture( + return self.ctx.framebuffer( + color_attachments=self.ctx.texture( self.default_pixel_shape, components=self.n_channels, samples=samples, ), - depth_attachment=ctx.depth_renderbuffer( + depth_attachment=self.ctx.depth_renderbuffer( self.default_pixel_shape, samples=samples ) @@ -326,7 +323,7 @@ class Camera(object): def get_pixel_array(self) -> np.ndarray: raw = self.get_raw_fbo_data(dtype='f4') flat_arr = np.frombuffer(raw, dtype='f4') - arr = flat_arr.reshape([*reversed(self.fbo.size), self.n_channels]) + arr = flat_arr.reshape([*reversed(self.draw_fbo.size), self.n_channels]) arr = arr[::-1] # Convert from float return (self.rgb_max_val * arr).astype(self.pixel_array_dtype) @@ -342,7 +339,7 @@ class Camera(object): return texture # Getting camera attributes - def get_pixel_size(self): + def get_pixel_size(self) -> float: return self.frame.get_shape()[0] / self.get_pixel_shape()[0] def get_pixel_shape(self) -> tuple[int, int]: @@ -354,6 +351,10 @@ class Camera(object): def get_pixel_height(self) -> int: return self.get_pixel_shape()[1] + def get_aspect_ratio(self): + pw, ph = self.get_pixel_shape() + return pw / ph + def get_frame_height(self) -> float: return self.frame.get_height() @@ -376,17 +377,15 @@ class Camera(object): whether frame_height or frame_width remains fixed while the other changes accordingly. """ - pixel_height = self.get_pixel_height() - pixel_width = self.get_pixel_width() frame_height = self.get_frame_height() frame_width = self.get_frame_width() - aspect_ratio = fdiv(pixel_width, pixel_height) + aspect_ratio = self.get_aspect_ratio() if not fixed_dimension: frame_height = frame_width / aspect_ratio else: frame_width = aspect_ratio * frame_height - self.frame.set_height(frame_height) - self.frame.set_width(frame_width) + self.frame.set_height(frame_height, stretch=true) + self.frame.set_width(frame_width, stretch=true) # Rendering def capture(self, *mobjects: Mobject) -> None: @@ -446,9 +445,10 @@ class Camera(object): # Program and vertex array shader_program, vert_format = self.get_shader_program(shader_wrapper) + attributes = shader_wrapper.vert_attributes vao = self.ctx.vertex_array( program=shader_program, - content=[(vbo, vert_format, *shader_wrapper.vert_attributes)], + content=[(vbo, vert_format, *attributes)], index_buffer=ibo, ) return { From b99b88fd2577e67d6d6106827f2d5d9d866831eb Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Tue, 24 Jan 2023 12:05:08 -0800 Subject: [PATCH 11/14] Update Scene.get_image to resize window if needed --- manimlib/scene/scene.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/manimlib/scene/scene.py b/manimlib/scene/scene.py index 0602d2e6..a37b057e 100644 --- a/manimlib/scene/scene.py +++ b/manimlib/scene/scene.py @@ -289,7 +289,15 @@ class Scene(object): # Only these methods should touch the camera def get_image(self) -> Image: - return self.camera.get_image() + if self.window is not None: + self.window.size = self.camera.get_pixel_shape() + self.window.swap_buffers() + self.update_frame() + self.window.swap_buffers() + image = self.camera.get_image() + if self.window is not None: + self.window.to_default_position() + return image def show(self) -> None: self.update_frame(ignore_skipping=True) From b1f0270316ccab479e28c31f7b8854f76b26316d Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Tue, 24 Jan 2023 12:05:25 -0800 Subject: [PATCH 12/14] Change threshold for bevel reduction --- manimlib/shaders/quadratic_bezier_stroke/geom.glsl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manimlib/shaders/quadratic_bezier_stroke/geom.glsl b/manimlib/shaders/quadratic_bezier_stroke/geom.glsl index bcf288b9..ef5029d0 100644 --- a/manimlib/shaders/quadratic_bezier_stroke/geom.glsl +++ b/manimlib/shaders/quadratic_bezier_stroke/geom.glsl @@ -100,8 +100,8 @@ void get_corners( float buff2 = 0.5 * v_stroke_width[2] + aaw; // Add correction for sharp angles to prevent weird bevel effects - if(v_joint_product[0].w < -0.75) buff0 *= 4 * (v_joint_product[0].w + 1.0); - if(v_joint_product[2].w < -0.75) buff2 *= 4 * (v_joint_product[2].w + 1.0); + if(v_joint_product[0].w < -0.9) buff0 *= 10 * (v_joint_product[0].w + 1.0); + if(v_joint_product[2].w < -0.9) buff2 *= 10 * (v_joint_product[2].w + 1.0); // Unit normal and joint angles vec3 normal0 = get_joint_unit_normal(v_joint_product[0]); From 97789fff35d43048ce776bf56bb57c6f3d6e1be1 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Tue, 24 Jan 2023 12:05:39 -0800 Subject: [PATCH 13/14] Swap buffers when resetting to default position --- manimlib/window.py | 1 + 1 file changed, 1 insertion(+) diff --git a/manimlib/window.py b/manimlib/window.py index f5bdf09a..93337654 100644 --- a/manimlib/window.py +++ b/manimlib/window.py @@ -49,6 +49,7 @@ class Window(PygletWindow): def to_default_position(self): self.size = self.default_size self.position = self.default_position + self.swap_buffers() def find_initial_position(self, size: tuple[int, int]) -> tuple[int, int]: custom_position = get_customization()["window_position"] From aa6c321a0a1e8864a8e1813149f3965298a0d1f6 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Tue, 24 Jan 2023 14:25:02 -0800 Subject: [PATCH 14/14] Change InteractiveScene dot config --- manimlib/scene/interactive_scene.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manimlib/scene/interactive_scene.py b/manimlib/scene/interactive_scene.py index 0e9cac08..80c4c548 100644 --- a/manimlib/scene/interactive_scene.py +++ b/manimlib/scene/interactive_scene.py @@ -68,8 +68,8 @@ class InteractiveScene(Scene): """ corner_dot_config = dict( color=WHITE, - radius=0.05, - glow_factor=1.0, + radius=0.025, + glow_factor=2.0, ) selection_rectangle_stroke_color = WHITE selection_rectangle_stroke_width = 1.0