Add better timings, and proper configuration

This commit is contained in:
TennesseeTrash 2025-06-08 00:23:46 +02:00
parent 0b32af33b8
commit 5750bb8177
13 changed files with 385 additions and 240 deletions

View file

@ -1,4 +1,3 @@
include(${PROJECT_SOURCE_DIR}/CMake/ctre.cmake)
include(${PROJECT_SOURCE_DIR}/CMake/pugixml.cmake) include(${PROJECT_SOURCE_DIR}/CMake/pugixml.cmake)
add_library(Kanimaji STATIC) add_library(Kanimaji STATIC)
@ -10,7 +9,6 @@ target_compile_features(Kanimaji
target_link_libraries(Kanimaji target_link_libraries(Kanimaji
PRIVATE PRIVATE
ctre
pugixml pugixml
) )
@ -21,7 +19,8 @@ target_include_directories(Kanimaji
target_sources(Kanimaji target_sources(Kanimaji
PRIVATE PRIVATE
"Source/Colour.cpp" "Source/Error.cpp"
"Source/Kanimaji.cpp" "Source/Kanimaji.cpp"
"Source/Settings.cpp"
"Source/SVG.cpp" "Source/SVG.cpp"
) )

View file

@ -1,20 +0,0 @@
#ifndef KANIMAJI_CONFIG_HPP
#define KANIMAJI_CONFIG_HPP
#include "Colour.hpp"
namespace Kanimaji
{
struct GroupConfig
{
double Width;
Colour Colour;
};
struct AnimationSettings
{
};
}
#endif // KANIMAJI_CONFIG_HPP

View file

@ -1,9 +1,32 @@
#ifndef KANIMAJI_ERROR_HPP #ifndef KANIMAJI_ERROR_HPP
#define KANIMAJI_ERROR_HPP #define KANIMAJI_ERROR_HPP
#include <stdexcept>
namespace Kanimaji namespace Kanimaji
{ {
class Error : public std::runtime_error
{
using std::runtime_error::runtime_error;
};
class FileError : public Error
{
using Error::Error;
};
class ParseError : public Error
{
public:
ParseError(std::size_t current, std::string_view message);
std::size_t Position() const
{
return mPosition;
}
private:
std::size_t mPosition;
};
} }
#endif // KANIMAJI_ERROR_HPP #endif // KANIMAJI_ERROR_HPP

View file

@ -1,16 +1,20 @@
#ifndef KANIMAJI_KANIMAJI_HPP #ifndef KANIMAJI_KANIMAJI_HPP
#define KANIMAJI_KANIMAJI_HPP #define KANIMAJI_KANIMAJI_HPP
#include "Settings.hpp"
#include <filesystem>
#include <string> #include <string>
namespace Kanimaji namespace Kanimaji
{ {
bool Animate(const std::string& source, const std::string& destination); using Path = std::filesystem::path;
constexpr bool Animate(const std::string& filename) void AnimateFile(const Path& source, const Path& destination,
{ const AnimationSettings& settings = AnimationSettings::Default());
return Animate(filename, filename);
} std::string Animate(const std::string& source,
const AnimationSettings& settings = AnimationSettings::Default());
} }
#endif // KANIMAJI_KANIMAJI_HPP #endif // KANIMAJI_KANIMAJI_HPP

View file

@ -1,5 +1,5 @@
#ifndef KANIMAJI_COLOUR_HPP #ifndef KANIMAJI_RGB_HPP
#define KANIMAJI_COLOUR_HPP #define KANIMAJI_RGB_HPP
#include <cstdint> #include <cstdint>
#include <string> #include <string>
@ -56,16 +56,16 @@ namespace Kanimaji
} }
} }
struct Colour struct RGB
{ {
std::uint8_t Red; std::uint8_t Red;
std::uint8_t Green; std::uint8_t Green;
std::uint8_t Blue; std::uint8_t Blue;
static constexpr Colour FromHex(std::string_view hex) static constexpr RGB FromHex(std::string_view hex)
{ {
if (hex.empty()) { if (hex.empty()) {
return Colour { .Red = 0, .Green = 0, .Blue = 0 }; return RGB { .Red = 0, .Green = 0, .Blue = 0 };
} }
if (hex[0] == '#') { if (hex[0] == '#') {
@ -74,12 +74,12 @@ namespace Kanimaji
for (char ch : hex) { for (char ch : hex) {
if ((ch < '0' || ch > '9') && (ch < 'A' || ch > 'F') && (ch < 'a' && ch > 'f')) { if ((ch < '0' || ch > '9') && (ch < 'A' || ch > 'F') && (ch < 'a' && ch > 'f')) {
return Colour { .Red = 0, .Green = 0, .Blue = 0 }; return RGB { .Red = 0, .Green = 0, .Blue = 0 };
} }
} }
if (hex.length() == 3) { if (hex.length() == 3) {
return Colour { return RGB {
.Red = Implementation::ToDec(hex[0]), .Red = Implementation::ToDec(hex[0]),
.Green = Implementation::ToDec(hex[1]), .Green = Implementation::ToDec(hex[1]),
.Blue = Implementation::ToDec(hex[2]), .Blue = Implementation::ToDec(hex[2]),
@ -87,14 +87,14 @@ namespace Kanimaji
} }
if (hex.length() == 6) { if (hex.length() == 6) {
return Colour { return RGB {
.Red = Implementation::ToDec(hex[0], hex[1]), .Red = Implementation::ToDec(hex[0], hex[1]),
.Green = Implementation::ToDec(hex[2], hex[3]), .Green = Implementation::ToDec(hex[2], hex[3]),
.Blue = Implementation::ToDec(hex[4], hex[5]), .Blue = Implementation::ToDec(hex[4], hex[5]),
}; };
} }
return Colour { .Red = 0, .Green = 0, .Blue = 0 }; return RGB { .Red = 0, .Green = 0, .Blue = 0 };
} }
constexpr std::string ToHex() const constexpr std::string ToHex() const
@ -108,4 +108,4 @@ namespace Kanimaji
}; };
} }
#endif // KANIMAJI_COLOUR_HPP #endif // KANIMAJI_RGB_HPP

View file

@ -0,0 +1,52 @@
#ifndef KANIMAJI_CONFIG_HPP
#define KANIMAJI_CONFIG_HPP
#include "RGB.hpp"
#include <chrono>
#include <functional>
namespace Kanimaji
{
enum class Flag {
Enable, Disable,
};
enum class Progression {
Linear,
EaseIn,
EaseOut,
EaseInOut,
};
std::string ToString(Progression progression);
struct StrokeStyle
{
double Width;
RGB Colour;
};
using TimeScalingFunc = std::function<double(double)>;
using Duration = std::chrono::duration<double, std::ratio<1>>;
struct AnimationSettings
{
Progression StrokeProgression;
StrokeStyle UnfilledStroke;
StrokeStyle FilledStroke;
RGB StrokeFillingColour;
Flag EnableBrush;
StrokeStyle Brush;
StrokeStyle BrushBorder;
TimeScalingFunc LengthToTimeScaling;
Duration WaitBeforeRepeating;
Duration DelayBetweenStrokes;
static AnimationSettings Default();
};
}
#endif // KANIMAJI_CONFIG_HPP

View file

@ -1 +0,0 @@
#include "Kanimaji/Colour.hpp"

View file

@ -0,0 +1,11 @@
#include "Kanimaji/Error.hpp"
#include <format>
namespace Kanimaji
{
ParseError::ParseError(std::size_t current, std::string_view message)
: Error(std::format("[At: {}] {}", current, message))
, mPosition(current)
{}
}

View file

@ -1,9 +1,7 @@
#include "Kanimaji/Colour.hpp"
#include "Kanimaji/Config.hpp"
#include "Kanimaji/Kanimaji.hpp" #include "Kanimaji/Kanimaji.hpp"
#include "Kanimaji/Settings.hpp"
#include "SVG.hpp" #include "SVG.hpp"
#include <ctre.hpp>
#include <pugixml.hpp> #include <pugixml.hpp>
#include <format> #include <format>
@ -13,57 +11,48 @@ namespace Kanimaji
{ {
namespace namespace
{ {
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 Colour StrokeFillingColour = Colour::FromHex("#F00");
// What the fuck is this?
constexpr double WaitAfter = 1.5;
using namespace std::string_view_literals; using namespace std::string_view_literals;
constexpr auto StrokeProgressionFormat = ( constexpr auto StrokeProgressionFormat =
" @keyframes stroke-{} {{\n" " "
" 0.000% {{ stroke-dashoffset: {:.3f}; }}\n" "@keyframes stroke-{} {{ "
" {:.3f}% {{ stroke-dashoffset: {:.3f}; }}\n" "0.000% {{ stroke-dashoffset: {:.3f}; }} "
" {:.3f}% {{ stroke-dashoffset: 0; }}\n" "{:.3f}% {{ stroke-dashoffset: {:.3f}; }} "
" 100.000% {{ stroke-dashoffset: 0; }}\n" "{:.3f}% {{ stroke-dashoffset: 0; }} "
" }}\n"sv "100.000% {{ stroke-dashoffset: 0; }} }}\n"sv;
); constexpr auto AnimationVisibilityFormat =
constexpr auto AnimationVisibilityFormat = ( " "
" @keyframes showhide-{} {{\n" "@keyframes display-{} {{ "
" {:.3f}% {{ visibility: hidden; }}\n" "{:.3f}% {{ visibility: hidden; }} "
" {:.3f}% {{ stroke: {}; }}\n" "{:.3f}% {{ stroke: {}; }} }}\n"sv;
" }}\n"sv constexpr auto AnimationProgressionFormat =
); " "
constexpr auto AnimationProgressionFormat = ( "#{} {{ "
" #{} {{\n" "stroke-dasharray: {:.3f} {:.3f}; "
" stroke-dasharray: {:.3f} {:.3f};\n" "stroke-dashoffset: 0; "
" stroke-dashoffset: 0;\n" "animation: stroke-{} {:.3f}s {} infinite, "
" animation: stroke-{} {:.3f}s {} infinite,\n" "display-{} {:.3f}s step-start infinite; }}\n"sv;
" showhide-{} {:.3f}s step-start infinite;\n" constexpr auto BrushVisibilityFormat =
" }}\n"sv " "
); "@keyframes display-brush-{} {{ "
constexpr auto BrushVisibilityFormat = ( "{:.3f}% {{ visibility: hidden; }} "
" @keyframes showhide-brush-{} {{\n" "{:.3f}% {{ visibility: visible; }} "
" {:.3f}% {{ visibility: hidden; }}\n" "100.000% {{ visibility: hidden; }} }}\n"sv;
" {:.3f}% {{ visibility: visible; }}\n" constexpr auto BrushProgressionFormat =
" 100.000% {{ visibility: hidden; }}\n" " "
" }}\n"sv "#{}, #{} {{ "
); "stroke-dasharray: 0 {:.3f}; "
constexpr auto BrushProgressionFormat = ( "animation: stroke-{} {:.3f}s {} infinite, "
" #{}, #{} {{\n" "display-brush-{} {:.3f}s step-start infinite; }}\n"sv;
" stroke-dasharray: 0 {:.3f};\n"
" animation: stroke-{} {:.3f}s {} infinite,\n"
" showhide-brush-{} {:.3f}s step-start infinite;\n"
" }}\n"sv
);
constexpr auto StylesHeader =
"\n "
"/* Styles generated automatically by Kanimaji, please avoid editing manually. */\n"sv;
pugi::xml_node append_group(pugi::xml_node &svg, std::string_view name, GroupConfig config) double AsSeconds(auto duration) {
return std::max(0.0, duration.count());
}
pugi::xml_node AppendGroup(pugi::xml_node& svg, std::string_view name, StrokeStyle config)
{ {
pugi::xml_node newGroup = svg.append_child("g"); pugi::xml_node newGroup = svg.append_child("g");
newGroup.append_attribute("id") = name; newGroup.append_attribute("id") = name;
@ -73,81 +62,95 @@ namespace Kanimaji
); );
return newGroup; return newGroup;
} }
}
bool Animate(const std::string& source, const std::string& destination) void AmendComment(pugi::xml_document& doc)
{ {
pugi::xml_document doc;
pugi::xml_parse_result readResult = doc.load_file(source.c_str(), pugi::parse_full);
if (!readResult) {
return false;
}
pugi::xml_node comment = doc.find_child([](pugi::xml_node& node) { pugi::xml_node comment = doc.find_child([](pugi::xml_node& node) {
return node.type() == pugi::node_comment; return node.type() == pugi::node_comment;
}); });
if (comment) {
std::string text = comment.value(); std::string text = comment.value();
text.append( text.append(
"\n===============================================================================\n\n" "\n"
"===============================================================================\n"
"\n"
"This file has been modified by the Kanimaji tool\n" "This file has been modified by the Kanimaji tool\n"
"avilable here: (TODO)\n\n" "avilable here: (Still very WIP, will be available later.)\n"
"\n"
"The Kanimaji tool is based on the original kanimaji.py script.\n" "The Kanimaji tool is based on the original kanimaji.py script.\n"
"Copyright (c) 2016 Maurizio Monge\n" "Copyright (c) 2016 Maurizio Monge\n"
"The script is available here: https://github.com/maurimo/kanimaji\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" "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" "Modifications (compared to the original KanjiVG file):\n"
"* The stroke numbers were removed\n" "* This comment was amended with this section\n"
"* The stroke numbers were removed to prevent decrease clutter\n"
"* Special Styles section (and accompanying <g> and <use> tags) were generated\n" "* Special Styles section (and accompanying <g> and <use> tags) were generated\n"
); );
comment.set_value(text); comment.set_value(text);
}
}
void Animate(pugi::xml_document& doc, const AnimationSettings& settings)
{
pugi::xml_node svg = doc.child("svg"); pugi::xml_node svg = doc.child("svg");
if (!svg) {
throw Error("Unexpected document format: Expected to find a SVG element");
}
svg.remove_child(svg.find_child([](pugi::xml_node& node) { svg.remove_child(svg.find_child([](pugi::xml_node& node) {
return std::string_view(node.attribute("id").as_string()).contains("StrokeNumbers"); return std::string_view(node.attribute("id").as_string()).contains("StrokeNumbers");
})); }));
pugi::xml_node paths = svg.find_child([](pugi::xml_node& node) { pugi::xml_node pathsContainer = svg.find_child([](pugi::xml_node& node) {
return std::string_view(node.attribute("id").as_string()).contains("StrokePaths"); return std::string_view(node.attribute("id").as_string()).contains("StrokePaths");
}); });
paths.attribute("style") = "fill:none;visibility:hidden;"; pathsContainer.attribute("style") = "fill:none;visibility:hidden;";
std::string baseId = paths.attribute("id").as_string(); std::string baseId = pathsContainer.attribute("id").as_string();
baseId.erase(0, baseId.find('_') + 1); baseId.erase(0, baseId.find('_') + 1);
// 1st pass for getting information // 1st pass for getting information
double totalLength = 0.0; double totalLength = 0.0;
for (const auto& path : svg.select_nodes("//path")) { pugi::xpath_node_set paths = svg.select_nodes("//path");
for (const auto& path : paths) {
std::string_view d = path.node().attribute("d").as_string(); std::string_view d = path.node().attribute("d").as_string();
try { totalLength += settings.LengthToTimeScaling(SVG::Path(d).Length());
totalLength += SVG::Path(d).Length();
}
catch (const SVG::ParseError& e) {
return false;
}
} }
std::string styles = std::string strokeKeyframes;
"\n /* Styles autogenerated by Kanimaji, please do not edit manually. */\n"; std::string strokeDisplay;
std::string strokeProgress;
std::string brushKeyframes;
std::string brushProgress;
auto background = append_group(svg, "kvg:Kanimaji_Stroke_Background_" + baseId, StrokeUnfilledConfig); auto background = AppendGroup(svg, "kvg:Kanimaji_Stroke_Background_" + baseId,
auto brushBorder = append_group(svg, "kvg:Kanimaji_Brush_Background_" + baseId, BrushBorderConfig); settings.UnfilledStroke);
auto animation = append_group(svg, "kvg:Kanimaji_Animation_" + baseId, StrokeFilledConfig); auto brushBorder = AppendGroup(svg, "kvg:Kanimaji_Brush_Border_" + baseId,
auto brush = append_group(svg, "kvg:Kanimaji_Brush_" + baseId, BrushConfig); settings.BrushBorder);
auto animation = AppendGroup(svg, "kvg:Kanimaji_Animation_" + baseId,
settings.FilledStroke);
auto brush = AppendGroup(svg, "kvg:Kanimaji_Brush_" + baseId, settings.Brush);
totalLength += paths.size() * AsSeconds(settings.DelayBetweenStrokes);
double totalTime = totalLength
+ AsSeconds(settings.WaitBeforeRepeating)
- AsSeconds(settings.DelayBetweenStrokes);
double previousLength = 0.0; double previousLength = 0.0;
double currentLength = 0.0; double currentLength = 0.0;
// 2nd pass to prepare the CSS // 2nd pass to prepare the CSS
for (const auto& xpath : svg.select_nodes("//path")) { for (const auto& path : paths) {
pugi::xml_node path = xpath.node(); std::string_view d = path.node().attribute("d").as_string();
double segmentLength = SVG::Path(path.attribute("d").as_string()).Length(); double segmentLength = SVG::Path(d).Length();
previousLength = currentLength; previousLength = currentLength;
currentLength += segmentLength; currentLength += settings.LengthToTimeScaling(segmentLength);
double startTime = 100.0 * (previousLength / totalLength); double startTime = 100.0 * (previousLength / totalTime);
double endTime = 100.0 * (currentLength / totalLength); double endTime = 100.0 * (currentLength / totalTime);
pugi::xml_attribute idAttr = path.attribute("id"); currentLength += AsSeconds(settings.DelayBetweenStrokes);
pugi::xml_attribute idAttr = path.node().attribute("id");
if (!idAttr) { if (!idAttr) {
continue; continue;
} }
@ -163,50 +166,92 @@ namespace Kanimaji
pathId = pathId.substr(4); pathId = pathId.substr(4);
} }
double totalTime = 4.0; strokeKeyframes.append(std::format(StrokeProgressionFormat,
styles.append(std::format(StrokeProgressionFormat,
pathId, segmentLength, startTime, segmentLength, endTime)); pathId, segmentLength, startTime, segmentLength, endTime));
styles.append(std::format(AnimationVisibilityFormat, strokeDisplay.append(std::format(AnimationVisibilityFormat,
pathId, startTime, endTime, StrokeFillingColour.ToHex())); pathId, startTime, endTime,
styles.append(std::format(AnimationProgressionFormat, settings.StrokeFillingColour.ToHex()));
strokeProgress.append(std::format(AnimationProgressionFormat,
css + "-kanimaji-animation", segmentLength, segmentLength, css + "-kanimaji-animation", segmentLength, segmentLength,
pathId, totalTime, "ease-in-out", pathId, totalTime)); pathId, totalTime, ToString(settings.StrokeProgression),
styles.append(std::format(BrushVisibilityFormat, pathId, startTime, endTime - 0.001)); pathId, totalTime));
styles.append(std::format(BrushProgressionFormat, if (settings.EnableBrush == Flag::Enable) {
css + "-kanimaji-brush", css + "-kanimaji-brush-background", brushKeyframes.append(std::format(BrushVisibilityFormat,
segmentLength, pathId, totalTime, "ease-in-out", pathId, pathId, startTime, endTime - 0.001));
brushProgress.append(std::format(BrushProgressionFormat,
css + "-kanimaji-brush",
css + "-kanimaji-brush-border", segmentLength,
pathId, totalTime,
ToString(settings.StrokeProgression), pathId,
totalTime)); totalTime));
}
pugi::xml_node useBackground = background.append_child("use"); pugi::xml_node useBackground = background.append_child("use");
useBackground.append_attribute("id") = id + "-kanimaji-background"; useBackground.append_attribute("id") = id + "-kanimaji-background";
useBackground.append_attribute("href") = "#" + id; useBackground.append_attribute("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"); pugi::xml_node useAnimation = animation.append_child("use");
useAnimation.append_attribute("id") = id + "-kanimaji-animation"; useAnimation.append_attribute("id") = id + "-kanimaji-animation";
useAnimation.append_attribute("href") = "#" + id; useAnimation.append_attribute("href") = "#" + id;
if (settings.EnableBrush == Flag::Enable) {
pugi::xml_node useBrushBackground = brushBorder.append_child("use");
useBrushBackground.append_attribute("id") = id + "-kanimaji-brush-border";
useBrushBackground.append_attribute("href") = "#" + id;
pugi::xml_node useBrush = brush.append_child("use"); pugi::xml_node useBrush = brush.append_child("use");
useBrush.append_attribute("id") = id + "-kanimaji-brush"; useBrush.append_attribute("id") = id + "-kanimaji-brush";
useBrush.append_attribute("href") = "#" + id; useBrush.append_attribute("href") = "#" + id;
} }
}
// This is how we add styles - prepare the whole thing in a separate buffer first. // This is how we add styles - prepare the whole thing in a separate buffer first.
std::string styles;
styles.append(StylesHeader);
styles.append(strokeKeyframes);
styles.append(strokeDisplay);
styles.append(strokeProgress);
styles.append(brushKeyframes);
styles.append(brushProgress);
styles.append(" "); styles.append(" ");
pugi::xml_node style = svg.prepend_child("style"); pugi::xml_node style = svg.prepend_child("style");
style.append_attribute("id") = "kvg:Kanimaji_Style"; style.append_attribute("id") = "kvg:Kanimaji_Style";
style.append_child(pugi::node_pcdata).set_value(styles); style.append_child(pugi::node_pcdata).set_value(styles);
}
bool writeResult = doc.save_file(destination.c_str());
if (!writeResult) {
return false;
} }
return true; void AnimateFile(const Path& source, const Path& destination, const AnimationSettings& settings)
{
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(source.c_str(), pugi::parse_full);
if (!result) {
constexpr std::string_view errorFormat = "Failed to load file from {}, due to: {}";
throw FileError(std::format(errorFormat, source.string(), result.description()));
}
AmendComment(doc);
Animate(doc, settings);
if (!doc.save_file(destination.c_str())) {
throw FileError(std::format("Failed to save file to {}", destination.string()));
}
}
std::string Animate(const std::string& source, const AnimationSettings& settings)
{
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_string(source.c_str(), pugi::parse_full);
if (!result) {
constexpr std::string_view errorFormat = "Failed to parse SVG document: {}";
throw ParseError(result.offset, std::format(errorFormat, result.description()));
}
AmendComment(doc);
Animate(doc, settings);
std::stringstream str;
doc.save(str);
return str.str();
} }
} }

