Autoware.Auto
rotating_calipers.hpp
Go to the documentation of this file.
1 // Copyright 2017-2019 the Autoware Foundation
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 
19 
20 #ifndef GEOMETRY__BOUNDING_BOX__ROTATING_CALIPERS_HPP_
21 #define GEOMETRY__BOUNDING_BOX__ROTATING_CALIPERS_HPP_
22 #include <geometry/convex_hull.hpp>
23 #include <geometry/common_2d.hpp>
25 
26 #include <algorithm>
27 #include <cstring>
28 #include <limits>
29 #include <list>
30 
31 namespace autoware
32 {
33 namespace common
34 {
35 namespace geometry
36 {
37 namespace bounding_box
38 {
39 namespace details
40 {
47 template<typename PointT>
48 uint32_t update_angles(const Point4<PointT> & edges, Point4<PointT> & directions)
49 {
50  // find smallest angle to next
51  float32_t best_cos_th = -std::numeric_limits<float32_t>::max();
52  float32_t best_edge_dir_mag = 0.0F;
53  uint32_t advance_idx = 0U;
54  for (uint32_t idx = 0U; idx < edges.size(); ++idx) {
55  const float32_t edge_dir_mag =
56  std::max(norm_2d(edges[idx]) * norm_2d(directions[idx]), autoware::common::types::FEPS);
57  const float32_t cos_th = dot_2d(edges[idx], directions[idx]) / edge_dir_mag;
58  if (cos_th > best_cos_th) {
59  best_cos_th = cos_th;
60  best_edge_dir_mag = edge_dir_mag;
61  advance_idx = idx;
62  }
63  }
64 
65  // update directions by smallest angle
66  const float32_t sin_th =
67  cross_2d(directions[advance_idx], edges[advance_idx]) / best_edge_dir_mag;
68  for (uint32_t idx = 0U; idx < edges.size(); ++idx) {
69  rotate_2d(directions[idx], best_cos_th, sin_th);
70  }
71 
72  return advance_idx;
73 }
74 
82 template<typename IT, typename PointT>
84  const IT begin,
85  const IT end,
86  const Point4<IT> & support,
87  Point4<PointT> & edges)
88 {
89  for (uint32_t idx = 0U; idx < support.size(); ++idx) {
90  auto tmp_it = support[idx];
91  ++tmp_it;
92  const PointT & q = (tmp_it == end) ? *begin : *tmp_it;
93  edges[idx] = minus_2d(q, *support[idx]);
94  }
95 }
96 
105 template<typename IT>
106 void init_bbox(const IT begin, const IT end, Point4<IT> & support)
107 {
108  // compute initial orientation using first two points
109  auto pt_it = begin;
110  ++pt_it;
111  const auto u = minus_2d(*pt_it, *begin);
112  support[0U] = begin;
113  std::array<float32_t, 3U> metric{{
114  -std::numeric_limits<float32_t>::max(),
115  -std::numeric_limits<float32_t>::max(),
116  std::numeric_limits<float32_t>::max()
117  }};
118  // track u * p, fabsf(u x p), and -u * p
119  for (pt_it = begin; pt_it != end; ++pt_it) {
120  // check points against orientation for run_ptr
121  const auto & pt = *pt_it;
122  // u * p
123  const float32_t up = dot_2d(u, pt);
124  if (up > metric[0U]) {
125  metric[0U] = up;
126  support[1U] = pt_it;
127  }
128  // -u * p
129  if (up < metric[2U]) {
130  metric[2U] = up;
131  support[3U] = pt_it;
132  }
133  // u x p
134  const float32_t uxp = cross_2d(u, pt);
135  if (uxp > metric[1U]) {
136  metric[1U] = uxp;
137  support[2U] = pt_it;
138  }
139  }
140 }
153 template<typename IT, typename MetricF>
154 BoundingBox rotating_calipers_impl(const IT begin, const IT end, const MetricF metric_fn)
155 {
156  using PointT = base_type<decltype(*begin)>;
157  // sanity check TODO(c.ho) more checks (up to n = 2?)
158  if (begin == end) {
159  throw std::domain_error("Malformed list, size = " + std::to_string(std::distance(begin, end)));
160  }
161  // initial scan to get anchor points
162  Point4<IT> support;
163  init_bbox(begin, end, support);
164  // initialize directions accordingly
165  Point4<PointT> directions;
166  { // I just don't want the temporary variable floating around
167  auto tmp = support[0U];
168  ++tmp;
169  directions[0U] = minus_2d(*tmp, *support[0U]);
170  directions[1U] = get_normal(directions[0U]);
171  directions[2U] = minus_2d(directions[0U]);
172  directions[3U] = minus_2d(directions[1U]);
173  }
174  // initialize edges
175  Point4<PointT> edges;
176  init_edges(begin, end, support, edges);
177  // size of initial guess
178  BoundingBox bbox;
179  decltype(BoundingBox::corners) best_corners;
180  compute_corners(best_corners, support, directions);
181  size_2d(best_corners, bbox.size);
182  bbox.value = metric_fn(bbox.size);
183  // rotating calipers step: incrementally advance, update angles, points, compute area
184  for (auto it = begin; it != end; ++it) {
185  // find smallest angle to next, update directions
186  const uint32_t advance_idx = update_angles(edges, directions);
187  // recompute area
188  decltype(BoundingBox::corners) corners;
189  compute_corners(corners, support, directions);
190  decltype(BoundingBox::size) tmp_size;
191  size_2d(corners, tmp_size);
192  const float32_t tmp_value = metric_fn(tmp_size);
193  // update best if necessary
194  if (tmp_value < bbox.value) {
195  // corners: memcpy is fine since I know corners and best_corners are distinct
196  (void)std::memcpy(&best_corners[0U], &corners[0U], sizeof(corners));
197  // size
198  bbox.size = tmp_size;
199  bbox.value = tmp_value;
200  }
201  // Step to next iteration of calipers
202  {
203  // update smallest support
204  auto next_it = support[advance_idx];
205  ++next_it;
206  const auto run_it = (end == next_it) ? begin : next_it;
207  support[advance_idx] = run_it;
208  // update edges
209  next_it = run_it;
210  ++next_it;
211  const PointT & next = (end == next_it) ? (*begin) : (*next_it);
212  edges[advance_idx] = minus_2d(next, *run_it);
213  }
214  }
215 
216  finalize_box(best_corners, bbox);
217 
218  // TODO(christopher.ho) check if one of the sizes is too small, fuzz corner 1 and 2
219  // This doesn't cause any issues now, it shouldn't happen in practice, and even if it did,
220  // it would probably get smoothed out by prediction. But, this could cause issues with the
221  // collision detection algorithms (i.e GJK), but probably not.
222 
223  return bbox;
224 }
225 } // namespace details
226 
233 template<typename IT>
234 BoundingBox minimum_area_bounding_box(const IT begin, const IT end)
235 {
236  const auto metric_fn = [](const decltype(BoundingBox::size) & pt) -> float32_t
237  {
238  return pt.x * pt.y;
239  };
240  return details::rotating_calipers_impl(begin, end, metric_fn);
241 }
242 
249 template<typename IT>
251 {
252  const auto metric_fn = [](const decltype(BoundingBox::size) & pt) -> float32_t
253  {
254  return pt.x + pt.y;
255  };
256  return details::rotating_calipers_impl(begin, end, metric_fn);
257 }
258 
265 template<typename PointT>
266 BoundingBox minimum_area_bounding_box(std::list<PointT> & list)
267 {
268  const auto last = convex_hull(list);
269  return minimum_area_bounding_box(list.cbegin(), last);
270 }
271 
278 template<typename PointT>
280 {
281  const auto last = convex_hull(list);
282  return minimum_perimeter_bounding_box(list.cbegin(), last);
283 }
284 } // namespace bounding_box
285 } // namespace geometry
286 } // namespace common
287 } // namespace autoware
288 #endif // GEOMETRY__BOUNDING_BOX__ROTATING_CALIPERS_HPP_
autoware::common::geometry::bounding_box::BoundingBox
autoware_auto_msgs::msg::BoundingBox BoundingBox
Definition: bounding_box_common.hpp:38
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:265
autoware::common::geometry::cross_2d
auto cross_2d(const T1 &pt, const T2 &q)
compute p x q = p1 * q2 - p2 * q1
Definition: common_2d.hpp:118
autoware::common::geometry::bounding_box::minimum_area_bounding_box
BoundingBox minimum_area_bounding_box(const IT begin, const IT end)
Compute the minimum area bounding box given a convex hull of points. This function is exposed in case...
Definition: rotating_calipers.hpp:234
autoware::common::geometry::bounding_box::details::compute_corners
void compute_corners(decltype(BoundingBox::corners) &corners, const Point4< IT > &supports, const Point4< PointT > &directions)
given support points and two orthogonal directions, compute corners of bounding box
Definition: bounding_box_common.hpp:111
convex_hull.hpp
This file implements the monotone chain algorithm to compute 2D convex hulls on linked lists of point...
u
DifferentialState u
Definition: kinematic_bicycle.snippet.hpp:29
common_2d.hpp
This file includes common functionality for 2D geometry, such as dot products.
autoware::common::geometry::bounding_box::minimum_perimeter_bounding_box
BoundingBox minimum_perimeter_bounding_box(const IT begin, const IT end)
Compute the minimum perimeter bounding box given a convex hull of points This function is exposed in ...
Definition: rotating_calipers.hpp:250
autoware::common::geometry::bounding_box::details::init_edges
void init_edges(const IT begin, const IT end, const Point4< IT > &support, Point4< PointT > &edges)
Given support points i, find direction of edge: e = P[i+1] - P[i].
Definition: rotating_calipers.hpp:83
autoware::common::geometry::bounding_box::details::update_angles
uint32_t update_angles(const Point4< PointT > &edges, Point4< PointT > &directions)
Find which (next) edge has smallest angle delta to directions, rotate directions.
Definition: rotating_calipers.hpp:48
autoware::common::geometry::bounding_box::details::init_bbox
void init_bbox(const IT begin, const IT end, Point4< IT > &support)
Scan through list to find support points for bounding box oriented in direction of u = P[1] - P[0].
Definition: rotating_calipers.hpp:106
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:131
autoware
This file defines the lanelet2_map_provider_node class.
Definition: quick_sort.hpp:24
autoware::common::geometry::bounding_box::details::rotating_calipers_impl
BoundingBox rotating_calipers_impl(const IT begin, const IT end, const MetricF metric_fn)
Compute the minimum bounding box for a convex hull using the rotating calipers method....
Definition: rotating_calipers.hpp:154
autoware::common::types::FEPS
constexpr float32_t FEPS
arbitrary small constant: 1.0E-6F
Definition: types.hpp:46
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::norm_2d
auto norm_2d(const T &pt)
get magnitude of x and y components:
Definition: common_2d.hpp:278
bounding_box_common.hpp
Common functionality for bounding box computation algorithms.
autoware::common::types::float32_t
float float32_t
Definition: types.hpp:36
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:145
autoware::common::geometry::rotate_2d
void rotate_2d(T &pt, const float32_t cos_th, const float32_t sin_th)
rotate point given precomputed sin and cos
Definition: common_2d.hpp:236
autoware::common::geometry::bounding_box::details::base_type
typename std::remove_cv< typename std::remove_reference< T >::type >::type base_type
Templated alias for getting a type without CV or reference qualification.
Definition: bounding_box_common.hpp:72
autoware::common::geometry::bounding_box::details::size_2d
void GEOMETRY_PUBLIC size_2d(const decltype(BoundingBox::corners) &corners, geometry_msgs::msg::Point32 &ret)
Compute length, width, area of bounding box.
Definition: bounding_box.cpp:39
autoware::common::geometry::bounding_box::details::Point4
std::array< PointT, 4U > Point4
Templated alias for an array of 4 objects of type PointT.
Definition: bounding_box_common.hpp:76
autoware::common::geometry::bounding_box::details::finalize_box
void GEOMETRY_PUBLIC finalize_box(const decltype(BoundingBox::corners) &corners, BoundingBox &box)
Computes centroid and orientation of a box given corners.
Definition: bounding_box.cpp:47
get_open_port.end
end
Definition: scripts/get_open_port.py:23