Eclipse SUMO - Simulation of Urban MObility
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>
33 #include <utils/common/StdDefs.h>
35 #include <utils/common/ToString.h>
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 
56 PositionVector::PositionVector(const std::vector<Position>& v) {
57  std::copy(v.begin(), v.end(), std::back_inserter(*this));
58 }
59 
60 
61 PositionVector::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 
75 bool
76 PositionVector::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 
109 bool
110 PositionVector::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 
132 double
133 PositionVector::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)) {
141  Position closest = poly.positionAtOffset2D(poly.nearest_offset_to_point2D(*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 
160 bool
161 PositionVector::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 
174 bool
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 
188 Position
189 PositionVector::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 
200 Position
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 
211 const Position&
212 PositionVector::operator[](int index) const {
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 
230 Position&
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 
249 Position
250 PositionVector::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 
274 Position
275 PositionVector::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 
295 Position
296 PositionVector::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 
316 double
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 
341 double
344 }
345 
346 
347 double
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 
369 Position
370 PositionVector::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 
392 Position
393 PositionVector::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 
404 Position
405 PositionVector::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 
424 Boundary
426  Boundary ret;
427  for (const Position& i : *this) {
428  ret.add(i);
429  }
430  return ret;
431 }
432 
433 
434 Position
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 
451 Position
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 
502 void
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 
511 void
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 
520 Position
522  if (size() == 1) {
523  return (*this)[0];
524  } else {
525  return positionAtOffset(double((length() / 2.)));
526  }
527 }
528 
529 
530 double
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 
543 double
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 
556 double
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 
578 bool
579 PositionVector::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 
592 bool
593 PositionVector::crosses(const Position& p1, const Position& p2) const {
594  return intersects(p1, p2);
595 }
596 
597 
598 std::pair<PositionVector, PositionVector>
599 PositionVector::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 
645 std::ostream&
646 operator<<(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 
657 void
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 
670 void
671 PositionVector::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 
678 void
680  add(-offset.x(), -offset.y(), -offset.z());
681 }
682 
683 
684 void
686  add(offset.x(), offset.y(), offset.z());
687 }
688 
689 
691 PositionVector::added(const Position& offset) const {
692  PositionVector pv;
693  for (auto i1 = begin(); i1 != end(); ++i1) {
694  pv.push_back(*i1 + offset);
695  }
696  return pv;
697 }
698 
699 
700 void
702  for (int i = 0; i < (int)size(); i++) {
703  (*this)[i].mul(1, -1);
704  }
705 }
706 
707 
709 
710 
711 int
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 
729 double
731  double angle = atan2(p.y(), p.x());
732  return angle < 0.0 ? angle : angle + 2.0 * M_PI;
733 }
734 
735 void
737  std::sort(begin(), end(), increasing_x_y_sorter());
738 }
739 
740 
742 
743 
744 int
746  if (p1.x() != p2.x()) {
747  return p1.x() < p2.x();
748  }
749  return p1.y() < p2.y();
750 }
751 
752 
753 double
754 PositionVector::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 
759 void
760 PositionVector::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 
769 void
770 PositionVector::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 
780 PositionVector::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 
820 PositionVector::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 
863 PositionVector::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 
881 double
883  if (size() == 0) {
884  return INVALID_DOUBLE;
885  }
886  return front().angleTo2D(back());
887 }
888 
889 
890 double
891 PositionVector::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 
926 double
927 PositionVector::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 
963 Position
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 =
981  GeomHelper::nearest_offset_on_line_to_point2D(*i, *(i + 1), p, true);
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 
1013 int
1014 PositionVector::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 
1033 int
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 
1063 int
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 
1082 std::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 
1096 std::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 
1116 void
1117 PositionVector::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 
1138 void
1139 PositionVector::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 
1170 Position
1171 PositionVector::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 
1177 void
1178 PositionVector::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 
1283 void
1284 PositionVector::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 
1343 double
1344 PositionVector::localAngle(const Position& from, const Position& pos, const Position& to) {
1345  return GeomHelper::angleDiff(from.angleTo2D(pos), pos.angleTo2D(to));
1346 }
1347 
1348 double
1350  if ((pos + 1) < (int)size()) {
1351  return (*this)[pos].angleTo2D((*this)[pos + 1]);
1352  }
1353  return INVALID_DOUBLE;
1354 }
1355 
1356 
1357 void
1359  if ((size() != 0) && ((*this)[0] != back())) {
1360  push_back((*this)[0]);
1361  }
1362 }
1363 
1364 
1365 std::vector<double>
1366 PositionVector::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 
1385 double
1386 PositionVector::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().distanceTo(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 
1401 void
1403  if (empty()) {
1404  push_back(p);
1405  } else {
1406  insert(begin(), p);
1407  }
1408 }
1409 
1410 
1411 void
1413  if (empty()) {
1414  throw ProcessError("PositionVector is empty");
1415  } else {
1416  erase(begin());
1417  }
1418 }
1419 
1420 
1421 void
1423  if (size() == 0 || !p.almostSame(back())) {
1424  push_back(p);
1425  }
1426 }
1427 
1428 
1429 void
1431  if ((size() == 0) || !p.almostSame(front())) {
1432  push_front(p);
1433  }
1434 }
1435 
1436 
1437 void
1438 PositionVector::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 
1451 bool
1453  return (size() >= 2) && ((*this)[0] == back());
1454 }
1455 
1456 
1457 bool
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 
1470 void
1471 PositionVector::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 
1514 bool
1516  return static_cast<vp>(*this) == static_cast<vp>(v2);
1517 }
1518 
1519 
1520 bool
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 
1553 bool
1554 PositionVector::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 
1568 bool
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 
1582 bool
1583 PositionVector::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 
1666 void
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 
1683  PositionVector result = *this;
1684  bool changed = true;
1685  while (changed && result.size() > 3) {
1686  changed = false;
1687  for (int i = 0; i < (int)result.size(); i++) {
1688  const Position& p1 = result[i];
1689  const Position& p2 = result[(i + 2) % result.size()];
1690  const int middleIndex = (i + 1) % result.size();
1691  const Position& p0 = result[middleIndex];
1692  // https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_two_points
1693  const double triangleArea2 = fabs((p2.y() - p1.y()) * p0.x() - (p2.x() - p1.x()) * p0.y() + p2.x() * p1.y() - p2.y() * p1.x());
1694  const double distIK = p1.distanceTo2D(p2);
1695  if (distIK > NUMERICAL_EPS && triangleArea2 / distIK < NUMERICAL_EPS) {
1696  changed = true;
1697  result.erase(result.begin() + middleIndex);
1698  break;
1699  }
1700  }
1701  }
1702  return result;
1703 }
1704 
1705 
1707 PositionVector::getOrthogonal(const Position& p, double extend, bool before, double length, double deg) const {
1708  PositionVector result;
1709  PositionVector tmp = *this;
1710  tmp.extrapolate2D(extend);
1711  const double baseOffset = tmp.nearest_offset_to_point2D(p);
1712  if (baseOffset == GeomHelper::INVALID_OFFSET || size() < 2) {
1713  // fail
1714  return result;
1715  }
1716  Position base = tmp.positionAtOffset2D(baseOffset);
1717  const int closestIndex = tmp.indexOfClosest(base);
1718  const double closestOffset = tmp.offsetAtIndex2D(closestIndex);
1719  result.push_back(base);
1720  if (fabs(baseOffset - closestOffset) > NUMERICAL_EPS) {
1721  result.push_back(tmp[closestIndex]);
1722  if ((closestOffset < baseOffset) != before) {
1723  deg *= -1;
1724  }
1725  } else if (before) {
1726  // take the segment before closestIndex if possible
1727  if (closestIndex > 0) {
1728  result.push_back(tmp[closestIndex - 1]);
1729  } else {
1730  result.push_back(tmp[1]);
1731  deg *= -1;
1732  }
1733  } else {
1734  // take the segment after closestIndex if possible
1735  if (closestIndex < (int)size() - 1) {
1736  result.push_back(tmp[closestIndex + 1]);
1737  } else {
1738  result.push_back(tmp[-1]);
1739  deg *= -1;
1740  }
1741  }
1742  result = result.getSubpart2D(0, length);
1743  // rotate around base
1744  result.add(base * -1);
1745  result.rotate2D(DEG2RAD(deg));
1746  result.add(base);
1747  return result;
1748 }
1749 
1750 
1753  PositionVector result = *this;
1754  if (size() == 0) {
1755  return result;
1756  }
1757  const double z0 = (*this)[0].z();
1758  // the z-delta of the first segment
1759  const double dz = (*this)[1].z() - z0;
1760  // if the shape only has 2 points it is as smooth as possible already
1761  if (size() > 2 && dz != 0) {
1762  dist = MIN2(dist, length2D());
1763  // check wether we need to insert a new point at dist
1764  Position pDist = positionAtOffset2D(dist);
1765  int iLast = indexOfClosest(pDist);
1766  // prevent close spacing to reduce impact of rounding errors in z-axis
1767  if (pDist.distanceTo2D((*this)[iLast]) > POSITION_EPS * 20) {
1768  iLast = result.insertAtClosest(pDist, false);
1769  }
1770  double dist2 = result.offsetAtIndex2D(iLast);
1771  const double dz2 = result[iLast].z() - z0;
1772  double seen = 0;
1773  for (int i = 1; i < iLast; ++i) {
1774  seen += result[i].distanceTo2D(result[i - 1]);
1775  result[i].set(result[i].x(), result[i].y(), z0 + dz2 * seen / dist2);
1776  }
1777  }
1778  return result;
1779 
1780 }
1781 
1782 
1784 PositionVector::interpolateZ(double zStart, double zEnd) const {
1785  PositionVector result = *this;
1786  if (size() == 0) {
1787  return result;
1788  }
1789  result[0].setz(zStart);
1790  result[-1].setz(zEnd);
1791  const double dz = zEnd - zStart;
1792  const double length = length2D();
1793  double seen = 0;
1794  for (int i = 1; i < (int)size() - 1; ++i) {
1795  seen += result[i].distanceTo2D(result[i - 1]);
1796  result[i].setz(zStart + dz * seen / length);
1797  }
1798  return result;
1799 }
1800 
1801 
1803 PositionVector::resample(double maxLength, const bool adjustEnd) const {
1804  PositionVector result;
1805  if (maxLength == 0) {
1806  return result;
1807  }
1808  const double length = length2D();
1809  if (length < POSITION_EPS) {
1810  return result;
1811  }
1812  maxLength = length / ceil(length / maxLength);
1813  for (double pos = 0; pos <= length; pos += maxLength) {
1814  result.push_back(positionAtOffset2D(pos));
1815  }
1816  // check if we have to adjust last element
1817  if (adjustEnd && !result.empty() && (result.back() != back())) {
1818  // add last element
1819  result.push_back(back());
1820  }
1821  return result;
1822 }
1823 
1824 
1825 double
1827  if (index < 0 || index >= (int)size()) {
1829  }
1830  double seen = 0;
1831  for (int i = 1; i <= index; ++i) {
1832  seen += (*this)[i].distanceTo2D((*this)[i - 1]);
1833  }
1834  return seen;
1835 }
1836 
1837 
1838 double
1839 PositionVector::getMaxGrade(double& maxJump) const {
1840  double result = 0;
1841  for (int i = 1; i < (int)size(); ++i) {
1842  const Position& p1 = (*this)[i - 1];
1843  const Position& p2 = (*this)[i];
1844  const double distZ = fabs(p1.z() - p2.z());
1845  const double dist2D = p1.distanceTo2D(p2);
1846  if (dist2D == 0) {
1847  maxJump = MAX2(maxJump, distZ);
1848  } else {
1849  result = MAX2(result, distZ / dist2D);
1850  }
1851  }
1852  return result;
1853 }
1854 
1855 
1857 PositionVector::bezier(int numPoints) {
1858  // inspired by David F. Rogers
1859  assert(size() < 33);
1860  static const double fac[33] = {
1861  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,
1862  6227020800.0, 87178291200.0, 1307674368000.0, 20922789888000.0, 355687428096000.0, 6402373705728000.0,
1863  121645100408832000.0, 2432902008176640000.0, 51090942171709440000.0, 1124000727777607680000.0,
1864  25852016738884976640000.0, 620448401733239439360000.0, 15511210043330985984000000.0,
1865  403291461126605635584000000.0, 10888869450418352160768000000.0, 304888344611713860501504000000.0,
1866  8841761993739701954543616000000.0, 265252859812191058636308480000000.0,
1867  8222838654177922817725562880000000.0, 263130836933693530167218012160000000.0
1868  };
1869  PositionVector ret;
1870  const int npts = (int)size();
1871  // calculate the points on the Bezier curve
1872  const double step = (double) 1.0 / (numPoints - 1);
1873  double t = 0.;
1874  Position prev;
1875  for (int i1 = 0; i1 < numPoints; i1++) {
1876  if ((1.0 - t) < 5e-6) {
1877  t = 1.0;
1878  }
1879  double x = 0., y = 0., z = 0.;
1880  for (int i = 0; i < npts; i++) {
1881  const double ti = (i == 0) ? 1.0 : pow(t, i);
1882  const double tni = (npts == i + 1) ? 1.0 : pow(1 - t, npts - i - 1);
1883  const double basis = fac[npts - 1] / (fac[i] * fac[npts - 1 - i]) * ti * tni;
1884  x += basis * at(i).x();
1885  y += basis * at(i).y();
1886  z += basis * at(i).z();
1887  }
1888  t += step;
1889  Position current(x, y, z);
1890  if (prev != current && !std::isnan(x) && !std::isnan(y) && !std::isnan(z)) {
1891  ret.push_back(current);
1892  }
1893  prev = current;
1894  }
1895  return ret;
1896 }
1897 
1898 
1900  // The test is based on the computation of a signed area enclosed by the polygon.
1901  // If the polygon is in the upper (resp. the lower) half-plane and the area is
1902  // negatively (resp. positively) signed, then the polygon is CW oriented. In case
1903  // the polygon has points with both positive and negative y-coordinates, we translate
1904  // the polygon to apply the above simple area-based test.
1905  double area = 0.0;
1906  const double y_min = std::min_element(begin(), end(), [](Position p1, Position p2) {
1907  return p1.y() < p2.y();
1908  })->y();
1909  const double gap = y_min > 0.0 ? 0.0 : y_min;
1910  add(0., gap, 0.);
1911  const int last = (int)size() - 1;
1912  for (int i = 0; i < last; i++) {
1913  const Position& firstPoint = at(i);
1914  const Position& secondPoint = at(i + 1);
1915  area += (secondPoint.x() - firstPoint.x()) / (secondPoint.y() + firstPoint.y()) / 2.0;
1916  }
1917  area += (at(0).x() - at(last).x()) / (at(0).y() + at(last).y()) / 2.0;
1918  add(0., -gap, 0.);
1919  return area < 0.0;
1920 }
1921 
1922 
1923 /****************************************************************************/
#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:35
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,...
Definition: GeomHelper.cpp:82
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)
Definition: GeomHelper.cpp:88
static double legacyDegree(const double angle, const bool positive=false)
Definition: GeomHelper.cpp:214
static double angleDiff(const double angle1, const double angle2)
Returns the difference of the second angle to the first angle in radiants.
Definition: GeomHelper.cpp:178
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:37
double distanceSquaredTo2D(const Position &p2) const
returns the square of the distance to another position (Only using x and y positions)
Definition: Position.h:276
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:286
double dotProduct(const Position &pos) const
returns the dot product (scalar product) between this point and the second one
Definition: Position.h:299
static const Position INVALID
used to indicate that a position is valid
Definition: Position.h:317
double distanceTo2D(const Position &p2) const
returns the euclidean distance in the x-y-plane
Definition: Position.h:271
double distanceTo(const Position &p2) const
returns the euclidean distance in 3 dimension
Definition: Position.h:261
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:281
bool almostSame(const Position &p2, double maxDiv=POSITION_EPS) const
check if two position is almost the sme as other
Definition: Position.h:256
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 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)
int insertAtClosest(const Position &p, bool interpolateZ)
inserts p between the two closest positions
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