devotee 0.2.0-beta.32

Visualization engine
Documentation
use std::env;
use std::time::Duration;

use devotee::input::winit_input::KeyboardMouse;
use devotee::util::vector::Vector;
use devotee::visual::adapter::Converter;
use devotee::visual::adapter::generic::Adapter;
use devotee::visual::image::ImageMut;
use devotee::visual::{Paint, Painter};
use devotee_backend::Middleware;
use devotee_backend::middling::{InputHandler, Surface};
use devotee_backend_pixels::{
    PixelsBackend, PixelsContext, PixelsEvent, PixelsEventContext, PixelsEventControl, PixelsInit,
    PixelsSurface,
};
use devotee_backend_softbuffer::{
    SoftBackend, SoftContext, SoftEvent, SoftEventContext, SoftEventControl, SoftInit, SoftSurface,
};
use winit::event::MouseButton;

fn main() {
    let style = env::var("ADAPT_STYLE").ok();

    let input = KeyboardMouse::new();

    match style {
        Some(pixels) if pixels.to_lowercase().trim() == "pixels" => {
            let internal = Internal::default();
            let adapting = Adapting { input, internal };
            let mut backend = PixelsBackend::new(adapting);
            backend.run().unwrap();
        }
        Some(soft) if soft.to_lowercase().trim() == "soft" => {
            let internal = Internal::default();
            let adapting = Adapting { input, internal };
            let mut backend = SoftBackend::new(adapting);

            backend.run().unwrap();
        }
        _ => {
            println!("Set an environment variable `ADAPT_STYLE` to either `pixels` or `soft` value")
        }
    }
}

#[derive(Default)]
struct Internal {
    position: Option<Vector<f32>>,
}

impl Internal {
    fn update(&mut self, duration: Duration, input: &KeyboardMouse) {
        if input.mouse().is_pressed(MouseButton::Left) {
            if let Some(mouse) = input.mouse().position() {
                self.position = Some(mouse.map(|v| v as _));
            }
        } else if let Some(position) = &mut self.position {
            *position.y_mut() += 32.0 * duration.as_secs_f32();
        }
    }

    fn render<Cfg>(&mut self, render: &mut RenderImage<Cfg::Surface<'_, '_>, Cfg::Converter>)
    where
        Cfg: Config,
    {
        if let Some(position) = self.position {
            let mut painter = Painter::<'_, _>::new(render);
            painter.line((0.0, 0.0).into(), position, &Color::Light);
        }
    }
}

type RenderImage<'a, 'b, S, C> = Adapter<'a, 'b, S, C>;

trait Config {
    type Surface<'a, 'b: 'a>: Surface<Texel = <Self::Converter as Converter>::Texel>;
    type Converter: Converter<Pixel = Color, Texel: Clone>;
}

struct Adapting {
    input: KeyboardMouse,
    internal: Internal,
}

impl
    Middleware<
        PixelsInit<'_>,
        PixelsContext<'_>,
        PixelsSurface<'_, '_>,
        PixelsEvent,
        PixelsEventContext<'_, '_>,
        PixelsEventControl<'_>,
    > for Adapting
{
    fn on_init(&mut self, init: &mut PixelsInit<'_>) {
        init.set_render_window_size(320, 240);
        init.window()
            .set_title("Adapting demo: Pixels version, use LMB");
    }

    fn on_update(&mut self, context: &mut PixelsContext<'_>) {
        self.internal.update(context.delta(), &self.input);
        InputHandler::<_, PixelsEventContext>::update(&mut self.input);
    }

    fn on_render(&mut self, surface: &mut PixelsSurface<'_, '_>) {
        let mut adapter = Adapter::new(surface, &PixelsConverter);
        adapter.clear(Color::Dark);
        self.internal.render::<PixelsConfig>(&mut adapter);
    }

    fn on_event(
        &mut self,
        event: PixelsEvent,
        event_context: &PixelsEventContext<'_, '_>,
        _: &mut PixelsEventControl<'_>,
    ) -> Option<PixelsEvent> {
        if let PixelsEvent::Window(window_event) = event {
            self.input
                .handle_event(window_event, event_context)
                .map(PixelsEvent::Window)
        } else {
            None
        }
    }
}

struct PixelsConfig;

impl Config for PixelsConfig {
    type Surface<'a, 'b: 'a> = PixelsSurface<'a, 'b>;

    type Converter = PixelsConverter;
}

struct PixelsConverter;

impl Converter for PixelsConverter {
    type Pixel = Color;
    type Texel = [u8; 4];

    fn forward(&self, pixel: &Self::Pixel) -> Self::Texel {
        match pixel {
            Color::Dark => [0x40, 0x20, 0x20, 0xff],
            Color::Light => [0xe0, 0xe0, 0xe0, 0xff],
        }
    }

    fn inverse(&self, texel: &Self::Texel) -> Self::Pixel {
        if texel[0] > 0x80 {
            Color::Light
        } else {
            Color::Dark
        }
    }
}

impl
    Middleware<
        SoftInit<'_>,
        SoftContext<'_>,
        SoftSurface<'_>,
        SoftEvent,
        SoftEventContext,
        SoftEventControl<'_>,
    > for Adapting
{
    fn on_init(&mut self, init: &mut SoftInit<'_>) {
        init.set_render_window_size(320, 240);
        init.window()
            .set_title("Adapting demo: SoftBuffer version, use LMB");
    }

    fn on_update(&mut self, context: &mut SoftContext<'_>) {
        self.internal.update(context.delta(), &self.input);
        InputHandler::<_, SoftEventContext>::update(&mut self.input);
    }

    fn on_render(&mut self, surface: &mut SoftSurface<'_>) {
        let mut adapter = Adapter::new(surface, &SoftConverter);
        adapter.clear(Color::Dark);
        self.internal.render::<SoftConfig>(&mut adapter);
    }

    fn on_event(
        &mut self,
        event: SoftEvent,
        event_context: &SoftEventContext,
        _: &mut SoftEventControl,
    ) -> Option<SoftEvent> {
        if let SoftEvent::Window(event) = event {
            self.input
                .handle_event(event, event_context)
                .map(SoftEvent::Window)
        } else {
            Some(event)
        }
    }
}

struct SoftConfig;

impl Config for SoftConfig {
    type Surface<'a, 'b: 'a> = SoftSurface<'a>;

    type Converter = SoftConverter;
}

struct SoftConverter;

impl Converter for SoftConverter {
    type Pixel = Color;
    type Texel = u32;

    fn forward(&self, pixel: &Self::Pixel) -> Self::Texel {
        match pixel {
            Color::Dark => 0x202040,
            Color::Light => 0xe0e0e0,
        }
    }

    fn inverse(&self, _: &Self::Texel) -> Self::Pixel {
        Color::Dark
    }
}

#[derive(Clone, Copy, Debug)]
enum Color {
    Dark,
    Light,
}