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
"""
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

View File

@ -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)