-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3b4c837
commit 6077f8c
Showing
3 changed files
with
498 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
// Copyright (C) 2024 Albin Johansson (GNU General Public License v3.0) | ||
|
||
#pragma once | ||
|
||
#include <ostream> // ostream | ||
|
||
#include "tactile/base/container/vector.hpp" | ||
#include "tactile/base/id.hpp" | ||
#include "tactile/base/prelude.hpp" | ||
|
||
namespace tactile { | ||
|
||
/** | ||
* Represents a location in a two-dimensional matrix. | ||
*/ | ||
struct MatrixIndex final { | ||
usize row; ///< The row index. | ||
usize col; ///< The column index. | ||
|
||
[[nodiscard]] auto operator==(const MatrixIndex&) const noexcept -> bool = default; | ||
}; | ||
|
||
/** | ||
* Outputs a matrix index to a stream. | ||
* | ||
* \param stream The output stream. | ||
* \param index The matrix index to emit. | ||
* | ||
* \return | ||
* The provided stream. | ||
*/ | ||
auto operator<<(std::ostream& stream, const MatrixIndex& index) -> std::ostream&; | ||
|
||
/** | ||
* Represents the size of a two-dimensional matrix. | ||
*/ | ||
struct MatrixExtent final { | ||
usize rows; ///< The number of rows. | ||
usize cols; ///< The number of columns. | ||
|
||
[[nodiscard]] auto operator==(const MatrixExtent&) const noexcept -> bool = default; | ||
}; | ||
|
||
/** | ||
* Outputs a matrix extent to a stream. | ||
* | ||
* \param stream The output stream. | ||
* \param extent The matrix extent to emit. | ||
* | ||
* \return | ||
* The provided stream. | ||
*/ | ||
auto operator<<(std::ostream& stream, const MatrixExtent& extent) -> std::ostream&; | ||
|
||
/** | ||
* Represents a two-dimensional grid of tile identifiers. | ||
*/ | ||
class TileMatrix final { | ||
public: | ||
/** | ||
* Creates an empty tile matrix with extent (0, 0). | ||
*/ | ||
TileMatrix() noexcept = default; | ||
|
||
/** | ||
* Creates an empty tile matrix with a given extent. | ||
* | ||
* \param extent The initial extent. | ||
*/ | ||
explicit TileMatrix(const MatrixExtent& extent); | ||
|
||
TACTILE_DEFAULT_COPY(TileMatrix); | ||
TACTILE_DEFAULT_MOVE(TileMatrix); | ||
|
||
~TileMatrix() noexcept = default; | ||
|
||
/** | ||
* Changes the extent of the matrix. | ||
* | ||
* \param new_extent The new extent. | ||
*/ | ||
void resize(const MatrixExtent& new_extent); | ||
|
||
/** | ||
* Sets the number of rows in the matrix. | ||
* | ||
* \param rows The new number of rows. | ||
*/ | ||
void set_row_count(usize rows); | ||
|
||
/** | ||
* Sets the number of columns in the matrix. | ||
* | ||
* \param cols The new number of columns. | ||
*/ | ||
void set_column_count(usize cols); | ||
|
||
/** | ||
* Returns the tile identifier at a given index. | ||
* | ||
* \param index The index of the desired tile. | ||
* | ||
* \return | ||
* A tile identifier. | ||
* | ||
* \throw Exception if the index is invalid. | ||
*/ | ||
[[nodiscard]] | ||
auto at(MatrixIndex index) -> TileID&; | ||
|
||
/** | ||
* \copydoc at | ||
*/ | ||
[[nodiscard]] | ||
auto at(MatrixIndex index) const -> TileID; | ||
|
||
/** | ||
* Returns the tile identifier at a given index. | ||
* | ||
* \note | ||
* This function performs no bounds checking. | ||
* | ||
* \pre | ||
* The provided index must be valid. | ||
* | ||
* \param index The index of the desired tile. | ||
* | ||
* \return | ||
* A tile identifier. | ||
*/ | ||
[[nodiscard]] auto operator[](MatrixIndex index) noexcept -> TileID&; | ||
|
||
/** | ||
* \copydoc operator[] | ||
*/ | ||
[[nodiscard]] auto operator[](MatrixIndex index) const noexcept -> TileID; | ||
|
||
/** | ||
* Indicates whether an index is valid, i.e., whether it refers to a tile in the matrix. | ||
* | ||
* \param index The index that will be checked. | ||
* | ||
* \return | ||
* True if the index is valid; false otherwise. | ||
*/ | ||
[[nodiscard]] | ||
auto is_valid(const MatrixIndex& index) const noexcept -> bool; | ||
|
||
/** | ||
* Returns the current extent of the matrix. | ||
* | ||
* \return | ||
* A matrix extent. | ||
*/ | ||
[[nodiscard]] | ||
auto get_extent() const noexcept -> const MatrixExtent&; | ||
|
||
private: | ||
MatrixExtent mExtent {0, 0}; | ||
Vector<Vector<TileID>> mRows {}; | ||
}; | ||
|
||
} // namespace tactile |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
// Copyright (C) 2024 Albin Johansson (GNU General Public License v3.0) | ||
|
||
#include "tactile/core/layer/tile_matrix.hpp" | ||
|
||
#include "tactile/core/debug/assert.hpp" | ||
#include "tactile/core/debug/exception.hpp" | ||
|
||
namespace tactile { | ||
namespace { | ||
|
||
void _add_rows(Vector<Vector<TileID>>& rows, const usize n, const usize cols) | ||
{ | ||
rows.reserve(rows.size() + n); | ||
for (usize i = 0; i < n; ++i) { | ||
rows.emplace_back(cols, kEmptyTile); | ||
} | ||
} | ||
|
||
void _add_columns(Vector<Vector<TileID>>& rows, const usize n) | ||
{ | ||
for (auto& row : rows) { | ||
row.reserve(row.size() + n); | ||
for (usize i = 0; i < n; ++i) { | ||
row.push_back(kEmptyTile); | ||
} | ||
} | ||
} | ||
|
||
void _remove_rows(Vector<Vector<TileID>>& rows, const usize n) | ||
{ | ||
for (usize i = 0; i < n; ++i) { | ||
TACTILE_ASSERT(!rows.empty()); | ||
rows.pop_back(); | ||
} | ||
} | ||
|
||
void _remove_columns(Vector<Vector<TileID>>& rows, const usize n) | ||
{ | ||
for (auto& row : rows) { | ||
for (usize i = 0; i < n; ++i) { | ||
TACTILE_ASSERT(!row.empty()); | ||
row.pop_back(); | ||
} | ||
} | ||
} | ||
|
||
} // namespace | ||
|
||
auto operator<<(std::ostream& stream, const MatrixIndex& index) -> std::ostream& | ||
{ | ||
stream << '(' << index.row << ';' << index.col << ')'; | ||
return stream; | ||
} | ||
|
||
auto operator<<(std::ostream& stream, const MatrixExtent& extent) -> std::ostream& | ||
{ | ||
stream << '(' << extent.rows << ';' << extent.cols << ')'; | ||
return stream; | ||
} | ||
|
||
TileMatrix::TileMatrix(const MatrixExtent& extent) | ||
: mExtent {extent} | ||
{ | ||
mRows.reserve(mExtent.rows); | ||
mRows.assign(mExtent.rows, Vector<TileID>(mExtent.cols, kEmptyTile)); | ||
} | ||
|
||
void TileMatrix::resize(const MatrixExtent& new_extent) | ||
{ | ||
set_column_count(new_extent.cols); | ||
set_row_count(new_extent.rows); | ||
} | ||
|
||
void TileMatrix::set_row_count(const usize rows) | ||
{ | ||
if (rows != mExtent.rows) { | ||
if (rows > mExtent.rows) { | ||
_add_rows(mRows, rows - mExtent.rows, mExtent.cols); | ||
} | ||
else { | ||
_remove_rows(mRows, mExtent.rows - rows); | ||
} | ||
|
||
mExtent.rows = rows; | ||
} | ||
} | ||
|
||
void TileMatrix::set_column_count(const usize cols) | ||
{ | ||
if (cols != mExtent.cols) { | ||
if (cols > mExtent.cols) { | ||
_add_columns(mRows, cols - mExtent.cols); | ||
} | ||
else { | ||
_remove_columns(mRows, mExtent.cols - cols); | ||
} | ||
|
||
mExtent.cols = cols; | ||
} | ||
} | ||
|
||
auto TileMatrix::at(const MatrixIndex index) -> TileID& | ||
{ | ||
if (is_valid(index)) [[likely]] { | ||
return operator[](index); | ||
} | ||
|
||
throw Exception {"bad matrix index"}; | ||
} | ||
|
||
auto TileMatrix::at(const MatrixIndex index) const -> TileID | ||
{ | ||
if (is_valid(index)) [[likely]] { | ||
return operator[](index); | ||
} | ||
|
||
throw Exception {"bad matrix index"}; | ||
} | ||
|
||
auto TileMatrix::operator[](const MatrixIndex index) noexcept -> TileID& | ||
{ | ||
TACTILE_ASSERT_MSG(index.row < mExtent.rows, "bad row index"); | ||
TACTILE_ASSERT_MSG(index.col < mExtent.cols, "bad column index"); | ||
return mRows[index.row][index.col]; | ||
} | ||
|
||
auto TileMatrix::operator[](const MatrixIndex index) const noexcept -> TileID | ||
{ | ||
TACTILE_ASSERT_MSG(index.row < mExtent.rows, "bad row index"); | ||
TACTILE_ASSERT_MSG(index.col < mExtent.cols, "bad column index"); | ||
return mRows[index.row][index.col]; | ||
} | ||
|
||
auto TileMatrix::is_valid(const MatrixIndex& index) const noexcept -> bool | ||
{ | ||
return (index.row < mExtent.rows) && (index.col < mExtent.cols); | ||
} | ||
|
||
auto TileMatrix::get_extent() const noexcept -> const MatrixExtent& | ||
{ | ||
return mExtent; | ||
} | ||
|
||
} // namespace tactile |
Oops, something went wrong.