def viewing_rays(self, screen): lower_angle, upper_angle = self.viewing_angles(screen) projected_RIGHT = self.project( RIGHT) / get_norm(self.project(RIGHT)) lower_ray = rotate_vector( projected_RIGHT, lower_angle, axis=self.projection_direction()) upper_ray = rotate_vector( projected_RIGHT, upper_angle, axis=self.projection_direction()) return lower_ray, upper_ray
def calc_centers_by_radii(r1, r2, r3, init_angle=0): if init_angle is None: init_angle = TAU * np.random.random_sample() r12 = r1 + r2 r23 = r2 + r3 r13 = r1 + r3 cos_theta = (r12**2 + r13**2 - r23**2) / (2 * r12 * r13) theta = math.acos(cos_theta) p1 = ORIGIN p2 = p1 + rotate_vector(RIGHT, init_angle) * r12 p3 = p1 + rotate_vector(RIGHT * r13, init_angle + theta) return p1, p2, p3
def get_arc_center(self): """ Looks at the normals to the first two anchors, and finds their intersection points """ # First two anchors and handles a1, h, a2 = self.get_points()[:3] # Tangent vectors t1 = h - a1 t2 = h - a2 # Normals n1 = rotate_vector(t1, TAU / 4) n2 = rotate_vector(t2, TAU / 4) return find_intersection(a1, n1, a2, n2)
def generate_points(self): start_angle = np.pi / 2 + self.arc_angle / 2 end_angle = np.pi / 2 - self.arc_angle / 2 self.add(Arc( start_angle=start_angle, angle=-self.arc_angle )) tick_angle_range = np.linspace(start_angle, end_angle, self.num_ticks) for index, angle in enumerate(tick_angle_range): vect = rotate_vector(RIGHT, angle) tick = Line((1 - self.tick_length) * vect, vect) label = TexMobject(str(10 * index)) label.set_height(self.tick_length) label.shift((1 + self.tick_length) * vect) self.add(tick, label) needle = Polygon( LEFT, UP, RIGHT, stroke_width=0, fill_opacity=1, fill_color=self.needle_color ) needle.stretch_to_fit_width(self.needle_width) needle.stretch_to_fit_height(self.needle_height) needle.rotate(start_angle - np.pi / 2, about_point=ORIGIN) self.add(needle) self.needle = needle self.center_offset = self.get_center()
def set_points_by_ends(self, start, end, buff=0, path_arc=0): vect = end - start dist = get_norm(vect) if np.isclose(dist, 0): self.set_points_as_corners([start, end]) return self if path_arc: neg = path_arc < 0 if neg: path_arc = -path_arc start, end = end, start radius = (dist / 2) / math.sin(path_arc / 2) alpha = (PI - path_arc) / 2 center = start + radius * normalize( rotate_vector(end - start, alpha)) raw_arc_points = Arc.create_quadratic_bezier_points( angle=path_arc - 2 * buff / radius, start_angle=angle_of_vector(start - center) + buff / radius, ) if neg: raw_arc_points = raw_arc_points[::-1] self.set_points(center + radius * raw_arc_points) else: if buff > 0 and dist > 0: start = start + vect * (buff / dist) end = end - vect * (buff / dist) self.set_points_as_corners([start, end]) return self
def add_smooth_curve_to(self, *points): """ If two points are passed in, the first is intepretted as a handle, the second as an anchor """ if len(points) == 1: handle2 = None new_anchor = points[0] elif len(points) == 2: handle2, new_anchor = points else: name = sys._getframe(0).f_code.co_name raise Exception("Only call {} with 1 or 2 points".format(name)) if self.has_new_path_started(): self.add_line_to(new_anchor) else: self.throw_error_if_no_points() last_h2, last_a2 = self.points[-2:] last_tangent = (last_a2 - last_h2) handle1 = last_a2 + last_tangent if handle2 is None: to_anchor_vect = new_anchor - last_a2 new_tangent = rotate_vector(last_tangent, PI, axis=to_anchor_vect) handle2 = new_anchor - new_tangent self.append_points([last_a2, handle1, handle2, new_anchor]) return self
def init_points(self): start_angle = np.pi / 2 + self.arc_angle / 2 end_angle = np.pi / 2 - self.arc_angle / 2 self.add(Arc(start_angle=start_angle, angle=-self.arc_angle)) tick_angle_range = np.linspace(start_angle, end_angle, self.num_ticks) for index, angle in enumerate(tick_angle_range): vect = rotate_vector(RIGHT, angle) tick = Line((1 - self.tick_length) * vect, vect) label = TexMobject(str(10 * index)) label.set_height(self.tick_length) label.shift((1 + self.tick_length) * vect) self.add(tick, label) needle = Polygon(LEFT, UP, RIGHT, stroke_width=0, fill_opacity=1, fill_color=self.needle_color) needle.stretch_to_fit_width(self.needle_width) needle.stretch_to_fit_height(self.needle_height) needle.rotate(start_angle - np.pi / 2, about_point=ORIGIN) self.add(needle) self.needle = needle self.center_offset = self.get_center()
def add_smooth_curve_to(self, *points): """ If two points are passed in, the first is intepretted as a handle, the second as an anchor """ if len(points) == 1: handle2 = None new_anchor = points[0] elif len(points) == 2: handle2, new_anchor = points else: name = sys._getframe(0).f_code.co_name raise Exception("Only call {} with 1 or 2 points".format(name)) if self.has_new_path_started(): self.add_line_to(new_anchor) else: self.throw_error_if_no_points() last_h2, last_a2 = self.points[-2:] last_tangent = (last_a2 - last_h2) handle1 = last_a2 + last_tangent if handle2 is None: to_anchor_vect = new_anchor - last_a2 new_tangent = rotate_vector( last_tangent, PI, axis=to_anchor_vect ) handle2 = new_anchor - new_tangent self.append_points([ last_a2, handle1, handle2, new_anchor ]) return self
def get_graph_label(self, graph, label="f(x)", x=None, direction=RIGHT, buff=MED_SMALL_BUFF, color=None): if isinstance(label, str): label = Tex(label) if color is None: label.match_color(graph) if x is None: # Searching from the right, find a point # whose y value is in bounds max_y = FRAME_Y_RADIUS - label.get_height() max_x = FRAME_X_RADIUS - label.get_width() for x0 in np.arange(*self.x_range)[::-1]: pt = self.i2gp(x0, graph) if abs(pt[0]) < max_x and abs(pt[1]) < max_y: x = x0 break if x is None: x = self.x_range[1] point = self.input_to_graph_point(x, graph) angle = self.angle_of_tangent(x, graph) normal = rotate_vector(RIGHT, angle + 90 * DEGREES) if normal[1] < 0: normal *= -1 label.next_to(point, normal, buff=buff) label.shift_onto_screen() return label
def __init__(self, n=6, **kwargs): digest_config(self, kwargs, locals()) if self.start_angle is None: # 0 for odd, 90 for even self.start_angle = (n % 2) * 90 * DEGREES start_vect = rotate_vector(RIGHT, self.start_angle) vertices = compass_directions(n, start_vect) super().__init__(*vertices, **kwargs)
def __init__(self, n=6, **kwargs): digest_config(self, kwargs, locals()) if self.start_angle is None: if n % 2 == 0: self.start_angle = 0 else: self.start_angle = 90 * DEGREES start_vect = rotate_vector(RIGHT, self.start_angle) vertices = compass_directions(n, start_vect) Polygon.__init__(self, *vertices, **kwargs)
def get_arc_center(self): """ Looks at the normals to the first two anchors, and finds their intersection points """ # First two anchors and handles a1, h1, h2, a2 = self.points[:4] # Tangent vectors t1 = h1 - a1 t2 = h2 - a2 # Normals n1 = rotate_vector(t1, TAU / 4) n2 = rotate_vector(t2, TAU / 4) try: return line_intersection( line1=(a1, a1 + n1), line2=(a2, a2 + n2), ) except Exception: warnings.warn("Can't find Arc center, using ORIGIN instead") return np.array(ORIGIN)
def __init__(self, n=6, *args, **kwargs): self.args_name = \ ["mobject_or_point", "radius", "start_angle", "element", "normal_vector"] self.args = \ [ORIGIN, 1, None, "point", "OUT"] self.mobject_or_point, self.radius, self.start_angle, self.element, self.normal_vector = \ generate_args(self, args, self.args) kwargs = merge_config_kwargs(self, kwargs, self.args_name) self.mobject_or_point = Location(self.mobject_or_point) if self.element == "point": if self.start_angle is None: if n % 2 == 0: self.start_angle = 0 else: self.start_angle = 90 * DEGREES start_vect = rotate_vector(self.radius*RIGHT, self.start_angle) vertices = np.add(compass_directions(n, start_vect), np.repeat([self.mobject_or_point], n, axis=0)) GeomPolygon.__init__(self, *vertices, **kwargs)
def __init__(self, n=6, **kwargs): #### EULERTOUR_INIT_START #### if not hasattr(self, "args"): self.args = serialize_args([]) if not hasattr(self, "config"): self.config = serialize_config({ 'n': n, **kwargs, }) #### EULERTOUR_INIT_START #### digest_config(self, kwargs, locals()) if self.start_angle is None: if n % 2 == 0: self.start_angle = 0 else: self.start_angle = 90 * DEGREES start_vect = rotate_vector(RIGHT, self.start_angle) vertices = compass_directions(n, start_vect) Polygon.__init__(self, *vertices, **kwargs) #### EULERTOUR_INIT_END #### register_mobject(self)
def fractalification_iteration(vmobject, dimension=1.05, num_inserted_anchors_range=list(range(1, 4))): num_points = vmobject.get_num_points() if num_points > 0: # original_anchors = vmobject.get_anchors() original_anchors = [ vmobject.point_from_proportion(x) for x in np.linspace(0, 1 - 1. / num_points, num_points) ] new_anchors = [] for p1, p2, in zip(original_anchors, original_anchors[1:]): num_inserts = random.choice(num_inserted_anchors_range) inserted_points = [ interpolate(p1, p2, alpha) for alpha in np.linspace(0, 1, num_inserts + 2)[1:-1] ] mass_scaling_factor = 1. / (num_inserts + 1) length_scaling_factor = mass_scaling_factor**(1. / dimension) target_length = get_norm(p1 - p2) * length_scaling_factor curr_length = get_norm(p1 - p2) * mass_scaling_factor # offset^2 + curr_length^2 = target_length^2 offset_len = np.sqrt(target_length**2 - curr_length**2) unit_vect = (p1 - p2) / get_norm(p1 - p2) offset_unit_vect = rotate_vector(unit_vect, np.pi / 2) inserted_points = [ point + u * offset_len * offset_unit_vect for u, point in zip(it.cycle([-1, 1]), inserted_points) ] new_anchors += [p1] + inserted_points new_anchors.append(original_anchors[-1]) vmobject.set_points_as_corners(new_anchors) vmobject.set_submobjects([ fractalification_iteration(submob, dimension, num_inserted_anchors_range) for submob in vmobject.submobjects ]) return vmobject
def fractalification_iteration(vmobject, dimension=1.05, num_inserted_anchors_range=list(range(1, 4))): num_points = vmobject.get_num_points() if num_points > 0: # original_anchors = vmobject.get_anchors() original_anchors = [ vmobject.point_from_proportion(x) for x in np.linspace(0, 1 - 1. / num_points, num_points) ] new_anchors = [] for p1, p2, in zip(original_anchors, original_anchors[1:]): num_inserts = random.choice(num_inserted_anchors_range) inserted_points = [ interpolate(p1, p2, alpha) for alpha in np.linspace(0, 1, num_inserts + 2)[1:-1] ] mass_scaling_factor = 1. / (num_inserts + 1) length_scaling_factor = mass_scaling_factor**(1. / dimension) target_length = get_norm(p1 - p2) * length_scaling_factor curr_length = get_norm(p1 - p2) * mass_scaling_factor # offset^2 + curr_length^2 = target_length^2 offset_len = np.sqrt(target_length**2 - curr_length**2) unit_vect = (p1 - p2) / get_norm(p1 - p2) offset_unit_vect = rotate_vector(unit_vect, np.pi / 2) inserted_points = [ point + u * offset_len * offset_unit_vect for u, point in zip(it.cycle([-1, 1]), inserted_points) ] new_anchors += [p1] + inserted_points new_anchors.append(original_anchors[-1]) vmobject.set_points_as_corners(new_anchors) vmobject.submobjects = [ fractalification_iteration( submob, dimension, num_inserted_anchors_range) for submob in vmobject.submobjects ] return vmobject
def __init__(self, n=3, **kwargs): digest_config(self, kwargs, locals()) start_vect = rotate_vector(RIGHT, self.start_angle) vertices = compass_directions(n, start_vect) Polygon.__init__(self, *vertices, **kwargs)
def move_to(self, point_or_mobject): vect = rotate_vector(UP + LEFT, self.orientation_line.get_angle()) self.next_to(point_or_mobject, vect, buff=0) return self
def move_to(self, point_or_mobject): vect = rotate_vector( UP + LEFT, self.orientation_line.get_angle() ) self.next_to(point_or_mobject, vect, buff=0) return self
def set_points_by_ends(self, start, end, buff=0, path_arc=0): # Find the right tip length and thickness vect = end - start length = max(get_norm(vect), 1e-8) thickness = self.thickness w_ratio = fdiv(self.max_width_to_length_ratio, fdiv(thickness, length)) if w_ratio < 1: thickness *= w_ratio tip_width = self.tip_width_ratio * thickness tip_length = tip_width / (2 * np.tan(self.tip_angle / 2)) t_ratio = fdiv(self.max_tip_length_to_length_ratio, fdiv(tip_length, length)) if t_ratio < 1: tip_length *= t_ratio tip_width *= t_ratio # Find points for the stem if path_arc == 0: points1 = (length - tip_length) * np.array( [RIGHT, 0.5 * RIGHT, ORIGIN]) points1 += thickness * UP / 2 points2 = points1[::-1] + thickness * DOWN else: # Solve for radius so that the tip-to-tail length matches |end - start| a = 2 * (1 - np.cos(path_arc)) b = -2 * tip_length * np.sin(path_arc) c = tip_length**2 - length**2 R = (-b + np.sqrt(b**2 - 4 * a * c)) / (2 * a) # Find arc points points1 = Arc.create_quadratic_bezier_points(path_arc) points2 = np.array(points1[::-1]) points1 *= (R + thickness / 2) points2 *= (R - thickness / 2) if path_arc < 0: tip_length *= -1 rot_T = rotation_matrix_transpose(PI / 2 - path_arc, OUT) for points in points1, points2: points[:] = np.dot(points, rot_T) points += R * DOWN self.set_points(points1) # Tip self.add_line_to(tip_width * UP / 2) self.add_line_to(tip_length * LEFT) self.tip_index = len(self.get_points()) - 1 self.add_line_to(tip_width * DOWN / 2) self.add_line_to(points2[0]) # Close it out self.append_points(points2) self.add_line_to(points1[0]) if length > 0: # Final correction super().scale(length / self.get_length()) self.rotate(angle_of_vector(vect) - self.get_angle()) self.rotate( PI / 2 - np.arccos(normalize(vect)[2]), axis=rotate_vector(self.get_unit_vector(), -PI / 2), ) self.shift(start - self.get_start()) self.refresh_triangulation()