From e4cc190ee78611ea29a00abb236a6c58786b1723 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Tue, 17 Apr 2018 18:35:35 +0200 Subject: [PATCH 01/21] added more options for histograms --- active_projects/eop/histograms.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/active_projects/eop/histograms.py b/active_projects/eop/histograms.py index 97ed6445..5f89a407 100644 --- a/active_projects/eop/histograms.py +++ b/active_projects/eop/histograms.py @@ -18,7 +18,11 @@ class Histogram(VMobject): "y_scale" : 1.0, "x_labels" : "auto", # widths, mids, auto, none, [...] "y_labels" : "auto", # auto, none, [...] - "x_min" : 0 + "y_label_position" : "top", # "center" + "x_min" : 0, + "bar_stroke_width" : 5, + "outline_stroke_width" : 0, + "stroke_color" : WHITE } def __init__(self, x_values, y_values, mode = "widths", **kwargs): @@ -119,6 +123,8 @@ class Histogram(VMobject): bar = Rectangle( width = self.widths_scaled[i], height = self.y_values_scaled[i], + stroke_width = self.bar_stroke_width, + stroke_color = self.stroke_color, ) t = float(x - self.x_min)/(self.x_max - self.x_min) bar_color = interpolate_color( @@ -127,7 +133,6 @@ class Histogram(VMobject): t ) bar.set_fill(color = bar_color, opacity = 1) - bar.set_stroke(width = 0) bar.next_to(previous_bar,RIGHT,buff = 0, aligned_edge = DOWN) self.bars.add(bar) @@ -137,7 +142,12 @@ class Histogram(VMobject): self.x_labels_group.add(x_label) y_label = TextMobject(self.y_labels[i]) - y_label.next_to(bar, UP) + if self.y_label_position == "top": + y_label.next_to(bar, UP) + elif self.y_label_position == "center": + y_label.move_to(bar) + else: + raise Exception("y_label_position must be top or center") self.y_labels_group.add(y_label) if i == 0: @@ -155,8 +165,9 @@ class Histogram(VMobject): # lower left outline_points.append(outline_points[0]) - self.outline = Polygon(*outline_points) - self.outline.set_stroke(color = WHITE) + self.outline = Polygon(*outline_points, + stroke_width = self.outline_stroke_width, + stroke_color = self.stroke_color) self.add(self.bars, self.x_labels_group, self.y_labels_group, self.outline) print self.submobjects From a6919b087a1ad60125463eca58595df6675665a3 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Tue, 17 Apr 2018 18:35:59 +0200 Subject: [PATCH 02/21] merged code from pascal.py --- active_projects/eop/chapter1.py | 39 +++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/active_projects/eop/chapter1.py b/active_projects/eop/chapter1.py index 8830cd86..3f005104 100644 --- a/active_projects/eop/chapter1.py +++ b/active_projects/eop/chapter1.py @@ -1102,15 +1102,8 @@ class DieFace(SVGMobject): self.value = value self.file_name = "Dice-" + str(value) self.ensure_valid_file() - - paths, attributes = svg2paths(self.file_path) - print paths, attributes SVGMobject.__init__(self, file_name = self.file_name) - # for submob in self.submobject_family(): - # if type(submob) == Rectangle: - # submob.set_fill(opacity = 0) - # submob.set_stroke(width = 7) - + class RowOfDice(VGroup): CONFIG = { "values" : range(1,7) @@ -1126,7 +1119,7 @@ class RowOfDice(VGroup): -class ShowUncertainty1(PiCreatureScene): +class ShowUncertainty1(Scene): def throw_a_die(self): @@ -1147,8 +1140,6 @@ class ShowUncertainty1(PiCreatureScene): run_time = 0.5) ) - - def hist_from_tallies(self): x_scale = self.row_of_dice.get_width() / np.size(self.tallies) hist = Histogram(np.ones(6), self.tallies, @@ -1163,7 +1154,7 @@ class ShowUncertainty1(PiCreatureScene): def construct(self): - self.row_of_dice = RowOfDice().scale(0.5).move_to(3 * DOWN) + self.row_of_dice = RowOfDice().scale(0.5).move_to(2 * DOWN) self.add(self.row_of_dice) self.tallies = np.zeros(6) @@ -1177,6 +1168,30 @@ class ShowUncertainty1(PiCreatureScene): +class IdealizedDieHistogram(Scene): + + def construct(self): + + self.row_of_dice = RowOfDice().scale(0.5).move_to(2 * DOWN) + self.add(self.row_of_dice) + self.probs = 1.0/6 * np.ones(6) + x_scale = self.row_of_dice.get_width() / np.size(self.probs) + + y_labels = ["${1\over 6}$"] * 6 + + hist = Histogram(np.ones(6), self.probs, + mode = "widths", + x_labels = "none", + y_labels = y_labels, + y_label_position = "center", + y_scale = 20, + x_scale = x_scale, + ) + hist.next_to(self.row_of_dice, UP) + + self.add(hist) + + class ShowUncertainty2(PiCreatureScene): From 1e663fa2abe8d5362580b2f7ed860b550289a114 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Wed, 18 Apr 2018 20:26:03 +0200 Subject: [PATCH 03/21] cleanup --- active_projects/eop/histograms.py | 55 ------------------------------- 1 file changed, 55 deletions(-) diff --git a/active_projects/eop/histograms.py b/active_projects/eop/histograms.py index 5f89a407..01d1c356 100644 --- a/active_projects/eop/histograms.py +++ b/active_projects/eop/histograms.py @@ -285,58 +285,3 @@ class FlashThroughHistogram(Animation): - -class SampleScene(Scene): - - def construct(self): - - x_values = np.array([1,2,3,4,5]) - y_values = np.array([4,3,5,2,3]) - - hist1 = Histogram( - x_values = x_values, - y_values = y_values, - x_scale = 0.5, - y_scale = 0.5, - ).shift(1*DOWN) - self.add(hist1) - self.wait() - - y_values2 = np.array([3,8,7,15,5]) - - hist2 = Histogram( - x_values = x_values, - y_values = y_values2, - x_scale = 0.5, - y_scale = 0.5, - x_labels = text_range(1,6,1), - ) - - v1 = hist1.get_lower_left_point() - v2 = hist2.get_lower_left_point() - hist2.shift(v1 - v2) - - # self.play( - # ReplacementTransform(hist1,hist2) - # ) - - self.play( - FlashThroughHistogram( - hist1, - direction = "horizontal", - mode = "linear", - run_time = 10, - rate_func = None, - ) - ) - - - - - - - - - - - From 11a0145f26b2328e5d457218d60766fbd93d9bf1 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Wed, 18 Apr 2018 20:26:14 +0200 Subject: [PATCH 04/21] moving code --- active_projects/eop/pascal.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/active_projects/eop/pascal.py b/active_projects/eop/pascal.py index 688d3a5e..ad2a0819 100644 --- a/active_projects/eop/pascal.py +++ b/active_projects/eop/pascal.py @@ -10,21 +10,6 @@ GRADE_COLOR_1 = RED GRADE_COLOR_2 = BLUE -def rainbow_color(alpha): - nb_colors = 100 - rainbow = color_gradient([RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE], nb_colors) - rainbow = np.append(rainbow,PURPLE) - index = int(alpha * nb_colors) - return rainbow[index] - -def graded_color(n,k): - if n != 0: - alpha = float(k)/n - else: - alpha = 0.5 - color = interpolate_color(GRADE_COLOR_1, GRADE_COLOR_2, alpha) - return color - def graded_square(n,k): return Square( From dcf9f5c98dfa5ae42b7ab1ae2671e4462a2ac764 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Wed, 18 Apr 2018 20:26:49 +0200 Subject: [PATCH 05/21] cleanup --- active_projects/eop/area_coin_toss.py | 243 -------------------------- 1 file changed, 243 deletions(-) delete mode 100644 active_projects/eop/area_coin_toss.py diff --git a/active_projects/eop/area_coin_toss.py b/active_projects/eop/area_coin_toss.py deleted file mode 100644 index e1e64fc4..00000000 --- a/active_projects/eop/area_coin_toss.py +++ /dev/null @@ -1,243 +0,0 @@ -from big_ol_pile_of_manim_imports import * -from eop.pascal import * - - -WIDTH = 12 -HEIGHT = 1 -GRADE_COLOR_1 = COLOR_HEADS = RED -GRADE_COLOR_2 = COLOR_TAILS = BLUE -NB_ROWS = 6 - - -class Coin(Circle): - CONFIG = { - "radius": 0.2, - "stroke_width": 3, - "stroke_color": WHITE, - "fill_opacity": 1, - "symbol": "\euro", - } - - def __init__(self, **kwargs): - Circle.__init__(self,**kwargs) - self.symbol_mob = TextMobject(self.symbol, stroke_color = self.stroke_color) - self.symbol_mob.scale_to_fit_height(0.5*self.get_height()).move_to(self) - self.add(self.symbol_mob) - - -class Heads(Coin): - CONFIG = { - "fill_color": COLOR_HEADS, - "symbol": "H", - } - - -class Tails(Coin): - CONFIG = { - "fill_color": COLOR_TAILS, - "symbol": "T", - } - -class CoinStack(VGroup): - CONFIG = { - "spacing": 0.1, - "size": 5, - "face": Heads, - } - - def generate_points(self): - for n in range(self.size): - coin = self.face() - coin.shift(n * self.spacing * RIGHT) - self.add(coin) - -class HeadsStack(CoinStack): - CONFIG = { "face": Heads } - -class TailsStack(CoinStack): - CONFIG = { "face": Tails } - -class TallyStack(VGroup): - - def __init__(self,h,t,**kwargs): - self.nb_heads = h - self.nb_tails = t - VGroup.__init__(self,**kwargs) - - def generate_points(self): - stack1 = HeadsStack(size = self.nb_heads) - stack2 = TailsStack(size = self.nb_tails) - stack2.next_to(stack1, RIGHT, buff = SMALL_BUFF) - self.add(stack1, stack2) - - -class AreaSplittingScene(Scene): - - def create_rect_row(self,n): - rects_group = VGroup() - for k in range(n+1): - proportion = float(choose(n,k)) / 2**n - new_rect = Rectangle( - width = proportion * WIDTH, - height = HEIGHT, - fill_color = graded_color(n,k), - fill_opacity = 1 - ) - new_rect.next_to(rects_group,RIGHT,buff = 0) - rects_group.add(new_rect) - return rects_group - - def split_rect_row(self,rect_row): - - split_row = VGroup() - for rect in rect_row.submobjects: - half = rect.copy().stretch_in_place(0.5,0) - left_half = half.next_to(rect.get_center(),LEFT,buff = 0) - right_half = half.copy().next_to(rect.get_center(),RIGHT,buff = 0) - split_row.add(left_half, right_half) - return split_row - - - def rect_center(self,n,i,j): - if n < 0: - raise Exception("wrong indices " + str(n) + ", " + str(i) + ", " + str(j)) - if i < 0 or i > n: - raise Exception("wrong indices " + str(n) + ", " + str(i) + ", " + str(j)) - if j > choose(n,i) or j < 0: - raise Exception("wrong indices " + str(n) + ", " + str(i) + ", " + str(j)) - - rect = self.brick_array[n][i] - width = rect.get_width() - left_x = rect.get_center()[0] - width/2 - spacing = width / choose(n,i) - x = left_x + (j+0.5) * spacing - return np.array([x,rect.get_center()[1], rect.get_center()[2]]) - - def construct(self): - - # Draw the bricks - - brick_wall = VGroup() - rect_row = self.create_rect_row(0) - rect_row.move_to(3.5*UP + 0*HEIGHT*DOWN) - self.add(rect_row) - brick_wall.add(rect_row) - self.brick_array = [[rect_row.submobjects[0]]] - - for n in range(NB_ROWS): - # copy and shift - new_rect_row = rect_row.copy() - self.add(new_rect_row) - self.play(new_rect_row.shift,HEIGHT * DOWN) - self.wait() - - #split - split_row = self.split_rect_row(new_rect_row) - self.play(FadeIn(split_row)) - self.remove(new_rect_row) - self.wait() - - # merge - rect_row = self.create_rect_row(n+1) - rect_row.move_to(3.5*UP + (n+1)*HEIGHT*DOWN) - self.play(FadeIn(rect_row)) - brick_wall.add(rect_row) - self.remove(split_row) - self.wait() - - # add to brick dict - rect_array = [] - for rect in rect_row.submobjects: - rect_array.append(rect) - - self.brick_array.append(rect_array) - - - self.play( - brick_wall.set_fill, {"opacity" : 0.2} - ) - - - # Draw the branches - - for (n, rect_row_array) in enumerate(self.brick_array): - for (i, rect) in enumerate(rect_row_array): - pos = rect.get_center() - tally = TallyStack(n - i, i) - tally.move_to(pos) - - - # from the left - lines = VGroup() - - if i > 0: - for j in range(choose(n-1,i-1)): - start_pos = self.rect_center(n-1,i-1,j) - end_pos = self.rect_center(n,i,j) - lines.add(Line(start_pos,end_pos, stroke_color = GRADE_COLOR_2)) - self.play( - LaggedStart(ShowCreation, lines)) - - # from the right - lines = VGroup() - - if i < n: - for j in range(choose(n-1,i)): - start_pos = self.rect_center(n-1,i,j) - if i != 0: - end_pos = self.rect_center(n,i,choose(n-1,i-1) + j) - else: - end_pos = self.rect_center(n,i,j) - - lines.add(Line(start_pos,end_pos, stroke_color = GRADE_COLOR_1)) - self.play( - LaggedStart(ShowCreation, lines)) - - - - #self.play(FadeIn(tally)) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - self.wait() - - - From 6d632bfaf544d610828d2ceba7369203d01f69a2 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Wed, 18 Apr 2018 20:27:15 +0200 Subject: [PATCH 06/21] progress on main animation (areas and tallies splitting and merging) --- active_projects/eop/chapter1.py | 672 ++++++++++++++++++++++---------- 1 file changed, 465 insertions(+), 207 deletions(-) diff --git a/active_projects/eop/chapter1.py b/active_projects/eop/chapter1.py index 3f005104..a71c40a0 100644 --- a/active_projects/eop/chapter1.py +++ b/active_projects/eop/chapter1.py @@ -4,7 +4,7 @@ from active_projects.eop.histograms import * import scipy.special -COIN_RADIUS = 0.3 +COIN_RADIUS = 0.25 COIN_THICKNESS = 0.4 * COIN_RADIUS COIN_FORESHORTENING = 0.3 COIN_NB_RIDGES = 20 @@ -34,6 +34,23 @@ def binary(i): def nb_of_ones(i): return binary(i).count(1) + +def rainbow_color(alpha): + nb_colors = 100 + rainbow = color_gradient([RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE], nb_colors) + rainbow = np.append(rainbow,PURPLE) + index = int(alpha * nb_colors) + return rainbow[index] + +def graded_color(n,k): + if n != 0: + alpha = float(k)/n + else: + alpha = 0.5 + color = interpolate_color(GRADE_COLOR_1, GRADE_COLOR_2, alpha) + return color + + class PiCreatureCoin(VMobject): CONFIG = { "diameter": 0.8, @@ -296,6 +313,7 @@ class TallyStack(VGroup): def move_anchor_to(self, new_anchor): for submob in self.submobjects: submob.shift(new_anchor - self.anchor) + self.anchor = new_anchor return self @@ -697,83 +715,6 @@ class Introduction(TeacherStudentsScene): ) -# # # # # # # # # # # # # # # # # -# Old version with SampleSpace # -# # # # # # # # # # # # # # # # # - - # def show_independent_events(self): - # sample_space = SampleSpace( - # full_space_config = { - # "height" : 3, - # "width" : 3, - # "fill_opacity" : 0 - # } - # ) - # sample_space.divide_horizontally(0.4) - # sample_space.horizontal_parts.set_fill(opacity = 0) - # h_labels = [ - # TexMobject("P(", "A", ")"), - # TexMobject("P(\\text{not }", "A", ")"), - # ] - # for label in h_labels: - # label.scale(0.7) - # #self.color_label(label) - # sample_space.get_side_braces_and_labels(h_labels) - # sample_space.add_braces_and_labels() - # h_parts = sample_space.horizontal_parts - # for (label, part) in zip(h_labels, h_parts): - # label.next_to(part, 2 * LEFT) - # sample_space.add(label) - - # values = [0.2, 0.2] - # color_pairs = [(GREEN, BLUE), (GREEN_E, BLUE_E)] - # v_parts = VGroup() - # for tup in zip(h_parts, values, color_pairs): - # part, value, colors = tup - # part.divide_vertically(value, colors = colors) - # part.vertical_parts.set_fill(opacity = 0.8) - # #label = TexMobject( - # # "P(", "B", "|", given_str, "A", ")" - # #) - # #label.scale(0.7) - # #self.color_label(label) - # if part == h_parts[0]: - # part.get_subdivision_braces_and_labels( - # part.vertical_parts, [label], DOWN - # ) - # sample_space.add( - # part.vertical_parts.braces, - # # part.vertical_parts.labels, - # ) - # v_parts.add(part.vertical_parts.submobjects) - - - # v_labels = [ - # TexMobject("P(", "B", ")"), - # TexMobject("P(\\text{not }", "B", ")"), - # ] - # for (label, part) in zip(v_labels, v_parts[1::2]): - # label.scale(0.7) - # label.next_to(part, DOWN) - # sample_space.add(label) - - - # sample_space.to_edge(LEFT) - - # self.add(sample_space) - # self.sample_space = sample_space - - # self.wait() - - - - - - # def color_label(self, label): - # label.set_color_by_tex("B", RED) - # label.set_color_by_tex("I", GREEN) - - class IllustrateAreaModel2(GraphScene): @@ -967,134 +908,6 @@ class IllustrateAreaModel3(Scene): - -class AreaSplitting(Scene): - - def create_rect_row(self,n): - rects_group = VGroup() - for k in range(n+1): - proportion = float(choose(n,k)) / 2**n - new_rect = Rectangle( - width = proportion * WIDTH, - height = HEIGHT, - fill_color = graded_color(n,k), - fill_opacity = 1 - ) - new_rect.next_to(rects_group,RIGHT,buff = 0) - rects_group.add(new_rect) - return rects_group - - def split_rect_row(self,rect_row): - - split_row = VGroup() - for rect in rect_row.submobjects: - half = rect.copy().stretch_in_place(0.5,0) - left_half = half.next_to(rect.get_center(),LEFT,buff = 0) - right_half = half.copy().next_to(rect.get_center(),RIGHT,buff = 0) - split_row.add(left_half, right_half) - return split_row - - - def rect_center(self,n,i,j): - if n < 0: - raise Exception("wrong indices " + str(n) + ", " + str(i) + ", " + str(j)) - if i < 0 or i > n: - raise Exception("wrong indices " + str(n) + ", " + str(i) + ", " + str(j)) - if j > choose(n,i) or j < 0: - raise Exception("wrong indices " + str(n) + ", " + str(i) + ", " + str(j)) - - rect = self.brick_array[n][i] - width = rect.get_width() - left_x = rect.get_center()[0] - width/2 - spacing = width / choose(n,i) - x = left_x + (j+0.5) * spacing - return np.array([x,rect.get_center()[1], rect.get_center()[2]]) - - def construct(self): - - # Draw the bricks - - brick_wall = VGroup() - rect_row = self.create_rect_row(0) - rect_row.move_to(3.5*UP + 0*HEIGHT*DOWN) - self.add(rect_row) - brick_wall.add(rect_row) - self.brick_array = [[rect_row.submobjects[0]]] - - for n in range(NB_ROWS): - # copy and shift - new_rect_row = rect_row.copy() - self.add(new_rect_row) - self.play(new_rect_row.shift,HEIGHT * DOWN) - self.wait() - - #split - split_row = self.split_rect_row(new_rect_row) - self.play(FadeIn(split_row)) - self.remove(new_rect_row) - self.wait() - - # merge - rect_row = self.create_rect_row(n+1) - rect_row.move_to(3.5*UP + (n+1)*HEIGHT*DOWN) - self.play(FadeIn(rect_row)) - brick_wall.add(rect_row) - self.remove(split_row) - self.wait() - - # add to brick dict - rect_array = [] - for rect in rect_row.submobjects: - rect_array.append(rect) - - self.brick_array.append(rect_array) - - - self.play( - brick_wall.set_fill, {"opacity" : 0.2} - ) - - - # Draw the branches - - for (n, rect_row_array) in enumerate(self.brick_array): - for (i, rect) in enumerate(rect_row_array): - pos = rect.get_center() - tally = TallyStack(n - i, i) - tally.move_to(pos) - - - # from the left - lines = VGroup() - - if i > 0: - for j in range(choose(n-1,i-1)): - start_pos = self.rect_center(n-1,i-1,j) - end_pos = self.rect_center(n,i,j) - lines.add(Line(start_pos,end_pos, stroke_color = GRADE_COLOR_2)) - self.play( - LaggedStart(ShowCreation, lines)) - - # from the right - lines = VGroup() - - if i < n: - for j in range(choose(n-1,i)): - start_pos = self.rect_center(n-1,i,j) - if i != 0: - end_pos = self.rect_center(n,i,choose(n-1,i-1) + j) - else: - end_pos = self.rect_center(n,i,j) - - lines.add(Line(start_pos,end_pos, stroke_color = GRADE_COLOR_1)) - self.play( - LaggedStart(ShowCreation, lines)) - - - - #self.play(FadeIn(tally)) - - class DieFace(SVGMobject): def __init__(self, value, **kwargs): @@ -1281,7 +1094,452 @@ class ShowUncertainty3(Scene): - +class PascalBrickRow(VMobject): + + CONFIG = { + "left_color" : YELLOW, + "right_color" : BLUE, + "height" : 1.0, + "width" : 8.0, + } + + def __init__(self, n, **kwargs): + self.subdiv_level = n + self.coloring_level = n + VMobject.__init__(self, **kwargs) + + + def generate_points(self): + + self.submobjects = [] + self.rects = self.get_rects_for_level(self.coloring_level) + self.add(self.rects) + self.subdivs = self.get_subdivs_for_level(self.subdiv_level) + self.add(self.subdivs) + + self.border = SurroundingRectangle(self, + buff = 0, color = WHITE) + self.add(self.border) + + + + def get_rects_for_level(self,r): + rects = VGroup() + for k in range(r + 1): + proportion = float(choose(r,k)) / 2**r + new_rect = Rectangle( + width = proportion * self.width, + height = self.height, + fill_color = graded_color(r,k), + fill_opacity = 1, + stroke_width = 0 + ) + if len(rects.submobjects) > 0: + new_rect.next_to(rects,RIGHT,buff = 0) + else: + new_rect.next_to(0.5 * self.width * LEFT, RIGHT, buff = 0) + rects.add(new_rect) + return rects + + def get_subdivs_for_level(self,r): + subdivs = VGroup() + x = - 0.5 * self.width + for k in range(0, r): + proportion = float(choose(r,k)) / 2**r + x += proportion * self.width + subdiv = Line( + x * RIGHT + 0.5 * self.height * UP, + x * RIGHT + 0.5 * self.height * DOWN, + ) + subdivs.add(subdiv) + return subdivs + + + def get_outcome_centers_for_level(self,r): + + dpos = float(self.width) / (2 ** r) * RIGHT + print "dpos =", dpos + pos = 0.5 * self.width * LEFT + 0.5 * dpos + centers = [] + for k in range(0, 2 ** r): + centers.append(self.get_center() + pos + k * dpos) + + return centers + + def get_outcome_width_for_level(self,r): + return self.width / (2**r) + + def get_rect_widths_for_level_(self, r): + ret_arr = [] + for k in range(0, r): + proportion = float(choose(r,k)) / 2**r + ret_arr.append(proportion * self.width) + return ret_arr + + + + + +class SplitRectsInBrickWall(Animation): + + def __init__(self, mobject, **kwargs): + + r = self.subdiv_level = mobject.subdiv_level + 1 + + self.subdivs = VGroup() + x = - 0.5 * mobject.width + + for k in range(0, r): + proportion = float(choose(r,k)) / 2**r + x += proportion * mobject.width + subdiv = DashedLine( + x * RIGHT + 0.5 * mobject.height * UP, + x * RIGHT + 0.5 * mobject.height * UP, + ) + self.subdivs.add(subdiv) + mobject.add(self.subdivs) + + Animation.__init__(self, mobject, **kwargs) + + + + + + def update_mobject(self, alpha): + for subdiv in self.subdivs: + x = subdiv.get_start()[0] + start = x * RIGHT + 0.5 * self.mobject.height * UP + end = start + alpha * 0.5 * self.mobject.height * (DOWN - UP) + subdiv.put_start_and_end_on(start,end) + + + + + +class PascalBrickRowScene(Scene): + + def split_tallies(self): + + 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 + ] + 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) + 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) + 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 merge_rects_by_subdiv(self): + + half_merged_row = self.row.copy() + half_merged_row.subdiv_level += 1 + half_merged_row.generate_points() + self.play(FadeIn(half_merged_row)) + self.row = half_merged_row + + def merge_tallies(self): + + r = self.row.subdiv_level + tally_targets = [ + rect.get_center() + for rect in self.row.get_rects_for_level(r) + ] + + 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() + + self.play(FadeIn(merged_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 construct(self): + + randy = CoinFlippingPiCreature() + randy = randy.scale(0.5).move_to(3*DOWN + 6*LEFT) + self.add(randy) + self.row = PascalBrickRow(1, height = 2, width = 10) + + self.play(FlipCoin(randy), + FadeIn(self.row)) + + self.wait() + + # put tallies on top + + self.tallies = VGroup(*[ + TallyStack(1 - i, i) for i in range(2) + ]) + for (tally, rect) in zip(self.tallies, self.row.rects): + new_anchor = rect.get_center() + 1.2 * 0.5 * rect.get_height() * UP + tally.move_anchor_to(new_anchor) + self.play(FadeIn(tally)) + + self.add_foreground_mobject(self.tallies) + self.wait() + + + + # # # # # # # # + # SECOND FLIP # + # # # # # # # # + + + + self.play(FlipCoin(randy)) + self.wait() + + + self.play( + SplitRectsInBrickWall(self.row) + ) + self.wait() + + self.split_tallies() + self.wait() + self.merge_rects_by_subdiv() + self.wait() + self.merge_tallies() + self.merge_rects_by_coloring() + self.wait() + self.move_tallies_on_top() + + + # # # # # # # # + # THIRD FLIP # + # # # # # # # # + + + self.play(FlipCoin(randy)) + + self.wait() + + + self.play( + SplitRectsInBrickWall(self.row) + ) + self.wait() + + self.split_tallies() + self.wait() + self.merge_rects_by_subdiv() + self.wait() + self.merge_tallies() + self.merge_rects_by_coloring() + self.wait() + self.move_tallies_on_top() + + + # # # # # # # # + # FOURTH FLIP # + # # # # # # # # + + + self.play(FlipCoin(randy)) + + self.wait() + + + self.play( + SplitRectsInBrickWall(self.row) + ) + self.wait() + + self.split_tallies() + self.wait() + self.merge_rects_by_subdiv() + self.wait() + self.merge_tallies() + self.merge_rects_by_coloring() + self.wait() + self.move_tallies_on_top() + + + + + + +class AreaSplitting(Scene): + + def create_rect_row(self,n): + rects_group = VGroup() + for k in range(n+1): + proportion = float(choose(n,k)) / 2**n + new_rect = Rectangle( + width = proportion * WIDTH, + height = HEIGHT, + fill_color = graded_color(n,k), + fill_opacity = 1 + ) + new_rect.next_to(rects_group,RIGHT,buff = 0) + rects_group.add(new_rect) + return rects_group + + def split_rect_row(self,rect_row): + + split_row = VGroup() + for rect in rect_row.submobjects: + half = rect.copy().stretch_in_place(0.5,0) + left_half = half.next_to(rect.get_center(),LEFT,buff = 0) + right_half = half.copy().next_to(rect.get_center(),RIGHT,buff = 0) + split_row.add(left_half, right_half) + return split_row + + + def rect_center(self,n,i,j): + if n < 0: + raise Exception("wrong indices " + str(n) + ", " + str(i) + ", " + str(j)) + if i < 0 or i > n: + raise Exception("wrong indices " + str(n) + ", " + str(i) + ", " + str(j)) + if j > choose(n,i) or j < 0: + raise Exception("wrong indices " + str(n) + ", " + str(i) + ", " + str(j)) + + rect = self.brick_array[n][i] + width = rect.get_width() + left_x = rect.get_center()[0] - width/2 + spacing = width / choose(n,i) + x = left_x + (j+0.5) * spacing + return np.array([x,rect.get_center()[1], rect.get_center()[2]]) + + def construct(self): + + # Draw the bricks + + brick_wall = VGroup() + rect_row = self.create_rect_row(0) + rect_row.move_to(3.5*UP + 0*HEIGHT*DOWN) + self.add(rect_row) + brick_wall.add(rect_row) + self.brick_array = [[rect_row.submobjects[0]]] + + for n in range(NB_ROWS): + # copy and shift + new_rect_row = rect_row.copy() + self.add(new_rect_row) + self.play(new_rect_row.shift,HEIGHT * DOWN) + self.wait() + + #split + split_row = self.split_rect_row(new_rect_row) + self.play(FadeIn(split_row)) + self.remove(new_rect_row) + self.wait() + + # merge + rect_row = self.create_rect_row(n+1) + rect_row.move_to(3.5*UP + (n+1)*HEIGHT*DOWN) + self.play(FadeIn(rect_row)) + brick_wall.add(rect_row) + self.remove(split_row) + self.wait() + + # add to brick dict + rect_array = [] + for rect in rect_row.submobjects: + rect_array.append(rect) + + self.brick_array.append(rect_array) + + + self.play( + brick_wall.set_fill, {"opacity" : 0.2} + ) + + + # Draw the branches + + for (n, rect_row_array) in enumerate(self.brick_array): + for (i, rect) in enumerate(rect_row_array): + pos = rect.get_center() + tally = TallyStack(n - i, i) + tally.move_to(pos) + + + # from the left + lines = VGroup() + + if i > 0: + for j in range(choose(n-1,i-1)): + start_pos = self.rect_center(n-1,i-1,j) + end_pos = self.rect_center(n,i,j) + lines.add(Line(start_pos,end_pos, stroke_color = GRADE_COLOR_2)) + self.play( + LaggedStart(ShowCreation, lines)) + + # from the right + lines = VGroup() + + if i < n: + for j in range(choose(n-1,i)): + start_pos = self.rect_center(n-1,i,j) + if i != 0: + end_pos = self.rect_center(n,i,choose(n-1,i-1) + j) + else: + end_pos = self.rect_center(n,i,j) + + lines.add(Line(start_pos,end_pos, stroke_color = GRADE_COLOR_1)) + self.play( + LaggedStart(ShowCreation, lines)) + + + + #self.play(FadeIn(tally)) From 7427e376a98c3ba3316d4d2810715f94f94920d4 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Thu, 19 Apr 2018 17:19:31 +0200 Subject: [PATCH 07/21] improvements on the brick wall scene --- active_projects/eop/chapter1.py | 503 +++++++++++++++++++++++--------- 1 file changed, 361 insertions(+), 142 deletions(-) diff --git a/active_projects/eop/chapter1.py b/active_projects/eop/chapter1.py index a71c40a0..27ddb76e 100644 --- a/active_projects/eop/chapter1.py +++ b/active_projects/eop/chapter1.py @@ -4,9 +4,9 @@ from active_projects.eop.histograms import * import scipy.special -COIN_RADIUS = 0.25 +COIN_RADIUS = 0.18 COIN_THICKNESS = 0.4 * COIN_RADIUS -COIN_FORESHORTENING = 0.3 +COIN_FORESHORTENING = 0.5 COIN_NB_RIDGES = 20 COIN_STROKE_WIDTH = 2 @@ -15,6 +15,7 @@ COIN_SEQUENCE_SPACING = 0.1 GRADE_COLOR_1 = COLOR_HEADS = RED GRADE_COLOR_2 = COLOR_TAILS = BLUE +TALLY_BACKGROUND_WIDTH = 1.0 def binary(i): # returns an array of 0s and 1s @@ -175,7 +176,8 @@ class UprightTails(UprightCoin): class CoinSequence(VGroup): CONFIG = { "sequence": [], - "spacing": COIN_SEQUENCE_SPACING + "spacing": COIN_SEQUENCE_SPACING, + "direction": RIGHT } def __init__(self, sequence, **kwargs): @@ -189,7 +191,7 @@ class CoinSequence(VGroup): new_coin = UprightTails() else: new_coin = UprightCoin(symbol = symbol) - new_coin.shift(offset * RIGHT) + new_coin.shift(offset * self.direction) self.add(new_coin) offset += self.spacing @@ -302,13 +304,22 @@ class TallyStack(VGroup): def generate_points(self): stack1 = HeadsStack(size = self.nb_heads, coin_thickness = self.coin_thickness) stack2 = TailsStack(size = self.nb_tails, coin_thickness = self.coin_thickness) - stack1.next_to(self.anchor, LEFT, buff = SMALL_BUFF) - stack2.next_to(self.anchor, RIGHT, buff = SMALL_BUFF) + stack1.next_to(self.anchor, LEFT, buff = 0.5 * SMALL_BUFF) + stack2.next_to(self.anchor, RIGHT, buff = 0.5 * SMALL_BUFF) stack1.align_to(self.anchor, DOWN) stack2.align_to(self.anchor, DOWN) self.heads_stack = stack1 self.tails_stack = stack2 self.add(stack1, stack2) + background_rect = RoundedRectangle( + width = TALLY_BACKGROUND_WIDTH, + height = TALLY_BACKGROUND_WIDTH, + corner_radius = 0.1, + fill_color = DARK_GREY, + fill_opacity = 1.0, + stroke_width = 3 + ).align_to(self.anchor, DOWN).shift(0.1 * DOWN) + self.add_to_back(background_rect) def move_anchor_to(self, new_anchor): for submob in self.submobjects: @@ -1101,6 +1112,7 @@ class PascalBrickRow(VMobject): "right_color" : BLUE, "height" : 1.0, "width" : 8.0, + "outcome_shrinkage_factor" : 0.85 } def __init__(self, n, **kwargs): @@ -1166,10 +1178,70 @@ class PascalBrickRow(VMobject): return centers + def get_outcome_rects_for_level(self,r, with_labels = False): + + centers = self.get_outcome_centers_for_level(r) + outcome_width = self.outcome_shrinkage_factor * float(self.width) / (2 ** r) + outcome_height = self.outcome_shrinkage_factor * self.height + rect = RoundedRectangle( + width = outcome_width, + height = outcome_height, + corner_radius = 0.1, + fill_color = BLACK, + fill_opacity = 0.2, + stroke_width = 0 + ) + rects = VGroup() + for center in centers: + rects.add(rect.copy().move_to(center)) + + if with_labels == False: + return rects + + # else + sequences = self.get_coin_sequences_for_level(r) + labels = VGroup() + for (seq, rect) in zip(sequences, rects): + coin_seq = CoinSequence(seq, direction = DOWN) + coin_seq.move_to(rect) + rect.add(coin_seq) + + return rects + + def get_coin_sequences_for_level(self,r): + # array of arrays of characters + if r < 0 or int(r) != r: + raise Exception("Level must be a positive integer") + if r == 0: + return [] + if r == 1: + return [["H"], ["T"]] + + previous_seq_array = self.get_coin_sequences_for_level(r - 1) + subdiv_lengths = [choose(r - 1, k) for k in range(r)] + + seq_array = [] + index = 0 + for length in subdiv_lengths: + + for seq in previous_seq_array[index:index + length]: + seq_copy = copy.copy(seq) + seq_copy.append("H") + seq_array.append(seq_copy) + + for seq in previous_seq_array[index:index + length]: + seq_copy = copy.copy(seq) + seq_copy.append("T") + seq_array.append(seq_copy) + index += length + + return seq_array + + def get_outcome_width_for_level(self,r): return self.width / (2**r) - def get_rect_widths_for_level_(self, r): + def get_rect_widths_for_level(self, r): ret_arr = [] for k in range(0, r): proportion = float(choose(r,k)) / 2**r @@ -1218,7 +1290,7 @@ class SplitRectsInBrickWall(Animation): class PascalBrickRowScene(Scene): - def split_tallies(self): + def split_tallies(self, direction = DOWN): self.tallies_copy = self.tallies.copy() self.add_foreground_mobject(self.tallies_copy) @@ -1232,19 +1304,42 @@ class PascalBrickRowScene(Scene): 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): + if len(self.decimals) > 0: + decimal = self.decimals[i] + else: + decimal = VMobject() + 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) - self.play(tally.move_anchor_to, target_left) + v = target_left - tally.anchor + + self.play( + tally.move_anchor_to, target_left, + decimal.shift,v + ) tally.anchor = target_left self.play(Transform(tally, new_tally_left)) tally_copy = self.tallies_copy[i] + decimal_copy = decimal.copy() + 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)) @@ -1256,6 +1351,130 @@ class PascalBrickRowScene(Scene): tally.nb_tails = new_tally_left.nb_tails + + + def split_tallies_at_once(self, direction = DOWN): + + 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 = [] + if len(self.decimals) > 0: + self.decimal_copies = VGroup() + + for (i, tally) in enumerate(self.tallies): + + if len(self.decimals) > 0: + decimal = self.decimals[i] + else: + decimal = VMobject() + + target_left = tally_targets_left[i] + v = target_left - tally.anchor + + anims1.append(tally.move_anchor_to) + anims1.append(target_left) + anims1.append(decimal.shift) + anims1.append(v) + + tally.anchor = target_left + + tally_copy = self.tallies_copy[i] + decimal_copy = decimal.copy() + + target_right = tally_targets_right[i] + v = target_right - tally_copy.anchor + + anims1.append(tally_copy.move_anchor_to) + anims1.append(target_right) + anims1.append(decimal_copy.shift) + anims1.append(v) + self.decimal_copies.add(decimal_copy) + + tally_copy.anchor = target_right + + + self.play(*anims1) + anims2 = [] + + for (i, tally) in enumerate(self.tallies): + + new_tally_left = TallyStack(tally.nb_heads + 1, tally.nb_tails) + new_tally_left.move_anchor_to(tally.anchor) + anims2.append(Transform(tally, new_tally_left)) + + tally_copy = self.tallies_copy[i] + + new_tally_right = TallyStack(tally.nb_heads, tally.nb_tails + 1) + new_tally_right.move_anchor_to(tally_copy.anchor) + anims2.append(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 + + + self.play(*anims2) + + if len(self.decimals) > 0: + self.add_foreground_mobject(self.decimal_copies) + + + def split_decimals_alone(self): + + r = self.row.coloring_level + targets_left = [] + targets_right = [] + + for rect in self.row.get_rects_for_level(r): + target = rect.get_center() + 0.25 * rect.get_width() * LEFT + targets_left.append(target) + target = rect.get_center() + 0.25 * rect.get_width() * RIGHT + targets_right.append(target) + + anims = [] + self.decimal_copies = VGroup() + + for (i, decimal) in enumerate(self.decimals): + + anims.append(decimal.move_to) + anims.append(targets_left[i]) + + decimal_copy = decimal.copy() + + anims.append(decimal_copy.move_to) + anims.append(targets_right[i]) + self.decimal_copies.add(decimal_copy) + + + self.play(*anims) + + self.add_foreground_mobject(self.decimal_copies) + + + + + def merge_rects_by_subdiv(self): half_merged_row = self.row.copy() @@ -1264,7 +1483,7 @@ class PascalBrickRowScene(Scene): self.play(FadeIn(half_merged_row)) self.row = half_merged_row - def merge_tallies(self): + def merge_tallies(self, direction = UP): r = self.row.subdiv_level tally_targets = [ @@ -1272,6 +1491,11 @@ class PascalBrickRowScene(Scene): 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) @@ -1302,6 +1526,35 @@ class PascalBrickRowScene(Scene): self.row = merged_row + def merge_decimals(self): + + anims = [] + if self.decimals in self.mobjects: + anims.append(FadeOut(self.decimals)) + if self.decimal_copies in self.mobjects: + anims.append(FadeOut(self.decimal_copies)) + + self.new_decimals = VGroup() + self.decimal_copies = VGroup() + + r = self.row.coloring_level + for (i, rect) in enumerate(self.row.rects): + k = choose(r,i) + decimal = Integer(k) + decimal.move_to(rect) + self.new_decimals.add(decimal) + + anims.append(FadeIn(self.new_decimals)) + self.play(*anims) + + self.remove(self.decimal_copies) + self.decimals = self.new_decimals.copy() + #self.remove(self.new_decimals) + self.add_foreground_mobject(self.decimals) + + + + def move_tallies_on_top(self): self.play( self.tallies.shift, 1.2 * 0.5 * self.row.height * UP @@ -1311,11 +1564,15 @@ class PascalBrickRowScene(Scene): def construct(self): + self.force_skipping() + randy = CoinFlippingPiCreature() randy = randy.scale(0.5).move_to(3*DOWN + 6*LEFT) self.add(randy) self.row = PascalBrickRow(1, height = 2, width = 10) + self.decimals = VGroup() + self.play(FlipCoin(randy), FadeIn(self.row)) @@ -1360,6 +1617,33 @@ class PascalBrickRowScene(Scene): self.wait() self.move_tallies_on_top() + # show individual outcomes + outcomes = self.row.get_outcome_rects_for_level(2, with_labels = True) + self.play( + LaggedStart(FadeIn, outcomes) + ) + self.wait() + self.play( + LaggedStart(FadeOut, outcomes) + ) + + # show their numbers + nb_outcomes = [1,2,1] + self.decimals = VGroup() + for (n,rect) in zip(nb_outcomes, self.row.rects): + decimal = Integer(n).move_to(rect) + self.decimals.add(decimal) + self.play( + LaggedStart(FadeIn, self.decimals) + ) + self.wait() + self.play( + LaggedStart(FadeOut, self.decimals) + ) + + self.decimals = VGroup() + + # # # # # # # # # THIRD FLIP # @@ -1386,167 +1670,102 @@ class PascalBrickRowScene(Scene): self.move_tallies_on_top() + + # 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) + ) + + # show their numbers + nb_outcomes = [1,3,3,1] + self.decimals = VGroup() + for (n,rect) in zip(nb_outcomes, self.row.rects): + decimal = Integer(n).move_to(rect) + self.decimals.add(decimal) + self.play( + LaggedStart(FadeIn, self.decimals) + ) + self.wait() + self.add_foreground_mobject(self.decimals) + + # # # # # # # # # FOURTH FLIP # # # # # # # # # - self.play(FlipCoin(randy)) self.wait() - self.play( SplitRectsInBrickWall(self.row) ) self.wait() - self.split_tallies() + self.add_foreground_mobject(self.tallies[-1]) + # this tweaks an undesirable overlap in the next animation + self.split_tallies_at_once(direction = LEFT) self.wait() self.merge_rects_by_subdiv() self.wait() - self.merge_tallies() + self.merge_tallies(direction = LEFT) self.merge_rects_by_coloring() + self.merge_decimals() self.wait() - self.move_tallies_on_top() + self.revert_to_original_skipping_status() + # # # # # # # # + # FIFTH FLIP # + # # # # # # # # + self.play(FlipCoin(randy)) - - -class AreaSplitting(Scene): - - def create_rect_row(self,n): - rects_group = VGroup() - for k in range(n+1): - proportion = float(choose(n,k)) / 2**n - new_rect = Rectangle( - width = proportion * WIDTH, - height = HEIGHT, - fill_color = graded_color(n,k), - fill_opacity = 1 - ) - new_rect.next_to(rects_group,RIGHT,buff = 0) - rects_group.add(new_rect) - return rects_group - - def split_rect_row(self,rect_row): - - split_row = VGroup() - for rect in rect_row.submobjects: - half = rect.copy().stretch_in_place(0.5,0) - left_half = half.next_to(rect.get_center(),LEFT,buff = 0) - right_half = half.copy().next_to(rect.get_center(),RIGHT,buff = 0) - split_row.add(left_half, right_half) - return split_row - - - def rect_center(self,n,i,j): - if n < 0: - raise Exception("wrong indices " + str(n) + ", " + str(i) + ", " + str(j)) - if i < 0 or i > n: - raise Exception("wrong indices " + str(n) + ", " + str(i) + ", " + str(j)) - if j > choose(n,i) or j < 0: - raise Exception("wrong indices " + str(n) + ", " + str(i) + ", " + str(j)) - - rect = self.brick_array[n][i] - width = rect.get_width() - left_x = rect.get_center()[0] - width/2 - spacing = width / choose(n,i) - x = left_x + (j+0.5) * spacing - return np.array([x,rect.get_center()[1], rect.get_center()[2]]) - - def construct(self): - - # Draw the bricks - - brick_wall = VGroup() - rect_row = self.create_rect_row(0) - rect_row.move_to(3.5*UP + 0*HEIGHT*DOWN) - self.add(rect_row) - brick_wall.add(rect_row) - self.brick_array = [[rect_row.submobjects[0]]] - - for n in range(NB_ROWS): - # copy and shift - new_rect_row = rect_row.copy() - self.add(new_rect_row) - self.play(new_rect_row.shift,HEIGHT * DOWN) - self.wait() - - #split - split_row = self.split_rect_row(new_rect_row) - self.play(FadeIn(split_row)) - self.remove(new_rect_row) - self.wait() - - # merge - rect_row = self.create_rect_row(n+1) - rect_row.move_to(3.5*UP + (n+1)*HEIGHT*DOWN) - self.play(FadeIn(rect_row)) - brick_wall.add(rect_row) - self.remove(split_row) - self.wait() - - # add to brick dict - rect_array = [] - for rect in rect_row.submobjects: - rect_array.append(rect) - - self.brick_array.append(rect_array) - + self.wait() self.play( - brick_wall.set_fill, {"opacity" : 0.2} + SplitRectsInBrickWall(self.row) ) + self.wait() + + # this tweaks an undesirable overlap in the next animation + 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() - # Draw the branches - - for (n, rect_row_array) in enumerate(self.brick_array): - for (i, rect) in enumerate(rect_row_array): - pos = rect.get_center() - tally = TallyStack(n - i, i) - tally.move_to(pos) - - - # from the left - lines = VGroup() - - if i > 0: - for j in range(choose(n-1,i-1)): - start_pos = self.rect_center(n-1,i-1,j) - end_pos = self.rect_center(n,i,j) - lines.add(Line(start_pos,end_pos, stroke_color = GRADE_COLOR_2)) - self.play( - LaggedStart(ShowCreation, lines)) - - # from the right - lines = VGroup() - - if i < n: - for j in range(choose(n-1,i)): - start_pos = self.rect_center(n-1,i,j) - if i != 0: - end_pos = self.rect_center(n,i,choose(n-1,i-1) + j) - else: - end_pos = self.rect_center(n,i,j) - - lines.add(Line(start_pos,end_pos, stroke_color = GRADE_COLOR_1)) - self.play( - LaggedStart(ShowCreation, lines)) - - - - #self.play(FadeIn(tally)) - - - - - + # # # # # # # # # + # FURTHER FLIPS # + # # # # # # # # # + # 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) + for i in range(4): + self.play(FlipCoin(randy)) + self.play(SplitRectsInBrickWall(self.row)) + self.split_decimals_alone() + self.merge_rects_by_subdiv() + self.merge_rects_by_coloring() + self.merge_decimals() + self.wait() From bd1c312f3bffe3f6562daf91e9b7a8ed94860c53 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Thu, 19 Apr 2018 17:53:31 +0200 Subject: [PATCH 08/21] tweaked first area model example --- active_projects/eop/chapter1.py | 74 +++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/active_projects/eop/chapter1.py b/active_projects/eop/chapter1.py index 27ddb76e..09c6f87d 100644 --- a/active_projects/eop/chapter1.py +++ b/active_projects/eop/chapter1.py @@ -503,16 +503,27 @@ class Introduction(TeacherStudentsScene): self.series = series + + # # # # # # # # # # # # # # # # # # # show examples of the area model # # # # # # # # # # # # # # # # # # # - def show_area_model1(self): +class IllustrateAreaModel1(Scene): + + def construct(self): + + color_A = YELLOW + color_not_A = YELLOW_E + color_B = MAROON + color_not_B = MAROON_E + opacity_B = 0.7 + # show independent events - sample_space_width = sample_space_height = 2.5 + sample_space_width = sample_space_height = 3 p_of_A = 0.7 p_of_not_A = 1 - p_of_A p_of_B = 0.8 @@ -523,15 +534,15 @@ class Introduction(TeacherStudentsScene): width = p_of_A * sample_space_width, height = 1 * sample_space_height, stroke_width = 0, - fill_color = BLUE, + fill_color = color_A, fill_opacity = 1.0 - ).move_to(2 * RIGHT + 1.5 * UP) + ).move_to(3 * RIGHT + 1.5 * UP) rect_not_A = Rectangle( width = p_of_not_A * sample_space_width, height = 1 * sample_space_height, stroke_width = 0, - fill_color = BLUE_E, + fill_color = color_not_A, fill_opacity = 1.0 ).next_to(rect_A, RIGHT, buff = 0) @@ -554,15 +565,15 @@ class Introduction(TeacherStudentsScene): width = 1 * sample_space_width, height = p_of_B * sample_space_height, stroke_width = 0, - fill_color = GREEN, - fill_opacity = 0.5 + fill_color = color_B, + fill_opacity = opacity_B ) rect_not_B = Rectangle( width = 1 * sample_space_width, height = p_of_not_B * sample_space_height, stroke_width = 0, - fill_color = GREEN_E, - fill_opacity = 0.5 + fill_color = color_not_B, + fill_opacity = opacity_B ).next_to(rect_B, UP, buff = 0) VGroup(rect_B, rect_not_B).move_to(VGroup(rect_A, rect_not_A)) @@ -594,7 +605,8 @@ class Introduction(TeacherStudentsScene): # ) indep_formula = TexMobject("P(A\\text{ and }B)", "=", "P(A)", "\cdot", "P(B)") - indep_formula = indep_formula.scale(0.7).next_to(rect_not_B, UP, buff = MED_LARGE_BUFF) + indep_formula = indep_formula.scale(0.7) + label_p_of_b = indep_formula.get_part_by_tex("P(B)") label_A_and_B_copy = label_A_and_B.copy() label_A_copy = label_A.copy() @@ -615,34 +627,34 @@ class Introduction(TeacherStudentsScene): # show conditional prob - rect_A_and_B.set_fill(color = GREEN, opacity = 0.5) + rect_A_and_B.set_fill(color = RED, opacity = 0.5) rect_A_and_not_B = Rectangle( width = p_of_A * sample_space_width, height = p_of_not_B * sample_space_height, stroke_width = 0, - fill_color = GREEN_E, - fill_opacity = 0.5 + fill_color = color_not_B, + fill_opacity = opacity_B ).next_to(rect_A_and_B, UP, buff = 0) rect_not_A_and_B = Rectangle( width = p_of_not_A * sample_space_width, height = p_of_B * sample_space_height, stroke_width = 0, - fill_color = GREEN, - fill_opacity = 0.5 + fill_color = color_B, + fill_opacity = opacity_B ).next_to(rect_A_and_B, RIGHT, buff = 0) rect_not_A_and_not_B = Rectangle( width = p_of_not_A * sample_space_width, height = p_of_not_B * sample_space_height, stroke_width = 0, - fill_color = GREEN_E, - fill_opacity = 0.5 + fill_color = color_not_B, + fill_opacity = opacity_B ).next_to(rect_not_A_and_B, UP, buff = 0) - indep_formula.next_to(rect_not_A, LEFT, buff = 4) - indep_formula.shift(UP) + indep_formula.next_to(rect_not_A, LEFT, buff = 5) + #indep_formula.shift(UP) self.play(Write(indep_formula)) @@ -663,16 +675,16 @@ class Introduction(TeacherStudentsScene): width = p_of_A * sample_space_width, height = p_of_B_knowing_A * sample_space_height, stroke_width = 3, - fill_color = GREEN, - fill_opacity = 0.5 + fill_color = color_B, + fill_opacity = opacity_B ).align_to(rect_A_and_B, DOWN).align_to(rect_A_and_B, LEFT) rect_A_and_not_B.target = Rectangle( width = p_of_A * sample_space_width, height = (1 - p_of_B_knowing_A) * sample_space_height, stroke_width = 0, - fill_color = GREEN_E, - fill_opacity = 0.5 + fill_color = color_not_B, + fill_opacity = opacity_B ).next_to(rect_A_and_B.target, UP, buff = 0) brace_B.target = Brace(rect_A_and_B.target, LEFT) @@ -690,6 +702,7 @@ class Introduction(TeacherStudentsScene): #self.play(FadeOut(label_B_copy)) self.remove(indep_formula.get_part_by_tex("P(B)")) + indep_formula.remove(indep_formula.get_part_by_tex("P(B)")) label_B_knowing_A_copy = label_B_knowing_A.copy() self.add(label_B_knowing_A_copy) @@ -712,18 +725,15 @@ class Introduction(TeacherStudentsScene): label_A_copy.move_to, rearranged_formula[-1][10], label_A_and_B_copy.move_to, rearranged_formula[-1][3], indep_formula.get_part_by_tex("=").move_to, rearranged_formula.get_part_by_tex("="), - Transform(indep_formula.get_part_by_tex("\cdot"), rearranged_formula[2][6]), + Transform(indep_formula.get_part_by_tex("\cdot"), rearranged_formula[2][8]), ) + rect = SurroundingRectangle(rearranged_formula) + self.play(ShowCreation(rect)) + + + self.wait() - self.play( - FadeOut(VGroup( - indep_formula, rect_A, rect_B, rect_not_A, rect_not_B, - rect_A_and_B, rect_A_and_not_B, rect_not_A_and_B, rect_not_A_and_not_B, - brace_A, brace_B, label_A, label_B_knowing_A, label_A_and_B, - label_B_knowing_A_copy - )) - ) From 83bebfb109c37cf06c8ab7185a8214c70a825a9b Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Thu, 19 Apr 2018 23:02:52 +0200 Subject: [PATCH 09/21] now both integral bounds can move --- scene/graph_scene.py | 65 +++++++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/scene/graph_scene.py b/scene/graph_scene.py index a224ba59..d1a3d5e0 100644 --- a/scene/graph_scene.py +++ b/scene/graph_scene.py @@ -57,6 +57,11 @@ class GraphScene(Scene): def setup(self): self.default_graph_colors_cycle = it.cycle(self.default_graph_colors) + self.left_T_label = VGroup() + self.left_v_line = VGroup() + self.right_T_label = VGroup() + self.right_v_line = VGroup() + def setup_axes(self, animate=False): # TODO, once eoc is done, refactor this to be less redundant. x_num_range = float(self.x_max - self.x_min) @@ -291,7 +296,7 @@ class GraphScene(Scene): def get_area(self, graph, t_min, t_max): - numerator = max(t_max - t_min, 0.01) + numerator = max(t_max - t_min, 0.0001) dx = float(numerator) / self.num_rects return self.get_riemann_rectangles( graph, @@ -439,13 +444,17 @@ class GraphScene(Scene): return group - def add_T_label(self, x_val, color = WHITE, animated = False, **kwargs): + def add_T_label(self, x_val, side = RIGHT, label = None, color = WHITE, animated = False, **kwargs): triangle = RegularPolygon(n=3, start_angle = np.pi/2) triangle.scale_to_fit_height(MED_SMALL_BUFF) triangle.move_to(self.coords_to_point(x_val, 0), UP) triangle.set_fill(color, 1) triangle.set_stroke(width = 0) - T_label = TexMobject(self.variable_point_label, fill_color = color) + if label == None: + T_label = TexMobject(self.variable_point_label, fill_color = color) + else: + T_label = TexMobject(label, fill_color = color) + T_label.next_to(triangle, DOWN) v_line = self.get_vertical_line_to_graph( x_val, self.v_graph, @@ -462,8 +471,13 @@ class GraphScene(Scene): else: self.add(triangle, v_line, T_label) - self.T_label_group = VGroup(T_label, triangle) - self.right_v_line = v_line + if np.all(side == LEFT): + self.left_T_label_group = VGroup(T_label, triangle) + self.left_v_line = v_line + elif np.all(side == RIGHT): + self.right_T_label_group = VGroup(T_label, triangle) + self.right_v_line = v_line + @@ -472,6 +486,7 @@ class GraphScene(Scene): graph, new_t_min, new_t_max, + fade_close_to_origin = True, run_time = 1.0 ): curr_t_min = self.x_axis.point_to_number(self.area.get_left()) @@ -482,33 +497,39 @@ class GraphScene(Scene): new_t_max = curr_t_max group = VGroup(self.area) - if hasattr(self, "right_v_line"): - group.add(self.right_v_line) - else: - group.add(VGroup()) - # because update_group expects 3 elements in group - if hasattr(self, "T_label_group"): - group.add(self.T_label_group) - else: - group.add(VGroup()) + group.add(self.left_v_line) + group.add(self.left_T_label_group) + group.add(self.right_v_line) + group.add(self.right_T_label_group) def update_group(group, alpha): - area, v_line, T_label = group + area, left_v_line, left_T_label, right_v_line, right_T_label = group t_min = interpolate(curr_t_min, new_t_min, alpha) t_max = interpolate(curr_t_max, new_t_max, alpha) new_area = self.get_area(graph,t_min, t_max) - new_v_line = self.get_vertical_line_to_graph( + + new_left_v_line = self.get_vertical_line_to_graph( + t_min, graph + ) + new_left_v_line.set_color(left_v_line.get_color()) + left_T_label.move_to(new_left_v_line.get_bottom(), UP) + + new_right_v_line = self.get_vertical_line_to_graph( t_max, graph ) - new_v_line.set_color(v_line.get_color()) - T_label.move_to(new_v_line.get_bottom(), UP) + new_right_v_line.set_color(right_v_line.get_color()) + right_T_label.move_to(new_right_v_line.get_bottom(), UP) - #Fade close to 0 - if len(T_label) > 0: - T_label[0].set_fill(opacity = min(1, t_max)) + # Fade close to 0 + if fade_close_to_origin: + if len(left_T_label) > 0: + left_T_label[0].set_fill(opacity = min(1, np.abs(t_min))) + if len(right_T_label) > 0: + right_T_label[0].set_fill(opacity = min(1, np.abs(t_max))) Transform(area, new_area).update(1) - Transform(v_line, new_v_line).update(1) + Transform(left_v_line, new_left_v_line).update(1) + Transform(right_v_line, new_right_v_line).update(1) return group return UpdateFromAlphaFunc(group, update_group, run_time = run_time) From ad6b1fee20c7c99a68c9f63308f573ffbbbf34aa Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Thu, 19 Apr 2018 23:03:29 +0200 Subject: [PATCH 10/21] finished area model scene 2 (normal dist) --- active_projects/eop/chapter1.py | 130 ++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 55 deletions(-) diff --git a/active_projects/eop/chapter1.py b/active_projects/eop/chapter1.py index 09c6f87d..75e0381f 100644 --- a/active_projects/eop/chapter1.py +++ b/active_projects/eop/chapter1.py @@ -740,95 +740,115 @@ class IllustrateAreaModel1(Scene): class IllustrateAreaModel2(GraphScene): CONFIG = { - "x_min" : -3.5, - "x_max" : 3.5, - "y_min" : -0, - "y_max" : 0.6, - "graph_origin": 3*DOWN, - "num_rects": 20, + "x_min" : -3.0, + "x_max" : 3.0, + "y_min" : 0, + "y_max" : 1.0, + "num_rects": 400, "y_axis_label" : "", "x_axis_label" : "", - "variable_point_label" : "x", - "y_axis_height" : 4, - "graph_origin": 2.5 * DOWN + 3 * LEFT, + "variable_point_label" : "a", + "graph_origin": 2.5 * DOWN + 4 * RIGHT, "x_axis_width": 5, - "y_axis_height": 3 + "y_axis_height": 5 } def construct(self): - x_max_1 = 0 - x_min_1 = -x_max_1 - - x_max_2 = 3.5 - x_min_2 = -x_max_2 + # integral bounds + x_min_1 = -0.0001 + x_max_1 = 0.0001 + x_min_2 = self.x_min + x_max_2 = self.x_max self.setup_axes() - graph = self.get_graph(lambda x: np.exp(-x**2) / ((0.5 * TAU) ** 0.5)) + self.remove(self.x_axis, self.y_axis) + graph = self.get_graph(lambda x: np.exp(-x**2) * 2.0 / TAU ** 0.5) + area = self.area = self.get_area(graph, x_min_1, x_max_1) - self.add(graph) - cdf_formula = TexMobject("P(|X-\mu| < x) = \int_{-x}^x {\exp(-{1\over 2}({t\over \sigma})^2) \over \sigma\sqrt{2\pi}} dt") - - cdf_formula.set_color_by_tex("x", YELLOW) - cdf_formula.next_to(graph, RIGHT, buff = -1) - self.add(cdf_formula) + pdf_formula = TexMobject("p(x) = {1\over \sigma\sqrt{2\pi}}e^{-{1\over 2}({x\over\sigma})^2}") + pdf_formula.set_color(graph.color) + + cdf_formula = TexMobject("P(|X| < ", "a", ") = \int", "_{-a}", "^a", "p(x) dx") + cdf_formula.set_color_by_tex("a", YELLOW) + cdf_formula.next_to(graph, LEFT, buff = 2) + pdf_formula.next_to(cdf_formula, UP) + + formulas = VGroup(pdf_formula, cdf_formula) + self.play(Write(pdf_formula)) + self.play(Write(cdf_formula)) + self.wait() + + + self.play(ShowCreation(self.x_axis)) + self.play(ShowCreation(graph)) + self.play(FadeIn(area)) self.v_graph = graph - self.add_T_label(x_min_1, color = YELLOW, animated = False) + self.add_T_label( + x_min_1, + label = "-a", + side = LEFT, + color = YELLOW, + animated = False + ) + self.add_T_label( + x_max_1, + label = "a", + side = RIGHT, + color = YELLOW, + animated = False + ) - self.remove(self.T_label_group, self.right_v_line) - #self.T_label_group[0].set_fill(opacity = 0).set_stroke(width = 0) - #self.T_label_group[1].set_fill(opacity = 0).set_stroke(width = 0) - #self.right_v_line.set_fill(opacity = 0).set_stroke(width = 0) - - #self.add(self.T_label_group) - area = self.area = self.get_area(graph, x_min_1, x_max_1) - - right_bound_label = TexMobject("x", color = YELLOW) - right_bound_label.next_to(self.coords_to_point(0,0), DOWN) - right_bound_label.target = right_bound_label.copy().next_to(self.coords_to_point(self.x_max,0), DOWN) - right_bound_label.set_fill(opacity = 0).set_stroke(width = 0) - - left_bound_label = TexMobject("-x", color = YELLOW) - left_bound_label.next_to(self.coords_to_point(0,0), DOWN) - left_bound_label.target = right_bound_label.copy().next_to(self.coords_to_point(self.x_min,0), DOWN) - left_bound_label.set_fill(opacity = 0).set_stroke(width = 0) - - #integral = self.get_riemann_rectangles( - #graph,x_min = self.x_min, x_max = x_max_1) - self.add(area) - def integral_update_func(t): - return 100 * scipy.special.erf( + return scipy.special.erf( self.point_to_coords(self.right_v_line.get_center())[0] ) - cdf_value = DecimalNumber(0, unit = "\%") - cdf_value.move_to(self.coords_to_point(0,0.2)) + def integral_update_func_percent(t): + return 100 * integral_update_func(t) + + equals_sign = TexMobject("=").next_to(cdf_formula, buff = MED_LARGE_BUFF) + + cdf_value = DecimalNumber(0, color = graph.color, num_decimal_points = 3) + cdf_value.next_to(equals_sign) + self.play( + FadeIn(equals_sign), + FadeIn(cdf_value) + ) self.add_foreground_mobject(cdf_value) + cdf_percentage = DecimalNumber(0, unit = "\%") + cdf_percentage.move_to(self.coords_to_point(0,0.2)) + self.add_foreground_mobject(cdf_percentage) + self.add(ContinualChangingDecimal( decimal_number_mobject = cdf_value, number_update_func = integral_update_func, + num_decimal_points = 3 + )) + + self.add(ContinualChangingDecimal( + decimal_number_mobject = cdf_percentage, + number_update_func = integral_update_func_percent, num_decimal_points = 1 )) - anim = self.get_animation_integral_bounds_change( - graph, x_min_2, x_max_2, run_time = 3) - # changing_cdf_value = ChangingDecimal( - # decimal_number_mobject = cdf_value, - # number_update_func = integral_update_func, - # num_decimal_points = 1 - # ) + anim = self.get_animation_integral_bounds_change( + graph, x_min_2, x_max_2, + run_time = 3) + self.play( anim ) + rect = SurroundingRectangle(formulas, buff = 0.5 * MED_LARGE_BUFF) + self.play(ShowCreation(rect)) From e75c68b9f24cabb1d08c670fa19f820d14390f5c Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 20 Apr 2018 07:56:06 +0200 Subject: [PATCH 11/21] quick fix for GraphScene --- scene/graph_scene.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scene/graph_scene.py b/scene/graph_scene.py index d1a3d5e0..549da2fa 100644 --- a/scene/graph_scene.py +++ b/scene/graph_scene.py @@ -468,15 +468,15 @@ class GraphScene(Scene): Write(T_label, run_time = 1), **kwargs ) - else: - self.add(triangle, v_line, T_label) if np.all(side == LEFT): self.left_T_label_group = VGroup(T_label, triangle) self.left_v_line = v_line + self.add(self.left_T_label_group, self.left_v_line) elif np.all(side == RIGHT): self.right_T_label_group = VGroup(T_label, triangle) self.right_v_line = v_line + self.add(self.right_T_label_group, self.right_v_line) From bcf90c2b89a76fbdfb810bee6f392d846bf62b45 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 20 Apr 2018 07:56:28 +0200 Subject: [PATCH 12/21] fix for labels in area model 2 --- active_projects/eop/chapter1.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/active_projects/eop/chapter1.py b/active_projects/eop/chapter1.py index 75e0381f..af5ed99a 100644 --- a/active_projects/eop/chapter1.py +++ b/active_projects/eop/chapter1.py @@ -802,7 +802,12 @@ class IllustrateAreaModel2(GraphScene): color = YELLOW, animated = False ) - + # don't show the labels just yet + self.remove( + self.left_T_label_group[0], + self.right_T_label_group[0], + ) + def integral_update_func(t): return scipy.special.erf( self.point_to_coords(self.right_v_line.get_center())[0] From aa178628a88c00c3506256ecf63f8d8d57e3a552 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 20 Apr 2018 08:48:16 +0200 Subject: [PATCH 13/21] fixed drawing bars of height 0 --- active_projects/eop/histograms.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/active_projects/eop/histograms.py b/active_projects/eop/histograms.py index 01d1c356..21bae127 100644 --- a/active_projects/eop/histograms.py +++ b/active_projects/eop/histograms.py @@ -126,6 +126,10 @@ class Histogram(VMobject): stroke_width = self.bar_stroke_width, stroke_color = self.stroke_color, ) + if bar.height == 0: + bar.height = 0.01 + bar.generate_points() + t = float(x - self.x_min)/(self.x_max - self.x_min) bar_color = interpolate_color( self.start_color, From 77379c8907fc78d720ab74ffc7da0e0113df80f9 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 20 Apr 2018 08:48:28 +0200 Subject: [PATCH 14/21] finished area model 3 --- active_projects/eop/chapter1.py | 34 +++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/active_projects/eop/chapter1.py b/active_projects/eop/chapter1.py index af5ed99a..d3c6e58e 100644 --- a/active_projects/eop/chapter1.py +++ b/active_projects/eop/chapter1.py @@ -728,7 +728,7 @@ class IllustrateAreaModel1(Scene): Transform(indep_formula.get_part_by_tex("\cdot"), rearranged_formula[2][8]), ) - rect = SurroundingRectangle(rearranged_formula) + rect = SurroundingRectangle(rearranged_formula, buff = 0.5 * MED_LARGE_BUFF) self.play(ShowCreation(rect)) @@ -863,7 +863,7 @@ class IllustrateAreaModel3(Scene): def construct(self): formula = TexMobject("E[X] = \sum_{i=1}^N p_i x_i").move_to(3 * LEFT + UP) - self.add(formula) + self.play(Write(formula)) x_scale = 5.0 @@ -904,18 +904,14 @@ class IllustrateAreaModel3(Scene): group = VGroup(brace, p_label) braces.add(brace) p_labels.add(p_label) - self.play( - Write(group) - ) + + self.play( + LaggedStart(FadeIn,braces), + LaggedStart(FadeIn, p_labels) + ) - labels = VGroup() - for (y, bar) in zip(y_values, hist.bars): - label = TexMobject(str(int(y))).scale(0.7).next_to(bar, UP, buff = SMALL_BUFF) - self.play(FadeIn(label)) - labels.add(label) - y_average = np.mean(y_values) averaged_y_values = y_average * np.ones(np.shape(y_values)) @@ -923,27 +919,33 @@ class IllustrateAreaModel3(Scene): mode = "widths", x_scale = x_scale, y_scale = y_scale, - x_labels = "none" + x_labels = "none", + y_labels = "none" ).fade(0.2) ghost_hist = hist.copy().fade(0.8) - labels.fade(0.8) self.bring_to_back(ghost_hist) - self.play(Transform(hist, averaged_hist)) + self.play(Transform(hist, averaged_hist, run_time = 3)) + self.wait() - average_label = TexMobject(str(y_average)).scale(0.7).next_to(averaged_hist, UP, SMALL_BUFF) + average_brace = Brace(averaged_hist, RIGHT, buff = 0.1) + average_label = TexMobject(str(y_average)).scale(0.7) + average_label.next_to(average_brace, RIGHT, SMALL_BUFF) + average_group = VGroup(average_brace, average_label) one_brace = Brace(averaged_hist, DOWN, buff = 0.1) one_p_label = TexMobject(str(1)).next_to(one_brace, DOWN, buff = SMALL_BUFF).scale(0.7) one_group = VGroup(one_brace, one_p_label) self.play( - FadeIn(average_label), + FadeIn(average_group), Transform(braces, one_brace), Transform(p_labels, one_p_label), ) + rect = SurroundingRectangle(formula, buff = 0.5 * MED_LARGE_BUFF) + self.play(ShowCreation(rect)) From 8ab8b4f9a9f342c0183587359d2947c950802636 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 20 Apr 2018 09:08:18 +0200 Subject: [PATCH 15/21] histogram positioned in origin by default --- active_projects/eop/histograms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/active_projects/eop/histograms.py b/active_projects/eop/histograms.py index 21bae127..0974569d 100644 --- a/active_projects/eop/histograms.py +++ b/active_projects/eop/histograms.py @@ -174,7 +174,7 @@ class Histogram(VMobject): stroke_color = self.stroke_color) self.add(self.bars, self.x_labels_group, self.y_labels_group, self.outline) - print self.submobjects + self.move_to(ORIGIN) def get_lower_left_point(self): return self.bars[0].get_anchors()[-2] From a5dee2d5b5eb70f6ee5564d834127837e6418b85 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 20 Apr 2018 09:08:52 +0200 Subject: [PATCH 16/21] finished ShowUncertainty1 (die) --- active_projects/eop/chapter1.py | 48 +++++++++++---------------------- 1 file changed, 16 insertions(+), 32 deletions(-) diff --git a/active_projects/eop/chapter1.py b/active_projects/eop/chapter1.py index d3c6e58e..781f4b58 100644 --- a/active_projects/eop/chapter1.py +++ b/active_projects/eop/chapter1.py @@ -977,6 +977,7 @@ class RowOfDice(VGroup): new_die.submobjects[0].set_stroke(width = 7) new_die.next_to(self, RIGHT) self.add(new_die) + self.move_to(ORIGIN) @@ -986,9 +987,6 @@ class ShowUncertainty1(Scene): eye = np.random.randint(1,7) face = self.row_of_dice.submobjects[eye - 1] - self.tallies[eye - 1] += 1 - - new_hist = self.hist_from_tallies() self.play( ApplyMethod(face.submobjects[0].set_fill, {"opacity": 1}, @@ -996,36 +994,23 @@ class ShowUncertainty1(Scene): run_time = 0.3, ), ) - self.play( - Transform(self.dice_histogram, new_hist, - run_time = 0.5) - ) - - def hist_from_tallies(self): - x_scale = self.row_of_dice.get_width() / np.size(self.tallies) - hist = Histogram(np.ones(6), self.tallies, - mode = "widths", - x_labels = "none", - y_scale = 0.5, - x_scale = x_scale - ) - - hist.next_to(self.row_of_dice, UP) - return hist def construct(self): - self.row_of_dice = RowOfDice().scale(0.5).move_to(2 * DOWN) + self.row_of_dice = RowOfDice().scale(1) self.add(self.row_of_dice) - self.tallies = np.zeros(6) - self.dice_histogram = self.hist_from_tallies() - - self.add(self.dice_histogram) - - for i in range(30): + for i in range(5): self.throw_a_die() - self.wait() + self.wait(1) + + for i in range(10): + self.throw_a_die() + self.wait(0.3) + + for i in range(10): + self.throw_a_die() + self.wait(0.1) @@ -1033,10 +1018,8 @@ class IdealizedDieHistogram(Scene): def construct(self): - self.row_of_dice = RowOfDice().scale(0.5).move_to(2 * DOWN) - self.add(self.row_of_dice) self.probs = 1.0/6 * np.ones(6) - x_scale = self.row_of_dice.get_width() / np.size(self.probs) + x_scale = 1.0 y_labels = ["${1\over 6}$"] * 6 @@ -1048,9 +1031,10 @@ class IdealizedDieHistogram(Scene): y_scale = 20, x_scale = x_scale, ) - hist.next_to(self.row_of_dice, UP) + hist.remove(hist.y_labels_group) - self.add(hist) + self.play(FadeIn(hist)) + self.play(LaggedStart(FadeIn, hist.y_labels_group)) From f3782cc460961c4eb2c492806a5da44115e8428f Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 20 Apr 2018 09:57:20 +0200 Subject: [PATCH 17/21] dice row now vertical, replaced coin toss with sick pi creatures --- active_projects/eop/chapter1.py | 62 ++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/active_projects/eop/chapter1.py b/active_projects/eop/chapter1.py index 781f4b58..847c18b5 100644 --- a/active_projects/eop/chapter1.py +++ b/active_projects/eop/chapter1.py @@ -967,7 +967,8 @@ class DieFace(SVGMobject): class RowOfDice(VGroup): CONFIG = { - "values" : range(1,7) + "values" : range(1,7), + "direction": RIGHT, } def generate_points(self): @@ -975,7 +976,7 @@ class RowOfDice(VGroup): new_die = DieFace(value) new_die.submobjects[0].set_fill(opacity = 0) new_die.submobjects[0].set_stroke(width = 7) - new_die.next_to(self, RIGHT) + new_die.next_to(self, self.direction) self.add(new_die) self.move_to(ORIGIN) @@ -997,7 +998,7 @@ class ShowUncertainty1(Scene): def construct(self): - self.row_of_dice = RowOfDice().scale(1) + self.row_of_dice = RowOfDice(direction = DOWN).scale(0.5) self.add(self.row_of_dice) for i in range(5): @@ -1019,7 +1020,7 @@ class IdealizedDieHistogram(Scene): def construct(self): self.probs = 1.0/6 * np.ones(6) - x_scale = 1.0 + x_scale = 1.3 y_labels = ["${1\over 6}$"] * 6 @@ -1031,14 +1032,19 @@ class IdealizedDieHistogram(Scene): y_scale = 20, x_scale = x_scale, ) + hist.rotate(-TAU/4) + + for label in hist.y_labels_group: + label.rotate(TAU/4) hist.remove(hist.y_labels_group) + self.play(FadeIn(hist)) self.play(LaggedStart(FadeIn, hist.y_labels_group)) -class ShowUncertainty2(PiCreatureScene): +class ShowUncertainty2(Scene): def throw_darts(self, n, run_time = 1): @@ -1123,6 +1129,52 @@ class ShowUncertainty3(Scene): +SICKLY_GREEN = "#9BBD37" + +class OneIn200HaveDisease(Scene): + def construct(self): + title = TextMobject("1 in 200") + title.to_edge(UP) + creature = PiCreature() + + all_creatures = VGroup(*[ + VGroup(*[ + creature.copy() + for y in range(20) + ]).arrange_submobjects(DOWN, SMALL_BUFF) + for x in range(10) + ]).arrange_submobjects(RIGHT, SMALL_BUFF) + all_creatures.scale_to_fit_height(FRAME_HEIGHT * 0.8) + all_creatures.next_to(title, DOWN) + randy = all_creatures[0][0] + all_creatures[0].remove(randy) + randy.change_mode("sick") + randy.set_color(SICKLY_GREEN) + randy.save_state() + randy.scale_to_fit_height(3) + randy.center() + randy.change_mode("plain") + randy.set_color(BLUE) + + self.add(randy) + + p_sick = TexMobject("p(","\\text{sick}",") = 0.5\%") + p_sick.set_color_by_tex("sick", SICKLY_GREEN) + p_sick.next_to(randy, RIGHT+UP) + self.add(p_sick) + self.wait() + self.play( + randy.change_mode, "sick", + randy.set_color, SICKLY_GREEN + ) + self.play(Blink(randy)) + self.play(randy.restore) + self.play( + FadeOut(p_sick), + Write(title), + LaggedStart(FadeIn, all_creatures, run_time = 3) + ) + self.wait() From b847b24cf773aa76b3fa6830edf1767d13169789 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 20 Apr 2018 10:34:21 +0200 Subject: [PATCH 18/21] finished TS Scene --- active_projects/eop/chapter1.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/active_projects/eop/chapter1.py b/active_projects/eop/chapter1.py index 847c18b5..f2f1ceb8 100644 --- a/active_projects/eop/chapter1.py +++ b/active_projects/eop/chapter1.py @@ -440,10 +440,10 @@ class Introduction(TeacherStudentsScene): def construct(self): self.show_series() - self.show_area_model1() + self.show_examples() def show_series(self): - series = VideoSeries() + series = VideoSeries(num_videos = 11) series.to_edge(UP) this_video = series[0] this_video.set_color(YELLOW) @@ -494,9 +494,10 @@ class Introduction(TeacherStudentsScene): self.play( FadeOut(self.teacher.bubble), FadeOut(self.teacher.bubble.content), + self.get_teacher().change_mode, "raise_right_hand", *[ ApplyMethod(pi.change_mode, "pondering") - for pi in self.get_pi_creatures() + for pi in self.get_students() ] ) self.wait() @@ -504,6 +505,11 @@ class Introduction(TeacherStudentsScene): self.series = series + def show_examples(self): + + self.wait(10) + # put examples here in video editor + # # # # # # # # # # # # # # # # # # # show examples of the area model # From 6e2a4f4c5d430360088b77009b0f6186d8970561 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 20 Apr 2018 18:33:23 +0200 Subject: [PATCH 19/21] working on showing the relations btw successive rows --- active_projects/eop/chapter1.py | 170 +++++++++++++++++++++++--------- 1 file changed, 125 insertions(+), 45 deletions(-) diff --git a/active_projects/eop/chapter1.py b/active_projects/eop/chapter1.py index f2f1ceb8..54fb105b 100644 --- a/active_projects/eop/chapter1.py +++ b/active_projects/eop/chapter1.py @@ -195,6 +195,7 @@ class CoinSequence(VGroup): self.add(new_coin) offset += self.spacing + class FlatCoin(UprightCoin): # For use in coin stacks CONFIG = { @@ -386,8 +387,6 @@ class CoinFlipTree(VGroup): new_row.append([leaf, root_tally + i, root_point]) # leaf and its parent x += dx - #print "tallies for row", level, ":", [new_row[i][1] for i in range(2**level)] - if sorted: # sort the new_row by its tallies sorted_row = [] @@ -399,7 +398,6 @@ class CoinFlipTree(VGroup): sorted_leaf[0][0] = x x += dx sorted_row.append(leaf) - print "sorted roots:", [sorted_row[i][2][0] for i in range(2**level)] self.rows.append(sorted_row) else: self.rows.append(new_row) @@ -879,7 +877,6 @@ class IllustrateAreaModel3(Scene): prob_strings = ["{1\over 8}","{3\over 8}","{3\over 8}","{1\over 8}"] cumulative_probabilities = np.cumsum(probabilities) cumulative_probabilities = np.insert(cumulative_probabilities, 0, 0) - print cumulative_probabilities y_values = np.array([0, 1, 2, 3]) hist = Histogram(probabilities, y_values, @@ -1184,14 +1181,15 @@ class OneIn200HaveDisease(Scene): -class PascalBrickRow(VMobject): +class PascalBrickWall(VMobject): CONFIG = { "left_color" : YELLOW, "right_color" : BLUE, "height" : 1.0, "width" : 8.0, - "outcome_shrinkage_factor" : 0.85 + "outcome_shrinkage_factor_x" : 0.85, + "outcome_shrinkage_factor_y" : 0.95 } def __init__(self, n, **kwargs): @@ -1232,6 +1230,11 @@ class PascalBrickRow(VMobject): rects.add(new_rect) return rects + def get_center(self): + return self.rects.get_center() + # just in case some more submobs have been added + # that displace the center + def get_subdivs_for_level(self,r): subdivs = VGroup() x = - 0.5 * self.width @@ -1243,13 +1246,13 @@ class PascalBrickRow(VMobject): x * RIGHT + 0.5 * self.height * DOWN, ) subdivs.add(subdiv) + subdivs.move_to(self.get_center()) return subdivs def get_outcome_centers_for_level(self,r): dpos = float(self.width) / (2 ** r) * RIGHT - print "dpos =", dpos pos = 0.5 * self.width * LEFT + 0.5 * dpos centers = [] for k in range(0, 2 ** r): @@ -1260,8 +1263,8 @@ class PascalBrickRow(VMobject): def get_outcome_rects_for_level(self,r, with_labels = False): centers = self.get_outcome_centers_for_level(r) - outcome_width = self.outcome_shrinkage_factor * float(self.width) / (2 ** r) - outcome_height = self.outcome_shrinkage_factor * self.height + outcome_width = self.outcome_shrinkage_factor_x * float(self.width) / (2 ** r) + outcome_height = self.outcome_shrinkage_factor_y * self.height rect = RoundedRectangle( width = outcome_width, height = outcome_height, @@ -1274,6 +1277,8 @@ class PascalBrickRow(VMobject): for center in centers: rects.add(rect.copy().move_to(center)) + + if with_labels == False: return rects @@ -1282,9 +1287,11 @@ class PascalBrickRow(VMobject): labels = VGroup() for (seq, rect) in zip(sequences, rects): coin_seq = CoinSequence(seq, direction = DOWN) - coin_seq.move_to(rect) + coin_seq.shift(rect.get_center() - coin_seq.get_center()) + # not simply move_to bc coin_seq is not centered rect.add(coin_seq) + rects.move_to(self.get_center()) return rects def get_coin_sequences_for_level(self,r): @@ -1344,10 +1351,11 @@ class SplitRectsInBrickWall(Animation): proportion = float(choose(r,k)) / 2**r x += proportion * mobject.width subdiv = DashedLine( - x * RIGHT + 0.5 * mobject.height * UP, - x * RIGHT + 0.5 * mobject.height * UP, + mobject.get_center() + x * RIGHT + 0.5 * mobject.height * UP, + mobject.get_center() + x * RIGHT + 0.5 * mobject.height * UP, ) self.subdivs.add(subdiv) + mobject.add(self.subdivs) Animation.__init__(self, mobject, **kwargs) @@ -1359,15 +1367,17 @@ class SplitRectsInBrickWall(Animation): def update_mobject(self, alpha): for subdiv in self.subdivs: x = subdiv.get_start()[0] - start = x * RIGHT + 0.5 * self.mobject.height * UP - end = start + alpha * 0.5 * self.mobject.height * (DOWN - UP) + start = self.mobject.get_center() + end = self.mobject.get_center() + start += x * RIGHT + 0.5 * self.mobject.height * UP + end += x * RIGHT + alpha * 0.5 * self.mobject.height * DOWN subdiv.put_start_and_end_on(start,end) -class PascalBrickRowScene(Scene): +class PascalBrickWallScene(Scene): def split_tallies(self, direction = DOWN): @@ -1430,9 +1440,7 @@ class PascalBrickRowScene(Scene): tally.nb_tails = new_tally_left.nb_tails - - - def split_tallies_at_once(self, direction = DOWN): + def tally_split_animations(self, direction = DOWN): self.tallies_copy = self.tallies.copy() self.add_foreground_mobject(self.tallies_copy) @@ -1487,12 +1495,11 @@ class PascalBrickRowScene(Scene): anims1.append(target_right) anims1.append(decimal_copy.shift) anims1.append(v) - self.decimal_copies.add(decimal_copy) + if len(self.decimals) > 0: + self.decimal_copies.add(decimal_copy) tally_copy.anchor = target_right - - self.play(*anims1) anims2 = [] for (i, tally) in enumerate(self.tallies): @@ -1513,11 +1520,24 @@ class PascalBrickRowScene(Scene): tally.nb_tails = new_tally_left.nb_tails - self.play(*anims2) if len(self.decimals) > 0: self.add_foreground_mobject(self.decimal_copies) + 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): + anims1, anims2 = self.tally_split_animations(direction = direction) + self.play(*anims1) + self.wait(0.3) + self.play(*anims2) + + + def split_decimals_alone(self): @@ -1621,6 +1641,11 @@ class PascalBrickRowScene(Scene): k = choose(r,i) decimal = Integer(k) decimal.move_to(rect) + if rect.get_width() < 0.2: + # then the rect is too narrow, + # let the decimal go in dignity + decimal.set_stroke(width = 0) + decimal.set_fill(opacity = 0) self.new_decimals.add(decimal) anims.append(FadeIn(self.new_decimals)) @@ -1648,7 +1673,7 @@ class PascalBrickRowScene(Scene): randy = CoinFlippingPiCreature() randy = randy.scale(0.5).move_to(3*DOWN + 6*LEFT) self.add(randy) - self.row = PascalBrickRow(1, height = 2, width = 10) + self.row = PascalBrickWall(1, height = 2, width = 10) self.decimals = VGroup() @@ -1687,7 +1712,7 @@ class PascalBrickRowScene(Scene): ) self.wait() - self.split_tallies() + self.split_tallies_in_two_steps() self.wait() self.merge_rects_by_subdiv() self.wait() @@ -1725,7 +1750,7 @@ class PascalBrickRowScene(Scene): # # # # # # # # - # THIRD FLIP # + # THIRD FLIP # # # # # # # # # @@ -1739,7 +1764,7 @@ class PascalBrickRowScene(Scene): ) self.wait() - self.split_tallies() + self.split_tallies_in_two_steps() self.wait() self.merge_rects_by_subdiv() self.wait() @@ -1797,10 +1822,9 @@ class PascalBrickRowScene(Scene): self.merge_decimals() self.wait() - self.revert_to_original_skipping_status() # # # # # # # # - # FIFTH FLIP # + # FIFTH FLIP # # # # # # # # # self.play(FlipCoin(randy)) @@ -1812,7 +1836,6 @@ class PascalBrickRowScene(Scene): ) self.wait() - # this tweaks an undesirable overlap in the next animation self.split_tallies_at_once(direction = LEFT) self.wait() self.merge_rects_by_subdiv() @@ -1823,9 +1846,12 @@ class PascalBrickRowScene(Scene): self.wait() - # # # # # # # # # - # FURTHER FLIPS # - # # # # # # # # # + # # # # # # # # + # SIXTH FLIP # + # # # # # # # # + + + self.revert_to_original_skipping_status() # removing the tallies (boy are they sticky) self.play(FadeOut(self.tallies)) @@ -1837,22 +1863,76 @@ class PascalBrickRowScene(Scene): self.remove_foreground_mobject(tally) self.remove(tally) - for i in range(4): - self.play(FlipCoin(randy)) - self.play(SplitRectsInBrickWall(self.row)) - self.split_decimals_alone() - self.merge_rects_by_subdiv() - self.merge_rects_by_coloring() - self.merge_decimals() - self.wait() - - - - - + # 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 + ) + + self.play( + SplitRectsInBrickWall(self.row) + ) + self.wait() + + # this tweaks an undesirable overlap in the next animation + self.merge_rects_by_subdiv() + + self.wait() + + # show individual outcomes + outcomes = previous_row.get_outcome_rects_for_level(5, with_labels = False) + grouped_outcomes = VGroup() + index = 0 + for i in range(6): + size = choose(5,i) + grouped_outcomes.add(VGroup(outcomes[index:index + size])) + index += size + + + grouped_outcomes_copy = grouped_outcomes.copy() + self.play( + LaggedStart(FadeIn, grouped_outcomes), + LaggedStart(FadeIn, grouped_outcomes_copy), + ) + self.wait() + target_outcomes = self.row.get_outcome_rects_for_level(6, with_labels = False) + target_outcomes.shift(w) + grouped_target_outcomes = VGroup() + index = 0 + for i in range(7): + size = choose(6,i) + grouped_target_outcomes.add(VGroup(target_outcomes[index:index + size])) + index += size + + self.play( + Transform(grouped_outcomes[2],grouped_target_outcomes[2][0][5:]) + ) + + self.play( + Transform(grouped_outcomes_copy[2],grouped_target_outcomes[3][0][:10]) + ) From 791b40037d40e7af7c9a8adc26775505aad9e055 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 20 Apr 2018 21:49:39 +0200 Subject: [PATCH 20/21] fixed centering issue --- active_projects/eop/chapter1.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/active_projects/eop/chapter1.py b/active_projects/eop/chapter1.py index 54fb105b..7574ff98 100644 --- a/active_projects/eop/chapter1.py +++ b/active_projects/eop/chapter1.py @@ -1230,10 +1230,6 @@ class PascalBrickWall(VMobject): rects.add(new_rect) return rects - def get_center(self): - return self.rects.get_center() - # just in case some more submobs have been added - # that displace the center def get_subdivs_for_level(self,r): subdivs = VGroup() @@ -1277,6 +1273,7 @@ class PascalBrickWall(VMobject): for center in centers: rects.add(rect.copy().move_to(center)) + rects.move_to(self.get_center()) if with_labels == False: @@ -1291,7 +1288,6 @@ class PascalBrickWall(VMobject): # not simply move_to bc coin_seq is not centered rect.add(coin_seq) - rects.move_to(self.get_center()) return rects def get_coin_sequences_for_level(self,r): @@ -1368,9 +1364,8 @@ class SplitRectsInBrickWall(Animation): for subdiv in self.subdivs: x = subdiv.get_start()[0] start = self.mobject.get_center() - end = self.mobject.get_center() start += x * RIGHT + 0.5 * self.mobject.height * UP - end += x * RIGHT + alpha * 0.5 * self.mobject.height * DOWN + end = start + alpha * self.mobject.height * DOWN subdiv.put_start_and_end_on(start,end) @@ -1579,6 +1574,7 @@ class PascalBrickWallScene(Scene): half_merged_row = self.row.copy() half_merged_row.subdiv_level += 1 half_merged_row.generate_points() + half_merged_row.move_to(self.row.get_center()) self.play(FadeIn(half_merged_row)) self.row = half_merged_row @@ -1668,7 +1664,7 @@ class PascalBrickWallScene(Scene): def construct(self): - self.force_skipping() + #self.force_skipping() randy = CoinFlippingPiCreature() randy = randy.scale(0.5).move_to(3*DOWN + 6*LEFT) @@ -1893,11 +1889,12 @@ class PascalBrickWallScene(Scene): ) self.wait() - # this tweaks an undesirable overlap in the next animation self.merge_rects_by_subdiv() self.wait() + + print previous_row.get_center() # show individual outcomes outcomes = previous_row.get_outcome_rects_for_level(5, with_labels = False) grouped_outcomes = VGroup() @@ -1919,7 +1916,6 @@ class PascalBrickWallScene(Scene): target_outcomes = self.row.get_outcome_rects_for_level(6, with_labels = False) - target_outcomes.shift(w) grouped_target_outcomes = VGroup() index = 0 for i in range(7): From 8bd4502f6584df9490d7b616944d40456b638fe7 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Mon, 23 Apr 2018 17:10:17 +0200 Subject: [PATCH 21/21] animated brick wall up to the sum rule --- active_projects/eop/chapter1.py | 141 +++++++++++++++++++++++++++++--- 1 file changed, 131 insertions(+), 10 deletions(-) diff --git a/active_projects/eop/chapter1.py b/active_projects/eop/chapter1.py index 7574ff98..5f28ee60 100644 --- a/active_projects/eop/chapter1.py +++ b/active_projects/eop/chapter1.py @@ -1134,7 +1134,7 @@ class ShowUncertainty3(Scene): SICKLY_GREEN = "#9BBD37" -class OneIn200HaveDisease(Scene): +class OneIn200HasDisease(Scene): def construct(self): title = TextMobject("1 in 200") title.to_edge(UP) @@ -1226,7 +1226,7 @@ class PascalBrickWall(VMobject): if len(rects.submobjects) > 0: new_rect.next_to(rects,RIGHT,buff = 0) else: - new_rect.next_to(0.5 * self.width * LEFT, RIGHT, buff = 0) + new_rect.next_to(self.get_center() + 0.5 * self.width * LEFT, RIGHT, buff = 0) rects.add(new_rect) return rects @@ -1261,10 +1261,12 @@ class PascalBrickWall(VMobject): centers = self.get_outcome_centers_for_level(r) outcome_width = self.outcome_shrinkage_factor_x * float(self.width) / (2 ** r) outcome_height = self.outcome_shrinkage_factor_y * self.height + corner_radius = min(0.1, 0.3 * min(outcome_width, outcome_height)) + # this scales down the corner radius for very narrow rects rect = RoundedRectangle( width = outcome_width, height = outcome_height, - corner_radius = 0.1, + corner_radius = corner_radius, fill_color = BLACK, fill_opacity = 0.2, stroke_width = 0 @@ -1894,7 +1896,6 @@ class PascalBrickWallScene(Scene): self.wait() - print previous_row.get_center() # show individual outcomes outcomes = previous_row.get_outcome_rects_for_level(5, with_labels = False) grouped_outcomes = VGroup() @@ -1906,29 +1907,149 @@ class PascalBrickWallScene(Scene): 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 + n = 5 # level to split + k = 2 # tally to split - - target_outcomes = self.row.get_outcome_rects_for_level(6, with_labels = False) + target_outcomes = self.row.get_outcome_rects_for_level(n + 1, with_labels = False) grouped_target_outcomes = VGroup() index = 0 - for i in range(7): - size = choose(6,i) + 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[2],grouped_target_outcomes[2][0][5:]) + Transform(grouped_outcomes[k],grouped_target_outcomes[k][0][old_tally_sizes[k - 1]:]) ) self.play( - Transform(grouped_outcomes_copy[2],grouped_target_outcomes[3][0][:10]) + Transform(grouped_outcomes_copy[k],grouped_target_outcomes[k + 1][0][:old_tally_sizes[k]]) ) + + + old_tally_sizes.append(0) # makes the ege cases work properly + + # split the other + 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],grouped_target_outcomes[k][0][old_tally_sizes[k - 1]:]), + Transform(grouped_outcomes_copy[k - 1],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], + ) + + # show new outcome sizes + new_decimals = VGroup() + for (i,rect) in zip(new_tally_sizes, new_rects): + decimal = Integer(i).move_to(rect) + new_decimals.add(decimal) + + self.play( + FadeOut(decimals_copy[k - 1]), + FadeOut(decimals_copy2[k]), + FadeIn(new_decimals[k]) + ) + + # move the old decimals into the new row + anims = [] + anims.append(decimals_copy2[0].move_to) + anims.append(new_rects[0]) + for i in range(1,k) + range(k + 1, n): + anims.append(decimals_copy[i - 1].move_to) + anims.append(new_rects[i]) + anims.append(decimals_copy2[i].move_to) + anims.append(new_rects[i]) + anims.append(decimals_copy[n].move_to) + anims.append(new_rects[n + 1]) + + self.play(*anims) + + # fade them out and fade in their sums + anims = [] + for i in range(1,k) + range(k + 1, n): + anims.append(FadeOut(decimals_copy[i - 1])) + anims.append(FadeOut(decimals_copy2[i])) + anims.append(FadeIn(new_decimals[i])) + + self.play(*anims) + + self.add_foreground_mobject(new_decimals) + + + + + + + + + + + + + + + + + + + + + + + + +