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(
57  norm_2d(edges[idx]) * norm_2d(
58  directions[idx]), std::numeric_limits<float32_t>::epsilon());
59  const float32_t cos_th = dot_2d(edges[idx], directions[idx]) / edge_dir_mag;
60  if (cos_th > best_cos_th) {
61  best_cos_th = cos_th;
62  best_edge_dir_mag = edge_dir_mag;
63  advance_idx = idx;
64  }
65  }
66 
67  // update directions by smallest angle
68  const float32_t sin_th =
69  cross_2d(directions[advance_idx], edges[advance_idx]) / best_edge_dir_mag;
70  for (uint32_t idx = 0U; idx < edges.size(); ++idx) {
71  rotate_2d(directions[idx], best_cos_th, sin_th);
72  }
73 
74  return advance_idx;
75 }
76 
84 template<typename IT, typename PointT>
86  const IT begin,
87  const IT end,
88  const Point4<IT> & support,
89  Point4<PointT> & edges)
90 {
91  for (uint32_t idx = 0U; idx < support.size(); ++idx) {
92  auto tmp_it = support[idx];
93  ++tmp_it;
94  const PointT & q = (tmp_it == end) ? *begin : *tmp_it;
95  edges[idx] = minus_2d(q, *support[idx]);
96  }
97 }
98 
107 template<typename IT>
108 void init_bbox(const IT begin, const IT end, Point4<IT> & support)
109 {
110  // compute initial orientation using first two points
111  auto pt_it = begin;
112  ++pt_it;
113  const auto u = minus_2d(*pt_it, *begin);
114  support[0U] = begin;
115  std::array<float32_t, 3U> metric{{
116  -std::numeric_limits<float32_t>::max(),
117  -std::numeric_limits<float32_t>::max(),
118  std::numeric_limits<float32_t>::max()
119  }};
120  // track u * p, fabsf(u x p), and -u * p
121  for (pt_it = begin; pt_it != end; ++pt_it) {
122  // check points against orientation for run_ptr
123  const auto & pt = *pt_it;
124  // u * p
125  const float32_t up = dot_2d(u, pt);
126  if (up > metric[0U]) {
127  metric[0U] = up;
128  support[1U] = pt_it;
129  }
130  // -u * p
131  if (up < metric[2U]) {
132  metric[2U] = up;
133  support[3U] = pt_it;
134  }
135  // u x p
136  const float32_t uxp = cross_2d(u, pt);
137  if (uxp > metric[1U]) {
138  metric[1U] = uxp;
139  support[2U] = pt_it;
140  }
141  }
142 }
155 template<typename IT, typename MetricF>
156 BoundingBox rotating_calipers_impl(const IT begin, const IT end, const MetricF metric_fn)
157 {
158  using PointT = base_type<decltype(*begin)>;
159  // sanity check TODO(c.ho) more checks (up to n = 2?)
160  if (begin == end) {
161  throw std::domain_error("Malformed list, size = " + std::to_string(std::distance(begin, end)));
162  }
163  // initial scan to get anchor points
164  Point4<IT> support;
165  init_bbox(begin, end, support);
166  // initialize directions accordingly
167  Point4<PointT> directions;
168  { // I just don't want the temporary variable floating around
169  auto tmp = support[0U];
170  ++tmp;
171  directions[0U] = minus_2d(*tmp, *support[0U]);
172  directions[1U] = get_normal(directions[0U]);
173  directions[2U] = minus_2d(directions[0U]);
174  directions[3U] = minus_2d(directions[1U]);
175  }
176  // initialize edges
177  Point4<PointT> edges;
178  init_edges(begin, end, support, edges);
179  // size of initial guess
180  BoundingBox bbox;
181  decltype(BoundingBox::corners) best_corners;
182  compute_corners(best_corners, support, directions);
183  size_2d(best_corners, bbox.size);
184  bbox.value = metric_fn(bbox.size);
185  // rotating calipers step: incrementally advance, update angles, points, compute area
186  for (auto it = begin; it != end; ++it) {
187  // find smallest angle to next, update directions
188  const uint32_t advance_idx = update_angles(edges, directions);
189  // recompute area
190  decltype(BoundingBox::corners) corners;
191  compute_corners(corners, support, directions);
192  decltype(BoundingBox::size) tmp_size;
193  size_2d(corners, tmp_size);
194  const float32_t tmp_value = metric_fn(tmp_size);
195  // update best if necessary
196  if (tmp_value < bbox.value) {
197  // corners: memcpy is fine since I know corners and best_corners are distinct
198  (void)std::memcpy(&best_corners[0U], &corners[0U], sizeof(corners));
199  // size
200  bbox.size = tmp_size;
201  bbox.value = tmp_value;
202  }
203  // Step to next iteration of calipers
204  {
205  // update smallest support
206  auto next_it = support[advance_idx];
207  ++next_it;
208  const auto run_it = (end == next_it) ? begin : next_it;
209  support[advance_idx] = run_it;
210  // update edges
211  next_it = run_it;
212  ++next_it;
213  const PointT & next = (end == next_it) ? (*begin) : (*next_it);
214  edges[advance_idx] = minus_2d(next, *run_it);
215  }
216  }
217 
218  finalize_box(best_corners, bbox);
219 
220  // TODO(christopher.ho) check if one of the sizes is too small, fuzz corner 1 and 2
221  // This doesn't cause any issues now, it shouldn't happen in practice, and even if it did,
222  // it would probably get smoothed out by prediction. But, this could cause issues with the
223  // collision detection algorithms (i.e GJK), but probably not.
224 
225  return bbox;
226 }
227 } // namespace details
228 
235 template<typename IT>
236 BoundingBox minimum_area_bounding_box(const IT begin, const IT end)
237 {
238  const auto metric_fn = [](const decltype(BoundingBox::size) & pt) -> float32_t
239  {
240  return pt.x * pt.y;
241  };
242  return details::rotating_calipers_impl(begin, end, metric_fn);
243 }
244 
251 template<typename IT>
253 {
254  const auto metric_fn = [](const decltype(BoundingBox::size) & pt) -> float32_t
255  {
256  return pt.x + pt.y;
257  };
258  return details::rotating_calipers_impl(begin, end, metric_fn);
259 }
260 
267 template<typename PointT>
268 BoundingBox minimum_area_bounding_box(std::list<PointT> & list)
269 {
270  const auto last = convex_hull(list);
271  return minimum_area_bounding_box(list.cbegin(), last);
272 }
273 
280 template<typename PointT>
282 {
283  const auto last = convex_hull(list);
284  return minimum_perimeter_bounding_box(list.cbegin(), last);
285 }
286 } // namespace bounding_box
287 } // namespace geometry
288 } // namespace common
289 } // namespace autoware
290 #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:281
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:134
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:236
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:252
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:85
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:108
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:147
autoware
This file defines the lanelet2_map_provider_node class.
Definition: quick_sort.hpp:24
autoware::localization::ndt::next
SerializedNDTMapPoint next(std::array< T, 9U > &pc_its)
Get the next voxel representation from the iterators of a serialized ndt map.
Definition: ndt_map.hpp:263
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:156
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:294
bounding_box_common.hpp
Common functionality for bounding box computation algorithms.
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:161
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:252
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:55
get_open_port.end
end
Definition: scripts/get_open_port.py:23