mirror of
https://github.com/3b1b/manim.git
synced 2025-07-30 05:24:22 +08:00
920
active_projects/hyperdarts.py
Normal file
920
active_projects/hyperdarts.py
Normal file
@ -0,0 +1,920 @@
|
|||||||
|
from manimlib.imports import *
|
||||||
|
|
||||||
|
|
||||||
|
OUTPUT_DIRECTORY = "hyperdarts"
|
||||||
|
|
||||||
|
|
||||||
|
class HyperdartScene(Scene):
|
||||||
|
CONFIG = {
|
||||||
|
"square_width": 6,
|
||||||
|
"square_style": {
|
||||||
|
"stroke_width": 2,
|
||||||
|
"fill_color": BLUE,
|
||||||
|
"fill_opacity": 0.5,
|
||||||
|
},
|
||||||
|
"circle_style": {
|
||||||
|
"fill_color": RED_E,
|
||||||
|
"fill_opacity": 1,
|
||||||
|
"stroke_width": 0,
|
||||||
|
},
|
||||||
|
"circle_center_dot_radius": 0.025,
|
||||||
|
"default_line_style": {
|
||||||
|
"stroke_width": 2,
|
||||||
|
"stroke_color": WHITE,
|
||||||
|
},
|
||||||
|
"default_dot_config": {
|
||||||
|
"fill_color": WHITE,
|
||||||
|
"background_stroke_width": 1,
|
||||||
|
"background_stroke_color": BLACK,
|
||||||
|
"radius": 0.5 * DEFAULT_DOT_RADIUS,
|
||||||
|
},
|
||||||
|
"dart_sound": "dart_low",
|
||||||
|
"default_bullseye_shadow_opacity": 0.35,
|
||||||
|
}
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
self.square = self.get_square()
|
||||||
|
self.circle = self.get_circle()
|
||||||
|
self.circle_center_dot = self.get_circle_center_dot()
|
||||||
|
|
||||||
|
self.add(self.square)
|
||||||
|
self.add(self.circle)
|
||||||
|
self.add(self.circle_center_dot)
|
||||||
|
|
||||||
|
def get_square(self):
|
||||||
|
return Square(
|
||||||
|
side_length=self.square_width,
|
||||||
|
**self.square_style
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_circle(self, square=None):
|
||||||
|
square = square or self.square
|
||||||
|
circle = Circle(**self.circle_style)
|
||||||
|
circle.replace(square)
|
||||||
|
return circle
|
||||||
|
|
||||||
|
def get_circle_center_dot(self, circle=None):
|
||||||
|
circle = circle or self.circle
|
||||||
|
return Dot(
|
||||||
|
circle.get_center(),
|
||||||
|
radius=self.circle_center_dot_radius,
|
||||||
|
fill_color=BLACK,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_number_plane(self):
|
||||||
|
square = self.square
|
||||||
|
unit_size = square.get_width() / 2
|
||||||
|
plane = NumberPlane(
|
||||||
|
axis_config={
|
||||||
|
"unit_size": unit_size,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
plane.add_coordinates()
|
||||||
|
plane.shift(square.get_center() - plane.c2p(0, 0))
|
||||||
|
return plane
|
||||||
|
|
||||||
|
def get_random_points(self, n):
|
||||||
|
square = self.square
|
||||||
|
points = np.random.uniform(-1, 1, 3 * n).reshape((n, 3))
|
||||||
|
|
||||||
|
points[:, 0] *= square.get_width() / 2
|
||||||
|
points[:, 1] *= square.get_height() / 2
|
||||||
|
points[:, 2] = 0
|
||||||
|
points += square.get_center()
|
||||||
|
return points
|
||||||
|
|
||||||
|
def get_random_point(self):
|
||||||
|
return self.get_random_points(1)[0]
|
||||||
|
|
||||||
|
def get_dot(self, point):
|
||||||
|
return Dot(point, **self.default_dot_config)
|
||||||
|
|
||||||
|
# Hit transform rules
|
||||||
|
def is_inside(self, point, circle=None):
|
||||||
|
circle = circle or self.circle
|
||||||
|
return get_norm(point - circle.get_center()) <= circle.get_width() / 2
|
||||||
|
|
||||||
|
def get_new_radius(self, point, circle=None):
|
||||||
|
circle = circle or self.circle
|
||||||
|
center = circle.get_center()
|
||||||
|
radius = circle.get_width() / 2
|
||||||
|
p_dist = get_norm(point - center)
|
||||||
|
return np.sqrt(radius**2 - p_dist**2)
|
||||||
|
|
||||||
|
def get_hit_distance_line(self, point, circle=None):
|
||||||
|
circle = circle or self.circle
|
||||||
|
|
||||||
|
line = Line(
|
||||||
|
circle.get_center(), point,
|
||||||
|
**self.default_line_style
|
||||||
|
)
|
||||||
|
return line
|
||||||
|
|
||||||
|
def get_chord(self, point, circle=None):
|
||||||
|
circle = circle or self.circle
|
||||||
|
|
||||||
|
center = circle.get_center()
|
||||||
|
p_angle = angle_of_vector(point - center)
|
||||||
|
chord = Line(DOWN, UP)
|
||||||
|
new_radius = self.get_new_radius(point, circle)
|
||||||
|
chord.scale(new_radius)
|
||||||
|
chord.rotate(p_angle)
|
||||||
|
chord.move_to(point)
|
||||||
|
chord.set_style(**self.default_line_style)
|
||||||
|
return chord
|
||||||
|
|
||||||
|
def get_radii_to_chord(self, chord, circle=None):
|
||||||
|
circle = circle or self.circle
|
||||||
|
|
||||||
|
center = circle.get_center()
|
||||||
|
radii = VGroup(*[
|
||||||
|
DashedLine(center, point)
|
||||||
|
for point in chord.get_start_and_end()
|
||||||
|
])
|
||||||
|
radii.set_style(**self.default_line_style)
|
||||||
|
return radii
|
||||||
|
|
||||||
|
def get_all_hit_lines(self, point, circle=None):
|
||||||
|
h_line = self.get_hit_distance_line(point, circle)
|
||||||
|
chord = self.get_chord(point, circle)
|
||||||
|
radii = self.get_radii_to_chord(chord, circle)
|
||||||
|
return VGroup(h_line, chord, radii)
|
||||||
|
|
||||||
|
def get_dart(self, length=1.5):
|
||||||
|
dart = SVGMobject(file_name="dart")
|
||||||
|
dart.rotate(135 * DEGREES)
|
||||||
|
dart.set_width(length)
|
||||||
|
dart.rotate(45 * DEGREES, UP)
|
||||||
|
dart.rotate(-10 * DEGREES)
|
||||||
|
|
||||||
|
dart.set_fill(GREY)
|
||||||
|
dart.set_sheen(2, UL)
|
||||||
|
dart.set_stroke(BLACK, 0.5, background=True)
|
||||||
|
return dart
|
||||||
|
|
||||||
|
# New circle
|
||||||
|
def get_new_circle_from_point(self, point, circle=None):
|
||||||
|
return self.get_new_circle(
|
||||||
|
self.get_new_radius(point, circle),
|
||||||
|
circle,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_new_circle_from_chord(self, chord, circle=None):
|
||||||
|
return self.get_new_circle(
|
||||||
|
chord.get_length() / 2,
|
||||||
|
circle,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_new_circle(self, new_radius, circle=None):
|
||||||
|
circle = circle or self.circle
|
||||||
|
new_circle = self.get_circle()
|
||||||
|
new_circle.set_width(2 * new_radius)
|
||||||
|
new_circle.move_to(circle)
|
||||||
|
return new_circle
|
||||||
|
|
||||||
|
# Sound
|
||||||
|
def add_dart_sound(self, time_offset=0, gain=-20, **kwargs):
|
||||||
|
self.add_sound(
|
||||||
|
self.dart_sound,
|
||||||
|
time_offset=time_offset,
|
||||||
|
gain=-20,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Animations
|
||||||
|
def show_full_hit_process(self, point, pace="slow", with_dart=True):
|
||||||
|
assert(pace in ["slow", "fast"])
|
||||||
|
|
||||||
|
to_fade = VGroup()
|
||||||
|
|
||||||
|
if with_dart:
|
||||||
|
dart, dot = self.show_hit_with_dart(point)
|
||||||
|
to_fade.add(dart, dot)
|
||||||
|
else:
|
||||||
|
dot = self.show_hit(point)
|
||||||
|
to_fade.add(dot)
|
||||||
|
|
||||||
|
if pace == "slow":
|
||||||
|
self.wait(0.5)
|
||||||
|
|
||||||
|
# TODO, automatically act based on hit or miss?
|
||||||
|
|
||||||
|
lines = self.show_geometry(point, pace)
|
||||||
|
chord_and_shadow = self.show_circle_shrink(lines[1], pace=pace)
|
||||||
|
|
||||||
|
to_fade.add_to_back(chord_and_shadow, lines)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
FadeOut(to_fade),
|
||||||
|
run_time=(1 if pace == "slow" else 0.5)
|
||||||
|
)
|
||||||
|
|
||||||
|
def show_hits_with_darts(self, points, run_time=0.5, added_anims=None):
|
||||||
|
if added_anims is None:
|
||||||
|
added_anims = []
|
||||||
|
|
||||||
|
darts = VGroup(*[
|
||||||
|
self.get_dart().move_to(point, DR)
|
||||||
|
for point in points
|
||||||
|
])
|
||||||
|
dots = VGroup(*[
|
||||||
|
self.get_dot(point)
|
||||||
|
for point in points
|
||||||
|
])
|
||||||
|
|
||||||
|
for dart in darts:
|
||||||
|
dart.save_state()
|
||||||
|
dart.set_x(-(FRAME_WIDTH + dart.get_width()) / 2)
|
||||||
|
dart.rotate(20 * DEGREES)
|
||||||
|
|
||||||
|
n_points = len(points)
|
||||||
|
self.play(
|
||||||
|
ShowIncreasingSubsets(
|
||||||
|
dots,
|
||||||
|
rate_func=squish_rate_func(linear, 0.5, 1),
|
||||||
|
),
|
||||||
|
LaggedStart(*[
|
||||||
|
Restore(
|
||||||
|
dart,
|
||||||
|
path_arc=-20 * DEGREES,
|
||||||
|
rate_func=linear,
|
||||||
|
run_time=run_time,
|
||||||
|
)
|
||||||
|
for dart in darts
|
||||||
|
], lag_ratio=(1 / n_points)),
|
||||||
|
*added_anims,
|
||||||
|
run_time=run_time
|
||||||
|
)
|
||||||
|
for n in range(n_points):
|
||||||
|
self.add_dart_sound(
|
||||||
|
time_offset=(-n / (2 * n_points))
|
||||||
|
)
|
||||||
|
|
||||||
|
return darts, dots
|
||||||
|
|
||||||
|
def show_hit_with_dart(self, point, run_time=0.25, **kwargs):
|
||||||
|
darts, dots = self.show_hits_with_darts([point], run_time, **kwargs)
|
||||||
|
return darts[0], dots[0]
|
||||||
|
|
||||||
|
def show_hit(self, point, pace="slow", added_anims=None):
|
||||||
|
assert(pace in ["slow", "fast"])
|
||||||
|
if added_anims is None:
|
||||||
|
added_anims = []
|
||||||
|
|
||||||
|
dot = self.get_dot(point)
|
||||||
|
if pace == "slow":
|
||||||
|
self.play(
|
||||||
|
FadeInFromLarge(dot, rate_func=rush_into),
|
||||||
|
*added_anims,
|
||||||
|
run_time=0.5,
|
||||||
|
)
|
||||||
|
elif pace == "fast":
|
||||||
|
self.add(dot)
|
||||||
|
# self.add_dart_sound()
|
||||||
|
return dot
|
||||||
|
|
||||||
|
def show_geometry(self, point, pace="slow"):
|
||||||
|
assert(pace in ["slow", "fast"])
|
||||||
|
|
||||||
|
lines = self.get_all_hit_lines(point, self.circle)
|
||||||
|
h_line, chord, radii = lines
|
||||||
|
|
||||||
|
if pace == "slow":
|
||||||
|
self.play(
|
||||||
|
ShowCreation(h_line),
|
||||||
|
GrowFromCenter(chord),
|
||||||
|
)
|
||||||
|
self.play(*map(ShowCreation, radii))
|
||||||
|
elif pace == "fast":
|
||||||
|
self.play(
|
||||||
|
ShowCreation(h_line),
|
||||||
|
GrowFromCenter(chord),
|
||||||
|
*map(ShowCreation, radii),
|
||||||
|
run_time=0.5
|
||||||
|
)
|
||||||
|
return lines
|
||||||
|
|
||||||
|
def show_circle_shrink(self, chord, pace="slow", shadow_opacity=None):
|
||||||
|
circle = self.circle
|
||||||
|
chord_copy = chord.copy()
|
||||||
|
new_circle = self.get_new_circle_from_chord(chord)
|
||||||
|
to_fade = VGroup(chord_copy)
|
||||||
|
|
||||||
|
if shadow_opacity is None:
|
||||||
|
shadow_opacity = self.default_bullseye_shadow_opacity
|
||||||
|
if shadow_opacity > 0:
|
||||||
|
shadow = circle.copy()
|
||||||
|
shadow.set_opacity(shadow_opacity)
|
||||||
|
to_fade.add_to_back(shadow)
|
||||||
|
if circle in self.mobjects:
|
||||||
|
index = self.mobjects.index(circle)
|
||||||
|
self.mobjects.insert(index, shadow)
|
||||||
|
else:
|
||||||
|
self.add(shadow, self.circle_center_dot)
|
||||||
|
|
||||||
|
outline = VGroup(*[
|
||||||
|
VMobject().pointwise_become_partial(new_circle, a, b)
|
||||||
|
for (a, b) in [(0, 0.5), (0.5, 1)]
|
||||||
|
])
|
||||||
|
outline.rotate(chord.get_angle())
|
||||||
|
outline.set_fill(opacity=0)
|
||||||
|
outline.set_stroke(YELLOW, 2)
|
||||||
|
|
||||||
|
assert(pace in ["slow", "fast"])
|
||||||
|
|
||||||
|
if pace == "slow":
|
||||||
|
self.play(
|
||||||
|
chord_copy.move_to, circle.get_center(),
|
||||||
|
circle.set_opacity, 0.5,
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
Rotating(
|
||||||
|
chord_copy,
|
||||||
|
radians=PI,
|
||||||
|
),
|
||||||
|
ShowCreation(
|
||||||
|
outline,
|
||||||
|
lag_ratio=0
|
||||||
|
),
|
||||||
|
run_time=1,
|
||||||
|
rate_func=smooth,
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
Transform(circle, new_circle),
|
||||||
|
FadeOut(outline),
|
||||||
|
)
|
||||||
|
elif pace == "fast":
|
||||||
|
outline = new_circle.copy()
|
||||||
|
outline.set_fill(opacity=0)
|
||||||
|
outline.set_stroke(YELLOW, 2)
|
||||||
|
outline.move_to(chord)
|
||||||
|
outline.generate_target()
|
||||||
|
outline.target.move_to(circle)
|
||||||
|
self.play(
|
||||||
|
chord_copy.move_to, circle,
|
||||||
|
Transform(circle, new_circle),
|
||||||
|
# MoveToTarget(
|
||||||
|
# outline,
|
||||||
|
# remover=True
|
||||||
|
# )
|
||||||
|
)
|
||||||
|
# circle.become(new_circle)
|
||||||
|
# circle.become(new_circle)
|
||||||
|
# self.remove(new_circle)
|
||||||
|
return to_fade
|
||||||
|
|
||||||
|
def show_miss(self, point, with_dart=True):
|
||||||
|
square = self.square
|
||||||
|
miss = TextMobject("Miss!")
|
||||||
|
miss.next_to(point, UP)
|
||||||
|
to_fade = VGroup(miss)
|
||||||
|
|
||||||
|
if with_dart:
|
||||||
|
dart, dot = self.show_hit_with_dart(point)
|
||||||
|
to_fade.add(dart, dot)
|
||||||
|
else:
|
||||||
|
dot = self.show_hit(point)
|
||||||
|
to_fade.add(dot)
|
||||||
|
self.play(
|
||||||
|
ApplyMethod(
|
||||||
|
square.set_color, YELLOW,
|
||||||
|
rate_func=lambda t: (1 - t),
|
||||||
|
),
|
||||||
|
GrowFromCenter(miss),
|
||||||
|
run_time=0.25
|
||||||
|
)
|
||||||
|
return to_fade
|
||||||
|
|
||||||
|
def show_game_over(self):
|
||||||
|
game_over = TextMobject("GAME OVER")
|
||||||
|
game_over.set_width(FRAME_WIDTH - 1)
|
||||||
|
rect = FullScreenFadeRectangle(opacity=0.25)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
FadeIn(rect),
|
||||||
|
FadeInFromLarge(game_over),
|
||||||
|
)
|
||||||
|
return VGroup(rect, game_over)
|
||||||
|
|
||||||
|
|
||||||
|
class Dartboard(VGroup):
|
||||||
|
CONFIG = {
|
||||||
|
"radius": 3,
|
||||||
|
"n_sectors": 20,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
n_sectors = self.n_sectors
|
||||||
|
angle = TAU / n_sectors
|
||||||
|
|
||||||
|
segments = VGroup(*[
|
||||||
|
VGroup(*[
|
||||||
|
AnnularSector(
|
||||||
|
inner_radius=in_r,
|
||||||
|
outer_radius=out_r,
|
||||||
|
start_angle=n * angle,
|
||||||
|
angle=angle,
|
||||||
|
color=color,
|
||||||
|
)
|
||||||
|
for n, color in zip(
|
||||||
|
range(n_sectors),
|
||||||
|
it.cycle(colors)
|
||||||
|
)
|
||||||
|
])
|
||||||
|
for colors, in_r, out_r in [
|
||||||
|
([LIGHT_GREY, DARKER_GREY], 0, 1),
|
||||||
|
([GREEN_E, RED_E], 0.5, 0.55),
|
||||||
|
([GREEN_E, RED_E], 0.95, 1),
|
||||||
|
]
|
||||||
|
])
|
||||||
|
segments.rotate(-angle / 2)
|
||||||
|
bullseyes = VGroup(*[
|
||||||
|
Circle(radius=r)
|
||||||
|
for r in [0.07, 0.035]
|
||||||
|
])
|
||||||
|
bullseyes.set_fill(opacity=1)
|
||||||
|
bullseyes.set_stroke(width=0)
|
||||||
|
bullseyes[0].set_color(GREEN_E)
|
||||||
|
bullseyes[1].set_color(RED_E)
|
||||||
|
|
||||||
|
self.add(*segments, *bullseyes)
|
||||||
|
self.scale(self.radius)
|
||||||
|
|
||||||
|
|
||||||
|
# Problem statement
|
||||||
|
|
||||||
|
class IntroduceGame(HyperdartScene):
|
||||||
|
CONFIG = {
|
||||||
|
"random_seed": 0,
|
||||||
|
"square_width": 5,
|
||||||
|
"num_darts_in_initial_flurry": 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
self.show_flurry_of_points()
|
||||||
|
self.show_board_dimensions()
|
||||||
|
self.introduce_bullseye()
|
||||||
|
self.show_miss_example()
|
||||||
|
self.show_shrink_rule()
|
||||||
|
|
||||||
|
def show_flurry_of_points(self):
|
||||||
|
square = self.square
|
||||||
|
circle = self.circle
|
||||||
|
|
||||||
|
title = TextMobject("Hyperdarts")
|
||||||
|
title.scale(1.5)
|
||||||
|
title.to_edge(UP)
|
||||||
|
|
||||||
|
n = self.num_darts_in_initial_flurry
|
||||||
|
points = np.random.normal(size=n * 3).reshape((n, 3))
|
||||||
|
points[:, 2] = 0
|
||||||
|
points *= 0.75
|
||||||
|
|
||||||
|
board = Dartboard()
|
||||||
|
board.match_width(square)
|
||||||
|
board.move_to(square)
|
||||||
|
|
||||||
|
pre_square = Circle(color=WHITE)
|
||||||
|
pre_square.replace(square)
|
||||||
|
|
||||||
|
self.remove(circle, square)
|
||||||
|
self.add(board)
|
||||||
|
|
||||||
|
darts, dots = self.show_hits_with_darts(
|
||||||
|
points,
|
||||||
|
added_anims=[FadeInFromDown(title)]
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
def func(p):
|
||||||
|
theta = angle_of_vector(p) % (TAU / 4)
|
||||||
|
if theta > TAU / 8:
|
||||||
|
theta = TAU / 4 - theta
|
||||||
|
p *= 1 / np.cos(theta)
|
||||||
|
return p
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
*[
|
||||||
|
ApplyPointwiseFunction(func, pieces, run_time=1)
|
||||||
|
for pieces in [*board[:3], *dots]
|
||||||
|
],
|
||||||
|
*[
|
||||||
|
MaintainPositionRelativeTo(dart, dot)
|
||||||
|
for dart, dot in zip(darts, dots)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.flurry_dots = dots
|
||||||
|
self.darts = darts
|
||||||
|
self.title = title
|
||||||
|
self.board = board
|
||||||
|
|
||||||
|
def show_board_dimensions(self):
|
||||||
|
square = self.square
|
||||||
|
|
||||||
|
labels = VGroup(*[
|
||||||
|
TextMobject("2 ft").next_to(
|
||||||
|
square.get_edge_center(vect), vect,
|
||||||
|
)
|
||||||
|
for vect in [DOWN, RIGHT]
|
||||||
|
])
|
||||||
|
labels.set_color(YELLOW)
|
||||||
|
|
||||||
|
h_line, v_line = lines = VGroup(*[
|
||||||
|
DashedLine(
|
||||||
|
square.get_edge_center(v1),
|
||||||
|
square.get_edge_center(-v1),
|
||||||
|
).next_to(label, v2)
|
||||||
|
for label, v1, v2 in zip(labels, [LEFT, UP], [UP, LEFT])
|
||||||
|
])
|
||||||
|
lines.match_color(labels)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
LaggedStartMap(ShowCreation, lines),
|
||||||
|
LaggedStartMap(FadeInFromDown, labels),
|
||||||
|
lag_ratio=0.5
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
self.square_dimensions = VGroup(lines, labels)
|
||||||
|
|
||||||
|
def introduce_bullseye(self):
|
||||||
|
square = self.square
|
||||||
|
circle = self.circle
|
||||||
|
board = self.board
|
||||||
|
circle.save_state()
|
||||||
|
circle.replace(board[-1])
|
||||||
|
|
||||||
|
label = TextMobject("Bullseye")
|
||||||
|
label.scale(1.5)
|
||||||
|
label.next_to(square, LEFT, aligned_edge=UP)
|
||||||
|
label.set_color(RED)
|
||||||
|
arrow = Arrow(
|
||||||
|
label.get_bottom(),
|
||||||
|
circle.get_corner(DR)
|
||||||
|
)
|
||||||
|
|
||||||
|
radius = DashedLine(
|
||||||
|
square.get_center(),
|
||||||
|
square.get_left(),
|
||||||
|
stroke_width=2,
|
||||||
|
)
|
||||||
|
radius_label = TextMobject("1 ft")
|
||||||
|
radius_label.next_to(radius, DOWN, SMALL_BUFF)
|
||||||
|
|
||||||
|
self.add(circle, self.square_dimensions)
|
||||||
|
self.play(
|
||||||
|
FadeInFromLarge(circle),
|
||||||
|
FadeInFromDown(label),
|
||||||
|
ShowCreation(arrow),
|
||||||
|
LaggedStartMap(FadeOut, self.flurry_dots, run_time=1),
|
||||||
|
LaggedStartMap(FadeOut, self.darts, run_time=1),
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.add(square, board, arrow, circle)
|
||||||
|
self.play(
|
||||||
|
Restore(circle),
|
||||||
|
ApplyMethod(
|
||||||
|
arrow.scale, 0.4,
|
||||||
|
{"about_point": arrow.get_start()}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
self.add(radius, self.circle_center_dot)
|
||||||
|
self.play(
|
||||||
|
ShowCreation(radius),
|
||||||
|
FadeInFrom(radius_label, RIGHT),
|
||||||
|
FadeIn(self.circle_center_dot),
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
FadeOut(label),
|
||||||
|
Uncreate(arrow),
|
||||||
|
FadeOut(board)
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
s_lines, s_labels = self.square_dimensions
|
||||||
|
self.play(
|
||||||
|
FadeOut(s_lines),
|
||||||
|
FadeOut(radius),
|
||||||
|
FadeOut(radius_label),
|
||||||
|
FadeOut(self.title),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.circle_dimensions = VGroup(
|
||||||
|
radius, radius_label,
|
||||||
|
)
|
||||||
|
|
||||||
|
def show_miss_example(self):
|
||||||
|
square = self.square
|
||||||
|
point = square.get_corner(UL) + 0.5 * DR
|
||||||
|
|
||||||
|
miss_word = TextMobject("Miss!")
|
||||||
|
miss_word.scale(1.5)
|
||||||
|
miss_word.next_to(point, UP, LARGE_BUFF)
|
||||||
|
|
||||||
|
dart, dot = self.show_hit_with_dart(point)
|
||||||
|
self.play(FadeInFromDown(miss_word))
|
||||||
|
self.wait()
|
||||||
|
game_over = self.show_game_over()
|
||||||
|
self.wait()
|
||||||
|
self.play(
|
||||||
|
*map(FadeOut, [dart, dot, miss_word, game_over])
|
||||||
|
)
|
||||||
|
|
||||||
|
def show_shrink_rule(self):
|
||||||
|
circle = self.circle
|
||||||
|
point = 0.5 * circle.point_from_proportion(0.2)
|
||||||
|
|
||||||
|
# First example
|
||||||
|
self.show_full_hit_process(point)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# Close to border
|
||||||
|
label = TextMobject("Bad shot $\\Rightarrow$ much shrinkage")
|
||||||
|
label.scale(1.5)
|
||||||
|
label.to_edge(UP)
|
||||||
|
|
||||||
|
point = 0.98 * circle.point_from_proportion(3 / 8)
|
||||||
|
circle.save_state()
|
||||||
|
self.play(FadeInFromDown(label))
|
||||||
|
self.show_full_hit_process(point)
|
||||||
|
self.wait()
|
||||||
|
self.play(Restore(circle))
|
||||||
|
|
||||||
|
# Close to center
|
||||||
|
new_label = TextMobject("Good shot $\\Rightarrow$ less shrinkage")
|
||||||
|
new_label.scale(1.5)
|
||||||
|
new_label.to_edge(UP)
|
||||||
|
point = 0.2 * circle.point_from_proportion(3 / 8)
|
||||||
|
self.play(
|
||||||
|
FadeInFromDown(new_label),
|
||||||
|
FadeOutAndShift(label, UP),
|
||||||
|
)
|
||||||
|
self.show_full_hit_process(point)
|
||||||
|
self.wait()
|
||||||
|
self.play(FadeOut(new_label))
|
||||||
|
|
||||||
|
# Play on
|
||||||
|
for x in range(3):
|
||||||
|
r1, r2 = np.random.random(size=2)
|
||||||
|
point = r1 * circle.point_from_proportion(r2)
|
||||||
|
self.show_full_hit_process(point)
|
||||||
|
point = circle.get_right() + 0.5 * UR
|
||||||
|
self.show_miss(point)
|
||||||
|
self.wait()
|
||||||
|
self.show_game_over()
|
||||||
|
|
||||||
|
|
||||||
|
class ShowScoring(HyperdartScene):
|
||||||
|
def setup(self):
|
||||||
|
super().setup()
|
||||||
|
self.add_score_counter()
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
self.comment_on_score()
|
||||||
|
self.show_several_hits()
|
||||||
|
|
||||||
|
def comment_on_score(self):
|
||||||
|
score_label = self.score_label
|
||||||
|
comment = TextMobject("\\# Bullseyes")
|
||||||
|
# rect = SurroundingRectangle(comment)
|
||||||
|
# rect.set_stroke(width=1)
|
||||||
|
# comment.add(rect)
|
||||||
|
comment.set_color(YELLOW)
|
||||||
|
comment.next_to(score_label, DOWN, LARGE_BUFF)
|
||||||
|
comment.set_x(midpoint(
|
||||||
|
self.square.get_left(),
|
||||||
|
LEFT_SIDE,
|
||||||
|
)[0])
|
||||||
|
arrow = Arrow(
|
||||||
|
comment.get_top(),
|
||||||
|
score_label[1].get_bottom(),
|
||||||
|
buff=0.2,
|
||||||
|
)
|
||||||
|
arrow.match_color(comment)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
FadeInFromDown(comment),
|
||||||
|
GrowArrow(arrow),
|
||||||
|
)
|
||||||
|
|
||||||
|
def show_several_hits(self):
|
||||||
|
points = [UR, DL, 0.5 * UL, 0.5 * DR]
|
||||||
|
for point in points:
|
||||||
|
self.show_full_hit_process(point, pace="fast")
|
||||||
|
self.show_miss(2 * UR)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
#
|
||||||
|
def add_score_counter(self):
|
||||||
|
score = Integer(0)
|
||||||
|
score_label = VGroup(
|
||||||
|
TextMobject("Score: "),
|
||||||
|
score
|
||||||
|
)
|
||||||
|
score_label.arrange(RIGHT, aligned_edge=DOWN)
|
||||||
|
score_label.scale(1.5)
|
||||||
|
score_label.to_corner(UL)
|
||||||
|
|
||||||
|
self.add(score_label)
|
||||||
|
|
||||||
|
self.score = score
|
||||||
|
self.score_label = score_label
|
||||||
|
|
||||||
|
def increment_score(self):
|
||||||
|
score = self.score
|
||||||
|
new_score = score.copy()
|
||||||
|
new_score.increment_value(1)
|
||||||
|
self.play(
|
||||||
|
FadeOutAndShift(score, UP),
|
||||||
|
FadeInFrom(new_score, DOWN),
|
||||||
|
run_time=1,
|
||||||
|
)
|
||||||
|
self.remove(new_score)
|
||||||
|
score.increment_value()
|
||||||
|
score.move_to(new_score)
|
||||||
|
self.add(score)
|
||||||
|
|
||||||
|
def show_hit(self, point, *args, **kwargs):
|
||||||
|
result = super().show_hit(point, *args, **kwargs)
|
||||||
|
if self.is_inside(point):
|
||||||
|
self.increment_score()
|
||||||
|
return result
|
||||||
|
|
||||||
|
def show_hit_with_dart(self, point, *args, **kwargs):
|
||||||
|
result = super().show_hit_with_dart(point, *args, **kwargs)
|
||||||
|
if self.is_inside(point):
|
||||||
|
self.increment_score()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class ShowSeveralRounds(ShowScoring):
|
||||||
|
CONFIG = {
|
||||||
|
"n_rounds": 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
for x in range(self.n_rounds):
|
||||||
|
self.show_single_round()
|
||||||
|
self.reset_board()
|
||||||
|
|
||||||
|
def show_single_round(self, pace="fast"):
|
||||||
|
while True:
|
||||||
|
point = self.get_random_point()
|
||||||
|
if self.is_inside(point):
|
||||||
|
self.show_full_hit_process(point, pace=pace)
|
||||||
|
else:
|
||||||
|
to_fade = self.show_miss(point)
|
||||||
|
self.wait(0.5)
|
||||||
|
self.play(
|
||||||
|
ShowCreationThenFadeAround(self.score_label),
|
||||||
|
FadeOut(to_fade)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
def reset_board(self):
|
||||||
|
score = self.score
|
||||||
|
new_score = score.copy()
|
||||||
|
new_score.set_value(0)
|
||||||
|
self.play(
|
||||||
|
self.circle.match_width, self.square,
|
||||||
|
FadeOutAndShift(score, UP),
|
||||||
|
FadeInFrom(new_score, DOWN),
|
||||||
|
)
|
||||||
|
score.set_value(0)
|
||||||
|
self.add(score)
|
||||||
|
self.remove(new_score)
|
||||||
|
|
||||||
|
|
||||||
|
class ShowSeveralRoundsQuickly(ShowSeveralRounds):
|
||||||
|
CONFIG = {
|
||||||
|
"n_rounds": 15,
|
||||||
|
}
|
||||||
|
|
||||||
|
def show_full_hit_process(self, point, *args, **kwargs):
|
||||||
|
lines = self.get_all_hit_lines(point)
|
||||||
|
|
||||||
|
dart = self.show_hit_with_dart(point)
|
||||||
|
self.add(lines)
|
||||||
|
self.score.increment_value(1)
|
||||||
|
to_fade = self.show_circle_shrink(lines[1], pace="fast")
|
||||||
|
to_fade.add(*lines, *dart)
|
||||||
|
self.play(FadeOut(to_fade), run_time=0.5)
|
||||||
|
|
||||||
|
def increment_score(self):
|
||||||
|
pass # Handled elsewhere
|
||||||
|
|
||||||
|
|
||||||
|
class ShowSeveralRoundsVeryQuickly(ShowSeveralRoundsQuickly):
|
||||||
|
def construct(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ShowUniformDistribution(HyperdartScene):
|
||||||
|
CONFIG = {
|
||||||
|
"dart_sound": "dart_high",
|
||||||
|
"n_points": 1000,
|
||||||
|
}
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
self.add_title()
|
||||||
|
self.show_random_points()
|
||||||
|
self.exchange_titles()
|
||||||
|
self.show_random_points()
|
||||||
|
|
||||||
|
def get_square(self):
|
||||||
|
return super().get_square().to_edge(DOWN)
|
||||||
|
|
||||||
|
def add_title(self):
|
||||||
|
# square = self.square
|
||||||
|
title = TextMobject("All points in the square are equally likely")
|
||||||
|
title.scale(1.5)
|
||||||
|
title.to_edge(UP)
|
||||||
|
|
||||||
|
new_title = TextMobject("``Uniform distribution'' on the square")
|
||||||
|
new_title.scale(1.5)
|
||||||
|
new_title.to_edge(UP)
|
||||||
|
|
||||||
|
self.play(FadeInFromDown(title))
|
||||||
|
|
||||||
|
self.title = title
|
||||||
|
self.new_title = new_title
|
||||||
|
|
||||||
|
def show_random_points(self):
|
||||||
|
points = self.get_random_points(self.n_points)
|
||||||
|
dots = VGroup(*[
|
||||||
|
Dot(point, radius=0.02)
|
||||||
|
for point in points
|
||||||
|
])
|
||||||
|
dots.set_fill(opacity=0.75)
|
||||||
|
|
||||||
|
run_time = 5
|
||||||
|
self.play(LaggedStartMap(
|
||||||
|
FadeInFromLarge, dots,
|
||||||
|
run_time=run_time,
|
||||||
|
))
|
||||||
|
for x in range(1000):
|
||||||
|
self.add_dart_sound(
|
||||||
|
time_offset=-run_time * np.random.random(),
|
||||||
|
gain=-10,
|
||||||
|
gain_to_background=-5,
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
def exchange_titles(self):
|
||||||
|
self.play(
|
||||||
|
FadeInFromDown(self.new_title),
|
||||||
|
FadeOutAndShift(self.title, UP),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ExpectedScoreEqualsQMark(Scene):
|
||||||
|
def construct(self):
|
||||||
|
equation = TextMobject(
|
||||||
|
"\\textbf{E}[Score] = ???",
|
||||||
|
tex_to_color_map={
|
||||||
|
"???": YELLOW,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
aka = TextMobject("a.k.a. Long-term average")
|
||||||
|
aka.next_to(equation, DOWN)
|
||||||
|
|
||||||
|
self.play(Write(equation))
|
||||||
|
self.wait(2)
|
||||||
|
self.play(FadeInFrom(aka, UP))
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
|
||||||
|
class ScoreHistogram(Scene):
|
||||||
|
CONFIG = {
|
||||||
|
"axes_config": {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
self.add_axes()
|
||||||
|
self.add_score_label()
|
||||||
|
self.setup_histogram()
|
||||||
|
self.show_many_runs()
|
||||||
|
|
||||||
|
def add_axes(self):
|
||||||
|
axes = Axes(**self.axes_config)
|
||||||
|
|
||||||
|
def add_score_label(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setup_histogram(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def show_many_runs(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
def add_one_run(self, animate=True):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_random_score(self):
|
||||||
|
pass
|
4926
active_projects/spirals.py
Normal file
4926
active_projects/spirals.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -33,6 +33,7 @@ class AnimationGroup(Animation):
|
|||||||
self.group = Group(*remove_list_redundancies(
|
self.group = Group(*remove_list_redundancies(
|
||||||
[anim.mobject for anim in animations]
|
[anim.mobject for anim in animations]
|
||||||
))
|
))
|
||||||
|
self.init_run_time()
|
||||||
Animation.__init__(self, self.group, **kwargs)
|
Animation.__init__(self, self.group, **kwargs)
|
||||||
|
|
||||||
def get_all_mobjects(self):
|
def get_all_mobjects(self):
|
||||||
@ -41,7 +42,7 @@ class AnimationGroup(Animation):
|
|||||||
def begin(self):
|
def begin(self):
|
||||||
for anim in self.animations:
|
for anim in self.animations:
|
||||||
anim.begin()
|
anim.begin()
|
||||||
self.init_run_time()
|
# self.init_run_time()
|
||||||
|
|
||||||
def finish(self):
|
def finish(self):
|
||||||
for anim in self.animations:
|
for anim in self.animations:
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
from manimlib.animation.animation import Animation
|
from manimlib.animation.animation import Animation
|
||||||
|
from manimlib.animation.composition import Succession
|
||||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||||
|
from manimlib.mobject.mobject import Group
|
||||||
from manimlib.utils.bezier import integer_interpolate
|
from manimlib.utils.bezier import integer_interpolate
|
||||||
from manimlib.utils.config_ops import digest_config
|
from manimlib.utils.config_ops import digest_config
|
||||||
from manimlib.utils.rate_functions import linear
|
from manimlib.utils.rate_functions import linear
|
||||||
@ -7,6 +9,7 @@ from manimlib.utils.rate_functions import double_smooth
|
|||||||
from manimlib.utils.rate_functions import smooth
|
from manimlib.utils.rate_functions import smooth
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import itertools as it
|
||||||
|
|
||||||
|
|
||||||
class ShowPartial(Animation):
|
class ShowPartial(Animation):
|
||||||
@ -132,4 +135,42 @@ class ShowIncreasingSubsets(Animation):
|
|||||||
def interpolate_mobject(self, alpha):
|
def interpolate_mobject(self, alpha):
|
||||||
n_submobs = len(self.all_submobs)
|
n_submobs = len(self.all_submobs)
|
||||||
index = int(self.int_func(alpha * n_submobs))
|
index = int(self.int_func(alpha * n_submobs))
|
||||||
|
self.update_submobject_list(index)
|
||||||
|
|
||||||
|
def update_submobject_list(self, index):
|
||||||
self.mobject.submobjects = self.all_submobs[:index]
|
self.mobject.submobjects = self.all_submobs[:index]
|
||||||
|
|
||||||
|
|
||||||
|
class ShowSubmobjectsOneByOne(ShowIncreasingSubsets):
|
||||||
|
def __init__(self, group, **kwargs):
|
||||||
|
new_group = Group(*group)
|
||||||
|
super().__init__(new_group, **kwargs)
|
||||||
|
|
||||||
|
def update_submobject_list(self, index):
|
||||||
|
# N = len(self.all_submobs)
|
||||||
|
if index == 0:
|
||||||
|
self.mobject.submobjects = []
|
||||||
|
else:
|
||||||
|
self.mobject.submobjects = self.all_submobs[index - 1]
|
||||||
|
|
||||||
|
|
||||||
|
# TODO, this is broken...
|
||||||
|
class AddTextWordByWord(Succession):
|
||||||
|
CONFIG = {
|
||||||
|
# If given a value for run_time, it will
|
||||||
|
# override the time_per_char
|
||||||
|
"run_time": None,
|
||||||
|
"time_per_char": 0.06,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, text_mobject, **kwargs):
|
||||||
|
digest_config(self, kwargs)
|
||||||
|
tpc = self.time_per_char
|
||||||
|
anims = it.chain(*[
|
||||||
|
[
|
||||||
|
ShowIncreasingSubsets(word, run_time=tpc * len(word)),
|
||||||
|
Animation(word, run_time=0.005 * len(word)**1.5),
|
||||||
|
]
|
||||||
|
for word in text_mobject
|
||||||
|
])
|
||||||
|
super().__init__(*anims, **kwargs)
|
||||||
|
@ -154,8 +154,7 @@ class PatreonEndScreen(PatreonThanks, PiCreatureScene):
|
|||||||
"randomize_order": True,
|
"randomize_order": True,
|
||||||
"capitalize": True,
|
"capitalize": True,
|
||||||
"name_y_spacing": 0.7,
|
"name_y_spacing": 0.7,
|
||||||
# "thanks_words": "Funded by the community, with special thanks to:",
|
"thanks_words": "My thanks to all the patrons among you",
|
||||||
"thanks_words": "Early access, name in credits and more at 3b1b.org/support",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def construct(self):
|
def construct(self):
|
||||||
@ -245,7 +244,7 @@ class PatreonEndScreen(PatreonThanks, PiCreatureScene):
|
|||||||
columns.next_to(underline, DOWN, buff=2)
|
columns.next_to(underline, DOWN, buff=2)
|
||||||
|
|
||||||
columns.generate_target()
|
columns.generate_target()
|
||||||
columns.target.to_edge(DOWN, buff=2)
|
columns.target.to_edge(DOWN, buff=4)
|
||||||
vect = columns.target.get_center() - columns.get_center()
|
vect = columns.target.get_center() - columns.get_center()
|
||||||
distance = get_norm(vect)
|
distance = get_norm(vect)
|
||||||
wait_time = 20
|
wait_time = 20
|
||||||
@ -259,11 +258,15 @@ class PatreonEndScreen(PatreonThanks, PiCreatureScene):
|
|||||||
self.wait(wait_time)
|
self.wait(wait_time)
|
||||||
|
|
||||||
def modify_patron_name(self, name):
|
def modify_patron_name(self, name):
|
||||||
if name.lower() == "RedAgent14".lower():
|
modification_map = {
|
||||||
return "Brian Shepetofsky"
|
"RedAgent14": "Brian Shepetofsky",
|
||||||
elif name.lower() == "DeathByShrimp".lower():
|
"DeathByShrimp": "Henry Bresnahan",
|
||||||
return "Henry Bresnahan"
|
"akostrikov": "Aleksandr Kostrikov",
|
||||||
|
"Jacob Baxter": "Will Fleshman",
|
||||||
|
}
|
||||||
|
for n1, n2 in modification_map.items():
|
||||||
|
if name.lower() == n1.lower():
|
||||||
|
return n2
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
@ -202,8 +202,6 @@ class TipableVMobject(VMobject):
|
|||||||
return get_norm(start - end)
|
return get_norm(start - end)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Arc(TipableVMobject):
|
class Arc(TipableVMobject):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"radius": 1.0,
|
"radius": 1.0,
|
||||||
|
@ -50,6 +50,15 @@ class PMobject(Mobject):
|
|||||||
self.color = color
|
self.color = color
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def get_stroke_width(self):
|
||||||
|
return self.stroke_width
|
||||||
|
|
||||||
|
def set_stroke_width(self, width, family=True):
|
||||||
|
mobs = self.family_members_with_points() if family else [self]
|
||||||
|
for mob in mobs:
|
||||||
|
mob.stroke_width = width
|
||||||
|
return self
|
||||||
|
|
||||||
# def set_color_by_gradient(self, start_color, end_color):
|
# def set_color_by_gradient(self, start_color, end_color):
|
||||||
def set_color_by_gradient(self, *colors):
|
def set_color_by_gradient(self, *colors):
|
||||||
self.rgbas = np.array(list(map(
|
self.rgbas = np.array(list(map(
|
||||||
@ -158,6 +167,12 @@ class PMobject(Mobject):
|
|||||||
self.rgbas = interpolate(
|
self.rgbas = interpolate(
|
||||||
mobject1.rgbas, mobject2.rgbas, alpha
|
mobject1.rgbas, mobject2.rgbas, alpha
|
||||||
)
|
)
|
||||||
|
self.set_stroke_width(interpolate(
|
||||||
|
mobject1.get_stroke_width(),
|
||||||
|
mobject2.get_stroke_width(),
|
||||||
|
alpha,
|
||||||
|
))
|
||||||
|
return self
|
||||||
|
|
||||||
def pointwise_become_partial(self, mobject, a, b):
|
def pointwise_become_partial(self, mobject, a, b):
|
||||||
lower_index, upper_index = [
|
lower_index, upper_index = [
|
||||||
@ -206,6 +221,14 @@ class Mobject2D(PMobject):
|
|||||||
Mobject.__init__(self, **kwargs)
|
Mobject.__init__(self, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class PGroup(PMobject):
|
||||||
|
def __init__(self, *pmobs, **kwargs):
|
||||||
|
if not all([isinstance(m, PMobject) for m in pmobs]):
|
||||||
|
raise Exception("All submobjects must be of type PMobject")
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.add(*pmobs)
|
||||||
|
|
||||||
|
|
||||||
class PointCloudDot(Mobject1D):
|
class PointCloudDot(Mobject1D):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"radius": 0.075,
|
"radius": 0.075,
|
||||||
|
@ -543,6 +543,8 @@ class Scene(Container):
|
|||||||
self.file_writer.write_frame(frame)
|
self.file_writer.write_frame(frame)
|
||||||
|
|
||||||
def add_sound(self, sound_file, time_offset=0, gain=None, **kwargs):
|
def add_sound(self, sound_file, time_offset=0, gain=None, **kwargs):
|
||||||
|
if self.skip_animations:
|
||||||
|
return
|
||||||
time = self.get_time() + time_offset
|
time = self.get_time() + time_offset
|
||||||
self.file_writer.add_sound(sound_file, time, gain, **kwargs)
|
self.file_writer.add_sound(sound_file, time, gain, **kwargs)
|
||||||
|
|
||||||
|
@ -88,7 +88,9 @@ class NameAnimationScene(Scene):
|
|||||||
return self.animated_name.replace(" ", "") + "Animation"
|
return self.animated_name.replace(" ", "") + "Animation"
|
||||||
|
|
||||||
|
|
||||||
names = []
|
names = [
|
||||||
|
"Happy 18th Birthday\\\\Pranavi Hiremath!"
|
||||||
|
]
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
for name in names:
|
for name in names:
|
||||||
|
@ -45,7 +45,7 @@ def stage_scenes(module_name):
|
|||||||
animation_dir = os.path.join(
|
animation_dir = os.path.join(
|
||||||
os.path.expanduser('~'),
|
os.path.expanduser('~'),
|
||||||
"Dropbox (3Blue1Brown)/3Blue1Brown Team Folder/videos",
|
"Dropbox (3Blue1Brown)/3Blue1Brown Team Folder/videos",
|
||||||
"windmill", "1440p60"
|
"spirals", "1440p60"
|
||||||
)
|
)
|
||||||
#
|
#
|
||||||
files = os.listdir(animation_dir)
|
files = os.listdir(animation_dir)
|
||||||
|
Reference in New Issue
Block a user