3b1b-manim/moser/main.py
2015-04-26 14:25:43 -07:00

518 lines
16 KiB
Python

#!/usr/bin/env python
import numpy as np
import itertools as it
import operator as op
from copy import deepcopy
from random import random
from animation import *
from mobject import *
from image_mobject import *
from constants import *
from region import *
from scene import Scene
from moser_helpers import *
from graphs import *
RADIUS = SPACE_HEIGHT - 0.1
CIRCLE_DENSITY = DEFAULT_POINT_DENSITY_1D*RADIUS
movie_prefix = "moser/"
############################################
class CircleScene(Scene):
def __init__(self, radians, *args, **kwargs):
Scene.__init__(self, *args, **kwargs)
self.radius = RADIUS
self.circle = Circle(density = CIRCLE_DENSITY).scale(self.radius)
self.points = [
(self.radius * np.cos(angle), self.radius * np.sin(angle), 0)
for angle in radians
]
self.dots = [Dot(point) for point in self.points]
self.lines = [Line(p1, p2) for p1, p2 in it.combinations(self.points, 2)]
self.add(self.circle, *self.dots + self.lines)
class GraphScene(Scene):
#Note, the placement of vertices in this is pretty hard coded, be
#warned if you want to change it.
def __init__(self, graph, *args, **kwargs):
Scene.__init__(self, *args, **kwargs)
#See CUBE_GRAPH above for format of graph
self.graph = graph
self.points = map(np.array, graph["vertices"])
self.vertices = self.dots = [Dot(p) for p in self.points]
self.edges = [
Line(self.points[i], self.points[j])
for i, j in graph["edges"]
]
self.add(*self.dots + self.edges)
def generate_regions(self):
regions = [
region_from_line_boundary(*[
[
self.points[rc[i]],
self.points[rc[(i+1)%len(rc)]]
]
for i in range(len(rc))
])
for rc in self.graph["region_cycles"]
]
regions[-1].complement()#Outer region painted outwardly...
self.regions = regions
##################################################
def count_lines(*radians):
#TODO, Count things explicitly?
sc = CircleScene(radians)
text_center = (sc.radius + 1, sc.radius -1, 0)
scale_factor = 0.4
text = tex_mobject(r"\text{How Many Lines?}", size = r"\large")
n = len(radians)
formula, answer = tex_mobject([
r"{%d \choose 2} = \frac{%d(%d - 1)}{2} = "%(n, n, n),
str(choose(n, 2))
])
text.scale(scale_factor).shift(text_center)
x = text_center[0]
new_lines = [
Line((x-1, y, 0), (x+1, y, 0))
for y in np.arange(
-(sc.radius - 1),
sc.radius - 1,
(2*sc.radius - 2)/len(sc.lines)
)
]
sc.add(text)
sc.dither()
sc.animate(*[
Transform(line1, line2, run_time = 2)
for line1, line2 in zip(sc.lines, new_lines)
])
sc.dither()
sc.remove(text)
sc.count(new_lines)
anims = [FadeIn(formula)]
for mob in sc.mobjects:
if mob == sc.number: #put in during animate_count
anims.append(Transform(mob, answer))
else:
anims.append(FadeOut(mob))
sc.animate(*anims, run_time = 1)
sc.write_to_movie(movie_prefix + "CountLines%dPoints"%len(radians))
def count_intersection_points(*radians):
radians = [r % (2*np.pi) for r in radians]
radians.sort()
sc = CircleScene(radians)
intersection_points = [
intersection((p[0], p[2]), (p[1], p[3]))
for p in it.combinations(sc.points, 4)
]
intersection_dots = [Dot(point) for point in intersection_points]
text_center = (sc.radius + 0.5, sc.radius -0.5, 0)
size = r"\large"
scale_factor = 0.4
text = tex_mobject(r"\text{How Many Intersection Points?}", size = size)
n = len(radians)
formula, answer = tex_mobjects([
r"{%d \choose 4} = \frac{%d(%d - 1)(%d - 2)(%d-3)}{1\cdot 2\cdot 3 \cdot 4}="%(n, n, n, n, n),
str(choose(n, 4))
])
text.scale(scale_factor).shift(text_center)
# new_points = [
# (text_center[0], y, 0)
# for y in np.arange(
# -(sc.radius - 1),
# sc.radius - 1,
# (2*sc.radius - 2)/choose(len(sc.points), 4)
# )
# ]
# new_dots = CompoundMobject(*[
# Dot(point) for point in new_points
# ])
sc.add(text)
sc.count(intersection_dots, "show", num_offset = (0, 0, 0))
sc.dither()
# sc.animate(Transform(intersection_dots, new_dots))
anims = []
for mob in sc.mobjects:
if mob == sc.number: #put in during animate_count
anims.append(Transform(mob, answer))
else:
anims.append(FadeOut(mob))
anims.append(Animation(formula))
sc.animate(*anims, run_time = 1)
name = "CountIntersectionPoints%dPoints"%len(radians)
sc.write_to_movie(movie_prefix + name)
def non_general_position():
radians = np.arange(1, 7)
new_radians = (np.pi/3)*radians
sc1 = CircleScene(radians)
sc2 = CircleScene(new_radians)
center_region = reduce(
Region.intersect,
[
HalfPlane((sc1.points[x], sc1.points[(x+3)%6]))
for x in [0, 4, 2]#Ya know, trust it
]
)
center_region
text = tex_mobject(r"\text{This region disappears}", size = r"\large")
text.center().scale(0.5).shift((-sc1.radius, sc1.radius-0.3, 0))
arrow = Arrow(
point = (-0.35, -0.1, 0),
direction = (1, -1, 0),
length = sc1.radius + 1,
color = "white",
)
sc1.highlight_region(center_region, "green")
sc1.add(text, arrow)
sc1.dither(2)
sc1.remove(text, arrow)
sc1.reset_background()
sc1.animate(*[
Transform(mob1, mob2, run_time = DEFAULT_ANIMATION_RUN_TIME)
for mob1, mob2 in zip(sc1.mobjects, sc2.mobjects)
])
sc1.write_to_movie(movie_prefix + "NonGeneralPosition")
def line_corresponds_with_pair(radians, r1, r2):
sc = CircleScene(radians)
#Remove from sc.lines list, so they won't be faded out
assert r1 in radians and r2 in radians
line_index = list(it.combinations(radians, 2)).index((r1, r2))
radians = list(radians)
dot0_index, dot1_index = radians.index(r1), radians.index(r2)
line, dot0, dot1 = sc.lines[line_index], sc.dots[dot0_index], sc.dots[dot1_index]
sc.lines.remove(line)
sc.dots.remove(dot0)
sc.dots.remove(dot1)
sc.dither()
sc.animate(*[
FadeOut(mob, alpha_func = not_quite_there)
for mob in sc.lines + sc.dots
])
sc.add(sc.circle)
sc.animate(*[
ScaleInPlace(mob, 3, alpha_func = there_and_back)
for mob in (dot0, dot1)
])
sc.animate(Transform(line, dot0))
name = "LineCorrspondsWithPair%d%d"%(dot0_index, dot1_index)
sc.write_to_movie(movie_prefix + name)
def illustrate_n_choose_k(n, k):
sc = Scene()
nrange = range(1, n+1)
tuples = list(it.combinations(nrange, k))
nrange_mobs = tex_mobjects([str(n) + r'\;' for n in nrange])
tuple_mobs = tex_mobjects(
[
(r'\\&' if c%(20//k) == 0 else r'\;\;') + str(p)
for p, c in zip(tuples, it.count())
],
size = r"\small"
)
tuple_terms = {
2 : "pairs",
3 : "triplets",
4 : "quadruplets",
}
tuple_term = tuple_terms[k] if k in tuple_terms else "tuples"
form1, count, form2 = tex_mobject([
r"{%d \choose %d} = "%(n, k),
"%d"%choose(n, k),
r" \text{ total %s}"%tuple_term
])
for mob in nrange_mobs:
mob.shift((0, 2, 0))
for mob in form1, count, form2:
mob.shift((0, -SPACE_HEIGHT + 1, 0))
count_center = count.get_center()
for mob in tuple_mobs:
mob.scale(0.6)
sc.add(*nrange_mobs)
sc.dither()
run_time = 6.0
frame_time = run_time / len(tuples)
for tup, count in zip(tuples, it.count()):
count_mob = tex_mobject(str(count+1))
count_mob.center().shift(count_center)
sc.add(count_mob)
tuple_copy = CompoundMobject(*[nrange_mobs[index-1] for index in tup])
tuple_copy.highlight()
sc.add(tuple_copy)
sc.add(tuple_mobs[count])
sc.dither(frame_time)
sc.remove(count_mob)
sc.remove(tuple_copy)
sc.add(count_mob)
sc.animate(FadeIn(CompoundMobject(form1, form2)))
sc.write_to_movie(movie_prefix + "Illustrate%dChoose%d"%(n, k))
def intersection_point_correspondances(radians, indices):
assert(len(indices) == 4)
indices.sort()
sc = CircleScene(radians)
intersection_point = intersection(
(sc.points[indices[0]], sc.points[indices[2]]),
(sc.points[indices[1]], sc.points[indices[3]])
)
intersection_point = tuple(list(intersection_point) + [0])
intersection_dot = Dot(intersection_point)
intersection_dot_arrow = Arrow(intersection_point).nudge()
sc.add(intersection_dot)
pairs = list(it.combinations(range(len(radians)), 2))
lines_to_save = [
sc.lines[pairs.index((indices[p0], indices[p1]))]
for p0, p1 in [(0, 2), (1, 3)]
]
dots_to_save = [
sc.dots[p]
for p in indices
]
line_statement = tex_mobject(r"\text{Pair of Lines}")
dots_statement = tex_mobject(r"&\text{Quadruplet of} \\ &\text{outer dots}")
for mob in line_statement, dots_statement:
mob.center()
mob.scale(0.7)
mob.shift((SPACE_WIDTH-2, SPACE_HEIGHT - 1, 0))
fade_outs = []
line_highlights = []
dot_highlights = []
dot_pointers = []
for mob in sc.mobjects:
if mob in lines_to_save:
line_highlights.append(Highlight(mob))
elif mob in dots_to_save:
dot_highlights.append(Highlight(mob))
dot_pointers.append(Arrow(mob.get_center()).nudge())
elif mob != intersection_dot:
fade_outs.append(FadeOut(mob, alpha_func = not_quite_there))
sc.add(intersection_dot_arrow)
sc.animate(Highlight(intersection_dot))
sc.remove(intersection_dot_arrow)
sc.animate(*fade_outs)
sc.dither()
sc.add(line_statement)
sc.animate(*line_highlights)
sc.remove(line_statement)
sc.dither()
sc.add(dots_statement, *dot_pointers)
sc.animate(*dot_highlights)
sc.remove(dots_statement, *dot_pointers)
name = "IntersectionPointCorrespondances"
for ind in indices:
name += str(ind)
sc.write_to_movie(movie_prefix + name)
def lines_intersect_outside(radians, indices):
assert(len(indices) == 4)
indices.sort()
sc = CircleScene(radians)
intersection_point = intersection(
(sc.points[indices[0]], sc.points[indices[1]]),
(sc.points[indices[2]], sc.points[indices[3]])
)
intersection_point = tuple(list(intersection_point) + [0])
intersection_dot = Dot(intersection_point)
pairs = list(it.combinations(range(len(radians)), 2))
lines_to_save = [
sc.lines[pairs.index((indices[p0], indices[p1]))]
for p0, p1 in [(0, 1), (2, 3)]
]
sc.animate(*[
FadeOut(mob, alpha_func = not_quite_there)
for mob in sc.mobjects if mob not in lines_to_save
])
sc.animate(*[
Transform(
Line(sc.points[indices[p0]], sc.points[indices[p1]]),
Line(sc.points[indices[p0]], intersection_point))
for p0, p1 in [(0, 1), (3, 2)]
] + [ShowCreation(intersection_dot)])
name = "LinesIntersectOutside"
for ind in indices:
name += str(ind)
sc.write_to_movie(movie_prefix + name)
def quadruplets_to_intersections(*radians):
sc = CircleScene(radians)
quadruplets = it.combinations(range(len(radians)), 4)
frame_time = 1.0
for quad in quadruplets:
intersection_dot = Dot(intersection(
(sc.points[quad[0]], sc.points[quad[2]]),
(sc.points[quad[1]], sc.points[quad[3]])
)).repeat(3)
dot_quad = [deepcopy(sc.dots[i]) for i in quad]
for dot in dot_quad:
dot.scale_in_place(2)
# arrows = [Arrow(d.get_center()) for d in dot_quad]
dot_quad = CompoundMobject(*dot_quad)
# arrows = CompoundMobject(*arrows)
dot_quad.highlight()
# sc.add(arrows)
sc.add(dot_quad)
sc.dither(frame_time / 3)
sc.animate(Transform(
dot_quad,
intersection_dot,
run_time = 3*frame_time/2
))
# sc.remove(arrows)
name = "QuadrupletsToIntersections" + len(radians)
sc.write_to_movie(movie_prefix + name)
def defining_graph(graph):
gs = GraphScene(graph)
dots, lines = gs.vertices, gs.edges
gs.remove(*dots + lines)
all_dots = CompoundMobject(*dots)
gs.animate(ShowCreation(all_dots))
gs.remove(all_dots)
gs.add(*dots)
gs.dither()
gs.animate(*[
ShowCreation(line) for line in lines
])
#Move to new graph
new_graph = deepcopy(graph)
new_graph["vertices"] = [
(v[0] + 3*random(), v[1] + 3*random(), 0)
for v in new_graph["vertices"]
]
ngs = GraphScene(new_graph)
gs.animate(*[
Transform(m[0], m[1])
for m in zip(gs.mobjects, ngs.mobjects)
], run_time = 7.0)
name = "DefiningGraph" + graph["name"]
gs.write_to_movie(movie_prefix + name)
def doubled_edges(graph):
gs = GraphScene(graph)
lines_to_double = gs.edges[:9:3]
crazy_lines = [
(
line,
Line(line.end, line.start),
CurvedLine(line.start, line.end) ,
CurvedLine(line.end, line.start)
)
for line in lines_to_double
]
anims = []
outward_curved_lines = []
kwargs = {"run_time" : 3.0}
for straight, backwards, inward, outward in crazy_lines:
anims += [
Transform(straight, inward, **kwargs),
Transform(backwards, outward, **kwargs),
]
outward_curved_lines.append(outward)
gs.animate(*anims)
gs.dither()
gs.remove(*outward_curved_lines)
name = "DoubledEdges" + graph["name"]
gs.write_to_movie(movie_prefix + name)
def eulers_formula(graph):
gs = GraphScene(graph)
terms = "V - E + F =2".split(" ")
form = dict([
(key, mob)
for key, mob in zip(terms, tex_mobjects(terms))
])
for mob in form.values():
mob.shift((0, SPACE_HEIGHT-1.5, 0))
formula = CompoundMobject(*form.values())
new_form = dict([
(key, deepcopy(mob).shift((0, -0.7, 0)))
for key, mob in zip(form.keys(), form.values())
])
gs.add(formula)
colored_dots = [
deepcopy(d).scale_in_place(1.5).highlight("red")
for d in gs.dots
]
colored_edges = [
deepcopy(e).highlight("red")
for e in gs.edges
]
frame_time = 0.3
gs.generate_regions()
parameters = [
(colored_dots, "V", "mobject", "-", "show_creation"),
(colored_edges, "E", "mobject", "+", "show_creation"),
(gs.regions, "F", "region", "=2", "show_all")
]
for items, letter, item_type, symbol, mode in parameters:
gs.count(
items,
item_type = item_type,
mode = mode,
num_offset = new_form[letter].get_center(),
run_time = frame_time*len(items)
)
gs.dither()
if item_type == "mobject":
gs.remove(*items)
gs.add(new_form[symbol])
gs.reset_background()
name = "EulersFormula" + graph["name"]
gs.write_to_movie(movie_prefix + name)
##################################################
if __name__ == '__main__':
radians = np.arange(0, 6, 6.0/7)
# count_lines(*radians)
# count_lines(*radians[:4])
# count_intersection_points(*radians[:4])
# count_intersection_points(*radians[:6])
# count_intersection_points(*radians)
# non_general_position()
# line_corresponds_with_pair(radians, radians[3], radians[4])
# line_corresponds_with_pair(radians, radians[2], radians[5])
# illustrate_n_choose_k(7, 2)
# illustrate_n_choose_k(6, 4)
# intersection_point_correspondances(radians, range(0, 7, 2))
# lines_intersect_outside(radians, [2, 4, 5, 6])
quadruplets_to_intersections(*radians[:6])
# defining_graph(SAMPLE_GRAPH)
# doubled_edges(CUBE_GRAPH)
# eulers_formula(CUBE_GRAPH)
# eulers_formula(SAMPLE_GRAPH)
# eulers_formula(OCTOHEDRON_GRAPH)