← circus

Embedded state collection morphing

Embedded M -> N morphing

Python Code


"""Example: M->N shape group morphing using ShapeCollectionState

Demonstrates morphing between different numbers of independent shapes:
- 3 triangles + 2 squares -> 1 ellipse + 3 rectangles

Each shape has independent colors and properties. The mapping uses
the same VertexLoopMapper strategies as hole morphing (ClusteringMapper by default).
"""

from dataclasses import replace
from svan2d.component.state import (
    StateCollectionState,
    TriangleState,
    SquareState,
)
from svan2d.converter.converter_type import ConverterType
from svan2d.core.logger import configure_logging
from svan2d.core.point2d import Point2D
from svan2d.velement import VElement
from svan2d.transition.segment import hold
from svan2d.vscene import VScene
from svan2d.vscene.vscene_exporter import VSceneExporter
from svan2d.core.color import Color

configure_logging(level="INFO")


def main():
    # Create the scene
    scene = VScene(width=256, height=256, background=Color("#000017"))

    states = [
        TriangleState(
            pos=Point2D(-70, -50),
            size=30,
            fill_color=Color("#F3B700"),
            scale=0,
        ),
        TriangleState(
            pos=Point2D(0, -50),
            size=30,
            fill_color=Color("#FAA300"),
            scale=0,
        ),
        TriangleState(
            pos=Point2D(70, -50),
            size=30,
            fill_color=Color("#E57C04"),
            scale=0,
        ),
        SquareState(
            pos=Point2D(-50, 50),
            size=40,
            fill_color=Color("#F63E02"),
            scale=0,
        ),
        SquareState(
            pos=Point2D(50, 50),
            size=40,
            fill_color=Color("#FF0000"),
            scale=0,
        ),
    ]

    morph_states = [replace(state, scale=1) for state in states]

    collection_1 = StateCollectionState(states=morph_states[:3])
    collection_2 = StateCollectionState(states=morph_states[3:])

    # Create animation

    # 1 fade in

    start_elements = [
        VElement().keystate(s1, at=0).keystate(s2, at=0.2)
        for s1, s2 in zip(
            states[:3],
            morph_states[:3],
        )
    ]

    scene.add_elements(start_elements)

    # 2. Morph
    # skip_render_at=True to avoid double rendering

    morph_element = (
        VElement()
        .keystate(collection_1, at=0.2, skip_render_at=True)
        .keystate(collection_2, at=0.8, skip_render_at=True)
    )

    scene.add_element(morph_element)

    # fade out

    end_elements = [
        VElement().keystate(s1, at=0.8).keystate(s2, at=1)
        for s1, s2 in zip(
            morph_states[3:],
            states[3:],
        )
    ]

    scene.add_elements(end_elements)

    # Create the exporter
    exporter = VSceneExporter(
        scene=scene,
        converter=ConverterType.PLAYWRIGHT,
        output_dir="output/",
    )

    # Export to MP4
    exporter.to_mp4(
        filename="embedded_state_collection_morphing",
        total_frames=90,
        framerate=30,
        png_width_px=1024,
    )


if __name__ == "__main__":
    main()