10#include <lanelet2_core/geometry/LineString.h>
11#include <rclcpp/rclcpp.hpp>
19double pointToSegmentDistance(
const Eigen::Vector2d& point,
20 const Eigen::Vector2d& segment_start,
21 const Eigen::Vector2d& segment_end) {
22 const Eigen::Vector2d segment = segment_end - segment_start;
23 const double squared_length = segment.squaredNorm();
24 if (squared_length == 0.0) {
25 return (point - segment_start).norm();
27 const double interpolation = std::clamp((point - segment_start).dot(segment) / squared_length, 0.0, 1.0);
28 return (point - (segment_start + interpolation * segment)).norm();
31void adaptivelySampleLineStringRange(
const std::vector<Eigen::Vector2d>& line_string,
34 const double max_lateral_error,
35 std::vector<bool>& keep) {
39 if (last <= first + 1) {
44 std::vector<std::pair<size_t, size_t>> ranges = {{first, last}};
45 while (!ranges.empty()) {
46 const auto [range_first, range_last] = ranges.back();
49 double largest_distance = -1.0;
50 size_t largest_distance_idx = range_first;
51 for (
size_t i = range_first + 1; i < range_last; ++i) {
52 const double distance = pointToSegmentDistance(line_string[i], line_string[range_first], line_string[range_last]);
53 if (distance > largest_distance) {
54 largest_distance = distance;
55 largest_distance_idx = i;
59 if (largest_distance > max_lateral_error) {
60 keep[largest_distance_idx] =
true;
61 if (largest_distance_idx > range_first + 1) {
62 ranges.emplace_back(range_first, largest_distance_idx);
64 if (range_last > largest_distance_idx + 1) {
65 ranges.emplace_back(largest_distance_idx, range_last);
74 double wrapped_angle = angle;
75 while (wrapped_angle > M_PI) wrapped_angle -= 2 * M_PI;
76 while (wrapped_angle < -M_PI) wrapped_angle += 2 * M_PI;
81 double dot_product = v1.dot(v2);
82 double determinant = v1.x() * v2.y() - v1.y() * v2.x();
83 double angle = std::atan2(determinant, dot_product);
87std::optional<IntersectionOfLinesResult>
intersectionOfLines(
const std::vector<Eigen::Vector2d>& line1,
88 const std::vector<Eigen::Vector2d>& line2) {
92 if (line1.size() < 2 || line2.size() < 2) {
97 const double a1 = line1[1].y() - line1[0].y();
98 const double b1 = line1[0].x() - line1[1].x();
99 const double c1 = line1[0].x() * line1[1].y() - line1[1].x() * line1[0].y();
100 const double a2 = line2[1].y() - line2[0].y();
101 const double b2 = line2[0].x() - line2[1].x();
102 const double c2 = line2[0].x() * line2[1].y() - line2[1].x() * line2[0].y();
105 const double det = a1 * b2 - a2 * b1;
107 const double x = (b2 * c1 - b1 * c2) / det;
108 const double y = (a1 * c2 - a2 * c1) / det;
112 result.
intersects_line1 = (x >= std::min(line1[0].x(), line1[1].x()) && x <= std::max(line1[0].x(), line1[1].x()) &&
113 y >= std::min(line1[0].y(), line1[1].y()) && y <= std::max(line1[0].y(), line1[1].y()));
114 result.
intersects_line2 = (x >= std::min(line2[0].x(), line2[1].x()) && x <= std::max(line2[0].x(), line2[1].x()) &&
115 y >= std::min(line2[0].y(), line2[1].y()) && y <= std::max(line2[0].y(), line2[1].y()));
124 const Eigen::Vector2d& prev_point,
125 const Eigen::Vector2d& next_point) {
126 Eigen::Vector2d tangent;
128 if (point == prev_point && point != next_point) {
129 tangent = (next_point - point).normalized();
130 }
else if (point != prev_point && point == next_point) {
131 tangent = (point - prev_point).normalized();
132 }
else if (point == prev_point && point == next_point) {
133 tangent = Eigen::Vector2d(0.0, 0.0);
135 const Eigen::Vector2d prev_to_point_unit = (point - prev_point).normalized();
136 const Eigen::Vector2d point_to_next_unit = (next_point - point).normalized();
137 tangent = (prev_to_point_unit + point_to_next_unit).normalized();
144 const Eigen::Vector2d& prev_point,
145 const Eigen::Vector2d& next_point) {
147 Eigen::Vector2d normal = Eigen::Vector2d(tangent.y(), -tangent.x());
155 std::vector<Eigen::Vector3d> resampled_line_string;
158 double sampling_distance = delta;
160 resampled_line_string.push_back(line_string.front());
162 sampling_distance = offset;
166 for (
size_t i = 1; i < line_string.size(); ++i) {
168 double segment_length = (
to2d(line_string[i]) -
to2d(line_string[i - 1])).norm();
169 const Eigen::Vector3d segment_direction = (line_string[i] - line_string[i - 1]) / segment_length;
172 while (segment_length >= sampling_distance) {
173 const Eigen::Vector3d resampled_point = line_string[i - 1] + sampling_distance * segment_direction;
174 resampled_line_string.push_back(resampled_point);
175 sampling_distance += delta;
179 sampling_distance = sampling_distance - segment_length;
183 offset = sampling_distance;
185 return resampled_line_string;
189 const double max_lateral_error,
190 const std::vector<size_t>& break_after_indices) {
191 std::vector<size_t> retained_indices;
192 if (line_string.empty()) {
193 return retained_indices;
195 if (max_lateral_error <= 0.0) {
196 retained_indices.resize(line_string.size());
197 std::iota(retained_indices.begin(), retained_indices.end(), 0);
198 return retained_indices;
202 std::vector<size_t> valid_breaks;
203 for (
const size_t index : break_after_indices) {
204 if (index < line_string.size() - 1) {
205 valid_breaks.push_back(index);
208 std::sort(valid_breaks.begin(), valid_breaks.end());
209 valid_breaks.erase(std::unique(valid_breaks.begin(), valid_breaks.end()), valid_breaks.end());
212 std::vector<bool> keep(line_string.size(),
false);
213 size_t segment_start = 0;
214 for (
const size_t break_after : valid_breaks) {
215 if (break_after < segment_start) {
218 adaptivelySampleLineStringRange(line_string, segment_start, break_after, max_lateral_error, keep);
219 segment_start = break_after + 1;
221 adaptivelySampleLineStringRange(line_string, segment_start, line_string.size() - 1, max_lateral_error, keep);
223 for (
size_t i = 0; i < keep.size(); ++i) {
225 retained_indices.push_back(i);
228 return retained_indices;
240 const Eigen::Vector2d& point,
const Eigen::Vector2d& axis,
const std::vector<Eigen::Vector2d>& line_string) {
243 double closest_distance_to_line_segment = std::numeric_limits<double>::max();
244 bool found_at_least_one_intersection =
false;
247 std::vector<Eigen::Vector2d> axis_line = {point, point + axis};
250 for (
size_t i = 0; i < line_string.size() - 1; ++i) {
251 std::vector<Eigen::Vector2d> line_segment = {line_string[i], line_string[i + 1]};
255 const Eigen::Vector2d& intersection = inner_result->intersection;
256 const bool intersects_line_segment = inner_result->intersects_line2;
257 found_at_least_one_intersection =
true;
258 if (intersects_line_segment) {
263 double distance_to_line_segment =
264 std::min((intersection - line_string[i]).norm(), (intersection - line_string[i + 1]).norm());
265 if (distance_to_line_segment < closest_distance_to_line_segment) {
266 closest_distance_to_line_segment = distance_to_line_segment;
272 if (!found_at_least_one_intersection) {
280 const Eigen::Vector2d& point,
281 const Eigen::Vector2d& prev_point,
282 const Eigen::Vector2d& next_point,
283 const std::vector<Eigen::Vector2d>& line_string) {
286 if (normal == Eigen::Vector2d(0.0, 0.0)) {
std::optional< ProjectPointToLineStringAlongAxisResult > projectPointToLineStringAlongAxis(const Eigen::Vector2d &point, const Eigen::Vector2d &axis, const std::vector< Eigen::Vector2d > &line_string)
Projects a point along an axis to the closest line segment of a line string.
std::optional< ProjectPointToLineStringAlongAxisResult > projectPointToLineStringAlongNormal(const Eigen::Vector2d &point, const Eigen::Vector2d &prev_point, const Eigen::Vector2d &next_point, const std::vector< Eigen::Vector2d > &line_string)
Projects a point to the closest line segment of a line string along the normal to the tangent at the ...
double angleBetweenVectors(const Eigen::Vector2d &v1, const Eigen::Vector2d &v2)
Computes the angle between two 2D vectors.
Eigen::Vector2d tangentOfPointAlongLineString(const Eigen::Vector2d &point, const Eigen::Vector2d &prev_point, const Eigen::Vector2d &next_point)
Computes a unit vector tangential to a point along a line string.
Eigen::Vector2d projectPointToLineString(const Eigen::Vector2d &point, const std::vector< Eigen::Vector2d > &line_string)
Projects a point to the closest line segment of a line string.
std::vector< size_t > adaptivelySampleLineString(const std::vector< Eigen::Vector2d > &line_string, double max_lateral_error, const std::vector< size_t > &break_after_indices={})
Simplifies a 2D line string within a maximum lateral error.
lanelet::BasicPoint2d toLanelet(const Eigen::Vector2d &point)
Converts a 2D Eigen point to a Lanelet point.
double wrapAngle(const double angle)
Wraps an angle to the range [-π, π].
Eigen::Vector2d normalOfPointAlongLineString(const Eigen::Vector2d &point, const Eigen::Vector2d &prev_point, const Eigen::Vector2d &next_point)
Computes a unit vector normal to a point along a line string.
std::vector< Eigen::Vector3d > resampleLineString(const std::vector< Eigen::Vector3d > &line_string, const double delta, double &offset)
Resamples a 3D line string with a constant sampling distance.
Eigen::Vector2d to2d(const Eigen::Vector3d &point)
Converts a 3D Eigen point to a 2D Eigen point.
std::optional< IntersectionOfLinesResult > intersectionOfLines(const std::vector< Eigen::Vector2d > &line1, const std::vector< Eigen::Vector2d > &line2)
Computes the intersection of two 2D lines.
Return type of intersectionOfLines.
bool intersects_line2
whether intersection is on second line segment
bool intersects_line1
whether intersection is on first line segment
Eigen::Vector2d intersection
intersection point
Return type of projectPointToLineStringAlongAxis.
bool found_intersection_with_line_segment
whether intersection is on line segment
Eigen::Vector2d projected_point
intersection point