Cogs.Core
Polygon.cpp
1#include "Polygon.h"
2
3#include <glm/glm.hpp>
4#include <glm/gtc/epsilon.hpp>
5
6#include <algorithm>
7#include <iterator>
8
9namespace Cogs::Geometry {
10 enum Location {
11 Inside = -1,
12 Outside = 1
13 };
14
15 struct Vertex {
16 using List = std::vector<Vertex>;
17 static constexpr uint32_t None = 0xFFFFFFFF;
18
19 glm::vec3 position;
20 bool intersectionPoint;
21
22 Vertex(const glm::vec3& vec, bool intersection = false) : position(vec), intersectionPoint(intersection)
23 {
24 }
25
26 operator glm::vec3() const {
27 return position;
28 }
29 };
30
34 bool veryClose(const glm::vec3& lhs, const glm::vec3& rhs) {
35 static constexpr glm::vec3 cEpsilon(0.01f, 0.01f, 0.01f);
36
37 return glm::all(glm::epsilonEqual(lhs, rhs, cEpsilon));
38 }
39
43 void removeDuplicates(Vertex::List& vertices) {
44 Vertex prev = vertices.back();
45
46 for (uint32_t idx = 0; idx < vertices.size(); ) {
47 Vertex current = vertices[idx];
48
49 if (veryClose(prev.position, current.position)) {
50 if (current.intersectionPoint) {
51 if (idx) {
52 vertices.erase(vertices.begin() + (idx - 1));
53 }
54 else {
55 vertices.pop_back();
56 ++idx;
57 }
58 prev = current;
59 }
60 else {
61 vertices.erase(vertices.begin() + idx);
62 }
63 }
64 else {
65 prev = current;
66 ++idx;
67 }
68 }
69 }
70
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));
78
79 if (d < 0.0f) {
80 return -1;
81 }
82 else if (d > 0.0f) {
83 return 1;
84 }
85 return 0;
86 }
87
91 uint32_t findMatchingVertex(const glm::vec3& vertex, const Vertex::List& vertices) {
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());
95 }
96 }
97 return Vertex::None;
98 }
99
104 Location getVertexLocation(const Vertex& vertex, const Vertex::List& vertices) {
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;
109
110 for (auto i = vertices.begin(), e = vertices.end(); i != e; ++i) {
111 glm::vec3 end = *i;
112
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);
117 }
118 }
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);
122 }
123 }
124 }
125 start = end;
126 }
127 return (intersectioncount & 1) ? Location::Inside : Location::Outside;
128 }
129
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;
141
142 if (mine[(myIdx + 1) % myPointCount].position == theirs[(theirIdx + 1) % theirPointCount].position) {
143 do {
144 myPrevious = myCurrent;
145
146 myNextIdx = myIdx;
147 theirNextIdx = theirIdx;
148 myIdx = (myIdx + 1) % myPointCount;
149 theirIdx = (theirIdx + 1) % theirPointCount;
150
151 myCurrent = mine[myIdx];
152 }
153 while (myCurrent == theirs[theirIdx].position);
154 }
155 else if (mine[(myIdx + 1) % myPointCount].position == theirs[std::min(theirIdx - 1, theirPointCount - 1)].position) {
156 do {
157 myPrevious = myCurrent;
158
159 myNextIdx = myIdx;
160 theirNextIdx = theirIdx;
161 myIdx = (myIdx + 1) % myPointCount;
162 theirIdx = std::min(theirIdx - 1, theirPointCount - 1);
163
164 myCurrent = mine[myIdx];
165 }
166 while (myCurrent == theirs[theirIdx].position);
167 }
168 else {
169 myNextIdx = myIdx;
170 theirNextIdx = theirIdx;
171 myPrevious = myCurrent;
172 myCurrent = mine[(myIdx + 1) % myPointCount].position;
173 }
174
175 float shortestDist = std::numeric_limits< float >::max();
176 glm::vec3 closestPoint;
177 glm::vec3 theirPrevious = theirs.back().position;
178 glm::vec3 intersectionPoint;
179
180 for (theirIdx = 0; theirIdx < theirPointCount; ++theirIdx) {
181 glm::vec3 theirCurrent = theirs[theirIdx].position;
182
183 if (Cogs::Geometry::calcLineLineIntersectionPoint(myPrevious, myCurrent, theirPrevious, theirCurrent, intersectionPoint)) {
184 if ((intersectionPoint != myPrevious) && (intersectionPoint != myCurrent)) {
185 float dist = glm::distance(myPrevious, intersectionPoint);
186
187 if (dist < shortestDist) {
188 shortestDist = dist;
189 closestPoint = intersectionPoint;
190 }
191 }
192 }
193 theirPrevious = theirCurrent;
194 }
195
196 glm::vec3 testPoint;
197
198 if (shortestDist < std::numeric_limits< float >::max()) {
199 testPoint = glm::normalize(closestPoint - myCurrent) * (shortestDist * 0.5f);
200 }
201 else {
202 testPoint = (myCurrent + myPrevious) * 0.5f;
203 }
204 return getVertexLocation(testPoint, theirs);
205 }
206
211 bool generateIntersectionPoints(Vertex::List& mine, Vertex::List& theirs) {
212 removeDuplicates(mine);
213
214 // Adjust the position of any points of theirs that are close to ours...
215 for (auto mi = mine.begin(), me = mine.end(); mi != me; ++mi) {
216 for (auto ti = theirs.begin(), te = theirs.end(); ti != te; ++ti) {
217 if (veryClose(*mi, *ti)) {
218 *ti = *mi;
219 }
220 }
221 }
222
223 removeDuplicates(theirs);
224
225 glm::vec3 myPrevious = mine.back().position;
226
227 // Generate new points for any intersections between the two polygons...
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;
236
237 for (uint32_t theirIdx = 0, theirPointCount = static_cast<uint32_t>(theirs.size()); theirIdx < theirPointCount; ++theirIdx) {
238 theirCurrent = theirs[theirIdx].position;
239
240 if (Cogs::Geometry::calcLineLineIntersectionPoint(myPrevious, myCurrent, theirPrevious, theirCurrent, intersectionPoint)) {
241 if ((findMatchingVertex(intersectionPoint, mine) == Vertex::None) || (findMatchingVertex(intersectionPoint, theirs) == Vertex::None)) {
242 float dist = glm::distance(myPrevious, intersectionPoint);
243
244 if (dist < shortestDist) {
245 shortestDist = dist;
246 closestPoint = intersectionPoint;
247 theirBestIdx = theirIdx;
248 }
249 }
250 }
251 theirPrevious = theirCurrent;
252 }
253 if (theirBestIdx != Vertex::None) {
254 uint32_t match = findMatchingVertex(closestPoint, theirs);
255
256 if (match == Vertex::None) {
257 theirs.insert(theirs.begin() + theirBestIdx, Vertex(closestPoint, true));
258 }
259 else {
260 theirs[match] = Vertex(closestPoint, true);
261 }
262
263 match = findMatchingVertex(closestPoint, mine);
264 if (match == Vertex::None) {
265 mine.insert(mine.begin() + myIdx, Vertex(closestPoint, true));
266 myPointCount++;
267 myIdx++;
268 myPrevious = closestPoint;
269 continue;
270 }
271 else {
272 mine[match] = Vertex(closestPoint, true);
273 }
274 }
275 myIdx++;
276 myPrevious = myCurrent;
277 }
278
279 // Remove any duplicates that are now present in our vertices, and
280 // clear all intersectionPoint flags from generated points...
281 removeDuplicates(mine);
282 for (auto mi = mine.begin(), me = mine.end(); mi != me; ++mi) {
283 mi->intersectionPoint = false;
284 }
285
286 // And do the same for their vertices...
287 removeDuplicates(theirs);
288 for (auto ti = theirs.begin(), te = theirs.end(); ti != te; ++ti) {
289 ti->intersectionPoint = false;
290 }
291
292 uint32_t myIdx = 0;
293 uint32_t myPointCount = static_cast<uint32_t>(mine.size());
294 int generated = 0;
295
296 // Find one of my vertices that is outside the other polygon...
297 while ((getVertexLocation(mine[myIdx], theirs) != Location::Outside) || (findMatchingVertex(mine[myIdx], theirs) != Vertex::None)) {
298 myIdx = ++myIdx % myPointCount;
299
300 if (myIdx == 0) {
301 // We've walked through all our vertices and couldn't find
302 // one that is completely outside the other polygon.
303 return false;
304 }
305 }
306
307 uint32_t myLastIdx = myIdx;
308
309 // This outer loop should only deal with vertices that are outside the other
310 // polygon. When we encounter a vertex that is present on both polygons and
311 // our path immediately or eventually enters the other polygon, we will enter
312 // the inner loop until we exit the polygon again.
313 for (myIdx = ++myIdx % myPointCount; myIdx != myLastIdx; ) {
314 uint32_t theirIdx = findMatchingVertex(mine[myIdx], theirs);
315
316 if (theirIdx != Vertex::None) {
317 uint32_t myNextIdx;
318 uint32_t theirNextIdx;
319 Location location = findDivergenceForwards(mine, theirs, myIdx, theirIdx, myNextIdx, theirNextIdx);
320
321 if ((myIdx != myNextIdx) || (location == Location::Inside)) {
322 if (mine[myIdx].intersectionPoint) {
323 return generated;
324 }
325 mine[myIdx].intersectionPoint = true;
326 theirs[theirIdx].intersectionPoint = true;
327 generated++;
328
329 if (location == Location::Outside) {
330 mine[myNextIdx].intersectionPoint = true;
331 theirs[theirNextIdx].intersectionPoint = true;
332 myIdx = (myNextIdx + 1) % myPointCount;
333 generated++;
334 }
335 else {
336 // Now we will loop through our vertices that are inside the other polygon until we break out...
337 for (myIdx = (myNextIdx + 1) % myPointCount; ; ) {
338 theirIdx = findMatchingVertex(mine[myIdx], theirs);
339
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;
345 generated++;
346 break;
347 }
348 myIdx = (myNextIdx + 1) % myPointCount;
349 }
350 else {
351 myIdx = ++myIdx % myPointCount;
352 }
353 }
354 }
355 }
356 else {
357 myIdx = ++myIdx % myPointCount;
358 }
359 }
360 else {
361 myIdx = ++myIdx % myPointCount;
362 }
363 }
364 assert(!(generated & 1));
365 return generated;
366 }
367
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());
374
375 myPoint = Vertex::None;
376 theirPoint = Vertex::None;
377
378 for (uint32_t myIdx = 0, mySize = static_cast<uint32_t>(mine.size()); myIdx < mySize; ++myIdx) {
379 glm::vec3 p1 = mine[myIdx].position;
380
381 for (uint32_t theirIdx = 0; theirIdx < theirSize; ++theirIdx) {
382 float dist = Cogs::Geometry::distanceSqr(p1, theirs[theirIdx].position);
383
384 if (dist < closestDistance) {
385 myPoint = myIdx;
386 theirPoint = theirIdx;
387 closestDistance = dist;
388 }
389 }
390 }
391 return (myPoint != Vertex::None) && (theirPoint != Vertex::None);
392 }
393
398 uint32_t findNextIntersection(const Vertex::List& vertices, uint32_t startIdx, bool wrap = true) {
399 uint32_t vertexCount = static_cast<uint32_t>(vertices.size());
400 uint32_t idx = startIdx;
401
402 for (uint32_t counter = wrap ? vertexCount : (vertexCount - startIdx - 1); --counter; ) {
403 idx = (idx + 1) % vertexCount;
404
405 if (vertices[idx].intersectionPoint) {
406 assert(idx != startIdx);
407 return idx;
408 }
409 }
410 return Vertex::None;
411 }
412}
413
414Cogs::Geometry::Polygon::Polygon(const PointList& initialPoints) {
415 setPoints(initialPoints);
416}
417
423void Cogs::Geometry::Polygon::setPoints(const PointList& pointList) {
424 points = pointList;
425
426 if (!isClockwise(points)) {
427 std::reverse(points.begin(), points.end());
428 }
429}
430
431void Cogs::Geometry::Polygon::addPoint(const glm::vec3& point) {
432 points.push_back(point);
433}
434
439 if (!isClockwise(points)) {
440 std::reverse(points.begin(), points.end());
441 }
442
443 glm::vec3 prev = points.back();
444 glm::vec3 intersectionPoint;
445
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);
451
452 float inner = calcSignedArea(points.begin() + outerEndIdx, points.begin() + innerEndIdx + 1);
453 float outer = 0.0f;
454
455 prev = points[outerEndIdx - 1];
456
457 for (uint32_t idx = innerEndIdx; idx != outerEndIdx; idx = (idx + 1) % points.size()) {
458 glm::vec3 current = points[idx];
459
460 outer += (prev.x * current.y) - (current.x * prev.y);
461 prev = current;
462 }
463 if (inner <= 0.0f) {
464 if (outer <= 0.0f) {
465 // Both parts are clockwise... One is inside the other, so we can delete the smaller.
466 if (std::fabs(inner) < std::fabs(outer)) {
467 points.erase(points.begin() + outerEndIdx, points.begin() + innerEndIdx);
468 }
469 else {
470 points.erase(points.begin() + innerEndIdx, points.end());
471 points.erase(points.begin(), points.begin() + outerEndIdx);
472 }
473 }
474 else {
475 // The outer part is winding counter clockwise. We will reverse it and carry on.
476 assert(false);
477 }
478 }
479 else if (outer < 0.0f) {
480 // The inner part is counter clockwise. We will reverse it and carry on.
481 std::reverse(points.begin() + outerEndIdx, points.begin() + innerEndIdx);
482 }
483 else {
484 assert(false); // Both sections are counter-clockwise.
485 }
486 break;
487 }
488 else {
489 innerStartIdx = innerEndIdx++;
490 innerEndIdx %= points.size();
491 }
492 }
493 outerStartIdx = outerEndIdx++;
494 outerEndIdx %= points.size();
495 }
496}
497
498void Cogs::Geometry::Polygon::clearPoints() {
499 points.clear();
500}
501
508Cogs::Geometry::Polygon::List Cogs::Geometry::Polygon::add(const Polygon& /*polygon*/) const {
509 // TODO. :)
510 assert(false);
511 return {};
512}
513
528Cogs::Geometry::Polygon::List Cogs::Geometry::Polygon::subtract(const Polygon& polygon) const {
529 if ((points.size() > 2) && ( polygon.points.size() > 2)) {
530 Vertex::List mine;
531 Vertex::List theirs;
532 List outputs;
533
534 std::copy(points.begin(), points.end(), std::back_inserter(mine));
535 std::copy(polygon.points.begin(), polygon.points.end(), std::back_inserter(theirs));
536
537 if (generateIntersectionPoints(mine, theirs)) {
538 uint32_t myStartIdx = mine.front().intersectionPoint ? 0 : findNextIntersection(mine, 0);
539 uint32_t myEndIdx;
540 //bool removedSection = false;
541
542 // At this point we have created any additional vertices for all intersection
543 // points between the two polygons, and marked any others that existed at
544 // intersection points accordingly.
545
546 // This first loop attempts to cut out any sections where the polygon to be subtracted
547 // intersects this polygon, but does not cut it in two. We do this by finding the next
548 // intersection on this polygon after the vertex at index myStartIdx (which is an
549 // intersection itself). We then find the matching vertices on the other polygon
550 // (theirStartIdx and theirEndIdx). Using those four vertices we try to detect that
551 // they are orientated the way we expect them to be (the vertex following myStartIdx
552 // should be inside them, or the vertex following theirStartIdx should be inside us)
553 // and if so, we replace the vertices between myStartIdx and myEndIdx with the vertices
554 // from theirEndIdx to theirStartIdx.
555 while (myStartIdx != Vertex::None) {
556 myEndIdx = findNextIntersection(mine, myStartIdx);
557
558 uint32_t theirStartIdx = findMatchingVertex(mine[myEndIdx], theirs);
559 uint32_t theirEndIdx = findMatchingVertex(mine[myStartIdx], theirs);
560
561 if (findNextIntersection(theirs, theirStartIdx) == theirEndIdx) {
562 uint32_t myNextIdx = (myStartIdx + 1) % mine.size();
563 uint32_t theirNextIdx = (theirStartIdx + 1) % theirs.size();
564 bool removable = false;
565
566 if (myNextIdx == myEndIdx) {
567 if (getVertexLocation(theirs[theirNextIdx].position, mine) == Location::Inside) {
568 removable = true;
569 }
570 }
571 else {
572 if (getVertexLocation(mine[myNextIdx].position, theirs) == Location::Inside) {
573 removable = true;
574 }
575 }
576 if (removable) {
577 assert(mine[myStartIdx].intersectionPoint);
578 assert(mine[myEndIdx].intersectionPoint);
579 mine[myStartIdx].intersectionPoint = false;
580 mine[myEndIdx].intersectionPoint = false;
581
582 if (myNextIdx < myEndIdx) {
583 mine.erase(mine.begin() + myNextIdx, mine.begin() + myEndIdx);
584 }
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());
589 }
590
591 auto d = mine.begin() + myNextIdx;
592
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);
597 }
598 }
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);
603 }
604 for (auto i = theirs.begin(), e = theirs.begin() + theirEndIdx; i != e; ++i) {
605 assert(!i->intersectionPoint);
606 d = mine.insert(d, *i);
607 }
608 }
609
610 assert(theirs[theirStartIdx].intersectionPoint);
611 assert(theirs[theirEndIdx].intersectionPoint);
612 theirs[theirStartIdx].intersectionPoint = false;
613 theirs[theirEndIdx].intersectionPoint = false;
614 //removedSection = true;
615 }
616 }
617 myStartIdx = findNextIntersection(mine, myStartIdx, false);
618 }
619
620 // Any remaining intersections should cut what remains of this polygon into
621 // two or more pieces.
622
623 myStartIdx = mine.front().intersectionPoint ? 0 : findNextIntersection(mine, 0);
624
625 while (myStartIdx != Vertex::None) {
626 myEndIdx = findNextIntersection(mine, myStartIdx);
627
628 uint32_t myNextIdx = (myStartIdx + 1) % mine.size();
629 uint32_t theirStartIdx = findMatchingVertex(mine[myStartIdx].position, theirs);
630 uint32_t theirEndIdx = findMatchingVertex(mine[myEndIdx].position, theirs);
631 uint32_t theirNextIdx = (theirStartIdx + 1) % theirs.size();
632
633 if (findNextIntersection(theirs, theirStartIdx) == theirEndIdx) {
634 bool extractable = false;
635
636 if (myNextIdx == myEndIdx) {
637 if (getVertexLocation(theirs[theirNextIdx].position, mine) == Location::Inside) {
638 extractable = true;
639 }
640 }
641 else {
642 if (getVertexLocation(mine[myNextIdx].position, theirs) == Location::Outside) {
643 extractable = true;
644 }
645 }
646 if (extractable) {
647 Polygon& dest = outputs.emplace_back();
648
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);
652 }
653 else {
654 dest.points.insert(dest.points.end(), mine.begin() + myStartIdx, mine.begin() + myEndIdx + 1);
655 }
656
657 if (theirStartIdx < theirEndIdx) {
658 std::reverse_copy(theirs.begin() + theirStartIdx + 1, theirs.begin() + theirEndIdx, std::back_inserter(dest.points));
659 }
660 else {
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));
663 }
664 assert(mine[myStartIdx].intersectionPoint);
665 assert(mine[myEndIdx].intersectionPoint);
666 mine[myStartIdx].intersectionPoint = false;
667 mine[myEndIdx].intersectionPoint = false;
668
669 assert(theirs[theirStartIdx].intersectionPoint);
670 assert(theirs[theirEndIdx].intersectionPoint);
671 theirs[theirStartIdx].intersectionPoint = false;
672 theirs[theirEndIdx].intersectionPoint = false;
673
674 myEndIdx = findNextIntersection(mine, myEndIdx);
675
676 if (myEndIdx != Vertex::None) {
677 assert(myEndIdx != myStartIdx);
678
679 uint32_t theirFollowUpIdx = findMatchingVertex(mine[myEndIdx].position, theirs);
680 uint32_t theirPreviousIdx = findNextIntersection(theirs, theirFollowUpIdx);
681
682 if (findNextIntersection(theirs, theirPreviousIdx) == theirFollowUpIdx) {
683 myStartIdx = findMatchingVertex(theirs[theirPreviousIdx].position, mine);
684 myNextIdx = (myStartIdx + 1) % mine.size();
685
686 assert(mine[myStartIdx].intersectionPoint);
687 assert(mine[myEndIdx].intersectionPoint);
688 mine[myStartIdx].intersectionPoint = false;
689 mine[myEndIdx].intersectionPoint = false;
690
691 if (myNextIdx < myEndIdx) {
692 mine.erase(mine.begin() + myNextIdx, mine.begin() + myEndIdx);
693 }
694 else if (myNextIdx > myEndIdx) {
695 mine.erase(mine.begin() + myNextIdx, mine.end());
696 mine.erase(mine.begin(), mine.begin() + myEndIdx);
697 }
698
699 theirNextIdx = (theirFollowUpIdx + 1) % theirs.size();
700
701 auto d = mine.begin() + myNextIdx;
702
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);
707 }
708 }
709 else {
710 for (auto i = theirs.begin() + theirNextIdx, e = theirs.end(); i != e; ++i) {
711 assert(!i->intersectionPoint);
712 d = mine.insert(d, *i);
713 }
714 for (auto i = theirs.begin(), e = theirs.begin() + theirPreviousIdx; i != e; ++i) {
715 assert(!i->intersectionPoint);
716 d = mine.insert(d, *i);
717 }
718 }
719 assert(theirs[theirFollowUpIdx].intersectionPoint);
720 assert(theirs[theirPreviousIdx].intersectionPoint);
721 theirs[theirFollowUpIdx].intersectionPoint = false;
722 theirs[theirPreviousIdx].intersectionPoint = false;
723 }
724 }
725 }
726 }
727 myStartIdx = findNextIntersection(mine, myStartIdx);
728 }
729 // No more intersections could be found. Insert whatever remains
730 // in 'mine' into the output as a new polygon.
731 Polygon& dest = outputs.emplace_back();
732
733 dest.points.insert(dest.points.end(), mine.begin(), mine.end());
734 }
735 else if (isPointInside(polygon.points[0])) {
736 // The given polygon is completely enclosed within this one.
737 // We will create a new polygon with a hole in the middle.
738 uint32_t myIdx;
739 uint32_t theirIdx;
740 Polygon& dest = outputs.emplace_back();
741
742 findClosestVertices(mine, theirs, myIdx, theirIdx);
743
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());
748 }
749 else if (!polygon.isPointInside(points[0])) {
750 // The other polygon does not intersect this one in anyway.
751 // Return a copy of ourself unchanged.
752 return {*this};
753 }
754 return outputs;
755 }
756 return {*this};
757}
758
762bool Cogs::Geometry::Polygon::isClockwise(const PointList& pointList) {
763 return calcSignedArea(pointList.begin(), pointList.end()) < 0.0f;
764}
765
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;
778
779 for (auto i = pointList.begin(), e = pointList.end(); i != e; ++i) {
780 glm::vec3 end = *i;
781
782 if (start.y > end.y) {
783 if ((p.y >= end.y) && (p.y <= start.y)) {
784 intersectioncount += calcLineLineIntersectionPoint(p, pEnd, start, end, intersectionpoint);
785 }
786 }
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);
790 }
791 }
792 start = end;
793 }
794 if (intersectioncount & 1) {
795 return true;
796 }
797 }
798 return false;
799}
800
807float Cogs::Geometry::Polygon::calcSignedArea(const PointList::const_iterator& start, const PointList::const_iterator& end) {
808 float area = 0.0f;
809
810 if ((end - start) > 2) {
811 glm::vec3 prev = *(end - 1);
812
813 for (auto i = start; i != end; ++i) {
814 glm::vec3 current = *i;
815
816 area += (prev.x * current.y) - (current.x * prev.y);
817 prev = current;
818 }
819 }
820 return area * 0.5f;
821}
Generic 2D concave or convex polygon.
Definition: Polygon.h:16
void setPoints(const PointList &pointList)
Assigns a new set of points to this polygon.
Definition: Polygon.cpp:423
static float calcSignedArea(const PointList::const_iterator &start, const PointList::const_iterator &end)
Calculates the signed area of the given list of points.
Definition: Polygon.cpp:807
static bool isClockwise(const PointList &pointList)
Helper function for testing the orientation of the given point list.
Definition: Polygon.cpp:762
void fixUp()
Ensures winding is clockwise and attempts to unwrap self-intersecting sections.
Definition: Polygon.cpp:438
List add(const Polygon &polygon) const
Adds the specified polygon to this one.
Definition: Polygon.cpp:508
List subtract(const Polygon &polygon) const
Subtracts the specified polygon from this one.
Definition: Polygon.cpp:528
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...
Definition: Polygon.cpp:398
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.
Definition: Polygon.cpp:76
void removeDuplicates(Vertex::List &vertices)
Removes any adjacent vertices that are within five millimetres of their neighbours.
Definition: Polygon.cpp:43
bool findClosestVertices(const Vertex::List &mine, const Vertex::List &theirs, uint32_t &myPoint, uint32_t &theirPoint)
Finds the closest vertices from the two polygons.
Definition: Polygon.cpp:371
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...
Definition: Polygon.cpp:211
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.
Definition: Polygon.cpp:104
bool veryClose(const glm::vec3 &lhs, const glm::vec3 &rhs)
Tests whether the two points are within one centimetre of each other.
Definition: Polygon.cpp:34
uint32_t findMatchingVertex(const glm::vec3 &vertex, const Vertex::List &vertices)
Searches for the vertex at the specified location.
Definition: Polygon.cpp:91
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...
Definition: Polygon.cpp:136