209 lines
6.3 KiB
Python
209 lines
6.3 KiB
Python
from manim import *
|
|
import numpy as np
|
|
import random
|
|
|
|
class OSKVLogoReveal(Scene):
|
|
def construct(self):
|
|
self.camera.background_color = "#050510"
|
|
|
|
# === PART 1: Particles converge to center ===
|
|
particles = VGroup()
|
|
for _ in range(100):
|
|
dot = Dot(
|
|
point=np.array([
|
|
random.uniform(-8, 8),
|
|
random.uniform(-5, 5),
|
|
0
|
|
]),
|
|
radius=random.uniform(0.02, 0.06),
|
|
color=random.choice([WHITE, BLUE_B, PURPLE_B])
|
|
)
|
|
dot.set_opacity(random.uniform(0.3, 0.8))
|
|
particles.add(dot)
|
|
|
|
self.add(particles)
|
|
|
|
# Converge to center
|
|
self.play(
|
|
*[dot.animate.move_to(
|
|
np.array([random.uniform(-0.5, 0.5), random.uniform(-0.2, 0.2), 0])
|
|
).set_opacity(0) for dot in particles],
|
|
run_time=1.5,
|
|
rate_func=rush_into
|
|
)
|
|
self.remove(particles)
|
|
|
|
# === PART 2: Flash and logo image appears ===
|
|
# White flash
|
|
flash = Rectangle(
|
|
width=16, height=10,
|
|
fill_color=WHITE,
|
|
fill_opacity=0.8,
|
|
stroke_width=0
|
|
)
|
|
self.play(FadeIn(flash, run_time=0.1))
|
|
self.play(FadeOut(flash, run_time=0.3))
|
|
|
|
# Load the actual OSKV logo
|
|
logo = ImageMobject("/Users/jakeshore/.clawdbot/workspace/oskv_logo.png")
|
|
logo.scale_to_fit_width(8)
|
|
logo.set_opacity(0)
|
|
self.add(logo)
|
|
|
|
# Fade in the logo
|
|
self.play(logo.animate.set_opacity(1), run_time=1)
|
|
|
|
# === PART 3: Geometric frame builds around logo ===
|
|
# Inner frame
|
|
inner_frame = RoundedRectangle(
|
|
width=9, height=3,
|
|
corner_radius=0.3,
|
|
color=WHITE,
|
|
stroke_width=1.5
|
|
)
|
|
inner_frame.set_stroke(opacity=0.5)
|
|
|
|
# Outer frame
|
|
outer_frame = RoundedRectangle(
|
|
width=10, height=3.8,
|
|
corner_radius=0.4,
|
|
color=BLUE_B,
|
|
stroke_width=1
|
|
)
|
|
outer_frame.set_stroke(opacity=0.3)
|
|
|
|
self.play(
|
|
Create(inner_frame),
|
|
run_time=0.8
|
|
)
|
|
self.play(
|
|
Create(outer_frame),
|
|
run_time=0.6
|
|
)
|
|
|
|
# === PART 4: Orbiting dots ===
|
|
orbit_dots = VGroup()
|
|
orbit_trails = VGroup()
|
|
|
|
n_orbits = 6
|
|
for i in range(n_orbits):
|
|
dot = Dot(radius=0.06, color=[BLUE, PURPLE, TEAL, WHITE, BLUE_B, PURPLE_B][i])
|
|
dot.set_glow_factor(1.5)
|
|
|
|
angle = i * TAU / n_orbits
|
|
radius_x = 5.5
|
|
radius_y = 2.2
|
|
dot.move_to([
|
|
radius_x * np.cos(angle),
|
|
radius_y * np.sin(angle),
|
|
0
|
|
])
|
|
orbit_dots.add(dot)
|
|
|
|
self.play(
|
|
LaggedStart(*[GrowFromCenter(d) for d in orbit_dots], lag_ratio=0.1),
|
|
run_time=0.6
|
|
)
|
|
|
|
# Orbit around the logo
|
|
orbit_anims = []
|
|
for i, dot in enumerate(orbit_dots):
|
|
orbit_anims.append(
|
|
Rotate(dot, TAU * 0.4, about_point=ORIGIN)
|
|
)
|
|
|
|
self.play(*orbit_anims, run_time=2, rate_func=smooth)
|
|
|
|
# === PART 5: Pulse effect ===
|
|
# Scale pulse
|
|
logo_group = VGroup(inner_frame, outer_frame)
|
|
|
|
self.play(
|
|
logo.animate.scale(1.08),
|
|
logo_group.animate.scale(1.08),
|
|
*[dot.animate.scale(1.5).set_opacity(0.5) for dot in orbit_dots],
|
|
rate_func=there_and_back,
|
|
run_time=0.6
|
|
)
|
|
|
|
# === PART 6: Expanding ring waves ===
|
|
for _ in range(2):
|
|
ring = Circle(radius=0.5, color=BLUE_B, stroke_width=3)
|
|
ring.set_stroke(opacity=0.8)
|
|
self.add(ring)
|
|
self.play(
|
|
ring.animate.scale(12).set_stroke(opacity=0),
|
|
run_time=0.8,
|
|
rate_func=rush_from
|
|
)
|
|
self.remove(ring)
|
|
|
|
# === PART 7: Corner accents ===
|
|
corners = VGroup()
|
|
corner_positions = [
|
|
(UP + LEFT, 0), (UP + RIGHT, -PI/2),
|
|
(DOWN + RIGHT, PI), (DOWN + LEFT, PI/2)
|
|
]
|
|
|
|
for pos, rot in corner_positions:
|
|
corner = VGroup(
|
|
Line(ORIGIN, RIGHT * 0.5, color=TEAL, stroke_width=2),
|
|
Line(ORIGIN, UP * 0.5, color=TEAL, stroke_width=2)
|
|
)
|
|
corner.rotate(rot)
|
|
corner.move_to(pos * np.array([4.8, 1.8, 0]))
|
|
corners.add(corner)
|
|
|
|
self.play(
|
|
LaggedStart(*[Create(c) for c in corners], lag_ratio=0.1),
|
|
run_time=0.6
|
|
)
|
|
|
|
# === PART 8: Ambient floating particles ===
|
|
ambient = VGroup()
|
|
for _ in range(40):
|
|
p = Dot(
|
|
radius=random.uniform(0.01, 0.04),
|
|
color=random.choice([WHITE, BLUE_B, PURPLE_B, TEAL])
|
|
)
|
|
p.set_opacity(random.uniform(0.15, 0.4))
|
|
p.move_to([random.uniform(-7, 7), random.uniform(-4, 4), 0])
|
|
ambient.add(p)
|
|
|
|
self.play(FadeIn(ambient), run_time=0.3)
|
|
|
|
# Gentle upward drift
|
|
drift_anims = [
|
|
p.animate.shift(UP * random.uniform(0.3, 0.8)).set_opacity(0)
|
|
for p in ambient
|
|
]
|
|
|
|
# Second pulse while particles drift
|
|
self.play(
|
|
*drift_anims,
|
|
logo.animate.scale(1.05),
|
|
logo_group.animate.scale(1.05),
|
|
rate_func=there_and_back,
|
|
run_time=2
|
|
)
|
|
|
|
# === PART 9: Final hold with slow orbit ===
|
|
final_orbit = [
|
|
Rotate(dot, TAU * 0.15, about_point=ORIGIN)
|
|
for dot in orbit_dots
|
|
]
|
|
|
|
self.play(*final_orbit, run_time=2, rate_func=linear)
|
|
|
|
# Hold
|
|
self.wait(1)
|
|
|
|
# === PART 10: Fade to black ===
|
|
everything = VGroup(logo_group, orbit_dots, corners)
|
|
self.play(
|
|
FadeOut(everything),
|
|
logo.animate.set_opacity(0),
|
|
FadeOut(ambient),
|
|
run_time=1.5
|
|
)
|