mirror of
https://github.com/3b1b/manim.git
synced 2025-08-02 02:35:22 +08:00
Preliminary script wrapper implemented and applied to moser/main.py, more moser/main.py functions as well
This commit is contained in:
18
animation.py
18
animation.py
@ -177,6 +177,7 @@ class FadeIn(Animation):
|
||||
class Transform(Animation):
|
||||
def __init__(self, mobject1, mobject2,
|
||||
run_time = DEFAULT_TRANSFORM_RUN_TIME,
|
||||
black_out_extra_points = True,
|
||||
*args, **kwargs):
|
||||
count1, count2 = mobject1.get_num_points(), mobject2.get_num_points()
|
||||
if count2 == 0:
|
||||
@ -190,7 +191,7 @@ class Transform(Animation):
|
||||
self.reference_mobjects.append(mobject2)
|
||||
self.name += "To" + str(mobject2)
|
||||
|
||||
if count2 < count1:
|
||||
if black_out_extra_points and count2 < count1:
|
||||
#Ensure redundant pixels fade to black
|
||||
indices = np.arange(
|
||||
0, count1-1, float(count1) / count2
|
||||
@ -222,6 +223,21 @@ class Transform(Animation):
|
||||
)[self.non_redundant_m2_indices]
|
||||
)
|
||||
|
||||
class SemiCircleTransform(Transform):
|
||||
def update_mobject(self, alpha):
|
||||
sm, em = self.starting_mobject, self.ending_mobject
|
||||
midpoints = (sm.points + em.points) / 2
|
||||
angle = alpha * np.pi
|
||||
rotation_matrix = np.matrix([
|
||||
[np.cos(angle), np.sin(angle), 0],
|
||||
[-np.sin(angle), np.cos(angle), 0],
|
||||
[0, 0, 1],
|
||||
])
|
||||
self.mobject.points = np.dot(
|
||||
sm.points - midpoints,
|
||||
np.transpose(rotation_matrix)
|
||||
) + midpoints
|
||||
|
||||
class FadeToColor(Transform):
|
||||
def __init__(self, mobject, color, *args, **kwargs):
|
||||
target = copy.deepcopy(mobject).highlight(color)
|
||||
|
@ -7,7 +7,7 @@ DEFAULT_POINT_DENSITY_2D = 25 #if PRODUCTION_QUALITY else 20
|
||||
DEFAULT_POINT_DENSITY_1D = 150 #if PRODUCTION_QUALITY else 50
|
||||
|
||||
DEFAULT_HEIGHT = 1440 if PRODUCTION_QUALITY else 480
|
||||
DEFAULT_WIDTH = 2560 if PRODUCTION_QUALITY else 640
|
||||
DEFAULT_WIDTH = 2560 if PRODUCTION_QUALITY else 840
|
||||
#All in seconds
|
||||
DEFAULT_FRAME_DURATION = 0.04 if PRODUCTION_QUALITY else 0.1
|
||||
DEFAULT_ANIMATION_RUN_TIME = 3.0
|
||||
|
@ -22,9 +22,9 @@ def get_pixels(image_array):
|
||||
|
||||
def paint_region(region, image_array = None, color = None):
|
||||
pixels = get_pixels(image_array)
|
||||
# assert region.shape == pixels.shape[:2]
|
||||
assert region.shape == pixels.shape[:2]
|
||||
if color is None:
|
||||
rgb = np.random.random(3)
|
||||
rgb = 0.5 * np.random.random(3) #Random darker colors
|
||||
else:
|
||||
rgb = np.array(Color(color).get_rgb())
|
||||
pixels[region.bool_grid] = (255*rgb).astype('uint8')
|
||||
|
22
helpers.py
22
helpers.py
@ -4,27 +4,31 @@ from PIL import Image
|
||||
from colour import Color
|
||||
from random import random
|
||||
import string
|
||||
import re
|
||||
|
||||
from constants import *
|
||||
|
||||
def hash_args(args):
|
||||
args = map(lambda arg : arg.__name__ if type(arg) == type(hash_args) else arg, args)
|
||||
return str(hash(str(args))%1000) if args else ""
|
||||
|
||||
def random_color():
|
||||
color = Color()
|
||||
color.set_rgb([1 - 0.5 * random() for x in range(3)])
|
||||
return color
|
||||
|
||||
################################################
|
||||
|
||||
def to_cammel_case(name):
|
||||
parts = name.split("_")
|
||||
parts = [
|
||||
return "".join([
|
||||
filter(
|
||||
lambda c : c not in string.punctuation + string.whitespace, part
|
||||
).capitalize()
|
||||
for part in parts
|
||||
]
|
||||
return "".join(parts)
|
||||
for part in name.split("_")
|
||||
])
|
||||
|
||||
def initials(name, sep_values = [" ", "_"]):
|
||||
return "".join([
|
||||
s[0] for s in re.split("|".join(sep_values), name)
|
||||
])
|
||||
|
||||
################################################
|
||||
|
||||
def drag_pixels(frames):
|
||||
curr = frames[0]
|
||||
|
385
moser/main.py
385
moser/main.py
@ -4,7 +4,8 @@ import numpy as np
|
||||
import itertools as it
|
||||
import operator as op
|
||||
from copy import deepcopy
|
||||
from random import random
|
||||
from random import random, randint
|
||||
import sys
|
||||
|
||||
|
||||
from animation import *
|
||||
@ -13,14 +14,12 @@ from image_mobject import *
|
||||
from constants import *
|
||||
from region import *
|
||||
from scene import Scene
|
||||
from script_wrapper import create_scene
|
||||
|
||||
from moser_helpers import *
|
||||
from graphs import *
|
||||
|
||||
|
||||
movie_prefix = "moser/"
|
||||
|
||||
def count_lines(*radians):
|
||||
def count_lines(radians):
|
||||
#TODO, Count things explicitly?
|
||||
sc = CircleScene(radians)
|
||||
text_center = (sc.radius + 1, sc.radius -1, 0)
|
||||
@ -57,10 +56,10 @@ def count_lines(*radians):
|
||||
else:
|
||||
anims.append(FadeOut(mob))
|
||||
sc.animate(*anims, run_time = 1)
|
||||
sc.write_to_movie(movie_prefix + "CountLines%dPoints"%len(radians))
|
||||
|
||||
return sc
|
||||
|
||||
def count_intersection_points(*radians):
|
||||
def count_intersection_points(radians):
|
||||
radians = [r % (2*np.pi) for r in radians]
|
||||
radians.sort()
|
||||
sc = CircleScene(radians)
|
||||
@ -104,8 +103,7 @@ def count_intersection_points(*radians):
|
||||
anims.append(Animation(formula))
|
||||
sc.animate(*anims, run_time = 1)
|
||||
|
||||
name = "CountIntersectionPoints%dPoints"%len(radians)
|
||||
sc.write_to_movie(movie_prefix + name)
|
||||
return sc
|
||||
|
||||
def non_general_position():
|
||||
radians = np.arange(1, 7)
|
||||
@ -138,15 +136,15 @@ def non_general_position():
|
||||
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):
|
||||
return sc1
|
||||
|
||||
def line_corresponds_with_pair(radians, dot0_index, dot1_index):
|
||||
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)
|
||||
r1, r2 = radians[dot0_index], radians[dot1_index]
|
||||
line_index = list(it.combinations(radians, 2)).index((r1, r2))
|
||||
line, dot0, dot1 = sc.lines[line_index], sc.dots[dot0_index], sc.dots[dot1_index]
|
||||
sc.lines.remove(line)
|
||||
sc.dots.remove(dot0)
|
||||
@ -162,8 +160,8 @@ def line_corresponds_with_pair(radians, r1, r2):
|
||||
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)
|
||||
|
||||
return sc
|
||||
|
||||
def illustrate_n_choose_k(n, k):
|
||||
sc = Scene()
|
||||
@ -213,7 +211,8 @@ def illustrate_n_choose_k(n, k):
|
||||
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))
|
||||
|
||||
return sc
|
||||
|
||||
def intersection_point_correspondances(radians, indices):
|
||||
assert(len(indices) == 4)
|
||||
@ -268,10 +267,7 @@ def intersection_point_correspondances(radians, indices):
|
||||
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)
|
||||
return sc
|
||||
|
||||
def lines_intersect_outside(radians, indices):
|
||||
assert(len(indices) == 4)
|
||||
@ -299,12 +295,9 @@ def lines_intersect_outside(radians, indices):
|
||||
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)
|
||||
return sc
|
||||
|
||||
def quadruplets_to_intersections(*radians):
|
||||
def quadruplets_to_intersections(radians):
|
||||
sc = CircleScene(radians)
|
||||
quadruplets = it.combinations(range(len(radians)), 4)
|
||||
frame_time = 1.0
|
||||
@ -330,8 +323,7 @@ def quadruplets_to_intersections(*radians):
|
||||
))
|
||||
# sc.remove(arrows)
|
||||
|
||||
name = "QuadrupletsToIntersections%d"%len(radians)
|
||||
sc.write_to_movie(movie_prefix + name)
|
||||
return sc
|
||||
|
||||
def defining_graph(graph):
|
||||
gs = GraphScene(graph)
|
||||
@ -358,8 +350,7 @@ def defining_graph(graph):
|
||||
for m in zip(gs.mobjects, ngs.mobjects)
|
||||
], run_time = 7.0)
|
||||
|
||||
name = "DefiningGraph" + graph["name"]
|
||||
gs.write_to_movie(movie_prefix + name)
|
||||
return gs
|
||||
|
||||
def doubled_edges(graph):
|
||||
gs = GraphScene(graph)
|
||||
@ -386,8 +377,7 @@ def doubled_edges(graph):
|
||||
gs.dither()
|
||||
gs.remove(*outward_curved_lines)
|
||||
|
||||
name = "DoubledEdges" + graph["name"]
|
||||
gs.write_to_movie(movie_prefix + name)
|
||||
return gs
|
||||
|
||||
|
||||
def eulers_formula(graph):
|
||||
@ -435,10 +425,9 @@ def eulers_formula(graph):
|
||||
gs.add(new_form[symbol])
|
||||
gs.reset_background()
|
||||
|
||||
name = "EulersFormula" + graph["name"]
|
||||
gs.write_to_movie(movie_prefix + name)
|
||||
return gs
|
||||
|
||||
def apply_euler_to_moser(*radians):
|
||||
def cannot_directly_apply_euler_to_moser(radians):
|
||||
cs = CircleScene(radians)
|
||||
cs.remove(cs.n_equals)
|
||||
n_equals, intersection_count = tex_mobjects([
|
||||
@ -479,11 +468,9 @@ def apply_euler_to_moser(*radians):
|
||||
ShowCreation(dot) for dot in cs.intersection_dots
|
||||
])
|
||||
|
||||
return cs
|
||||
|
||||
name = "ApplyEulerToMoser%d"%len(radians)
|
||||
cs.write_to_movie(movie_prefix + name)
|
||||
|
||||
def show_moser_graph_lines(*radians):
|
||||
def show_moser_graph_lines(radians):
|
||||
radians = list(set(map(lambda x : x%(2*np.pi), radians)))
|
||||
radians.sort()
|
||||
|
||||
@ -509,38 +496,306 @@ def show_moser_graph_lines(*radians):
|
||||
])
|
||||
cs.count(cs.circle_pieces, color = "yellow",
|
||||
run_time = 2.0, num_offset = (0, 0, 0))
|
||||
name = "ShowMoserGraphLines%d"%len(radians)
|
||||
cs.write_to_movie(movie_prefix + name)
|
||||
return cs
|
||||
|
||||
def apply_euler_to_moser():
|
||||
#Boy is this an ugly implementation..., maybe you should
|
||||
#make a generic formula manipuating module
|
||||
sc = Scene()
|
||||
expressions = []
|
||||
for i in range(4):
|
||||
V_exp = "V" if i < 2 else r"\left(n + {n \choose 4} \right)"
|
||||
E_exp = "E" if i < 3 else r"\left({n \choose 2} + 2{n \choose 4}\right)"
|
||||
if i == 0:
|
||||
form = [V_exp, "-", E_exp, "+", "F", "=", "2"]
|
||||
else:
|
||||
form = ["F", "&=", E_exp, "-", V_exp, "+", "2"]
|
||||
if i == 3:
|
||||
form += [r"\\&=",r"{n \choose 4} + {n \choose 2}+", "2"]
|
||||
expressions.append(tex_mobjects(form))
|
||||
final_F_pos = (-SPACE_WIDTH+1, 0, 0)
|
||||
for exp in expressions:
|
||||
shift_val = final_F_pos - exp[0].get_center()
|
||||
for mob in exp:
|
||||
mob.shift(shift_val)
|
||||
#rearange first expression
|
||||
expressions[0] = [
|
||||
expressions[0][x]
|
||||
for x in [4, 5, 2, 1, 0, 3, 6] #TODO, Better way in general for rearrangements?
|
||||
]
|
||||
for i in range(3):
|
||||
sc.remove(*sc.mobjects)
|
||||
sc.add(*expressions[i])
|
||||
sc.dither()
|
||||
sc.animate(*[
|
||||
SemiCircleTransform(x, y, run_time = 2) if i == 0 else Transform(x, y)
|
||||
for x, y in zip(expressions[i], expressions[i+1])
|
||||
])
|
||||
sc.dither()
|
||||
equals, simplified_exp = expressions[-1][-3], expressions[-1][-2:]
|
||||
sc.animate(*[
|
||||
FadeIn(mob)
|
||||
for mob in [equals] + simplified_exp
|
||||
])
|
||||
sc.remove(*sc.mobjects)
|
||||
shift_val = -CompoundMobject(*simplified_exp).get_center()
|
||||
sc.animate(*[
|
||||
ApplyMethod((Mobject.shift, shift_val), mob)
|
||||
for mob in simplified_exp
|
||||
])
|
||||
sc.dither()
|
||||
one, two = tex_mobject("1"), simplified_exp[-1]
|
||||
one.center().shift(two.get_center())
|
||||
two.highlight()
|
||||
sc.dither()
|
||||
sc.animate(SemiCircleTransform(two, one))
|
||||
|
||||
return sc
|
||||
|
||||
def draw_pascals_triangle(nrows):
|
||||
pts = PascalsTriangleScene(nrows)
|
||||
pts.remove(*pts.mobjects)
|
||||
pts.add(pts.coords_to_mobs[0][0])
|
||||
for n in range(1, nrows):
|
||||
starts = [deepcopy(pts.coords_to_mobs[n-1][0])]
|
||||
starts += [
|
||||
CompoundMobject(
|
||||
pts.coords_to_mobs[n-1][k-1],
|
||||
pts.coords_to_mobs[n-1][k]
|
||||
)
|
||||
for k in range(1, n)
|
||||
]
|
||||
starts.append(deepcopy(pts.coords_to_mobs[n-1][n-1]))
|
||||
pts.animate(*[
|
||||
Transform(starts[i], pts.coords_to_mobs[n][i],
|
||||
run_time = 1.5, black_out_extra_points = False)
|
||||
for i in range(n+1)
|
||||
])
|
||||
|
||||
return pts
|
||||
|
||||
def pascal_rule_example(nrows):
|
||||
assert(nrows > 1)
|
||||
pts = PascalsTriangleScene(nrows)
|
||||
pts.dither()
|
||||
n = randint(2, nrows-1)
|
||||
k = randint(1, n-1)
|
||||
pts.coords_to_mobs[n][k].highlight("green")
|
||||
pts.dither()
|
||||
plus = tex_mobject("+").scale(0.5)
|
||||
nums_above = [pts.coords_to_mobs[n-1][k-1], pts.coords_to_mobs[n-1][k]]
|
||||
plus.center().shift(sum(map(Mobject.get_center, nums_above)) / 2)
|
||||
pts.add(plus)
|
||||
for mob in nums_above + [plus]:
|
||||
mob.highlight("yellow")
|
||||
pts.dither()
|
||||
|
||||
return pts
|
||||
|
||||
def pascals_triangle_with_n_choose_k(nrows):
|
||||
pts = PascalsTriangleScene(nrows)
|
||||
pts.generate_n_choose_k_mobs()
|
||||
mob_dicts = (pts.coords_to_mobs, pts.coords_to_n_choose_k)
|
||||
for i in [0, 1]:
|
||||
pts.dither()
|
||||
pts.remove(*pts.mobjects)
|
||||
pts.animate(*[
|
||||
SemiCircleTransform(
|
||||
deepcopy(mob_dicts[i][n][k]),
|
||||
mob_dicts[1-i][n][k]
|
||||
)
|
||||
for n, k in pts.coords
|
||||
])
|
||||
pts.remove(*pts.mobjects)
|
||||
pts.add(*[mob_dicts[1-i][n][k] for n, k in pts.coords])
|
||||
|
||||
return pts
|
||||
|
||||
def pascals_triangle_sum_rows(nrows):
|
||||
pts = PascalsTriangleScene(nrows)
|
||||
pluses = []
|
||||
powers_of_two = []
|
||||
equalses = []
|
||||
powers_of_two_symbols = []
|
||||
plus = tex_mobject("+")
|
||||
desired_plus_width = pts.coords_to_mobs[0][0].get_width()
|
||||
if plus.get_width() > desired_plus_width:
|
||||
plus.scale(desired_plus_width / plus.get_width())
|
||||
for n, k in pts.coords:
|
||||
if k == 0:
|
||||
continue
|
||||
new_plus = deepcopy(plus)
|
||||
new_plus.center().shift(pts.coords_to_mobs[n][k].get_center())
|
||||
new_plus.shift((-pts.cell_width / 2.0, 0, 0))
|
||||
pluses.append(new_plus)
|
||||
equals = tex_mobject("=")
|
||||
equals.scale(min(1, 0.7 * pts.cell_height / equals.get_width()))
|
||||
for n in range(nrows):
|
||||
new_equals = deepcopy(equals)
|
||||
pof2 = tex_mobjects(str(2**n))
|
||||
symbol = tex_mobject("2^{%d}"%n)
|
||||
desired_center = np.array((
|
||||
pts.diagram_width / 2.0,
|
||||
pts.coords_to_mobs[n][0].get_center()[1],
|
||||
0
|
||||
))
|
||||
new_equals.shift(desired_center - new_equals.get_center())
|
||||
desired_center += (1.5*equals.get_width(), 0, 0)
|
||||
scale_factor = pts.coords_to_mobs[0][0].get_height() / pof2.get_height()
|
||||
for mob in pof2, symbol:
|
||||
mob.center().scale(scale_factor).shift(desired_center)
|
||||
symbol.shift((0, 0.5*equals.get_height(), 0)) #FUAH! Stupid
|
||||
powers_of_two.append(pof2)
|
||||
equalses.append(new_equals)
|
||||
powers_of_two_symbols.append(symbol)
|
||||
pts.animate(FadeIn(CompoundMobject(*pluses)))
|
||||
run_time = 0.5
|
||||
to_remove = []
|
||||
for n in range(nrows):
|
||||
start = CompoundMobject(*[pts.coords_to_mobs[n][k] for k in range(n+1)])
|
||||
to_remove.append(start)
|
||||
pts.animate(
|
||||
Transform(start, powers_of_two[n]),
|
||||
FadeIn(equalses[n]),
|
||||
run_time = run_time
|
||||
)
|
||||
pts.dither()
|
||||
pts.remove(*to_remove)
|
||||
pts.add(*powers_of_two)
|
||||
for n in range(nrows):
|
||||
pts.animate(SemiCircleTransform(
|
||||
powers_of_two[n], powers_of_two_symbols[n],
|
||||
run_time = run_time
|
||||
))
|
||||
pts.remove(powers_of_two[n])
|
||||
pts.add(powers_of_two_symbols[n])
|
||||
|
||||
return pts
|
||||
|
||||
|
||||
##################################################
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
movie_prefix = "moser/"
|
||||
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)
|
||||
# apply_euler_to_moser(*radians)
|
||||
show_moser_graph_lines(*radians[:6])
|
||||
|
||||
|
||||
|
||||
n_pascal_rows = 7
|
||||
big_n_pascal_rows = 11
|
||||
def int_list_to_string(int_list):
|
||||
return "-".join(map(str, int_list))
|
||||
|
||||
function_tuples = [
|
||||
(
|
||||
count_lines,
|
||||
[
|
||||
(radians),
|
||||
(radians[:4]),
|
||||
],
|
||||
lambda args : str(len(args[0]))
|
||||
),
|
||||
(
|
||||
count_intersection_points,
|
||||
[
|
||||
(radians[:4]),
|
||||
(radians[:6]),
|
||||
(radians),
|
||||
],
|
||||
lambda args : str(len(args[0]))
|
||||
),
|
||||
(
|
||||
non_general_position,
|
||||
[()],
|
||||
None,
|
||||
),
|
||||
(
|
||||
line_corresponds_with_pair,
|
||||
[(radians, 2, 5)],
|
||||
lambda args : "%d-%d"%(args[1], args[2])
|
||||
),
|
||||
(
|
||||
illustrate_n_choose_k,
|
||||
[
|
||||
(7, 2),
|
||||
(6, 4),
|
||||
],
|
||||
int_list_to_string
|
||||
),
|
||||
(
|
||||
intersection_point_correspondances,
|
||||
[(radians, range(0, 7, 2))],
|
||||
lambda args : int_list_to_string(args[1])
|
||||
),
|
||||
(
|
||||
lines_intersect_outside,
|
||||
[(radians, [2, 4, 5, 6])],
|
||||
lambda args : int_list_to_string(args[1])
|
||||
),
|
||||
(
|
||||
quadruplets_to_intersections,
|
||||
[(radians[:6])],
|
||||
lambda args : str(len(args[0]))
|
||||
),
|
||||
(
|
||||
defining_graph,
|
||||
[(SAMPLE_GRAPH)],
|
||||
lambda args : args[0]["name"]
|
||||
),
|
||||
(
|
||||
doubled_edges,
|
||||
[(CUBE_GRAPH)],
|
||||
lambda args : args[0]["name"]
|
||||
),
|
||||
(
|
||||
eulers_formula,
|
||||
[
|
||||
(CUBE_GRAPH),
|
||||
(SAMPLE_GRAPH),
|
||||
(OCTOHEDRON_GRAPH),
|
||||
],
|
||||
lambda args : args[0]["name"],
|
||||
),
|
||||
(
|
||||
cannot_directly_apply_euler_to_moser,
|
||||
[(radians)],
|
||||
lambda args : str(len(args[0]))
|
||||
),
|
||||
(
|
||||
show_moser_graph_lines,
|
||||
[(radians[:6])],
|
||||
lambda args : str(len(args[0]))
|
||||
),
|
||||
(
|
||||
apply_euler_to_moser,
|
||||
(),
|
||||
None,
|
||||
),
|
||||
(
|
||||
draw_pascals_triangle,
|
||||
[(n_pascal_rows)],
|
||||
lambda args : str(args[0])
|
||||
),
|
||||
(
|
||||
pascal_rule_example,
|
||||
[(n_pascal_rows)],
|
||||
lambda args : str(args[0]),
|
||||
),
|
||||
(
|
||||
pascals_triangle_with_n_choose_k,
|
||||
[(n_pascal_rows)],
|
||||
lambda args : str(args[0]),
|
||||
),
|
||||
(
|
||||
pascals_triangle_sum_rows,
|
||||
[(n_pascal_rows)],
|
||||
lambda args : str(args[0])
|
||||
),
|
||||
(
|
||||
pascals_triangle_sum_rows,
|
||||
[(big_n_pascal_rows)],
|
||||
lambda args : str(args[0])
|
||||
),
|
||||
]
|
||||
|
||||
create_scene(sys.argv[1:], function_tuples, movie_prefix)
|
||||
|
||||
|
||||
|
||||
|
@ -4,6 +4,7 @@ import itertools as it
|
||||
|
||||
from constants import *
|
||||
from image_mobject import *
|
||||
from region import *
|
||||
from scene import Scene
|
||||
|
||||
RADIUS = SPACE_HEIGHT - 0.1
|
||||
@ -122,6 +123,68 @@ class GraphScene(Scene):
|
||||
regions[-1].complement()#Outer region painted outwardly...
|
||||
self.regions = regions
|
||||
|
||||
class PascalsTriangleScene(Scene):
|
||||
def __init__(self, nrows, *args, **kwargs):
|
||||
Scene.__init__(self, *args, **kwargs)
|
||||
diagram_height = 2*SPACE_HEIGHT - 1
|
||||
diagram_width = 1.5*SPACE_WIDTH
|
||||
cell_height = diagram_height / nrows
|
||||
cell_width = diagram_width / nrows
|
||||
portion_to_fill = 0.7
|
||||
bottom_left = np.array(
|
||||
(-cell_width * nrows / 2.0, -cell_height * nrows / 2.0, 0)
|
||||
)
|
||||
num_to_num_mob = {}
|
||||
coords_to_mobs = {}
|
||||
coords = [(n, k) for n in range(nrows) for k in range(n+1)]
|
||||
for n, k in coords:
|
||||
num = choose(n, k)
|
||||
center = bottom_left + (
|
||||
cell_width * (k+nrows/2.0 - n/2.0),
|
||||
cell_height * (nrows - n),
|
||||
0
|
||||
)
|
||||
if num not in num_to_num_mob:
|
||||
num_to_num_mob[num] = tex_mobject(str(num))
|
||||
num_mob = deepcopy(num_to_num_mob[num])
|
||||
scale_factor = min(
|
||||
1,
|
||||
portion_to_fill * cell_height / num_mob.get_height(),
|
||||
portion_to_fill * cell_width / num_mob.get_width(),
|
||||
)
|
||||
num_mob.center().scale(scale_factor).shift(center)
|
||||
if n not in coords_to_mobs:
|
||||
coords_to_mobs[n] = {}
|
||||
coords_to_mobs[n][k] = num_mob
|
||||
self.add(*[coords_to_mobs[n][k] for n, k in coords])
|
||||
#Add attributes
|
||||
self.nrows = nrows
|
||||
self.coords = coords
|
||||
self.diagram_height = diagram_height
|
||||
self.diagram_width = diagram_width
|
||||
self.cell_height = cell_height
|
||||
self.cell_width = cell_width
|
||||
self.portion_to_fill= portion_to_fill
|
||||
self.coords_to_mobs = coords_to_mobs
|
||||
|
||||
|
||||
def generate_n_choose_k_mobs(self):
|
||||
self.coords_to_n_choose_k = {}
|
||||
for n, k in self.coords:
|
||||
nck_mob = tex_mobject(r"{%d \choose %d}"%(n, k))
|
||||
scale_factor = min(
|
||||
1,
|
||||
self.portion_to_fill * self.cell_height / nck_mob.get_height(),
|
||||
self.portion_to_fill * self.cell_width / nck_mob.get_width(),
|
||||
)
|
||||
center = self.coords_to_mobs[n][k].get_center()
|
||||
nck_mob.center().scale(scale_factor).shift(center)
|
||||
if n not in self.coords_to_n_choose_k:
|
||||
self.coords_to_n_choose_k[n] = {}
|
||||
self.coords_to_n_choose_k[n][k] = nck_mob
|
||||
|
||||
|
||||
|
||||
##################################################
|
||||
|
||||
def choose(n, r):
|
||||
|
2
scene.py
2
scene.py
@ -62,6 +62,8 @@ class Scene(object):
|
||||
|
||||
def remove(self, *mobjects):
|
||||
for mobject in mobjects:
|
||||
if not isinstance(mobject, Mobject):
|
||||
raise Exception("Removing something which is not a mobject")
|
||||
while mobject in self.mobjects:
|
||||
self.mobjects.remove(mobject)
|
||||
|
||||
|
73
script_wrapper.py
Normal file
73
script_wrapper.py
Normal file
@ -0,0 +1,73 @@
|
||||
import sys
|
||||
import getopt
|
||||
import imp
|
||||
import itertools as it
|
||||
from helpers import initials, to_cammel_case
|
||||
|
||||
def print_help_message():
|
||||
print '<script name> -f <function name or initials>'
|
||||
|
||||
def get_scene_and_name(function_string, args_string,
|
||||
function_tuples):
|
||||
possible_func_args = []
|
||||
for func, args_list, args_to_string in function_tuples:
|
||||
if function_string in ("", func.__name__, initials(func.__name__)):
|
||||
for args in args_list:
|
||||
if not isinstance(args, tuple):
|
||||
args = (args,)
|
||||
if not args_to_string:
|
||||
args_to_string = lambda x : ""
|
||||
this_args_string = args_to_string(args)
|
||||
if args_string in ("", this_args_string):
|
||||
possible_func_args.append(
|
||||
(func, args, this_args_string)
|
||||
)
|
||||
if len(possible_func_args) == 0:
|
||||
print "No function_string arg_string pair \
|
||||
matching \"%s\" and \"%s\""%(function_string, args_string)
|
||||
sys.exit(0)
|
||||
elif len(possible_func_args) == 1:
|
||||
index = 0
|
||||
|
||||
else:
|
||||
print "Multiple functions/arg pairs satisfying this " + \
|
||||
"description, choose from the following list:"
|
||||
count = 0
|
||||
for func, args, args_string in possible_func_args:
|
||||
print "%d: %s%s"%(
|
||||
count,
|
||||
to_cammel_case(func.__name__),
|
||||
args_string
|
||||
)
|
||||
count += 1
|
||||
index = int(raw_input("Choose a number from above: "))
|
||||
function, args, args_string = possible_func_args[index]
|
||||
scene_name = to_cammel_case(function.__name__) + args_string
|
||||
print "Writing %s..."%scene_name
|
||||
scene = function(*args)
|
||||
return scene, scene_name
|
||||
|
||||
|
||||
def create_scene(sys_argv, function_tuples, movie_prefix = ""):
|
||||
try:
|
||||
opts, args = getopt.getopt(sys_argv, "ho:vf:v")#TODO, Learn about this
|
||||
except getopt.GetoptError as err:
|
||||
print str(err)
|
||||
sys.exit(2)
|
||||
function_string = ""
|
||||
args_extension = ""
|
||||
for opt, arg in opts:
|
||||
if opt == '-h':
|
||||
print_help_message()
|
||||
return
|
||||
elif opt == '-f':
|
||||
function_string = arg
|
||||
elif opt == "-a":
|
||||
args_extension = arg
|
||||
scene, name = get_scene_and_name(
|
||||
function_string,
|
||||
args_extension,
|
||||
function_tuples
|
||||
)
|
||||
print name
|
||||
|
Reference in New Issue
Block a user