libtcod
Loading...
Searching...
No Matches
matrix.hpp
1/* BSD 3-Clause License
2 *
3 * Copyright © 2008-2025, Jice and the libtcod contributors.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * 3. Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32#pragma once
33#ifndef LIBTCOD_MATRIX_HPP_
34#define LIBTCOD_MATRIX_HPP_
35#include <array>
36#include <stdexcept>
37#include <string>
38#include <vector>
39
40namespace tcod {
41namespace internal {
43template <typename ArrayType>
44[[nodiscard]] static std::string array_as_string(const ArrayType& arr) {
45 std::string result{"{"};
46 for (const auto& it : arr) {
47 result += std::to_string(it);
48 if (&it != &arr.back()) {
49 result += ", ";
50 }
51 }
52 result += "}";
53 return result;
54}
55} // namespace internal
56
64template <typename T, size_t Dimensions>
66 public:
67 using size_type = int; // The int size of indexes.
68 using shape_type = std::array<size_type, Dimensions>; // The type used to store the matrixes shape.
69 using stride_type = std::array<size_type, Dimensions>; // The type used to store strides.
70 using index_type = std::array<size_type, Dimensions>; // The type used to index the container.
71 using reference = T&;
72 using const_reference = const T&;
74 constexpr MatrixView() = default;
76 constexpr MatrixView(const shape_type& shape_xy, const stride_type& strides_xy, T* data) noexcept
77 : shape_xy_{shape_xy}, strides_xy_{strides_xy}, data_{reinterpret_cast<data_ptr>(data)} {};
78
80 [[nodiscard]] constexpr reference operator[](const index_type& index) noexcept {
81 return *reinterpret_cast<T*>(data_ + get_offset(index));
82 }
83
84 [[nodiscard]] constexpr const_reference operator[](const index_type& index) const noexcept {
85 return *reinterpret_cast<const T*>(data_ + get_offset(index));
86 }
87
88 [[nodiscard]] constexpr reference at(const index_type& index) {
89 return *reinterpret_cast<T*>(data_ + check_range(index));
90 }
91
92 [[nodiscard]] constexpr const_reference at(const index_type& index) const {
93 return *reinterpret_cast<const T*>(data_ + check_range(index));
94 }
95
97 [[nodiscard]] constexpr bool in_bounds(const index_type& index) const noexcept {
98 for (size_t dimension = 0; dimension < Dimensions; ++dimension) {
99 if (!(0 <= index.at(dimension) && index.at(dimension) < shape_xy_.at(dimension))) return false;
100 }
101 return true;
102 }
103
104 private:
105 // `unsigned char*` pointer which has the same const as `T`.
106 using data_ptr = typename std::conditional<std::is_const<T>::value, const unsigned char*, unsigned char*>::type;
108 [[nodiscard]] constexpr size_t get_offset(const index_type& index) const noexcept {
109 size_t data_index = 0;
110 for (size_t dimension{0}; dimension < Dimensions; ++dimension) {
111 data_index += strides_xy_.at(dimension) * index.at(dimension);
112 }
113 return data_index;
114 }
116 constexpr size_t check_range(const index_type& index) const {
117 using internal::array_as_string;
118 if (!in_bounds(index)) {
119 throw std::out_of_range(
120 std::string("Out of bounds lookup ") + array_as_string(index) + " on matrix of shape " +
121 array_as_string(shape_xy_) + ".");
122 }
123 return get_offset(index);
124 }
125 shape_type shape_xy_; // The shape of this view.
126 stride_type strides_xy_; // The strides of this view in bytes.
127 data_ptr data_; // A pointer to the viewed memory.
128};
129
138template <typename T, size_t Dimensions, typename Container = std::vector<T>>
139class Matrix {
140 public:
141 using size_type = int; // The int size of indexes.
142 using shape_type = std::array<size_type, Dimensions>; // The type used to measure the matrixes shape.
143 using index_type = std::array<size_type, Dimensions>; // The type used to index the container.
144 using reference = typename Container::reference;
145 using const_reference = typename Container::const_reference;
147 constexpr Matrix() = default;
148
150 constexpr explicit Matrix(const shape_type& shape) : shape_{shape}, data_(get_size_from_shape(shape)) {}
151
153 constexpr Matrix(const shape_type& shape, const T& fill_value)
154 : shape_{shape}, data_(get_size_from_shape(shape), fill_value) {}
155
157 [[nodiscard]] constexpr auto begin() noexcept { return data_.begin(); }
159 [[nodiscard]] constexpr auto begin() const noexcept { return data_.cbegin(); }
160
162 [[nodiscard]] constexpr auto end() noexcept { return data_.end(); }
164 [[nodiscard]] constexpr auto end() const noexcept { return data_.cend(); }
165
167 [[nodiscard]] constexpr reference operator[](const index_type& index) noexcept { return data_[get_index(index)]; }
168
170 [[nodiscard]] constexpr const_reference operator[](const index_type& index) const noexcept {
171 return data_[get_index(index)];
172 }
173
174 [[nodiscard]] constexpr reference at(const index_type& index) { return data_.at(check_range(index)); }
175
177 [[nodiscard]] constexpr const_reference at(const index_type& index) const { return data_.at(check_range(index)); }
178
180 [[nodiscard]] constexpr const shape_type& get_shape() const noexcept { return shape_; }
181
183 [[nodiscard]] constexpr bool in_bounds(const index_type& index) const noexcept {
184 for (size_t dimension = 0; dimension < Dimensions; ++dimension) {
185 if (!(0 <= index.at(dimension) && index.at(dimension) < shape_.at(dimension))) return false;
186 }
187 return true;
188 }
189
191 [[nodiscard]] constexpr operator MatrixView<T, Dimensions>() noexcept {
192 return {get_shape(), get_strides(), data_.data()};
193 }
194
195 [[nodiscard]] constexpr operator MatrixView<const T, Dimensions>() const noexcept {
196 return {get_shape(), get_strides(), data_.data()};
197 }
198
200 [[nodiscard]] constexpr Container& get_container() noexcept { return data_; }
201
203 [[nodiscard]] constexpr const Container& get_container() const noexcept { return data_; }
204
205 template <class Archive>
206 void serialize(Archive& archive) {
207 archive(shape_, data_);
208 }
209
210 private:
212 [[nodiscard]] static constexpr size_t get_size_from_shape(const shape_type& shape) noexcept {
213 size_t size = 1;
214 for (auto& it : shape) size *= it;
215 return size;
216 }
218 [[nodiscard]] constexpr size_t get_index(const index_type& index) const noexcept {
219 size_t stride = 1;
220 size_t data_index = 0;
221 for (size_t dimension = 0; dimension < Dimensions; ++dimension) {
222 data_index += stride * index.at(dimension);
223 stride *= shape_.at(dimension);
224 }
225 return data_index;
226 }
228 constexpr size_t check_range(const index_type& index) const {
229 using internal::array_as_string;
230 if (!in_bounds(index)) {
231 throw std::out_of_range(
232 std::string("Out of bounds lookup ") + array_as_string(index) + " on matrix of shape " +
233 array_as_string(shape_) + ".");
234 }
235 return get_index(index);
236 }
238 [[nodiscard]] constexpr index_type get_strides() const noexcept {
239 index_type strides{};
240 int stride = static_cast<int>(sizeof(T));
241 for (size_t dimension = 0; dimension < Dimensions; ++dimension) {
242 strides.at(dimension) = stride;
243 stride *= shape_.at(dimension);
244 }
245 return strides;
246 }
247 shape_type shape_;
248 Container data_;
249};
250} // namespace tcod
251#endif // LIBTCOD_MATRIX_HPP_
constexpr Matrix(const shape_type &shape)
Create a matrix of the given shape.
Definition matrix.hpp:150
constexpr reference at(const index_type &index)
Get the item at index, checking bounds.
Definition matrix.hpp:174
constexpr bool in_bounds(const index_type &index) const noexcept
Return true if index is within the bounds of this matrix.
Definition matrix.hpp:183
constexpr const_reference at(const index_type &index) const
Get the const item at index, checking bounds.
Definition matrix.hpp:177
constexpr const Container & get_container() const noexcept
Get the const flat container for this matrix.
Definition matrix.hpp:203
constexpr auto end() const noexcept
Return the iterator end.
Definition matrix.hpp:164
constexpr Container & get_container() noexcept
Get the flat container for this matrix.
Definition matrix.hpp:200
constexpr Matrix()=default
Default constructor.
constexpr reference operator[](const index_type &index) noexcept
Get the item at index.
Definition matrix.hpp:167
constexpr Matrix(const shape_type &shape, const T &fill_value)
Create a matrix of the given shape filled with a default value.
Definition matrix.hpp:153
constexpr const shape_type & get_shape() const noexcept
Return the shape of this matrix.
Definition matrix.hpp:180
constexpr auto begin() const noexcept
Return the iterator beginning.
Definition matrix.hpp:159
constexpr auto begin() noexcept
Return the iterator beginning.
Definition matrix.hpp:157
constexpr const_reference operator[](const index_type &index) const noexcept
Get the const item at index.
Definition matrix.hpp:170
constexpr auto end() noexcept
Return the iterator end.
Definition matrix.hpp:162
A view into a strided multi-dimensional array.
Definition matrix.hpp:65
constexpr reference at(const index_type &index)
Get the item at index, checking bounds.
Definition matrix.hpp:88
constexpr MatrixView(const shape_type &shape_xy, const stride_type &strides_xy, T *data) noexcept
Create a new multi-dimensional view.
Definition matrix.hpp:76
constexpr reference operator[](const index_type &index) noexcept
Get the item at index.
Definition matrix.hpp:80
constexpr const_reference at(const index_type &index) const
Get the const item at index, checking bounds.
Definition matrix.hpp:92
constexpr MatrixView()=default
Default constructor.
constexpr const_reference operator[](const index_type &index) const noexcept
Get the const item at index.
Definition matrix.hpp:84
constexpr bool in_bounds(const index_type &index) const noexcept
Return true if index is within the bounds of this matrix.
Definition matrix.hpp:97
The libtcod namespace.
Definition bresenham.hpp:157