From d94870ba244bc686dcc181b249f9dd30dbfe3373 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Mon, 12 Mar 2018 13:45:56 +0100 Subject: [PATCH 1/7] Sketched anim for Birthday Paradox --- active_projects/eop/birthday.py | 47 +++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 active_projects/eop/birthday.py diff --git a/active_projects/eop/birthday.py b/active_projects/eop/birthday.py new file mode 100644 index 00000000..f59f7021 --- /dev/null +++ b/active_projects/eop/birthday.py @@ -0,0 +1,47 @@ +from helpers import * +from mobject import Mobject +from mobject.vectorized_mobject import * +from animation.animation import Animation +from animation.transform import * +from animation.simple_animations import * +from topics.geometry import * +from scene import Scene +from camera import * +from topics.number_line import * +from topics.three_dimensions import * +from topics.light import * +from topics.characters import * +from topics.numerals import * + + + +class Birthday(Scene): + + def construct(self): + + sidelength = 6.0 + corner = np.array([-sidelength/2,-sidelength/2,0]) + nb_days_left = 365.0 + toggle = False + + def probability(): + width = rect.get_width() + height = rect.get_height() + return width * height / sidelength**2 + + rect = Square().scale(sidelength/2) + + while probability() > 0.5: + + self.add(rect.copy()) + nb_days_left -= 1 + + if toggle: + dim = 0 + else: + dim = 1 + + rect.stretch_about_point(nb_days_left / 365, dim, corner) + + toggle = not toggle + From 1024d454a09c61b4ea490f5c01e10e56d6ace2dc Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Mon, 12 Mar 2018 13:46:41 +0100 Subject: [PATCH 2/7] Primes animation --- primes.py | 265 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 primes.py diff --git a/primes.py b/primes.py new file mode 100644 index 00000000..aab741e7 --- /dev/null +++ b/primes.py @@ -0,0 +1,265 @@ +from helpers import * +from mobject import Mobject +from mobject.vectorized_mobject import * +from animation.animation import Animation +from animation.transform import * +from animation.simple_animations import * +from topics.geometry import * +from scene import Scene +from camera import * +from topics.number_line import * +from topics.three_dimensions import * +from topics.light import * +from topics.characters import * +from topics.numerals import * + +def is_prime(n): + + for i in primes(n**0.5): + if n % i == 0: + return False + + return True + +def primes(max_n): + + if max_n < 2: + return [] + + numbers = range(2, int(max_n) + 1) + p = [] + + while len(numbers) > 0: + q = numbers[0] + p.append(q) + numbers = [x for x in numbers if x % q != 0] + + return p + +def prime_factors(n): + + if is_prime(n): + return [n] + + i = 0 + primes_list = primes(n/2) + + factors = [] + r = n + + while r >= 2: + p = primes_list[i] + if r % p == 0: + factors.append(p) + r = r/p + else: + i += 1 + + return factors + + +RUN_TIME = 0.5 +DOWN_SHIFT = 0.0 * DOWN + +class Primes(Scene): + + def construct(self): + + N = 100 + + primes_list = np.array(primes(N)) + + palette = ["#FBA125", "#76CD42", "#30CCF5", "#9377C4", "#F95137", + # 2 3 5 7 11 + "#1B442E", TEAL_E, MAROON_A, DARK_BROWN, PINK, + # 13 17 19 23 29 + "#9C25FB", GREEN_E, MAROON_E, GOLD_E, GREEN_E, + # 31 37 41 43 47 # last prime to occur in a factorization + LIGHT_BROWN, DARK_BLUE, GREY_BROWN, GREEN_C, BLUE_C, + # 53 59 61 67 71 + PURPLE_C, RED_C, YELLOW_E, TEAL_C, MAROON_C] + # 73 79 83 89 97 + + nb_primes = len(primes_list) + print nb_primes + + prime_points_radius = 3.2 + angles = np.arange(TAU/4, -3*TAU/4, -TAU/float(nb_primes)) + print len(angles), angles + prime_points = [prime_points_radius * (np.cos(theta) * RIGHT + + np.sin(theta) * UP) + for theta in angles] + print len(prime_points) + + wheel = Wheel() + + angles = [TAU] + colors = [LIGHT_GREY] + + wheel.update_sectors(angles, colors) + wheel.rotate(-TAU/4).shift(DOWN_SHIFT) + self.add(wheel) + + number = DecimalNumber(1, num_decimal_points = 0).scale(2).shift(DOWN_SHIFT) + self.add(number) + self.wait(RUN_TIME) + + j = 0 + + for i in range(2,N+1): + + factors = prime_factors(i) + factor_indices = [np.where(primes_list == x)[0][0] for x in factors] + + nb_sectors = float(len(factor_indices)) + new_angles = np.ones(nb_sectors) / nb_sectors * TAU + + new_colors = [] + for index in factor_indices: + new_colors.append(palette[index]) + + self.play( + UpdateAngles(wheel, new_angles = new_angles, new_colors = new_colors, + run_time = RUN_TIME), + ChangeDecimalToValue(number, i, run_time = RUN_TIME) + ) + self.wait(RUN_TIME) + + if is_prime(i): + full_wheel = VGroup(wheel,number).copy() + full_wheel_copy = full_wheel.copy() + full_wheel_copy.scale(0.15).move_to(prime_points[j]) + print j + j += 1 + self.play( + Transform(full_wheel, full_wheel_copy) + ) + + + + +class Wheel(VMobject): + + CONFIG = { + "inner_radius" : 1.2, + "outer_radius" : 2.4, + "nb_sectors" : 25, + "colors" : [BLACK] * 25 + } + + def generate_points(self): + + angle = TAU/self.nb_sectors + angle_range = np.arange(0,TAU,angle) + for j in range(self.nb_sectors - len(angle_range)): + angle_range = np.append(angle_range, TAU) + self.colors.append(BLACK) + + for (i,theta) in enumerate(angle_range): + if theta != TAU: + use_angle = angle + else: + use_angle = 0 + sector = AnnularSector( + inner_radius = self.inner_radius, + outer_radius = self.outer_radius, + angle = use_angle, + start_angle = theta, + fill_color = self.colors[i], + fill_opacity = 1, + stroke_color = WHITE, + stroke_width = 5 + ).rotate_about_origin(TAU/2, axis = UP).shift(DOWN_SHIFT) + self.add(sector) + + def update_sectors(self, new_angles, new_colors): + + if len(new_angles) > self.nb_sectors: + raise "More angles than sectors!" + + for i in range(len(new_angles), self.nb_sectors): + new_angles = np.append(new_angles, 0) + new_colors.append(BLACK) + + self.colors = new_colors + + new_start_angles = -np.cumsum(new_angles) + new_angles + + for (i,sector) in enumerate(self.submobjects): + sector.angle = new_angles[i] + sector.start_angle = new_start_angles[i] + sector.set_fill(color = new_colors[i]) + sector.generate_points() + sector.rotate_about_origin(TAU/2, axis = UP).shift(DOWN_SHIFT) + + + + + +class UpdateAngles(Animation): + + def __init__(self,mobject,**kwargs): + + self.old_angles = [] + for (i, sector) in enumerate(mobject.submobjects): + self.old_angles.append(sector.angle) + + self.old_angles = np.array(self.old_angles) + self.old_start_angles = np.cumsum(self.old_angles) - self.old_angles + TAU/4 + + digest_config(self, kwargs) + Animation.__init__(self,mobject,**kwargs) + + def update_submobject(self, submobject, starting_submobject, alpha): + + i = 0 + for submob in self.mobject.submobjects: + if submobject == submob: + break + else: + i += 1 + + for j in range(len(self.new_angles), self.mobject.nb_sectors): + + self.new_angles = np.append(self.new_angles, 0) + self.new_colors.append(BLACK) + + self.new_start_angles = np.cumsum(self.new_angles) - self.new_angles + TAU/4 + # this should be in __init__! + # but has no effect there + + submobject.angle = interpolate( + self.old_angles[i], self.new_angles[i], alpha + ) + submobject.start_angle = interpolate( + self.old_start_angles[i], + self.new_start_angles[i], alpha + ) + + + interpolated_color = interpolate_color( + self.mobject.colors[i], + self.new_colors[i], + alpha + ) + + submobject.set_fill(color = interpolated_color) + submobject.generate_points() + submobject.rotate_about_origin(TAU/2, axis = UP).shift(DOWN_SHIFT) + + if alpha > 0.95: + self.mobject.colors[i] = self.new_colors[i] + + + + + + + + + + + + + + From bde09dab12c7419fdfba8c043a6dc3566c222f98 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 23 Mar 2018 09:36:04 +0100 Subject: [PATCH 3/7] started histogram class --- active_projects/eop/histograms.py | 131 ++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 active_projects/eop/histograms.py diff --git a/active_projects/eop/histograms.py b/active_projects/eop/histograms.py new file mode 100644 index 00000000..394fb016 --- /dev/null +++ b/active_projects/eop/histograms.py @@ -0,0 +1,131 @@ +from helpers import * +from mobject import Mobject +from mobject.vectorized_mobject import * +from animation.animation import Animation +from animation.transform import * +from animation.simple_animations import * +from topics.geometry import * +from scene import Scene +from camera import * +from topics.number_line import * +from topics.three_dimensions import * +from topics.light import * +from topics.characters import * +from topics.numerals import * + + + +class Histogram(VMobject): + + CONFIG = { + "start_color" : YELLOW, + "end_color" : BLUE, + "x_scale" : 1.0, + "y_scale" : 1.0, + } + + def __init__(self, x_values, y_values, **kwargs): + + digest_config(self, kwargs) + + # preliminaries + self.x_values = x_values + self.y_values = y_values + + self.x_steps = x_values[1:] - x_values[:-1] + self.x_min = x_values[0] - self.x_steps[0] * 0.5 + self.x_posts = (x_values[1:] + x_values[:-1]) * 0.5 + self.x_max = x_values[-1] + self.x_steps[-1] * 0.5 + self.x_posts = np.insert(self.x_posts,0,self.x_min) + self.x_posts = np.append(self.x_posts,self.x_max) + + self.x_widths = self.x_posts[1:] - self.x_posts[:-1] + + self.x_values_scaled = self.x_scale * x_values + self.x_steps_scaled = self.x_scale * self.x_steps + self.x_posts_scaled = self.x_scale * self.x_posts + self.x_min_scaled = self.x_scale * self.x_min + self.x_max_scaled = self.x_scale * self.x_max + self.x_widths_scaled = self.x_scale * self.x_widths + + self.y_values_scaled = self.y_scale * self.y_values + + VMobject.__init__(self, **kwargs) + + def generate_points(self): + + previous_bar = ORIGIN + + outline_points = [] + + for (i,x) in enumerate(self.x_values): + + bar = Rectangle( + width = self.x_widths_scaled[i], + height = self.y_values_scaled[i], + ) + t = float(x - self.x_values[0])/(self.x_values[-1] - self.x_values[0]) + bar_color = interpolate_color( + self.start_color, + self.end_color, + t + ) + bar.set_fill(color = bar_color, opacity = 1) + bar.set_stroke(width = 0) + bar.next_to(previous_bar,RIGHT,buff = 0) + if i != 0: + height_diff = previous_bar.get_height() - bar.get_height() + bar.shift(height_diff * 0.5 * DOWN) + + self.add(bar) + + if i == 0: + # start with the lower left + outline_points.append(bar.get_anchors()[-2]) + + # upper two points of each bar + outline_points.append(bar.get_anchors()[0]) + outline_points.append(bar.get_anchors()[1]) + + previous_bar = bar + + # close the outline + # lower right + outline_points.append(bar.get_anchors()[2]) + # lower left + outline_points.append(outline_points[0]) + + self.outline = Polygon(*outline_points) + self.outline.set_stroke(color = WHITE) + self.add(self.outline) + + + +class SampleScene(Scene): + + def construct(self): + + x_values = np.array([1,3,6,10,15]) + y_values = np.array([15,10,6,3,1]) + + hist = Histogram( + x_values = x_values, + y_values = y_values, + x_scale = 0.2, + y_scale = 0.2, + ) + self.add(hist) + self.wait() + + + + + + + + + + + + + From 52cabaaaf004e0f4d49b6c36d6b880222e5b99e6 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 23 Mar 2018 15:08:29 +0100 Subject: [PATCH 4/7] Histogram can be flashed through linearly and randomly --- active_projects/eop/histograms.py | 140 +++++++++++++++++++++++++++--- 1 file changed, 126 insertions(+), 14 deletions(-) diff --git a/active_projects/eop/histograms.py b/active_projects/eop/histograms.py index 394fb016..4046a7d4 100644 --- a/active_projects/eop/histograms.py +++ b/active_projects/eop/histograms.py @@ -12,13 +12,13 @@ from topics.three_dimensions import * from topics.light import * from topics.characters import * from topics.numerals import * - +from random import * class Histogram(VMobject): CONFIG = { - "start_color" : YELLOW, + "start_color" : RED, "end_color" : BLUE, "x_scale" : 1.0, "y_scale" : 1.0, @@ -52,12 +52,14 @@ class Histogram(VMobject): VMobject.__init__(self, **kwargs) + def generate_points(self): previous_bar = ORIGIN - + self.bars = [] outline_points = [] + for (i,x) in enumerate(self.x_values): bar = Rectangle( @@ -72,12 +74,10 @@ class Histogram(VMobject): ) bar.set_fill(color = bar_color, opacity = 1) bar.set_stroke(width = 0) - bar.next_to(previous_bar,RIGHT,buff = 0) - if i != 0: - height_diff = previous_bar.get_height() - bar.get_height() - bar.shift(height_diff * 0.5 * DOWN) + bar.next_to(previous_bar,RIGHT,buff = 0, aligned_edge = DOWN) self.add(bar) + self.bars.append(bar) if i == 0: # start with the lower left @@ -99,25 +99,137 @@ class Histogram(VMobject): self.outline.set_stroke(color = WHITE) self.add(self.outline) + def get_lower_left_point(self): + return self.bars[0].get_anchors()[-2] + + + + + +class FlashThroughHistogram(Animation): + CONFIG = { + "cell_height" : 1.0, + "cell_color" : WHITE, + "cell_opacity" : 0.8, + "hist_opacity" : 0.2 + } + + def __init__(self, mobject, mode = "random", **kwargs): + + digest_config(self,kwargs) + + self.cell_height_scaled = mobject.y_scale * self.cell_height + self.prototype_cell = Rectangle( + width = 1, + height = self.cell_height_scaled, + fill_color = self.cell_color, + fill_opacity = self.cell_opacity, + stroke_width = 0, + ) + + x_values = mobject.x_values + y_values = mobject.y_values + + self.mode = mode + + self.generate_cell_indices(x_values,y_values) + Animation.__init__(self,mobject,**kwargs) + + + + def generate_cell_indices(self,x_values,y_values): + + self.cell_indices = [] + for (i,x) in enumerate(x_values): + + nb_cells = int(y_values[i]) + for j in range(nb_cells): + self.cell_indices.append((i, j)) + + self.reordered_cell_indices = self.cell_indices + if self.mode == "random": + shuffle(self.reordered_cell_indices) + + + def cell_center_for_index(self,i,j): + x = self.mobject.x_values_scaled[i] - self.mobject.x_min_scaled + y = (j + 0.5) * self.cell_height_scaled + center = self.mobject.get_lower_left_point() + x * RIGHT + y * UP + return center + + + def update_mobject(self,t): + + if t == 0: + self.mobject.add(self.prototype_cell) + + flash_nb = int(t * (len(self.cell_indices) - 1)) + (i,j) = self.reordered_cell_indices[flash_nb] + cell_center = self.cell_center_for_index(i,j) + self.prototype_cell.width = self.mobject.x_widths_scaled[i] + self.prototype_cell.generate_points() + self.prototype_cell.move_to(cell_center) + + if t == 1: + self.mobject.remove(self.prototype_cell) + + + + + + + + + + + + + + + class SampleScene(Scene): def construct(self): - x_values = np.array([1,3,6,10,15]) - y_values = np.array([15,10,6,3,1]) + x_values = np.array([1,2,3,4,5]) + y_values = np.array([15,10,6,3,10]) - hist = Histogram( + hist1 = Histogram( x_values = x_values, y_values = y_values, + x_scale = 0.5, + y_scale = 0.2, + ).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.2, y_scale = 0.2, ) - self.add(hist) - self.wait() - - + + 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, + mode = "linear_horizontal", + run_time = 3, + rate_func = None, + ) + ) From 0d20a94c294f48abac45475f1edb18405c10d4e6 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 23 Mar 2018 17:00:12 +0100 Subject: [PATCH 5/7] histogram can be flashed through horizontally and vertically, linearly or at random --- active_projects/eop/histograms.py | 62 +++++++++++++++++++------------ 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/active_projects/eop/histograms.py b/active_projects/eop/histograms.py index 4046a7d4..ec3b3538 100644 --- a/active_projects/eop/histograms.py +++ b/active_projects/eop/histograms.py @@ -108,20 +108,19 @@ class Histogram(VMobject): class FlashThroughHistogram(Animation): CONFIG = { - "cell_height" : 1.0, "cell_color" : WHITE, "cell_opacity" : 0.8, "hist_opacity" : 0.2 } - def __init__(self, mobject, mode = "random", **kwargs): + def __init__(self, mobject, direction = "horizontal", mode = "random", **kwargs): - digest_config(self,kwargs) + digest_config(self, kwargs) - self.cell_height_scaled = mobject.y_scale * self.cell_height + self.cell_height = mobject.y_scale self.prototype_cell = Rectangle( width = 1, - height = self.cell_height_scaled, + height = self.cell_height, fill_color = self.cell_color, fill_opacity = self.cell_opacity, stroke_width = 0, @@ -131,6 +130,7 @@ class FlashThroughHistogram(Animation): y_values = mobject.y_values self.mode = mode + self.direction = direction self.generate_cell_indices(x_values,y_values) Animation.__init__(self,mobject,**kwargs) @@ -142,7 +142,7 @@ class FlashThroughHistogram(Animation): self.cell_indices = [] for (i,x) in enumerate(x_values): - nb_cells = int(y_values[i]) + nb_cells = y_values[i] for j in range(nb_cells): self.cell_indices.append((i, j)) @@ -151,11 +151,25 @@ class FlashThroughHistogram(Animation): shuffle(self.reordered_cell_indices) - def cell_center_for_index(self,i,j): - x = self.mobject.x_values_scaled[i] - self.mobject.x_min_scaled - y = (j + 0.5) * self.cell_height_scaled - center = self.mobject.get_lower_left_point() + x * RIGHT + y * UP - return center + def cell_for_index(self,i,j): + + if self.direction == "vertical": + width = self.mobject.x_scale + height = self.mobject.y_scale + x = (i + 0.5) * self.mobject.x_scale + y = (j + 0.5) * self.mobject.y_scale + center = self.mobject.get_lower_left_point() + x * RIGHT + y * UP + + elif self.direction == "horizontal": + width = self.mobject.x_scale / self.mobject.y_values[i] + height = self.mobject.y_scale * self.mobject.y_values[i] + x = i * self.mobject.x_scale + (j + 0.5) * width + y = height / 2 + center = self.mobject.get_lower_left_point() + x * RIGHT + y * UP + + cell = Rectangle(width = width, height = height) + cell.move_to(center) + return cell def update_mobject(self,t): @@ -163,15 +177,16 @@ class FlashThroughHistogram(Animation): if t == 0: self.mobject.add(self.prototype_cell) - flash_nb = int(t * (len(self.cell_indices) - 1)) + flash_nb = int(t * (len(self.cell_indices))) - 1 (i,j) = self.reordered_cell_indices[flash_nb] - cell_center = self.cell_center_for_index(i,j) - self.prototype_cell.width = self.mobject.x_widths_scaled[i] + cell = self.cell_for_index(i,j) + self.prototype_cell.width = cell.get_width() + self.prototype_cell.height = cell.get_height() self.prototype_cell.generate_points() - self.prototype_cell.move_to(cell_center) + self.prototype_cell.move_to(cell.get_center()) - if t == 1: - self.mobject.remove(self.prototype_cell) + #if t == 1: + # self.mobject.remove(self.prototype_cell) @@ -194,13 +209,13 @@ class SampleScene(Scene): def construct(self): x_values = np.array([1,2,3,4,5]) - y_values = np.array([15,10,6,3,10]) + 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.2, + y_scale = 0.5, ).shift(1*DOWN) self.add(hist1) self.wait() @@ -210,8 +225,8 @@ class SampleScene(Scene): hist2 = Histogram( x_values = x_values, y_values = y_values2, - x_scale = 0.2, - y_scale = 0.2, + x_scale = 0.5, + y_scale = 0.5, ) v1 = hist1.get_lower_left_point() @@ -225,8 +240,9 @@ class SampleScene(Scene): self.play( FlashThroughHistogram( hist1, - mode = "linear_horizontal", - run_time = 3, + direction = "horizontal", + mode = "linear", + run_time = 10, rate_func = None, ) ) From 709b4fbfd79b55af36f3363838e4467844c08b46 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Mon, 26 Mar 2018 00:27:45 +0200 Subject: [PATCH 6/7] Playing with Pascal --- active_projects/eop/pascal.py | 207 ++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 active_projects/eop/pascal.py diff --git a/active_projects/eop/pascal.py b/active_projects/eop/pascal.py new file mode 100644 index 00000000..b2f5f40d --- /dev/null +++ b/active_projects/eop/pascal.py @@ -0,0 +1,207 @@ +from helpers import * +from mobject import Mobject +from mobject.vectorized_mobject import * +from animation.animation import Animation +from animation.transform import * +from animation.simple_animations import * +from topics.geometry import * +from scene import Scene +from camera import * + +nb_levels = 50 + +dev_x_step = 2 +dev_y_step = 5 + +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] + + + + + + +class PascalScene(Scene): + + def construct(self): + + unit_width = 0.25 + top_height = 4.0 + level_height = 2.0 * top_height / nb_levels + + start_points = np.array([top_height * UP]) + + dev_start = start_points[0] + + j = 0 + + for n in range(nb_levels): + + half_width = 0.5 * (n + 0.5) * unit_width + + stop_points_left = start_points.copy() + stop_points_left[:,0] -= 0.5 * unit_width + stop_points_left[:,1] -= level_height + + stop_points_right = start_points.copy() + stop_points_right[:,0] += 0.5 * unit_width + stop_points_right[:,1] -= level_height + + for (p,q) in zip(start_points,stop_points_left): + alpha = np.abs((p[0]+q[0])/2) / half_width + color = rainbow_color(alpha) + line = Line(p,q, stroke_color = color) + self.add(line) + + for (i,(p,q)) in enumerate(zip(start_points,stop_points_right)): + alpha = np.abs((p[0]+q[0])/2) / half_width + color = rainbow_color(alpha) + line = Line(p,q, stroke_color = color) + self.add(line) + + if (n + 1) % dev_y_step == 0 and n != 1: + j += dev_x_step + dev_stop = stop_points_left[j] + line = Line(dev_start,dev_stop,stroke_color = WHITE) + self.add(line) + dot = Dot(dev_stop, fill_color = WHITE) + self.add_foreground_mobject(dot) + dev_start = dev_stop + + start_points = np.append(stop_points_left,[stop_points_right[-1]], axis = 0) + + + self.wait() + + + +class RescaledPascalScene(Scene): + + def construct(self): + + half_width = 3.0 + top_height = 4.0 + level_height = 2.0 * top_height / nb_levels + + start_points = np.array([top_height * UP]) + left_edge = top_height * UP + half_width * LEFT + right_edge = top_height * UP + half_width * RIGHT + + dev_start = start_points[0] + + j = 0 + + for n in range(nb_levels): + + if n == 0: + start_points_left_shift = np.array([left_edge]) + else: + start_points_left_shift = start_points[:-1] + start_points_left_shift = np.insert(start_points_left_shift,0,left_edge, axis = 0) + stop_points_left = 0.5 * (start_points + start_points_left_shift) + stop_points_left += level_height * DOWN + + + if n == 0: + start_points_right_shift = np.array([right_edge]) + else: + start_points_right_shift = start_points[1:] + start_points_right_shift = np.append(start_points_right_shift,np.array([right_edge]), axis = 0) + stop_points_right = 0.5 * (start_points + start_points_right_shift) + stop_points_right += level_height * DOWN + + + for (i,(p,q)) in enumerate(zip(start_points,stop_points_left)): + + color = LIGHT_GRAY + + if n % 2 == 0 and i <= n/2: + m = n/2 + 0.25 + jj = i + alpha = 1 - float(jj)/m + color = rainbow_color(alpha) + + elif n % 2 == 0 and i > n/2: + m = n/2 + 0.25 + jj = n - i + 0.5 + alpha = 1 - float(jj)/m + color = rainbow_color(alpha) + + elif n % 2 == 1 and i <= n/2: + m = n/2 + 0.75 + jj = i + alpha = 1 - float(jj)/m + color = rainbow_color(alpha) + + elif n % 2 == 1 and i > n/2: + m = n/2 + 0.75 + jj = n - i + 0.5 + alpha = 1 - float(jj)/m + color = rainbow_color(alpha) + + line = Line(p,q, stroke_color = color) + self.add(line) + + for (i,(p,q)) in enumerate(zip(start_points,stop_points_right)): + + color = LIGHT_GRAY + + if n % 2 == 0 and i < n/2: + m = n/2 + 0.25 + jj = i + 0.5 + alpha = 1 - float(jj)/m + color = rainbow_color(alpha) + + elif n % 2 == 0 and i >= n/2: + m = n/2 + 0.25 + jj = n - i + alpha = 1 - float(jj)/m + color = rainbow_color(alpha) + + elif n % 2 == 1 and i <= n/2: + m = n/2 + 0.75 + jj = i + 0.5 + alpha = 1 - float(jj)/m + color = rainbow_color(alpha) + + elif n % 2 == 1 and i > n/2: + m = n/2 + 0.75 + jj = n - i + alpha = 1 - float(jj)/m + color = rainbow_color(alpha) + + + line = Line(p,q, stroke_color = color) + self.add(line) + + if (n + 1) % dev_y_step == 0 and n != 1: + j += dev_x_step + dev_stop = stop_points_left[j] + line = Line(dev_start,dev_stop,stroke_color = WHITE) + self.add(line) + dot = Dot(dev_stop, fill_color = WHITE) + self.add_foreground_mobject(dot) + dev_start = dev_stop + + + + start_points = np.append(stop_points_left,[stop_points_right[-1]], axis = 0) + + left_edge += level_height * DOWN + right_edge += level_height * DOWN + + + self.wait() + + + + + + + + + From 698e79ebeaf270771993d739e5039ef5479ae04a Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Thu, 29 Mar 2018 10:03:00 +0200 Subject: [PATCH 7/7] added x labels --- active_projects/eop/histograms.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/active_projects/eop/histograms.py b/active_projects/eop/histograms.py index ec3b3538..be0ea491 100644 --- a/active_projects/eop/histograms.py +++ b/active_projects/eop/histograms.py @@ -14,6 +14,13 @@ from topics.characters import * from topics.numerals import * from random import * +def text_range(start,stop,step): + numbers = np.arange(start,stop,step) + labels = [] + for x in numbers: + labels.append(str(x)) + return labels + class Histogram(VMobject): @@ -51,6 +58,7 @@ class Histogram(VMobject): self.y_values_scaled = self.y_scale * self.y_values VMobject.__init__(self, **kwargs) + digest_config(self, kwargs) def generate_points(self): @@ -58,7 +66,7 @@ class Histogram(VMobject): previous_bar = ORIGIN self.bars = [] outline_points = [] - + self.x_labels = text_range(self.x_values[0], self.x_max, self.x_steps[0]) for (i,x) in enumerate(self.x_values): @@ -79,6 +87,10 @@ class Histogram(VMobject): self.add(bar) self.bars.append(bar) + label = TextMobject(self.x_labels[i]) + label.next_to(bar,DOWN) + self.add(label) + if i == 0: # start with the lower left outline_points.append(bar.get_anchors()[-2]) @@ -227,6 +239,7 @@ class SampleScene(Scene): 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()