def construct(self): # Generate transformation animations of the twin dragon curve anims = list() fractal = VMobject() fractal.shift(UP) for order in range(-1, self.max_order + 1): new_fractal = TwinDragon(order=order) new_fractal.shift(UP) run_time = 0.5 if order >= 0 else 0 anims.append( Transform( fractal, new_fractal, submobject_mode="all_at_once", run_time=run_time, )) fractal = new_fractal # Add the channel name text = TextMobject("Solara570") text.scale(2).to_edge(DOWN, buff=1.2) # Now sit back and watch self.play( Succession(*anims, rate_func=smooth), Write(text, lag_factor=2.5), run_time=4.5, )
def init_colors(self): VMobject.init_colors(self) self.axes.set_stroke(self.axes_color, self.stroke_width) self.main_lines.set_stroke(self.color, self.stroke_width) self.secondary_lines.set_stroke(self.secondary_color, self.secondary_stroke_width) return self
def init_colors(self): VMobject.init_colors(self) self.axes.set_stroke(self.axes_color, self.stroke_width) self.main_lines.set_stroke(self.color, self.stroke_width) self.secondary_lines.set_stroke( self.secondary_color, self.secondary_stroke_width ) return self
def __init__(self, **kwargs): digest_config(self, kwargs) if self.leftmost_tick is None: tf = self.tick_frequency self.leftmost_tick = tf * np.ceil(self.x_min / tf) VMobject.__init__(self, **kwargs) if self.include_tip: self.add_tip()
def __init__(self, vmobject=None, **kwargs): VMobject.__init__(self, **kwargs) if vmobject is not None: self.points = np.array(vmobject.points) self.match_style(vmobject, family=False) self.submobjects = [ ThreeDVMobject(submob, **kwargs) for submob in vmobject.submobjects ]
def insert_n_anchor_points(self, n): if not self.path_arc: n_anchors = self.get_num_anchor_points() new_num_points = 3 * (n_anchors + n) - 2 self.points = np.array([ self.point_from_proportion(alpha) for alpha in np.linspace(0, 1, new_num_points) ]) else: VMobject.insert_n_anchor_points(self, n)
def init_colors(self): VMobject.init_colors(self) internal_pis = [ pi for pi in self.get_family() if isinstance(pi, PiCreature) ] random.seed(self.random_seed) for pi in reversed(internal_pis): color = random.choice(self.colors) pi.set_color(color) pi.set_stroke(color, width=0)
def init_colors(self): VMobject.init_colors(self) internal_pis = [ pi for pi in self.submobject_family() if isinstance(pi, PiCreature) ] random.seed(self.random_seed) for pi in reversed(internal_pis): color = random.choice(self.colors) pi.set_color(color) pi.set_stroke(color, width=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=BLACK, stroke_width=0, fill_color=BLACK, fill_opacity=fill_opacity) return self
def __init__(self, number, **kwargs): VMobject.__init__(self, **kwargs) self.number = number self.initial_config = kwargs if isinstance(number, complex): formatter = self.get_complex_formatter() else: formatter = self.get_formatter() num_string = formatter.format(number) shows_zero = np.round(number, self.num_decimal_places) == 0 if num_string.startswith("-") and shows_zero: if self.include_sign: num_string = "+" + num_string[1:] else: num_string = num_string[1:] self.add( *[SingleStringTexMobject(char, **kwargs) for char in num_string]) # Add non-numerical bits if self.show_ellipsis: self.add(SingleStringTexMobject("\\dots")) if num_string.startswith("-"): minus = self.submobjects[0] minus.next_to(self.submobjects[1], LEFT, buff=self.digit_to_digit_buff) if self.unit is not None: self.unit_sign = SingleStringTexMobject(self.unit, color=self.color) self.add(self.unit_sign) self.arrange_submobjects(buff=self.digit_to_digit_buff, aligned_edge=DOWN) # Handle alignment of parts that should be aligned # to the bottom for i, c in enumerate(num_string): if c == "-" and len(num_string) > i + 1: self[i].align_to(self[i + 1], alignment_vect=UP) elif c == ",": self[i].shift(self[i].get_height() * DOWN / 2) if self.unit and self.unit.startswith("^"): self.unit_sign.align_to(self, UP) # if self.include_background_rectangle: self.add_background_rectangle()
def __init__(self, obj, text, brace_direction=DOWN, **kwargs): VMobject.__init__(self, **kwargs) self.brace_direction = brace_direction if isinstance(obj, list): obj = VMobject(*obj) self.brace = Brace(obj, brace_direction, **kwargs) if isinstance(text, tuple) or isinstance(text, list): self.label = self.label_constructor(*text, **kwargs) else: self.label = self.label_constructor(str(text)) if self.label_scale != 1: self.label.scale(self.label_scale) self.brace.put_at_tip(self.label) self.submobjects = [self.brace, self.label]
def use_to_mobjects(self, use_element): # Remove initial "#" character ref = use_element.getAttribute("xlink:href")[1:] if ref not in self.ref_to_element: warnings.warn("%s not recognized" % ref) return VMobject() return self.get_mobjects_from(self.ref_to_element[ref])
def shift_brace(self, obj, **kwargs): if isinstance(obj, list): obj = VMobject(*obj) self.brace = Brace(obj, self.brace_direction, **kwargs) self.brace.put_at_tip(self.label) self.submobjects[0] = self.brace return self
def __init__(self, obj, text, brace_direction=DOWN, **kwargs): VMobject.__init__(self, **kwargs) self.brace_direction = brace_direction if isinstance(obj, list): obj = VMobject(*obj) self.brace = Brace(obj, brace_direction, **kwargs) if isinstance(text, tuple) or isinstance(text, list): self.label = self.label_constructor(*text, **kwargs) else: self.label = self.label_constructor(str(text)) if self.label_scale != 1: self.label.scale(self.label_scale) self.brace.put_at_tip(self.label) self.submobjects = [self.brace, self.label]
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=BLACK, stroke_width=0, fill_color=BLACK, fill_opacity=fill_opacity ) return self
def get_mobjects_from(self, element): result = [] if not isinstance(element, minidom.Element): return result if element.tagName == 'defs': self.update_ref_to_element(element) elif element.tagName == 'style': pass # TODO, handle style elif element.tagName in ['g', 'svg']: result += it.chain(*[ self.get_mobjects_from(child) for child in element.childNodes ]) elif element.tagName == 'path': result.append( self.path_string_to_mobject(element.getAttribute('d'))) elif element.tagName == 'use': result += self.use_to_mobjects(element) elif element.tagName == 'rect': result.append(self.rect_to_mobject(element)) elif element.tagName == 'circle': result.append(self.circle_to_mobject(element)) elif element.tagName == 'ellipse': result.append(self.ellipse_to_mobject(element)) elif element.tagName in ['polygon', 'polyline']: result.append(self.polygon_to_mobject(element)) else: pass # TODO # warnings.warn("Unknown element type: " + element.tagName) result = [m for m in result if m is not None] self.handle_transforms(element, VMobject(*result)) if len(result) > 1 and not self.unpack_groups: result = [VGroup(*result)] return result
def add_tip(self, add_at_end=True): tip = VMobject( close_new_points=True, mark_paths_closed=True, fill_color=self.color, fill_opacity=1, stroke_color=self.color, stroke_width=0, ) tip.add_at_end = add_at_end self.set_tip_points(tip, add_at_end, preserve_normal=False) self.add(tip) if not hasattr(self, 'tip'): self.tip = VGroup() self.tip.match_style(tip) self.tip.add(tip) return tip
def add_tip(self, add_at_end=True): tip = VMobject( close_new_points=True, mark_paths_closed=True, fill_color=self.color, fill_opacity=1, stroke_color=self.color, stroke_width=0, ) tip.add_at_end = add_at_end self.set_tip_points(tip, add_at_end, preserve_normal=False) self.add(tip) if not hasattr(self, 'tip'): self.tip = VGroup() self.tip.match_style(tip) self.tip.add(tip) return tip
def generate_points(self): if self.order < 0: return VMobject() tc = TohruCurve(order=self.order) kc = KannaCurve(order=self.order) kc.shift(tc.get_end() - kc.get_start()) group = VGroup(tc, kc).center() self.add(group)
def __init__(self, number, **kwargs): VMobject.__init__(self, **kwargs) # TODO, make this more ediable with a getter and setter self.number = number ndp = self.num_decimal_places # Build number string if isinstance(number, complex): num_string = '%.*f%s%.*fi' % (ndp, number.real, "-" if number.imag < 0 else "+", ndp, abs(number.imag)) else: num_string = '%.*f' % (ndp, number) negative_zero_string = "-%.*f" % (ndp, 0.) if num_string == negative_zero_string: num_string = num_string[1:] self.add(*[TexMobject(char, **kwargs) for char in num_string]) # Add non-numerical bits if self.show_ellipsis: self.add(TexMobject("\\dots")) if num_string.startswith("-"): minus = self.submobjects[0] minus.next_to(self.submobjects[1], LEFT, buff=self.digit_to_digit_buff) if self.unit is not None: self.unit_sign = TexMobject(self.unit, color=self.color) self.add(self.unit_sign) self.arrange_submobjects(buff=self.digit_to_digit_buff, aligned_edge=DOWN) # Handle alignment of parts that should be aligned # to the bottom for i, c in enumerate(num_string): if c == "-" and len(num_string) > i + 1: self[i].align_to(self[i + 1], alignment_vect=UP) if self.unit and self.unit.startswith("^"): self.unit_sign.align_to(self, UP) # if self.include_background_rectangle: self.add_background_rectangle()
def get_det_text(matrix, determinant=None, background_rect=True, initial_scale_factor=2): parens = TexMobject(["(", ")"]) parens.scale(initial_scale_factor) parens.stretch_to_fit_height(matrix.get_height()) l_paren, r_paren = parens.split() l_paren.next_to(matrix, LEFT, buff=0.1) r_paren.next_to(matrix, RIGHT, buff=0.1) det = TextMobject("det").next_to(l_paren, LEFT, buff=0.1) if background_rect: det.add_background_rectangle() det_text = VMobject(det, l_paren, r_paren) if determinant is not None: eq = TexMobject("=") eq.next_to(r_paren, RIGHT, buff=0.1) result = TexMobject(str(determinant)) result.next_to(eq, RIGHT, buff=0.2) det_text.add(eq, result) return det_text
def __init__(self, matrix, **kwargs): """ Matrix can either either include numbres, tex_strings, or mobjects """ VMobject.__init__(self, **kwargs) matrix = np.array(matrix, ndmin=1) mob_matrix = self.matrix_to_mob_matrix(matrix) self.organize_mob_matrix(mob_matrix) self.elements = VGroup(*mob_matrix.flatten()) self.add(self.elements) self.add_brackets() self.center() self.mob_matrix = mob_matrix if self.add_background_rectangles_to_entries: for mob in self.elements: mob.add_background_rectangle() if self.include_background_rectangle: self.add_background_rectangle()
def get_det_text(matrix, determinant=None, background_rect=True, initial_scale_factor=2): parens = TexMobject(["(", ")"]) parens.scale(initial_scale_factor) parens.stretch_to_fit_height(matrix.get_height()) l_paren, r_paren = parens.split() l_paren.next_to(matrix, LEFT, buff=0.1) r_paren.next_to(matrix, RIGHT, buff=0.1) det = TextMobject("det") det.scale(initial_scale_factor) det.next_to(l_paren, LEFT, buff=0.1) if background_rect: det.add_background_rectangle() det_text = VMobject(det, l_paren, r_paren) if determinant is not None: eq = TexMobject("=") eq.next_to(r_paren, RIGHT, buff=0.1) result = TexMobject(str(determinant)) result.next_to(eq, RIGHT, buff=0.2) det_text.add(eq, result) return det_text
def __init__(self, matrix, **kwargs): """ Matrix can either either include numbres, tex_strings, or mobjects """ VMobject.__init__(self, **kwargs) matrix = np.array(matrix) if matrix.ndim == 1: matrix = matrix.reshape((matrix.size, 1)) if not isinstance(matrix[0][0], Mobject): matrix = matrix.astype("string") matrix = self.string_matrix_to_mob_matrix(matrix) self.organize_mob_matrix(matrix) self.add(*matrix.flatten()) self.add_brackets() self.center() self.mob_matrix = matrix if self.add_background_rectangles: for mob in matrix.flatten(): mob.add_background_rectangle()
def __init__(self, matrix, **kwargs): """ Matrix can either either include numbres, tex_strings, or mobjects """ VMobject.__init__(self, **kwargs) matrix = np.array(matrix) if matrix.ndim == 1: matrix = matrix.reshape((matrix.size, 1)) mob_matrix = self.matrix_to_mob_matrix(matrix) self.organize_mob_matrix(mob_matrix) self.elements = VGroup(*mob_matrix.flatten()) self.add(self.elements) self.add_brackets() self.center() self.mob_matrix = mob_matrix if self.add_background_rectangles_to_entries: for mob in self.elements: mob.add_background_rectangle() if self.include_background_rectangle: self.add_background_rectangle()
def add_pupil_light_spot(self, pupils): # Purely an artifact of how the SVGs were drawn. # In a perfect world, this wouldn't be needed for pupil in pupils: index = 16 sub_points = pupil.points[:index] pupil.points = pupil.points[index + 2:] circle = VMobject() circle.points = sub_points circle.set_stroke(width=0) circle.set_fill(WHITE, 1) pupil.add(circle)
def show_ghost_movement(self, vector): if isinstance(vector, Arrow): vector = vector.get_end() - vector.get_start() elif len(vector) == 2: vector = np.append(np.array(vector), 0.0) x_max = int(FRAME_X_RADIUS + abs(vector[0])) y_max = int(FRAME_Y_RADIUS + abs(vector[1])) dots = VMobject(*[ Dot(x * RIGHT + y * UP) for x in range(-x_max, x_max) for y in range(-y_max, y_max) ]) dots.set_fill(BLACK, opacity=0) dots_halfway = dots.copy().shift(vector / 2).set_fill(WHITE, 1) dots_end = dots.copy().shift(vector) self.play(Transform( dots, dots_halfway, rate_func=rush_into )) self.play(Transform( dots, dots_end, rate_func=rush_from )) self.remove(dots)
def generate_points(self): self.add(self.source_point) self.lighthouse = Lighthouse() self.ambient_light = AmbientLight( source_point=VectorizedPoint(location=self.get_source_point()), color=self.color, num_levels=self.num_levels, radius=self.radius, opacity_function=self.opacity_function, max_opacity=self.max_opacity_ambient) if self.has_screen(): self.spotlight = Spotlight( source_point=VectorizedPoint(location=self.get_source_point()), color=self.color, num_levels=self.num_levels, radius=self.radius, screen=self.screen, opacity_function=self.opacity_function, max_opacity=self.max_opacity_spotlight, camera_mob=self.camera_mob) else: self.spotlight = Spotlight() self.shadow = VMobject(fill_color=SHADOW_COLOR, fill_opacity=1.0, stroke_color=BLACK) self.lighthouse.next_to(self.get_source_point(), DOWN, buff=0) self.ambient_light.move_source_to(self.get_source_point()) if self.has_screen(): self.spotlight.move_source_to(self.get_source_point()) self.update_shadow() self.add(self.ambient_light, self.spotlight, self.lighthouse, self.shadow)
def generate_points(self): if self.x_radius is None: center_to_edge = (FRAME_X_RADIUS + abs(self.center_point[0])) self.x_radius = center_to_edge / self.x_unit_size if self.y_radius is None: center_to_edge = (FRAME_Y_RADIUS + abs(self.center_point[1])) self.y_radius = center_to_edge / self.y_unit_size self.axes = VMobject() self.main_lines = VMobject() self.secondary_lines = VMobject() tuples = [ ( self.x_radius, self.x_line_frequency, self.y_radius * DOWN, self.y_radius * UP, RIGHT ), ( self.y_radius, self.y_line_frequency, self.x_radius * LEFT, self.x_radius * RIGHT, UP, ), ] for radius, freq, start, end, unit in tuples: main_range = np.arange(0, radius, freq) step = freq / float(freq + self.secondary_line_ratio) for v in np.arange(0, radius, step): line1 = Line(start + v * unit, end + v * unit) line2 = Line(start - v * unit, end - v * unit) if v == 0: self.axes.add(line1) elif v in main_range: self.main_lines.add(line1, line2) else: self.secondary_lines.add(line1, line2) self.add(self.secondary_lines, self.main_lines, self.axes) self.stretch(self.x_unit_size, 0) self.stretch(self.y_unit_size, 1) self.shift(self.center_point) # Put x_axis before y_axis y_axis, x_axis = self.axes.split() self.axes = VMobject(x_axis, y_axis)
def generate_points(self): self.add(self.source_point) self.lighthouse = Lighthouse() self.ambient_light = AmbientLight( source_point=VectorizedPoint(location=self.get_source_point()), color=self.color, num_levels=self.num_levels, radius=self.radius, opacity_function=self.opacity_function, max_opacity=self.max_opacity_ambient ) if self.has_screen(): self.spotlight = Spotlight( source_point=VectorizedPoint(location=self.get_source_point()), color=self.color, num_levels=self.num_levels, radius=self.radius, screen=self.screen, opacity_function=self.opacity_function, max_opacity=self.max_opacity_spotlight, camera_mob=self.camera_mob ) else: self.spotlight = Spotlight() self.shadow = VMobject(fill_color=SHADOW_COLOR, fill_opacity=1.0, stroke_color=BLACK) self.lighthouse.next_to(self.get_source_point(), DOWN, buff=0) self.ambient_light.move_source_to(self.get_source_point()) if self.has_screen(): self.spotlight.move_source_to(self.get_source_point()) self.update_shadow() self.add(self.ambient_light, self.spotlight, self.lighthouse, self.shadow)
def show_ghost_movement(self, vector): if isinstance(vector, Arrow): vector = vector.get_end() - vector.get_start() elif len(vector) == 2: vector = np.append(np.array(vector), 0.0) x_max = int(FRAME_X_RADIUS + abs(vector[0])) y_max = int(FRAME_Y_RADIUS + abs(vector[1])) dots = VMobject(*[ Dot(x * RIGHT + y * UP) for x in range(-x_max, x_max) for y in range(-y_max, y_max) ]) dots.set_fill(BLACK, opacity=0) dots_halfway = dots.copy().shift(vector / 2).set_fill(WHITE, 1) dots_end = dots.copy().shift(vector) self.play(Transform(dots, dots_halfway, rate_func=rush_into)) self.play(Transform(dots, dots_end, rate_func=rush_from)) self.remove(dots)
class LightSource(VMobject): # combines: # a lighthouse # an ambient light # a spotlight # and a shadow CONFIG = { "source_point": VectorizedPoint(location=ORIGIN, stroke_width=0, fill_opacity=0), "color": LIGHT_COLOR, "num_levels": 10, "radius": 10.0, "screen": None, "opacity_function": inverse_quadratic(1, 2, 1), "max_opacity_ambient": AMBIENT_FULL, "max_opacity_spotlight": SPOTLIGHT_FULL, "camera_mob": None } def generate_points(self): self.add(self.source_point) self.lighthouse = Lighthouse() self.ambient_light = AmbientLight( source_point=VectorizedPoint(location=self.get_source_point()), color=self.color, num_levels=self.num_levels, radius=self.radius, opacity_function=self.opacity_function, max_opacity=self.max_opacity_ambient ) if self.has_screen(): self.spotlight = Spotlight( source_point=VectorizedPoint(location=self.get_source_point()), color=self.color, num_levels=self.num_levels, radius=self.radius, screen=self.screen, opacity_function=self.opacity_function, max_opacity=self.max_opacity_spotlight, camera_mob=self.camera_mob ) else: self.spotlight = Spotlight() self.shadow = VMobject(fill_color=SHADOW_COLOR, fill_opacity=1.0, stroke_color=BLACK) self.lighthouse.next_to(self.get_source_point(), DOWN, buff=0) self.ambient_light.move_source_to(self.get_source_point()) if self.has_screen(): self.spotlight.move_source_to(self.get_source_point()) self.update_shadow() self.add(self.ambient_light, self.spotlight, self.lighthouse, self.shadow) def has_screen(self): if self.screen is None: return False elif np.size(self.screen.points) == 0: return False else: return True def dim_ambient(self): self.set_max_opacity_ambient(AMBIENT_DIMMED) def set_max_opacity_ambient(self, new_opacity): self.max_opacity_ambient = new_opacity self.ambient_light.dimming(new_opacity) def dim_spotlight(self): self.set_max_opacity_spotlight(SPOTLIGHT_DIMMED) def set_max_opacity_spotlight(self, new_opacity): self.max_opacity_spotlight = new_opacity self.spotlight.dimming(new_opacity) def set_camera_mob(self, new_cam_mob): self.camera_mob = new_cam_mob self.spotlight.camera_mob = new_cam_mob def set_screen(self, new_screen): if self.has_screen(): self.spotlight.screen = new_screen else: # Note: See below index = self.submobjects.index(self.spotlight) camera_mob = self.spotlight.camera_mob self.remove(self.spotlight) self.spotlight = Spotlight( source_point=VectorizedPoint(location=self.get_source_point()), color=self.color, num_levels=self.num_levels, radius=self.radius, screen=new_screen, camera_mob=self.camera_mob, opacity_function=self.opacity_function, max_opacity=self.max_opacity_spotlight, ) self.spotlight.move_source_to(self.get_source_point()) # Note: This line will make spotlight show up at the end # of the submojects list, which can make it show up on # top of the shadow. To make it show up in the # same spot, you could try the following line, # where "index" is what I defined above: self.submobjects.insert(index, self.spotlight) # self.add(self.spotlight) # in any case self.screen = new_screen def move_source_to(self, point): apoint = np.array(point) v = apoint - self.get_source_point() # Note: As discussed, things stand to behave better if source # point is a submobject, so that it automatically interpolates # during an animation, and other updates can be defined wrt # that source point's location self.source_point.set_location(apoint) # self.lighthouse.next_to(apoint,DOWN,buff = 0) # self.ambient_light.move_source_to(apoint) self.lighthouse.shift(v) # self.ambient_light.shift(v) self.ambient_light.move_source_to(apoint) if self.has_screen(): self.spotlight.move_source_to(apoint) self.update() return self def change_spotlight_opacity_function(self, new_of): self.spotlight.change_opacity_function(new_of) def set_radius(self, new_radius): self.radius = new_radius self.ambient_light.radius = new_radius self.spotlight.radius = new_radius def update(self): self.update_lighthouse() self.update_ambient() self.spotlight.update_sectors() self.update_shadow() def update_lighthouse(self): self.lighthouse.move_to(self.get_source_point()) # new_lh = Lighthouse() # new_lh.move_to(ORIGIN) # new_lh.apply_matrix(self.rotation_matrix()) # new_lh.shift(self.get_source_point()) # self.lighthouse.submobjects = new_lh.submobjects def update_ambient(self): new_ambient_light = AmbientLight( source_point=VectorizedPoint(location=ORIGIN), color=self.color, num_levels=self.num_levels, radius=self.radius, opacity_function=self.opacity_function, max_opacity=self.max_opacity_ambient ) new_ambient_light.apply_matrix(self.rotation_matrix()) new_ambient_light.move_source_to(self.get_source_point()) self.ambient_light.submobjects = new_ambient_light.submobjects def get_source_point(self): return self.source_point.get_location() def rotation_matrix(self): if self.camera_mob is None: return np.eye(3) phi = self.camera_mob.get_center()[0] theta = self.camera_mob.get_center()[1] R1 = np.array([ [1, 0, 0], [0, np.cos(phi), -np.sin(phi)], [0, np.sin(phi), np.cos(phi)] ]) R2 = np.array([ [np.cos(theta + TAU / 4), -np.sin(theta + TAU / 4), 0], [np.sin(theta + TAU / 4), np.cos(theta + TAU / 4), 0], [0, 0, 1] ]) R = np.dot(R2, R1) return R def update_shadow(self): point = self.get_source_point() projected_screen_points = [] if not self.has_screen(): return for point in self.screen.get_anchors(): projected_screen_points.append(self.spotlight.project(point)) projected_source = project_along_vector( self.get_source_point(), self.spotlight.projection_direction()) projected_point_cloud_3d = np.append( projected_screen_points, np.reshape(projected_source, (1, 3)), axis=0 ) # z_to_vector(self.spotlight.projection_direction()) rotation_matrix = self.rotation_matrix() back_rotation_matrix = rotation_matrix.T # i. e. its inverse rotated_point_cloud_3d = np.dot( projected_point_cloud_3d, back_rotation_matrix.T) # these points now should all have z = 0 point_cloud_2d = rotated_point_cloud_3d[:, :2] # now we can compute the convex hull hull_2d = ConvexHull(point_cloud_2d) # guaranteed to run ccw hull = [] # we also need the projected source point source_point_2d = np.dot(self.spotlight.project( self.get_source_point()), back_rotation_matrix.T)[:2] index = 0 for point in point_cloud_2d[hull_2d.vertices]: if np.all(np.abs(point - source_point_2d) < 1.0e-6): source_index = index index += 1 continue point_3d = np.array([point[0], point[1], 0]) hull.append(point_3d) index += 1 hull_mobject = VMobject() hull_mobject.set_points_as_corners(hull) hull_mobject.apply_matrix(rotation_matrix) anchors = hull_mobject.get_anchors() # add two control points for the outer cone if np.size(anchors) == 0: self.shadow.points = [] return ray1 = anchors[source_index - 1] - projected_source ray1 = ray1 / np.linalg.norm(ray1) * 100 ray2 = anchors[source_index] - projected_source ray2 = ray2 / np.linalg.norm(ray2) * 100 outpoint1 = anchors[source_index - 1] + ray1 outpoint2 = anchors[source_index] + ray2 new_anchors = anchors[:source_index] new_anchors = np.append(new_anchors, np.array( [outpoint1, outpoint2]), axis=0) new_anchors = np.append(new_anchors, anchors[source_index:], axis=0) self.shadow.set_points_as_corners(new_anchors) # shift it closer to the camera so it is in front of the spotlight self.shadow.mark_paths_closed = True
def __init__(self, path_string, **kwargs): digest_locals(self) VMobject.__init__(self, **kwargs)
def __init__(self, *vertices, **kwargs): assert len(vertices) > 1 digest_locals(self) VMobject.__init__(self, **kwargs)
def __init__(self, **kwargs): digest_config(self, kwargs, locals()) self.ensure_valid_file() VMobject.__init__(self, **kwargs) self.move_into_position()
def __init__(self, angle, **kwargs): self.angle = angle VMobject.__init__(self, **kwargs)
def __init__(self, path_string, **kwargs): digest_locals(self) VMobject.__init__(self, **kwargs)
def __init__(self, start, end, **kwargs): digest_config(self, kwargs) self.set_start_and_end(start, end) VMobject.__init__(self, **kwargs)
def get_piece_movement(self, pieces): start = VMobject(*pieces) target = VMobject(*[mob.target for mob in pieces]) if self.leave_ghost_vectors: self.add(start.copy().fade(0.7)) return Transform(start, target, submobject_mode="all_at_once")
def __init__(self, *args, **kwargs): VMobject.__init__(self, *args, **kwargs) shade_in_3d(self)
def get_center(self): result = VMobject.get_center(self) if hasattr(self, "center_offset"): result -= self.center_offset return result
def update_shadow(self): point = self.get_source_point() projected_screen_points = [] if not self.has_screen(): return for point in self.screen.get_anchors(): projected_screen_points.append(self.spotlight.project(point)) projected_source = project_along_vector( self.get_source_point(), self.spotlight.projection_direction()) projected_point_cloud_3d = np.append( projected_screen_points, np.reshape(projected_source, (1, 3)), axis=0 ) # z_to_vector(self.spotlight.projection_direction()) rotation_matrix = self.rotation_matrix() back_rotation_matrix = rotation_matrix.T # i. e. its inverse rotated_point_cloud_3d = np.dot( projected_point_cloud_3d, back_rotation_matrix.T) # these points now should all have z = 0 point_cloud_2d = rotated_point_cloud_3d[:, :2] # now we can compute the convex hull hull_2d = ConvexHull(point_cloud_2d) # guaranteed to run ccw hull = [] # we also need the projected source point source_point_2d = np.dot(self.spotlight.project( self.get_source_point()), back_rotation_matrix.T)[:2] index = 0 for point in point_cloud_2d[hull_2d.vertices]: if np.all(np.abs(point - source_point_2d) < 1.0e-6): source_index = index index += 1 continue point_3d = np.array([point[0], point[1], 0]) hull.append(point_3d) index += 1 hull_mobject = VMobject() hull_mobject.set_points_as_corners(hull) hull_mobject.apply_matrix(rotation_matrix) anchors = hull_mobject.get_anchors() # add two control points for the outer cone if np.size(anchors) == 0: self.shadow.points = [] return ray1 = anchors[source_index - 1] - projected_source ray1 = ray1 / np.linalg.norm(ray1) * 100 ray2 = anchors[source_index] - projected_source ray2 = ray2 / np.linalg.norm(ray2) * 100 outpoint1 = anchors[source_index - 1] + ray1 outpoint2 = anchors[source_index] + ray2 new_anchors = anchors[:source_index] new_anchors = np.append(new_anchors, np.array( [outpoint1, outpoint2]), axis=0) new_anchors = np.append(new_anchors, anchors[source_index:], axis=0) self.shadow.set_points_as_corners(new_anchors) # shift it closer to the camera so it is in front of the spotlight self.shadow.mark_paths_closed = True
def init_colors(self): VMobject.init_colors(self) self.set_color_by_gradient(*self.colors)
def __init__(self, angle, **kwargs): self.angle = angle VMobject.__init__(self, **kwargs)
def init_colors(self): VMobject.init_colors(self) self.set_color_by_gradient(*self.colors) for order in sorted(self.order_to_stroke_width_map.keys()): if self.order >= order: self.set_stroke(width=self.order_to_stroke_width_map[order])
def __init__(self, file_name=None, **kwargs): digest_config(self, kwargs) self.file_name = self.file_name or file_name self.ensure_valid_file() VMobject.__init__(self, **kwargs) self.move_into_position()
class NumberPlane(VMobject): CONFIG = { "color": BLUE_D, "secondary_color": BLUE_E, "axes_color": WHITE, "secondary_stroke_width": 1, # TODO: Allow coordinate center of NumberPlane to not be at (0, 0) "x_radius": None, "y_radius": None, "x_unit_size": 1, "y_unit_size": 1, "center_point": ORIGIN, "x_line_frequency": 1, "y_line_frequency": 1, "secondary_line_ratio": 1, "written_coordinate_height": 0.2, "propagate_style_to_family": False, "make_smooth_after_applying_functions": True, } def generate_points(self): if self.x_radius is None: center_to_edge = (FRAME_X_RADIUS + abs(self.center_point[0])) self.x_radius = center_to_edge / self.x_unit_size if self.y_radius is None: center_to_edge = (FRAME_Y_RADIUS + abs(self.center_point[1])) self.y_radius = center_to_edge / self.y_unit_size self.axes = VMobject() self.main_lines = VMobject() self.secondary_lines = VMobject() tuples = [ ( self.x_radius, self.x_line_frequency, self.y_radius * DOWN, self.y_radius * UP, RIGHT ), ( self.y_radius, self.y_line_frequency, self.x_radius * LEFT, self.x_radius * RIGHT, UP, ), ] for radius, freq, start, end, unit in tuples: main_range = np.arange(0, radius, freq) step = freq / float(freq + self.secondary_line_ratio) for v in np.arange(0, radius, step): line1 = Line(start + v * unit, end + v * unit) line2 = Line(start - v * unit, end - v * unit) if v == 0: self.axes.add(line1) elif v in main_range: self.main_lines.add(line1, line2) else: self.secondary_lines.add(line1, line2) self.add(self.secondary_lines, self.main_lines, self.axes) self.stretch(self.x_unit_size, 0) self.stretch(self.y_unit_size, 1) self.shift(self.center_point) # Put x_axis before y_axis y_axis, x_axis = self.axes.split() self.axes = VMobject(x_axis, y_axis) def init_colors(self): VMobject.init_colors(self) self.axes.set_stroke(self.axes_color, self.stroke_width) self.main_lines.set_stroke(self.color, self.stroke_width) self.secondary_lines.set_stroke( self.secondary_color, self.secondary_stroke_width ) return self def get_center_point(self): return self.coords_to_point(0, 0) def coords_to_point(self, x, y): x, y = np.array([x, y]) result = self.axes.get_center() result += x * self.get_x_unit_size() * RIGHT result += y * self.get_y_unit_size() * UP return result def point_to_coords(self, point): new_point = point - self.axes.get_center() x = new_point[0] / self.get_x_unit_size() y = new_point[1] / self.get_y_unit_size() return x, y # Does not recompute center, unit_sizes for each call; useful for # iterating over large lists of points, but does assume these # attributes are kept accurate. (Could alternatively have a method # which returns a function dynamically created after a single # call to each of get_center(), get_x_unit_size(), etc.) def point_to_coords_cheap(self, point): new_point = point - self.center_point x = new_point[0] / self.x_unit_size y = new_point[1] / self.y_unit_size return x, y def get_x_unit_size(self): return self.axes.get_width() / (2.0 * self.x_radius) def get_y_unit_size(self): return self.axes.get_height() / (2.0 * self.y_radius) def get_coordinate_labels(self, x_vals=None, y_vals=None): coordinate_labels = VGroup() if x_vals is None: x_vals = range(-int(self.x_radius), int(self.x_radius) + 1) if y_vals is None: y_vals = range(-int(self.y_radius), int(self.y_radius) + 1) for index, vals in enumerate([x_vals, y_vals]): num_pair = [0, 0] for val in vals: if val == 0: continue num_pair[index] = val point = self.coords_to_point(*num_pair) num = TexMobject(str(val)) num.add_background_rectangle() num.scale_to_fit_height( self.written_coordinate_height ) num.next_to(point, DOWN + LEFT, buff=SMALL_BUFF) coordinate_labels.add(num) self.coordinate_labels = coordinate_labels return coordinate_labels def get_axes(self): return self.axes def get_axis_labels(self, x_label="x", y_label="y"): x_axis, y_axis = self.get_axes().split() quads = [ (x_axis, x_label, UP, RIGHT), (y_axis, y_label, RIGHT, UP), ] labels = VGroup() for axis, tex, vect, edge in quads: label = TexMobject(tex) label.add_background_rectangle() label.next_to(axis, vect) label.to_edge(edge) labels.add(label) self.axis_labels = labels return labels def add_coordinates(self, x_vals=None, y_vals=None): self.add(*self.get_coordinate_labels(x_vals, y_vals)) return self def get_vector(self, coords, **kwargs): point = coords[0] * RIGHT + coords[1] * UP arrow = Arrow(ORIGIN, point, **kwargs) return arrow def prepare_for_nonlinear_transform(self, num_inserted_anchor_points=50): for mob in self.family_members_with_points(): num_anchors = mob.get_num_anchor_points() if num_inserted_anchor_points > num_anchors: mob.insert_n_anchor_points( num_inserted_anchor_points - num_anchors) mob.make_smooth() return self
def __init__(self, start, end, **kwargs): digest_config(self, kwargs) self.set_start_and_end(start, end) VMobject.__init__(self, **kwargs)
def __init__(self, rows, columns, **kwargs): digest_config(self, kwargs, locals()) VMobject.__init__(self, **kwargs)
def __init__(self, points, **kwargs): VMobject.__init__(self, **kwargs) self.set_points(points)
def __init__(self, *vertices, **kwargs): assert len(vertices) > 1 digest_locals(self) VMobject.__init__(self, **kwargs)
def __init__(self, rows, columns, **kwargs): digest_config(self, kwargs, locals()) VMobject.__init__(self, **kwargs)
def __init__(self, points, **kwargs): VMobject.__init__(self, **kwargs) self.set_points(points)
def get_piece_movement(self, pieces): start = VMobject(*pieces) target = VMobject(*[mob.target for mob in pieces]) if self.leave_ghost_vectors: self.add(start.copy().fade(0.7)) return Transform(start, target, submobject_mode="all_at_once")
def __init__(self, mobject, **kwargs): VMobject.__init__(self, **kwargs) self.mobject = mobject self.submobjects = self.get_eyes().submobjects