diff --git a/modules/core/inc/tactile/core/map/layer/group_layer.hpp b/modules/core/inc/tactile/core/map/layer/group_layer.hpp new file mode 100644 index 0000000000..7b05bef2a7 --- /dev/null +++ b/modules/core/inc/tactile/core/map/layer/group_layer.hpp @@ -0,0 +1,47 @@ +// Copyright (C) 2023 Albin Johansson (GNU General Public License v3.0) + +#pragma once + +#include "tactile/core/api.hpp" +#include "tactile/core/map/layer/layer.hpp" +#include "tactile/core/map/layer/layer_behavior_delegate.hpp" +#include "tactile/core/prelude.hpp" + +namespace tactile { + +/** + * \brief A layer variant consisting of a collection of other layers. + */ +class TACTILE_CORE_API GroupLayer final : public ILayer { + public: + void accept(ILayerVisitor& visitor) override; + + void set_persistent_id(Maybe id) override; + + void set_opacity(float opacity) override; + + void set_visible(bool visible) override; + + [[nodiscard]] + auto get_persistent_id() const -> Maybe override; + + [[nodiscard]] + auto get_opacity() const -> float override; + + [[nodiscard]] + auto is_visible() const -> bool override; + + [[nodiscard]] + auto clone() const -> Shared override; + + [[nodiscard]] + auto get_meta() -> Metadata& override; + + [[nodiscard]] + auto get_meta() const -> const Metadata& override; + + private: + LayerBehaviorDelegate mDelegate; +}; + +} // namespace tactile diff --git a/modules/core/inc/tactile/core/map/layer/layer_behavior_delegate.hpp b/modules/core/inc/tactile/core/map/layer/layer_behavior_delegate.hpp new file mode 100644 index 0000000000..ed26d00df9 --- /dev/null +++ b/modules/core/inc/tactile/core/map/layer/layer_behavior_delegate.hpp @@ -0,0 +1,45 @@ +// Copyright (C) 2023 Albin Johansson (GNU General Public License v3.0) + +#pragma once + +#include "tactile/core/api.hpp" +#include "tactile/core/context/metadata.hpp" +#include "tactile/core/functional/maybe.hpp" +#include "tactile/core/prelude.hpp" + +namespace tactile { + +/** + * \brief Used by layer types to implement the common subset of the `ILayer` interface. + */ +class TACTILE_CORE_API LayerBehaviorDelegate final { + public: + void set_persistent_id(Maybe id); + + void set_opacity(float opacity); + + void set_visible(bool visible); + + [[nodiscard]] + auto get_persistent_id() const -> Maybe; + + [[nodiscard]] + auto get_opacity() const -> float; + + [[nodiscard]] + auto is_visible() const -> bool; + + [[nodiscard]] + auto get_meta() -> Metadata&; + + [[nodiscard]] + auto get_meta() const -> const Metadata&; + + private: + Metadata mMeta; + Maybe mPersistentId; + float mOpacity {1.0f}; + bool mVisible {true}; +}; + +} // namespace tactile diff --git a/modules/core/inc/tactile/core/map/layer/object_layer.hpp b/modules/core/inc/tactile/core/map/layer/object_layer.hpp new file mode 100644 index 0000000000..6c1dec281a --- /dev/null +++ b/modules/core/inc/tactile/core/map/layer/object_layer.hpp @@ -0,0 +1,47 @@ +// Copyright (C) 2023 Albin Johansson (GNU General Public License v3.0) + +#pragma once + +#include "tactile/core/api.hpp" +#include "tactile/core/map/layer/layer.hpp" +#include "tactile/core/map/layer/layer_behavior_delegate.hpp" +#include "tactile/core/prelude.hpp" + +namespace tactile { + +/** + * \brief A layer variant consisting of an arbitrary collection of objects. + */ +class TACTILE_CORE_API ObjectLayer final : public ILayer { + public: + void accept(ILayerVisitor& visitor) override; + + void set_persistent_id(Maybe id) override; + + void set_opacity(float opacity) override; + + void set_visible(bool visible) override; + + [[nodiscard]] + auto get_persistent_id() const -> Maybe override; + + [[nodiscard]] + auto get_opacity() const -> float override; + + [[nodiscard]] + auto is_visible() const -> bool override; + + [[nodiscard]] + auto clone() const -> Shared override; + + [[nodiscard]] + auto get_meta() -> Metadata& override; + + [[nodiscard]] + auto get_meta() const -> const Metadata& override; + + private: + LayerBehaviorDelegate mDelegate; +}; + +} // namespace tactile diff --git a/modules/core/inc/tactile/core/map/layer/tile_layer.hpp b/modules/core/inc/tactile/core/map/layer/tile_layer.hpp new file mode 100644 index 0000000000..a7cc99c5b2 --- /dev/null +++ b/modules/core/inc/tactile/core/map/layer/tile_layer.hpp @@ -0,0 +1,47 @@ +// Copyright (C) 2023 Albin Johansson (GNU General Public License v3.0) + +#pragma once + +#include "tactile/core/api.hpp" +#include "tactile/core/map/layer/layer.hpp" +#include "tactile/core/map/layer/layer_behavior_delegate.hpp" +#include "tactile/core/prelude.hpp" + +namespace tactile { + +/** + * \brief A layer variant consisting of a two-dimensional grid of tile identifiers. + */ +class TACTILE_CORE_API TileLayer final : public ILayer { + public: + void accept(ILayerVisitor& visitor) override; + + void set_persistent_id(Maybe id) override; + + void set_opacity(float opacity) override; + + void set_visible(bool visible) override; + + [[nodiscard]] + auto get_persistent_id() const -> Maybe override; + + [[nodiscard]] + auto get_opacity() const -> float override; + + [[nodiscard]] + auto is_visible() const -> bool override; + + [[nodiscard]] + auto clone() const -> Shared override; + + [[nodiscard]] + auto get_meta() -> Metadata& override; + + [[nodiscard]] + auto get_meta() const -> const Metadata& override; + + private: + LayerBehaviorDelegate mDelegate; +}; + +} // namespace tactile diff --git a/modules/core/src/tactile/core/map/layer/group_layer.cpp b/modules/core/src/tactile/core/map/layer/group_layer.cpp new file mode 100644 index 0000000000..a612e11c29 --- /dev/null +++ b/modules/core/src/tactile/core/map/layer/group_layer.cpp @@ -0,0 +1,63 @@ +// Copyright (C) 2023 Albin Johansson (GNU General Public License v3.0) + +#include "tactile/core/map/layer/group_layer.hpp" + +#include "tactile/core/map/layer/layer_visitor.hpp" + +namespace tactile { + +void GroupLayer::accept(ILayerVisitor& visitor) +{ + visitor.visit(*this); +} + +void GroupLayer::set_persistent_id(const Maybe id) +{ + mDelegate.set_persistent_id(id); +} + +void GroupLayer::set_opacity(const float opacity) +{ + mDelegate.set_opacity(opacity); +} + +void GroupLayer::set_visible(const bool visible) +{ + mDelegate.set_visible(visible); +} + +auto GroupLayer::get_persistent_id() const -> Maybe +{ + return mDelegate.get_persistent_id(); +} + +auto GroupLayer::get_opacity() const -> float +{ + return mDelegate.get_opacity(); +} + +auto GroupLayer::is_visible() const -> bool +{ + return mDelegate.is_visible(); +} + +auto GroupLayer::clone() const -> Shared +{ + auto clone = make_shared(*this); + + clone->set_persistent_id(kNone); + + return clone; +} + +auto GroupLayer::get_meta() -> Metadata& +{ + return mDelegate.get_meta(); +} + +auto GroupLayer::get_meta() const -> const Metadata& +{ + return mDelegate.get_meta(); +} + +} // namespace tactile diff --git a/modules/core/src/tactile/core/map/layer/layer_behavior_delegate.cpp b/modules/core/src/tactile/core/map/layer/layer_behavior_delegate.cpp new file mode 100644 index 0000000000..48702e245d --- /dev/null +++ b/modules/core/src/tactile/core/map/layer/layer_behavior_delegate.cpp @@ -0,0 +1,49 @@ +// Copyright (C) 2023 Albin Johansson (GNU General Public License v3.0) + +#include "tactile/core/map/layer/layer_behavior_delegate.hpp" + +#include // clamp + +namespace tactile { + +void LayerBehaviorDelegate::set_persistent_id(const Maybe id) +{ + mPersistentId = id; +} + +void LayerBehaviorDelegate::set_opacity(const float opacity) +{ + mOpacity = std::clamp(opacity, 0.0f, 1.0f); +} + +void LayerBehaviorDelegate::set_visible(const bool visible) +{ + mVisible = visible; +} + +auto LayerBehaviorDelegate::get_persistent_id() const -> Maybe +{ + return mPersistentId; +} + +auto LayerBehaviorDelegate::get_opacity() const -> float +{ + return mOpacity; +} + +auto LayerBehaviorDelegate::is_visible() const -> bool +{ + return mVisible; +} + +auto LayerBehaviorDelegate::get_meta() -> Metadata& +{ + return mMeta; +} + +auto LayerBehaviorDelegate::get_meta() const -> const Metadata& +{ + return mMeta; +} + +} // namespace tactile diff --git a/modules/core/src/tactile/core/map/layer/object_layer.cpp b/modules/core/src/tactile/core/map/layer/object_layer.cpp new file mode 100644 index 0000000000..c8ffa09e03 --- /dev/null +++ b/modules/core/src/tactile/core/map/layer/object_layer.cpp @@ -0,0 +1,63 @@ +// Copyright (C) 2023 Albin Johansson (GNU General Public License v3.0) + +#include "tactile/core/map/layer/object_layer.hpp" + +#include "tactile/core/map/layer/layer_visitor.hpp" + +namespace tactile { + +void ObjectLayer::accept(ILayerVisitor& visitor) +{ + visitor.visit(*this); +} + +void ObjectLayer::set_persistent_id(const Maybe id) +{ + mDelegate.set_persistent_id(id); +} + +void ObjectLayer::set_opacity(const float opacity) +{ + mDelegate.set_opacity(opacity); +} + +void ObjectLayer::set_visible(const bool visible) +{ + mDelegate.set_visible(visible); +} + +auto ObjectLayer::get_persistent_id() const -> Maybe +{ + return mDelegate.get_persistent_id(); +} + +auto ObjectLayer::get_opacity() const -> float +{ + return mDelegate.get_opacity(); +} + +auto ObjectLayer::is_visible() const -> bool +{ + return mDelegate.is_visible(); +} + +auto ObjectLayer::clone() const -> Shared +{ + auto clone = make_shared(*this); + + clone->set_persistent_id(kNone); + + return clone; +} + +auto ObjectLayer::get_meta() -> Metadata& +{ + return mDelegate.get_meta(); +} + +auto ObjectLayer::get_meta() const -> const Metadata& +{ + return mDelegate.get_meta(); +} + +} // namespace tactile diff --git a/modules/core/src/tactile/core/map/layer/tile_layer.cpp b/modules/core/src/tactile/core/map/layer/tile_layer.cpp new file mode 100644 index 0000000000..3e2213dee0 --- /dev/null +++ b/modules/core/src/tactile/core/map/layer/tile_layer.cpp @@ -0,0 +1,63 @@ +// Copyright (C) 2023 Albin Johansson (GNU General Public License v3.0) + +#include "tactile/core/map/layer/tile_layer.hpp" + +#include "tactile/core/map/layer/layer_visitor.hpp" + +namespace tactile { + +void TileLayer::accept(ILayerVisitor& visitor) +{ + visitor.visit(*this); +} + +void TileLayer::set_persistent_id(const Maybe id) +{ + mDelegate.set_persistent_id(id); +} + +void TileLayer::set_opacity(const float opacity) +{ + mDelegate.set_opacity(opacity); +} + +void TileLayer::set_visible(const bool visible) +{ + mDelegate.set_visible(visible); +} + +auto TileLayer::get_persistent_id() const -> Maybe +{ + return mDelegate.get_persistent_id(); +} + +auto TileLayer::get_opacity() const -> float +{ + return mDelegate.get_opacity(); +} + +auto TileLayer::is_visible() const -> bool +{ + return mDelegate.is_visible(); +} + +auto TileLayer::clone() const -> Shared +{ + auto clone = make_shared(*this); + + clone->set_persistent_id(kNone); + + return clone; +} + +auto TileLayer::get_meta() -> Metadata& +{ + return mDelegate.get_meta(); +} + +auto TileLayer::get_meta() const -> const Metadata& +{ + return mDelegate.get_meta(); +} + +} // namespace tactile diff --git a/modules/core/test/context/meta_context_test.cpp b/modules/core/test/context/meta_context_test.cpp new file mode 100644 index 0000000000..d7816ff602 --- /dev/null +++ b/modules/core/test/context/meta_context_test.cpp @@ -0,0 +1,28 @@ +// Copyright (C) 2023 Albin Johansson (GNU General Public License v3.0) + +#include + +#include "tactile/core/map/layer/group_layer.hpp" +#include "tactile/core/map/layer/object_layer.hpp" +#include "tactile/core/map/layer/tile_layer.hpp" + +using namespace tactile; + +template +class IMetaContextTest : public testing::Test {}; + +using MetaContextTypes = testing::Types; + +TYPED_TEST_SUITE(IMetaContextTest, MetaContextTypes); + +/// \tests tactile::IMetaContext::get_meta +TYPED_TEST(IMetaContextTest, GetMeta) +{ + TypeParam context {}; + const auto& const_context = context; + + context.get_meta().set_name("foo"); + + EXPECT_EQ(context.get_meta().get_name(), "foo"); + EXPECT_EQ(const_context.get_meta().get_name(), "foo"); +} diff --git a/modules/core/test/map/layer/layer_test.cpp b/modules/core/test/map/layer/layer_test.cpp new file mode 100644 index 0000000000..03c99975b0 --- /dev/null +++ b/modules/core/test/map/layer/layer_test.cpp @@ -0,0 +1,84 @@ +// Copyright (C) 2023 Albin Johansson (GNU General Public License v3.0) + +#include + +#include "tactile/core/map/layer/group_layer.hpp" +#include "tactile/core/map/layer/object_layer.hpp" +#include "tactile/core/map/layer/tile_layer.hpp" + +using namespace tactile; + +using LayerTypes = testing::Types; + +template +class ILayerTest : public testing::Test {}; + +TYPED_TEST_SUITE(ILayerTest, LayerTypes); + +/// \tests tactile::ILayer::set_persistent_id +/// \tests tactile::ILayer::get_persistent_id +TYPED_TEST(ILayerTest, PersistentId) +{ + TypeParam layer {}; + EXPECT_FALSE(layer.get_persistent_id().has_value()); + + layer.set_persistent_id(42); + EXPECT_EQ(layer.get_persistent_id(), 42); + + layer.set_persistent_id(kNone); + EXPECT_FALSE(layer.get_persistent_id().has_value()); +} + +/// \tests tactile::ILayer::set_opacity +/// \tests tactile::ILayer::get_opacity +TYPED_TEST(ILayerTest, Opacity) +{ + TypeParam layer {}; + EXPECT_EQ(layer.get_opacity(), 1.0f); + + layer.set_opacity(0.5f); + EXPECT_EQ(layer.get_opacity(), 0.5f); + + layer.set_opacity(-0.1f); + EXPECT_EQ(layer.get_opacity(), 0.0f); + + layer.set_opacity(1.1f); + EXPECT_EQ(layer.get_opacity(), 1.0f); +} + +/// \tests tactile::ILayer::set_visible +/// \tests tactile::ILayer::is_visible +TYPED_TEST(ILayerTest, Visibility) +{ + TypeParam layer {}; + EXPECT_TRUE(layer.is_visible()); + + layer.set_visible(false); + EXPECT_FALSE(layer.is_visible()); + + layer.set_visible(true); + EXPECT_TRUE(layer.is_visible()); +} + +/// \tests tactile::ILayer::clone +TYPED_TEST(ILayerTest, Clone) +{ + const int32 id {827}; + const float opacity {0.8f}; + const bool visible {false}; + + TypeParam source_layer {}; + source_layer.set_persistent_id(id); + source_layer.set_opacity(opacity); + source_layer.set_visible(visible); + + const auto layer_clone = source_layer.clone(); + + EXPECT_EQ(source_layer.get_persistent_id(), id); + EXPECT_EQ(source_layer.get_opacity(), opacity); + EXPECT_EQ(source_layer.is_visible(), visible); + + EXPECT_FALSE(layer_clone->get_persistent_id().has_value()); + EXPECT_EQ(layer_clone->get_opacity(), source_layer.get_opacity()); + EXPECT_EQ(layer_clone->is_visible(), source_layer.is_visible()); +}