From b1665c628043662e395ccdc0b3c09a42e1032801 Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Mon, 25 Jul 2022 16:15:30 +0200 Subject: [PATCH] :sparkles: Emote construction --- automate-rs/resources/blobfox.yml | 3 +- automate-rs/resources/neugeme.yml | 3 + automate-rs/src/decl.rs | 7 +- automate-rs/src/emote.rs | 117 ++++++++++++++++++++++++++++++ automate-rs/src/main.rs | 49 +++++++++++++ 5 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 automate-rs/src/emote.rs diff --git a/automate-rs/resources/blobfox.yml b/automate-rs/resources/blobfox.yml index c920640..d7b5e67 100644 --- a/automate-rs/resources/blobfox.yml +++ b/automate-rs/resources/blobfox.yml @@ -2,11 +2,12 @@ name: 'blobfox' base_off: null variants: 'base': - src: '../vector/blobfox.svg' + src: '../../vector/blobfox.svg' 'boop': assets: - type: svg + name: 'lgbtq_heart' src: 'assets/lgbtq_heart.svg' overwrites: diff --git a/automate-rs/resources/neugeme.yml b/automate-rs/resources/neugeme.yml index a5a7016..fb3859b 100644 --- a/automate-rs/resources/neugeme.yml +++ b/automate-rs/resources/neugeme.yml @@ -9,3 +9,6 @@ variants: fill: 0x02FFFD - id: 'blobfox' remove: true + + 'sleepy': + base: 'base' diff --git a/automate-rs/src/decl.rs b/automate-rs/src/decl.rs index a56d84c..16115f9 100644 --- a/automate-rs/src/decl.rs +++ b/automate-rs/src/decl.rs @@ -15,14 +15,15 @@ pub struct Variant { pub src: Option, // Loads every asset from an SVG file #[serde(default)] - pub assets: Vec, // Loads individual assets + pub assets: Vec, // Loads individual assets #[serde(default)] pub overwrites: Vec, // Operations on assets } #[derive(Serialize, Deserialize, Clone, Debug)] -pub struct Asset { +pub struct AssetDecl { + pub name: String, pub src: Option, } @@ -96,7 +97,7 @@ impl Variant { } } -impl Asset { +impl AssetDecl { /// Replaces every path relative to the yaml file to paths relative to the cwd pub fn canonicalize(&mut self, path: &Path) { match &mut self.src { diff --git a/automate-rs/src/emote.rs b/automate-rs/src/emote.rs new file mode 100644 index 0000000..38aadaa --- /dev/null +++ b/automate-rs/src/emote.rs @@ -0,0 +1,117 @@ +use super::{AssetDecl, Variant}; + +use xmltree::{XMLNode, Element}; +use std::collections::HashMap; +use std::fs::File; + +#[derive(Debug, Clone)] +pub struct Emote { + pub assets: HashMap, + + // TODO: metadata (contributors, etc.) +} + +#[derive(Debug, Clone)] +pub struct Asset { + element: Element, +} + +impl Emote { + pub fn empty() -> Self { + Self { + assets: HashMap::new(), + } + } + + pub fn from_decl(variant_decl: Variant, emotes: &HashMap) -> Option { + // Load base or src + let mut res = if let Some(src) = variant_decl.src { + let file = File::open(src).ok()?; + let xml = Element::parse(file).ok()?; + Self::from_xml(xml)? + } else if let Some(base) = variant_decl.base { + emotes.get(&base)?.clone() + } else { + Emote::empty() + }; + + // Load individual assets + for asset_decl in variant_decl.assets { + let name = asset_decl.name.clone(); + res.assets.insert(name, Asset::from_decl(asset_decl)?); + } + + // Apply overwrites (TODO) + + Some(res) + } + + /// Loads an emote from an svg file. + /// Top-level `` elements are ignored + pub fn from_xml(root: Element) -> Option { + let iter = root.children.into_iter() + .map(|elem| -> Box> { + if let XMLNode::Element(elem2) = elem { + if elem2.name == "g" { + Box::new(elem2.children.into_iter()) + } else { + Box::new(Some(XMLNode::Element(elem2)).into_iter()) + } + } else { + Box::new(None.into_iter()) + } + }) + .flatten() + .filter_map(|elem| { + match elem { + XMLNode::Element(elem) => Some(elem), + _ => None + } + }) + .filter(filter_drawable); + + let mut assets = HashMap::new(); + + for elem in iter { + if let Some(name) = elem.attributes.get("label").or(elem.attributes.get("id")).cloned() { + let asset = Asset::new(elem); + assets.insert(name, asset); + } + } + + Some(Self { + assets + }) + } +} + +impl Asset { + pub fn new(element: Element) -> Self { + Self { + element + } + } + + // TODO: allow loading assets from other variants (requires a more thorough dependency management) + // TODO: allow searching for a specific element + pub fn from_decl(declaration: AssetDecl) -> Option { + let mut element = if let Some(src) = declaration.src { + let file = File::open(src).ok()?; + Element::parse(file).ok()? + } else { + unimplemented!() + }; + + Some(Self { + element + }) + } +} + +// NOTE: This isn't really viable, +fn filter_drawable(elem: &Element) -> bool { + match elem.name.as_str() { + "title" | "sodipodi:namedview" | "image" | "metadata" => false, + _ => true, + } +} diff --git a/automate-rs/src/main.rs b/automate-rs/src/main.rs index 36bb8d8..3706ce5 100644 --- a/automate-rs/src/main.rs +++ b/automate-rs/src/main.rs @@ -1,9 +1,13 @@ use clap::Parser; use std::path::PathBuf; +use std::collections::HashMap; pub mod decl; use decl::*; +pub mod emote; +use emote::*; + fn main() { let args = Args::parse(); @@ -26,6 +30,51 @@ fn main() { } println!("{:#?}", declaration); + + let mut emotes = HashMap::new(); + + let to_generate = if args.names.len() > 0 { + args.names + } else { + declaration.variants.keys().cloned().collect() + }; + + for name in to_generate { + match construct_emote(&name, &declaration, &mut emotes) { + Some(emote) => { + emotes.insert(name, dbg!(emote)); + // generate emote + } + None => { + eprintln!("Errors occured while generating emote {}, skipping!", name); + } + } + } + + // if !emotes.contains_key(&name) { + // let emote = Emote::from_decl(variant, &mut emotes); + // if let Some(emote) = emote { + // emotes.insert(name, emote); + // } + // } + // } +} + +/// Recursively constructs the emote with variant declaration named `named`. +/// If the declaration has a base, that base is generated first. +// I could unwrap the recursion into a loop with a stack, but I'm lazy :3 +fn construct_emote(name: &str, declaration: &Declaration, emotes: &mut HashMap) -> Option { + if let Some(emote_decl) = declaration.variants.get(name) { + if let Some(base_name) = emote_decl.base.clone() { + let base = construct_emote(&base_name, declaration, emotes)?; + emotes.insert(base_name, base); + } + + Emote::from_decl(emote_decl.clone(), &*emotes) + } else { + eprintln!("No emote named {}!", name); + None + } } #[derive(Parser, Debug)]