header_utils
Loading...
Searching...
No Matches
square_grid.h
1
4
5#pragma once
6
7#include "squares.h"
8#include <vector>
9#include <stdexcept>
10
12{
13 template <typename T>
14 concept void_or_boolean = std::same_as<void, T> || std::convertible_to<T, bool>;
15
20
21 template <typename FUNC, typename T>
22 concept tile_callback =
23 std::invocable<FUNC, glm::ivec2, T const&> ||
24 std::invocable<FUNC, T const&> ||
25 std::invocable<FUNC, glm::ivec2> ||
26 std::invocable<FUNC, T const&, glm::ivec2>;
27 template <typename FUNC, typename T>
28 concept query_tile_callback =
29 std::predicate<FUNC, glm::ivec2, T const&> ||
30 std::predicate<FUNC, T const&> ||
31 std::predicate<FUNC, glm::ivec2> ||
32 std::predicate<FUNC, T const&, glm::ivec2>;
33 template <typename FUNC, typename T>
34 concept change_tile_callback =
35 std::invocable<FUNC, glm::ivec2, T&> ||
36 std::invocable<FUNC, T&> ||
37 std::invocable<FUNC, glm::ivec2> ||
38 std::invocable<FUNC, T&, glm::ivec2>;
39
41 template <typename TILE_DATA, bool RESIZABLE = true>
42 struct grid
43 {
44 public:
45
46 static constexpr bool resizable = RESIZABLE;
47 using tile_data_type = TILE_DATA;
48
49 grid() requires RESIZABLE = default;
50 grid(int w, int h, TILE_DATA const& default_tile) { Reset(w, h, default_tile); }
51 grid(glm::ivec2 size, TILE_DATA const& default_tile) : grid(size.x, size.y, default_tile) {}
52 grid(int w, int h) { Reset(w, h); }
53 explicit grid(glm::ivec2 size) : grid(size.x, size.y) {}
54
55 void reset(int w, int h, TILE_DATA const& default_tile) requires RESIZABLE { Reset(w, h, default_tile); }
56 void reset(int w, int h, std::vector<TILE_DATA> tiles) requires RESIZABLE { Reset(w, h, tiles); }
57 void reset(int w, int h) requires RESIZABLE { Reset(w, h); }
58
59 void reset(glm::ivec2 size) requires RESIZABLE { Reset(size.x, size.y); }
60 void reset(glm::ivec2 size, TILE_DATA const& default_tile) requires RESIZABLE { Reset(size.x, size.y, default_tile); }
61
62 template <change_tile_callback<TILE_DATA> TILE_RESET_FUNC>
63 void reset_from(glm::ivec2 size, TILE_RESET_FUNC&& tile_reset) requires RESIZABLE { Reset(size.x, size.y, std::forward<TILE_RESET_FUNC>(tile_reset)); }
64
66
67 [[nodiscard]] TILE_DATA& operator[](int i) { return mTiles[i]; }
68 [[nodiscard]] TILE_DATA const& operator[](int i) const { return mTiles[i]; }
69
70#if defined(__cpp_multidimensional_subscript)
71 [[nodiscard]] TILE_DATA& operator[](int x, int y) { return mTiles[index(x, y)]; }
72 [[nodiscard]] TILE_DATA const& operator[](int x, int y) const { return mTiles[index(x, y)]; }
73#endif
74
75 [[nodiscard]] TILE_DATA& operator[](glm::ivec2 v) { return mTiles[index(v)]; }
76 [[nodiscard]] TILE_DATA const& operator[](glm::ivec2 v) const { return mTiles[index(v)]; }
77
78 [[nodiscard]] bool is_valid(int x, int y) const noexcept { return x >= 0 && y >= 0 && x < mWidth && y < mHeight; }
79 [[nodiscard]] bool is_valid(glm::vec2 world_pos, glm::vec2 tile_size) const noexcept { return is_valid(world_pos_to_tile_pos(world_pos, tile_size)); }
80 [[nodiscard]] bool is_valid(glm::ivec2 pos) const noexcept { return is_valid(pos.x, pos.y); }
81 [[nodiscard]] bool is_index_valid(int index) const noexcept { return index >= 0 && index < (int)mTiles.size(); }
82
83 [[nodiscard]] bool is_valid(int x, int y, int edge_width) const noexcept { return x >= edge_width && y >= edge_width && x < mWidth - edge_width && y < mHeight - edge_width; }
84 [[nodiscard]] bool is_valid(glm::vec2 world_pos, glm::vec2 tile_size, int edge_width) const noexcept { return is_valid(world_pos_to_tile_pos(world_pos, tile_size), edge_width); }
85 [[nodiscard]] bool is_valid(glm::ivec2 pos, int edge_width) const noexcept { return is_valid(pos.x, pos.y, edge_width); }
86
87 [[nodiscard]] inline int index(int x, int y) const noexcept { return x + y * mWidth; }
88 [[nodiscard]] inline int index(glm::ivec2 pos) const noexcept { return pos.x + pos.y * mWidth; }
89 [[nodiscard]] inline int valid_index(int x, int y) const noexcept { return is_valid(x, y) ? x + y * mWidth : -1; }
90 [[nodiscard]] inline int valid_index(glm::ivec2 pos) const noexcept { return is_valid(pos) ? pos.x + pos.y * mWidth : -1; }
91
92 [[nodiscard]] TILE_DATA const* at(glm::ivec2 pos) const noexcept { if (!is_valid(pos)) return nullptr; return &mTiles[index(pos)]; }
93 [[nodiscard]] TILE_DATA const* at(int x, int y) const noexcept { return at(glm::ivec2{ x, y }); }
94 [[nodiscard]] TILE_DATA* at(glm::ivec2 pos) noexcept { if (!is_valid(pos)) return nullptr; return &mTiles[index(pos)]; }
95 [[nodiscard]] TILE_DATA* at(int x, int y) noexcept { return at(glm::ivec2{ x, y }); }
96 [[nodiscard]] TILE_DATA const* at_index(int index) const noexcept { return is_index_valid(index) ? &mTiles[index] : nullptr; }
97 [[nodiscard]] TILE_DATA* at_index(int index) noexcept { return is_index_valid(index) ? &mTiles[index] : nullptr; }
98
99 [[nodiscard]] TILE_DATA const& safe_at(glm::ivec2 pos, TILE_DATA const& outside) const noexcept { if (auto at = this->at(pos)) return *at; return outside; }
100 [[nodiscard]] TILE_DATA const& safe_at(int x, int y, TILE_DATA const& outside) const noexcept { return safe_at(glm::ivec2{ x, y }, outside); }
101 [[nodiscard]] TILE_DATA& safe_at(glm::ivec2 pos, TILE_DATA& outside) noexcept { if (auto at = this->at(pos)) return *at; return outside; }
102 [[nodiscard]] TILE_DATA& safe_at(int x, int y, TILE_DATA& outside) noexcept { return safe_at(glm::ivec2{ x, y }, outside); }
103
104 [[nodiscard]] int width() const noexcept { return mWidth; }
105 [[nodiscard]] int height() const noexcept { return mHeight; }
106 [[nodiscard]] glm::ivec2 size() const noexcept { return { mWidth, mHeight }; }
107 [[nodiscard]] irec2 perimeter() const noexcept { return irec2::from_size({}, size()); }
108 [[nodiscard]] irec2 bounds() const noexcept { return perimeter(); }
109 [[nodiscard]] rec2 bounds(glm::vec2 tile_size) const noexcept { return perimeter() * tile_size; }
110 [[nodiscard]] std::span<TILE_DATA const> tiles() const { return mTiles; }
111 [[nodiscard]] std::span<TILE_DATA> tiles() { return mTiles; }
112 [[nodiscard]] size_t tile_count() const noexcept { return mTiles.size(); }
113
114 /*
117 bool IsReachable(glm::ivec2 src_tile, glm::ivec2 dest_tile);
118 */
119
120 enum class iteration_flags
121 {
122 with_self,
123 only_valid,
124 diagonals
125 };
126
128
129 template <enum_flags<iteration_flags> FLAGS = enum_flags<iteration_flags>{ iteration_flags::with_self, iteration_flags::only_valid }>
130 auto for_each_neighbor(glm::ivec2 of, change_tile_callback<TILE_DATA> auto&& func)
131 {
132 static constexpr auto ONLY_VALID = FLAGS.contain(iteration_flags::only_valid);
133 using return_type = decltype(this->apply<ONLY_VALID>(glm::ivec2{ 0, 0 }, func));
134
135 if constexpr (std::is_void_v<return_type>)
136 {
137 if constexpr (FLAGS.contain(iteration_flags::with_self))
138 apply<ONLY_VALID>(of, func);
139 apply<ONLY_VALID>({ of.x - 1, of.y }, func);
140 apply<ONLY_VALID>({ of.x + 1, of.y }, func);
141 apply<ONLY_VALID>({ of.x, of.y - 1 }, func);
142 apply<ONLY_VALID>({ of.x, of.y + 1 }, func);
143
144 if constexpr (FLAGS.contain(iteration_flags::diagonals))
145 {
146 apply<ONLY_VALID>({ of.x - 1, of.y - 1 }, func);
147 apply<ONLY_VALID>({ of.x + 1, of.y + 1 }, func);
148 apply<ONLY_VALID>({ of.x + 1, of.y - 1 }, func);
149 apply<ONLY_VALID>({ of.x - 1, of.y + 1 }, func);
150 }
151 }
152 else
153 {
154 if constexpr (FLAGS.contain(iteration_flags::with_self))
155 if (auto ret = apply<ONLY_VALID>(of, func)) return ret;
156 if (auto ret = apply<ONLY_VALID>({ of.x - 1, of.y }, func)) return ret;
157 if (auto ret = apply<ONLY_VALID>({ of.x + 1, of.y }, func)) return ret;
158 if (auto ret = apply<ONLY_VALID>({ of.x, of.y - 1 }, func)) return ret;
159 if (auto ret = apply<ONLY_VALID>({ of.x, of.y + 1 }, func)) return ret;
160
161 if constexpr (FLAGS.contain(iteration_flags::diagonals))
162 {
163 if (auto ret = apply<ONLY_VALID>({ of.x - 1, of.y - 1 }, func)) return ret;
164 if (auto ret = apply<ONLY_VALID>({ of.x + 1, of.y + 1 }, func)) return ret;
165 if (auto ret = apply<ONLY_VALID>({ of.x + 1, of.y - 1 }, func)) return ret;
166 if (auto ret = apply<ONLY_VALID>({ of.x - 1, of.y + 1 }, func)) return ret;
167 }
168 return return_type{};
169 }
170 }
171
172 template <enum_flags<iteration_flags> FLAGS = enum_flags<iteration_flags>{ iteration_flags::with_self, iteration_flags::only_valid }>
173 auto for_each_selected_neighbor(glm::ivec2 of, direction_set neighbor_set, change_tile_callback<TILE_DATA> auto&& func)
174 {
175 static constexpr auto ONLY_VALID = FLAGS.contain(iteration_flags::only_valid);
176 using return_type = decltype(this->apply<ONLY_VALID>(glm::ivec2{ 0, 0 }, func));
177
178 if constexpr (std::is_void_v<return_type>)
179 {
180 if constexpr (FLAGS.contain(iteration_flags::with_self))
181 apply<ONLY_VALID>(of, func);
182
183 neighbor_set.for_each([this, of, &func](direction d) {
184 apply<ONLY_VALID>(of + to_ivec(d), func);
185 });
186 }
187 else
188 {
189 if constexpr (FLAGS.contain(iteration_flags::with_self))
190 if (auto ret = apply<ONLY_VALID>(of, func)) return ret;
191
192 return neighbor_set.for_each([this, of, &func](direction d) {
193 return apply<ONLY_VALID>(of + to_ivec(d), func);
194 });
195 }
196 }
197
199 template <enum_flags<iteration_flags> FLAGS = enum_flags<iteration_flags>{ iteration_flags::only_valid }>
200 auto for_each_tile_in_rect(irec2 const& tile_rect, change_tile_callback<TILE_DATA> auto&& func)
201 {
202 static constexpr auto ONLY_VALID = FLAGS.contain(iteration_flags::only_valid);
203 using return_type = decltype(this->template apply<ONLY_VALID>(glm::ivec2{ 0, 0 }, func));
204
205 irec2 rect = tile_rect;
206 if constexpr (ONLY_VALID)
207 rect = tile_rect.clipped_to(this->bounds());
208
209 for (int y = rect.top(); y < rect.bottom(); y++)
210 for (int x = rect.left(); x < rect.right(); x++)
211 {
212 if constexpr (std::is_void_v<return_type>)
213 this->template apply<ONLY_VALID>({ x, y }, func);
214 else
215 {
216 if (auto ret = this->template apply<ONLY_VALID>({ x, y }, func))
217 return ret;
218 }
219 }
220
221 return default_value<return_type>();
222 }
223
224 template <enum_flags<iteration_flags> FLAGS = enum_flags<iteration_flags>{ iteration_flags::only_valid }>
225 auto for_each_tile_in_perimeter(irec2 const& tile_rect, change_tile_callback<TILE_DATA> auto&& func)
226 {
227 static constexpr auto ONLY_VALID = FLAGS.contain(iteration_flags::only_valid);
228 using return_type = decltype(this->apply<ONLY_VALID>(glm::ivec2{ 0, 0 }, func));
229
230 irec2 rect = tile_rect;
231 if constexpr (ONLY_VALID)
232 rect = tile_rect.clipped_to(bounds());
233
234 if constexpr (std::is_void_v<return_type>)
235 {
236 for (int x = rect.left(); x < rect.right(); x++)
237 {
238 apply<ONLY_VALID>({ x, rect.top() }, func);
239 apply<ONLY_VALID>({ x, rect.bottom() - 1 }, func);
240 }
241 for (int y = rect.top() + 1; y < rect.bottom() - 1; y++)
242 {
243 apply<ONLY_VALID>({ rect.left(), y }, func);
244 apply<ONLY_VALID>({ rect.right() - 1, y }, func);
245 }
246 }
247 else
248 {
249 for (int x = rect.left(); x < rect.right(); x++)
250 {
251 if (auto ret = apply<ONLY_VALID>({ x, rect.top() }, func)) return ret;
252 if (auto ret = apply<ONLY_VALID>({ x, rect.bottom() - 1 }, func)) return ret;
253 }
254 for (int y = rect.top() + 1; y < rect.bottom() - 1; y++)
255 {
256 if (auto ret = apply<ONLY_VALID>({ rect.left(), y }, func)) return ret;
257 if (auto ret = apply<ONLY_VALID>({ rect.right() - 1, y }, func)) return ret;
258 }
259
260 return return_type{};
261 }
262 }
263
264 template<enum_flags<iteration_flags> FLAGS = enum_flags<iteration_flags>{ iteration_flags::only_valid }, typename TILE_SET>
265 auto for_each_tile_in_set(TILE_SET&& tiles, change_tile_callback<TILE_DATA> auto&& func)
266 {
267 static constexpr auto ONLY_VALID = FLAGS.contain(iteration_flags::only_valid);
268 using return_type = decltype(this->apply<ONLY_VALID>(glm::ivec2{ 0, 0 }, func));
269
270 if constexpr (std::is_void_v<return_type>)
271 {
272 for (auto&& tile : tiles)
273 apply<ONLY_VALID>(tile, func);
274 }
275 else
276 {
277 for (auto&& tile : tiles)
278 if (auto ret = apply<ONLY_VALID>(tile, func)) return ret;
279 return return_type{};
280 }
281 }
282
283 template <change_tile_callback<TILE_DATA> FUNC>
284 auto for_each_tile(FUNC&& func)
285 {
286 irec2 rect = { 0, 0, this->mWidth, this->mHeight };
287 return this->template for_each_tile_in_rect<enum_flags<iteration_flags>{}>(rect, std::forward<FUNC>(func));
288 }
289
290 template <enum_flags<iteration_flags> FLAGS = enum_flags<iteration_flags>{ iteration_flags::only_valid }>
291 auto for_each_tile_in_polygon(std::span<glm::vec2 const> poly_points, glm::vec2 tile_size, change_tile_callback<TILE_DATA> auto&& func);
292
293 template <enum_flags<iteration_flags> FLAGS = enum_flags<iteration_flags>{ iteration_flags::only_valid }, typename FUNC>
294 auto for_each_tile_in_row(int row, FUNC&& func);
295
296 template <enum_flags<iteration_flags> FLAGS = enum_flags<iteration_flags>{ iteration_flags::only_valid }, typename FUNC>
297 auto for_each_tile_in_column(int column, FUNC&& func);
298
300 template <query_tile_callback<TILE_DATA> FUNC>
301 bool line_cast(glm::ivec2 start, glm::ivec2 end, FUNC&& blocks_func, bool ignore_start) const
302 {
303 int delta_x{ end.x - start.x };
304 // if x1 == x2, then it does not matter what we set here
305 signed char const ix((delta_x > 0) - (delta_x < 0));
306 delta_x = std::abs(delta_x) << 1;
307
308 int delta_y(end.y - start.y);
309 // if y1 == y2, then it does not matter what we set here
310 signed char const iy((delta_y > 0) - (delta_y < 0));
311 delta_y = std::abs(delta_y) << 1;
312
313 if (!ignore_start && apply(start, blocks_func))
314 return false;
315
316 if (delta_x >= delta_y)
317 {
318 // error may go below zero
319 int error(delta_y - (delta_x >> 1));
320
321 while (start.x != end.x)
322 {
323 // reduce error, while taking into account the corner case of error == 0
324 if ((error > 0) || (!error && (ix > 0)))
325 {
326 error -= delta_x;
327 start.y += iy;
328 }
329 // else do nothing
330
331 error += delta_y;
332 start.x += ix;
333
334 if (apply(start, blocks_func))
335 return false;
336 }
337 }
338 else
339 {
340 // error may go below zero
341 int error(delta_x - (delta_y >> 1));
342
343 while (start.y != end.y)
344 {
345 // reduce error, while taking into account the corner case of error == 0
346 if ((error > 0) || (!error && (iy > 0)))
347 {
348 error -= delta_y;
349 start.x += ix;
350 }
351 // else do nothing
352
353 error += delta_x;
354 start.y += iy;
355
356 if (apply(start, blocks_func))
357 return false;
358 }
359 }
360
361 return true;
362 }
363
365
366 template <bool ONLY_VALID = true, change_tile_callback<TILE_DATA> FUNC>
367 auto apply(glm::ivec2 to, FUNC&& func)
368 {
369 //if constexpr (ONLY_VALID) if (!is_valid(to)) return return_type{};
370 using invocable_type = std::remove_cvref_t<FUNC>;
371 if constexpr (std::invocable<invocable_type, glm::ivec2, tile_data_type&>)
372 return (!ONLY_VALID || this->is_valid(to)) ? func(to, *this->at(to)) : default_value<decltype(func(to, *this->at(to)))>();
373 else if constexpr (std::invocable<invocable_type, tile_data_type&, glm::ivec2>)
374 return (!ONLY_VALID || this->is_valid(to)) ? func(*this->at(to), to) : default_value<decltype(func(*this->at(to), to))>();
375 else if constexpr (std::invocable<invocable_type, tile_data_type&>)
376 return (!ONLY_VALID || this->is_valid(to)) ? func(*this->at(to)) : default_value<decltype(func(*this->at(to)))>();
377 else
378 return (!ONLY_VALID || this->is_valid(to)) ? func(to) : default_value<decltype(func(to))>();
379 }
380
381 template <bool ONLY_VALID = true, tile_callback<TILE_DATA> FUNC>
382 auto apply(glm::ivec2 to, FUNC&& func) const
383 {
384 using invocable_type = std::remove_cvref_t<FUNC>;
385 if constexpr (std::invocable<invocable_type, glm::ivec2, tile_data_type const&>)
386 return (!ONLY_VALID || this->is_valid(to)) ? func(to, *this->at(to)) : default_value<decltype(func(to, *this->at(to)))>();
387 else if constexpr (std::invocable<invocable_type, tile_data_type const&, glm::ivec2>)
388 return (!ONLY_VALID || this->is_valid(to)) ? func(*this->at(to), to) : default_value<decltype(func(*this->at(to), to))>();
389 else if constexpr (std::invocable<invocable_type, tile_data_type const&>)
390 return (!ONLY_VALID || this->is_valid(to)) ? func(*this->at(to)) : default_value<decltype(func(*this->at(to)))>();
391 else
392 return (!ONLY_VALID || this->is_valid(to)) ? func(to) : default_value<decltype(func(to))>();
393 }
394
395 void flip_row(int row)
396 {
397 if (row >= 0 && row < mHeight)
398 std::reverse(GetRowStart(row), GetRowStart(row) + mWidth);
399 }
400
401 void flip_horizontal()
402 {
403 for (int i = 0; i < mHeight; i++)
404 std::reverse(GetRowStart(i), GetRowStart(i) + mWidth);
405 }
406
407 void flip_vertical()
408 {
409 for (int i = 0; i < mHeight / 2; i++)
410 std::swap_ranges(GetRowStart(i), GetRowStart(i) + mWidth, GetRowStart(mHeight - i - 1));
411 }
412
413 void rotate_row(int row, int by);
414 void rotate_column(int column, int by);
415
416 void shift_row(int row, int by, TILE_DATA const& add_tile);
417 void shift_column(int column, int by, TILE_DATA const& add_tile);
418
419 void rotate_rows(int by);
420 void rotate_columns(int by);
421
422 void shift_rows(int by, TILE_DATA const& add_tile)
423 {
424 if (by == 0) return;
425 else if (by > 0)
426 {
427 auto num_elems_to_shift = by * mWidth;
428 std::move_backward(mTiles.begin(), mTiles.begin() + (mTiles.size() - num_elems_to_shift), mTiles.end());
429 std::ranges::fill_n(mTiles.begin(), num_elems_to_shift, add_tile);
430 }
431 else if (by < 0)
432 {
433 auto num_elems_to_shift = -by * mWidth;
434 std::move(mTiles.begin() + num_elems_to_shift, mTiles.end(), mTiles.begin());
435 std::ranges::fill_n(mTiles.begin() + (mTiles.size() - num_elems_to_shift), num_elems_to_shift, add_tile);
436 }
437 }
438 void shift_columns(int by, TILE_DATA const& add_tile);
439
440 void rotate_180() requires RESIZABLE
441 {
442 for (int i = 0; i < mHeight / 2; i++)
443 std::swap_ranges(std::make_reverse_iterator(GetRowStart(i) + mWidth), std::make_reverse_iterator(GetRowStart(i)), GetRowStart(mHeight - i - 1));
444
446 if (mHeight % 1)
447 std::reverse(GetRowStart(mHeight / 2 + 1), GetRowStart(mHeight / 2 + 1) + mWidth);
448 }
449
450 void resize(glm::uvec2 new_size, const TILE_DATA& new_element) requires RESIZABLE
451 {
452 ResizeY(new_size.y, new_element);
453 ResizeX(new_size.x, new_element);
454 }
455
456 void resize(glm::uvec2 new_size) requires RESIZABLE
457 {
458 const TILE_DATA new_element{};
459 resize(new_size, new_element);
460 }
461
462 void resize(int w, int h) requires RESIZABLE
463 {
464 if (w < 0) throw std::invalid_argument{ "width cannot be negative" };
465 if (h < 0) throw std::invalid_argument{ "height cannot be negative" };
466 resize({ w, h });
467 }
468
469 void clear() { for (auto& tile : mTiles) tile = {}; }
470 void clear(TILE_DATA const& to) { for (auto& tile : mTiles) tile = to; }
471
472 protected:
473
474 auto GetRowStart(int row) { return mTiles.begin() + row * mWidth; }
475 auto GetTileIterator(int x, int y) { return mTiles.begin() + index(x, y); }
476
477 void Reset(int w, int h, TILE_DATA const& default_tile)
478 {
479 if (w < 0) throw std::invalid_argument{ "width cannot be negative" };
480 if (h < 0) throw std::invalid_argument{ "height cannot be negative" };
481
482 mTiles.clear();
483 mWidth = w;
484 mHeight = h;
485 mTiles.resize(w * h, default_tile);
486 }
487
488 template <change_tile_callback<TILE_DATA> TILE_RESET_FUNC>
489 void Reset(int w, int h, TILE_RESET_FUNC&& tile_reset)
490 {
491 if (w < 0) throw std::invalid_argument{ "width cannot be negative" };
492 if (h < 0) throw std::invalid_argument{ "height cannot be negative" };
493
494 mTiles.clear();
495 mWidth = w;
496 mHeight = h;
497 mTiles.resize(w * h);
498 for_each_tile(tile_reset);
499 //for_each_tile([&tile_reset](glm::ivec2 t, TILE_DATA& tile) { tile_reset(); });
500 }
501
502 void Reset(int w, int h)
503 {
504 if (w < 0) throw std::invalid_argument{ "width cannot be negative" };
505 if (h < 0) throw std::invalid_argument{ "height cannot be negative" };
506
507 mTiles.clear();
508 mWidth = w;
509 mHeight = h;
510 mTiles.resize(w * h);
511 }
512
513 void Reset(int w, int h, std::vector<TILE_DATA> tiles)
514 {
515 if (w < 0) throw std::invalid_argument{ "width cannot be negative" };
516 if (h < 0) throw std::invalid_argument{ "height cannot be negative" };
517 if (size_t(w * h) > tiles.size()) throw std::invalid_argument{ "not enough tiles in vector" };
518
519 tiles.resize(w * h);
520 mTiles = std::move(tiles);
521 mWidth = w;
522 mHeight = h;
523 }
524
525 void ResizeY(int new_y, const TILE_DATA& new_element)
526 {
527 if (new_y < 0) throw std::invalid_argument("new_y cannot be negative");
528
529 const auto new_count = new_y * mWidth;
530 mTiles.resize(new_count, new_element);
531 mHeight = new_y;
532 }
533
534 void ResizeX(int new_x, const TILE_DATA& new_element)
535 {
536 if (new_x < 0) throw std::invalid_argument("new_x cannot be negative");
538 /*
539 std::vector<T> new_vector;
540 new_vector.reserve(new_size);
541 for (size_t y=0; y<mHeight; ++y)
542 {
543 for (size_t x=0; x<mSize.x; ++x)
544 {
545 new_vector.push_back(std::move(mTiles[y * mSize.x + x]));
546 }
547 for (size_t x=mSize.x; x<new_x; ++x)
548 {
549 new_vector.push_back(new_element);
550 }
551 }
552 mTiles = std::move(new_vector);
553 */
554 const auto new_count = new_x * mHeight;
555
556 if (new_x > mWidth)
557 {
558 mTiles.resize(new_count, new_element);
559
560 for (int yy = 0; yy < mHeight; ++yy)
561 {
562 auto y = mHeight - yy - 1;
563 const auto begin_range = y * mWidth;
564 const auto end_range = begin_range + mWidth;
565 const auto new_end_range = y * new_x + mWidth;
566 std::swap_ranges(std::make_reverse_iterator(mTiles.begin() + end_range), std::make_reverse_iterator(mTiles.begin() + begin_range), std::make_reverse_iterator(mTiles.begin() + new_end_range));
567 }
568 }
569 else
570 {
571 const auto dif = mWidth - new_x;
572 for (int y = 1; y < mHeight; ++y)
573 {
574 const auto begin_range = y * mWidth;
575 const auto end_range = begin_range + new_x;
576 std::move(mTiles.begin() + begin_range, mTiles.begin() + end_range, mTiles.begin() + begin_range - (dif * y));
577 }
578 mTiles.resize(new_count);
579 }
580
581 mWidth = new_x;
582 }
583
584 int mWidth = 0;
585 int mHeight = 0;
586 std::vector<TILE_DATA> mTiles;
587 };
588
590}
constexpr decltype(auto) at(random_access_range auto &range, std::integral auto index)
Returns a reference to the value at index of range.
Definition ranges.h:61
constexpr CONTAINER to(RANGE &&range, TYPES &&... args)
to<container>();
Definition ranges.h:386
named< glm::vec2, "world_pos", traits::location > world_pos
World position with float values.
Definition squares.h:203
constexpr auto for_each(FUNC &&callback) const
Calls callback for each enum flag in the set.
Definition enum_flags.h:326
auto for_each_neighbor(glm::ivec2 of, change_tile_callback< TILE_DATA > auto &&func)
TODO: set_* functions that mirror for_each_*, e.g. set_rect(rect, tile);.
bool line_cast(glm::ivec2 start, glm::ivec2 end, FUNC &&blocks_func, bool ignore_start) const
Returns whether the (bresenham) line between start and end is free of blocking tiles,...
TILE_DATA & operator[](int i)
Accessors & Queries.
Definition square_grid.h:67
auto for_each_tile_in_rect(irec2 const &tile_rect, change_tile_callback< TILE_DATA > auto &&func)
TODO: deduce thos.
auto apply(glm::ivec2 to, FUNC &&func)
Modifiers.