View file

@ -9,11 +9,6 @@
namespace Kanimaji::SVG namespace Kanimaji::SVG
{ {
ParseError::ParseError(std::size_t current, std::string_view message)
: Error(std::format("[At: {}] {}", current, message))
, mPosition(current)
{}
struct Vec2 struct Vec2
{ {
double x; double x;

View file

@ -1,31 +1,14 @@
#ifndef KANIMAJI_SVG_HPP #ifndef KANIMAJI_SVG_HPP
#define KANIMAJI_SVG_HPP #define KANIMAJI_SVG_HPP
#include "Kanimaji/Error.hpp"
#include <memory> #include <memory>
#include <stdexcept>
#include <string_view> #include <string_view>
#include <vector> #include <vector>
namespace Kanimaji::SVG namespace Kanimaji::SVG
{ {
class Error : public std::runtime_error
{
using std::runtime_error::runtime_error;
};
class ParseError : public Error
{
public:
ParseError(std::size_t current, std::string_view message);
std::size_t Position() const
{
return mPosition;
}
private:
std::size_t mPosition;
};
class Path class Path
{ {
public: public:

View file

@ -0,0 +1,51 @@
#include "Kanimaji/Settings.hpp"
#include <cmath>
#include <utility>
namespace Kanimaji
{
std::string ToString(Progression progression)
{
switch (progression) {
case Progression::Linear: return "linear";
case Progression::EaseIn: return "ease-in";
case Progression::EaseOut: return "ease-out";
case Progression::EaseInOut: return "ease-in-out";
default: std::unreachable();
};
}
AnimationSettings AnimationSettings::Default()
{
using namespace std::chrono_literals;
return AnimationSettings {
.StrokeProgression = Progression::EaseInOut,
.UnfilledStroke = StrokeStyle {
.Width = 2.0,
.Colour = RGB::FromHex("#EEEEEE"),
},
.FilledStroke = StrokeStyle {
.Width = 3.0,
.Colour = RGB::FromHex("#000000"),
},
.StrokeFillingColour = RGB::FromHex("#FF0000"),
.EnableBrush = Flag::Enable,
.Brush = StrokeStyle {
.Width = 5.5,
.Colour = RGB::FromHex("#FF0000"),
},
.BrushBorder = StrokeStyle {
.Width = 7.0,
.Colour = RGB::FromHex("#666666"),
},
.LengthToTimeScaling = [] (double length) -> double {
return std::pow(length, 1.0 / 3.0) / 6.0;
},
.WaitBeforeRepeating = 1s,
.DelayBetweenStrokes = 50ms,
};
}
}

View file

@ -12,8 +12,11 @@ int main(const int argc, const char *argv[])
// return 1; // return 1;
//} //}
if (!Kanimaji::Animate("084b8.svg", "084b8-out.svg")) { try {
std::println("Could not animate the specified file."); Kanimaji::AnimateFile("084b8.svg", "084b8-out.svg");
}
catch (const std::exception& e) {
std::println("Could not animate the input file");
return 1; return 1;
} }