Ctrl K
ComunidadGuías…

intro/part4_activation.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""
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)