Zach Reizner | 15c9ffa | 2015-03-31 17:56:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2015 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
Haixia Shi | aa2f4a5 | 2015-11-02 10:54:29 -0800 | [diff] [blame] | 17 | #include "separate_rects.h" |
Zach Reizner | 15c9ffa | 2015-03-31 17:56:36 -0700 | [diff] [blame] | 18 | #include <algorithm> |
| 19 | #include <assert.h> |
| 20 | #include <iostream> |
| 21 | #include <map> |
| 22 | #include <set> |
| 23 | #include <utility> |
| 24 | #include <vector> |
| 25 | |
Haixia Shi | aa2f4a5 | 2015-11-02 10:54:29 -0800 | [diff] [blame] | 26 | namespace separate_rects { |
Zach Reizner | 15c9ffa | 2015-03-31 17:56:36 -0700 | [diff] [blame] | 27 | |
| 28 | enum EventType { START, END }; |
| 29 | |
| 30 | template <typename TId, typename TNum> |
| 31 | struct StartedRect { |
| 32 | IdSet<TId> id_set; |
| 33 | TNum left, top, bottom; |
| 34 | |
| 35 | // Note that this->left is not part of the key. That field is only to mark the |
| 36 | // left edge of the rectangle. |
| 37 | bool operator<(const StartedRect<TId, TNum> &rhs) const { |
| 38 | return (top < rhs.top || (top == rhs.top && bottom < rhs.bottom)) || |
| 39 | (top == rhs.top && bottom == rhs.bottom && id_set < rhs.id_set); |
| 40 | } |
| 41 | }; |
| 42 | |
| 43 | template <typename TId, typename TNum> |
| 44 | struct SweepEvent { |
| 45 | EventType type; |
| 46 | union { |
| 47 | TNum x; |
| 48 | TNum y; |
| 49 | }; |
| 50 | |
| 51 | TId rect_id; |
| 52 | |
| 53 | bool operator<(const SweepEvent<TId, TNum> &rhs) const { |
| 54 | return (y < rhs.y || (y == rhs.y && rect_id < rhs.rect_id)); |
| 55 | } |
| 56 | }; |
| 57 | |
| 58 | template <typename TNum> |
| 59 | std::ostream &operator<<(std::ostream &os, const Rect<TNum> &rect) { |
| 60 | return os << rect.bounds[0] << ", " << rect.bounds[1] << ", " |
| 61 | << rect.bounds[2] << ", " << rect.bounds[3]; |
| 62 | } |
| 63 | |
| 64 | template <typename TUInt> |
| 65 | std::ostream &operator<<(std::ostream &os, const IdSet<TUInt> &obj) { |
| 66 | int bits = IdSet<TUInt>::max_elements; |
| 67 | TUInt mask = ((TUInt)0x1) << (bits - 1); |
| 68 | for (int i = 0; i < bits; i++) |
| 69 | os << ((obj.getBits() & (mask >> i)) ? "1" : "0"); |
| 70 | return os; |
| 71 | } |
| 72 | |
| 73 | template <typename TNum, typename TId> |
Haixia Shi | aa2f4a5 | 2015-11-02 10:54:29 -0800 | [diff] [blame] | 74 | void separate_rects(const std::vector<Rect<TNum>> &in, |
Zach Reizner | a281f8f | 2015-10-09 10:02:54 -0700 | [diff] [blame] | 75 | std::vector<RectSet<TId, TNum>> *out) { |
Zach Reizner | 15c9ffa | 2015-03-31 17:56:36 -0700 | [diff] [blame] | 76 | // Overview: |
| 77 | // This algorithm is a line sweep algorithm that travels from left to right. |
| 78 | // The sweep stops at each vertical edge of each input rectangle in sorted |
| 79 | // order of x-coordinate. At each stop, the sweep line is examined in order of |
| 80 | // y-coordinate from top to bottom. Along the way, a running set of rectangle |
| 81 | // IDs is either added to or subtracted from as the top and bottom edges are |
| 82 | // encountered, respectively. At each change of that running set, a copy of |
| 83 | // that set is recorded in along with the the y-coordinate it happened at in a |
| 84 | // list. This list is then interpreted as a sort of vertical cross section of |
| 85 | // our output set of non-overlapping rectangles. Based of the algorithm found |
| 86 | // at: http://stackoverflow.com/a/2755498 |
| 87 | |
Adrian Salido | 6862df5 | 2016-10-20 19:35:55 -0700 | [diff] [blame] | 88 | if (in.size() > IdSet<TId>::max_elements) { |
Zach Reizner | 15c9ffa | 2015-03-31 17:56:36 -0700 | [diff] [blame] | 89 | return; |
| 90 | } |
| 91 | |
| 92 | // Events are when the sweep line encounters the starting or ending edge of |
| 93 | // any input rectangle. |
Zach Reizner | a281f8f | 2015-10-09 10:02:54 -0700 | [diff] [blame] | 94 | std::set<SweepEvent<TId, TNum>> sweep_h_events; // Left or right bounds |
| 95 | std::set<SweepEvent<TId, TNum>> sweep_v_events; // Top or bottom bounds |
Zach Reizner | 15c9ffa | 2015-03-31 17:56:36 -0700 | [diff] [blame] | 96 | |
| 97 | // A started rect is a rectangle whose left, top, bottom edge, and set of |
| 98 | // rectangle IDs is known. The key of this map includes all that information |
| 99 | // (except the left edge is never used to determine key equivalence or |
| 100 | // ordering), |
| 101 | std::map<StartedRect<TId, TNum>, bool> started_rects; |
| 102 | |
| 103 | // This is cleared after every event. Its declaration is here to avoid |
| 104 | // reallocating a vector and its buffers every event. |
Zach Reizner | a281f8f | 2015-10-09 10:02:54 -0700 | [diff] [blame] | 105 | std::vector<std::pair<TNum, IdSet<TId>>> active_regions; |
Zach Reizner | 15c9ffa | 2015-03-31 17:56:36 -0700 | [diff] [blame] | 106 | |
| 107 | // This pass will add rectangle start and end events to be triggered as the |
| 108 | // algorithm sweeps from left to right. |
| 109 | for (TId i = 0; i < in.size(); i++) { |
| 110 | const Rect<TNum> &rect = in[i]; |
Haixia Shi | f3d3603 | 2015-11-02 10:04:49 -0800 | [diff] [blame] | 111 | |
| 112 | // Filter out empty or invalid rects. |
| 113 | if (rect.left >= rect.right || rect.top >= rect.bottom) |
| 114 | continue; |
| 115 | |
Zach Reizner | 15c9ffa | 2015-03-31 17:56:36 -0700 | [diff] [blame] | 116 | SweepEvent<TId, TNum> evt; |
| 117 | evt.rect_id = i; |
| 118 | |
| 119 | evt.type = START; |
| 120 | evt.x = rect.left; |
| 121 | sweep_h_events.insert(evt); |
| 122 | |
| 123 | evt.type = END; |
| 124 | evt.x = rect.right; |
| 125 | sweep_h_events.insert(evt); |
| 126 | } |
| 127 | |
Zach Reizner | a281f8f | 2015-10-09 10:02:54 -0700 | [diff] [blame] | 128 | for (typename std::set<SweepEvent<TId, TNum>>::iterator it = |
Zach Reizner | 15c9ffa | 2015-03-31 17:56:36 -0700 | [diff] [blame] | 129 | sweep_h_events.begin(); |
| 130 | it != sweep_h_events.end(); ++it) { |
| 131 | const SweepEvent<TId, TNum> &h_evt = *it; |
| 132 | const Rect<TNum> &rect = in[h_evt.rect_id]; |
| 133 | |
| 134 | // During this event, we have encountered a vertical starting or ending edge |
| 135 | // of a rectangle so want to append or remove (respectively) that rectangles |
| 136 | // top and bottom from the vertical sweep line. |
| 137 | SweepEvent<TId, TNum> v_evt; |
| 138 | v_evt.rect_id = h_evt.rect_id; |
| 139 | if (h_evt.type == START) { |
| 140 | v_evt.type = START; |
| 141 | v_evt.y = rect.top; |
| 142 | sweep_v_events.insert(v_evt); |
| 143 | |
| 144 | v_evt.type = END; |
| 145 | v_evt.y = rect.bottom; |
| 146 | sweep_v_events.insert(v_evt); |
| 147 | } else { |
| 148 | v_evt.type = START; |
| 149 | v_evt.y = rect.top; |
Zach Reizner | a281f8f | 2015-10-09 10:02:54 -0700 | [diff] [blame] | 150 | typename std::set<SweepEvent<TId, TNum>>::iterator start_it = |
Zach Reizner | 15c9ffa | 2015-03-31 17:56:36 -0700 | [diff] [blame] | 151 | sweep_v_events.find(v_evt); |
| 152 | assert(start_it != sweep_v_events.end()); |
| 153 | sweep_v_events.erase(start_it); |
| 154 | |
| 155 | v_evt.type = END; |
| 156 | v_evt.y = rect.bottom; |
Zach Reizner | a281f8f | 2015-10-09 10:02:54 -0700 | [diff] [blame] | 157 | typename std::set<SweepEvent<TId, TNum>>::iterator end_it = |
Zach Reizner | 15c9ffa | 2015-03-31 17:56:36 -0700 | [diff] [blame] | 158 | sweep_v_events.find(v_evt); |
| 159 | assert(end_it != sweep_v_events.end()); |
| 160 | sweep_v_events.erase(end_it); |
| 161 | } |
| 162 | |
| 163 | // Peeks ahead to see if there are other rectangles sharing a vertical edge |
| 164 | // with the current sweep line. If so, we want to continue marking up the |
| 165 | // sweep line before actually processing the rectangles the sweep line is |
| 166 | // intersecting. |
Zach Reizner | a281f8f | 2015-10-09 10:02:54 -0700 | [diff] [blame] | 167 | typename std::set<SweepEvent<TId, TNum>>::iterator next_it = it; |
Zach Reizner | 15c9ffa | 2015-03-31 17:56:36 -0700 | [diff] [blame] | 168 | ++next_it; |
| 169 | if (next_it != sweep_h_events.end()) { |
| 170 | if (next_it->x == h_evt.x) { |
| 171 | continue; |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | #ifdef RECTS_DEBUG |
| 176 | std::cout << h_evt.x << std::endl; |
| 177 | #endif |
| 178 | |
| 179 | // After the following for loop, active_regions will be a list of |
| 180 | // y-coordinates paired with the set of rectangle IDs that are intersect at |
| 181 | // that y-coordinate (and the current sweep line's x-coordinate). For |
| 182 | // example if the current sweep line were the left edge of a scene with only |
| 183 | // one rectangle of ID 0 and bounds (left, top, right, bottom) == (2, 3, 4, |
| 184 | // 5), active_regions will be [({ 0 }, 3), {}, 5]. |
| 185 | active_regions.clear(); |
| 186 | IdSet<TId> active_set; |
Zach Reizner | a281f8f | 2015-10-09 10:02:54 -0700 | [diff] [blame] | 187 | for (typename std::set<SweepEvent<TId, TNum>>::iterator it = |
Zach Reizner | 15c9ffa | 2015-03-31 17:56:36 -0700 | [diff] [blame] | 188 | sweep_v_events.begin(); |
| 189 | it != sweep_v_events.end(); ++it) { |
| 190 | const SweepEvent<TId, TNum> &v_evt = *it; |
| 191 | |
| 192 | if (v_evt.type == START) { |
| 193 | active_set.add(v_evt.rect_id); |
| 194 | } else { |
| 195 | active_set.subtract(v_evt.rect_id); |
| 196 | } |
| 197 | |
| 198 | if (active_regions.size() > 0 && active_regions.back().first == v_evt.y) { |
| 199 | active_regions.back().second = active_set; |
| 200 | } else { |
| 201 | active_regions.push_back(std::make_pair(v_evt.y, active_set)); |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | #ifdef RECTS_DEBUG |
| 206 | std::cout << "x:" << h_evt.x; |
Zach Reizner | a281f8f | 2015-10-09 10:02:54 -0700 | [diff] [blame] | 207 | for (std::vector<std::pair<TNum, IdSet>>::iterator it = |
Zach Reizner | 15c9ffa | 2015-03-31 17:56:36 -0700 | [diff] [blame] | 208 | active_regions.begin(); |
| 209 | it != active_regions.end(); ++it) { |
| 210 | std::cout << " " << it->first << "(" << it->second << ")" |
| 211 | << ","; |
| 212 | } |
| 213 | std::cout << std::endl; |
| 214 | #endif |
| 215 | |
| 216 | // To determine which started rectangles are ending this event, we make them |
| 217 | // all as false, or unseen during this sweep line. |
| 218 | for (typename std::map<StartedRect<TId, TNum>, bool>::iterator it = |
| 219 | started_rects.begin(); |
| 220 | it != started_rects.end(); ++it) { |
| 221 | it->second = false; |
| 222 | } |
| 223 | |
| 224 | // This for loop will iterate all potential new rectangles and either |
| 225 | // discover it was already started (and then mark it true), or that it is a |
| 226 | // new rectangle and add it to the started rectangles. A started rectangle |
| 227 | // is unique if it has a distinct top, bottom, and set of rectangle IDs. |
| 228 | // This is tricky because a potential rectangle could be encountered here |
| 229 | // that has a non-unique top and bottom, so it shares geometry with an |
| 230 | // already started rectangle, but the set of rectangle IDs differs. In that |
| 231 | // case, we have a new rectangle, and the already existing started rectangle |
| 232 | // will not be marked as seen ("true" in the std::pair) and will get ended |
| 233 | // by the for loop after this one. This is as intended. |
Zach Reizner | a281f8f | 2015-10-09 10:02:54 -0700 | [diff] [blame] | 234 | for (typename std::vector<std::pair<TNum, IdSet<TId>>>::iterator it = |
Zach Reizner | 15c9ffa | 2015-03-31 17:56:36 -0700 | [diff] [blame] | 235 | active_regions.begin(); |
| 236 | it != active_regions.end(); ++it) { |
| 237 | IdSet<TId> region_set = it->second; |
| 238 | |
| 239 | if (region_set.isEmpty()) |
| 240 | continue; |
| 241 | |
| 242 | // An important property of active_regions is that each region where a set |
| 243 | // of rectangles applies is bounded at the bottom by the next (in the |
| 244 | // vector) region's starting y-coordinate. |
Zach Reizner | a281f8f | 2015-10-09 10:02:54 -0700 | [diff] [blame] | 245 | typename std::vector<std::pair<TNum, IdSet<TId>>>::iterator next_it = it; |
Zach Reizner | 15c9ffa | 2015-03-31 17:56:36 -0700 | [diff] [blame] | 246 | ++next_it; |
| 247 | assert(next_it != active_regions.end()); |
| 248 | |
| 249 | TNum region_top = it->first; |
| 250 | TNum region_bottom = next_it->first; |
| 251 | |
| 252 | StartedRect<TId, TNum> rect_key; |
| 253 | rect_key.id_set = region_set; |
| 254 | rect_key.left = h_evt.x; |
| 255 | rect_key.top = region_top; |
| 256 | rect_key.bottom = region_bottom; |
| 257 | |
| 258 | // Remember that rect_key.left is ignored for the purposes of searching |
| 259 | // the started rects. This follows from the fact that a previously started |
| 260 | // rectangle would by definition have a left bound less than the current |
| 261 | // event's x-coordinate. We are interested in continuing the started |
| 262 | // rectangles by marking them seen (true) but we don't know, care, or wish |
| 263 | // to change the left bound at this point. If there are no matching |
| 264 | // rectangles for this region, start a new one and mark it as seen (true). |
| 265 | typename std::map<StartedRect<TId, TNum>, bool>::iterator |
| 266 | started_rect_it = started_rects.find(rect_key); |
| 267 | if (started_rect_it == started_rects.end()) { |
| 268 | started_rects[rect_key] = true; |
| 269 | } else { |
| 270 | started_rect_it->second = true; |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | // This for loop ends all rectangles that were unseen during this event. |
| 275 | // Because this is the first event where we didn't see this rectangle, it's |
| 276 | // right edge is exactly the current event's x-coordinate. With this, we |
| 277 | // have the final piece of information to output this rectangle's geometry |
| 278 | // and set of input rectangle IDs. To end a started rectangle, we erase it |
| 279 | // from the started_rects map and append the completed rectangle to the |
| 280 | // output vector. |
| 281 | for (typename std::map<StartedRect<TId, TNum>, bool>::iterator it = |
| 282 | started_rects.begin(); |
| 283 | it != started_rects.end(); |
| 284 | /* inc in body */) { |
| 285 | if (!it->second) { |
| 286 | const StartedRect<TId, TNum> &proto_rect = it->first; |
| 287 | Rect<TNum> out_rect; |
| 288 | out_rect.left = proto_rect.left; |
| 289 | out_rect.top = proto_rect.top; |
| 290 | out_rect.right = h_evt.x; |
| 291 | out_rect.bottom = proto_rect.bottom; |
| 292 | out->push_back(RectSet<TId, TNum>(proto_rect.id_set, out_rect)); |
| 293 | started_rects.erase(it++); // Also increments out iterator. |
| 294 | |
| 295 | #ifdef RECTS_DEBUG |
| 296 | std::cout << " <" << proto_rect.id_set << "(" << rect << ")" |
| 297 | << std::endl; |
| 298 | #endif |
| 299 | } else { |
| 300 | // Remember this for loop has no built in increment step. We do it here. |
| 301 | ++it; |
| 302 | } |
| 303 | } |
| 304 | } |
| 305 | } |
| 306 | |
Haixia Shi | aa2f4a5 | 2015-11-02 10:54:29 -0800 | [diff] [blame] | 307 | void separate_frects_64(const std::vector<Rect<float>> &in, |
Zach Reizner | a281f8f | 2015-10-09 10:02:54 -0700 | [diff] [blame] | 308 | std::vector<RectSet<uint64_t, float>> *out) { |
Haixia Shi | aa2f4a5 | 2015-11-02 10:54:29 -0800 | [diff] [blame] | 309 | separate_rects(in, out); |
Zach Reizner | a281f8f | 2015-10-09 10:02:54 -0700 | [diff] [blame] | 310 | } |
| 311 | |
Haixia Shi | aa2f4a5 | 2015-11-02 10:54:29 -0800 | [diff] [blame] | 312 | void separate_rects_64(const std::vector<Rect<int>> &in, |
Zach Reizner | a281f8f | 2015-10-09 10:02:54 -0700 | [diff] [blame] | 313 | std::vector<RectSet<uint64_t, int>> *out) { |
Haixia Shi | aa2f4a5 | 2015-11-02 10:54:29 -0800 | [diff] [blame] | 314 | separate_rects(in, out); |
Zach Reizner | 15c9ffa | 2015-03-31 17:56:36 -0700 | [diff] [blame] | 315 | } |
| 316 | |
Haixia Shi | aa2f4a5 | 2015-11-02 10:54:29 -0800 | [diff] [blame] | 317 | } // namespace separate_rects |