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-2026 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, it is probably only one point
499 result = lengthSum == 0. ? tmp[0] : Position(x / lengthSum, y / lengthSum);
500 }
501 return result + offset;
502}
503
504
505void
507 Position centroid = getCentroid();
508 for (int i = 0; i < static_cast<int>(size()); i++) {
509 (*this)[i] = centroid + (((*this)[i] - centroid) * factor);
510 }
511}
512
513
514void
516 Position centroid = getCentroid();
517 for (int i = 0; i < static_cast<int>(size()); i++) {
518 (*this)[i] = centroid + (((*this)[i] - centroid) + offset);
519 }
520}
521
522
525 if (size() == 1) {
526 return (*this)[0];
527 } else {
528 return positionAtOffset(double((length() / 2.)));
529 }
530}
531
532
533double
535 if (size() == 0) {
536 return 0;
537 }
538 double len = 0;
539 for (const_iterator i = begin(); i != end() - 1; i++) {
540 len += (*i).distanceTo(*(i + 1));
541 }
542 return len;
543}
544
545
546double
548 if (size() == 0) {
549 return 0;
550 }
551 double len = 0;
552 for (const_iterator i = begin(); i != end() - 1; i++) {
553 len += (*i).distanceTo2D(*(i + 1));
554 }
555 return len;
556}
557
558
559double
561 if (size() < 3) {
562 return 0;
563 }
564 double area = 0;
565 PositionVector tmp = *this;
566 if (!isClosed()) { // make sure its closed
567 tmp.push_back(tmp[0]);
568 }
569 const int endIndex = (int)tmp.size() - 1;
570 // http://en.wikipedia.org/wiki/Polygon
571 for (int i = 0; i < endIndex; i++) {
572 area += tmp[i].x() * tmp[i + 1].y() - tmp[i + 1].x() * tmp[i].y();
573 }
574 if (area < 0) { // we whether we had cw or ccw order
575 area *= -1;
576 }
577 return area / 2;
578}
579
580
581bool
582PositionVector::partialWithin(const AbstractPoly& poly, double offset) const {
583 if (size() < 2) {
584 return false;
585 }
586 for (const_iterator i = begin(); i != end(); i++) {
587 if (poly.around(*i, offset)) {
588 return true;
589 }
590 }
591 return false;
592}
593
594
595bool
596PositionVector::crosses(const Position& p1, const Position& p2) const {
597 return intersects(p1, p2);
598}
599
600
601std::pair<PositionVector, PositionVector>
602PositionVector::splitAt(double where, bool use2D) const {
603 const double len = use2D ? length2D() : length();
604 if (size() < 2) {
605 throw InvalidArgument("Vector to short for splitting");
606 }
607 if (where < 0 || where > len) {
608 throw InvalidArgument("Invalid split position " + toString(where) + " for vector of length " + toString(len));
609 }
610 if (where <= POSITION_EPS || where >= len - POSITION_EPS) {
611 WRITE_WARNINGF(TL("Splitting vector close to end (pos: %, length: %)"), toString(where), toString(len));
612 }
613 PositionVector first, second;
614 first.push_back((*this)[0]);
615 double seen = 0;
616 const_iterator it = begin() + 1;
617 double next = use2D ? first.back().distanceTo2D(*it) : first.back().distanceTo(*it);
618 // see how many points we can add to first
619 while (where >= seen + next + POSITION_EPS) {
620 seen += next;
621 first.push_back(*it);
622 it++;
623 next = use2D ? first.back().distanceTo2D(*it) : first.back().distanceTo(*it);
624 }
625 if (fabs(where - (seen + next)) > POSITION_EPS || it == end() - 1) {
626 // we need to insert a new point because 'where' is not close to an
627 // existing point or it is to close to the endpoint
628 const Position p = (use2D
629 ? positionAtOffset2D(first.back(), *it, where - seen)
630 : positionAtOffset(first.back(), *it, where - seen));
631 first.push_back(p);
632 second.push_back(p);
633 } else {
634 first.push_back(*it);
635 }
636 // add the remaining points to second
637 for (; it != end(); it++) {
638 second.push_back(*it);
639 }
640 assert(first.size() >= 2);
641 assert(second.size() >= 2);
642 assert(first.back() == second.front());
643 assert(fabs((use2D ? first.length2D() + second.length2D() : first.length() + second.length()) - len) < 2 * POSITION_EPS);
644 return std::pair<PositionVector, PositionVector>(first, second);
645}
646
647
648std::ostream&
649operator<<(std::ostream& os, const PositionVector& geom) {
650 for (PositionVector::const_iterator i = geom.begin(); i != geom.end(); i++) {
651 if (i != geom.begin()) {
652 os << " ";
653 }
654 os << (*i);
655 }
656 return os;
657}
658
659
660void
662 // We take the centroid of the points as an origin for the angle computations
663 // that will follow but other points could be taken (the center of the bounding
664 // box of the polygon for instance). Each of these can potentially lead
665 // to a different result in the case of a non-convex polygon.
666 const Position centroid = std::accumulate(begin(), end(), Position(0, 0)) / (double)size();
667 sub(centroid);
668 std::sort(begin(), end(), as_poly_cw_sorter());
669 add(centroid);
670}
671
672
673void
674PositionVector::add(double xoff, double yoff, double zoff) {
675 for (int i = 0; i < (int)size(); i++) {
676 (*this)[i].add(xoff, yoff, zoff);
677 }
678}
679
680
681void
683 add(-offset.x(), -offset.y(), -offset.z());
684}
685
686
687void
689 add(offset.x(), offset.y(), offset.z());
690}
691
692
694PositionVector::added(const Position& offset) const {
696 for (auto i1 = begin(); i1 != end(); ++i1) {
697 pv.push_back(*i1 + offset);
698 }
699 return pv;
700}
701
702
703void
705 for (int i = 0; i < (int)size(); i++) {
706 (*this)[i].mul(1, -1);
707 }
708}
709
710
712
713
714int
716 double angle1 = atAngle2D(p1);
717 double angle2 = atAngle2D(p2);
718 if (angle1 > angle2) {
719 return true;
720 }
721 if (angle1 == angle2) {
722 double squaredDistance1 = p1.dotProduct(p1);
723 double squaredDistance2 = p2.dotProduct(p2);
724 if (squaredDistance1 < squaredDistance2) {
725 return true;
726 }
727 }
728 return false;
729}
730
731
732double
734 double angle = atan2(p.y(), p.x());
735 return angle < 0.0 ? angle : angle + 2.0 * M_PI;
736}
737
738void
740 std::sort(begin(), end(), increasing_x_y_sorter());
741}
742
743
745
746
747int
749 if (p1.x() != p2.x()) {
750 return p1.x() < p2.x();
751 }
752 return p1.y() < p2.y();
753}
754
755
756double
757PositionVector::isLeft(const Position& P0, const Position& P1, const Position& P2) const {
758 return (P1.x() - P0.x()) * (P2.y() - P0.y()) - (P2.x() - P0.x()) * (P1.y() - P0.y());
759}
760
761
762void
763PositionVector::append(const PositionVector& v, double sameThreshold) {
764 if ((size() > 0) && (v.size() > 0) && (back().distanceTo(v[0]) < sameThreshold)) {
765 copy(v.begin() + 1, v.end(), back_inserter(*this));
766 } else {
767 copy(v.begin(), v.end(), back_inserter(*this));
768 }
769}
770
771
772void
773PositionVector::prepend(const PositionVector& v, double sameThreshold) {
774 if ((size() > 0) && (v.size() > 0) && (front().distanceTo(v.back()) < sameThreshold)) {
775 insert(begin(), v.begin(), v.end() - 1);
776 } else {
777 insert(begin(), v.begin(), v.end());
778 }
779}
780
781
783PositionVector::getSubpart(double beginOffset, double endOffset) const {
784 PositionVector ret;
785 Position begPos = front();
786 if (beginOffset > POSITION_EPS) {
787 begPos = positionAtOffset(beginOffset);
788 }
789 Position endPos = back();
790 if (endOffset < length() - POSITION_EPS) {
791 endPos = positionAtOffset(endOffset);
792 }
793 ret.push_back(begPos);
794
795 double seen = 0;
796 const_iterator i = begin();
797 // skip previous segments
798 while ((i + 1) != end()
799 &&
800 seen + (*i).distanceTo(*(i + 1)) < beginOffset) {
801 seen += (*i).distanceTo(*(i + 1));
802 i++;
803 }
804 // append segments in between
805 while ((i + 1) != end()
806 &&
807 seen + (*i).distanceTo(*(i + 1)) < endOffset) {
808
809 ret.push_back_noDoublePos(*(i + 1));
810 seen += (*i).distanceTo(*(i + 1));
811 i++;
812 }
813 // append end
814 ret.push_back_noDoublePos(endPos);
815 if (ret.size() == 1) {
816 ret.push_back(endPos);
817 }
818 return ret;
819}
820
821
823PositionVector::getSubpart2D(double beginOffset, double endOffset) const {
824 if (size() == 0) {
825 return PositionVector();
826 }
827 PositionVector ret;
828 Position begPos = front();
829 if (beginOffset > POSITION_EPS) {
830 begPos = positionAtOffset2D(beginOffset);
831 }
832 Position endPos = back();
833 if (endOffset < length2D() - POSITION_EPS) {
834 endPos = positionAtOffset2D(endOffset);
835 }
836 ret.push_back(begPos);
837
838 double seen = 0;
839 const_iterator i = begin();
840 // skip previous segments
841 while ((i + 1) != end()
842 &&
843 seen + (*i).distanceTo2D(*(i + 1)) < beginOffset) {
844 seen += (*i).distanceTo2D(*(i + 1));
845 i++;
846 }
847 // append segments in between
848 while ((i + 1) != end()
849 &&
850 seen + (*i).distanceTo2D(*(i + 1)) < endOffset) {
851
852 ret.push_back_noDoublePos(*(i + 1));
853 seen += (*i).distanceTo2D(*(i + 1));
854 i++;
855 }
856 // append end
857 ret.push_back_noDoublePos(endPos);
858 if (ret.size() == 1) {
859 ret.push_back(endPos);
860 }
861 return ret;
862}
863
864
866PositionVector::getSubpartByIndex(int beginIndex, int count) const {
867 if (size() == 0) {
868 return PositionVector();
869 }
870 if (beginIndex < 0) {
871 beginIndex += (int)size();
872 }
873 assert(count >= 0);
874 assert(beginIndex < (int)size());
875 assert(beginIndex + count <= (int)size());
876 PositionVector result;
877 for (int i = beginIndex; i < beginIndex + count; ++i) {
878 result.push_back((*this)[i]);
879 }
880 return result;
881}
882
883
884double
886 if (size() == 0) {
887 return INVALID_DOUBLE;
888 }
889 return front().angleTo2D(back());
890}
891
892
893double
894PositionVector::nearest_offset_to_point2D(const Position& p, bool perpendicular) const {
895 if (size() == 0) {
896 return INVALID_DOUBLE;
897 }
898 double minDist = std::numeric_limits<double>::max();
899 double nearestPos = GeomHelper::INVALID_OFFSET;
900 double seen = 0;
901 for (const_iterator i = begin(); i != end() - 1; i++) {
902 const double pos =
903 GeomHelper::nearest_offset_on_line_to_point2D(*i, *(i + 1), p, perpendicular);
904 const double dist2 = pos == GeomHelper::INVALID_OFFSET ? minDist : p.distanceSquaredTo2D(positionAtOffset2D(*i, *(i + 1), pos));
905 if (dist2 < minDist) {
906 nearestPos = pos + seen;
907 minDist = dist2;
908 }
909 if (perpendicular && i != begin() && pos == GeomHelper::INVALID_OFFSET) {
910 // even if perpendicular is set we still need to check the distance to the inner points
911 const double cornerDist2 = p.distanceSquaredTo2D(*i);
912 if (cornerDist2 < minDist) {
913 const double pos1 =
914 GeomHelper::nearest_offset_on_line_to_point2D(*(i - 1), *i, p, false);
915 const double pos2 =
916 GeomHelper::nearest_offset_on_line_to_point2D(*i, *(i + 1), p, false);
917 if (pos1 == (*(i - 1)).distanceTo2D(*i) && pos2 == 0.) {
918 nearestPos = seen;
919 minDist = cornerDist2;
920 }
921 }
922 }
923 seen += (*i).distanceTo2D(*(i + 1));
924 }
925 return nearestPos;
926}
927
928
929double
930PositionVector::nearest_offset_to_point25D(const Position& p, bool perpendicular) const {
931 if (size() == 0) {
932 return INVALID_DOUBLE;
933 }
934 double minDist = std::numeric_limits<double>::max();
935 double nearestPos = GeomHelper::INVALID_OFFSET;
936 double seen = 0;
937 for (const_iterator i = begin(); i != end() - 1; i++) {
938 const double pos =
939 GeomHelper::nearest_offset_on_line_to_point2D(*i, *(i + 1), p, perpendicular);
940 const double dist = pos == GeomHelper::INVALID_OFFSET ? minDist : p.distanceTo2D(positionAtOffset2D(*i, *(i + 1), pos));
941 if (dist < minDist) {
942 const double pos25D = pos * (*i).distanceTo(*(i + 1)) / (*i).distanceTo2D(*(i + 1));
943 nearestPos = pos25D + seen;
944 minDist = dist;
945 }
946 if (perpendicular && i != begin() && pos == GeomHelper::INVALID_OFFSET) {
947 // even if perpendicular is set we still need to check the distance to the inner points
948 const double cornerDist = p.distanceTo2D(*i);
949 if (cornerDist < minDist) {
950 const double pos1 =
951 GeomHelper::nearest_offset_on_line_to_point2D(*(i - 1), *i, p, false);
952 const double pos2 =
953 GeomHelper::nearest_offset_on_line_to_point2D(*i, *(i + 1), p, false);
954 if (pos1 == (*(i - 1)).distanceTo2D(*i) && pos2 == 0.) {
955 nearestPos = seen;
956 minDist = cornerDist;
957 }
958 }
959 }
960 seen += (*i).distanceTo(*(i + 1));
961 }
962 return nearestPos;
963}
964
965
968 if (size() == 0) {
969 return Position::INVALID;
970 }
971 // @toDo this duplicates most of the code in nearest_offset_to_point2D. It should be refactored
972 if (extend) {
973 PositionVector extended = *this;
974 const double dist = 2 * distance2D(p);
975 extended.extrapolate(dist);
976 return extended.transformToVectorCoordinates(p) - Position(dist, 0);
977 }
978 double minDist = std::numeric_limits<double>::max();
979 double nearestPos = -1;
980 double seen = 0;
981 int sign = 1;
982 for (const_iterator i = begin(); i != end() - 1; i++) {
983 const double pos =
985 const double dist = pos < 0 ? minDist : p.distanceTo2D(positionAtOffset(*i, *(i + 1), pos));
986 if (dist < minDist) {
987 nearestPos = pos + seen;
988 minDist = dist;
989 sign = isLeft(*i, *(i + 1), p) >= 0 ? -1 : 1;
990 }
991 if (i != begin() && pos == GeomHelper::INVALID_OFFSET) {
992 // even if perpendicular is set we still need to check the distance to the inner points
993 const double cornerDist = p.distanceTo2D(*i);
994 if (cornerDist < minDist) {
995 const double pos1 =
996 GeomHelper::nearest_offset_on_line_to_point2D(*(i - 1), *i, p, false);
997 const double pos2 =
998 GeomHelper::nearest_offset_on_line_to_point2D(*i, *(i + 1), p, false);
999 if (pos1 == (*(i - 1)).distanceTo2D(*i) && pos2 == 0.) {
1000 nearestPos = seen;
1001 minDist = cornerDist;
1002 sign = isLeft(*(i - 1), *i, p) >= 0 ? -1 : 1;
1003 }
1004 }
1005 }
1006 seen += (*i).distanceTo2D(*(i + 1));
1007 }
1008 if (nearestPos != -1) {
1009 return Position(nearestPos, sign * minDist);
1010 } else {
1011 return Position::INVALID;
1012 }
1013}
1014
1015
1016int
1017PositionVector::indexOfClosest(const Position& p, bool twoD) const {
1018 if (size() == 0) {
1019 return -1;
1020 }
1021 double minDist = std::numeric_limits<double>::max();
1022 double dist;
1023 int closest = 0;
1024 for (int i = 0; i < (int)size(); i++) {
1025 const Position& p2 = (*this)[i];
1026 dist = twoD ? p.distanceTo2D(p2) : p.distanceTo(p2);
1027 if (dist < minDist) {
1028 closest = i;
1029 minDist = dist;
1030 }
1031 }
1032 return closest;
1033}
1034
1035
1036int
1038 if (size() == 0) {
1039 return -1;
1040 }
1041 double minDist = std::numeric_limits<double>::max();
1042 int insertionIndex = 1;
1043 for (int i = 0; i < (int)size() - 1; i++) {
1044 const double length = GeomHelper::nearest_offset_on_line_to_point2D((*this)[i], (*this)[i + 1], p, false);
1045 const Position& outIntersection = PositionVector::positionAtOffset2D((*this)[i], (*this)[i + 1], length);
1046 const double dist = p.distanceTo2D(outIntersection);
1047 if (dist < minDist) {
1048 insertionIndex = i + 1;
1049 minDist = dist;
1050 }
1051 }
1052 // check if we have to adjust Position Z
1053 if (interpolateZ) {
1054 // obtain previous and next Z
1055 const double previousZ = (begin() + (insertionIndex - 1))->z();
1056 const double nextZ = (begin() + insertionIndex)->z();
1057 // insert new position using x and y of p, and the new z
1058 insert(begin() + insertionIndex, Position(p.x(), p.y(), ((previousZ + nextZ) / 2.0)));
1059 } else {
1060 insert(begin() + insertionIndex, p);
1061 }
1062 return insertionIndex;
1063}
1064
1065
1066int
1068 if (size() == 0) {
1069 return -1;
1070 }
1071 double minDist = std::numeric_limits<double>::max();
1072 int removalIndex = 0;
1073 for (int i = 0; i < (int)size(); i++) {
1074 const double dist = p.distanceTo2D((*this)[i]);
1075 if (dist < minDist) {
1076 removalIndex = i;
1077 minDist = dist;
1078 }
1079 }
1080 erase(begin() + removalIndex);
1081 return removalIndex;
1082}
1083
1084
1085std::vector<double>
1087 std::vector<double> ret;
1088 if (other.size() == 0) {
1089 return ret;
1090 }
1091 for (const_iterator i = other.begin(); i != other.end() - 1; i++) {
1092 std::vector<double> atSegment = intersectsAtLengths2D(*i, *(i + 1));
1093 copy(atSegment.begin(), atSegment.end(), back_inserter(ret));
1094 }
1095 return ret;
1096}
1097
1098
1099std::vector<double>
1101 std::vector<double> ret;
1102 if (size() == 0) {
1103 return ret;
1104 }
1105 double pos = 0;
1106 for (const_iterator i = begin(); i != end() - 1; i++) {
1107 const Position& p1 = *i;
1108 const Position& p2 = *(i + 1);
1109 double x, y, m;
1110 if (intersects(p1, p2, lp1, lp2, 0., &x, &y, &m)) {
1111 ret.push_back(Position(x, y).distanceTo2D(p1) + pos);
1112 }
1113 pos += p1.distanceTo2D(p2);
1114 }
1115 return ret;
1116}
1117
1118
1119void
1120PositionVector::extrapolate(const double val, const bool onlyFirst, const bool onlyLast) {
1121 if (size() > 1) {
1122 Position& p1 = (*this)[0];
1123 Position& p2 = (*this)[1];
1124 const Position offset = (p2 - p1) * (val / p1.distanceTo(p2));
1125 if (!onlyLast) {
1126 p1.sub(offset);
1127 }
1128 if (!onlyFirst) {
1129 if (size() == 2) {
1130 p2.add(offset);
1131 } else {
1132 const Position& e1 = (*this)[-2];
1133 Position& e2 = (*this)[-1];
1134 e2.sub((e1 - e2) * (val / e1.distanceTo(e2)));
1135 }
1136 }
1137 }
1138}
1139
1140
1141void
1142PositionVector::extrapolate2D(const double val, const bool onlyFirst) {
1143 if (size() > 1) {
1144 Position& p1 = (*this)[0];
1145 Position& p2 = (*this)[1];
1146 if (p1.distanceTo2D(p2) > 0) {
1147 const Position offset = (p2 - p1) * (val / p1.distanceTo2D(p2));
1148 p1.sub(offset);
1149 if (!onlyFirst) {
1150 if (size() == 2) {
1151 p2.add(offset);
1152 } else {
1153 const Position& e1 = (*this)[-2];
1154 Position& e2 = (*this)[-1];
1155 e2.sub((e1 - e2) * (val / e1.distanceTo2D(e2)));
1156 }
1157 }
1158 }
1159 }
1160}
1161
1162
1165 PositionVector ret;
1166 for (const_reverse_iterator i = rbegin(); i != rend(); i++) {
1167 ret.push_back(*i);
1168 }
1169 return ret;
1170}
1171
1172
1174PositionVector::sideOffset(const Position& beg, const Position& end, const double amount) {
1175 const double scale = amount / beg.distanceTo2D(end);
1176 return Position((beg.y() - end.y()) * scale, (end.x() - beg.x()) * scale);
1177}
1178
1179
1180void
1181PositionVector::move2side(double amount, double maxExtension) {
1182 if (size() < 2) {
1183 return;
1184 }
1185 removeDoublePoints(POSITION_EPS, true);
1186 if (length2D() == 0 || amount == 0) {
1187 return;
1188 }
1189 PositionVector shape;
1190 std::vector<int> recheck;
1191 for (int i = 0; i < static_cast<int>(size()); i++) {
1192 if (i == 0) {
1193 const Position& from = (*this)[i];
1194 const Position& to = (*this)[i + 1];
1195 if (from != to) {
1196 shape.push_back(from - sideOffset(from, to, amount));
1197#ifdef DEBUG_MOVE2SIDE
1198 if (gDebugFlag1) {
1199 std::cout << " " << i << "a=" << shape.back() << "\n";
1200 }
1201#endif
1202 }
1203 } else if (i == static_cast<int>(size()) - 1) {
1204 const Position& from = (*this)[i - 1];
1205 const Position& to = (*this)[i];
1206 if (from != to) {
1207 shape.push_back(to - sideOffset(from, to, amount));
1208#ifdef DEBUG_MOVE2SIDE
1209 if (gDebugFlag1) {
1210 std::cout << " " << i << "b=" << shape.back() << "\n";
1211 }
1212#endif
1213 }
1214 } else {
1215 const Position& from = (*this)[i - 1];
1216 const Position& me = (*this)[i];
1217 const Position& to = (*this)[i + 1];
1218 PositionVector fromMe(from, me);
1219 fromMe.extrapolate2D(me.distanceTo2D(to));
1220 const double extrapolateDev = fromMe[1].distanceTo2D(to);
1221 if (fabs(extrapolateDev) < POSITION_EPS) {
1222 // parallel case, just shift the middle point
1223 shape.push_back(me - sideOffset(from, to, amount));
1224#ifdef DEBUG_MOVE2SIDE
1225 if (gDebugFlag1) {
1226 std::cout << " " << i << "c=" << shape.back() << "\n";
1227 }
1228#endif
1229 } else if (fabs(extrapolateDev - 2 * me.distanceTo2D(to)) < POSITION_EPS) {
1230 // counterparallel case, just shift the middle point
1231 PositionVector fromMe2(from, me);
1232 fromMe2.extrapolate2D(amount);
1233 shape.push_back(fromMe2[1]);
1234#ifdef DEBUG_MOVE2SIDE
1235 if (gDebugFlag1) {
1236 std::cout << " " << i << "d=" << shape.back() << " " << i << "_from=" << from << " " << i << "_me=" << me << " " << i << "_to=" << to << "\n";
1237 }
1238#endif
1239 } else {
1240 Position offsets = sideOffset(from, me, amount);
1241 Position offsets2 = sideOffset(me, to, amount);
1242 PositionVector l1(from - offsets, me - offsets);
1243 PositionVector l2(me - offsets2, to - offsets2);
1244 Position meNew = l1.intersectionPosition2D(l2[0], l2[1], maxExtension);
1245 if (meNew == Position::INVALID) {
1246 recheck.push_back(i);
1247 continue;
1248 }
1249 meNew = meNew + Position(0, 0, me.z());
1250 shape.push_back(meNew);
1251#ifdef DEBUG_MOVE2SIDE
1252 if (gDebugFlag1) {
1253 std::cout << " " << i << "e=" << shape.back() << "\n";
1254 }
1255#endif
1256 }
1257 // copy original z value
1258 shape.back().set(shape.back().x(), shape.back().y(), me.z());
1259 const double angle = localAngle(from, me, to);
1260 if (fabs(angle) > NUMERICAL_EPS) {
1261 const double length = from.distanceTo2D(me) + me.distanceTo2D(to);
1262 const double radius = length / angle;
1263#ifdef DEBUG_MOVE2SIDE
1264 if (gDebugFlag1) {
1265 std::cout << " i=" << i << " a=" << RAD2DEG(angle) << " l=" << length << " r=" << radius << " t=" << amount * 1.8 << "\n";
1266 }
1267#endif
1268 if ((radius < 0 && -radius < amount * 1.8) || fabs(RAD2DEG(angle)) > 170) {
1269 recheck.push_back(i);
1270 }
1271 }
1272 }
1273 }
1274 if (!recheck.empty()) {
1275 // try to adjust positions to avoid clipping
1276 shape = *this;
1277 for (int i = (int)recheck.size() - 1; i >= 0; i--) {
1278 shape.erase(shape.begin() + recheck[i]);
1279 }
1280 shape.move2side(amount, maxExtension);
1281 }
1282 *this = shape;
1283}
1284
1285
1286void
1287PositionVector::move2sideCustom(std::vector<double> amount, double maxExtension) {
1288 if (size() < 2) {
1289 return;
1290 }
1291 if (length2D() == 0) {
1292 return;
1293 }
1294 if (size() != amount.size()) {
1295 throw InvalidArgument("Numer of offsets (" + toString(amount.size())
1296 + ") does not match number of points (" + toString(size()) + ")");
1297 }
1298 PositionVector shape;
1299 for (int i = 0; i < static_cast<int>(size()); i++) {
1300 if (i == 0) {
1301 const Position& from = (*this)[i];
1302 const Position& to = (*this)[i + 1];
1303 if (from != to) {
1304 shape.push_back(from - sideOffset(from, to, amount[i]));
1305 }
1306 } else if (i == static_cast<int>(size()) - 1) {
1307 const Position& from = (*this)[i - 1];
1308 const Position& to = (*this)[i];
1309 if (from != to) {
1310 shape.push_back(to - sideOffset(from, to, amount[i]));
1311 }
1312 } else {
1313 const Position& from = (*this)[i - 1];
1314 const Position& me = (*this)[i];
1315 const Position& to = (*this)[i + 1];
1316 PositionVector fromMe(from, me);
1317 fromMe.extrapolate2D(me.distanceTo2D(to));
1318 const double extrapolateDev = fromMe[1].distanceTo2D(to);
1319 if (fabs(extrapolateDev) < POSITION_EPS) {
1320 // parallel case, just shift the middle point
1321 shape.push_back(me - sideOffset(from, to, amount[i]));
1322 } else if (fabs(extrapolateDev - 2 * me.distanceTo2D(to)) < POSITION_EPS) {
1323 // counterparallel case, just shift the middle point
1324 PositionVector fromMe2(from, me);
1325 fromMe2.extrapolate2D(amount[i]);
1326 shape.push_back(fromMe2[1]);
1327 } else {
1328 Position offsets = sideOffset(from, me, amount[i]);
1329 Position offsets2 = sideOffset(me, to, amount[i]);
1330 PositionVector l1(from - offsets, me - offsets);
1331 PositionVector l2(me - offsets2, to - offsets2);
1332 Position meNew = l1.intersectionPosition2D(l2[0], l2[1], maxExtension);
1333 if (meNew == Position::INVALID) {
1334 continue;
1335 }
1336 meNew = meNew + Position(0, 0, me.z());
1337 shape.push_back(meNew);
1338 }
1339 // copy original z value
1340 shape.back().set(shape.back().x(), shape.back().y(), me.z());
1341 }
1342 }
1343 *this = shape;
1344}
1345
1346double
1347PositionVector::localAngle(const Position& from, const Position& pos, const Position& to) {
1348 return GeomHelper::angleDiff(from.angleTo2D(pos), pos.angleTo2D(to));
1349}
1350
1351double
1353 if ((pos + 1) < (int)size()) {
1354 return (*this)[pos].angleTo2D((*this)[pos + 1]);
1355 }
1356 return INVALID_DOUBLE;
1357}
1358
1359
1360void
1362 if ((size() > 1) && (front() == back())) {
1363 pop_back();
1364 }
1365}
1366
1367
1368void
1370 if ((size() != 0) && ((*this)[0] != back())) {
1371 push_back((*this)[0]);
1372 }
1373}
1374
1375
1376std::vector<double>
1377PositionVector::distances(const PositionVector& s, bool perpendicular) const {
1378 std::vector<double> ret;
1379 const_iterator i;
1380 for (i = begin(); i != end(); i++) {
1381 const double dist = s.distance2D(*i, perpendicular);
1382 if (dist != GeomHelper::INVALID_OFFSET) {
1383 ret.push_back(dist);
1384 }
1385 }
1386 for (i = s.begin(); i != s.end(); i++) {
1387 const double dist = distance2D(*i, perpendicular);
1388 if (dist != GeomHelper::INVALID_OFFSET) {
1389 ret.push_back(dist);
1390 }
1391 }
1392 return ret;
1393}
1394
1395
1396double
1397PositionVector::distance2D(const Position& p, bool perpendicular) const {
1398 if (size() == 0) {
1399 return std::numeric_limits<double>::max();
1400 } else if (size() == 1) {
1401 return front().distanceTo2D(p);
1402 }
1403 const double nearestOffset = nearest_offset_to_point2D(p, perpendicular);
1404 if (nearestOffset == GeomHelper::INVALID_OFFSET) {
1406 } else {
1407 return p.distanceTo2D(positionAtOffset2D(nearestOffset));
1408 }
1409}
1410
1411
1412void
1414 if (empty()) {
1415 push_back(p);
1416 } else {
1417 insert(begin(), p);
1418 }
1419}
1420
1421
1422void
1424 if (empty()) {
1425 throw ProcessError("PositionVector is empty");
1426 } else {
1427 erase(begin());
1428 }
1429}
1430
1431
1432void
1434 if (size() == 0 || !p.almostSame(back())) {
1435 push_back(p);
1436 }
1437}
1438
1439
1440void
1442 if ((size() == 0) || !p.almostSame(front())) {
1443 push_front(p);
1444 }
1445}
1446
1447
1448void
1449PositionVector::insert_noDoublePos(const std::vector<Position>::iterator& at, const Position& p) {
1450 if (at == begin()) {
1452 } else if (at == end()) {
1454 } else {
1455 if (!p.almostSame(*at) && !p.almostSame(*(at - 1))) {
1456 insert(at, p);
1457 }
1458 }
1459}
1460
1461
1462bool
1464 return (size() >= 2) && ((*this)[0] == back());
1465}
1466
1467
1468bool
1470 // iterate over all positions and check if is NAN
1471 for (auto i = begin(); i != end(); i++) {
1472 if (i->isNAN()) {
1473 return true;
1474 }
1475 }
1476 // all ok, then return false
1477 return false;
1478}
1479
1480void
1482 const double limit = 2 * pow(10, -precision);
1483 if (length2D() < limit) {
1484 extrapolate2D(limit);
1485 }
1486}
1487
1488void
1489PositionVector::round(int precision, bool avoidDegeneration) {
1490 if (avoidDegeneration && size() > 1) {
1491 ensureMinLength(precision);
1492 }
1493 for (int i = 0; i < (int)size(); i++) {
1494 (*this)[i].round(precision);
1495 }
1496}
1497
1498
1499void
1500PositionVector::removeDoublePoints(double minDist, bool assertLength, int beginOffset, int endOffset, bool resample) {
1501 int curSize = (int)size() - beginOffset - endOffset;
1502 if (curSize > 1) {
1503 iterator last = begin() + beginOffset;
1504 for (iterator i = last + 1; i != (end() - endOffset) && (!assertLength || curSize > 2);) {
1505 if (last->almostSame(*i, minDist)) {
1506 if (i + 1 == end() - endOffset) {
1507 // special case: keep the last point and remove the next-to-last
1508 if (resample && last > begin() && (last - 1)->distanceTo(*i) >= 2 * minDist) {
1509 // resample rather than remove point after a long segment
1510 const double shiftBack = minDist - last->distanceTo(*i);
1511 //if (gDebugFlag1) std::cout << " resample endOffset beforeLast=" << *(last - 1) << " last=" << *last << " i=" << *i;
1512 (*last) = positionAtOffset(*(last - 1), *last, (last - 1)->distanceTo(*last) - shiftBack);
1513 //if (gDebugFlag1) std::cout << " lastNew=" << *last;
1514 last = i;
1515 ++i;
1516 } else {
1517 erase(last);
1518 i = end() - endOffset;
1519 }
1520 } else {
1521 if (resample && i + 1 != end() && last->distanceTo(*(i + 1)) >= 2 * minDist) {
1522 // resample rather than remove points before a long segment
1523 const double shiftForward = minDist - last->distanceTo(*i);
1524 //if (gDebugFlag1) std::cout << " resample last=" << *last << " i=" << *i << " next=" << *(i + 1);
1525 (*i) = positionAtOffset(*i, *(i + 1), shiftForward);
1526 //if (gDebugFlag1) std::cout << " iNew=" << *i << "\n";
1527 last = i;
1528 ++i;
1529 } else {
1530 i = erase(i);
1531 }
1532 }
1533 curSize--;
1534 } else {
1535 last = i;
1536 ++i;
1537 }
1538 }
1539 }
1540}
1541
1542
1543bool
1545 return static_cast<vp>(*this) == static_cast<vp>(v2);
1546}
1547
1548
1549bool
1551 return static_cast<vp>(*this) != static_cast<vp>(v2);
1552}
1553
1556 if (length() != v2.length()) {
1557 WRITE_ERROR(TL("Trying to subtract PositionVectors of different lengths."));
1558 }
1559 PositionVector pv;
1560 auto i1 = begin();
1561 auto i2 = v2.begin();
1562 while (i1 != end()) {
1563 pv.add(*i1 - *i2);
1564 }
1565 return pv;
1566}
1567
1570 if (length() != v2.length()) {
1571 WRITE_ERROR(TL("Trying to add PositionVectors of different lengths."));
1572 }
1573 PositionVector pv;
1574 auto i1 = begin();
1575 auto i2 = v2.begin();
1576 while (i1 != end()) {
1577 pv.add(*i1 + *i2);
1578 }
1579 return pv;
1580}
1581
1582bool
1583PositionVector::almostSame(const PositionVector& v2, double maxDiv) const {
1584 if (size() != v2.size()) {
1585 return false;
1586 }
1587 auto i2 = v2.begin();
1588 for (auto i1 = begin(); i1 != end(); i1++) {
1589 if (!i1->almostSame(*i2, maxDiv)) {
1590 return false;
1591 }
1592 i2++;
1593 }
1594 return true;
1595}
1596
1597bool
1599 if (size() < 2) {
1600 return false;
1601 }
1602 for (const_iterator i = begin(); i != end(); i++) {
1603 if ((*i).z() != 0) {
1604 return true;
1605 }
1606 }
1607 return false;
1608}
1609
1610
1611bool
1612PositionVector::intersects(const Position& p11, const Position& p12, const Position& p21, const Position& p22, const double withinDist, double* x, double* y, double* mu) {
1613 const double eps = std::numeric_limits<double>::epsilon();
1614 const double denominator = (p22.y() - p21.y()) * (p12.x() - p11.x()) - (p22.x() - p21.x()) * (p12.y() - p11.y());
1615 const double numera = (p22.x() - p21.x()) * (p11.y() - p21.y()) - (p22.y() - p21.y()) * (p11.x() - p21.x());
1616 const double numerb = (p12.x() - p11.x()) * (p11.y() - p21.y()) - (p12.y() - p11.y()) * (p11.x() - p21.x());
1617 /* Are the lines coincident? */
1618 if (fabs(numera) < eps && fabs(numerb) < eps && fabs(denominator) < eps) {
1619 double a1;
1620 double a2;
1621 double a3;
1622 double a4;
1623 double a = -1e12;
1624 if (p11.x() != p12.x()) {
1625 a1 = p11.x() < p12.x() ? p11.x() : p12.x();
1626 a2 = p11.x() < p12.x() ? p12.x() : p11.x();
1627 a3 = p21.x() < p22.x() ? p21.x() : p22.x();
1628 a4 = p21.x() < p22.x() ? p22.x() : p21.x();
1629 } else {
1630 a1 = p11.y() < p12.y() ? p11.y() : p12.y();
1631 a2 = p11.y() < p12.y() ? p12.y() : p11.y();
1632 a3 = p21.y() < p22.y() ? p21.y() : p22.y();
1633 a4 = p21.y() < p22.y() ? p22.y() : p21.y();
1634 }
1635 if (a1 <= a3 && a3 <= a2) {
1636 if (a4 < a2) {
1637 a = (a3 + a4) / 2;
1638 } else {
1639 a = (a2 + a3) / 2;
1640 }
1641 }
1642 if (a3 <= a1 && a1 <= a4) {
1643 if (a2 < a4) {
1644 a = (a1 + a2) / 2;
1645 } else {
1646 a = (a1 + a4) / 2;
1647 }
1648 }
1649 if (a != -1e12) {
1650 if (x != nullptr) {
1651 if (p11.x() != p12.x()) {
1652 *mu = (a - p11.x()) / (p12.x() - p11.x());
1653 *x = a;
1654 *y = p11.y() + (*mu) * (p12.y() - p11.y());
1655 } else {
1656 *x = p11.x();
1657 *y = a;
1658 if (p12.y() == p11.y()) {
1659 *mu = 0;
1660 } else {
1661 *mu = (a - p11.y()) / (p12.y() - p11.y());
1662 }
1663 }
1664 }
1665 return true;
1666 }
1667 return false;
1668 }
1669 /* Are the lines parallel */
1670 if (fabs(denominator) < eps) {
1671 return false;
1672 }
1673 /* Is the intersection along the segments */
1674 double mua = numera / denominator;
1675 /* reduce rounding errors for lines ending in the same point */
1676 if (fabs(p12.x() - p22.x()) < eps && fabs(p12.y() - p22.y()) < eps) {
1677 mua = 1.;
1678 } else {
1679 const double offseta = withinDist / p11.distanceTo2D(p12);
1680 const double offsetb = withinDist / p21.distanceTo2D(p22);
1681 const double mub = numerb / denominator;
1682 if (mua < -offseta || mua > 1 + offseta || mub < -offsetb || mub > 1 + offsetb) {
1683 return false;
1684 }
1685 }
1686 if (x != nullptr) {
1687 *x = p11.x() + mua * (p12.x() - p11.x());
1688 *y = p11.y() + mua * (p12.y() - p11.y());
1689 *mu = mua;
1690 }
1691 return true;
1692}
1693
1694
1695void
1697 const double s = sin(angle);
1698 const double c = cos(angle);
1699 for (int i = 0; i < (int)size(); i++) {
1700 const double x = (*this)[i].x();
1701 const double y = (*this)[i].y();
1702 const double z = (*this)[i].z();
1703 const double xnew = x * c - y * s;
1704 const double ynew = x * s + y * c;
1705 (*this)[i].set(xnew, ynew, z);
1706 }
1707}
1708
1709
1710void
1711PositionVector::rotate2D(const Position& pos, double angle) {
1712 PositionVector aux = *this;
1713 aux.sub(pos);
1714 aux.rotate2D(angle);
1715 aux.add(pos);
1716 *this = aux;
1717}
1718
1719
1720void
1722 if (size() > 1) {
1723 // translate position vector to (0,0), rotate, and traslate back again
1724 const Position offset = front();
1725 sub(offset);
1726 rotate2D(angle);
1727 add(offset);
1728 }
1729}
1730
1731
1734 PositionVector result = *this;
1735 bool changed = true;
1736 while (changed && result.size() > 3) {
1737 changed = false;
1738 for (int i = 0; i < (int)result.size(); i++) {
1739 const Position& p1 = result[i];
1740 const Position& p2 = result[(i + 2) % result.size()];
1741 const int middleIndex = (i + 1) % result.size();
1742 const Position& p0 = result[middleIndex];
1743 // https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_two_points
1744 const double triangleArea2 = fabs((p2.y() - p1.y()) * p0.x() - (p2.x() - p1.x()) * p0.y() + p2.x() * p1.y() - p2.y() * p1.x());
1745 const double distIK = p1.distanceTo2D(p2);
1746 if (distIK > NUMERICAL_EPS && triangleArea2 / distIK < NUMERICAL_EPS) {
1747 changed = true;
1748 result.erase(result.begin() + middleIndex);
1749 break;
1750 }
1751 }
1752 }
1753 return result;
1754}
1755
1756
1757const PositionVector
1758PositionVector::simplified2(const bool closed, const double eps) const {
1759 // this is a variation of the https://en.wikipedia.org/wiki/Visvalingam%E2%80%93Whyatt_algorithm
1760 // which uses the distance instead of the area
1761 // the benefits over the initial implementation are:
1762 // 3D support, no degenerate results for a sequence of small distances, keeping the longest part of a line
1763 // drawbacks: complexity of the code, speed
1764 if (size() < 3) {
1765 return *this;
1766 }
1767 auto calcScore = [&](const PositionVector & pv, int index) {
1768 if (!closed && (index == 0 || index == (int)pv.size() - 1)) {
1769 return eps + 1.;
1770 }
1771 const Position& p = pv[index];
1772 const Position& a = pv[(index + (int)pv.size() - 1) % pv.size()];
1773 const Position& b = pv[(index + 1) % pv.size()];
1774 const double distAB = a.distanceTo(b);
1775 if (distAB < MIN2(eps, NUMERICAL_EPS)) {
1776 // avoid division by 0 and degenerate cases due to very small baseline
1777 return (a.distanceTo(p) + b.distanceTo(p)) / 2.;
1778 }
1779 // https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Vector_formulation
1780 // calculating the distance of p to the line defined by a and b
1781 const Position dir = (b - a) / distAB;
1782 const double projectedLength = (a - p).dotProduct(dir);
1783 if (projectedLength <= -distAB) {
1784 return b.distanceTo(p);
1785 }
1786 if (projectedLength >= 0.) {
1787 return a.distanceTo(p);
1788 }
1789 const Position distVector = (a - p) - dir * projectedLength;
1790 return distVector.length();
1791 };
1792 std::vector<double> scores;
1793 double minScore = eps + 1.;
1794 int minIndex = -1;
1795 for (int i = 0; i < (int)size(); i++) {
1796 scores.push_back(calcScore(*this, i));
1797 if (scores.back() < minScore) {
1798 minScore = scores.back();
1799 minIndex = i;
1800 }
1801 }
1802 if (minScore >= eps) {
1803 return *this;
1804 }
1805 PositionVector result(*this);
1806 while (minScore < eps) {
1807 result.erase(result.begin() + minIndex);
1808 if (result.size() < 3) {
1809 break;
1810 }
1811 scores.erase(scores.begin() + minIndex);
1812 const int prevIndex = (minIndex + (int)result.size() - 1) % result.size();
1813 scores[prevIndex] = calcScore(result, prevIndex);
1814 scores[minIndex % result.size()] = calcScore(result, minIndex % result.size());
1815 minScore = eps + 1.;
1816 for (int i = 0; i < (int)result.size(); i++) {
1817 if (scores[i] < minScore) {
1818 minScore = scores[i];
1819 minIndex = i;
1820 }
1821 }
1822 }
1823 return result;
1824}
1825
1826
1828PositionVector::getOrthogonal(const Position& p, double extend, bool before, double length, double deg) const {
1829 PositionVector result;
1830 PositionVector tmp = *this;
1831 tmp.extrapolate2D(extend);
1832 const double baseOffset = tmp.nearest_offset_to_point2D(p);
1833 if (baseOffset == GeomHelper::INVALID_OFFSET || size() < 2) {
1834 // fail
1835 return result;
1836 }
1837 Position base = tmp.positionAtOffset2D(baseOffset);
1838 const int closestIndex = tmp.indexOfClosest(base);
1839 const double closestOffset = tmp.offsetAtIndex2D(closestIndex);
1840 result.push_back(base);
1841 if (fabs(baseOffset - closestOffset) > NUMERICAL_EPS) {
1842 result.push_back(tmp[closestIndex]);
1843 if ((closestOffset < baseOffset) != before) {
1844 deg *= -1;
1845 }
1846 } else if (before) {
1847 // take the segment before closestIndex if possible
1848 if (closestIndex > 0) {
1849 result.push_back(tmp[closestIndex - 1]);
1850 } else {
1851 result.push_back(tmp[1]);
1852 deg *= -1;
1853 }
1854 } else {
1855 // take the segment after closestIndex if possible
1856 if (closestIndex < (int)size() - 1) {
1857 result.push_back(tmp[closestIndex + 1]);
1858 } else {
1859 result.push_back(tmp[-1]);
1860 deg *= -1;
1861 }
1862 }
1863 result = result.getSubpart2D(0, length);
1864 // rotate around base
1865 result.add(base * -1);
1866 result.rotate2D(DEG2RAD(deg));
1867 result.add(base);
1868 return result;
1869}
1870
1871
1874 PositionVector result = *this;
1875 if (size() == 0) {
1876 return result;
1877 }
1878 const double z0 = (*this)[0].z();
1879 // the z-delta of the first segment
1880 const double dz = (*this)[1].z() - z0;
1881 // if the shape only has 2 points it is as smooth as possible already
1882 if (size() > 2 && dz != 0) {
1883 dist = MIN2(dist, length2D());
1884 // check wether we need to insert a new point at dist
1885 Position pDist = positionAtOffset2D(dist);
1886 int iLast = indexOfClosest(pDist);
1887 // prevent close spacing to reduce impact of rounding errors in z-axis
1888 if (pDist.distanceTo2D((*this)[iLast]) > POSITION_EPS * 20) {
1889 iLast = result.insertAtClosest(pDist, false);
1890 }
1891 double dist2 = result.offsetAtIndex2D(iLast);
1892 const double dz2 = result[iLast].z() - z0;
1893 double seen = 0;
1894 for (int i = 1; i < iLast; ++i) {
1895 seen += result[i].distanceTo2D(result[i - 1]);
1896 result[i].set(result[i].x(), result[i].y(), z0 + dz2 * seen / dist2);
1897 }
1898 }
1899 return result;
1900
1901}
1902
1903
1905PositionVector::interpolateZ(double zStart, double zEnd) const {
1906 PositionVector result = *this;
1907 if (size() == 0) {
1908 return result;
1909 }
1910 result[0].setz(zStart);
1911 result[-1].setz(zEnd);
1912 const double dz = zEnd - zStart;
1913 const double length = length2D();
1914 double seen = 0;
1915 for (int i = 1; i < (int)size() - 1; ++i) {
1916 seen += result[i].distanceTo2D(result[i - 1]);
1917 result[i].setz(zStart + dz * seen / length);
1918 }
1919 return result;
1920}
1921
1922
1924PositionVector::resample(double maxLength, const bool adjustEnd) const {
1925 PositionVector result;
1926 if (maxLength == 0) {
1927 return result;
1928 }
1929 const double length = length2D();
1930 if (length < POSITION_EPS) {
1931 return result;
1932 }
1933 maxLength = length / ceil(length / maxLength);
1934 for (double pos = 0; pos <= length; pos += maxLength) {
1935 result.push_back(positionAtOffset2D(pos));
1936 }
1937 // check if we have to adjust last element
1938 if (adjustEnd && !result.empty() && (result.back() != back())) {
1939 // add last element
1940 result.push_back(back());
1941 }
1942 return result;
1943}
1944
1945
1946double
1948 if (index < 0 || index >= (int)size()) {
1950 }
1951 double seen = 0;
1952 for (int i = 1; i <= index; ++i) {
1953 seen += (*this)[i].distanceTo2D((*this)[i - 1]);
1954 }
1955 return seen;
1956}
1957
1958
1959double
1960PositionVector::getMaxGrade(double& maxJump) const {
1961 double result = 0;
1962 for (int i = 1; i < (int)size(); ++i) {
1963 const Position& p1 = (*this)[i - 1];
1964 const Position& p2 = (*this)[i];
1965 const double distZ = fabs(p1.z() - p2.z());
1966 const double dist2D = p1.distanceTo2D(p2);
1967 if (dist2D == 0) {
1968 maxJump = MAX2(maxJump, distZ);
1969 } else {
1970 result = MAX2(result, distZ / dist2D);
1971 }
1972 }
1973 return result;
1974}
1975
1976
1977double
1979 double minZ = std::numeric_limits<double>::max();
1980 for (const Position& i : *this) {
1981 minZ = MIN2(minZ, i.z());
1982 }
1983 return minZ;
1984}
1985
1986
1989 // inspired by David F. Rogers
1990 assert(size() < 33);
1991 static const double fac[33] = {
1992 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,
1993 6227020800.0, 87178291200.0, 1307674368000.0, 20922789888000.0, 355687428096000.0, 6402373705728000.0,
1994 121645100408832000.0, 2432902008176640000.0, 51090942171709440000.0, 1124000727777607680000.0,
1995 25852016738884976640000.0, 620448401733239439360000.0, 15511210043330985984000000.0,
1996 403291461126605635584000000.0, 10888869450418352160768000000.0, 304888344611713860501504000000.0,
1997 8841761993739701954543616000000.0, 265252859812191058636308480000000.0,
1998 8222838654177922817725562880000000.0, 263130836933693530167218012160000000.0
1999 };
2000 PositionVector ret;
2001 const int npts = (int)size();
2002 // calculate the points on the Bezier curve
2003 const double step = (double) 1.0 / (numPoints - 1);
2004 double t = 0.;
2005 Position prev;
2006 for (int i1 = 0; i1 < numPoints; i1++) {
2007 if ((1.0 - t) < 5e-6) {
2008 t = 1.0;
2009 }
2010 double x = 0., y = 0., z = 0.;
2011 for (int i = 0; i < npts; i++) {
2012 const double ti = (i == 0) ? 1.0 : pow(t, i);
2013 const double tni = (npts == i + 1) ? 1.0 : pow(1 - t, npts - i - 1);
2014 const double basis = fac[npts - 1] / (fac[i] * fac[npts - 1 - i]) * ti * tni;
2015 x += basis * at(i).x();
2016 y += basis * at(i).y();
2017 z += basis * at(i).z();
2018 }
2019 t += step;
2020 Position current(x, y, z);
2021 if (prev != current && !std::isnan(x) && !std::isnan(y) && !std::isnan(z)) {
2022 ret.push_back(current);
2023 }
2024 prev = current;
2025 }
2026 return ret;
2027}
2028
2029
2031 // The test is based on the computation of a signed area enclosed by the polygon.
2032 // If the polygon is in the upper (resp. the lower) half-plane and the area is
2033 // negatively (resp. positively) signed, then the polygon is CW oriented. In case
2034 // the polygon has points with both positive and negative y-coordinates, we translate
2035 // the polygon to apply the above simple area-based test.
2036 double area = 0.0;
2037 const double y_min = std::min_element(begin(), end(), [](Position p1, Position p2) {
2038 return p1.y() < p2.y();
2039 })->y();
2040 const double gap = y_min > 0.0 ? 0.0 : y_min;
2041 add(0., gap, 0.);
2042 const int last = (int)size() - 1;
2043 for (int i = 0; i < last; i++) {
2044 const Position& firstPoint = at(i);
2045 const Position& secondPoint = at(i + 1);
2046 area += (secondPoint.x() - firstPoint.x()) / (secondPoint.y() + firstPoint.y()) / 2.0;
2047 }
2048 area += (at(0).x() - at(last).x()) / (at(0).y() + at(last).y()) / 2.0;
2049 add(0., -gap, 0.);
2050 return area < 0.0;
2051}
2052
2053
2054/****************************************************************************/
#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:44
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:49
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