from constants import * from mobject.tex_mobject import TexMobject from mobject.vectorized_mobject import VMobject from scene.scene import Scene from utils.simple_functions import choose DEFAULT_COUNT_NUM_OFFSET = (FRAME_X_RADIUS - 1, FRAME_Y_RADIUS - 1, 0) DEFAULT_COUNT_RUN_TIME = 5.0 class CountingScene(Scene): def count(self, items, item_type = "mobject", *args, **kwargs): if item_type == "mobject": self.count_mobjects(items, *args, **kwargs) elif item_type == "region": self.count_regions(items, *args, **kwargs) else: raise Exception("Unknown item_type, should be mobject or region") return self def count_mobjects( self, mobjects, mode = "highlight", color = "red", display_numbers = True, num_offset = DEFAULT_COUNT_NUM_OFFSET, run_time = DEFAULT_COUNT_RUN_TIME): """ Note, leaves final number mobject as "number" attribute mode can be "highlight", "show_creation" or "show", otherwise a warning is given and nothing is animating during the count """ if len(mobjects) > 50: #TODO raise Exception("I don't know if you should be counting \ too many mobjects...") if len(mobjects) == 0: raise Exception("Counting mobject list of length 0") if mode not in ["highlight", "show_creation", "show"]: raise Warning("Unknown mode") frame_time = run_time / len(mobjects) if mode == "highlight": self.add(*mobjects) for mob, num in zip(mobjects, it.count(1)): if display_numbers: num_mob = TexMobject(str(num)) num_mob.center().shift(num_offset) self.add(num_mob) if mode == "highlight": original_color = mob.color mob.set_color(color) self.wait(frame_time) mob.set_color(original_color) if mode == "show_creation": self.play(ShowCreation(mob, run_time = frame_time)) if mode == "show": self.add(mob) self.wait(frame_time) if display_numbers: self.remove(num_mob) if display_numbers: self.add(num_mob) self.number = num_mob return self def count_regions(self, regions, mode = "one_at_a_time", num_offset = DEFAULT_COUNT_NUM_OFFSET, run_time = DEFAULT_COUNT_RUN_TIME, **unused_kwargsn): if mode not in ["one_at_a_time", "show_all"]: raise Warning("Unknown mode") frame_time = run_time / (len(regions)) for region, count in zip(regions, it.count(1)): num_mob = TexMobject(str(count)) num_mob.center().shift(num_offset) self.add(num_mob) self.set_color_region(region) self.wait(frame_time) if mode == "one_at_a_time": self.reset_background() self.remove(num_mob) self.add(num_mob) self.number = num_mob return self class PascalsTriangle(VMobject): CONFIG = { "nrows" : 7, "height" : FRAME_HEIGHT - 1, "width" : 1.5*FRAME_X_RADIUS, "portion_to_fill" : 0.7 } def generate_points(self): self.cell_height = float(self.height) / self.nrows self.cell_width = float(self.width) / self.nrows self.bottom_left = (self.cell_width * self.nrows / 2.0)*LEFT + \ (self.cell_height * self.nrows / 2.0)*DOWN num_to_num_mob = {} self.coords_to_mobs = {} self.coords = [ (n, k) for n in range(self.nrows) for k in range(n+1) ] for n, k in self.coords: num = choose(n, k) center = self.coords_to_center(n, k) num_mob = TexMobject(str(num)) scale_factor = min( 1, self.portion_to_fill * self.cell_height / num_mob.get_height(), self.portion_to_fill * self.cell_width / num_mob.get_width(), ) num_mob.center().scale(scale_factor).shift(center) if n not in self.coords_to_mobs: self.coords_to_mobs[n] = {} self.coords_to_mobs[n][k] = num_mob self.add(*[ self.coords_to_mobs[n][k] for n, k in self.coords ]) return self def coords_to_center(self, n, k): x_offset = self.cell_width * (k+self.nrows/2.0 - n/2.0) y_offset = self.cell_height * (self.nrows - n) return self.bottom_left + x_offset*RIGHT + y_offset*UP def generate_n_choose_k_mobs(self): self.coords_to_n_choose_k = {} for n, k in self.coords: nck_mob = TexMobject(r"{%d \choose %d}"%(n, k)) scale_factor = min( 1, self.portion_to_fill * self.cell_height / nck_mob.get_height(), self.portion_to_fill * self.cell_width / nck_mob.get_width(), ) center = self.coords_to_mobs[n][k].get_center() nck_mob.center().scale(scale_factor).shift(center) if n not in self.coords_to_n_choose_k: self.coords_to_n_choose_k[n] = {} self.coords_to_n_choose_k[n][k] = nck_mob return self def fill_with_n_choose_k(self): if not hasattr(self, "coords_to_n_choose_k"): self.generate_n_choose_k_mobs() self.submobjects = [] self.add(*[ self.coords_to_n_choose_k[n][k] for n, k in self.coords ]) return self def generate_sea_of_zeros(self): zero = TexMobject("0") self.sea_of_zeros = [] for n in range(self.nrows): for a in range((self.nrows - n)/2 + 1): for k in (n + a + 1, -a -1): self.coords.append((n, k)) mob = zero.copy() mob.shift(self.coords_to_center(n, k)) self.coords_to_mobs[n][k] = mob self.add(mob) return self