def add_T_label(self, x_val, side=consts.RIGHT, label=None, color=Color('WHITE'), animated=False, **kwargs): triangle = RegularPolygon(n=3, start_angle=np.pi / 2) triangle.set_height(consts.MED_SMALL_BUFF) triangle.move_to(self.coords_to_point(x_val, 0), consts.UP) triangle.set_fill(color, 1) triangle.set_stroke(width=0) if label is None: T_label = TexMobject(self.variable_point_label, fill_color=color) else: T_label = TexMobject(label, fill_color=color) T_label.next_to(triangle, consts.DOWN) v_line = self.get_vertical_line_to_graph( x_val, self.v_graph, color=Color('YELLOW') ) if animated: self.play( DrawBorderThenFill(triangle), ShowCreation(v_line), Write(T_label, run_time=1), **kwargs ) if np.all(side == consts.LEFT): self.left_T_label_group = VGroup(T_label, triangle) self.left_v_line = v_line self.add(self.left_T_label_group, self.left_v_line) elif np.all(side == consts.RIGHT): self.right_T_label_group = VGroup(T_label, triangle) self.right_v_line = v_line self.add(self.right_T_label_group, self.right_v_line)
class PartyHat(SVGMobject): CONFIG = { "file_name": "party_hat", "height": 1.5, "pi_creature": None, "stroke_width": 0, "fill_opacity": 1, "frills_colors": [Color('MAROON_B'), Color('PURPLE')], "cone_color": Color('GREEN'), "dots_colors": [Color('YELLOW')], } NUM_FRILLS = 7 NUM_DOTS = 6 def __init__(self, **kwargs): SVGMobject.__init__(self, **kwargs) self.set_height(self.height) if self.pi_creature is not None: self.next_to(self.pi_creature.eyes, consts.UP, buff=0) self.frills = VGroup(*self[:self.NUM_FRILLS]) self.cone = self[self.NUM_FRILLS] self.dots = VGroup(*self[self.NUM_FRILLS + 1:]) self.frills.set_color_by_gradient(*self.frills_colors) self.cone.set_color(self.cone_color) self.dots.set_color_by_gradient(*self.dots_colors)
def __init__(self, pi_creature, **kwargs): SVGMobject.__init__(self, **kwargs) self.set_stroke(Color('WHITE'), width=0) self.set_fill(Color('GREY'), 1) self.set_width( self.glasses_width_to_eyes_width * pi_creature.eyes.get_width() ) self.move_to(pi_creature.eyes, consts.UP)
class Guitar(SVGMobject): CONFIG = { "file_name": "guitar", "height": 2.5, "fill_color": Color('GREY'), "fill_opacity": 1, "stroke_color": Color('WHITE'), "stroke_width": 0.5, }
class Lightbulb(SVGMobject): CONFIG = { "file_name": "lightbulb", "height": 1, "stroke_color": Color('YELLOW'), "stroke_width": 3, "fill_color": Color('YELLOW'), "fill_opacity": 0, }
def __init__(self, **kwargs): super().__init__(**kwargs) body = Cube(side_length=1) for dim, scale_factor in enumerate(self.body_dimensions): body.stretch(scale_factor, dim=dim) body.set_width(self.width) body.set_fill(self.shaded_body_color, opacity=1) body.sort(lambda p: p[2]) body[-1].set_fill(self.body_color) screen_plate = body.copy() keyboard = VGroup(*[ VGroup(*[ Square(**self.key_color_kwargs) for x in range(12 - y % 2) ]).arrange(consts.RIGHT, buff=consts.SMALL_BUFF) for y in range(4) ]).arrange(consts.DOWN, buff=consts.MED_SMALL_BUFF) keyboard.stretch_to_fit_width( self.keyboard_width_to_body_width * body.get_width(), ) keyboard.stretch_to_fit_height( self.keyboard_height_to_body_height * body.get_height(), ) keyboard.next_to(body, consts.OUT, buff=0.1 * consts.SMALL_BUFF) keyboard.shift(consts.MED_SMALL_BUFF * consts.UP) body.add(keyboard) screen_plate.stretch(self.screen_thickness / self.body_dimensions[2], dim=2) screen = Rectangle( stroke_width=0, fill_color=Color('BLACK'), fill_opacity=1, ) screen.replace(screen_plate, stretch=True) screen.scale_in_place(self.screen_width_to_screen_plate_width) screen.next_to(screen_plate, consts.OUT, buff=0.1 * consts.SMALL_BUFF) screen_plate.add(screen) screen_plate.next_to(body, consts.UP, buff=0) screen_plate.rotate( self.open_angle, consts.RIGHT, about_point=screen_plate.get_bottom() ) self.screen_plate = screen_plate self.screen = screen axis = Line( body.get_corner(consts.UP + consts.LEFT + consts.OUT), body.get_corner(consts.UP + consts.RIGHT + consts.OUT), color=Color('BLACK'), stroke_width=2 ) self.axis = axis self.add(body, screen_plate, axis) self.rotate(5 * np.pi / 12, consts.LEFT, about_point=consts.ORIGIN) self.rotate(np.pi / 6, consts.DOWN, about_point=consts.ORIGIN)
def set_colors_by_radial_gradient( self, center=None, radius=1, inner_color=Color('WHITE'), outer_color=Color('BLACK')): self.set_submobject_colors_by_radial_gradient( center, radius, inner_color, outer_color) return self
def __init__(self, **kwargs): SVGMobject.__init__(self, **kwargs) circle = Circle( stroke_width=3, stroke_color=Color('GREEN'), fill_opacity=1, fill_color=Color('BLUE_C'), ) circle.replace(self) self.add_to_back(circle)
class VideoSeries(VGroup): CONFIG = { "num_videos": 11, "gradient_colors": [Color('BLUE_B'), Color('BLUE_D')], } def __init__(self, **kwargs): digest_config(self, kwargs) videos = [VideoIcon() for x in range(self.num_videos)] VGroup.__init__(self, *videos, **kwargs) self.arrange() self.set_width(consts.FRAME_WIDTH - consts.MED_LARGE_BUFF) self.set_color_by_gradient(*self.gradient_colors)
def __init__(self, **kwargs): SVGMobject.__init__(self, **kwargs) self.set_stroke(Color('WHITE'), width=0) colors = [Color('BLUE_E'), Color(hex_l='#008445'), Color('GREEN_B')] index_lists = [ (10, 11, 12, 13, 14, 21, 22, 23, 24, 27, 28, 29, 30), (0, 1, 2, 3, 4, 15, 16, 17, 26), (5, 6, 7, 8, 9, 18, 19, 20, 25) ] for color, index_list in zip(colors, index_lists): for i in index_list: self.submobjects[i].set_fill(color, opacity=1) self.set_height(self.height) self.center()
class Broadcast(LaggedStart): CONFIG = { "small_radius": 0.0, "big_radius": 5, "n_circles": 5, "start_stroke_width": 8, "color": Color('WHITE'), "remover": True, "lag_ratio": 0.2, "run_time": 3, "remover": True, } def __init__(self, focal_point, **kwargs): digest_config(self, kwargs) circles = VGroup() for x in range(self.n_circles): circle = Circle( radius=self.big_radius, stroke_color=Color('BLACK'), stroke_width=0, ) circle.add_updater(lambda c: c.move_to(focal_point)) circle.save_state() circle.set_width(self.small_radius * 2) circle.set_stroke(self.color, self.start_stroke_width) circles.add(circle) animations = [Restore(circle) for circle in circles] super().__init__(*animations, **kwargs)
def add_vector(self, vector, color=Color('YELLOW'), animate=True, **kwargs): if not isinstance(vector, Arrow): vector = Vector(vector, color=color, **kwargs) if animate: self.play(GrowArrow(vector)) self.add(vector) return vector
def get_front_line(self): return DashedLine( self.get_corner(consts.UP + consts.RIGHT), self.get_corner(consts.DOWN + consts.RIGHT), color=Color('BLUE'), dash_length=0.05, )
def __init__(self, **kwargs): SVGMobject.__init__(self, **kwargs) path = self.submobjects[0] subpaths = path.get_subpaths() path.clear_points() for indices in [(0, 1), (2, 3), (4, 6, 7), (5,), (8,)]: part = VMobject() for index in indices: part.append_points(subpaths[index]) path.add(part) self.set_height(self.height) self.set_stroke(color=Color('WHITE'), width=0) self.set_fill(self.color, opacity=1) orientation_line = Line(self.get_left(), self.get_right()) orientation_line.set_stroke(width=0) self.add(orientation_line) self.orientation_line = orientation_line for light, color in zip(self.get_lights(), self.light_colors): light.set_fill(color, 1) light.is_subpath = False self.add_treds_to_tires()
class SuitSymbol(SVGMobject): CONFIG = { "height": 0.5, "fill_opacity": 1, "stroke_width": 0, "red": "#D02028", "black": Color('BLACK'), } def __init__(self, suit_name, **kwargs): digest_config(self, kwargs) suits_to_colors = { "hearts": self.red, "diamonds": self.red, "spades": self.black, "clubs": self.black, } if suit_name not in suits_to_colors: raise Exception("Invalid suit name") SVGMobject.__init__(self, file_name=suit_name, **kwargs) color = suits_to_colors[suit_name] self.set_stroke(width=0) self.set_fill(color, 1) self.set_height(self.height)
class FocusOn(Transform): CONFIG = { "opacity": 0.2, "color": Color('GREY'), "run_time": 2, "remover": True, } def __init__(self, focus_point, **kwargs): self.focus_point = focus_point # Initialize with blank mobject, while create_target # and create_starting_mobject handle the meat super().__init__(VMobject(), **kwargs) def create_target(self): little_dot = Dot(radius=0) little_dot.set_fill(self.color, opacity=self.opacity) little_dot.add_updater(lambda d: d.move_to(self.focus_point)) return little_dot def create_starting_mobject(self): return Dot( radius=consts.FRAME_X_RADIUS + consts.FRAME_Y_RADIUS, stroke_width=0, fill_color=self.color, fill_opacity=0, )
def set_style_data(self, stroke_color=None, stroke_width=None, fill_color=None, fill_opacity=None, family=True ): # Unchangable style, except for fill_opacity VMobject.set_style_data( self, stroke_color=Color('BLACK'), stroke_width=0, fill_color=Color('BLACK'), fill_opacity=fill_opacity ) return self
class BackgroundRectangle(SurroundingRectangle): CONFIG = { "color": Color('BLACK'), "stroke_width": 0, "stroke_opacity": 0, "fill_opacity": 0.75 } def __init__(self, mobject, **kwargs): SurroundingRectangle.__init__(self, mobject, **kwargs) self.original_fill_opacity = self.fill_opacity def pointwise_become_partial(self, mobject, a, b): self.set_fill(opacity=b * self.original_fill_opacity) return self def set_style_data(self, stroke_color=None, stroke_width=None, fill_color=None, fill_opacity=None, family=True ): # Unchangable style, except for fill_opacity VMobject.set_style_data( self, stroke_color=Color('BLACK'), stroke_width=0, fill_color=Color('BLACK'), fill_opacity=fill_opacity ) return self def get_fill_color(self): return Color(self.color)
class DashedVMobject(VMobject): CONFIG = { "num_dashes": 15, "positive_space_ratio": 0.5, "color": Color('WHITE') } def __init__(self, vmobject, **kwargs): VMobject.__init__(self, **kwargs) num_dashes = self.num_dashes ps_ratio = self.positive_space_ratio if num_dashes > 0: # End points of the unit interval for division alphas = np.linspace(0, 1, num_dashes + 1) # This determines the length of each "dash" full_d_alpha = (1.0 / num_dashes) partial_d_alpha = full_d_alpha * ps_ratio # Rescale so that the last point of vmobject will # be the end of the last dash alphas /= (1 - full_d_alpha + partial_d_alpha) self.add(*[ vmobject.get_subcurve(alpha, alpha + partial_d_alpha) for alpha in alphas[:-1] ]) # Family is already taken care of by get_subcurve # implementation self.match_style(vmobject, family=False)
def set_color(self, color=Color('YELLOW_C'), family=True): rgba = color_to_rgba(color) mobs = self.family_members_with_points() if family else [self] for mob in mobs: mob.rgbas[:, :] = rgba self.color = color return self
class TracedPath(VMobject): CONFIG = { "stroke_width": 2, "stroke_color": Color('WHITE'), "min_distance_to_new_point": 0.1, } def __init__(self, traced_point_func, **kwargs): super().__init__(**kwargs) self.traced_point_func = traced_point_func self.add_updater(lambda m: m.update_path()) def update_path(self): new_point = self.traced_point_func() if self.has_no_points(): self.start_new_path(new_point) self.add_line_to(new_point) else: # Set the end to be the new point self.points[-1] = new_point # Second to last point nppcc = self.n_points_per_cubic_curve dist = get_norm(new_point - self.points[-nppcc]) if dist >= self.min_distance_to_new_point: self.add_line_to(new_point)
def set_colors_by_radial_gradient(self, center=None, radius=1, inner_color=Color('WHITE'), outer_color=Color('BLACK')): start_rgba, end_rgba = list( map(color_to_rgba, [inner_color, outer_color])) if center is None: center = self.get_center() for mob in self.family_members_with_points(): num_points = mob.get_num_points() t = min(1, np.abs(mob.get_center() - center) / radius) mob.rgbas = np.array([interpolate(start_rgba, end_rgba, t)] * num_points) return self
class AnnularSector(Arc): CONFIG = { "inner_radius": 1, "outer_radius": 2, "angle": consts.TAU / 4, "start_angle": 0, "fill_opacity": 1, "stroke_width": 0, "color": Color('WHITE'), } def generate_points(self): inner_arc, outer_arc = [ Arc( start_angle=self.start_angle, angle=self.angle, radius=radius, arc_center=self.arc_center, ) for radius in (self.inner_radius, self.outer_radius) ] outer_arc.reverse_points() self.append_points(inner_arc.points) self.add_line_to(outer_arc.points[0]) self.append_points(outer_arc.points) self.add_line_to(inner_arc.points[0])
class Circle(Arc): CONFIG = { "color": Color('RED'), "close_new_points": True, "anchors_span_full_range": False } def __init__(self, **kwargs): Arc.__init__(self, 0, consts.TAU, **kwargs) def surround(self, mobject, dim_to_match=0, stretch=False, buffer_factor=1.2): # Ignores dim_to_match and stretch; result will always be a circle # TODO: Perhaps create an ellipse class to handle singele-dimension stretching # Something goes wrong here when surrounding lines? # TODO: Figure out and fix self.replace(mobject, dim_to_match, stretch) self.set_width( np.sqrt(mobject.get_width()**2 + mobject.get_height()**2)) self.scale(buffer_factor) def point_at_angle(self, angle): start_angle = angle_of_vector(self.points[0] - self.get_center()) return self.point_from_proportion((angle - start_angle) / consts.TAU)
def set_submobject_colors_by_radial_gradient( self, center=None, radius=1, inner_color=Color('WHITE'), outer_color=Color('BLACK')): if center is None: center = self.get_center() for mob in self.family_members_with_points(): t = get_norm(mob.get_center() - center) / radius t = min(t, 1) mob_color = interpolate_color(inner_color, outer_color, t) mob.set_color(mob_color, family=False) return self
def lock_in_faded_grid(self, dimness=0.7, axes_dimness=0.5): plane = self.add_plane() axes = plane.get_axes() plane.fade(dimness) axes.set_color(Color('WHITE')) axes.fade(axes_dimness) self.add(axes) self.freeze_background()
class Point(PMobject): CONFIG = { "color": Color('BLACK'), } def __init__(self, location=consts.ORIGIN, **kwargs): PMobject.__init__(self, **kwargs) self.add_points([location])
def get_submobject_index_labels(mobject, label_height=0.15): labels = VGroup() for n, submob in enumerate(mobject): label = Integer(n) label.set_height(label_height) label.move_to(submob) label.set_stroke(Color('BLACK'), 5, background=True) labels.add(label) return labels
class Dot(Circle): CONFIG = { "radius": DEFAULT_DOT_RADIUS, "stroke_width": 0, "fill_opacity": 1.0, "color": Color('WHITE') } def __init__(self, point=consts.ORIGIN, **kwargs): Circle.__init__(self, arc_center=point, **kwargs)
def __init__(self, **kwargs): Container.__init__(self, **kwargs) self.color = Color(self.color) if self.name is None: self.name = self.__class__.__name__ self.updaters = [] self.updating_suspended = False self.reset_points() self.generate_points() self.init_colors()