// 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 #include #include // The Polygon2 object represents a simple polygon: No duplicate vertices, // closed (each vertex is shared by exactly two edges), and no // self-intersections at interior edge points. The 'vertexPool' array can // contain more points than needed to define the polygon, which allows the // vertex pool to have multiple polygons associated with it. Thus, the // programmer must ensure that the vertex pool persists as long as any // Polygon2 objects exist that depend on the pool. The number of polygon // vertices is 'numIndices' and must be 3 or larger. The 'indices' array // refers to the points in 'vertexPool' that are part of the polygon and must // have 'numIndices' unique elements. The edges of the polygon are pairs of // indices into 'vertexPool', // edge[0] = (indices[0], indices[1]) // : // edge[numIndices-2] = (indices[numIndices-2], indices[numIndices-1]) // edge[numIndices-1] = (indices[numIndices-1], indices[0]) // The programmer should ensure the polygon is simple. The geometric // queries are valid regardless of whether the polygon is oriented clockwise // or counterclockwise. // // NOTE: Comparison operators are not provided. The semantics of equal // polygons is complicated and (at the moment) not useful. The vertex pools // can be different and indices do not match, but the vertices they reference // can match. Even with a shared vertex pool, the indices can be "rotated", // leading to the same polygon abstractly but the data structures do not // match. namespace WwiseGTE { template class Polygon2 { public: // Construction. The constructor succeeds when 'numIndices' >= 3 and // 'vertexPool' and 'indices' are not null; we cannot test whether you // have a valid number of elements in the input arrays. A copy is // made of 'indices', but the 'vertexPool' is not copied. If the // constructor fails, the internal vertex pointer is set to null, the // index array has no elements, and the orientation is set to // clockwise. Polygon2(Vector2 const* vertexPool, int numIndices, int const* indices, bool counterClockwise) : mVertexPool(vertexPool), mCounterClockwise(counterClockwise) { if (numIndices >= 3 && vertexPool && indices) { for (int i = 0; i < numIndices; ++i) { mVertices.insert(indices[i]); } if (numIndices == static_cast(mVertices.size())) { mIndices.resize(numIndices); std::copy(indices, indices + numIndices, mIndices.begin()); return; } // At least one duplicated vertex was encountered, so the // polygon is not simple. Fail the constructor call. mVertices.clear(); } // Invalid input to the Polygon2 constructor. mVertexPool = nullptr; mCounterClockwise = false; } // To validate construction, create an object as shown: // Polygon2 polygon(parameters); // if (!polygon) { ; } inline operator bool() const { return mVertexPool != nullptr; } // Member access. inline Vector2 const* GetVertexPool() const { return mVertexPool; } inline std::set const& GetVertices() const { return mVertices; } inline std::vector const& GetIndices() const { return mIndices; } inline bool CounterClockwise() const { return mCounterClockwise; } // Geometric queries. Vector2 ComputeVertexAverage() const { Vector2 average = Vector2::Zero(); if (mVertexPool) { for (int index : mVertices) { average += mVertexPool[index]; } average /= static_cast(mVertices.size()); } return average; } Real ComputePerimeterLength() const { Real length(0); if (mVertexPool) { Vector2 v0 = mVertexPool[mIndices.back()]; for (int index : mIndices) { Vector2 v1 = mVertexPool[index]; length += Length(v1 - v0); v0 = v1; } } return length; } Real ComputeArea() const { Real area(0); if (mVertexPool) { int const numIndices = static_cast(mIndices.size()); Vector2 v0 = mVertexPool[mIndices[numIndices - 2]]; Vector2 v1 = mVertexPool[mIndices[numIndices - 1]]; for (int index : mIndices) { Vector2 v2 = mVertexPool[index]; area += v1[0] * (v2[1] - v0[1]); v0 = v1; v1 = v2; } area *= (Real)0.5; } return std::fabs(area); } // Simple polygons have no self-intersections at interior points // of edges. The test is an exhaustive all-pairs intersection // test for edges, which is inefficient for polygons with a large // number of vertices. TODO: Provide an efficient algorithm that // uses the algorithm of class RectangleManager.h. bool IsSimple() const { if (!mVertexPool) { return false; } // For mVertexPool to be nonnull, the number of indices is // guaranteed to be at least 3. int const numIndices = static_cast(mIndices.size()); if (numIndices == 3) { // The polygon is a triangle. return true; } return IsSimpleInternal(); } // Convex polygons are simple polygons where the angles between // consecutive edges are less than or equal to pi radians. bool IsConvex() const { if (!mVertexPool) { return false; } // For mVertexPool to be nonnull, the number of indices is // guaranteed to be at least 3. int const numIndices = static_cast(mIndices.size()); if (numIndices == 3) { // The polygon is a triangle. return true; } return IsSimpleInternal() && IsConvexInternal(); } private: // These calls have preconditions that mVertexPool is not null and // mIndices.size() > 3. The heart of the algorithms are implemented // here. bool IsSimpleInternal() const { Segment2 seg0, seg1; TIQuery, Segment2> query; typename TIQuery, Segment2>::Result result; int const numIndices = static_cast(mIndices.size()); for (int i0 = 0; i0 < numIndices; ++i0) { int i0p1 = (i0 + 1) % numIndices; seg0.p[0] = mVertexPool[mIndices[i0]]; seg0.p[1] = mVertexPool[mIndices[i0p1]]; int i1min = (i0 + 2) % numIndices; int i1max = (i0 - 2 + numIndices) % numIndices; for (int i1 = i1min; i1 <= i1max; ++i1) { int i1p1 = (i1 + 1) % numIndices; seg1.p[0] = mVertexPool[mIndices[i1]]; seg1.p[1] = mVertexPool[mIndices[i1p1]]; result = query(seg0, seg1); if (result.intersect) { return false; } } } return true; } bool IsConvexInternal() const { Real sign = (mCounterClockwise ? (Real)1 : (Real)-1); int const numIndices = static_cast(mIndices.size()); for (int i = 0; i < numIndices; ++i) { int iPrev = (i + numIndices - 1) % numIndices; int iNext = (i + 1) % numIndices; Vector2 vPrev = mVertexPool[mIndices[iPrev]]; Vector2 vCurr = mVertexPool[mIndices[i]]; Vector2 vNext = mVertexPool[mIndices[iNext]]; Vector2 edge0 = vCurr - vPrev; Vector2 edge1 = vNext - vCurr; Real test = sign * DotPerp(edge0, edge1); if (test < (Real)0) { return false; } } return true; } Vector2 const* mVertexPool; std::set mVertices; std::vector mIndices; bool mCounterClockwise; }; }