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