Emote construction

feat/automation-rs
Shad Amethyst 2 years ago
parent 6596e715b1
commit b1665c6280
Signed by: amethyst
GPG Key ID: D970C8DD1D6DEE36

@ -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:

@ -9,3 +9,6 @@ variants:
fill: 0x02FFFD
- id: 'blobfox'
remove: true
'sleepy':
base: 'base'

@ -15,14 +15,15 @@ pub struct Variant {
pub src: Option<PathBuf>, // Loads every asset from an SVG file
#[serde(default)]
pub assets: Vec<Asset>, // Loads individual assets
pub assets: Vec<AssetDecl>, // Loads individual assets
#[serde(default)]
pub overwrites: Vec<Overwrite>, // Operations on assets
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Asset {
pub struct AssetDecl {
pub name: String,
pub src: Option<PathBuf>,
}
@ -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 {

@ -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<String, Asset>,
// 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<String, Emote>) -> Option<Self> {
// 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 `<g>` elements are ignored
pub fn from_xml(root: Element) -> Option<Self> {
let iter = root.children.into_iter()
.map(|elem| -> Box<dyn Iterator<Item=XMLNode>> {
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<Self> {
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,
}
}

@ -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<String, Emote>) -> Option<Emote> {
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)]

Loading…
Cancel
Save