From 0c810b43a901160016601466e91040858759e578 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Fri, 4 Jan 2019 14:14:59 -0800 Subject: [PATCH] Most of BlocksAndWallScene is setup --- active_projects/clacks.py | 156 +++++++++++++++++++++++++++++++------- 1 file changed, 130 insertions(+), 26 deletions(-) diff --git a/active_projects/clacks.py b/active_projects/clacks.py index 0c3df6a4..433fd4c0 100644 --- a/active_projects/clacks.py +++ b/active_projects/clacks.py @@ -3,25 +3,24 @@ from big_ol_pile_of_manim_imports import * class SlidingBlocks(VGroup): CONFIG = { - "block_dynamic_configs": [ - { - "mass": 100, - "velocity": -2, - "distance": 7, - "width": 1, - }, - { - "mass": 1, - "velocity": 0, - "distance": 3, - "width": 1, - }, - ], + "block1_config": { + "mass": 1, + "velocity": -2, + "distance": 7, + "width": 1, + }, + "block2_config": { + "mass": 1, + "velocity": 0, + "distance": 3, + "width": 1, + }, "block_style": { "fill_opacity": 1, "fill_color": (GREY, LIGHT_GREY), "stroke_width": 3, "stroke_color": WHITE, + "sheen_direction": UL, } } @@ -31,27 +30,69 @@ class SlidingBlocks(VGroup): self.floor = surrounding_scene.floor self.wall = surrounding_scene.wall - for config in self.block_dynamic_configs: - self.add(self.get_block(**config)) + self.block1 = self.get_block(**self.block1_config) + self.block2 = self.get_block(**self.block2_config) + self.phase_space_point_tracker = self.get_phase_space_point_tracker() + self.add( + self.block1, self.block2, + self.phase_space_point_tracker, + ) self.add_updater(self.__class__.update_positions) + # From here, there's enough information to create + # a list of all clack times and locations + def get_block(self, mass, width, distance, velocity): block = Square(side_length=width) + block.mass = mass + block.velocity = velocity + block.set_style(**self.block_style) + block.set_fill(color=interpolate_color( + block.get_fill_color(), + BLUE_E, + (1 - 1.0 / (max(np.log10(mass), 1))) + )) block.move_to( self.floor.get_top()[1] * UP + (self.wall.get_right()[0] + distance) * RIGHT, DL, ) - block.mass = mass - block.velocity = velocity - label = block.label = TextMobject("{}kg".format(mass)) + label = block.label = TextMobject( + "{:,}\\,kg".format(mass) + ) label.scale(0.8) label.next_to(block, UP, SMALL_BUFF) block.add(label) return block + def get_phase_space_point_tracker(self): + block1, block2 = self.block1, self.block2 + w2 = block2.get_width() + s1 = block1.get_left()[0] - self.wall.get_right()[0] - w2 + s2 = block2.get_right()[0] - self.wall.get_right()[0] - w2 + result = VectorizedPoint([ + s1 * np.sqrt(block1.mass), + s2 * np.sqrt(block2.mass), + 0 + ]) + + result.velocity = np.array([ + np.sqrt(block1.mass) * block1.velocity, + np.sqrt(block2.mass) * block2.velocity, + 0 + ]) + return result + def update_positions(self, dt): + self.phase_space_point_tracker.shift( + self.phase_space_point_tracker.velocity * dt + ) + self.update_blocks_from_phase_space_point_tracker() + + def old_update_positions(self, dt): + # Based on velocity diagram bouncing...didn't work for + # large masses, due to frame rate mismatch blocks = self.submobjects for block in blocks: block.shift(block.velocity * dt * RIGHT) @@ -74,13 +115,45 @@ class SlidingBlocks(VGroup): # Second block hits wall blocks[1].velocity *= -1 blocks[1].move_to(self.wall.get_corner(DR), DL) + if blocks[0].get_left()[0] < blocks[1].get_right()[0]: + blocks[0].move_to(blocks[1].get_corner(DR), DL) self.surrounding_scene.clack(blocks[1].get_left()) return self + def update_blocks_from_phase_space_point_tracker(self): + block1, block2 = self.block1, self.block2 + + ps_point = self.phase_space_point_tracker.get_location() + theta = np.arctan(np.sqrt(block2.mass / block1.mass)) + ps_point_angle = angle_of_vector(ps_point) + n_clacks = int(ps_point_angle / theta) + # TODO, pass on n_clack information to surrounding_scene + reflected_point = rotate_vector( + ps_point, + -2 * np.ceil(n_clacks / 2) * theta + ) + reflected_point = np.abs(reflected_point) + + shadow_wall_x = self.wall.get_right()[0] + block2.get_width() + floor_y = self.floor.get_top()[1] + s1 = reflected_point[0] / np.sqrt(block1.mass) + s2 = reflected_point[1] / np.sqrt(block2.mass) + block1.move_to( + (shadow_wall_x + s1) * RIGHT + + floor_y * UP, + DL, + ) + block2.move_to( + (shadow_wall_x + s2) * RIGHT + + floor_y * UP, + DR, + ) + class BlocksAndWallScene(Scene): CONFIG = { - "print_clack_times": True, + "print_clack_times": False, + "count_clacks": True, "sliding_blocks_config": {}, "floor_y": -2, "wall_x": -5, @@ -89,14 +162,36 @@ class BlocksAndWallScene(Scene): def setup(self): self.floor = self.get_floor() self.wall = self.get_wall() - self.blocks = SlidingBlocks(self) + self.blocks = SlidingBlocks(self, **self.sliding_blocks_config) + self.add(self.floor, self.wall, self.blocks) + if self.count_clacks: + self.add_clack_counter() + + self.track_times() + + def track_times(self): self.clack_times = [] time_tracker = ValueTracker() time_tracker.add_updater(lambda m, dt: m.increment_value(dt)) self.add(time_tracker) self.get_time = time_tracker.get_value - self.add(self.floor, self.wall, self.blocks) + def add_clack_counter(self): + clack_counter_label = TextMobject("\\# Clacks: ") + clack_counter = Integer(0) + clack_counter.next_to( + clack_counter_label[-1], RIGHT, + aligned_edge=DOWN, + ) + clack_group = VGroup( + clack_counter_label, + clack_counter, + ) + clack_group.to_corner(UR) + clack_group.shift(LEFT) + self.add(clack_group) + + self.clack_counter = clack_counter def tear_down(self): if self.print_clack_times: @@ -120,11 +215,10 @@ class BlocksAndWallScene(Scene): floor.shift(self.floor_y * UP) return floor - def get_blocks(self): - pass - def clack(self, location): self.clack_times.append(self.get_time()) + if self.count_clacks: + self.clack_counter.increment_value() # Animted scenes @@ -135,5 +229,15 @@ class MathAndPhysicsConspiring(Scene): class ThreeSimpleClacks(BlocksAndWallScene): + CONFIG = { + "sliding_blocks_config": { + "block1_config": { + "mass": 100, + "width": 1.5, + } + } + } + def construct(self): - self.wait(10) + while self.blocks[0].get_left()[0] < FRAME_WIDTH / 2: + self.wait(1)