mirror of
https://github.com/3b1b/manim.git
synced 2025-07-31 05:52:34 +08:00
Randolph and Mortimer
This commit is contained in:
@ -112,27 +112,27 @@ class Animation(object):
|
||||
|
||||
|
||||
#Fuck this is cool!
|
||||
class TransformAnimations(Transform):
|
||||
def __init__(self, start_anim, end_anim,
|
||||
alpha_func = squish_alpha_func(high_inflection_0_to_1),
|
||||
**kwargs):
|
||||
self.start_anim, self.end_anim = start_anim, end_anim
|
||||
Transform.__init__(
|
||||
self,
|
||||
start_anim.mobject,
|
||||
end_anim.mobject,
|
||||
run_time = max(start_anim.run_time, end_anim.run_time),
|
||||
alpha_func = alpha_func,
|
||||
**kwargs
|
||||
)
|
||||
#Rewire starting and ending mobjects
|
||||
start_anim.mobject = self.starting_mobject
|
||||
end_anim.mobject = self.ending_mobject
|
||||
# class TransformAnimations(Transform):
|
||||
# def __init__(self, start_anim, end_anim,
|
||||
# alpha_func = squish_alpha_func(high_inflection_0_to_1),
|
||||
# **kwargs):
|
||||
# self.start_anim, self.end_anim = start_anim, end_anim
|
||||
# Transform.__init__(
|
||||
# self,
|
||||
# start_anim.mobject,
|
||||
# end_anim.mobject,
|
||||
# run_time = max(start_anim.run_time, end_anim.run_time),
|
||||
# alpha_func = alpha_func,
|
||||
# **kwargs
|
||||
# )
|
||||
# #Rewire starting and ending mobjects
|
||||
# start_anim.mobject = self.starting_mobject
|
||||
# end_anim.mobject = self.ending_mobject
|
||||
|
||||
def update(self, alpha):
|
||||
self.start_anim.update(alpha)
|
||||
self.end_anim.update(alpha)
|
||||
Transform.update(self, alpha)
|
||||
# def update(self, alpha):
|
||||
# self.start_anim.update(alpha)
|
||||
# self.end_anim.update(alpha)
|
||||
# Transform.update(self, alpha)
|
||||
|
||||
|
||||
|
||||
|
@ -59,19 +59,21 @@ class Transform(Animation):
|
||||
)
|
||||
|
||||
class SemiCircleTransform(Transform):
|
||||
def __init__(self, mobject1, mobject2, counterclockwise = True,
|
||||
*args, **kwargs):
|
||||
Transform.__init__(self, mobject1, mobject2, *args, **kwargs)
|
||||
self.axis = (0, 0, 1) if counterclockwise else (0, 0, -1)
|
||||
|
||||
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
|
||||
rot_matrix = rotation_matrix(angle, self.axis)[:2, :2]
|
||||
self.mobject.points[:,:2] = np.dot(
|
||||
(sm.points - midpoints)[:,:2],
|
||||
np.transpose(rot_matrix)
|
||||
) + midpoints[:,:2]
|
||||
self.mobject.points[:,2] = (1-alpha)*sm.points[:,2] + alpha*em.points[:,2]
|
||||
self.mobject.rgbs = (1-alpha)*sm.rgbs + alpha*em.rgbs
|
||||
|
||||
class FadeToColor(Transform):
|
||||
|
@ -55,3 +55,9 @@ TEMPLATE_TEXT_FILE = os.path.join(TEX_DIR, "text_template.tex")
|
||||
|
||||
LOGO_PATH = os.path.join(IMAGE_DIR, "logo.png")
|
||||
|
||||
DARK_BLUE = "#236B8E"
|
||||
DARK_BROWN = "#8B4513"
|
||||
LIGHT_BROWN = "#CD853F"
|
||||
|
||||
|
||||
|
||||
|
13
displayer.py
13
displayer.py
@ -33,7 +33,9 @@ def paint_region(region, image_array = None, color = None):
|
||||
return pixels
|
||||
|
||||
def paint_mobject(mobject, image_array = None):
|
||||
pixels = get_pixels(image_array)
|
||||
pixels = get_pixels(image_array)
|
||||
if mobject.get_num_points() == 0:
|
||||
return pixels
|
||||
height = pixels.shape[0]
|
||||
width = pixels.shape[1]
|
||||
space_height = SPACE_HEIGHT
|
||||
@ -48,9 +50,12 @@ def paint_mobject(mobject, image_array = None):
|
||||
points[:,1] *= -1
|
||||
#Map points to pixel space, then create pixel array first in terms
|
||||
#of its flattened version
|
||||
points += np.array(
|
||||
[space_width, space_height]*points.shape[0]
|
||||
).reshape(points.shape)
|
||||
try:
|
||||
points += np.array(
|
||||
[space_width, space_height]*points.shape[0]
|
||||
).reshape(points.shape)
|
||||
except:
|
||||
print points.shape, mobject.points.shape
|
||||
points *= np.array(
|
||||
[width / (2.0 * space_width), height / (2.0 * space_height)]*\
|
||||
points.shape[0]
|
||||
|
@ -3,3 +3,4 @@ from image_mobject import *
|
||||
from simple_mobjects import *
|
||||
from three_dimensional_mobjects import *
|
||||
from function_graphs import *
|
||||
from creatures import *
|
87
mobject/creatures.py
Normal file
87
mobject/creatures.py
Normal file
@ -0,0 +1,87 @@
|
||||
import numpy as np
|
||||
import itertools as it
|
||||
import os
|
||||
|
||||
from image_mobject import *
|
||||
from mobject import *
|
||||
from simple_mobjects import *
|
||||
|
||||
|
||||
class PiCreature(CompoundMobject):
|
||||
DEFAULT_COLOR = "blue"
|
||||
def __init__(self, color = DEFAULT_COLOR, **kwargs):
|
||||
scale_val = 0.5
|
||||
mouth_to_eyes_distance = 0.25
|
||||
part_names = [
|
||||
'arm',
|
||||
'body',
|
||||
'left_eye',
|
||||
'right_eye',
|
||||
'left_leg',
|
||||
'right_leg',
|
||||
'mouth',
|
||||
]
|
||||
white_part_names = ['left_eye', 'right_eye', 'mouth']
|
||||
directory = os.path.join(IMAGE_DIR, "PiCreature")
|
||||
|
||||
self.parts = []
|
||||
self.white_parts = []
|
||||
for part_name in part_names:
|
||||
path = os.path.join(directory, "pi_creature_"+part_name)
|
||||
path += ".png"
|
||||
mob = ImageMobject(path)
|
||||
mob.scale(scale_val)
|
||||
if part_name in white_part_names:
|
||||
self.white_parts.append(mob)
|
||||
else:
|
||||
mob.highlight(color)
|
||||
setattr(self, part_name, mob)
|
||||
self.parts.append(mob)
|
||||
self.mouth.center().shift(
|
||||
self.left_eye.get_center()/2 +
|
||||
self.right_eye.get_center()/2 -
|
||||
(0, mouth_to_eyes_distance, 0)
|
||||
)
|
||||
self.reload_parts()
|
||||
|
||||
def reload_parts(self):
|
||||
CompoundMobject.__init__(self, *self.parts)
|
||||
return self
|
||||
|
||||
def highlight(self, color):
|
||||
for part in set(self.parts).difference(self.white_parts):
|
||||
part.highlight(color)
|
||||
return self.reload_parts()
|
||||
|
||||
def give_frown(self):
|
||||
center = self.mouth.get_center()
|
||||
self.mouth.center()
|
||||
self.mouth.apply_function(lambda (x, y, z) : (x, -y, z))
|
||||
self.mouth.shift(center)
|
||||
return self.reload_parts()
|
||||
|
||||
def give_straight_face(self):
|
||||
center = self.mouth.get_center()
|
||||
self.mouth.center()
|
||||
new_mouth = tex_mobject("-").scale(0.5)
|
||||
new_mouth.center().shift(self.mouth.get_center())
|
||||
new_mouth.shift(center)
|
||||
self.parts[self.parts.index(self.mouth)] = new_mouth
|
||||
self.white_parts[self.white_parts.index(self.mouth)] = new_mouth
|
||||
self.mouth = new_mouth
|
||||
return self.reload_parts()
|
||||
|
||||
class Randolph(PiCreature):
|
||||
pass #Nothing more than an alternative name
|
||||
|
||||
class Mortimer(PiCreature):
|
||||
def __init__(self, *args, **kwargs):
|
||||
PiCreature.__init__(self, *args, **kwargs)
|
||||
self.highlight(DARK_BROWN)
|
||||
self.give_straight_face()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -88,10 +88,23 @@ class Mobject(object):
|
||||
|
||||
def shift(self, vector):
|
||||
cycle = it.cycle(vector)
|
||||
v = np.array([cycle.next() for x in range(self.points.size)]).reshape(self.points.shape)
|
||||
v = np.array([
|
||||
cycle.next()
|
||||
for x in range(self.points.size)
|
||||
]).reshape(self.points.shape)
|
||||
self.points += v
|
||||
return self
|
||||
|
||||
def wag(self, wag_direction = [0, 1, 0], wag_axis = [-1, 0, 0]):
|
||||
alphas = np.dot(self.points, np.transpose(wag_axis))
|
||||
alphas -= min(alphas)
|
||||
alphas /= max(alphas)
|
||||
self.points += np.dot(
|
||||
alphas.reshape((len(alphas), 1)),
|
||||
np.array(wag_direction).reshape((1, 3))
|
||||
)
|
||||
return self
|
||||
|
||||
def center(self):
|
||||
self.shift(-self.get_center())
|
||||
return self
|
||||
@ -116,9 +129,6 @@ class Mobject(object):
|
||||
self.add_points(points, rgbs)
|
||||
return self
|
||||
|
||||
def get_num_points(self):
|
||||
return self.points.shape[0]
|
||||
|
||||
def pose_at_angle(self):
|
||||
self.rotate(np.pi / 7)
|
||||
self.rotate(np.pi / 7, [1, 0, 0])
|
||||
@ -155,6 +165,10 @@ class Mobject(object):
|
||||
return self
|
||||
|
||||
### Getters ###
|
||||
|
||||
def get_num_points(self):
|
||||
return self.points.shape[0]
|
||||
|
||||
def get_center(self):
|
||||
return np.apply_along_axis(np.mean, 0, self.points)
|
||||
|
||||
|
@ -83,7 +83,7 @@ class Line(Mobject1D):
|
||||
def __init__(self, start, end, density = DEFAULT_POINT_DENSITY_1D, *args, **kwargs):
|
||||
self.start = np.array(start)
|
||||
self.end = np.array(end)
|
||||
density *= np.linalg.norm(self.start - self.end)
|
||||
density *= self.get_length()
|
||||
Mobject1D.__init__(self, density = density, *args, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
@ -92,6 +92,16 @@ class Line(Mobject1D):
|
||||
for t in np.arange(0, 1, self.epsilon)
|
||||
])
|
||||
|
||||
def get_length(self):
|
||||
return np.linalg.norm(self.start - self.end)
|
||||
|
||||
def get_slope(self):
|
||||
rise, run = [
|
||||
float(self.end[i] - self.start[i])
|
||||
for i in [1, 0]
|
||||
]
|
||||
return rise/run
|
||||
|
||||
class CurvedLine(Line):
|
||||
def generate_points(self):
|
||||
equidistant_point = rotate_vector(
|
||||
|
@ -1,82 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import numpy as np
|
||||
import itertools as it
|
||||
import operator as op
|
||||
from copy import deepcopy
|
||||
from random import random, randint
|
||||
import sys
|
||||
import inspect
|
||||
|
||||
|
||||
from animation import *
|
||||
from mobject import *
|
||||
from image_mobject import *
|
||||
from constants import *
|
||||
from region import *
|
||||
from scene import Scene
|
||||
from script_wrapper import command_line_create_scene
|
||||
|
||||
from moser_helpers import *
|
||||
from graphs import *
|
||||
|
||||
if __name__ == "__main__":
|
||||
prefix = "moser_images/"
|
||||
# cs_outer = CircleScene(RADIANS[:6])
|
||||
# cs_outer.highlight_region(
|
||||
# Region(lambda x, y : x**2 + y**2 > RADIUS**2)
|
||||
# )
|
||||
|
||||
# cs_graph = CircleScene(RADIANS)
|
||||
# cs_graph.generate_intersection_dots()
|
||||
# cs_graph.add(*cs_graph.intersection_dots)
|
||||
# cs_graph.chop_lines_at_intersection_points()
|
||||
# cs_graph.chop_circle_at_points()
|
||||
# for line in cs_graph.lines:
|
||||
# line.scale_in_place(0.5)
|
||||
# for piece in cs_graph.smaller_circle_pieces:
|
||||
# piece.highlight("yellow")
|
||||
# cs_graph.remove(*cs_graph.circle_pieces)
|
||||
# cs_graph.add(*cs_graph.smaller_circle_pieces)
|
||||
|
||||
savable_things = [
|
||||
# (Mobject(), "Blackness")
|
||||
# (tex_mobject(r"V-E+F=2"), "EulersFormula"),
|
||||
# (PascalsTriangleScene(N_PASCAL_ROWS), "PascalsTriangle"),
|
||||
# (tex_mobject(r"1, 2, 4, 8, 16, 31, \dots"), "FalsePattern"),
|
||||
# (
|
||||
# tex_mobject(r"""
|
||||
# \underbrace{1, 2, 4, 16, 31, 57, 99, 163, 256, 386, \dots}_{
|
||||
# \text{What is this pattern?}
|
||||
# }
|
||||
# """),
|
||||
# "WhatIsThisPattern"
|
||||
# ),
|
||||
# (tex_mobject(r"n \choose k"), "NChooseK"),
|
||||
# (GraphScene(SAMPLE_GRAPH), "SampleGraph"),
|
||||
# (text_mobject("You don't even want me to draw this..."), "DontWantToDraw"),
|
||||
# (tex_mobject(r"{100 \choose 2} = \frac{100 \cdot 99}{2} = 4950"), "100Choose2"),
|
||||
# (text_mobject("What? You actually want me to draw it? Okay..."), "ReallyDontWant"),
|
||||
# (text_mobject(r"There! You happy? \\ It's just one big blue blob."), "YouHappy"),
|
||||
# (
|
||||
# tex_mobject(
|
||||
# r"{100 \choose 4} = \frac{(100)(99)(98)(97)}{(1)(2)(3)(4)} = 3,921,225"
|
||||
# ),
|
||||
# "100Choose4"
|
||||
# ),
|
||||
# (text_mobject("Euler's Characteristic Formula"), "EF_Words"),
|
||||
# (cs_outer, "OuterRegion"),
|
||||
# (text_mobject("Pause and see if you can remember on your own!"), "Recap")
|
||||
# (CircleScene([2*np.pi*random() for x in range(100)]), "CircleScene100")
|
||||
# (text_mobject(r"""
|
||||
# \textbf{Eul$\cdot$er's} (\text{oil}\textschwa\text{rz}), \emph{adj}:
|
||||
# \begin{enumerate}
|
||||
# \item Beautiful
|
||||
# \item Demonstrating an unexpected logical aesthetic, especially in the context of mathematics.
|
||||
# \end{enumerate}
|
||||
# """), "EulersDefinition"),
|
||||
# (cs_graph, "SuitableGraph"),
|
||||
]
|
||||
for thing, name in savable_things:
|
||||
thing.save_image(prefix + name)
|
||||
|
@ -1,128 +0,0 @@
|
||||
import numpy as np
|
||||
import itertools as it
|
||||
|
||||
from constants import *
|
||||
from helpers import *
|
||||
from image_mobject import *
|
||||
from region import *
|
||||
from scene import Scene
|
||||
|
||||
from graphs import *
|
||||
|
||||
RADIUS = SPACE_HEIGHT - 0.1
|
||||
CIRCLE_DENSITY = DEFAULT_POINT_DENSITY_1D*RADIUS
|
||||
MOVIE_PREFIX = "moser/"
|
||||
RADIANS = np.arange(0, 6, 6.0/7)
|
||||
MORE_RADIANS = np.append(RADIANS, RADIANS + 0.5)
|
||||
N_PASCAL_ROWS = 7
|
||||
BIG_N_PASCAL_ROWS = 11
|
||||
|
||||
############################################
|
||||
|
||||
class CircleScene(Scene):
|
||||
args_list = [
|
||||
(RADIANS[:x],)
|
||||
for x in range(1, len(RADIANS)+1)
|
||||
]
|
||||
@staticmethod
|
||||
def args_to_string(*args):
|
||||
return str(len(args[0])) #Length of radians
|
||||
|
||||
def __init__(self, radians, radius = RADIUS, *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.n_equals = tex_mobject(
|
||||
"n=%d"%len(radians),
|
||||
).shift((-SPACE_WIDTH+1, SPACE_HEIGHT-1.5, 0))
|
||||
self.add(self.circle, self.n_equals, *self.dots + self.lines)
|
||||
|
||||
|
||||
def generate_intersection_dots(self):
|
||||
"""
|
||||
Generates and adds attributes intersection_points and
|
||||
intersection_dots, but does not yet add them to the scene
|
||||
"""
|
||||
self.intersection_points = [
|
||||
intersection((p[0], p[2]), (p[1], p[3]))
|
||||
for p in it.combinations(self.points, 4)
|
||||
]
|
||||
self.intersection_dots = [
|
||||
Dot(point) for point in self.intersection_points
|
||||
]
|
||||
|
||||
def chop_lines_at_intersection_points(self):
|
||||
if not hasattr(self, "intersection_dots"):
|
||||
self.generate_intersection_dots()
|
||||
self.remove(*self.lines)
|
||||
self.lines = []
|
||||
for point_pair in it.combinations(self.points, 2):
|
||||
int_points = filter(
|
||||
lambda p : is_on_line(p, *point_pair),
|
||||
self.intersection_points
|
||||
)
|
||||
points = list(point_pair) + int_points
|
||||
points = map(lambda p : (p[0], p[1], 0), points)
|
||||
points.sort(cmp = lambda x,y: cmp(x[0], y[0]))
|
||||
self.lines += [
|
||||
Line(points[i], points[i+1])
|
||||
for i in range(len(points)-1)
|
||||
]
|
||||
self.add(*self.lines)
|
||||
|
||||
def chop_circle_at_points(self):
|
||||
self.remove(self.circle)
|
||||
self.circle_pieces = []
|
||||
self.smaller_circle_pieces = []
|
||||
for i in range(len(self.points)):
|
||||
pp = self.points[i], self.points[(i+1)%len(self.points)]
|
||||
transform = np.array([
|
||||
[pp[0][0], pp[1][0], 0],
|
||||
[pp[0][1], pp[1][1], 0],
|
||||
[0, 0, 1]
|
||||
])
|
||||
circle = deepcopy(self.circle)
|
||||
smaller_circle = deepcopy(self.circle)
|
||||
for c in circle, smaller_circle:
|
||||
c.points = np.dot(
|
||||
c.points,
|
||||
np.transpose(np.linalg.inv(transform))
|
||||
)
|
||||
c.filter_out(
|
||||
lambda p : p[0] < 0 or p[1] < 0
|
||||
)
|
||||
if c == smaller_circle:
|
||||
c.filter_out(
|
||||
lambda p : p[0] > 4*p[1] or p[1] > 4*p[0]
|
||||
)
|
||||
c.points = np.dot(
|
||||
c.points,
|
||||
np.transpose(transform)
|
||||
)
|
||||
self.circle_pieces.append(circle)
|
||||
self.smaller_circle_pieces.append(smaller_circle)
|
||||
self.add(*self.circle_pieces)
|
||||
|
||||
def generate_regions(self):
|
||||
self.regions = plane_partition_from_points(*self.points)
|
||||
interior = Region(lambda x, y : x**2 + y**2 < self.radius**2)
|
||||
map(lambda r : r.intersect(interior), self.regions)
|
||||
self.exterior = interior.complement()
|
||||
|
||||
|
||||
##################################################
|
||||
|
||||
def int_list_to_string(int_list):
|
||||
return "-".join(map(str, int_list))
|
||||
|
||||
def moser_function(n):
|
||||
return choose(n, 4) + choose(n, 2) + 1
|
||||
|
||||
|
||||
|
@ -38,17 +38,20 @@ CUBE_GRAPH = {
|
||||
|
||||
SAMPLE_GRAPH = {
|
||||
"name" : "SampleGraph",
|
||||
# 4 2 3
|
||||
# 4 2 3 8
|
||||
# 0 1
|
||||
#
|
||||
# 5
|
||||
# 7
|
||||
# 5 6
|
||||
"vertices" :[
|
||||
( 0, 0, 0),
|
||||
( 2, 0, 0),
|
||||
( 1, 1, 0),
|
||||
( 3, 1, 0),
|
||||
(-1, 1, 0),
|
||||
( 1, 2, 0),
|
||||
( 3, 2, 0),
|
||||
(-1, 2, 0),
|
||||
(-2,-2, 0),
|
||||
( 2,-2, 0),
|
||||
( 4,-1, 0),
|
||||
( 6, 2, 0),
|
||||
],
|
||||
"edges" : [
|
||||
(0, 1),
|
||||
@ -61,6 +64,11 @@ SAMPLE_GRAPH = {
|
||||
(4, 5),
|
||||
(0, 5),
|
||||
(1, 5),
|
||||
(5, 6),
|
||||
(6, 7),
|
||||
(7, 1),
|
||||
(7, 8),
|
||||
(8, 3),
|
||||
],
|
||||
"region_cycles" : [
|
||||
(0, 1, 2),
|
||||
@ -68,7 +76,9 @@ SAMPLE_GRAPH = {
|
||||
(2, 4, 0),
|
||||
(4, 5, 0),
|
||||
(0, 5, 1),
|
||||
(4, 5, 1, 3),
|
||||
(1, 5, 6, 7),
|
||||
(1, 7, 8, 3),
|
||||
(4, 5, 6, 7, 8),
|
||||
]
|
||||
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ from graphs import *
|
||||
|
||||
from mobject import *
|
||||
from animation import *
|
||||
from region import *
|
||||
from constants import *
|
||||
from helpers import *
|
||||
|
||||
@ -20,14 +21,16 @@ class GraphScene(Scene):
|
||||
return args[0]["name"]
|
||||
|
||||
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"])
|
||||
Scene.__init__(self, *args, **kwargs)
|
||||
|
||||
def construct(self):
|
||||
self.points = map(np.array, self.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"]
|
||||
for i, j in self.graph["edges"]
|
||||
]
|
||||
self.add(*self.dots + self.edges)
|
||||
|
||||
@ -45,6 +48,50 @@ class GraphScene(Scene):
|
||||
regions[-1].complement()#Outer region painted outwardly...
|
||||
self.regions = regions
|
||||
|
||||
def generate_spanning_tree(self):
|
||||
pass
|
||||
|
||||
def draw_vertices(self):
|
||||
self.clear()
|
||||
self.animate(ShowCreation(CompoundMobject(*self.vertices)))
|
||||
|
||||
def draw_edges(self):
|
||||
self.animate(*[
|
||||
ShowCreation(edge, run_time = 1.0)
|
||||
for edge in self.edges
|
||||
])
|
||||
|
||||
def replace_vertices_with(self, mobject):
|
||||
mobject.center()
|
||||
diameter = max(mobject.get_height(), mobject.get_width())
|
||||
self.animate(*[
|
||||
SemiCircleTransform(
|
||||
vertex,
|
||||
deepcopy(mobject).shift(vertex.get_center())
|
||||
)
|
||||
for vertex in self.vertices
|
||||
] + [
|
||||
ApplyMethod(
|
||||
edge.scale_in_place,
|
||||
(edge.get_length() - diameter) / edge.get_length()
|
||||
)
|
||||
for edge in self.edges
|
||||
])
|
||||
|
||||
def annotate_edges(self, mobject):
|
||||
angles = map(np.arctan, map(Line.get_slope, self.edges))
|
||||
self.edge_annotations = [
|
||||
deepcopy(mobject).rotate(angle).shift(edge.get_center())
|
||||
for angle, edge in zip(angles, self.edges)
|
||||
]
|
||||
self.animate(*[
|
||||
FadeIn(ann)
|
||||
for ann in self.edge_annotations
|
||||
])
|
||||
|
||||
|
||||
|
||||
|
||||
BIG_N_PASCAL_ROWS = 11
|
||||
N_PASCAL_ROWS = 7
|
||||
class PascalsTriangleScene(Scene):
|
||||
|
@ -63,7 +63,7 @@ def find_scene_class_and_args(scene_string, args_extension,
|
||||
sys.exit(0)
|
||||
return possible_results[index]
|
||||
|
||||
def command_line_create_scene(sys_argv, movie_prefix = ""):
|
||||
def command_line_create_scene(movie_prefix = ""):
|
||||
scene_classes = [
|
||||
pair[1]
|
||||
for pair in inspect.getmembers(
|
||||
@ -74,7 +74,7 @@ def command_line_create_scene(sys_argv, movie_prefix = ""):
|
||||
)
|
||||
]
|
||||
try:
|
||||
opts, args = getopt.getopt(sys_argv, "h:l:p")
|
||||
opts, args = getopt.getopt(sys.argv[1:], 'hlps')
|
||||
except getopt.GetoptError as err:
|
||||
print str(err)
|
||||
sys.exit(2)
|
||||
@ -82,7 +82,7 @@ def command_line_create_scene(sys_argv, movie_prefix = ""):
|
||||
scene_string = ""
|
||||
args_extension = ""
|
||||
display_config = PRODUCTION_QUALITY_DISPLAY_CONFIG
|
||||
preview = False
|
||||
action = "write"
|
||||
|
||||
for opt, arg in opts:
|
||||
if opt == '-h':
|
||||
@ -92,7 +92,9 @@ def command_line_create_scene(sys_argv, movie_prefix = ""):
|
||||
display_config = LOW_QUALITY_DISPLAY_CONFIG
|
||||
elif opt == '-p':
|
||||
display_config = LOW_QUALITY_DISPLAY_CONFIG
|
||||
preview = True
|
||||
action = "preview"
|
||||
elif opt == '-s':
|
||||
action = "show_frame"
|
||||
if len(args) > 0:
|
||||
scene_string = args[0]
|
||||
if len(args) > 1:
|
||||
@ -105,10 +107,14 @@ def command_line_create_scene(sys_argv, movie_prefix = ""):
|
||||
name = SceneClass.__name__ + SceneClass.args_to_string(*args)
|
||||
print "Constructing %s..."%name
|
||||
scene = SceneClass(*args, display_config = display_config)
|
||||
if preview:
|
||||
scene.preview()
|
||||
else:
|
||||
if action == "write":
|
||||
scene.write_to_movie(movie_prefix + name)
|
||||
elif action == "preview":
|
||||
scene.preview()
|
||||
elif action == "show_frame":
|
||||
scene.show_frame()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
48
scripts/ecf_graph_scenes.py
Normal file
48
scripts/ecf_graph_scenes.py
Normal file
@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import numpy as np
|
||||
import itertools as it
|
||||
from copy import deepcopy
|
||||
import sys
|
||||
|
||||
|
||||
from animation import *
|
||||
from mobject import *
|
||||
from constants import *
|
||||
from region import *
|
||||
from scene import Scene, GraphScene
|
||||
from moser_main import EulersFormula
|
||||
from script_wrapper import command_line_create_scene
|
||||
|
||||
MOVIE_PREFIX = "ecf_graph_scenes/"
|
||||
|
||||
class IntroduceGraphs(GraphScene):
|
||||
def construct(self):
|
||||
GraphScene.construct(self)
|
||||
self.draw_vertices()
|
||||
self.draw_edges()
|
||||
self.dither()
|
||||
self.clear()
|
||||
self.add(*self.edges)
|
||||
self.replace_vertices_with(SimpleFace().scale(0.4))
|
||||
friends = text_mobject("Friends").scale(0.5)
|
||||
self.annotate_edges(friends.shift((0, friends.get_height()/2, 0)))
|
||||
self.animate(*[
|
||||
SemiCircleTransform(vertex, Dot(point))
|
||||
for vertex, point in zip(self.vertices, self.points)
|
||||
]+[
|
||||
Transform(ann, line)
|
||||
for ann, line in zip(
|
||||
self.edge_annotations,
|
||||
self.edges
|
||||
)
|
||||
])
|
||||
self.dither()
|
||||
|
||||
class PlanarGraphDefinition(Scene):
|
||||
def construct(self):
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
command_line_create_scene(MOVIE_PREFIX)
|
@ -1,24 +1,19 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
|
||||
from animation import *
|
||||
from mobject import *
|
||||
from constants import *
|
||||
from helpers import *
|
||||
from scene import *
|
||||
from image_mobject import *
|
||||
import itertools as it
|
||||
import os
|
||||
|
||||
|
||||
import numpy as np
|
||||
|
||||
DARK_BLUE = "#236B8E"
|
||||
DARK_BROWN = "#8B4513"
|
||||
LIGHT_BROWN = "#CD853F"
|
||||
LOGO_RADIUS = 1.5
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
circle = Circle(density = 100, color = 'skyblue').repeat(5).scale(LOGO_RADIUS)
|
||||
sphere = Sphere(density = 50, color = DARK_BLUE).scale(LOGO_RADIUS)
|
@ -8,7 +8,6 @@ from copy import deepcopy
|
||||
|
||||
from animation import *
|
||||
from mobject import *
|
||||
from image_mobject import *
|
||||
from constants import *
|
||||
from region import *
|
||||
from scene import Scene
|
||||
|
@ -11,14 +11,123 @@ import inspect
|
||||
|
||||
from animation import *
|
||||
from mobject import *
|
||||
from image_mobject import *
|
||||
from constants import *
|
||||
from region import *
|
||||
from scene import Scene
|
||||
from scene import Scene, GraphScene, PascalsTriangleScene
|
||||
from script_wrapper import command_line_create_scene
|
||||
|
||||
from moser_helpers import *
|
||||
from graphs import *
|
||||
RADIUS = SPACE_HEIGHT - 0.1
|
||||
CIRCLE_DENSITY = DEFAULT_POINT_DENSITY_1D*RADIUS
|
||||
MOVIE_PREFIX = "moser/"
|
||||
RADIANS = np.arange(0, 6, 6.0/7)
|
||||
MORE_RADIANS = np.append(RADIANS, RADIANS + 0.5)
|
||||
N_PASCAL_ROWS = 7
|
||||
BIG_N_PASCAL_ROWS = 11
|
||||
|
||||
def int_list_to_string(int_list):
|
||||
return "-".join(map(str, int_list))
|
||||
|
||||
def moser_function(n):
|
||||
return choose(n, 4) + choose(n, 2) + 1
|
||||
|
||||
###########################################
|
||||
|
||||
class CircleScene(Scene):
|
||||
args_list = [
|
||||
(RADIANS[:x],)
|
||||
for x in range(1, len(RADIANS)+1)
|
||||
]
|
||||
@staticmethod
|
||||
def args_to_string(*args):
|
||||
return str(len(args[0])) #Length of radians
|
||||
|
||||
def __init__(self, radians, radius = RADIUS, *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.n_equals = tex_mobject(
|
||||
"n=%d"%len(radians),
|
||||
).shift((-SPACE_WIDTH+1, SPACE_HEIGHT-1.5, 0))
|
||||
self.add(self.circle, self.n_equals, *self.dots + self.lines)
|
||||
|
||||
|
||||
def generate_intersection_dots(self):
|
||||
"""
|
||||
Generates and adds attributes intersection_points and
|
||||
intersection_dots, but does not yet add them to the scene
|
||||
"""
|
||||
self.intersection_points = [
|
||||
intersection((p[0], p[2]), (p[1], p[3]))
|
||||
for p in it.combinations(self.points, 4)
|
||||
]
|
||||
self.intersection_dots = [
|
||||
Dot(point) for point in self.intersection_points
|
||||
]
|
||||
|
||||
def chop_lines_at_intersection_points(self):
|
||||
if not hasattr(self, "intersection_dots"):
|
||||
self.generate_intersection_dots()
|
||||
self.remove(*self.lines)
|
||||
self.lines = []
|
||||
for point_pair in it.combinations(self.points, 2):
|
||||
int_points = filter(
|
||||
lambda p : is_on_line(p, *point_pair),
|
||||
self.intersection_points
|
||||
)
|
||||
points = list(point_pair) + int_points
|
||||
points = map(lambda p : (p[0], p[1], 0), points)
|
||||
points.sort(cmp = lambda x,y: cmp(x[0], y[0]))
|
||||
self.lines += [
|
||||
Line(points[i], points[i+1])
|
||||
for i in range(len(points)-1)
|
||||
]
|
||||
self.add(*self.lines)
|
||||
|
||||
def chop_circle_at_points(self):
|
||||
self.remove(self.circle)
|
||||
self.circle_pieces = []
|
||||
self.smaller_circle_pieces = []
|
||||
for i in range(len(self.points)):
|
||||
pp = self.points[i], self.points[(i+1)%len(self.points)]
|
||||
transform = np.array([
|
||||
[pp[0][0], pp[1][0], 0],
|
||||
[pp[0][1], pp[1][1], 0],
|
||||
[0, 0, 1]
|
||||
])
|
||||
circle = deepcopy(self.circle)
|
||||
smaller_circle = deepcopy(self.circle)
|
||||
for c in circle, smaller_circle:
|
||||
c.points = np.dot(
|
||||
c.points,
|
||||
np.transpose(np.linalg.inv(transform))
|
||||
)
|
||||
c.filter_out(
|
||||
lambda p : p[0] < 0 or p[1] < 0
|
||||
)
|
||||
if c == smaller_circle:
|
||||
c.filter_out(
|
||||
lambda p : p[0] > 4*p[1] or p[1] > 4*p[0]
|
||||
)
|
||||
c.points = np.dot(
|
||||
c.points,
|
||||
np.transpose(transform)
|
||||
)
|
||||
self.circle_pieces.append(circle)
|
||||
self.smaller_circle_pieces.append(smaller_circle)
|
||||
self.add(*self.circle_pieces)
|
||||
|
||||
def generate_regions(self):
|
||||
self.regions = plane_partition_from_points(*self.points)
|
||||
interior = Region(lambda x, y : x**2 + y**2 < self.radius**2)
|
||||
map(lambda r : r.intersect(interior), self.regions)
|
||||
self.exterior = interior.complement()
|
||||
|
||||
|
||||
class CountSections(CircleScene):
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -688,8 +797,14 @@ class EulersFormula(GraphScene):
|
||||
for d in self.dots
|
||||
]
|
||||
colored_edges = [
|
||||
deepcopy(e).highlight("red")
|
||||
CompoundMobject(
|
||||
Line(midpoint, start),
|
||||
Line(midpoint, end),
|
||||
).highlight("red")
|
||||
for e in self.edges
|
||||
for start, end, midpoint in [
|
||||
(e.start, e.end, (e.start + e.end)/2)
|
||||
]
|
||||
]
|
||||
frame_time = 0.3
|
||||
|
||||
@ -1585,7 +1700,7 @@ class IntersectionChoppingExamples(Scene):
|
||||
##################################################
|
||||
|
||||
if __name__ == "__main__":
|
||||
command_line_create_scene(sys.argv[1:], MOVIE_PREFIX)
|
||||
command_line_create_scene(MOVIE_PREFIX)
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user