mirror of
https://github.com/3b1b/videos.git
synced 2025-08-31 21:58:59 +00:00
Add various scenes that had been disorganized
This commit is contained in:
parent
487e33299f
commit
a187a409fa
5 changed files with 562 additions and 0 deletions
|
@ -2,6 +2,7 @@ from manimlib.constants import WHITE
|
|||
from manimlib.constants import BLACK
|
||||
from manimlib.constants import DOWN
|
||||
from manimlib.constants import UP
|
||||
from manimlib.constants import BLUE
|
||||
from manimlib.scene.scene import Scene
|
||||
from manimlib.mobject.frame import FullScreenRectangle
|
||||
from manimlib.mobject.frame import ScreenRectangle
|
||||
|
@ -29,3 +30,16 @@ class Spotlight(Scene):
|
|||
animated_screen = AnimatedBoundary(screen)
|
||||
self.add(screen, animated_screen)
|
||||
self.wait(16)
|
||||
|
||||
|
||||
class VideoWrapper(Scene):
|
||||
def construct(self):
|
||||
self.add(FullScreenRectangle())
|
||||
screen = ScreenRectangle()
|
||||
screen.set_fill(BLACK, 1)
|
||||
screen.set_stroke(BLUE, 0)
|
||||
screen.set_height(6)
|
||||
screen.to_edge(DOWN)
|
||||
|
||||
self.add(screen, AnimatedBoundary(screen))
|
||||
self.wait(32)
|
||||
|
|
252
outside_videos/misc_thumbnails.py
Normal file
252
outside_videos/misc_thumbnails.py
Normal file
|
@ -0,0 +1,252 @@
|
|||
from manim_imports_ext import *
|
||||
|
||||
|
||||
class LinalgThumbnail(ThreeDScene):
|
||||
CONFIG = {
|
||||
"camera_config": {
|
||||
"anti_alias_width": 0,
|
||||
}
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
grid = NumberPlane((-10, 10), (-10, 10), faded_line_ratio=1)
|
||||
grid.set_stroke(width=6)
|
||||
grid.faded_lines.set_stroke(width=1)
|
||||
grid.apply_matrix([[3, 2], [1, -1]])
|
||||
# self.add(grid)
|
||||
|
||||
frame = self.camera.frame
|
||||
frame.reorient(0, 75)
|
||||
|
||||
cube = Cube()
|
||||
cube.set_color(BLUE)
|
||||
cube.set_opacity(0.5)
|
||||
|
||||
edges = VGroup()
|
||||
for vect in [OUT, RIGHT, UP, LEFT, DOWN, IN]:
|
||||
face = Square()
|
||||
face.shift(OUT)
|
||||
face.apply_matrix(z_to_vector(vect))
|
||||
edges.add(face)
|
||||
for sm in edges.family_members_with_points():
|
||||
sm.flat_stroke = False
|
||||
sm.joint_type = "round"
|
||||
|
||||
edges.set_stroke(WHITE, 4)
|
||||
edges.replace(cube)
|
||||
edges.apply_depth_test()
|
||||
cube = Group(cube, edges)
|
||||
|
||||
cube2 = cube.copy().apply_matrix(
|
||||
[[1, 0, 1], [0, 1, 0], [0, 0, 1]]
|
||||
)
|
||||
# cube2.match_height(cube)
|
||||
arrow = Vector(RIGHT)
|
||||
arrow.rotate(PI / 2, RIGHT)
|
||||
group = Group(cube, arrow, cube2)
|
||||
group.arrange(RIGHT, buff=MED_LARGE_BUFF)
|
||||
self.add(group)
|
||||
|
||||
# kw ={
|
||||
# "thickness": 0.1,
|
||||
# # "max_tip_length_to_length_ratio": 0.2,
|
||||
# }
|
||||
# self.add(Vector(grid.c2p(1, 0), fill_color=GREEN, **kw))
|
||||
# self.add(Vector(grid.c2p(0, 1), fill_color=RED, **kw))
|
||||
|
||||
# self.add(FullScreenFadeRectangle(fill_opacity=0.1))
|
||||
|
||||
|
||||
class CSThumbnail(Scene):
|
||||
def construct(self):
|
||||
self.add(self.get_background())
|
||||
|
||||
def get_background(self, n=12, k=50, zero_color=GREY_C, one_color=GREY_B):
|
||||
choices = (Integer(0, color=zero_color), Integer(1, color=one_color))
|
||||
background = VGroup(*(
|
||||
random.choice(choices).copy()
|
||||
for x in range(n * k)
|
||||
))
|
||||
background.arrange_in_grid(n, k)
|
||||
background.set_height(FRAME_HEIGHT)
|
||||
return background
|
||||
|
||||
|
||||
class GroupThumbnail(ThreeDScene):
|
||||
def construct(self):
|
||||
cube = Cube()
|
||||
cubes = Group(cube)
|
||||
for axis in [[1, 1, 1], [1, 1, -1], [1, -1, 1], [1, -1, -1]]:
|
||||
for angle in [60 * DEGREES]:
|
||||
cubes.add(cube.copy().rotate(angle, axis))
|
||||
|
||||
cubes.rotate(95 * DEGREES, RIGHT)
|
||||
cubes.rotate(30 * DEGREES, UP)
|
||||
cubes.set_height(6)
|
||||
cubes.center()
|
||||
# cubes.set_y(-0.5)
|
||||
cubes.set_color(BLUE_D)
|
||||
cubes.set_shadow(0.65)
|
||||
cubes.set_gloss(0.5)
|
||||
self.add(cubes)
|
||||
|
||||
|
||||
class BaselThumbnail(Scene):
|
||||
def construct(self):
|
||||
# Lake
|
||||
lake_radius = 6
|
||||
lake_center = ORIGIN
|
||||
|
||||
lake = Circle(
|
||||
fill_color=BLUE,
|
||||
fill_opacity=0.0,
|
||||
radius=lake_radius,
|
||||
stroke_color=BLUE_D,
|
||||
stroke_width=3,
|
||||
)
|
||||
lake.move_to(lake_center)
|
||||
|
||||
R = 2
|
||||
light_template = VGroup()
|
||||
rs = np.linspace(0, 1, 100)
|
||||
for r1, r2 in zip(rs, rs[1:]):
|
||||
dot1 = Dot(radius=R * r1).flip()
|
||||
dot2 = Dot(radius=R * r2)
|
||||
dot2.append_vectorized_mobject(dot1)
|
||||
dot2.insert_n_curves(100)
|
||||
dot2.set_fill(YELLOW, opacity=0.5 * (1 - r1)**2)
|
||||
dot2.set_stroke(width=0)
|
||||
light_template.add(dot2)
|
||||
|
||||
houses = VGroup()
|
||||
lights = VGroup()
|
||||
for i in range(16):
|
||||
theta = -TAU / 4 + (i + 0.5) * TAU / 16
|
||||
pos = lake_center + lake_radius * np.array([np.cos(theta), np.sin(theta), 0])
|
||||
house = Lighthouse()
|
||||
house.set_fill(GREY_B)
|
||||
house.set_stroke(width=0)
|
||||
house.set_height(0.5)
|
||||
house.move_to(pos)
|
||||
light = light_template.copy()
|
||||
light.move_to(pos)
|
||||
houses.add(house)
|
||||
lights.add(light)
|
||||
|
||||
self.add(lake)
|
||||
self.add(houses)
|
||||
self.add(lights)
|
||||
|
||||
# Equation
|
||||
equation = Tex(
|
||||
"1", "+", "{1 \\over 4}", "+",
|
||||
"{1 \\over 9}", "+", "{1 \\over 16}", "+",
|
||||
"{1 \\over 25}", "+", "\\cdots"
|
||||
)
|
||||
equation.scale(1.8)
|
||||
equation.move_to(2 * UP)
|
||||
answer = Tex("= \\frac{\\pi^2}{6}", color=YELLOW)
|
||||
answer.scale(3)
|
||||
answer.move_to(1.25 * DOWN)
|
||||
equation.add(answer)
|
||||
|
||||
shadow = VGroup()
|
||||
for w in np.linspace(20, 0, 50):
|
||||
shadow.add(equation.copy().set_fill(opacity=0).set_stroke(BLACK, width=w, opacity=0.02))
|
||||
self.add(shadow)
|
||||
self.add(equation)
|
||||
|
||||
self.wait()
|
||||
|
||||
|
||||
class Eola1Thumbnail(Scene):
|
||||
def construct(self):
|
||||
plane = NumberPlane(
|
||||
x_range=(-2, 2),
|
||||
y_range=(-5, 5),
|
||||
)
|
||||
plane.set_width(FRAME_WIDTH / 3)
|
||||
plane.to_edge(LEFT, buff=0)
|
||||
plane.shift(1.5 * DOWN)
|
||||
vect = Arrow(
|
||||
plane.get_origin(), plane.c2p(1, 2),
|
||||
buff=0,
|
||||
thickness=0.1,
|
||||
)
|
||||
vect.set_color(YELLOW)
|
||||
self.add(plane, vect)
|
||||
|
||||
coords = IntegerMatrix([[1], [2]])
|
||||
coords.set_height(3)
|
||||
coords.set_color(TEAL)
|
||||
coords.center()
|
||||
coords.match_y(vect)
|
||||
self.add(coords)
|
||||
|
||||
symbol = Tex("\\vec{\\textbf{v} } \\in V")
|
||||
symbol.set_color(BLUE)
|
||||
symbol.set_width(FRAME_WIDTH / 3 - 1)
|
||||
symbol.set_x(FRAME_WIDTH / 3)
|
||||
symbol.match_y(vect)
|
||||
self.add(symbol)
|
||||
|
||||
lines = VGroup(*(Line(DOWN, UP) for x in range(2)))
|
||||
lines.set_height(FRAME_HEIGHT)
|
||||
lines.arrange(RIGHT, buff=FRAME_WIDTH / 3)
|
||||
lines.set_stroke(GREY_A, 5)
|
||||
self.add(lines)
|
||||
|
||||
title = Text("Vectors", font_size=120)
|
||||
title.to_edge(UP, buff=MED_SMALL_BUFF)
|
||||
shadow = VGroup()
|
||||
for w in np.linspace(50, 0, 100):
|
||||
shadow.add(title.copy().set_fill(opacity=0).set_stroke(BLACK, width=w, opacity=0.01))
|
||||
self.add(shadow)
|
||||
self.add(title)
|
||||
|
||||
|
||||
def pendulum_vector_field_func(theta, omega, mu=0.3, g=9.8, L=3):
|
||||
return [omega, -np.sqrt(g / L) * np.sin(theta) - mu * omega]
|
||||
|
||||
|
||||
class ODEThumbnail(Scene):
|
||||
def construct(self):
|
||||
plane = NumberPlane()
|
||||
field = VectorField(
|
||||
pendulum_vector_field_func, plane,
|
||||
step_multiple=0.5,
|
||||
magnitude_range=(0, 5),
|
||||
length_func=lambda norm: 0.35 * sigmoid(norm),
|
||||
)
|
||||
field.set_opacity(0.75)
|
||||
|
||||
# self.add(plane)
|
||||
# self.add(field)
|
||||
# return
|
||||
|
||||
# Solution curve
|
||||
|
||||
dt = 0.1
|
||||
t = 0
|
||||
total_time = 50
|
||||
|
||||
def func(point):
|
||||
return plane.c2p(*pendulum_vector_field_func(*plane.p2c(point)))
|
||||
|
||||
points = [plane.c2p(-4 * TAU / 4, 4.0)]
|
||||
while t < total_time:
|
||||
t += dt
|
||||
points.append(points[-1] + dt * func(points[-1]))
|
||||
|
||||
line = VMobject()
|
||||
line.set_points_smoothly(points, true_smooth=True)
|
||||
line.set_stroke([WHITE, WHITE, BLACK], width=[5, 1])
|
||||
# line.set_stroke((BLUE_C, BLUE_E), width=(10, 1))
|
||||
|
||||
line_fuzz = VGroup()
|
||||
N = 50
|
||||
for width in np.linspace(50, 0, N):
|
||||
line_fuzz.add(line.copy().set_stroke(BLACK, width=width, opacity=2 / N))
|
||||
|
||||
self.add(line_fuzz)
|
||||
self.add(line)
|
74
outside_videos/mug_to_torus.py
Normal file
74
outside_videos/mug_to_torus.py
Normal file
|
@ -0,0 +1,74 @@
|
|||
from manim_imports_ext import *
|
||||
|
||||
|
||||
class MugToTorus(ThreeDScene):
|
||||
def construct(self):
|
||||
frame = self.camera.frame
|
||||
frame.reorient(-20, 60)
|
||||
|
||||
R1, R2 = (2, 0.75)
|
||||
|
||||
def torus_func(u, v):
|
||||
v1 = np.array([-math.sin(u), 0, math.cos(u)])
|
||||
v2 = math.cos(v) * v1 + math.sin(v) * UP
|
||||
return R1 * v1 + R2 * v2
|
||||
|
||||
def cylinder_func(u, v):
|
||||
return (math.cos(v), math.sin(v), u)
|
||||
|
||||
left_half_torus = ParametricSurface(
|
||||
torus_func,
|
||||
u_range=(-PI / 2, PI + PI / 2),
|
||||
v_range=(0, TAU),
|
||||
)
|
||||
right_half_torus = ParametricSurface(
|
||||
torus_func,
|
||||
u_range=(PI, TAU),
|
||||
v_range=(0, TAU),
|
||||
)
|
||||
cylinder = ParametricSurface(
|
||||
cylinder_func,
|
||||
u_range=(PI, TAU),
|
||||
v_range=(0, TAU),
|
||||
)
|
||||
cylinder.set_width(3)
|
||||
cylinder.set_depth(5, stretch=True)
|
||||
cylinder.move_to(ORIGIN, LEFT)
|
||||
|
||||
disk = Disk3D(resolution=(2, 50))
|
||||
disk.match_width(cylinder)
|
||||
disk.move_to(cylinder, IN)
|
||||
disk.scale(1.001)
|
||||
low_disk = disk.copy()
|
||||
|
||||
for mob in (left_half_torus, right_half_torus, cylinder, low_disk, disk):
|
||||
mob.set_color(GREY)
|
||||
mob.set_gloss(0.7)
|
||||
|
||||
left_half_torus.save_state()
|
||||
left_half_torus.set_depth(3, about_point=ORIGIN)
|
||||
|
||||
self.add(left_half_torus)
|
||||
self.add(cylinder)
|
||||
self.add(low_disk, disk)
|
||||
self.add(frame)
|
||||
|
||||
self.play(disk.animate.move_to(cylinder, OUT), run_time=2)
|
||||
|
||||
for mob in (disk, low_disk):
|
||||
mob.generate_target()
|
||||
mob.target.rotate(90 * DEGREES, DOWN)
|
||||
mob.target.set_depth(2 * R2)
|
||||
disk.target.move_to(right_half_torus, OUT + LEFT)
|
||||
low_disk.target.rotate(PI, UP)
|
||||
low_disk.target.move_to(right_half_torus, IN + LEFT)
|
||||
|
||||
self.play(
|
||||
MoveToTarget(disk),
|
||||
MoveToTarget(low_disk),
|
||||
Transform(cylinder, right_half_torus),
|
||||
Restore(left_half_torus, rate_func=squish_rate_func(smooth, 0, 0.75)),
|
||||
frame.animate.reorient(-10, 80),
|
||||
run_time=5,
|
||||
)
|
||||
self.wait()
|
84
outside_videos/podcast.py
Normal file
84
outside_videos/podcast.py
Normal file
|
@ -0,0 +1,84 @@
|
|||
from manim_imports_ext import *
|
||||
|
||||
|
||||
class PodcastIntro(Scene):
|
||||
def construct(self):
|
||||
tower = self.get_radio_tower()
|
||||
|
||||
n_rings = 15
|
||||
min_radius = 0.5
|
||||
max_radius = 9
|
||||
max_width = 20
|
||||
min_width = 0
|
||||
max_opacity = 1
|
||||
min_opacity = 0
|
||||
rings = VGroup(*(
|
||||
self.get_circle(radius=r)
|
||||
for r in np.linspace(min_radius, max_radius, n_rings)
|
||||
))
|
||||
tuples = zip(
|
||||
rings,
|
||||
np.linspace(max_width, min_width, n_rings),
|
||||
np.linspace(max_opacity, min_opacity, n_rings),
|
||||
)
|
||||
for ring, width, opacity in tuples:
|
||||
ring.set_stroke(width=width, opacity=opacity)
|
||||
ring.save_state()
|
||||
ring.scale(0)
|
||||
ring.set_stroke(WHITE, width=2)
|
||||
|
||||
self.play(
|
||||
ShowCreation(tower[0], lag_ratio=0.1),
|
||||
run_time=3
|
||||
)
|
||||
self.play(
|
||||
FadeIn(tower[1], scale=10, run_time=1),
|
||||
LaggedStart(
|
||||
*(
|
||||
Restore(ring, rate_func=linear)
|
||||
for ring in reversed(rings)
|
||||
),
|
||||
run_time=4,
|
||||
lag_ratio=0.08
|
||||
)
|
||||
)
|
||||
|
||||
def get_radio_tower(self):
|
||||
base = VGroup()
|
||||
line1 = Line(DL, UP)
|
||||
line2 = Line(DR, UP)
|
||||
base.add(line1, line2)
|
||||
base.set_width(2, stretch=True)
|
||||
base.set_height(4, stretch=True)
|
||||
base.to_edge(DOWN, buff=1.5)
|
||||
# alphas = [0, 0.2, 0.4, 0.6, 0.7, 0.8, 0.85]
|
||||
values = np.array([0, *(1 / n for n in range(1, 11))])
|
||||
alphas = np.cumsum(values)
|
||||
alphas /= alphas[-1]
|
||||
for a1, a2 in zip(alphas, alphas[1:]):
|
||||
base.add(
|
||||
Line(line1.pfp(a1), line2.pfp(a2)),
|
||||
Line(line2.pfp(a1), line1.pfp(a2)),
|
||||
)
|
||||
base.set_stroke(GREY_A, width=2)
|
||||
VGroup(line1, line2).set_stroke(width=4)
|
||||
|
||||
dot = Dot(line1.get_end(), radius=0.125)
|
||||
dot.set_color(WHITE)
|
||||
dot.set_gloss(0.5)
|
||||
tower = VGroup(base, dot)
|
||||
tower.set_height(3)
|
||||
tower.shift(-line1.get_end())
|
||||
tower.set_stroke(background=True)
|
||||
|
||||
return tower
|
||||
|
||||
def get_circle(self, center=ORIGIN, radius=1):
|
||||
arc1 = Arc(PI, 3 * PI / 2)
|
||||
arc2 = Arc(PI / 2, PI / 2)
|
||||
arc1.set_color(BLUE)
|
||||
arc2.set_color(GREY_BROWN)
|
||||
circle = VGroup(arc1, arc2)
|
||||
circle.set_width(2 * radius)
|
||||
circle.move_to(center)
|
||||
return circle
|
138
outside_videos/sudanese_band.py
Normal file
138
outside_videos/sudanese_band.py
Normal file
|
@ -0,0 +1,138 @@
|
|||
from manim_imports_ext import *
|
||||
|
||||
|
||||
def stereo_project_point(point, axis=0, r=1, max_norm=10000):
|
||||
point = fdiv(point * r, point[axis] + r)
|
||||
point[axis] = 0
|
||||
norm = get_norm(point)
|
||||
if norm > max_norm:
|
||||
point *= max_norm / norm
|
||||
return point
|
||||
|
||||
|
||||
def sudanese_band_func(eta, phi):
|
||||
z1 = math.sin(eta) * np.exp(complex(0, phi))
|
||||
z2 = math.cos(eta) * np.exp(complex(0, phi / 2))
|
||||
r4_point = np.array([z1.real, z1.imag, z2.real, z2.imag])
|
||||
r4_point[:3] = rotate_vector(r4_point[:3], PI / 3, axis=[1, 1, 1])
|
||||
return stereo_project_point(r4_point, axis=0)[1:]
|
||||
|
||||
|
||||
def mobius_strip_func(u, phi):
|
||||
vect = rotate_vector(RIGHT, phi / 2, axis=UP)
|
||||
vect = rotate_vector(vect, phi, axis=OUT)
|
||||
ref_point = np.array([np.cos(phi), np.sin(phi), 0])
|
||||
return ref_point + 0.7 * (u - 0.5) * vect
|
||||
|
||||
|
||||
def reversed_band(band_func):
|
||||
return lambda x, phi: band_func(x, -phi)
|
||||
|
||||
|
||||
def get_full_surface(band_func, x_range):
|
||||
surface = ParametricSurface(
|
||||
band_func, x_range, (0, TAU),
|
||||
)
|
||||
surface.set_color(BLUE_D)
|
||||
surface.set_shadow(0.5)
|
||||
surface.add_updater(lambda m: m.sort_faces_back_to_front(DOWN))
|
||||
# surface = TexturedSurface(surface, "EarthTextureMap", "NightEarthTextureMap")
|
||||
# surface = TexturedSurface(surface, "WaterColor")
|
||||
# inv_surface = ParametricSurface(
|
||||
# reversed_band(band_func), x_range[::-1], (0, TAU),
|
||||
# )
|
||||
m1, m2 = meshes = VGroup(
|
||||
SurfaceMesh(surface, normal_nudge=1e-3),
|
||||
SurfaceMesh(surface, normal_nudge=-1e-3),
|
||||
)
|
||||
bound = VGroup(
|
||||
ParametricCurve(lambda t: band_func(x_range[0], t), (0, TAU)),
|
||||
ParametricCurve(lambda t: band_func(x_range[1], t), (0, TAU)),
|
||||
)
|
||||
bound.set_stroke(RED, 3)
|
||||
bound.apply_depth_test()
|
||||
meshes.set_stroke(WHITE, 0.5, 0.5)
|
||||
return Group(surface, m1, m2, bound)
|
||||
return Group(surface, bound)
|
||||
|
||||
|
||||
def get_sudanese_band(circle_on_xy_plane=False):
|
||||
s_band = get_full_surface(
|
||||
sudanese_band_func,
|
||||
(0, PI),
|
||||
)
|
||||
angle = angle_of_vector(s_band[-1][0].get_start() - s_band[-1][1].get_start())
|
||||
s_band.rotate(PI / 2 - angle)
|
||||
if circle_on_xy_plane:
|
||||
s_band.rotate(90 * DEGREES, DOWN)
|
||||
s_band.shift(-s_band[-1][0].get_start())
|
||||
return s_band
|
||||
|
||||
|
||||
class SudaneseBand(ThreeDScene):
|
||||
circle_on_xy_plane = True
|
||||
|
||||
def construct(self):
|
||||
frame = self.camera.frame
|
||||
frame.reorient(-45, 70)
|
||||
frame.add_updater(
|
||||
lambda m, dt: m.increment_theta(2 * dt * DEGREES)
|
||||
)
|
||||
self.add(frame)
|
||||
|
||||
s_band = get_sudanese_band(self.circle_on_xy_plane)
|
||||
m_band = get_full_surface(mobius_strip_func, (0, 1))
|
||||
for band in s_band, m_band:
|
||||
band.set_height(6)
|
||||
|
||||
# self.play(ShowCreation(m_band[0]))
|
||||
# self.play(
|
||||
# FadeIn(m_band[1]),
|
||||
# FadeIn(m_band[2]),
|
||||
# ShowCreation(m_band[3]),
|
||||
# )
|
||||
self.add(m_band)
|
||||
self.wait()
|
||||
m_band.save_state()
|
||||
self.play(
|
||||
Transform(m_band, s_band),
|
||||
run_time=8,
|
||||
)
|
||||
# self.wait()
|
||||
self.play(frame.animate.reorient(-30, 110), run_time=4)
|
||||
# self.play(frame.animate.reorient(-30, 70), run_time=3)
|
||||
self.wait(2)
|
||||
frame.clear_updaters()
|
||||
self.play(
|
||||
m_band.animate.restore(),
|
||||
frame.animate.reorient(-45, 70),
|
||||
run_time=8,
|
||||
)
|
||||
|
||||
# self.embed()
|
||||
|
||||
|
||||
class SudaneseBandToKleinBottle(ThreeDScene):
|
||||
def construct(self):
|
||||
frame = self.camera.frame
|
||||
frame.reorient(-70, 70)
|
||||
# frame.add_updater(
|
||||
# lambda m, dt: m.increment_theta(2 * dt * DEGREES)
|
||||
# )
|
||||
# self.add(frame)
|
||||
|
||||
s_band = get_sudanese_band()
|
||||
s_band[1:3].set_opacity(0)
|
||||
circ = s_band[-1]
|
||||
s_band.shift(-circ.get_center())
|
||||
sb_copy = s_band.copy()
|
||||
|
||||
self.add(s_band)
|
||||
self.play(
|
||||
Rotate(sb_copy, PI, axis=RIGHT, about_point=ORIGIN),
|
||||
run_time=4
|
||||
)
|
||||
self.play(frame.animate.reorient(360 - 70, 70), run_time=15)
|
||||
self.wait()
|
||||
|
||||
self.embed()
|
Loading…
Add table
Reference in a new issue