Species inheritance, set-fill

feat/template
Shad Amethyst 2 years ago
parent 45c6895833
commit de48f936e9
Signed by untrusted user: amethyst
GPG Key ID: D970C8DD1D6DEE36

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="svg5" space="preserve" height="128" docname="blobcat.svg" viewBox="0 0 33.866668 33.866668" width="128" version="1.2.1 (9c6d41e410, 2022-07-14)">
<title id="title30762">blobcat</title>
<sodipodi:namedview bordercolor="#ffffff" showpageshadow="2" document-units="mm" zoom="3.8198421" window-y="0" borderopacity="1" cy="67.542059" pagecheckerboard="1" pageshadow="0" pageopacity="0" pagecolor="#505050" id="namedview7" units="px" window-x="0" cx="40.839384" window-maximized="1" window-height="779" showgrid="false" deskcolor="#505050" window-width="1536" current-layer="layer3">
<inkscape:grid type="xygrid" id="grid10"/>
</sodipodi:namedview>
<defs id="defs2"/>
<g style="display:none" label="ref" groupmode="layer" id="ref">
<image x="0.45707837" preserveAspectRatio="none" style="display:none;image-rendering:optimizeSpeed" height="56.029915" id="blobfoxmsnuggleleftcat" label="blobfoxmsnuggleleftcat" y="-16.623016" width="56.029915" href="../original/blobfoxmsnuggleleftcat.png"/>
<image height="44.290115" preserveAspectRatio="none" style="display:none;image-rendering:optimizeSpeed" width="44.290115" href="../original/blobfoxnomcat.png" y="-10.339302" x="-3.1429539" id="blobfoxnomcat" label="blobfoxnomcat"/>
<image href="../original/blobfoxmsnugglecentercat.png" preserveAspectRatio="none" width="55.942009" y="-17.482618" id="blobfoxmsnugglecentercat" x="-11.634965" height="55.942009" style="display:none;image-rendering:optimizeSpeed" label="blobfoxmsnugglecentercat"/>
<image height="33.866665" x="0" style="display:none;image-rendering:optimizeSpeed" preserveAspectRatio="none" href="../original/blobfox.png" width="33.866665" label="blobfox" id="blobfox" y="0"/>
</g>
<g groupmode="layer" style="display:inline" id="Base" label="Base">
<path style="fill:#e9ae20;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" id="left-ear" d="M 4.0778933,12.468497 C 2.0572046,7.4297489 2.5604085,3.6951828 2.6743801,2.856386 2.7720357,2.1376698 3.4357646,1.7692645 4.1639818,1.8924422 7.3443941,2.430408 10.157621,4.0991994 12.720122,6.1421708 9.2129023,8.1547016 5.9854952,10.31934 4.0778933,12.468497 Z" nodetypes="csscc" label="left-ear"/>
<path style="fill:#fcc21b;fill-opacity:1;stroke:none;stroke-width:0.999999px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.4501" label="body" id="body" nodetypes="sccsssssss" transform="scale(0.26458333)" d="M 49.112445,21.099228 C 22.954714,27.711036 1.527486,58.455986 0,95 c 0,0 0.14555099,8.03117 2.203125,14.33203 5.6666732,6.49391 20.789398,15.3256 40.376953,17.79492 18.881266,2.38028 43.113333,-0.44591 58.314452,-3.86328 15.20113,-3.41737 22.82639,-13.27959 23.81641,-15.80664 0.99,-2.52705 0.10795,-7.720166 -0.5586,-9.939452 -0.81185,-2.70308 -2.0586,-6.277707 -0.39843,-11.148437 1.66016,-4.87073 6.29813,-26.667335 -1.93795,-40.330311 C 104.00212,16.487149 60.621666,18.190078 49.112445,21.099228 Z"/>
<path nodetypes="cssssccscc" id="right-ear" style="fill:#e9ae20;fill-opacity:1;stroke-width:1.065;stroke-linecap:round" label="right-ear" d="m 22.327555,5.7244503 c 1.295051,-1.2796236 3.500332,-2.9878384 7.386322,-3.5963965 0.537632,-0.084195 1.143941,-0.1550213 1.7273,0.7015614 1.469977,2.1584583 1.173915,6.0445604 0.6116,9.0693588 -0.117183,0.630353 -0.257711,1.453438 -0.632576,1.684499 -0.320739,0.197698 -0.580104,0.184392 -0.718333,-0.06853 C 30.272125,10.961178 26.453622,8.9536838 24.896724,10.183999 23.826653,9.6235262 22.962471,8.7347198 22.619988,8.1437471 22.194453,7.4094641 21.964165,6.144611 22.327555,5.7244523 Z"/>
<path style="fill:#8a6135;fill-opacity:1;stroke:none;stroke-width:1.065;stroke-linecap:round;stroke-opacity:1" id="right-ear-fluff" d="m 24.892986,10.184048 c 1.125933,-1.215965 1.462231,-2.7360641 2.132129,-4.2954731 0.725687,-1.6892791 2.107058,-2.4070084 2.893408,-2.7287788 0.596644,-0.2441436 0.788066,-0.075182 0.935394,0.4300284 0.147325,0.5052097 1.075655,6.4176515 -0.150808,9.9265275 -0.672899,-1.745206 -1.860977,-2.691313 -2.721842,-3.119965 0.308581,0.43608 0.264187,0.774385 0.08088,1.27261 -0.798459,-0.541977 -1.099302,-0.858099 -1.429826,-1.583679 -0.503826,0.411547 -1.236317,0.401682 -1.739336,0.09873 z" nodetypes="cssscccccc" label="right-ear-fluff"/>
</g>
<g id="Features" label="Features" groupmode="layer">
<path d="m 7.3527787,14.424665 c 0.1797912,-1.565032 1.4709763,-2.669662 2.4784762,-2.637439 1.0481491,0.03352 1.7111831,1.106758 1.6559461,1.895304 -0.109337,1.560853 -0.980796,2.946701 -2.4842238,2.935699 -0.9121407,-0.0067 -1.7841833,-1.027265 -1.6501985,-2.193564 z" id="left-eye" label="left-eye" nodetypes="sssss" style="fill:#313131;fill-opacity:1;stroke:none;stroke-width:0.265;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.46432"/>
<path nodetypes="sssss" style="fill:#313131;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.46432" d="m 19.490943,16.103085 c 0.179791,-1.80508 1.475044,-2.789355 2.482544,-2.757132 1.04815,0.03352 1.921875,1.215376 1.852143,2.278752 -0.113562,1.731761 -1.084893,2.877453 -2.588321,2.866451 -0.912141,-0.0067 -1.862721,-1.219882 -1.746366,-2.388071 z" label="right-eye" id="right-eye"/>
<path style="fill:none;stroke:#313131;stroke-width:1.165;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 9.9318896,19.995623 c 0.5486154,1.686713 1.0637784,2.752111 1.5767164,3.423841 0.992384,-0.828422 2.25927,-1.977918 2.761386,-2.480155 0.783749,1.274883 1.856673,2.745088 2.294365,3.345841 1.285631,-1.024786 2.518532,-2.115492 3.025709,-2.688925" id="mouth" nodetypes="ccccc" label="mouth"/>
<path style="fill:none;fill-opacity:1;stroke:#313131;stroke-width:1.065;stroke-linecap:round;stroke-opacity:1" nodetypes="cc" label="left-whisker-2" d="M 6.1830023,19.578813 C 4.1871054,17.828608 2.4340957,16.609582 1.3611551,16.012965" id="left-whisker-2"/>
<path style="fill:none;fill-opacity:1;stroke:#313131;stroke-width:1.065;stroke-linecap:round;stroke-opacity:1" id="left-whisker" nodetypes="cc" label="left-whisker" d="M 5.9107876,21.690708 C 3.9277707,21.232921 1.7485086,21.187496 0.54513174,21.179815"/>
<path id="right-whisker-2" d="m 23.430617,22.366051 c 2.540328,-0.912678 4.284056,-1.105124 4.982397,-1.142629" style="fill:none;fill-opacity:1;stroke:#313131;stroke-width:1.065;stroke-linecap:round;stroke-opacity:1" label="right-whisker-2"/>
<path id="right-whisker" label="right-whisker" d="m 22.565965,24.701884 c 1.919035,0.580733 3.985374,1.582507 4.971704,2.283901" style="fill:none;fill-opacity:1;stroke:#313131;stroke-width:1.065;stroke-linecap:round;stroke-opacity:1"/>
</g>
<metadata id="metadata30760">
<rdf:RDF>
<cc:Work about="">
<dc:rights>
<cc:Agent>
<dc:title>Blobfox team (https://git.shadamethyst.xyz/adri326/blobfox), licensed under the Apache 2.0 License</dc:title>
</cc:Agent>
</dc:rights>
<dc:title>blobcat</dc:title>
<dc:creator>
<cc:Agent>
<dc:title>Feuerfuchs</dc:title>
</cc:Agent>
</dc:creator>
<dc:source>https://git.shadamethyst.xyz/adri326/blobfox</dc:source>
</cc:Work>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 7.6 KiB

@ -0,0 +1,2 @@
name = "blobcat"
base = "../blobfox/"

@ -0,0 +1,4 @@
<g id="hands-reach">
{{#set-fill}}#fcc21b|{{#reach_aww}}#left-hand{{/reach_aww}}{{/set-fill}}
{{#set-fill}}#fcc21b|{{#reach_aww}}#right-hand{{/reach_aww}}{{/set-fill}}
</g>

@ -0,0 +1 @@
{{#base}}#mouth{{/base}}

@ -0,0 +1,6 @@
<g id="whiskers">
{{#base}}#left-whisker{{/base}}
{{#base}}#left-whisker-2{{/base}}
{{#base}}#right-whisker{{/base}}
{{#base}}#right-whisker-2{{/base}}
</g>

@ -0,0 +1,2 @@
{{! Should be included after >base if there is no arm, object, etc. on the emote }}
<g id="base-feature" />

@ -1,7 +1,7 @@
<g id="base"> <g id="base">
{{#base}}#body{{/base}}
{{#boop_owo}}#left-ear{{/boop_owo}} {{#boop_owo}}#left-ear{{/boop_owo}}
{{#boop_owo}}#left-ear-fluff{{/boop_owo}} {{#boop_owo}}#left-ear-fluff{{/boop_owo}}
{{#base}}#body{{/base}}
{{#base}}#hair{{/base}} {{#base}}#hair{{/base}}
{{#base}}#right-ear{{/base}} {{#base}}#right-ear{{/base}}
{{#base}}#right-ear-fluff{{/base}} {{#base}}#right-ear-fluff{{/base}}

@ -1,6 +1,6 @@
<g id="base"> <g id="base">
{{#base}}#body{{/base}}
{{#base}}#left-ear{{/base}} {{#base}}#left-ear{{/base}}
{{#base}}#body{{/base}}
{{#base}}#hair{{/base}} {{#base}}#hair{{/base}}
{{#base}}#right-ear{{/base}} {{#base}}#right-ear{{/base}}
{{#base}}#right-ear-fluff{{/base}} {{#base}}#right-ear-fluff{{/base}}

@ -0,0 +1,2 @@
{{! cf. base-extra }}
<g id="reach-extra" />

@ -1,6 +1,7 @@
{{>header}} {{>header}}
<g id="body"> <g id="body">
{{>base}} {{>base}}
{{>base-extra}}
</g> </g>
<g id="features"> <g id="features">
{{>eyes}} {{>eyes}}

@ -1,6 +1,7 @@
{{>header}} {{>header}}
<g id="body"> <g id="body">
{{>base}} {{>base}}
{{>base-extra}}
</g> </g>
<g id="features"> <g id="features">
{{>eyes}} {{>eyes}}

@ -1,6 +1,7 @@
{{>header}} {{>header}}
<g id="body"> <g id="body">
{{>base}} {{>base}}
{{>base-extra}}
</g> </g>
<g id="features"> <g id="features">
{{>eyes-aww}} {{>eyes-aww}}

@ -1,6 +1,7 @@
{{>header}} {{>header}}
<g id="body"> <g id="body">
{{>base-owo}} {{>base-owo}}
{{>base-extra}}
</g> </g>
<g id="features"> <g id="features">
{{>eyes-owo}} {{>eyes-owo}}

@ -1,6 +1,7 @@
{{>header}} {{>header}}
<g id="body"> <g id="body">
{{>base}} {{>base}}
{{>base-extra}}
</g> </g>
<g id="features"> <g id="features">
{{>eyes-snug}} {{>eyes-snug}}

@ -1,6 +1,7 @@
{{>header}} {{>header}}
<g id="body"> <g id="body">
{{>base-owo}} {{>base-owo}}
{{>base-extra}}
</g> </g>
<g id="features"> <g id="features">
{{>eyes-owo}} {{>eyes-owo}}

@ -2,6 +2,7 @@
<g id="body"> <g id="body">
{{>base}} {{>base}}
{{>hands-reach}} {{>hands-reach}}
{{>reach-extra}}
</g> </g>
<g id="features"> <g id="features">
{{>eyes}} {{>eyes}}

@ -2,6 +2,7 @@
<g id="body"> <g id="body">
{{>base}} {{>base}}
{{>hands-reach}} {{>hands-reach}}
{{>reach-extra}}
</g> </g>
<g id="features"> <g id="features">
{{>eyes-aww}} {{>eyes-aww}}

@ -50,14 +50,23 @@ impl From<png::EncodingError> for ExportError {
pub fn get_new_bbox(svg: &Tree) -> Option<(f64, f64, f64, f64)> { pub fn get_new_bbox(svg: &Tree) -> Option<(f64, f64, f64, f64)> {
let bbox = svg.root().calculate_bbox()?; 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 { } 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, output_name: String,
args: &super::Args, args: &super::Args,
) -> Result<(), ExportError> { ) -> Result<(), ExportError> {
if args.resize { if !args.no_resize {
svg_str = resize(svg_str)?; svg_str = resize(svg_str)?;
} }

@ -68,9 +68,9 @@ pub struct Args {
#[clap(value_parser)] #[clap(value_parser)]
names: Vec<String>, names: Vec<String>,
/// Automatically resize the SVG's viewBox, defaults to true /// Disable automatically resizing the SVG's viewBox, defaults to false
#[clap(short, long, value_parser, default_value = "true")] #[clap(short, long, value_parser, default_value = "false")]
resize: bool, no_resize: bool,
/// Dimension to export the images as; can be specified multiple times /// Dimension to export the images as; can be specified multiple times
#[clap(long, value_parser)] #[clap(long, value_parser)]

@ -59,15 +59,30 @@ pub fn load_species(path: impl AsRef<Path>) -> Result<SpeciesDecl, ParseError> {
let mut res: SpeciesDecl = toml::from_str(&declaration)?; 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; // Read the `templates` directory and populate the `templates` field;
// on error, ignore the directory. // 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 // 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 // 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) Ok(res)
} }

@ -34,6 +34,12 @@ impl RenderingContext {
Context::with_loader(self.clone()).compile(template.chars()) Context::with_loader(self.clone()).compile(template.chars())
} }
fn render_to_string(&self, string: &str) -> Result<String, mustache::Error> {
Context::with_loader(self.clone())
.compile(string.chars())?
.render_data_to_string(&self.get_data())
}
pub fn get_data(&self) -> Data { pub fn get_data(&self) -> Data {
let mut builder = MapBuilder::new(); 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::<Vec<_>>()[..] {
// 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("<!-- Error in stringifying xml -->")
}
}
Err(err) => {
format!("<!-- Error in parsing xml: {} -->", err)
}
}
} else {
String::from("<!-- Error in parsing color or element -->")
}
} else {
String::from("<!-- Invalid syntax: expected `color|xml` -->")
}
});
builder.build() builder.build()
} }

Loading…
Cancel
Save