forked from amethyst/blobfox
parent
235722ff83
commit
1cd8665df7
@ -0,0 +1,4 @@
|
|||||||
|
name = "snuggle"
|
||||||
|
dx = -60
|
||||||
|
dy = -40
|
||||||
|
transform = "scale(1.02 1.02) translate(-1.5 -1)"
|
After Width: | Height: | Size: 8.2 KiB |
@ -0,0 +1,15 @@
|
|||||||
|
{{! Left ear }}
|
||||||
|
{{#set-fill}} {{vars.ear_color}} | {{#snuggle_right}}#left-ear{{/snuggle_right}} {{/set-fill}}
|
||||||
|
|
||||||
|
{{! Body }}
|
||||||
|
<defs>
|
||||||
|
<clipPath id="body-clip">
|
||||||
|
{{#snuggle_right}}#body{{/snuggle_right}}
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
{{#set-fill}} {{vars.body_color}} | {{#snuggle_right}}#body{{/snuggle_right}} {{/set-fill}}
|
||||||
|
{{#set-fill}} {{vars.body_color}} | {{#snuggle_right}}#hair{{/snuggle_right}} {{/set-fill}}
|
||||||
|
|
||||||
|
{{! Right ear }}
|
||||||
|
{{#set-fill}} {{vars.ear_color}} | {{#snuggle_right}}#right-ear{{/snuggle_right}} {{/set-fill}}
|
||||||
|
{{#set-fill}} {{vars.ear_fluff_color}} | {{#snuggle_right}}#right-ear-fluff{{/snuggle_right}} {{/set-fill}}
|
@ -0,0 +1,9 @@
|
|||||||
|
{{>header}}
|
||||||
|
{{>body}}
|
||||||
|
|
||||||
|
<g transform="rotate(-10 45 75)">
|
||||||
|
{{>eyes}}
|
||||||
|
{{>nose}}
|
||||||
|
{{>mouth}}
|
||||||
|
</g>
|
||||||
|
{{>footer}}
|
@ -0,0 +1,148 @@
|
|||||||
|
//! Very crude tool for generating snuggle emotes
|
||||||
|
use clap::Parser;
|
||||||
|
use std::fmt::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use xmltree::{Element, XMLNode};
|
||||||
|
|
||||||
|
use blobfox_template::{
|
||||||
|
parse,
|
||||||
|
template,
|
||||||
|
export,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
struct Desc {
|
||||||
|
dx: f64,
|
||||||
|
dy: f64,
|
||||||
|
|
||||||
|
scale: Option<f64>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
transform: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args = Args::parse();
|
||||||
|
|
||||||
|
let left = std::fs::read_to_string(&args.input_left).unwrap_or_else(|err| {
|
||||||
|
panic!("Couldn't open {}: {}", args.input_right.display(), err);
|
||||||
|
});
|
||||||
|
let right = std::fs::read_to_string(&args.input_right).unwrap_or_else(|err| {
|
||||||
|
panic!("Couldn't open {}: {}", args.input_right.display(), err);
|
||||||
|
});
|
||||||
|
|
||||||
|
let desc = std::fs::read_to_string(&args.desc).unwrap_or_else(|err| {
|
||||||
|
panic!("Couldn't open {}: {}", args.desc.display(), err);
|
||||||
|
});
|
||||||
|
let desc: Desc = toml::from_str(&desc).unwrap();
|
||||||
|
|
||||||
|
let snuggle = generate_snuggle(left, right, desc);
|
||||||
|
let snuggle = export::xml_to_str(&snuggle).unwrap();
|
||||||
|
|
||||||
|
let output_dir = args.output_dir.clone().unwrap_or(PathBuf::from("output/"));
|
||||||
|
|
||||||
|
export::export(
|
||||||
|
snuggle,
|
||||||
|
&output_dir,
|
||||||
|
args.name.clone(),
|
||||||
|
&args.clone().into()
|
||||||
|
).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_snuggle(left: String, right: String, desc: Desc) -> Element {
|
||||||
|
let left_usvg = export::get_usvg(&left).unwrap();
|
||||||
|
let left_bbox = left_usvg.svg_node().view_box.rect;
|
||||||
|
|
||||||
|
//
|
||||||
|
let mut mask = Element::new("mask");
|
||||||
|
mask.attributes.insert("id".to_string(), "snuggle-mask".to_string());
|
||||||
|
|
||||||
|
let mut rect = Element::new("rect");
|
||||||
|
rect.attributes.insert("fill".to_string(), "white".to_string());
|
||||||
|
// TODO: use scale?
|
||||||
|
rect.attributes.insert("x".to_string(), (desc.dx + left_bbox.x()).to_string());
|
||||||
|
rect.attributes.insert("y".to_string(), (desc.dy + left_bbox.y()).to_string());
|
||||||
|
rect.attributes.insert("width".to_string(), left_bbox.width().to_string());
|
||||||
|
rect.attributes.insert("height".to_string(), left_bbox.height().to_string());
|
||||||
|
|
||||||
|
mask.children.push(XMLNode::Element(rect));
|
||||||
|
|
||||||
|
let mut right_mask = Element::new("g");
|
||||||
|
right_mask.attributes.insert("transform".to_string(), desc.transform);
|
||||||
|
|
||||||
|
let mut right_xml = Element::parse(right.as_bytes()).unwrap();
|
||||||
|
template::set_fill("#000000", &mut right_xml);
|
||||||
|
template::set_stroke("#000000", &mut right_xml);
|
||||||
|
|
||||||
|
for child in right_xml.children {
|
||||||
|
if let XMLNode::Element(child) = child {
|
||||||
|
right_mask.children.push(XMLNode::Element(child));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mask.children.push(XMLNode::Element(right_mask));
|
||||||
|
|
||||||
|
let mut right_xml = Element::parse(right.as_bytes()).unwrap();
|
||||||
|
let left_xml = Element::parse(left.as_bytes()).unwrap();
|
||||||
|
|
||||||
|
let mut left_group = Element::new("g");
|
||||||
|
left_group.attributes.insert("transform".to_string(), format!(
|
||||||
|
"translate({} {})",
|
||||||
|
desc.dx,
|
||||||
|
desc.dy
|
||||||
|
));
|
||||||
|
left_group.children = left_xml.children;
|
||||||
|
|
||||||
|
let mut left_group2 = Element::new("g");
|
||||||
|
left_group2.attributes.insert("mask".to_string(), "url(#snuggle-mask)".to_string());
|
||||||
|
left_group2.children.push(XMLNode::Element(left_group));
|
||||||
|
|
||||||
|
let mut res = Element::new("svg");
|
||||||
|
res.attributes.insert("xmlns".to_string(), "http://www.w3.org/2000/svg".to_string());
|
||||||
|
res.attributes.insert("version".to_string(), "1.1".to_string());
|
||||||
|
res.attributes.insert("width".to_string(), "128".to_string());
|
||||||
|
res.attributes.insert("height".to_string(), "128".to_string());
|
||||||
|
res.children.push(XMLNode::Element(mask));
|
||||||
|
res.children.append(&mut right_xml.children);
|
||||||
|
res.children.push(XMLNode::Element(left_group2));
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Clone)]
|
||||||
|
#[clap(author, version, about, long_about = None)]
|
||||||
|
struct Args {
|
||||||
|
#[clap(value_parser)]
|
||||||
|
input_left: PathBuf,
|
||||||
|
|
||||||
|
#[clap(value_parser)]
|
||||||
|
input_right: PathBuf,
|
||||||
|
|
||||||
|
#[clap(short, long, value_parser)]
|
||||||
|
desc: PathBuf,
|
||||||
|
|
||||||
|
/// Disable automatically resizing the SVG's viewBox, defaults to false
|
||||||
|
#[clap(short, long, value_parser, default_value = "false")]
|
||||||
|
no_resize: bool,
|
||||||
|
|
||||||
|
#[clap(long, value_parser)]
|
||||||
|
name: String,
|
||||||
|
|
||||||
|
/// Dimension to export the images as; can be specified multiple times
|
||||||
|
#[clap(long, value_parser)]
|
||||||
|
dim: Vec<u32>,
|
||||||
|
|
||||||
|
/// Output directory
|
||||||
|
#[clap(short, long, value_parser)]
|
||||||
|
output_dir: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Args> for export::ExportArgs {
|
||||||
|
fn from(args: Args) -> export::ExportArgs {
|
||||||
|
export::ExportArgs {
|
||||||
|
no_resize: args.no_resize,
|
||||||
|
dim: args.dim,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
pub mod parse;
|
||||||
|
pub mod template;
|
||||||
|
pub mod export;
|
After Width: | Height: | Size: 8.8 KiB |
Loading…
Reference in new issue