def bordered_group( self, *children: Mobject, border_attrs: Optional[Dict] = None, title: Optional[str] = None, title_attrs: Optional[Dict] = None, ): group = VGroup(*children) width = ( abs(max(child.get_x(RIGHT) for child in children) - min(child.get_x(LEFT) for child in children)) + 1.2 ) height = abs(max(child.get_y(UP) for child in children) - min(child.get_y(DOWN) for child in children)) + 1.2 rect = Rectangle(**_merge(border_attrs, width=width, height=height)) rect.move_to(group.get_center_of_mass()) group.add_to_back(rect) if title: label = self.text_box( title, **_merge(title_attrs, bg_color=BLACK, border_color=BLACK, rounded=False, shadow=False) ) label.scale(0.8) label.move_to(group.get_top()) group.add(label) return group
def _box( self, parent: VGroup, bg_color, color, rounded, shadow, text, wrap_at, border_color, text_attrs: Optional[dict], border_builder: Callable[[Text], Polygon], ) -> VGroup: if wrap_at: text = "\n".join(wrap(text, wrap_at)) title = Text(text, font=self.text_font, color=color, **(text_attrs if text_attrs else {})) border = border_builder(title) border.set_color(border_color) bg_color, bg_opacity = self._color_and_opacity(bg_color, text) border.set_fill(color=bg_color, opacity=bg_opacity) if rounded: border.round_corners(ROUNDED_RADIUS) title.move_to(border) parent.add(border, title) if shadow and bg_opacity: s_rect = border.copy() s_rect.set_color(SHADOW_COLOR) shadow_opacity = SHADOW_OPACITY s_rect.set_stroke(width=0) s_rect.set_background_stroke(color=GREEN, opacity=shadow_opacity, width=0) s_rect.set_fill(opacity=shadow_opacity) s_rect.scale(1 + SHADOW_SHIFT) s_rect.shift(SHADOW_SHIFT * DR) parent.add_to_back(s_rect) return parent