251 lines
8.0 KiB
Python
251 lines
8.0 KiB
Python
from manim import *
|
|
import numpy as np
|
|
import random
|
|
|
|
class OSKVLogo3D(ThreeDScene):
|
|
def construct(self):
|
|
self.camera.background_color = "#050510"
|
|
|
|
# === PART 1: 3D Camera Sweep with Rotating Geometry ===
|
|
self.set_camera_orientation(phi=70 * DEGREES, theta=-45 * DEGREES)
|
|
|
|
# Wireframe sphere
|
|
sphere_wireframe = Surface(
|
|
lambda u, v: np.array([
|
|
2 * np.cos(u) * np.sin(v),
|
|
2 * np.sin(u) * np.sin(v),
|
|
2 * np.cos(v)
|
|
]),
|
|
u_range=[0, TAU],
|
|
v_range=[0, PI],
|
|
resolution=(12, 6),
|
|
fill_opacity=0.05,
|
|
stroke_width=1,
|
|
stroke_color=BLUE_B,
|
|
stroke_opacity=0.6
|
|
)
|
|
|
|
# Inner sphere
|
|
inner_sphere = Surface(
|
|
lambda u, v: np.array([
|
|
0.8 * np.cos(u) * np.sin(v),
|
|
0.8 * np.sin(u) * np.sin(v),
|
|
0.8 * np.cos(v)
|
|
]),
|
|
u_range=[0, TAU],
|
|
v_range=[0, PI],
|
|
resolution=(8, 4),
|
|
fill_opacity=0.1,
|
|
fill_color=PURPLE,
|
|
stroke_width=0.5,
|
|
stroke_color=PURPLE_B,
|
|
stroke_opacity=0.4
|
|
)
|
|
|
|
# Torus rings
|
|
torus = Surface(
|
|
lambda u, v: np.array([
|
|
(2.5 + 0.12 * np.cos(v)) * np.cos(u),
|
|
(2.5 + 0.12 * np.cos(v)) * np.sin(u),
|
|
0.12 * np.sin(v)
|
|
]),
|
|
u_range=[0, TAU],
|
|
v_range=[0, TAU],
|
|
resolution=(24, 6),
|
|
fill_opacity=0.3,
|
|
fill_color=TEAL,
|
|
stroke_width=0.5,
|
|
stroke_color=TEAL,
|
|
stroke_opacity=0.5
|
|
)
|
|
|
|
torus2 = Surface(
|
|
lambda u, v: np.array([
|
|
0.12 * np.sin(v),
|
|
(2.5 + 0.12 * np.cos(v)) * np.cos(u),
|
|
(2.5 + 0.12 * np.cos(v)) * np.sin(u),
|
|
]),
|
|
u_range=[0, TAU],
|
|
v_range=[0, TAU],
|
|
resolution=(24, 6),
|
|
fill_opacity=0.3,
|
|
fill_color=BLUE,
|
|
stroke_width=0.5,
|
|
stroke_color=BLUE,
|
|
stroke_opacity=0.5
|
|
)
|
|
|
|
# 3D floating dots
|
|
dots_3d = VGroup()
|
|
for _ in range(40):
|
|
r = random.uniform(3, 5)
|
|
theta_r = random.uniform(0, TAU)
|
|
phi_r = random.uniform(0, PI)
|
|
dot = Dot3D(
|
|
point=np.array([
|
|
r * np.sin(phi_r) * np.cos(theta_r),
|
|
r * np.sin(phi_r) * np.sin(theta_r),
|
|
r * np.cos(phi_r)
|
|
]),
|
|
radius=random.uniform(0.03, 0.07),
|
|
color=random.choice([BLUE, PURPLE, TEAL, WHITE])
|
|
)
|
|
dot.set_opacity(random.uniform(0.3, 0.7))
|
|
dots_3d.add(dot)
|
|
|
|
# Build scene
|
|
self.play(Create(sphere_wireframe), run_time=1.5)
|
|
self.play(
|
|
Create(inner_sphere),
|
|
FadeIn(dots_3d),
|
|
run_time=1
|
|
)
|
|
self.play(Create(torus), Create(torus2), run_time=1.5)
|
|
self.move_camera(theta=-20 * DEGREES, phi=60 * DEGREES, run_time=1)
|
|
|
|
# Rotate everything
|
|
all_3d = VGroup(sphere_wireframe, inner_sphere, torus, torus2, dots_3d)
|
|
|
|
self.play(Rotate(all_3d, PI/3, axis=UP), run_time=1.5)
|
|
self.move_camera(theta=30 * DEGREES, phi=50 * DEGREES, run_time=1)
|
|
|
|
# === PART 2: Collapse to 2D ===
|
|
self.play(all_3d.animate.scale(0.3).set_opacity(0.15), run_time=1)
|
|
self.move_camera(theta=0, phi=0, run_time=0.8)
|
|
|
|
# Flash
|
|
flash = Rectangle(width=20, height=12, fill_color=WHITE, fill_opacity=0.7, stroke_width=0)
|
|
self.play(FadeIn(flash, run_time=0.15))
|
|
self.play(FadeOut(flash, run_time=0.3), FadeOut(all_3d, run_time=0.3))
|
|
|
|
# === PART 3: Logo reveal ===
|
|
# Since ImageMobject can be tricky in 3D, use it after camera is face-on
|
|
logo = ImageMobject("/Users/jakeshore/.clawdbot/workspace/oskv_logo.png")
|
|
logo.scale_to_fit_width(7)
|
|
|
|
self.play(FadeIn(logo), run_time=0.8)
|
|
|
|
# === PART 4: 3D cube wireframe around logo ===
|
|
cube_size = 4.5
|
|
vertices = [
|
|
np.array([x, y, z]) * cube_size / 2
|
|
for x in [-1, 1] for y in [-1, 1] for z in [-1, 1]
|
|
]
|
|
edges_idx = [
|
|
(0,1),(0,2),(0,4),(1,3),(1,5),(2,3),
|
|
(2,6),(3,7),(4,5),(4,6),(5,7),(6,7)
|
|
]
|
|
|
|
cube_edges = VGroup()
|
|
for i, j in edges_idx:
|
|
line = Line3D(start=vertices[i], end=vertices[j], thickness=0.01, color=BLUE_B)
|
|
line.set_opacity(0.4)
|
|
cube_edges.add(line)
|
|
|
|
vertex_dots = VGroup()
|
|
for v in vertices:
|
|
dot = Dot3D(point=v, radius=0.06, color=TEAL)
|
|
dot.set_opacity(0.7)
|
|
vertex_dots.add(dot)
|
|
|
|
cube_group = VGroup(cube_edges, vertex_dots)
|
|
|
|
self.play(
|
|
LaggedStart(*[Create(e) for e in cube_edges], lag_ratio=0.05),
|
|
LaggedStart(*[GrowFromCenter(d) for d in vertex_dots], lag_ratio=0.05),
|
|
run_time=1.5
|
|
)
|
|
|
|
# Rotate cube
|
|
self.play(
|
|
Rotate(cube_group, PI/4, axis=UP + RIGHT * 0.5),
|
|
run_time=2
|
|
)
|
|
|
|
# === PART 5: Orbiting parametric paths ===
|
|
orbit_ring = ParametricFunction(
|
|
lambda t: np.array([5 * np.cos(t), 1.5 * np.sin(t), 0.8 * np.sin(t)]),
|
|
t_range=[0, TAU],
|
|
color=PURPLE,
|
|
stroke_width=2,
|
|
stroke_opacity=0.6
|
|
)
|
|
orbit_ring2 = ParametricFunction(
|
|
lambda t: np.array([5 * np.cos(t), -0.8 * np.sin(t), 1.5 * np.sin(t)]),
|
|
t_range=[0, TAU],
|
|
color=TEAL,
|
|
stroke_width=2,
|
|
stroke_opacity=0.5
|
|
)
|
|
|
|
self.play(Create(orbit_ring), Create(orbit_ring2), run_time=1)
|
|
|
|
# Energy dots
|
|
energy_dots = VGroup()
|
|
for i in range(4):
|
|
t = i * TAU / 4
|
|
dot = Dot3D(
|
|
point=np.array([5*np.cos(t), 1.5*np.sin(t), 0.8*np.sin(t)]),
|
|
radius=0.08, color=PURPLE
|
|
)
|
|
energy_dots.add(dot)
|
|
|
|
energy_dots2 = VGroup()
|
|
for i in range(4):
|
|
t = i * TAU / 4
|
|
dot = Dot3D(
|
|
point=np.array([5*np.cos(t), -0.8*np.sin(t), 1.5*np.sin(t)]),
|
|
radius=0.08, color=TEAL
|
|
)
|
|
energy_dots2.add(dot)
|
|
|
|
self.play(FadeIn(energy_dots), FadeIn(energy_dots2), run_time=0.3)
|
|
|
|
# Rotate energy + cube
|
|
self.play(
|
|
Rotate(energy_dots, PI/2, about_point=ORIGIN, axis=OUT),
|
|
Rotate(energy_dots2, PI/2, about_point=ORIGIN, axis=UP),
|
|
Rotate(cube_group, PI/6, axis=UP),
|
|
run_time=1.5
|
|
)
|
|
|
|
# === PART 6: Pulse ===
|
|
self.play(
|
|
cube_group.animate.scale(1.1),
|
|
rate_func=there_and_back,
|
|
run_time=0.7
|
|
)
|
|
|
|
# Ring wave
|
|
ring3d = ParametricFunction(
|
|
lambda t: np.array([0.5*np.cos(t), 0.5*np.sin(t), 0]),
|
|
t_range=[0, TAU],
|
|
color=WHITE,
|
|
stroke_width=3,
|
|
stroke_opacity=0.8
|
|
)
|
|
self.add(ring3d)
|
|
self.play(
|
|
ring3d.animate.scale(15).set_stroke(opacity=0),
|
|
run_time=0.8
|
|
)
|
|
self.remove(ring3d)
|
|
|
|
# Final slow rotation
|
|
self.play(
|
|
Rotate(cube_group, PI/8, axis=UP + OUT * 0.3),
|
|
Rotate(energy_dots, PI/4, about_point=ORIGIN, axis=OUT),
|
|
Rotate(energy_dots2, PI/4, about_point=ORIGIN, axis=UP),
|
|
run_time=2
|
|
)
|
|
|
|
self.wait(1)
|
|
|
|
# Fade out
|
|
everything = VGroup(cube_group, orbit_ring, orbit_ring2, energy_dots, energy_dots2)
|
|
self.play(
|
|
FadeOut(everything),
|
|
FadeOut(logo),
|
|
run_time=1.5
|
|
)
|