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) {
37 const glm::vec3 cEpsilon(0.01f, 0.01f, 0.01f);
39 return glm::all(glm::epsilonEqual(lhs, rhs, cEpsilon));
46 Vertex prev = vertices.back();
48 for (uint32_t idx = 0; idx < vertices.size(); ) {
49 Vertex current = vertices[idx];
51 if (
veryClose(prev.position, current.position)) {
52 if (current.intersectionPoint) {
54 vertices.erase(vertices.begin() + (idx - 1));
63 vertices.erase(vertices.begin() + idx);
78 int calcLineSide(
const glm::vec3& start,
const glm::vec3& end,
const glm::vec3& point) {
79 float d = ((end.x - start.x) * (point.y - start.y)) - ((end.y - start.y) * (point.x - start.x));
94 for (
auto i = vertices.begin(), e = vertices.end(); i != e; ++i) {
95 if (i->position == vertex) {
96 return static_cast<uint32_t
>(i - vertices.begin());
107 glm::vec3 vEnd(vertex.position.x + 1000000.0f, vertex.position.y, 0.0f);
108 glm::vec3 start = vertices.back();
109 glm::vec3 intersectionpoint;
110 int intersectioncount = 0;
112 for (
auto i = vertices.begin(), e = vertices.end(); i != e; ++i) {
115 if ((vertex.position != start) && (vertex.position != end)) {
116 if (start.y > end.y) {
117 if ((vertex.position.y >= end.y) && (vertex.position.y < start.y)) {
118 intersectioncount += Cogs::Geometry::calcLineLineIntersectionPoint(vertex.position, vEnd, start, end, intersectionpoint);
121 else if (start.y < end.y) {
122 if ((vertex.position.y >= start.y) && (vertex.position.y < end.y)) {
123 intersectioncount += Cogs::Geometry::calcLineLineIntersectionPoint(vertex.position, vEnd, start, end, intersectionpoint);
129 return (intersectioncount & 1) ? Location::Inside : Location::Outside;
138 Location
findDivergenceForwards(
const Vertex::List& mine,
const Vertex::List& theirs, uint32_t myIdx, uint32_t theirIdx, uint32_t& myNextIdx, uint32_t& theirNextIdx) {
139 uint32_t myPointCount =
static_cast<uint32_t
>(mine.size());
140 uint32_t theirPointCount =
static_cast<uint32_t
>(theirs.size());
141 glm::vec3 myPrevious;
142 glm::vec3 myCurrent = mine[myIdx].position;
144 if (mine[(myIdx + 1) % myPointCount].position == theirs[(theirIdx + 1) % theirPointCount].position) {
146 myPrevious = myCurrent;
149 theirNextIdx = theirIdx;
150 myIdx = (myIdx + 1) % myPointCount;
151 theirIdx = (theirIdx + 1) % theirPointCount;
153 myCurrent = mine[myIdx];
155 while (myCurrent == theirs[theirIdx].position);
157 else if (mine[(myIdx + 1) % myPointCount].position == theirs[std::min(theirIdx - 1, theirPointCount - 1)].position) {
159 myPrevious = myCurrent;
162 theirNextIdx = theirIdx;
163 myIdx = (myIdx + 1) % myPointCount;
164 theirIdx = std::min(theirIdx - 1, theirPointCount - 1);
166 myCurrent = mine[myIdx];
168 while (myCurrent == theirs[theirIdx].position);
172 theirNextIdx = theirIdx;
173 myPrevious = myCurrent;
174 myCurrent = mine[(myIdx + 1) % myPointCount].position;
177 float shortestDist = std::numeric_limits< float >::max();
178 glm::vec3 closestPoint;
179 glm::vec3 theirPrevious = theirs.back().position;
180 glm::vec3 intersectionPoint;
182 for (theirIdx = 0; theirIdx < theirPointCount; ++theirIdx) {
183 glm::vec3 theirCurrent = theirs[theirIdx].position;
185 if (Cogs::Geometry::calcLineLineIntersectionPoint(myPrevious, myCurrent, theirPrevious, theirCurrent, intersectionPoint)) {
186 if ((intersectionPoint != myPrevious) && (intersectionPoint != myCurrent)) {
187 float dist = glm::distance(myPrevious, intersectionPoint);
189 if (dist < shortestDist) {
191 closestPoint = intersectionPoint;
195 theirPrevious = theirCurrent;
200 if (shortestDist < std::numeric_limits< float >::max()) {
201 testPoint = glm::normalize(closestPoint - myCurrent) * (shortestDist * 0.5f);
204 testPoint = (myCurrent + myPrevious) * 0.5f;
217 for (
auto mi = mine.begin(), me = mine.end(); mi != me; ++mi) {
218 for (
auto ti = theirs.begin(), te = theirs.end(); ti != te; ++ti) {
227 glm::vec3 myPrevious = mine.back().position;
230 for (uint32_t myIdx = 0, myPointCount =
static_cast<uint32_t
>(mine.size()); myIdx < myPointCount; ) {
231 glm::vec3 myCurrent = mine[myIdx].position;
232 glm::vec3 intersectionPoint;
233 float shortestDist = std::numeric_limits< float >::max();
234 glm::vec3 closestPoint;
235 uint32_t theirBestIdx = Vertex::None;
236 glm::vec3 theirPrevious = theirs.back().position;
237 glm::vec3 theirCurrent;
239 for (uint32_t theirIdx = 0, theirPointCount =
static_cast<uint32_t
>(theirs.size()); theirIdx < theirPointCount; ++theirIdx) {
240 theirCurrent = theirs[theirIdx].position;
242 if (Cogs::Geometry::calcLineLineIntersectionPoint(myPrevious, myCurrent, theirPrevious, theirCurrent, intersectionPoint)) {
244 float dist = glm::distance(myPrevious, intersectionPoint);
246 if (dist < shortestDist) {
248 closestPoint = intersectionPoint;
249 theirBestIdx = theirIdx;
253 theirPrevious = theirCurrent;
255 if (theirBestIdx != Vertex::None) {
258 if (match == Vertex::None) {
259 theirs.insert(theirs.begin() + theirBestIdx,
Vertex(closestPoint,
true));
262 theirs[match] =
Vertex(closestPoint,
true);
266 if (match == Vertex::None) {
267 mine.insert(mine.begin() + myIdx,
Vertex(closestPoint,
true));
270 myPrevious = closestPoint;
274 mine[match] =
Vertex(closestPoint,
true);
278 myPrevious = myCurrent;
284 for (
auto mi = mine.begin(), me = mine.end(); mi != me; ++mi) {
285 mi->intersectionPoint =
false;
290 for (
auto ti = theirs.begin(), te = theirs.end(); ti != te; ++ti) {
291 ti->intersectionPoint =
false;
295 uint32_t myPointCount =
static_cast<uint32_t
>(mine.size());
300 myIdx = ++myIdx % myPointCount;
309 uint32_t myLastIdx = myIdx;
315 for (myIdx = ++myIdx % myPointCount; myIdx != myLastIdx; ) {
318 if (theirIdx != Vertex::None) {
320 uint32_t theirNextIdx;
323 if ((myIdx != myNextIdx) || (location == Location::Inside)) {
324 if (mine[myIdx].intersectionPoint) {
327 mine[myIdx].intersectionPoint =
true;
328 theirs[theirIdx].intersectionPoint =
true;
331 if (location == Location::Outside) {
332 mine[myNextIdx].intersectionPoint =
true;
333 theirs[theirNextIdx].intersectionPoint =
true;
334 myIdx = (myNextIdx + 1) % myPointCount;
339 for (myIdx = (myNextIdx + 1) % myPointCount; ; ) {
342 if (theirIdx != Vertex::None) {
343 if (
findDivergenceForwards(mine, theirs, myIdx, theirIdx, myNextIdx, theirNextIdx) == Location::Outside) {
344 mine[myNextIdx].intersectionPoint =
true;
345 theirs[theirNextIdx].intersectionPoint =
true;
346 myIdx = (myNextIdx + 1) % myPointCount;
350 myIdx = (myNextIdx + 1) % myPointCount;
353 myIdx = ++myIdx % myPointCount;
359 myIdx = ++myIdx % myPointCount;
363 myIdx = ++myIdx % myPointCount;
366 assert(!(generated & 1));
373 bool findClosestVertices(
const Vertex::List& mine,
const Vertex::List& theirs, uint32_t& myPoint, uint32_t& theirPoint) {
374 float closestDistance = std::numeric_limits< float >::max();
375 uint32_t theirSize =
static_cast<uint32_t
>(theirs.size());
377 myPoint = Vertex::None;
378 theirPoint = Vertex::None;
380 for (uint32_t myIdx = 0, mySize =
static_cast<uint32_t
>(mine.size()); myIdx < mySize; ++myIdx) {
381 glm::vec3 p1 = mine[myIdx].position;
383 for (uint32_t theirIdx = 0; theirIdx < theirSize; ++theirIdx) {
384 float dist = Cogs::Geometry::distanceSqr(p1, theirs[theirIdx].position);
386 if (dist < closestDistance) {
388 theirPoint = theirIdx;
389 closestDistance = dist;
393 return (myPoint != Vertex::None) && (theirPoint != Vertex::None);
401 uint32_t vertexCount =
static_cast<uint32_t
>(vertices.size());
402 uint32_t idx = startIdx;
404 for (uint32_t counter = wrap ? vertexCount : (vertexCount - startIdx - 1); --counter; ) {
405 idx = (idx + 1) % vertexCount;
407 if (vertices[idx].intersectionPoint) {
408 assert(idx != startIdx);
416Cogs::Geometry::Polygon::Polygon(
const PointList& initialPoints) {
428 if (!isClockwise(points)) {
429 std::reverse(points.begin(), points.end());
433void Cogs::Geometry::Polygon::addPoint(
const glm::vec3& point) {
434 points.push_back(point);
441 if (!isClockwise(points)) {
442 std::reverse(points.begin(), points.end());
445 glm::vec3 prev = points.back();
446 glm::vec3 intersectionPoint;
448 for (uint32_t outerStartIdx =
static_cast<uint32_t
>(points.size() - 1), outerEndIdx = 0; outerEndIdx < (points.size() - 1); ) {
449 for (uint32_t innerStartIdx = outerEndIdx + 1, innerEndIdx = innerStartIdx + 1; innerEndIdx < (points.size() - 1); ) {
450 if (Cogs::Geometry::calcLineLineIntersectionPoint(points[outerStartIdx], points[outerEndIdx], points[innerStartIdx], points[innerEndIdx], intersectionPoint)) {
451 points.insert(points.begin() + innerEndIdx++, intersectionPoint);
452 points.insert(points.begin() + outerEndIdx, intersectionPoint);
454 float inner = calcSignedArea(points.begin() + outerEndIdx, points.begin() + innerEndIdx + 1);
457 prev = points[outerEndIdx - 1];
459 for (uint32_t idx = innerEndIdx; idx != outerEndIdx; idx = (idx + 1) % points.size()) {
460 glm::vec3 current = points[idx];
462 outer += (prev.x * current.y) - (current.x * prev.y);
468 if (std::fabs(inner) < std::fabs(outer)) {
469 points.erase(points.begin() + outerEndIdx, points.begin() + innerEndIdx);
472 points.erase(points.begin() + innerEndIdx, points.end());
473 points.erase(points.begin(), points.begin() + outerEndIdx);
481 else if (outer < 0.0f) {
483 std::reverse(points.begin() + outerEndIdx, points.begin() + innerEndIdx);
491 innerStartIdx = innerEndIdx++;
492 innerEndIdx %= points.size();
495 outerStartIdx = outerEndIdx++;
496 outerEndIdx %= points.size();
500void Cogs::Geometry::Polygon::clearPoints() {
531 if ((points.size() > 2) && ( polygon.points.size() > 2)) {
536 std::copy(points.begin(), points.end(), std::back_inserter(mine));
537 std::copy(polygon.points.begin(), polygon.points.end(), std::back_inserter(theirs));
557 while (myStartIdx != Vertex::None) {
564 uint32_t myNextIdx = (myStartIdx + 1) % mine.size();
565 uint32_t theirNextIdx = (theirStartIdx + 1) % theirs.size();
566 bool removable =
false;
568 if (myNextIdx == myEndIdx) {
569 if (
getVertexLocation(theirs[theirNextIdx].position, mine) == Location::Inside) {
579 assert(mine[myStartIdx].intersectionPoint);
580 assert(mine[myEndIdx].intersectionPoint);
581 mine[myStartIdx].intersectionPoint =
false;
582 mine[myEndIdx].intersectionPoint =
false;
584 if (myNextIdx < myEndIdx) {
585 mine.erase(mine.begin() + myNextIdx, mine.begin() + myEndIdx);
587 else if (myNextIdx > myEndIdx) {
588 mine.erase(mine.begin() + myNextIdx, mine.end());
589 mine.erase(mine.begin(), mine.begin() + myEndIdx);
590 myNextIdx =
static_cast<uint32_t
>(mine.size());
593 auto d = mine.begin() + myNextIdx;
595 if (theirNextIdx < theirEndIdx) {
596 for (
auto i = theirs.begin() + theirNextIdx, e = theirs.begin() + theirEndIdx; i != e; ++i) {
597 assert(!i->intersectionPoint);
598 d = mine.insert(d, *i);
601 else if (theirNextIdx > theirEndIdx) {
602 for (
auto i = theirs.begin() + theirNextIdx, e = theirs.end(); i != e; ++i) {
603 assert(!i->intersectionPoint);
604 d = mine.insert(d, *i);
606 for (
auto i = theirs.begin(), e = theirs.begin() + theirEndIdx; i != e; ++i) {
607 assert(!i->intersectionPoint);
608 d = mine.insert(d, *i);
612 assert(theirs[theirStartIdx].intersectionPoint);
613 assert(theirs[theirEndIdx].intersectionPoint);
614 theirs[theirStartIdx].intersectionPoint =
false;
615 theirs[theirEndIdx].intersectionPoint =
false;
627 while (myStartIdx != Vertex::None) {
630 uint32_t myNextIdx = (myStartIdx + 1) % mine.size();
633 uint32_t theirNextIdx = (theirStartIdx + 1) % theirs.size();
636 bool extractable =
false;
638 if (myNextIdx == myEndIdx) {
639 if (
getVertexLocation(theirs[theirNextIdx].position, mine) == Location::Inside) {
649 Polygon& dest = outputs.emplace_back();
651 if (myEndIdx < myStartIdx) {
652 dest.points.insert(dest.points.end(), mine.begin() + myStartIdx, mine.end());
653 dest.points.insert(dest.points.end(), mine.begin(), mine.begin() + myEndIdx + 1);
656 dest.points.insert(dest.points.end(), mine.begin() + myStartIdx, mine.begin() + myEndIdx + 1);
659 if (theirStartIdx < theirEndIdx) {
660 std::reverse_copy(theirs.begin() + theirStartIdx + 1, theirs.begin() + theirEndIdx, std::back_inserter(dest.points));
663 std::reverse_copy(theirs.begin(), theirs.begin() + theirEndIdx, std::back_inserter(dest.points));
664 std::reverse_copy(theirs.begin() + theirStartIdx + 1, theirs.end(), std::back_inserter(dest.points));
666 assert(mine[myStartIdx].intersectionPoint);
667 assert(mine[myEndIdx].intersectionPoint);
668 mine[myStartIdx].intersectionPoint =
false;
669 mine[myEndIdx].intersectionPoint =
false;
671 assert(theirs[theirStartIdx].intersectionPoint);
672 assert(theirs[theirEndIdx].intersectionPoint);
673 theirs[theirStartIdx].intersectionPoint =
false;
674 theirs[theirEndIdx].intersectionPoint =
false;
678 if (myEndIdx != Vertex::None) {
679 assert(myEndIdx != myStartIdx);
686 myNextIdx = (myStartIdx + 1) % mine.size();
688 assert(mine[myStartIdx].intersectionPoint);
689 assert(mine[myEndIdx].intersectionPoint);
690 mine[myStartIdx].intersectionPoint =
false;
691 mine[myEndIdx].intersectionPoint =
false;
693 if (myNextIdx < myEndIdx) {
694 mine.erase(mine.begin() + myNextIdx, mine.begin() + myEndIdx);
696 else if (myNextIdx > myEndIdx) {
697 mine.erase(mine.begin() + myNextIdx, mine.end());
698 mine.erase(mine.begin(), mine.begin() + myEndIdx);
701 theirNextIdx = (theirFollowUpIdx + 1) % theirs.size();
703 auto d = mine.begin() + myNextIdx;
705 if (theirNextIdx <= theirPreviousIdx) {
706 for (
auto i = theirs.begin() + theirNextIdx, e = theirs.begin() + theirPreviousIdx; i != e; ++i) {
707 assert(!i->intersectionPoint);
708 d = mine.insert(d, *i);
712 for (
auto i = theirs.begin() + theirNextIdx, e = theirs.end(); i != e; ++i) {
713 assert(!i->intersectionPoint);
714 d = mine.insert(d, *i);
716 for (
auto i = theirs.begin(), e = theirs.begin() + theirPreviousIdx; i != e; ++i) {
717 assert(!i->intersectionPoint);
718 d = mine.insert(d, *i);
721 assert(theirs[theirFollowUpIdx].intersectionPoint);
722 assert(theirs[theirPreviousIdx].intersectionPoint);
723 theirs[theirFollowUpIdx].intersectionPoint =
false;
724 theirs[theirPreviousIdx].intersectionPoint =
false;
733 Polygon& dest = outputs.emplace_back();
735 dest.points.insert(dest.points.end(), mine.begin(), mine.end());
737 else if (isPointInside(polygon.points[0])) {
742 Polygon& dest = outputs.emplace_back();
746 dest.points.insert(dest.points.end(), mine.begin(), mine.begin() + myIdx + 1);
747 std::reverse_copy(theirs.begin(), theirs.begin() + theirIdx + 1, std::back_inserter(dest.points));
748 std::reverse_copy(theirs.begin() + theirIdx, theirs.end(), std::back_inserter(dest.points));
749 dest.points.insert(dest.points.end(), mine.begin() + myIdx, mine.end());
751 else if (!polygon.isPointInside(points[0])) {
765 return calcSignedArea(pointList.begin(), pointList.end()) < 0.0f;
774bool Cogs::Geometry::Polygon::isPointInside(
const glm::vec3& p,
const PointList& pointList) {
775 if (pointList.size() > 2) {
776 glm::vec3 pEnd = p + glm::vec3(1000000.0f, 0.0, 0.0f);
777 glm::vec3 start = pointList.back();
778 glm::vec3 intersectionpoint;
779 int intersectioncount = 0;
781 for (
auto i = pointList.begin(), e = pointList.end(); i != e; ++i) {
784 if (start.y > end.y) {
785 if ((p.y >= end.y) && (p.y <= start.y)) {
786 intersectioncount += calcLineLineIntersectionPoint(p, pEnd, start, end, intersectionpoint);
789 else if (start.y < end.y) {
790 if ((p.y >= start.y) && (p.y <= end.y)) {
791 intersectioncount += calcLineLineIntersectionPoint(p, pEnd, start, end, intersectionpoint);
796 if (intersectioncount & 1) {
812 if ((end - start) > 2) {
813 glm::vec3 prev = *(end - 1);
815 for (
auto i = start; i != end; ++i) {
816 glm::vec3 current = *i;
818 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...