From d6b23324a699b51e4374f3b15fe1dfb99ae9f9b8 Mon Sep 17 00:00:00 2001 From: Sahil Makhijani Date: Sun, 31 Jan 2021 16:05:55 +0530 Subject: [PATCH 1/3] Interactive Mobjects Performance Improvements --- manimlib/mobject/interactive.py | 77 +++++++++++++++++---------------- manimlib/scene/scene.py | 29 +++++++++---- 2 files changed, 60 insertions(+), 46 deletions(-) diff --git a/manimlib/mobject/interactive.py b/manimlib/mobject/interactive.py index 7e72d816..79c80f23 100644 --- a/manimlib/mobject/interactive.py +++ b/manimlib/mobject/interactive.py @@ -22,21 +22,19 @@ class MotionMobject(Mobject): You could hold and drag this object to any position """ - CONFIG = { - "listen_to_events": True - } - def __init__(self, mobject, **kwargs): super().__init__(**kwargs) + assert(isinstance(mobject, Mobject)) self.mobject = mobject + self.mobject.listen_to_events = True + self.mobject.on_mouse_drag = self.mob_on_mouse_drag # To avoid locking it as static mobject self.mobject.add_updater(lambda mob: None) self.add(mobject) - def on_mouse_drag(self, point, d_point, buttons, modifiers): - if self.mobject.is_point_touching(point): - self.mobject.move_to(point) - return False + def mob_on_mouse_drag(self, point, d_point, buttons, modifiers): + self.mobject.move_to(point) + return False class Button(Mobject): @@ -44,19 +42,17 @@ class Button(Mobject): Pass any mobject and register an on_click method """ - CONFIG = { - "listen_to_events": True - } - def __init__(self, mobject, on_click, **kwargs): super().__init__(**kwargs) - self.mobject, self.on_click = mobject, on_click + self.on_click = on_click + self.mobject = mobject + self.mobject.listen_to_events = True + self.mobject.on_mouse_press = self.mob_on_mouse_press self.add(self.mobject) - def on_mouse_press(self, point, button, mods): - if self.mobject.is_point_touching(point): - self.on_click() - return False + def mob_on_mouse_press(self, point, button, mods): + self.on_click() + return False # Controls @@ -193,6 +189,9 @@ class Checkbox(ContolMobject): class LinearNumberSlider(ContolMobject): CONFIG = { + # Since, only slider circle listnes to drag event + "listen_to_events": False, + "value_type": np.float64, "min_value": -10.0, "max_value": 10.0, @@ -222,6 +221,9 @@ class LinearNumberSlider(ContolMobject): self.slider_axis.set_opacity(0.0) self.slider.move_to(self.slider_axis) + self.slider.listen_to_events = True + self.slider.on_mouse_drag = self.slider_on_mouse_drag + super().__init__(value, self.bar, self.slider, self.slider_axis, ** kwargs) def assert_value(self, value): @@ -231,10 +233,9 @@ class LinearNumberSlider(ContolMobject): prop = (value - self.min_value) / (self.max_value - self.min_value) self.slider.move_to(self.slider_axis.point_from_proportion(prop)) - def on_mouse_drag(self, point, d_point, buttons, modifiers): - if self.slider.is_point_touching(point): - self.set_value(self.get_value_from_point(point)) - return False + def slider_on_mouse_drag(self, point, d_point, buttons, modifiers): + self.set_value(self.get_value_from_point(point)) + return False # Helper Methods @@ -371,6 +372,8 @@ class Textbox(ContolMobject): digest_config(self, kwargs) self.isActive = self.isInitiallyActive self.box = Rectangle(**self.box_kwargs) + self.box.listen_to_events = True + self.box.on_mouse_press = self.box_on_mouse_press self.text = Text(value, **self.text_kwargs) super().__init__(value, self.box, self.text, **kwargs) self.update_text(value) @@ -397,11 +400,10 @@ class Textbox(ContolMobject): else: self.box.set_stroke(self.deactive_color) - def on_mouse_press(self, point, button, mods): - if self.box.is_point_touching(point): - self.isActive = not self.isActive - self.active_anim(self.isActive) - return False + def box_on_mouse_press(self, point, button, mods): + self.isActive = not self.isActive + self.active_anim(self.isActive) + return False def on_key_press(self, symbol, modifiers): char = chr(symbol) @@ -425,7 +427,6 @@ class Textbox(ContolMobject): class ControlPanel(Group): CONFIG = { - "listen_to_events": True, "panel_kwargs": { "width": FRAME_WIDTH / 4, "height": MED_SMALL_BUFF + FRAME_HEIGHT, @@ -451,6 +452,8 @@ class ControlPanel(Group): self.panel = Rectangle(**self.panel_kwargs) self.panel.to_corner(UP + LEFT, buff=0) self.panel.shift(self.panel.get_height() * UP) + self.panel.listen_to_events = True + self.panel.on_mouse_scroll = self.panel_on_mouse_scroll self.panel_opener_rect = Rectangle(**self.opener_kwargs) self.panel_info_text = Text(**self.opener_text_kwargs) @@ -458,6 +461,8 @@ class ControlPanel(Group): self.panel_opener = Group(self.panel_opener_rect, self.panel_info_text) self.panel_opener.next_to(self.panel, DOWN, aligned_edge=DOWN) + self.panel_opener.listen_to_events = True + self.panel_opener.on_mouse_drag = self.panel_opener_on_mouse_drag self.controls = Group(*controls) self.controls.arrange(DOWN, center=False, aligned_edge=ORIGIN) @@ -510,14 +515,12 @@ class ControlPanel(Group): self.move_panel_and_controls_to_panel_opener() return self - def on_mouse_drag(self, point, d_point, buttons, modifiers): - if self.panel_opener.is_point_touching(point): - self.panel_opener.match_y(Dot(point)) - self.move_panel_and_controls_to_panel_opener() - return False + def panel_opener_on_mouse_drag(self, point, d_point, buttons, modifiers): + self.panel_opener.match_y(Dot(point)) + self.move_panel_and_controls_to_panel_opener() + return False - def on_mouse_scroll(self, point, offset): - if self.panel.is_point_touching(point): - factor = 10 * offset[1] - self.controls.set_y(self.controls.get_y() + factor) - return False + def panel_on_mouse_scroll(self, point, offset): + factor = 10 * offset[1] + self.controls.set_y(self.controls.get_y() + factor) + return False diff --git a/manimlib/scene/scene.py b/manimlib/scene/scene.py index 293b7a71..f15aaa3a 100644 --- a/manimlib/scene/scene.py +++ b/manimlib/scene/scene.py @@ -58,6 +58,9 @@ class Scene(object): self.mouse_point = Point() self.mouse_drag_point = Point() + self.mob_listners = [] + self.mobjects_to_drag = [] + # Much nicer to work with deterministic scenes if self.random_seed is not None: random.seed(self.random_seed) @@ -205,6 +208,9 @@ class Scene(object): """ self.remove(*new_mobjects) self.mobjects += new_mobjects + for new_mob in new_mobjects: + for mob_listner in filter(lambda mob: mob.listen_to_events, reversed(new_mob.get_family())): + self.mob_listners.insert(0, mob_listner) return self def add_mobjects_among(self, values): @@ -232,6 +238,9 @@ class Scene(object): def bring_to_back(self, *mobjects): self.remove(*mobjects) self.mobjects = list(mobjects) + self.mobjects + for new_mob in reversed(mobjects): + for mob_listner in filter(lambda mob: mob.listen_to_events, reversed(new_mob.get_family())): + self.mob_listners.append(mob_listner) return self def clear(self): @@ -519,10 +528,7 @@ class Scene(object): in reversed order. So the top most mobject's event is called first. This helps in event bubbling. """ - return filter( - lambda mob: mob.listen_to_events, - reversed(self.get_mobject_family_members()) - ) + return self.mob_listners def on_mouse_motion(self, point, d_point): self.mouse_point.move_to(point) @@ -548,13 +554,16 @@ class Scene(object): def on_mouse_drag(self, point, d_point, buttons, modifiers): self.mouse_drag_point.move_to(point) - for mob_listener in self.get_event_listeners_mobjects(): - if mob_listener.is_point_touching(point): - propagate_event = mob_listener.on_mouse_drag(point, d_point, buttons, modifiers) - if propagate_event is not None and propagate_event is False: - return + for mob_listener in self.mobjects_to_drag: + propagate_event = mob_listener.on_mouse_drag(point, d_point, buttons, modifiers) + if propagate_event is not None and propagate_event is False: + return def on_mouse_press(self, point, button, mods): + for mob_listener in self.get_event_listeners_mobjects(): + if mob_listener.is_point_touching(point): + self.mobjects_to_drag.append(mob_listener) + for mob_listener in self.get_event_listeners_mobjects(): if mob_listener.is_point_touching(point): propagate_event = mob_listener.on_mouse_press(point, button, mods) @@ -562,6 +571,8 @@ class Scene(object): return def on_mouse_release(self, point, button, mods): + self.mobjects_to_drag = [] + for mob_listener in self.get_event_listeners_mobjects(): if mob_listener.is_point_touching(point): propagate_event = mob_listener.on_mouse_release(point, button, mods) From fdcac10861f574bb223c3f7c589870a5de6ea923 Mon Sep 17 00:00:00 2001 From: Sahil Makhijani Date: Mon, 1 Feb 2021 01:41:37 +0530 Subject: [PATCH 2/3] Commented out self.embed in ControlsExample --- example_scenes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/example_scenes.py b/example_scenes.py index d8b7d4b2..f2a95727 100644 --- a/example_scenes.py +++ b/example_scenes.py @@ -497,7 +497,8 @@ class ControlsExample(Scene): self.add(MotionMobject(text)) self.textbox.set_value("Manim") - self.embed() + # self.wait(60) + # self.embed() # See https://github.com/3b1b/videos for many, many more From 2061f95ef557977952b0ed8d7f0c73e3f2656f03 Mon Sep 17 00:00:00 2001 From: Sahil Makhijani Date: Mon, 1 Feb 2021 22:55:36 +0530 Subject: [PATCH 3/3] Fixed ControlMobject Typo --- manimlib/mobject/interactive.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/manimlib/mobject/interactive.py b/manimlib/mobject/interactive.py index 79c80f23..bb27c99e 100644 --- a/manimlib/mobject/interactive.py +++ b/manimlib/mobject/interactive.py @@ -57,7 +57,7 @@ class Button(Mobject): # Controls -class ContolMobject(ValueTracker): +class ControlMobject(ValueTracker): CONFIG = { "listen_to_events": True } @@ -84,7 +84,7 @@ class ContolMobject(ValueTracker): pass -class EnableDisableButton(ContolMobject): +class EnableDisableButton(ControlMobject): CONFIG = { "value_type": np.dtype(bool), "rect_kwargs": { @@ -118,7 +118,7 @@ class EnableDisableButton(ContolMobject): return False -class Checkbox(ContolMobject): +class Checkbox(ControlMobject): CONFIG = { "value_type": np.dtype(bool), "rect_kwargs": { @@ -187,7 +187,7 @@ class Checkbox(ContolMobject): return cross -class LinearNumberSlider(ContolMobject): +class LinearNumberSlider(ControlMobject): CONFIG = { # Since, only slider circle listnes to drag event "listen_to_events": False, @@ -349,7 +349,7 @@ class ColorSliders(Group): return rgba[3] -class Textbox(ContolMobject): +class Textbox(ControlMobject): CONFIG = { "value_type": np.dtype(object),