SUMO - Simulation of Urban MObility
PositionVector.cpp
Go to the documentation of this file.
1 /****************************************************************************/
10 // A list of positions
11 /****************************************************************************/
12 // SUMO, Simulation of Urban MObility; see http://sumo.dlr.de/
13 // Copyright (C) 2001-2017 DLR (http://www.dlr.de/) and contributors
14 /****************************************************************************/
15 //
16 // This file is part of SUMO.
17 // SUMO is free software: you can redistribute it and/or modify
18 // it under the terms of the GNU General Public License as published by
19 // the Free Software Foundation, either version 3 of the License, or
20 // (at your option) any later version.
21 //
22 /****************************************************************************/
23 
24 
25 // ===========================================================================
26 // included modules
27 // ===========================================================================
28 #ifdef _MSC_VER
29 #include <windows_config.h>
30 #else
31 #include <config.h>
32 #endif
33 
34 #include <queue>
35 #include <cmath>
36 #include <iostream>
37 #include <algorithm>
38 #include <cassert>
39 #include <iterator>
40 #include <limits>
41 #include <utils/common/StdDefs.h>
43 #include <utils/common/ToString.h>
44 #include "AbstractPoly.h"
45 #include "Position.h"
46 #include "PositionVector.h"
47 #include "GeomHelper.h"
48 #include "Helper_ConvexHull.h"
49 #include "Boundary.h"
50 
51 #ifdef CHECK_MEMORY_LEAKS
52 #include <foreign/nvwa/debug_new.h>
53 #endif // CHECK_MEMORY_LEAKS
54 
55 // ===========================================================================
56 // method definitions
57 // ===========================================================================
58 
60 
61 
62 PositionVector::PositionVector(const std::vector<Position>& v) {
63  std::copy(v.begin(), v.end(), std::back_inserter(*this));
64 }
65 
66 
67 PositionVector::PositionVector(const std::vector<Position>::const_iterator beg, const std::vector<Position>::const_iterator end) {
68  std::copy(beg, end, std::back_inserter(*this));
69 }
70 
71 
73  push_back(p1);
74  push_back(p2);
75 }
76 
77 
79 
80 
81 bool
82 PositionVector::around(const Position& p, SUMOReal offset) const {
83  if (offset != 0) {
84  PositionVector tmp(*this);
85  tmp.scaleAbsolute(offset);
86  return tmp.around(p);
87  }
88  SUMOReal angle = 0;
89  for (const_iterator i = begin(); i != end() - 1; i++) {
90  Position p1(
91  (*i).x() - p.x(),
92  (*i).y() - p.y());
93  Position p2(
94  (*(i + 1)).x() - p.x(),
95  (*(i + 1)).y() - p.y());
96  angle += GeomHelper::angle2D(p1, p2);
97  }
98  Position p1(
99  (*(end() - 1)).x() - p.x(),
100  (*(end() - 1)).y() - p.y());
101  Position p2(
102  (*(begin())).x() - p.x(),
103  (*(begin())).y() - p.y());
104  angle += GeomHelper::angle2D(p1, p2);
105  return (!(fabs(angle) < M_PI));
106 }
107 
108 
109 bool
111  for (const_iterator i = begin(); i != end() - 1; i++) {
112  if (poly.around(*i, offset)) {
113  return true;
114  }
115  }
116  return false;
117 }
118 
119 
120 SUMOReal
122  SUMOReal result = 0;
123  // this points within poly
124  for (const_iterator i = begin(); i != end() - 1; i++) {
125  if (poly.around(*i)) {
126  Position closest = poly.positionAtOffset2D(poly.nearest_offset_to_point2D(*i));
127  if (fabs(closest.z() - (*i).z()) < zThreshold) {
128  result = MAX2(result, poly.distance2D(*i));
129  }
130  }
131  }
132  // polys points within this
133  for (const_iterator i = poly.begin(); i != poly.end() - 1; i++) {
134  if (around(*i)) {
136  if (fabs(closest.z() - (*i).z()) < zThreshold) {
137  result = MAX2(result, distance2D(*i));
138  }
139  }
140  }
141  return result;
142 }
143 
144 
145 bool
146 PositionVector::intersects(const Position& p1, const Position& p2) const {
147  if (size() < 2) {
148  return false;
149  }
150  for (const_iterator i = begin(); i != end() - 1; i++) {
151  if (intersects(*i, *(i + 1), p1, p2)) {
152  return true;
153  }
154  }
155  return false;
156 }
157 
158 
159 bool
161  if (size() < 2) {
162  return false;
163  }
164  for (const_iterator i = begin(); i != end() - 1; i++) {
165  if (v1.intersects(*i, *(i + 1))) {
166  return true;
167  }
168  }
169  return false;
170 }
171 
172 
173 Position
174 PositionVector::intersectionPosition2D(const Position& p1, const Position& p2, const SUMOReal withinDist) const {
175  for (const_iterator i = begin(); i != end() - 1; i++) {
176  SUMOReal x, y, m;
177  if (intersects(*i, *(i + 1), p1, p2, withinDist, &x, &y, &m)) {
178  return Position(x, y);
179  }
180  }
181  return Position::INVALID;
182 }
183 
184 
185 Position
187  for (const_iterator i = begin(); i != end() - 1; i++) {
188  if (v1.intersects(*i, *(i + 1))) {
189  return v1.intersectionPosition2D(*i, *(i + 1));
190  }
191  }
192  return Position::INVALID;
193 }
194 
195 
196 const Position&
197 PositionVector::operator[](int index) const {
198  if (index >= 0) {
199  return at(index);
200  } else {
201  return at((int)size() + index);
202  }
203 }
204 
205 
206 Position&
208  if (index >= 0) {
209  return at(index);
210  } else {
211  return at((int)size() + index);
212  }
213 }
214 
215 
216 Position
218  assert(size() != 0);
219  const_iterator i = begin();
220  SUMOReal seenLength = 0;
221  do {
222  const SUMOReal nextLength = (*i).distanceTo(*(i + 1));
223  if (seenLength + nextLength > pos) {
224  return positionAtOffset(*i, *(i + 1), pos - seenLength, lateralOffset);
225  }
226  seenLength += nextLength;
227  } while (++i != end() - 1);
228  if (lateralOffset == 0 || size() < 2) {
229  return back();
230  } else {
231  return positionAtOffset(*(end() - 2), *(end() - 1), (*(end() - 2)).distanceTo(*(end() - 1)), lateralOffset);
232  }
233 }
234 
235 
236 Position
238  const_iterator i = begin();
239  SUMOReal seenLength = 0;
240  do {
241  const SUMOReal nextLength = (*i).distanceTo2D(*(i + 1));
242  if (seenLength + nextLength > pos) {
243  return positionAtOffset2D(*i, *(i + 1), pos - seenLength, lateralOffset);
244  }
245  seenLength += nextLength;
246  } while (++i != end() - 1);
247  return back();
248 }
249 
250 
251 SUMOReal
253  if (pos < 0) {
254  pos += length();
255  }
256  const_iterator i = begin();
257  SUMOReal seenLength = 0;
258  do {
259  const Position& p1 = *i;
260  const Position& p2 = *(i + 1);
261  const SUMOReal nextLength = p1.distanceTo(p2);
262  if (seenLength + nextLength > pos) {
263  return p1.angleTo2D(p2);
264  }
265  seenLength += nextLength;
266  } while (++i != end() - 1);
267  const Position& p1 = (*this)[-2];
268  const Position& p2 = back();
269  return p1.angleTo2D(p2);
270 }
271 
272 
273 SUMOReal
276 }
277 
278 
279 SUMOReal
281  const_iterator i = begin();
282  SUMOReal seenLength = 0;
283  do {
284  const Position& p1 = *i;
285  const Position& p2 = *(i + 1);
286  const SUMOReal nextLength = p1.distanceTo(p2);
287  if (seenLength + nextLength > pos) {
288  return RAD2DEG(atan2(p2.z() - p1.z(), p1.distanceTo2D(p2)));
289  }
290  seenLength += nextLength;
291  } while (++i != end() - 1);
292  const Position& p1 = (*this)[-2];
293  const Position& p2 = back();
294  return RAD2DEG(atan2(p2.z() - p1.z(), p1.distanceTo2D(p2)));
295 }
296 
297 
298 Position
299 PositionVector::positionAtOffset(const Position& p1, const Position& p2, SUMOReal pos, SUMOReal lateralOffset) {
300  const SUMOReal dist = p1.distanceTo(p2);
301  if (pos < 0 || dist < pos) {
302  return Position::INVALID;
303  }
304  if (lateralOffset != 0) {
305  const Position offset = sideOffset(p1, p2, -lateralOffset); // move in the same direction as Position::move2side
306  if (pos == 0.) {
307  return p1 + offset;
308  }
309  return p1 + (p2 - p1) * (pos / dist) + offset;
310  }
311  if (pos == 0.) {
312  return p1;
313  }
314  return p1 + (p2 - p1) * (pos / dist);
315 }
316 
317 
318 Position
319 PositionVector::positionAtOffset2D(const Position& p1, const Position& p2, SUMOReal pos, SUMOReal lateralOffset) {
320  const SUMOReal dist = p1.distanceTo2D(p2);
321  if (pos < 0 || dist < pos) {
322  return Position::INVALID;
323  }
324  if (lateralOffset != 0) {
325  const Position offset = sideOffset(p1, p2, -lateralOffset); // move in the same direction as Position::move2side
326  if (pos == 0.) {
327  return p1 + offset;
328  }
329  return p1 + (p2 - p1) * (pos / dist) + offset;
330  }
331  if (pos == 0.) {
332  return p1;
333  }
334  return p1 + (p2 - p1) * (pos / dist);
335 }
336 
337 
338 Boundary
340  Boundary ret;
341  for (const_iterator i = begin(); i != end(); i++) {
342  ret.add(*i);
343  }
344  return ret;
345 }
346 
347 
348 Position
350  SUMOReal x = 0;
351  SUMOReal y = 0;
352  SUMOReal z = 0;
353  for (const_iterator i = begin(); i != end(); i++) {
354  x += (*i).x();
355  y += (*i).y();
356  z += (*i).z();
357  }
358  return Position(x / (SUMOReal) size(), y / (SUMOReal) size(), z / (SUMOReal)size());
359 }
360 
361 
362 Position
364  PositionVector tmp = *this;
365  if (!isClosed()) { // make sure its closed
366  tmp.push_back(tmp[0]);
367  }
368  const int endIndex = (int)tmp.size() - 1;
369  SUMOReal div = 0; // 6 * area including sign
370  SUMOReal x = 0;
371  SUMOReal y = 0;
372  if (tmp.area() != 0) { // numerical instability ?
373  // http://en.wikipedia.org/wiki/Polygon
374  for (int i = 0; i < endIndex; i++) {
375  const SUMOReal z = tmp[i].x() * tmp[i + 1].y() - tmp[i + 1].x() * tmp[i].y();
376  div += z; // area formula
377  x += (tmp[i].x() + tmp[i + 1].x()) * z;
378  y += (tmp[i].y() + tmp[i + 1].y()) * z;
379  }
380  div *= 3; // 6 / 2, the 2 comes from the area formula
381  return Position(x / div, y / div);
382  } else {
383  // compute by decomposing into line segments
384  // http://en.wikipedia.org/wiki/Centroid#By_geometric_decomposition
385  SUMOReal lengthSum = 0;
386  for (int i = 0; i < endIndex; i++) {
387  SUMOReal length = tmp[i].distanceTo(tmp[i + 1]);
388  x += (tmp[i].x() + tmp[i + 1].x()) * length / 2;
389  y += (tmp[i].y() + tmp[i + 1].y()) * length / 2;
390  lengthSum += length;
391  }
392  if (lengthSum == 0) {
393  // it is probably only one point
394  return tmp[0];
395  }
396  return Position(x / lengthSum, y / lengthSum);
397  }
398 }
399 
400 
401 void
403  Position centroid = getCentroid();
404  for (int i = 0; i < static_cast<int>(size()); i++) {
405  (*this)[i] = centroid + (((*this)[i] - centroid) * factor);
406  }
407 }
408 
409 
410 void
412  Position centroid = getCentroid();
413  for (int i = 0; i < static_cast<int>(size()); i++) {
414  (*this)[i] = centroid + (((*this)[i] - centroid) + offset);
415  }
416 }
417 
418 
419 Position
421  if (size() == 1) {
422  return (*this)[0];
423  }
424  return positionAtOffset(SUMOReal((length() / 2.)));
425 }
426 
427 
428 SUMOReal
430  SUMOReal len = 0;
431  for (const_iterator i = begin(); i != end() - 1; i++) {
432  len += (*i).distanceTo(*(i + 1));
433  }
434  return len;
435 }
436 
437 
438 SUMOReal
440  SUMOReal len = 0;
441  for (const_iterator i = begin(); i != end() - 1; i++) {
442  len += (*i).distanceTo2D(*(i + 1));
443  }
444  return len;
445 }
446 
447 
448 SUMOReal
450  if (size() < 3) {
451  return 0;
452  }
453  SUMOReal area = 0;
454  PositionVector tmp = *this;
455  if (!isClosed()) { // make sure its closed
456  tmp.push_back(tmp[0]);
457  }
458  const int endIndex = (int)tmp.size() - 1;
459  // http://en.wikipedia.org/wiki/Polygon
460  for (int i = 0; i < endIndex; i++) {
461  area += tmp[i].x() * tmp[i + 1].y() - tmp[i + 1].x() * tmp[i].y();
462  }
463  if (area < 0) { // we whether we had cw or ccw order
464  area *= -1;
465  }
466  return area / 2;
467 }
468 
469 
470 bool
472  for (const_iterator i = begin(); i != end() - 1; i++) {
473  if (poly.around(*i, offset)) {
474  return true;
475  }
476  }
477  return false;
478 }
479 
480 
481 bool
482 PositionVector::crosses(const Position& p1, const Position& p2) const {
483  return intersects(p1, p2);
484 }
485 
486 
487 std::pair<PositionVector, PositionVector>
489  if (size() < 2) {
490  throw InvalidArgument("Vector to short for splitting");
491  }
492  if (where <= POSITION_EPS || where >= length() - POSITION_EPS) {
493  WRITE_WARNING("Splitting vector close to end (pos: " + toString(where) + ", length: " + toString(length()) + ")");
494  }
495  PositionVector first, second;
496  first.push_back((*this)[0]);
497  SUMOReal seen = 0;
498  const_iterator it = begin() + 1;
499  SUMOReal next = first.back().distanceTo(*it);
500  // see how many points we can add to first
501  while (where >= seen + next + POSITION_EPS) {
502  seen += next;
503  first.push_back(*it);
504  it++;
505  next = first.back().distanceTo(*it);
506  }
507  if (fabs(where - (seen + next)) > POSITION_EPS || it == end() - 1) {
508  // we need to insert a new point because 'where' is not close to an
509  // existing point or it is to close to the endpoint
510  const Position p = positionAtOffset(first.back(), *it, where - seen);
511  first.push_back(p);
512  second.push_back(p);
513  } else {
514  first.push_back(*it);
515  }
516  // add the remaining points to second
517  for (; it != end(); it++) {
518  second.push_back(*it);
519  }
520  assert(first.size() >= 2);
521  assert(second.size() >= 2);
522  assert(first.back() == second.front());
523  assert(fabs(first.length() + second.length() - length()) < 2 * POSITION_EPS);
524  return std::pair<PositionVector, PositionVector>(first, second);
525 }
526 
527 
528 std::ostream&
529 operator<<(std::ostream& os, const PositionVector& geom) {
530  for (PositionVector::const_iterator i = geom.begin(); i != geom.end(); i++) {
531  if (i != geom.begin()) {
532  os << " ";
533  }
534  os << (*i);
535  }
536  return os;
537 }
538 
539 
540 void
542  std::sort(begin(), end(), as_poly_cw_sorter());
543 }
544 
545 
546 void
548  for (int i = 0; i < static_cast<int>(size()); i++) {
549  (*this)[i].add(xoff, yoff, zoff);
550  }
551 }
552 
553 
554 void
556  add(offset.x(), offset.y(), offset.z());
557 }
558 
559 
560 void
562  for (int i = 0; i < static_cast<int>(size()); i++) {
563  (*this)[i].mul(1, -1);
564  }
565 }
566 
567 
569 
570 
571 int
573  return atan2(p1.x(), p1.y()) < atan2(p2.x(), p2.y());
574 }
575 
576 
577 void
579  std::sort(begin(), end(), increasing_x_y_sorter());
580 }
581 
582 
584 
585 
586 int
588  const Position& p2) const {
589  if (p1.x() != p2.x()) {
590  return p1.x() < p2.x();
591  }
592  return p1.y() < p2.y();
593 }
594 
595 
596 SUMOReal
597 PositionVector::isLeft(const Position& P0, const Position& P1, const Position& P2) const {
598  return (P1.x() - P0.x()) * (P2.y() - P0.y()) - (P2.x() - P0.x()) * (P1.y() - P0.y());
599 }
600 
601 
604  PositionVector ret = *this;
605  ret.sortAsPolyCWByAngle();
606  return simpleHull_2D(ret);
607 }
608 
609 
610 void
612  if (size() > 0 && v.size() > 0 && back().distanceTo(v[0]) < sameThreshold) {
613  copy(v.begin() + 1, v.end(), back_inserter(*this));
614  } else {
615  copy(v.begin(), v.end(), back_inserter(*this));
616  }
617 }
618 
619 
621 PositionVector::getSubpart(SUMOReal beginOffset, SUMOReal endOffset) const {
622  PositionVector ret;
623  Position begPos = front();
624  if (beginOffset > POSITION_EPS) {
625  begPos = positionAtOffset(beginOffset);
626  }
627  Position endPos = back();
628  if (endOffset < length() - POSITION_EPS) {
629  endPos = positionAtOffset(endOffset);
630  }
631  ret.push_back(begPos);
632 
633  SUMOReal seen = 0;
634  const_iterator i = begin();
635  // skip previous segments
636  while ((i + 1) != end()
637  &&
638  seen + (*i).distanceTo(*(i + 1)) < beginOffset) {
639  seen += (*i).distanceTo(*(i + 1));
640  i++;
641  }
642  // append segments in between
643  while ((i + 1) != end()
644  &&
645  seen + (*i).distanceTo(*(i + 1)) < endOffset) {
646 
647  ret.push_back_noDoublePos(*(i + 1));
648  seen += (*i).distanceTo(*(i + 1));
649  i++;
650  }
651  // append end
652  ret.push_back_noDoublePos(endPos);
653  if (ret.size() == 1) {
654  ret.push_back(endPos);
655  }
656  return ret;
657 }
658 
659 
661 PositionVector::getSubpart2D(SUMOReal beginOffset, SUMOReal endOffset) const {
662  PositionVector ret;
663  Position begPos = front();
664  if (beginOffset > POSITION_EPS) {
665  begPos = positionAtOffset2D(beginOffset);
666  }
667  Position endPos = back();
668  if (endOffset < length2D() - POSITION_EPS) {
669  endPos = positionAtOffset2D(endOffset);
670  }
671  ret.push_back(begPos);
672 
673  SUMOReal seen = 0;
674  const_iterator i = begin();
675  // skip previous segments
676  while ((i + 1) != end()
677  &&
678  seen + (*i).distanceTo2D(*(i + 1)) < beginOffset) {
679  seen += (*i).distanceTo2D(*(i + 1));
680  i++;
681  }
682  // append segments in between
683  while ((i + 1) != end()
684  &&
685  seen + (*i).distanceTo2D(*(i + 1)) < endOffset) {
686 
687  ret.push_back_noDoublePos(*(i + 1));
688  seen += (*i).distanceTo2D(*(i + 1));
689  i++;
690  }
691  // append end
692  ret.push_back_noDoublePos(endPos);
693  if (ret.size() == 1) {
694  ret.push_back(endPos);
695  }
696  return ret;
697 }
698 
699 
701 PositionVector::getSubpartByIndex(int beginIndex, int count) const {
702  if (beginIndex < 0) {
703  beginIndex += (int)size();
704  }
705  assert(count >= 0);
706  assert(beginIndex < (int)size());
707  assert(beginIndex + count <= (int)size());
708  PositionVector result;
709  for (int i = beginIndex; i < beginIndex + count; ++i) {
710  result.push_back((*this)[i]);
711  }
712  return result;
713 }
714 
715 
716 SUMOReal
718  return front().angleTo2D(back());
719 }
720 
721 
722 SUMOReal
723 PositionVector::nearest_offset_to_point2D(const Position& p, bool perpendicular) const {
726  SUMOReal seen = 0;
727  for (const_iterator i = begin(); i != end() - 1; i++) {
728  const SUMOReal pos =
729  GeomHelper::nearest_offset_on_line_to_point2D(*i, *(i + 1), p, perpendicular);
730  const SUMOReal dist = pos == GeomHelper::INVALID_OFFSET ? minDist : p.distanceTo2D(positionAtOffset2D(*i, *(i + 1), pos));
731  if (dist < minDist) {
732  nearestPos = pos + seen;
733  minDist = dist;
734  }
735  if (perpendicular && i != begin() && pos == GeomHelper::INVALID_OFFSET) {
736  // even if perpendicular is set we still need to check the distance to the inner points
737  const SUMOReal cornerDist = p.distanceTo2D(*i);
738  if (cornerDist < minDist) {
739  const SUMOReal pos1 =
740  GeomHelper::nearest_offset_on_line_to_point2D(*(i - 1), *i, p, false);
741  const SUMOReal pos2 =
742  GeomHelper::nearest_offset_on_line_to_point2D(*i, *(i + 1), p, false);
743  if (pos1 == (*(i - 1)).distanceTo2D(*i) && pos2 == 0.) {
744  nearestPos = seen;
745  minDist = cornerDist;
746  }
747  }
748  }
749  seen += (*i).distanceTo2D(*(i + 1));
750  }
751  return nearestPos;
752 }
753 
754 
755 Position
757  // @toDo this duplicates most of the code in nearest_offset_to_point2D. It should be refactored
758  if (extend) {
759  PositionVector extended = *this;
760  const SUMOReal dist = 2 * distance2D(p);
761  extended.extrapolate(dist);
762  return extended.transformToVectorCoordinates(p) - Position(dist, 0);
763  }
765  SUMOReal nearestPos = -1;
766  SUMOReal seen = 0;
767  int sign = 1;
768  for (const_iterator i = begin(); i != end() - 1; i++) {
769  const SUMOReal pos =
770  GeomHelper::nearest_offset_on_line_to_point2D(*i, *(i + 1), p, true);
771  const SUMOReal dist = pos < 0 ? minDist : p.distanceTo2D(positionAtOffset(*i, *(i + 1), pos));
772  if (dist < minDist) {
773  nearestPos = pos + seen;
774  minDist = dist;
775  sign = isLeft(*i, *(i + 1), p) >= 0 ? -1 : 1;
776  }
777  if (i != begin() && pos == GeomHelper::INVALID_OFFSET) {
778  // even if perpendicular is set we still need to check the distance to the inner points
779  const SUMOReal cornerDist = p.distanceTo2D(*i);
780  if (cornerDist < minDist) {
781  const SUMOReal pos1 =
782  GeomHelper::nearest_offset_on_line_to_point2D(*(i - 1), *i, p, false);
783  const SUMOReal pos2 =
784  GeomHelper::nearest_offset_on_line_to_point2D(*i, *(i + 1), p, false);
785  if (pos1 == (*(i - 1)).distanceTo2D(*i) && pos2 == 0.) {
786  nearestPos = seen;
787  minDist = cornerDist;
788  sign = isLeft(*(i - 1), *i, p) >= 0 ? -1 : 1;
789  }
790  }
791  }
792  seen += (*i).distanceTo2D(*(i + 1));
793  }
794  if (nearestPos != -1) {
795  return Position(nearestPos, sign * minDist);
796  } else {
797  return Position::INVALID;
798  }
799 }
800 
801 
802 int
804  assert(size() > 0);
806  SUMOReal dist;
807  int closest = 0;
808  for (int i = 0; i < (int)size(); i++) {
809  dist = p.distanceTo((*this)[i]);
810  if (dist < minDist) {
811  closest = i;
812  minDist = dist;
813  }
814  }
815  return closest;
816 }
817 
818 
819 int
822  int insertionIndex = 1;
823  for (int i = 0; i < (int)size() - 1; i++) {
824  const SUMOReal length = GeomHelper::nearest_offset_on_line_to_point2D((*this)[i], (*this)[i + 1], p, false);
825  const Position& outIntersection = PositionVector::positionAtOffset2D((*this)[i], (*this)[i + 1], length);
826  const SUMOReal dist = p.distanceTo2D(outIntersection);
827  if (dist < minDist) {
828  insertionIndex = i + 1;
829  minDist = dist;
830  }
831  }
832  insert(begin() + insertionIndex, p);
833  return insertionIndex;
834 }
835 
836 
837 int
839  if (size() == 0) {
840  return -1;
841  }
843  int removalIndex = 0;
844  for (int i = 0; i < (int)size(); i++) {
845  const SUMOReal dist = p.distanceTo2D((*this)[i]);
846  if (dist < minDist) {
847  removalIndex = i;
848  minDist = dist;
849  }
850  }
851  erase(begin() + removalIndex);
852  return removalIndex;
853 }
854 
855 
856 std::vector<SUMOReal>
858  std::vector<SUMOReal> ret;
859  for (const_iterator i = other.begin(); i != other.end() - 1; i++) {
860  std::vector<SUMOReal> atSegment = intersectsAtLengths2D(*i, *(i + 1));
861  copy(atSegment.begin(), atSegment.end(), back_inserter(ret));
862  }
863  return ret;
864 }
865 
866 
867 std::vector<SUMOReal>
869  std::vector<SUMOReal> ret;
870  SUMOReal pos = 0;
871  for (const_iterator i = begin(); i != end() - 1; i++) {
872  const Position& p1 = *i;
873  const Position& p2 = *(i + 1);
874  SUMOReal x, y, m;
875  if (intersects(p1, p2, lp1, lp2, 0., &x, &y, &m)) {
876  ret.push_back(Position(x, y).distanceTo2D(p1) + pos);
877  }
878  pos += p1.distanceTo2D(p2);
879  }
880  return ret;
881 }
882 
883 
884 void
885 PositionVector::extrapolate(const SUMOReal val, const bool onlyFirst) {
886  assert(size() > 1);
887  Position& p1 = (*this)[0];
888  Position& p2 = (*this)[1];
889  const Position offset = (p2 - p1) * (val / p1.distanceTo(p2));
890  p1.sub(offset);
891  if (!onlyFirst) {
892  if (size() == 2) {
893  p2.add(offset);
894  } else {
895  const Position& e1 = (*this)[-2];
896  Position& e2 = (*this)[-1];
897  e2.sub((e1 - e2) * (val / e1.distanceTo(e2)));
898  }
899  }
900 }
901 
902 
903 void
904 PositionVector::extrapolate2D(const SUMOReal val, const bool onlyFirst) {
905  assert(size() > 1);
906  Position& p1 = (*this)[0];
907  Position& p2 = (*this)[1];
908  const Position offset = (p2 - p1) * (val / p1.distanceTo2D(p2));
909  p1.sub(offset);
910  if (!onlyFirst) {
911  if (size() == 2) {
912  p2.add(offset);
913  } else {
914  const Position& e1 = (*this)[-2];
915  Position& e2 = (*this)[-1];
916  e2.sub((e1 - e2) * (val / e1.distanceTo2D(e2)));
917  }
918  }
919 }
920 
921 
924  PositionVector ret;
925  for (const_reverse_iterator i = rbegin(); i != rend(); i++) {
926  ret.push_back(*i);
927  }
928  return ret;
929 }
930 
931 
932 Position
933 PositionVector::sideOffset(const Position& beg, const Position& end, const SUMOReal amount) {
934  const SUMOReal scale = amount / beg.distanceTo2D(end);
935  return Position((beg.y() - end.y()) * scale, (end.x() - beg.x()) * scale);
936 }
937 
938 
939 void
941  if (size() < 2) {
942  return;
943  }
944  if (length2D() == 0) {
945  return;
946  }
947  PositionVector shape;
948  for (int i = 0; i < static_cast<int>(size()); i++) {
949  if (i == 0) {
950  const Position& from = (*this)[i];
951  const Position& to = (*this)[i + 1];
952  shape.push_back(from - sideOffset(from, to, amount));
953  } else if (i == static_cast<int>(size()) - 1) {
954  const Position& from = (*this)[i - 1];
955  const Position& to = (*this)[i];
956  shape.push_back(to - sideOffset(from, to, amount));
957  } else {
958  const Position& from = (*this)[i - 1];
959  const Position& me = (*this)[i];
960  const Position& to = (*this)[i + 1];
961  PositionVector fromMe(from, me);
962  fromMe.extrapolate2D(me.distanceTo2D(to));
963  const SUMOReal extrapolateDev = fromMe[1].distanceTo2D(to);
964  if (fabs(extrapolateDev) < POSITION_EPS) {
965  // parallel case, just shift the middle point
966  shape.push_back(me - sideOffset(from, to, amount));
967  } else if (fabs(extrapolateDev - 2 * me.distanceTo2D(to)) < POSITION_EPS) {
968  // counterparallel case, just shift the middle point
969  PositionVector fromMe(from, me);
970  fromMe.extrapolate2D(amount);
971  shape.push_back(fromMe[1]);
972  } else {
973  Position offsets = sideOffset(from, me, amount);
974  Position offsets2 = sideOffset(me, to, amount);
975  PositionVector l1(from - offsets, me - offsets);
976  PositionVector l2(me - offsets2, to - offsets2);
977  Position meNew = l1.intersectionPosition2D(l2[0], l2[1], 100);
978  if (meNew == Position::INVALID) {
979  throw InvalidArgument("no line intersection");
980  }
981  meNew = meNew + Position(0, 0, me.z());
982  shape.push_back(meNew);
983  }
984  // copy original z value
985  shape.back().set(shape.back().x(), shape.back().y(), me.z());
986  }
987  }
988  *this = shape;
989 }
990 
991 
992 SUMOReal
994  assert((int)size() > pos + 1);
995  return (*this)[pos].angleTo2D((*this)[pos + 1]);
996 }
997 
998 
999 void
1001  if (size() == 0 || (*this)[0] == back()) {
1002  return;
1003  }
1004  push_back((*this)[0]);
1005 }
1006 
1007 
1008 std::vector<SUMOReal>
1009 PositionVector::distances(const PositionVector& s, bool perpendicular) const {
1010  std::vector<SUMOReal> ret;
1011  const_iterator i;
1012  for (i = begin(); i != end(); i++) {
1013  const SUMOReal dist = s.distance2D(*i, perpendicular);
1014  if (dist != GeomHelper::INVALID_OFFSET) {
1015  ret.push_back(dist);
1016  }
1017  }
1018  for (i = s.begin(); i != s.end(); i++) {
1019  const SUMOReal dist = distance2D(*i, perpendicular);
1020  if (dist != GeomHelper::INVALID_OFFSET) {
1021  ret.push_back(dist);
1022  }
1023  }
1024  return ret;
1025 }
1026 
1027 
1028 SUMOReal
1029 PositionVector::distance2D(const Position& p, bool perpendicular) const {
1030  if (size() == 0) {
1032  } else if (size() == 1) {
1033  return front().distanceTo(p);
1034  }
1035  const SUMOReal nearestOffset = nearest_offset_to_point2D(p, perpendicular);
1036  if (nearestOffset == GeomHelper::INVALID_OFFSET) {
1038  } else {
1039  return p.distanceTo2D(positionAtOffset2D(nearestOffset));
1040  }
1041 }
1042 
1043 
1044 void
1046  if (size() == 0 || !p.almostSame(back())) {
1047  push_back(p);
1048  }
1049 }
1050 
1051 
1052 void
1054  if (size() == 0 || !p.almostSame(front())) {
1055  insert(begin(), p);
1056  }
1057 }
1058 
1059 
1060 bool
1062  return size() >= 2 && (*this)[0] == back();
1063 }
1064 
1065 
1066 void
1067 PositionVector::removeDoublePoints(SUMOReal minDist, bool assertLength) {
1068  if (size() > 1) {
1069  iterator last = begin();
1070  for (iterator i = begin() + 1; i != end() && (!assertLength || size() > 2);) {
1071  if (last->almostSame(*i, minDist)) {
1072  i = erase(i);
1073  } else {
1074  last = i;
1075  ++i;
1076  }
1077  }
1078  }
1079 }
1080 
1081 
1082 bool
1084  if (size() == v2.size()) {
1085  for (int i = 0; i < (int)size(); i++) {
1086  if ((*this)[i] != v2[i]) {
1087  return false;
1088  }
1089  }
1090  return true;
1091  } else {
1092  return false;
1093  }
1094 }
1095 
1096 
1097 bool
1099  if (size() > 2) {
1100  return false;
1101  }
1102  for (const_iterator i = begin(); i != end() - 1; i++) {
1103  if ((*i).z() != (*(i + 1)).z()) {
1104  return true;
1105  }
1106  }
1107  return false;
1108 }
1109 
1110 
1111 bool
1112 PositionVector::intersects(const Position& p11, const Position& p12, const Position& p21, const Position& p22, const SUMOReal withinDist, SUMOReal* x, SUMOReal* y, SUMOReal* mu) {
1113  const SUMOReal eps = std::numeric_limits<SUMOReal>::epsilon();
1114  const double denominator = (p22.y() - p21.y()) * (p12.x() - p11.x()) - (p22.x() - p21.x()) * (p12.y() - p11.y());
1115  const double numera = (p22.x() - p21.x()) * (p11.y() - p21.y()) - (p22.y() - p21.y()) * (p11.x() - p21.x());
1116  const double numerb = (p12.x() - p11.x()) * (p11.y() - p21.y()) - (p12.y() - p11.y()) * (p11.x() - p21.x());
1117  /* Are the lines coincident? */
1118  if (fabs(numera) < eps && fabs(numerb) < eps && fabs(denominator) < eps) {
1119  SUMOReal a1;
1120  SUMOReal a2;
1121  SUMOReal a3;
1122  SUMOReal a4;
1123  SUMOReal a = -1e12;
1124  if (p11.x() != p12.x()) {
1125  a1 = p11.x() < p12.x() ? p11.x() : p12.x();
1126  a2 = p11.x() < p12.x() ? p12.x() : p11.x();
1127  a3 = p21.x() < p22.x() ? p21.x() : p22.x();
1128  a4 = p21.x() < p22.x() ? p22.x() : p21.x();
1129  } else {
1130  a1 = p11.y() < p12.y() ? p11.y() : p12.y();
1131  a2 = p11.y() < p12.y() ? p12.y() : p11.y();
1132  a3 = p21.y() < p22.y() ? p21.y() : p22.y();
1133  a4 = p21.y() < p22.y() ? p22.y() : p21.y();
1134  }
1135  if (a1 <= a3 && a3 <= a2) {
1136  if (a4 < a2) {
1137  a = (a3 + a4) / 2;
1138  } else {
1139  a = (a2 + a3) / 2;
1140  }
1141  }
1142  if (a3 <= a1 && a1 <= a4) {
1143  if (a2 < a4) {
1144  a = (a1 + a2) / 2;
1145  } else {
1146  a = (a1 + a4) / 2;
1147  }
1148  }
1149  if (a != -1e12) {
1150  if (x != 0) {
1151  if (p11.x() != p12.x()) {
1152  *mu = (a - p11.x()) / (p12.x() - p11.x());
1153  *x = a;
1154  *y = p11.y() + (*mu) * (p12.y() - p11.y());
1155  } else {
1156  *x = p11.x();
1157  *y = a;
1158  if (p12.y() == p11.y()) {
1159  *mu = 0;
1160  } else {
1161  *mu = (a - p11.y()) / (p12.y() - p11.y());
1162  }
1163  }
1164  }
1165  return true;
1166  }
1167  return false;
1168  }
1169  /* Are the lines parallel */
1170  if (fabs(denominator) < eps) {
1171  return false;
1172  }
1173  /* Is the intersection along the segments */
1174  double mua = numera / denominator;
1175  /* reduce rounding errors for lines ending in the same point */
1176  if (fabs(p12.x() - p22.x()) < eps && fabs(p12.y() - p22.y()) < eps) {
1177  mua = 1.;
1178  } else {
1179  const double offseta = withinDist / p11.distanceTo2D(p12);
1180  const double offsetb = withinDist / p21.distanceTo2D(p22);
1181  const double mub = numerb / denominator;
1182  if (mua < -offseta || mua > 1 + offseta || mub < -offsetb || mub > 1 + offsetb) {
1183  return false;
1184  }
1185  }
1186  if (x != 0) {
1187  *x = p11.x() + mua * (p12.x() - p11.x());
1188  *y = p11.y() + mua * (p12.y() - p11.y());
1189  *mu = mua;
1190  }
1191  return true;
1192 }
1193 
1194 
1195 void
1197  const SUMOReal s = sin(angle);
1198  const SUMOReal c = cos(angle);
1199  for (int i = 0; i < (int)size(); i++) {
1200  const SUMOReal x = (*this)[i].x();
1201  const SUMOReal y = (*this)[i].y();
1202  const SUMOReal z = (*this)[i].z();
1203  const SUMOReal xnew = x * c - y * s;
1204  const SUMOReal ynew = x * s + y * c;
1205  (*this)[i].set(xnew, ynew, z);
1206  }
1207 }
1208 
1209 
1212  PositionVector result = *this;
1213  bool changed = true;
1214  while (changed && result.size() > 3) {
1215  changed = false;
1216  for (int i = 0; i < (int)result.size(); i++) {
1217  const Position& p1 = result[i];
1218  const Position& p2 = result[(i + 2) % result.size()];
1219  const int middleIndex = (i + 1) % result.size();
1220  const Position& p0 = result[middleIndex];
1221  // https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_two_points
1222  const SUMOReal triangleArea2 = fabs((p2.y() - p1.y()) * p0.x() - (p2.x() - p1.x()) * p0.y() + p2.x() * p1.y() - p2.y() * p1.x());
1223  const SUMOReal distIK = p1.distanceTo2D(p2);
1224  if (distIK > NUMERICAL_EPS && triangleArea2 / distIK < NUMERICAL_EPS) {
1225  changed = true;
1226  result.erase(result.begin() + middleIndex);
1227  break;
1228  }
1229  }
1230  }
1231  return result;
1232 }
1233 
1234 
1236 PositionVector::getOrthogonal(const Position& p, SUMOReal extend, SUMOReal& distToClosest) const {
1237  PositionVector result;
1238  PositionVector tmp = *this;
1239  tmp.extrapolate2D(extend);
1240  const SUMOReal baseOffset = tmp.nearest_offset_to_point2D(p);
1241  if (baseOffset == GeomHelper::INVALID_OFFSET) {
1242  // fail
1243  return result;
1244  }
1245  Position base = tmp.positionAtOffset2D(baseOffset);
1246  distToClosest = tmp[tmp.indexOfClosest(base)].distanceTo2D(base);
1247  if (p.distanceTo2D(base) > NUMERICAL_EPS) {
1248  result.push_back(p);
1249  result.push_back(base);
1250  }
1251  return result;
1252 }
1253 
1254 
1257  PositionVector result = *this;
1258  const SUMOReal z0 = (*this)[0].z();
1259  // the z-delta of the first segment
1260  const SUMOReal dz = (*this)[1].z() - z0;
1261  // if the shape only has 2 points it is as smooth as possible already
1262  if (size() > 2 && dz != 0) {
1263  dist = MIN2(dist, length());
1264  // check wether we need to insert a new point at dist
1265  Position pDist = positionAtOffset(dist);
1266  int iLast = indexOfClosest(pDist);
1267  // prevent close spacing to reduce impact of rounding errors in z-axis
1268  if (pDist.distanceTo2D((*this)[iLast]) > POSITION_EPS * 20) {
1269  iLast = result.insertAtClosest(pDist);
1270  }
1271  SUMOReal dist2 = result.offsetAtIndex2D(iLast);
1272  const SUMOReal dz2 = result[iLast].z() - z0;
1273  SUMOReal seen = 0;
1274  for (int i = 1; i < iLast; ++i) {
1275  seen += result[i].distanceTo2D(result[i - 1]);
1276  result[i].set(result[i].x(), result[i].y(), z0 + dz2 * seen / dist2);
1277  }
1278  }
1279  return result;
1280 
1281 }
1282 
1283 
1284 SUMOReal
1286  if (index < 0 || index >= (int)size()) {
1288  }
1289  SUMOReal seen = 0;
1290  for (int i = 1; i <= index; ++i) {
1291  seen += (*this)[i].distanceTo2D((*this)[i - 1]);
1292  }
1293  return seen;
1294 }
1295 
1296 
1297 SUMOReal
1299  SUMOReal result = 0;
1300  for (int i = 1; i < (int)size(); ++i) {
1301  const Position& p1 = (*this)[i - 1];
1302  const Position& p2 = (*this)[i];
1303  result = MAX2(result, (SUMOReal)fabs((p1.z() - p2.z()) / p1.distanceTo2D(p2)));
1304  }
1305  return result;
1306 }
1307 
1308 /****************************************************************************/
1309 
void sub(SUMOReal dx, SUMOReal dy)
Substracts the given position from this one.
Definition: Position.h:153
static SUMOReal 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:94
clase for increasing Sorter
static Position sideOffset(const Position &beg, const Position &end, const SUMOReal amount)
get a side position of position vector using a offset
bool hasElevation() const
return whether two positions differ in z-coordinate
SUMOReal rotationAtOffset(SUMOReal pos) const
Returns the rotation at the given length.
SUMOReal nearest_offset_to_point2D(const Position &p, bool perpendicular=true) const
return the nearest offest to point 2D
PositionVector getSubpart2D(SUMOReal beginOffset, SUMOReal endOffset) const
get subpart of a position vector in two dimensions (Z is ignored)
void sortAsPolyCWByAngle()
short as polygon CV by angle
void add(const Position &pos)
Adds the given position to this one.
Definition: Position.h:133
PositionVector getOrthogonal(const Position &p, SUMOReal extend, SUMOReal &distToClosest) const
return orthogonal through p (extending this vector if necessary)
#define M_PI
Definition: angles.h:37
friend std::ostream & operator<<(std::ostream &os, const PositionVector &geom)
Position getCentroid() const
Returns the centroid (closes the polygon if unclosed)
bool intersects(const Position &p1, const Position &p2) const
Returns the information whether this list of points interesects the given line.
void scaleRelative(SUMOReal factor)
enlarges/shrinks the polygon by a factor based at the centroid
T MAX2(T a, T b)
Definition: StdDefs.h:70
SUMOReal getOverlapWith(const PositionVector &poly, SUMOReal zThreshold) const
Returns the maximum overlaps between this and the given polygon (when not separated by at least zThre...
PositionVector getSubpartByIndex(int beginIndex, int count) const
get subpart of a position vector using index and a cout
bool partialWithin(const AbstractPoly &poly, SUMOReal offset=0) const
Returns the information whether this polygon lies partially within the given polygon.
#define RAD2DEG(x)
Definition: GeomHelper.h:46
SUMOReal distanceTo(const Position &p2) const
returns the euclidean distance in 3 dimension
Definition: Position.h:240
bool around(const Position &p, SUMOReal offset=0) const
Returns the information whether the position vector describes a polygon lying around the given point...
bool almostSame(const Position &p2, SUMOReal maxDiv=POSITION_EPS) const
checki if two position is almost the sme as other
Definition: Position.h:235
SUMOReal beginEndAngle() const
returns the angle in radians of the line connecting the first and the last position ...
bool isClosed() const
check if PositionVector is closed
const Position & operator[](int index) const
returns the constat position at the given index !!! exceptions?
SUMOReal x() const
Returns the x-position.
Definition: Position.h:63
Position positionAtOffset2D(SUMOReal pos, SUMOReal lateralOffset=0) const
Returns the position at the given length.
A class that stores a 2D geometrical boundary.
Definition: Boundary.h:48
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:200
SUMOReal 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) ...
PositionVector reverse() const
reverse position vector
SUMOReal slopeDegreeAtOffset(SUMOReal pos) const
Returns the slope at the given length.
void rotate2D(SUMOReal angle)
PositionVector convexHull() const
~PositionVector()
Destructor.
void extrapolate2D(const SUMOReal val, const bool onlyFirst=false)
extrapolate position vector in two dimensions (Z is ignored)
SUMOReal length2D() const
Returns the length.
std::vector< SUMOReal > distances(const PositionVector &s, bool perpendicular=false) const
distances of all my points to s and all of s points to myself
void push_front_noDoublePos(const Position &p)
insert in front a non double position
#define max(a, b)
Definition: polyfonts.c:65
PositionVector simplified() const
return the same shape with intermediate colinear points removed
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:55
static SUMOReal legacyDegree(const SUMOReal angle, const bool positive=false)
Definition: GeomHelper.cpp:204
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:46
A list of positions.
void add(SUMOReal xoff, SUMOReal yoff, SUMOReal zoff)
int indexOfClosest(const Position &p) const
index of the closest position to p
int operator()(const Position &p1, const Position &p2) const
comparing operation for sort
SUMOReal z() const
Returns the z-position.
Definition: Position.h:73
Position positionAtOffset(SUMOReal pos, SUMOReal lateralOffset=0) const
Returns the position at the given length.
T MIN2(T a, T b)
Definition: StdDefs.h:64
#define POSITION_EPS
Definition: config.h:182
int insertAtClosest(const Position &p)
inserts p between the two closest positions and returns the insertion index
void sortByIncreasingXY()
shory by increasing X-Y Psitions
bool operator==(const PositionVector &v2) const
comparing operation
std::pair< PositionVector, PositionVector > splitAt(SUMOReal where) const
Returns the two lists made when this list vector is splitted at the given point.
PositionVector()
Constructor. Creates an empty position vector.
SUMOReal length() const
Returns the length.
SUMOReal rotationDegreeAtOffset(SUMOReal pos) const
Returns the rotation at the given length.
PositionVector simpleHull_2D(const PositionVector &V)
#define sign(a)
Definition: polyfonts.c:68
void removeDoublePoints(SUMOReal minDist=POSITION_EPS, bool assertLength=false)
Removes positions if too near.
PositionVector smoothedZFront(SUMOReal dist=std::numeric_limits< SUMOReal >::max()) const
returned vector that is smoothed at the front (within dist)
SUMOReal offsetAtIndex2D(int index) const
return the offset at the given index
SUMOReal angleAt2D(int pos) const
get angle in certain position of position vector
void add(SUMOReal x, SUMOReal y, SUMOReal z=0)
Makes the boundary include the given coordinate.
Definition: Boundary.cpp:90
void scaleAbsolute(SUMOReal offset)
enlarges/shrinks the polygon by an absolute offset based at the centroid
SUMOReal y() const
Returns the y-position.
Definition: Position.h:68
bool overlapsWith(const AbstractPoly &poly, SUMOReal offset=0) const
Returns the information whether the given polygon overlaps with this.
static SUMOReal nearest_offset_on_line_to_point2D(const Position &lineStart, const Position &lineEnd, const Position &p, bool perpendicular=true)
Definition: GeomHelper.cpp:100
Position intersectionPosition2D(const Position &p1, const Position &p2, const SUMOReal withinDist=0.) const
Returns the position of the intersection.
Position getLineCenter() const
get line center
SUMOReal distanceTo2D(const Position &p2) const
returns the euclidean distance in the x-y-plane
Definition: Position.h:250
void move2side(SUMOReal amount)
move position vector to side using certain ammount
#define SUMOReal
Definition: config.h:208
#define NUMERICAL_EPS
Definition: config.h:158
void push_back_noDoublePos(const Position &p)
insert in back a non double position
int operator()(const Position &p1, const Position &p2) const
comparing operation
Position getPolygonCenter() const
Returns the arithmetic of all corner points.
SUMOReal area() const
Returns the area (0 for non-closed)
virtual bool around(const Position &p, SUMOReal offset=0) const =0
std::vector< SUMOReal > intersectsAtLengths2D(const PositionVector &other) const
For all intersections between this vector and other, return the 2D-length of the subvector from this ...
SUMOReal getMaxGrade() const
return the maximum grade of all segments as a fraction of zRange/length2D
void closePolygon()
ensures that the last position equals the first
Boundary getBoxBoundary() const
Returns a boundary enclosing this list of lines.
bool crosses(const Position &p1, const Position &p2) const
int removeClosest(const Position &p)
removes the point closest to p and return the removal index
PositionVector getSubpart(SUMOReal beginOffset, SUMOReal endOffset) const
get subpart of a position vector
SUMOReal angleTo2D(const Position &other) const
returns the angle in the plane of the vector pointing from here to the other position ...
Definition: Position.h:260
void append(const PositionVector &v, SUMOReal sameThreshold=2.0)
void extrapolate(const SUMOReal val, const bool onlyFirst=false)
extrapolate position vector
static const SUMOReal INVALID_OFFSET
a value to signify offsets outside the range of [0, Line.length()]
Definition: GeomHelper.h:59
SUMOReal isLeft(const Position &P0, const Position &P1, const Position &P2) const
get left
static const Position INVALID
used to indicate that a position is valid
Definition: Position.h:278
Position transformToVectorCoordinates(const Position &p, bool extend=false) const
return position p within the length-wise coordinate system defined by this position vector...