mirror of
https://github.com/3b1b/manim.git
synced 2025-08-21 05:44:04 +00:00
commit
ec5c2acd3f
22 changed files with 3557 additions and 59 deletions
411
active_projects/shadows.py
Normal file
411
active_projects/shadows.py
Normal file
|
@ -0,0 +1,411 @@
|
||||||
|
from big_ol_pile_of_manim_imports import *
|
||||||
|
|
||||||
|
|
||||||
|
# Helpers
|
||||||
|
def get_shadow(mobject, opacity=0.5):
|
||||||
|
result = mobject.deepcopy()
|
||||||
|
result.apply_function(lambda p: [p[0], p[1], 0])
|
||||||
|
color = interpolate_color(
|
||||||
|
mobject.get_fill_color(), BLACK,
|
||||||
|
mobject.get_fill_opacity()
|
||||||
|
)
|
||||||
|
# color = BLACK
|
||||||
|
result.set_fill(color, opacity=opacity)
|
||||||
|
result.set_stroke(BLACK, 0.5, opacity=opacity)
|
||||||
|
result.set_shade_in_3d(False)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_boundary_points(shadow, n_points=20):
|
||||||
|
points = shadow.get_points_defining_boundary()
|
||||||
|
return np.array([
|
||||||
|
points[np.argmax(np.dot(points, vect.T))]
|
||||||
|
for vect in compass_directions(n_points)
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def get_area(planar_mobject):
|
||||||
|
boundary = get_boundary_points(planar_mobject, 100)
|
||||||
|
xs = boundary[:, 0]
|
||||||
|
ys = boundary[:, 1]
|
||||||
|
dxs = np.append(xs[-1], xs[:-1]) - xs
|
||||||
|
dys = np.append(ys[-1], ys[:-1]) - ys
|
||||||
|
return abs(sum([
|
||||||
|
0.5 * (x * dy - y * dx)
|
||||||
|
for x, dx, y, dy in zip(xs, dxs, ys, dys)
|
||||||
|
]))
|
||||||
|
|
||||||
|
|
||||||
|
def get_xy_plane_projection_point(p1, p2):
|
||||||
|
"""
|
||||||
|
Draw a line from source to p1 to p2. Where does it
|
||||||
|
intersect the xy plane?
|
||||||
|
"""
|
||||||
|
vect = p2 - p1
|
||||||
|
return p1 - (p1[2] / vect[2]) * vect
|
||||||
|
|
||||||
|
|
||||||
|
# Scenes
|
||||||
|
|
||||||
|
|
||||||
|
class ShowShadows(ThreeDScene):
|
||||||
|
CONFIG = {
|
||||||
|
"object_center": [0, 0, 3],
|
||||||
|
"area_label_center": [0, -1.5, 0],
|
||||||
|
"surface_area": 6.0,
|
||||||
|
"num_reorientations": 10,
|
||||||
|
"camera_config": {
|
||||||
|
"light_source_start_point": 10 * OUT,
|
||||||
|
"frame_center": [0, 0, 0.5],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
self.add_plane()
|
||||||
|
self.setup_orientation_trackers()
|
||||||
|
self.setup_object_and_shadow()
|
||||||
|
self.add_shadow_area_label()
|
||||||
|
self.add_surface_area_label()
|
||||||
|
|
||||||
|
def add_plane(self):
|
||||||
|
plane = self.plane = Rectangle(
|
||||||
|
width=FRAME_WIDTH,
|
||||||
|
height=24.2,
|
||||||
|
stroke_width=0,
|
||||||
|
fill_color=WHITE,
|
||||||
|
fill_opacity=0.35,
|
||||||
|
)
|
||||||
|
plane.set_sheen(0.2, DR)
|
||||||
|
grid = NumberPlane(
|
||||||
|
color=LIGHT_GREY,
|
||||||
|
secondary_color=DARK_GREY,
|
||||||
|
y_radius=int(plane.get_height() / 2),
|
||||||
|
stroke_width=1,
|
||||||
|
secondary_line_ratio=0,
|
||||||
|
)
|
||||||
|
plane.add(grid)
|
||||||
|
plane.add(VectorizedPoint(10 * IN))
|
||||||
|
plane.set_shade_in_3d(True, z_index_as_group=True)
|
||||||
|
self.add(plane)
|
||||||
|
|
||||||
|
def setup_orientation_trackers(self):
|
||||||
|
# Euler angles
|
||||||
|
self.alpha_tracker = ValueTracker(0)
|
||||||
|
self.beta_tracker = ValueTracker(0)
|
||||||
|
self.gamma_tracker = ValueTracker(0)
|
||||||
|
|
||||||
|
def setup_object_and_shadow(self):
|
||||||
|
self.obj3d = updating_mobject_from_func(self.get_reoriented_object)
|
||||||
|
self.shadow = updating_mobject_from_func(lambda: get_shadow(self.obj3d))
|
||||||
|
|
||||||
|
def add_shadow_area_label(self):
|
||||||
|
text = TextMobject("Shadow area: ")
|
||||||
|
decimal = DecimalNumber(0)
|
||||||
|
label = VGroup(text, decimal)
|
||||||
|
label.arrange_submobjects(RIGHT)
|
||||||
|
label.scale(1.5)
|
||||||
|
label.move_to(self.area_label_center - decimal.get_center())
|
||||||
|
self.shadow_area_label = label
|
||||||
|
self.shadow_area_decimal = decimal
|
||||||
|
|
||||||
|
# def update_decimal(decimal):
|
||||||
|
# # decimal.set_value(get_area(self.shadow))
|
||||||
|
# self.add_fixed_in_frame_mobjects(decimal)
|
||||||
|
|
||||||
|
# decimal.add_updater(update_decimal)
|
||||||
|
continual_update = ContinualChangingDecimal(
|
||||||
|
decimal,
|
||||||
|
lambda a: get_area(self.shadow),
|
||||||
|
position_update_func=lambda d: self.add_fixed_in_frame_mobjects(d)
|
||||||
|
)
|
||||||
|
|
||||||
|
# self.add_fixed_orientation_mobjects(label)
|
||||||
|
self.add_fixed_in_frame_mobjects(label)
|
||||||
|
self.add(label)
|
||||||
|
self.add(continual_update)
|
||||||
|
|
||||||
|
def add_surface_area_label(self):
|
||||||
|
text = TextMobject("Surface area: ")
|
||||||
|
decimal = DecimalNumber(self.surface_area)
|
||||||
|
label = VGroup(text, decimal)
|
||||||
|
label.arrange_submobjects(RIGHT)
|
||||||
|
label.scale(1.25)
|
||||||
|
label.set_fill(YELLOW)
|
||||||
|
label.set_background_stroke(width=3)
|
||||||
|
label.next_to(self.obj3d, RIGHT, LARGE_BUFF)
|
||||||
|
label.shift(MED_LARGE_BUFF * IN)
|
||||||
|
self.surface_area_label = label
|
||||||
|
self.add_fixed_orientation_mobjects(label)
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
# Show creation
|
||||||
|
obj3d = self.obj3d.copy()
|
||||||
|
obj3d.clear_updaters()
|
||||||
|
temp_shadow = updating_mobject_from_func(lambda: get_shadow(obj3d))
|
||||||
|
self.add(temp_shadow)
|
||||||
|
self.move_camera(
|
||||||
|
phi=60 * DEGREES,
|
||||||
|
theta=-120 * DEGREES,
|
||||||
|
added_anims=[
|
||||||
|
LaggedStart(DrawBorderThenFill, obj3d)
|
||||||
|
],
|
||||||
|
run_time=2
|
||||||
|
)
|
||||||
|
self.begin_ambient_camera_rotation(0.01)
|
||||||
|
self.remove(obj3d, temp_shadow)
|
||||||
|
|
||||||
|
average_label = self.get_average_label()
|
||||||
|
# Reorient
|
||||||
|
self.add(self.obj3d, self.shadow)
|
||||||
|
for n in range(self.num_reorientations):
|
||||||
|
self.randomly_reorient()
|
||||||
|
if n == 3:
|
||||||
|
self.add_fixed_in_frame_mobjects(average_label)
|
||||||
|
self.play(Write(average_label, run_time=2))
|
||||||
|
else:
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
def randomly_reorient(self, run_time=3):
|
||||||
|
a, b, c = TAU * np.random.random(3)
|
||||||
|
self.play(
|
||||||
|
self.alpha_tracker.set_value, a,
|
||||||
|
self.beta_tracker.set_value, b,
|
||||||
|
self.gamma_tracker.set_value, c,
|
||||||
|
run_time=run_time
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
def get_object(self):
|
||||||
|
cube = Cube()
|
||||||
|
cube.set_height(1)
|
||||||
|
# cube.set_width(2, stretch=True)
|
||||||
|
cube.set_stroke(WHITE, 0.5)
|
||||||
|
return cube
|
||||||
|
|
||||||
|
def get_reoriented_object(self):
|
||||||
|
obj3d = self.get_object()
|
||||||
|
angles = [
|
||||||
|
self.alpha_tracker.get_value(),
|
||||||
|
self.beta_tracker.get_value(),
|
||||||
|
self.gamma_tracker.get_value(),
|
||||||
|
]
|
||||||
|
vects = [OUT, RIGHT, OUT]
|
||||||
|
|
||||||
|
center = self.object_center
|
||||||
|
obj3d.move_to(center)
|
||||||
|
for angle, vect in zip(angles, vects):
|
||||||
|
obj3d.rotate(angle, vect, about_point=center)
|
||||||
|
return obj3d
|
||||||
|
|
||||||
|
def get_average_label(self):
|
||||||
|
rect = SurroundingRectangle(
|
||||||
|
self.shadow_area_decimal,
|
||||||
|
buff=SMALL_BUFF,
|
||||||
|
color=RED,
|
||||||
|
)
|
||||||
|
words = TextMobject(
|
||||||
|
"Average", "=",
|
||||||
|
"$\\frac{\\text{Surface area}}{4}$"
|
||||||
|
)
|
||||||
|
words.scale(1.5)
|
||||||
|
words[0].match_color(rect)
|
||||||
|
words[2].set_color(self.surface_area_label[0].get_fill_color())
|
||||||
|
words.set_background_stroke(width=3)
|
||||||
|
words.next_to(
|
||||||
|
rect, DOWN,
|
||||||
|
index_of_submobject_to_align=0,
|
||||||
|
)
|
||||||
|
# words.shift(MED_LARGE_BUFF * LEFT)
|
||||||
|
return VGroup(rect, words)
|
||||||
|
|
||||||
|
|
||||||
|
class ShowInfinitelyFarLightSource(ShowShadows):
|
||||||
|
CONFIG = {
|
||||||
|
"num_reorientations": 1,
|
||||||
|
"camera_center": [0, 0, 1],
|
||||||
|
}
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
self.force_skipping()
|
||||||
|
ShowShadows.construct(self)
|
||||||
|
self.revert_to_original_skipping_status()
|
||||||
|
|
||||||
|
self.add_light_source_based_shadow_updater()
|
||||||
|
self.add_light()
|
||||||
|
self.move_light_around()
|
||||||
|
self.show_vertical_lines()
|
||||||
|
|
||||||
|
def add_light(self):
|
||||||
|
light = self.light = self.get_light()
|
||||||
|
light_source = self.camera.light_source
|
||||||
|
light.move_to(light_source)
|
||||||
|
light_source.add_updater(lambda m: m.move_to(light))
|
||||||
|
self.add(light_source)
|
||||||
|
self.add_fixed_orientation_mobjects(light)
|
||||||
|
|
||||||
|
def move_light_around(self):
|
||||||
|
light = self.light
|
||||||
|
self.add_foreground_mobjects(self.shadow_area_label)
|
||||||
|
self.play(
|
||||||
|
light.move_to, 5 * OUT + DOWN,
|
||||||
|
run_time=3
|
||||||
|
)
|
||||||
|
self.play(Rotating(
|
||||||
|
light, angle=TAU, about_point=5 * OUT,
|
||||||
|
rate_func=smooth, run_time=3
|
||||||
|
))
|
||||||
|
self.play(
|
||||||
|
light.move_to, 30 * OUT,
|
||||||
|
run_time=3,
|
||||||
|
)
|
||||||
|
self.remove(light)
|
||||||
|
|
||||||
|
def show_vertical_lines(self):
|
||||||
|
lines = self.get_vertical_lines()
|
||||||
|
obj3d = self.obj3d
|
||||||
|
shadow = self.shadow
|
||||||
|
target_obj3d = obj3d.copy()
|
||||||
|
target_obj3d.become(shadow)
|
||||||
|
target_obj3d.match_style(obj3d)
|
||||||
|
target_obj3d.set_shade_in_3d(False)
|
||||||
|
source_obj3d = obj3d.copy()
|
||||||
|
source_obj3d.set_shade_in_3d(False)
|
||||||
|
source_obj3d.fade(1)
|
||||||
|
|
||||||
|
self.play(LaggedStart(ShowCreation, lines))
|
||||||
|
self.wait()
|
||||||
|
self.add(source_obj3d, lines)
|
||||||
|
self.play(
|
||||||
|
ReplacementTransform(source_obj3d, target_obj3d),
|
||||||
|
run_time=2
|
||||||
|
)
|
||||||
|
self.add(target_obj3d, lines)
|
||||||
|
self.play(FadeOut(target_obj3d),)
|
||||||
|
self.wait()
|
||||||
|
lines.add_updater(lambda m: m.become(self.get_vertical_lines()))
|
||||||
|
for x in range(5):
|
||||||
|
self.randomly_reorient()
|
||||||
|
|
||||||
|
def add_light_source_based_shadow_updater(self):
|
||||||
|
shadow = self.shadow
|
||||||
|
light_source = self.camera.light_source
|
||||||
|
obj3d = self.obj3d
|
||||||
|
center = obj3d.get_center()
|
||||||
|
|
||||||
|
def update(shadow):
|
||||||
|
lsp = light_source.get_center()
|
||||||
|
proj_center = get_xy_plane_projection_point(lsp, center)
|
||||||
|
c_to_lsp = lsp - center
|
||||||
|
unit_c_to_lsp = normalize(c_to_lsp)
|
||||||
|
rotation = rotation_matrix(
|
||||||
|
angle=np.arccos(np.dot(unit_c_to_lsp, OUT)),
|
||||||
|
axis=normalize(np.cross(unit_c_to_lsp, OUT))
|
||||||
|
)
|
||||||
|
new_shadow = get_shadow(
|
||||||
|
self.obj3d.copy().apply_matrix(rotation)
|
||||||
|
)
|
||||||
|
shadow.become(new_shadow)
|
||||||
|
shadow.scale(get_norm(lsp) / get_norm(c_to_lsp))
|
||||||
|
shadow.move_to(proj_center)
|
||||||
|
return shadow
|
||||||
|
shadow.add_updater(update)
|
||||||
|
|
||||||
|
def get_light(self):
|
||||||
|
n_rings = 40
|
||||||
|
radii = np.linspace(0, 2, n_rings)
|
||||||
|
rings = VGroup(*[
|
||||||
|
Annulus(inner_radius=r1, outer_radius=r2)
|
||||||
|
for r1, r2 in zip(radii, radii[1:])
|
||||||
|
])
|
||||||
|
opacities = np.linspace(1, 0, n_rings)**1.5
|
||||||
|
for opacity, ring in zip(opacities, rings):
|
||||||
|
ring.set_fill(YELLOW, opacity)
|
||||||
|
ring.set_stroke(YELLOW, width=0.1, opacity=opacity)
|
||||||
|
return rings
|
||||||
|
|
||||||
|
def get_vertical_lines(self):
|
||||||
|
shadow = self.shadow
|
||||||
|
points = get_boundary_points(shadow, 10)
|
||||||
|
# half_points = [(p1 + p2) / 2 for p1, p2 in adjacent_pairs(points)]
|
||||||
|
# points = np.append(points, half_points, axis=0)
|
||||||
|
light_source = self.light.get_center()
|
||||||
|
lines = VGroup(*[
|
||||||
|
DashedLine(light_source, point)
|
||||||
|
for point in points
|
||||||
|
])
|
||||||
|
lines.set_shade_in_3d(True)
|
||||||
|
for line in lines:
|
||||||
|
line.remove(*line[:int(0.8 * len(line))])
|
||||||
|
line[-10:].set_shade_in_3d(False)
|
||||||
|
line.set_stroke(YELLOW, 1)
|
||||||
|
return lines
|
||||||
|
|
||||||
|
|
||||||
|
class CylinderShadows(ShowShadows):
|
||||||
|
CONFIG = {
|
||||||
|
"surface_area": 2 * PI + 2 * PI * 2,
|
||||||
|
"area_label_center": [0, -2, 0],
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
height = 2
|
||||||
|
cylinder = ParametricSurface(
|
||||||
|
lambda u, v: np.array([
|
||||||
|
np.cos(TAU * v),
|
||||||
|
np.sin(TAU * v),
|
||||||
|
height * (1 - u)
|
||||||
|
]),
|
||||||
|
resolution=(6, 32)
|
||||||
|
)
|
||||||
|
# circle = Circle(radius=1)
|
||||||
|
circle = ParametricSurface(
|
||||||
|
lambda u, v: np.array([
|
||||||
|
(v + 0.01) * np.cos(TAU * u),
|
||||||
|
(v + 0.01) * np.sin(TAU * u),
|
||||||
|
0,
|
||||||
|
]),
|
||||||
|
resolution=(16, 8)
|
||||||
|
)
|
||||||
|
# circle.set_fill(GREEN, opacity=0.5)
|
||||||
|
for surface in cylinder, circle:
|
||||||
|
surface.set_fill_by_checkerboard(GREEN, GREEN_E, opacity=1.0)
|
||||||
|
# surface.set_fill(GREEN, opacity=0.5)
|
||||||
|
cylinder.add(circle)
|
||||||
|
cylinder.add(circle.copy().flip().move_to(height * OUT))
|
||||||
|
cylinder.set_shade_in_3d(True)
|
||||||
|
cylinder.set_stroke(width=0)
|
||||||
|
cylinder.scale(1.003)
|
||||||
|
return cylinder
|
||||||
|
|
||||||
|
|
||||||
|
class PrismShadows(ShowShadows):
|
||||||
|
CONFIG = {
|
||||||
|
"surface_area": 3 * np.sqrt(3) / 2 + 3 * (np.sqrt(3) * 2),
|
||||||
|
"object_center": [0, 0, 3],
|
||||||
|
"area_label_center": [0, -2.25, 0],
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
height = 2
|
||||||
|
prism = VGroup()
|
||||||
|
triangle = RegularPolygon(3)
|
||||||
|
verts = triangle.get_anchors()[:3]
|
||||||
|
rects = [
|
||||||
|
Polygon(v1, v2, v2 + height * OUT, v1 + height * OUT)
|
||||||
|
for v1, v2 in adjacent_pairs(verts)
|
||||||
|
]
|
||||||
|
prism.add(triangle, *rects)
|
||||||
|
prism.add(triangle.copy().shift(height * OUT))
|
||||||
|
triangle.reverse_points()
|
||||||
|
prism.set_shade_in_3d(True)
|
||||||
|
prism.set_fill(PINK, 0.8)
|
||||||
|
prism.set_stroke(WHITE, 1)
|
||||||
|
return prism
|
||||||
|
|
||||||
|
|
||||||
|
class TheseFourPiAreSquare(PiCreatureScene):
|
||||||
|
def construct(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def create_pi_creatures(self):
|
||||||
|
pass
|
|
@ -56,6 +56,7 @@ from mobject.three_dimensions import *
|
||||||
from mobject.types.image_mobject import *
|
from mobject.types.image_mobject import *
|
||||||
from mobject.types.point_cloud_mobject import *
|
from mobject.types.point_cloud_mobject import *
|
||||||
from mobject.types.vectorized_mobject import *
|
from mobject.types.vectorized_mobject import *
|
||||||
|
from mobject.updater import *
|
||||||
from mobject.value_tracker import *
|
from mobject.value_tracker import *
|
||||||
|
|
||||||
from for_3b1b_videos.common_scenes import *
|
from for_3b1b_videos.common_scenes import *
|
||||||
|
|
|
@ -23,7 +23,6 @@ from utils.simple_functions import clip_in_place
|
||||||
|
|
||||||
class ThreeDCamera(Camera):
|
class ThreeDCamera(Camera):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"sun_vect": 5 * UP + LEFT,
|
|
||||||
"shading_factor": 0.2,
|
"shading_factor": 0.2,
|
||||||
"distance": 20.0,
|
"distance": 20.0,
|
||||||
"default_distance": 5.0,
|
"default_distance": 5.0,
|
||||||
|
@ -167,7 +166,7 @@ class ThreeDCamera(Camera):
|
||||||
distance = self.get_distance()
|
distance = self.get_distance()
|
||||||
rot_matrix = self.get_rotation_matrix()
|
rot_matrix = self.get_rotation_matrix()
|
||||||
|
|
||||||
points -= frame_center
|
points = points - frame_center
|
||||||
points = np.dot(points, rot_matrix.T)
|
points = np.dot(points, rot_matrix.T)
|
||||||
zs = points[:, 2]
|
zs = points[:, 2]
|
||||||
for i in 0, 1:
|
for i in 0, 1:
|
||||||
|
@ -184,7 +183,7 @@ class ThreeDCamera(Camera):
|
||||||
factor[(distance - zs) < 0] = 10**6
|
factor[(distance - zs) < 0] = 10**6
|
||||||
# clip_in_place(factor, 0, 10**6)
|
# clip_in_place(factor, 0, 10**6)
|
||||||
points[:, i] *= factor
|
points[:, i] *= factor
|
||||||
points += frame_center
|
points = points + frame_center
|
||||||
return points
|
return points
|
||||||
|
|
||||||
def project_point(self, point):
|
def project_point(self, point):
|
||||||
|
|
12
constants.py
12
constants.py
|
@ -114,11 +114,11 @@ PI = np.pi
|
||||||
TAU = 2 * PI
|
TAU = 2 * PI
|
||||||
DEGREES = TAU / 360
|
DEGREES = TAU / 360
|
||||||
|
|
||||||
ANIMATIONS_DIR = os.path.join(MEDIA_DIR, "animations")
|
VIDEO_DIR = os.path.join(MEDIA_DIR, "videos")
|
||||||
RASTER_IMAGE_DIR = os.path.join(MEDIA_DIR, "designs", "raster_images")
|
RASTER_IMAGE_DIR = os.path.join(MEDIA_DIR, "designs", "raster_images")
|
||||||
SVG_IMAGE_DIR = os.path.join(MEDIA_DIR, "designs", "svg_images")
|
SVG_IMAGE_DIR = os.path.join(MEDIA_DIR, "designs", "svg_images")
|
||||||
# TODO, staged scenes should really go into a subdirectory of a given scenes directory
|
# TODO, staged scenes should really go into a subdirectory of a given scenes directory
|
||||||
STAGED_SCENES_DIR = os.path.join(ANIMATIONS_DIR, "staged_scenes")
|
STAGED_SCENES_DIR = os.path.join(VIDEO_DIR, "staged_scenes")
|
||||||
###
|
###
|
||||||
THIS_DIR = os.path.dirname(os.path.realpath(__file__))
|
THIS_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||||
FILE_DIR = os.path.join(THIS_DIR, "files")
|
FILE_DIR = os.path.join(THIS_DIR, "files")
|
||||||
|
@ -128,7 +128,7 @@ TEX_IMAGE_DIR = TEX_DIR # TODO, What is this doing?
|
||||||
MOBJECT_DIR = os.path.join(FILE_DIR, "mobjects")
|
MOBJECT_DIR = os.path.join(FILE_DIR, "mobjects")
|
||||||
IMAGE_MOBJECT_DIR = os.path.join(MOBJECT_DIR, "image")
|
IMAGE_MOBJECT_DIR = os.path.join(MOBJECT_DIR, "image")
|
||||||
|
|
||||||
for folder in [FILE_DIR, RASTER_IMAGE_DIR, SVG_IMAGE_DIR, ANIMATIONS_DIR, TEX_DIR,
|
for folder in [FILE_DIR, RASTER_IMAGE_DIR, SVG_IMAGE_DIR, VIDEO_DIR, TEX_DIR,
|
||||||
TEX_IMAGE_DIR, MOBJECT_DIR, IMAGE_MOBJECT_DIR,
|
TEX_IMAGE_DIR, MOBJECT_DIR, IMAGE_MOBJECT_DIR,
|
||||||
STAGED_SCENES_DIR]:
|
STAGED_SCENES_DIR]:
|
||||||
if not os.path.exists(folder):
|
if not os.path.exists(folder):
|
||||||
|
@ -136,8 +136,10 @@ for folder in [FILE_DIR, RASTER_IMAGE_DIR, SVG_IMAGE_DIR, ANIMATIONS_DIR, TEX_DI
|
||||||
|
|
||||||
TEX_USE_CTEX = False
|
TEX_USE_CTEX = False
|
||||||
TEX_TEXT_TO_REPLACE = "YourTextHere"
|
TEX_TEXT_TO_REPLACE = "YourTextHere"
|
||||||
TEMPLATE_TEX_FILE = os.path.join(THIS_DIR, "tex_template.tex" if not TEX_USE_CTEX
|
TEMPLATE_TEX_FILE = os.path.join(
|
||||||
else "ctex_template.tex")
|
THIS_DIR, "tex_template.tex" if not TEX_USE_CTEX
|
||||||
|
else "ctex_template.tex"
|
||||||
|
)
|
||||||
with open(TEMPLATE_TEX_FILE, "r") as infile:
|
with open(TEMPLATE_TEX_FILE, "r") as infile:
|
||||||
TEMPLATE_TEXT_FILE_BODY = infile.read()
|
TEMPLATE_TEXT_FILE_BODY = infile.read()
|
||||||
TEMPLATE_TEX_FILE_BODY = TEMPLATE_TEXT_FILE_BODY.replace(
|
TEMPLATE_TEX_FILE_BODY = TEMPLATE_TEXT_FILE_BODY.replace(
|
||||||
|
|
|
@ -260,7 +260,7 @@ def main():
|
||||||
scene_names_to_classes = dict(inspect.getmembers(module, is_scene))
|
scene_names_to_classes = dict(inspect.getmembers(module, is_scene))
|
||||||
|
|
||||||
# config["output_directory"] = os.path.join(
|
# config["output_directory"] = os.path.join(
|
||||||
# ANIMATIONS_DIR,
|
# VIDEO_DIR,
|
||||||
# config["file"].replace(".py", "")
|
# config["file"].replace(".py", "")
|
||||||
# )
|
# )
|
||||||
|
|
||||||
|
|
|
@ -227,16 +227,16 @@ class PatreonEndScreen(PatreonThanks):
|
||||||
RIGHT, buff=LARGE_BUFF,
|
RIGHT, buff=LARGE_BUFF,
|
||||||
aligned_edge=UP,
|
aligned_edge=UP,
|
||||||
)
|
)
|
||||||
|
if columns.get_width() > self.max_patron_width:
|
||||||
columns.set_width(total_width - 1)
|
columns.set_width(total_width - 1)
|
||||||
columns.next_to(black_rect, DOWN, 3 * LARGE_BUFF)
|
|
||||||
columns.to_edge(RIGHT)
|
|
||||||
|
|
||||||
thanks.align_to(columns, alignment_vect=RIGHT)
|
thanks.to_edge(RIGHT)
|
||||||
|
columns.next_to(thanks, DOWN, 3 * LARGE_BUFF)
|
||||||
|
|
||||||
self.add(columns, black_rect, line, thanks)
|
self.add(columns, black_rect, line, thanks)
|
||||||
self.play(
|
self.play(
|
||||||
columns.move_to, 2 * DOWN, DOWN,
|
columns.move_to, 2 * DOWN, DOWN,
|
||||||
columns.to_edge, RIGHT,
|
columns.align_to, thanks, {"alignment_vect": RIGHT},
|
||||||
rate_func=None,
|
rate_func=None,
|
||||||
run_time=self.run_time,
|
run_time=self.run_time,
|
||||||
)
|
)
|
||||||
|
|
|
@ -88,7 +88,7 @@ class Matrix(VMobject):
|
||||||
|
|
||||||
def matrix_to_mob_matrix(self, matrix):
|
def matrix_to_mob_matrix(self, matrix):
|
||||||
return np.vectorize(self.element_to_mobject)(
|
return np.vectorize(self.element_to_mobject)(
|
||||||
matrix
|
matrix, **self.element_to_mobject_config
|
||||||
)
|
)
|
||||||
|
|
||||||
def organize_mob_matrix(self, matrix):
|
def organize_mob_matrix(self, matrix):
|
||||||
|
|
|
@ -113,7 +113,7 @@ class Mobject(Container):
|
||||||
|
|
||||||
def save_image(self, name=None):
|
def save_image(self, name=None):
|
||||||
self.get_image().save(
|
self.get_image().save(
|
||||||
os.path.join(ANIMATIONS_DIR, (name or str(self)) + ".png")
|
os.path.join(VIDEO_DIR, (name or str(self)) + ".png")
|
||||||
)
|
)
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
|
@ -186,6 +186,7 @@ class Mobject(Container):
|
||||||
|
|
||||||
def clear_updaters(self):
|
def clear_updaters(self):
|
||||||
self.updaters = []
|
self.updaters = []
|
||||||
|
return self
|
||||||
|
|
||||||
# Transforming operations
|
# Transforming operations
|
||||||
|
|
||||||
|
@ -703,7 +704,7 @@ class Mobject(Container):
|
||||||
# Getters
|
# Getters
|
||||||
|
|
||||||
def get_points_defining_boundary(self):
|
def get_points_defining_boundary(self):
|
||||||
return self.points
|
return self.get_all_points()
|
||||||
|
|
||||||
def get_num_points(self):
|
def get_num_points(self):
|
||||||
return len(self.points)
|
return len(self.points)
|
||||||
|
@ -743,7 +744,8 @@ class Mobject(Container):
|
||||||
|
|
||||||
def get_boundary_point(self, direction):
|
def get_boundary_point(self, direction):
|
||||||
all_points = self.get_points_defining_boundary()
|
all_points = self.get_points_defining_boundary()
|
||||||
return all_points[np.argmax(np.dot(all_points, direction))]
|
index = np.argmax(np.dot(all_points, np.array(direction).T))
|
||||||
|
return all_points[index]
|
||||||
|
|
||||||
def get_z_index_reference_point(self):
|
def get_z_index_reference_point(self):
|
||||||
# TODO, better place to define default z_index_group?
|
# TODO, better place to define default z_index_group?
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
|
import itertools as it
|
||||||
from colour import Color
|
from colour import Color
|
||||||
|
|
||||||
from mobject.mobject import Mobject
|
from mobject.mobject import Mobject
|
||||||
|
@ -313,6 +313,7 @@ class VMobject(Mobject):
|
||||||
submob.shade_in_3d = value
|
submob.shade_in_3d = value
|
||||||
if z_index_as_group:
|
if z_index_as_group:
|
||||||
submob.z_index_group = self
|
submob.z_index_group = self
|
||||||
|
return self
|
||||||
|
|
||||||
# Drawing
|
# Drawing
|
||||||
def start_at(self, point):
|
def start_at(self, point):
|
||||||
|
@ -482,7 +483,9 @@ class VMobject(Mobject):
|
||||||
return self.points[::3]
|
return self.points[::3]
|
||||||
|
|
||||||
def get_points_defining_boundary(self):
|
def get_points_defining_boundary(self):
|
||||||
return self.get_anchors()
|
return np.array(list(it.chain(*[
|
||||||
|
sm.get_anchors() for sm in self.get_family()
|
||||||
|
])))
|
||||||
|
|
||||||
# Alignment
|
# Alignment
|
||||||
def align_points(self, vmobject):
|
def align_points(self, vmobject):
|
||||||
|
|
4
mobject/updater.py
Normal file
4
mobject/updater.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
def updating_mobject_from_func(func):
|
||||||
|
mob = func()
|
||||||
|
mob.add_updater(lambda m: mob.become(func()))
|
||||||
|
return mob
|
1236
old_projects/borsuk_addition.py
Normal file
1236
old_projects/borsuk_addition.py
Normal file
File diff suppressed because it is too large
Load diff
|
@ -10,7 +10,7 @@ from big_ol_pile_of_manim_imports import *
|
||||||
from script_wrapper import command_line_create_scene
|
from script_wrapper import command_line_create_scene
|
||||||
|
|
||||||
MOVIE_PREFIX = "counting_in_binary/"
|
MOVIE_PREFIX = "counting_in_binary/"
|
||||||
BASE_HAND_FILE = os.path.join(ANIMATIONS_DIR, MOVIE_PREFIX, "Base.mp4")
|
BASE_HAND_FILE = os.path.join(VIDEO_DIR, MOVIE_PREFIX, "Base.mp4")
|
||||||
FORCED_FRAME_DURATION = 0.02
|
FORCED_FRAME_DURATION = 0.02
|
||||||
TIME_RANGE = (0, 42)
|
TIME_RANGE = (0, 42)
|
||||||
INITIAL_PADDING = 27
|
INITIAL_PADDING = 27
|
||||||
|
@ -84,7 +84,7 @@ class Hand(ImageMobject):
|
||||||
def __init__(self, num, small = False, **kwargs):
|
def __init__(self, num, small = False, **kwargs):
|
||||||
Mobject2D.__init__(self, **kwargs)
|
Mobject2D.__init__(self, **kwargs)
|
||||||
path = os.path.join(
|
path = os.path.join(
|
||||||
ANIMATIONS_DIR, MOVIE_PREFIX, "images", "Hand%d.png"%num
|
VIDEO_DIR, MOVIE_PREFIX, "images", "Hand%d.png"%num
|
||||||
)
|
)
|
||||||
invert = False
|
invert = False
|
||||||
if not self.read_in_cached_attrs(path, invert):
|
if not self.read_in_cached_attrs(path, invert):
|
||||||
|
@ -160,7 +160,7 @@ class SaveEachNumber(OverHand):
|
||||||
OverHand.construct(self)
|
OverHand.construct(self)
|
||||||
for count in COUNT_TO_FRAME_NUM:
|
for count in COUNT_TO_FRAME_NUM:
|
||||||
path = os.path.join(
|
path = os.path.join(
|
||||||
ANIMATIONS_DIR, MOVIE_PREFIX, "images",
|
VIDEO_DIR, MOVIE_PREFIX, "images",
|
||||||
"Hand%d.png"%count
|
"Hand%d.png"%count
|
||||||
)
|
)
|
||||||
Image.fromarray(self.frames[COUNT_TO_FRAME_NUM[count]]).save(path)
|
Image.fromarray(self.frames[COUNT_TO_FRAME_NUM[count]]).save(path)
|
||||||
|
|
|
@ -1459,6 +1459,7 @@ class GeometryProofLand(Scene):
|
||||||
PINK, RED, YELLOW, GREEN, GREEN_A, BLUE,
|
PINK, RED, YELLOW, GREEN, GREEN_A, BLUE,
|
||||||
MAROON_E, MAROON_B, YELLOW, BLUE,
|
MAROON_E, MAROON_B, YELLOW, BLUE,
|
||||||
],
|
],
|
||||||
|
"text": "Geometry proof land",
|
||||||
}
|
}
|
||||||
|
|
||||||
def construct(self):
|
def construct(self):
|
||||||
|
@ -1469,6 +1470,7 @@ class GeometryProofLand(Scene):
|
||||||
colors = list(self.colors)
|
colors = list(self.colors)
|
||||||
random.shuffle(colors)
|
random.shuffle(colors)
|
||||||
word_outlines.set_color_by_gradient(*colors)
|
word_outlines.set_color_by_gradient(*colors)
|
||||||
|
word_outlines.set_stroke(width=5)
|
||||||
|
|
||||||
circles = VGroup()
|
circles = VGroup()
|
||||||
for letter in word:
|
for letter in word:
|
||||||
|
@ -1485,14 +1487,16 @@ class GeometryProofLand(Scene):
|
||||||
LaggedStart(MoveToTarget, circles),
|
LaggedStart(MoveToTarget, circles),
|
||||||
run_time=2
|
run_time=2
|
||||||
)
|
)
|
||||||
|
self.add(word_outlines, circles)
|
||||||
self.play(LaggedStart(
|
self.play(LaggedStart(
|
||||||
ShowCreationThenDestruction, word_outlines,
|
FadeIn, word_outlines,
|
||||||
run_time=4
|
run_time=3,
|
||||||
))
|
rate_func=there_and_back,
|
||||||
|
), Animation(circles))
|
||||||
self.wait()
|
self.wait()
|
||||||
|
|
||||||
def get_geometry_proof_land_word(self):
|
def get_geometry_proof_land_word(self):
|
||||||
word = TextMobject("Geometry proof land")
|
word = TextMobject(self.text)
|
||||||
word.rotate(-90 * DEGREES)
|
word.rotate(-90 * DEGREES)
|
||||||
word.scale(0.25)
|
word.scale(0.25)
|
||||||
word.shift(3 * RIGHT)
|
word.shift(3 * RIGHT)
|
||||||
|
@ -1502,6 +1506,7 @@ class GeometryProofLand(Scene):
|
||||||
word.center()
|
word.center()
|
||||||
word.to_edge(UP)
|
word.to_edge(UP)
|
||||||
word.set_color_by_gradient(*self.colors)
|
word.set_color_by_gradient(*self.colors)
|
||||||
|
word.set_background_stroke(width=0)
|
||||||
return word
|
return word
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ if __name__ == "__main__":
|
||||||
animated_name=name,
|
animated_name=name,
|
||||||
write_to_movie=True,
|
write_to_movie=True,
|
||||||
output_directory=os.path.join(
|
output_directory=os.path.join(
|
||||||
ANIMATIONS_DIR,
|
VIDEO_DIR,
|
||||||
"active_projects",
|
"active_projects",
|
||||||
"name_animations",
|
"name_animations",
|
||||||
),
|
),
|
||||||
|
|
|
@ -57,7 +57,7 @@ class Hand(ImageMobject):
|
||||||
def __init__(self, num, **kwargs):
|
def __init__(self, num, **kwargs):
|
||||||
Mobject2D.__init__(self, **kwargs)
|
Mobject2D.__init__(self, **kwargs)
|
||||||
path = os.path.join(
|
path = os.path.join(
|
||||||
ANIMATIONS_DIR, MOVIE_PREFIX, "images", "Hand%d.png"%num
|
VIDEO_DIR, MOVIE_PREFIX, "images", "Hand%d.png"%num
|
||||||
)
|
)
|
||||||
invert = False
|
invert = False
|
||||||
if self.read_in_cached_attrs(path, invert):
|
if self.read_in_cached_attrs(path, invert):
|
||||||
|
@ -83,14 +83,14 @@ class EdgeDetection(SceneFromVideo):
|
||||||
return "-".join([filename.split(".")[0], str(t1), str(t2)])
|
return "-".join([filename.split(".")[0], str(t1), str(t2)])
|
||||||
|
|
||||||
def construct(self, filename, t1, t2):
|
def construct(self, filename, t1, t2):
|
||||||
path = os.path.join(ANIMATIONS_DIR, filename)
|
path = os.path.join(VIDEO_DIR, filename)
|
||||||
SceneFromVideo.construct(self, path)
|
SceneFromVideo.construct(self, path)
|
||||||
self.apply_gaussian_blur()
|
self.apply_gaussian_blur()
|
||||||
self.apply_edge_detection(t1, t2)
|
self.apply_edge_detection(t1, t2)
|
||||||
|
|
||||||
class BufferedCounting(SceneFromVideo):
|
class BufferedCounting(SceneFromVideo):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
path = os.path.join(ANIMATIONS_DIR, "CountingInBinary.m4v")
|
path = os.path.join(VIDEO_DIR, "CountingInBinary.m4v")
|
||||||
time_range = (3, 42)
|
time_range = (3, 42)
|
||||||
SceneFromVideo.construct(self, path, time_range = time_range)
|
SceneFromVideo.construct(self, path, time_range = time_range)
|
||||||
self.buffer_pixels(spreads = (3, 2))
|
self.buffer_pixels(spreads = (3, 2))
|
||||||
|
@ -130,7 +130,7 @@ class ClearLeftSide(SceneFromVideo):
|
||||||
return scenename
|
return scenename
|
||||||
|
|
||||||
def construct(self, scenename):
|
def construct(self, scenename):
|
||||||
path = os.path.join(ANIMATIONS_DIR, MOVIE_PREFIX, scenename + ".mp4")
|
path = os.path.join(VIDEO_DIR, MOVIE_PREFIX, scenename + ".mp4")
|
||||||
SceneFromVideo.construct(self, path)
|
SceneFromVideo.construct(self, path)
|
||||||
self.set_color_region_over_time_range(
|
self.set_color_region_over_time_range(
|
||||||
Region(lambda x, y : x < -1, shape = self.shape)
|
Region(lambda x, y : x < -1, shape = self.shape)
|
||||||
|
@ -148,7 +148,7 @@ class DraggedPixels(SceneFromVideo):
|
||||||
return args[0]
|
return args[0]
|
||||||
|
|
||||||
def construct(self, video):
|
def construct(self, video):
|
||||||
path = os.path.join(ANIMATIONS_DIR, MOVIE_PREFIX, video+".mp4")
|
path = os.path.join(VIDEO_DIR, MOVIE_PREFIX, video+".mp4")
|
||||||
SceneFromVideo.construct(self, path)
|
SceneFromVideo.construct(self, path)
|
||||||
self.drag_pixels()
|
self.drag_pixels()
|
||||||
|
|
||||||
|
@ -165,11 +165,11 @@ class DraggedPixels(SceneFromVideo):
|
||||||
|
|
||||||
class SaveEachNumber(SceneFromVideo):
|
class SaveEachNumber(SceneFromVideo):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
path = os.path.join(ANIMATIONS_DIR, MOVIE_PREFIX, "ClearLeftSideBufferedCounting.mp4")
|
path = os.path.join(VIDEO_DIR, MOVIE_PREFIX, "ClearLeftSideBufferedCounting.mp4")
|
||||||
SceneFromVideo.construct(self, path)
|
SceneFromVideo.construct(self, path)
|
||||||
for count in COUNT_TO_FRAME_NUM:
|
for count in COUNT_TO_FRAME_NUM:
|
||||||
path = os.path.join(
|
path = os.path.join(
|
||||||
ANIMATIONS_DIR, MOVIE_PREFIX, "images",
|
VIDEO_DIR, MOVIE_PREFIX, "images",
|
||||||
"Hand%d.png"%count
|
"Hand%d.png"%count
|
||||||
)
|
)
|
||||||
Image.fromarray(self.frames[COUNT_TO_FRAME_NUM[count]]).save(path)
|
Image.fromarray(self.frames[COUNT_TO_FRAME_NUM[count]]).save(path)
|
||||||
|
@ -184,7 +184,7 @@ class ShowCounting(SceneFromVideo):
|
||||||
return filename
|
return filename
|
||||||
|
|
||||||
def construct(self, filename):
|
def construct(self, filename):
|
||||||
path = os.path.join(ANIMATIONS_DIR, MOVIE_PREFIX, filename + ".mp4")
|
path = os.path.join(VIDEO_DIR, MOVIE_PREFIX, filename + ".mp4")
|
||||||
SceneFromVideo.construct(self, path)
|
SceneFromVideo.construct(self, path)
|
||||||
total_time = len(self.frames)*self.frame_duration
|
total_time = len(self.frames)*self.frame_duration
|
||||||
for count in range(32):
|
for count in range(32):
|
||||||
|
@ -208,7 +208,7 @@ class ShowFrameNum(SceneFromVideo):
|
||||||
return filename
|
return filename
|
||||||
|
|
||||||
def construct(self, filename):
|
def construct(self, filename):
|
||||||
path = os.path.join(ANIMATIONS_DIR, MOVIE_PREFIX, filename+".mp4")
|
path = os.path.join(VIDEO_DIR, MOVIE_PREFIX, filename+".mp4")
|
||||||
SceneFromVideo.construct(self, path)
|
SceneFromVideo.construct(self, path)
|
||||||
for frame, count in zip(self.frames, it.count()):
|
for frame, count in zip(self.frames, it.count()):
|
||||||
print(count + "of" + len(self.frames))
|
print(count + "of" + len(self.frames))
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from big_ol_pile_of_manim_imports import *
|
from big_ol_pile_of_manim_imports import *
|
||||||
from active_projects.quaternions import *
|
from old_projects.quaternions import *
|
||||||
|
|
||||||
W_COLOR = YELLOW
|
W_COLOR = YELLOW
|
||||||
I_COLOR = GREEN
|
I_COLOR = GREEN
|
||||||
|
@ -148,6 +148,25 @@ class Gimbal(VGroup):
|
||||||
|
|
||||||
# Scenes
|
# Scenes
|
||||||
|
|
||||||
|
class ButFirst(TeacherStudentsScene):
|
||||||
|
def construct(self):
|
||||||
|
for student in self.students:
|
||||||
|
student.change("surprised")
|
||||||
|
|
||||||
|
self.teacher_says("But first!")
|
||||||
|
self.change_all_student_modes("happy")
|
||||||
|
self.play(RemovePiCreatureBubble(
|
||||||
|
self.teacher,
|
||||||
|
target_mode="raise_right_hand"
|
||||||
|
))
|
||||||
|
self.change_student_modes(
|
||||||
|
*["pondering"] * 3,
|
||||||
|
look_at_arg=self.screen,
|
||||||
|
)
|
||||||
|
self.play(self.teacher.look_at, self.screen)
|
||||||
|
self.wait(4)
|
||||||
|
|
||||||
|
|
||||||
class Introduction(QuaternionHistory):
|
class Introduction(QuaternionHistory):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"names_and_quotes": [
|
"names_and_quotes": [
|
||||||
|
@ -767,6 +786,16 @@ class EulerAnglesAndGimbal(ShowSeveralQuaternionRotations):
|
||||||
return line
|
return line
|
||||||
|
|
||||||
|
|
||||||
|
class InterpolationFail(Scene):
|
||||||
|
def construct(self):
|
||||||
|
words = TextMobject(
|
||||||
|
"Sometimes interpolating 3d\\\\"
|
||||||
|
"orientations is tricky..."
|
||||||
|
)
|
||||||
|
words.to_edge(UP)
|
||||||
|
self.add(words)
|
||||||
|
|
||||||
|
|
||||||
class QuaternionInterpolation(ShowSeveralQuaternionRotations):
|
class QuaternionInterpolation(ShowSeveralQuaternionRotations):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
self.add_q_tracker()
|
self.add_q_tracker()
|
||||||
|
@ -1354,3 +1383,27 @@ class ExpandOutFullProduct(TeacherStudentsScene):
|
||||||
look_at_arg=words
|
look_at_arg=words
|
||||||
)
|
)
|
||||||
self.wait(5)
|
self.wait(5)
|
||||||
|
|
||||||
|
|
||||||
|
class Link(Scene):
|
||||||
|
def construct(self):
|
||||||
|
word = TextMobject("eater.net/quaternions")
|
||||||
|
word.add_background_rectangle()
|
||||||
|
rect = SurroundingRectangle(word)
|
||||||
|
rect.set_color(BLUE)
|
||||||
|
arrow = Vector(UR, color=GREEN)
|
||||||
|
arrow.next_to(rect, UP)
|
||||||
|
arrow.align_to(rect, RIGHT)
|
||||||
|
short_arrow = arrow.copy().scale(0.8, about_edge=DL)
|
||||||
|
|
||||||
|
self.add(word)
|
||||||
|
self.play(
|
||||||
|
ShowCreation(rect),
|
||||||
|
GrowArrow(arrow),
|
||||||
|
)
|
||||||
|
for x in range(10):
|
||||||
|
self.play(Transform(
|
||||||
|
arrow, short_arrow,
|
||||||
|
rate_func=there_and_back,
|
||||||
|
run_time=2
|
||||||
|
))
|
|
@ -97,12 +97,6 @@ def stereo_project(mobject, axis=0, r=1, outer_r=10, **kwargs):
|
||||||
return mobject
|
return mobject
|
||||||
|
|
||||||
|
|
||||||
def updating_mobject_from_func(func):
|
|
||||||
mob = func()
|
|
||||||
mob.add_updater(lambda m: mob.become(func()))
|
|
||||||
return mob
|
|
||||||
|
|
||||||
|
|
||||||
class Linus(VGroup):
|
class Linus(VGroup):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"body_config": {
|
"body_config": {
|
1786
old_projects/turbulence.py
Normal file
1786
old_projects/turbulence.py
Normal file
File diff suppressed because it is too large
Load diff
|
@ -5219,19 +5219,21 @@ class Thumbnail(DistanceProductScene):
|
||||||
product[i-1:i+2]
|
product[i-1:i+2]
|
||||||
for i in frac_indices
|
for i in frac_indices
|
||||||
])
|
])
|
||||||
parts[::2].set_color(GREEN)
|
parts[::2].set_color(WHITE)
|
||||||
parts[1::2].set_color(BLUE)
|
parts[1::2].set_color(WHITE)
|
||||||
parts[-1].set_color(WHITE)
|
parts[-1].set_color(WHITE)
|
||||||
parts[-1].set_stroke(RED, 1)
|
parts[-1].set_background_stroke(color=YELLOW, width=3)
|
||||||
parts[-1].scale(1.5, about_edge=LEFT)
|
parts[-1].scale(1.5, about_edge=LEFT)
|
||||||
|
product[-4:].next_to(product[:-4], DOWN, MED_LARGE_BUFF)
|
||||||
|
product.scale(1.2).center().to_edge(UP)
|
||||||
|
|
||||||
new_proof = TextMobject("New proof")
|
# new_proof = TextMobject("New proof")
|
||||||
new_proof.scale(2.5)
|
# new_proof.scale(2.5)
|
||||||
new_proof.set_color(YELLOW)
|
# new_proof.set_color(YELLOW)
|
||||||
new_proof.set_stroke(RED, 0.75)
|
# new_proof.set_stroke(RED, 0.75)
|
||||||
new_proof.next_to(product, DOWN, MED_LARGE_BUFF)
|
# new_proof.next_to(product, DOWN, MED_LARGE_BUFF)
|
||||||
|
|
||||||
circle = self.circle = Circle(radius=8, color=RED)
|
circle = self.circle = Circle(radius=8, color=YELLOW)
|
||||||
circle.move_to(3 * DOWN, DOWN)
|
circle.move_to(3 * DOWN, DOWN)
|
||||||
bottom_dot = Dot(color=BLUE)
|
bottom_dot = Dot(color=BLUE)
|
||||||
bottom_dot.move_to(circle.get_bottom())
|
bottom_dot.move_to(circle.get_bottom())
|
||||||
|
@ -5260,7 +5262,7 @@ class Thumbnail(DistanceProductScene):
|
||||||
self.add(circle)
|
self.add(circle)
|
||||||
self.add(lights)
|
self.add(lights)
|
||||||
self.add(product)
|
self.add(product)
|
||||||
self.add(new_proof)
|
# self.add(new_proof)
|
||||||
self.add(bottom_dot, observer)
|
self.add(bottom_dot, observer)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -173,7 +173,7 @@ class Scene(Container):
|
||||||
###
|
###
|
||||||
|
|
||||||
def continual_update(self, dt):
|
def continual_update(self, dt):
|
||||||
for mobject in self.get_mobjects():
|
for mobject in self.get_mobject_family_members():
|
||||||
mobject.update(dt)
|
mobject.update(dt)
|
||||||
for continual_animation in self.continual_animations:
|
for continual_animation in self.continual_animations:
|
||||||
continual_animation.update(dt)
|
continual_animation.update(dt)
|
||||||
|
@ -194,7 +194,7 @@ class Scene(Container):
|
||||||
return True
|
return True
|
||||||
any_time_based_update = any([
|
any_time_based_update = any([
|
||||||
len(m.get_time_based_updaters()) > 0
|
len(m.get_time_based_updaters()) > 0
|
||||||
for m in self.get_mobjects()
|
for m in self.get_mobject_family_members()
|
||||||
])
|
])
|
||||||
if any_time_based_update:
|
if any_time_based_update:
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import inspect
|
import inspect
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from constants import ANIMATIONS_DIR
|
from constants import VIDEO_DIR
|
||||||
|
|
||||||
|
|
||||||
def add_extension_if_not_present(file_name, extension):
|
def add_extension_if_not_present(file_name, extension):
|
||||||
|
@ -28,7 +28,7 @@ def get_scene_output_directory(scene_class):
|
||||||
file_path = os.path.join(*sub_parts)
|
file_path = os.path.join(*sub_parts)
|
||||||
file_path = file_path.replace(".pyc", "")
|
file_path = file_path.replace(".pyc", "")
|
||||||
file_path = file_path.replace(".py", "")
|
file_path = file_path.replace(".py", "")
|
||||||
return guarantee_existance(os.path.join(ANIMATIONS_DIR, file_path))
|
return guarantee_existance(os.path.join(VIDEO_DIR, file_path))
|
||||||
|
|
||||||
|
|
||||||
def get_movie_output_directory(scene_class, camera_config, frame_duration):
|
def get_movie_output_directory(scene_class, camera_config, frame_duration):
|
||||||
|
|
|
@ -170,7 +170,7 @@ def get_unit_normal(v1, v2):
|
||||||
|
|
||||||
|
|
||||||
def compass_directions(n=4, start_vect=RIGHT):
|
def compass_directions(n=4, start_vect=RIGHT):
|
||||||
angle = 2 * np.pi / n
|
angle = TAU / n
|
||||||
return np.array([
|
return np.array([
|
||||||
rotate_vector(start_vect, k * angle)
|
rotate_vector(start_vect, k * angle)
|
||||||
for k in range(n)
|
for k in range(n)
|
||||||
|
|
Loading…
Add table
Reference in a new issue