mirror of
https://github.com/3b1b/manim.git
synced 2025-07-30 05:24:22 +08:00
True end of Music and Measure draft 1
This commit is contained in:
@ -98,10 +98,6 @@ class ImageMobject(Mobject):
|
||||
points *= 2 * SPACE_WIDTH / width
|
||||
self.add_points(points, rgbs = rgbs)
|
||||
|
||||
def should_buffer_points(self):
|
||||
# potentially changed in subclasses
|
||||
return False
|
||||
|
||||
class Face(ImageMobject):
|
||||
DEFAULT_CONFIG = {
|
||||
"mode" : "simple",
|
||||
|
@ -1,5 +1,6 @@
|
||||
import numpy as np
|
||||
import itertools as it
|
||||
import operator as op
|
||||
import os
|
||||
from PIL import Image
|
||||
from random import random
|
||||
@ -7,6 +8,7 @@ from copy import deepcopy
|
||||
from colour import Color
|
||||
import inspect
|
||||
|
||||
|
||||
from constants import *
|
||||
from helpers import *
|
||||
import displayer as disp
|
||||
@ -312,10 +314,6 @@ class Mobject(object):
|
||||
return color
|
||||
|
||||
### Stuff subclasses should deal with ###
|
||||
def should_buffer_points(self):
|
||||
# potentially changed in subclasses
|
||||
return GENERALLY_BUFFER_POINTS
|
||||
|
||||
def generate_points(self):
|
||||
#Typically implemented in subclass, unless purposefully left blank
|
||||
pass
|
||||
@ -371,6 +369,11 @@ class CompoundMobject(Mobject):
|
||||
for mobject in mobjects:
|
||||
self.original_mobs_num_points.append(mobject.points.shape[0])
|
||||
self.add_points(mobject.points, mobject.rgbs)
|
||||
self.should_buffer_points = reduce(
|
||||
op.and_,
|
||||
[m.should_buffer_points for m in mobjects],
|
||||
GENERALLY_BUFFER_POINTS
|
||||
)
|
||||
|
||||
def split(self):
|
||||
result = []
|
||||
|
@ -110,7 +110,10 @@ class Piano(ImageMobject):
|
||||
left = self.get_left()[0]
|
||||
keys = []
|
||||
for count in range(14):
|
||||
key = Mobject(color = "white")
|
||||
key = Mobject(
|
||||
color = "white",
|
||||
should_buffer_points = False
|
||||
)
|
||||
x0 = left + count*self.ivory_jump
|
||||
x1 = x0 + self.ivory_jump
|
||||
key.add_points(
|
||||
@ -380,7 +383,32 @@ class ChallengeOne(Scene):
|
||||
)
|
||||
for vib in vibrations:
|
||||
vib.set_run_time(3.0)
|
||||
self.play(bottom_vibration, top_vib_1)
|
||||
self.play(bottom_vibration, top_vib_1)
|
||||
|
||||
class JustByAnalyzingTheNumber(Scene):
|
||||
def construct(self):
|
||||
nums = [
|
||||
1.5,
|
||||
np.sqrt(2),
|
||||
2**(5/12.),
|
||||
4/3.,
|
||||
1.2020569031595942,
|
||||
]
|
||||
last = None
|
||||
r_equals = tex_mobject("r=").shift(2*LEFT)
|
||||
self.add(r_equals)
|
||||
for num in nums:
|
||||
mob = tex_mobject(str(num)).next_to(r_equals)
|
||||
mob.highlight()
|
||||
mob.sort_points()
|
||||
if last:
|
||||
self.play(DelayByOrder(Transform(last, mob, run_time = 0.5)))
|
||||
self.remove(last)
|
||||
self.add(mob)
|
||||
else:
|
||||
self.add(mob)
|
||||
self.dither()
|
||||
last = mob
|
||||
|
||||
class QuestionAndAnswer(Scene):
|
||||
def construct(self):
|
||||
@ -430,6 +458,7 @@ class PlaySimpleRatio(Scene):
|
||||
(Fraction(8, 5), "skyblue"),
|
||||
(Fraction(211, 198), "orange"),
|
||||
(Fraction(1093, 826), "red"),
|
||||
((np.exp(np.pi)-np.pi)/15, "#7e008e")
|
||||
]
|
||||
@staticmethod
|
||||
def args_to_string(fraction, color):
|
||||
@ -444,9 +473,18 @@ class PlaySimpleRatio(Scene):
|
||||
num_periods = fraction, run_time = 5.0,
|
||||
center = 2*UP, color = color
|
||||
)
|
||||
self.add(fraction_mobject(fraction).shift(0.5*UP))
|
||||
if isinstance(fraction, Fraction):
|
||||
mob = fraction_mobject(fraction).shift(0.5*UP)
|
||||
else:
|
||||
mob = tex_mobject("\\frac{e^\\pi - \\pi}{15} \\approx \\frac{4}{3}")
|
||||
mob.shift(0.5*UP)
|
||||
self.add(mob)
|
||||
self.play(string1, string2)
|
||||
|
||||
class MostRationalsSoundBad(Scene):
|
||||
def construct(self):
|
||||
self.add(text_mobject("Most rational numbers sound bad!"))
|
||||
|
||||
class FlashOnXProximity(Animation):
|
||||
def __init__(self, mobject, x_val, *close_mobjects, **kwargs):
|
||||
self.x_val = x_val
|
||||
@ -572,6 +610,26 @@ class IrrationalGang(Scene):
|
||||
self.play(BlinkPiCreature(randy))
|
||||
|
||||
|
||||
class ConstructPiano(Scene):
|
||||
def construct(self):
|
||||
piano = Piano()
|
||||
keys = piano.split()
|
||||
anims = []
|
||||
askew = deepcopy(keys[-1])
|
||||
keys[-1].rotate_in_place(np.pi/5)
|
||||
for key in keys:
|
||||
key.should_buffer_points = False
|
||||
key_copy = deepcopy(key).to_corner(DOWN+LEFT)
|
||||
key_copy.scale_in_place(0.25)
|
||||
key_copy.shift(1.8*random.random()*SPACE_WIDTH*RIGHT)
|
||||
key_copy.shift(1.8*random.random()*SPACE_HEIGHT*UP)
|
||||
key_copy.rotate(2*np.pi*random.random())
|
||||
anims.append(Transform(key_copy, key))
|
||||
self.play(*anims, run_time = 3.0)
|
||||
self.dither()
|
||||
self.play(Transform(anims[-1].mobject, askew))
|
||||
self.dither()
|
||||
|
||||
|
||||
class PianoTuning(Scene):
|
||||
def construct(self):
|
||||
@ -629,12 +687,22 @@ class PianoTuning(Scene):
|
||||
approximate_form = tex_mobject("\\approx"+str(2**(float(half_steps)/12)))
|
||||
approximate_form.scale(0.75)
|
||||
approximate_form.next_to(ratio)
|
||||
if interval == 5:
|
||||
num_den = (3, 2)
|
||||
elif interval == 4:
|
||||
num_den = (4, 3)
|
||||
should_be = text_mobject("Should be $\\frac{%d}{%d}$"%num_den)
|
||||
should_be.next_to(u_brace, DOWN)
|
||||
|
||||
self.play(ApplyMethod(low.highlight, colors[0]))
|
||||
self.play(
|
||||
ApplyMethod(high.highlight, colors[interval]),
|
||||
Transform(Point(u_brace.get_left()), u_brace),
|
||||
)
|
||||
self.dither()
|
||||
self.play(ShimmerIn(should_be))
|
||||
self.dither()
|
||||
self.remove(should_be)
|
||||
terms = product.split()
|
||||
for term, semicircle in zip(terms, semicircles):
|
||||
self.add(term, semicircle)
|
||||
@ -702,14 +770,15 @@ class PowersOfTwelfthRoot(Scene):
|
||||
class SupposeThereIsASavant(Scene):
|
||||
def construct(self):
|
||||
words = "Suppose there is a musical savant " + \
|
||||
"who find pleasure in all pairs of " + \
|
||||
"who finds pleasure in all pairs of " + \
|
||||
"notes whose frequencies have a rational ratio"
|
||||
words = text_mobject(words.split(" ")).split()
|
||||
words[4].highlight()
|
||||
words[5].highlight()
|
||||
for word in words:
|
||||
self.add(word)
|
||||
self.dither(0.2)
|
||||
words = words.split(" ")
|
||||
word_mobs = text_mobject(words).split()
|
||||
word_mobs[4].highlight()
|
||||
word_mobs[5].highlight()
|
||||
for word, word_mob in zip(words, word_mobs):
|
||||
self.add(word_mob)
|
||||
self.dither(0.1*len(word))
|
||||
|
||||
class AllValuesBetween1And2(NumberLineScene):
|
||||
def construct(self):
|
||||
@ -771,6 +840,10 @@ class AllValuesBetween1And2(NumberLineScene):
|
||||
self.dither(0.5)
|
||||
|
||||
|
||||
class ChallengeTwo(Scene):
|
||||
def construct(self):
|
||||
self.add(text_mobject("Challenge #2"))
|
||||
|
||||
class CoveringSetsWithOpenIntervals(IntervalScene):
|
||||
def construct(self):
|
||||
IntervalScene.construct(self)
|
||||
@ -804,12 +877,12 @@ class CoveringSetsWithOpenIntervals(IntervalScene):
|
||||
class DefineOpenInterval(IntervalScene):
|
||||
def construct(self):
|
||||
IntervalScene.construct(self)
|
||||
open_interval, line = self.add_open_interval(0.5, 0.05, run_time = 1.0)
|
||||
open_interval, line = self.add_open_interval(0.5, 0.75, run_time = 1.0)
|
||||
left, right = open_interval.get_left(), open_interval.get_right()
|
||||
a, less_than1, x, less_than2, b = \
|
||||
tex_mobject(["a", "<", "x", "<", "b"]).shift(UP).split()
|
||||
left_arrow = Arrow(a, left)
|
||||
right_arrow = Arrow(b, right)
|
||||
left_arrow = Arrow(a.get_corner(DOWN+LEFT), left)
|
||||
right_arrow = Arrow(b.get_corner(DOWN+RIGHT), right)
|
||||
|
||||
self.play(*[ShimmerIn(mob) for mob in a, less_than1, x])
|
||||
self.play(ShowCreation(left_arrow))
|
||||
@ -931,11 +1004,14 @@ class RationalsAreDense(IntervalScene):
|
||||
self.dither()
|
||||
|
||||
|
||||
class SurelyItsImpossible(Scene):
|
||||
def construct(self):
|
||||
self.add(text_mobject("Surely it's impossible!"))
|
||||
|
||||
class HowCanYouNotCoverEntireInterval(IntervalScene):
|
||||
def construct(self):
|
||||
IntervalScene.construct(self)
|
||||
words = text_mobject("""
|
||||
small_words = text_mobject("""
|
||||
In case you are wondering, it is indeed true
|
||||
that if you cover all real numbers between 0
|
||||
and 1 with a set of open intervals, the sum
|
||||
@ -944,7 +1020,14 @@ class HowCanYouNotCoverEntireInterval(IntervalScene):
|
||||
as obvious as it might seem, just try to prove
|
||||
it for yourself!
|
||||
""")
|
||||
words.scale(0.5).to_corner(UP+RIGHT)
|
||||
small_words.scale(0.5).to_corner(UP+RIGHT)
|
||||
big_words = text_mobject("""
|
||||
Covering all numbers from 0 to 1 \\emph{will}
|
||||
force the sum of the lengths of your intervals
|
||||
to be at least 1.
|
||||
""")
|
||||
big_words.next_to(self.number_line, DOWN, buff = 0.5)
|
||||
|
||||
ticks = self.add_fraction_ticks()
|
||||
left = self.number_line.number_to_point(0)
|
||||
right = self.number_line.number_to_point(1)
|
||||
@ -963,10 +1046,22 @@ class HowCanYouNotCoverEntireInterval(IntervalScene):
|
||||
Transform(tick, full_line)
|
||||
for tick in ticks.split()
|
||||
])
|
||||
self.play(ShimmerIn(big_words))
|
||||
self.dither()
|
||||
# self.play(DelayByOrder(FadeToColor(full_line, "red")))
|
||||
self.play(ShimmerIn(words))
|
||||
self.play(ShimmerIn(small_words))
|
||||
self.dither()
|
||||
|
||||
class PauseNow(Scene):
|
||||
def construct(self):
|
||||
top_words = text_mobject("Try for yourself!").scale(2).shift(3*UP)
|
||||
bot_words = text_mobject("""
|
||||
If you've never seen this before, you will get
|
||||
the most out of the solution and the intuitions
|
||||
I illustrate only after pulling out a pencil and
|
||||
paper and taking a wack at it yourself.
|
||||
""").next_to(top_words, DOWN, buff = 0.5)
|
||||
self.add(top_words, bot_words)
|
||||
|
||||
class StepsToSolution(IntervalScene):
|
||||
def construct(self):
|
||||
@ -975,12 +1070,14 @@ class StepsToSolution(IntervalScene):
|
||||
steps = map(text_mobject, [
|
||||
"Enumerate all rationals in (0, 1)",
|
||||
"Assign one interval to each rational",
|
||||
"Choose sum of the form $\\sum_{n=1}^\\infty a_n = 1$",
|
||||
"Choose sum of the form $\\mathlarger{\\sum}_{n=1}^\\infty a_n = 1$",
|
||||
"Pick any $\\epsilon$ such that $0 < \\epsilon < 1$",
|
||||
"Stretch the $n$th interval to have length $\\epsilon/2^n$",
|
||||
])
|
||||
for step in steps:
|
||||
step.shift(DOWN)
|
||||
for step in steps[2:]:
|
||||
step.shift(DOWN)
|
||||
for step, count in zip(steps, it.count()):
|
||||
self.add(step)
|
||||
self.dither()
|
||||
@ -1036,11 +1133,21 @@ class StepsToSolution(IntervalScene):
|
||||
def assign_intervals_to_rationals(self):
|
||||
anims = []
|
||||
for tick in self.ticks:
|
||||
interval = OpenInterval(tick.get_center(), self.spacing/2)
|
||||
interval = OpenInterval(tick.get_center(), self.spacing)
|
||||
interval.scale_in_place(0.5)
|
||||
squished = deepcopy(interval).stretch_to_fit_width(0)
|
||||
anims.append(Transform(squished, interval))
|
||||
self.play(*anims)
|
||||
self.show_frame()
|
||||
self.dither()
|
||||
to_remove = [self.number_line] + self.number_mobs
|
||||
self.play(*[
|
||||
ApplyMethod(mob.shift, 2*SPACE_WIDTH*RIGHT)
|
||||
for mob in to_remove
|
||||
])
|
||||
self.remove(*to_remove)
|
||||
self.dither()
|
||||
|
||||
self.intervals = [a.mobject for a in anims]
|
||||
kwargs = {
|
||||
"run_time" : 2.0,
|
||||
@ -1054,16 +1161,22 @@ class StepsToSolution(IntervalScene):
|
||||
|
||||
def add_terms(self):
|
||||
self.ones = []
|
||||
scale_val = 0.3
|
||||
scale_val = 0.6
|
||||
plus = None
|
||||
for count in range(1, 10):
|
||||
frac_bottom = tex_mobject("\\over %d"%(2**count))
|
||||
frac_bottom.scale(scale_val)
|
||||
frac_bottom.shift(0.5*UP + (-SPACE_WIDTH+self.spacing*count)*RIGHT)
|
||||
one = tex_mobject("1").scale(scale_val)
|
||||
one.next_to(frac_bottom, UP, buff = 0.1)
|
||||
self.ones.append(one)
|
||||
compound = CompoundMobject(frac_bottom, one)
|
||||
if plus:
|
||||
compound.next_to(plus)
|
||||
else:
|
||||
compound.to_edge(LEFT)
|
||||
plus = tex_mobject("+").scale(scale_val)
|
||||
plus.next_to(frac_bottom, buff = self.spacing/4)
|
||||
plus.next_to(compound)
|
||||
frac_bottom, one = compound.split()
|
||||
self.ones.append(one)
|
||||
self.add(frac_bottom, one, plus)
|
||||
self.dither(0.2)
|
||||
dots = tex_mobject("\\dots").scale(scale_val).next_to(plus)
|
||||
@ -1117,6 +1230,9 @@ class OurSumCanBeArbitrarilySmall(Scene):
|
||||
self.play(ShimmerIn(words[1].highlight()))
|
||||
self.dither()
|
||||
|
||||
class ProofDoesNotEqualIntuition(Scene):
|
||||
def construct(self):
|
||||
self.add(text_mobject("Proof $\\ne$ Intuition"))
|
||||
|
||||
class StillFeelsCounterintuitive(IntervalScene):
|
||||
def construct(self):
|
||||
@ -1134,6 +1250,9 @@ class StillFeelsCounterintuitive(IntervalScene):
|
||||
self.play(ShowCreation(ticks))
|
||||
self.dither()
|
||||
|
||||
class VisualIntuition(Scene):
|
||||
def construct(self):
|
||||
self.add(text_mobject("Visual Intuition:"))
|
||||
|
||||
class ZoomInOnSqrt2Over2(IntervalScene):
|
||||
def construct(self):
|
||||
@ -1192,19 +1311,31 @@ class ShiftSetupByOne(IntervalScene):
|
||||
self.add(epsilon_mob)
|
||||
fraction_ticks = self.add_fraction_ticks()
|
||||
self.remove(fraction_ticks)
|
||||
all_intervals, all_lines = self.cover_fractions(
|
||||
intervals, lines = self.cover_fractions(
|
||||
epsilon = 0.01,
|
||||
num_fractions = 150,
|
||||
run_time_per_interval = 0,
|
||||
)
|
||||
self.remove(*all_intervals+all_lines)
|
||||
self.remove(*intervals+lines)
|
||||
for interval, frac in zip(intervals, rationals()):
|
||||
interval.scale_in_place(2.0/frac.denominator)
|
||||
|
||||
intervals, lines = self.cover_fractions(epsilon = 0.01)
|
||||
|
||||
for interval in intervals[:10]:
|
||||
squished = deepcopy(interval).stretch_to_fit_width(0)
|
||||
self.play(Transform(squished, interval), run_time = 0.2)
|
||||
self.remove(squished)
|
||||
self.add(interval)
|
||||
for interval in intervals[10:50]:
|
||||
self.add(interval)
|
||||
self.dither(0.1)
|
||||
for interval in intervals[50:]:
|
||||
self.add(interval)
|
||||
self.dither()
|
||||
mobs_shifts = [
|
||||
(intervals+lines, UP),
|
||||
(intervals, UP),
|
||||
([self.number_line, new_interval], side_shift_val*LEFT),
|
||||
(intervals+lines, DOWN)
|
||||
(intervals, DOWN)
|
||||
]
|
||||
for mobs, shift_val in mobs_shifts:
|
||||
self.play(*[
|
||||
@ -1245,7 +1376,7 @@ class ShiftSetupByOne(IntervalScene):
|
||||
|
||||
self.add(words)
|
||||
self.dither()
|
||||
self.remove(*intervals+lines)
|
||||
self.remove(*intervals)
|
||||
self.add(answer1)
|
||||
self.play(ShowCreation(fraction_ticks, run_time = 5.0))
|
||||
self.add(answer2)
|
||||
@ -1259,16 +1390,14 @@ class ShiftSetupByOne(IntervalScene):
|
||||
words[1].highlight()
|
||||
words[3].highlight()
|
||||
self.add(*words)
|
||||
for interval, frac in zip(all_intervals, rationals()):
|
||||
interval.scale_in_place(2.0/frac.denominator)
|
||||
self.add(interval)
|
||||
self.dither(0.1)
|
||||
self.play(ShowCreation(
|
||||
CompoundMobject(*intervals),
|
||||
run_time = 5.0
|
||||
))
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
command_line_create_scene(MOVIE_PREFIX)
|
||||
|
||||
|
Reference in New Issue
Block a user