mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
starting main moser video
This commit is contained in:
parent
398b92406c
commit
d79db9d142
13 changed files with 225 additions and 46 deletions
BIN
Images/simple_face.png
Normal file
BIN
Images/simple_face.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
BIN
Images/speech_bubble.png
Normal file
BIN
Images/speech_bubble.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 63 KiB |
BIN
Images/thought_bubble.png
Normal file
BIN
Images/thought_bubble.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
BIN
Images/video_icon.png
Normal file
BIN
Images/video_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
|
@ -10,6 +10,7 @@ import inspect
|
|||
from images2gif import writeGif
|
||||
|
||||
from helpers import *
|
||||
from constants import *
|
||||
from mobject import Mobject
|
||||
|
||||
class Animation(object):
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import os
|
||||
|
||||
PRODUCTION_QUALITY = True
|
||||
GENERALLY_BUFF_POINTS = False
|
||||
GENERALLY_BUFF_POINTS = True
|
||||
|
||||
DEFAULT_POINT_DENSITY_2D = 25 #if PRODUCTION_QUALITY else 20
|
||||
DEFAULT_POINT_DENSITY_1D = 200 #if PRODUCTION_QUALITY else 50
|
||||
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
|
||||
#All in seconds
|
||||
DEFAULT_FRAME_DURATION = 0.04 #if PRODUCTION_QUALITY else 0.1
|
||||
DEFAULT_FRAME_DURATION = 0.04 if PRODUCTION_QUALITY else 0.1
|
||||
DEFAULT_ANIMATION_RUN_TIME = 3.0
|
||||
DEFAULT_TRANSFORM_RUN_TIME = 1.0
|
||||
DEFAULT_DITHER_TIME = 1.0
|
||||
|
|
|
@ -5,6 +5,8 @@ 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
|
||||
|
||||
|
@ -14,12 +16,12 @@ import numpy as np
|
|||
DARK_BLUE = "#236B8E"
|
||||
DARK_BROWN = "#8B4513"
|
||||
LIGHT_BROWN = "#CD853F"
|
||||
LOGO_RADIUS = 1.5
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
size = 1.5
|
||||
circle = Circle(color = 'skyblue').repeat(4).scale(size)
|
||||
sphere = Sphere(density = 100, color = DARK_BLUE).scale(size)
|
||||
circle = Circle(density = 100, color = 'skyblue').repeat(5).scale(LOGO_RADIUS)
|
||||
sphere = Sphere(density = 100, color = DARK_BLUE).scale(LOGO_RADIUS)
|
||||
sphere.rotate(-np.pi / 7, [1, 0, 0])
|
||||
sphere.rotate(-np.pi / 7)
|
||||
alpha = 0.3
|
||||
|
@ -27,20 +29,21 @@ if __name__ == '__main__':
|
|||
Mobject.interpolate(circle, sphere, iris, alpha)
|
||||
for mob, color in [(iris, LIGHT_BROWN), (circle, DARK_BROWN)]:
|
||||
mob.highlight(color, lambda (x, y, z) : x < 0 and y > 0)
|
||||
mob.highlight("black", lambda point: np.linalg.norm(point) < size/2)
|
||||
mob.highlight("black", lambda point: np.linalg.norm(point) < 0.55*LOGO_RADIUS)
|
||||
|
||||
#TODO, reimplement all of this.
|
||||
name = tex_mobject("3Blue1Brown").center()
|
||||
name = tex_mobject(r"\text{3Blue1Brown}").center()
|
||||
name.highlight("gray")
|
||||
# name.highlight(DARK_BROWN, lambda (x, y, z) : x < 0 and y > 0)
|
||||
name.shift((0, -2, 0))
|
||||
create_eye = Transform(
|
||||
sc = Scene()
|
||||
sc.animate(Transform(
|
||||
circle, iris,
|
||||
run_time = DEFAULT_ANIMATION_RUN_TIME,
|
||||
name = "LogoGeneration"
|
||||
).then(
|
||||
Animation(name, dither_time = 0)
|
||||
).drag_pixels()
|
||||
create_eye.write_to_movie()
|
||||
index = int(DEFAULT_ANIMATION_RUN_TIME / DEFAULT_ANIMATION_PAUSE_TIME)
|
||||
create_eye.frames[index].save(LOGO_PATH)
|
||||
run_time = DEFAULT_ANIMATION_RUN_TIME
|
||||
))
|
||||
sc.add(name)
|
||||
sc.dither()
|
||||
sc.frames = drag_pixels(sc.frames)
|
||||
sc.write_to_movie("LogoGeneration", end_dither_time = 0)
|
||||
|
||||
|
||||
# index = int(DEFAULT_ANIMATION_RUN_TIME / DEFAULT_ANIMATION_PAUSE_TIME)
|
||||
# create_eye.frames[index].save(LOGO_PATH)
|
||||
|
|
15
helpers.py
15
helpers.py
|
@ -4,6 +4,7 @@ from PIL import Image
|
|||
from colour import Color
|
||||
from random import random
|
||||
import string
|
||||
|
||||
from constants import *
|
||||
|
||||
def hash_args(args):
|
||||
|
@ -25,13 +26,13 @@ def to_cammel_case(name):
|
|||
]
|
||||
return "".join(parts)
|
||||
|
||||
def drag_pixels(images):
|
||||
curr = np.array(images[0])
|
||||
new_images = []
|
||||
for image in images:
|
||||
curr += (curr == 0) * np.array(image)
|
||||
new_images.append(Image.fromarray(curr))
|
||||
return new_images
|
||||
def drag_pixels(frames):
|
||||
curr = frames[0]
|
||||
new_frames = []
|
||||
for frame in frames:
|
||||
curr += (curr == 0) * np.array(frame)
|
||||
new_frames.append(np.array(curr))
|
||||
return new_frames
|
||||
|
||||
def invert_image(image):
|
||||
arr = np.array(image)
|
||||
|
|
|
@ -4,6 +4,7 @@ import os
|
|||
from PIL import Image
|
||||
from random import random
|
||||
from copy import deepcopy
|
||||
from colour import Color
|
||||
|
||||
from constants import *
|
||||
from helpers import *
|
||||
|
@ -264,6 +265,11 @@ class Vector(Arrow):
|
|||
class Dot(Mobject1D): #Use 1D density, even though 2D
|
||||
DEFAULT_COLOR = "white"
|
||||
def __init__(self, center = (0, 0, 0), radius = 0.05, *args, **kwargs):
|
||||
center = np.array(center)
|
||||
if center.size == 1:
|
||||
raise Exception("Center must have 2 or 3 coordinates!")
|
||||
elif center.size == 2:
|
||||
center = np.append(center, [0])
|
||||
self.center = center
|
||||
self.radius = radius
|
||||
Mobject1D.__init__(self, *args, **kwargs)
|
||||
|
|
|
@ -17,27 +17,15 @@ RADIUS = SPACE_HEIGHT - 0.1
|
|||
CIRCLE_DENSITY = DEFAULT_POINT_DENSITY_1D*RADIUS
|
||||
|
||||
|
||||
####
|
||||
def moser_function(n):
|
||||
return choose(n, 4) + choose(n, 2) + 1
|
||||
|
||||
def choose(n, r):
|
||||
if n < r: return 0
|
||||
r = min(r, n-r)
|
||||
if r == 0: return 1
|
||||
numer = reduce(op.mul, xrange(n, n-r, -1), 1)
|
||||
denom = reduce(op.mul, xrange(1, r+1), 1)
|
||||
return numer//denom
|
||||
####
|
||||
|
||||
|
||||
def logo_to_circle():
|
||||
from generate_logo import LIGHT_BROWN
|
||||
from generate_logo import DARK_BROWN, LOGO_RADIUS
|
||||
sc = Scene()
|
||||
small_circle = Circle(
|
||||
density = CIRCLE_DENSITY,
|
||||
color = 'skyblue'
|
||||
).highlight(LIGHT_BROWN, lambda (x, y, z) : x < 0 and y > 0)
|
||||
).scale(LOGO_RADIUS).highlight(
|
||||
DARK_BROWN, lambda (x, y, z) : x < 0 and y > 0
|
||||
)
|
||||
big_circle = Circle(density = CIRCLE_DENSITY).scale(RADIUS)
|
||||
sc.add(small_circle)
|
||||
sc.dither()
|
||||
|
@ -102,7 +90,7 @@ def summarize_pattern(*radians):
|
|||
new_lines = CompoundMobject(*[
|
||||
Line(points[x], points[y]) for y in xrange(x)
|
||||
])
|
||||
num = tex_mobject(str(moser_function(x + 1)))
|
||||
num = tex_mobject(str(moser_function(x + 1))).center()
|
||||
sc.animate(
|
||||
Transform(last_num, num) if last_num else ShowCreation(num),
|
||||
FadeIn(new_lines),
|
||||
|
@ -270,6 +258,7 @@ def next_few_videos(*radians):
|
|||
return sc
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
radians = [1, 3, 5, 2, 4, 6]
|
||||
more_radians = radians + [10, 13, 20, 17, 15, 21, 18.5]
|
||||
|
@ -282,13 +271,20 @@ if __name__ == '__main__':
|
|||
# connect_points(*radians).write_to_movie("moser/ConnectPoints")
|
||||
# response_invitation().write_to_movie("moser/ResponseInvitation")
|
||||
# different_points(radians, different_radians).write_to_movie("moser/DifferentPoints")
|
||||
next_few_videos(*radians).write_to_movie("moser/NextFewVideos")
|
||||
|
||||
# next_few_videos(*radians).write_to_movie("moser/NextFewVideos")
|
||||
# summarize_pattern(*different_radians).write_to_movie("moser/PatternWithDifferentPoints")
|
||||
|
||||
#Images
|
||||
# tex_mobject(r"""
|
||||
# \underbrace{1, 2, 4, 8, 16, 31, \dots}_\text{What?}
|
||||
# """).save_image("moser/NumberList")
|
||||
# """).save_image("moser/NumberList31")
|
||||
# tex_mobject("""
|
||||
# 1, 2, 4, 8, 16, 32, 63, \dots
|
||||
# """).save_image("moser/NumberList63")
|
||||
# tex_mobject("""
|
||||
# 1, 2, 4, 8, 15, \dots
|
||||
# """).save_image("moser/NumberList15")
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
137
moser/main.py
Normal file
137
moser/main.py
Normal file
|
@ -0,0 +1,137 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import numpy as np
|
||||
import itertools as it
|
||||
import operator as op
|
||||
from copy import deepcopy
|
||||
|
||||
|
||||
from animation import *
|
||||
from mobject import *
|
||||
from image_mobject import *
|
||||
from constants import *
|
||||
from region import *
|
||||
from scene import Scene
|
||||
|
||||
from moser_helpers import *
|
||||
|
||||
RADIUS = SPACE_HEIGHT - 0.1
|
||||
CIRCLE_DENSITY = DEFAULT_POINT_DENSITY_1D*RADIUS
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
##################################################
|
||||
|
||||
def count_lines(*radians):
|
||||
sc = CircleScene(radians)
|
||||
text = tex_mobject(r"\text{How Many Lines?}", size = r"\large")
|
||||
text_center = (sc.radius + 1, sc.radius -1, 0)
|
||||
text.scale(0.4).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)
|
||||
])
|
||||
return sc
|
||||
|
||||
|
||||
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 = CompoundMobject(*[
|
||||
Dot(point) for point in intersection_points
|
||||
])
|
||||
how_many = tex_mobject(r"""
|
||||
\text{How many}\\
|
||||
\text{intersection points?}
|
||||
""", size = r"\large")
|
||||
text_center = (sc.radius + 1, sc.radius -1, 0)
|
||||
how_many.scale(0.4).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(how_many)
|
||||
sc.animate(ShowCreation(intersection_dots))
|
||||
sc.add(intersection_dots)
|
||||
sc.animate(Transform(intersection_dots, new_dots))
|
||||
sc.add(tex_mobject(str(len(new_points))).center())
|
||||
return sc
|
||||
|
||||
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)
|
||||
])
|
||||
return sc1
|
||||
|
||||
|
||||
##################################################
|
||||
|
||||
if __name__ == '__main__':
|
||||
radians = np.arange(0, 6, 6.0/7)
|
||||
count_lines(*radians).write_to_movie("moser/CountLines")
|
||||
count_intersection_points(*radians).write_to_movie("moser/CountIntersectionPoints")
|
||||
non_general_position().write_to_movie("moser/NonGeneralPosition")
|
34
moser/moser_helpers.py
Normal file
34
moser/moser_helpers.py
Normal file
|
@ -0,0 +1,34 @@
|
|||
import numpy as np
|
||||
import operator as op
|
||||
import itertools as it
|
||||
|
||||
def choose(n, r):
|
||||
if n < r: return 0
|
||||
if r == 0: return 1
|
||||
denom = reduce(op.mul, xrange(1, r+1), 1)
|
||||
numer = reduce(op.mul, xrange(n, n-r, -1), 1)
|
||||
return numer//denom
|
||||
|
||||
def moser_function(n):
|
||||
return choose(n, 4) + choose(n, 2) + 1
|
||||
|
||||
def intersection(line1, line2):
|
||||
"""
|
||||
A "line" should come in the form [(x0, y0), (x1, y1)] for two
|
||||
points it runs through
|
||||
"""
|
||||
p0, p1, p2, p3 = map(
|
||||
lambda tup : np.array(tup[:2]),
|
||||
[line1[0], line1[1], line2[0], line2[1]]
|
||||
)
|
||||
p1, p2, p3 = map(lambda x : x - p0, [p1, p2, p3])
|
||||
transform = np.zeros((2, 2))
|
||||
transform[:,0], transform[:,1] = p1, p2
|
||||
if np.linalg.det(transform) == 0: return
|
||||
inv = np.linalg.inv(transform)
|
||||
new_p3 = np.dot(inv, p3.reshape((2, 1)))
|
||||
#Where does line connecting (0, 1) to new_p3 hit x axis
|
||||
x_intercept = new_p3[0] / (1 - new_p3[1])
|
||||
result = np.dot(transform, [[x_intercept], [0]])
|
||||
result = result.reshape((2,)) + p0
|
||||
return result
|
1
scene.py
1
scene.py
|
@ -36,6 +36,7 @@ class Scene(object):
|
|||
)
|
||||
self.background = self.original_background
|
||||
self.shape = self.background.shape[:2]
|
||||
#TODO, space shape
|
||||
self.name = name
|
||||
|
||||
def __str__(self):
|
||||
|
|
Loading…
Add table
Reference in a new issue