def get_subdivision_braces_and_labels( self, parts, labels, direction, buff = SMALL_BUFF, min_num_quads = 1 ): label_mobs = VGroup() braces = VGroup() for label, part in zip(labels, parts): brace = Brace( part, direction, min_num_quads = min_num_quads, buff = buff ) if isinstance(label, Mobject): label_mob = label else: label_mob = TexMobject(label) label_mob.scale(self.default_label_scale_val) label_mob.next_to(brace, direction, buff) braces.add(brace) label_mobs.add(label_mob) parts.braces = braces parts.labels = label_mobs parts.label_kwargs = { "labels" : label_mobs.copy(), "direction" : direction, "buff" : buff, } return VGroup(parts.braces, parts.labels)
def get_riemann_rectangles( self, graph, x_min = None, x_max = None, dx = 0.1, input_sample_type = "left", stroke_width = 1, start_color = BLUE, end_color = GREEN): x_min = x_min if x_min is not None else self.x_min x_max = x_max if x_max is not None else self.x_max rectangles = VGroup() for x in np.arange(x_min, x_max, dx): if input_sample_type == "left": sample_input = x elif input_sample_type == "right": sample_input = x+dx else: raise Exception("Invalid input sample type") graph_point = self.input_to_graph_point(sample_input, graph) points = VGroup(*map(VectorizedPoint, [ self.coords_to_point(x, 0), self.coords_to_point(x+dx, 0), graph_point ])) rect = Rectangle() rect.replace(points, stretch = True) rect.set_fill(opacity = 1) rectangles.add(rect) rectangles.gradient_highlight(start_color, end_color) rectangles.set_stroke(BLACK, width = stroke_width) return rectangles
def get_coordinate_labels(self, *numbers): # TODO: Should merge this with the code from NumberPlane.get_coordinate_labels result = VGroup() nudge = 0.1*(DOWN+RIGHT) if len(numbers) == 0: numbers = range(-int(self.x_radius), int(self.x_radius)+1) numbers += [ complex(0, y) for y in range(-int(self.y_radius), int(self.y_radius)+1) ] for number in numbers: if number == complex(0, 0): continue point = self.number_to_point(number) num_str = str(number).replace("j", "i") if num_str.startswith("0"): num_str = "0" elif num_str in ["1i", "-1i"]: num_str = num_str.replace("1", "") num_mob = TexMobject(num_str) num_mob.add_background_rectangle() num_mob.scale_to_fit_height(self.written_coordinate_height) num_mob.next_to(point, DOWN+LEFT, SMALL_BUFF) result.add(num_mob) self.coordinate_labels = result return result
def get_number_mobjects(self, *numbers, **kwargs): #TODO, handle decimals if len(numbers) == 0: numbers = self.default_numbers_to_display() result = VGroup() for number in numbers: mob = TexMobject(str(int(number))) mob.scale_to_fit_height(3 * self.tick_size) mob.shift(self.number_to_point(number), self.get_vertical_number_offset(**kwargs)) result.add(mob) return result
def get_number_mob(self, num): result = VGroup() place = 0 max_place = self.max_place while place < max_place: digit = TexMobject(str(self.get_place_num(num, place))) if place >= len(self.digit_place_colors): self.digit_place_colors += self.digit_place_colors digit.highlight(self.digit_place_colors[place]) digit.scale(self.num_scale_factor) digit.next_to(result, LEFT, buff = SMALL_BUFF, aligned_edge = DOWN) result.add(digit) place += 1 return result
def get_number_mob(self, num): result = VGroup() place = 0 max_place = self.max_place while place < max_place: digit = TexMobject(str(self.get_place_num(num, place))) if place >= len(self.digit_place_colors): self.digit_place_colors += self.digit_place_colors digit.set_color(self.digit_place_colors[place]) digit.scale(self.num_scale_factor) digit.next_to(result, LEFT, buff=SMALL_BUFF, aligned_edge=DOWN) result.add(digit) place += 1 return result
def get_number_mobjects(self, *numbers, **kwargs): #TODO, handle decimals if len(numbers) == 0: numbers = self.default_numbers_to_display() result = VGroup() for number in numbers: mob = TexMobject(str(int(number))) mob.scale_to_fit_height(3*self.tick_size) mob.shift( self.number_to_point(number), self.get_vertical_number_offset(**kwargs) ) result.add(mob) return result
def get_division_along_dimension(self, p_list, dim, colors, vect): p_list = self.complete_p_list(p_list) colors = color_gradient(colors, len(p_list)) last_point = self.full_space.get_edge_center(-vect) parts = VGroup() for factor, color in zip(p_list, colors): part = SampleSpace() part.set_fill(color, 1) part.replace(self.full_space, stretch = True) part.stretch(factor, dim) part.move_to(last_point, -vect) last_point = part.get_edge_center(vect) parts.add(part) return parts
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 get_riemann_rectangles( self, graph, x_min = None, x_max = None, dx = 0.1, input_sample_type = "left", stroke_width = 1, stroke_color = BLACK, fill_opacity = 1, start_color = None, end_color = None, show_signed_area = True, width_scale_factor = 1.001 ): x_min = x_min if x_min is not None else self.x_min x_max = x_max if x_max is not None else self.x_max if start_color is None: start_color = self.default_riemann_start_color if end_color is None: end_color = self.default_riemann_end_color rectangles = VGroup() x_range = np.arange(x_min, x_max, dx) colors = color_gradient([start_color, end_color], len(x_range)) for x, color in zip(x_range, colors): if input_sample_type == "left": sample_input = x elif input_sample_type == "right": sample_input = x+dx else: raise Exception("Invalid input sample type") graph_point = self.input_to_graph_point(sample_input, graph) points = VGroup(*map(VectorizedPoint, [ self.coords_to_point(x, 0), self.coords_to_point(x+width_scale_factor*dx, 0), graph_point ])) rect = Rectangle() rect.replace(points, stretch = True) if graph_point[1] < self.graph_origin[1] and show_signed_area: fill_color = invert_color(color) else: fill_color = color rect.set_fill(fill_color, opacity = fill_opacity) rect.set_stroke(stroke_color, width = stroke_width) rectangles.add(rect) return rectangles
def get_number_design(self, value, symbol): num = int(value) n_rows = { 2 : 2, 3 : 3, 4 : 2, 5 : 2, 6 : 3, 7 : 3, 8 : 3, 9 : 4, 10 : 4, }[num] n_cols = 1 if num in [2, 3] else 2 insertion_indices = { 5 : [0], 7 : [0], 8 : [0, 1], 9 : [1], 10 : [0, 2], }.get(num, []) top = self.get_top() + symbol.get_height()*DOWN bottom = self.get_bottom() + symbol.get_height()*UP column_points = [ interpolate(top, bottom, alpha) for alpha in np.linspace(0, 1, n_rows) ] design = VGroup(*[ symbol.copy().move_to(point) for point in column_points ]) if n_cols == 2: space = 0.2*self.get_width() column_copy = design.copy().shift(space*RIGHT) design.shift(space*LEFT) design.add(*column_copy) design.add(*[ symbol.copy().move_to( center_of_mass(column_points[i:i+2]) ) for i in insertion_indices ]) for symbol in design: if symbol.get_center()[1] < self.get_center()[1]: symbol.rotate_in_place(np.pi) return design
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=BLACK, stroke_width=0, ) circle.move_to(focal_point) circle.save_state() circle.scale_to_fit_width(self.small_radius * 2) circle.set_stroke(WHITE, 8) circles.add(circle) LaggedStart.__init__(self, ApplyMethod, circles, lambda c: (c.restore, ), **kwargs)
def get_coordinate_labels(self, x_vals=None, y_vals=None): coordinate_labels = VGroup() if x_vals == None: x_vals = range(-int(self.x_radius), int(self.x_radius) + 1) if y_vals == 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_riemann_rectangles(self, x_min = None, x_max = None, dx = 0.1, stroke_width = 1, start_color = BLUE, end_color = GREEN): assert(hasattr(self, "func")) x_min = x_min if x_min is not None else self.x_min x_max = x_max if x_max is not None else self.x_max rectangles = VGroup() for x in np.arange(x_min, x_max, dx): points = VGroup(*map(VectorizedPoint, [ self.coords_to_point(x, 0), self.coords_to_point(x+dx, self.func(x+dx)), ])) rect = Rectangle() rect.replace(points, stretch = True) rect.set_fill(opacity = 1) rectangles.add(rect) rectangles.gradient_highlight(start_color, end_color) rectangles.set_stroke(BLACK, width = stroke_width) return rectangles
def get_secant_slope_group( self, x, graph, dx = None, dx_line_color = None, df_line_color = None, dx_label = None, df_label = None, include_secant_line = True, secant_line_color = None, secant_line_length = 10, ): """ Resulting group is of the form VGroup( dx_line, df_line, dx_label, (if applicable) df_label, (if applicable) secant_line, (if applicable) ) with attributes of those names. """ kwargs = locals() kwargs.pop("self") group = VGroup() group.kwargs = kwargs dx = dx or float(self.x_max - self.x_min)/10 dx_line_color = dx_line_color or self.default_input_color df_line_color = df_line_color or graph.get_color() p1 = self.input_to_graph_point(x, graph) p2 = self.input_to_graph_point(x+dx, graph) interim_point = p2[0]*RIGHT + p1[1]*UP group.dx_line = Line( p1, interim_point, color = dx_line_color ) group.df_line = Line( interim_point, p2, color = df_line_color ) group.add(group.dx_line, group.df_line) labels = VGroup() if dx_label is not None: group.dx_label = TexMobject(dx_label) labels.add(group.dx_label) group.add(group.dx_label) if df_label is not None: group.df_label = TexMobject(df_label) labels.add(group.df_label) group.add(group.df_label) if len(labels) > 0: max_width = 0.8*group.dx_line.get_width() max_height = 0.8*group.df_line.get_height() if labels.get_width() > max_width: labels.scale_to_fit_width(max_width) if labels.get_height() > max_height: labels.scale_to_fit_height(max_height) if dx_label is not None: group.dx_label.next_to( group.dx_line, np.sign(dx)*DOWN, buff = group.dx_label.get_height()/2 ) group.dx_label.highlight(group.dx_line.get_color()) if df_label is not None: group.df_label.next_to( group.df_line, np.sign(dx)*RIGHT, buff = group.df_label.get_height()/2 ) group.df_label.highlight(group.df_line.get_color()) if include_secant_line: secant_line_color = secant_line_color or self.default_derivative_color group.secant_line = Line(p1, p2, color = secant_line_color) group.secant_line.scale_in_place( secant_line_length/group.secant_line.get_length() ) group.add(group.secant_line) return group
class CountingScene(Scene): CONFIG = { "digit_place_colors" : [YELLOW, MAROON_B, RED, GREEN, BLUE, PURPLE_D], "counting_dot_starting_position" : (SPACE_WIDTH-1)*RIGHT + (SPACE_HEIGHT-1)*UP, "count_dot_starting_radius" : 0.5, "dot_configuration_height" : 2, "ones_configuration_location" : UP+2*RIGHT, "num_scale_factor" : 2, "num_start_location" : 2*DOWN, } def setup(self): self.dots = VGroup() self.number = 0 self.max_place = 0 self.number_mob = VGroup(TexMobject(str(self.number))) self.number_mob.scale(self.num_scale_factor) self.number_mob.shift(self.num_start_location) self.dot_templates = [] self.dot_template_iterators = [] self.curr_configurations = [] self.arrows = VGroup() self.add(self.number_mob) def get_template_configuration(self, place): #This should probably be replaced for non-base-10 counting scenes down_right = (0.5)*RIGHT + (np.sqrt(3)/2)*DOWN result = [] for down_right_steps in range(5): for left_steps in range(down_right_steps): result.append( down_right_steps*down_right + left_steps*LEFT ) return reversed(result[:self.get_place_max(place)]) def get_dot_template(self, place): #This should be replaced for non-base-10 counting scenes down_right = (0.5)*RIGHT + (np.sqrt(3)/2)*DOWN dots = VGroup(*[ Dot( point, radius = 0.25, fill_opacity = 0, stroke_width = 2, stroke_color = WHITE, ) for point in self.get_template_configuration(place) ]) dots.scale_to_fit_height(self.dot_configuration_height) return dots def add_configuration(self): new_template = self.get_dot_template(len(self.dot_templates)) new_template.move_to(self.ones_configuration_location) left_vect = (new_template.get_width()+LARGE_BUFF)*LEFT new_template.shift( left_vect*len(self.dot_templates) ) self.dot_templates.append(new_template) self.dot_template_iterators.append( it.cycle(new_template) ) self.curr_configurations.append(VGroup()) def count(self, max_val, run_time_per_anim = 1): for x in range(max_val): self.increment(run_time_per_anim) def increment(self, run_time_per_anim = 1): moving_dot = Dot( self.counting_dot_starting_position, radius = self.count_dot_starting_radius, color = self.digit_place_colors[0], ) moving_dot.generate_target() moving_dot.set_fill(opacity = 0) kwargs = { "run_time" : run_time_per_anim } continue_rolling_over = True first_move = True place = 0 while continue_rolling_over: added_anims = [] if first_move: added_anims += self.get_digit_increment_animations() first_move = False moving_dot.target.replace( self.dot_template_iterators[place].next() ) self.play(MoveToTarget(moving_dot), *added_anims, **kwargs) self.curr_configurations[place].add(moving_dot) if len(self.curr_configurations[place].split()) == self.get_place_max(place): full_configuration = self.curr_configurations[place] self.curr_configurations[place] = VGroup() place += 1 center = full_configuration.get_center_of_mass() radius = 0.6*max( full_configuration.get_width(), full_configuration.get_height(), ) circle = Circle( radius = radius, stroke_width = 0, fill_color = self.digit_place_colors[place], fill_opacity = 0.5, ) circle.move_to(center) moving_dot = VGroup(circle, full_configuration) moving_dot.generate_target() moving_dot[0].set_fill(opacity = 0) else: continue_rolling_over = False def get_digit_increment_animations(self): result = [] self.number += 1 is_next_digit = self.is_next_digit() if is_next_digit: self.max_place += 1 new_number_mob = self.get_number_mob(self.number) new_number_mob.move_to(self.number_mob, RIGHT) if is_next_digit: self.add_configuration() place = len(new_number_mob.split())-1 result.append(FadeIn(self.dot_templates[place])) arrow = Arrow( new_number_mob[place].get_top(), self.dot_templates[place].get_bottom(), color = self.digit_place_colors[place] ) self.arrows.add(arrow) result.append(ShowCreation(arrow)) result.append(Transform( self.number_mob, new_number_mob, submobject_mode = "lagged_start" )) return result def get_number_mob(self, num): result = VGroup() place = 0 max_place = self.max_place while place < max_place: digit = TexMobject(str(self.get_place_num(num, place))) if place >= len(self.digit_place_colors): self.digit_place_colors += self.digit_place_colors digit.highlight(self.digit_place_colors[place]) digit.scale(self.num_scale_factor) digit.next_to(result, LEFT, buff = SMALL_BUFF, aligned_edge = DOWN) result.add(digit) place += 1 return result def is_next_digit(self): return False def get_place_num(self, num, place): return 0 def get_place_max(self, place): return 0
class CountingScene(Scene): CONFIG = { "digit_place_colors": [YELLOW, MAROON_B, RED, GREEN, BLUE, PURPLE_D], "counting_dot_starting_position": (FRAME_X_RADIUS - 1) * RIGHT + (FRAME_Y_RADIUS - 1) * UP, "count_dot_starting_radius": 0.5, "dot_configuration_height": 2, "ones_configuration_location": UP + 2 * RIGHT, "num_scale_factor": 2, "num_start_location": 2 * DOWN, } def setup(self): self.dots = VGroup() self.number = 0 self.max_place = 0 self.number_mob = VGroup(TexMobject(str(self.number))) self.number_mob.scale(self.num_scale_factor) self.number_mob.shift(self.num_start_location) self.dot_templates = [] self.dot_template_iterators = [] self.curr_configurations = [] self.arrows = VGroup() self.add(self.number_mob) def get_template_configuration(self, place): #This should probably be replaced for non-base-10 counting scenes down_right = (0.5) * RIGHT + (np.sqrt(3) / 2) * DOWN result = [] for down_right_steps in range(5): for left_steps in range(down_right_steps): result.append(down_right_steps * down_right + left_steps * LEFT) return reversed(result[:self.get_place_max(place)]) def get_dot_template(self, place): #This should be replaced for non-base-10 counting scenes down_right = (0.5) * RIGHT + (np.sqrt(3) / 2) * DOWN dots = VGroup(*[ Dot( point, radius=0.25, fill_opacity=0, stroke_width=2, stroke_color=WHITE, ) for point in self.get_template_configuration(place) ]) dots.scale_to_fit_height(self.dot_configuration_height) return dots def add_configuration(self): new_template = self.get_dot_template(len(self.dot_templates)) new_template.move_to(self.ones_configuration_location) left_vect = (new_template.get_width() + LARGE_BUFF) * LEFT new_template.shift(left_vect * len(self.dot_templates)) self.dot_templates.append(new_template) self.dot_template_iterators.append(it.cycle(new_template)) self.curr_configurations.append(VGroup()) def count(self, max_val, run_time_per_anim=1): for x in range(max_val): self.increment(run_time_per_anim) def increment(self, run_time_per_anim=1): moving_dot = Dot( self.counting_dot_starting_position, radius=self.count_dot_starting_radius, color=self.digit_place_colors[0], ) moving_dot.generate_target() moving_dot.set_fill(opacity=0) kwargs = {"run_time": run_time_per_anim} continue_rolling_over = True first_move = True place = 0 while continue_rolling_over: added_anims = [] if first_move: added_anims += self.get_digit_increment_animations() first_move = False moving_dot.target.replace( self.dot_template_iterators[place].next()) self.play(MoveToTarget(moving_dot), *added_anims, **kwargs) self.curr_configurations[place].add(moving_dot) if len(self.curr_configurations[place].split() ) == self.get_place_max(place): full_configuration = self.curr_configurations[place] self.curr_configurations[place] = VGroup() place += 1 center = full_configuration.get_center_of_mass() radius = 0.6 * max( full_configuration.get_width(), full_configuration.get_height(), ) circle = Circle( radius=radius, stroke_width=0, fill_color=self.digit_place_colors[place], fill_opacity=0.5, ) circle.move_to(center) moving_dot = VGroup(circle, full_configuration) moving_dot.generate_target() moving_dot[0].set_fill(opacity=0) else: continue_rolling_over = False def get_digit_increment_animations(self): result = [] self.number += 1 is_next_digit = self.is_next_digit() if is_next_digit: self.max_place += 1 new_number_mob = self.get_number_mob(self.number) new_number_mob.move_to(self.number_mob, RIGHT) if is_next_digit: self.add_configuration() place = len(new_number_mob.split()) - 1 result.append(FadeIn(self.dot_templates[place])) arrow = Arrow(new_number_mob[place].get_top(), self.dot_templates[place].get_bottom(), color=self.digit_place_colors[place]) self.arrows.add(arrow) result.append(ShowCreation(arrow)) result.append( Transform(self.number_mob, new_number_mob, submobject_mode="lagged_start")) return result def get_number_mob(self, num): result = VGroup() place = 0 max_place = self.max_place while place < max_place: digit = TexMobject(str(self.get_place_num(num, place))) if place >= len(self.digit_place_colors): self.digit_place_colors += self.digit_place_colors digit.set_color(self.digit_place_colors[place]) digit.scale(self.num_scale_factor) digit.next_to(result, LEFT, buff=SMALL_BUFF, aligned_edge=DOWN) result.add(digit) place += 1 return result def is_next_digit(self): return False def get_place_num(self, num, place): return 0 def get_place_max(self, place): return 0