diff --git a/modules/core/inc/tactile/core/tileset/attached_tileset.hpp b/modules/core/inc/tactile/core/tileset/attached_tileset.hpp new file mode 100644 index 0000000000..f6390fb7fc --- /dev/null +++ b/modules/core/inc/tactile/core/tileset/attached_tileset.hpp @@ -0,0 +1,89 @@ +// Copyright (C) 2023 Albin Johansson (GNU General Public License v3.0) + +#pragma once + +#include "tactile/core/api.hpp" +#include "tactile/core/container/smart_ptr.hpp" +#include "tactile/core/functional/maybe.hpp" +#include "tactile/core/misc/id_types.hpp" +#include "tactile/core/prelude.hpp" +#include "tactile/core/tileset/tileset.hpp" + +namespace tactile { + +/** + * \brief Represents a tileset that is attached to a map. + * + * \see `Tileset` + */ +class TACTILE_CORE_API AttachedTileset final { + public: + /** + * \brief Creates an attached tileset. + * + * \param tileset the associated tileset. + * \param first_tile_id the global tile identifier to associate with the first tile. + */ + AttachedTileset(Shared tileset, TileID first_tile_id); + + /** + * \brief Converts a tile identifier to a tile index. + * + * \pre The tile identifier must be associated with the attached tileset. + * + * \param tile_id the tile identifier. + * + * \return a tile index. + * + * \see `has_tile()` + */ + [[nodiscard]] + auto to_index(TileID tile_id) const -> TileIndex; + + /** + * \brief Indicates whether a tile identifier is contained in the tileset. + * + * \param tile_id the tile identifier. + * + * \return true if the identifier is valid; false otherwise. + */ + [[nodiscard]] + auto has_tile(TileID tile_id) const -> bool; + + /** + * \brief Returns the first (lowest) associated tile identifier. + * + * \return a tile identifier. + */ + [[nodiscard]] + auto get_first_tile_id() const -> TileID; + + /** + * \brief Returns the last (greatest) associated tile identifier. + * + * \return a tile identifier. + */ + [[nodiscard]] + auto get_last_tile_id() const -> TileID; + + /** + * \brief Returns the associated tileset. + * + * \return a tileset reference. + */ + [[nodiscard]] + auto get_tileset() -> Tileset&; + + /** + * \copydoc get_tileset() + */ + [[nodiscard]] + auto get_tileset() const -> const Tileset&; + + private: + Shared mTileset; + TileID mFirstTileId; + TileID mLastTileId; +}; + +} // namespace tactile diff --git a/modules/core/src/tactile/core/tileset/attached_tileset.cpp b/modules/core/src/tactile/core/tileset/attached_tileset.cpp new file mode 100644 index 0000000000..358fd2772a --- /dev/null +++ b/modules/core/src/tactile/core/tileset/attached_tileset.cpp @@ -0,0 +1,49 @@ +// Copyright (C) 2023 Albin Johansson (GNU General Public License v3.0) + +#include "tactile/core/tileset/attached_tileset.hpp" + +#include // move + +#include "tactile/core/debug/assert.hpp" +#include "tactile/core/debug/error.hpp" + +namespace tactile { + +AttachedTileset::AttachedTileset(Shared tileset, const TileID first_tile_id) + : mTileset {tileset ? std::move(tileset) : throw RuntimeError {"Null tileset"}}, + mFirstTileId {first_tile_id}, + mLastTileId {first_tile_id.value + (mTileset->tile_count() - 1)} +{} + +auto AttachedTileset::to_index(const TileID tile_id) const -> TileIndex +{ + TACTILE_ASSERT(has_tile(tile_id)); + return TileIndex {tile_id.value - mFirstTileId.value}; +} + +auto AttachedTileset::has_tile(const TileID tile_id) const -> bool +{ + return tile_id >= mFirstTileId && tile_id <= mLastTileId; +} + +auto AttachedTileset::get_first_tile_id() const -> TileID +{ + return mFirstTileId; +} + +auto AttachedTileset::get_last_tile_id() const -> TileID +{ + return mLastTileId; +} + +auto AttachedTileset::get_tileset() -> Tileset& +{ + return *mTileset; +} + +auto AttachedTileset::get_tileset() const -> const Tileset& +{ + return *mTileset; +} + +} // namespace tactile diff --git a/modules/core/test/tileset/attached_tileset_test.cpp b/modules/core/test/tileset/attached_tileset_test.cpp new file mode 100644 index 0000000000..61162e6482 --- /dev/null +++ b/modules/core/test/tileset/attached_tileset_test.cpp @@ -0,0 +1,59 @@ +// Copyright (C) 2023 Albin Johansson (GNU General Public License v3.0) + +#include "tactile/core/tileset/attached_tileset.hpp" + +#include + +#include "tactile/core/debug/error.hpp" +#include "testutil/tileset_helpers.hpp" + +using namespace tactile; + +/// \tests tactile::AttachedTileset::AttachedTileset +TEST(AttachedTileset, ConstructorWithNullTileset) +{ + EXPECT_THROW(AttachedTileset(nullptr, TileID {1}), RuntimeError); +} + +/// \tests tactile::AttachedTileset::to_index +TEST(AttachedTileset, ToIndex) +{ + const auto tileset_info = test::make_dummy_tileset_info(); + const auto tileset = make_shared(tileset_info); + + const AttachedTileset attached_tileset {tileset, TileID {42}}; + const auto first_id = attached_tileset.get_first_tile_id(); + const auto last_id = attached_tileset.get_last_tile_id(); + + EXPECT_EQ(attached_tileset.to_index(first_id), TileIndex {0}); + EXPECT_EQ(attached_tileset.to_index(last_id), TileIndex {tileset->tile_count() - 1}); +} + +/// \tests tactile::AttachedTileset::has_tile +TEST(AttachedTileset, HasTile) +{ + const auto tileset_info = test::make_dummy_tileset_info(); + const auto tileset = make_shared(tileset_info); + + const AttachedTileset attached_tileset {tileset, TileID {1}}; + const auto first_id = attached_tileset.get_first_tile_id(); + const auto last_id = attached_tileset.get_last_tile_id(); + + EXPECT_TRUE(attached_tileset.has_tile(first_id)); + EXPECT_TRUE(attached_tileset.has_tile(last_id)); + + EXPECT_FALSE(attached_tileset.has_tile(TileID {first_id.value - 1})); + EXPECT_FALSE(attached_tileset.has_tile(TileID {last_id.value + 1})); +} + +/// \tests tactile::AttachedTileset::get_first_tile_id +/// \tests tactile::AttachedTileset::get_last_tile_id +TEST(AttachedTileset, TileIdentifiers) +{ + const auto tileset_info = test::make_dummy_tileset_info(); + const auto tileset = make_shared(tileset_info); + const AttachedTileset attached_tileset {tileset, TileID {27}}; + + EXPECT_EQ(attached_tileset.get_first_tile_id(), TileID {27}); + EXPECT_EQ(attached_tileset.get_last_tile_id(), TileID {27 + tileset->tile_count() - 1}); +}