|
| 1 | +""" |
| 2 | +Scene 2: IntroScene |
| 3 | +Documents (PDF, DOCX, PPTX, XLSX) flow into docproc and produce clean.md. |
| 4 | +Gruvbox dark theme. |
| 5 | +""" |
| 6 | + |
| 7 | +from manim import * |
| 8 | + |
| 9 | +# ── Gruvbox Dark Palette ───────────────────────────────────────────────────── |
| 10 | +BG_COLOR = "#282828" # gruvbox bg |
| 11 | +FG = "#ebdbb2" # gruvbox fg |
| 12 | +ACCENT = "#83a598" # gruvbox bright-blue (docproc / engine) |
| 13 | +GRAY = "#928374" # gruvbox gray |
| 14 | +GREEN = "#b8bb26" # gruvbox bright-green (output) |
| 15 | +ORANGE = "#fe8019" # gruvbox bright-orange |
| 16 | +YELLOW = "#fabd2f" # gruvbox bright-yellow |
| 17 | +RED_G = "#fb4934" # gruvbox bright-red |
| 18 | +BLUE_G = "#83a598" # gruvbox bright-blue |
| 19 | +DARK_BG = "#1d2021" # gruvbox bg-hard |
| 20 | + |
| 21 | + |
| 22 | +# Per-format chip colors (all Gruvbox hues) |
| 23 | +DOC_COLORS = { |
| 24 | + "PDF": "#fb4934", # bright-red |
| 25 | + "DOCX": "#83a598", # bright-blue |
| 26 | + "PPTX": "#fe8019", # bright-orange |
| 27 | + "XLSX": "#b8bb26", # bright-green |
| 28 | +} |
| 29 | + |
| 30 | + |
| 31 | +class IntroScene(Scene): |
| 32 | + def construct(self): |
| 33 | + self.camera.background_color = BG_COLOR |
| 34 | + |
| 35 | + # ── Document chips (left column) ───────────────────────────────────── |
| 36 | + doc_nodes = VGroup( |
| 37 | + *[self._doc_chip(lbl, col) for lbl, col in DOC_COLORS.items()] |
| 38 | + ).arrange(DOWN, buff=0.35) |
| 39 | + doc_nodes.to_edge(LEFT, buff=1.5) |
| 40 | + |
| 41 | + # ── docproc engine box (centre) ────────────────────────────────────── |
| 42 | + engine_rect = RoundedRectangle( |
| 43 | + corner_radius=0.15, width=2.6, height=1.1, |
| 44 | + fill_color=DARK_BG, fill_opacity=1, |
| 45 | + stroke_color=ACCENT, stroke_width=2.5, |
| 46 | + ) |
| 47 | + engine_label = Text("docproc", font="Courier New", font_size=26, |
| 48 | + color=ACCENT, weight=BOLD) |
| 49 | + engine_label.move_to(engine_rect.get_center()) |
| 50 | + engine = VGroup(engine_rect, engine_label).move_to(ORIGIN) |
| 51 | + |
| 52 | + # ── clean.md output box (right) ────────────────────────────────────── |
| 53 | + out_rect = RoundedRectangle( |
| 54 | + corner_radius=0.15, width=2.6, height=1.1, |
| 55 | + fill_color=DARK_BG, fill_opacity=1, |
| 56 | + stroke_color=GREEN, stroke_width=2.5, |
| 57 | + ) |
| 58 | + out_label = Text("clean.md", font="Courier New", font_size=24, |
| 59 | + color=GREEN, weight=BOLD) |
| 60 | + out_label.move_to(out_rect.get_center()) |
| 61 | + output = VGroup(out_rect, out_label).to_edge(RIGHT, buff=1.5) |
| 62 | + |
| 63 | + # ── Subtitle ───────────────────────────────────────────────────────── |
| 64 | + subtitle = Text( |
| 65 | + "Document → Markdown → AI", |
| 66 | + font="Helvetica", font_size=26, color=GRAY |
| 67 | + ).to_edge(DOWN, buff=0.55) |
| 68 | + |
| 69 | + # Subtle glow behind engine |
| 70 | + pulse = Circle(radius=1.4, stroke_width=0) |
| 71 | + pulse.set_fill(ACCENT, opacity=0.07) |
| 72 | + pulse.move_to(engine.get_center()) |
| 73 | + |
| 74 | + # ── Animations ────────────────────────────────────────────────────── |
| 75 | + self.play(FadeIn(pulse, scale=0.6), FadeIn(engine, scale=0.85), run_time=0.7) |
| 76 | + |
| 77 | + self.play( |
| 78 | + LaggedStart( |
| 79 | + *[FadeIn(d, shift=RIGHT * 0.25) for d in doc_nodes], |
| 80 | + lag_ratio=0.2, |
| 81 | + ), |
| 82 | + run_time=0.8, |
| 83 | + ) |
| 84 | + |
| 85 | + arrows_in = [ |
| 86 | + Arrow(d.get_right(), engine_rect.get_left(), buff=0.12, |
| 87 | + color=d[1].color, stroke_width=2, |
| 88 | + max_tip_length_to_length_ratio=0.15) |
| 89 | + for d in doc_nodes |
| 90 | + ] |
| 91 | + self.play( |
| 92 | + LaggedStart(*[GrowArrow(a) for a in arrows_in], lag_ratio=0.15), |
| 93 | + run_time=1.0, |
| 94 | + ) |
| 95 | + |
| 96 | + # Pulse engine |
| 97 | + self.play( |
| 98 | + pulse.animate.scale(1.15).set_fill(opacity=0.14), |
| 99 | + engine_label.animate.set_color(FG), |
| 100 | + run_time=0.4, |
| 101 | + ) |
| 102 | + self.play( |
| 103 | + pulse.animate.scale(1 / 1.15).set_fill(opacity=0.07), |
| 104 | + engine_label.animate.set_color(ACCENT), |
| 105 | + run_time=0.4, |
| 106 | + ) |
| 107 | + |
| 108 | + arrow_out = Arrow( |
| 109 | + engine_rect.get_right(), out_rect.get_left(), |
| 110 | + buff=0.12, color=GREEN, stroke_width=2.5, |
| 111 | + max_tip_length_to_length_ratio=0.15 |
| 112 | + ) |
| 113 | + self.play(GrowArrow(arrow_out), FadeIn(output, shift=RIGHT * 0.3), run_time=0.8) |
| 114 | + self.play(Write(subtitle), run_time=0.7) |
| 115 | + self.wait(1.2) |
| 116 | + |
| 117 | + @staticmethod |
| 118 | + def _doc_chip(label: str, color: str) -> VGroup: |
| 119 | + rect = RoundedRectangle( |
| 120 | + corner_radius=0.1, width=1.5, height=0.52, |
| 121 | + fill_color=DARK_BG, fill_opacity=1, |
| 122 | + stroke_color=color, stroke_width=1.8, |
| 123 | + ) |
| 124 | + text = Text(label, font="Courier New", font_size=20, color=color, weight=BOLD) |
| 125 | + text.move_to(rect.get_center()) |
| 126 | + return VGroup(rect, text) |
0 commit comments