Porytiles
Loading...
Searching...
No Matches
metatile.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <array>
4
11
12namespace porytiles {
13
14namespace metatile {
15
16inline constexpr std::size_t tiles_per_side = 2;
18inline constexpr std::size_t tiles_per_metatile = tiles_per_metatile_layer * 3;
19inline constexpr std::size_t side_length_pix = tiles_per_side * tile::side_length_pix;
20inline constexpr std::size_t entries_per_metatile_dual = 8;
21inline constexpr std::size_t entries_per_metatile_triple = 12;
22inline constexpr std::size_t metatiles_per_row = 8;
23
24enum class Layer : std::uint8_t { bottom = 0, middle = 1, top = 2 };
25
26[[nodiscard]] inline std::string to_string(Layer layer)
27{
28 switch (layer) {
29 case Layer::bottom:
30 return "bottom";
31 case Layer::middle:
32 return "middle";
33 case Layer::top:
34 return "top";
35 }
36 panic("unhandled Layer value");
37}
38
39inline std::ostream &operator<<(std::ostream &os, const Layer &layer)
40{
41 os << to_string(layer);
42 return os;
43}
44
45enum class Subtile : std::uint8_t { northwest = 0, northeast = 1, southwest = 2, southeast = 3 };
46
47[[nodiscard]] inline Subtile subtile_from_index(std::size_t i)
48{
49 return static_cast<Subtile>(i);
50}
51
52[[nodiscard]] inline std::string to_string(Subtile layer)
53{
54 switch (layer) {
56 return "northwest(" + std::to_string(static_cast<std::uint8_t>(layer)) + ")";
58 return "northeast(" + std::to_string(static_cast<std::uint8_t>(layer)) + ")";
60 return "southwest(" + std::to_string(static_cast<std::uint8_t>(layer)) + ")";
62 return "southeast(" + std::to_string(static_cast<std::uint8_t>(layer)) + ")";
63 }
64 panic("unhandled Subtile value");
65}
66
67inline std::ostream &operator<<(std::ostream &os, const Subtile &subtile)
68{
69 os << to_string(subtile);
70 return os;
71}
72
90[[nodiscard]] inline std::tuple<std::size_t, Layer, Subtile> from_tile_index(std::size_t tile_index)
91{
92 const std::size_t metatile_index = tile_index / tiles_per_metatile;
93 const std::size_t local_index = tile_index % tiles_per_metatile;
94 const auto layer = static_cast<Layer>(local_index / tiles_per_metatile_layer);
95 const auto subtile = static_cast<Subtile>(local_index % tiles_per_metatile_layer);
96
97 return {metatile_index, layer, subtile};
98}
99
117[[nodiscard]] inline std::tuple<Layer, Subtile> from_internal_tile_index(std::size_t tile_index)
118{
119 if (tile_index >= tiles_per_metatile) {
120 panic("tile_index (" + std::to_string(tile_index) + ") >= tiles_per_metatile");
121 }
122
123 const std::size_t local_index = tile_index % tiles_per_metatile;
124 const auto layer = static_cast<Layer>(local_index / tiles_per_metatile_layer);
125 const auto subtile = static_cast<Subtile>(local_index % tiles_per_metatile_layer);
126
127 return {layer, subtile};
128}
129
130[[nodiscard]] inline std::string message_header(
131 const TextFormatter &format,
132 std::size_t index,
133 Layer layer,
134 Subtile subtile,
135 std::size_t subtile_row,
136 std::size_t subtile_col)
137{
138 return format.format(
139 "{} {}({})|{}|{}|{},{}",
140 FormatParam{"metatile"},
142 FormatParam{index},
143 FormatParam{to_string(layer)},
144 FormatParam{to_string(subtile)},
145 FormatParam{std::to_string(subtile_row)},
146 FormatParam{std::to_string(subtile_col)});
147}
148
149[[nodiscard]] inline std::string
150message_header(const TextFormatter &format, std::size_t index, Layer layer, Subtile subtile)
151{
152 return format.format(
153 "{} {}({})|{}|{}",
154 FormatParam{"metatile"},
156 FormatParam{index},
157 FormatParam{to_string(layer)},
158 FormatParam{to_string(subtile)});
159}
160
161[[nodiscard]] inline std::string message_header(const TextFormatter &format, std::size_t index, Subtile subtile)
162{
163 return format.format(
164 "{} {}({})|{}",
165 FormatParam{"metatile"},
167 FormatParam{index},
168 FormatParam{to_string(subtile)});
169}
170
171} // namespace metatile
172
185template <SupportsTransparency PixelType>
186class Metatile {
187 public:
188 Metatile() : id_{} {}
189
190 bool operator==(const Metatile &) const = default;
191
201 [[nodiscard]] bool is_transparent() const
202 requires requires(const PixelType &p) { p.is_transparent(); }
203 {
204 const bool bottom_transparent =
205 std::ranges::all_of(bottom(), [](const auto &tile) { return tile.is_transparent(); });
206 const bool middle_transparent =
207 std::ranges::all_of(middle(), [](const auto &tile) { return tile.is_transparent(); });
208 const bool top_transparent = std::ranges::all_of(top(), [](const auto &tile) { return tile.is_transparent(); });
209 return bottom_transparent && middle_transparent && top_transparent;
210 }
211
223 [[nodiscard]] bool is_transparent(const PixelType &extrinsic) const
224 requires requires(const PixelType &p) { p.is_transparent(p); }
225 {
226 const bool bottom_transparent =
227 std::ranges::all_of(bottom(), [=](const auto &tile) { return tile.is_transparent(extrinsic); });
228 const bool middle_transparent =
229 std::ranges::all_of(middle(), [=](const auto &tile) { return tile.is_transparent(extrinsic); });
230 const bool top_transparent =
231 std::ranges::all_of(top(), [=](const auto &tile) { return tile.is_transparent(extrinsic); });
232 return bottom_transparent && middle_transparent && top_transparent;
233 }
234
243 [[nodiscard]] std::array<PixelTile<PixelType>, metatile::tiles_per_metatile> decompose() const
244 {
245 std::array<PixelTile<PixelType>, metatile::tiles_per_metatile> tiles{};
246
247 auto out_it = tiles.begin();
248 out_it = std::ranges::copy(bottom(), out_it).out;
249 out_it = std::ranges::copy(middle(), out_it).out;
250 std::ranges::copy(top(), out_it);
251
252 return tiles;
253 }
254
264 [[nodiscard]] LayerMode infer_layer_mode() const
265 requires requires(const PixelType &p) { p.is_transparent(); }
266 {
267 return infer_layer_mode_impl([](const PixelType &pixel) { return pixel.is_transparent(); });
268 }
269
280 [[nodiscard]] LayerMode infer_layer_mode(const PixelType &extrinsic) const
281 requires requires(const PixelType &p) { p.is_transparent(p); }
282 {
283 return infer_layer_mode_impl([&extrinsic](const PixelType &pixel) { return pixel.is_transparent(extrinsic); });
284 }
285
300 [[nodiscard]] LayerType infer_layer_type() const
301 requires requires(const PixelType &p) { p.is_transparent(); }
302 {
303 return infer_layer_type_impl([](const PixelType &pixel) { return pixel.is_transparent(); });
304 }
305
321 [[nodiscard]] LayerType infer_layer_type(const PixelType &extrinsic) const
322 requires requires(const PixelType &p) { p.is_transparent(p); }
323 {
324 return infer_layer_type_impl([&extrinsic](const PixelType &pixel) { return pixel.is_transparent(extrinsic); });
325 }
326
336 [[nodiscard]] const PixelTile<PixelType> &bottom(std::size_t i) const
337 {
338 if (i > 3) {
339 panic(std::format("index {} out of bounds: must be [0,3]", i));
340 }
341 return bottom_[i];
342 }
343
352 [[nodiscard]] const std::array<PixelTile<PixelType>, metatile::tiles_per_metatile_layer> &bottom() const
353 {
354 return bottom_;
355 }
356
366 void set_bottom(std::size_t i, PixelTile<PixelType> tile)
367 {
368 if (i > 3) {
369 panic(std::format("index {} out of bounds: must be [0,3]", i));
370 }
371 bottom_[i] = std::move(tile);
372 }
373
383 [[nodiscard]] const PixelTile<PixelType> &middle(std::size_t i) const
384 {
385 if (i > 3) {
386 panic(std::format("index {} out of bounds: must be [0,3]", i));
387 }
388 return middle_[i];
389 }
390
399 [[nodiscard]] const std::array<PixelTile<PixelType>, metatile::tiles_per_metatile_layer> &middle() const
400 {
401 return middle_;
402 }
403
413 void set_middle(std::size_t i, PixelTile<PixelType> tile)
414 {
415 if (i > 3) {
416 panic(std::format("index {} out of bounds: must be [0,3]", i));
417 }
418 middle_[i] = std::move(tile);
419 }
420
430 [[nodiscard]] const PixelTile<PixelType> &top(std::size_t i) const
431 {
432 if (i > 3) {
433 panic(std::format("index {} out of bounds: must be [0,3]", i));
434 }
435 return top_[i];
436 }
437
446 [[nodiscard]] const std::array<PixelTile<PixelType>, metatile::tiles_per_metatile_layer> &top() const
447 {
448 return top_;
449 }
450
460 void set_top(std::size_t i, PixelTile<PixelType> tile)
461 {
462 if (i > 3) {
463 panic(std::format("index {} out of bounds: must be [0,3]", i));
464 }
465 top_[i] = std::move(tile);
466 }
467
468 private:
480 template <typename TransparencyPredicate>
481 [[nodiscard]] bool layer_has_content(
483 TransparencyPredicate is_transparent_pred) const
484 {
485 return std::ranges::any_of(layer, [&is_transparent_pred](const auto &tile) {
486 return std::ranges::any_of(
487 tile.pix(), [&is_transparent_pred](const auto &pixel) { return !is_transparent_pred(pixel); });
488 });
489 }
490
501 template <typename TransparencyPredicate>
502 [[nodiscard]] LayerMode infer_layer_mode_impl(TransparencyPredicate is_transparent_pred) const
503 {
504 const bool bottom_has_content = layer_has_content(bottom_, is_transparent_pred);
505 const bool middle_has_content = layer_has_content(middle_, is_transparent_pred);
506 const bool top_has_content = layer_has_content(top_, is_transparent_pred);
507
508 if (bottom_has_content && middle_has_content && top_has_content) {
509 return LayerMode::triple;
510 }
511 return LayerMode::dual;
512 }
513
524 template <typename TransparencyPredicate>
525 [[nodiscard]] LayerType infer_layer_type_impl(TransparencyPredicate is_transparent_pred) const
526 {
527 // If triple-layer mode, always return normal
528 const LayerMode mode = infer_layer_mode_impl(is_transparent_pred);
529 if (mode == LayerMode::triple) {
530 return LayerType::normal;
531 }
532
533 // For dual-layer mode, determine which layers have content
534 const bool bottom_has_content = layer_has_content(bottom_, is_transparent_pred);
535 const bool middle_has_content = layer_has_content(middle_, is_transparent_pred);
536 const bool top_has_content = layer_has_content(top_, is_transparent_pred);
537
538 // Apply the case logic
539 if (bottom_has_content && !middle_has_content && top_has_content) {
540 // Case 6: bottom/top content -> split
541 return LayerType::split;
542 }
543 if (bottom_has_content && (middle_has_content || !top_has_content)) {
544 // Case 1: bottom only -> covered
545 // Case 5: bottom/middle content -> covered
546 return LayerType::covered;
547 }
548 // All other cases (including no content) -> normal
549 // Case 0: completely transparent -> normal
550 // Case 2: middle only -> normal
551 // Case 3: top only -> normal
552 // Case 4: middle/top content -> normal
553 return LayerType::normal;
554 }
555
556 std::array<PixelTile<PixelType>, metatile::tiles_per_metatile_layer> bottom_;
557 std::array<PixelTile<PixelType>, metatile::tiles_per_metatile_layer> middle_;
558 std::array<PixelTile<PixelType>, metatile::tiles_per_metatile_layer> top_;
559 std::size_t id_;
560};
561
562namespace metatile {
563
564template <typename T>
565[[nodiscard]] std::vector<PixelTile<T>> decompose(const std::vector<Metatile<T>> &metatiles)
566{
567 std::vector<PixelTile<T>> tiles;
568 tiles.reserve(metatiles.size() * tiles_per_metatile);
569 for (const auto &mt : metatiles) {
570 tiles.append_range(mt.decompose());
571 }
572 return tiles;
573
574 // This is a less performant version that uses std::ranges
575 //
576 // auto tiles = metatiles
577 // | std::views::transform([](const auto& mt) { return mt.decompose(); })
578 // | std::views::join
579 // | std::ranges::to<std::vector>();
580 //
581 // This transforms each Metatile to a vector<Tile>, flattens (joins) all the vectors together, and collects into a
582 // final vector<Tile>.
583}
584
585} // namespace metatile
586
587} // namespace porytiles
A text parameter with associated styling for formatted output.
The core tileset entity - a 2x2 grid of PixelTile objects arranged into three layers.
Definition metatile.hpp:186
const std::array< PixelTile< PixelType >, metatile::tiles_per_metatile_layer > & top() const
Get a constant reference to the entire top layer array.
Definition metatile.hpp:446
bool operator==(const Metatile &) const =default
LayerType infer_layer_type() const
Infers the layer type based on which layers contain content (intrinsic transparency only).
Definition metatile.hpp:300
LayerMode infer_layer_mode() const
Infers the layer mode (dual or triple) based on metatile content (intrinsic transparency only).
Definition metatile.hpp:264
void set_bottom(std::size_t i, PixelTile< PixelType > tile)
Set a PixelTile in the bottom layer.
Definition metatile.hpp:366
void set_top(std::size_t i, PixelTile< PixelType > tile)
Set a Tile in the top layer.
Definition metatile.hpp:460
const PixelTile< PixelType > & top(std::size_t i) const
Get a constant reference to a PixelTile from the top layer.
Definition metatile.hpp:430
std::array< PixelTile< PixelType >, metatile::tiles_per_metatile > decompose() const
Decomposes this metatile into an array of PixelTiles in metatile order.
Definition metatile.hpp:243
const std::array< PixelTile< PixelType >, metatile::tiles_per_metatile_layer > & middle() const
Get a constant reference to the entire middle layer array.
Definition metatile.hpp:399
bool is_transparent(const PixelType &extrinsic) const
Checks if this entire metatile is transparent.
Definition metatile.hpp:223
LayerMode infer_layer_mode(const PixelType &extrinsic) const
Infers the layer mode (dual or triple) based on metatile content.
Definition metatile.hpp:280
const std::array< PixelTile< PixelType >, metatile::tiles_per_metatile_layer > & bottom() const
Get a constant reference to the entire bottom layer array.
Definition metatile.hpp:352
LayerType infer_layer_type(const PixelType &extrinsic) const
Infers the layer type based on which layers contain content.
Definition metatile.hpp:321
const PixelTile< PixelType > & bottom(std::size_t i) const
Get a constant reference to a PixelTile from the bottom layer.
Definition metatile.hpp:336
const PixelTile< PixelType > & middle(std::size_t i) const
Get a constant reference to a PixelTile from the middle layer.
Definition metatile.hpp:383
bool is_transparent() const
Checks if this entire metatile is transparent (intrinsic transparency only).
Definition metatile.hpp:201
void set_middle(std::size_t i, PixelTile< PixelType > tile)
Set a PixelTile in the middle layer.
Definition metatile.hpp:413
An 8x8 tile backed by literal-array-based per-pixel storage of an arbitrary pixel type.
Abstract base class for applying text styling with context-aware formatting.
virtual std::string format(const std::string &format_str, const std::vector< FormatParam > &params) const
Formats a string with styled parameters using fmtlib syntax.
constexpr std::size_t entries_per_metatile_dual
Definition metatile.hpp:20
constexpr std::size_t entries_per_metatile_triple
Definition metatile.hpp:21
constexpr std::size_t tiles_per_metatile
Definition metatile.hpp:18
std::string to_string(Layer layer)
Definition metatile.hpp:26
std::ostream & operator<<(std::ostream &os, const Layer &layer)
Definition metatile.hpp:39
constexpr std::size_t side_length_pix
Definition metatile.hpp:19
constexpr std::size_t tiles_per_side
Definition metatile.hpp:16
std::tuple< std::size_t, Layer, Subtile > from_tile_index(std::size_t tile_index)
Decomposes a global tile index into its metatile index, layer, and subtile position.
Definition metatile.hpp:90
constexpr std::size_t metatiles_per_row
Definition metatile.hpp:22
std::tuple< Layer, Subtile > from_internal_tile_index(std::size_t tile_index)
Decomposes an internal tile index into its layer and subtile position within a metatile.
Definition metatile.hpp:117
constexpr std::size_t tiles_per_metatile_layer
Definition metatile.hpp:17
std::string message_header(const TextFormatter &format, std::size_t index, Layer layer, Subtile subtile, std::size_t subtile_row, std::size_t subtile_col)
Definition metatile.hpp:130
Subtile subtile_from_index(std::size_t i)
Definition metatile.hpp:47
constexpr std::size_t side_length_pix
LayerMode
Specifies whether a metatile uses dual-layer or triple-layer mode.
Definition layer.hpp:21
void panic(const StringViewSourceLoc &s)
Unconditionally terminates the program with a panic message.
Definition panic.cpp:43
std::string int_to_hex_str(T t)
Converts an integer value to a hexadecimal string with "0x" prefix.
LayerType
Specifies which layers of a metatile are used for rendering.
Definition layer.hpp:97
Utility functions for string manipulation and formatting.