Initial working Kanimaji implementation
This commit is contained in:
parent
4e87a67296
commit
99901b878a
8 changed files with 588 additions and 83 deletions
|
@ -1,29 +1,78 @@
|
|||
#include "Kanimaji/Colour.hpp"
|
||||
#include "Kanimaji/Config.hpp"
|
||||
#include "Kanimaji/Kanimaji.hpp"
|
||||
#include "SVG.hpp"
|
||||
|
||||
#include <ctre.hpp>
|
||||
#include <pugixml.hpp>
|
||||
|
||||
#include <print>
|
||||
#include <format>
|
||||
#include <string_view>
|
||||
|
||||
namespace Kanimaji
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr std::string_view strokeBorderWidth = "4.5";
|
||||
constexpr std::string_view strokeBorderColour = "#666";
|
||||
constexpr GroupConfig StrokeBorderConfig { .Width = 5, .Colour = Colour::FromHex("#666") };
|
||||
constexpr GroupConfig StrokeUnfilledConfig{ .Width = 2, .Colour = Colour::FromHex("#EEE") };
|
||||
constexpr GroupConfig StrokeFilledConfig { .Width = 3.1, .Colour = Colour::FromHex("#000") };
|
||||
constexpr GroupConfig BrushConfig { .Width = 5.5, .Colour = Colour::FromHex("#F00") };
|
||||
constexpr GroupConfig BrushBorderConfig { .Width = 7, .Colour = Colour::FromHex("#666") };
|
||||
|
||||
constexpr std::string_view strokeUnfilledWidth = "3";
|
||||
constexpr std::string_view strokeUnfilledColour = "#EEE";
|
||||
constexpr std::string_view strokeFillingColour = "#F00";
|
||||
constexpr std::string_view strokeFilledWidth = "3.1";
|
||||
constexpr std::string_view strokeFilledColour = "#000";
|
||||
constexpr Colour StrokeFillingColour = Colour::FromHex("#F00");
|
||||
|
||||
constexpr std::string_view brushColour = "#F00";
|
||||
constexpr std::string_view brushWidth = "5.5";
|
||||
constexpr std::string_view brushBorderColour = "#666";
|
||||
constexpr std::string_view brushBorderWidth = "7";
|
||||
// What the fuck is this?
|
||||
constexpr double WaitAfter = 1.5;
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
constexpr auto StrokeProgressionFormat = (
|
||||
" @keyframes stroke-{} {{\n"
|
||||
" 0.000% {{ stroke-dashoffset: {:.3f}; }}\n"
|
||||
" {:.3f}% {{ stroke-dashoffset: {:.3f}; }}\n"
|
||||
" {:.3f}% {{ stroke-dashoffset: 0; }}\n"
|
||||
" 100.000% {{ stroke-dashoffset: 0; }}\n"
|
||||
" }}\n"sv
|
||||
);
|
||||
constexpr auto AnimationVisibilityFormat = (
|
||||
" @keyframes showhide-{} {{\n"
|
||||
" {:.3f}% {{ visibility: hidden; }}\n"
|
||||
" {:.3f}% {{ stroke: {}; }}\n"
|
||||
" }}\n"sv
|
||||
);
|
||||
constexpr auto AnimationProgressionFormat = (
|
||||
" #{} {{\n"
|
||||
" stroke-dasharray: {:.3f} {:.3f};\n"
|
||||
" stroke-dashoffset: 0;\n"
|
||||
" animation: stroke-{} {:.3f}s {} infinite,\n"
|
||||
" showhide-{} {:.3f}s step-start infinite;\n"
|
||||
" }}\n"sv
|
||||
);
|
||||
constexpr auto BrushVisibilityFormat = (
|
||||
" @keyframes showhide-brush-{} {{\n"
|
||||
" {:.3f}% {{ visibility: hidden; }}\n"
|
||||
" {:.3f}% {{ visibility: visible; }}\n"
|
||||
" 100.000% {{ visibility: hidden; }}\n"
|
||||
" }}\n"sv
|
||||
);
|
||||
constexpr auto BrushProgressionFormat = (
|
||||
" #{}, #{} {{\n"
|
||||
" stroke-dasharray: 0 {:.3f};\n"
|
||||
" animation: stroke-{} {:.3f}s {} infinite,\n"
|
||||
" showhide-brush-{} {:.3f}s step-start infinite;\n"
|
||||
" }}\n"sv
|
||||
);
|
||||
|
||||
|
||||
pugi::xml_node append_group(pugi::xml_node &svg, std::string_view name, GroupConfig config)
|
||||
{
|
||||
pugi::xml_node newGroup = svg.append_child("g");
|
||||
newGroup.append_attribute("id") = name;
|
||||
newGroup.append_attribute("style") = std::format(
|
||||
"fill:none;stroke:{};stroke-width:{};stroke-linecap:round;stroke-linejoin:round;",
|
||||
config.Colour.ToHex(), config.Width
|
||||
);
|
||||
return newGroup;
|
||||
}
|
||||
}
|
||||
|
||||
bool Animate(const std::string& source, const std::string& destination)
|
||||
|
@ -39,13 +88,13 @@ namespace Kanimaji
|
|||
});
|
||||
std::string text = comment.value();
|
||||
text.append(
|
||||
"\n"
|
||||
"===============================================================================\n"
|
||||
"\n"
|
||||
"\n===============================================================================\n\n"
|
||||
"This file has been modified by the Kanimaji tool\n"
|
||||
"avilable here: (TODO)\n\n"
|
||||
"The Kanimaji tool is based on the original kanimaji.py\n"
|
||||
"script available here: https://github.com/maurimo/kanimaji\n\n"
|
||||
"The Kanimaji tool is based on the original kanimaji.py script.\n"
|
||||
"Copyright (c) 2016 Maurizio Monge\n"
|
||||
"The script is available here: https://github.com/maurimo/kanimaji\n"
|
||||
"Used under the terms of the MIT licence as specified in the project's README.md\n\n"
|
||||
"Modifications (compared to the original KanjiVG file):\n"
|
||||
"* The stroke numbers were removed\n"
|
||||
"* Special Styles section (and accompanying <g> and <use> tags) were generated\n"
|
||||
|
@ -53,9 +102,6 @@ namespace Kanimaji
|
|||
comment.set_value(text);
|
||||
|
||||
pugi::xml_node svg = doc.child("svg");
|
||||
pugi::xml_attribute xmlns = svg.attribute("xmlns");
|
||||
svg.append_attribute("xmlns:xlink") = "http://www.w3.org/1999/xlink";
|
||||
svg.append_attribute("xlink:used") = "";
|
||||
|
||||
svg.remove_child(svg.find_child([](pugi::xml_node& node) {
|
||||
return std::string_view(node.attribute("id").as_string()).contains("StrokeNumbers");
|
||||
|
@ -64,78 +110,94 @@ namespace Kanimaji
|
|||
pugi::xml_node paths = svg.find_child([](pugi::xml_node& node) {
|
||||
return std::string_view(node.attribute("id").as_string()).contains("StrokePaths");
|
||||
});
|
||||
paths.attribute("style") = "fill:none;visibility:hidden;";
|
||||
std::string baseId = paths.attribute("id").as_string();
|
||||
baseId.erase(0, baseId.find('_') + 1);
|
||||
std::println("{}", baseId);
|
||||
|
||||
// 1st pass for getting information
|
||||
float totalLength = 50;
|
||||
double totalLength = 0.0;
|
||||
for (const auto& path : svg.select_nodes("//path")) {
|
||||
std::println("{}", path.node().attribute("d").as_string());
|
||||
std::string_view d = path.node().attribute("d").as_string();
|
||||
totalLength += SVG::Path(d).Length();
|
||||
try {
|
||||
totalLength += SVG::Path(d).Length();
|
||||
}
|
||||
catch (const SVG::ParseError& e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string styles = "/* Styles autogenerated by Kanimaji, please do not edit manually. */";
|
||||
std::string styles =
|
||||
"\n /* Styles autogenerated by Kanimaji, please do not edit manually. */\n";
|
||||
|
||||
pugi::xml_node background = svg.append_child("g");
|
||||
background.append_attribute("id") = "kvg:Kanimaji_Background_" + baseId;
|
||||
background.append_attribute("style") = std::format(
|
||||
"fill:none;stroke:{};stroke-width:{};stroke-linecap:round;stroke-linejoin:round;",
|
||||
strokeUnfilledColour, strokeUnfilledWidth
|
||||
);
|
||||
|
||||
pugi::xml_node brushBackground = svg.append_child("g");
|
||||
brushBackground.append_attribute("id") = "kvg:Kanimaji_BrushBackground_" + baseId;
|
||||
brushBackground.append_attribute("style") = std::format(
|
||||
"fill:none;stroke:{};stroke-width:{},stroke-linecap:round;stroke-linejoin:round;",
|
||||
brushBorderColour, brushBorderWidth
|
||||
);
|
||||
|
||||
pugi::xml_node animation = svg.append_child("g");
|
||||
animation.append_attribute("id") = "kvg:Kanimaji_Animation_" + baseId;
|
||||
animation.append_attribute("style") = std::format(
|
||||
"fill:none;stroke:{};stroke-width:{};stroke-linecap:round;stroke-linejoin:round;",
|
||||
strokeFilledColour, strokeFilledWidth
|
||||
);
|
||||
|
||||
pugi::xml_node brush = svg.append_child("g");
|
||||
brush.append_attribute("id") = "kvg:Kanimaji_Brush_" + baseId;
|
||||
brush.append_attribute("style") = std::format(
|
||||
"fill:none;stroke:{};stroke-width:{};stroke-linecap:round;stroke-linejoin:round;",
|
||||
brushColour, brushWidth
|
||||
);
|
||||
auto background = append_group(svg, "kvg:Kanimaji_Stroke_Background_" + baseId, StrokeUnfilledConfig);
|
||||
auto brushBorder = append_group(svg, "kvg:Kanimaji_Brush_Background_" + baseId, BrushBorderConfig);
|
||||
auto animation = append_group(svg, "kvg:Kanimaji_Animation_" + baseId, StrokeFilledConfig);
|
||||
auto brush = append_group(svg, "kvg:Kanimaji_Brush_" + baseId, BrushConfig);
|
||||
|
||||
double previousLength = 0.0;
|
||||
double currentLength = 0.0;
|
||||
// 2nd pass to prepare the CSS
|
||||
for (const auto& xpath : svg.select_nodes("//path")) {
|
||||
pugi::xml_node path = xpath.node();
|
||||
std::string id = path.attribute("id").as_string();
|
||||
std::string css = id;
|
||||
css.replace(css.find(":"), 1, "\\\\3a ");
|
||||
|
||||
// Generate the actual styles
|
||||
// - A few simplifications for now
|
||||
// - Do not use the path lengths and appropriate scaled time,
|
||||
// use a simplified version based solely on the stroke number.
|
||||
double segmentLength = SVG::Path(path.attribute("d").as_string()).Length();
|
||||
previousLength = currentLength;
|
||||
currentLength += segmentLength;
|
||||
|
||||
double startTime = 100.0 * (previousLength / totalLength);
|
||||
double endTime = 100.0 * (currentLength / totalLength);
|
||||
|
||||
pugi::xml_attribute idAttr = path.attribute("id");
|
||||
if (!idAttr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string id = idAttr.as_string();
|
||||
std::string css = id;
|
||||
if (auto colonPos = css.find(':'); colonPos != css.npos) {
|
||||
css.replace(css.find(":"), 1, "\\:");
|
||||
}
|
||||
|
||||
std::string pathId = id;
|
||||
if (pathId.starts_with("kvg:")) {
|
||||
pathId = pathId.substr(4);
|
||||
}
|
||||
|
||||
double totalTime = 4.0;
|
||||
|
||||
styles.append(std::format(StrokeProgressionFormat,
|
||||
pathId, segmentLength, startTime, segmentLength, endTime));
|
||||
styles.append(std::format(AnimationVisibilityFormat,
|
||||
pathId, startTime, endTime, StrokeFillingColour.ToHex()));
|
||||
styles.append(std::format(AnimationProgressionFormat,
|
||||
css + "-kanimaji-animation", segmentLength, segmentLength,
|
||||
pathId, totalTime, "ease-in-out", pathId, totalTime));
|
||||
styles.append(std::format(BrushVisibilityFormat, pathId, startTime, endTime - 0.001));
|
||||
styles.append(std::format(BrushProgressionFormat,
|
||||
css + "-kanimaji-brush", css + "-kanimaji-brush-background",
|
||||
segmentLength, pathId, totalTime, "ease-in-out", pathId,
|
||||
totalTime));
|
||||
|
||||
|
||||
pugi::xml_node useBackground = background.append_child("use");
|
||||
useBackground.append_attribute("id") = id + "-background";
|
||||
useBackground.append_attribute("xlink:href") = "#" + id;
|
||||
useBackground.append_attribute("id") = id + "-kanimaji-background";
|
||||
useBackground.append_attribute("href") = "#" + id;
|
||||
|
||||
pugi::xml_node useBrushBackground = brushBackground.append_child("use");
|
||||
useBrushBackground.append_attribute("id") = id + "-brush-background";
|
||||
useBrushBackground.append_attribute("xlink:href") = "#" + id;
|
||||
pugi::xml_node useBrushBackground = brushBorder.append_child("use");
|
||||
useBrushBackground.append_attribute("id") = id + "-kanimaji-brush-background";
|
||||
useBrushBackground.append_attribute("href") = "#" + id;
|
||||
|
||||
pugi::xml_node useAnimation = animation.append_child("use");
|
||||
useAnimation.append_attribute("id") = id + "-animation";
|
||||
useAnimation.append_attribute("xlink:href") = "#" + id;
|
||||
useAnimation.append_attribute("id") = id + "-kanimaji-animation";
|
||||
useAnimation.append_attribute("href") = "#" + id;
|
||||
|
||||
pugi::xml_node useBrush = brush.append_child("use");
|
||||
useBrush.append_attribute("id") = id + "-brush";
|
||||
useBrush.append_attribute("xlink:href") = "#" + id;
|
||||
useBrush.append_attribute("id") = id + "-kanimaji-brush";
|
||||
useBrush.append_attribute("href") = "#" + id;
|
||||
}
|
||||
|
||||
// This is how we add styles - prepare the whole thing in a separate buffer first.
|
||||
styles.append(" ");
|
||||
pugi::xml_node style = svg.prepend_child("style");
|
||||
style.append_attribute("id") = "kvg:Kanimaji_Style";
|
||||
style.append_child(pugi::node_pcdata).set_value(styles);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue