Autoware.Auto
intersection.hpp
Go to the documentation of this file.
1 // Copyright 2020 Embotech AG, Zurich, Switzerland
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 // Co-developed by Tier IV, Inc. and Apex.AI, Inc.
16 
17 #ifndef GEOMETRY__INTERSECTION_HPP_
18 #define GEOMETRY__INTERSECTION_HPP_
19 
20 
21 #include <autoware_auto_vehicle_msgs/msg/vehicle_kinematic_state.hpp>
22 #include <autoware_auto_perception_msgs/msg/bounding_box.hpp>
23 #include <autoware_auto_planning_msgs/msg/trajectory_point.hpp>
24 #include <geometry/convex_hull.hpp>
25 #include <geometry/common_2d.hpp>
26 
27 #include <limits>
28 #include <vector>
29 #include <iostream>
30 #include <list>
31 #include <utility>
32 #include <type_traits>
33 #include <algorithm>
34 
35 namespace autoware
36 {
37 namespace common
38 {
39 namespace geometry
40 {
50 
51 using Point = geometry_msgs::msg::Point32;
52 
53 namespace details
54 {
55 
57 using Line = std::pair<Point, Point>;
58 
60 // defined or have float members x and y
65 template<typename Iter>
66 std::vector<Line> get_sorted_face_list(const Iter start, const Iter end)
67 {
68  // First get a sorted list of points - convex_hull does that by modifying its argument
69  auto corner_list = std::list<Point>(start, end);
70  const auto first_interior_point = convex_hull(corner_list);
71 
72  std::vector<Line> face_list{};
73  auto itLast = corner_list.begin();
74  auto itNext = std::next(itLast, 1);
75  do {
76  face_list.emplace_back(Line{*itLast, *itNext});
77  itLast = itNext;
78  itNext = std::next(itNext, 1);
79  } while ((itNext != first_interior_point) && (itNext != corner_list.end()));
80 
81  face_list.emplace_back(Line{*itLast, corner_list.front()});
82 
83  return face_list;
84 }
85 
87 template<template<typename ...> class Iterable1T, template<typename ...> class Iterable2T,
88  typename PointT>
90  const Iterable1T<PointT> & external,
91  const Iterable2T<PointT> & internal,
92  std::list<PointT> & result)
93 {
94  std::copy_if(
95  internal.begin(), internal.end(), std::back_inserter(result),
96  [&external](const auto & pt) {
97  return common::geometry::is_point_inside_polygon_2d(external.begin(), external.end(), pt);
98  });
99 }
100 
102 template<template<typename ...> class Iterable1T, template<typename ...> class Iterable2T,
103  typename PointT>
105  const Iterable1T<PointT> & polygon1,
106  const Iterable2T<PointT> & polygon2,
107  std::list<PointT> & result)
108 {
109  using FloatT = decltype(point_adapter::x_(std::declval<PointT>()));
111 
112  auto get_edge = [](const auto & list, const auto & iterator) {
113  const auto next_it = std::next(iterator);
114  const auto & next_pt = (next_it != list.end()) ? *next_it : list.front();
115  return std::make_pair(*iterator, next_pt);
116  };
117 
118  // Get the max absolute value out of two intervals and a scalar.
119  auto compute_eps_scale = [](const auto & i1, const auto & i2, const auto val) {
120  auto get_abs_max = [](const auto & interval) {
121  return std::max(std::fabs(Interval::min(interval)), std::fabs(Interval::max(interval)));
122  };
123  return std::max(std::fabs(val), std::max(get_abs_max(i1), get_abs_max(i2)));
124  };
125 
126  // Compare each edge from polygon1 to each edge from polygon2
127  for (auto corner1_it = polygon1.begin(); corner1_it != polygon1.end(); ++corner1_it) {
128  const auto & edge1 = get_edge(polygon1, corner1_it);
129 
130  Interval edge1_x_interval{
131  std::min(point_adapter::x_(edge1.first), point_adapter::x_(edge1.second)),
132  std::max(point_adapter::x_(edge1.first), point_adapter::x_(edge1.second))};
133 
134  Interval edge1_y_interval{
135  std::min(point_adapter::y_(edge1.first), point_adapter::y_(edge1.second)),
136  std::max(point_adapter::y_(edge1.first), point_adapter::y_(edge1.second))};
137 
138  for (auto corner2_it = polygon2.begin(); corner2_it != polygon2.end(); ++corner2_it) {
139  try {
140  const auto & edge2 = get_edge(polygon2, corner2_it);
141  const auto & intersection =
143  edge1.first, minus_2d(edge1.second, edge1.first),
144  edge2.first, minus_2d(edge2.second, edge2.first));
145 
146  Interval edge2_x_interval{
147  std::min(point_adapter::x_(edge2.first), point_adapter::x_(edge2.second)),
148  std::max(point_adapter::x_(edge2.first), point_adapter::x_(edge2.second))};
149 
150  Interval edge2_y_interval{
151  std::min(point_adapter::y_(edge2.first), point_adapter::y_(edge2.second)),
152  std::max(point_adapter::y_(edge2.first), point_adapter::y_(edge2.second))};
153 
154  // The accumulated floating point error depends on the magnitudes of each end of the
155  // intervals. Hence the upper bound of the absolute magnitude should be taken into account
156  // while computing the epsilon.
157  const auto max_feps_scale = std::max(
158  compute_eps_scale(edge1_x_interval, edge2_x_interval, point_adapter::x_(intersection)),
159  compute_eps_scale(edge1_y_interval, edge2_y_interval, point_adapter::y_(intersection))
160  );
161  const auto feps = max_feps_scale * std::numeric_limits<FloatT>::epsilon();
162  // Only accept intersections that lie on both of the line segments (edges)
163  if (Interval::contains(edge1_x_interval, point_adapter::x_(intersection), feps) &&
164  Interval::contains(edge2_x_interval, point_adapter::x_(intersection), feps) &&
165  Interval::contains(edge1_y_interval, point_adapter::y_(intersection), feps) &&
166  Interval::contains(edge2_y_interval, point_adapter::y_(intersection), feps))
167  {
168  result.push_back(intersection);
169  }
170  } catch (const std::runtime_error &) {
171  // Parallel lines. TODO(yunus.caliskan): #1229
172  continue;
173  }
174  }
175  }
176 }
177 
178 
179 } // namespace details
180 
181 // TODO(s.me) implement GJK(+EPA) algorithm as well as per Chris Ho's suggestion
183 // defined or have float members x and y
185 // This uses SAT for doing the actual checking
186 // (https://en.wikipedia.org/wiki/Hyperplane_separation_theorem#Use_in_collision_detection)
192 template<typename Iter>
193 bool intersect(const Iter begin1, const Iter end1, const Iter begin2, const Iter end2)
194 {
195  // Obtain sorted lists of faces of both boxes, merge them into one big list of faces
196  auto faces = details::get_sorted_face_list(begin1, end1);
197  const auto faces_2 = details::get_sorted_face_list(begin2, end2);
198  faces.reserve(faces.size() + faces_2.size());
199  faces.insert(faces.end(), faces_2.begin(), faces_2.end() );
200 
201  // Also look at last line
202  for (const auto & face : faces) {
203  // Compute normal vector to the face and define a closure to get progress along it
204  const auto normal = get_normal(minus_2d(face.second, face.first));
205  auto get_position_along_line = [&normal](auto point)
206  {
207  return dot_2d(normal, minus_2d(point, Point{}) );
208  };
209 
210  // Define a function to get the minimum and maximum projected position of the corners
211  // of a given bounding box along the normal line of the face
212  auto get_projected_min_max = [&get_position_along_line, &normal](Iter begin, Iter end)
213  {
214  const auto zero_point = Point{};
215  auto min_corners =
216  get_position_along_line(closest_line_point_2d(normal, zero_point, *begin));
217  auto max_corners = min_corners;
218 
219  for (auto & point = begin; point != end; ++point) {
220  const auto point_projected = closest_line_point_2d(normal, zero_point, *point);
221  const auto position_along_line = get_position_along_line(point_projected);
222  min_corners = std::min(min_corners, position_along_line);
223  max_corners = std::max(max_corners, position_along_line);
224  }
225  return std::pair<float, float>{min_corners, max_corners};
226  };
227 
228  // Perform the actual computations for the extent computation
229  auto minmax_1 = get_projected_min_max(begin1, end1);
230  auto minmax_2 = get_projected_min_max(begin2, end2);
231 
232  // Check for any intersections
233  const auto eps = std::numeric_limits<decltype(minmax_1.first)>::epsilon();
234  if (minmax_1.first > minmax_2.second + eps || minmax_2.first > minmax_1.second + eps) {
235  // Found separating hyperplane, stop
236  return false;
237  }
238  }
239 
240  // No separating hyperplane found, boxes collide
241  return true;
242 }
243 
263 template<template<typename ...> class Iterable1T, template<typename ...> class Iterable2T,
264  typename PointT>
266  const Iterable1T<PointT> & polygon1,
267  const Iterable2T<PointT> & polygon2)
268 {
269  std::list<PointT> result;
270  details::append_contained_points(polygon1, polygon2, result);
271  details::append_contained_points(polygon2, polygon1, result);
272  details::append_intersection_points(polygon1, polygon2, result);
273  const auto end_it = common::geometry::convex_hull(result);
274  result.resize(static_cast<uint32_t>(std::distance(result.cbegin(), end_it)));
275  return result;
276 }
277 
278 
290 template<template<typename ...> class Iterable1T, template<typename ...> class Iterable2T,
291  typename PointT>
293  const Iterable1T<PointT> & polygon1,
294  const Iterable2T<PointT> & polygon2
295 )
296 {
297  constexpr auto eps = std::numeric_limits<float32_t>::epsilon();
298  const auto intersection = convex_polygon_intersection2d(polygon1, polygon2);
299 
300  const auto intersection_area =
301  common::geometry::area_2d(intersection.begin(), intersection.end());
302 
303  if (intersection_area < eps) {
304  return 0.0F; // There's either no intersection or the points are collinear
305  }
306 
307  const auto polygon1_area =
308  common::geometry::area_2d(polygon1.begin(), polygon1.end());
309  const auto polygon2_area =
310  common::geometry::area_2d(polygon2.begin(), polygon2.end());
311 
312  const auto union_area = polygon1_area + polygon2_area - intersection_area;
313  if (union_area < eps) {
314  throw std::domain_error("IoU is undefined for polygons with a zero union area");
315  }
316 
317  return intersection_area / union_area;
318 }
319 
320 } // namespace geometry
321 } // namespace common
322 } // namespace autoware
323 
324 #endif // GEOMETRY__INTERSECTION_HPP_
motion::motion_testing_nodes::TrajectoryPoint
autoware_auto_planning_msgs::msg::TrajectoryPoint TrajectoryPoint
Definition: motion_testing_publisher.hpp:36
autoware::common::geometry::times_2d
T times_2d(const T &p, const float32_t a)
The scalar multiplication operation, p * a.
Definition: common_2d.hpp:254
autoware::common::geometry::intersect
bool intersect(const Iter begin1, const Iter end1, const Iter begin2, const Iter end2)
Check if polyhedra defined by two given sets of points intersect.
Definition: intersection.hpp:193
autoware::common::geometry::get_normal
T get_normal(const T &pt)
compute q s.t. p T q, or p * q = 0 This is the equivalent of a 90 degree ccw rotation
Definition: common_2d.hpp:327
autoware::common::geometry::details::Line
std::pair< Point, Point > Line
Alias for a std::pair of two points.
Definition: intersection.hpp:57
autoware::common::geometry::point_adapter::x_
auto x_(const PointT &pt)
Gets the x value for a point.
Definition: common_2d.hpp:50
convex_hull.hpp
This file implements the monotone chain algorithm to compute 2D convex hulls on linked lists of point...
autoware::common::geometry::details::append_contained_points
void append_contained_points(const Iterable1T< PointT > &external, const Iterable2T< PointT > &internal, std::list< PointT > &result)
Append points of the polygon internal that are contained in the polygon exernal.
Definition: intersection.hpp:89
autoware::common::geometry::details::get_sorted_face_list
std::vector< Line > get_sorted_face_list(const Iter start, const Iter end)
Compute a sorted list of faces of a polyhedron given a list of points.
Definition: intersection.hpp:66
autoware::common::geometry::convex_polygon_intersection2d
std::list< PointT > convex_polygon_intersection2d(const Iterable1T< PointT > &polygon1, const Iterable2T< PointT > &polygon2)
Get the intersection between two polygons. The polygons should be provided in an identical format to ...
Definition: intersection.hpp:265
autoware::common::geometry::closest_line_point_2d
T closest_line_point_2d(const T &p, const T &q, const T &r)
Compute the closest point on the line going through p-q to point r.
Definition: common_2d.hpp:378
common_2d.hpp
This file includes common functionality for 2D geometry, such as dot products.
autoware::common::geometry::area_2d
auto area_2d(const IT begin, const IT end) noexcept
Definition: common_2d.hpp:504
autoware::common::geometry::dot_2d
auto dot_2d(const T1 &pt, const T2 &q)
compute p * q = p1 * q1 + p2 * q2
Definition: common_2d.hpp:193
autoware::common::geometry::Point
geometry_msgs::msg::Point32 Point
Definition: intersection.hpp:51
autoware::common::geometry::is_point_inside_polygon_2d
bool is_point_inside_polygon_2d(const IteratorType &start_it, const IteratorType &end_it, const PointType &p)
Check if the given point is inside or on the edge of the given polygon.
Definition: common_2d.hpp:545
BoundingBox
autoware_auto_perception_msgs::msg::BoundingBox BoundingBox
Definition: tf2_autoware_auto_msgs.hpp:39
autoware
This file defines the lanelet2_map_provider_node class.
Definition: quick_sort.hpp:24
autoware::common::geometry::convex_hull
std::list< PointT >::const_iterator convex_hull(std::list< PointT > &list)
A static memory implementation of convex hull computation. Shuffles points around the deque such that...
Definition: convex_hull.hpp:185
autoware::common::geometry::Interval::min
static T min(const Interval &i)
The minimum bound of the interval.
Definition: interval.hpp:134
autoware::common::geometry::norm_2d
auto norm_2d(const T &pt)
get magnitude of x and y components:
Definition: common_2d.hpp:340
autoware::common::geometry::details::append_intersection_points
void append_intersection_points(const Iterable1T< PointT > &polygon1, const Iterable2T< PointT > &polygon2, std::list< PointT > &result)
Append the intersecting points between two polygons into the output list.
Definition: intersection.hpp:104
autoware::common::geometry::convex_intersection_over_union_2d
common::types::float32_t convex_intersection_over_union_2d(const Iterable1T< PointT > &polygon1, const Iterable2T< PointT > &polygon2)
Compute the intersection over union of two 2d convex polygons. If any of the polygons span a zero are...
Definition: intersection.hpp:292
autoware::common::types::float32_t
float float32_t
Definition: types.hpp:45
autoware::common::geometry::minus_2d
T minus_2d(const T &p, const T &q)
Compute the 2d difference between two points, p - q.
Definition: common_2d.hpp:207
autoware::common::geometry::Interval::contains
static bool contains(const Interval &i, const T &value, const T &eps=std::numeric_limits< T >::epsilon())
Test for whether a given interval contains a given value within the given epsilon.
Definition: interval.hpp:313
autoware::common::geometry::intersection_2d
T intersection_2d(const T &pt, const T &u, const T &q, const T &v)
solve p + t * u = q + s * v Ref: https://stackoverflow.com/questions/563198/ whats-the-most-efficent-...
Definition: common_2d.hpp:273
autoware::common::geometry::Interval::max
static T max(const Interval &i)
The maximum bound of the interval.
Definition: interval.hpp:137
autoware::common::geometry::point_adapter::y_
auto y_(const PointT &pt)
Gets the y value for a point.
Definition: common_2d.hpp:66
autoware::common::geometry::Interval
Data structure to contain scalar interval bounds.
Definition: interval.hpp:52
get_open_port.end
end
Definition: scripts/get_open_port.py:23