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