123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239 |
- // 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.2020.01.10
- #pragma once
- #include <Mathematics/Logger.h>
- #include <Mathematics/PrimalQuery2.h>
- #include <memory>
- #include <map>
- #include <queue>
- #include <vector>
- // Triangulate polygons using ear clipping. The algorithm is described in
- // https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
- // The algorithm for processing nested polygons involves a division, so the
- // ComputeType must be rational-based, say, BSRational. If you process only
- // triangles that are simple, you may use BSNumber for the ComputeType.
- namespace WwiseGTE
- {
- template <typename InputType, typename ComputeType>
- class TriangulateEC
- {
- public:
- // The class is a functor to support triangulating multiple polygons
- // that share vertices in a collection of points. The interpretation
- // of 'numPoints' and 'points' is described before each operator()
- // function. Preconditions are numPoints >= 3 and points is a nonnull
- // pointer to an array of at least numPoints elements. If the
- // preconditions are satisfied, then operator() functions will return
- // 'true'; otherwise, they return 'false'.
- TriangulateEC(int numPoints, Vector2<InputType> const* points)
- :
- mNumPoints(numPoints),
- mPoints(points)
- {
- LogAssert(numPoints >= 3 && points != nullptr, "Invalid input.");
- mComputePoints.resize(mNumPoints);
- mIsConverted.resize(mNumPoints);
- std::fill(mIsConverted.begin(), mIsConverted.end(), false);
- mQuery.Set(mNumPoints, &mComputePoints[0]);
- }
- TriangulateEC(std::vector<Vector2<InputType>> const& points)
- :
- mNumPoints(static_cast<int>(points.size())),
- mPoints(points.data())
- {
- LogAssert(mNumPoints >= 3 && mPoints != nullptr, "Invalid input.");
- mComputePoints.resize(mNumPoints);
- mIsConverted.resize(mNumPoints);
- std::fill(mIsConverted.begin(), mIsConverted.end(), false);
- mQuery.Set(mNumPoints, &mComputePoints[0]);
- }
- // Access the triangulation after each operator() call.
- inline std::vector<std::array<int, 3>> const& GetTriangles() const
- {
- return mTriangles;
- }
- // The outer polygons have counterclockwise ordered vertices. The
- // inner polygons have clockwise ordered vertices.
- typedef std::vector<int> Polygon;
- // The input 'points' represents an array of vertices for a simple
- // polygon. The vertices are points[0] through points[numPoints-1] and
- // are listed in counterclockwise order.
- bool operator()()
- {
- mTriangles.clear();
- if (mPoints)
- {
- // Compute the points for the queries.
- for (int i = 0; i < mNumPoints; ++i)
- {
- if (!mIsConverted[i])
- {
- mIsConverted[i] = true;
- for (int j = 0; j < 2; ++j)
- {
- mComputePoints[i][j] = mPoints[i][j];
- }
- }
- }
- // Triangulate the unindexed polygon.
- InitializeVertices(mNumPoints, nullptr);
- DoEarClipping(mNumPoints, nullptr);
- return true;
- }
- else
- {
- return false;
- }
- }
- // The input 'points' represents an array of vertices that contains
- // the vertices of a simple polygon.
- bool operator()(Polygon const& polygon)
- {
- mTriangles.clear();
- if (mPoints)
- {
- // Compute the points for the queries.
- int const numIndices = static_cast<int>(polygon.size());
- int const* indices = polygon.data();
- for (int i = 0; i < numIndices; ++i)
- {
- int index = indices[i];
- if (!mIsConverted[index])
- {
- mIsConverted[index] = true;
- for (int j = 0; j < 2; ++j)
- {
- mComputePoints[index][j] = mPoints[index][j];
- }
- }
- }
- // Triangulate the indexed polygon.
- InitializeVertices(numIndices, indices);
- DoEarClipping(numIndices, indices);
- return true;
- }
- else
- {
- return false;
- }
- }
- // The input 'points' is a shared array of vertices that contains the
- // vertices for two simple polygons, an outer polygon and an inner
- // polygon. The inner polygon must be strictly inside the outer
- // polygon.
- bool operator()(Polygon const& outer, Polygon const& inner)
- {
- mTriangles.clear();
- if (mPoints)
- {
- // Two extra elements are needed to duplicate the endpoints of
- // the edge introduced to combine outer and inner polygons.
- int numPointsPlusExtras = mNumPoints + 2;
- if (numPointsPlusExtras > static_cast<int>(mComputePoints.size()))
- {
- mComputePoints.resize(numPointsPlusExtras);
- mIsConverted.resize(numPointsPlusExtras);
- mIsConverted[mNumPoints] = false;
- mIsConverted[mNumPoints + 1] = false;
- mQuery.Set(numPointsPlusExtras, &mComputePoints[0]);
- }
- // Convert any points that have not been encountered in other
- // triangulation calls.
- int const numOuterIndices = static_cast<int>(outer.size());
- int const* outerIndices = outer.data();
- for (int i = 0; i < numOuterIndices; ++i)
- {
- int index = outerIndices[i];
- if (!mIsConverted[index])
- {
- mIsConverted[index] = true;
- for (int j = 0; j < 2; ++j)
- {
- mComputePoints[index][j] = mPoints[index][j];
- }
- }
- }
- int const numInnerIndices = static_cast<int>(inner.size());
- int const* innerIndices = inner.data();
- for (int i = 0; i < numInnerIndices; ++i)
- {
- int index = innerIndices[i];
- if (!mIsConverted[index])
- {
- mIsConverted[index] = true;
- for (int j = 0; j < 2; ++j)
- {
- mComputePoints[index][j] = mPoints[index][j];
- }
- }
- }
- // Combine the outer polygon and the inner polygon into a
- // simple polygon by inserting two edges connecting mutually
- // visible vertices, one from the outer polygon and one from
- // the inner polygon.
- int nextElement = mNumPoints; // The next available element.
- std::map<int, int> indexMap;
- std::vector<int> combined;
- if (!CombinePolygons(nextElement, outer, inner, indexMap, combined))
- {
- // An unexpected condition was encountered.
- return false;
- }
- // The combined polygon is now in the format of a simple
- // polygon, albeit one with coincident edges.
- int numVertices = static_cast<int>(combined.size());
- int* const indices = &combined[0];
- InitializeVertices(numVertices, indices);
- DoEarClipping(numVertices, indices);
- // Map the duplicate indices back to the original indices.
- RemapIndices(indexMap);
- return true;
- }
- else
- {
- return false;
- }
- }
- // The input 'points' is a shared array of vertices that contains the
- // vertices for multiple simple polygons, an outer polygon and one or
- // more inner polygons. The inner polygons must be nonoverlapping and
- // strictly inside the outer polygon.
- bool operator()(Polygon const& outer, std::vector<Polygon> const& inners)
- {
- mTriangles.clear();
- if (mPoints)
- {
- // Two extra elements per inner polygon are needed to
- // duplicate the endpoints of the edges introduced to combine
- // outer and inner polygons.
- int numPointsPlusExtras = mNumPoints + 2 * (int)inners.size();
- if (numPointsPlusExtras > static_cast<int>(mComputePoints.size()))
- {
- mComputePoints.resize(numPointsPlusExtras);
- mIsConverted.resize(numPointsPlusExtras);
- for (int i = mNumPoints; i < numPointsPlusExtras; ++i)
- {
- mIsConverted[i] = false;
- }
- mQuery.Set(numPointsPlusExtras, &mComputePoints[0]);
- }
- // Convert any points that have not been encountered in other
- // triangulation calls.
- int const numOuterIndices = static_cast<int>(outer.size());
- int const* outerIndices = outer.data();
- for (int i = 0; i < numOuterIndices; ++i)
- {
- int index = outerIndices[i];
- if (!mIsConverted[index])
- {
- mIsConverted[index] = true;
- for (int j = 0; j < 2; ++j)
- {
- mComputePoints[index][j] = mPoints[index][j];
- }
- }
- }
- for (auto const& inner : inners)
- {
- int const numInnerIndices = static_cast<int>(inner.size());
- int const* innerIndices = inner.data();
- for (int i = 0; i < numInnerIndices; ++i)
- {
- int index = innerIndices[i];
- if (!mIsConverted[index])
- {
- mIsConverted[index] = true;
- for (int j = 0; j < 2; ++j)
- {
- mComputePoints[index][j] = mPoints[index][j];
- }
- }
- }
- }
- // Combine the outer polygon and the inner polygons into a
- // simple polygon by inserting two edges per inner polygon
- // connecting mutually visible vertices.
- int nextElement = mNumPoints; // The next available element.
- std::map<int, int> indexMap;
- std::vector<int> combined;
- if (!ProcessOuterAndInners(nextElement, outer, inners, indexMap, combined))
- {
- // An unexpected condition was encountered.
- return false;
- }
- // The combined polygon is now in the format of a simple
- // polygon, albeit with coincident edges.
- int numVertices = static_cast<int>(combined.size());
- int* const indices = &combined[0];
- InitializeVertices(numVertices, indices);
- DoEarClipping(numVertices, indices);
- // Map the duplicate indices back to the original indices.
- RemapIndices(indexMap);
- return true;
- }
- else
- {
- return false;
- }
- }
- // A tree of nested polygons. The root node corresponds to an outer
- // polygon. The children of the root correspond to inner polygons,
- // which are nonoverlapping polygons strictly contained in the outer
- // polygon. Each inner polygon may itself contain an outer polygon,
- // thus leading to a hierarchy of polygons. The outer polygons have
- // vertices listed in counterclockwise order. The inner polygons have
- // vertices listed in clockwise order.
- class Tree
- {
- public:
- Polygon polygon;
- std::vector<std::shared_ptr<Tree>> child;
- };
- // The input 'positions' is a shared array of vertices that contains
- // the vertices for multiple simple polygons in a tree of polygons.
- bool operator()(std::shared_ptr<Tree> const& tree)
- {
- mTriangles.clear();
- if (mPoints)
- {
- // Two extra elements per inner polygon are needed to
- // duplicate the endpoints of the edges introduced to combine
- // outer and inner polygons.
- int numPointsPlusExtras = mNumPoints + InitializeFromTree(tree);
- if (numPointsPlusExtras > static_cast<int>(mComputePoints.size()))
- {
- mComputePoints.resize(numPointsPlusExtras);
- mIsConverted.resize(numPointsPlusExtras);
- for (int i = mNumPoints; i < numPointsPlusExtras; ++i)
- {
- mIsConverted[i] = false;
- }
- mQuery.Set(numPointsPlusExtras, &mComputePoints[0]);
- }
- int nextElement = mNumPoints;
- std::map<int, int> indexMap;
- std::queue<std::shared_ptr<Tree>> treeQueue;
- treeQueue.push(tree);
- while (treeQueue.size() > 0)
- {
- std::shared_ptr<Tree> outer = treeQueue.front();
- treeQueue.pop();
- int numChildren = static_cast<int>(outer->child.size());
- int numVertices;
- int const* indices;
- if (numChildren == 0)
- {
- // The outer polygon is a simple polygon (no nested
- // inner polygons). Triangulate the simple polygon.
- numVertices = static_cast<int>(outer->polygon.size());
- indices = outer->polygon.data();
- InitializeVertices(numVertices, indices);
- DoEarClipping(numVertices, indices);
- }
- else
- {
- // Place the next level of outer polygon nodes on the
- // queue for triangulation.
- std::vector<Polygon> inners(numChildren);
- for (int c = 0; c < numChildren; ++c)
- {
- std::shared_ptr<Tree> inner = outer->child[c];
- inners[c] = inner->polygon;
- int numGrandChildren = static_cast<int>(inner->child.size());
- for (int g = 0; g < numGrandChildren; ++g)
- {
- treeQueue.push(inner->child[g]);
- }
- }
- // Combine the outer polygon and the inner polygons
- // into a simple polygon by inserting two edges per
- // inner polygon connecting mutually visible vertices.
- std::vector<int> combined;
- ProcessOuterAndInners(nextElement, outer->polygon, inners, indexMap, combined);
- // The combined polygon is now in the format of a
- // simple polygon, albeit with coincident edges.
- numVertices = static_cast<int>(combined.size());
- indices = &combined[0];
- InitializeVertices(numVertices, indices);
- DoEarClipping(numVertices, indices);
- }
- }
- // Map the duplicate indices back to the original indices.
- RemapIndices(indexMap);
- return true;
- }
- else
- {
- return false;
- }
- }
- private:
- // Create the vertex objects that store the various lists required by
- // the ear-clipping algorithm.
- void InitializeVertices(int numVertices, int const* indices)
- {
- mVertices.clear();
- mVertices.resize(numVertices);
- mCFirst = -1;
- mCLast = -1;
- mRFirst = -1;
- mRLast = -1;
- mEFirst = -1;
- mELast = -1;
- // Create a circular list of the polygon vertices for dynamic
- // removal of vertices.
- int numVerticesM1 = numVertices - 1;
- for (int i = 0; i <= numVerticesM1; ++i)
- {
- Vertex& vertex = V(i);
- vertex.index = (indices ? indices[i] : i);
- vertex.vPrev = (i > 0 ? i - 1 : numVerticesM1);
- vertex.vNext = (i < numVerticesM1 ? i + 1 : 0);
- }
- // Create a circular list of the polygon vertices for dynamic
- // removal of vertices. Keep track of two linear sublists, one
- // for the convex vertices and one for the reflex vertices.
- // This is an O(N) process where N is the number of polygon
- // vertices.
- for (int i = 0; i <= numVerticesM1; ++i)
- {
- if (IsConvex(i))
- {
- InsertAfterC(i);
- }
- else
- {
- InsertAfterR(i);
- }
- }
- }
- // Apply ear clipping to the input polygon. Polygons with holes are
- // preprocessed to obtain an index array that is nearly a simple
- // polygon. This outer polygon has a pair of coincident edges per
- // inner polygon.
- void DoEarClipping(int numVertices, int const* indices)
- {
- // If the polygon is convex, just create a triangle fan.
- if (mRFirst == -1)
- {
- int numVerticesM1 = numVertices - 1;
- if (indices)
- {
- for (int i = 1; i < numVerticesM1; ++i)
- {
- mTriangles.push_back( { indices[0], indices[i], indices[i + 1] } );
- }
- }
- else
- {
- for (int i = 1; i < numVerticesM1; ++i)
- {
- mTriangles.push_back( { 0, i, i + 1 } );
- }
- }
- return;
- }
- // Identify the ears and build a circular list of them. Let V0,
- // V1, and V2 be consecutive vertices forming a triangle T. The
- // vertex V1 is an ear if no other vertices of the polygon lie
- // inside T. Although it is enough to show that V1 is not an ear
- // by finding at least one other vertex inside T, it is sufficient
- // to search only the reflex vertices. This is an O(C*R) process,
- // where C is the number of convex vertices and R is the number of
- // reflex vertices with N = C+R. The order is O(N^2), for example
- // when C = R = N/2.
- for (int i = mCFirst; i != -1; i = V(i).sNext)
- {
- if (IsEar(i))
- {
- InsertEndE(i);
- }
- }
- V(mEFirst).ePrev = mELast;
- V(mELast).eNext = mEFirst;
- // Remove the ears, one at a time.
- bool bRemoveAnEar = true;
- while (bRemoveAnEar)
- {
- // Add the triangle with the ear to the output list of
- // triangles.
- int iVPrev = V(mEFirst).vPrev;
- int iVNext = V(mEFirst).vNext;
- mTriangles.push_back( { V(iVPrev).index, V(mEFirst).index, V(iVNext).index } );
- // Remove the vertex corresponding to the ear.
- RemoveV(mEFirst);
- if (--numVertices == 3)
- {
- // Only one triangle remains, just remove the ear and
- // copy it.
- mEFirst = RemoveE(mEFirst);
- iVPrev = V(mEFirst).vPrev;
- iVNext = V(mEFirst).vNext;
- mTriangles.push_back( { V(iVPrev).index, V(mEFirst).index, V(iVNext).index } );
- bRemoveAnEar = false;
- continue;
- }
- // Removal of the ear can cause an adjacent vertex to become
- // an ear or to stop being an ear.
- Vertex& vPrev = V(iVPrev);
- if (vPrev.isEar)
- {
- if (!IsEar(iVPrev))
- {
- RemoveE(iVPrev);
- }
- }
- else
- {
- bool wasReflex = !vPrev.isConvex;
- if (IsConvex(iVPrev))
- {
- if (wasReflex)
- {
- RemoveR(iVPrev);
- }
- if (IsEar(iVPrev))
- {
- InsertBeforeE(iVPrev);
- }
- }
- }
- Vertex& vNext = V(iVNext);
- if (vNext.isEar)
- {
- if (!IsEar(iVNext))
- {
- RemoveE(iVNext);
- }
- }
- else
- {
- bool wasReflex = !vNext.isConvex;
- if (IsConvex(iVNext))
- {
- if (wasReflex)
- {
- RemoveR(iVNext);
- }
- if (IsEar(iVNext))
- {
- InsertAfterE(iVNext);
- }
- }
- }
- // Remove the ear.
- mEFirst = RemoveE(mEFirst);
- }
- }
- // Given an outer polygon that contains an inner polygon, this
- // function determines a pair of visible vertices and inserts two
- // coincident edges to generate a nearly simple polygon.
- bool CombinePolygons(int nextElement, Polygon const& outer,
- Polygon const& inner, std::map<int, int>& indexMap,
- std::vector<int>& combined)
- {
- int const numOuterIndices = static_cast<int>(outer.size());
- int const* outerIndices = outer.data();
- int const numInnerIndices = static_cast<int>(inner.size());
- int const* innerIndices = inner.data();
- // Locate the inner-polygon vertex of maximum x-value, call this
- // vertex M.
- ComputeType xmax = mComputePoints[innerIndices[0]][0];
- int xmaxIndex = 0;
- for (int i = 1; i < numInnerIndices; ++i)
- {
- ComputeType x = mComputePoints[innerIndices[i]][0];
- if (x > xmax)
- {
- xmax = x;
- xmaxIndex = i;
- }
- }
- Vector2<ComputeType> M = mComputePoints[innerIndices[xmaxIndex]];
- // Find the edge whose intersection Intr with the ray M+t*(1,0)
- // minimizes
- // the ray parameter t >= 0.
- ComputeType const cmax = static_cast<ComputeType>(std::numeric_limits<InputType>::max());
- ComputeType const zero = static_cast<ComputeType>(0);
- Vector2<ComputeType> intr{ cmax, M[1] };
- int v0min = -1, v1min = -1, endMin = -1;
- int i0, i1;
- ComputeType s = cmax;
- ComputeType t = cmax;
- for (i0 = numOuterIndices - 1, i1 = 0; i1 < numOuterIndices; i0 = i1++)
- {
- // Consider only edges for which the first vertex is below
- // (or on) the ray and the second vertex is above (or on)
- // the ray.
- Vector2<ComputeType> diff0 = mComputePoints[outerIndices[i0]] - M;
- if (diff0[1] > zero)
- {
- continue;
- }
- Vector2<ComputeType> diff1 = mComputePoints[outerIndices[i1]] - M;
- if (diff1[1] < zero)
- {
- continue;
- }
- // At this time, diff0.y <= 0 and diff1.y >= 0.
- int currentEndMin = -1;
- if (diff0[1] < zero)
- {
- if (diff1[1] > zero)
- {
- // The intersection of the edge and ray occurs at an
- // interior edge point.
- s = diff0[1] / (diff0[1] - diff1[1]);
- t = diff0[0] + s * (diff1[0] - diff0[0]);
- }
- else // diff1.y == 0
- {
- // The vertex Outer[i1] is the intersection of the
- // edge and the ray.
- t = diff1[0];
- currentEndMin = i1;
- }
- }
- else // diff0.y == 0
- {
- if (diff1[1] > zero)
- {
- // The vertex Outer[i0] is the intersection of the
- // edge and the ray;
- t = diff0[0];
- currentEndMin = i0;
- }
- else // diff1.y == 0
- {
- if (diff0[0] < diff1[0])
- {
- t = diff0[0];
- currentEndMin = i0;
- }
- else
- {
- t = diff1[0];
- currentEndMin = i1;
- }
- }
- }
- if (zero <= t && t < intr[0])
- {
- intr[0] = t;
- v0min = i0;
- v1min = i1;
- if (currentEndMin == -1)
- {
- // The current closest point is an edge-interior
- // point.
- endMin = -1;
- }
- else
- {
- // The current closest point is a vertex.
- endMin = currentEndMin;
- }
- }
- else if (t == intr[0])
- {
- // The current closest point is a vertex shared by
- // multiple edges; thus, endMin and currentMin refer to
- // the same point.
- LogAssert(endMin != -1 && currentEndMin != -1, "Unexpected condition.");
- // We need to select the edge closest to M. The previous
- // closest edge is <outer[v0min],outer[v1min]>. The
- // current candidate is <outer[i0],outer[i1]>.
- Vector2<ComputeType> shared = mComputePoints[outerIndices[i1]];
- // For the previous closest edge, endMin refers to a
- // vertex of the edge. Get the index of the other vertex.
- int other = (endMin == v0min ? v1min : v0min);
- // The new edge is closer if the other vertex of the old
- // edge is left-of the new edge.
- diff0 = mComputePoints[outerIndices[i0]] - shared;
- diff1 = mComputePoints[outerIndices[other]] - shared;
- ComputeType dotperp = DotPerp(diff0, diff1);
- if (dotperp > zero)
- {
- // The new edge is closer to M.
- v0min = i0;
- v1min = i1;
- endMin = currentEndMin;
- }
- }
- }
- // The intersection intr[0] stored only the t-value of the ray.
- // The actual point is (mx,my)+t*(1,0), so intr[0] must be
- // adjusted.
- intr[0] += M[0];
- int maxCosIndex;
- if (endMin == -1)
- {
- // If you reach this assert, there is a good chance that you
- // have two inner polygons that share a vertex or an edge.
- LogAssert(v0min >= 0 && v1min >= 0, "Is this an invalid nested polygon?");
- // Select one of Outer[v0min] and Outer[v1min] that has an
- // x-value larger than M.x, call this vertex P. The triangle
- // <M,I,P> must contain an outer-polygon vertex that is
- // visible to M, which is possibly P itself.
- Vector2<ComputeType> sTriangle[3]; // <P,M,I> or <P,I,M>
- int pIndex;
- if (mComputePoints[outerIndices[v0min]][0] > mComputePoints[outerIndices[v1min]][0])
- {
- sTriangle[0] = mComputePoints[outerIndices[v0min]];
- sTriangle[1] = intr;
- sTriangle[2] = M;
- pIndex = v0min;
- }
- else
- {
- sTriangle[0] = mComputePoints[outerIndices[v1min]];
- sTriangle[1] = M;
- sTriangle[2] = intr;
- pIndex = v1min;
- }
- // If any outer-polygon vertices other than P are inside the
- // triangle <M,I,P>, then at least one of these vertices must
- // be a reflex vertex. It is sufficient to locate the reflex
- // vertex R (if any) in <M,I,P> that minimizes the angle
- // between R-M and (1,0). The data member mQuery is used for
- // the reflex query.
- Vector2<ComputeType> diff = sTriangle[0] - M;
- ComputeType maxSqrLen = Dot(diff, diff);
- ComputeType maxCos = diff[0] * diff[0] / maxSqrLen;
- PrimalQuery2<ComputeType> localQuery(3, sTriangle);
- maxCosIndex = pIndex;
- for (int i = 0; i < numOuterIndices; ++i)
- {
- if (i == pIndex)
- {
- continue;
- }
- int curr = outerIndices[i];
- int prev = outerIndices[(i + numOuterIndices - 1) % numOuterIndices];
- int next = outerIndices[(i + 1) % numOuterIndices];
- if (mQuery.ToLine(curr, prev, next) <= 0
- && localQuery.ToTriangle(mComputePoints[curr], 0, 1, 2) <= 0)
- {
- // The vertex is reflex and inside the <M,I,P>
- // triangle.
- diff = mComputePoints[curr] - M;
- ComputeType sqrLen = Dot(diff, diff);
- ComputeType cs = diff[0] * diff[0] / sqrLen;
- if (cs > maxCos)
- {
- // The reflex vertex forms a smaller angle with
- // the positive x-axis, so it becomes the new
- // visible candidate.
- maxSqrLen = sqrLen;
- maxCos = cs;
- maxCosIndex = i;
- }
- else if (cs == maxCos && sqrLen < maxSqrLen)
- {
- // The reflex vertex has angle equal to the
- // current minimum but the length is smaller, so
- // it becomes the new visible candidate.
- maxSqrLen = sqrLen;
- maxCosIndex = i;
- }
- }
- }
- }
- else
- {
- maxCosIndex = endMin;
- }
- // The visible vertices are Position[Inner[xmaxIndex]] and
- // Position[Outer[maxCosIndex]]. Two coincident edges with
- // these endpoints are inserted to connect the outer and inner
- // polygons into a simple polygon. Each of the two Position[]
- // values must be duplicated, because the original might be
- // convex (or reflex) and the duplicate is reflex (or convex).
- // The ear-clipping algorithm needs to distinguish between them.
- combined.resize(numOuterIndices + numInnerIndices + 2);
- int cIndex = 0;
- for (int i = 0; i <= maxCosIndex; ++i, ++cIndex)
- {
- combined[cIndex] = outerIndices[i];
- }
- for (int i = 0; i < numInnerIndices; ++i, ++cIndex)
- {
- int j = (xmaxIndex + i) % numInnerIndices;
- combined[cIndex] = innerIndices[j];
- }
- int innerIndex = innerIndices[xmaxIndex];
- mComputePoints[nextElement] = mComputePoints[innerIndex];
- combined[cIndex] = nextElement;
- auto iter = indexMap.find(innerIndex);
- if (iter != indexMap.end())
- {
- innerIndex = iter->second;
- }
- indexMap[nextElement] = innerIndex;
- ++cIndex;
- ++nextElement;
- int outerIndex = outerIndices[maxCosIndex];
- mComputePoints[nextElement] = mComputePoints[outerIndex];
- combined[cIndex] = nextElement;
- iter = indexMap.find(outerIndex);
- if (iter != indexMap.end())
- {
- outerIndex = iter->second;
- }
- indexMap[nextElement] = outerIndex;
- ++cIndex;
- ++nextElement;
- for (int i = maxCosIndex + 1; i < numOuterIndices; ++i, ++cIndex)
- {
- combined[cIndex] = outerIndices[i];
- }
- return true;
- }
- // Given an outer polygon that contains a set of nonoverlapping inner
- // polygons, this function determines pairs of visible vertices and
- // inserts coincident edges to generate a nearly simple polygon. It
- // repeatedly calls CombinePolygons for each inner polygon of the
- // outer polygon.
- bool ProcessOuterAndInners(int& nextElement, Polygon const& outer,
- std::vector<Polygon> const& inners, std::map<int, int>& indexMap,
- std::vector<int>& combined)
- {
- // Sort the inner polygons based on maximum x-values.
- int numInners = static_cast<int>(inners.size());
- std::vector<std::pair<ComputeType, int>> pairs(numInners);
- for (int p = 0; p < numInners; ++p)
- {
- int numIndices = static_cast<int>(inners[p].size());
- int const* indices = inners[p].data();
- ComputeType xmax = mComputePoints[indices[0]][0];
- for (int j = 1; j < numIndices; ++j)
- {
- ComputeType x = mComputePoints[indices[j]][0];
- if (x > xmax)
- {
- xmax = x;
- }
- }
- pairs[p].first = xmax;
- pairs[p].second = p;
- }
- std::sort(pairs.begin(), pairs.end());
- // Merge the inner polygons with the outer polygon.
- Polygon currentPolygon = outer;
- for (int p = numInners - 1; p >= 0; --p)
- {
- Polygon const& polygon = inners[pairs[p].second];
- Polygon currentCombined;
- if (!CombinePolygons(nextElement, currentPolygon, polygon, indexMap, currentCombined))
- {
- return false;
- }
- currentPolygon = std::move(currentCombined);
- nextElement += 2;
- }
- for (auto index : currentPolygon)
- {
- combined.push_back(index);
- }
- return true;
- }
- // The insertion of coincident edges to obtain a nearly simple polygon
- // requires duplication of vertices in order that the ear-clipping
- // algorithm work correctly. After the triangulation, the indices of
- // the duplicated vertices are converted to the original indices.
- void RemapIndices(std::map<int, int> const& indexMap)
- {
- // The triangulation includes indices to the duplicated outer and
- // inner vertices. These indices must be mapped back to the
- // original ones.
- for (auto& tri : mTriangles)
- {
- for (int i = 0; i < 3; ++i)
- {
- auto iter = indexMap.find(tri[i]);
- if (iter != indexMap.end())
- {
- tri[i] = iter->second;
- }
- }
- }
- }
- // Two extra elements are needed in the position array per
- // outer-inners polygon. This function computes the total number of
- // extra elements needed for the input tree and it converts InputType
- // vertices to ComputeType values.
- int InitializeFromTree(std::shared_ptr<Tree> const& tree)
- {
- // Use a breadth-first search to process the outer-inners pairs
- // of the tree of nested polygons.
- int numExtraPoints = 0;
- std::queue<std::shared_ptr<Tree>> treeQueue;
- treeQueue.push(tree);
- while (treeQueue.size() > 0)
- {
- // The 'root' is an outer polygon.
- std::shared_ptr<Tree> outer = treeQueue.front();
- treeQueue.pop();
- // Count number of extra points for this outer-inners pair.
- int numChildren = static_cast<int>(outer->child.size());
- numExtraPoints += 2 * numChildren;
- // Convert outer points from InputType to ComputeType.
- int const numOuterIndices = static_cast<int>(outer->polygon.size());
- int const* outerIndices = outer->polygon.data();
- for (int i = 0; i < numOuterIndices; ++i)
- {
- int index = outerIndices[i];
- if (!mIsConverted[index])
- {
- mIsConverted[index] = true;
- for (int j = 0; j < 2; ++j)
- {
- mComputePoints[index][j] = mPoints[index][j];
- }
- }
- }
- // The grandchildren of the outer polygon are also outer
- // polygons. Insert them into the queue for processing.
- for (int c = 0; c < numChildren; ++c)
- {
- // The 'child' is an inner polygon.
- std::shared_ptr<Tree> inner = outer->child[c];
- // Convert inner points from InputType to ComputeType.
- int const numInnerIndices = static_cast<int>(inner->polygon.size());
- int const* innerIndices = inner->polygon.data();
- for (int i = 0; i < numInnerIndices; ++i)
- {
- int index = innerIndices[i];
- if (!mIsConverted[index])
- {
- mIsConverted[index] = true;
- for (int j = 0; j < 2; ++j)
- {
- mComputePoints[index][j] = mPoints[index][j];
- }
- }
- }
- int numGrandChildren = static_cast<int>(inner->child.size());
- for (int g = 0; g < numGrandChildren; ++g)
- {
- treeQueue.push(inner->child[g]);
- }
- }
- }
- return numExtraPoints;
- }
- // The input polygon.
- int mNumPoints;
- Vector2<InputType> const* mPoints;
- // The output triangulation.
- std::vector<std::array<int, 3>> mTriangles;
- // The array of points used for geometric queries. If you want to be
- // certain of a correct result, choose ComputeType to be BSNumber.
- // The InputType points are convertex to ComputeType points on demand;
- // the mIsConverted array keeps track of which input points have been
- // converted.
- std::vector<Vector2<ComputeType>> mComputePoints;
- std::vector<bool> mIsConverted;
- PrimalQuery2<ComputeType> mQuery;
- // Doubly linked lists for storing specially tagged vertices.
- class Vertex
- {
- public:
- Vertex()
- :
- index(-1),
- vPrev(-1),
- vNext(-1),
- sPrev(-1),
- sNext(-1),
- ePrev(-1),
- eNext(-1),
- isConvex(false),
- isEar(false)
- {
- }
- int index; // index of vertex in position array
- int vPrev, vNext; // vertex links for polygon
- int sPrev, sNext; // convex/reflex vertex links (disjoint lists)
- int ePrev, eNext; // ear links
- bool isConvex, isEar;
- };
- inline Vertex& V(int i)
- {
- return mVertices[i];
- }
- bool IsConvex(int i)
- {
- Vertex& vertex = V(i);
- int curr = vertex.index;
- int prev = V(vertex.vPrev).index;
- int next = V(vertex.vNext).index;
- vertex.isConvex = (mQuery.ToLine(curr, prev, next) > 0);
- return vertex.isConvex;
- }
- bool IsEar(int i)
- {
- Vertex& vertex = V(i);
- if (mRFirst == -1)
- {
- // The remaining polygon is convex.
- vertex.isEar = true;
- return true;
- }
- // Search the reflex vertices and test if any are in the triangle
- // <V[prev],V[curr],V[next]>.
- int prev = V(vertex.vPrev).index;
- int curr = vertex.index;
- int next = V(vertex.vNext).index;
- vertex.isEar = true;
- for (int j = mRFirst; j != -1; j = V(j).sNext)
- {
- // Check if the test vertex is already one of the triangle
- // vertices.
- if (j == vertex.vPrev || j == i || j == vertex.vNext)
- {
- continue;
- }
- // V[j] has been ruled out as one of the original vertices of
- // the triangle <V[prev],V[curr],V[next]>. When triangulating
- // polygons with holes, V[j] might be a duplicated vertex, in
- // which case it does not affect the earness of V[curr].
- int test = V(j).index;
- if (mComputePoints[test] == mComputePoints[prev]
- || mComputePoints[test] == mComputePoints[curr]
- || mComputePoints[test] == mComputePoints[next])
- {
- continue;
- }
- // Test if the vertex is inside or on the triangle. When it
- // is, it causes V[curr] not to be an ear.
- if (mQuery.ToTriangle(test, prev, curr, next) <= 0)
- {
- vertex.isEar = false;
- break;
- }
- }
- return vertex.isEar;
- }
- // insert convex vertex
- void InsertAfterC(int i)
- {
- if (mCFirst == -1)
- {
- // Add the first convex vertex.
- mCFirst = i;
- }
- else
- {
- V(mCLast).sNext = i;
- V(i).sPrev = mCLast;
- }
- mCLast = i;
- }
- // insert reflex vertesx
- void InsertAfterR(int i)
- {
- if (mRFirst == -1)
- {
- // Add the first reflex vertex.
- mRFirst = i;
- }
- else
- {
- V(mRLast).sNext = i;
- V(i).sPrev = mRLast;
- }
- mRLast = i;
- }
- // insert ear at end of list
- void InsertEndE(int i)
- {
- if (mEFirst == -1)
- {
- // Add the first ear.
- mEFirst = i;
- mELast = i;
- }
- V(mELast).eNext = i;
- V(i).ePrev = mELast;
- mELast = i;
- }
- // insert ear after efirst
- void InsertAfterE(int i)
- {
- Vertex& first = V(mEFirst);
- int currENext = first.eNext;
- Vertex& vertex = V(i);
- vertex.ePrev = mEFirst;
- vertex.eNext = currENext;
- first.eNext = i;
- V(currENext).ePrev = i;
- }
- // insert ear before efirst
- void InsertBeforeE(int i)
- {
- Vertex& first = V(mEFirst);
- int currEPrev = first.ePrev;
- Vertex& vertex = V(i);
- vertex.ePrev = currEPrev;
- vertex.eNext = mEFirst;
- first.ePrev = i;
- V(currEPrev).eNext = i;
- }
- // remove vertex
- void RemoveV(int i)
- {
- int currVPrev = V(i).vPrev;
- int currVNext = V(i).vNext;
- V(currVPrev).vNext = currVNext;
- V(currVNext).vPrev = currVPrev;
- }
- // remove ear at i
- int RemoveE(int i)
- {
- int currEPrev = V(i).ePrev;
- int currENext = V(i).eNext;
- V(currEPrev).eNext = currENext;
- V(currENext).ePrev = currEPrev;
- return currENext;
- }
- // remove reflex vertex
- void RemoveR(int i)
- {
- LogAssert(mRFirst != -1 && mRLast != -1, "Reflex vertices must exist.");
- if (i == mRFirst)
- {
- mRFirst = V(i).sNext;
- if (mRFirst != -1)
- {
- V(mRFirst).sPrev = -1;
- }
- V(i).sNext = -1;
- }
- else if (i == mRLast)
- {
- mRLast = V(i).sPrev;
- if (mRLast != -1)
- {
- V(mRLast).sNext = -1;
- }
- V(i).sPrev = -1;
- }
- else
- {
- int currSPrev = V(i).sPrev;
- int currSNext = V(i).sNext;
- V(currSPrev).sNext = currSNext;
- V(currSNext).sPrev = currSPrev;
- V(i).sNext = -1;
- V(i).sPrev = -1;
- }
- }
- // The doubly linked list.
- std::vector<Vertex> mVertices;
- int mCFirst, mCLast; // linear list of convex vertices
- int mRFirst, mRLast; // linear list of reflex vertices
- int mEFirst, mELast; // cyclical list of ears
- };
- }
|