198 lines
6.6 KiB
Python
198 lines
6.6 KiB
Python
from manim import *
|
|
import numpy as np
|
|
|
|
class EpicAdReportCardV2(ThreeDScene):
|
|
def construct(self):
|
|
self.camera.background_color = "#0a0a1a"
|
|
self.set_camera_orientation(phi=0 * DEGREES, theta=-90 * DEGREES, zoom=0.8)
|
|
|
|
# ============ ACT 1: EPIC INTRO WITH 3D SHAPES ============
|
|
|
|
# Create a grid of cubes that form the backdrop
|
|
cubes = VGroup()
|
|
for x in range(-4, 5):
|
|
for y in range(-3, 4):
|
|
cube = Cube(side_length=0.3, fill_color=BLUE_E, fill_opacity=0.15, stroke_width=0.5, stroke_color=BLUE_D)
|
|
cube.shift(x * 0.6 * RIGHT + y * 0.6 * UP)
|
|
cubes.add(cube)
|
|
|
|
# Scatter cubes in z-space initially
|
|
for i, cube in enumerate(cubes):
|
|
cube.shift(OUT * np.random.uniform(-8, -3))
|
|
cube.set_opacity(0)
|
|
|
|
# Fly cubes into grid formation
|
|
self.play(
|
|
*[cube.animate.shift(IN * np.random.uniform(3, 8)).set_opacity(0.15) for cube in cubes],
|
|
run_time=2,
|
|
rate_func=rush_from
|
|
)
|
|
|
|
# ============ ACT 2: TITLE SLAM ============
|
|
|
|
# Title - positioned flat facing camera
|
|
title_line1 = Text("ADVERTISING", font_size=84, color=WHITE, weight=BOLD)
|
|
title_line2 = Text("REPORT CARD", font_size=84, color=GOLD, weight=BOLD)
|
|
title_line2.next_to(title_line1, DOWN, buff=0.2)
|
|
title_group = VGroup(title_line1, title_line2)
|
|
title_group.shift(UP * 0.5)
|
|
|
|
# Scale up from nothing with flash
|
|
title_group.scale(0.01)
|
|
|
|
flash = Circle(radius=0.1, color=GOLD, fill_opacity=1, stroke_width=0)
|
|
|
|
self.play(
|
|
title_group.animate.scale(100),
|
|
flash.animate.scale(80).set_opacity(0),
|
|
run_time=1,
|
|
rate_func=rush_from
|
|
)
|
|
self.remove(flash)
|
|
|
|
# Pulse effect
|
|
self.play(
|
|
title_group.animate.scale(1.15),
|
|
rate_func=there_and_back,
|
|
run_time=0.4
|
|
)
|
|
self.play(
|
|
title_group.animate.scale(1.08),
|
|
rate_func=there_and_back,
|
|
run_time=0.3
|
|
)
|
|
|
|
self.wait(0.5)
|
|
|
|
# ============ ACT 3: CAMERA TILT TO REVEAL 3D DEPTH ============
|
|
|
|
# Now tilt the camera to show depth
|
|
self.move_camera(phi=55 * DEGREES, theta=-70 * DEGREES, run_time=2, added_anims=[
|
|
title_group.animate.shift(UP * 1.5),
|
|
cubes.animate.set_opacity(0.08)
|
|
])
|
|
|
|
# ============ ACT 4: 3D RATING BARS ============
|
|
|
|
labels_data = [
|
|
("IMPACT", RED_C, 0.95),
|
|
("CLARITY", BLUE_C, 0.88),
|
|
("ROI", GREEN_C, 0.92),
|
|
("CREATIVITY", YELLOW_C, 0.97),
|
|
("STRATEGY", PURPLE_C, 0.90),
|
|
]
|
|
|
|
all_bars = VGroup()
|
|
|
|
for i, (label, color, rating) in enumerate(labels_data):
|
|
# 3D bar using a rectangular prism
|
|
bar_width = 5 * rating
|
|
bar = Prism(dimensions=[bar_width, 0.35, 0.25], fill_color=color, fill_opacity=0.85, stroke_width=1, stroke_color=WHITE)
|
|
bar.shift(LEFT * (5 - bar_width) / 2) # align left
|
|
|
|
# Background bar
|
|
bg_bar = Prism(dimensions=[5, 0.35, 0.25], fill_color=GRAY, fill_opacity=0.2, stroke_width=0.5, stroke_color=GRAY)
|
|
|
|
# Label
|
|
label_text = Text(label, font_size=22, color=WHITE, weight=BOLD)
|
|
label_text.next_to(bg_bar, LEFT, buff=0.4)
|
|
|
|
# Percentage
|
|
pct = Text(f"{int(rating*100)}%", font_size=28, color=color, weight=BOLD)
|
|
pct.next_to(bg_bar, RIGHT, buff=0.4)
|
|
|
|
row = VGroup(label_text, bg_bar, bar, pct)
|
|
row.shift(DOWN * i * 0.65)
|
|
all_bars.add(row)
|
|
|
|
all_bars.shift(DOWN * 0.8)
|
|
|
|
# Animate each bar sliding in from the left
|
|
for i, row in enumerate(all_bars):
|
|
label_text, bg_bar, bar, pct = row
|
|
|
|
# Start positions
|
|
bar.save_state()
|
|
bar_target = bar.copy()
|
|
bar.stretch(0.01, 0) # squish to nothing
|
|
bar.align_to(bg_bar, LEFT)
|
|
|
|
self.play(
|
|
FadeIn(label_text, shift=RIGHT * 0.5),
|
|
FadeIn(bg_bar),
|
|
run_time=0.2
|
|
)
|
|
self.play(
|
|
bar.animate.restore(),
|
|
FadeIn(pct, shift=LEFT * 0.3),
|
|
run_time=0.4,
|
|
rate_func=rush_from
|
|
)
|
|
|
|
self.wait(0.5)
|
|
|
|
# ============ ACT 5: EPIC CAMERA ORBIT ============
|
|
|
|
self.begin_ambient_camera_rotation(rate=0.15)
|
|
self.wait(4)
|
|
self.stop_ambient_camera_rotation()
|
|
|
|
# ============ ACT 6: GRAND FINALE ============
|
|
|
|
# Return camera to front
|
|
self.move_camera(phi=0 * DEGREES, theta=-90 * DEGREES, run_time=1.5)
|
|
|
|
# Clear bars with style
|
|
self.play(
|
|
*[row.animate.shift(RIGHT * 15) for row in all_bars],
|
|
title_group.animate.shift(UP * 8),
|
|
cubes.animate.set_opacity(0),
|
|
run_time=1,
|
|
rate_func=rush_into
|
|
)
|
|
self.remove(all_bars, title_group, cubes)
|
|
|
|
# Final epic text
|
|
final1 = Text("EXCELLENCE", font_size=120, color=GOLD, weight=BOLD)
|
|
final2 = Text("DELIVERED", font_size=120, color=WHITE, weight=BOLD)
|
|
final2.next_to(final1, DOWN, buff=0.15)
|
|
finale = VGroup(final1, final2)
|
|
|
|
# Star burst effect
|
|
lines = VGroup()
|
|
for angle in range(0, 360, 15):
|
|
line = Line(ORIGIN, RIGHT * 8, color=GOLD, stroke_width=2, stroke_opacity=0.6)
|
|
line.rotate(angle * DEGREES)
|
|
lines.add(line)
|
|
|
|
lines.scale(0.01)
|
|
finale.scale(0.01)
|
|
|
|
self.play(
|
|
finale.animate.scale(100),
|
|
lines.animate.scale(100).set_opacity(0),
|
|
run_time=1.2,
|
|
rate_func=rush_from
|
|
)
|
|
self.remove(lines)
|
|
|
|
# Final pulse
|
|
self.play(
|
|
finale.animate.scale(1.1),
|
|
rate_func=there_and_back,
|
|
run_time=0.5
|
|
)
|
|
|
|
# Add a subtle glow ring
|
|
ring = Annulus(inner_radius=2.5, outer_radius=3, color=GOLD, fill_opacity=0.3, stroke_width=0)
|
|
ring.shift(DOWN * 0.3)
|
|
|
|
self.play(
|
|
FadeIn(ring),
|
|
ring.animate.scale(1.5).set_opacity(0),
|
|
run_time=1.5
|
|
)
|
|
self.remove(ring)
|
|
|
|
self.wait(1.5)
|