Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
PositionVector.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// https://www.eclipse.org/legal/epl-2.0/
7// This Source Code may also be made available under the following Secondary
8// Licenses when the conditions for such availability set forth in the Eclipse
9// Public License 2.0 are satisfied: GNU General Public License, version 2
10// or later which is available at
11// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13/****************************************************************************/
21// A list of positions
22/****************************************************************************/
23#include <config.h>
24
25#include <queue>
26#include <cmath>
27#include <iostream>
28#include <algorithm>
29#include <numeric>
30#include <cassert>
31#include <iterator>
32#include <limits>
36#include "AbstractPoly.h"
37#include "Position.h"
38#include "PositionVector.h"
39#include "GeomHelper.h"
40#include "Boundary.h"
41
42//#define DEBUG_MOVE2SIDE
43
44// ===========================================================================
45// static members
46// ===========================================================================
48
49// ===========================================================================
50// method definitions
51// ===========================================================================
52
54
55
56PositionVector::PositionVector(const std::vector<Position>& v) {
57 std::copy(v.begin(), v.end(), std::back_inserter(*this));
58}
59
60
61PositionVector::PositionVector(const std::vector<Position>::const_iterator beg, const std::vector<Position>::const_iterator end) {
62 std::copy(beg, end, std::back_inserter(*this));
63}
64
65
67 push_back(p1);
68 push_back(p2);
69}
70
71
73
74
75bool
76PositionVector::around(const Position& p, double offset) const {
77 if (size() < 2) {
78 return false;
79 }
80 if (offset != 0) {
81 PositionVector tmp(*this);
82 tmp.scaleAbsolute(offset);
83 return tmp.around(p);
84 }
85 double angle = 0;
86 // iterate over all points, and obtain angle between current and next
87 for (const_iterator i = begin(); i != (end() - 1); i++) {
88 Position p1(
89 i->x() - p.x(),
90 i->y() - p.y());
91 Position p2(
92 (i + 1)->x() - p.x(),
93 (i + 1)->y() - p.y());
94 angle += GeomHelper::angle2D(p1, p2);
95 }
96 // add angle between last and first point
97 Position p1(
98 (end() - 1)->x() - p.x(),
99 (end() - 1)->y() - p.y());
100 Position p2(
101 begin()->x() - p.x(),
102 begin()->y() - p.y());
103 angle += GeomHelper::angle2D(p1, p2);
104 // if angle is less than PI, then point lying in Polygon
105 return (!(fabs(angle) < M_PI));
106}
107
108
109bool
110PositionVector::overlapsWith(const AbstractPoly& poly, double offset) const {
111 if (
112 // check whether one of my points lies within the given poly
113 partialWithin(poly, offset) ||
114 // check whether the polygon lies within me
115 poly.partialWithin(*this, offset)) {
116 return true;
117 }
118 if (size() >= 2) {
119 for (const_iterator i = begin(); i != end() - 1; i++) {
120 if (poly.crosses(*i, *(i + 1))) {
121 return true;
122 }
123 }
124 if (size() > 2 && poly.crosses(back(), front())) {
125 return true;
126 }
127 }
128 return false;
129}
130
131
132double
133PositionVector::getOverlapWith(const PositionVector& poly, double zThreshold) const {
134 double result = 0;
135 if ((size() == 0) || (poly.size() == 0)) {
136 return result;
137 }
138 // this points within poly
139 for (const_iterator i = begin(); i != end() - 1; i++) {
140 if (poly.around(*i)) {
142 if (fabs(closest.z() - (*i).z()) < zThreshold) {
143 result = MAX2(result, poly.distance2D(*i));
144 }
145 }
146 }
147 // polys points within this
148 for (const_iterator i = poly.begin(); i != poly.end() - 1; i++) {
149 if (around(*i)) {
151 if (fabs(closest.z() - (*i).z()) < zThreshold) {
152 result = MAX2(result, distance2D(*i));
153 }
154 }
155 }
156 return result;
157}
158
159
160bool
161PositionVector::intersects(const Position& p1, const Position& p2) const {
162 if (size() < 2) {
163 return false;
164 }
165 for (const_iterator i = begin(); i != end() - 1; i++) {
166 if (intersects(*i, *(i + 1), p1, p2)) {
167 return true;
168 }
169 }
170 return false;
171}
172
173
174bool
176 if (size() < 2) {
177 return false;
178 }
179 for (const_iterator i = begin(); i != end() - 1; i++) {
180 if (v1.intersects(*i, *(i + 1))) {
181 return true;
182 }
183 }
184 return false;
185}
186
187
189PositionVector::intersectionPosition2D(const Position& p1, const Position& p2, const double withinDist) const {
190 for (const_iterator i = begin(); i != end() - 1; i++) {
191 double x, y, m;
192 if (intersects(*i, *(i + 1), p1, p2, withinDist, &x, &y, &m)) {
193 return Position(x, y);
194 }
195 }
196 return Position::INVALID;
197}
198
199
202 for (const_iterator i = begin(); i != end() - 1; i++) {
203 if (v1.intersects(*i, *(i + 1))) {
204 return v1.intersectionPosition2D(*i, *(i + 1));
205 }
206 }
207 return Position::INVALID;
208}
209
210
211const Position&
213 /* bracket operators works as in Python. Examples:
214 - A = {'a', 'b', 'c', 'd'} (size 4)
215 - A [2] returns 'c' because 0 < 2 < 4
216 - A [100] throws an exception because 100 > 4
217 - A [-1] returns 'd' because 4 - 1 = 3
218 - A [-100] throws an exception because (4-100) < 0
219 */
220 if (index >= 0 && index < (int)size()) {
221 return at(index);
222 } else if (index < 0 && -index <= (int)size()) {
223 return at((int)size() + index);
224 } else {
225 throw OutOfBoundsException("Index out of range in bracket operator of PositionVector");
226 }
227}
228
229
232 /* bracket operators works as in Python. Examples:
233 - A = {'a', 'b', 'c', 'd'} (size 4)
234 - A [2] returns 'c' because 0 < 2 < 4
235 - A [100] throws an exception because 100 > 4
236 - A [-1] returns 'd' because 4 - 1 = 3
237 - A [-100] throws an exception because (4-100) < 0
238 */
239 if (index >= 0 && index < (int)size()) {
240 return at(index);
241 } else if (index < 0 && -index <= (int)size()) {
242 return at((int)size() + index);
243 } else {
244 throw OutOfBoundsException("Index out of range in bracket operator of PositionVector");
245 }
246}
247
248
250PositionVector::positionAtOffset(double pos, double lateralOffset) const {
251 if (size() == 0) {
252 return Position::INVALID;
253 }
254 if (size() == 1) {
255 return front();
256 }
257 const_iterator i = begin();
258 double seenLength = 0;
259 do {
260 const double nextLength = (*i).distanceTo(*(i + 1));
261 if (seenLength + nextLength > pos) {
262 return positionAtOffset(*i, *(i + 1), pos - seenLength, lateralOffset);
263 }
264 seenLength += nextLength;
265 } while (++i != end() - 1);
266 if (lateralOffset == 0 || size() < 2) {
267 return back();
268 } else {
269 return positionAtOffset(*(end() - 2), *(end() - 1), (*(end() - 2)).distanceTo(*(end() - 1)), lateralOffset);
270 }
271}
272
273
275PositionVector::sidePositionAtAngle(double pos, double lateralOffset, double angle) const {
276 if (size() == 0) {
277 return Position::INVALID;
278 }
279 if (size() == 1) {
280 return front();
281 }
282 const_iterator i = begin();
283 double seenLength = 0;
284 do {
285 const double nextLength = (*i).distanceTo(*(i + 1));
286 if (seenLength + nextLength > pos) {
287 return sidePositionAtAngle(*i, *(i + 1), pos - seenLength, lateralOffset, angle);
288 }
289 seenLength += nextLength;
290 } while (++i != end() - 1);
291 return sidePositionAtAngle(*(end() - 2), *(end() - 1), (*(end() - 2)).distanceTo(*(end() - 1)), lateralOffset, angle);
292}
293
294
296PositionVector::positionAtOffset2D(double pos, double lateralOffset, bool extrapolateBeyond) const {
297 if (size() == 0) {
298 return Position::INVALID;
299 }
300 if (size() == 1) {
301 return front();
302 }
303 const_iterator i = begin();
304 double seenLength = 0;
305 do {
306 const double nextLength = (*i).distanceTo2D(*(i + 1));
307 if (seenLength + nextLength > pos) {
308 return positionAtOffset2D(*i, *(i + 1), pos - seenLength, lateralOffset, extrapolateBeyond);
309 }
310 seenLength += nextLength;
311 } while (++i != end() - 1);
312 if (extrapolateBeyond) {
313 return positionAtOffset2D(*(i - 1), *i, pos - seenLength + (*i).distanceTo2D(*(i - 1)), lateralOffset, extrapolateBeyond);
314 }
315 return back();
316}
317
318
319double
321 if ((size() == 0) || (size() == 1)) {
322 return INVALID_DOUBLE;
323 }
324 if (pos < 0) {
325 pos += length();
326 }
327 const_iterator i = begin();
328 double seenLength = 0;
329 do {
330 const Position& p1 = *i;
331 const Position& p2 = *(i + 1);
332 const double nextLength = p1.distanceTo(p2);
333 if (seenLength + nextLength > pos) {
334 return p1.angleTo2D(p2);
335 }
336 seenLength += nextLength;
337 } while (++i != end() - 1);
338 const Position& p1 = (*this)[-2];
339 const Position& p2 = back();
340 return p1.angleTo2D(p2);
341}
342
343
344double
348
349
350double
352 if (size() == 0) {
353 return INVALID_DOUBLE;
354 }
355 const_iterator i = begin();
356 double seenLength = 0;
357 do {
358 const Position& p1 = *i;
359 const Position& p2 = *(i + 1);
360 const double nextLength = p1.distanceTo(p2);
361 if (seenLength + nextLength > pos) {
362 return RAD2DEG(p1.slopeTo2D(p2));
363 }
364 seenLength += nextLength;
365 } while (++i != end() - 1);
366 const Position& p1 = (*this)[-2];
367 const Position& p2 = back();
368 return RAD2DEG(p1.slopeTo2D(p2));
369}
370
371
373PositionVector::positionAtOffset(const Position& p1, const Position& p2, double pos, double lateralOffset) {
374 const double dist = p1.distanceTo(p2);
375 if (pos < 0. || dist < pos) {
376 return Position::INVALID;
377 }
378 if (lateralOffset != 0) {
379 if (dist == 0.) {
380 return Position::INVALID;
381 }
382 const Position offset = sideOffset(p1, p2, -lateralOffset); // move in the same direction as Position::move2side
383 if (pos == 0.) {
384 return p1 + offset;
385 }
386 return p1 + (p2 - p1) * (pos / dist) + offset;
387 }
388 if (pos == 0.) {
389 return p1;
390 }
391 return p1 + (p2 - p1) * (pos / dist);
392}
393
394
396PositionVector::sidePositionAtAngle(const Position& p1, const Position& p2, double pos, double lateralOffset, double angle) {
397 const double dist = p1.distanceTo(p2);
398 if (pos < 0. || dist < pos || dist == 0) {
399 return Position::INVALID;
400 }
401 angle -= DEG2RAD(90);
402 const Position offset(cos(angle) * lateralOffset, sin(angle) * lateralOffset);
403 return p1 + (p2 - p1) * (pos / dist) + offset;
404}
405
406
408PositionVector::positionAtOffset2D(const Position& p1, const Position& p2, double pos, double lateralOffset, bool extrapolateBeyond) {
409 const double dist = p1.distanceTo2D(p2);
410 if ((pos < 0 || dist < pos) && !extrapolateBeyond) {
411 return Position::INVALID;
412 }
413 if (dist == 0) {
414 return p1;
415 }
416 if (lateralOffset != 0) {
417 const Position offset = sideOffset(p1, p2, -lateralOffset); // move in the same direction as Position::move2side
418 if (pos == 0.) {
419 return p1 + offset;
420 }
421 return p1 + (p2 - p1) * (pos / dist) + offset;
422 }
423 if (pos == 0.) {
424 return p1;
425 }
426 return p1 + (p2 - p1) * (pos / dist);
427}
428
429
432 Boundary ret;
433 for (const Position& i : *this) {
434 ret.add(i);
435 }
436 return ret;
437}
438
439
442 if (size() == 0) {
443 return Position::INVALID;
444 }
445 double x = 0;
446 double y = 0;
447 double z = 0;
448 for (const Position& i : *this) {
449 x += i.x();
450 y += i.y();
451 z += i.z();
452 }
453 return Position(x / (double) size(), y / (double) size(), z / (double)size());
454}
455
456
459 if (size() == 0) {
460 return Position::INVALID;
461 } else if (size() == 1) {
462 return (*this)[0];
463 } else if (size() == 2) {
464 return ((*this)[0] + (*this)[1]) * 0.5;
465 }
466 PositionVector tmp = *this;
467 if (!isClosed()) { // make sure its closed
468 tmp.push_back(tmp[0]);
469 }
470 // shift to origin to increase numerical stability
471 Position offset = tmp[0];
472 Position result;
473 tmp.sub(offset);
474 const int endIndex = (int)tmp.size() - 1;
475 double div = 0; // 6 * area including sign
476 double x = 0;
477 double y = 0;
478 if (tmp.area() != 0) { // numerical instability ?
479 // http://en.wikipedia.org/wiki/Polygon
480 for (int i = 0; i < endIndex; i++) {
481 const double z = tmp[i].x() * tmp[i + 1].y() - tmp[i + 1].x() * tmp[i].y();
482 div += z; // area formula
483 x += (tmp[i].x() + tmp[i + 1].x()) * z;
484 y += (tmp[i].y() + tmp[i + 1].y()) * z;
485 }
486 div *= 3; // 6 / 2, the 2 comes from the area formula
487 result = Position(x / div, y / div);
488 } else {
489 // compute by decomposing into line segments
490 // http://en.wikipedia.org/wiki/Centroid#By_geometric_decomposition
491 double lengthSum = 0;
492 for (int i = 0; i < endIndex; i++) {
493 double length = tmp[i].distanceTo(tmp[i + 1]);
494 x += (tmp[i].x() + tmp[i + 1].x()) * length / 2;
495 y += (tmp[i].y() + tmp[i + 1].y()) * length / 2;
496 lengthSum += length;
497 }
498 if (lengthSum == 0) {
499 // it is probably only one point
500 result = tmp[0];
501 }
502 result = Position(x / lengthSum, y / lengthSum) + offset;
503 }
504 return result + offset;
505}
506
507
508void
510 Position centroid = getCentroid();
511 for (int i = 0; i < static_cast<int>(size()); i++) {
512 (*this)[i] = centroid + (((*this)[i] - centroid) * factor);
513 }
514}
515
516
517void
519 Position centroid = getCentroid();
520 for (int i = 0; i < static_cast<int>(size()); i++) {
521 (*this)[i] = centroid + (((*this)[i] - centroid) + offset);
522 }
523}
524
525
528 if (size() == 1) {
529 return (*this)[0];
530 } else {
531 return positionAtOffset(double((length() / 2.)));
532 }
533}
534
535
536double
538 if (size() == 0) {
539 return 0;
540 }
541 double len = 0;
542 for (const_iterator i = begin(); i != end() - 1; i++) {
543 len += (*i).distanceTo(*(i + 1));
544 }
545 return len;
546}
547
548
549double
551 if (size() == 0) {
552 return 0;
553 }
554 double len = 0;
555 for (const_iterator i = begin(); i != end() - 1; i++) {
556 len += (*i).distanceTo2D(*(i + 1));
557 }
558 return len;
559}
560
561
562double
564 if (size() < 3) {
565 return 0;
566 }
567 double area = 0;
568 PositionVector tmp = *this;
569 if (!isClosed()) { // make sure its closed
570 tmp.push_back(tmp[0]);
571 }
572 const int endIndex = (int)tmp.size() - 1;
573 // http://en.wikipedia.org/wiki/Polygon
574 for (int i = 0; i < endIndex; i++) {
575 area += tmp[i].x() * tmp[i + 1].y() - tmp[i + 1].x() * tmp[i].y();
576 }
577 if (area < 0) { // we whether we had cw or ccw order
578 area *= -1;
579 }
580 return area / 2;
581}
582
583
584bool
585PositionVector::partialWithin(const AbstractPoly& poly, double offset) const {
586 if (size() < 2) {
587 return false;
588 }
589 for (const_iterator i = begin(); i != end(); i++) {
590 if (poly.around(*i, offset)) {
591 return true;
592 }
593 }
594 return false;
595}
596
597
598bool
599PositionVector::crosses(const Position& p1, const Position& p2) const {
600 return intersects(p1, p2);
601}
602
603
604std::pair<PositionVector, PositionVector>
605PositionVector::splitAt(double where, bool use2D) const {
606 const double len = use2D ? length2D() : length();
607 if (size() < 2) {
608 throw InvalidArgument("Vector to short for splitting");
609 }
610 if (where < 0 || where > len) {
611 throw InvalidArgument("Invalid split position " + toString(where) + " for vector of length " + toString(len));
612 }
613 if (where <= POSITION_EPS || where >= len - POSITION_EPS) {
614 WRITE_WARNINGF(TL("Splitting vector close to end (pos: %, length: %)"), toString(where), toString(len));
615 }
616 PositionVector first, second;
617 first.push_back((*this)[0]);
618 double seen = 0;
619 const_iterator it = begin() + 1;
620 double next = use2D ? first.back().distanceTo2D(*it) : first.back().distanceTo(*it);
621 // see how many points we can add to first
622 while (where >= seen + next + POSITION_EPS) {
623 seen += next;
624 first.push_back(*it);
625 it++;
626 next = use2D ? first.back().distanceTo2D(*it) : first.back().distanceTo(*it);
627 }
628 if (fabs(where - (seen + next)) > POSITION_EPS || it == end() - 1) {
629 // we need to insert a new point because 'where' is not close to an
630 // existing point or it is to close to the endpoint
631 const Position p = (use2D
632 ? positionAtOffset2D(first.back(), *it, where - seen)
633 : positionAtOffset(first.back(), *it, where - seen));
634 first.push_back(p);
635 second.push_back(p);
636 } else {
637 first.push_back(*it);
638 }
639 // add the remaining points to second
640 for (; it != end(); it++) {
641 second.push_back(*it);
642 }
643 assert(first.size() >= 2);
644 assert(second.size() >= 2);
645 assert(first.back() == second.front());
646 assert(fabs((use2D ? first.length2D() + second.length2D() : first.length() + second.length()) - len) < 2 * POSITION_EPS);
647 return std::pair<PositionVector, PositionVector>(first, second);
648}
649
650
651std::ostream&
652operator<<(std::ostream& os, const PositionVector& geom) {
653 for (PositionVector::const_iterator i = geom.begin(); i != geom.end(); i++) {
654 if (i != geom.begin()) {
655 os << " ";
656 }
657 os << (*i);
658 }
659 return os;
660}
661
662
663void
665 // We take the centroid of the points as an origin for the angle computations
666 // that will follow but other points could be taken (the center of the bounding
667 // box of the polygon for instance). Each of these can potentially lead
668 // to a different result in the case of a non-convex polygon.
669 const Position centroid = std::accumulate(begin(), end(), Position(0, 0)) / (double)size();
670 sub(centroid);
671 std::sort(begin(), end(), as_poly_cw_sorter());
672 add(centroid);
673}
674
675
676void
677PositionVector::add(double xoff, double yoff, double zoff) {
678 for (int i = 0; i < (int)size(); i++) {
679 (*this)[i].add(xoff, yoff, zoff);
680 }
681}
682
683
684void
686 add(-offset.x(), -offset.y(), -offset.z());
687}
688
689
690void
692 add(offset.x(), offset.y(), offset.z());
693}
694
695
697PositionVector::added(const Position& offset) const {
699 for (auto i1 = begin(); i1 != end(); ++i1) {
700 pv.push_back(*i1 + offset);
701 }
702 return pv;
703}
704
705
706void
708 for (int i = 0; i < (int)size(); i++) {
709 (*this)[i].mul(1, -1);
710 }
711}
712
713
715
716
717int
719 double angle1 = atAngle2D(p1);
720 double angle2 = atAngle2D(p2);
721 if (angle1 > angle2) {
722 return true;
723 }
724 if (angle1 == angle2) {
725 double squaredDistance1 = p1.dotProduct(p1);
726 double squaredDistance2 = p2.dotProduct(p2);
727 if (squaredDistance1 < squaredDistance2) {
728 return true;
729 }
730 }
731 return false;
732}
733
734
735double
737 double angle = atan2(p.y(), p.x());
738 return angle < 0.0 ? angle : angle + 2.0 * M_PI;
739}
740
741void
743 std::sort(begin(), end(), increasing_x_y_sorter());
744}
745
746
748
749
750int
752 if (p1.x() != p2.x()) {
753 return p1.x() < p2.x();
754 }
755 return p1.y() < p2.y();
756}
757
758
759double
760PositionVector::isLeft(const Position& P0, const Position& P1, const Position& P2) const {
761 return (P1.x() - P0.x()) * (P2.y() - P0.y()) - (P2.x() - P0.x()) * (P1.y() - P0.y());
762}
763
764
765void
766PositionVector::append(const PositionVector& v, double sameThreshold) {
767 if ((size() > 0) && (v.size() > 0) && (back().distanceTo(v[0]) < sameThreshold)) {
768 copy(v.begin() + 1, v.end(), back_inserter(*this));
769 } else {
770 copy(v.begin(), v.end(), back_inserter(*this));
771 }
772}
773
774
775void
776PositionVector::prepend(const PositionVector& v, double sameThreshold) {
777 if ((size() > 0) && (v.size() > 0) && (front().distanceTo(v.back()) < sameThreshold)) {
778 insert(begin(), v.begin(), v.end() - 1);
779 } else {
780 insert(begin(), v.begin(), v.end());
781 }
782}
783
784
786PositionVector::getSubpart(double beginOffset, double endOffset) const {
787 PositionVector ret;
788 Position begPos = front();
789 if (beginOffset > POSITION_EPS) {
790 begPos = positionAtOffset(beginOffset);
791 }
792 Position endPos = back();
793 if (endOffset < length() - POSITION_EPS) {
794 endPos = positionAtOffset(endOffset);
795 }
796 ret.push_back(begPos);
797
798 double seen = 0;
799 const_iterator i = begin();
800 // skip previous segments
801 while ((i + 1) != end()
802 &&
803 seen + (*i).distanceTo(*(i + 1)) < beginOffset) {
804 seen += (*i).distanceTo(*(i + 1));
805 i++;
806 }
807 // append segments in between
808 while ((i + 1) != end()
809 &&
810 seen + (*i).distanceTo(*(i + 1)) < endOffset) {
811
812 ret.push_back_noDoublePos(*(i + 1));
813 seen += (*i).distanceTo(*(i + 1));
814 i++;
815 }
816 // append end
817 ret.push_back_noDoublePos(endPos);
818 if (ret.size() == 1) {
819 ret.push_back(endPos);
820 }
821 return ret;
822}
823
824
826PositionVector::getSubpart2D(double beginOffset, double endOffset) const {
827 if (size() == 0) {
828 return PositionVector();
829 }
830 PositionVector ret;
831 Position begPos = front();
832 if (beginOffset > POSITION_EPS) {
833 begPos = positionAtOffset2D(beginOffset);
834 }
835 Position endPos = back();
836 if (endOffset < length2D() - POSITION_EPS) {
837 endPos = positionAtOffset2D(endOffset);
838 }
839 ret.push_back(begPos);
840
841 double seen = 0;
842 const_iterator i = begin();
843 // skip previous segments
844 while ((i + 1) != end()
845 &&
846 seen + (*i).distanceTo2D(*(i + 1)) < beginOffset) {
847 seen += (*i).distanceTo2D(*(i + 1));
848 i++;
849 }
850 // append segments in between
851 while ((i + 1) != end()
852 &&
853 seen + (*i).distanceTo2D(*(i + 1)) < endOffset) {
854
855 ret.push_back_noDoublePos(*(i + 1));
856 seen += (*i).distanceTo2D(*(i + 1));
857 i++;
858 }
859 // append end
860 ret.push_back_noDoublePos(endPos);
861 if (ret.size() == 1) {
862 ret.push_back(endPos);
863 }
864 return ret;
865}
866
867
869PositionVector::getSubpartByIndex(int beginIndex, int count) const {
870 if (size() == 0) {
871 return PositionVector();
872 }
873 if (beginIndex < 0) {
874 beginIndex += (int)size();
875 }
876 assert(count >= 0);
877 assert(beginIndex < (int)size());
878 assert(beginIndex + count <= (int)size());
879 PositionVector result;
880 for (int i = beginIndex; i < beginIndex + count; ++i) {
881 result.push_back((*this)[i]);
882 }
883 return result;
884}
885
886
887double
889 if (size() == 0) {
890 return INVALID_DOUBLE;
891 }
892 return front().angleTo2D(back());
893}
894
895
896double
897PositionVector::nearest_offset_to_point2D(const Position& p, bool perpendicular) const {
898 if (size() == 0) {
899 return INVALID_DOUBLE;
900 }
901 double minDist = std::numeric_limits<double>::max();
902 double nearestPos = GeomHelper::INVALID_OFFSET;
903 double seen = 0;
904 for (const_iterator i = begin(); i != end() - 1; i++) {
905 const double pos =
906 GeomHelper::nearest_offset_on_line_to_point2D(*i, *(i + 1), p, perpendicular);
907 const double dist2 = pos == GeomHelper::INVALID_OFFSET ? minDist : p.distanceSquaredTo2D(positionAtOffset2D(*i, *(i + 1), pos));
908 if (dist2 < minDist) {
909 nearestPos = pos + seen;
910 minDist = dist2;
911 }
912 if (perpendicular && i != begin() && pos == GeomHelper::INVALID_OFFSET) {
913 // even if perpendicular is set we still need to check the distance to the inner points
914 const double cornerDist2 = p.distanceSquaredTo2D(*i);
915 if (cornerDist2 < minDist) {
916 const double pos1 =
917 GeomHelper::nearest_offset_on_line_to_point2D(*(i - 1), *i, p, false);
918 const double pos2 =
919 GeomHelper::nearest_offset_on_line_to_point2D(*i, *(i + 1), p, false);
920 if (pos1 == (*(i - 1)).distanceTo2D(*i) && pos2 == 0.) {
921 nearestPos = seen;
922 minDist = cornerDist2;
923 }
924 }
925 }
926 seen += (*i).distanceTo2D(*(i + 1));
927 }
928 return nearestPos;
929}
930
931
932double
933PositionVector::nearest_offset_to_point25D(const Position& p, bool perpendicular) const {
934 if (size() == 0) {
935 return INVALID_DOUBLE;
936 }
937 double minDist = std::numeric_limits<double>::max();
938 double nearestPos = GeomHelper::INVALID_OFFSET;
939 double seen = 0;
940 for (const_iterator i = begin(); i != end() - 1; i++) {
941 const double pos =
942 GeomHelper::nearest_offset_on_line_to_point2D(*i, *(i + 1), p, perpendicular);
943 const double dist = pos == GeomHelper::INVALID_OFFSET ? minDist : p.distanceTo2D(positionAtOffset2D(*i, *(i + 1), pos));
944 if (dist < minDist) {
945 const double pos25D = pos * (*i).distanceTo(*(i + 1)) / (*i).distanceTo2D(*(i + 1));
946 nearestPos = pos25D + seen;
947 minDist = dist;
948 }
949 if (perpendicular && i != begin() && pos == GeomHelper::INVALID_OFFSET) {
950 // even if perpendicular is set we still need to check the distance to the inner points
951 const double cornerDist = p.distanceTo2D(*i);
952 if (cornerDist < minDist) {
953 const double pos1 =
954 GeomHelper::nearest_offset_on_line_to_point2D(*(i - 1), *i, p, false);
955 const double pos2 =
956 GeomHelper::nearest_offset_on_line_to_point2D(*i, *(i + 1), p, false);
957 if (pos1 == (*(i - 1)).distanceTo2D(*i) && pos2 == 0.) {
958 nearestPos = seen;
959 minDist = cornerDist;
960 }
961 }
962 }
963 seen += (*i).distanceTo(*(i + 1));
964 }
965 return nearestPos;
966}
967
968
971 if (size() == 0) {
972 return Position::INVALID;
973 }
974 // @toDo this duplicates most of the code in nearest_offset_to_point2D. It should be refactored
975 if (extend) {
976 PositionVector extended = *this;
977 const double dist = 2 * distance2D(p);
978 extended.extrapolate(dist);
979 return extended.transformToVectorCoordinates(p) - Position(dist, 0);
980 }
981 double minDist = std::numeric_limits<double>::max();
982 double nearestPos = -1;
983 double seen = 0;
984 int sign = 1;
985 for (const_iterator i = begin(); i != end() - 1; i++) {
986 const double pos =
988 const double dist = pos < 0 ? minDist : p.distanceTo2D(positionAtOffset(*i, *(i + 1), pos));
989 if (dist < minDist) {
990 nearestPos = pos + seen;
991 minDist = dist;
992 sign = isLeft(*i, *(i + 1), p) >= 0 ? -1 : 1;
993 }
994 if (i != begin() && pos == GeomHelper::INVALID_OFFSET) {
995 // even if perpendicular is set we still need to check the distance to the inner points
996 const double cornerDist = p.distanceTo2D(*i);
997 if (cornerDist < minDist) {
998 const double pos1 =
999 GeomHelper::nearest_offset_on_line_to_point2D(*(i - 1), *i, p, false);
1000 const double pos2 =
1001 GeomHelper::nearest_offset_on_line_to_point2D(*i, *(i + 1), p, false);
1002 if (pos1 == (*(i - 1)).distanceTo2D(*i) && pos2 == 0.) {
1003 nearestPos = seen;
1004 minDist = cornerDist;
1005 sign = isLeft(*(i - 1), *i, p) >= 0 ? -1 : 1;
1006 }
1007 }
1008 }
1009 seen += (*i).distanceTo2D(*(i + 1));
1010 }
1011 if (nearestPos != -1) {
1012 return Position(nearestPos, sign * minDist);
1013 } else {
1014 return Position::INVALID;
1015 }
1016}
1017
1018
1019int
1020PositionVector::indexOfClosest(const Position& p, bool twoD) const {
1021 if (size() == 0) {
1022 return -1;
1023 }
1024 double minDist = std::numeric_limits<double>::max();
1025 double dist;
1026 int closest = 0;
1027 for (int i = 0; i < (int)size(); i++) {
1028 const Position& p2 = (*this)[i];
1029 dist = twoD ? p.distanceTo2D(p2) : p.distanceTo(p2);
1030 if (dist < minDist) {
1031 closest = i;
1032 minDist = dist;
1033 }
1034 }
1035 return closest;
1036}
1037
1038
1039int
1041 if (size() == 0) {
1042 return -1;
1043 }
1044 double minDist = std::numeric_limits<double>::max();
1045 int insertionIndex = 1;
1046 for (int i = 0; i < (int)size() - 1; i++) {
1047 const double length = GeomHelper::nearest_offset_on_line_to_point2D((*this)[i], (*this)[i + 1], p, false);
1048 const Position& outIntersection = PositionVector::positionAtOffset2D((*this)[i], (*this)[i + 1], length);
1049 const double dist = p.distanceTo2D(outIntersection);
1050 if (dist < minDist) {
1051 insertionIndex = i + 1;
1052 minDist = dist;
1053 }
1054 }
1055 // check if we have to adjust Position Z
1056 if (interpolateZ) {
1057 // obtain previous and next Z
1058 const double previousZ = (begin() + (insertionIndex - 1))->z();
1059 const double nextZ = (begin() + insertionIndex)->z();
1060 // insert new position using x and y of p, and the new z
1061 insert(begin() + insertionIndex, Position(p.x(), p.y(), ((previousZ + nextZ) / 2.0)));
1062 } else {
1063 insert(begin() + insertionIndex, p);
1064 }
1065 return insertionIndex;
1066}
1067
1068
1069int
1071 if (size() == 0) {
1072 return -1;
1073 }
1074 double minDist = std::numeric_limits<double>::max();
1075 int removalIndex = 0;
1076 for (int i = 0; i < (int)size(); i++) {
1077 const double dist = p.distanceTo2D((*this)[i]);
1078 if (dist < minDist) {
1079 removalIndex = i;
1080 minDist = dist;
1081 }
1082 }
1083 erase(begin() + removalIndex);
1084 return removalIndex;
1085}
1086
1087
1088std::vector<double>
1090 std::vector<double> ret;
1091 if (other.size() == 0) {
1092 return ret;
1093 }
1094 for (const_iterator i = other.begin(); i != other.end() - 1; i++) {
1095 std::vector<double> atSegment = intersectsAtLengths2D(*i, *(i + 1));
1096 copy(atSegment.begin(), atSegment.end(), back_inserter(ret));
1097 }
1098 return ret;
1099}
1100
1101
1102std::vector<double>
1104 std::vector<double> ret;
1105 if (size() == 0) {
1106 return ret;
1107 }
1108 double pos = 0;
1109 for (const_iterator i = begin(); i != end() - 1; i++) {
1110 const Position& p1 = *i;
1111 const Position& p2 = *(i + 1);
1112 double x, y, m;
1113 if (intersects(p1, p2, lp1, lp2, 0., &x, &y, &m)) {
1114 ret.push_back(Position(x, y).distanceTo2D(p1) + pos);
1115 }
1116 pos += p1.distanceTo2D(p2);
1117 }
1118 return ret;
1119}
1120
1121
1122void
1123PositionVector::extrapolate(const double val, const bool onlyFirst, const bool onlyLast) {
1124 if (size() > 1) {
1125 Position& p1 = (*this)[0];
1126 Position& p2 = (*this)[1];
1127 const Position offset = (p2 - p1) * (val / p1.distanceTo(p2));
1128 if (!onlyLast) {
1129 p1.sub(offset);
1130 }
1131 if (!onlyFirst) {
1132 if (size() == 2) {
1133 p2.add(offset);
1134 } else {
1135 const Position& e1 = (*this)[-2];
1136 Position& e2 = (*this)[-1];
1137 e2.sub((e1 - e2) * (val / e1.distanceTo(e2)));
1138 }
1139 }
1140 }
1141}
1142
1143
1144void
1145PositionVector::extrapolate2D(const double val, const bool onlyFirst) {
1146 if (size() > 1) {
1147 Position& p1 = (*this)[0];
1148 Position& p2 = (*this)[1];
1149 if (p1.distanceTo2D(p2) > 0) {
1150 const Position offset = (p2 - p1) * (val / p1.distanceTo2D(p2));
1151 p1.sub(offset);
1152 if (!onlyFirst) {
1153 if (size() == 2) {
1154 p2.add(offset);
1155 } else {
1156 const Position& e1 = (*this)[-2];
1157 Position& e2 = (*this)[-1];
1158 e2.sub((e1 - e2) * (val / e1.distanceTo2D(e2)));
1159 }
1160 }
1161 }
1162 }
1163}
1164
1165
1168 PositionVector ret;
1169 for (const_reverse_iterator i = rbegin(); i != rend(); i++) {
1170 ret.push_back(*i);
1171 }
1172 return ret;
1173}
1174
1175
1177PositionVector::sideOffset(const Position& beg, const Position& end, const double amount) {
1178 const double scale = amount / beg.distanceTo2D(end);
1179 return Position((beg.y() - end.y()) * scale, (end.x() - beg.x()) * scale);
1180}
1181
1182
1183void
1184PositionVector::move2side(double amount, double maxExtension) {
1185 if (size() < 2) {
1186 return;
1187 }
1188 removeDoublePoints(POSITION_EPS, true);
1189 if (length2D() == 0 || amount == 0) {
1190 return;
1191 }
1192 PositionVector shape;
1193 std::vector<int> recheck;
1194 for (int i = 0; i < static_cast<int>(size()); i++) {
1195 if (i == 0) {
1196 const Position& from = (*this)[i];
1197 const Position& to = (*this)[i + 1];
1198 if (from != to) {
1199 shape.push_back(from - sideOffset(from, to, amount));
1200#ifdef DEBUG_MOVE2SIDE
1201 if (gDebugFlag1) {
1202 std::cout << " " << i << "a=" << shape.back() << "\n";
1203 }
1204#endif
1205 }
1206 } else if (i == static_cast<int>(size()) - 1) {
1207 const Position& from = (*this)[i - 1];
1208 const Position& to = (*this)[i];
1209 if (from != to) {
1210 shape.push_back(to - sideOffset(from, to, amount));
1211#ifdef DEBUG_MOVE2SIDE
1212 if (gDebugFlag1) {
1213 std::cout << " " << i << "b=" << shape.back() << "\n";
1214 }
1215#endif
1216 }
1217 } else {
1218 const Position& from = (*this)[i - 1];
1219 const Position& me = (*this)[i];
1220 const Position& to = (*this)[i + 1];
1221 PositionVector fromMe(from, me);
1222 fromMe.extrapolate2D(me.distanceTo2D(to));
1223 const double extrapolateDev = fromMe[1].distanceTo2D(to);
1224 if (fabs(extrapolateDev) < POSITION_EPS) {
1225 // parallel case, just shift the middle point
1226 shape.push_back(me - sideOffset(from, to, amount));
1227#ifdef DEBUG_MOVE2SIDE
1228 if (gDebugFlag1) {
1229 std::cout << " " << i << "c=" << shape.back() << "\n";
1230 }
1231#endif
1232 } else if (fabs(extrapolateDev - 2 * me.distanceTo2D(to)) < POSITION_EPS) {
1233 // counterparallel case, just shift the middle point
1234 PositionVector fromMe2(from, me);
1235 fromMe2.extrapolate2D(amount);
1236 shape.push_back(fromMe2[1]);
1237#ifdef DEBUG_MOVE2SIDE
1238 if (gDebugFlag1) {
1239 std::cout << " " << i << "d=" << shape.back() << " " << i << "_from=" << from << " " << i << "_me=" << me << " " << i << "_to=" << to << "\n";
1240 }
1241#endif
1242 } else {
1243 Position offsets = sideOffset(from, me, amount);
1244 Position offsets2 = sideOffset(me, to, amount);
1245 PositionVector l1(from - offsets, me - offsets);
1246 PositionVector l2(me - offsets2, to - offsets2);
1247 Position meNew = l1.intersectionPosition2D(l2[0], l2[1], maxExtension);
1248 if (meNew == Position::INVALID) {
1249 recheck.push_back(i);
1250 continue;
1251 }
1252 meNew = meNew + Position(0, 0, me.z());
1253 shape.push_back(meNew);
1254#ifdef DEBUG_MOVE2SIDE
1255 if (gDebugFlag1) {
1256 std::cout << " " << i << "e=" << shape.back() << "\n";
1257 }
1258#endif
1259 }
1260 // copy original z value
1261 shape.back().set(shape.back().x(), shape.back().y(), me.z());
1262 const double angle = localAngle(from, me, to);
1263 if (fabs(angle) > NUMERICAL_EPS) {
1264 const double length = from.distanceTo2D(me) + me.distanceTo2D(to);
1265 const double radius = length / angle;
1266#ifdef DEBUG_MOVE2SIDE
1267 if (gDebugFlag1) {
1268 std::cout << " i=" << i << " a=" << RAD2DEG(angle) << " l=" << length << " r=" << radius << " t=" << amount * 1.8 << "\n";
1269 }
1270#endif
1271 if ((radius < 0 && -radius < amount * 1.8) || fabs(RAD2DEG(angle)) > 170) {
1272 recheck.push_back(i);
1273 }
1274 }
1275 }
1276 }
1277 if (!recheck.empty()) {
1278 // try to adjust positions to avoid clipping
1279 shape = *this;
1280 for (int i = (int)recheck.size() - 1; i >= 0; i--) {
1281 shape.erase(shape.begin() + recheck[i]);
1282 }
1283 shape.move2side(amount, maxExtension);
1284 }
1285 *this = shape;
1286}
1287
1288
1289void
1290PositionVector::move2sideCustom(std::vector<double> amount, double maxExtension) {
1291 if (size() < 2) {
1292 return;
1293 }
1294 if (length2D() == 0) {
1295 return;
1296 }
1297 if (size() != amount.size()) {
1298 throw InvalidArgument("Numer of offsets (" + toString(amount.size())
1299 + ") does not match number of points (" + toString(size()) + ")");
1300 }
1301 PositionVector shape;
1302 for (int i = 0; i < static_cast<int>(size()); i++) {
1303 if (i == 0) {
1304 const Position& from = (*this)[i];
1305 const Position& to = (*this)[i + 1];
1306 if (from != to) {
1307 shape.push_back(from - sideOffset(from, to, amount[i]));
1308 }
1309 } else if (i == static_cast<int>(size()) - 1) {
1310 const Position& from = (*this)[i - 1];
1311 const Position& to = (*this)[i];
1312 if (from != to) {
1313 shape.push_back(to - sideOffset(from, to, amount[i]));
1314 }
1315 } else {
1316 const Position& from = (*this)[i - 1];
1317 const Position& me = (*this)[i];
1318 const Position& to = (*this)[i + 1];
1319 PositionVector fromMe(from, me);
1320 fromMe.extrapolate2D(me.distanceTo2D(to));
1321 const double extrapolateDev = fromMe[1].distanceTo2D(to);
1322 if (fabs(extrapolateDev) < POSITION_EPS) {
1323 // parallel case, just shift the middle point
1324 shape.push_back(me - sideOffset(from, to, amount[i]));
1325 } else if (fabs(extrapolateDev - 2 * me.distanceTo2D(to)) < POSITION_EPS) {
1326 // counterparallel case, just shift the middle point
1327 PositionVector fromMe2(from, me);
1328 fromMe2.extrapolate2D(amount[i]);
1329 shape.push_back(fromMe2[1]);
1330 } else {
1331 Position offsets = sideOffset(from, me, amount[i]);
1332 Position offsets2 = sideOffset(me, to, amount[i]);
1333 PositionVector l1(from - offsets, me - offsets);
1334 PositionVector l2(me - offsets2, to - offsets2);
1335 Position meNew = l1.intersectionPosition2D(l2[0], l2[1], maxExtension);
1336 if (meNew == Position::INVALID) {
1337 continue;
1338 }
1339 meNew = meNew + Position(0, 0, me.z());
1340 shape.push_back(meNew);
1341 }
1342 // copy original z value
1343 shape.back().set(shape.back().x(), shape.back().y(), me.z());
1344 }
1345 }
1346 *this = shape;
1347}
1348
1349double
1350PositionVector::localAngle(const Position& from, const Position& pos, const Position& to) {
1351 return GeomHelper::angleDiff(from.angleTo2D(pos), pos.angleTo2D(to));
1352}
1353
1354double
1356 if ((pos + 1) < (int)size()) {
1357 return (*this)[pos].angleTo2D((*this)[pos + 1]);
1358 }
1359 return INVALID_DOUBLE;
1360}
1361
1362
1363void
1365 if ((size() > 1) && (front() == back())) {
1366 pop_back();
1367 }
1368}
1369
1370
1371void
1373 if ((size() != 0) && ((*this)[0] != back())) {
1374 push_back((*this)[0]);
1375 }
1376}
1377
1378
1379std::vector<double>
1380PositionVector::distances(const PositionVector& s, bool perpendicular) const {
1381 std::vector<double> ret;
1382 const_iterator i;
1383 for (i = begin(); i != end(); i++) {
1384 const double dist = s.distance2D(*i, perpendicular);
1385 if (dist != GeomHelper::INVALID_OFFSET) {
1386 ret.push_back(dist);
1387 }
1388 }
1389 for (i = s.begin(); i != s.end(); i++) {
1390 const double dist = distance2D(*i, perpendicular);
1391 if (dist != GeomHelper::INVALID_OFFSET) {
1392 ret.push_back(dist);
1393 }
1394 }
1395 return ret;
1396}
1397
1398
1399double
1400PositionVector::distance2D(const Position& p, bool perpendicular) const {
1401 if (size() == 0) {
1402 return std::numeric_limits<double>::max();
1403 } else if (size() == 1) {
1404 return front().distanceTo2D(p);
1405 }
1406 const double nearestOffset = nearest_offset_to_point2D(p, perpendicular);
1407 if (nearestOffset == GeomHelper::INVALID_OFFSET) {
1409 } else {
1410 return p.distanceTo2D(positionAtOffset2D(nearestOffset));
1411 }
1412}
1413
1414
1415void
1417 if (empty()) {
1418 push_back(p);
1419 } else {
1420 insert(begin(), p);
1421 }
1422}
1423
1424
1425void
1427 if (empty()) {
1428 throw ProcessError("PositionVector is empty");
1429 } else {
1430 erase(begin());
1431 }
1432}
1433
1434
1435void
1437 if (size() == 0 || !p.almostSame(back())) {
1438 push_back(p);
1439 }
1440}
1441
1442
1443void
1445 if ((size() == 0) || !p.almostSame(front())) {
1446 push_front(p);
1447 }
1448}
1449
1450
1451void
1452PositionVector::insert_noDoublePos(const std::vector<Position>::iterator& at, const Position& p) {
1453 if (at == begin()) {
1455 } else if (at == end()) {
1457 } else {
1458 if (!p.almostSame(*at) && !p.almostSame(*(at - 1))) {
1459 insert(at, p);
1460 }
1461 }
1462}
1463
1464
1465bool
1467 return (size() >= 2) && ((*this)[0] == back());
1468}
1469
1470
1471bool
1473 // iterate over all positions and check if is NAN
1474 for (auto i = begin(); i != end(); i++) {
1475 if (i->isNAN()) {
1476 return true;
1477 }
1478 }
1479 // all ok, then return false
1480 return false;
1481}
1482
1483void
1485 const double limit = 2 * pow(10, -precision);
1486 if (length2D() < limit) {
1487 extrapolate2D(limit);
1488 }
1489}
1490
1491void
1492PositionVector::round(int precision, bool avoidDegeneration) {
1493 if (avoidDegeneration && size() > 1) {
1494 ensureMinLength(precision);
1495 }
1496 for (int i = 0; i < (int)size(); i++) {
1497 (*this)[i].round(precision);
1498 }
1499}
1500
1501
1502void
1503PositionVector::removeDoublePoints(double minDist, bool assertLength, int beginOffset, int endOffset, bool resample) {
1504 int curSize = (int)size() - beginOffset - endOffset;
1505 if (curSize > 1) {
1506 iterator last = begin() + beginOffset;
1507 for (iterator i = last + 1; i != (end() - endOffset) && (!assertLength || curSize > 2);) {
1508 if (last->almostSame(*i, minDist)) {
1509 if (i + 1 == end() - endOffset) {
1510 // special case: keep the last point and remove the next-to-last
1511 if (resample && last > begin() && (last - 1)->distanceTo(*i) >= 2 * minDist) {
1512 // resample rather than remove point after a long segment
1513 const double shiftBack = minDist - last->distanceTo(*i);
1514 //if (gDebugFlag1) std::cout << " resample endOffset beforeLast=" << *(last - 1) << " last=" << *last << " i=" << *i;
1515 (*last) = positionAtOffset(*(last - 1), *last, (last - 1)->distanceTo(*last) - shiftBack);
1516 //if (gDebugFlag1) std::cout << " lastNew=" << *last;
1517 last = i;
1518 ++i;
1519 } else {
1520 erase(last);
1521 i = end() - endOffset;
1522 }
1523 } else {
1524 if (resample && i + 1 != end() && last->distanceTo(*(i + 1)) >= 2 * minDist) {
1525 // resample rather than remove points before a long segment
1526 const double shiftForward = minDist - last->distanceTo(*i);
1527 //if (gDebugFlag1) std::cout << " resample last=" << *last << " i=" << *i << " next=" << *(i + 1);
1528 (*i) = positionAtOffset(*i, *(i + 1), shiftForward);
1529 //if (gDebugFlag1) std::cout << " iNew=" << *i << "\n";
1530 last = i;
1531 ++i;
1532 } else {
1533 i = erase(i);
1534 }
1535 }
1536 curSize--;
1537 } else {
1538 last = i;
1539 ++i;
1540 }
1541 }
1542 }
1543}
1544
1545
1546bool
1548 return static_cast<vp>(*this) == static_cast<vp>(v2);
1549}
1550
1551
1552bool
1554 return static_cast<vp>(*this) != static_cast<vp>(v2);
1555}
1556
1559 if (length() != v2.length()) {
1560 WRITE_ERROR(TL("Trying to subtract PositionVectors of different lengths."));
1561 }
1562 PositionVector pv;
1563 auto i1 = begin();
1564 auto i2 = v2.begin();
1565 while (i1 != end()) {
1566 pv.add(*i1 - *i2);
1567 }
1568 return pv;
1569}
1570
1573 if (length() != v2.length()) {
1574 WRITE_ERROR(TL("Trying to add PositionVectors of different lengths."));
1575 }
1576 PositionVector pv;
1577 auto i1 = begin();
1578 auto i2 = v2.begin();
1579 while (i1 != end()) {
1580 pv.add(*i1 + *i2);
1581 }
1582 return pv;
1583}
1584
1585bool
1586PositionVector::almostSame(const PositionVector& v2, double maxDiv) const {
1587 if (size() != v2.size()) {
1588 return false;
1589 }
1590 auto i2 = v2.begin();
1591 for (auto i1 = begin(); i1 != end(); i1++) {
1592 if (!i1->almostSame(*i2, maxDiv)) {
1593 return false;
1594 }
1595 i2++;
1596 }
1597 return true;
1598}
1599
1600bool
1602 if (size() < 2) {
1603 return false;
1604 }
1605 for (const_iterator i = begin(); i != end(); i++) {
1606 if ((*i).z() != 0) {
1607 return true;
1608 }
1609 }
1610 return false;
1611}
1612
1613
1614bool
1615PositionVector::intersects(const Position& p11, const Position& p12, const Position& p21, const Position& p22, const double withinDist, double* x, double* y, double* mu) {
1616 const double eps = std::numeric_limits<double>::epsilon();
1617 const double denominator = (p22.y() - p21.y()) * (p12.x() - p11.x()) - (p22.x() - p21.x()) * (p12.y() - p11.y());
1618 const double numera = (p22.x() - p21.x()) * (p11.y() - p21.y()) - (p22.y() - p21.y()) * (p11.x() - p21.x());
1619 const double numerb = (p12.x() - p11.x()) * (p11.y() - p21.y()) - (p12.y() - p11.y()) * (p11.x() - p21.x());
1620 /* Are the lines coincident? */
1621 if (fabs(numera) < eps && fabs(numerb) < eps && fabs(denominator) < eps) {
1622 double a1;
1623 double a2;
1624 double a3;
1625 double a4;
1626 double a = -1e12;
1627 if (p11.x() != p12.x()) {
1628 a1 = p11.x() < p12.x() ? p11.x() : p12.x();
1629 a2 = p11.x() < p12.x() ? p12.x() : p11.x();
1630 a3 = p21.x() < p22.x() ? p21.x() : p22.x();
1631 a4 = p21.x() < p22.x() ? p22.x() : p21.x();
1632 } else {
1633 a1 = p11.y() < p12.y() ? p11.y() : p12.y();
1634 a2 = p11.y() < p12.y() ? p12.y() : p11.y();
1635 a3 = p21.y() < p22.y() ? p21.y() : p22.y();
1636 a4 = p21.y() < p22.y() ? p22.y() : p21.y();
1637 }
1638 if (a1 <= a3 && a3 <= a2) {
1639 if (a4 < a2) {
1640 a = (a3 + a4) / 2;
1641 } else {
1642 a = (a2 + a3) / 2;
1643 }
1644 }
1645 if (a3 <= a1 && a1 <= a4) {
1646 if (a2 < a4) {
1647 a = (a1 + a2) / 2;
1648 } else {
1649 a = (a1 + a4) / 2;
1650 }
1651 }
1652 if (a != -1e12) {
1653 if (x != nullptr) {
1654 if (p11.x() != p12.x()) {
1655 *mu = (a - p11.x()) / (p12.x() - p11.x());
1656 *x = a;
1657 *y = p11.y() + (*mu) * (p12.y() - p11.y());
1658 } else {
1659 *x = p11.x();
1660 *y = a;
1661 if (p12.y() == p11.y()) {
1662 *mu = 0;
1663 } else {
1664 *mu = (a - p11.y()) / (p12.y() - p11.y());
1665 }
1666 }
1667 }
1668 return true;
1669 }
1670 return false;
1671 }
1672 /* Are the lines parallel */
1673 if (fabs(denominator) < eps) {
1674 return false;
1675 }
1676 /* Is the intersection along the segments */
1677 double mua = numera / denominator;
1678 /* reduce rounding errors for lines ending in the same point */
1679 if (fabs(p12.x() - p22.x()) < eps && fabs(p12.y() - p22.y()) < eps) {
1680 mua = 1.;
1681 } else {
1682 const double offseta = withinDist / p11.distanceTo2D(p12);
1683 const double offsetb = withinDist / p21.distanceTo2D(p22);
1684 const double mub = numerb / denominator;
1685 if (mua < -offseta || mua > 1 + offseta || mub < -offsetb || mub > 1 + offsetb) {
1686 return false;
1687 }
1688 }
1689 if (x != nullptr) {
1690 *x = p11.x() + mua * (p12.x() - p11.x());
1691 *y = p11.y() + mua * (p12.y() - p11.y());
1692 *mu = mua;
1693 }
1694 return true;
1695}
1696
1697
1698void
1700 const double s = sin(angle);
1701 const double c = cos(angle);
1702 for (int i = 0; i < (int)size(); i++) {
1703 const double x = (*this)[i].x();
1704 const double y = (*this)[i].y();
1705 const double z = (*this)[i].z();
1706 const double xnew = x * c - y * s;
1707 const double ynew = x * s + y * c;
1708 (*this)[i].set(xnew, ynew, z);
1709 }
1710}
1711
1712
1713void
1714PositionVector::rotate2D(const Position& pos, double angle) {
1715 PositionVector aux = *this;
1716 aux.sub(pos);
1717 aux.rotate2D(angle);
1718 aux.add(pos);
1719 *this = aux;
1720}
1721
1722
1723void
1725 if (size() > 1) {
1726 // translate position vector to (0,0), rotate, and traslate back again
1727 const Position offset = front();
1728 sub(offset);
1729 rotate2D(angle);
1730 add(offset);
1731 }
1732}
1733
1734
1737 PositionVector result = *this;
1738 bool changed = true;
1739 while (changed && result.size() > 3) {
1740 changed = false;
1741 for (int i = 0; i < (int)result.size(); i++) {
1742 const Position& p1 = result[i];
1743 const Position& p2 = result[(i + 2) % result.size()];
1744 const int middleIndex = (i + 1) % result.size();
1745 const Position& p0 = result[middleIndex];
1746 // https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_two_points
1747 const double triangleArea2 = fabs((p2.y() - p1.y()) * p0.x() - (p2.x() - p1.x()) * p0.y() + p2.x() * p1.y() - p2.y() * p1.x());
1748 const double distIK = p1.distanceTo2D(p2);
1749 if (distIK > NUMERICAL_EPS && triangleArea2 / distIK < NUMERICAL_EPS) {
1750 changed = true;
1751 result.erase(result.begin() + middleIndex);
1752 break;
1753 }
1754 }
1755 }
1756 return result;
1757}
1758
1759
1760const PositionVector
1761PositionVector::simplified2(const bool closed, const double eps) const {
1762 // this is a variation of the https://en.wikipedia.org/wiki/Visvalingam%E2%80%93Whyatt_algorithm
1763 // which uses the distance instead of the area
1764 // the benefits over the initial implementation are:
1765 // 3D support, no degenerate results for a sequence of small distances, keeping the longest part of a line
1766 // drawbacks: complexity of the code, speed
1767 if (size() < 3) {
1768 return *this;
1769 }
1770 auto calcScore = [&](const PositionVector & pv, int index) {
1771 if (!closed && (index == 0 || index == (int)pv.size() - 1)) {
1772 return eps + 1.;
1773 }
1774 const Position& p = pv[index];
1775 const Position& a = pv[(index + (int)pv.size() - 1) % pv.size()];
1776 const Position& b = pv[(index + 1) % pv.size()];
1777 const double distAB = a.distanceTo(b);
1778 if (distAB < MIN2(eps, NUMERICAL_EPS)) {
1779 // avoid division by 0 and degenerate cases due to very small baseline
1780 return (a.distanceTo(p) + b.distanceTo(p)) / 2.;
1781 }
1782 // https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Vector_formulation
1783 // calculating the distance of p to the line defined by a and b
1784 const Position dir = (b - a) / distAB;
1785 const double projectedLength = (a - p).dotProduct(dir);
1786 if (projectedLength <= -distAB) {
1787 return b.distanceTo(p);
1788 }
1789 if (projectedLength >= 0.) {
1790 return a.distanceTo(p);
1791 }
1792 const Position distVector = (a - p) - dir * projectedLength;
1793 return distVector.length();
1794 };
1795 std::vector<double> scores;
1796 double minScore = eps + 1.;
1797 int minIndex = -1;
1798 for (int i = 0; i < (int)size(); i++) {
1799 scores.push_back(calcScore(*this, i));
1800 if (scores.back() < minScore) {
1801 minScore = scores.back();
1802 minIndex = i;
1803 }
1804 }
1805 if (minScore >= eps) {
1806 return *this;
1807 }
1808 PositionVector result(*this);
1809 while (minScore < eps) {
1810 result.erase(result.begin() + minIndex);
1811 if (result.size() < 3) {
1812 break;
1813 }
1814 scores.erase(scores.begin() + minIndex);
1815 const int prevIndex = (minIndex + (int)result.size() - 1) % result.size();
1816 scores[prevIndex] = calcScore(result, prevIndex);
1817 scores[minIndex % result.size()] = calcScore(result, minIndex % result.size());
1818 minScore = eps + 1.;
1819 for (int i = 0; i < (int)result.size(); i++) {
1820 if (scores[i] < minScore) {
1821 minScore = scores[i];
1822 minIndex = i;
1823 }
1824 }
1825 }
1826 return result;
1827}
1828
1829
1831PositionVector::getOrthogonal(const Position& p, double extend, bool before, double length, double deg) const {
1832 PositionVector result;
1833 PositionVector tmp = *this;
1834 tmp.extrapolate2D(extend);
1835 const double baseOffset = tmp.nearest_offset_to_point2D(p);
1836 if (baseOffset == GeomHelper::INVALID_OFFSET || size() < 2) {
1837 // fail
1838 return result;
1839 }
1840 Position base = tmp.positionAtOffset2D(baseOffset);
1841 const int closestIndex = tmp.indexOfClosest(base);
1842 const double closestOffset = tmp.offsetAtIndex2D(closestIndex);
1843 result.push_back(base);
1844 if (fabs(baseOffset - closestOffset) > NUMERICAL_EPS) {
1845 result.push_back(tmp[closestIndex]);
1846 if ((closestOffset < baseOffset) != before) {
1847 deg *= -1;
1848 }
1849 } else if (before) {
1850 // take the segment before closestIndex if possible
1851 if (closestIndex > 0) {
1852 result.push_back(tmp[closestIndex - 1]);
1853 } else {
1854 result.push_back(tmp[1]);
1855 deg *= -1;
1856 }
1857 } else {
1858 // take the segment after closestIndex if possible
1859 if (closestIndex < (int)size() - 1) {
1860 result.push_back(tmp[closestIndex + 1]);
1861 } else {
1862 result.push_back(tmp[-1]);
1863 deg *= -1;
1864 }
1865 }
1866 result = result.getSubpart2D(0, length);
1867 // rotate around base
1868 result.add(base * -1);
1869 result.rotate2D(DEG2RAD(deg));
1870 result.add(base);
1871 return result;
1872}
1873
1874
1877 PositionVector result = *this;
1878 if (size() == 0) {
1879 return result;
1880 }
1881 const double z0 = (*this)[0].z();
1882 // the z-delta of the first segment
1883 const double dz = (*this)[1].z() - z0;
1884 // if the shape only has 2 points it is as smooth as possible already
1885 if (size() > 2 && dz != 0) {
1886 dist = MIN2(dist, length2D());
1887 // check wether we need to insert a new point at dist
1888 Position pDist = positionAtOffset2D(dist);
1889 int iLast = indexOfClosest(pDist);
1890 // prevent close spacing to reduce impact of rounding errors in z-axis
1891 if (pDist.distanceTo2D((*this)[iLast]) > POSITION_EPS * 20) {
1892 iLast = result.insertAtClosest(pDist, false);
1893 }
1894 double dist2 = result.offsetAtIndex2D(iLast);
1895 const double dz2 = result[iLast].z() - z0;
1896 double seen = 0;
1897 for (int i = 1; i < iLast; ++i) {
1898 seen += result[i].distanceTo2D(result[i - 1]);
1899 result[i].set(result[i].x(), result[i].y(), z0 + dz2 * seen / dist2);
1900 }
1901 }
1902 return result;
1903
1904}
1905
1906
1908PositionVector::interpolateZ(double zStart, double zEnd) const {
1909 PositionVector result = *this;
1910 if (size() == 0) {
1911 return result;
1912 }
1913 result[0].setz(zStart);
1914 result[-1].setz(zEnd);
1915 const double dz = zEnd - zStart;
1916 const double length = length2D();
1917 double seen = 0;
1918 for (int i = 1; i < (int)size() - 1; ++i) {
1919 seen += result[i].distanceTo2D(result[i - 1]);
1920 result[i].setz(zStart + dz * seen / length);
1921 }
1922 return result;
1923}
1924
1925
1927PositionVector::resample(double maxLength, const bool adjustEnd) const {
1928 PositionVector result;
1929 if (maxLength == 0) {
1930 return result;
1931 }
1932 const double length = length2D();
1933 if (length < POSITION_EPS) {
1934 return result;
1935 }
1936 maxLength = length / ceil(length / maxLength);
1937 for (double pos = 0; pos <= length; pos += maxLength) {
1938 result.push_back(positionAtOffset2D(pos));
1939 }
1940 // check if we have to adjust last element
1941 if (adjustEnd && !result.empty() && (result.back() != back())) {
1942 // add last element
1943 result.push_back(back());
1944 }
1945 return result;
1946}
1947
1948
1949double
1951 if (index < 0 || index >= (int)size()) {
1953 }
1954 double seen = 0;
1955 for (int i = 1; i <= index; ++i) {
1956 seen += (*this)[i].distanceTo2D((*this)[i - 1]);
1957 }
1958 return seen;
1959}
1960
1961
1962double
1963PositionVector::getMaxGrade(double& maxJump) const {
1964 double result = 0;
1965 for (int i = 1; i < (int)size(); ++i) {
1966 const Position& p1 = (*this)[i - 1];
1967 const Position& p2 = (*this)[i];
1968 const double distZ = fabs(p1.z() - p2.z());
1969 const double dist2D = p1.distanceTo2D(p2);
1970 if (dist2D == 0) {
1971 maxJump = MAX2(maxJump, distZ);
1972 } else {
1973 result = MAX2(result, distZ / dist2D);
1974 }
1975 }
1976 return result;
1977}
1978
1979
1980double
1982 double minZ = std::numeric_limits<double>::max();
1983 for (const Position& i : *this) {
1984 minZ = MIN2(minZ, i.z());
1985 }
1986 return minZ;
1987}
1988
1989
1992 // inspired by David F. Rogers
1993 assert(size() < 33);
1994 static const double fac[33] = {
1995 1.0, 1.0, 2.0, 6.0, 24.0, 120.0, 720.0, 5040.0, 40320.0, 362880.0, 3628800.0, 39916800.0, 479001600.0,
1996 6227020800.0, 87178291200.0, 1307674368000.0, 20922789888000.0, 355687428096000.0, 6402373705728000.0,
1997 121645100408832000.0, 2432902008176640000.0, 51090942171709440000.0, 1124000727777607680000.0,
1998 25852016738884976640000.0, 620448401733239439360000.0, 15511210043330985984000000.0,
1999 403291461126605635584000000.0, 10888869450418352160768000000.0, 304888344611713860501504000000.0,
2000 8841761993739701954543616000000.0, 265252859812191058636308480000000.0,
2001 8222838654177922817725562880000000.0, 263130836933693530167218012160000000.0
2002 };
2003 PositionVector ret;
2004 const int npts = (int)size();
2005 // calculate the points on the Bezier curve
2006 const double step = (double) 1.0 / (numPoints - 1);
2007 double t = 0.;
2008 Position prev;
2009 for (int i1 = 0; i1 < numPoints; i1++) {
2010 if ((1.0 - t) < 5e-6) {
2011 t = 1.0;
2012 }
2013 double x = 0., y = 0., z = 0.;
2014 for (int i = 0; i < npts; i++) {
2015 const double ti = (i == 0) ? 1.0 : pow(t, i);
2016 const double tni = (npts == i + 1) ? 1.0 : pow(1 - t, npts - i - 1);
2017 const double basis = fac[npts - 1] / (fac[i] * fac[npts - 1 - i]) * ti * tni;
2018 x += basis * at(i).x();
2019 y += basis * at(i).y();
2020 z += basis * at(i).z();
2021 }
2022 t += step;
2023 Position current(x, y, z);
2024 if (prev != current && !std::isnan(x) && !std::isnan(y) && !std::isnan(z)) {
2025 ret.push_back(current);
2026 }
2027 prev = current;
2028 }
2029 return ret;
2030}
2031
2032
2034 // The test is based on the computation of a signed area enclosed by the polygon.
2035 // If the polygon is in the upper (resp. the lower) half-plane and the area is
2036 // negatively (resp. positively) signed, then the polygon is CW oriented. In case
2037 // the polygon has points with both positive and negative y-coordinates, we translate
2038 // the polygon to apply the above simple area-based test.
2039 double area = 0.0;
2040 const double y_min = std::min_element(begin(), end(), [](Position p1, Position p2) {
2041 return p1.y() < p2.y();
2042 })->y();
2043 const double gap = y_min > 0.0 ? 0.0 : y_min;
2044 add(0., gap, 0.);
2045 const int last = (int)size() - 1;
2046 for (int i = 0; i < last; i++) {
2047 const Position& firstPoint = at(i);
2048 const Position& secondPoint = at(i + 1);
2049 area += (secondPoint.x() - firstPoint.x()) / (secondPoint.y() + firstPoint.y()) / 2.0;
2050 }
2051 area += (at(0).x() - at(last).x()) / (at(0).y() + at(last).y()) / 2.0;
2052 add(0., -gap, 0.);
2053 return area < 0.0;
2054}
2055
2056
2057/****************************************************************************/
#define DEG2RAD(x)
Definition GeomHelper.h:35
#define RAD2DEG(x)
Definition GeomHelper.h:36
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:287
#define WRITE_ERROR(msg)
Definition MsgHandler.h:295
#define TL(string)
Definition MsgHandler.h:304
std::ostream & operator<<(std::ostream &os, const PositionVector &geom)
bool gDebugFlag1
global utility flags for debugging
Definition StdDefs.cpp:43
const double INVALID_DOUBLE
invalid double
Definition StdDefs.h:68
T MIN2(T a, T b)
Definition StdDefs.h:80
T MAX2(T a, T b)
Definition StdDefs.h:86
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition ToString.h:46
virtual bool partialWithin(const AbstractPoly &poly, double offset=0) const =0
Returns whether the AbstractPoly is partially within the given polygon.
virtual bool crosses(const Position &p1, const Position &p2) const =0
Returns whether the AbstractPoly crosses the given line.
virtual bool around(const Position &p, double offset=0) const =0
Returns whether the AbstractPoly the given coordinate.
A class that stores a 2D geometrical boundary.
Definition Boundary.h:39
void add(double x, double y, double z=0)
Makes the boundary include the given coordinate.
Definition Boundary.cpp:75
static double angle2D(const Position &p1, const Position &p2)
Returns the angle between two vectors on a plane The angle is from vector 1 to vector 2,...
static const double INVALID_OFFSET
a value to signify offsets outside the range of [0, Line.length()]
Definition GeomHelper.h:50
static double nearest_offset_on_line_to_point2D(const Position &lineStart, const Position &lineEnd, const Position &p, bool perpendicular=true)
static double legacyDegree(const double angle, const bool positive=false)
static double angleDiff(const double angle1, const double angle2)
Returns the difference of the second angle to the first angle in radiants.
A point in 2D or 3D with translation and scaling methods.
Definition Position.h:37
double length() const
Computes the length of the given vector.
Definition Position.h:169
double distanceSquaredTo2D(const Position &p2) const
returns the square of the distance to another position (Only using x and y positions)
Definition Position.h:278
double slopeTo2D(const Position &other) const
returns the slope of the vector pointing from here to the other position (in radians between -M_PI an...
Definition Position.h:288
double dotProduct(const Position &pos) const
returns the dot product (scalar product) between this point and the second one
Definition Position.h:301
static const Position INVALID
used to indicate that a position is valid
Definition Position.h:323
double distanceTo2D(const Position &p2) const
returns the euclidean distance in the x-y-plane
Definition Position.h:273
double distanceTo(const Position &p2) const
returns the euclidean distance in 3 dimensions
Definition Position.h:263
void sub(double dx, double dy)
Subtracts the given position from this one.
Definition Position.h:149
double x() const
Returns the x-position.
Definition Position.h:52
void add(const Position &pos)
Adds the given position to this one.
Definition Position.h:129
double z() const
Returns the z-position.
Definition Position.h:62
double angleTo2D(const Position &other) const
returns the angle in the plane of the vector pointing from here to the other position (in radians bet...
Definition Position.h:283
bool almostSame(const Position &p2, double maxDiv=POSITION_EPS) const
check whether the other position has a euclidean distance of less than maxDiv
Definition Position.h:258
double y() const
Returns the y-position.
Definition Position.h:57
int operator()(const Position &p1, const Position &p2) const
comparing operation for sort
double atAngle2D(const Position &p) const
computes the angle of the given vector, in the range $[0,2*\pi[$
clase for increasing Sorter
int operator()(const Position &p1, const Position &p2) const
comparing operation
A list of positions.
bool isClockwiseOriented(void)
PositionVector operator-(const PositionVector &v2) const
subtracts two vectors (requires vectors of the same length)
void scaleAbsolute(double offset)
enlarges/shrinks the polygon by an absolute offset based at the centroid
double length2D() const
Returns the length.
void append(const PositionVector &v, double sameThreshold=2.0)
bool overlapsWith(const AbstractPoly &poly, double offset=0) const
Returns the information whether the given polygon overlaps with this.
PositionVector added(const Position &offset) const
double isLeft(const Position &P0, const Position &P1, const Position &P2) const
get left
double beginEndAngle() const
returns the angle in radians of the line connecting the first and the last position
double getMinZ() const
return minimum z-coordinate
double length() const
Returns the length.
void move2sideCustom(std::vector< double > amount, double maxExtension=100)
move position vector to side using a custom offset for each geometry point
void round(int precision, bool avoidDegeneration=true)
round all coordinates to the given precision
void sortAsPolyCWByAngle()
sort as polygon CW by angle
PositionVector simplified() const
return the same shape with intermediate colinear points removed
void rotate2D(double angle)
PositionVector()
Constructor. Creates an empty position vector.
Position getPolygonCenter() const
Returns the arithmetic of all corner points.
Position intersectionPosition2D(const Position &p1, const Position &p2, const double withinDist=0.) const
Returns the position of the intersection.
const Position & operator[](int index) const
returns the constant position at the given index, negative indices are interpreted python style
double rotationAtOffset(double pos) const
Returns the rotation at the given length.
std::vector< Position > vp
vector of position
void ensureMinLength(int precision)
ensure minimum length so that the geometry will not degenerate to 0-length on writing with the given ...
void push_front_noDoublePos(const Position &p)
insert in front a non double position
bool operator!=(const PositionVector &v2) const
comparing operation
PositionVector resample(double maxLength, const bool adjustEnd) const
resample shape (i.e. transform to segments, equal spacing)
void sortByIncreasingXY()
sort by increasing X-Y Positions
double rotationDegreeAtOffset(double pos) const
Returns the rotation at the given length.
bool isNAN() const
check if PositionVector is NAN
Position positionAtOffset(double pos, double lateralOffset=0) const
Returns the position at the given length.
void add(double xoff, double yoff, double zoff)
void closePolygon()
ensures that the last position equals the first
static Position sideOffset(const Position &beg, const Position &end, const double amount)
get a side position of position vector using a offset
std::vector< double > intersectsAtLengths2D(const PositionVector &other) const
For all intersections between this vector and other, return the 2D-length of the subvector from this ...
double distance2D(const Position &p, bool perpendicular=false) const
closest 2D-distance to point p (or -1 if perpendicular is true and the point is beyond this vector)
void prepend(const PositionVector &v, double sameThreshold=2.0)
double nearest_offset_to_point2D(const Position &p, bool perpendicular=true) const
return the nearest offest to point 2D
std::vector< double > distances(const PositionVector &s, bool perpendicular=false) const
distances of all my points to s and all of s points to myself
PositionVector getOrthogonal(const Position &p, double extend, bool before, double length=1.0, double deg=90) const
return orthogonal through p (extending this vector if necessary)
void openPolygon()
open polygon
int indexOfClosest(const Position &p, bool twoD=false) const
std::pair< PositionVector, PositionVector > splitAt(double where, bool use2D=false) const
Returns the two lists made when this list vector is splitted at the given point.
void move2side(double amount, double maxExtension=100)
move position vector to side using certain amount
bool almostSame(const PositionVector &v2, double maxDiv=POSITION_EPS) const
check if the two vectors have the same length and pairwise similar positions
bool crosses(const Position &p1, const Position &p2) const
Returns whether the AbstractPoly crosses the given line.
PositionVector getSubpart2D(double beginOffset, double endOffset) const
get subpart of a position vector in two dimensions (Z is ignored)
PositionVector interpolateZ(double zStart, double zEnd) const
returned vector that varies z smoothly over its length
Boundary getBoxBoundary() const
Returns a boundary enclosing this list of lines.
double offsetAtIndex2D(int index) const
return the offset at the given index
PositionVector smoothedZFront(double dist=std::numeric_limits< double >::max()) const
returned vector that is smoothed at the front (within dist)
double angleAt2D(int pos) const
get angle in certain position of position vector (in radians between -M_PI and M_PI)
void insert_noDoublePos(const std::vector< Position >::iterator &at, const Position &p)
insert in front a non double position
double slopeDegreeAtOffset(double pos) const
Returns the slope at the given length.
bool hasElevation() const
return whether two positions differ in z-coordinate
static const PositionVector EMPTY
empty Vector
void extrapolate(const double val, const bool onlyFirst=false, const bool onlyLast=false)
extrapolate position vector
PositionVector bezier(int numPoints)
return a bezier interpolation
Position getLineCenter() const
get line center
Position getCentroid() const
Returns the centroid (closes the polygon if unclosed)
double getOverlapWith(const PositionVector &poly, double zThreshold) const
Returns the maximum overlaps between this and the given polygon (when not separated by at least zThre...
PositionVector operator+(const PositionVector &v2) const
adds two vectors (requires vectors of the same length)
void extrapolate2D(const double val, const bool onlyFirst=false)
extrapolate position vector in two dimensions (Z is ignored)
void rotateAroundFirstElement2D(double angle)
int insertAtClosest(const Position &p, bool interpolateZ)
inserts p between the two closest positions
const PositionVector simplified2(const bool closed, const double eps=NUMERICAL_EPS) const
void push_front(const Position &p)
insert in front a Position
Position positionAtOffset2D(double pos, double lateralOffset=0, bool extrapolateBeyond=false) const
Returns the position at the given length.
void scaleRelative(double factor)
enlarges/shrinks the polygon by a factor based at the centroid
void push_back_noDoublePos(const Position &p)
insert in back a non double position
void removeDoublePoints(double minDist=POSITION_EPS, bool assertLength=false, int beginOffset=0, int endOffset=0, bool resample=false)
Removes positions if too near.
bool partialWithin(const AbstractPoly &poly, double offset=0) const
Returns the information whether this polygon lies partially within the given polygon.
double getMaxGrade(double &maxJump) const
double area() const
Returns the area (0 for non-closed)
bool isClosed() const
check if PositionVector is closed
void pop_front()
pop first Position
double nearest_offset_to_point25D(const Position &p, bool perpendicular=true) const
return the nearest offest to point 2D projected onto the 3D geometry
int removeClosest(const Position &p)
removes the point closest to p and return the removal index
static double localAngle(const Position &from, const Position &pos, const Position &to)
Position sidePositionAtAngle(double pos, double lateralOffset, double angle) const
bool intersects(const Position &p1, const Position &p2) const
Returns the information whether this list of points interesects the given line.
PositionVector reverse() const
reverse position vector
PositionVector getSubpartByIndex(int beginIndex, int count) const
get subpart of a position vector using index and a cout
bool operator==(const PositionVector &v2) const
comparing operation
void sub(const Position &offset)
PositionVector getSubpart(double beginOffset, double endOffset) const
get subpart of a position vector
~PositionVector()
Destructor.
bool around(const Position &p, double offset=0) const
Returns the information whether the position vector describes a polygon lying around the given point.
Position transformToVectorCoordinates(const Position &p, bool extend=false) const
return position p within the length-wise coordinate system defined by this position vector....
#define M_PI
Definition odrSpiral.cpp:45