Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
Triangle.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// https://www.eclipse.org/legal/epl-2.0/
7// This Source Code may also be made available under the following Secondary
8// Licenses when the conditions for such availability set forth in the Eclipse
9// Public License 2.0 are satisfied: GNU General Public License, version 2
10// or later which is available at
11// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13/****************************************************************************/
18// A simple triangle defined in 3D
19/****************************************************************************/
20#include <config.h>
21
22#include <algorithm>
23#include "Triangle.h"
24
25
26// ===========================================================================
27// static member definitions
28// ===========================================================================
29
31const double Triangle::EPSILON = 1e-9;
32
33// ===========================================================================
34// method definitions
35// ===========================================================================
37
38
39Triangle::Triangle(const Position& positionA, const Position& positionB, const Position& positionC) :
40 myA(positionA),
41 myB(positionB),
42 myC(positionC) {
43 // calculate boundary
44 myBoundary.add(positionA);
45 myBoundary.add(positionB);
46 myBoundary.add(positionC);
47}
48
49
51
52
53bool
55 return isPositionWithin(myA, myB, myC, pos);
56}
57
58
59bool
61 return isPositionWithin(Position(boundary.xmax(), boundary.ymax())) &&
62 isPositionWithin(Position(boundary.xmin(), boundary.ymin())) &&
63 isPositionWithin(Position(boundary.xmax(), boundary.ymin())) &&
64 isPositionWithin(Position(boundary.xmin(), boundary.ymax()));
65}
66
67
68bool
70 return intersectWithShape(shape, shape.getBoxBoundary());
71}
72
73
74bool
75Triangle::intersectWithShape(const PositionVector& shape, const Boundary& shapeBoundary) const {
76 // Check if the triangle's vertices are within the shape
77 if (shape.around(myA) || shape.around(myB) || shape.around(myC)) {
78 return true;
79 }
80 // Check if at least two corners of the shape boundary are within the triangle.
81 // (This acts as a heuristic to detect overlap without checking every edge first)
82 int cornersInside = 0;
83 if (isPositionWithin(Position(shapeBoundary.xmax(), shapeBoundary.ymax()))) {
84 cornersInside++;
85 }
86 if (isPositionWithin(Position(shapeBoundary.xmin(), shapeBoundary.ymin()))) {
87 cornersInside++;
88 }
89 if ((cornersInside < 2) && isPositionWithin(Position(shapeBoundary.xmax(), shapeBoundary.ymin()))) {
90 cornersInside++;
91 }
92 if ((cornersInside < 2) && isPositionWithin(Position(shapeBoundary.xmin(), shapeBoundary.ymax()))) {
93 cornersInside++;
94 }
95 if (cornersInside >= 2) {
96 return true;
97 }
98 // At this point, we need to check if any line of the shape intersects with the triangle.
99 // We treat the shape as a closed polygon.
100 const int shapeSize = (int)shape.size();
101 for (int i = 0; i < shapeSize; i++) {
102 const Position& p1 = shape[i];
103 // Wrap around to the first point
104 const Position& p2 = shape[(i + 1) % shapeSize];
105 // Avoid checking a zero-length segment if the shape is already explicitly closed in data
106 if (p1 == p2) {
107 continue;
108 } else if (lineIntersectsTriangle(p1, p2)) {
109 return true;
110 }
111 }
112 return false;
113}
114
115
116bool
117Triangle::intersectWithCircle(const Position& center, const double radius) const {
118 const auto squaredRadius = radius * radius;
119 return ((center.distanceSquaredTo2D(myA) <= squaredRadius) ||
120 (center.distanceSquaredTo2D(myB) <= squaredRadius) ||
121 (center.distanceSquaredTo2D(myC) <= squaredRadius) ||
122 isPositionWithin(center) ||
123 lineIntersectCircle(myA, myB, center, radius) ||
124 lineIntersectCircle(myB, myC, center, radius) ||
125 lineIntersectCircle(myC, myA, center, radius));
126}
127
128
129const Boundary&
131 return myBoundary;
132}
133
134
135const PositionVector
137 return PositionVector({myA, myB, myC});
138}
139
140
141std::vector<Triangle>
143 std::vector<Triangle> triangles;
144 // first open polygon
145 shape.openPolygon();
146 // we need at leas three vertex
147 if (shape.size() >= 3) {
148 // greedy algorithm
149 while (shape.size() > 3) {
150 int shapeSize = (int)shape.size();
151 int earIndex = -1;
152 // first find an "ear"
153 for (int i = 0; (i < shapeSize) && (earIndex == -1); i++) {
154 const auto& earA = shape[(i + shapeSize - 1) % shapeSize];
155 const auto& earB = shape[i];
156 const auto& earC = shape[(i + 1) % shapeSize];
157 if (isEar(earA, earB, earC, shape)) {
158 earIndex = i;
159 }
160 }
161 if (earIndex != -1) {
162 triangles.push_back(Triangle(
163 shape[(earIndex + shapeSize - 1) % shapeSize],
164 shape[earIndex],
165 shape[(earIndex + 1) % shapeSize])
166 );
167 shape.erase(shape.begin() + earIndex);
168 } else {
169 // simply remove the first three
170 triangles.push_back(Triangle(shape[0], shape[1], shape[2]));
171 shape.erase(shape.begin() + 1);
172 }
173 }
174 // add last triangle
175 triangles.push_back(Triangle(shape[0], shape[1], shape[2]));
176 }
177 return triangles;
178}
179
180
181bool
182Triangle::operator==(const Triangle& other) const {
183 return myA == other.myA && myB == other.myB && myC == other.myC;
184}
185
186
187bool
188Triangle::operator!=(const Triangle& other) const {
189 return !(*this == other);
190}
191
192
193bool
194Triangle::isPositionWithin(const Position& A, const Position& B, const Position& C, const Position& pos) {
195 // Calculate cross products for each edge of the triangle
196 const double crossAB = crossProduct(A, B, pos);
197 const double crossBC = crossProduct(B, C, pos);
198 const double crossCA = crossProduct(C, A, pos);
199 // check conditions
200 const bool allPositive = (crossAB > -EPSILON) && (crossBC > -EPSILON) && (crossCA > -EPSILON);
201 const bool allNegative = (crossAB < EPSILON) && (crossBC < EPSILON) && (crossCA < EPSILON);
202 return allPositive || allNegative;
203}
204
205
206bool
207Triangle::isEar(const Position& a, const Position& b, const Position& c, const PositionVector& shape) {
208 // Check if triangle ABC is counter-clockwise
209 if (crossProduct(a, b, c) <= 0) {
210 return false;
211 }
212 // Check if any other point in the polygon lies inside the triangle
213 for (const auto& pos : shape) {
214 if ((pos != a) && (pos != b) && (pos != c) && isPositionWithin(a, b, c, pos)) {
215 return false;
216 }
217 }
218 return true;
219}
220
221
222double
223Triangle::crossProduct(const Position& a, const Position& b, const Position& c) {
224 return (b.x() - a.x()) * (c.y() - a.y()) - (b.y() - a.y()) * (c.x() - a.x());
225}
226
227
228int
229Triangle::orientation(const Position& p, const Position& q, const Position& r) const {
230 const double val = (q.y() - p.y()) * (r.x() - q.x()) - (q.x() - p.x()) * (r.y() - q.y());
231 if (std::abs(val) < EPSILON) {
232 return 0;
233 } else if (val > 0) {
234 return 1;
235 } else {
236 return -1;
237 }
238}
239
240
241bool
242Triangle::onSegment(const Position& p, const Position& q, const Position& r) const {
243 return ((q.x() >= std::min(p.x(), r.x()) - EPSILON) &&
244 (q.x() <= std::max(p.x(), r.x()) + EPSILON) &&
245 (q.y() >= std::min(p.y(), r.y()) - EPSILON) &&
246 (q.y() <= std::max(p.y(), r.y()) + EPSILON));
247}
248
249
250bool
251Triangle::segmentsIntersect(const Position& p1, const Position& q1, const Position& p2, const Position& q2) const {
252 const int o1 = orientation(p1, q1, p2);
253 const int o2 = orientation(p1, q1, q2);
254 const int o3 = orientation(p2, q2, p1);
255 const int o4 = orientation(p2, q2, q1);
256 // General case: segments intersect if they have different orientations
257 // Special cases: checking if points are collinear and on segment
258 if ((o1 != o2) && (o3 != o4)) {
259 return true;
260 } else if ((o1 == 0) && onSegment(p1, p2, q1)) {
261 return true;
262 } else if ((o2 == 0) && onSegment(p1, q2, q1)) {
263 return true;
264 } else if ((o3 == 0) && onSegment(p2, p1, q2)) {
265 return true;
266 } else if ((o4 == 0) && onSegment(p2, q1, q2)) {
267 return true;
268 } else {
269 return false;
270 }
271}
272
273
274bool
276 return segmentsIntersect(p1, p2, myA, myB) ||
277 segmentsIntersect(p1, p2, myB, myC) ||
278 segmentsIntersect(p1, p2, myC, myA);
279}
280
281
282bool
283Triangle::lineIntersectCircle(const Position& posA, const Position& posB, const Position& center, const double radius) const {
284 // Calculate coefficients of the quadratic equation
285 const double dx = posB.x() - posA.x();
286 const double dy = posB.y() - posA.y();
287 const double a = dx * dx + dy * dy;
288 const double b = 2 * (dx * (posA.x() - center.x()) + dy * (posA.y() - center.y()));
289 const double c = (posA.x() - center.x()) * (posA.x() - center.x()) + (posA.y() - center.y()) * (posA.y() - center.y()) - radius * radius;
290 // Calculate the discriminant
291 const double discriminant = (b * b - 4 * a * c);
292 // Check the discriminant to determine the intersection
293 if (discriminant >= 0) {
294 // Calculate the two possible values of t
295 const double sqrtDiscriminant = sqrt(discriminant);
296 const double t1 = (-b + sqrtDiscriminant) / (2 * a);
297 const double t2 = (-b - sqrtDiscriminant) / (2 * a);
298 // if at least t1 or t2 is between [0,1], then intersect
299 return (t1 >= 0 && t1 <= 1) || (t2 >= 0 && t2 <= 1);
300 } else {
301 return false;
302 }
303}
304
305/****************************************************************************/
A class that stores a 2D geometrical boundary.
Definition Boundary.h:39
void add(double x, double y, double z=0)
Makes the boundary include the given coordinate.
Definition Boundary.cpp:75
double ymin() const
Returns minimum y-coordinate.
Definition Boundary.cpp:127
double xmin() const
Returns minimum x-coordinate.
Definition Boundary.cpp:115
double ymax() const
Returns maximum y-coordinate.
Definition Boundary.cpp:133
double xmax() const
Returns maximum x-coordinate.
Definition Boundary.cpp:121
A point in 2D or 3D with translation and scaling methods.
Definition Position.h:37
double distanceSquaredTo2D(const Position &p2) const
returns the square of the distance to another position (Only using x and y positions)
Definition Position.h:278
double x() const
Returns the x-position.
Definition Position.h:52
double y() const
Returns the y-position.
Definition Position.h:57
A list of positions.
void openPolygon()
open polygon
Boundary getBoxBoundary() const
Returns a boundary enclosing this list of lines.
bool around(const Position &p, double offset=0) const
Returns the information whether the position vector describes a polygon lying around the given point.
A simple triangle defined in 3D.
Definition Triangle.h:35
const Boundary & getBoundary() const
get triangle boundary
Definition Triangle.cpp:130
int orientation(const Position &p, const Position &q, const Position &r) const
Definition Triangle.cpp:229
bool segmentsIntersect(const Position &p1, const Position &q1, const Position &p2, const Position &q2) const
check if two line segments (p1,q1) and (p2,q2) intersect
Definition Triangle.cpp:251
Boundary myBoundary
triangle boundary
Definition Triangle.h:123
bool operator!=(const Triangle &other) const
inequality operator
Definition Triangle.cpp:188
bool isPositionWithin(const Position &pos) const
check if the given position is within this triangle
Definition Triangle.cpp:54
const PositionVector getShape() const
get shape boundary
Definition Triangle.cpp:136
bool isBoundaryFullWithin(const Boundary &boundary) const
check if the given position is FULL within this triangle
Definition Triangle.cpp:60
~Triangle()
destructor
Definition Triangle.cpp:50
static const double EPSILON
epsilon used for certain comparations
Definition Triangle.h:126
static const Triangle INVALID
invalid triangle
Definition Triangle.h:39
static bool isEar(const Position &a, const Position &b, const Position &c, const PositionVector &shape)
Check if the triangle (A, B, C) is an ear.
Definition Triangle.cpp:207
bool lineIntersectsTriangle(const Position &p1, const Position &p2) const
check if a line segment (p1, p2) intersects this triangle
Definition Triangle.cpp:275
static std::vector< Triangle > triangulate(PositionVector shape)
Definition Triangle.cpp:142
bool intersectWithShape(const PositionVector &shape) const
check if the given shape is within or intersect with this triangle
Definition Triangle.cpp:69
Position myB
second triangle position
Definition Triangle.h:117
Triangle()
default constructor
Definition Triangle.cpp:36
bool onSegment(const Position &p, const Position &q, const Position &r) const
check if point q lies on segment pr
Definition Triangle.cpp:242
Position myA
first triangle position
Definition Triangle.h:114
bool operator==(const Triangle &other) const
equalityoperators
Definition Triangle.cpp:182
Position myC
third triangle position
Definition Triangle.h:120
bool lineIntersectCircle(const Position &posA, const Position &posB, const Position &center, const double radius) const
function to check if line between posA and posB intersect circle
Definition Triangle.cpp:283
bool intersectWithCircle(const Position &center, const double radius) const
check if the given circle intersect with this triangle
Definition Triangle.cpp:117
static double crossProduct(const Position &a, const Position &b, const Position &c)
calculate cross product of the given points
Definition Triangle.cpp:223