string_art_ui 0.1.0-alpha.1

A gui interface for string_art.
use std::{ops::Deref, sync::Arc};

use egui::mutex::Mutex;
use string_art::{
    nails,
    verboser::{self, Verboser},
    Algorithm,
};

use crate::{args::{ArgLineCountState, Args}, SyncArgs};

#[derive(Default)]
pub struct Synced<T>(Arc<Mutex<T>>);

impl<T> Clone for Synced<T> {
    fn clone(&self) -> Self {
        Self(self.0.clone())
    }
}

impl<T> Deref for Synced<T> {
    type Target = Mutex<T>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

pub struct SyncedVerboser {
    synced: Synced<SyncData>,
    threads: usize,
    nails: usize,
}

impl SyncedVerboser {
    pub fn new(synced: Synced<SyncData>, args: &Args) -> Self {
        Self {
            synced,
            threads: match args.line_config.state {
                ArgLineCountState::Manual => args.line_config.manual
                    .iter()
                    .map(|group| group.iter().map(|item| item.cap).sum::<usize>())
                    .sum(),
                ArgLineCountState::Auto => args.line_config.auto.threads,
            },
            nails: args.nails.get(),
        }
    }
    
    pub fn verbose(&mut self, message: Message) {
        self.synced.lock().message = Some(message);
    }
}

impl Deref for SyncedVerboser {
    type Target = Synced<SyncData>;

    fn deref(&self) -> &Self::Target {
        &self.synced
    }
}

impl Verboser for SyncedVerboser {
    fn verbose(&mut self, message: verboser::Message) {
        self.verbose(match message {
            verboser::Message::CreatingNail(idx) => Message {
                message_type: MessageType::CreatingNail,
                message: format!(
                    "Nailing {}/{}, {}%",
                    idx,
                    self.nails,
                    (idx * 100) / self.nails
                ),
            },
            verboser::Message::Baking => Message {
                message_type: MessageType::Baking,
                message: String::from("Baking"),
            },
            verboser::Message::Dithering(idx, total) => Message {
                message_type: MessageType::Dithering,
                message: format!("Dithering {}/{}, {}%", idx, total, (idx * 100) / total),
            },
            verboser::Message::Computing(idx) => Message {
                message_type: MessageType::Computing,
                message: format!(
                    "Computing {}/{}, {}%",
                    idx,
                    self.threads,
                    (idx * 100) / self.threads
                ),
            },
        });
    }
}

#[derive(Default)]
pub struct SyncData {
    pub message: Option<Message>,
    pub computation: ComputationState,
    pub args: SyncArgs,
}

pub enum MessageType {
    LoadingImage,
    Baking,
    CreatingNail,
    Dithering,
    Computing,
    Error,
}

pub struct Message {
    message_type: MessageType,
    message: String,
}

impl Message {
    pub fn new(message_type: MessageType, message: impl ToString) -> Self {
        Self {
            message_type,
            message: message.to_string(),
        }
    }

    pub fn draw(&self, ui: &mut egui::Ui) {
        if let MessageType::Error = self.message_type {
            ui.label(
                egui::RichText::new(&self.message)
                    .italics()
                    .color(egui::Color32::RED),
            );
        } else {
            ui.label(&self.message);
        }
    }

    pub fn error(message: impl ToString) -> Self {
        Self::new(MessageType::Error, message)
    }
}

pub enum ComputationState {
    Idle,
    Running,
    Completed(Box<dyn Computation>),
}

impl Default for ComputationState {
    fn default() -> Self {
        Self::Idle
    }
}

pub trait Computation: Send + Sync {
    fn build_svg(&self, tickness: f32) -> svg::Document;

    fn build_instructions(&self) -> String;

    fn get_line_config(&self) -> string_art::LineConfig;
}

impl<N: nails::Handle<Link: ToString>> Computation for Algorithm<N> {
    fn build_svg(&self, tickness: f32) -> svg::Document {
        self.build_svg(tickness)
    }

    fn build_instructions(&self) -> String {
        self.build_instructions()
    }
    
    fn get_line_config(&self) -> string_art::LineConfig {
        self.line_selector().into()
    }
}