From a5c91036569969bb887eb49b2faf9a26674b4128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Tue, 2 Feb 2016 14:17:30 +0100 Subject: [PATCH] [core] compute the actual clipping masks that we have to draw with getStencils() --- src/mbgl/map/tile_id.hpp | 4 +- src/mbgl/util/clip_id.cpp | 111 ++++++++++++++++----- src/mbgl/util/clip_id.hpp | 20 ++-- test/util/clip_ids.cpp | 200 ++++++++++++++++++++++++++++++++++---- 4 files changed, 279 insertions(+), 56 deletions(-) diff --git a/src/mbgl/map/tile_id.hpp b/src/mbgl/map/tile_id.hpp index dddbce3bb..3ecc5c6ce 100644 --- a/src/mbgl/map/tile_id.hpp +++ b/src/mbgl/map/tile_id.hpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace mbgl { @@ -48,7 +49,8 @@ public: TileID parent(int8_t z, int8_t sourceMaxZoom) const; TileID normalized() const; - std::forward_list children(int8_t sourceMaxZoom) const; + std::forward_list + children(int8_t sourceMaxZoom = std::numeric_limits::max()) const; bool isChildOf(const TileID&) const; operator std::string() const; diff --git a/src/mbgl/util/clip_id.cpp b/src/mbgl/util/clip_id.cpp index b89315d97..8274be338 100644 --- a/src/mbgl/util/clip_id.cpp +++ b/src/mbgl/util/clip_id.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -10,13 +11,14 @@ #include #include #include +#include namespace mbgl { -ClipIDGenerator::Leaf::Leaf(Tile &tile_) : tile(tile_) {} +ClipIDGenerator::Leaf::Leaf(TileID id_, ClipID& clip_) : id(id_), clip(clip_) {} void ClipIDGenerator::Leaf::add(const TileID &p) { - if (p.isChildOf(tile.id)) { + if (p.isChildOf(id)) { // Ensure that no already present child is a parent of the new p. for (const auto& child : children) { if (p.isChildOf(child)) @@ -27,27 +29,16 @@ void ClipIDGenerator::Leaf::add(const TileID &p) { } bool ClipIDGenerator::Leaf::operator==(const Leaf &other) const { - return tile.id == other.tile.id && children == other.children; -} - -bool ClipIDGenerator::reuseExisting(Leaf &leaf) { - for (const auto& pool : pools) { - auto existing = std::find(pool.begin(), pool.end(), leaf); - if (existing != pool.end()) { - leaf.tile.clip = existing->tile.clip; - return true; - } - } - return false; + return id == other.id && children == other.children; } void ClipIDGenerator::update(std::forward_list tiles) { - Pool pool; - tiles.sort([](const Tile *a, const Tile *b) { return a->id < b->id; }); + std::size_t size = 0; + const auto end = tiles.end(); for (auto it = tiles.begin(); it != end; it++) { if (!*it) { @@ -56,37 +47,49 @@ void ClipIDGenerator::update(std::forward_list tiles) { } Tile &tile = **it; - Leaf clip { tile }; + // Use the actual zoom level for computing the clipping mask. + Leaf leaf{ TileID{ tile.id.sourceZ, tile.id.x, tile.id.y, tile.id.sourceZ }, tile.clip }; // Try to add all remaining ids as children. We sorted the tile list // by z earlier, so all preceding items cannot be children of the current // tile. for (auto child_it = std::next(it); child_it != end; child_it++) { - clip.add((*child_it)->id); + // Use the actual zoom level for computing the clipping mask. + const auto& childID = (*child_it)->id; + leaf.add(TileID { childID.sourceZ, childID.x, childID.y, childID.sourceZ }); } - clip.children.sort(); + leaf.children.sort(); // Loop through all existing pools and try to find a matching ClipID. - if (!reuseExisting(clip)) { + auto existing = std::find(pool.begin(), pool.end(), leaf); + if (existing != pool.end()) { + leaf.clip = existing->clip; + } else { // We haven't found an existing clip ID - pool.push_back(std::move(clip)); + leaf.clip = {}; + size++; } + + pool.emplace_back(std::move(leaf)); } - if (!pool.empty()) { - const uint32_t bit_count = util::ceil_log2(pool.size() + 1); + if (size > 0) { + const uint32_t bit_count = util::ceil_log2(size + 1); const std::bitset<8> mask = uint64_t(((1ul << bit_count) - 1) << bit_offset); // We are starting our count with 1 since we need at least 1 bit set to distinguish between // areas without any tiles whatsoever and the current area. uint8_t count = 1; - for (auto& leaf : pool) { - leaf.tile.clip.mask = mask; - leaf.tile.clip.reference = uint32_t(count++) << bit_offset; + for (auto& tile : tiles) { + tile->clip.mask |= mask; + + // Assign only to clip IDs that have no value yet. + if (tile->clip.reference.none()) { + tile->clip.reference = uint32_t(count++) << bit_offset; + } } bit_offset += bit_count; - pools.push_front(std::move(pool)); } if (bit_offset > 8) { @@ -94,4 +97,58 @@ void ClipIDGenerator::update(std::forward_list tiles) { } } +template +bool coveredByChildren(const TileID& id, const Container& container) { + for (const auto& child : id.children()) { + const auto lower = container.lower_bound(child); + if (lower == container.end() || + (lower->first != child && !coveredByChildren(child, container))) { + return false; + } + } + + // We looked at all four immediate children and verified that they're covered. + return true; +} + +std::map ClipIDGenerator::getStencils() const { + std::map stencils; + + // Merge everything. + for (auto& leaf : pool) { + auto res = stencils.emplace(leaf.id, leaf.clip); + if (!res.second) { + // Merge with the existing ClipID when there was already an element with the + // same tile ID. + res.first->second |= leaf.clip; + } + } + + for (auto it = stencils.begin(); it != stencils.end(); ++it) { + auto& childId = it->first; + auto& childClip = it->second; + + // Loop through all preceding stencils, and find all parents. + + for (auto parentIt = std::reverse_iterator::iterator>(it); + parentIt != stencils.rend(); ++parentIt) { + auto& parentId = parentIt->first; + if (childId.isChildOf(parentId)) { + // Once we have a parent, we add the bits that this ID hasn't set yet. + const auto& parentClip = parentIt->second; + const auto mask = ~(childClip.mask & parentClip.mask); + childClip.reference |= mask & parentClip.reference; + childClip.mask |= parentClip.mask; + } + } + } + + // Remove tiles that are entirely covered by children. + util::erase_if(stencils, [&] (const auto& stencil) { + return coveredByChildren(stencil.first, stencils); + }); + + return stencils; +} + } // namespace mbgl diff --git a/src/mbgl/util/clip_id.hpp b/src/mbgl/util/clip_id.hpp index 71a07708c..2c128d264 100644 --- a/src/mbgl/util/clip_id.hpp +++ b/src/mbgl/util/clip_id.hpp @@ -14,7 +14,6 @@ namespace mbgl { class Tile; -class TileID; struct ClipID { inline ClipID() {} @@ -26,28 +25,33 @@ struct ClipID { inline bool operator==(const ClipID &other) const { return mask == other.mask && reference == other.reference; } + + inline ClipID& operator|=(const ClipID &other) { + mask |= other.mask; + reference |= other.reference; + return *this; + } }; class ClipIDGenerator { private: struct Leaf { - Leaf(Tile &tile); + Leaf(TileID, ClipID&); void add(const TileID &p); bool operator==(const Leaf &other) const; - Tile &tile; + const TileID id; std::forward_list children; + ClipID& clip; }; - typedef std::vector Pool; - std::forward_list pools; uint8_t bit_offset = 0; - -private: - bool reuseExisting(Leaf &leaf); + std::vector pool; public: void update(std::forward_list tiles); + + std::map getStencils() const; }; diff --git a/test/util/clip_ids.cpp b/test/util/clip_ids.cpp index 4ecd1797e..f61e7b1e2 100644 --- a/test/util/clip_ids.cpp +++ b/test/util/clip_ids.cpp @@ -8,9 +8,9 @@ using namespace mbgl; -template void generate(const T &sources) { - ClipIDGenerator generator; +using Stencil = std::pair; +template void generate(ClipIDGenerator& generator, const T &sources) { for (size_t j = 0; j < sources.size(); j++) { std::forward_list tile_ptrs; std::transform(sources[j].begin(), sources[j].end(), std::front_inserter(tile_ptrs), [](const std::shared_ptr &tile) { return tile.get(); }); @@ -18,7 +18,7 @@ template void generate(const T &sources) { } } -template void print(const T &sources) { +void print(const std::vector>> &sources) { for (size_t j = 0; j < sources.size(); j++) { for (size_t i = 0; i < sources[j].size(); i++) { std::cout << " ASSERT_EQ(ClipID(\"" << sources[j][i]->clip.mask << "\", \"" << sources[j][i]->clip.reference << "\"), sources[" << j << "][" << i << "]->clip);\n"; @@ -26,6 +26,18 @@ template void print(const T &sources) { } } +void print(const std::map& stencils) { + std::cout << " auto it = stencils.begin();\n"; + std::cout << " ASSERT_EQ(" << stencils.size() << ", stencils.size());\n"; + for (auto& stencil : stencils) { + std::cout << " ASSERT_EQ(Stencil(TileID{ " << (int)stencil.first.z << ", " + << stencil.first.x << ", " << stencil.first.y << ", " + << (int)stencil.first.sourceZ << " }, { \"" << stencil.second.mask.to_string() + << "\", \"" << stencil.second.reference.to_string() << "\"}), *it++);\n"; + } + std::cout << " ASSERT_EQ(stencils.end(), it);\n"; +} + TEST(ClipIDs, ParentAndFourChildren) { const std::vector>> sources = { { @@ -37,7 +49,8 @@ TEST(ClipIDs, ParentAndFourChildren) { }, }; - generate(sources); + ClipIDGenerator generator; + generate(generator, sources); // print(sources); ASSERT_EQ(ClipID("00000111", "00000010"), sources[0][0]->clip); @@ -45,6 +58,18 @@ TEST(ClipIDs, ParentAndFourChildren) { ASSERT_EQ(ClipID("00000111", "00000100"), sources[0][2]->clip); ASSERT_EQ(ClipID("00000111", "00000101"), sources[0][3]->clip); ASSERT_EQ(ClipID("00000111", "00000001"), sources[0][4]->clip); + + const auto stencils = generator.getStencils(); + // print(stencils); + + auto it = stencils.begin(); + ASSERT_EQ(4, stencils.size()); + ASSERT_EQ(Stencil(TileID{ 1, 0, 0, 1 }, { "00000111", "00000010"}), *it++); + ASSERT_EQ(Stencil(TileID{ 1, 0, 1, 1 }, { "00000111", "00000011"}), *it++); + ASSERT_EQ(Stencil(TileID{ 1, 1, 0, 1 }, { "00000111", "00000100"}), *it++); + ASSERT_EQ(Stencil(TileID{ 1, 1, 1, 1 }, { "00000111", "00000101"}), *it++); + ASSERT_EQ(stencils.end(), it); + } TEST(ClipIDs, ParentAndFourChildrenNegative) { @@ -58,7 +83,8 @@ TEST(ClipIDs, ParentAndFourChildrenNegative) { }, }; - generate(sources); + ClipIDGenerator generator; + generate(generator, sources); // print(sources); ASSERT_EQ(ClipID("00000111", "00000010"), sources[0][0]->clip); @@ -66,6 +92,17 @@ TEST(ClipIDs, ParentAndFourChildrenNegative) { ASSERT_EQ(ClipID("00000111", "00000100"), sources[0][2]->clip); ASSERT_EQ(ClipID("00000111", "00000101"), sources[0][3]->clip); ASSERT_EQ(ClipID("00000111", "00000001"), sources[0][4]->clip); + + const auto stencils = generator.getStencils(); + // print(stencils); + + auto it = stencils.begin(); + ASSERT_EQ(4, stencils.size()); + ASSERT_EQ(Stencil(TileID{ 1, -2, 0, 1 }, { "00000111", "00000010"}), *it++); + ASSERT_EQ(Stencil(TileID{ 1, -2, 1, 1 }, { "00000111", "00000011"}), *it++); + ASSERT_EQ(Stencil(TileID{ 1, -1, 0, 1 }, { "00000111", "00000100"}), *it++); + ASSERT_EQ(Stencil(TileID{ 1, -1, 1, 1 }, { "00000111", "00000101"}), *it++); + ASSERT_EQ(stencils.end(), it); } TEST(ClipIDs, NegativeParentAndMissingLevel) { @@ -79,7 +116,8 @@ TEST(ClipIDs, NegativeParentAndMissingLevel) { }, }; - generate(sources); + ClipIDGenerator generator; + generate(generator, sources); // print(sources); ASSERT_EQ(ClipID("00000111", "00000001"), sources[0][0]->clip); @@ -87,6 +125,17 @@ TEST(ClipIDs, NegativeParentAndMissingLevel) { ASSERT_EQ(ClipID("00000111", "00000011"), sources[0][2]->clip); ASSERT_EQ(ClipID("00000111", "00000101"), sources[0][3]->clip); ASSERT_EQ(ClipID("00000111", "00000010"), sources[0][4]->clip); + + const auto stencils = generator.getStencils(); + // print(stencils); + + auto it = stencils.begin(); + ASSERT_EQ(4, stencils.size()); + ASSERT_EQ(Stencil(TileID{ 2, -2, 0, 2 }, { "00000111", "00000010"}), *it++); + ASSERT_EQ(Stencil(TileID{ 2, -2, 1, 2 }, { "00000111", "00000011"}), *it++); + ASSERT_EQ(Stencil(TileID{ 2, -1, 0, 2 }, { "00000111", "00000100"}), *it++); + ASSERT_EQ(Stencil(TileID{ 2, -1, 1, 2 }, { "00000111", "00000101"}), *it++); + ASSERT_EQ(stencils.end(), it); } @@ -103,7 +152,8 @@ TEST(ClipIDs, SevenOnSameLevel) { }, }; - generate(sources); + ClipIDGenerator generator; + generate(generator, sources); // print(sources); ASSERT_EQ(ClipID("00000111", "00000001"), sources[0][0]->clip); @@ -113,6 +163,20 @@ TEST(ClipIDs, SevenOnSameLevel) { ASSERT_EQ(ClipID("00000111", "00000101"), sources[0][4]->clip); ASSERT_EQ(ClipID("00000111", "00000110"), sources[0][5]->clip); ASSERT_EQ(ClipID("00000111", "00000111"), sources[0][6]->clip); + + const auto stencils = generator.getStencils(); + // print(stencils); + + auto it = stencils.begin(); + ASSERT_EQ(7, stencils.size()); + ASSERT_EQ(Stencil(TileID{ 2, 0, 0, 2 }, { "00000111", "00000001"}), *it++); + ASSERT_EQ(Stencil(TileID{ 2, 0, 1, 2 }, { "00000111", "00000010"}), *it++); + ASSERT_EQ(Stencil(TileID{ 2, 0, 2, 2 }, { "00000111", "00000011"}), *it++); + ASSERT_EQ(Stencil(TileID{ 2, 1, 0, 2 }, { "00000111", "00000100"}), *it++); + ASSERT_EQ(Stencil(TileID{ 2, 1, 1, 2 }, { "00000111", "00000101"}), *it++); + ASSERT_EQ(Stencil(TileID{ 2, 1, 2, 2 }, { "00000111", "00000110"}), *it++); + ASSERT_EQ(Stencil(TileID{ 2, 2, 0, 2 }, { "00000111", "00000111"}), *it++); + ASSERT_EQ(stencils.end(), it); } TEST(ClipIDs, MultipleLevels) { @@ -133,7 +197,8 @@ TEST(ClipIDs, MultipleLevels) { }, }; - generate(sources); + ClipIDGenerator generator; + generate(generator, sources); // print(sources); ASSERT_EQ(ClipID("00001111", "00000001"), sources[0][0]->clip); @@ -148,6 +213,23 @@ TEST(ClipIDs, MultipleLevels) { ASSERT_EQ(ClipID("00001111", "00000010"), sources[0][9]->clip); ASSERT_EQ(ClipID("00001111", "00000111"), sources[0][10]->clip); ASSERT_EQ(ClipID("00001111", "00001000"), sources[0][11]->clip); + + const auto stencils = generator.getStencils(); + // print(stencils); + + auto it = stencils.begin(); + ASSERT_EQ(10, stencils.size()); + ASSERT_EQ(Stencil(TileID{ 2, 1, 0, 2 }, { "00001111", "00000010"}), *it++); + ASSERT_EQ(Stencil(TileID{ 3, 0, 0, 3 }, { "00001111", "00000011"}), *it++); + ASSERT_EQ(Stencil(TileID{ 3, 1, 0, 3 }, { "00001111", "00000101"}), *it++); + ASSERT_EQ(Stencil(TileID{ 3, 1, 1, 3 }, { "00001111", "00000110"}), *it++); + ASSERT_EQ(Stencil(TileID{ 3, 2, 0, 3 }, { "00001111", "00000111"}), *it++); + ASSERT_EQ(Stencil(TileID{ 3, 2, 1, 3 }, { "00001111", "00001000"}), *it++); + ASSERT_EQ(Stencil(TileID{ 4, 0, 2, 4 }, { "00001111", "00001001"}), *it++); + ASSERT_EQ(Stencil(TileID{ 4, 0, 3, 4 }, { "00001111", "00001010"}), *it++); + ASSERT_EQ(Stencil(TileID{ 4, 1, 2, 4 }, { "00001111", "00001011"}), *it++); + ASSERT_EQ(Stencil(TileID{ 4, 1, 3, 4 }, { "00001111", "00001100"}), *it++); + ASSERT_EQ(stencils.end(), it); } @@ -157,18 +239,19 @@ TEST(ClipIDs, Bug206) { std::make_shared(TileID { 10, 162, 395, 10 }), std::make_shared(TileID { 10, 162, 396, 10 }), std::make_shared(TileID { 10, 163, 395, 10 }), - std::make_shared(TileID { 11, 326, 791, 10 }), - std::make_shared(TileID { 12, 654, 1582, 10 }), - std::make_shared(TileID { 12, 654, 1583, 10 }), - std::make_shared(TileID { 12, 655, 1582, 10 }), - std::make_shared(TileID { 12, 655, 1583, 10 }), + std::make_shared(TileID { 11, 326, 791, 11 }), + std::make_shared(TileID { 12, 654, 1582, 12 }), + std::make_shared(TileID { 12, 654, 1583, 12 }), + std::make_shared(TileID { 12, 655, 1582, 12 }), + std::make_shared(TileID { 12, 655, 1583, 12 }), std::make_shared(TileID { 10, 163, 396, 10 }), std::make_shared(TileID { 10, 164, 395, 10 }), std::make_shared(TileID { 10, 164, 396, 10 }), }, }; - generate(sources); + ClipIDGenerator generator; + generate(generator, sources); // print(sources); ASSERT_EQ(ClipID("00001111", "00000001"), sources[0][0]->clip); @@ -182,6 +265,24 @@ TEST(ClipIDs, Bug206) { ASSERT_EQ(ClipID("00001111", "00000100"), sources[0][8]->clip); ASSERT_EQ(ClipID("00001111", "00000101"), sources[0][9]->clip); ASSERT_EQ(ClipID("00001111", "00000110"), sources[0][10]->clip); + + const auto stencils = generator.getStencils(); + // print(stencils); + + auto it = stencils.begin(); + ASSERT_EQ(11, stencils.size()); + ASSERT_EQ(Stencil(TileID{ 10, 162, 395, 10 }, { "00001111", "00000001"}), *it++); + ASSERT_EQ(Stencil(TileID{ 10, 162, 396, 10 }, { "00001111", "00000010"}), *it++); + ASSERT_EQ(Stencil(TileID{ 10, 163, 395, 10 }, { "00001111", "00000011"}), *it++); + ASSERT_EQ(Stencil(TileID{ 10, 163, 396, 10 }, { "00001111", "00000100"}), *it++); + ASSERT_EQ(Stencil(TileID{ 10, 164, 395, 10 }, { "00001111", "00000101"}), *it++); + ASSERT_EQ(Stencil(TileID{ 10, 164, 396, 10 }, { "00001111", "00000110"}), *it++); + ASSERT_EQ(Stencil(TileID{ 11, 326, 791, 11 }, { "00001111", "00000111"}), *it++); + ASSERT_EQ(Stencil(TileID{ 12, 654, 1582, 12 }, { "00001111", "00001000"}), *it++); + ASSERT_EQ(Stencil(TileID{ 12, 654, 1583, 12 }, { "00001111", "00001001"}), *it++); + ASSERT_EQ(Stencil(TileID{ 12, 655, 1582, 12 }, { "00001111", "00001010"}), *it++); + ASSERT_EQ(Stencil(TileID{ 12, 655, 1583, 12 }, { "00001111", "00001011"}), *it++); + ASSERT_EQ(stencils.end(), it); } @@ -208,27 +309,44 @@ TEST(ClipIDs, MultipleSources) { }, }; - generate(sources); + ClipIDGenerator generator; + generate(generator, sources); // print(sources); ASSERT_EQ(ClipID("00000111", "00000001"), sources[0][0]->clip); ASSERT_EQ(ClipID("00000111", "00000010"), sources[0][1]->clip); ASSERT_EQ(ClipID("00000111", "00000011"), sources[0][2]->clip); ASSERT_EQ(ClipID("00000111", "00000100"), sources[0][3]->clip); + ASSERT_EQ(ClipID("00011000", "00001000"), sources[1][0]->clip); - ASSERT_EQ(ClipID("00000111", "00000010"), sources[1][1]->clip); + ASSERT_EQ(ClipID("00011111", "00000010"), sources[1][1]->clip); ASSERT_EQ(ClipID("00011000", "00010000"), sources[1][2]->clip); - ASSERT_EQ(ClipID("00000111", "00000100"), sources[1][3]->clip); + ASSERT_EQ(ClipID("00011111", "00000100"), sources[1][3]->clip); + ASSERT_EQ(ClipID("11100000", "00100000"), sources[2][0]->clip); ASSERT_EQ(ClipID("11100000", "01000000"), sources[2][1]->clip); ASSERT_EQ(ClipID("11100000", "01100000"), sources[2][2]->clip); ASSERT_EQ(ClipID("11100000", "10000000"), sources[2][3]->clip); - ASSERT_EQ(ClipID("00011000", "00010000"), sources[2][4]->clip); + ASSERT_EQ(ClipID("11111000", "00010000"), sources[2][4]->clip); + + const auto stencils = generator.getStencils(); + // print(stencils); + + auto it = stencils.begin(); + ASSERT_EQ(7, stencils.size()); + ASSERT_EQ(Stencil(TileID{ 1, 0, 0, 1 }, { "11111111", "00101001"}), *it++); + ASSERT_EQ(Stencil(TileID{ 1, 0, 1, 1 }, { "11111111", "01001001"}), *it++); + ASSERT_EQ(Stencil(TileID{ 1, 1, 0, 1 }, { "11111111", "01101001"}), *it++); + ASSERT_EQ(Stencil(TileID{ 1, 1, 1, 1 }, { "11111111", "10000010"}), *it++); + ASSERT_EQ(Stencil(TileID{ 2, 1, 1, 2 }, { "11111111", "00010001"}), *it++); + ASSERT_EQ(Stencil(TileID{ 2, 2, 1, 2 }, { "11111111", "01101011"}), *it++); + ASSERT_EQ(Stencil(TileID{ 2, 2, 2, 2 }, { "11111111", "10000100"}), *it++); + ASSERT_EQ(stencils.end(), it); } TEST(ClipIDs, DuplicateIDs) { - const std::vector>> sources = { + const std::vector>> sources = { { std::make_shared(TileID { 2, 0, 0, 2 }), std::make_shared(TileID { 2, 0, 1, 2 }), @@ -240,7 +358,8 @@ TEST(ClipIDs, DuplicateIDs) { } }; - generate(sources); + ClipIDGenerator generator; + generate(generator, sources); // print(sources); ASSERT_EQ(ClipID("00000011", "00000001"), sources[0][0]->clip); @@ -248,4 +367,45 @@ TEST(ClipIDs, DuplicateIDs) { ASSERT_EQ(ClipID("00000011", "00000001"), sources[1][0]->clip); ASSERT_EQ(ClipID("00000011", "00000010"), sources[1][1]->clip); ASSERT_EQ(ClipID("00000011", "00000010"), sources[1][2]->clip); + + const auto stencils = generator.getStencils(); + // print(stencils); + + auto it = stencils.begin(); + ASSERT_EQ(2, stencils.size()); + ASSERT_EQ(Stencil(TileID{ 2, 0, 0, 2 }, { "00000011", "00000001"}), *it++); + ASSERT_EQ(Stencil(TileID{ 2, 0, 1, 2 }, { "00000011", "00000010"}), *it++); + ASSERT_EQ(stencils.end(), it); +} + +TEST(ClipIDs, SecondSourceHasParentOfFirstSource) { + const std::vector>> sources = { + { + std::make_shared(TileID { 1, 0, 0, 1 }), + }, + { + std::make_shared(TileID { 0, 0, 0, 0 }), + std::make_shared(TileID { 1, 0, 0, 1 }), + }, + { + std::make_shared(TileID { 0, 0, 0, 0 }), + } + }; + + ClipIDGenerator generator; + generate(generator, sources); + // print(sources); + + ASSERT_EQ(ClipID("00000001", "00000001"), sources[0][0]->clip); + ASSERT_EQ(ClipID("00000010", "00000010"), sources[1][0]->clip); + ASSERT_EQ(ClipID("00000011", "00000001"), sources[1][1]->clip); + + const auto stencils = generator.getStencils(); + // print(stencils); + + auto it = stencils.begin(); + ASSERT_EQ(2, stencils.size()); + ASSERT_EQ(Stencil(TileID{ 0, 0, 0, 0 }, { "00000110", "00000110"}), *it++); + ASSERT_EQ(Stencil(TileID{ 1, 0, 0, 1 }, { "00000111", "00000101"}), *it++); + ASSERT_EQ(stencils.end(), it); }