Interactive Mobjects Performance Improvements

This commit is contained in:
Sahil Makhijani
2021-01-31 16:05:55 +05:30
parent b63ba1c7a4
commit d6b23324a6
2 changed files with 60 additions and 46 deletions

View File

@ -22,21 +22,19 @@ class MotionMobject(Mobject):
You could hold and drag this object to any position You could hold and drag this object to any position
""" """
CONFIG = {
"listen_to_events": True
}
def __init__(self, mobject, **kwargs): def __init__(self, mobject, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
assert(isinstance(mobject, Mobject))
self.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 # To avoid locking it as static mobject
self.mobject.add_updater(lambda mob: None) self.mobject.add_updater(lambda mob: None)
self.add(mobject) self.add(mobject)
def on_mouse_drag(self, point, d_point, buttons, modifiers): def mob_on_mouse_drag(self, point, d_point, buttons, modifiers):
if self.mobject.is_point_touching(point): self.mobject.move_to(point)
self.mobject.move_to(point) return False
return False
class Button(Mobject): class Button(Mobject):
@ -44,19 +42,17 @@ class Button(Mobject):
Pass any mobject and register an on_click method Pass any mobject and register an on_click method
""" """
CONFIG = {
"listen_to_events": True
}
def __init__(self, mobject, on_click, **kwargs): def __init__(self, mobject, on_click, **kwargs):
super().__init__(**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) self.add(self.mobject)
def on_mouse_press(self, point, button, mods): def mob_on_mouse_press(self, point, button, mods):
if self.mobject.is_point_touching(point): self.on_click()
self.on_click() return False
return False
# Controls # Controls
@ -193,6 +189,9 @@ class Checkbox(ContolMobject):
class LinearNumberSlider(ContolMobject): class LinearNumberSlider(ContolMobject):
CONFIG = { CONFIG = {
# Since, only slider circle listnes to drag event
"listen_to_events": False,
"value_type": np.float64, "value_type": np.float64,
"min_value": -10.0, "min_value": -10.0,
"max_value": 10.0, "max_value": 10.0,
@ -222,6 +221,9 @@ class LinearNumberSlider(ContolMobject):
self.slider_axis.set_opacity(0.0) self.slider_axis.set_opacity(0.0)
self.slider.move_to(self.slider_axis) 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) super().__init__(value, self.bar, self.slider, self.slider_axis, ** kwargs)
def assert_value(self, value): def assert_value(self, value):
@ -231,10 +233,9 @@ class LinearNumberSlider(ContolMobject):
prop = (value - self.min_value) / (self.max_value - self.min_value) prop = (value - self.min_value) / (self.max_value - self.min_value)
self.slider.move_to(self.slider_axis.point_from_proportion(prop)) self.slider.move_to(self.slider_axis.point_from_proportion(prop))
def on_mouse_drag(self, point, d_point, buttons, modifiers): def slider_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))
self.set_value(self.get_value_from_point(point)) return False
return False
# Helper Methods # Helper Methods
@ -371,6 +372,8 @@ class Textbox(ContolMobject):
digest_config(self, kwargs) digest_config(self, kwargs)
self.isActive = self.isInitiallyActive self.isActive = self.isInitiallyActive
self.box = Rectangle(**self.box_kwargs) 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) self.text = Text(value, **self.text_kwargs)
super().__init__(value, self.box, self.text, **kwargs) super().__init__(value, self.box, self.text, **kwargs)
self.update_text(value) self.update_text(value)
@ -397,11 +400,10 @@ class Textbox(ContolMobject):
else: else:
self.box.set_stroke(self.deactive_color) self.box.set_stroke(self.deactive_color)
def on_mouse_press(self, point, button, mods): def box_on_mouse_press(self, point, button, mods):
if self.box.is_point_touching(point): self.isActive = not self.isActive
self.isActive = not self.isActive self.active_anim(self.isActive)
self.active_anim(self.isActive) return False
return False
def on_key_press(self, symbol, modifiers): def on_key_press(self, symbol, modifiers):
char = chr(symbol) char = chr(symbol)
@ -425,7 +427,6 @@ class Textbox(ContolMobject):
class ControlPanel(Group): class ControlPanel(Group):
CONFIG = { CONFIG = {
"listen_to_events": True,
"panel_kwargs": { "panel_kwargs": {
"width": FRAME_WIDTH / 4, "width": FRAME_WIDTH / 4,
"height": MED_SMALL_BUFF + FRAME_HEIGHT, "height": MED_SMALL_BUFF + FRAME_HEIGHT,
@ -451,6 +452,8 @@ class ControlPanel(Group):
self.panel = Rectangle(**self.panel_kwargs) self.panel = Rectangle(**self.panel_kwargs)
self.panel.to_corner(UP + LEFT, buff=0) self.panel.to_corner(UP + LEFT, buff=0)
self.panel.shift(self.panel.get_height() * UP) 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_opener_rect = Rectangle(**self.opener_kwargs)
self.panel_info_text = Text(**self.opener_text_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 = Group(self.panel_opener_rect, self.panel_info_text)
self.panel_opener.next_to(self.panel, DOWN, aligned_edge=DOWN) 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 = Group(*controls)
self.controls.arrange(DOWN, center=False, aligned_edge=ORIGIN) self.controls.arrange(DOWN, center=False, aligned_edge=ORIGIN)
@ -510,14 +515,12 @@ class ControlPanel(Group):
self.move_panel_and_controls_to_panel_opener() self.move_panel_and_controls_to_panel_opener()
return self return self
def on_mouse_drag(self, point, d_point, buttons, modifiers): def panel_opener_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.panel_opener.match_y(Dot(point)) self.move_panel_and_controls_to_panel_opener()
self.move_panel_and_controls_to_panel_opener() return False
return False
def on_mouse_scroll(self, point, offset): def panel_on_mouse_scroll(self, point, offset):
if self.panel.is_point_touching(point): factor = 10 * offset[1]
factor = 10 * offset[1] self.controls.set_y(self.controls.get_y() + factor)
self.controls.set_y(self.controls.get_y() + factor) return False
return False

View File

@ -58,6 +58,9 @@ class Scene(object):
self.mouse_point = Point() self.mouse_point = Point()
self.mouse_drag_point = Point() self.mouse_drag_point = Point()
self.mob_listners = []
self.mobjects_to_drag = []
# Much nicer to work with deterministic scenes # Much nicer to work with deterministic scenes
if self.random_seed is not None: if self.random_seed is not None:
random.seed(self.random_seed) random.seed(self.random_seed)
@ -205,6 +208,9 @@ class Scene(object):
""" """
self.remove(*new_mobjects) self.remove(*new_mobjects)
self.mobjects += 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 return self
def add_mobjects_among(self, values): def add_mobjects_among(self, values):
@ -232,6 +238,9 @@ class Scene(object):
def bring_to_back(self, *mobjects): def bring_to_back(self, *mobjects):
self.remove(*mobjects) self.remove(*mobjects)
self.mobjects = list(mobjects) + self.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 return self
def clear(self): def clear(self):
@ -519,10 +528,7 @@ class Scene(object):
in reversed order. So the top most mobject's event is called first. in reversed order. So the top most mobject's event is called first.
This helps in event bubbling. This helps in event bubbling.
""" """
return filter( return self.mob_listners
lambda mob: mob.listen_to_events,
reversed(self.get_mobject_family_members())
)
def on_mouse_motion(self, point, d_point): def on_mouse_motion(self, point, d_point):
self.mouse_point.move_to(point) self.mouse_point.move_to(point)
@ -548,13 +554,16 @@ class Scene(object):
def on_mouse_drag(self, point, d_point, buttons, modifiers): def on_mouse_drag(self, point, d_point, buttons, modifiers):
self.mouse_drag_point.move_to(point) self.mouse_drag_point.move_to(point)
for mob_listener in self.get_event_listeners_mobjects(): for mob_listener in self.mobjects_to_drag:
if mob_listener.is_point_touching(point): propagate_event = mob_listener.on_mouse_drag(point, d_point, buttons, modifiers)
propagate_event = mob_listener.on_mouse_drag(point, d_point, buttons, modifiers) if propagate_event is not None and propagate_event is False:
if propagate_event is not None and propagate_event is False: return
return
def on_mouse_press(self, point, button, mods): 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(): for mob_listener in self.get_event_listeners_mobjects():
if mob_listener.is_point_touching(point): if mob_listener.is_point_touching(point):
propagate_event = mob_listener.on_mouse_press(point, button, mods) propagate_event = mob_listener.on_mouse_press(point, button, mods)
@ -562,6 +571,8 @@ class Scene(object):
return return
def on_mouse_release(self, point, button, mods): def on_mouse_release(self, point, button, mods):
self.mobjects_to_drag = []
for mob_listener in self.get_event_listeners_mobjects(): for mob_listener in self.get_event_listeners_mobjects():
if mob_listener.is_point_touching(point): if mob_listener.is_point_touching(point):
propagate_event = mob_listener.on_mouse_release(point, button, mods) propagate_event = mob_listener.on_mouse_release(point, button, mods)