diff --git a/manimlib/once_useful_constructs/complex_transformation_scene.py b/manimlib/once_useful_constructs/complex_transformation_scene.py deleted file mode 100644 index b21747dd..00000000 --- a/manimlib/once_useful_constructs/complex_transformation_scene.py +++ /dev/null @@ -1,156 +0,0 @@ -from manimlib.animation.animation import Animation -from manimlib.animation.movement import ComplexHomotopy -from manimlib.animation.transform import MoveToTarget -from manimlib.constants import * -from manimlib.mobject.coordinate_systems import ComplexPlane -from manimlib.mobject.types.vectorized_mobject import VGroup -from manimlib.scene.scene import Scene - - -# TODO, refactor this full scene -class ComplexTransformationScene(Scene): - CONFIG = { - "plane_config": {}, - "background_fade_factor": 0.5, - "use_multicolored_plane": False, - "vert_start_color": BLUE, # TODO - "vert_end_color": BLUE, - "horiz_start_color": BLUE, - "horiz_end_color": BLUE, - "num_anchors_to_add_per_line": 50, - "post_transformation_stroke_width": None, - "default_apply_complex_function_kwargs": { - "run_time": 5, - }, - "background_label_scale_val": 0.5, - "include_coordinate_labels": True, - } - - def setup(self): - self.foreground_mobjects = [] - self.transformable_mobjects = [] - self.add_background_plane() - if self.include_coordinate_labels: - self.add_coordinate_labels() - - def add_foreground_mobject(self, mobject): - self.add_foreground_mobjects(mobject) - - def add_transformable_mobjects(self, *mobjects): - self.transformable_mobjects += list(mobjects) - self.add(*mobjects) - - def add_foreground_mobjects(self, *mobjects): - self.foreground_mobjects += list(mobjects) - Scene.add(self, *mobjects) - - def add(self, *mobjects): - Scene.add(self, *list(mobjects) + self.foreground_mobjects) - - def play(self, *animations, **kwargs): - Scene.play( - self, - *list(animations) + list(map(Animation, self.foreground_mobjects)), - **kwargs - ) - - def add_background_plane(self): - background = ComplexPlane(**self.plane_config) - background.fade(self.background_fade_factor) - self.add(background) - self.background = background - - def add_coordinate_labels(self): - self.background.add_coordinates() - self.add(self.background) - - def add_transformable_plane(self, **kwargs): - self.plane = self.get_transformable_plane() - self.add(self.plane) - - def get_transformable_plane(self, x_range=None, y_range=None): - """ - x_range and y_range would be tuples (min, max) - """ - plane_config = dict(self.plane_config) - shift_val = ORIGIN - if x_range is not None: - x_min, x_max = x_range - plane_config["x_radius"] = x_max - x_min - shift_val += (x_max + x_min) * RIGHT / 2. - if y_range is not None: - y_min, y_max = y_range - plane_config["y_radius"] = y_max - y_min - shift_val += (y_max + y_min) * UP / 2. - plane = ComplexPlane(**plane_config) - plane.shift(shift_val) - if self.use_multicolored_plane: - self.paint_plane(plane) - return plane - - def prepare_for_transformation(self, mob): - if hasattr(mob, "prepare_for_nonlinear_transform"): - mob.prepare_for_nonlinear_transform( - self.num_anchors_to_add_per_line - ) - # TODO... - - def paint_plane(self, plane): - for lines in planes, plane.secondary_lines: - lines.set_color_by_gradient( - self.vert_start_color, - self.vert_end_color, - self.horiz_start_color, - self.horiz_end_color, - ) - # plane.axes.set_color_by_gradient( - # self.horiz_start_color, - # self.vert_start_color - # ) - - def z_to_point(self, z): - return self.background.number_to_point(z) - - def get_transformer(self, **kwargs): - transform_kwargs = dict(self.default_apply_complex_function_kwargs) - transform_kwargs.update(kwargs) - transformer = VGroup() - if hasattr(self, "plane"): - self.prepare_for_transformation(self.plane) - transformer.add(self.plane) - transformer.add(*self.transformable_mobjects) - return transformer, transform_kwargs - - def apply_complex_function(self, func, added_anims=[], **kwargs): - transformer, transform_kwargs = self.get_transformer(**kwargs) - transformer.generate_target() - # Rescale, apply function, scale back - transformer.target.shift(-self.background.get_center_point()) - transformer.target.scale(1. / self.background.unit_size) - transformer.target.apply_complex_function(func) - transformer.target.scale(self.background.unit_size) - transformer.target.shift(self.background.get_center_point()) - # - - for mob in transformer.target[0].family_members_with_points(): - mob.make_smooth() - if self.post_transformation_stroke_width is not None: - transformer.target.set_stroke( - width=self.post_transformation_stroke_width) - self.play( - MoveToTarget(transformer, **transform_kwargs), - *added_anims - ) - - def apply_complex_homotopy(self, complex_homotopy, added_anims=[], **kwargs): - transformer, transform_kwargs = self.get_transformer(**kwargs) - - # def homotopy(x, y, z, t): - # output = complex_homotopy(complex(x, y), t) - # rescaled_output = self.z_to_point(output) - # return (rescaled_output.real, rescaled_output.imag, z) - - self.play( - ComplexHomotopy(complex_homotopy, transformer, **transform_kwargs), - *added_anims - ) diff --git a/manimlib/once_useful_constructs/counting.py b/manimlib/once_useful_constructs/counting.py deleted file mode 100644 index ad54da2e..00000000 --- a/manimlib/once_useful_constructs/counting.py +++ /dev/null @@ -1,263 +0,0 @@ -from manimlib.animation.creation import ShowCreation -from manimlib.animation.fading import FadeIn -from manimlib.animation.transform import MoveToTarget -from manimlib.animation.transform import Transform -from manimlib.constants import * -from manimlib.mobject.geometry import Arrow -from manimlib.mobject.geometry import Circle -from manimlib.mobject.geometry import Dot -from manimlib.mobject.svg.tex_mobject import Tex -from manimlib.mobject.types.vectorized_mobject import VGroup -from manimlib.scene.scene import Scene - -import itertools as it - -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(Tex(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 - 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.set_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( - next(self.dot_template_iterators[place]) - ) - 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, - lag_ratio=0.5 - )) - return result - - def get_number_mob(self, num): - result = VGroup() - place = 0 - max_place = self.max_place - while place < max_place: - digit = Tex(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 - - -class PowerCounter(CountingScene): - def is_next_digit(self): - number = self.number - while number > 1: - if number % self.base != 0: - return False - number /= self.base - return True - - def get_place_max(self, place): - return self.base - - def get_place_num(self, num, place): - return (num / (self.base ** place)) % self.base - - -class CountInDecimal(PowerCounter): - CONFIG = { - "base": 10, - } - - def construct(self): - for x in range(11): - self.increment() - for x in range(85): - self.increment(0.25) - for x in range(20): - self.increment() - - -class CountInTernary(PowerCounter): - CONFIG = { - "base": 3, - "dot_configuration_height": 1, - "ones_configuration_location": UP + 4 * RIGHT - } - - def construct(self): - self.count(27) - - # def get_template_configuration(self, place): - # return [ORIGIN, UP] - - -class CountInBinaryTo256(PowerCounter): - CONFIG = { - "base": 2, - "dot_configuration_height": 1, - "ones_configuration_location": UP + 5 * RIGHT - } - - def construct(self): - self.count(128, 0.3) - - def get_template_configuration(self, place): - return [ORIGIN, UP] - - -class FactorialBase(CountingScene): - CONFIG = { - "dot_configuration_height": 1, - "ones_configuration_location": UP + 4 * RIGHT - } - - def construct(self): - self.count(30, 0.4) - - def is_next_digit(self): - return self.number == self.factorial(self.max_place + 1) - - def get_place_max(self, place): - return place + 2 - - def get_place_num(self, num, place): - return (num / self.factorial(place + 1)) % self.get_place_max(place) - - def factorial(self, n): - if (n == 1): - return 1 - else: - return n * self.factorial(n - 1) diff --git a/manimlib/once_useful_constructs/graph_theory.py b/manimlib/once_useful_constructs/graph_theory.py deleted file mode 100644 index 9baee424..00000000 --- a/manimlib/once_useful_constructs/graph_theory.py +++ /dev/null @@ -1,413 +0,0 @@ -from functools import reduce -import itertools as it -import operator as op - -import numpy as np - -from manimlib.constants import * -from manimlib.scene.scene import Scene -from manimlib.utils.rate_functions import there_and_back -from manimlib.utils.space_ops import center_of_mass - - -class Graph(): - def __init__(self): - # List of points in R^3 - # vertices = [] - # List of pairs of indices of vertices - # edges = [] - # List of tuples of indices of vertices. The last should - # be a cycle whose interior is the entire graph, and when - # regions are computed its complement will be taken. - # region_cycles = [] - - self.construct() - - def construct(self): - pass - - def __str__(self): - return self.__class__.__name__ - - -class CubeGraph(Graph): - """ - 5 7 - 12 - 03 - 4 6 - """ - - def construct(self): - self.vertices = [ - (x, y, 0) - for r in (1, 2) - for x, y in it.product([-r, r], [-r, r]) - ] - self.edges = [ - (0, 1), - (0, 2), - (3, 1), - (3, 2), - (4, 5), - (4, 6), - (7, 5), - (7, 6), - (0, 4), - (1, 5), - (2, 6), - (3, 7), - ] - self.region_cycles = [ - [0, 2, 3, 1], - [4, 0, 1, 5], - [4, 6, 2, 0], - [6, 7, 3, 2], - [7, 5, 1, 3], - [4, 6, 7, 5], # By convention, last region will be "outside" - ] - - -class SampleGraph(Graph): - """ - 4 2 3 8 - 0 1 - 7 - 5 6 - """ - - def construct(self): - self.vertices = [ - (0, 0, 0), - (2, 0, 0), - (1, 2, 0), - (3, 2, 0), - (-1, 2, 0), - (-2, -2, 0), - (2, -2, 0), - (4, -1, 0), - (6, 2, 0), - ] - self.edges = [ - (0, 1), - (1, 2), - (1, 3), - (3, 2), - (2, 4), - (4, 0), - (2, 0), - (4, 5), - (0, 5), - (1, 5), - (5, 6), - (6, 7), - (7, 1), - (7, 8), - (8, 3), - ] - self.region_cycles = [ - (0, 1, 2), - (1, 3, 2), - (2, 4, 0), - (4, 5, 0), - (0, 5, 1), - (1, 5, 6, 7), - (1, 7, 8, 3), - (4, 5, 6, 7, 8, 3, 2), - ] - - -class OctohedronGraph(Graph): - """ - 3 - - 1 0 - 2 - 4 5 - """ - - def construct(self): - self.vertices = [ - (r * np.cos(angle), r * np.sin(angle) - 1, 0) - for r, s in [(1, 0), (3, 3)] - for angle in (np.pi / 6) * np.array([s, 4 + s, 8 + s]) - ] - self.edges = [ - (0, 1), - (1, 2), - (2, 0), - (5, 0), - (0, 3), - (3, 5), - (3, 1), - (3, 4), - (1, 4), - (4, 2), - (4, 5), - (5, 2), - ] - self.region_cycles = [ - (0, 1, 2), - (0, 5, 3), - (3, 1, 0), - (3, 4, 1), - (1, 4, 2), - (2, 4, 5), - (5, 0, 2), - (3, 4, 5), - ] - - -class CompleteGraph(Graph): - def __init__(self, num_vertices, radius=3): - self.num_vertices = num_vertices - self.radius = radius - Graph.__init__(self) - - def construct(self): - self.vertices = [ - (self.radius * np.cos(theta), self.radius * np.sin(theta), 0) - for x in range(self.num_vertices) - for theta in [2 * np.pi * x / self.num_vertices] - ] - self.edges = it.combinations(list(range(self.num_vertices)), 2) - - def __str__(self): - return Graph.__str__(self) + str(self.num_vertices) - - -class DiscreteGraphScene(Scene): - args_list = [ - (CubeGraph(),), - (SampleGraph(),), - (OctohedronGraph(),), - ] - - @staticmethod - def args_to_string(*args): - return str(args[0]) - - def __init__(self, graph, *args, **kwargs): - # See CubeGraph() above for format of graph - self.graph = graph - Scene.__init__(self, *args, **kwargs) - - def construct(self): - self._points = list(map(np.array, self.graph.vertices)) - self.vertices = self.dots = [Dot(p) for p in self._points] - self.edges = self.lines = [ - Line(self._points[i], self._points[j]) - for i, j in self.graph.edges - ] - self.add(*self.dots + self.edges) - - def generate_regions(self): - regions = [ - self.region_from_cycle(cycle) - for cycle in self.graph.region_cycles - ] - regions[-1].complement() # Outer region painted outwardly... - self.regions = regions - - def region_from_cycle(self, cycle): - point_pairs = [ - [ - self._points[cycle[i]], - self._points[cycle[(i + 1) % len(cycle)]] - ] - for i in range(len(cycle)) - ] - return region_from_line_boundary( - *point_pairs, shape=self.shape - ) - - def draw_vertices(self, **kwargs): - self.clear() - self.play(ShowCreation(Mobject(*self.vertices), **kwargs)) - - def draw_edges(self): - self.play(*[ - ShowCreation(edge, run_time=1.0) - for edge in self.edges - ]) - - def accent_vertices(self, **kwargs): - self.remove(*self.vertices) - start = Mobject(*self.vertices) - end = Mobject(*[ - Dot(point, radius=3 * Dot.DEFAULT_RADIUS, color="lightgreen") - for point in self._points - ]) - self.play(Transform( - start, end, rate_func=there_and_back, - **kwargs - )) - self.remove(start) - self.add(*self.vertices) - - def replace_vertices_with(self, mobject): - mobject.center() - diameter = max(mobject.get_height(), mobject.get_width()) - self.play(*[ - CounterclockwiseTransform( - vertex, - mobject.copy().shift(vertex.get_center()) - ) - for vertex in self.vertices - ] + [ - ApplyMethod( - edge.scale, - (edge.get_length() - diameter) / edge.get_length() - ) - for edge in self.edges - ]) - - def annotate_edges(self, mobject, fade_in=True, **kwargs): - angles = list(map(np.arctan, list(map(Line.get_slope, self.edges)))) - self.edge_annotations = [ - mobject.copy().rotate(angle).move_to(edge.get_center()) - for angle, edge in zip(angles, self.edges) - ] - if fade_in: - self.play(*[ - FadeIn(ann, **kwargs) - for ann in self.edge_annotations - ]) - - def trace_cycle(self, cycle=None, color="yellow", run_time=2.0): - if cycle is None: - cycle = self.graph.region_cycles[0] - next_in_cycle = it.cycle(cycle) - next(next_in_cycle) # jump one ahead - self.traced_cycle = Mobject(*[ - Line(self._points[i], self._points[j]).set_color(color) - for i, j in zip(cycle, next_in_cycle) - ]) - self.play( - ShowCreation(self.traced_cycle), - run_time=run_time - ) - - def generate_spanning_tree(self, root=0, color="yellow"): - self.spanning_tree_root = 0 - pairs = deepcopy(self.graph.edges) - pairs += [tuple(reversed(pair)) for pair in pairs] - self.spanning_tree_index_pairs = [] - curr = root - spanned_vertices = set([curr]) - to_check = set([curr]) - while len(to_check) > 0: - curr = to_check.pop() - for pair in pairs: - if pair[0] == curr and pair[1] not in spanned_vertices: - self.spanning_tree_index_pairs.append(pair) - spanned_vertices.add(pair[1]) - to_check.add(pair[1]) - self.spanning_tree = Mobject(*[ - Line( - self._points[pair[0]], - self._points[pair[1]] - ).set_color(color) - for pair in self.spanning_tree_index_pairs - ]) - - def generate_treeified_spanning_tree(self): - bottom = -FRAME_Y_RADIUS + 1 - x_sep = 1 - y_sep = 2 - if not hasattr(self, "spanning_tree"): - self.generate_spanning_tree() - root = self.spanning_tree_root - color = self.spanning_tree.get_color() - indices = list(range(len(self._points))) - # Build dicts - parent_of = dict([ - tuple(reversed(pair)) - for pair in self.spanning_tree_index_pairs - ]) - children_of = dict([(index, []) for index in indices]) - for child in parent_of: - children_of[parent_of[child]].append(child) - - x_coord_of = {root: 0} - y_coord_of = {root: bottom} - # width to allocate to a given node, computed as - # the maximum number of decendents in a single generation, - # minus 1, multiplied by x_sep - width_of = {} - for index in indices: - next_generation = children_of[index] - curr_max = max(1, len(next_generation)) - while next_generation != []: - next_generation = reduce(op.add, [ - children_of[node] - for node in next_generation - ]) - curr_max = max(curr_max, len(next_generation)) - width_of[index] = x_sep * (curr_max - 1) - to_process = [root] - while to_process != []: - index = to_process.pop() - if index not in y_coord_of: - y_coord_of[index] = y_sep + y_coord_of[parent_of[index]] - children = children_of[index] - left_hand = x_coord_of[index] - width_of[index] / 2.0 - for child in children: - x_coord_of[child] = left_hand + width_of[child] / 2.0 - left_hand += width_of[child] + x_sep - to_process += children - - new_points = [ - np.array([ - x_coord_of[index], - y_coord_of[index], - 0 - ]) - for index in indices - ] - self.treeified_spanning_tree = Mobject(*[ - Line(new_points[i], new_points[j]).set_color(color) - for i, j in self.spanning_tree_index_pairs - ]) - - def generate_dual_graph(self): - point_at_infinity = np.array([np.inf] * 3) - cycles = self.graph.region_cycles - self.dual_points = [ - center_of_mass([ - self._points[index] - for index in cycle - ]) - for cycle in cycles - ] - self.dual_vertices = [ - Dot(point).set_color("green") - for point in self.dual_points - ] - self.dual_vertices[-1] = Circle().scale(FRAME_X_RADIUS + FRAME_Y_RADIUS) - self.dual_points[-1] = point_at_infinity - - self.dual_edges = [] - for pair in self.graph.edges: - dual_point_pair = [] - for cycle in cycles: - if not (pair[0] in cycle and pair[1] in cycle): - continue - index1, index2 = cycle.index(pair[0]), cycle.index(pair[1]) - if abs(index1 - index2) in [1, len(cycle) - 1]: - dual_point_pair.append( - self.dual_points[cycles.index(cycle)] - ) - assert(len(dual_point_pair) == 2) - for i in 0, 1: - if all(dual_point_pair[i] == point_at_infinity): - new_point = np.array(dual_point_pair[1 - i]) - vect = center_of_mass([ - self._points[pair[0]], - self._points[pair[1]] - ]) - new_point - new_point += FRAME_X_RADIUS * vect / get_norm(vect) - dual_point_pair[i] = new_point - self.dual_edges.append( - Line(*dual_point_pair).set_color() - ) diff --git a/manimlib/once_useful_constructs/light.py b/manimlib/once_useful_constructs/light.py deleted file mode 100644 index 5ec132b9..00000000 --- a/manimlib/once_useful_constructs/light.py +++ /dev/null @@ -1,602 +0,0 @@ -from traceback import * - -from scipy.spatial import ConvexHull - -from manimlib.animation.composition import LaggedStartMap -from manimlib.animation.fading import FadeIn -from manimlib.animation.fading import FadeOut -from manimlib.animation.transform import Transform -from manimlib.constants import * -from manimlib.mobject.geometry import AnnularSector -from manimlib.mobject.geometry import Annulus -from manimlib.mobject.svg.svg_mobject import SVGMobject -from manimlib.mobject.types.vectorized_mobject import VMobject -from manimlib.mobject.types.vectorized_mobject import VectorizedPoint -from manimlib.utils.space_ops import angle_between_vectors -from manimlib.utils.space_ops import project_along_vector -from manimlib.utils.space_ops import rotate_vector -from manimlib.utils.space_ops import z_to_vector - -LIGHT_COLOR = YELLOW -SHADOW_COLOR = BLACK -SWITCH_ON_RUN_TIME = 1.5 -FAST_SWITCH_ON_RUN_TIME = 0.1 -NUM_LEVELS = 30 -NUM_CONES = 7 # in first lighthouse scene -NUM_VISIBLE_CONES = 5 # ibidem -ARC_TIP_LENGTH = 0.2 -AMBIENT_FULL = 0.8 -AMBIENT_DIMMED = 0.5 -SPOTLIGHT_FULL = 0.8 -SPOTLIGHT_DIMMED = 0.5 -LIGHTHOUSE_HEIGHT = 0.8 - -DEGREES = TAU / 360 - - -def inverse_power_law(maxint, scale, cutoff, exponent): - return (lambda r: maxint * (cutoff / (r / scale + cutoff))**exponent) - - -def inverse_quadratic(maxint, scale, cutoff): - return inverse_power_law(maxint, scale, cutoff, 2) - - -class SwitchOn(LaggedStartMap): - CONFIG = { - "lag_ratio": 0.2, - "run_time": SWITCH_ON_RUN_TIME - } - - def __init__(self, light, **kwargs): - if (not isinstance(light, AmbientLight) and not isinstance(light, Spotlight)): - raise Exception( - "Only AmbientLights and Spotlights can be switched on") - LaggedStartMap.__init__( - self, FadeIn, light, **kwargs - ) - - -class SwitchOff(LaggedStartMap): - CONFIG = { - "lag_ratio": 0.2, - "run_time": SWITCH_ON_RUN_TIME - } - - def __init__(self, light, **kwargs): - if (not isinstance(light, AmbientLight) and not isinstance(light, Spotlight)): - raise Exception( - "Only AmbientLights and Spotlights can be switched off") - light.set_submobjects(light.submobjects[::-1]) - LaggedStartMap.__init__(self, FadeOut, light, **kwargs) - light.set_submobjects(light.submobjects[::-1]) - - -class Lighthouse(SVGMobject): - CONFIG = { - "height": LIGHTHOUSE_HEIGHT, - "fill_color": WHITE, - "fill_opacity": 1.0, - } - - def __init__(self, **kwargs): - super().__init__("lighthouse", **kwargs) - - def move_to(self, point): - self.next_to(point, DOWN, buff=0) - - -class AmbientLight(VMobject): - - # Parameters are: - # * a source point - # * an opacity function - # * a light color - # * a max opacity - # * a radius (larger than the opacity's dropoff length) - # * the number of subdivisions (levels, annuli) - - CONFIG = { - "source_point": VectorizedPoint(location=ORIGIN, stroke_width=0, fill_opacity=0), - "opacity_function": lambda r: 1.0 / (r + 1.0)**2, - "color": LIGHT_COLOR, - "max_opacity": 1.0, - "num_levels": NUM_LEVELS, - "radius": 5.0 - } - - def init_points(self): - # in theory, this method is only called once, right? - # so removing submobs shd not be necessary - # - # Note: Usually, yes, it is only called within Mobject.__init__, - # but there is no strong guarantee of that, and you may want certain - # update functions to regenerate points here and there. - for submob in self.submobjects: - self.remove(submob) - - self.add(self.source_point) - - # create annuli - self.radius = float(self.radius) - dr = self.radius / self.num_levels - for r in np.arange(0, self.radius, dr): - alpha = self.max_opacity * self.opacity_function(r) - annulus = Annulus( - inner_radius=r, - outer_radius=r + dr, - color=self.color, - fill_opacity=alpha - ) - annulus.move_to(self.get_source_point()) - self.add(annulus) - - def move_source_to(self, point): - # old_source_point = self.get_source_point() - # self.shift(point - old_source_point) - self.move_to(point) - - return self - - def get_source_point(self): - return self.source_point.get_location() - - def dimming(self, new_alpha): - old_alpha = self.max_opacity - self.max_opacity = new_alpha - for submob in self.submobjects: - old_submob_alpha = submob.fill_opacity - new_submob_alpha = old_submob_alpha * new_alpha / old_alpha - submob.set_fill(opacity=new_submob_alpha) - - -class Spotlight(VMobject): - CONFIG = { - "source_point": VectorizedPoint(location=ORIGIN, stroke_width=0, fill_opacity=0), - "opacity_function": lambda r: 1.0 / (r / 2 + 1.0)**2, - "color": GREEN, # LIGHT_COLOR, - "max_opacity": 1.0, - "num_levels": 10, - "radius": 10.0, - "screen": None, - "camera_mob": None - } - - def projection_direction(self): - # Note: This seems reasonable, though for it to work you'd - # need to be sure that any 3d scene including a spotlight - # somewhere assigns that spotlights "camera" attribute - # to be the camera associated with that scene. - if self.camera_mob is None: - return OUT - else: - [phi, theta, r] = self.camera_mob.get_center() - v = np.array([np.sin(phi) * np.cos(theta), - np.sin(phi) * np.sin(theta), np.cos(phi)]) - return v # /get_norm(v) - - def project(self, point): - v = self.projection_direction() - w = project_along_vector(point, v) - return w - - def get_source_point(self): - return self.source_point.get_location() - - def init_points(self): - self.set_submobjects([]) - - self.add(self.source_point) - - if self.screen is not None: - # look for the screen and create annular sectors - lower_angle, upper_angle = self.viewing_angles(self.screen) - self.radius = float(self.radius) - dr = self.radius / self.num_levels - lower_ray, upper_ray = self.viewing_rays(self.screen) - - for r in np.arange(0, self.radius, dr): - new_sector = self.new_sector(r, dr, lower_angle, upper_angle) - self.add(new_sector) - - def new_sector(self, r, dr, lower_angle, upper_angle): - alpha = self.max_opacity * self.opacity_function(r) - annular_sector = AnnularSector( - inner_radius=r, - outer_radius=r + dr, - color=self.color, - fill_opacity=alpha, - start_angle=lower_angle, - angle=upper_angle - lower_angle - ) - # rotate (not project) it into the viewing plane - rotation_matrix = z_to_vector(self.projection_direction()) - annular_sector.apply_matrix(rotation_matrix) - # now rotate it inside that plane - rotated_RIGHT = np.dot(RIGHT, rotation_matrix.T) - projected_RIGHT = self.project(RIGHT) - omega = angle_between_vectors(rotated_RIGHT, projected_RIGHT) - annular_sector.rotate(omega, axis=self.projection_direction()) - annular_sector.move_arc_center_to(self.get_source_point()) - - return annular_sector - - def viewing_angle_of_point(self, point): - # as measured from the positive x-axis - v1 = self.project(RIGHT) - v2 = self.project(np.array(point) - self.get_source_point()) - absolute_angle = angle_between_vectors(v1, v2) - # determine the angle's sign depending on their plane's - # choice of orientation. That choice is set by the camera - # position, i. e. projection direction - - if np.dot(self.projection_direction(), np.cross(v1, v2)) > 0: - return absolute_angle - else: - return -absolute_angle - - def viewing_angles(self, screen): - - screen_points = screen.get_anchors() - projected_screen_points = list(map(self.project, screen_points)) - - viewing_angles = np.array(list(map(self.viewing_angle_of_point, - projected_screen_points))) - - lower_angle = upper_angle = 0 - if len(viewing_angles) != 0: - lower_angle = np.min(viewing_angles) - upper_angle = np.max(viewing_angles) - - if upper_angle - lower_angle > TAU / 2: - lower_angle, upper_angle = upper_angle, lower_angle + TAU - return lower_angle, upper_angle - - 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 opening_angle(self): - l, u = self.viewing_angles(self.screen) - return u - l - - def start_angle(self): - l, u = self.viewing_angles(self.screen) - return l - - def stop_angle(self): - l, u = self.viewing_angles(self.screen) - return u - - def move_source_to(self, point): - self.source_point.set_location(np.array(point)) - # self.source_point.move_to(np.array(point)) - # self.move_to(point) - self.update_sectors() - return self - - def update_sectors(self): - if self.screen is None: - return - for submob in self.submobjects: - if type(submob) == AnnularSector: - lower_angle, upper_angle = self.viewing_angles(self.screen) - # dr = submob.outer_radius - submob.inner_radius - dr = self.radius / self.num_levels - new_submob = self.new_sector( - submob.inner_radius, dr, lower_angle, upper_angle - ) - # submob.points = new_submob.points - # submob.set_fill(opacity = 10 * self.opacity_function(submob.outer_radius)) - Transform(submob, new_submob).update(1) - - def dimming(self, new_alpha): - old_alpha = self.max_opacity - self.max_opacity = new_alpha - for submob in self.submobjects: - # Note: Maybe it'd be best to have a Shadow class so that the - # type can be checked directly? - if type(submob) != AnnularSector: - # it's the shadow, don't dim it - continue - old_submob_alpha = submob.fill_opacity - new_submob_alpha = old_submob_alpha * new_alpha / old_alpha - submob.set_fill(opacity=new_submob_alpha) - - def change_opacity_function(self, new_f): - self.opacity_function = new_f - dr = self.radius / self.num_levels - - sectors = [] - for submob in self.submobjects: - if type(submob) == AnnularSector: - sectors.append(submob) - - for (r, submob) in zip(np.arange(0, self.radius, dr), sectors): - if type(submob) != AnnularSector: - # it's the shadow, don't dim it - continue - alpha = self.opacity_function(r) - submob.set_fill(opacity=alpha) - -# Warning: This class is likely quite buggy. - - -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 init_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 self.screen.get_num_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.set_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.resize_points(0) - return - - ray1 = anchors[source_index - 1] - projected_source - ray1 = ray1 / get_norm(ray1) * 100 - - ray2 = anchors[source_index] - projected_source - ray2 = ray2 / get_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 - - -# Redefining what was once a ContinualAnimation class -# as a function -def ScreenTracker(light_source): - light_source.add_updater(lambda m: m.update()) - return light_source diff --git a/manimlib/once_useful_constructs/reconfigurable_scene.py b/manimlib/once_useful_constructs/reconfigurable_scene.py deleted file mode 100644 index 96e68d87..00000000 --- a/manimlib/once_useful_constructs/reconfigurable_scene.py +++ /dev/null @@ -1,66 +0,0 @@ -from manimlib.animation.transform import Transform -from manimlib.constants import * -from manimlib.mobject.mobject import Mobject -from manimlib.scene.scene import Scene - - -class ReconfigurableScene(Scene): - """ - Note, this seems to no longer work as intented. - """ - CONFIG = { - "allow_recursion": True, - } - - def setup(self): - self.states = [] - self.num_recursions = 0 - - def transition_to_alt_config( - self, - return_to_original_configuration=True, - transformation_kwargs=None, - **new_config - ): - if transformation_kwargs is None: - transformation_kwargs = {} - original_state = self.get_state() - state_copy = original_state.copy() - self.states.append(state_copy) - if not self.allow_recursion: - return - alt_scene = self.__class__( - skip_animations=True, - allow_recursion=False, - **new_config - ) - alt_state = alt_scene.states[len(self.states) - 1] - - if return_to_original_configuration: - self.clear() - self.transition_between_states( - state_copy, alt_state, - **transformation_kwargs - ) - self.transition_between_states( - state_copy, original_state, - **transformation_kwargs - ) - self.clear() - self.add(*original_state) - else: - self.transition_between_states( - original_state, alt_state, - **transformation_kwargs - ) - self.__dict__.update(new_config) - - def get_state(self): - # Want to return a mobject that maintains the most - # structure. The way to do that is to extract only - # those that aren't inside another. - return Mobject(*self.get_top_level_mobjects()) - - def transition_between_states(self, start_state, target_state, **kwargs): - self.play(Transform(start_state, target_state, **kwargs)) - self.wait() diff --git a/manimlib/once_useful_constructs/region.py b/manimlib/once_useful_constructs/region.py deleted file mode 100644 index 1bc95a05..00000000 --- a/manimlib/once_useful_constructs/region.py +++ /dev/null @@ -1,107 +0,0 @@ -from copy import deepcopy -import itertools as it - -from manimlib.constants import * -from manimlib.mobject.mobject import Mobject -from manimlib.utils.iterables import adjacent_pairs - -# Warning: This is all now pretty deprecated, and should not be expected to work - - -class Region(Mobject): - CONFIG = { - "display_mode": "region" - } - - def __init__(self, condition=(lambda x, y: True), **kwargs): - """ - Condition must be a function which takes in two real - arrays (representing x and y values of space respectively) - and return a boolean array. This can essentially look like - a function from R^2 to {True, False}, but & and | must be - used in place of "and" and "or" - """ - Mobject.__init__(self, **kwargs) - self.condition = condition - - def _combine(self, region, op): - self.condition = lambda x, y: op( - self.condition(x, y), - region.condition(x, y) - ) - - def union(self, region): - self._combine(region, lambda bg1, bg2: bg1 | bg2) - return self - - def intersect(self, region): - self._combine(region, lambda bg1, bg2: bg1 & bg2) - return self - - def complement(self): - self.bool_grid = ~self.bool_grid - return self - - -class HalfPlane(Region): - def __init__(self, point_pair, upper_left=True, *args, **kwargs): - """ - point_pair of the form [(x_0, y_0,...), (x_1, y_1,...)] - - Pf upper_left is True, the side of the region will be - everything on the upper left side of the line through - the point pair - """ - if not upper_left: - point_pair = list(point_pair) - point_pair.reverse() - (x0, y0), (x1, y1) = point_pair[0][:2], point_pair[1][:2] - - def condition(x, y): - return (x1 - x0) * (y - y0) > (y1 - y0) * (x - x0) - Region.__init__(self, condition, *args, **kwargs) - - -def region_from_line_boundary(*lines, **kwargs): - reg = Region(**kwargs) - for line in lines: - reg.intersect(HalfPlane(line, **kwargs)) - return reg - - -def region_from_polygon_vertices(*vertices, **kwargs): - return region_from_line_boundary(*adjacent_pairs(vertices), **kwargs) - - -def plane_partition(*lines, **kwargs): - """ - A 'line' is a pair of points [(x0, y0,...), (x1, y1,...)] - - Returns the list of regions of the plane cut out by - these lines - """ - result = [] - half_planes = [HalfPlane(line, **kwargs) for line in lines] - complements = [deepcopy(hp).complement() for hp in half_planes] - num_lines = len(lines) - for bool_list in it.product(*[[True, False]] * num_lines): - reg = Region(**kwargs) - for i in range(num_lines): - if bool_list[i]: - reg.intersect(half_planes[i]) - else: - reg.intersect(complements[i]) - if reg.bool_grid.any(): - result.append(reg) - return result - - -def plane_partition_from_points(*points, **kwargs): - """ - Returns list of regions cut out by the complete graph - with points from the argument as vertices. - - Each point comes in the form (x, y) - """ - lines = [[p1, p2] for (p1, p2) in it.combinations(points, 2)] - return plane_partition(*lines, **kwargs)