devotee 0.1.47

Visualization engine
Documentation
use std::f64::consts;

use devotee::app;
use devotee::app::config;
use devotee::app::context::Context;
use devotee::app::input::key_mouse::{KeyMouse, VirtualKeyCode};
use devotee::app::root::Root;
use devotee::app::setup;
use devotee::util::vector::Vector;
use devotee::visual::color;
use devotee::visual::prelude::*;
use devotee::visual::sprite::Sprite;
use devotee_backend_softbuffer::SoftbufferBackend;

fn main() {
    let init_config = setup::Builder::<Config>::new()
        .with_render_target(Sprite::with_color(0.into()))
        .with_input(Default::default())
        .with_root_constructor(|_| Default::default())
        .with_title("pentacle")
        .with_scale(3);
    let app = app::App::<_, SoftbufferBackend>::with_setup(init_config).unwrap();

    app.run();
}

struct Config;

impl config::Config for Config {
    type Root = Pentacle;
    type Converter = Converter;
    type Input = KeyMouse;
    type RenderTarget = Sprite<SummationPalette, 128, 128>;

    fn converter() -> Self::Converter {
        Converter
    }

    fn background_color() -> SummationPalette {
        0.into()
    }
}

#[derive(Default)]
struct Pentacle {
    rotation: f64,
    counter: f64,
}

impl Root<Config> for Pentacle {
    fn update(&mut self, update: &mut Context<Config>) {
        if update.input().keys().just_pressed(VirtualKeyCode::Escape) {
            update.shutdown();
        }
        let delta = update.delta().as_secs_f64();
        if update.input().keys().is_pressed(VirtualKeyCode::Z) {
            self.rotation += delta;
        }
        self.counter += delta;
    }

    fn render(&self, render: &mut Sprite<SummationPalette, 128, 128>) {
        let mut render = render.painter();

        render.clear(0.into());

        let radius = 48.0 + 8.0 * self.rotation.sin();
        let center = Vector::new(64, 64);

        render.circle((64, 64), radius as i32, draw(2.into()));
        render.circle_f((64, 64), 32, draw(2.into()));

        let radius = radius + 8.0;

        let vertices: Vec<_> = (0..5)
            .map(|i| {
                let a = 0.25 * self.rotation + 2.0 / 5.0 * i as f64 * consts::TAU;

                center
                    + (
                        (radius * a.cos()).round() as i32,
                        (radius * a.sin()).round() as i32,
                    )
            })
            .collect();

        render.polygon(&vertices, draw(2.into()));
        if self.counter.round() as i32 % 2 == 1 {
            render.polygon_f(&vertices, draw(3.into()));
        }

        render.rect((0, 0), (128, 128), draw(2.into()));
    }
}

#[derive(Copy, Clone, PartialEq)]
struct SummationPalette {
    value: u8,
}

impl From<u8> for SummationPalette {
    #[inline]
    fn from(value: u8) -> Self {
        Self { value }
    }
}

impl Color for SummationPalette {
    fn mix(self, other: Self) -> Self {
        Self {
            value: self.value.saturating_add(other.value),
        }
    }
}

struct Converter;

impl color::Converter for Converter {
    type Palette = SummationPalette;
    #[inline]
    fn convert(&self, color: &Self::Palette) -> u32 {
        let brightness = color.value.saturating_mul(64);

        if brightness > 0x80 {
            (brightness as u32) << 16
        } else {
            (brightness as u32) << 16 | (brightness as u32) << 8 | brightness as u32
        }
    }
}