4#include <glm/gtc/epsilon.hpp>
16 using List = std::vector<Vertex>;
17 static constexpr uint32_t None = 0xFFFFFFFF;
20 bool intersectionPoint;
22 Vertex(
const glm::vec3& vec,
bool intersection =
false) : position(vec), intersectionPoint(intersection)
26 operator glm::vec3()
const {
34 bool veryClose(
const glm::vec3& lhs,
const glm::vec3& rhs) {
35 static constexpr glm::vec3 cEpsilon(0.01f, 0.01f, 0.01f);
37 return glm::all(glm::epsilonEqual(lhs, rhs, cEpsilon));
44 Vertex prev = vertices.back();
46 for (uint32_t idx = 0; idx < vertices.size(); ) {
47 Vertex current = vertices[idx];
49 if (
veryClose(prev.position, current.position)) {
50 if (current.intersectionPoint) {
52 vertices.erase(vertices.begin() + (idx - 1));
61 vertices.erase(vertices.begin() + idx);
76 int calcLineSide(
const glm::vec3& start,
const glm::vec3& end,
const glm::vec3& point) {
77 float d = ((end.x - start.x) * (point.y - start.y)) - ((end.y - start.y) * (point.x - start.x));
92 for (
auto i = vertices.begin(), e = vertices.end(); i != e; ++i) {
93 if (i->position == vertex) {
94 return static_cast<uint32_t
>(i - vertices.begin());
105 glm::vec3 vEnd(vertex.position.x + 1000000.0f, vertex.position.y, 0.0f);
106 glm::vec3 start = vertices.back();
107 glm::vec3 intersectionpoint;
108 int intersectioncount = 0;
110 for (
auto i = vertices.begin(), e = vertices.end(); i != e; ++i) {
113 if ((vertex.position != start) && (vertex.position != end)) {
114 if (start.y > end.y) {
115 if ((vertex.position.y >= end.y) && (vertex.position.y < start.y)) {
116 intersectioncount += Cogs::Geometry::calcLineLineIntersectionPoint(vertex.position, vEnd, start, end, intersectionpoint);
119 else if (start.y < end.y) {
120 if ((vertex.position.y >= start.y) && (vertex.position.y < end.y)) {
121 intersectioncount += Cogs::Geometry::calcLineLineIntersectionPoint(vertex.position, vEnd, start, end, intersectionpoint);
127 return (intersectioncount & 1) ? Location::Inside : Location::Outside;
136 Location
findDivergenceForwards(
const Vertex::List& mine,
const Vertex::List& theirs, uint32_t myIdx, uint32_t theirIdx, uint32_t& myNextIdx, uint32_t& theirNextIdx) {
137 uint32_t myPointCount =
static_cast<uint32_t
>(mine.size());
138 uint32_t theirPointCount =
static_cast<uint32_t
>(theirs.size());
139 glm::vec3 myPrevious;
140 glm::vec3 myCurrent = mine[myIdx].position;
142 if (mine[(myIdx + 1) % myPointCount].position == theirs[(theirIdx + 1) % theirPointCount].position) {
144 myPrevious = myCurrent;
147 theirNextIdx = theirIdx;
148 myIdx = (myIdx + 1) % myPointCount;
149 theirIdx = (theirIdx + 1) % theirPointCount;
151 myCurrent = mine[myIdx];
153 while (myCurrent == theirs[theirIdx].position);
155 else if (mine[(myIdx + 1) % myPointCount].position == theirs[std::min(theirIdx - 1, theirPointCount - 1)].position) {
157 myPrevious = myCurrent;
160 theirNextIdx = theirIdx;
161 myIdx = (myIdx + 1) % myPointCount;
162 theirIdx = std::min(theirIdx - 1, theirPointCount - 1);
164 myCurrent = mine[myIdx];
166 while (myCurrent == theirs[theirIdx].position);
170 theirNextIdx = theirIdx;
171 myPrevious = myCurrent;
172 myCurrent = mine[(myIdx + 1) % myPointCount].position;
175 float shortestDist = std::numeric_limits< float >::max();
176 glm::vec3 closestPoint;
177 glm::vec3 theirPrevious = theirs.back().position;
178 glm::vec3 intersectionPoint;
180 for (theirIdx = 0; theirIdx < theirPointCount; ++theirIdx) {
181 glm::vec3 theirCurrent = theirs[theirIdx].position;
183 if (Cogs::Geometry::calcLineLineIntersectionPoint(myPrevious, myCurrent, theirPrevious, theirCurrent, intersectionPoint)) {
184 if ((intersectionPoint != myPrevious) && (intersectionPoint != myCurrent)) {
185 float dist = glm::distance(myPrevious, intersectionPoint);
187 if (dist < shortestDist) {
189 closestPoint = intersectionPoint;
193 theirPrevious = theirCurrent;
198 if (shortestDist < std::numeric_limits< float >::max()) {
199 testPoint = glm::normalize(closestPoint - myCurrent) * (shortestDist * 0.5f);
202 testPoint = (myCurrent + myPrevious) * 0.5f;
215 for (
auto mi = mine.begin(), me = mine.end(); mi != me; ++mi) {
216 for (
auto ti = theirs.begin(), te = theirs.end(); ti != te; ++ti) {
225 glm::vec3 myPrevious = mine.back().position;
228 for (uint32_t myIdx = 0, myPointCount =
static_cast<uint32_t
>(mine.size()); myIdx < myPointCount; ) {
229 glm::vec3 myCurrent = mine[myIdx].position;
230 glm::vec3 intersectionPoint;
231 float shortestDist = std::numeric_limits< float >::max();
232 glm::vec3 closestPoint;
233 uint32_t theirBestIdx = Vertex::None;
234 glm::vec3 theirPrevious = theirs.back().position;
235 glm::vec3 theirCurrent;
237 for (uint32_t theirIdx = 0, theirPointCount =
static_cast<uint32_t
>(theirs.size()); theirIdx < theirPointCount; ++theirIdx) {
238 theirCurrent = theirs[theirIdx].position;
240 if (Cogs::Geometry::calcLineLineIntersectionPoint(myPrevious, myCurrent, theirPrevious, theirCurrent, intersectionPoint)) {
242 float dist = glm::distance(myPrevious, intersectionPoint);
244 if (dist < shortestDist) {
246 closestPoint = intersectionPoint;
247 theirBestIdx = theirIdx;
251 theirPrevious = theirCurrent;
253 if (theirBestIdx != Vertex::None) {
256 if (match == Vertex::None) {
257 theirs.insert(theirs.begin() + theirBestIdx,
Vertex(closestPoint,
true));
260 theirs[match] =
Vertex(closestPoint,
true);
264 if (match == Vertex::None) {
265 mine.insert(mine.begin() + myIdx,
Vertex(closestPoint,
true));
268 myPrevious = closestPoint;
272 mine[match] =
Vertex(closestPoint,
true);
276 myPrevious = myCurrent;
282 for (
auto mi = mine.begin(), me = mine.end(); mi != me; ++mi) {
283 mi->intersectionPoint =
false;
288 for (
auto ti = theirs.begin(), te = theirs.end(); ti != te; ++ti) {
289 ti->intersectionPoint =
false;
293 uint32_t myPointCount =
static_cast<uint32_t
>(mine.size());
298 myIdx = ++myIdx % myPointCount;
307 uint32_t myLastIdx = myIdx;
313 for (myIdx = ++myIdx % myPointCount; myIdx != myLastIdx; ) {
316 if (theirIdx != Vertex::None) {
318 uint32_t theirNextIdx;
321 if ((myIdx != myNextIdx) || (location == Location::Inside)) {
322 if (mine[myIdx].intersectionPoint) {
325 mine[myIdx].intersectionPoint =
true;
326 theirs[theirIdx].intersectionPoint =
true;
329 if (location == Location::Outside) {
330 mine[myNextIdx].intersectionPoint =
true;
331 theirs[theirNextIdx].intersectionPoint =
true;
332 myIdx = (myNextIdx + 1) % myPointCount;
337 for (myIdx = (myNextIdx + 1) % myPointCount; ; ) {
340 if (theirIdx != Vertex::None) {
341 if (
findDivergenceForwards(mine, theirs, myIdx, theirIdx, myNextIdx, theirNextIdx) == Location::Outside) {
342 mine[myNextIdx].intersectionPoint =
true;
343 theirs[theirNextIdx].intersectionPoint =
true;
344 myIdx = (myNextIdx + 1) % myPointCount;
348 myIdx = (myNextIdx + 1) % myPointCount;
351 myIdx = ++myIdx % myPointCount;
357 myIdx = ++myIdx % myPointCount;
361 myIdx = ++myIdx % myPointCount;
364 assert(!(generated & 1));
371 bool findClosestVertices(
const Vertex::List& mine,
const Vertex::List& theirs, uint32_t& myPoint, uint32_t& theirPoint) {
372 float closestDistance = std::numeric_limits< float >::max();
373 uint32_t theirSize =
static_cast<uint32_t
>(theirs.size());
375 myPoint = Vertex::None;
376 theirPoint = Vertex::None;
378 for (uint32_t myIdx = 0, mySize =
static_cast<uint32_t
>(mine.size()); myIdx < mySize; ++myIdx) {
379 glm::vec3 p1 = mine[myIdx].position;
381 for (uint32_t theirIdx = 0; theirIdx < theirSize; ++theirIdx) {
382 float dist = Cogs::Geometry::distanceSqr(p1, theirs[theirIdx].position);
384 if (dist < closestDistance) {
386 theirPoint = theirIdx;
387 closestDistance = dist;
391 return (myPoint != Vertex::None) && (theirPoint != Vertex::None);
399 uint32_t vertexCount =
static_cast<uint32_t
>(vertices.size());
400 uint32_t idx = startIdx;
402 for (uint32_t counter = wrap ? vertexCount : (vertexCount - startIdx - 1); --counter; ) {
403 idx = (idx + 1) % vertexCount;
405 if (vertices[idx].intersectionPoint) {
406 assert(idx != startIdx);
414Cogs::Geometry::Polygon::Polygon(
const PointList& initialPoints) {
426 if (!isClockwise(points)) {
427 std::reverse(points.begin(), points.end());
431void Cogs::Geometry::Polygon::addPoint(
const glm::vec3& point) {
432 points.push_back(point);
439 if (!isClockwise(points)) {
440 std::reverse(points.begin(), points.end());
443 glm::vec3 prev = points.back();
444 glm::vec3 intersectionPoint;
446 for (uint32_t outerStartIdx =
static_cast<uint32_t
>(points.size() - 1), outerEndIdx = 0; outerEndIdx < (points.size() - 1); ) {
447 for (uint32_t innerStartIdx = outerEndIdx + 1, innerEndIdx = innerStartIdx + 1; innerEndIdx < (points.size() - 1); ) {
448 if (Cogs::Geometry::calcLineLineIntersectionPoint(points[outerStartIdx], points[outerEndIdx], points[innerStartIdx], points[innerEndIdx], intersectionPoint)) {
449 points.insert(points.begin() + innerEndIdx++, intersectionPoint);
450 points.insert(points.begin() + outerEndIdx, intersectionPoint);
452 float inner = calcSignedArea(points.begin() + outerEndIdx, points.begin() + innerEndIdx + 1);
455 prev = points[outerEndIdx - 1];
457 for (uint32_t idx = innerEndIdx; idx != outerEndIdx; idx = (idx + 1) % points.size()) {
458 glm::vec3 current = points[idx];
460 outer += (prev.x * current.y) - (current.x * prev.y);
466 if (std::fabs(inner) < std::fabs(outer)) {
467 points.erase(points.begin() + outerEndIdx, points.begin() + innerEndIdx);
470 points.erase(points.begin() + innerEndIdx, points.end());
471 points.erase(points.begin(), points.begin() + outerEndIdx);
479 else if (outer < 0.0f) {
481 std::reverse(points.begin() + outerEndIdx, points.begin() + innerEndIdx);
489 innerStartIdx = innerEndIdx++;
490 innerEndIdx %= points.size();
493 outerStartIdx = outerEndIdx++;
494 outerEndIdx %= points.size();
498void Cogs::Geometry::Polygon::clearPoints() {
529 if ((points.size() > 2) && ( polygon.points.size() > 2)) {
534 std::copy(points.begin(), points.end(), std::back_inserter(mine));
535 std::copy(polygon.points.begin(), polygon.points.end(), std::back_inserter(theirs));
555 while (myStartIdx != Vertex::None) {
562 uint32_t myNextIdx = (myStartIdx + 1) % mine.size();
563 uint32_t theirNextIdx = (theirStartIdx + 1) % theirs.size();
564 bool removable =
false;
566 if (myNextIdx == myEndIdx) {
567 if (
getVertexLocation(theirs[theirNextIdx].position, mine) == Location::Inside) {
577 assert(mine[myStartIdx].intersectionPoint);
578 assert(mine[myEndIdx].intersectionPoint);
579 mine[myStartIdx].intersectionPoint =
false;
580 mine[myEndIdx].intersectionPoint =
false;
582 if (myNextIdx < myEndIdx) {
583 mine.erase(mine.begin() + myNextIdx, mine.begin() + myEndIdx);
585 else if (myNextIdx > myEndIdx) {
586 mine.erase(mine.begin() + myNextIdx, mine.end());
587 mine.erase(mine.begin(), mine.begin() + myEndIdx);
588 myNextIdx =
static_cast<uint32_t
>(mine.size());
591 auto d = mine.begin() + myNextIdx;
593 if (theirNextIdx < theirEndIdx) {
594 for (
auto i = theirs.begin() + theirNextIdx, e = theirs.begin() + theirEndIdx; i != e; ++i) {
595 assert(!i->intersectionPoint);
596 d = mine.insert(d, *i);
599 else if (theirNextIdx > theirEndIdx) {
600 for (
auto i = theirs.begin() + theirNextIdx, e = theirs.end(); i != e; ++i) {
601 assert(!i->intersectionPoint);
602 d = mine.insert(d, *i);
604 for (
auto i = theirs.begin(), e = theirs.begin() + theirEndIdx; i != e; ++i) {
605 assert(!i->intersectionPoint);
606 d = mine.insert(d, *i);
610 assert(theirs[theirStartIdx].intersectionPoint);
611 assert(theirs[theirEndIdx].intersectionPoint);
612 theirs[theirStartIdx].intersectionPoint =
false;
613 theirs[theirEndIdx].intersectionPoint =
false;
625 while (myStartIdx != Vertex::None) {
628 uint32_t myNextIdx = (myStartIdx + 1) % mine.size();
631 uint32_t theirNextIdx = (theirStartIdx + 1) % theirs.size();
634 bool extractable =
false;
636 if (myNextIdx == myEndIdx) {
637 if (
getVertexLocation(theirs[theirNextIdx].position, mine) == Location::Inside) {
647 Polygon& dest = outputs.emplace_back();
649 if (myEndIdx < myStartIdx) {
650 dest.points.insert(dest.points.end(), mine.begin() + myStartIdx, mine.end());
651 dest.points.insert(dest.points.end(), mine.begin(), mine.begin() + myEndIdx + 1);
654 dest.points.insert(dest.points.end(), mine.begin() + myStartIdx, mine.begin() + myEndIdx + 1);
657 if (theirStartIdx < theirEndIdx) {
658 std::reverse_copy(theirs.begin() + theirStartIdx + 1, theirs.begin() + theirEndIdx, std::back_inserter(dest.points));
661 std::reverse_copy(theirs.begin(), theirs.begin() + theirEndIdx, std::back_inserter(dest.points));
662 std::reverse_copy(theirs.begin() + theirStartIdx + 1, theirs.end(), std::back_inserter(dest.points));
664 assert(mine[myStartIdx].intersectionPoint);
665 assert(mine[myEndIdx].intersectionPoint);
666 mine[myStartIdx].intersectionPoint =
false;
667 mine[myEndIdx].intersectionPoint =
false;
669 assert(theirs[theirStartIdx].intersectionPoint);
670 assert(theirs[theirEndIdx].intersectionPoint);
671 theirs[theirStartIdx].intersectionPoint =
false;
672 theirs[theirEndIdx].intersectionPoint =
false;
676 if (myEndIdx != Vertex::None) {
677 assert(myEndIdx != myStartIdx);
684 myNextIdx = (myStartIdx + 1) % mine.size();
686 assert(mine[myStartIdx].intersectionPoint);
687 assert(mine[myEndIdx].intersectionPoint);
688 mine[myStartIdx].intersectionPoint =
false;
689 mine[myEndIdx].intersectionPoint =
false;
691 if (myNextIdx < myEndIdx) {
692 mine.erase(mine.begin() + myNextIdx, mine.begin() + myEndIdx);
694 else if (myNextIdx > myEndIdx) {
695 mine.erase(mine.begin() + myNextIdx, mine.end());
696 mine.erase(mine.begin(), mine.begin() + myEndIdx);
699 theirNextIdx = (theirFollowUpIdx + 1) % theirs.size();
701 auto d = mine.begin() + myNextIdx;
703 if (theirNextIdx <= theirPreviousIdx) {
704 for (
auto i = theirs.begin() + theirNextIdx, e = theirs.begin() + theirPreviousIdx; i != e; ++i) {
705 assert(!i->intersectionPoint);
706 d = mine.insert(d, *i);
710 for (
auto i = theirs.begin() + theirNextIdx, e = theirs.end(); i != e; ++i) {
711 assert(!i->intersectionPoint);
712 d = mine.insert(d, *i);
714 for (
auto i = theirs.begin(), e = theirs.begin() + theirPreviousIdx; i != e; ++i) {
715 assert(!i->intersectionPoint);
716 d = mine.insert(d, *i);
719 assert(theirs[theirFollowUpIdx].intersectionPoint);
720 assert(theirs[theirPreviousIdx].intersectionPoint);
721 theirs[theirFollowUpIdx].intersectionPoint =
false;
722 theirs[theirPreviousIdx].intersectionPoint =
false;
731 Polygon& dest = outputs.emplace_back();
733 dest.points.insert(dest.points.end(), mine.begin(), mine.end());
735 else if (isPointInside(polygon.points[0])) {
740 Polygon& dest = outputs.emplace_back();
744 dest.points.insert(dest.points.end(), mine.begin(), mine.begin() + myIdx + 1);
745 std::reverse_copy(theirs.begin(), theirs.begin() + theirIdx + 1, std::back_inserter(dest.points));
746 std::reverse_copy(theirs.begin() + theirIdx, theirs.end(), std::back_inserter(dest.points));
747 dest.points.insert(dest.points.end(), mine.begin() + myIdx, mine.end());
749 else if (!polygon.isPointInside(points[0])) {
763 return calcSignedArea(pointList.begin(), pointList.end()) < 0.0f;
772bool Cogs::Geometry::Polygon::isPointInside(
const glm::vec3& p,
const PointList& pointList) {
773 if (pointList.size() > 2) {
774 glm::vec3 pEnd = p + glm::vec3(1000000.0f, 0.0, 0.0f);
775 glm::vec3 start = pointList.back();
776 glm::vec3 intersectionpoint;
777 int intersectioncount = 0;
779 for (
auto i = pointList.begin(), e = pointList.end(); i != e; ++i) {
782 if (start.y > end.y) {
783 if ((p.y >= end.y) && (p.y <= start.y)) {
784 intersectioncount += calcLineLineIntersectionPoint(p, pEnd, start, end, intersectionpoint);
787 else if (start.y < end.y) {
788 if ((p.y >= start.y) && (p.y <= end.y)) {
789 intersectioncount += calcLineLineIntersectionPoint(p, pEnd, start, end, intersectionpoint);
794 if (intersectioncount & 1) {
810 if ((end - start) > 2) {
811 glm::vec3 prev = *(end - 1);
813 for (
auto i = start; i != end; ++i) {
814 glm::vec3 current = *i;
816 area += (prev.x * current.y) - (current.x * prev.y);
Generic 2D concave or convex polygon.
void setPoints(const PointList &pointList)
Assigns a new set of points to this polygon.
static float calcSignedArea(const PointList::const_iterator &start, const PointList::const_iterator &end)
Calculates the signed area of the given list of points.
static bool isClockwise(const PointList &pointList)
Helper function for testing the orientation of the given point list.
void fixUp()
Ensures winding is clockwise and attempts to unwrap self-intersecting sections.
List add(const Polygon &polygon) const
Adds the specified polygon to this one.
List subtract(const Polygon &polygon) const
Subtracts the specified polygon from this one.
Contains geometry calculations and generation.
uint32_t findNextIntersection(const Vertex::List &vertices, uint32_t startIdx, bool wrap=true)
Searches the provided list of vertices looking for the next one that is marked as being an intersecti...
int calcLineSide(const glm::vec3 &start, const glm::vec3 &end, const glm::vec3 &point)
Calculates which side of the line (start-end) the given point lies.
void removeDuplicates(Vertex::List &vertices)
Removes any adjacent vertices that are within five millimetres of their neighbours.
bool findClosestVertices(const Vertex::List &mine, const Vertex::List &theirs, uint32_t &myPoint, uint32_t &theirPoint)
Finds the closest vertices from the two polygons.
bool generateIntersectionPoints(Vertex::List &mine, Vertex::List &theirs)
Inserts additional vertices into the two provided lists at all the intersection points of the two pol...
Location getVertexLocation(const Vertex &vertex, const Vertex::List &vertices)
Determines whether the given vertex is located inside the polygon defined by the list of vertices.
bool veryClose(const glm::vec3 &lhs, const glm::vec3 &rhs)
Tests whether the two points are within one centimetre of each other.
uint32_t findMatchingVertex(const glm::vec3 &vertex, const Vertex::List &vertices)
Searches for the vertex at the specified location.
Location findDivergenceForwards(const Vertex::List &mine, const Vertex::List &theirs, uint32_t myIdx, uint32_t theirIdx, uint32_t &myNextIdx, uint32_t &theirNextIdx)
Trace a path from the two specified points (which are located at the same position) until they diverg...