Preview App
use std::f64::consts::PI;

use glam::{DVec3, dvec2, dvec3};
use itertools::Itertools;
use log::LevelFilter;
use ranim::{
    animation::{creation::WritingAnim, lagged::LaggedAnim, transform::TransformAnim},
    color::palettes::manim,
    components::{Anchor, ScaleHint},
    items::{
        Group,
        vitem::{
            VItem,
            geometry::{Polygon, Rectangle, Square},
            svg::SvgItem,
            typst::typst_svg,
        },
    },
    prelude::*,
    timeline::TimeMark,
    utils::rate_functions::{linear, smooth},
};

fn build_logo(logo_width: f64) -> [VItem; 6] {
    let red_bg_rect = Rectangle::new(logo_width / 2.0, logo_width).with(|rect| {
        rect.set_color(manim::RED_C.with_alpha(0.5))
            .put_center_on(dvec3(-logo_width / 4.0, 0.0, 0.0));
    });
    let red_rect = Rectangle::new(logo_width / 4.0, logo_width).with(|rect| {
        rect.set_color(manim::RED_C)
            .put_anchor_on(Anchor::edge(1, 0, 0), dvec3(-logo_width / 4.0, 0.0, 0.0));
    });

    let green_bg_sq = Square::new(logo_width / 2.0).with(|sq| {
        sq.set_color(manim::GREEN_C.with_alpha(0.5))
            .put_center_on(dvec3(logo_width / 4.0, logo_width / 4.0, 0.0));
    });
    let green_triangle = Polygon::new(vec![
        dvec3(0.0, logo_width / 2.0, 0.0),
        dvec3(logo_width / 2.0, logo_width / 2.0, 0.0),
        dvec3(logo_width / 2.0, 0.0, 0.0),
    ])
    .with(|tri| {
        tri.set_color(manim::GREEN_C);
    }); //
    let blue_bg_sq = Square::new(logo_width / 2.0).with(|sq| {
        sq.set_color(manim::BLUE_C.with_alpha(0.5))
            .put_center_on(dvec3(logo_width / 4.0, -logo_width / 4.0, 0.0));
    });
    let blue_triangle = green_triangle.clone().with(|tri| {
        tri.set_color(manim::BLUE_C)
            .rotate(PI, DVec3::Z)
            .shift(DVec3::NEG_Y * logo_width / 2.0);
    }); //
    [
        VItem::from(red_bg_rect),
        VItem::from(red_rect),
        VItem::from(green_bg_sq),
        VItem::from(green_triangle),
        VItem::from(blue_bg_sq),
        VItem::from(blue_triangle),
    ]
}
#[scene]
struct RanimLogoScene;

impl SceneConstructor for RanimLogoScene {
    fn construct(self, r: &mut RanimScene, _r_cam: ItemId<CameraFrame>) {
        let frame_size = dvec2(8.0 * 16.0 / 9.0, 8.0);
        let logo_width = frame_size.y * 0.618;

        let logo = build_logo(logo_width);
        let r_logo = logo.map(|item| r.insert(item));

        let ranim_text = Group::<VItem>::from(
            SvgItem::new(typst_svg(
                r#"
#align(center)[
    #text(10pt, font: "LXGW Bright")[Ranim]
]"#,
            ))
            .with(|text| {
                text.set_color(manim::WHITE)
                    .scale_to(ScaleHint::PorportionalY(1.0))
                    .put_center_on(DVec3::NEG_Y * 2.5);
            }),
        );
        let r_ranim_text = r.insert(ranim_text);

        r_logo.iter().for_each(|item| {
            r.timeline_mut(item)
                .play_with(|item| item.write().with_duration(3.0).with_rate_func(smooth));
        });
        r.timelines_mut().sync();

        let gap_ratio = 1.0 / 60.0;
        let gap = logo_width * gap_ratio;
        let scale = (logo_width - gap * 2.0) / logo_width;
        let scale = [
            dvec3(scale, 1.0, 1.0),
            dvec3(scale, scale, 1.0),
            dvec3(scale, scale, 1.0),
        ];
        let anchor = [
            Anchor::edge(-1, 0, 0),
            Anchor::edge(1, 1, 0),
            Anchor::edge(1, -1, 0),
        ];
        r_logo
            .iter()
            .chunks(2)
            .into_iter()
            .zip(scale.into_iter().zip(anchor))
            .for_each(|(chunk, (scale, anchor))| {
                let chunk = chunk.collect_array::<2>().unwrap();
                r.timeline_mut(&chunk).iter_mut().for_each(|timeline| {
                    timeline.play_with(|item| {
                        item.transform(|data| {
                            data.scale_by_anchor(scale, anchor)
                                .scale_by_anchor(dvec3(0.9, 0.9, 1.0), Anchor::ORIGIN)
                                .shift(dvec3(0.0, 1.3, 0.0));
                        })
                        .with_rate_func(smooth)
                    });
                });
            });
        r.timeline_mut(&r_ranim_text)
            .forward(0.5)
            .play_with(|text| {
                text.lagged(0.2, |item| {
                    item.write().with_duration(2.0).with_rate_func(linear)
                })
                .with_duration(2.0)
            });
        r.timelines_mut().sync();

        r.insert_time_mark(
            r.timelines().max_total_secs(),
            TimeMark::Capture("preview.png".to_string()),
        );
        r.timelines_mut().forward(1.0);

        r_logo.iter().for_each(|r_logo_part| {
            r.timeline_mut(r_logo_part)
                .play_with(|item| item.unwrite().with_duration(3.0).with_rate_func(smooth));
        });
        r.timeline_mut(&r_ranim_text).play_with(|text| {
            text.lagged(0.0, |item| {
                item.unwrite().with_duration(3.0).with_rate_func(linear)
            })
        });
    }
}

fn main() {
    #[cfg(not(target_arch = "wasm32"))]
    {
        #[cfg(debug_assertions)]
        pretty_env_logger::formatted_timed_builder()
            .filter(Some("ranim"), LevelFilter::Trace)
            .init();
        #[cfg(not(debug_assertions))]
        pretty_env_logger::formatted_timed_builder()
            .filter(Some("ranim"), LevelFilter::Info)
            .init();
    }

    #[cfg(feature = "app")]
    run_scene_app(RanimLogoScene);
    #[cfg(not(feature = "app"))]
    render_scene(RanimLogoScene, &AppOptions::default());
}