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
|
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
|
|
||||||
|
@ -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)
|
||||||
|
Reference in New Issue
Block a user