mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
Posterior update animations in eop/bayes
This commit is contained in:
parent
f6340f42d7
commit
a21c96991f
4 changed files with 684 additions and 317 deletions
615
eop/bayes.py
615
eop/bayes.py
|
@ -43,7 +43,7 @@ class BayesOpeningQuote(OpeningQuote):
|
|||
class IntroducePokerHand(PiCreatureScene, SampleSpaceScene):
|
||||
CONFIG = {
|
||||
"community_cards_center" : 1.5*DOWN,
|
||||
"community_card_values" : ["AS", "QH", "10H", "2C", "5H"],
|
||||
"community_card_values" : ["10S", "QH", "AH", "2C", "5H"],
|
||||
"your_hand_values" : ["JS", "KC"],
|
||||
}
|
||||
def construct(self):
|
||||
|
@ -118,6 +118,7 @@ class IntroducePokerHand(PiCreatureScene, SampleSpaceScene):
|
|||
straight_cards.submobjects.sort(card_cmp)
|
||||
straight_cards.arrange_submobjects(RIGHT, buff = SMALL_BUFF)
|
||||
straight_cards.next_to(community_cards, UP, aligned_edge = LEFT)
|
||||
you.hand.target.shift(MED_SMALL_BUFF*UP)
|
||||
|
||||
self.play(LaggedStart(
|
||||
MoveToTarget,
|
||||
|
@ -125,7 +126,6 @@ class IntroducePokerHand(PiCreatureScene, SampleSpaceScene):
|
|||
run_time = 1.5
|
||||
))
|
||||
self.play(MoveToTarget(you.hand))
|
||||
self.play(you.change, "hooray", straight_cards)
|
||||
self.play(LaggedStart(
|
||||
ApplyMethod,
|
||||
straight_cards,
|
||||
|
@ -135,12 +135,14 @@ class IntroducePokerHand(PiCreatureScene, SampleSpaceScene):
|
|||
lag_ratio = 0.5,
|
||||
remover = True,
|
||||
))
|
||||
self.play(you.change, "hooray", straight_cards)
|
||||
self.dither(2)
|
||||
self.play(
|
||||
selected_community_cards.restore,
|
||||
you.hand.restore,
|
||||
you.change_mode, "happy"
|
||||
)
|
||||
self.dither()
|
||||
|
||||
def show_flush_potential(self):
|
||||
you, her = self.you, self.her
|
||||
|
@ -156,6 +158,10 @@ class IntroducePokerHand(PiCreatureScene, SampleSpaceScene):
|
|||
her.hand.target.next_to(heart_cards, UP)
|
||||
her.hand.target.to_edge(UP)
|
||||
|
||||
her.glasses.save_state()
|
||||
her.glasses.move_to(her.hand.target)
|
||||
her.glasses.set_fill(opacity = 0)
|
||||
|
||||
heart_qs = VGroup()
|
||||
hearts = VGroup()
|
||||
q_marks = VGroup()
|
||||
|
@ -177,7 +183,7 @@ class IntroducePokerHand(PiCreatureScene, SampleSpaceScene):
|
|||
self.play(LaggedStart(DrawBorderThenFill, heart_qs))
|
||||
self.play(
|
||||
her.change, "happy",
|
||||
DrawBorderThenFill(her.glasses)
|
||||
her.glasses.restore,
|
||||
)
|
||||
self.pi_creatures.remove(her)
|
||||
new_suit_pairs = [
|
||||
|
@ -272,19 +278,20 @@ class IntroducePokerHand(PiCreatureScene, SampleSpaceScene):
|
|||
sample_space.divide_horizontally(
|
||||
p, colors = [SuitSymbol.CONFIG["red"], BLUE_E]
|
||||
)
|
||||
top_label, bottom_label = sample_space.get_side_labels([
|
||||
braces, labels = sample_space.get_side_braces_and_labels([
|
||||
percentage.get_tex_string(), "95.5\\%"
|
||||
])
|
||||
top_label, bottom_label = labels
|
||||
|
||||
self.play(
|
||||
FadeIn(sample_space),
|
||||
ReplacementTransform(percentage, top_label[1])
|
||||
ReplacementTransform(percentage, top_label)
|
||||
)
|
||||
self.play(*map(GrowFromCenter, [
|
||||
label[0] for label in top_label, bottom_label
|
||||
brace for brace in braces
|
||||
]))
|
||||
self.dither(2)
|
||||
self.play(Write(bottom_label[1]))
|
||||
self.play(Write(bottom_label))
|
||||
self.dither(2)
|
||||
|
||||
self.sample_space = sample_space
|
||||
|
@ -297,7 +304,7 @@ class IntroducePokerHand(PiCreatureScene, SampleSpaceScene):
|
|||
flush_hands, non_flush_hands = hand_lists = [
|
||||
[self.get_hand(her, keys) for keys in key_list]
|
||||
for key_list in [
|
||||
[("3H", "8H"), ("4H", "AH"), ("JH", "KH")],
|
||||
[("3H", "8H"), ("4H", "5H"), ("JH", "KH")],
|
||||
[("AC", "6D"), ("3D", "6S"), ("JH", "4C")],
|
||||
]
|
||||
]
|
||||
|
@ -343,10 +350,7 @@ class IntroducePokerHand(PiCreatureScene, SampleSpaceScene):
|
|||
self.money = money
|
||||
|
||||
def change_belief(self):
|
||||
numbers = VGroup(*[
|
||||
label[1]
|
||||
for label in self.sample_space.horizontal_parts.labels
|
||||
])
|
||||
numbers = self.sample_space.horizontal_parts.labels
|
||||
rect = Rectangle(stroke_width = 0)
|
||||
rect.set_fill(BLACK, 1)
|
||||
rect.stretch_to_fit_width(numbers.get_width())
|
||||
|
@ -354,11 +358,12 @@ class IntroducePokerHand(PiCreatureScene, SampleSpaceScene):
|
|||
rect.move_to(numbers, UP)
|
||||
|
||||
self.play(FadeIn(rect))
|
||||
self.change_horizontal_division(
|
||||
0.2,
|
||||
anims = self.get_horizontal_division_change_animations(0.2)
|
||||
anims.append(Animation(rect))
|
||||
self.play(
|
||||
*anims,
|
||||
run_time = 3,
|
||||
rate_func = there_and_back,
|
||||
added_anims = [Animation(rect)]
|
||||
rate_func = there_and_back
|
||||
)
|
||||
self.play(FadeOut(rect))
|
||||
|
||||
|
@ -371,7 +376,7 @@ class IntroducePokerHand(PiCreatureScene, SampleSpaceScene):
|
|||
cards.target.move_to(self.deck)
|
||||
cards.target.to_edge(LEFT)
|
||||
|
||||
self.sample_space.add(self.sample_space.horizontal_parts.labels)
|
||||
self.sample_space.add_braces_and_labels()
|
||||
|
||||
self.play(
|
||||
self.deck.scale, 0.7,
|
||||
|
@ -389,9 +394,7 @@ class IntroducePokerHand(PiCreatureScene, SampleSpaceScene):
|
|||
subtitle.scale(0.8)
|
||||
subtitle.next_to(title, DOWN)
|
||||
prior_word = subtitle.get_part_by_tex("prior")
|
||||
numbers = VGroup(*[
|
||||
label[1] for label in self.sample_space.horizontal_parts.labels
|
||||
])
|
||||
numbers = self.sample_space.horizontal_parts.labels
|
||||
rect = SurroundingRectangle(numbers, color = GREEN)
|
||||
arrow = Arrow(prior_word.get_bottom(), rect.get_top())
|
||||
arrow.highlight(GREEN)
|
||||
|
@ -431,15 +434,9 @@ class IntroducePokerHand(PiCreatureScene, SampleSpaceScene):
|
|||
her.to_corner(UP+RIGHT)
|
||||
you.make_eye_contact(her)
|
||||
|
||||
glasses = SVGMobject(file_name = "sunglasses")
|
||||
glasses.set_stroke(WHITE, width = 0)
|
||||
glasses.set_fill(GREY, 1)
|
||||
glasses.scale_to_fit_width(
|
||||
1.1*her.eyes.get_width()
|
||||
)
|
||||
glasses.move_to(her.eyes, UP)
|
||||
|
||||
glasses = SunGlasses(her)
|
||||
her.glasses = glasses
|
||||
|
||||
self.you = you
|
||||
self.her = her
|
||||
return VGroup(you, her)
|
||||
|
@ -464,7 +461,6 @@ class IntroducePokerHand(PiCreatureScene, SampleSpaceScene):
|
|||
)
|
||||
return hand
|
||||
|
||||
|
||||
class HowDoesPokerWork(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
self.student_says(
|
||||
|
@ -484,7 +480,566 @@ class YourGutKnowsBayesRule(TeacherStudentsScene):
|
|||
self.change_student_modes("confused", "gracious", "guilty")
|
||||
self.dither(3)
|
||||
|
||||
|
||||
class UpdatePokerPrior(SampleSpaceScene):
|
||||
CONFIG = {
|
||||
"double_heart_template" : "HH",
|
||||
"cash_string" : "\\$\\$\\$",
|
||||
}
|
||||
def construct(self):
|
||||
self.force_skipping()
|
||||
|
||||
self.add_sample_space()
|
||||
self.add_top_conditionals()
|
||||
self.react_to_top_conditionals()
|
||||
self.add_bottom_conditionals()
|
||||
# self.ask_where_conditionals_come_from()
|
||||
# self.vary_conditionals()
|
||||
self.show_restricted_space()
|
||||
self.write_P_flush_given_bet()
|
||||
self.reshape_rectangles()
|
||||
self.compare_prior_to_posterior()
|
||||
self.tweak_estimates()
|
||||
self.compute_posterior()
|
||||
|
||||
def add_sample_space(self):
|
||||
p = 1./22
|
||||
sample_space = SampleSpace(fill_opacity = 0)
|
||||
sample_space.shift(LEFT)
|
||||
sample_space.divide_horizontally(p, colors = [
|
||||
SuitSymbol.CONFIG["red"], BLUE_E
|
||||
])
|
||||
labels = self.get_prior_labels(p)
|
||||
braces_and_labels = sample_space.get_side_braces_and_labels(labels)
|
||||
|
||||
self.play(
|
||||
DrawBorderThenFill(sample_space),
|
||||
Write(braces_and_labels)
|
||||
)
|
||||
self.dither()
|
||||
|
||||
sample_space.add(braces_and_labels)
|
||||
self.sample_space = sample_space
|
||||
|
||||
def add_top_conditionals(self):
|
||||
top_part = self.sample_space.horizontal_parts[0]
|
||||
color = average_color(YELLOW, GREEN, GREEN)
|
||||
p = 0.97
|
||||
top_part.divide_vertically(p, colors = [color, BLUE])
|
||||
label = self.get_conditional_label(p, True)
|
||||
brace, _ignore = top_part.get_top_braces_and_labels([label])
|
||||
|
||||
explanation = TextMobject(
|
||||
"Probability of", "high bet", "given", "flush"
|
||||
)
|
||||
explanation.highlight_by_tex("high bet", GREEN)
|
||||
explanation.highlight_by_tex("flush", RED)
|
||||
explanation.scale(0.6)
|
||||
explanation.next_to(label, UP)
|
||||
|
||||
self.play(
|
||||
FadeIn(top_part.vertical_parts),
|
||||
Write(explanation, run_time = 3),
|
||||
GrowFromCenter(brace),
|
||||
)
|
||||
self.play(LaggedStart(FadeIn, label, run_time = 2, lag_ratio = 0.7))
|
||||
self.dither(2)
|
||||
|
||||
self.sample_space.add(brace, label)
|
||||
|
||||
self.top_explanation = explanation
|
||||
self.top_conditional_rhs = label[-1]
|
||||
|
||||
def react_to_top_conditionals(self):
|
||||
her = PiCreature(color = BLUE_B).flip()
|
||||
her.next_to(self.sample_space, RIGHT)
|
||||
her.to_edge(RIGHT)
|
||||
glasses = SunGlasses(her)
|
||||
glasses.save_state()
|
||||
glasses.shift(UP)
|
||||
glasses.set_fill(opacity = 0)
|
||||
her.glasses = glasses
|
||||
|
||||
self.play(FadeIn(her))
|
||||
self.play(glasses.restore)
|
||||
self.play(
|
||||
her.change_mode, "happy",
|
||||
Animation(glasses)
|
||||
)
|
||||
self.dither(2)
|
||||
|
||||
self.her = her
|
||||
|
||||
def add_bottom_conditionals(self):
|
||||
her = self.her
|
||||
bottom_part = self.sample_space.horizontal_parts[1]
|
||||
p = 0.3
|
||||
bottom_part.divide_vertically(p, colors = [GREEN_E, BLUE_E])
|
||||
label = self.get_conditional_label(p, False)
|
||||
brace, _ignore = bottom_part.get_bottom_braces_and_labels([label])
|
||||
|
||||
explanation = TextMobject(
|
||||
"Probability of", "high bet", "given", "no flush"
|
||||
)
|
||||
explanation.highlight_by_tex("high bet", GREEN)
|
||||
explanation.highlight_by_tex("no flush", RED)
|
||||
explanation.scale(0.6)
|
||||
explanation.next_to(label, DOWN)
|
||||
|
||||
self.play(DrawBorderThenFill(bottom_part.vertical_parts))
|
||||
self.play(GrowFromCenter(brace))
|
||||
self.play(
|
||||
her.change_mode, "shruggie",
|
||||
MaintainPositionRelativeTo(her.glasses, her.eyes)
|
||||
)
|
||||
self.play(Write(explanation))
|
||||
self.dither()
|
||||
self.play(*[
|
||||
ReplacementTransform(
|
||||
VGroup(explanation[j].copy()),
|
||||
VGroup(*label[i1:i2]),
|
||||
run_time = 2,
|
||||
rate_func = squish_rate_func(smooth, a, a+0.5)
|
||||
)
|
||||
for a, (i1, i2, j) in zip(np.linspace(0, 0.5, 4), [
|
||||
(0, 1, 0),
|
||||
(1, 2, 1),
|
||||
(2, 3, 2),
|
||||
(3, 6, 3),
|
||||
])
|
||||
])
|
||||
self.play(Write(VGroup(*label[-2:])))
|
||||
self.dither(2)
|
||||
self.play(*map(FadeOut, [her, her.glasses]))
|
||||
|
||||
self.sample_space.add(brace, label)
|
||||
self.bottom_explanation = explanation
|
||||
self.bottom_conditional_rhs = label[-1]
|
||||
|
||||
def ask_where_conditionals_come_from(self):
|
||||
randy = Randolph().flip()
|
||||
randy.scale(0.75)
|
||||
randy.to_edge(RIGHT)
|
||||
randy.shift(2*DOWN)
|
||||
words = TextMobject("Where do these \\\\", "numbers", "come from?")
|
||||
numbers_word = words.get_part_by_tex("numbers")
|
||||
numbers_word.highlight(YELLOW)
|
||||
words.scale(0.7)
|
||||
bubble = ThoughtBubble(height = 3, width = 4)
|
||||
bubble.pin_to(randy)
|
||||
bubble.shift(MED_LARGE_BUFF*RIGHT)
|
||||
bubble.add_content(words)
|
||||
|
||||
numbers = VGroup(
|
||||
self.top_conditional_rhs,
|
||||
self.bottom_conditional_rhs
|
||||
)
|
||||
numbers.save_state()
|
||||
arrows = VGroup(*[
|
||||
Arrow(
|
||||
numbers_word.get_left(),
|
||||
num.get_right(),
|
||||
buff = 2*SMALL_BUFF
|
||||
)
|
||||
for num in numbers
|
||||
])
|
||||
|
||||
questions = VGroup(*map(TextMobject, [
|
||||
"Does she bluff?",
|
||||
"How much does she have?",
|
||||
"Does she take risks?",
|
||||
"What's her model of me?",
|
||||
"\\vdots"
|
||||
]))
|
||||
questions.arrange_submobjects(DOWN, aligned_edge = LEFT)
|
||||
questions[-1].next_to(questions[-2], DOWN)
|
||||
questions.scale(0.7)
|
||||
questions.next_to(randy, UP)
|
||||
questions.shift_onto_screen()
|
||||
|
||||
self.play(
|
||||
randy.change_mode, "confused",
|
||||
ShowCreation(bubble),
|
||||
Write(words, run_time = 2)
|
||||
)
|
||||
self.play(*map(ShowCreation, arrows))
|
||||
self.play(numbers.highlight, YELLOW)
|
||||
self.play(Blink(randy))
|
||||
self.play(randy.change_mode, "maybe")
|
||||
self.play(*map(FadeOut, [
|
||||
bubble, words, arrows
|
||||
]))
|
||||
for question in questions:
|
||||
self.play(
|
||||
FadeIn(question),
|
||||
randy.look_at, question
|
||||
)
|
||||
self.dither()
|
||||
self.play(Blink(randy))
|
||||
self.dither()
|
||||
self.play(
|
||||
randy.change_mode, "pondering",
|
||||
FadeOut(questions)
|
||||
)
|
||||
|
||||
self.randy = randy
|
||||
|
||||
def vary_conditionals(self):
|
||||
randy = self.randy
|
||||
rects = VGroup(*[
|
||||
SurroundingRectangle(
|
||||
VGroup(explanation),
|
||||
buff = SMALL_BUFF,
|
||||
)
|
||||
for explanation, rhs in zip(
|
||||
[self.top_explanation, self.bottom_explanation],
|
||||
[self.top_conditional_rhs, self.bottom_conditional_rhs],
|
||||
)
|
||||
])
|
||||
|
||||
new_conditionals = [
|
||||
(0.91, 0.4),
|
||||
(0.83, 0.1),
|
||||
(0.99, 0.2),
|
||||
(0.97, 0.3),
|
||||
]
|
||||
|
||||
self.play(*map(ShowCreation, rects))
|
||||
self.play(FadeOut(rects))
|
||||
for i, value in enumerate(it.chain(*new_conditionals)):
|
||||
self.play(
|
||||
randy.look_at, rects[i%2],
|
||||
*self.get_conditional_change_anims(i%2, value)
|
||||
)
|
||||
if i%2 == 1:
|
||||
self.dither()
|
||||
self.play(FadeOut(randy))
|
||||
|
||||
def show_restricted_space(self):
|
||||
high_bet_space, low_bet_space = [
|
||||
VGroup(*[
|
||||
self.sample_space.horizontal_parts[i].vertical_parts[j]
|
||||
for i in range(2)
|
||||
])
|
||||
for j in range(2)
|
||||
]
|
||||
words = TexMobject("P(", self.cash_string, ")")
|
||||
words.highlight_by_tex(self.cash_string, GREEN)
|
||||
words.next_to(self.sample_space, RIGHT)
|
||||
low_bet_space.generate_target()
|
||||
for submob in low_bet_space.target:
|
||||
submob.highlight(average_color(
|
||||
submob.get_color(), *[BLACK]*4
|
||||
))
|
||||
arrows = VGroup(*[
|
||||
Arrow(
|
||||
words.get_left(),
|
||||
submob.get_edge_center(vect),
|
||||
color = submob.get_color()
|
||||
)
|
||||
for submob, vect in zip(high_bet_space, [DOWN, RIGHT])
|
||||
])
|
||||
|
||||
self.play(MoveToTarget(low_bet_space))
|
||||
self.play(
|
||||
Write(words),
|
||||
*map(ShowCreation, arrows)
|
||||
)
|
||||
self.dither()
|
||||
for rect in high_bet_space:
|
||||
self.play(Indicate(rect, scale_factor = 1))
|
||||
self.play(*map(FadeOut, [words, arrows]))
|
||||
|
||||
self.high_bet_space = high_bet_space
|
||||
|
||||
def write_P_flush_given_bet(self):
|
||||
posterior_tex = TexMobject(
|
||||
"P(", self.double_heart_template,
|
||||
"|", self.cash_string, ")"
|
||||
)
|
||||
posterior_tex.scale(0.7)
|
||||
posterior_tex.highlight_by_tex(self.cash_string, GREEN)
|
||||
self.insert_double_heart(posterior_tex)
|
||||
rects = self.high_bet_space.copy()
|
||||
rects = [rects[0].copy()] + list(rects)
|
||||
for rect in rects:
|
||||
rect.generate_target()
|
||||
numerator = rects[0].target
|
||||
plus = TexMobject("+")
|
||||
denominator = VGroup(rects[1].target, plus, rects[2].target)
|
||||
denominator.arrange_submobjects(RIGHT, buff = SMALL_BUFF)
|
||||
frac_line = TexMobject("\\over")
|
||||
frac_line.stretch_to_fit_width(denominator.get_width())
|
||||
fraction = VGroup(numerator, frac_line, denominator)
|
||||
fraction.arrange_submobjects(DOWN)
|
||||
|
||||
arrow = TexMobject("\\downarrow")
|
||||
group = VGroup(posterior_tex, arrow, fraction)
|
||||
group.arrange_submobjects(DOWN)
|
||||
group.to_corner(UP+RIGHT)
|
||||
|
||||
self.play(Write(posterior_tex))
|
||||
self.play(Write(arrow))
|
||||
self.play(MoveToTarget(rects[0]))
|
||||
self.dither()
|
||||
self.play(*it.chain(
|
||||
map(Write, [frac_line, plus]),
|
||||
map(MoveToTarget, rects[1:])
|
||||
))
|
||||
self.dither(3)
|
||||
|
||||
self.posterior_tex = posterior_tex
|
||||
self.to_fade = VGroup(arrow, frac_line, plus)
|
||||
self.to_post_rects = VGroup(VGroup(*rects[:2]),rects[2])
|
||||
|
||||
def reshape_rectangles(self):
|
||||
post_rects = self.get_posterior_rectangles()
|
||||
braces, labels = self.get_posterior_rectangle_braces_and_labels(
|
||||
post_rects, [self.posterior_tex.copy()]
|
||||
)
|
||||
height_rect = SurroundingRectangle(braces)
|
||||
|
||||
self.play(
|
||||
FadeOut(self.to_fade),
|
||||
ReplacementTransform(
|
||||
self.to_post_rects, post_rects,
|
||||
run_time = 2,
|
||||
),
|
||||
)
|
||||
self.dither(2)
|
||||
self.play(ReplacementTransform(self.posterior_tex, labels[0]))
|
||||
self.posterior_tex = labels[0]
|
||||
self.play(GrowFromCenter(braces))
|
||||
self.dither()
|
||||
self.play(ShowCreation(height_rect))
|
||||
self.play(FadeOut(height_rect))
|
||||
self.dither()
|
||||
|
||||
self.post_rects = post_rects
|
||||
|
||||
def compare_prior_to_posterior(self):
|
||||
prior_tex = self.sample_space.horizontal_parts.labels[0]
|
||||
post_tex = self.posterior_tex
|
||||
prior_rect, post_rect = [
|
||||
SurroundingRectangle(tex, stroke_width = 2)
|
||||
for tex in [prior_tex, post_tex]
|
||||
]
|
||||
|
||||
post_words = TextMobject("Posterior", "probability")
|
||||
post_words.scale(0.8)
|
||||
post_words.to_corner(UP+RIGHT)
|
||||
post_arrow = Arrow(
|
||||
post_words[0].get_bottom(), post_tex.get_top(),
|
||||
color = WHITE
|
||||
)
|
||||
|
||||
self.play(ShowCreation(prior_rect))
|
||||
self.dither()
|
||||
self.play(ReplacementTransform(prior_rect, post_rect))
|
||||
self.dither()
|
||||
self.play(FadeOut(post_rect))
|
||||
self.play(Indicate(post_tex.get_part_by_tex(self.cash_string)))
|
||||
self.dither()
|
||||
self.play(
|
||||
Write(post_words),
|
||||
ShowCreation(post_arrow)
|
||||
)
|
||||
self.dither()
|
||||
self.play(post_words[1].fade, 0.8)
|
||||
self.dither(2)
|
||||
self.play(*map(FadeOut, [post_words, post_arrow]))
|
||||
|
||||
def tweak_estimates(self):
|
||||
post_rects = self.post_rects
|
||||
self.revert_to_original_skipping_status()
|
||||
self.preview_tweaks(post_rects)
|
||||
|
||||
def preview_tweaks(self, post_rects):
|
||||
new_value_lists = [
|
||||
(0.85, 0.1, 0.11),
|
||||
(0.97, 0.3, 1./22),
|
||||
]
|
||||
for new_values in new_value_lists:
|
||||
for i, value in zip(range(2), new_values):
|
||||
self.play(*self.get_conditional_change_anims(
|
||||
i, value, post_rects
|
||||
))
|
||||
self.play(*self.get_prior_change_anims(
|
||||
new_values[-1], post_rects
|
||||
))
|
||||
self.dither()
|
||||
|
||||
def compute_posterior(self):
|
||||
pass
|
||||
|
||||
######
|
||||
|
||||
def get_prior_labels(self, value):
|
||||
p_str = "%0.3f"%value
|
||||
q_str = "%0.3f"%(1-value)
|
||||
labels = [
|
||||
TexMobject(
|
||||
"P(", s, self.double_heart_template, ")",
|
||||
"= ", num
|
||||
)
|
||||
for s, num in ("", p_str), ("\\text{not }", q_str)
|
||||
]
|
||||
for label in labels:
|
||||
label.scale(0.7)
|
||||
self.insert_double_heart(label)
|
||||
return labels
|
||||
|
||||
def get_conditional_label(self, value, given_flush = True):
|
||||
label = TexMobject(
|
||||
"P(", self.cash_string, "|",
|
||||
"" if given_flush else "\\text{not }",
|
||||
self.double_heart_template, ")",
|
||||
"=", str(value)
|
||||
)
|
||||
self.insert_double_heart(label)
|
||||
label.highlight_by_tex(self.cash_string, GREEN)
|
||||
label.scale(0.7)
|
||||
return label
|
||||
|
||||
def insert_double_heart(self, tex_mob):
|
||||
double_heart = SuitSymbol("hearts")
|
||||
double_heart.add(SuitSymbol("hearts"))
|
||||
double_heart.arrange_submobjects(RIGHT, buff = SMALL_BUFF)
|
||||
double_heart.get_tex_string = lambda : self.double_heart_template
|
||||
template = tex_mob.get_part_by_tex(self.double_heart_template)
|
||||
double_heart.replace(template)
|
||||
tex_mob.submobjects[tex_mob.index_of_part(template)] = double_heart
|
||||
return tex_mob
|
||||
|
||||
def get_prior_change_anims(self, value, post_rects = None):
|
||||
space = self.sample_space
|
||||
parts = space.horizontal_parts
|
||||
anims = self.get_horizontal_division_change_animations(
|
||||
value, new_label_kwargs = {
|
||||
"labels" : self.get_prior_labels(value)
|
||||
}
|
||||
)
|
||||
if post_rects is not None:
|
||||
anims += self.get_posterior_rectangle_change_anims(post_rects)
|
||||
return anims
|
||||
|
||||
def get_conditional_change_anims(
|
||||
self, sub_sample_space_index, value,
|
||||
post_rects = None
|
||||
):
|
||||
parts = self.sample_space.horizontal_parts
|
||||
sub_sample_space = parts[sub_sample_space_index]
|
||||
given_flush = (sub_sample_space_index == 0)
|
||||
label = self.get_conditional_label(value, given_flush)
|
||||
|
||||
anims = self.get_division_change_animations(
|
||||
sub_sample_space, sub_sample_space.vertical_parts, value,
|
||||
dimension = 0,
|
||||
new_label_kwargs = {"labels" : [label]},
|
||||
)
|
||||
|
||||
if post_rects is not None:
|
||||
anims += self.get_posterior_rectangle_change_anims(post_rects)
|
||||
|
||||
return anims
|
||||
|
||||
def get_top_conditional_change_anims(self, *args, **kwargs):
|
||||
return self.get_conditional_change_anims(0, *args, **kwargs)
|
||||
|
||||
def get_bottom_conditional_change_anims(self, *args, **kwargs):
|
||||
return self.get_conditional_change_anims(1, *args, **kwargs)
|
||||
|
||||
def get_prior_rectangles(self):
|
||||
return VGroup(*[
|
||||
self.sample_space.horizontal_parts[i].vertical_parts[0]
|
||||
for i in range(2)
|
||||
])
|
||||
|
||||
def get_posterior_rectangles(self):
|
||||
prior_rects = self.get_prior_rectangles()
|
||||
areas = [
|
||||
rect.get_width()*rect.get_height()
|
||||
for rect in prior_rects
|
||||
]
|
||||
total_area = sum(areas)
|
||||
total_height = prior_rects.get_height()
|
||||
|
||||
post_rects = prior_rects.copy()
|
||||
for rect, area in zip(post_rects, areas):
|
||||
rect.stretch_to_fit_height(total_height * area/total_area)
|
||||
rect.stretch_to_fit_width(
|
||||
area/rect.get_height()
|
||||
)
|
||||
post_rects.arrange_submobjects(DOWN, buff = 0)
|
||||
post_rects.next_to(
|
||||
self.sample_space.full_space, RIGHT, MED_LARGE_BUFF
|
||||
)
|
||||
return post_rects
|
||||
|
||||
def get_posterior_rectangle_braces_and_labels(self, post_rects, labels):
|
||||
braces = VGroup()
|
||||
label_mobs = VGroup()
|
||||
for label, rect in zip(labels, post_rects):
|
||||
if not isinstance(label, Mobject):
|
||||
label_mob = TexMobject(label)
|
||||
label_mob.scale(0.7)
|
||||
else:
|
||||
label_mob = label
|
||||
brace = Brace(
|
||||
rect, RIGHT,
|
||||
buff = SMALL_BUFF,
|
||||
min_num_quads = 2
|
||||
)
|
||||
label_mob.next_to(brace, RIGHT, SMALL_BUFF)
|
||||
|
||||
label_mobs.add(label_mob)
|
||||
braces.add(brace)
|
||||
post_rects.braces = braces
|
||||
post_rects.labels = label_mobs
|
||||
return VGroup(braces, label_mobs)
|
||||
|
||||
def update_posterior_braces(self, post_rects):
|
||||
braces = post_rects.braces
|
||||
labels = post_rects.labels
|
||||
for rect, brace, label in zip(post_rects, braces, labels):
|
||||
brace.stretch_to_fit_height(rect.get_height())
|
||||
brace.next_to(rect, RIGHT, SMALL_BUFF)
|
||||
label.next_to(brace, RIGHT, SMALL_BUFF)
|
||||
|
||||
def get_posterior_rectangle_change_anims(self, post_rects):
|
||||
def update_rects(rects):
|
||||
new_rects = self.get_posterior_rectangles()
|
||||
Transform(rects, new_rects).update(1)
|
||||
if hasattr(rects, "braces"):
|
||||
self.update_posterior_braces(rects)
|
||||
return rects
|
||||
|
||||
anims = [UpdateFromFunc(post_rects, update_rects)]
|
||||
if hasattr(post_rects, "braces"):
|
||||
anims += map(Animation, [
|
||||
post_rects.labels, post_rects.braces
|
||||
])
|
||||
return anims
|
||||
|
||||
|
||||
class NextVideoWrapper(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
title = TextMobject("Next video: Bayesian networks")
|
||||
title.scale(0.8)
|
||||
title.to_edge(UP, buff = SMALL_BUFF)
|
||||
screen = ScreenRectangle(height = 4)
|
||||
screen.next_to(title, DOWN)
|
||||
title.save_state()
|
||||
title.shift(DOWN)
|
||||
title.set_fill(opacity = 0)
|
||||
|
||||
self.play(
|
||||
title.restore,
|
||||
self.teacher.change, "raise_right_hand"
|
||||
)
|
||||
self.play(ShowCreation(screen))
|
||||
self.change_student_modes(*["pondering"]*3)
|
||||
self.play(Animation(screen))
|
||||
self.dither(5)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -376,6 +376,8 @@ class VGroup(VMobject):
|
|||
class VectorizedPoint(VMobject):
|
||||
CONFIG = {
|
||||
"color" : BLACK,
|
||||
"fill_opacity" : 0,
|
||||
"stroke_width" : 0,
|
||||
"artificial_width" : 0.01,
|
||||
"artificial_height" : 0.01,
|
||||
}
|
||||
|
|
|
@ -11,253 +11,20 @@ from animation.simple_animations import Rotating
|
|||
from topics.geometry import Circle, Line, Rectangle, Square, Arc, Polygon
|
||||
from topics.three_dimensions import Cube
|
||||
|
||||
class DeckOfCards(VGroup):
|
||||
def __init__(self, **kwargs):
|
||||
possible_values = map(str, range(1, 11)) + ["J", "Q", "K"]
|
||||
possible_suits = ["hearts", "diamonds", "spades", "clubs"]
|
||||
VGroup.__init__(self, *[
|
||||
PlayingCard(value = value, suit = suit, **kwargs)
|
||||
for value in possible_values
|
||||
for suit in possible_suits
|
||||
])
|
||||
|
||||
|
||||
class PlayingCard(VGroup):
|
||||
class SunGlasses(SVGMobject):
|
||||
CONFIG = {
|
||||
"value" : None,
|
||||
"suit" : None,
|
||||
"key" : None, ##String like "8H" or "KS"
|
||||
"height" : 2,
|
||||
"height_to_width" : 3.5/2.5,
|
||||
"card_height_to_symbol_height" : 7,
|
||||
"card_width_to_corner_num_width" : 10,
|
||||
"card_height_to_corner_num_height" : 10,
|
||||
"color" : LIGHT_GREY,
|
||||
"turned_over" : False,
|
||||
"possible_suits" : ["hearts", "diamonds", "spades", "clubs"],
|
||||
"possible_values" : map(str, range(2, 11)) + ["J", "Q", "K", "A"],
|
||||
"file_name" : "sunglasses",
|
||||
"glasses_width_to_eyes_width" : 1.1,
|
||||
}
|
||||
|
||||
def __init__(self, key = None, **kwargs):
|
||||
VGroup.__init__(self, key = key, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
self.add(Rectangle(
|
||||
height = self.height,
|
||||
width = self.height/self.height_to_width,
|
||||
stroke_color = WHITE,
|
||||
stroke_width = 2,
|
||||
fill_color = self.color,
|
||||
fill_opacity = 1,
|
||||
))
|
||||
if self.turned_over:
|
||||
self.set_fill(DARK_GREY)
|
||||
self.set_stroke(LIGHT_GREY)
|
||||
contents = VectorizedPoint(self.get_center())
|
||||
else:
|
||||
value = self.get_value()
|
||||
symbol = self.get_symbol()
|
||||
design = self.get_design(value, symbol)
|
||||
corner_numbers = self.get_corner_numbers(value, symbol)
|
||||
contents = VGroup(design, corner_numbers)
|
||||
self.design = design
|
||||
self.corner_numbers = corner_numbers
|
||||
self.add(contents)
|
||||
|
||||
def get_value(self):
|
||||
value = self.value
|
||||
if value is None:
|
||||
if self.key is not None:
|
||||
value = self.key[:-1]
|
||||
else:
|
||||
value = random.choice(self.possible_values)
|
||||
value = string.upper(str(value))
|
||||
if value == "1":
|
||||
value = "A"
|
||||
if value not in self.possible_values:
|
||||
raise Exception("Invalid card value")
|
||||
|
||||
face_card_to_value = {
|
||||
"J" : 11,
|
||||
"Q" : 12,
|
||||
"K" : 13,
|
||||
"A" : 14,
|
||||
}
|
||||
try:
|
||||
self.numerical_value = int(value)
|
||||
except:
|
||||
self.numerical_value = face_card_to_value[value]
|
||||
return value
|
||||
|
||||
def get_symbol(self):
|
||||
suit = self.suit
|
||||
if suit is None:
|
||||
if self.key is not None:
|
||||
suit = dict([
|
||||
(string.upper(s[0]), s)
|
||||
for s in self.possible_suits
|
||||
])[string.upper(self.key[-1])]
|
||||
else:
|
||||
suit = random.choice(self.possible_suits)
|
||||
if suit not in self.possible_suits:
|
||||
raise Exception("Invalud suit value")
|
||||
self.suit = suit
|
||||
symbol_height = float(self.height) / self.card_height_to_symbol_height
|
||||
symbol = SuitSymbol(suit, height = symbol_height)
|
||||
return symbol
|
||||
|
||||
def get_design(self, value, symbol):
|
||||
if value == "A":
|
||||
return self.get_ace_design(symbol)
|
||||
if value in map(str, range(2, 11)):
|
||||
return self.get_number_design(value, symbol)
|
||||
else:
|
||||
return self.get_face_card_design(value, symbol)
|
||||
|
||||
def get_ace_design(self, symbol):
|
||||
design = symbol.copy().scale(1.5)
|
||||
design.move_to(self)
|
||||
return design
|
||||
|
||||
def get_number_design(self, value, symbol):
|
||||
num = int(value)
|
||||
n_rows = {
|
||||
2 : 2,
|
||||
3 : 3,
|
||||
4 : 2,
|
||||
5 : 2,
|
||||
6 : 3,
|
||||
7 : 3,
|
||||
8 : 3,
|
||||
9 : 4,
|
||||
10 : 4,
|
||||
}[num]
|
||||
n_cols = 1 if num in [2, 3] else 2
|
||||
insertion_indices = {
|
||||
5 : [0],
|
||||
7 : [0],
|
||||
8 : [0, 1],
|
||||
9 : [1],
|
||||
10 : [0, 2],
|
||||
}.get(num, [])
|
||||
|
||||
top = self.get_top() + symbol.get_height()*DOWN
|
||||
bottom = self.get_bottom() + symbol.get_height()*UP
|
||||
column_points = [
|
||||
interpolate(top, bottom, alpha)
|
||||
for alpha in np.linspace(0, 1, n_rows)
|
||||
]
|
||||
|
||||
design = VGroup(*[
|
||||
symbol.copy().move_to(point)
|
||||
for point in column_points
|
||||
])
|
||||
if n_cols == 2:
|
||||
space = 0.2*self.get_width()
|
||||
column_copy = design.copy().shift(space*RIGHT)
|
||||
design.shift(space*LEFT)
|
||||
design.add(*column_copy)
|
||||
design.add(*[
|
||||
symbol.copy().move_to(
|
||||
center_of_mass(column_points[i:i+2])
|
||||
)
|
||||
for i in insertion_indices
|
||||
])
|
||||
for symbol in design:
|
||||
if symbol.get_center()[1] < self.get_center()[1]:
|
||||
symbol.rotate_in_place(np.pi)
|
||||
return design
|
||||
|
||||
def get_face_card_design(self, value, symbol):
|
||||
from topics.characters import PiCreature
|
||||
sub_rect = Rectangle(
|
||||
stroke_color = BLACK,
|
||||
fill_opacity = 0,
|
||||
height = 0.9*self.get_height(),
|
||||
width = 0.6*self.get_width(),
|
||||
def __init__(self, pi_creature, **kwargs):
|
||||
SVGMobject.__init__(self, **kwargs)
|
||||
self.set_stroke(WHITE, width = 0)
|
||||
self.set_fill(GREY, 1)
|
||||
self.scale_to_fit_width(
|
||||
self.glasses_width_to_eyes_width*pi_creature.eyes.get_width()
|
||||
)
|
||||
sub_rect.move_to(self)
|
||||
self.move_to(pi_creature.eyes, UP)
|
||||
|
||||
pi_color = average_color(symbol.get_color(), GREY)
|
||||
pi_mode = {
|
||||
"J" : "plain",
|
||||
"Q" : "thinking",
|
||||
"K" : "hooray"
|
||||
}[value]
|
||||
pi_creature = PiCreature(
|
||||
mode = pi_mode,
|
||||
color = pi_color,
|
||||
)
|
||||
pi_creature.scale_to_fit_width(0.8*sub_rect.get_width())
|
||||
if value in ["Q", "K"]:
|
||||
prefix = "king" if value == "K" else "queen"
|
||||
crown = SVGMobject(file_name = prefix + "_crown")
|
||||
crown.set_stroke(width = 0)
|
||||
crown.set_fill(YELLOW, 1)
|
||||
crown.stretch_to_fit_width(0.5*sub_rect.get_width())
|
||||
crown.stretch_to_fit_height(0.17*sub_rect.get_height())
|
||||
crown.move_to(pi_creature.eyes.get_center(), DOWN)
|
||||
pi_creature.add_to_back(crown)
|
||||
to_top_buff = 0
|
||||
else:
|
||||
to_top_buff = SMALL_BUFF*sub_rect.get_height()
|
||||
pi_creature.next_to(sub_rect.get_top(), DOWN, to_top_buff)
|
||||
# pi_creature.shift(0.05*sub_rect.get_width()*RIGHT)
|
||||
|
||||
pi_copy = pi_creature.copy()
|
||||
pi_copy.rotate(np.pi, about_point = sub_rect.get_center())
|
||||
|
||||
return VGroup(sub_rect, pi_creature, pi_copy)
|
||||
|
||||
def get_corner_numbers(self, value, symbol):
|
||||
value_mob = TextMobject(value)
|
||||
width = self.get_width()/self.card_width_to_corner_num_width
|
||||
height = self.get_height()/self.card_height_to_corner_num_height
|
||||
value_mob.scale_to_fit_width(width)
|
||||
value_mob.stretch_to_fit_height(height)
|
||||
value_mob.next_to(
|
||||
self.get_corner(UP+LEFT), DOWN+RIGHT,
|
||||
buff = MED_LARGE_BUFF*width
|
||||
)
|
||||
value_mob.highlight(symbol.get_color())
|
||||
corner_symbol = symbol.copy()
|
||||
corner_symbol.scale_to_fit_width(width)
|
||||
corner_symbol.next_to(
|
||||
value_mob, DOWN,
|
||||
buff = MED_SMALL_BUFF*width
|
||||
)
|
||||
corner_group = VGroup(value_mob, corner_symbol)
|
||||
opposite_corner_group = corner_group.copy()
|
||||
opposite_corner_group.rotate(
|
||||
np.pi, about_point = self.get_center()
|
||||
)
|
||||
|
||||
return VGroup(corner_group, opposite_corner_group)
|
||||
|
||||
class SuitSymbol(SVGMobject):
|
||||
CONFIG = {
|
||||
"height" : 0.5,
|
||||
"fill_opacity" : 1,
|
||||
"stroke_width" : 0,
|
||||
"red" : "#D02028",
|
||||
"black" : BLACK,
|
||||
}
|
||||
def __init__(self, suit_name, **kwargs):
|
||||
digest_config(self, kwargs)
|
||||
suits_to_colors = {
|
||||
"hearts" : self.red,
|
||||
"diamonds" : self.red,
|
||||
"spades" : self.black,
|
||||
"clubs" : self.black,
|
||||
}
|
||||
if suit_name not in suits_to_colors:
|
||||
raise Exception("Invalid suit name")
|
||||
SVGMobject.__init__(self, file_name = suit_name, **kwargs)
|
||||
|
||||
color = suits_to_colors[suit_name]
|
||||
self.set_stroke(width = 0)
|
||||
self.set_fill(color, 1)
|
||||
self.scale_to_fit_height(self.height)
|
||||
|
||||
class Speedometer(VMobject):
|
||||
CONFIG = {
|
||||
|
|
|
@ -3,7 +3,7 @@ from helpers import *
|
|||
from scene import Scene
|
||||
|
||||
from animation.animation import Animation
|
||||
from animation.transform import Transform
|
||||
from animation.transform import Transform, MoveToTarget
|
||||
|
||||
from mobject import Mobject
|
||||
from mobject.vectorized_mobject import VGroup, VMobject, VectorizedPoint
|
||||
|
@ -22,30 +22,53 @@ class SampleSpaceScene(Scene):
|
|||
def add_sample_space(self, **config):
|
||||
self.add(self.get_sample_space(**config))
|
||||
|
||||
def change_horizontal_division(self, p_list, **kwargs):
|
||||
assert(hasattr(self.sample_space, "horizontal_parts"))
|
||||
added_anims = kwargs.pop("added_anims", [])
|
||||
new_division_kwargs = kwargs.pop("new_division_kwargs", {})
|
||||
added_label_kwargs = kwargs.pop("label_kwargs", {})
|
||||
def get_division_change_animations(
|
||||
self, sample_space, parts, p_list,
|
||||
dimension = 1,
|
||||
new_label_kwargs = None,
|
||||
**kwargs
|
||||
):
|
||||
if new_label_kwargs is None:
|
||||
new_label_kwargs = {}
|
||||
anims = []
|
||||
p_list = sample_space.complete_p_list(p_list)
|
||||
full_space = sample_space.full_space
|
||||
|
||||
curr_parts = self.sample_space.horizontal_parts
|
||||
new_division_kwargs["colors"] = [
|
||||
part.get_color() for part in curr_parts
|
||||
]
|
||||
new_parts = self.sample_space.get_horizontal_division(
|
||||
p_list, **new_division_kwargs
|
||||
)
|
||||
anims = [Transform(curr_parts, new_parts)]
|
||||
if hasattr(curr_parts, "labels"):
|
||||
label_kwargs = curr_parts.label_kwargs
|
||||
label_kwargs.update(added_label_kwargs)
|
||||
new_labels = self.sample_space.get_subdivision_labels(
|
||||
new_parts, **label_kwargs
|
||||
vect = DOWN if dimension == 1 else RIGHT
|
||||
parts.generate_target()
|
||||
for part, p in zip(parts.target, p_list):
|
||||
part.replace(full_space, stretch = True)
|
||||
part.stretch(p, dimension)
|
||||
parts.target.arrange_submobjects(vect, buff = 0)
|
||||
parts.target.move_to(full_space)
|
||||
anims.append(MoveToTarget(parts))
|
||||
if hasattr(parts, "labels"):
|
||||
label_kwargs = parts.label_kwargs
|
||||
label_kwargs.update(new_label_kwargs)
|
||||
new_braces, new_labels = sample_space.get_subdivision_braces_and_labels(
|
||||
parts.target, **label_kwargs
|
||||
)
|
||||
anims.append(Transform(curr_parts.labels, new_labels))
|
||||
anims += added_anims
|
||||
anims += [
|
||||
Transform(parts.braces, new_braces),
|
||||
Transform(parts.labels, new_labels),
|
||||
]
|
||||
return anims
|
||||
|
||||
self.play(*anims, **kwargs)
|
||||
def get_horizontal_division_change_animations(self, p_list, **kwargs):
|
||||
assert(hasattr(self.sample_space, "horizontal_parts"))
|
||||
return self.get_division_change_animations(
|
||||
self.sample_space, self.sample_space.horizontal_parts, p_list,
|
||||
dimension = 1,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def get_vertical_division_change_animations(self, p_list, **kwargs):
|
||||
assert(hasattr(self.sample_space, "vertical_parts"))
|
||||
return self.get_division_change_animations(
|
||||
self.sample_space, self.sample_space.vertical_parts, p_list,
|
||||
dimension = 0,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
||||
class SampleSpace(VGroup):
|
||||
|
@ -55,7 +78,8 @@ class SampleSpace(VGroup):
|
|||
"width" : 3,
|
||||
"fill_color" : DARK_GREY,
|
||||
"fill_opacity" : 0.8,
|
||||
"stroke_width" : 0,
|
||||
"stroke_width" : 0.5,
|
||||
"stroke_color" : LIGHT_GREY,
|
||||
},
|
||||
"default_label_scale_val" : 0.7,
|
||||
}
|
||||
|
@ -76,12 +100,16 @@ class SampleSpace(VGroup):
|
|||
def add_label(self, label):
|
||||
self.label = label
|
||||
|
||||
def complete_p_list(self, p_list):
|
||||
new_p_list = list(tuplify(p_list))
|
||||
remainder = 1.0 - sum(new_p_list)
|
||||
if abs(remainder) > EPSILON:
|
||||
new_p_list.append(remainder)
|
||||
return new_p_list
|
||||
|
||||
def get_division_along_dimension(self, p_list, dim, colors, vect):
|
||||
p_list = list(tuplify(p_list))
|
||||
if abs(1.0 - sum(p_list)) > EPSILON:
|
||||
p_list.append(1.0 - sum(p_list))
|
||||
p_list = self.complete_p_list(p_list)
|
||||
colors = color_gradient(colors, len(p_list))
|
||||
perp_dim = 1-dim
|
||||
|
||||
last_point = self.full_space.get_edge_center(-vect)
|
||||
parts = VGroup()
|
||||
|
@ -89,7 +117,7 @@ class SampleSpace(VGroup):
|
|||
part = SampleSpace()
|
||||
part.set_fill(color, 1)
|
||||
part.replace(self.full_space, stretch = True)
|
||||
part.stretch(factor, perp_dim)
|
||||
part.stretch(factor, dim)
|
||||
part.move_to(last_point, -vect)
|
||||
last_point = part.get_edge_center(vect)
|
||||
parts.add(part)
|
||||
|
@ -100,14 +128,14 @@ class SampleSpace(VGroup):
|
|||
colors = [GREEN_E, BLUE],
|
||||
vect = DOWN
|
||||
):
|
||||
return self.get_division_along_dimension(p_list, 0, colors, vect)
|
||||
return self.get_division_along_dimension(p_list, 1, colors, vect)
|
||||
|
||||
def get_vertical_division(
|
||||
self, p_list,
|
||||
colors = [MAROON_B, YELLOW],
|
||||
vect = RIGHT
|
||||
):
|
||||
return self.get_division_along_dimension(p_list, 1, colors, vect)
|
||||
return self.get_division_along_dimension(p_list, 0, colors, vect)
|
||||
|
||||
def divide_horizontally(self, *args, **kwargs):
|
||||
self.horizontal_parts = self.get_horizontal_division(*args, **kwargs)
|
||||
|
@ -117,38 +145,53 @@ class SampleSpace(VGroup):
|
|||
self.vertical_parts = self.get_vertical_division(*args, **kwargs)
|
||||
self.add(self.vertical_parts)
|
||||
|
||||
def get_subdivision_labels(self, parts, labels, direction, buff = SMALL_BUFF):
|
||||
def get_subdivision_braces_and_labels(self, parts, labels, direction, buff = SMALL_BUFF):
|
||||
label_brace_groups = VGroup()
|
||||
label_mobs = VGroup()
|
||||
braces = VGroup()
|
||||
for label, part in zip(labels, parts):
|
||||
brace = Brace(part, direction, min_num_quads = 1, buff = buff)
|
||||
label_mob = TexMobject(label)
|
||||
label_mob.scale(self.default_label_scale_val)
|
||||
if isinstance(label, Mobject):
|
||||
label_mob = label
|
||||
else:
|
||||
label_mob = TexMobject(label)
|
||||
label_mob.scale(self.default_label_scale_val)
|
||||
label_mob.next_to(brace, direction, buff)
|
||||
full_label = VGroup(brace, label_mob)
|
||||
part.add_label(full_label)
|
||||
label_brace_groups.add(full_label)
|
||||
parts.labels = label_brace_groups
|
||||
|
||||
braces.add(brace)
|
||||
label_mobs.add(label_mob)
|
||||
parts.braces = braces
|
||||
parts.labels = label_mobs
|
||||
parts.label_kwargs = {
|
||||
"labels" : labels,
|
||||
"direction" : direction,
|
||||
"buff" : buff,
|
||||
}
|
||||
return label_brace_groups
|
||||
return VGroup(parts.braces, parts.labels)
|
||||
|
||||
def get_side_labels(self, labels, direction = LEFT, **kwargs):
|
||||
def get_side_braces_and_labels(self, labels, direction = LEFT, **kwargs):
|
||||
assert(hasattr(self, "horizontal_parts"))
|
||||
parts = self.horizontal_parts
|
||||
return self.get_subdivision_labels(parts, labels, direction, **kwargs)
|
||||
return self.get_subdivision_braces_and_labels(parts, labels, direction, **kwargs)
|
||||
|
||||
def get_top_labels(self, labels, **kwargs):
|
||||
def get_top_braces_and_labels(self, labels, **kwargs):
|
||||
assert(hasattr(self, "vertical_parts"))
|
||||
parts = self.vertical_parts
|
||||
return self.get_subdivision_labels(parts, labels, UP, **kwargs)
|
||||
return self.get_subdivision_braces_and_labels(parts, labels, UP, **kwargs)
|
||||
|
||||
def get_bototm_labels(self, labels, **kwargs):
|
||||
def get_bottom_braces_and_labels(self, labels, **kwargs):
|
||||
assert(hasattr(self, "vertical_parts"))
|
||||
parts = self.vertical_parts
|
||||
return self.get_subdivision_labels(parts, labels, DOWN, **kwargs)
|
||||
return self.get_subdivision_braces_and_labels(parts, labels, DOWN, **kwargs)
|
||||
|
||||
def add_braces_and_labels(self):
|
||||
for attr in "horizontal_parts", "vertical_parts":
|
||||
if not hasattr(self, attr):
|
||||
continue
|
||||
parts = getattr(self, attr)
|
||||
for subattr in "braces", "labels":
|
||||
if hasattr(parts, subattr):
|
||||
self.add(getattr(parts, subattr))
|
||||
|
||||
|
||||
### Cards ###
|
||||
|
|
Loading…
Add table
Reference in a new issue