clawdbot-workspace/oskv_logo_3d.py

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
)