From be1d9c97eaeb205ca95e4b29958ba2eea4eee8ca Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Wed, 27 Jul 2022 16:24:25 +0200 Subject: [PATCH 01/18] :sparkles: Working mustache-based generator --- .gitignore | 2 + Cargo.toml | 12 ++ species/blobfox/assets/base.svg | 161 +++++++++++++++++++++ species/blobfox/species.toml | 1 + species/blobfox/templates/base.mustache | 7 + species/blobfox/templates/eyes.mustache | 4 + species/blobfox/templates/footer.mustache | 1 + species/blobfox/templates/header.mustache | 2 + species/blobfox/templates/mouth-w.mustache | 1 + species/blobfox/templates/nose.mustache | 4 + species/blobfox/variants/base.mustache | 6 + src/main.rs | 16 ++ src/parse.rs | 94 ++++++++++++ src/template.rs | 158 ++++++++++++++++++++ vector/blobfox.svg | 28 ++-- 15 files changed, 483 insertions(+), 14 deletions(-) create mode 100644 Cargo.toml create mode 100644 species/blobfox/assets/base.svg create mode 100644 species/blobfox/species.toml create mode 100644 species/blobfox/templates/base.mustache create mode 100644 species/blobfox/templates/eyes.mustache create mode 100644 species/blobfox/templates/footer.mustache create mode 100644 species/blobfox/templates/header.mustache create mode 100644 species/blobfox/templates/mouth-w.mustache create mode 100644 species/blobfox/templates/nose.mustache create mode 100644 species/blobfox/variants/base.mustache create mode 100644 src/main.rs create mode 100644 src/parse.rs create mode 100644 src/template.rs diff --git a/.gitignore b/.gitignore index b9fb92a..9d605e3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ original/ output/ +Cargo.lock +target/ diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..8febedd --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "blobfox-template" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = { version = "1.0.140", features = ["derive"] } +toml = "0.5.9" +xmltree = "0.10.3" +mustache = { git = "https://git.shadamethyst.xyz/adri326/rust-mustache.git" } diff --git a/species/blobfox/assets/base.svg b/species/blobfox/assets/base.svg new file mode 100644 index 0000000..e0bc79d --- /dev/null +++ b/species/blobfox/assets/base.svg @@ -0,0 +1,161 @@ + + + + + blobfox + + + + + + + + + + + + + + + + + + + + + + + + + Blobfox team (https://git.shadamethyst.xyz/adri326/blobfox), licensed under the Apache 2.0 License + + + blobfox + + + Feuerfuchs + + + https://git.shadamethyst.xyz/adri326/blobfox + + + Shad Amethyst + + + + + + diff --git a/species/blobfox/species.toml b/species/blobfox/species.toml new file mode 100644 index 0000000..f13fa38 --- /dev/null +++ b/species/blobfox/species.toml @@ -0,0 +1 @@ +# Add options in here as needs be diff --git a/species/blobfox/templates/base.mustache b/species/blobfox/templates/base.mustache new file mode 100644 index 0000000..cbeaa72 --- /dev/null +++ b/species/blobfox/templates/base.mustache @@ -0,0 +1,7 @@ + + {{#base}}body{{/base}} + {{#base}}left-ear{{/base}} + {{#base}}hair{{/base}} + {{#base}}right-ear{{/base}} + {{#base}}right-ear-fluff{{/base}} + diff --git a/species/blobfox/templates/eyes.mustache b/species/blobfox/templates/eyes.mustache new file mode 100644 index 0000000..0581ed4 --- /dev/null +++ b/species/blobfox/templates/eyes.mustache @@ -0,0 +1,4 @@ + + {{#base}}left-eye{{/base}} + {{#base}}right-eye{{/base}} + diff --git a/species/blobfox/templates/footer.mustache b/species/blobfox/templates/footer.mustache new file mode 100644 index 0000000..b590cc4 --- /dev/null +++ b/species/blobfox/templates/footer.mustache @@ -0,0 +1 @@ + diff --git a/species/blobfox/templates/header.mustache b/species/blobfox/templates/header.mustache new file mode 100644 index 0000000..05199ce --- /dev/null +++ b/species/blobfox/templates/header.mustache @@ -0,0 +1,2 @@ + + {{variant_name}} diff --git a/species/blobfox/templates/mouth-w.mustache b/species/blobfox/templates/mouth-w.mustache new file mode 100644 index 0000000..c0e10bc --- /dev/null +++ b/species/blobfox/templates/mouth-w.mustache @@ -0,0 +1 @@ +{{#base}}mouth{{/base}} diff --git a/species/blobfox/templates/nose.mustache b/species/blobfox/templates/nose.mustache new file mode 100644 index 0000000..815ce1e --- /dev/null +++ b/species/blobfox/templates/nose.mustache @@ -0,0 +1,4 @@ + + {{#base}}nose-outline{{/base}} + {{#base}}nose{{/base}} + diff --git a/species/blobfox/variants/base.mustache b/species/blobfox/variants/base.mustache new file mode 100644 index 0000000..30488d9 --- /dev/null +++ b/species/blobfox/variants/base.mustache @@ -0,0 +1,6 @@ +{{>header}} + {{>base}} + {{>eyes}} + {{>nose}} + {{>mouth-w}} +{{>footer}} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..9b82bfa --- /dev/null +++ b/src/main.rs @@ -0,0 +1,16 @@ +use std::sync::Arc; + +pub mod parse; +use parse::*; + +pub mod template; +use template::*; + +fn main() { + let species = Arc::new(dbg!(load_species("species/blobfox")).unwrap()); + let context = RenderingContext::new(species); + let template = context.compile("species/blobfox/variants/base.svg").unwrap(); + let rendered = template.render_data_to_string(&context.get_data()).unwrap(); + println!("{}", rendered); + std::fs::write("./test.svg", rendered).unwrap(); +} diff --git a/src/parse.rs b/src/parse.rs new file mode 100644 index 0000000..ea2214b --- /dev/null +++ b/src/parse.rs @@ -0,0 +1,94 @@ +use xmltree::{XMLNode, Element}; +use serde::{Serialize, Deserialize}; +use std::path::{PathBuf, Path}; +use std::collections::HashMap; + +/// Error returned upon failing to parse something +#[derive(Debug)] +pub enum ParseError { + Io(PathBuf, std::io::Error), + XmlParse(xmltree::ParseError), + Toml(toml::de::Error), +} + +impl From for ParseError { + fn from(err: xmltree::ParseError) -> Self { + Self::XmlParse(err) + } +} + +impl From for ParseError { + fn from(err: toml::de::Error) -> Self { + Self::Toml(err) + } +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct SpeciesDecl { + /// Imports xml and svg files from this folder if they aren't found + pub base: Option, + + #[serde(skip)] + pub templates: HashMap, + + #[serde(skip)] + pub variants: HashMap, + + #[serde(skip)] + pub assets: HashMap, +} + +/// Loads the given file as an XML tree +pub fn load_xml(path: impl AsRef) -> Result { + let file = std::fs::File::open(path.as_ref()).map_err(|err| { + ParseError::Io(path.as_ref().to_path_buf(), err) + })?; + + Ok(Element::parse(file)?) +} + +/// Loads the basic description of a SpeciesDecl +pub fn load_species(path: impl AsRef) -> Result { + let declaration_path = path.as_ref().join("species.toml"); + let declaration = std::fs::read_to_string(&declaration_path).map_err(|err| { + ParseError::Io(declaration_path, err) + })?; + + let mut res: SpeciesDecl = toml::from_str(&declaration)?; + + // Read the `templates` directory and populate the `templates` field; + // on error, ignore the directory. + res.templates = read_dir_xml(path.as_ref().join("templates")); + + // Read the `variants` directory + res.variants = read_dir_xml(path.as_ref().join("variants")); + + // Read the `assets` directory + res.assets = read_dir_xml(path.as_ref().join("assets")); + + Ok(res) +} + +fn read_dir_xml(path: impl AsRef) -> HashMap { + let mut res = HashMap::new(); + + if let Ok(iter) = std::fs::read_dir(path) { + for entry in iter.filter_map(|x| x.ok()) { + match (entry.path().file_stem(), entry.path().extension()) { + (Some(name), Some(ext)) => { + if matches!(ext.to_str(), Some("xml") | Some("svg") | Some("mustache")) { + if let Some(name) = name.to_str() { + res.insert( + name.to_string(), + entry.path().to_path_buf() + ); + } + } + } + _ => {} + } + } + } + + res +} diff --git a/src/template.rs b/src/template.rs new file mode 100644 index 0000000..4bf21e1 --- /dev/null +++ b/src/template.rs @@ -0,0 +1,158 @@ +use mustache::{ + Context, + PartialLoader, + Template, + MapBuilder, + Data, +}; +use super::*; +use std::collections::HashMap; +use std::path::Path; +use std::sync::{Arc, Mutex}; +use xmltree::{XMLNode, Element}; + +#[derive(Debug, Clone)] +pub struct RenderingContext { + species: Arc, + + rendered_variants: Arc>>, + + loaded_assets: Arc>>, +} + +impl RenderingContext { + pub fn new(species: Arc) -> Self { + Self { + species, + rendered_variants: Arc::new(Mutex::new(HashMap::new())), + loaded_assets: Arc::new(Mutex::new(HashMap::new())), + } + } + + pub fn compile(&self, path: impl AsRef) -> Result, mustache::Error> { + let template = std::fs::read_to_string(path)?; + Context::with_loader(self.clone()).compile(template.chars()) + } + + pub fn get_data(&self) -> Data { + let mut builder = MapBuilder::new(); + + builder = builder.insert_map("variant", |mut builder| { + for variant_name in self.species.variants.keys() { + let this = self.clone(); + let variant_name = variant_name.to_string(); + builder = builder.insert_fn(variant_name.clone(), move |selector| { + let svg = this.get_variant(&variant_name); + if let Some(svg) = svg { + if let Some(element) = query_selector(svg, &selector) { + if let Some(string) = xml_to_string(element) { + return string + } + } + } + + String::new() + }) + } + builder + }); + + for asset_name in self.species.assets.keys() { + let this = self.clone(); + let asset_name = asset_name.to_string(); + + builder = builder.insert_fn(asset_name.clone(), move |selector| { + let svg = this.get_asset(&asset_name); + if let Some(svg) = svg { + if let Some(element) = query_selector(svg, &selector) { + if let Some(string) = xml_to_string(element) { + return string + } + } + } + + String::new() + }); + } + + builder.build() + } + + pub fn get_variant(&self, name: &String) -> Option { + let rendered = self.rendered_variants.lock().unwrap().get(name).cloned(); + if let Some(rendered) = rendered { + Some(rendered) + } else if let Some(path) = self.species.variants.get(name) { + // TODO: log error + let template = self.compile(path).ok()?; + let data = self.get_data(); + let rendered = template.render_data_to_string(&data).ok()?; + + let parsed = Element::parse(rendered.as_bytes()).ok()?; + self.rendered_variants.lock().unwrap().insert(name.clone(), parsed.clone()); + + Some(parsed) + } else { + None + } + } + + pub fn get_asset(&self, name: &String) -> Option { + let loaded = self.loaded_assets.lock().unwrap().get(name).cloned(); + if let Some(loaded) = loaded { + Some(loaded) + } else if let Some(path) = self.species.assets.get(name) { + let string = std::fs::read_to_string(path).ok()?; + let parsed = Element::parse(string.as_bytes()).ok()?; + self.loaded_assets.lock().unwrap().insert(name.clone(), parsed.clone()); + + Some(parsed) + } else { + None + } + } +} + +impl PartialLoader for RenderingContext { + fn load(&self, name: impl AsRef) -> Result { + let name = name.as_ref().to_str().ok_or(mustache::Error::InvalidStr)?; + + if let Some(path) = self.species.templates.get(name) { + Ok(std::fs::read_to_string(path)?) + } else { + eprintln!("No template named {}", name); + Err(mustache::Error::NoFilename) + } + } +} + +pub fn query_selector(svg: Element, pattern: &str) -> Option { + if pattern == "" { + return Some(svg); + } + + for child in svg.children { + if let XMLNode::Element(child) = child { + if child.attributes.get("id").map(|id| id == pattern).unwrap_or(false) { + return Some(child); + } else if child.children.len() > 0 { + if let Some(res) = query_selector(child, pattern) { + return Some(res); + } + } + } + } + + None +} + +pub fn xml_to_string(element: Element) -> Option { + let mut s: Vec = Vec::new(); + let mut config = xmltree::EmitterConfig::default(); + config.perform_indent = true; + config.write_document_declaration = false; + + element.write_with_config(&mut s, config); + + String::from_utf8(s).ok() +} diff --git a/vector/blobfox.svg b/vector/blobfox.svg index 48c9a9d..e0bc79d 100644 --- a/vector/blobfox.svg +++ b/vector/blobfox.svg @@ -29,15 +29,15 @@ inkscape:pagecheckerboard="1" inkscape:document-units="mm" showgrid="false" - inkscape:zoom="4.1184431" - inkscape:cx="-8.4983571" - inkscape:cy="56.331967" + inkscape:zoom="10.154753" + inkscape:cx="59.381059" + inkscape:cy="57.214585" inkscape:window-width="1536" inkscape:window-height="779" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" - inkscape:current-layer="layer1" + inkscape:current-layer="layer3" units="px" inkscape:showpageshadow="2" inkscape:deskcolor="#505050"> @@ -69,7 +69,7 @@ id="layer1" style="display:inline"> @@ -105,31 +105,31 @@ From a0af4bf36c9655d54629ab7d96b3a897717003ea Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Wed, 27 Jul 2022 17:19:04 +0200 Subject: [PATCH 02/18] :sparkles: Future-proof query selector, clap CLI interface --- Cargo.toml | 2 + species/blobfox/assets/reach_aww.svg | 194 ++++++++++ species/blobfox/species.toml | 1 + species/blobfox/templates/base.mustache | 10 +- species/blobfox/templates/eyes-aww.mustache | 4 + species/blobfox/templates/eyes.mustache | 4 +- species/blobfox/templates/footer.mustache | 1 + .../blobfox/templates/hands-reach.mustache | 4 + species/blobfox/templates/header.mustache | 1 + species/blobfox/templates/mouth-w.mustache | 2 +- species/blobfox/templates/nose-aww.mustache | 4 + species/blobfox/templates/nose.mustache | 4 +- species/blobfox/variants/base.mustache | 12 +- species/blobfox/variants/owo.mustache | 10 + species/blobfox/variants/reach.mustache | 11 + species/blobfox/variants/reach_aww.mustache | 11 + src/main.rs | 58 ++- src/parse.rs | 5 +- src/template.rs | 18 +- vector/blobfox_reach_aww.svg | 346 ++++++++++-------- 20 files changed, 520 insertions(+), 182 deletions(-) create mode 100644 species/blobfox/assets/reach_aww.svg create mode 100644 species/blobfox/templates/eyes-aww.mustache create mode 100644 species/blobfox/templates/hands-reach.mustache create mode 100644 species/blobfox/templates/nose-aww.mustache create mode 100644 species/blobfox/variants/owo.mustache create mode 100644 species/blobfox/variants/reach.mustache create mode 100644 species/blobfox/variants/reach_aww.mustache diff --git a/Cargo.toml b/Cargo.toml index 8febedd..48fb261 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,5 @@ serde = { version = "1.0.140", features = ["derive"] } toml = "0.5.9" xmltree = "0.10.3" mustache = { git = "https://git.shadamethyst.xyz/adri326/rust-mustache.git" } +clap = { version = "3.2", features = ["derive"] } +mkdirp = "1.0.0" diff --git a/species/blobfox/assets/reach_aww.svg b/species/blobfox/assets/reach_aww.svg new file mode 100644 index 0000000..8af0283 --- /dev/null +++ b/species/blobfox/assets/reach_aww.svg @@ -0,0 +1,194 @@ + + + + + blobfox_reach_aww + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Blobfox team (https://git.shadamethyst.xyz/adri326/blobfox), licensed under the Apache 2.0 Licenseblobfox_reach_awwFeuerfuchshttps://git.shadamethyst.xyz/adri326/blobfoxShad Amethyst + + diff --git a/species/blobfox/species.toml b/species/blobfox/species.toml index f13fa38..dc894a6 100644 --- a/species/blobfox/species.toml +++ b/species/blobfox/species.toml @@ -1 +1,2 @@ # Add options in here as needs be +name = "blobfox" diff --git a/species/blobfox/templates/base.mustache b/species/blobfox/templates/base.mustache index cbeaa72..e653823 100644 --- a/species/blobfox/templates/base.mustache +++ b/species/blobfox/templates/base.mustache @@ -1,7 +1,7 @@ - {{#base}}body{{/base}} - {{#base}}left-ear{{/base}} - {{#base}}hair{{/base}} - {{#base}}right-ear{{/base}} - {{#base}}right-ear-fluff{{/base}} + {{#base}}#body{{/base}} + {{#base}}#left-ear{{/base}} + {{#base}}#hair{{/base}} + {{#base}}#right-ear{{/base}} + {{#base}}#right-ear-fluff{{/base}} diff --git a/species/blobfox/templates/eyes-aww.mustache b/species/blobfox/templates/eyes-aww.mustache new file mode 100644 index 0000000..7a818b2 --- /dev/null +++ b/species/blobfox/templates/eyes-aww.mustache @@ -0,0 +1,4 @@ + + {{#reach_aww}}#left-eye{{/reach_aww}} + {{#reach_aww}}#right-eye{{/reach_aww}} + diff --git a/species/blobfox/templates/eyes.mustache b/species/blobfox/templates/eyes.mustache index 0581ed4..935bf81 100644 --- a/species/blobfox/templates/eyes.mustache +++ b/species/blobfox/templates/eyes.mustache @@ -1,4 +1,4 @@ - {{#base}}left-eye{{/base}} - {{#base}}right-eye{{/base}} + {{#base}}#left-eye{{/base}} + {{#base}}#right-eye{{/base}} diff --git a/species/blobfox/templates/footer.mustache b/species/blobfox/templates/footer.mustache index b590cc4..c2667cb 100644 --- a/species/blobfox/templates/footer.mustache +++ b/species/blobfox/templates/footer.mustache @@ -1 +1,2 @@ + diff --git a/species/blobfox/templates/hands-reach.mustache b/species/blobfox/templates/hands-reach.mustache new file mode 100644 index 0000000..591bb43 --- /dev/null +++ b/species/blobfox/templates/hands-reach.mustache @@ -0,0 +1,4 @@ + + {{#reach_aww}}#left-hand{{/reach_aww}} + {{#reach_aww}}#right-hand{{/reach_aww}} + diff --git a/species/blobfox/templates/header.mustache b/species/blobfox/templates/header.mustache index 05199ce..54ae65c 100644 --- a/species/blobfox/templates/header.mustache +++ b/species/blobfox/templates/header.mustache @@ -1,2 +1,3 @@ {{variant_name}} + diff --git a/species/blobfox/templates/mouth-w.mustache b/species/blobfox/templates/mouth-w.mustache index c0e10bc..8e1d73a 100644 --- a/species/blobfox/templates/mouth-w.mustache +++ b/species/blobfox/templates/mouth-w.mustache @@ -1 +1 @@ -{{#base}}mouth{{/base}} +{{#base}}#mouth{{/base}} diff --git a/species/blobfox/templates/nose-aww.mustache b/species/blobfox/templates/nose-aww.mustache new file mode 100644 index 0000000..11abd48 --- /dev/null +++ b/species/blobfox/templates/nose-aww.mustache @@ -0,0 +1,4 @@ + + {{#base}}#nose{{/base}} + {{#reach_aww}}#nose-outline{{/reach_aww}} + diff --git a/species/blobfox/templates/nose.mustache b/species/blobfox/templates/nose.mustache index 815ce1e..ea89fd8 100644 --- a/species/blobfox/templates/nose.mustache +++ b/species/blobfox/templates/nose.mustache @@ -1,4 +1,4 @@ - {{#base}}nose-outline{{/base}} - {{#base}}nose{{/base}} + {{#base}}#nose-outline{{/base}} + {{#base}}#nose{{/base}} diff --git a/species/blobfox/variants/base.mustache b/species/blobfox/variants/base.mustache index 30488d9..c1ab55d 100644 --- a/species/blobfox/variants/base.mustache +++ b/species/blobfox/variants/base.mustache @@ -1,6 +1,10 @@ {{>header}} - {{>base}} - {{>eyes}} - {{>nose}} - {{>mouth-w}} + + {{>base}} + + + {{>eyes}} + {{>nose}} + {{>mouth-w}} + {{>footer}} diff --git a/species/blobfox/variants/owo.mustache b/species/blobfox/variants/owo.mustache new file mode 100644 index 0000000..d262dac --- /dev/null +++ b/species/blobfox/variants/owo.mustache @@ -0,0 +1,10 @@ +{{>header}} + + {{>base}} + + + {{>eyes-aww}} + {{>nose-aww}} + {{>mouth-w}} + +{{>footer}} diff --git a/species/blobfox/variants/reach.mustache b/species/blobfox/variants/reach.mustache new file mode 100644 index 0000000..56fa5ea --- /dev/null +++ b/species/blobfox/variants/reach.mustache @@ -0,0 +1,11 @@ +{{>header}} + + {{>base}} + {{>hands-reach}} + + + {{>eyes}} + {{>nose}} + {{>mouth-w}} + +{{>footer}} diff --git a/species/blobfox/variants/reach_aww.mustache b/species/blobfox/variants/reach_aww.mustache new file mode 100644 index 0000000..695f33e --- /dev/null +++ b/species/blobfox/variants/reach_aww.mustache @@ -0,0 +1,11 @@ +{{>header}} + + {{>base}} + {{>hands-reach}} + + + {{>eyes-aww}} + {{>nose-aww}} + {{>mouth-w}} + +{{>footer}} diff --git a/src/main.rs b/src/main.rs index 9b82bfa..4fc5398 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,6 @@ use std::sync::Arc; +use clap::Parser; +use std::path::PathBuf; pub mod parse; use parse::*; @@ -7,10 +9,56 @@ pub mod template; use template::*; fn main() { - let species = Arc::new(dbg!(load_species("species/blobfox")).unwrap()); + let args = Args::parse(); + + let species = Arc::new(load_species(args.decl).unwrap()); let context = RenderingContext::new(species); - let template = context.compile("species/blobfox/variants/base.svg").unwrap(); - let rendered = template.render_data_to_string(&context.get_data()).unwrap(); - println!("{}", rendered); - std::fs::write("./test.svg", rendered).unwrap(); + + let output_dir = args.output_dir.unwrap_or(PathBuf::from("output/vector/")); + + mkdirp::mkdirp(output_dir.clone()).unwrap(); + + if args.names.is_empty() { + for name in context.species().variants.keys() { + generate_variant(&context, name, &output_dir); + } + } else { + for name in args.names.iter() { + generate_variant(&context, name, &output_dir); + } + } +} + +fn generate_variant(context: &RenderingContext, name: &str, output_dir: &PathBuf) { + if let Some(path) = context.species().variants.get(name) { + match context.compile(path).and_then(|template| { + template.render_data_to_string(&context.get_data()) + }) { + Ok(rendered) => { + let output = output_dir.join(&format!("{}_{}.svg", context.species().name, name)); + std::fs::write(output, rendered).unwrap(); + } + Err(err) => { + eprintln!("Error while rendering {}: {}", name, err); + } + } + } else { + eprintln!("No variant named {}!", name); + } +} + +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +struct Args { + /// A folder containing the declaration from which the emotes should be generated + #[clap(short, long, value_parser)] + decl: PathBuf, + + /// List of the emote names to export + #[clap(value_parser)] + names: Vec, + + /// Output directory + #[clap(short, long, value_parser)] + output_dir: Option, } diff --git a/src/parse.rs b/src/parse.rs index ea2214b..f438616 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,4 +1,4 @@ -use xmltree::{XMLNode, Element}; +use xmltree::{Element}; use serde::{Serialize, Deserialize}; use std::path::{PathBuf, Path}; use std::collections::HashMap; @@ -28,6 +28,9 @@ pub struct SpeciesDecl { /// Imports xml and svg files from this folder if they aren't found pub base: Option, + /// The name of the species + pub name: String, + #[serde(skip)] pub templates: HashMap, diff --git a/src/template.rs b/src/template.rs index 4bf21e1..16ace95 100644 --- a/src/template.rs +++ b/src/template.rs @@ -111,6 +111,10 @@ impl RenderingContext { None } } + + pub fn species(&self) -> Arc { + Arc::clone(&self.species) + } } impl PartialLoader for RenderingContext { @@ -133,11 +137,13 @@ pub fn query_selector(svg: Element, pattern: &str) -> Option { for child in svg.children { if let XMLNode::Element(child) = child { - if child.attributes.get("id").map(|id| id == pattern).unwrap_or(false) { - return Some(child); - } else if child.children.len() > 0 { - if let Some(res) = query_selector(child, pattern) { - return Some(res); + if let ("#", pattern_id) = pattern.split_at(1) { + if child.attributes.get("id").map(|id| id == pattern_id).unwrap_or(false) { + return Some(child); + } else if child.children.len() > 0 { + if let Some(res) = query_selector(child, pattern) { + return Some(res); + } } } } @@ -152,7 +158,7 @@ pub fn xml_to_string(element: Element) -> Option { config.perform_indent = true; config.write_document_declaration = false; - element.write_with_config(&mut s, config); + element.write_with_config(&mut s, config).ok()?; String::from_utf8(s).ok() } diff --git a/vector/blobfox_reach_aww.svg b/vector/blobfox_reach_aww.svg index adaea0e..8af0283 100644 --- a/vector/blobfox_reach_aww.svg +++ b/vector/blobfox_reach_aww.svg @@ -2,159 +2,193 @@ blobfox_reach_awwBlobfox team (https://git.shadamethyst.xyz/adri326/blobfox), licensed under the Apache 2.0 Licenseblobfox_reach_awwFeuerfuchshttps://git.shadamethyst.xyz/adri326/blobfoxShad Amethyst + width="128" + height="128" + viewBox="0 0 33.866668 33.866668" + version="1.1" + id="svg5" + inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)" + sodipodi:docname="blobfox_reach_aww.svg" + xml:space="preserve" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> + blobfox_reach_aww + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Blobfox team (https://git.shadamethyst.xyz/adri326/blobfox), licensed under the Apache 2.0 Licenseblobfox_reach_awwFeuerfuchshttps://git.shadamethyst.xyz/adri326/blobfoxShad Amethyst + + From 727496a9d9ffa11e1a9d657fa1e93b11db5a8122 Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Wed, 27 Jul 2022 18:04:43 +0200 Subject: [PATCH 03/18] :sparkles: SVG cleaning script, snug --- Cargo.toml | 1 + species/blobfox/assets/snug.svg | 49 ++++++ species/blobfox/templates/base-snug.mustache | 7 + species/blobfox/templates/eyes-snug.mustache | 4 + species/blobfox/templates/nose-snug.mustache | 4 + species/blobfox/templates/tail-snug.mustache | 4 + species/blobfox/variants/snug.mustache | 10 ++ species/blobfox/variants/snug_aww.mustache | 10 ++ src/bin/clean.rs | 69 ++++++++ vector/blobfox_snug.svg | 164 ++++++------------- 10 files changed, 206 insertions(+), 116 deletions(-) create mode 100644 species/blobfox/assets/snug.svg create mode 100644 species/blobfox/templates/base-snug.mustache create mode 100644 species/blobfox/templates/eyes-snug.mustache create mode 100644 species/blobfox/templates/nose-snug.mustache create mode 100644 species/blobfox/templates/tail-snug.mustache create mode 100644 species/blobfox/variants/snug.mustache create mode 100644 species/blobfox/variants/snug_aww.mustache create mode 100644 src/bin/clean.rs diff --git a/Cargo.toml b/Cargo.toml index 48fb261..fa82c60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ name = "blobfox-template" version = "0.1.0" edition = "2021" +default-run = "blobfox-template" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/species/blobfox/assets/snug.svg b/species/blobfox/assets/snug.svg new file mode 100644 index 0000000..a158918 --- /dev/null +++ b/species/blobfox/assets/snug.svg @@ -0,0 +1,49 @@ + + + blobfox_snug + + + + + + + + + + + + + + + + + + + + + + + + + Blobfox team (https://git.shadamethyst.xyz/adri326/blobfox), licensed under the Apache 2.0 License + + + blobfox_snug + + + Feuerfuchs + + + https://git.shadamethyst.xyz/adri326/blobfox + + + Shad Amethyst + + + + + + diff --git a/species/blobfox/templates/base-snug.mustache b/species/blobfox/templates/base-snug.mustache new file mode 100644 index 0000000..b94b3c9 --- /dev/null +++ b/species/blobfox/templates/base-snug.mustache @@ -0,0 +1,7 @@ + + {{! TODO: update to the latest standard }} + {{#snug}}#left-ear{{/snug}} + {{#snug}}#right-ear{{/snug}} + {{#snug}}#right-ear-fluff{{/snug}} + {{#snug}}#body{{/snug}} + diff --git a/species/blobfox/templates/eyes-snug.mustache b/species/blobfox/templates/eyes-snug.mustache new file mode 100644 index 0000000..231190e --- /dev/null +++ b/species/blobfox/templates/eyes-snug.mustache @@ -0,0 +1,4 @@ + + {{#snug}}#left-eye{{/snug}} + {{#snug}}#right-eye{{/snug}} + diff --git a/species/blobfox/templates/nose-snug.mustache b/species/blobfox/templates/nose-snug.mustache new file mode 100644 index 0000000..f307896 --- /dev/null +++ b/species/blobfox/templates/nose-snug.mustache @@ -0,0 +1,4 @@ + + {{#snug}}#nose{{/snug}} + {{#snug}}#nose-outline{{/snug}} + diff --git a/species/blobfox/templates/tail-snug.mustache b/species/blobfox/templates/tail-snug.mustache new file mode 100644 index 0000000..fd1cbac --- /dev/null +++ b/species/blobfox/templates/tail-snug.mustache @@ -0,0 +1,4 @@ + + {{#snug}}#tail{{/snug}} + {{#snug}}#tail-outline{{/snug}} + diff --git a/species/blobfox/variants/snug.mustache b/species/blobfox/variants/snug.mustache new file mode 100644 index 0000000..9cce27d --- /dev/null +++ b/species/blobfox/variants/snug.mustache @@ -0,0 +1,10 @@ +{{>header}} + + {{>base-snug}} + {{>tail-snug}} + + + {{>eyes-snug}} + {{>nose-snug}} + +{{>footer}} diff --git a/species/blobfox/variants/snug_aww.mustache b/species/blobfox/variants/snug_aww.mustache new file mode 100644 index 0000000..70431a1 --- /dev/null +++ b/species/blobfox/variants/snug_aww.mustache @@ -0,0 +1,10 @@ +{{>header}} + + {{>base-snug}} + {{>tail-snug}} + + + {{>eyes-aww}} + {{>nose-aww}} + +{{>footer}} diff --git a/src/bin/clean.rs b/src/bin/clean.rs new file mode 100644 index 0000000..0b9aefd --- /dev/null +++ b/src/bin/clean.rs @@ -0,0 +1,69 @@ +use xmltree::{XMLNode, Element}; +use clap::Parser; +use std::collections::HashMap; +use std::path::PathBuf; + +fn main() { + let args = Args::parse(); + + for path in args.files { + let file = std::fs::File::open(path.clone()).unwrap_or_else(|err| { + panic!("Error while reading {}: {}", path.display(), err); + }); + let mut element = Element::parse(file).expect("Couldn't parse SVG!"); + + clean(&mut element); + + let mut s: Vec = Vec::new(); + element.write(&mut s).expect("Couldn't export SVG!"); + + std::fs::write(path.clone(), s).unwrap_or_else(|err| { + panic!("Error while writing {}: {}", path.display(), err); + }); + } +} + +fn clean(element: &mut Element) { + let mut counts: HashMap = HashMap::new(); + + fn count_rec(element: &Element, counts: &mut HashMap) { + if let Some(label) = element.attributes.get("label") { + if let Some(count) = counts.get_mut(label) { + *count += 1; + } else { + counts.insert(label.to_string(), 1); + } + } + + for child in element.children.iter() { + if let XMLNode::Element(ref child) = child { + count_rec(child, counts); + } + } + } + + count_rec(element, &mut counts); + + fn update_rec(element: &mut Element, counts: &HashMap) { + if let Some(label) = element.attributes.get("label") { + if let Some(1) = counts.get(label) { + element.attributes.insert("id".to_string(), label.to_string()); + } + } + + for child in element.children.iter_mut() { + if let XMLNode::Element(ref mut child) = child { + update_rec(child, counts); + } + } + } + + update_rec(element, &counts); +} + +#[derive(Parser)] +#[clap(author, version, about, long_about = None)] +struct Args { + #[clap(value_parser)] + files: Vec, +} diff --git a/vector/blobfox_snug.svg b/vector/blobfox_snug.svg index 1be4320..d1430df 100644 --- a/vector/blobfox_snug.svg +++ b/vector/blobfox_snug.svg @@ -1,118 +1,50 @@ - -blobfox_snugBlobfox team (https://git.shadamethyst.xyz/adri326/blobfox), licensed under the Apache 2.0 Licenseblobfox_snugFeuerfuchshttps://git.shadamethyst.xyz/adri326/blobfoxShad Amethyst + + blobfox_snug + + + + + + + + + + + + + + + + + + + + + + + + + Blobfox team (https://git.shadamethyst.xyz/adri326/blobfox), licensed under the Apache 2.0 License + + + blobfox_snug + + + Feuerfuchs + + + https://git.shadamethyst.xyz/adri326/blobfox + + + Shad Amethyst + + + + + + From 3dd89016ab73dd352428d57f5756abc7c9cc78ad Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Wed, 27 Jul 2022 19:10:23 +0200 Subject: [PATCH 04/18] :sparkles: boop_owo --- species/blobfox/assets/boop_owo.svg | 1 + species/blobfox/templates/base-owo.mustache | 8 + species/blobfox/templates/eyes-owo.mustache | 4 + species/blobfox/templates/hand-boop.mustache | 1 + species/blobfox/templates/header.mustache | 33 +++- species/blobfox/templates/nose-owo.mustache | 4 + species/blobfox/variants/boop.mustache | 11 ++ species/blobfox/variants/boop_aww.mustache | 11 ++ species/blobfox/variants/boop_owo.mustache | 11 ++ species/blobfox/variants/happy.mustache | 10 ++ species/blobfox/variants/owo.mustache | 6 +- .../blobfox/variants/snug_boop_owo.mustache | 11 ++ species/blobfox/variants/snug_owo.mustache | 10 ++ vector/blobfox_boop_owo.svg | 163 ++++++++++++++++++ 14 files changed, 280 insertions(+), 4 deletions(-) create mode 100644 species/blobfox/assets/boop_owo.svg create mode 100644 species/blobfox/templates/base-owo.mustache create mode 100644 species/blobfox/templates/eyes-owo.mustache create mode 100644 species/blobfox/templates/hand-boop.mustache create mode 100644 species/blobfox/templates/nose-owo.mustache create mode 100644 species/blobfox/variants/boop.mustache create mode 100644 species/blobfox/variants/boop_aww.mustache create mode 100644 species/blobfox/variants/boop_owo.mustache create mode 100644 species/blobfox/variants/happy.mustache create mode 100644 species/blobfox/variants/snug_boop_owo.mustache create mode 100644 species/blobfox/variants/snug_owo.mustache create mode 100644 vector/blobfox_boop_owo.svg diff --git a/species/blobfox/assets/boop_owo.svg b/species/blobfox/assets/boop_owo.svg new file mode 100644 index 0000000..f9557ef --- /dev/null +++ b/species/blobfox/assets/boop_owo.svg @@ -0,0 +1 @@ +blobfoxBlobfox team (https://git.shadamethyst.xyz/adri326/blobfox), licensed under the Apache 2.0 LicenseblobfoxFeuerfuchshttps://git.shadamethyst.xyz/adri326/blobfoxShad Amethyst \ No newline at end of file diff --git a/species/blobfox/templates/base-owo.mustache b/species/blobfox/templates/base-owo.mustache new file mode 100644 index 0000000..2142de4 --- /dev/null +++ b/species/blobfox/templates/base-owo.mustache @@ -0,0 +1,8 @@ + + {{#base}}#body{{/base}} + {{#boop_owo}}#left-ear{{/boop_owo}} + {{#boop_owo}}#left-ear-fluff{{/boop_owo}} + {{#base}}#hair{{/base}} + {{#base}}#right-ear{{/base}} + {{#base}}#right-ear-fluff{{/base}} + diff --git a/species/blobfox/templates/eyes-owo.mustache b/species/blobfox/templates/eyes-owo.mustache new file mode 100644 index 0000000..f3056ad --- /dev/null +++ b/species/blobfox/templates/eyes-owo.mustache @@ -0,0 +1,4 @@ + + {{#boop_owo}}#left-eye{{/boop_owo}} + {{#boop_owo}}#right-eye{{/boop_owo}} + diff --git a/species/blobfox/templates/hand-boop.mustache b/species/blobfox/templates/hand-boop.mustache new file mode 100644 index 0000000..716d758 --- /dev/null +++ b/species/blobfox/templates/hand-boop.mustache @@ -0,0 +1 @@ +{{#boop_owo}}#hand-boop{{/boop_owo}} diff --git a/species/blobfox/templates/header.mustache b/species/blobfox/templates/header.mustache index 54ae65c..5c94ac5 100644 --- a/species/blobfox/templates/header.mustache +++ b/species/blobfox/templates/header.mustache @@ -1,3 +1,34 @@ - + + + + + {{variant_name}} diff --git a/species/blobfox/templates/nose-owo.mustache b/species/blobfox/templates/nose-owo.mustache new file mode 100644 index 0000000..6655460 --- /dev/null +++ b/species/blobfox/templates/nose-owo.mustache @@ -0,0 +1,4 @@ + + {{#base}}#nose{{/base}} + {{#boop_owo}}#nose-outline{{/boop_owo}} + diff --git a/species/blobfox/variants/boop.mustache b/species/blobfox/variants/boop.mustache new file mode 100644 index 0000000..caa2ec3 --- /dev/null +++ b/species/blobfox/variants/boop.mustache @@ -0,0 +1,11 @@ +{{>header}} + + {{>base}} + + + {{>eyes}} + {{>nose}} + {{>mouth-w}} + {{>hand-boop}} + +{{>footer}} diff --git a/species/blobfox/variants/boop_aww.mustache b/species/blobfox/variants/boop_aww.mustache new file mode 100644 index 0000000..507867d --- /dev/null +++ b/species/blobfox/variants/boop_aww.mustache @@ -0,0 +1,11 @@ +{{>header}} + + {{>base}} + + + {{>eyes-aww}} + {{>nose-aww}} + {{>mouth-w}} + {{>hand-boop}} + +{{>footer}} diff --git a/species/blobfox/variants/boop_owo.mustache b/species/blobfox/variants/boop_owo.mustache new file mode 100644 index 0000000..e6ffd0b --- /dev/null +++ b/species/blobfox/variants/boop_owo.mustache @@ -0,0 +1,11 @@ +{{>header}} + + {{>base-owo}} + + + {{>eyes-owo}} + {{>nose-owo}} + {{>mouth-w}} + {{>hand-boop}} + +{{>footer}} diff --git a/species/blobfox/variants/happy.mustache b/species/blobfox/variants/happy.mustache new file mode 100644 index 0000000..c9e1847 --- /dev/null +++ b/species/blobfox/variants/happy.mustache @@ -0,0 +1,10 @@ +{{>header}} + + {{>base}} + + + {{>eyes-snug}} + {{>nose-snug}} + {{>mouth-w}} + +{{>footer}} diff --git a/species/blobfox/variants/owo.mustache b/species/blobfox/variants/owo.mustache index d262dac..ac0a191 100644 --- a/species/blobfox/variants/owo.mustache +++ b/species/blobfox/variants/owo.mustache @@ -1,10 +1,10 @@ {{>header}} - {{>base}} + {{>base-owo}} - {{>eyes-aww}} - {{>nose-aww}} + {{>eyes-owo}} + {{>nose-owo}} {{>mouth-w}} {{>footer}} diff --git a/species/blobfox/variants/snug_boop_owo.mustache b/species/blobfox/variants/snug_boop_owo.mustache new file mode 100644 index 0000000..5ebedf9 --- /dev/null +++ b/species/blobfox/variants/snug_boop_owo.mustache @@ -0,0 +1,11 @@ +{{>header}} + + {{>base-snug}} + {{>tail-snug}} + + + {{>eyes-owo}} + {{>nose-owo}} + {{>hand-boop}} + +{{>footer}} diff --git a/species/blobfox/variants/snug_owo.mustache b/species/blobfox/variants/snug_owo.mustache new file mode 100644 index 0000000..2c03160 --- /dev/null +++ b/species/blobfox/variants/snug_owo.mustache @@ -0,0 +1,10 @@ +{{>header}} + + {{>base-snug}} + {{>tail-snug}} + + + {{>eyes-owo}} + {{>nose-owo}} + +{{>footer}} diff --git a/vector/blobfox_boop_owo.svg b/vector/blobfox_boop_owo.svg new file mode 100644 index 0000000..8223ebc --- /dev/null +++ b/vector/blobfox_boop_owo.svg @@ -0,0 +1,163 @@ + + + +blobfoxBlobfox team (https://git.shadamethyst.xyz/adri326/blobfox), licensed under the Apache 2.0 LicenseblobfoxFeuerfuchshttps://git.shadamethyst.xyz/adri326/blobfoxShad Amethyst From 45c6895833c62f153e0c3f082f5ff520740e8895 Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Thu, 28 Jul 2022 10:46:58 +0200 Subject: [PATCH 05/18] :sparkles: viewBox fitting and resvg rendering --- Cargo.toml | 4 ++ src/export.rs | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 40 +++++++++++----- 3 files changed, 161 insertions(+), 11 deletions(-) create mode 100644 src/export.rs diff --git a/Cargo.toml b/Cargo.toml index fa82c60..0e69d91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,7 @@ xmltree = "0.10.3" mustache = { git = "https://git.shadamethyst.xyz/adri326/rust-mustache.git" } clap = { version = "3.2", features = ["derive"] } mkdirp = "1.0.0" +resvg = "0.23" +usvg = "0.23" +tiny-skia = "0.6" +png = "0.17" diff --git a/src/export.rs b/src/export.rs new file mode 100644 index 0000000..715db2a --- /dev/null +++ b/src/export.rs @@ -0,0 +1,128 @@ +use usvg::{ + Tree, + NodeExt, + Options, +}; +use xmltree::{Element}; +use std::path::{PathBuf}; +use std::collections::HashSet; + +#[derive(Debug)] +pub enum ExportError { + Xml(xmltree::Error), + XmlParse(xmltree::ParseError), + Usvg(usvg::Error), + Io(PathBuf, std::io::Error), + NoBBox, + Utf8(std::string::FromUtf8Error), + Encode(png::EncodingError), +} + +impl From for ExportError { + fn from(err: xmltree::ParseError) -> Self { + Self::XmlParse(err) + } +} + +impl From for ExportError { + fn from(err: xmltree::Error) -> Self { + Self::Xml(err) + } +} + +impl From for ExportError { + fn from(err: usvg::Error) -> Self { + Self::Usvg(err) + } +} + +impl From for ExportError { + fn from(err: std::string::FromUtf8Error) -> Self { + Self::Utf8(err) + } +} + +impl From for ExportError { + fn from(err: png::EncodingError) -> Self { + Self::Encode(err) + } +} + +pub fn get_new_bbox(svg: &Tree) -> Option<(f64, f64, f64, f64)> { + let bbox = svg.root().calculate_bbox()?; + if bbox.width() > bbox.height() { + let y = bbox.y() - (bbox.width() - bbox.height()) / 2.0; + + Some((bbox.x(), y, bbox.width(), bbox.width())) + } else { + let x = bbox.x() - (bbox.height() - bbox.width()) / 2.0; + + Some((x, bbox.y(), bbox.height(), bbox.height())) + } +} + +fn get_usvg(svg_str: &str) -> Result { + let usvg_options = Options::default(); + Tree::from_str(svg_str, &usvg_options.to_ref()) +} + +fn get_xml(svg_str: &str) -> Result { + Element::parse(svg_str.as_bytes()) +} + +fn xml_to_str(svg_xml: &Element) -> Result { + let mut s: Vec = Vec::new(); + + svg_xml.write(&mut s)?; + + Ok(String::from_utf8(s)?) +} + +pub fn resize(svg_str: String) -> Result { + if let Some(new_bbox) = get_new_bbox(&get_usvg(&svg_str)?) { + let mut svg_xml = get_xml(&svg_str)?; + svg_xml.attributes.insert( + "viewBox".to_string(), + format!("{} {} {} {}", new_bbox.0, new_bbox.1, new_bbox.2, new_bbox.3), + ); + + xml_to_str(&svg_xml) + } else { + Err(ExportError::NoBBox) + } +} + +pub fn export( + mut svg_str: String, + output_dir: &PathBuf, + output_name: String, + args: &super::Args, +) -> Result<(), ExportError> { + if args.resize { + svg_str = resize(svg_str)?; + } + + mkdirp::mkdirp(output_dir.join("vector")).unwrap(); + + let output = output_dir.join(&format!("vector/{}.svg", output_name)); + std::fs::write(output.clone(), svg_str.clone()).map_err(|err| ExportError::Io(output, err))?; + + let svg_usvg = get_usvg(&svg_str)?; + for resolution in args.dim.iter().copied().filter(|r| *r != 0).collect::>() { + mkdirp::mkdirp(output_dir.join(&format!("{}", resolution))).unwrap(); + let output = output_dir.join(&format!("{}/{}.png", resolution, output_name)); + + let mut image = tiny_skia::Pixmap::new(resolution, resolution).unwrap(); + + resvg::render( + &svg_usvg, + usvg::FitTo::Width(resolution), + tiny_skia::Transform::identity(), + image.as_mut() + ).unwrap(); + + image.save_png(output)?; + } + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 4fc5398..e0d3097 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,35 +8,45 @@ use parse::*; pub mod template; use template::*; +pub mod export; +use export::*; + fn main() { let args = Args::parse(); - let species = Arc::new(load_species(args.decl).unwrap()); + let species = Arc::new(load_species(args.decl.clone()).unwrap()); let context = RenderingContext::new(species); - let output_dir = args.output_dir.unwrap_or(PathBuf::from("output/vector/")); - - mkdirp::mkdirp(output_dir.clone()).unwrap(); + let output_dir = args.output_dir.clone().unwrap_or(PathBuf::from("output/")); if args.names.is_empty() { for name in context.species().variants.keys() { - generate_variant(&context, name, &output_dir); + generate_variant(&context, name, &output_dir, &args); } } else { for name in args.names.iter() { - generate_variant(&context, name, &output_dir); + generate_variant(&context, name, &output_dir, &args); } } } -fn generate_variant(context: &RenderingContext, name: &str, output_dir: &PathBuf) { +fn generate_variant(context: &RenderingContext, name: &str, output_dir: &PathBuf, args: &Args) { if let Some(path) = context.species().variants.get(name) { match context.compile(path).and_then(|template| { template.render_data_to_string(&context.get_data()) }) { - Ok(rendered) => { - let output = output_dir.join(&format!("{}_{}.svg", context.species().name, name)); - std::fs::write(output, rendered).unwrap(); + Ok(svg) => { + match export( + svg, + output_dir, + format!("{}_{}", context.species().name, name), + args + ) { + Ok(_) => {} + Err(err) => { + eprintln!("Error while rendering {}: {:?}", name, err); + } + } } Err(err) => { eprintln!("Error while rendering {}: {}", name, err); @@ -49,7 +59,7 @@ fn generate_variant(context: &RenderingContext, name: &str, output_dir: &PathBuf #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] -struct Args { +pub struct Args { /// A folder containing the declaration from which the emotes should be generated #[clap(short, long, value_parser)] decl: PathBuf, @@ -58,6 +68,14 @@ struct Args { #[clap(value_parser)] names: Vec, + /// Automatically resize the SVG's viewBox, defaults to true + #[clap(short, long, value_parser, default_value = "true")] + resize: bool, + + /// Dimension to export the images as; can be specified multiple times + #[clap(long, value_parser)] + dim: Vec, + /// Output directory #[clap(short, long, value_parser)] output_dir: Option, From de48f936e95b86e569ffdb20d2faf10ef4937017 Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Thu, 28 Jul 2022 13:16:41 +0200 Subject: [PATCH 06/18] :sparkles: Species inheritance, set-fill --- species/blobcat/assets/base.svg | 47 +++++++++++++++++++ species/blobcat/species.toml | 2 + species/blobcat/templates/base-extra.mustache | 1 + .../blobcat/templates/hands-reach.mustache | 4 ++ .../blobcat/templates/mouth-reach.mustache | 1 + species/blobcat/templates/mouth-w.mustache | 1 + species/blobcat/templates/nose-aww.mustache | 1 + species/blobcat/templates/nose-snug.mustache | 1 + species/blobcat/templates/nose.mustache | 1 + species/blobcat/templates/whiskers.mustache | 6 +++ species/blobfox/templates/base-extra.mustache | 2 + species/blobfox/templates/base-owo.mustache | 2 +- species/blobfox/templates/base.mustache | 2 +- .../blobfox/templates/reach-extra.mustache | 2 + species/blobfox/variants/base.mustache | 1 + species/blobfox/variants/boop.mustache | 1 + species/blobfox/variants/boop_aww.mustache | 1 + species/blobfox/variants/boop_owo.mustache | 1 + species/blobfox/variants/happy.mustache | 1 + species/blobfox/variants/owo.mustache | 1 + species/blobfox/variants/reach.mustache | 1 + species/blobfox/variants/reach_aww.mustache | 1 + src/export.rs | 21 ++++++--- src/main.rs | 6 +-- src/parse.rs | 21 +++++++-- src/template.rs | 46 ++++++++++++++++++ 26 files changed, 161 insertions(+), 14 deletions(-) create mode 100644 species/blobcat/assets/base.svg create mode 100644 species/blobcat/species.toml create mode 100644 species/blobcat/templates/base-extra.mustache create mode 100644 species/blobcat/templates/hands-reach.mustache create mode 100644 species/blobcat/templates/mouth-reach.mustache create mode 100644 species/blobcat/templates/mouth-w.mustache create mode 100644 species/blobcat/templates/nose-aww.mustache create mode 100644 species/blobcat/templates/nose-snug.mustache create mode 100644 species/blobcat/templates/nose.mustache create mode 100644 species/blobcat/templates/whiskers.mustache create mode 100644 species/blobfox/templates/base-extra.mustache create mode 100644 species/blobfox/templates/reach-extra.mustache diff --git a/species/blobcat/assets/base.svg b/species/blobcat/assets/base.svg new file mode 100644 index 0000000..3ac4901 --- /dev/null +++ b/species/blobcat/assets/base.svg @@ -0,0 +1,47 @@ + + + blobcat + + + + + + + + + + + + + + + + + + + + + + + + + + Blobfox team (https://git.shadamethyst.xyz/adri326/blobfox), licensed under the Apache 2.0 License + + + blobcat + + + Feuerfuchs + + + https://git.shadamethyst.xyz/adri326/blobfox + + + + diff --git a/species/blobcat/species.toml b/species/blobcat/species.toml new file mode 100644 index 0000000..5e598ca --- /dev/null +++ b/species/blobcat/species.toml @@ -0,0 +1,2 @@ +name = "blobcat" +base = "../blobfox/" diff --git a/species/blobcat/templates/base-extra.mustache b/species/blobcat/templates/base-extra.mustache new file mode 100644 index 0000000..ba90093 --- /dev/null +++ b/species/blobcat/templates/base-extra.mustache @@ -0,0 +1 @@ +{{>whiskers}} diff --git a/species/blobcat/templates/hands-reach.mustache b/species/blobcat/templates/hands-reach.mustache new file mode 100644 index 0000000..58d4100 --- /dev/null +++ b/species/blobcat/templates/hands-reach.mustache @@ -0,0 +1,4 @@ + + {{#set-fill}}#fcc21b|{{#reach_aww}}#left-hand{{/reach_aww}}{{/set-fill}} + {{#set-fill}}#fcc21b|{{#reach_aww}}#right-hand{{/reach_aww}}{{/set-fill}} + diff --git a/species/blobcat/templates/mouth-reach.mustache b/species/blobcat/templates/mouth-reach.mustache new file mode 100644 index 0000000..8e1d73a --- /dev/null +++ b/species/blobcat/templates/mouth-reach.mustache @@ -0,0 +1 @@ +{{#base}}#mouth{{/base}} diff --git a/species/blobcat/templates/mouth-w.mustache b/species/blobcat/templates/mouth-w.mustache new file mode 100644 index 0000000..8e1d73a --- /dev/null +++ b/species/blobcat/templates/mouth-w.mustache @@ -0,0 +1 @@ +{{#base}}#mouth{{/base}} diff --git a/species/blobcat/templates/nose-aww.mustache b/species/blobcat/templates/nose-aww.mustache new file mode 100644 index 0000000..b7d321d --- /dev/null +++ b/species/blobcat/templates/nose-aww.mustache @@ -0,0 +1 @@ + diff --git a/species/blobcat/templates/nose-snug.mustache b/species/blobcat/templates/nose-snug.mustache new file mode 100644 index 0000000..3a79eeb --- /dev/null +++ b/species/blobcat/templates/nose-snug.mustache @@ -0,0 +1 @@ + diff --git a/species/blobcat/templates/nose.mustache b/species/blobcat/templates/nose.mustache new file mode 100644 index 0000000..1b4b8bc --- /dev/null +++ b/species/blobcat/templates/nose.mustache @@ -0,0 +1 @@ + diff --git a/species/blobcat/templates/whiskers.mustache b/species/blobcat/templates/whiskers.mustache new file mode 100644 index 0000000..dfc44f2 --- /dev/null +++ b/species/blobcat/templates/whiskers.mustache @@ -0,0 +1,6 @@ + + {{#base}}#left-whisker{{/base}} + {{#base}}#left-whisker-2{{/base}} + {{#base}}#right-whisker{{/base}} + {{#base}}#right-whisker-2{{/base}} + diff --git a/species/blobfox/templates/base-extra.mustache b/species/blobfox/templates/base-extra.mustache new file mode 100644 index 0000000..5e859b1 --- /dev/null +++ b/species/blobfox/templates/base-extra.mustache @@ -0,0 +1,2 @@ +{{! Should be included after >base if there is no arm, object, etc. on the emote }} + diff --git a/species/blobfox/templates/base-owo.mustache b/species/blobfox/templates/base-owo.mustache index 2142de4..a5e4dec 100644 --- a/species/blobfox/templates/base-owo.mustache +++ b/species/blobfox/templates/base-owo.mustache @@ -1,7 +1,7 @@ - {{#base}}#body{{/base}} {{#boop_owo}}#left-ear{{/boop_owo}} {{#boop_owo}}#left-ear-fluff{{/boop_owo}} + {{#base}}#body{{/base}} {{#base}}#hair{{/base}} {{#base}}#right-ear{{/base}} {{#base}}#right-ear-fluff{{/base}} diff --git a/species/blobfox/templates/base.mustache b/species/blobfox/templates/base.mustache index e653823..515a103 100644 --- a/species/blobfox/templates/base.mustache +++ b/species/blobfox/templates/base.mustache @@ -1,6 +1,6 @@ - {{#base}}#body{{/base}} {{#base}}#left-ear{{/base}} + {{#base}}#body{{/base}} {{#base}}#hair{{/base}} {{#base}}#right-ear{{/base}} {{#base}}#right-ear-fluff{{/base}} diff --git a/species/blobfox/templates/reach-extra.mustache b/species/blobfox/templates/reach-extra.mustache new file mode 100644 index 0000000..1dc66f5 --- /dev/null +++ b/species/blobfox/templates/reach-extra.mustache @@ -0,0 +1,2 @@ +{{! cf. base-extra }} + diff --git a/species/blobfox/variants/base.mustache b/species/blobfox/variants/base.mustache index c1ab55d..a8b5ad8 100644 --- a/species/blobfox/variants/base.mustache +++ b/species/blobfox/variants/base.mustache @@ -1,6 +1,7 @@ {{>header}} {{>base}} + {{>base-extra}} {{>eyes}} diff --git a/species/blobfox/variants/boop.mustache b/species/blobfox/variants/boop.mustache index caa2ec3..e2228af 100644 --- a/species/blobfox/variants/boop.mustache +++ b/species/blobfox/variants/boop.mustache @@ -1,6 +1,7 @@ {{>header}} {{>base}} + {{>base-extra}} {{>eyes}} diff --git a/species/blobfox/variants/boop_aww.mustache b/species/blobfox/variants/boop_aww.mustache index 507867d..63a696f 100644 --- a/species/blobfox/variants/boop_aww.mustache +++ b/species/blobfox/variants/boop_aww.mustache @@ -1,6 +1,7 @@ {{>header}} {{>base}} + {{>base-extra}} {{>eyes-aww}} diff --git a/species/blobfox/variants/boop_owo.mustache b/species/blobfox/variants/boop_owo.mustache index e6ffd0b..d91d892 100644 --- a/species/blobfox/variants/boop_owo.mustache +++ b/species/blobfox/variants/boop_owo.mustache @@ -1,6 +1,7 @@ {{>header}} {{>base-owo}} + {{>base-extra}} {{>eyes-owo}} diff --git a/species/blobfox/variants/happy.mustache b/species/blobfox/variants/happy.mustache index c9e1847..dd826d3 100644 --- a/species/blobfox/variants/happy.mustache +++ b/species/blobfox/variants/happy.mustache @@ -1,6 +1,7 @@ {{>header}} {{>base}} + {{>base-extra}} {{>eyes-snug}} diff --git a/species/blobfox/variants/owo.mustache b/species/blobfox/variants/owo.mustache index ac0a191..4476a58 100644 --- a/species/blobfox/variants/owo.mustache +++ b/species/blobfox/variants/owo.mustache @@ -1,6 +1,7 @@ {{>header}} {{>base-owo}} + {{>base-extra}} {{>eyes-owo}} diff --git a/species/blobfox/variants/reach.mustache b/species/blobfox/variants/reach.mustache index 56fa5ea..8552195 100644 --- a/species/blobfox/variants/reach.mustache +++ b/species/blobfox/variants/reach.mustache @@ -2,6 +2,7 @@ {{>base}} {{>hands-reach}} + {{>reach-extra}} {{>eyes}} diff --git a/species/blobfox/variants/reach_aww.mustache b/species/blobfox/variants/reach_aww.mustache index 695f33e..cb22b44 100644 --- a/species/blobfox/variants/reach_aww.mustache +++ b/species/blobfox/variants/reach_aww.mustache @@ -2,6 +2,7 @@ {{>base}} {{>hands-reach}} + {{>reach-extra}} {{>eyes-aww}} diff --git a/src/export.rs b/src/export.rs index 715db2a..c7c0122 100644 --- a/src/export.rs +++ b/src/export.rs @@ -50,14 +50,23 @@ impl From for ExportError { pub fn get_new_bbox(svg: &Tree) -> Option<(f64, f64, f64, f64)> { let bbox = svg.root().calculate_bbox()?; - if bbox.width() > bbox.height() { - let y = bbox.y() - (bbox.width() - bbox.height()) / 2.0; - Some((bbox.x(), y, bbox.width(), bbox.width())) + // FIXME: remove once https://github.com/RazrFalcon/resvg/issues/528 is fixed + const MARGIN: f64 = 1.0; + + let x = bbox.x() - MARGIN; + let y = bbox.y() - MARGIN; + let width = bbox.width() + MARGIN * 2.0; + let height = bbox.height() + MARGIN * 2.0; + + if width > height { + let y = y - (width - height) / 2.0; + + Some((x, y, width, width)) } else { - let x = bbox.x() - (bbox.height() - bbox.width()) / 2.0; + let x = x - (height - width) / 2.0; - Some((x, bbox.y(), bbox.height(), bbox.height())) + Some((x, y, height, height)) } } @@ -98,7 +107,7 @@ pub fn export( output_name: String, args: &super::Args, ) -> Result<(), ExportError> { - if args.resize { + if !args.no_resize { svg_str = resize(svg_str)?; } diff --git a/src/main.rs b/src/main.rs index e0d3097..9e1984a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -68,9 +68,9 @@ pub struct Args { #[clap(value_parser)] names: Vec, - /// Automatically resize the SVG's viewBox, defaults to true - #[clap(short, long, value_parser, default_value = "true")] - resize: bool, + /// Disable automatically resizing the SVG's viewBox, defaults to false + #[clap(short, long, value_parser, default_value = "false")] + no_resize: bool, /// Dimension to export the images as; can be specified multiple times #[clap(long, value_parser)] diff --git a/src/parse.rs b/src/parse.rs index f438616..7370cbb 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -59,15 +59,30 @@ pub fn load_species(path: impl AsRef) -> Result { let mut res: SpeciesDecl = toml::from_str(&declaration)?; + if let Some(ref base) = &res.base { + let path = path.as_ref().to_path_buf().join(base); + let base = load_species(path)?; + + res.templates = base.templates; + res.variants = base.variants; + res.assets = base.assets; + } + // Read the `templates` directory and populate the `templates` field; // on error, ignore the directory. - res.templates = read_dir_xml(path.as_ref().join("templates")); + for (name, path) in read_dir_xml(path.as_ref().join("templates")) { + res.templates.insert(name, path); + } // Read the `variants` directory - res.variants = read_dir_xml(path.as_ref().join("variants")); + for (name, path) in read_dir_xml(path.as_ref().join("variants")) { + res.variants.insert(name, path); + } // Read the `assets` directory - res.assets = read_dir_xml(path.as_ref().join("assets")); + for (name, path) in read_dir_xml(path.as_ref().join("assets")) { + res.assets.insert(name, path); + } Ok(res) } diff --git a/src/template.rs b/src/template.rs index 16ace95..e427eed 100644 --- a/src/template.rs +++ b/src/template.rs @@ -34,6 +34,12 @@ impl RenderingContext { Context::with_loader(self.clone()).compile(template.chars()) } + fn render_to_string(&self, string: &str) -> Result { + Context::with_loader(self.clone()) + .compile(string.chars())? + .render_data_to_string(&self.get_data()) + } + pub fn get_data(&self) -> Data { let mut builder = MapBuilder::new(); @@ -75,6 +81,46 @@ impl RenderingContext { }); } + let this = self.clone(); + builder = builder.insert_fn("set-fill", move |input| { + // Parse `color|xml` + if let [color, xml] = input.splitn(2, '|').collect::>()[..] { + // Render `color` and `xml` + if let (Ok(color), Ok(xml)) = (this.render_to_string(&color), this.render_to_string(&xml)) { + // Convert `xml` to XML + match Element::parse(xml.as_bytes()) { + Ok(mut xml) => { + // Substitute the fill color + if let Some(style) = xml.attributes.get("style") { + xml.attributes.insert("style".to_string(), format!( + "{};fill: {};", + style, + color + )); + } + if let Some(_fill) = xml.attributes.get("fill") { + xml.attributes.insert("fill".to_string(), color); + } + + // Render XML to string + if let Some(res) = xml_to_string(xml) { + res + } else { + String::from("") + } + } + Err(err) => { + format!("", err) + } + } + } else { + String::from("") + } + } else { + String::from("") + } + }); + builder.build() } From 39990821b404ec37f3b5ea24ab9402027dcbda21 Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Thu, 28 Jul 2022 16:30:54 +0200 Subject: [PATCH 07/18] :bug: Clean up blobfox_snug, making it usable in blobcat_snug --- species/blobcat/templates/base-snug.mustache | 12 ++ species/blobcat/templates/nose-snug.mustache | 2 +- species/blobcat/templates/tail-snug.mustache | 4 + .../blobcat/templates/whiskers-snug.mustache | 5 + species/blobcat/variants/snug.mustache | 14 ++ species/blobfox/assets/snug.svg | 36 ++-- species/blobfox/templates/base-snug.mustache | 22 ++- vector/blobfox_snug.svg | 171 +++++++++++++++--- 8 files changed, 222 insertions(+), 44 deletions(-) create mode 100644 species/blobcat/templates/base-snug.mustache create mode 100644 species/blobcat/templates/tail-snug.mustache create mode 100644 species/blobcat/templates/whiskers-snug.mustache create mode 100644 species/blobcat/variants/snug.mustache diff --git a/species/blobcat/templates/base-snug.mustache b/species/blobcat/templates/base-snug.mustache new file mode 100644 index 0000000..568f677 --- /dev/null +++ b/species/blobcat/templates/base-snug.mustache @@ -0,0 +1,12 @@ + + + {{#base}}#left-ear{{/base}} + + + {{#set-fill}}#fcc21b|{{#snug}}#body{{/snug}}{{/set-fill}} + + + {{#base}}#right-ear{{/base}} + {{#base}}#right-ear-fluff{{/base}} + + diff --git a/species/blobcat/templates/nose-snug.mustache b/species/blobcat/templates/nose-snug.mustache index 3a79eeb..05fbe68 100644 --- a/species/blobcat/templates/nose-snug.mustache +++ b/species/blobcat/templates/nose-snug.mustache @@ -1 +1 @@ - + diff --git a/species/blobcat/templates/tail-snug.mustache b/species/blobcat/templates/tail-snug.mustache new file mode 100644 index 0000000..e6049e7 --- /dev/null +++ b/species/blobcat/templates/tail-snug.mustache @@ -0,0 +1,4 @@ + + {{#set-fill}}#fcc21b|{{#snug}}#tail{{/snug}}{{/set-fill}} + {{#snug}}#tail-outline{{/snug}} + diff --git a/species/blobcat/templates/whiskers-snug.mustache b/species/blobcat/templates/whiskers-snug.mustache new file mode 100644 index 0000000..280b250 --- /dev/null +++ b/species/blobcat/templates/whiskers-snug.mustache @@ -0,0 +1,5 @@ + + {{#base}}#left-whisker{{/base}} + {{#base}}#left-whisker-2{{/base}} + {{#base}}#right-whisker-2{{/base}} + diff --git a/species/blobcat/variants/snug.mustache b/species/blobcat/variants/snug.mustache new file mode 100644 index 0000000..0fd30ea --- /dev/null +++ b/species/blobcat/variants/snug.mustache @@ -0,0 +1,14 @@ + +{{>header}} + + {{>base-snug}} + {{>tail-snug}} + + + {{>eyes-snug}} + + {{#snug}}#nose{{/snug}} + + {{>whiskers-snug}} + +{{>footer}} diff --git a/species/blobfox/assets/snug.svg b/species/blobfox/assets/snug.svg index a158918..eb08925 100644 --- a/species/blobfox/assets/snug.svg +++ b/species/blobfox/assets/snug.svg @@ -1,27 +1,27 @@ - + blobfox_snug - - + + - From c43827d203d0a659ee868215d1a7d8b0da305d02 Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Sat, 30 Jul 2022 14:37:27 +0200 Subject: [PATCH 14/18] :bug: Fix infinite recursion with `parent.xyz` --- species/blobamber/assets/base.svg | 61 +++++++++++++++++++ species/blobamber/species.toml | 2 + .../blobamber/templates/body-basic.mustache | 15 +++++ species/blobamber/templates/eyes.mustache | 3 + species/blobamber/templates/hands.mustache | 16 +++++ species/blobcat/assets/base.svg | 40 ++++++------ species/blobcat/templates/hand-boop.mustache | 2 +- species/blobcat/templates/hands.mustache | 2 +- species/blobcat/templates/mouth.mustache | 2 +- species/blobcat/templates/nose.mustache | 6 +- species/blobcat/templates/tail.mustache | 2 +- src/template.rs | 54 +++++++++++----- vector/blobcat.svg | 16 ++--- 13 files changed, 170 insertions(+), 51 deletions(-) create mode 100644 species/blobamber/assets/base.svg create mode 100644 species/blobamber/species.toml create mode 100644 species/blobamber/templates/body-basic.mustache create mode 100644 species/blobamber/templates/eyes.mustache create mode 100644 species/blobamber/templates/hands.mustache diff --git a/species/blobamber/assets/base.svg b/species/blobamber/assets/base.svg new file mode 100644 index 0000000..0dae62e --- /dev/null +++ b/species/blobamber/assets/base.svg @@ -0,0 +1,61 @@ + + + blobcat + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Blobfox team (https://git.shadamethyst.xyz/adri326/blobfox), licensed under the Apache 2.0 License + + + blobcat + + + Feuerfuchs + + + https://git.shadamethyst.xyz/adri326/blobfox + + + + diff --git a/species/blobamber/species.toml b/species/blobamber/species.toml new file mode 100644 index 0000000..c6009b3 --- /dev/null +++ b/species/blobamber/species.toml @@ -0,0 +1,2 @@ +name = "blobamber" +base = "../blobcat/" diff --git a/species/blobamber/templates/body-basic.mustache b/species/blobamber/templates/body-basic.mustache new file mode 100644 index 0000000..44346be --- /dev/null +++ b/species/blobamber/templates/body-basic.mustache @@ -0,0 +1,15 @@ +{{! Left ear }} +{{#base}}#left-ear{{/base}} + +{{#base}}#body{{/base}} +{{#base}}#belly{{/base}} +{{#base}}#hair{{/base}} + +{{! Right ear }} +{{#base}}#right-ear{{/base}} +{{#base}}#right-ear-fluff{{/base}} + +{{! Whiskers }} +{{^tags.hands-reach}} + {{>whiskers}} +{{/tags.hands-reach}} diff --git a/species/blobamber/templates/eyes.mustache b/species/blobamber/templates/eyes.mustache new file mode 100644 index 0000000..cff1a5f --- /dev/null +++ b/species/blobamber/templates/eyes.mustache @@ -0,0 +1,3 @@ +{{#blobamber.base}}#blobamber-defs{{/blobamber.base}} + +{{>blobcat.eyes}} diff --git a/species/blobamber/templates/hands.mustache b/species/blobamber/templates/hands.mustache new file mode 100644 index 0000000..342d878 --- /dev/null +++ b/species/blobamber/templates/hands.mustache @@ -0,0 +1,16 @@ +{{#tags.hand-3c}} +{{#tags.holding}} + +{{/tags.holding}} +{{^tags.holding}} + +{{/tags.holding}} +{{/tags.hand-3c}} + +{{#set-fill}} + #6a6862 | {{>blobfox.hands}} +{{/set-fill}} + +{{#tags.hand-3c}} + +{{/tags.hand-3c}} diff --git a/species/blobcat/assets/base.svg b/species/blobcat/assets/base.svg index 3ac4901..83b05b7 100644 --- a/species/blobcat/assets/base.svg +++ b/species/blobcat/assets/base.svg @@ -1,30 +1,30 @@ - + blobcat - - + + - - {{#set-fill}}#fcc21b|{{#snug}}#body{{/snug}}{{/set-fill}} + {{#set-fill}} + {{vars.body_color}} + | {{#snug}}#body{{/snug}} + {{/set-fill}} {{#base}}#right-ear{{/base}} diff --git a/species/blobcat/templates/hands.mustache b/species/blobcat/templates/hands.mustache index c49edc3..d4457ed 100644 --- a/species/blobcat/templates/hands.mustache +++ b/species/blobcat/templates/hands.mustache @@ -1,3 +1,3 @@ {{#set-fill}} - #fcc21b | {{>blobfox.hands}} + {{vars.hand_color}} | {{>blobfox.hands}} {{/set-fill}} diff --git a/species/blobcat/templates/tail.mustache b/species/blobcat/templates/tail.mustache index 21ef72c..640f6fe 100644 --- a/species/blobcat/templates/tail.mustache +++ b/species/blobcat/templates/tail.mustache @@ -1 +1,4 @@ -{{#set-fill}}#fcc21b|{{>blobfox.tail}}{{/set-fill}} +{{#set-fill}} + {{vars.tail_color}} + | {{>blobfox.tail}} +{{/set-fill}} diff --git a/species/blobfox/species.toml b/species/blobfox/species.toml index bad6c59..7a9949e 100644 --- a/species/blobfox/species.toml +++ b/species/blobfox/species.toml @@ -1,6 +1,13 @@ # Add options in here as needs be name = "blobfox" +[vars] +body_color = "#ff8702" +ear_color = "#313131" +ear_fluff_color = "#3bdccc" +hand_color = "#ff8702" +tail_color = "#ff8702" + [variants] base = ["body-basic", "eyes-basic", "mouth-w"] happy = ["body-basic", "eyes-happy", "mouth-w"] diff --git a/src/parse.rs b/src/parse.rs index 3567cbd..a4aef5c 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -34,6 +34,9 @@ pub struct SpeciesDecl { #[serde(default)] pub variants: HashMap>, + #[serde(default)] + pub vars: HashMap, + #[serde(skip)] pub template_paths: HashMap, @@ -73,6 +76,11 @@ pub fn load_species(path: impl AsRef) -> Result { res.variant_paths = base.variant_paths.clone(); res.asset_paths = base.asset_paths.clone(); res.variants = base.variants.clone(); + for (key, value) in base.vars.iter() { + if !res.vars.contains_key(key) { + res.vars.insert(key.clone(), value.clone()); + } + } res.parent = Some(Box::new(base)); } diff --git a/src/template.rs b/src/template.rs index 58013cc..471efa9 100644 --- a/src/template.rs +++ b/src/template.rs @@ -124,6 +124,8 @@ impl RenderingContext { } }); + builder = builder.insert("vars", &self.species.vars).unwrap(); + if include_parent { let mut this = self.clone(); diff --git a/vector/blobamber_owo.svg b/vector/blobamber_owo.svg new file mode 100644 index 0000000..cb524e7 --- /dev/null +++ b/vector/blobamber_owo.svg @@ -0,0 +1,250 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 1f1b2fb560d925684222573c50efdf05e196d967 Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Sat, 30 Jul 2022 15:59:58 +0200 Subject: [PATCH 16/18] :sparkles: heart and egg --- species/blobcat/templates/whiskers.mustache | 8 ++++ species/blobfox/assets/egg.svg | 52 +++++++++++++++++++++ species/blobfox/assets/heart.svg | 50 ++++++++++++++++++++ species/blobfox/species.toml | 3 ++ species/blobfox/templates/hands.mustache | 11 +++++ species/blobfox/variants/egg.mustache | 10 ++++ species/blobfox/variants/heart.mustache | 10 ++++ 7 files changed, 144 insertions(+) create mode 100644 species/blobfox/assets/egg.svg create mode 100644 species/blobfox/assets/heart.svg create mode 100644 species/blobfox/variants/egg.mustache create mode 100644 species/blobfox/variants/heart.mustache diff --git a/species/blobcat/templates/whiskers.mustache b/species/blobcat/templates/whiskers.mustache index dfc44f2..8c0a005 100644 --- a/species/blobcat/templates/whiskers.mustache +++ b/species/blobcat/templates/whiskers.mustache @@ -1,6 +1,14 @@ +{{#tags.left-hand}}{{#tags.right-hand}}{{#tags.holding}} + +{{/tags.holding}}{{/tags.right-hand}}{{/tags.left-hand}} + {{#base}}#left-whisker{{/base}} {{#base}}#left-whisker-2{{/base}} {{#base}}#right-whisker{{/base}} {{#base}}#right-whisker-2{{/base}} + +{{#tags.left-hand}}{{#tags.right-hand}}{{#tags.holding}} + +{{/tags.holding}}{{/tags.right-hand}}{{/tags.left-hand}} diff --git a/species/blobfox/assets/egg.svg b/species/blobfox/assets/egg.svg new file mode 100644 index 0000000..b7aa6aa --- /dev/null +++ b/species/blobfox/assets/egg.svg @@ -0,0 +1,52 @@ + + + blobfox_egg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Blobfox team (https://git.shadamethyst.xyz/adri326/blobfox), licensed under the Apache 2.0 License + + + blobfox_egg + + + Feuerfuchs + + + https://git.shadamethyst.xyz/adri326/blobfox + + + Shad Amethyst + + + + + + diff --git a/species/blobfox/assets/heart.svg b/species/blobfox/assets/heart.svg new file mode 100644 index 0000000..f756250 --- /dev/null +++ b/species/blobfox/assets/heart.svg @@ -0,0 +1,50 @@ + + + blobfox_heart + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Blobfox team (https://git.shadamethyst.xyz/adri326/blobfox), licensed under the Apache 2.0 License + + + blobfox_heart + + + Feuerfuchs + + + https://git.shadamethyst.xyz/adri326/blobfox + + + Shad Amethyst + + + + + + diff --git a/species/blobfox/species.toml b/species/blobfox/species.toml index 7a9949e..5a99dc6 100644 --- a/species/blobfox/species.toml +++ b/species/blobfox/species.toml @@ -33,3 +33,6 @@ snug_boop_owo = ["body-snug", "ear-owo", "eyes-owo", "tail", "boop"] stabby = ["body-basic", "holding", "eyes-evil", "mouth-w", "hand-3c", "left-hand"] blush = ["body-basic", "ear-blush", "eyes-closed", "blush", "mouth-hmpf"] + +heart = ["body-basic", "eyes-basic", "left-hand", "right-hand", "holding", "big-object"] +egg = ["body-basic", "eyes-basic", "left-hand", "right-hand", "holding"] diff --git a/species/blobfox/templates/hands.mustache b/species/blobfox/templates/hands.mustache index 61903be..f9e39ab 100644 --- a/species/blobfox/templates/hands.mustache +++ b/species/blobfox/templates/hands.mustache @@ -12,4 +12,15 @@ {{#3c_evil}}#left-hand{{/3c_evil}} {{/tags.holding}} {{/tags.hand-3c}} + {{^tags.hand-3c}} + {{#tags.holding}} + {{#tags.left-hand}} + {{#heart}}#left-hand{{/heart}} + {{/tags.left-hand}} + + {{#tags.right-hand}} + {{#heart}}#right-hand{{/heart}} + {{/tags.right-hand}} + {{/tags.holding}} + {{/tags.hand-3c}} diff --git a/species/blobfox/variants/egg.mustache b/species/blobfox/variants/egg.mustache new file mode 100644 index 0000000..d0487e7 --- /dev/null +++ b/species/blobfox/variants/egg.mustache @@ -0,0 +1,10 @@ +{{>header}} + {{>body}} + + {{>eyes}} + + {{#egg}}#egg{{/egg}} + {{#egg}}#egg-reflection{{/egg}} + + {{>hands}} +{{>footer}} diff --git a/species/blobfox/variants/heart.mustache b/species/blobfox/variants/heart.mustache new file mode 100644 index 0000000..c32d417 --- /dev/null +++ b/species/blobfox/variants/heart.mustache @@ -0,0 +1,10 @@ +{{>header}} + {{>body}} + + {{>eyes}} + + {{#heart}}#heart{{/heart}} + {{#heart}}#heart-reflection{{/heart}} + + {{>hands}} +{{>footer}} From 8ff49c46a42f6f62e97fc0f54fa9c570c1b6ce05 Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Sun, 31 Jul 2022 17:13:10 +0200 Subject: [PATCH 17/18] :sparkles: Merge in output SVG --- src/export.rs | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/src/export.rs b/src/export.rs index c7c0122..7537777 100644 --- a/src/export.rs +++ b/src/export.rs @@ -3,7 +3,7 @@ use usvg::{ NodeExt, Options, }; -use xmltree::{Element}; +use xmltree::{XMLNode, Element}; use std::path::{PathBuf}; use std::collections::HashSet; @@ -48,7 +48,7 @@ impl From for ExportError { } } -pub fn get_new_bbox(svg: &Tree) -> Option<(f64, f64, f64, f64)> { +fn get_new_bbox(svg: &Tree) -> Option<(f64, f64, f64, f64)> { let bbox = svg.root().calculate_bbox()?; // FIXME: remove once https://github.com/RazrFalcon/resvg/issues/528 is fixed @@ -101,6 +101,45 @@ pub fn resize(svg_str: String) -> Result { } } +/// Finds all the `` in the svg and combines them all into one +pub fn combine_defs(svg_str: String) -> Result { + let mut svg_xml = get_xml(&svg_str)?; + + let mut defs = Vec::new(); + + fn collect_defs(element: &mut Element, defs: &mut Vec) { + for child in std::mem::take(&mut element.children) { + match child { + XMLNode::Element(child) if child.name == "defs" => { + defs.push(child); + } + mut child => { + if let XMLNode::Element(ref mut child) = &mut child { + collect_defs(child, defs); + } + element.children.push(child); + } + } + } + } + + collect_defs(&mut svg_xml, &mut defs); + + let mut defs_element = Element::new("defs"); + defs_element.children = defs + .into_iter() + .map(|def| { + def.children.into_iter().filter(|child| matches!(child, XMLNode::Element(_))) + }) + .flatten() + .collect::>(); + defs_element.attributes.insert("id".to_string(), "defs".to_string()); + + svg_xml.children.insert(0, XMLNode::Element(defs_element)); + + xml_to_str(&svg_xml) +} + pub fn export( mut svg_str: String, output_dir: &PathBuf, @@ -111,6 +150,8 @@ pub fn export( svg_str = resize(svg_str)?; } + svg_str = combine_defs(svg_str)?; + mkdirp::mkdirp(output_dir.join("vector")).unwrap(); let output = output_dir.join(&format!("vector/{}.svg", output_name)); From dd52f46dc27df45d4d40082a96177c09cb9b5f2d Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Sun, 31 Jul 2022 18:07:26 +0200 Subject: [PATCH 18/18] :sparkles: Blob Stella, and parametrize the existing body-basic and body-snug --- species/blobamber/species.toml | 1 + .../blobamber/templates/body-basic.mustache | 16 +- species/blobcat/templates/body-basic.mustache | 16 +- species/blobcat/templates/body-snug.mustache | 12 +- species/blobfox/species.toml | 2 +- species/blobfox/templates/body-basic.mustache | 20 +- species/blobfox/templates/body-snug.mustache | 21 +- species/blobstella/assets/base.svg | 57 +++++ species/blobstella/species.toml | 10 + .../blobstella/templates/body-basic.mustache | 26 +++ .../blobstella/templates/body-snug.mustache | 14 ++ vector/blobamber.svg | 207 ++++++++++++++++++ vector/blobstella.svg | 186 ++++++++++++++++ 13 files changed, 551 insertions(+), 37 deletions(-) create mode 100644 species/blobstella/assets/base.svg create mode 100644 species/blobstella/species.toml create mode 100644 species/blobstella/templates/body-basic.mustache create mode 100644 species/blobstella/templates/body-snug.mustache create mode 100644 vector/blobamber.svg create mode 100644 vector/blobstella.svg diff --git a/species/blobamber/species.toml b/species/blobamber/species.toml index acb5569..50ec5e4 100644 --- a/species/blobamber/species.toml +++ b/species/blobamber/species.toml @@ -7,3 +7,4 @@ ear_color = "#313131" ear_fluff_color = "#474747" hand_color = "#6a6862" tail_color = "#6a6862" +marks_color = "#c8c8c8" diff --git a/species/blobamber/templates/body-basic.mustache b/species/blobamber/templates/body-basic.mustache index 44346be..ad731ad 100644 --- a/species/blobamber/templates/body-basic.mustache +++ b/species/blobamber/templates/body-basic.mustache @@ -1,15 +1,3 @@ -{{! Left ear }} -{{#base}}#left-ear{{/base}} +{{>blobcat.body-basic}} -{{#base}}#body{{/base}} -{{#base}}#belly{{/base}} -{{#base}}#hair{{/base}} - -{{! Right ear }} -{{#base}}#right-ear{{/base}} -{{#base}}#right-ear-fluff{{/base}} - -{{! Whiskers }} -{{^tags.hands-reach}} - {{>whiskers}} -{{/tags.hands-reach}} +{{#set-fill}} {{vars.marks_color}} | {{#base}}#belly{{/base}} {{/set-fill}} diff --git a/species/blobcat/templates/body-basic.mustache b/species/blobcat/templates/body-basic.mustache index 92ec3e2..b11822a 100644 --- a/species/blobcat/templates/body-basic.mustache +++ b/species/blobcat/templates/body-basic.mustache @@ -1,12 +1,18 @@ {{! Left ear }} -{{#base}}#left-ear{{/base}} +{{#set-fill}} {{vars.ear_color}} | {{#base}}#left-ear{{/base}} {{/set-fill}} -{{#base}}#body{{/base}} -{{#base}}#hair{{/base}} +{{! Body }} + + + {{#base}}#body{{/base}} + + +{{#set-fill}} {{vars.body_color}} | {{#base}}#body{{/base}} {{/set-fill}} +{{#set-fill}} {{vars.body_color}} | {{#base}}#hair{{/base}} {{/set-fill}} {{! Right ear }} -{{#base}}#right-ear{{/base}} -{{#base}}#right-ear-fluff{{/base}} +{{#set-fill}} {{vars.ear_color}} | {{#base}}#right-ear{{/base}} {{/set-fill}} +{{#set-fill}} {{vars.ear_fluff_color}} | {{#base}}#right-ear-fluff{{/base}} {{/set-fill}} {{! Whiskers }} {{^tags.hands-reach}} diff --git a/species/blobcat/templates/body-snug.mustache b/species/blobcat/templates/body-snug.mustache index aa2ab12..b5dcb32 100644 --- a/species/blobcat/templates/body-snug.mustache +++ b/species/blobcat/templates/body-snug.mustache @@ -1,6 +1,12 @@ + + + {{#snug}}#body{{/snug}} + + + - {{#base}}#left-ear{{/base}} + {{#set-fill}} {{vars.ear_color}} | {{#base}}#left-ear{{/base}} {{/set-fill}} {{#set-fill}} @@ -9,8 +15,8 @@ {{/set-fill}} - {{#base}}#right-ear{{/base}} - {{#base}}#right-ear-fluff{{/base}} + {{#set-fill}} {{vars.ear_color}} | {{#base}}#right-ear{{/base}} {{/set-fill}} + {{#set-fill}} {{vars.ear_fluff_color}} | {{#base}}#right-ear-fluff{{/base}} {{/set-fill}} {{>tail}} diff --git a/species/blobfox/species.toml b/species/blobfox/species.toml index 5a99dc6..7fe64c8 100644 --- a/species/blobfox/species.toml +++ b/species/blobfox/species.toml @@ -4,7 +4,7 @@ name = "blobfox" [vars] body_color = "#ff8702" ear_color = "#313131" -ear_fluff_color = "#3bdccc" +ear_fluff_color = "#ebdccc" hand_color = "#ff8702" tail_color = "#ff8702" diff --git a/species/blobfox/templates/body-basic.mustache b/species/blobfox/templates/body-basic.mustache index d8f3b8e..9ccaefa 100644 --- a/species/blobfox/templates/body-basic.mustache +++ b/species/blobfox/templates/body-basic.mustache @@ -1,15 +1,21 @@ {{! Left ear }} {{#tags.ear-owo}} - {{#boop_owo}}#left-ear{{/boop_owo}} - {{#boop_owo}}#left-ear-fluff{{/boop_owo}} + {{#set-fill}} {{vars.ear_color}} | {{#boop_owo}}#left-ear{{/boop_owo}} {{/set-fill}} + {{#set-fill}} {{vars.ear_fluff_color}} | {{#boop_owo}}#left-ear-fluff{{/boop_owo}} {{/set-fill}} {{/tags.ear-owo}} {{^tags.ear-owo}} - {{#base}}#left-ear{{/base}} + {{#set-fill}} {{vars.ear_color}} | {{#base}}#left-ear{{/base}} {{/set-fill}} {{/tags.ear-owo}} -{{#base}}#body{{/base}} -{{#base}}#hair{{/base}} +{{! Body }} + + + {{#base}}#body{{/base}} + + +{{#set-fill}} {{vars.body_color}} | {{#base}}#body{{/base}} {{/set-fill}} +{{#set-fill}} {{vars.body_color}} | {{#base}}#hair{{/base}} {{/set-fill}} {{! Right ear }} -{{#base}}#right-ear{{/base}} -{{#base}}#right-ear-fluff{{/base}} +{{#set-fill}} {{vars.ear_color}} | {{#base}}#right-ear{{/base}} {{/set-fill}} +{{#set-fill}} {{vars.ear_fluff_color}} | {{#base}}#right-ear-fluff{{/base}} {{/set-fill}} diff --git a/species/blobfox/templates/body-snug.mustache b/species/blobfox/templates/body-snug.mustache index 2bf4891..6633363 100644 --- a/species/blobfox/templates/body-snug.mustache +++ b/species/blobfox/templates/body-snug.mustache @@ -1,15 +1,22 @@ + + + {{#snug}}#body{{/snug}} + + + {{! TODO: owo ear }} - {{#snug}}#left-ear{{/snug}} - {{#snug}}#body{{/snug}} - {{#snug}}#hair{{/snug}} - {{#snug}}#right-ear{{/snug}} - {{#snug}}#right-ear-fluff{{/snug}} + {{#set-fill}} {{vars.ear_color}} | {{#snug}}#left-ear{{/snug}} {{/set-fill}} + + {{#set-fill}} {{vars.body_color}} | {{#snug}}#body{{/snug}} {{/set-fill}} + {{#set-fill}} {{vars.body_color}} | {{#snug}}#hair{{/snug}} {{/set-fill}} + + {{#set-fill}} {{vars.ear_color}} | {{#snug}}#right-ear{{/snug}} {{/set-fill}} + {{#set-fill}} {{vars.ear_fluff_color}} | {{#snug}}#right-ear-fluff{{/snug}} {{/set-fill}} {{#tags.tail}} - {{#snug}}#tail{{/snug}} - {{#snug}}#tail-outline{{/snug}} + {{#set-fill}} {{vars.body_color}} | {{#snug}}#tail{{/snug}} {{/set-fill}} {{/tags.tail}} diff --git a/species/blobstella/assets/base.svg b/species/blobstella/assets/base.svg new file mode 100644 index 0000000..ebce6d1 --- /dev/null +++ b/species/blobstella/assets/base.svg @@ -0,0 +1,57 @@ + + + blobcat + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Blobfox team (https://git.shadamethyst.xyz/adri326/blobfox), licensed under the Apache 2.0 License + + + blobcat + + + Feuerfuchs + + + https://git.shadamethyst.xyz/adri326/blobfox + + + + diff --git a/species/blobstella/species.toml b/species/blobstella/species.toml new file mode 100644 index 0000000..9a4ba0f --- /dev/null +++ b/species/blobstella/species.toml @@ -0,0 +1,10 @@ +name = "blobstella" +base = "../blobcat/" + +[vars] +body_color = "#54b6e7" +ear_color = "#3398c7" +ear_fluff_color = "#224f66" +hand_color = "#54b6e7" +tail_color = "#54b6e7" +marks_color = "#eee64e" diff --git a/species/blobstella/templates/body-basic.mustache b/species/blobstella/templates/body-basic.mustache new file mode 100644 index 0000000..fb9dbc0 --- /dev/null +++ b/species/blobstella/templates/body-basic.mustache @@ -0,0 +1,26 @@ +{{! Left ear }} +{{#set-fill}} {{vars.ear_color}} | {{#base}}#left-ear{{/base}} {{/set-fill}} + +{{! Body }} + + + {{#base}}#body{{/base}} + + +{{#set-fill}} {{vars.body_color}} | {{#base}}#body{{/base}} {{/set-fill}} +{{#set-fill}} {{vars.body_color}} | {{#base}}#hair{{/base}} {{/set-fill}} + +{{! Right ear }} +{{#set-fill}} {{vars.ear_color}} | {{#base}}#right-ear{{/base}} {{/set-fill}} +{{#set-fill}} {{vars.ear_fluff_color}} | {{#base}}#right-ear-full{{/base}} {{/set-fill}} + +{{! Marks }} + + {{#base}}#left-marks{{/base}} + {{#base}}#right-marks{{/base}} + + +{{! Whiskers }} +{{^tags.hands-reach}} + {{>whiskers}} +{{/tags.hands-reach}} diff --git a/species/blobstella/templates/body-snug.mustache b/species/blobstella/templates/body-snug.mustache new file mode 100644 index 0000000..9253199 --- /dev/null +++ b/species/blobstella/templates/body-snug.mustache @@ -0,0 +1,14 @@ +{{>blobcat.body-snug}} + + + + {{#set-fill}} + {{vars.marks_color}} + | {{#base}}#left-marks{{/base}} + {{/set-fill}} + {{#set-fill}} + {{vars.marks_color}} + | {{#base}}#right-marks{{/base}} + {{/set-fill}} + + diff --git a/vector/blobamber.svg b/vector/blobamber.svg new file mode 100644 index 0000000..b88445f --- /dev/null +++ b/vector/blobamber.svg @@ -0,0 +1,207 @@ + + + +blobcatBlobfox team (https://git.shadamethyst.xyz/adri326/blobfox), licensed under the Apache 2.0 LicenseblobcatFeuerfuchshttps://git.shadamethyst.xyz/adri326/blobfox diff --git a/vector/blobstella.svg b/vector/blobstella.svg new file mode 100644 index 0000000..4351565 --- /dev/null +++ b/vector/blobstella.svg @@ -0,0 +1,186 @@ + + + +blobcatBlobfox team (https://git.shadamethyst.xyz/adri326/blobfox), licensed under the Apache 2.0 LicenseblobcatFeuerfuchshttps://git.shadamethyst.xyz/adri326/blobfox