def put_at_tip(self, mob: Mobject, use_next_to: bool = True, **kwargs): if use_next_to: mob.next_to(self.get_tip(), np.round(self.get_direction()), **kwargs) else: mob.move_to(self.get_tip()) buff = kwargs.get("buff", DEFAULT_MOBJECT_TO_MOBJECT_BUFFER) shift_distance = mob.get_width() / 2.0 + buff mob.shift(self.get_direction() * shift_distance) return self
def get_highlight(self, mobject: Mobject) -> Mobject: if isinstance(mobject, VMobject) and mobject.has_points( ) and not self.select_top_level_mobs: length = max([mobject.get_height(), mobject.get_width()]) result = VHighlight( mobject, max_stroke_addition=min([50 * length, 10]), ) result.add_updater(lambda m: m.replace(mobject, stretch=True)) return result elif isinstance(mobject, DotCloud): return Mobject() else: return self.get_corner_dots(mobject)
class Bubble(SVGMobject): CONFIG = { "direction": LEFT, "center_point": ORIGIN, "content_scale_factor": 0.75, "height": 5, "width": 8, "bubble_center_adjustment_factor": 1. / 8, "file_name": None, "fill_color": BLACK, "fill_opacity": 0.8, "stroke_color": WHITE, "stroke_width": 3, } def __init__(self, **kwargs): digest_config(self, kwargs, locals()) if self.file_name is None: raise Exception("Must invoke Bubble subclass") self.file_name = os.path.join("assets", self.file_name) SVGMobject.__init__(self, **kwargs) self.center() self.stretch_to_fit_height(self.height) self.stretch_to_fit_width(self.width) if self.direction[0] > 0: self.flip() self.direction_was_specified = ("direction" in kwargs) self.content = Mobject() def get_tip(self): # TODO, find a better way return self.get_corner(DOWN + self.direction) - 0.6 * self.direction def get_bubble_center(self): factor = self.bubble_center_adjustment_factor return self.get_center() + factor * self.get_height() * UP def move_tip_to(self, point): mover = VGroup(self) if self.content is not None: mover.add(self.content) mover.shift(point - self.get_tip()) return self def flip(self, axis=UP): Mobject.flip(self, axis=axis) if abs(axis[1]) > 0: self.direction = -np.array(self.direction) return self def pin_to(self, mobject): mob_center = mobject.get_center() want_to_flip = np.sign(mob_center[0]) != np.sign(self.direction[0]) can_flip = not self.direction_was_specified if want_to_flip and can_flip: self.flip() boundary_point = mobject.get_critical_point(UP - self.direction) vector_from_center = 1.0 * (boundary_point - mob_center) self.move_tip_to(mob_center + vector_from_center) return self def position_mobject_inside(self, mobject): scaled_width = self.content_scale_factor * self.get_width() if mobject.get_width() > scaled_width: mobject.set_width(scaled_width) mobject.shift(self.get_bubble_center() - mobject.get_center()) return mobject def add_content(self, mobject): self.position_mobject_inside(mobject) self.content = mobject return self.content def write(self, *text): self.add_content(TextMobject(*text)) return self def resize_to_content(self): target_width = self.content.get_width() target_width += max(MED_LARGE_BUFF, 2) target_height = self.content.get_height() target_height += 2.5 * LARGE_BUFF tip_point = self.get_tip() self.stretch_to_fit_width(target_width) self.stretch_to_fit_height(target_height) self.move_tip_to(tip_point) self.position_mobject_inside(self.content) def clear(self): self.add_content(VMobject()) return self
class Bubble(SVGMobject): CONFIG = { "direction": LEFT, "center_point": ORIGIN, "content_scale_factor": 0.75, "height": 5, "width": 8, "bubble_center_adjustment_factor": 1. / 8, "file_name": None, "fill_color": BLACK, "fill_opacity": 0.8, "stroke_color": WHITE, "stroke_width": 3, } def __init__(self, **kwargs): digest_config(self, kwargs, locals()) if self.file_name is None: raise Exception("Must invoke Bubble subclass") try: SVGMobject.__init__(self, **kwargs) except IOError as err: self.file_name = os.path.join(FILE_DIR, self.file_name) SVGMobject.__init__(self, **kwargs) self.center() self.stretch_to_fit_height(self.height) self.stretch_to_fit_width(self.width) if self.direction[0] > 0: Mobject.flip(self) self.direction_was_specified = ("direction" in kwargs) self.content = Mobject() def get_tip(self): # TODO, find a better way return self.get_corner(DOWN + self.direction) - 0.6 * self.direction def get_bubble_center(self): factor = self.bubble_center_adjustment_factor return self.get_center() + factor * self.get_height() * UP def move_tip_to(self, point): mover = VGroup(self) if self.content is not None: mover.add(self.content) mover.shift(point - self.get_tip()) return self def flip(self): Mobject.flip(self) self.direction = -np.array(self.direction) return self def pin_to(self, mobject): mob_center = mobject.get_center() want_to_flip = np.sign(mob_center[0]) != np.sign(self.direction[0]) can_flip = not self.direction_was_specified if want_to_flip and can_flip: self.flip() boundary_point = mobject.get_critical_point(UP - self.direction) vector_from_center = 1.0 * (boundary_point - mob_center) self.move_tip_to(mob_center + vector_from_center) return self def position_mobject_inside(self, mobject): scaled_width = self.content_scale_factor * self.get_width() if mobject.get_width() > scaled_width: mobject.set_width(scaled_width) mobject.shift( self.get_bubble_center() - mobject.get_center() ) return mobject def add_content(self, mobject): self.position_mobject_inside(mobject) self.content = mobject return self.content def write(self, *text): self.add_content(TextMobject(*text)) return self def resize_to_content(self): target_width = self.content.get_width() target_width += max(MED_LARGE_BUFF, 2) target_height = self.content.get_height() target_height += 2.5 * LARGE_BUFF tip_point = self.get_tip() self.stretch_to_fit_width(target_width) self.stretch_to_fit_height(target_height) self.move_tip_to(tip_point) self.position_mobject_inside(self.content) def clear(self): self.add_content(VMobject()) return self
def __init__(self, mobject: Mobject, **kwargs): digest_config(self, kwargs) kwargs["width"] = mobject.get_width() + 2 * self.buff kwargs["height"] = mobject.get_height() + 2 * self.buff Rectangle.__init__(self, **kwargs) self.move_to(mobject)