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