From 7d0decccaac3aa564b549d91a36279e7aca0814e Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 24 Feb 2023 19:30:30 +0000 Subject: Support for recursive colouring of asset factory faces Updates colours in sample model. --- assetFactory/style.cpp | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 assetFactory/style.cpp (limited to 'assetFactory/style.cpp') diff --git a/assetFactory/style.cpp b/assetFactory/style.cpp new file mode 100644 index 0000000..099d81a --- /dev/null +++ b/assetFactory/style.cpp @@ -0,0 +1,59 @@ +#include "style.h" + +ModelFactoryMesh::Color +Style::parseColour(const std::string_view & in) +{ + if (in.empty()) { + return {}; + } + if (in[0] == '#') { + ModelFactoryMesh::Color out {0, 0, 0, 1}; + std::generate_n(out.begin(), (in.length() - 1) / 2, [in = in.data() + 1]() mutable { + uint8_t channel; + std::from_chars(in, in + 2, channel, 16); + in += 2; + return static_cast(channel) / 256.F; + }); + return out; + } + return {}; +} + +void +Style::applyStyle(ModelFactoryMesh & mesh, const StyleStack & parents, const Shape::CreatedFaces & faces) const +{ + if (const auto effectiveColour = getProperty(parents, &Style::colour); !effectiveColour.empty()) { + const auto parsedColour = parseColour(effectiveColour); + for (const auto & face : faces) { + mesh.set_color(face.second, parsedColour); + } + } +} + +void +Style::applyStyle(ModelFactoryMesh & mesh, const StyleStack & parents, const ModelFactoryMesh::FaceHandle & face) const +{ + if (const auto effectiveColour = getProperty(parents, &Style::colour); !effectiveColour.empty()) { + const auto parsedColour = parseColour(effectiveColour); + mesh.set_color(face, parsedColour); + } +} + +std::string_view +Style::getProperty(const StyleStack & parents, std::string Style::*member) +{ + if (const auto itr = std::find_if(parents.rbegin(), parents.rend(), + [&member](auto && s) { + return !(s->*member).empty(); + }); + itr != parents.rend()) { + return (*itr)->*member; + } + return {}; +} + +bool +Style::persist(Persistence::PersistenceStore & store) +{ + return STORE_MEMBER(colour); +} -- cgit v1.2.3 From 5c703a1549b88339ca48ac3c48f67ab7503d223a Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 27 Feb 2023 23:56:03 +0000 Subject: Support for named colours in assets Fixes up some error handling in colour parser. --- assetFactory/style.cpp | 14 ++++++++++++-- res/brush47.xml | 6 +++--- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'assetFactory/style.cpp') diff --git a/assetFactory/style.cpp b/assetFactory/style.cpp index 099d81a..d2977a7 100644 --- a/assetFactory/style.cpp +++ b/assetFactory/style.cpp @@ -1,12 +1,16 @@ #include "style.h" +#include "assetFactory.h" ModelFactoryMesh::Color Style::parseColour(const std::string_view & in) { if (in.empty()) { - return {}; + throw std::runtime_error("Empty colour specification"); } if (in[0] == '#') { + if (in.length() > 9 || in.length() % 2 == 0) { + throw std::runtime_error("Invalid hex colour specification"); + } ModelFactoryMesh::Color out {0, 0, 0, 1}; std::generate_n(out.begin(), (in.length() - 1) / 2, [in = in.data() + 1]() mutable { uint8_t channel; @@ -16,7 +20,13 @@ Style::parseColour(const std::string_view & in) }); return out; } - return {}; + if (auto mf = std::dynamic_pointer_cast(Persistence::sharedObjects.at("assetFactory"))) { + if (const auto colour = mf->colours.find(in); colour != mf->colours.end()) { + const auto out = glm::vec3 {colour->second} / 256.F; + return {out.r, out.g, out.b, 1.f}; + } + } + throw std::runtime_error("No such asset factory colour"); } void diff --git a/res/brush47.xml b/res/brush47.xml index 1ece78f..477610c 100644 --- a/res/brush47.xml +++ b/res/brush47.xml @@ -16,15 +16,15 @@ - - + + - + -- cgit v1.2.3 From 98df33e0a52e086b68df2a62ce2f41cc6b67db63 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 2 Mar 2023 18:18:38 +0000 Subject: Parse colour values as they're read --- assetFactory/assetFactory.cpp | 32 ++++++++++++++++-- assetFactory/assetFactory.h | 4 ++- assetFactory/modelFactoryMesh.h | 3 +- assetFactory/style.cpp | 72 ++++++++++++++--------------------------- assetFactory/style.h | 18 +++++++++-- assetFactory/use.cpp | 3 +- test/test-assetFactory.cpp | 14 ++++---- 7 files changed, 83 insertions(+), 63 deletions(-) (limited to 'assetFactory/style.cpp') diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp index 016f29e..70f5337 100644 --- a/assetFactory/assetFactory.cpp +++ b/assetFactory/assetFactory.cpp @@ -30,12 +30,12 @@ AssetFactory::parseX11RGB(const char * path) { filesystem::FileStar rgb {path, "r"}; Colours out; - glm::u8vec3 colour; + Colour colour; char inname[BUFSIZ]; - while (fscanf(rgb, "%hhu %hhu %hhu %[^\n\r]s", &colour.r, &colour.g, &colour.b, inname) == 4) { + while (fscanf(rgb, "%f %f %f %[^\n\r]s", &colour.r, &colour.g, &colour.b, inname) == 4) { std::string name {inname}; normalizeColourName(name); - out.emplace(std::move(name), colour); + out.emplace(std::move(name), colour / 255.f); } return out; } @@ -49,6 +49,32 @@ AssetFactory::normalizeColourName(std::string & name) }; } +AssetFactory::ColourAlpha +AssetFactory::parseColour(std::string_view in) const +{ + if (in.empty()) { + throw std::runtime_error("Empty colour specification"); + } + if (in[0] == '#') { + if (in.length() > 9 || in.length() % 2 == 0) { + throw std::runtime_error("Invalid hex colour specification"); + } + ColourAlpha out {0, 0, 0, 1}; + std::generate_n(&out.r, (in.length() - 1) / 2, [in = in.data() + 1]() mutable { + uint8_t channel; + std::from_chars(in, in + 2, channel, 16); + in += 2; + return static_cast(channel) / 255.f; + }); + return out; + } + if (auto mf = std::dynamic_pointer_cast(Persistence::sharedObjects.at("assetFactory"))) { + if (const auto colour = mf->colours.find(in); colour != mf->colours.end()) { + return {colour->second, 1}; + } + } + throw std::runtime_error("No such asset factory colour"); +} bool AssetFactory::persist(Persistence::PersistenceStore & store) { diff --git a/assetFactory/assetFactory.h b/assetFactory/assetFactory.h index 42082eb..2bd576b 100644 --- a/assetFactory/assetFactory.h +++ b/assetFactory/assetFactory.h @@ -22,11 +22,13 @@ class AssetFactory : public Persistence::Persistable { public: using Shapes = std::map>; using Assets = std::map>; - using Colour = glm::u8vec3; + using Colour = glm::vec3; + using ColourAlpha = glm::vec4; using Colours = std::map>; AssetFactory(); [[nodiscard]] static std::shared_ptr loadXML(const std::filesystem::path &); + [[nodiscard]] ColourAlpha parseColour(std::string_view) const; Shapes shapes; Assets assets; diff --git a/assetFactory/modelFactoryMesh.h b/assetFactory/modelFactoryMesh.h index b4f5254..258913b 100644 --- a/assetFactory/modelFactoryMesh.h +++ b/assetFactory/modelFactoryMesh.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace OpenMesh { template struct glmvec : public VectorT { @@ -25,7 +26,7 @@ struct ModelFactoryTraits : public OpenMesh::DefaultTraits { VertexAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status); using Point = OpenMesh::glmvec; using Normal = OpenMesh::glmvec; - using Color = OpenMesh::glmvec; + using Color = glm::vec4; }; struct ModelFactoryMesh : public OpenMesh::PolyMesh_ArrayKernelT { diff --git a/assetFactory/style.cpp b/assetFactory/style.cpp index d2977a7..b2a2cf7 100644 --- a/assetFactory/style.cpp +++ b/assetFactory/style.cpp @@ -1,41 +1,16 @@ #include "style.h" #include "assetFactory.h" -ModelFactoryMesh::Color -Style::parseColour(const std::string_view & in) -{ - if (in.empty()) { - throw std::runtime_error("Empty colour specification"); - } - if (in[0] == '#') { - if (in.length() > 9 || in.length() % 2 == 0) { - throw std::runtime_error("Invalid hex colour specification"); - } - ModelFactoryMesh::Color out {0, 0, 0, 1}; - std::generate_n(out.begin(), (in.length() - 1) / 2, [in = in.data() + 1]() mutable { - uint8_t channel; - std::from_chars(in, in + 2, channel, 16); - in += 2; - return static_cast(channel) / 256.F; - }); - return out; - } - if (auto mf = std::dynamic_pointer_cast(Persistence::sharedObjects.at("assetFactory"))) { - if (const auto colour = mf->colours.find(in); colour != mf->colours.end()) { - const auto out = glm::vec3 {colour->second} / 256.F; - return {out.r, out.g, out.b, 1.f}; - } - } - throw std::runtime_error("No such asset factory colour"); -} - void Style::applyStyle(ModelFactoryMesh & mesh, const StyleStack & parents, const Shape::CreatedFaces & faces) const { - if (const auto effectiveColour = getProperty(parents, &Style::colour); !effectiveColour.empty()) { - const auto parsedColour = parseColour(effectiveColour); + if (const auto effectiveColour = getProperty(parents, &Style::colour, + [](auto && style) { + return style->colour.a > 0; + }); + effectiveColour.has_value()) { for (const auto & face : faces) { - mesh.set_color(face.second, parsedColour); + mesh.set_color(face.second, effectiveColour->get()); } } } @@ -43,27 +18,30 @@ Style::applyStyle(ModelFactoryMesh & mesh, const StyleStack & parents, const Sha void Style::applyStyle(ModelFactoryMesh & mesh, const StyleStack & parents, const ModelFactoryMesh::FaceHandle & face) const { - if (const auto effectiveColour = getProperty(parents, &Style::colour); !effectiveColour.empty()) { - const auto parsedColour = parseColour(effectiveColour); - mesh.set_color(face, parsedColour); - } -} - -std::string_view -Style::getProperty(const StyleStack & parents, std::string Style::*member) -{ - if (const auto itr = std::find_if(parents.rbegin(), parents.rend(), - [&member](auto && s) { - return !(s->*member).empty(); + if (const auto effectiveColour = getProperty(parents, &Style::colour, + [](auto && style) { + return style->colour.a > 0; }); - itr != parents.rend()) { - return (*itr)->*member; + effectiveColour.has_value()) { + mesh.set_color(face, effectiveColour->get()); } - return {}; } bool Style::persist(Persistence::PersistenceStore & store) { - return STORE_MEMBER(colour); + struct ColourParser : public Persistence::SelectionV { + using Persistence::SelectionV::SelectionV; + using Persistence::SelectionV::setValue; + void + setValue(std::string && str) override + { + if (auto mf + = std::dynamic_pointer_cast(Persistence::sharedObjects.at("assetFactory"))) { + v = mf->parseColour(str); + } + } + }; + + return STORE_HELPER(colour, ColourParser); } diff --git a/assetFactory/style.h b/assetFactory/style.h index 0c7ad5a..e8fd012 100644 --- a/assetFactory/style.h +++ b/assetFactory/style.h @@ -3,19 +3,31 @@ #include "modelFactoryMesh.h" #include "persistence.h" #include "shape.h" +#include #include +#include class Style { public: using StyleStack = std::vector; + using Colour = glm::vec3; + using ColourAlpha = glm::vec4; - static ModelFactoryMesh::Color parseColour(const std::string_view &); void applyStyle(ModelFactoryMesh &, const StyleStack & parents, const Shape::CreatedFaces &) const; void applyStyle(ModelFactoryMesh &, const StyleStack & parents, const ModelFactoryMesh::FaceHandle &) const; - static std::string_view getProperty(const StyleStack & parents, std::string Style::*member); + template + static std::optional> + getProperty(const StyleStack & parents, T Style::*member, auto && test) + { + if (const auto itr = std::find_if(parents.rbegin(), parents.rend(), std::forward(test)); + itr != parents.rend()) { + return (*itr)->*member; + } + return {}; + } - std::string colour; + ColourAlpha colour {}; protected: bool persist(Persistence::PersistenceStore & store); diff --git a/assetFactory/use.cpp b/assetFactory/use.cpp index a6fac5c..53fc9b9 100644 --- a/assetFactory/use.cpp +++ b/assetFactory/use.cpp @@ -18,7 +18,8 @@ struct Lookup : public Persistence::SelectionV { void setValue(std::string && str) override { - if (auto mf = std::dynamic_pointer_cast(Persistence::sharedObjects.at("assetFactory"))) { + if (auto mf = std::dynamic_pointer_cast( + Persistence::sharedObjects.at("assetFactory"))) { v = mf->shapes.at(str); } } diff --git a/test/test-assetFactory.cpp b/test/test-assetFactory.cpp index 89f6bf0..8feb831 100644 --- a/test/test-assetFactory.cpp +++ b/test/test-assetFactory.cpp @@ -76,7 +76,7 @@ BOOST_AUTO_TEST_CASE(brush47) wheelCylinder->position = {0, 0, 0.571}; wheelCylinder->scale = {1.142, 1.142, 0.07}; wheelCylinder->rotation = {0, 0, half_pi}; - wheelCylinder->colour = "#2C3539"; + wheelCylinder->colour = assetFactory.parseColour("#2C3539"); } assetFactory.shapes.emplace(wheel->id, wheel); } @@ -120,10 +120,10 @@ BOOST_AUTO_TEST_CASE(brush47) bodyLower->type = assetFactory.shapes.at("cuboid"); bodyLower->position = {0, 0, 1.2}; bodyLower->scale = {2.69, 19.38, 1.5}; - bodyLower->colour = "#1111DD"; + bodyLower->colour = assetFactory.parseColour("#1111DD"); auto & bottom = bodyLower->faceControllers["bottom"]; bottom = std::make_unique(); - bottom->colour = "#2C3539"; + bottom->colour = assetFactory.parseColour("#2C3539"); auto & bodyUpper = bodyLower->faceControllers["top"]; bodyUpper = std::make_unique(); bodyUpper->type = "extrude"; @@ -140,7 +140,7 @@ BOOST_AUTO_TEST_CASE(brush47) batteryBox->type = assetFactory.shapes.at("cuboid"); batteryBox->position = {0, 0, .2}; batteryBox->scale = {2.6, 4.5, 1}; - batteryBox->colour = "#2C3539"; + batteryBox->colour = assetFactory.parseColour("#2C3539"); } } std::transform(factoryMeshes.begin(), factoryMeshes.end(), std::back_inserter(meshes.objects), @@ -206,7 +206,7 @@ BOOST_AUTO_TEST_CASE(parseX11RGB) { const auto parsedColours = AssetFactory::parseX11RGB(FIXTURESDIR "rgb.txt"); BOOST_REQUIRE_EQUAL(parsedColours.size(), 20); - BOOST_CHECK_EQUAL(parsedColours.at("cyan"), AssetFactory::Colour(0, 255, 255)); - BOOST_CHECK_EQUAL(parsedColours.at("slategrey"), AssetFactory::Colour(112, 128, 144)); - BOOST_CHECK_EQUAL(parsedColours.at("lightsteelblue1"), AssetFactory::Colour(202, 225, 255)); + BOOST_CHECK_CLOSE_VEC(parsedColours.at("cyan"), AssetFactory::Colour(0, 1, 1)); + BOOST_CHECK_CLOSE_VEC(parsedColours.at("slategrey"), AssetFactory::Colour(0.44F, 0.5, 0.56F)); + BOOST_CHECK_CLOSE_VEC(parsedColours.at("lightsteelblue1"), AssetFactory::Colour(0.79, 0.88, 1)); } -- cgit v1.2.3 From 17ac090dd1dd245cf1e24b62b7333ba9be571bde Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 2 Mar 2023 18:36:34 +0000 Subject: Add ParseBase Acts as a base class for persistence parser, encompasses the parse stack and manages shared objects --- assetFactory/assetFactory.cpp | 2 +- assetFactory/style.cpp | 3 +-- assetFactory/use.cpp | 3 +-- lib/jsonParse-persistence.h | 4 +--- lib/persistence.cpp | 5 +++++ lib/persistence.h | 35 +++++++++++++++++++++++++++++++---- lib/saxParse-persistence.h | 5 +---- test/test-persistence.cpp | 10 ++++------ 8 files changed, 45 insertions(+), 22 deletions(-) (limited to 'assetFactory/style.cpp') diff --git a/assetFactory/assetFactory.cpp b/assetFactory/assetFactory.cpp index 70f5337..b9724c9 100644 --- a/assetFactory/assetFactory.cpp +++ b/assetFactory/assetFactory.cpp @@ -68,7 +68,7 @@ AssetFactory::parseColour(std::string_view in) const }); return out; } - if (auto mf = std::dynamic_pointer_cast(Persistence::sharedObjects.at("assetFactory"))) { + if (auto mf = Persistence::ParseBase::getShared("assetFactory")) { if (const auto colour = mf->colours.find(in); colour != mf->colours.end()) { return {colour->second, 1}; } diff --git a/assetFactory/style.cpp b/assetFactory/style.cpp index b2a2cf7..fc5c34e 100644 --- a/assetFactory/style.cpp +++ b/assetFactory/style.cpp @@ -36,8 +36,7 @@ Style::persist(Persistence::PersistenceStore & store) void setValue(std::string && str) override { - if (auto mf - = std::dynamic_pointer_cast(Persistence::sharedObjects.at("assetFactory"))) { + if (auto mf = Persistence::ParseBase::getShared("assetFactory")) { v = mf->parseColour(str); } } diff --git a/assetFactory/use.cpp b/assetFactory/use.cpp index 53fc9b9..708e310 100644 --- a/assetFactory/use.cpp +++ b/assetFactory/use.cpp @@ -18,8 +18,7 @@ struct Lookup : public Persistence::SelectionV { void setValue(std::string && str) override { - if (auto mf = std::dynamic_pointer_cast( - Persistence::sharedObjects.at("assetFactory"))) { + if (auto mf = Persistence::ParseBase::getShared("assetFactory")) { v = mf->shapes.at(str); } } diff --git a/lib/jsonParse-persistence.h b/lib/jsonParse-persistence.h index fa5e772..a676282 100644 --- a/lib/jsonParse-persistence.h +++ b/lib/jsonParse-persistence.h @@ -9,7 +9,7 @@ #include namespace Persistence { - class JsonParsePersistence : public json::jsonParser { + class JsonParsePersistence : public json::jsonParser, ParseBase { public: template inline T @@ -34,8 +34,6 @@ namespace Persistence { void endArray() override; void endObject() override; - Stack stk; - template inline void pushValue(T && value); inline SelectionPtr & current(); }; diff --git a/lib/persistence.cpp b/lib/persistence.cpp index 543f2fd..8c7c6a4 100644 --- a/lib/persistence.cpp +++ b/lib/persistence.cpp @@ -157,4 +157,9 @@ namespace Persistence { throw std::logic_error("Default write op shouldn't ever get called"); } /// LCOV_EXCL_STOP + + ParseBase::ParseBase() : sharedObjectsInstance {std::make_shared()} + { + sharedObjects = sharedObjectsInstance; + } } diff --git a/lib/persistence.h b/lib/persistence.h index 458ba4d..05cb49b 100644 --- a/lib/persistence.h +++ b/lib/persistence.h @@ -375,9 +375,36 @@ namespace Persistence { return true; } + class ParseBase { + public: + using SharedObjects = std::map>; + using SharedObjectsWPtr = std::weak_ptr; + using SharedObjectsPtr = std::shared_ptr; + + ParseBase(); + DEFAULT_MOVE_NO_COPY(ParseBase); + + template + static auto + getShared(auto && k) + { + return std::dynamic_pointer_cast(Persistence::ParseBase::sharedObjects.lock()->at(k)); + } + template + static auto + emplaceShared(T &&... v) + { + return sharedObjects.lock()->emplace(std::forward(v)...); + } + + protected: + Stack stk; + + private: + inline static thread_local SharedObjectsWPtr sharedObjects; + SharedObjectsPtr sharedObjectsInstance; + }; // TODO Move these - using SharedObjects = std::map>; - inline SharedObjects sharedObjects; using SeenSharedObjects = std::map; inline SeenSharedObjects seenSharedObjects; @@ -417,7 +444,7 @@ namespace Persistence { void setValue(std::string && id) override { - sharedObjects.emplace(id, this->v); + ParseBase::emplaceShared(id, this->v); } }; @@ -538,7 +565,7 @@ namespace Persistence { void setValue(std::string && id) override { - if (auto teo = std::dynamic_pointer_cast(sharedObjects.at(id))) { + if (auto teo = ParseBase::getShared(id)) { this->v = std::move(teo); } else { diff --git a/lib/saxParse-persistence.h b/lib/saxParse-persistence.h index 91daecc..6043b25 100644 --- a/lib/saxParse-persistence.h +++ b/lib/saxParse-persistence.h @@ -6,7 +6,7 @@ #include namespace Persistence { - class SAXParsePersistence : public SAXParse { + class SAXParsePersistence : public SAXParse, ParseBase { private: template struct Root : public Persistable { T t {}; @@ -36,8 +36,5 @@ namespace Persistence { void data(mxml_node_t *) override; void directive(mxml_node_t *) override; void cdata(mxml_node_t *) override; - - private: - Stack stk; }; } diff --git a/test/test-persistence.cpp b/test/test-persistence.cpp index 6bee010..7bca91a 100644 --- a/test/test-persistence.cpp +++ b/test/test-persistence.cpp @@ -83,16 +83,14 @@ struct TestObject : public Persistence::Persistable { } }; -struct JPP : public Persistence::JsonParsePersistence { +struct JPP { template T load_json(const std::filesystem::path & path) { BOOST_TEST_CONTEXT(path) { std::ifstream ss {path}; - auto to = loadState(ss); - Persistence::sharedObjects.clear(); - BOOST_CHECK(stk.empty()); + auto to = Persistence::JsonParsePersistence {}.loadState(ss); BOOST_REQUIRE(to); return to; } @@ -289,10 +287,10 @@ auto const TEST_STRINGS_DECODE_ONLY = boost::unit_test::data::make({ {R"J("\u056b ARMENIAN SMALL LETTER INI")J", "ի ARMENIAN SMALL LETTER INI"}, {R"J("\u0833 SAMARITAN PUNCTUATION BAU")J", "࠳ SAMARITAN PUNCTUATION BAU"}, }); -BOOST_DATA_TEST_CASE_F(JPP, load_strings, TEST_STRINGS + TEST_STRINGS_DECODE_ONLY, in, exp) +BOOST_DATA_TEST_CASE(load_strings, TEST_STRINGS + TEST_STRINGS_DECODE_ONLY, in, exp) { std::stringstream str {in}; - BOOST_CHECK_EQUAL(loadState(str), exp); + BOOST_CHECK_EQUAL(Persistence::JsonParsePersistence {}.loadState(str), exp); } using cpstr = std::tuple; -- cgit v1.2.3