From 0c0a8e346ca7b83009c0711596a73a444c04f618 Mon Sep 17 00:00:00 2001 From: TennesseeTrash Date: Mon, 16 Jun 2025 20:33:22 +0200 Subject: [PATCH 1/6] [Kanimaji] Add settings to stroke removal function --- .../Kanimaji/Include/Kanimaji/Kanimaji.hpp | 6 +- .../Kanimaji/Include/Kanimaji/Settings.hpp | 7 +++ Libraries/Kanimaji/Source/Kanimaji.cpp | 61 +++++++++++++------ Libraries/Kanimaji/Source/Settings.cpp | 10 +++ 4 files changed, 65 insertions(+), 19 deletions(-) diff --git a/Libraries/Kanimaji/Include/Kanimaji/Kanimaji.hpp b/Libraries/Kanimaji/Include/Kanimaji/Kanimaji.hpp index 79d3d9b..024e9f2 100644 --- a/Libraries/Kanimaji/Include/Kanimaji/Kanimaji.hpp +++ b/Libraries/Kanimaji/Include/Kanimaji/Kanimaji.hpp @@ -13,9 +13,11 @@ namespace Kanimaji std::string Animate(const std::string& source, const AnimationSettings& settings = AnimationSettings::Default()); - void RemoveStrokeNumbers(const std::string& source, const std::string& destination); + void RemoveStrokeNumbers(const std::string& source, const std::string& destination, + const RemovalSetttings& settings = RemovalSetttings::Default()); - std::string RemoveStrokeNumbers(const std::string& source); + std::string RemoveStrokeNumbers(const std::string& source, + const RemovalSetttings& settings = RemovalSetttings::Default()); } #endif // KANIMAJI_KANIMAJI_HPP diff --git a/Libraries/Kanimaji/Include/Kanimaji/Settings.hpp b/Libraries/Kanimaji/Include/Kanimaji/Settings.hpp index 47496fa..53f8597 100644 --- a/Libraries/Kanimaji/Include/Kanimaji/Settings.hpp +++ b/Libraries/Kanimaji/Include/Kanimaji/Settings.hpp @@ -49,6 +49,13 @@ namespace Kanimaji static AnimationSettings Default(); }; + + struct RemovalSetttings + { + StrokeStyle Style; + + static RemovalSetttings Default(); + }; } #endif // KANIMAJI_CONFIG_HPP diff --git a/Libraries/Kanimaji/Source/Kanimaji.cpp b/Libraries/Kanimaji/Source/Kanimaji.cpp index 3bf8034..d10eadb 100644 --- a/Libraries/Kanimaji/Source/Kanimaji.cpp +++ b/Libraries/Kanimaji/Source/Kanimaji.cpp @@ -12,42 +12,55 @@ namespace Kanimaji { namespace { - using namespace std::string_view_literals; - constexpr auto StrokeProgressionFormat = + constexpr std::string_view StaticStrokeStyleFormat( + "fill:none;stroke:{};stroke-width:{};stroke-linecap:round;stroke-linejoin:round;" + ); + + constexpr std::string_view StrokeProgressionFormat( " " "@keyframes stroke-{} {{ " "0.000% {{ stroke-dashoffset: {:.3f}; }} " "{:.3f}% {{ stroke-dashoffset: {:.3f}; }} " "{:.3f}% {{ stroke-dashoffset: 0; }} " - "100.000% {{ stroke-dashoffset: 0; }} }}\n"sv; - constexpr auto AnimationVisibilityFormat = + "100.000% {{ stroke-dashoffset: 0; }} }}\n" + ); + + constexpr std::string_view AnimationVisibilityFormat( " " "@keyframes display-{} {{ " "{:.3f}% {{ visibility: hidden; }} " - "{:.3f}% {{ stroke: {}; }} }}\n"sv; - constexpr auto AnimationProgressionFormat = + "{:.3f}% {{ stroke: {}; }} }}\n" + ); + + constexpr std::string_view AnimationProgressionFormat( " " "#{} {{ " "stroke-dasharray: {:.3f} {:.3f}; " "stroke-dashoffset: 0; " "animation: stroke-{} {:.3f}s {} infinite, " - "display-{} {:.3f}s step-start infinite; }}\n"sv; - constexpr auto BrushVisibilityFormat = + "display-{} {:.3f}s step-start infinite; }}\n" + ); + + constexpr std::string_view BrushVisibilityFormat( " " "@keyframes display-brush-{} {{ " "{:.3f}% {{ visibility: hidden; }} " "{:.3f}% {{ visibility: visible; }} " - "100.000% {{ visibility: hidden; }} }}\n"sv; - constexpr auto BrushProgressionFormat = + "100.000% {{ visibility: hidden; }} }}\n" + ); + + constexpr std::string_view BrushProgressionFormat( " " "#{}, #{} {{ " "stroke-dasharray: 0 {:.3f}; " "animation: stroke-{} {:.3f}s {} infinite, " - "display-brush-{} {:.3f}s step-start infinite; }}\n"sv; + "display-brush-{} {:.3f}s step-start infinite; }}\n" + ); - constexpr auto StylesHeader = + constexpr std::string_view StylesHeader( "\n " - "/* Styles generated automatically by Kanimaji, please avoid editing manually. */\n"sv; + "/* Styles generated automatically by Kanimaji, please avoid editing manually. */\n" + ); double AsSeconds(auto duration) { return std::max(0.0, duration.count()); @@ -65,8 +78,7 @@ namespace Kanimaji 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 + StaticStrokeStyleFormat, config.Colour.ToHex(), config.Width ); return newGroup; } @@ -270,7 +282,8 @@ namespace Kanimaji return str.str(); } - void RemoveStrokeNumbers(const std::string& source, const std::string& destination) + void RemoveStrokeNumbers(const std::string& source, const std::string& destination, + const RemovalSetttings& settings) { pugi::xml_document doc; pugi::xml_parse_result result = doc.load_file(source.c_str(), pugi::parse_full); @@ -284,6 +297,13 @@ namespace Kanimaji throw Error("Unexpected document format: Expected to find a SVG element"); } + pugi::xml_node pathsGroup = svg.find_child([](pugi::xml_node& node) { + return std::string_view(node.attribute("id").as_string()).contains("StrokePaths"); + }); + pathsGroup.attribute("style") = std::format( + StaticStrokeStyleFormat, settings.Style.Colour.ToHex(), settings.Style.Width + ); + RemoveStrokeNumbers(svg); if (!doc.save_file(destination.c_str())) { @@ -291,7 +311,7 @@ namespace Kanimaji } } - std::string RemoveStrokeNumbers(const std::string& source) + std::string RemoveStrokeNumbers(const std::string& source, const RemovalSetttings& settings) { pugi::xml_document doc; pugi::xml_parse_result result = doc.load_string(source.c_str(), pugi::parse_full); @@ -305,6 +325,13 @@ namespace Kanimaji throw Error("Unexpected document format: Expected to find a SVG element"); } + pugi::xml_node pathsGroup = svg.find_child([](pugi::xml_node& node) { + return std::string_view(node.attribute("id").as_string()).contains("StrokePaths"); + }); + pathsGroup.attribute("style") = std::format( + StaticStrokeStyleFormat, settings.Style.Colour.ToHex(), settings.Style.Width + ); + RemoveStrokeNumbers(svg); std::stringstream str; diff --git a/Libraries/Kanimaji/Source/Settings.cpp b/Libraries/Kanimaji/Source/Settings.cpp index b2d676f..4574cc6 100644 --- a/Libraries/Kanimaji/Source/Settings.cpp +++ b/Libraries/Kanimaji/Source/Settings.cpp @@ -48,4 +48,14 @@ namespace Kanimaji .DelayBetweenStrokes = 50ms, }; } + + RemovalSetttings RemovalSetttings::Default() + { + return RemovalSetttings { + .Style = StrokeStyle { + .Width = 3.0, + .Colour = RGB::FromHex("#000000"), + }, + }; + } } From 838d3dcdfefbed2e08562639c0f4494a812e7839 Mon Sep 17 00:00:00 2001 From: TennesseeTrash Date: Mon, 16 Jun 2025 20:33:47 +0200 Subject: [PATCH 2/6] Update the README with current API --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 39210af..df391ea 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,12 @@ void AnimateFile(const Path& source, const Path& destination, std::string Animate(const std::string& source, const AnimationSettings& settings = AnimationSettings::Default()); + +void RemoveStrokeNumbers(const std::string& source, const std::string& destination, + const RemovalSetttings& settings = RemovalSetttings::Default()); + +std::string RemoveStrokeNumbers(const std::string& source, + const RemovalSetttings& settings = RemovalSetttings::Default()); ``` The former function takes in two paths, where the first one specifies the file to be read, and @@ -113,7 +119,7 @@ Some caveats: The default settings generate a table with no characters (and horrible styling), you will need to specify custom settings like this for actual use: ```cpp -auto doc = GeneratePage({ +auto doc = GeneratePage(std::vector(), { .FullDocument = Flag::Enable, .OverrideIndentLevel = Flag::Disable, .IndentLevel = 0, From 7c3604fcf3ce613a22a1a87eb7cf87bf7ffc5d83 Mon Sep 17 00:00:00 2001 From: TennesseeTrash Date: Mon, 16 Jun 2025 23:01:03 +0200 Subject: [PATCH 3/6] [Tablegen] Add link elements to optimize image loading --- Libraries/Tablegen/Source/Tablegen.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/Libraries/Tablegen/Source/Tablegen.cpp b/Libraries/Tablegen/Source/Tablegen.cpp index e136dd8..a7a0f6b 100644 --- a/Libraries/Tablegen/Source/Tablegen.cpp +++ b/Libraries/Tablegen/Source/Tablegen.cpp @@ -64,11 +64,9 @@ namespace Tablegen constexpr std::string ImageStyle(std::string_view indent, std::size_t index, - std::string_view imageName, - std::string_view imageFormat, + std::string_view imagePath, std::string_view extraSpecifier = "") { - std::string imagePath = std::vformat(imageFormat, std::make_format_args(imageName)); return std::format(imageStyleFormat, indent, index, extraSpecifier, imagePath); } @@ -180,6 +178,21 @@ namespace Tablegen imageWrap.append_attribute("class") = "kanji-image-wrap"; imageWrap.append_attribute("id") = std::format("kanji-{}", i); + std::string imagePath = std::vformat( + settings.ImageFormat, std::make_format_args(character.ImagePath) + ); + std::string animationPath = std::vformat( + settings.AnimationFormat, std::make_format_args(character.AnimationPath) + ); + + pugi::xml_node prefetchImage = container.append_child("link"); + prefetchImage.append_attribute("rel") = "prefetch"; + prefetchImage.append_attribute("href") = imagePath; + + pugi::xml_node prefetchAnimation = container.append_child("link"); + prefetchAnimation.append_attribute("rel") = "prefetch"; + prefetchAnimation.append_attribute("href") = animationPath; + pugi::xml_node image = imageWrap.append_child("input"); image.append_attribute("type") = "checkbox"; image.append_attribute("class") = "kanji-image"; @@ -188,9 +201,8 @@ namespace Tablegen //////////////////////////////////////////////////////////////////// // CSS //////////////////////////////////////////////////////////////////// - staticImg.append(ImageStyle(indent, i, character.ImagePath, settings.ImageFormat)); - animatedImg.append(ImageStyle(indent, i, character.AnimationPath, - settings.AnimationFormat, ":checked")); + staticImg.append(ImageStyle(indent, i, imagePath)); + animatedImg.append(ImageStyle(indent, i, animationPath, ":checked")); //////////////////////////////////////////////////////////////////// // JavaScript From 5e22c0a727a8fcb589d155778d9c91f3c5f9c16b Mon Sep 17 00:00:00 2001 From: TennesseeTrash Date: Mon, 16 Jun 2025 23:16:50 +0200 Subject: [PATCH 4/6] [Tablegen] Make sure prefetches appear before styles --- Libraries/Tablegen/Source/Tablegen.cpp | 33 ++++++++++++++------------ 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/Libraries/Tablegen/Source/Tablegen.cpp b/Libraries/Tablegen/Source/Tablegen.cpp index a7a0f6b..56a4872 100644 --- a/Libraries/Tablegen/Source/Tablegen.cpp +++ b/Libraries/Tablegen/Source/Tablegen.cpp @@ -136,6 +136,9 @@ namespace Tablegen pugi::xml_node comment = table.append_child(pugi::node_comment); comment.set_value(" Autogenerated by Tablegen, please avoid editing manually. "); + pugi::xml_node prefetch = table.append_child("div"); + prefetch.append_attribute("hidden"); + pugi::xml_node script = table.append_child("script"); pugi::xml_node styles = table.append_child("style"); @@ -152,9 +155,24 @@ namespace Tablegen pugi::xml_node tableBody = table.append_child("tbody"); for (pugi::xml_node row; const auto& [i, character] : vw::enumerate(characters)) { + std::string imagePath = std::vformat( + settings.ImageFormat, std::make_format_args(character.ImagePath) + ); + std::string animationPath = std::vformat( + settings.AnimationFormat, std::make_format_args(character.AnimationPath) + ); + //////////////////////////////////////////////////////////////////// // HTML //////////////////////////////////////////////////////////////////// + pugi::xml_node prefetchImage = prefetch.append_child("link"); + prefetchImage.append_attribute("rel") = "prefetch"; + prefetchImage.append_attribute("href") = imagePath; + + pugi::xml_node prefetchAnimation = prefetch.append_child("link"); + prefetchAnimation.append_attribute("rel") = "prefetch"; + prefetchAnimation.append_attribute("href") = animationPath; + if (i % settings.CharactersPerRow == 0) { row = tableBody.append_child("tr"); row.append_attribute("class") = "kanji-table-row"; @@ -178,21 +196,6 @@ namespace Tablegen imageWrap.append_attribute("class") = "kanji-image-wrap"; imageWrap.append_attribute("id") = std::format("kanji-{}", i); - std::string imagePath = std::vformat( - settings.ImageFormat, std::make_format_args(character.ImagePath) - ); - std::string animationPath = std::vformat( - settings.AnimationFormat, std::make_format_args(character.AnimationPath) - ); - - pugi::xml_node prefetchImage = container.append_child("link"); - prefetchImage.append_attribute("rel") = "prefetch"; - prefetchImage.append_attribute("href") = imagePath; - - pugi::xml_node prefetchAnimation = container.append_child("link"); - prefetchAnimation.append_attribute("rel") = "prefetch"; - prefetchAnimation.append_attribute("href") = animationPath; - pugi::xml_node image = imageWrap.append_child("input"); image.append_attribute("type") = "checkbox"; image.append_attribute("class") = "kanji-image"; From c7d2d481c851ac0bda6799bb8f39e8b5912a4f84 Mon Sep 17 00:00:00 2001 From: TennesseeTrash Date: Mon, 16 Jun 2025 23:25:16 +0200 Subject: [PATCH 5/6] [Tablegen] Actually hide the div --- Libraries/Tablegen/Source/Tablegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/Tablegen/Source/Tablegen.cpp b/Libraries/Tablegen/Source/Tablegen.cpp index 56a4872..2f6bc25 100644 --- a/Libraries/Tablegen/Source/Tablegen.cpp +++ b/Libraries/Tablegen/Source/Tablegen.cpp @@ -137,7 +137,7 @@ namespace Tablegen comment.set_value(" Autogenerated by Tablegen, please avoid editing manually. "); pugi::xml_node prefetch = table.append_child("div"); - prefetch.append_attribute("hidden"); + prefetch.append_attribute("hidden") = "hidden"; pugi::xml_node script = table.append_child("script"); pugi::xml_node styles = table.append_child("style"); From e1e649975208170c7cee04a6b390f3f15de43061 Mon Sep 17 00:00:00 2001 From: TennesseeTrash Date: Mon, 16 Jun 2025 23:35:24 +0200 Subject: [PATCH 6/6] [Tablegen] Force preloading --- Libraries/Tablegen/Source/Tablegen.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Libraries/Tablegen/Source/Tablegen.cpp b/Libraries/Tablegen/Source/Tablegen.cpp index 2f6bc25..6b3084d 100644 --- a/Libraries/Tablegen/Source/Tablegen.cpp +++ b/Libraries/Tablegen/Source/Tablegen.cpp @@ -136,8 +136,8 @@ namespace Tablegen pugi::xml_node comment = table.append_child(pugi::node_comment); comment.set_value(" Autogenerated by Tablegen, please avoid editing manually. "); - pugi::xml_node prefetch = table.append_child("div"); - prefetch.append_attribute("hidden") = "hidden"; + pugi::xml_node preload = table.append_child("div"); + preload.append_attribute("hidden") = "hidden"; pugi::xml_node script = table.append_child("script"); pugi::xml_node styles = table.append_child("style"); @@ -165,13 +165,15 @@ namespace Tablegen //////////////////////////////////////////////////////////////////// // HTML //////////////////////////////////////////////////////////////////// - pugi::xml_node prefetchImage = prefetch.append_child("link"); - prefetchImage.append_attribute("rel") = "prefetch"; - prefetchImage.append_attribute("href") = imagePath; + pugi::xml_node preloadImage = preload.append_child("link"); + preloadImage.append_attribute("rel") = "preload"; + preloadImage.append_attribute("href") = imagePath; + preloadImage.append_attribute("as") = "image"; - pugi::xml_node prefetchAnimation = prefetch.append_child("link"); - prefetchAnimation.append_attribute("rel") = "prefetch"; - prefetchAnimation.append_attribute("href") = animationPath; + pugi::xml_node preloadAnimation = preload.append_child("link"); + preloadAnimation.append_attribute("rel") = "preload"; + preloadAnimation.append_attribute("href") = animationPath; + preloadAnimation.append_attribute("as") = "image"; if (i % settings.CharactersPerRow == 0) { row = tableBody.append_child("tr");