123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936 |
- // David Eberly, Geometric Tools, Redmond WA 98052
- // Copyright (c) 1998-2020
- // Distributed under the Boost Software License, Version 1.0.
- // https://www.boost.org/LICENSE_1_0.txt
- // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
- // Version: 4.0.2019.08.13
- #pragma once
- #include <Mathematics/Logger.h>
- #include <array>
- #include <cstdint>
- #include <map>
- #include <memory>
- #include <ostream>
- #include <vector>
- // Extract level surfaces using an adaptive approach to reduce the triangle
- // count. The implementation is for the algorithm described in the paper
- // Multiresolution Isosurface Extraction with Adaptive Skeleton Climbing
- // Tim Poston, Tien-Tsin Wong and Pheng-Ann Heng
- // Computer Graphics forum, volume 17, issue 3, September 1998
- // pages 137-147
- // https://onlinelibrary.wiley.com/doi/abs/10.1111/1467-8659.00261
- namespace WwiseGTE
- {
- // The image type T must be one of the integer types: int8_t, int16_t,
- // int32_t, uint8_t, uint16_t or uint32_t. Internal integer computations
- // are performed using int64_t. The type Real is for extraction to
- // floating-point vertices.
- template <typename T, typename Real>
- class AdaptiveSkeletonClimbing2
- {
- public:
- // Construction and destruction. The input image is assumed to
- // contain (2^N+1)-by-(2^N+1) elements where N >= 0. The organization
- // is row-major order for (x,y).
- AdaptiveSkeletonClimbing2(int N, T const* inputPixels)
- :
- mTwoPowerN(1 << N),
- mSize(mTwoPowerN + 1),
- mInputPixels(inputPixels),
- mXMerge(mSize),
- mYMerge(mSize)
- {
- static_assert(std::is_integral<T>::value && sizeof(T) <= 4,
- "Type T must be int{8,16,32}_t or uint{8,16,32}_t.");
- if (N <= 0 || mInputPixels == nullptr)
- {
- LogError("Invalid input.");
- }
- for (int i = 0; i < mSize; ++i)
- {
- mXMerge[i] = std::make_shared<LinearMergeTree>(N);
- mYMerge[i] = std::make_shared<LinearMergeTree>(N);
- }
- mXYMerge = std::make_unique<AreaMergeTree>(N, mXMerge, mYMerge);
- }
- // TODO: Refactor this class to have base class CurveExtractor.
- typedef std::array<Real, 2> Vertex;
- typedef std::array<int, 2> Edge;
- void Extract(Real level, int depth,
- std::vector<Vertex>& vertices, std::vector<Edge>& edges)
- {
- std::vector<Rectangle> rectangles;
- std::vector<Vertex> localVertices;
- std::vector<Edge> localEdges;
- SetLevel(level, depth);
- GetRectangles(rectangles);
- for (auto& rectangle : rectangles)
- {
- if (rectangle.type > 0)
- {
- GetComponents(level, rectangle, localVertices, localEdges);
- }
- }
- vertices = std::move(localVertices);
- edges = std::move(localEdges);
- }
- void MakeUnique(std::vector<Vertex>& vertices, std::vector<Edge>& edges)
- {
- size_t numVertices = vertices.size();
- size_t numEdges = edges.size();
- if (numVertices == 0 || numEdges == 0)
- {
- return;
- }
- // Compute the map of unique vertices and assign to them new and
- // unique indices.
- std::map<Vertex, int> vmap;
- int nextVertex = 0;
- for (size_t v = 0; v < numVertices; ++v)
- {
- // Keep only unique vertices.
- auto result = vmap.insert(std::make_pair(vertices[v], nextVertex));
- if (result.second)
- {
- ++nextVertex;
- }
- }
- // Compute the map of unique edges and assign to them new and
- // unique indices.
- std::map<Edge, int> emap;
- int nextEdge = 0;
- for (size_t e = 0; e < numEdges; ++e)
- {
- // Replace old vertex indices by new vertex indices.
- Edge& edge = edges[e];
- for (int i = 0; i < 2; ++i)
- {
- auto iter = vmap.find(vertices[edge[i]]);
- LogAssert(iter != vmap.end(), "Expecting the vertex to be in the vmap.");
- edge[i] = iter->second;
- }
- // Keep only unique edges.
- auto result = emap.insert(std::make_pair(edge, nextEdge));
- if (result.second)
- {
- ++nextEdge;
- }
- }
- // Pack the vertices into an array.
- vertices.resize(vmap.size());
- for (auto const& element : vmap)
- {
- vertices[element.second] = element.first;
- }
- // Pack the edges into an array.
- edges.resize(emap.size());
- for (auto const& element : emap)
- {
- edges[element.second] = element.first;
- }
- }
- private:
- // Helper classes for the skeleton climbing.
- struct QuadRectangle
- {
- QuadRectangle()
- :
- xOrigin(0),
- yOrigin(0),
- xStride(0),
- yStride(0),
- valid(false)
- {
- }
- QuadRectangle(int inXOrigin, int inYOrigin, int inXStride, int inYStride)
- {
- Initialize(inXOrigin, inYOrigin, inXStride, inYStride);
- }
- void Initialize(int inXOrigin, int inYOrigin, int inXStride, int inYStride)
- {
- xOrigin = inXOrigin;
- yOrigin = inYOrigin;
- xStride = inXStride;
- yStride = inYStride;
- valid = true;
- }
- int xOrigin, yOrigin, xStride, yStride;
- bool valid;
- };
- struct QuadNode
- {
- QuadNode()
- {
- // The members are uninitialized.
- }
- QuadNode(int xOrigin, int yOrigin, int xNext, int yNext, int stride)
- :
- r00(xOrigin, yOrigin, stride, stride),
- r10(xNext, yOrigin, stride, stride),
- r01(xOrigin, yNext, stride, stride),
- r11(xNext, yNext, stride, stride)
- {
- }
- void Initialize(int xOrigin, int yOrigin, int xNext, int yNext, int stride)
- {
- r00.Initialize(xOrigin, yOrigin, stride, stride);
- r10.Initialize(xNext, yOrigin, stride, stride);
- r01.Initialize(xOrigin, yNext, stride, stride);
- r11.Initialize(xNext, yNext, stride, stride);
- }
- bool IsMono() const
- {
- return !r10.valid && !r01.valid && !r11.valid;
- }
- int GetQuantity() const
- {
- int quantity = 0;
- if (r00.valid)
- {
- ++quantity;
- }
- if (r10.valid)
- {
- ++quantity;
- }
- if (r01.valid)
- {
- ++quantity;
- }
- if (r11.valid)
- {
- ++quantity;
- }
- return quantity;
- }
- QuadRectangle r00, r10, r01, r11;
- };
- class LinearMergeTree
- {
- public:
- LinearMergeTree(int N)
- :
- mTwoPowerN(1 << N),
- mNodes(2 * mTwoPowerN - 1)
- {
- }
- enum
- {
- CFG_NONE,
- CFG_INCR,
- CFG_DECR,
- CFG_MULT
- };
- // Member access.
- int GetQuantity() const
- {
- return 2 * mTwoPowerN - 1;
- }
- int GetNode(int i) const
- {
- return mNodes[i];
- }
- int GetEdge(int i) const
- {
- // assert: mNodes[i] == CFG_INCR || mNodes[i] == CFG_DECR
- // Traverse binary tree looking for incr or decr leaf node.
- int const firstLeaf = mTwoPowerN - 1;
- while (i < firstLeaf)
- {
- i = 2 * i + 1;
- if (mNodes[i] == CFG_NONE)
- {
- ++i;
- }
- }
- return i - firstLeaf;
- }
- void SetLevel(Real level, T const* data, int offset, int stride)
- {
- // Assert: The 'level' is not an image value. Because T is
- // an integer type, choose 'level' to be a Real-valued number
- // that does not represent an integer.
- // Determine the sign changes between pairs of consecutive
- // samples.
- int const firstLeaf = mTwoPowerN - 1;
- for (int i = 0, leaf = firstLeaf; i < mTwoPowerN; ++i, ++leaf)
- {
- int base = offset + stride * i;
- Real value0 = static_cast<Real>(data[base]);
- Real value1 = static_cast<Real>(data[base + stride]);
- if (value0 > level)
- {
- if (value1 > level)
- {
- mNodes[leaf] = CFG_NONE;
- }
- else
- {
- mNodes[leaf] = CFG_DECR;
- }
- }
- else // value0 < level
- {
- if (value1 > level)
- {
- mNodes[leaf] = CFG_INCR;
- }
- else
- {
- mNodes[leaf] = CFG_NONE;
- }
- }
- }
- // Propagate the sign change information up the binary tree.
- for (int i = firstLeaf - 1; i >= 0; --i)
- {
- int twoIp1 = 2 * i + 1;
- int child0 = mNodes[twoIp1];
- int child1 = mNodes[twoIp1 + 1];
- mNodes[i] = (child0 | child1);
- }
- }
- private:
- int mTwoPowerN;
- std::vector<int> mNodes;
- };
- struct Rectangle
- {
- Rectangle(int inXOrigin, int inYOrigin, int inXStride, int inYStride)
- :
- xOrigin(inXOrigin),
- yOrigin(inYOrigin),
- xStride(inXStride),
- yStride(inYStride),
- yOfXMin(-1),
- yOfXMax(-1),
- xOfYMin(-1),
- xOfYMax(-1),
- type(0)
- {
- }
- int xOrigin, yOrigin, xStride, yStride;
- int yOfXMin, yOfXMax, xOfYMin, xOfYMax;
- // A 4-bit flag for how the level set intersects the rectangle
- // boundary.
- // bit 0 = xmin edge
- // bit 1 = xmax edge
- // bit 2 = ymin edge
- // bit 3 = ymax edge
- // A bit is set if the corresponding edge is intersected by the
- // level set. This information is known from the CFG flags for
- // LinearMergeTree. Intersection occurs whenever the flag is
- // CFG_INCR or CFG_DECR.
- unsigned int type;
- };
- class AreaMergeTree
- {
- public:
- AreaMergeTree(int N,
- std::vector<std::shared_ptr<LinearMergeTree>> const& xMerge,
- std::vector<std::shared_ptr<LinearMergeTree>> const& yMerge)
- :
- mXMerge(xMerge),
- mYMerge(yMerge),
- mNodes(((1 << 2 * (N + 1)) - 1) / 3)
- {
- }
- void ConstructMono(int A, int LX, int LY, int xOrigin, int yOrigin,
- int stride, int depth)
- {
- if (stride > 1) // internal nodes
- {
- int hStride = stride / 2;
- int ABase = 4 * A;
- int A00 = ++ABase;
- int A10 = ++ABase;
- int A01 = ++ABase;
- int A11 = ++ABase;
- int LXBase = 2 * LX;
- int LX0 = ++LXBase;
- int LX1 = ++LXBase;
- int LYBase = 2 * LY;
- int LY0 = ++LYBase;
- int LY1 = ++LYBase;
- int xNext = xOrigin + hStride;
- int yNext = yOrigin + hStride;
- int depthM1 = depth - 1;
- ConstructMono(A00, LX0, LY0, xOrigin, yOrigin, hStride, depthM1);
- ConstructMono(A10, LX1, LY0, xNext, yOrigin, hStride, depthM1);
- ConstructMono(A01, LX0, LY1, xOrigin, yNext, hStride, depthM1);
- ConstructMono(A11, LX1, LY1, xNext, yNext, hStride, depthM1);
- if (depth >= 0)
- {
- // Merging is prevented above the specified depth in
- // the tree. This allows a single object to produce
- // any resolution isocontour rather than using
- // multiple objects to do so.
- mNodes[A].Initialize(xOrigin, yOrigin, xNext, yNext, hStride);
- return;
- }
- bool mono00 = mNodes[A00].IsMono();
- bool mono10 = mNodes[A10].IsMono();
- bool mono01 = mNodes[A01].IsMono();
- bool mono11 = mNodes[A11].IsMono();
- QuadNode node0(xOrigin, yOrigin, xNext, yNext, hStride);
- QuadNode node1 = node0;
- // Merge x first, y second.
- if (mono00 && mono10)
- {
- DoXMerge(node0.r00, node0.r10, LX, yOrigin);
- }
- if (mono01 && mono11)
- {
- DoXMerge(node0.r01, node0.r11, LX, yNext);
- }
- if (mono00 && mono01)
- {
- DoYMerge(node0.r00, node0.r01, xOrigin, LY);
- }
- if (mono10 && mono11)
- {
- DoYMerge(node0.r10, node0.r11, xNext, LY);
- }
- // Merge y first, x second.
- if (mono00 && mono01)
- {
- DoYMerge(node1.r00, node1.r01, xOrigin, LY);
- }
- if (mono10 && mono11)
- {
- DoYMerge(node1.r10, node1.r11, xNext, LY);
- }
- if (mono00 && mono10)
- {
- DoXMerge(node1.r00, node1.r10, LX, yOrigin);
- }
- if (mono01 && mono11)
- {
- DoXMerge(node1.r01, node1.r11, LX, yNext);
- }
- // Choose the merge that produced the smallest number of
- // rectangles.
- if (node0.GetQuantity() <= node1.GetQuantity())
- {
- mNodes[A] = node0;
- }
- else
- {
- mNodes[A] = node1;
- }
- }
- else // leaf nodes
- {
- mNodes[A].r00.Initialize(xOrigin, yOrigin, 1, 1);
- }
- }
- void GetRectangles(int A, int LX, int LY, int xOrigin, int yOrigin,
- int stride, std::vector<Rectangle>& rectangles)
- {
- int hStride = stride / 2;
- int ABase = 4 * A;
- int A00 = ++ABase;
- int A10 = ++ABase;
- int A01 = ++ABase;
- int A11 = ++ABase;
- int LXBase = 2 * LX;
- int LX0 = ++LXBase;
- int LX1 = ++LXBase;
- int LYBase = 2 * LY;
- int LY0 = ++LYBase;
- int LY1 = ++LYBase;
- int xNext = xOrigin + hStride;
- int yNext = yOrigin + hStride;
- QuadRectangle const& r00 = mNodes[A].r00;
- if (r00.valid)
- {
- if (r00.xStride == stride)
- {
- if (r00.yStride == stride)
- {
- rectangles.push_back(GetRectangle(r00, LX, LY));
- }
- else
- {
- rectangles.push_back(GetRectangle(r00, LX, LY0));
- }
- }
- else
- {
- if (r00.yStride == stride)
- {
- rectangles.push_back(GetRectangle(r00, LX0, LY));
- }
- else
- {
- GetRectangles(A00, LX0, LY0, xOrigin, yOrigin, hStride, rectangles);
- }
- }
- }
- QuadRectangle const& r10 = mNodes[A].r10;
- if (r10.valid)
- {
- if (r10.yStride == stride)
- {
- rectangles.push_back(GetRectangle(r10, LX1, LY));
- }
- else
- {
- GetRectangles(A10, LX1, LY0, xNext, yOrigin, hStride, rectangles);
- }
- }
- QuadRectangle const& r01 = mNodes[A].r01;
- if (r01.valid)
- {
- if (r01.xStride == stride)
- {
- rectangles.push_back(GetRectangle(r01, LX, LY1));
- }
- else
- {
- GetRectangles(A01, LX0, LY1, xOrigin, yNext, hStride, rectangles);
- }
- }
- QuadRectangle const& r11 = mNodes[A].r11;
- if (r11.valid)
- {
- GetRectangles(A11, LX1, LY1, xNext, yNext, hStride, rectangles);
- }
- }
- private:
- void DoXMerge(QuadRectangle& r0, QuadRectangle& r1, int LX, int yOrigin)
- {
- if (r0.valid && r1.valid && r0.yStride == r1.yStride)
- {
- // Rectangles are x-mergeable.
- int incr = 0, decr = 0;
- for (int y = 0; y <= r0.yStride; ++y)
- {
- switch (mXMerge[yOrigin + y]->GetNode(LX))
- {
- case LinearMergeTree::CFG_MULT:
- return;
- case LinearMergeTree::CFG_INCR:
- ++incr;
- break;
- case LinearMergeTree::CFG_DECR:
- ++decr;
- break;
- }
- }
- if (incr == 0 || decr == 0)
- {
- // Strongly mono, x-merge the rectangles.
- r0.xStride *= 2;
- r1.valid = false;
- }
- }
- }
- void DoYMerge(QuadRectangle& r0, QuadRectangle& r1, int xOrigin, int LY)
- {
- if (r0.valid && r1.valid && r0.xStride == r1.xStride)
- {
- // Rectangles are y-mergeable.
- int incr = 0, decr = 0;
- for (int x = 0; x <= r0.xStride; ++x)
- {
- switch (mYMerge[xOrigin + x]->GetNode(LY))
- {
- case LinearMergeTree::CFG_MULT:
- return;
- case LinearMergeTree::CFG_INCR:
- ++incr;
- break;
- case LinearMergeTree::CFG_DECR:
- ++decr;
- break;
- }
- }
- if (incr == 0 || decr == 0)
- {
- // Strongly mono, y-merge the rectangles.
- r0.yStride *= 2;
- r1.valid = false;
- }
- }
- }
- Rectangle GetRectangle(QuadRectangle const& qrect, int LX, int LY)
- {
- Rectangle rect(qrect.xOrigin, qrect.yOrigin, qrect.xStride, qrect.yStride);
- // xmin edge
- auto merge = mYMerge[qrect.xOrigin];
- if (merge->GetNode(LY) != LinearMergeTree::CFG_NONE)
- {
- rect.yOfXMin = merge->GetEdge(LY);
- if (rect.yOfXMin != -1)
- {
- rect.type |= 0x01;
- }
- }
- // xmax edge
- merge = mYMerge[qrect.xOrigin + qrect.xStride];
- if (merge->GetNode(LY) != LinearMergeTree::CFG_NONE)
- {
- rect.yOfXMax = merge->GetEdge(LY);
- if (rect.yOfXMax != -1)
- {
- rect.type |= 0x02;
- }
- }
- // ymin edge
- merge = mXMerge[qrect.yOrigin];
- if (merge->GetNode(LX) != LinearMergeTree::CFG_NONE)
- {
- rect.xOfYMin = merge->GetEdge(LX);
- if (rect.xOfYMin != -1)
- {
- rect.type |= 0x04;
- }
- }
- // ymax edge
- merge = mXMerge[qrect.yOrigin + qrect.yStride];
- if (merge->GetNode(LX) != LinearMergeTree::CFG_NONE)
- {
- rect.xOfYMax = merge->GetEdge(LX);
- if (rect.xOfYMax != -1)
- {
- rect.type |= 0x08;
- }
- }
- return rect;
- }
- std::vector<std::shared_ptr<LinearMergeTree>> mXMerge;
- std::vector<std::shared_ptr<LinearMergeTree>> mYMerge;
- std::vector<QuadNode> mNodes;
- };
- private:
- // Support for extraction of level sets.
- Real GetInterp(Real level, int base, int index, int increment)
- {
- Real f0 = static_cast<Real>(mInputPixels[index]);
- index += increment;
- Real f1 = static_cast<Real>(mInputPixels[index]);
- LogAssert((f0 - level) * (f1 - level) < (Real)0, "Unexpected condition.");
- return static_cast<Real>(base) + (level - f0) / (f1 - f0);
- }
- void AddVertex(std::vector<Vertex>& vertices, Real x, Real y)
- {
- Vertex vertex = { x, y };
- vertices.push_back(vertex);
- }
- void AddEdge(std::vector<Vertex>& vertices,
- std::vector<Edge>& edges, Real x0, Real y0, Real x1, Real y1)
- {
- int v0 = static_cast<int>(vertices.size());
- int v1 = v0 + 1;
- Edge edge = { v0, v1 };
- edges.push_back(edge);
- Vertex vertex0 = { x0, y0 };
- Vertex vertex1 = { x1, y1 };
- vertices.push_back(vertex0);
- vertices.push_back(vertex1);
- }
- void SetLevel(Real level, int depth)
- {
- int offset, stride;
- for (int y = 0; y < mSize; ++y)
- {
- offset = mSize * y;
- stride = 1;
- mXMerge[y]->SetLevel(level, mInputPixels, offset, stride);
- }
- for (int x = 0; x < mSize; ++x)
- {
- offset = x;
- stride = mSize;
- mYMerge[x]->SetLevel(level, mInputPixels, offset, stride);
- }
- mXYMerge->ConstructMono(0, 0, 0, 0, 0, mTwoPowerN, depth);
- }
- void GetRectangles(std::vector<Rectangle>& rectangles)
- {
- mXYMerge->GetRectangles(0, 0, 0, 0, 0, mTwoPowerN, rectangles);
- }
- void GetComponents(Real level, Rectangle const& rectangle,
- std::vector<Vertex>& vertices, std::vector<Edge>& edges)
- {
- int x, y;
- Real x0, y0, x1, y1;
- switch (rectangle.type)
- {
- case 3: // two vertices, on xmin and xmax
- LogAssert(rectangle.yOfXMin != -1, "Unexpected condition.");
- x = rectangle.xOrigin;
- y = rectangle.yOfXMin;
- x0 = static_cast<Real>(x);
- y0 = GetInterp(level, y, x + mSize * y, mSize);
- LogAssert(rectangle.yOfXMax != -1, "Unexpected condition.");
- x = rectangle.xOrigin + rectangle.xStride;
- y = rectangle.yOfXMax;
- x1 = static_cast<Real>(x);
- y1 = GetInterp(level, y, x + mSize * y, mSize);
- AddEdge(vertices, edges, x0, y0, x1, y1);
- break;
- case 5: // two vertices, on xmin and ymin
- LogAssert(rectangle.yOfXMin != -1, "Unexpected condition.");
- x = rectangle.xOrigin;
- y = rectangle.yOfXMin;
- x0 = static_cast<Real>(x);
- y0 = GetInterp(level, y, x + mSize * y, mSize);
- LogAssert(rectangle.xOfYMin != -1, "Unexpected condition.");
- x = rectangle.xOfYMin;
- y = rectangle.yOrigin;
- x1 = GetInterp(level, x, x + mSize * y, 1);
- y1 = static_cast<Real>(y);
- AddEdge(vertices, edges, x0, y0, x1, y1);
- break;
- case 6: // two vertices, on xmax and ymin
- LogAssert(rectangle.yOfXMax != -1, "Unexpected condition.");
- x = rectangle.xOrigin + rectangle.xStride;
- y = rectangle.yOfXMax;
- x0 = static_cast<Real>(x);
- y0 = GetInterp(level, y, x + mSize * y, mSize);
- LogAssert(rectangle.xOfYMin != -1, "Unexpected condition.");
- x = rectangle.xOfYMin;
- y = rectangle.yOrigin;
- x1 = GetInterp(level, x, x + mSize * y, 1);
- y1 = static_cast<Real>(y);
- AddEdge(vertices, edges, x0, y0, x1, y1);
- break;
- case 9: // two vertices, on xmin and ymax
- LogAssert(rectangle.yOfXMin != -1, "Unexpected condition.");
- x = rectangle.xOrigin;
- y = rectangle.yOfXMin;
- x0 = static_cast<Real>(x);
- y0 = GetInterp(level, y, x + mSize * y, mSize);
- LogAssert(rectangle.xOfYMax != -1, "Unexpected condition.");
- x = rectangle.xOfYMax;
- y = rectangle.yOrigin + rectangle.yStride;
- x1 = GetInterp(level, x, x + mSize * y, 1);
- y1 = static_cast<Real>(y);
- AddEdge(vertices, edges, x0, y0, x1, y1);
- break;
- case 10: // two vertices, on xmax and ymax
- LogAssert(rectangle.yOfXMax != -1, "Unexpected condition.");
- x = rectangle.xOrigin + rectangle.xStride;
- y = rectangle.yOfXMax;
- x0 = static_cast<Real>(x);
- y0 = GetInterp(level, y, x + mSize * y, mSize);
- LogAssert(rectangle.xOfYMax != -1, "Unexpected condition.");
- x = rectangle.xOfYMax;
- y = rectangle.yOrigin + rectangle.yStride;
- x1 = GetInterp(level, x, x + mSize * y, 1);
- y1 = static_cast<Real>(y);
- AddEdge(vertices, edges, x0, y0, x1, y1);
- break;
- case 12: // two vertices, on ymin and ymax
- LogAssert(rectangle.xOfYMin != -1, "Unexpected condition.");
- x = rectangle.xOfYMin;
- y = rectangle.yOrigin;
- x0 = GetInterp(level, x, x + mSize * y, 1);
- y0 = static_cast<Real>(y);
- LogAssert(rectangle.xOfYMax != -1, "Unexpected condition.");
- x = rectangle.xOfYMax;
- y = rectangle.yOrigin + rectangle.yStride;
- x1 = GetInterp(level, x, x + mSize * y, 1);
- y1 = static_cast<Real>(y);
- AddEdge(vertices, edges, x0, y0, x1, y1);
- break;
- case 15: // four vertices, one per edge, need to disambiguate
- {
- LogAssert(rectangle.xStride == 1 && rectangle.yStride == 1,
- "Unexpected condition.");
- LogAssert(rectangle.yOfXMin != -1, "Unexpected condition.");
- x = rectangle.xOrigin;
- y = rectangle.yOfXMin;
- x0 = static_cast<Real>(x);
- y0 = GetInterp(level, y, x + mSize * y, mSize);
- LogAssert(rectangle.yOfXMax != -1, "Unexpected condition.");
- x = rectangle.xOrigin + rectangle.xStride;
- y = rectangle.yOfXMax;
- x1 = static_cast<Real>(x);
- y1 = GetInterp(level, y, x + mSize * y, mSize);
- LogAssert(rectangle.xOfYMin != -1, "Unexpected condition.");
- x = rectangle.xOfYMin;
- y = rectangle.yOrigin;
- Real fx2 = GetInterp(level, x, x + mSize * y, 1);
- Real fy2 = static_cast<Real>(y);
- LogAssert(rectangle.xOfYMax != -1, "Unexpected condition.");
- x = rectangle.xOfYMax;
- y = rectangle.yOrigin + rectangle.yStride;
- Real fx3 = GetInterp(level, x, x + mSize * y, 1);
- Real fy3 = static_cast<Real>(y);
- int index = rectangle.xOrigin + mSize * rectangle.yOrigin;
- int64_t i00 = static_cast<int64_t>(mInputPixels[index]);
- ++index;
- int64_t i10 = static_cast<int64_t>(mInputPixels[index]);
- index += mSize;
- int64_t i11 = static_cast<int64_t>(mInputPixels[index]);
- --index;
- int64_t i01 = static_cast<int64_t>(mInputPixels[index]);
- int64_t det = i00 * i11 - i01 * i10;
- if (det > 0)
- {
- // Disjoint hyperbolic segments, pair <P0,P2> and <P1,P3>.
- AddEdge(vertices, edges, x0, y0, fx2, fy2);
- AddEdge(vertices, edges, x1, y1, fx3, fy3);
- }
- else if (det < 0)
- {
- // Disjoint hyperbolic segments, pair <P0,P3> and <P1,P2>.
- AddEdge(vertices, edges, x0, y0, fx3, fy3);
- AddEdge(vertices, edges, x1, y1, fx2, fy2);
- }
- else
- {
- // Plus-sign configuration, add branch point to
- // tessellation.
- Real fx4 = fx2, fy4 = y0;
- AddEdge(vertices, edges, x0, y0, fx4, fy4);
- AddEdge(vertices, edges, x1, y1, fx4, fy4);
- AddEdge(vertices, edges, fx2, fy2, fx4, fy4);
- AddEdge(vertices, edges, fx3, fy3, fx4, fy4);
- }
- break;
- }
- default:
- LogError("Unexpected condition.");
- }
- }
- // Support for debugging.
- void PrintRectangles(std::ostream& output, std::vector<Rectangle> const& rectangles)
- {
- for (size_t i = 0; i < rectangles.size(); ++i)
- {
- auto const& rectangle = rectangles[i];
- output << "rectangle " << i << std::endl;
- output << " x origin = " << rectangle.xOrigin << std::endl;
- output << " y origin = " << rectangle.yOrigin << std::endl;
- output << " x stride = " << rectangle.xStride << std::endl;
- output << " y stride = " << rectangle.yStride << std::endl;
- output << " flag = " << rectangle.type << std::endl;
- output << " y of xmin = " << rectangle.yOfXMin << std::endl;
- output << " y of xmax = " << rectangle.yOfXMax << std::endl;
- output << " x of ymin = " << rectangle.xOfYMin << std::endl;
- output << " x of ymax = " << rectangle.xOfYMax << std::endl;
- output << std::endl;
- }
- }
- // Storage of image data.
- int mTwoPowerN, mSize;
- T const* mInputPixels;
- // Trees for linear merging.
- std::vector<std::shared_ptr<LinearMergeTree>> mXMerge, mYMerge;
- // Tree for area merging.
- std::unique_ptr<AreaMergeTree> mXYMerge;
- };
- }
|