""" Lyra Intro - Partie 4 : Activation finale Concept : 6 modules s'allument un par un, envoient un flux vers le centre, LYRA emerge de la convergence de tous les flux. Rendu : manim -pqh part4_activation.py LyraActivation """ import numpy as np from manim import * class LyraActivation(Scene): def construct(self): np.random.seed(7) BLUE = ManimColor("#00D4FF") BLUE2 = ManimColor("#005577") WHITE = ManimColor("#E8F4FF") DARK = ManimColor("#000D1A") DIM = ManimColor("#041520") GREEN = ManimColor("#00FF99") GREY = ManimColor("#1A3040") self.camera.background_color = DARK # ===================================================================== # CONSTRUCTION : 6 modules en cercle autour du centre # ===================================================================== MODULES = [ ("STT", "Whisper", GREEN), ("LLM", "Ollama", BLUE), ("RAG", "ChromaDB", WHITE), ("MCP", "Fedora/TV/Hue", GREEN), ("TTS", "Piper", BLUE), ("TRACKING", "API :8765", WHITE), ] n = len(MODULES) radius = 2.8 angles = [PI / 2 + i * TAU / n for i in range(n)] # commence en haut positions = [radius * np.array([np.cos(a), np.sin(a), 0]) for a in angles] # Noeuds modules nodes = [] node_labels = [] node_subs = [] for i, ((name, sub, color), pos) in enumerate(zip(MODULES, positions)): ring = Circle(radius=0.42, color=GREY, fill_color=DIM, fill_opacity=1, stroke_width=1.5).move_to(pos) lbl = Text(name, font="Monospace", color=GREY, weight=BOLD).scale(0.26).move_to(pos) sublbl = Text(sub, font="Monospace", color=GREY).scale(0.16) sublbl.next_to(ring, DOWN, buff=0.08) nodes.append(ring) node_labels.append(lbl) node_subs.append(sublbl) labels_g = VGroup(*node_labels) subs_g = VGroup(*node_subs) # Centre -- noyau LYRA (dormant) core = Circle(radius=0.18, color=BLUE2, fill_color=DIM, fill_opacity=1, stroke_width=1.5) # Lignes grises (inactives) entre chaque module et le centre base_lines = VGroup(*[ DashedLine(pos * 0.42 / radius + pos * (1 - 0.42 / radius) * 0.12, pos * 0.42 / radius, color=GREY, stroke_width=0.6, stroke_opacity=0.3, dash_length=0.08) for pos in positions ]) # lignes simples du bord du noeud au centre base_lines = VGroup(*[ Line(pos * (1 - 0.44 / radius), ORIGIN, color=GREY, stroke_width=0.5, stroke_opacity=0.2) for pos in positions ]) # ===================================================================== # PHASE 1 : Apparition de la structure (0 -> 1.2s) # ===================================================================== self.play( LaggedStart( *[GrowFromCenter(n) for n in nodes], lag_ratio=0.07, run_time=0.55 ), FadeIn(labels_g, run_time=0.45), FadeIn(subs_g, run_time=0.45), ) self.play( FadeIn(base_lines, run_time=0.25), GrowFromCenter(core, run_time=0.25), ) # ===================================================================== # PHASE 2 : Chaque module s'allume et envoie un flux vers LYRA (1.2 -> 6s) # ===================================================================== active_streams = [] for i, ((name, sub, color), pos, node, lbl, sublbl) in enumerate( zip(MODULES, positions, nodes, node_labels, node_subs) ): # -- Allumage + flux en une seule animation -- stream_line = Line( pos * (1 - 0.44 / radius), ORIGIN, color=color, stroke_width=1.2, stroke_opacity=0.5 ) active_streams.append(stream_line) dot = Dot(pos * (1 - 0.44 / radius), radius=0.07, color=color) trail = TracedPath(dot.get_center, stroke_color=color, stroke_width=2.5, stroke_opacity=0.6, dissipating_time=0.3) self.add(trail) self.play( node.animate.set_stroke(color=color, width=2.5), lbl.animate.set_color(color), sublbl.animate.set_color(color).set_opacity(0.7), Flash(node, color=color, flash_radius=0.5, num_lines=8, line_length=0.15, run_time=0.2), Create(stream_line, run_time=0.2), dot.animate.move_to(ORIGIN).set_opacity(0), run_time=0.28, rate_func=rush_into ) self.remove(dot, trail) self.play( core.animate.set_stroke(color=color, width=2 + i * 0.4) .scale(1.06), run_time=0.12, rate_func=there_and_back ) # ===================================================================== # PHASE 3 : TOUS CONNECTES -- le coeur s'emballe (6 -> 7.2s) # ===================================================================== streams_g = VGroup(*active_streams) # Flash global + pulsations self.play( *[stream.animate.set_stroke(opacity=0.9, width=2.0) for stream in active_streams], core.animate.set_stroke(color=WHITE, width=4).scale(1.4), run_time=0.2 ) for _ in range(2): self.play( core.animate.scale(1.5).set_stroke(color=BLUE, width=5), run_time=0.12, rate_func=there_and_back ) # ===================================================================== # PHASE 4 : CONVERGENCE -- tout fonce vers le centre (7.2 -> 8.2s) # ===================================================================== # Les modules filent vers le centre self.play( *[node.animate.move_to(ORIGIN).scale(0.01).set_opacity(0) for node in nodes], FadeOut(labels_g, run_time=0.25), FadeOut(subs_g, run_time=0.25), FadeOut(streams_g, run_time=0.2), FadeOut(base_lines, run_time=0.2), run_time=0.38, rate_func=rush_into ) # ===================================================================== # PHASE 5 : EXPLOSION -- LYRA emerge (8.2 -> 10.5s) # ===================================================================== # Onde de choc shock = Circle(radius=0.01, color=WHITE, stroke_width=6, fill_opacity=0) self.add(shock) self.play( shock.animate.scale(120).set_stroke(opacity=0), core.animate.scale(0.1).set_opacity(0), run_time=0.45, rate_func=rush_from ) self.remove(shock, core) # LYRA lyra = Text("LYRA", font="Monospace", weight=BOLD, color=BLUE) lyra.scale(3.5) ring = Circle(radius=2.2, color=BLUE, stroke_width=2.5) ticks = VGroup( Line(UP * 2.35, UP * 2.7, color=BLUE, stroke_width=2), Line(DOWN * 2.35, DOWN * 2.7, color=BLUE, stroke_width=2), Line(RIGHT * 2.35, RIGHT * 2.7, color=BLUE, stroke_width=2), Line(LEFT * 2.35, LEFT * 2.7, color=BLUE, stroke_width=2), ) label_actif = Text("SYSTEME ACTIF", font="Monospace", color=WHITE) label_actif.scale(0.35).next_to(lyra, DOWN, buff=0.3) self.play( FadeIn(lyra, scale=0.1, run_time=0.35), Create(ring, run_time=0.35), ) self.play( LaggedStart(*[GrowFromCenter(t) for t in ticks], lag_ratio=0.08), FadeIn(label_actif, shift=UP * 0.1, run_time=0.3), Flash(lyra, color=BLUE, flash_radius=1.6, num_lines=24, line_length=0.7, run_time=0.45), Rotate(ring, angle=TAU * 0.12, run_time=0.5, rate_func=smooth), ) self.wait(0.25) self.play( FadeOut(ring, ticks, label_actif, run_time=0.2), lyra.animate.scale(0.01).set_opacity(0), run_time=0.38, rate_func=rush_into ) self.wait(0.05)