mirror of
https://github.com/3b1b/manim.git
synced 2025-07-28 04:23:16 +08:00
Interactive Mobjects Performance Improvements
This commit is contained in:
@ -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
|
||||
|
@ -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)
|
||||
|
Reference in New Issue
Block a user