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!
|
#Fuck this is cool!
|
||||||
class TransformAnimations(Transform):
|
# class TransformAnimations(Transform):
|
||||||
def __init__(self, start_anim, end_anim,
|
# def __init__(self, start_anim, end_anim,
|
||||||
alpha_func = squish_alpha_func(high_inflection_0_to_1),
|
# alpha_func = squish_alpha_func(high_inflection_0_to_1),
|
||||||
**kwargs):
|
# **kwargs):
|
||||||
self.start_anim, self.end_anim = start_anim, end_anim
|
# self.start_anim, self.end_anim = start_anim, end_anim
|
||||||
Transform.__init__(
|
# Transform.__init__(
|
||||||
self,
|
# self,
|
||||||
start_anim.mobject,
|
# start_anim.mobject,
|
||||||
end_anim.mobject,
|
# end_anim.mobject,
|
||||||
run_time = max(start_anim.run_time, end_anim.run_time),
|
# run_time = max(start_anim.run_time, end_anim.run_time),
|
||||||
alpha_func = alpha_func,
|
# alpha_func = alpha_func,
|
||||||
**kwargs
|
# **kwargs
|
||||||
)
|
# )
|
||||||
#Rewire starting and ending mobjects
|
# #Rewire starting and ending mobjects
|
||||||
start_anim.mobject = self.starting_mobject
|
# start_anim.mobject = self.starting_mobject
|
||||||
end_anim.mobject = self.ending_mobject
|
# end_anim.mobject = self.ending_mobject
|
||||||
|
|
||||||
def update(self, alpha):
|
# def update(self, alpha):
|
||||||
self.start_anim.update(alpha)
|
# self.start_anim.update(alpha)
|
||||||
self.end_anim.update(alpha)
|
# self.end_anim.update(alpha)
|
||||||
Transform.update(self, alpha)
|
# Transform.update(self, alpha)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,19 +59,21 @@ class Transform(Animation):
|
|||||||
)
|
)
|
||||||
|
|
||||||
class SemiCircleTransform(Transform):
|
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):
|
def update_mobject(self, alpha):
|
||||||
sm, em = self.starting_mobject, self.ending_mobject
|
sm, em = self.starting_mobject, self.ending_mobject
|
||||||
midpoints = (sm.points + em.points) / 2
|
midpoints = (sm.points + em.points) / 2
|
||||||
angle = alpha * np.pi
|
angle = alpha * np.pi
|
||||||
rotation_matrix = np.matrix([
|
rot_matrix = rotation_matrix(angle, self.axis)[:2, :2]
|
||||||
[np.cos(angle), np.sin(angle), 0],
|
self.mobject.points[:,:2] = np.dot(
|
||||||
[-np.sin(angle), np.cos(angle), 0],
|
(sm.points - midpoints)[:,:2],
|
||||||
[0, 0, 1],
|
np.transpose(rot_matrix)
|
||||||
])
|
) + midpoints[:,:2]
|
||||||
self.mobject.points = np.dot(
|
self.mobject.points[:,2] = (1-alpha)*sm.points[:,2] + alpha*em.points[:,2]
|
||||||
sm.points - midpoints,
|
|
||||||
np.transpose(rotation_matrix)
|
|
||||||
) + midpoints
|
|
||||||
self.mobject.rgbs = (1-alpha)*sm.rgbs + alpha*em.rgbs
|
self.mobject.rgbs = (1-alpha)*sm.rgbs + alpha*em.rgbs
|
||||||
|
|
||||||
class FadeToColor(Transform):
|
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")
|
LOGO_PATH = os.path.join(IMAGE_DIR, "logo.png")
|
||||||
|
|
||||||
|
DARK_BLUE = "#236B8E"
|
||||||
|
DARK_BROWN = "#8B4513"
|
||||||
|
LIGHT_BROWN = "#CD853F"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,6 +34,8 @@ def paint_region(region, image_array = None, color = None):
|
|||||||
|
|
||||||
def paint_mobject(mobject, image_array = None):
|
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]
|
height = pixels.shape[0]
|
||||||
width = pixels.shape[1]
|
width = pixels.shape[1]
|
||||||
space_height = SPACE_HEIGHT
|
space_height = SPACE_HEIGHT
|
||||||
@ -48,9 +50,12 @@ def paint_mobject(mobject, image_array = None):
|
|||||||
points[:,1] *= -1
|
points[:,1] *= -1
|
||||||
#Map points to pixel space, then create pixel array first in terms
|
#Map points to pixel space, then create pixel array first in terms
|
||||||
#of its flattened version
|
#of its flattened version
|
||||||
|
try:
|
||||||
points += np.array(
|
points += np.array(
|
||||||
[space_width, space_height]*points.shape[0]
|
[space_width, space_height]*points.shape[0]
|
||||||
).reshape(points.shape)
|
).reshape(points.shape)
|
||||||
|
except:
|
||||||
|
print points.shape, mobject.points.shape
|
||||||
points *= np.array(
|
points *= np.array(
|
||||||
[width / (2.0 * space_width), height / (2.0 * space_height)]*\
|
[width / (2.0 * space_width), height / (2.0 * space_height)]*\
|
||||||
points.shape[0]
|
points.shape[0]
|
||||||
|
@ -3,3 +3,4 @@ from image_mobject import *
|
|||||||
from simple_mobjects import *
|
from simple_mobjects import *
|
||||||
from three_dimensional_mobjects import *
|
from three_dimensional_mobjects import *
|
||||||
from function_graphs 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):
|
def shift(self, vector):
|
||||||
cycle = it.cycle(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
|
self.points += v
|
||||||
return self
|
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):
|
def center(self):
|
||||||
self.shift(-self.get_center())
|
self.shift(-self.get_center())
|
||||||
return self
|
return self
|
||||||
@ -116,9 +129,6 @@ class Mobject(object):
|
|||||||
self.add_points(points, rgbs)
|
self.add_points(points, rgbs)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def get_num_points(self):
|
|
||||||
return self.points.shape[0]
|
|
||||||
|
|
||||||
def pose_at_angle(self):
|
def pose_at_angle(self):
|
||||||
self.rotate(np.pi / 7)
|
self.rotate(np.pi / 7)
|
||||||
self.rotate(np.pi / 7, [1, 0, 0])
|
self.rotate(np.pi / 7, [1, 0, 0])
|
||||||
@ -155,6 +165,10 @@ class Mobject(object):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
### Getters ###
|
### Getters ###
|
||||||
|
|
||||||
|
def get_num_points(self):
|
||||||
|
return self.points.shape[0]
|
||||||
|
|
||||||
def get_center(self):
|
def get_center(self):
|
||||||
return np.apply_along_axis(np.mean, 0, self.points)
|
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):
|
def __init__(self, start, end, density = DEFAULT_POINT_DENSITY_1D, *args, **kwargs):
|
||||||
self.start = np.array(start)
|
self.start = np.array(start)
|
||||||
self.end = np.array(end)
|
self.end = np.array(end)
|
||||||
density *= np.linalg.norm(self.start - self.end)
|
density *= self.get_length()
|
||||||
Mobject1D.__init__(self, density = density, *args, **kwargs)
|
Mobject1D.__init__(self, density = density, *args, **kwargs)
|
||||||
|
|
||||||
def generate_points(self):
|
def generate_points(self):
|
||||||
@ -92,6 +92,16 @@ class Line(Mobject1D):
|
|||||||
for t in np.arange(0, 1, self.epsilon)
|
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):
|
class CurvedLine(Line):
|
||||||
def generate_points(self):
|
def generate_points(self):
|
||||||
equidistant_point = rotate_vector(
|
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 = {
|
SAMPLE_GRAPH = {
|
||||||
"name" : "SampleGraph",
|
"name" : "SampleGraph",
|
||||||
# 4 2 3
|
# 4 2 3 8
|
||||||
# 0 1
|
# 0 1
|
||||||
#
|
# 7
|
||||||
# 5
|
# 5 6
|
||||||
"vertices" :[
|
"vertices" :[
|
||||||
( 0, 0, 0),
|
( 0, 0, 0),
|
||||||
( 2, 0, 0),
|
( 2, 0, 0),
|
||||||
( 1, 1, 0),
|
( 1, 2, 0),
|
||||||
( 3, 1, 0),
|
( 3, 2, 0),
|
||||||
(-1, 1, 0),
|
(-1, 2, 0),
|
||||||
(-2,-2, 0),
|
(-2,-2, 0),
|
||||||
|
( 2,-2, 0),
|
||||||
|
( 4,-1, 0),
|
||||||
|
( 6, 2, 0),
|
||||||
],
|
],
|
||||||
"edges" : [
|
"edges" : [
|
||||||
(0, 1),
|
(0, 1),
|
||||||
@ -61,6 +64,11 @@ SAMPLE_GRAPH = {
|
|||||||
(4, 5),
|
(4, 5),
|
||||||
(0, 5),
|
(0, 5),
|
||||||
(1, 5),
|
(1, 5),
|
||||||
|
(5, 6),
|
||||||
|
(6, 7),
|
||||||
|
(7, 1),
|
||||||
|
(7, 8),
|
||||||
|
(8, 3),
|
||||||
],
|
],
|
||||||
"region_cycles" : [
|
"region_cycles" : [
|
||||||
(0, 1, 2),
|
(0, 1, 2),
|
||||||
@ -68,7 +76,9 @@ SAMPLE_GRAPH = {
|
|||||||
(2, 4, 0),
|
(2, 4, 0),
|
||||||
(4, 5, 0),
|
(4, 5, 0),
|
||||||
(0, 5, 1),
|
(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 mobject import *
|
||||||
from animation import *
|
from animation import *
|
||||||
|
from region import *
|
||||||
from constants import *
|
from constants import *
|
||||||
from helpers import *
|
from helpers import *
|
||||||
|
|
||||||
@ -20,14 +21,16 @@ class GraphScene(Scene):
|
|||||||
return args[0]["name"]
|
return args[0]["name"]
|
||||||
|
|
||||||
def __init__(self, graph, *args, **kwargs):
|
def __init__(self, graph, *args, **kwargs):
|
||||||
Scene.__init__(self, *args, **kwargs)
|
|
||||||
#See CUBE_GRAPH above for format of graph
|
#See CUBE_GRAPH above for format of graph
|
||||||
self.graph = 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.vertices = self.dots = [Dot(p) for p in self.points]
|
||||||
self.edges = [
|
self.edges = [
|
||||||
Line(self.points[i], self.points[j])
|
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)
|
self.add(*self.dots + self.edges)
|
||||||
|
|
||||||
@ -45,6 +48,50 @@ class GraphScene(Scene):
|
|||||||
regions[-1].complement()#Outer region painted outwardly...
|
regions[-1].complement()#Outer region painted outwardly...
|
||||||
self.regions = regions
|
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
|
BIG_N_PASCAL_ROWS = 11
|
||||||
N_PASCAL_ROWS = 7
|
N_PASCAL_ROWS = 7
|
||||||
class PascalsTriangleScene(Scene):
|
class PascalsTriangleScene(Scene):
|
||||||
|
@ -63,7 +63,7 @@ def find_scene_class_and_args(scene_string, args_extension,
|
|||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
return possible_results[index]
|
return possible_results[index]
|
||||||
|
|
||||||
def command_line_create_scene(sys_argv, movie_prefix = ""):
|
def command_line_create_scene(movie_prefix = ""):
|
||||||
scene_classes = [
|
scene_classes = [
|
||||||
pair[1]
|
pair[1]
|
||||||
for pair in inspect.getmembers(
|
for pair in inspect.getmembers(
|
||||||
@ -74,7 +74,7 @@ def command_line_create_scene(sys_argv, movie_prefix = ""):
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
try:
|
try:
|
||||||
opts, args = getopt.getopt(sys_argv, "h:l:p")
|
opts, args = getopt.getopt(sys.argv[1:], 'hlps')
|
||||||
except getopt.GetoptError as err:
|
except getopt.GetoptError as err:
|
||||||
print str(err)
|
print str(err)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
@ -82,7 +82,7 @@ def command_line_create_scene(sys_argv, movie_prefix = ""):
|
|||||||
scene_string = ""
|
scene_string = ""
|
||||||
args_extension = ""
|
args_extension = ""
|
||||||
display_config = PRODUCTION_QUALITY_DISPLAY_CONFIG
|
display_config = PRODUCTION_QUALITY_DISPLAY_CONFIG
|
||||||
preview = False
|
action = "write"
|
||||||
|
|
||||||
for opt, arg in opts:
|
for opt, arg in opts:
|
||||||
if opt == '-h':
|
if opt == '-h':
|
||||||
@ -92,7 +92,9 @@ def command_line_create_scene(sys_argv, movie_prefix = ""):
|
|||||||
display_config = LOW_QUALITY_DISPLAY_CONFIG
|
display_config = LOW_QUALITY_DISPLAY_CONFIG
|
||||||
elif opt == '-p':
|
elif opt == '-p':
|
||||||
display_config = LOW_QUALITY_DISPLAY_CONFIG
|
display_config = LOW_QUALITY_DISPLAY_CONFIG
|
||||||
preview = True
|
action = "preview"
|
||||||
|
elif opt == '-s':
|
||||||
|
action = "show_frame"
|
||||||
if len(args) > 0:
|
if len(args) > 0:
|
||||||
scene_string = args[0]
|
scene_string = args[0]
|
||||||
if len(args) > 1:
|
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)
|
name = SceneClass.__name__ + SceneClass.args_to_string(*args)
|
||||||
print "Constructing %s..."%name
|
print "Constructing %s..."%name
|
||||||
scene = SceneClass(*args, display_config = display_config)
|
scene = SceneClass(*args, display_config = display_config)
|
||||||
if preview:
|
if action == "write":
|
||||||
scene.preview()
|
|
||||||
else:
|
|
||||||
scene.write_to_movie(movie_prefix + name)
|
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
|
#!/usr/bin/env python
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
from animation import *
|
from animation import *
|
||||||
from mobject import *
|
from mobject import *
|
||||||
from constants import *
|
from constants import *
|
||||||
from helpers import *
|
from helpers import *
|
||||||
from scene import *
|
from scene import *
|
||||||
from image_mobject import *
|
|
||||||
import itertools as it
|
import itertools as it
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
DARK_BLUE = "#236B8E"
|
|
||||||
DARK_BROWN = "#8B4513"
|
|
||||||
LIGHT_BROWN = "#CD853F"
|
|
||||||
LOGO_RADIUS = 1.5
|
LOGO_RADIUS = 1.5
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
circle = Circle(density = 100, color = 'skyblue').repeat(5).scale(LOGO_RADIUS)
|
circle = Circle(density = 100, color = 'skyblue').repeat(5).scale(LOGO_RADIUS)
|
||||||
sphere = Sphere(density = 50, color = DARK_BLUE).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 animation import *
|
||||||
from mobject import *
|
from mobject import *
|
||||||
from image_mobject import *
|
|
||||||
from constants import *
|
from constants import *
|
||||||
from region import *
|
from region import *
|
||||||
from scene import Scene
|
from scene import Scene
|
||||||
|
@ -11,14 +11,123 @@ import inspect
|
|||||||
|
|
||||||
from animation import *
|
from animation import *
|
||||||
from mobject import *
|
from mobject import *
|
||||||
from image_mobject import *
|
|
||||||
from constants import *
|
from constants import *
|
||||||
from region import *
|
from region import *
|
||||||
from scene import Scene
|
from scene import Scene, GraphScene, PascalsTriangleScene
|
||||||
from script_wrapper import command_line_create_scene
|
from script_wrapper import command_line_create_scene
|
||||||
|
|
||||||
from moser_helpers import *
|
RADIUS = SPACE_HEIGHT - 0.1
|
||||||
from graphs import *
|
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):
|
class CountSections(CircleScene):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -688,8 +797,14 @@ class EulersFormula(GraphScene):
|
|||||||
for d in self.dots
|
for d in self.dots
|
||||||
]
|
]
|
||||||
colored_edges = [
|
colored_edges = [
|
||||||
deepcopy(e).highlight("red")
|
CompoundMobject(
|
||||||
|
Line(midpoint, start),
|
||||||
|
Line(midpoint, end),
|
||||||
|
).highlight("red")
|
||||||
for e in self.edges
|
for e in self.edges
|
||||||
|
for start, end, midpoint in [
|
||||||
|
(e.start, e.end, (e.start + e.end)/2)
|
||||||
|
]
|
||||||
]
|
]
|
||||||
frame_time = 0.3
|
frame_time = 0.3
|
||||||
|
|
||||||
@ -1585,7 +1700,7 @@ class IntersectionChoppingExamples(Scene):
|
|||||||
##################################################
|
##################################################
|
||||||
|
|
||||||
if __name__ == "__main__":
|
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