Files
manim/active_projects/eop/chapter1/brick_row_scene.py
2018-05-03 20:34:37 +02:00

691 lines
20 KiB
Python

from big_ol_pile_of_manim_imports import *
from active_projects.eop.reusable_imports import *
class BrickRowScene(PiCreatureScene):
def split_tallies(self, direction = DOWN):
# Split all tally symbols at once and move the copies
# either horizontally on top of the brick row
# or diagonally into the bricks
self.tallies_copy = self.tallies.copy()
self.add_foreground_mobject(self.tallies_copy)
tally_targets_left = [
rect.get_center() + 0.25 * rect.get_width() * LEFT
for rect in self.row.rects
]
tally_targets_right = [
rect.get_center() + 0.25 * rect.get_width() * RIGHT
for rect in self.row.rects
]
if np.all(direction == LEFT) or np.all(direction == RIGHT):
tally_y_pos = self.tallies[0].anchor[1]
for target in tally_targets_left:
target[1] = tally_y_pos
for target in tally_targets_right:
target[1] = tally_y_pos
for (i, tally) in enumerate(self.tallies):
target_left = tally_targets_left[i]
new_tally_left = TallyStack(tally.nb_heads + 1, tally.nb_tails)
new_tally_left.move_anchor_to(target_left)
v = target_left - tally.anchor
self.play(
tally.move_anchor_to, target_left,
)
tally.anchor = target_left
self.play(Transform(tally, new_tally_left))
tally_copy = self.tallies_copy[i]
target_right = tally_targets_right[i]
new_tally_right = TallyStack(tally.nb_heads, tally.nb_tails + 1)
new_tally_right.move_anchor_to(target_right)
v = target_right - tally_copy.anchor
self.play(tally_copy.move_anchor_to, target_right)
tally_copy.anchor = target_right
self.play(Transform(tally_copy, new_tally_right))
tally_copy.nb_heads = new_tally_right.nb_heads
tally_copy.nb_tails = new_tally_right.nb_tails
tally.nb_heads = new_tally_left.nb_heads
tally.nb_tails = new_tally_left.nb_tails
def tally_split_animations(self, direction = DOWN):
# Just creates the animations and returns them
# Execution can be timed afterwards
# Returns two lists: first all those going left, then those to the right
self.tallies_copy = self.tallies.copy()
self.add_foreground_mobject(self.tallies_copy)
tally_targets_left = [
rect.get_center() + 0.25 * rect.get_width() * LEFT
for rect in self.row.rects
]
tally_targets_right = [
rect.get_center() + 0.25 * rect.get_width() * RIGHT
for rect in self.row.rects
]
if np.all(direction == LEFT) or np.all(direction == RIGHT):
tally_y_pos = self.tallies[0].anchor[1]
for target in tally_targets_left:
target[1] = tally_y_pos
for target in tally_targets_right:
target[1] = tally_y_pos
anims1 = []
for (i, tally) in enumerate(self.tallies):
new_tally_left = TallyStack(tally.nb_heads + 1, tally.nb_tails)
target_left = tally_targets_left[i]
new_tally_left.move_anchor_to(target_left)
anims1.append(Transform(tally, new_tally_left))
tally.anchor = target_left
tally.nb_heads = new_tally_left.nb_heads
tally.nb_tails = new_tally_left.nb_tails
anims2 = []
for (i, tally) in enumerate(self.tallies_copy):
new_tally_right = TallyStack(tally.nb_heads, tally.nb_tails + 1)
target_right = tally_targets_right[i]
new_tally_right.move_anchor_to(target_right)
anims2.append(Transform(tally, new_tally_right))
tally.anchor = target_right
tally.nb_heads = new_tally_right.nb_heads
tally.nb_tails = new_tally_right.nb_tails
return anims1, anims2
def split_tallies_at_once(self, direction = DOWN):
anims1, anims2 = self.tally_split_animations(direction = direction)
self.play(*(anims1 + anims2))
def split_tallies_in_two_steps(self, direction = DOWN):
# First all those to the left, then those to the right
anims1, anims2 = self.tally_split_animations(direction = direction)
self.play(*anims1)
self.wait(0.3)
self.play(*anims2)
def merge_rects_by_subdiv(self):
half_merged_row = self.row.copy()
half_merged_row.subdiv_level += 1
half_merged_row.generate_points()
half_merged_row.move_to(self.row)
self.play(FadeIn(half_merged_row))
self.remove(self.row)
self.row = half_merged_row
def merge_tallies(self, direction = UP):
r = self.row.subdiv_level
tally_targets = [
rect.get_center()
for rect in self.row.get_rects_for_level(r)
]
if np.all(direction == LEFT) or np.all(direction == RIGHT):
y_pos = self.row.get_center()[1] + 1.2 * 0.5 * self.row.get_height()
for target in tally_targets:
target[1] = y_pos
anims = []
for (tally, target) in zip(self.tallies[1:], tally_targets[1:-1]):
anims.append(tally.move_anchor_to)
anims.append(target)
for (tally, target) in zip(self.tallies_copy[:-1], tally_targets[1:-1]):
anims.append(tally.move_anchor_to)
anims.append(target)
self.play(*anims)
# update anchors
for (tally, target) in zip(self.tallies[1:], tally_targets[1:-1]):
tally.anchor = target
for (tally, target) in zip(self.tallies_copy[:-1], tally_targets[1:-1]):
tally.anchor = target
self.remove(self.tallies_copy)
self.tallies.add(self.tallies_copy[-1])
def merge_rects_by_coloring(self):
merged_row = self.row.copy()
merged_row.coloring_level += 1
merged_row.generate_points()
merged_row.move_to(self.row)
self.play(FadeIn(merged_row))
self.remove(self.row)
self.row = merged_row
def move_tallies_on_top(self):
self.play(
self.tallies.shift, 1.2 * 0.5 * self.row.height * UP
)
for tally in self.tallies:
tally.anchor += 1.2 * 0.5 * self.row.height * UP
def create_pi_creature(self):
randy = CoinFlippingPiCreature(color = MAROON_E)
return randy
def construct(self):
randy = self.get_primary_pi_creature()
randy = randy.scale(0.5).move_to(3*DOWN + 6*LEFT)
#self.add(randy)
self.row = BrickRow(0, height = 2, width = 10)
self.wait()
self.play(FadeIn(self.row))
self.wait()
# move in all kinds of sequences
coin_seqs = VGroup()
for i in range(20):
n = np.random.randint(1,10)
seq = [np.random.choice(["H", "T"]) for j in range(n)]
coin_seq = CoinSequence(seq).scale(1.5)
loc = np.random.uniform(low = -10, high = 10) * RIGHT
loc += np.random.uniform(low = -6, high = 6) * UP
coin_seq.move_to(loc)
coin_seq.target = coin_seq.copy().scale(0.3).move_to(0.4 * loc)
coin_seq.target.fade(1)
coin_seqs.add(coin_seq)
self.play(
LaggedStart(
Succession, coin_seqs, lambda m: (FadeIn(m, run_time = 0.1), MoveToTarget(m)),
run_time = 5,
lag_ratio = 0.5
)
)
self.play(FlipCoin(randy) )
self.play(SplitRectsInBrickWall(self.row))
self.merge_rects_by_subdiv()
self.merge_rects_by_coloring()
#
# put tallies on top
single_flip_labels = VGroup(UprightHeads(), UprightTails())
for (label, rect) in zip(single_flip_labels, self.row.rects):
label.next_to(rect, UP)
self.play(FadeIn(label))
self.wait()
self.wait()
# # # # # # # #
# SECOND FLIP #
# # # # # # # #
self.play(FlipCoin(randy))
self.wait()
self.play(
SplitRectsInBrickWall(self.row)
)
self.wait()
# split sequences
single_flip_labels_copy = single_flip_labels.copy()
self.add(single_flip_labels_copy)
v = self.row.get_outcome_centers_for_level(2)[0] - single_flip_labels[0].get_center()
self.play(
single_flip_labels.shift, v
)
new_heads = VGroup(UprightHeads(), UprightHeads())
for i in range(2):
new_heads[i].move_to(single_flip_labels[i])
new_heads[i].shift(COIN_SEQUENCE_SPACING * DOWN)
self.play(FadeIn(new_heads))
v = self.row.get_outcome_centers_for_level(2)[-1] - single_flip_labels_copy[-1].get_center()
self.play(
single_flip_labels_copy.shift, v
)
new_tails = VGroup(UprightTails(), UprightTails())
for i in range(2):
new_tails[i].move_to(single_flip_labels_copy[i])
new_tails[i].shift(COIN_SEQUENCE_SPACING * DOWN)
self.play(FadeIn(new_tails))
decimal_tallies = VGroup()
# introduce notion of tallies
for (i, rect) in enumerate(self.row.get_rects_for_level(2)):
tally = DecimalTally(2-i, i)
tally.next_to(rect, UP)
decimal_tallies.add(tally)
self.play(FadeIn(tally))
self.wait()
self.add_foreground_mobject(single_flip_labels)
self.add_foreground_mobject(new_heads)
self.add_foreground_mobject(single_flip_labels_copy)
self.add_foreground_mobject(new_tails)
# show individual outcomes
outcomes = self.row.get_outcome_rects_for_level(2, with_labels = False)
self.play(
LaggedStart(FadeIn, outcomes)
)
self.wait()
self.play(
LaggedStart(FadeOut, outcomes),
)
self.wait()
self.play(
FadeOut(single_flip_labels),
FadeOut(new_heads),
FadeOut(single_flip_labels_copy),
FadeOut(new_tails)
)
self.wait()
self.tallies = VGroup()
for (i, rect) in enumerate(self.row.get_rects_for_level(2)):
tally = TallyStack(2-i, i, show_decimals = False)
tally.move_to(rect)
self.tallies.add(tally)
self.play(FadeIn(self.tallies))
self.wait()
anims = []
for (decimal_tally, tally_stack) in zip(decimal_tallies, self.tallies):
anims.append(ApplyFunction(
tally_stack.position_decimal_tally, decimal_tally
))
self.play(*anims)
self.wait()
# replace the original decimal tallies with
# the ones that belong to the TallyStacks
for (decimal_tally, tally_stack) in zip(decimal_tallies, self.tallies):
self.remove(decimal_tally)
tally_stack.position_decimal_tally(tally_stack.decimal_tally)
tally_stack.add(tally_stack.decimal_tally)
self.add_foreground_mobject(self.tallies)
self.merge_rects_by_subdiv()
self.wait()
self.merge_rects_by_coloring()
self.wait()
# # # # # # # # # # # # #
# CALLBACK TO SEQUENCES #
# # # # # # # # # # # # #
outcomes = self.row.get_outcome_rects_for_level(2, with_labels = True)
subdivs = self.row.get_sequence_subdivs_for_level(2)
self.play(
FadeIn(outcomes),
FadeIn(subdivs),
FadeOut(self.tallies)
)
self.wait()
rect_to_dice = outcomes[1]
N = 10
dice_width = rect_to_dice.get_width()/N
dice_height = rect_to_dice.get_height()/N
prototype_dice = Rectangle(
width = dice_width,
height = dice_height,
stroke_width = 2,
stroke_color = WHITE,
fill_color = WHITE,
fill_opacity = 0
)
prototype_dice.align_to(rect_to_dice, direction = UP)
prototype_dice.align_to(rect_to_dice, direction = LEFT)
all_dice = VGroup()
for i in range(N):
for j in range(N):
dice_copy = prototype_dice.copy()
dice_copy.shift(j * dice_width * RIGHT + i * dice_height * DOWN)
all_dice.add(dice_copy)
self.play(
LaggedStart(FadeIn, all_dice),
FadeOut(rect_to_dice.label)
)
self.wait()
table = Ellipse(width = 1.5, height = 1)
table.set_fill(color = GREEN_E, opacity = 1)
table.next_to(rect_to_dice, UP)
self.add(table)
coin1 = UprightHeads(radius = 0.1)
coin2 = UprightTails(radius = 0.1)
def get_random_point_in_ellipse(ellipse):
width = ellipse.get_width()
height = ellipse.get_height()
x = y = 1
while x**2 + y**2 > 0.9:
x = np.random.uniform(-1,1)
y = np.random.uniform(-1,1)
x *= width/2
y *= height/2
return ellipse.get_center() + x * RIGHT + y * UP
for dice in all_dice:
p1 = get_random_point_in_ellipse(table)
p2 = get_random_point_in_ellipse(table)
coin1.move_to(p1)
coin2.move_to(p2)
self.add(coin1, coin2)
self.play(
ApplyMethod(dice.set_fill, {"opacity" : 0.5},
rate_func = there_and_back,
run_time = 0.05)
)
self.wait()
self.play(
FadeOut(outcomes),
FadeOut(subdivs),
FadeOut(all_dice),
FadeOut(table),
FadeOut(coin1),
FadeOut(coin2),
FadeIn(self.tallies)
)
self.wait()
# # # # # # # #
# THIRD FLIP #
# # # # # # # #
# move row up, leace a copy without tallies below
new_row = self.row.copy()
self.clear()
self.add(randy, self.row, self.tallies)
self.bring_to_back(new_row)
self.play(
self.row.shift, 2.5 * UP,
self.tallies.shift, 2.5 * UP,
)
old_row = self.row
self.row = new_row
self.play(FlipCoin(randy))
self.wait()
self.play(
SplitRectsInBrickWall(self.row)
)
self.wait()
self.split_tallies_in_two_steps()
self.wait()
self.merge_rects_by_subdiv()
self.wait()
self.merge_tallies()
self.merge_rects_by_coloring()
self.wait()
self.move_tallies_on_top()
return
# show individual outcomes
outcomes = self.row.get_outcome_rects_for_level(3, with_labels = True)
self.play(
LaggedStart(FadeIn, outcomes)
)
self.wait()
self.play(
LaggedStart(FadeOut, outcomes)
)
# # # # # # # # # # # # # #
# # FOURTH FLIP IN DETAIL #
# # # # # # # # # # # # # #
# removing the tallies (boy are they sticky)
self.play(FadeOut(self.tallies))
self.remove(self.tallies, self.tallies_copy)
for tally in self.tallies:
self.remove_foreground_mobject(tally)
self.remove(tally)
for tally in self.tallies_copy:
self.remove_foreground_mobject(tally)
self.remove(tally)
# delete all the old crap hidden behind the row
# before we can move it
self.remove(*self.mobjects)
self.add(randy) #,self.decimals,self.decimal_copies)
previous_row = self.row.copy()
self.add(previous_row)
v = 1.25 * self.row.height * UP
self.play(
previous_row.shift, v,
#self.decimals.shift, v,
#self.decimal_copies.shift, v
)
self.add(self.row)
self.bring_to_back(self.row)
self.row.shift(v)
w = 1.5 * self.row.height * DOWN
self.play(
self.row.shift, w,
Animation(previous_row)
)
self.play(
SplitRectsInBrickWall(self.row)
)
self.wait()
self.merge_rects_by_subdiv()
self.wait()
n = 3 # level to split
k = 1 # tally to split
# show individual outcomes
outcomes = previous_row.get_outcome_rects_for_level(n, with_labels = False)
grouped_outcomes = VGroup()
index = 0
for i in range(n + 1):
size = choose(n,i)
grouped_outcomes.add(VGroup(outcomes[index:index + size]))
index += size
grouped_outcomes_copy = grouped_outcomes.copy()
original_grouped_outcomes = grouped_outcomes.copy()
# for later reference
self.play(
LaggedStart(FadeIn, grouped_outcomes),
LaggedStart(FadeIn, grouped_outcomes_copy),
)
self.wait()
# show how the outcomes in one tally split into two copies
# going into the neighboring tallies
#self.revert_to_original_skipping_status()
target_outcomes = self.row.get_outcome_rects_for_level(n + 1, with_labels = False)
grouped_target_outcomes = VGroup()
index = 0
old_tally_sizes = [choose(n,i) for i in range(n + 1)]
new_tally_sizes = [choose(n + 1,i) for i in range(n + 2)]
for i in range(n + 2):
size = new_tally_sizes[i]
grouped_target_outcomes.add(VGroup(target_outcomes[index:index + size]))
index += size
self.play(
Transform(grouped_outcomes[k][0],grouped_target_outcomes[k][0][old_tally_sizes[k - 1]:])
)
self.play(
Transform(grouped_outcomes_copy[k][0],grouped_target_outcomes[k + 1][0][:old_tally_sizes[k]])
)
old_tally_sizes.append(0) # makes the edge cases work properly
# split the other tallies
for i in range(k) + range(k + 1, n + 1):
self.play(
Transform(grouped_outcomes[i][0],
grouped_target_outcomes[i][0][old_tally_sizes[i - 1]:]
),
Transform(grouped_outcomes_copy[i][0],
grouped_target_outcomes[i + 1][0][:old_tally_sizes[i]]
)
)
self.wait()
# remove outcomes and sizes except for one tally
anims = []
for i in range(n + 1):
if i != k - 1:
anims.append(FadeOut(grouped_outcomes_copy[i]))
if i != k:
anims.append(FadeOut(grouped_outcomes[i]))
self.play(*anims)
self.wait()
self.play(
Transform(grouped_outcomes_copy[k - 1], original_grouped_outcomes[k - 1])
)
self.play(
Transform(grouped_outcomes[k], original_grouped_outcomes[k])
)
new_rects = self.row.get_rects_for_level(n + 1)
#decimals_copy = self.decimals.copy()
#decimals_copy2 = self.decimals.copy()
self.play(
Transform(grouped_outcomes[k][0],grouped_target_outcomes[k][0][old_tally_sizes[k - 1]:]),
Transform(grouped_outcomes_copy[k - 1][0],grouped_target_outcomes[k][0][:old_tally_sizes[k]]),
#decimals_copy[k - 1].move_to, new_rects[k],
#decimals_copy2[k].move_to, new_rects[k],
)
# # # # # # # #
# FIFTH FLIP #
# # # # # # # #
# self.remove(
# grouped_outcomes,
# grouped_outcomes_copy,
# grouped_target_outcomes,
# target_outcomes,
# outcomes,
# previous_row,
# original_grouped_outcomes)
self.clear()
self.add(randy, self.row)
#self.row.shift(0.5 * UP)
#return
self.merge_rects_by_coloring()
self.revert_to_original_skipping_status()
for i in range(1):
self.play(FlipCoin(randy))
self.wait()
self.play(
SplitRectsInBrickWall(self.row)
)
self.wait()
#self.split_tallies_at_once(direction = LEFT)
self.wait()
self.merge_rects_by_subdiv()
self.wait()
#self.merge_tallies(direction = LEFT)
self.merge_rects_by_coloring()
#self.merge_decimals()
self.wait()