Ion
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
binpacker.cc
Go to the documentation of this file.
1 
18 #include "ion/text/binpacker.h"
19 
20 #include <algorithm>
21 #include <limits>
22 
23 #include "base/integral_types.h"
24 #include "ion/base/logging.h"
25 
26 namespace ion {
27 namespace text {
28 
29 using math::Vector2ui;
30 
32 
38 
39 
40 class BinPacker::Skyline {
41  public:
43  typedef BinPacker::Rectangle Rectangle;
44 
45  explicit Skyline(const Vector2ui& bin_size);
46  explicit Skyline(const Skyline& from) // Copy constructor.
47  : bin_size_(from.bin_size_),
48  levels_(from.levels_) {}
49 
50  const Vector2ui& GetBinSize() const { return bin_size_; }
51 
54  bool Insert(Rectangle* rect);
55 
56  private:
58  struct Level {
59  Level() : x(0), y(0), width(0) {}
60  Level(int x_in, int y_in, int width_in)
61  : x(x_in), y(y_in), width(width_in) {}
62 
63  int32 x; // Leftmost X position.
64  int32 y; // Y coordinate of the Skyline level.
65  int32 width; // Skyline width.
66  };
67 
71  bool FindLevel(Rectangle* rect, size_t* level_index) const;
72 
76  void AddLevel(size_t index, const Rectangle& rect);
77 
81  bool RectangleFits(size_t level_index, const Vector2ui& size,
82  int32* y) const;
83 
85  void MergeLevels();
86 
87  const Vector2ui bin_size_;
88  std::vector<Level> levels_;
89 };
90 
91 BinPacker::Skyline::Skyline(const Vector2ui& bin_size)
92  : bin_size_(bin_size) {
94  levels_.push_back(Level(0, 0, bin_size[0]));
95 }
96 
97 bool BinPacker::Skyline::Insert(Rectangle* rect) {
98  size_t level_index;
99  if (FindLevel(rect, &level_index)) {
100  AddLevel(level_index, *rect);
101  return true;
102  }
103  return false;
104 }
105 
106 bool BinPacker::Skyline::FindLevel(Rectangle* rect, size_t* level_index) const {
107  size_t best_index = base::kInvalidIndex;
108  int32 best_width = std::numeric_limits<int32>::max();
109  int32 best_height = std::numeric_limits<int32>::max();
110 
113  const size_t num_levels = levels_.size();
114  for (size_t i = 0; i < num_levels; ++i) {
115  int32 y;
116  if (RectangleFits(i, rect->size, &y)) {
117  const Level& level = levels_[i];
118  const int32 height = y + static_cast<int32>(rect->size[1]);
119  if (height < best_height ||
120  (height == best_height && level.width < best_width)) {
121  best_height = height;
122  best_index = i;
123  best_width = level.width;
124  rect->bottom_left.Set(level.x, y);
125  }
126  }
127  }
128  if (best_index == base::kInvalidIndex) {
129  return false;
130  } else {
131  *level_index = best_index;
132  return true;
133  }
134 }
135 
136 void BinPacker::Skyline::AddLevel(size_t index, const Rectangle& rect) {
137  const Level level(rect.bottom_left[0],
138  rect.bottom_left[1] + rect.size[1],
139  rect.size[0]);
140  levels_.insert(levels_.begin() + index, level);
141 
142  DCHECK_LE(level.x + level.width, static_cast<int32>(bin_size_[0]));
143  DCHECK_LE(level.y, static_cast<int32>(bin_size_[1]));
144 
145  for (size_t i = index + 1; i < levels_.size(); ++i) {
146  const Level& prev_level = levels_[i - 1];
147  Level& cur_level = levels_[i];
148  DCHECK_LE(prev_level.x, cur_level.x);
149  const int x_diff = prev_level.x + prev_level.width - cur_level.x;
150  if (x_diff <= 0)
151  break;
152  cur_level.x += x_diff;
153  cur_level.width -= x_diff;
154  if (cur_level.width > 0)
155  break;
156  levels_.erase(levels_.begin() + i);
157  --i;
158  }
159  MergeLevels();
160 }
161 
162 bool BinPacker::Skyline::RectangleFits(
163  size_t level_index, const Vector2ui& size, int32* y) const {
164  const int x = levels_[level_index].x;
165  if (x + size[0] > bin_size_[0])
166  return false;
167 
168  int width_remaining = size[0];
169  size_t i = level_index;
170  *y = levels_[level_index].y;
171  while (width_remaining > 0) {
172  DCHECK_LT(i, levels_.size());
173  const Level& level = levels_[i];
174  *y = std::max(*y, level.y);
175  if (*y + size[1] > bin_size_[1])
176  return false;
177  width_remaining -= level.width;
178  ++i;
179  }
180  return true;
181 }
182 
183 void BinPacker::Skyline::MergeLevels() {
184  for (size_t i = 1; i < levels_.size(); ++i) {
185  if (levels_[i - 1].y == levels_[i].y) {
186  levels_[i - 1].width += levels_[i].width;
187  levels_.erase(levels_.begin() + i);
188  --i;
189  }
190  }
191 }
192 
194 
199 
200 
201 BinPacker::BinPacker() : num_rectangles_packed_(0) {}
202 
203 BinPacker::BinPacker(const BinPacker& from) { *this = from; }
204 
206 
208  rectangles_ = from.rectangles_;
209  if (from.skyline_.get())
210  skyline_.reset(new Skyline(*from.skyline_));
211  num_rectangles_packed_ = from.num_rectangles_packed_;
212  return *this;
213 }
214 
215 bool BinPacker::Pack(const Vector2ui& bin_size) {
216  if (!skyline_.get() || skyline_->GetBinSize() != bin_size) {
217  skyline_.reset(new Skyline(bin_size));
218  num_rectangles_packed_ = 0;
219  }
220 
223  for (size_t i = num_rectangles_packed_; i < rectangles_.size(); ++i) {
225  if (!skyline_->Insert(&rectangles_[i]))
226  break;
227  ++num_rectangles_packed_;
228  }
229  return num_rectangles_packed_ == rectangles_.size();
230 }
231 
232 } // namespace text
233 } // namespace ion
BinPacker()
BinPacker functions.
Definition: binpacker.cc:201
int level
std::string text
const size_t kInvalidIndex
kInvalidIndex is a size_t value that is very unlikely to be a valid index.
Definition: invalid.cc:23
bool Pack(const math::Vector2ui &bin_size)
Tries to pack all of the rectangles into a bin of the given size.
Definition: binpacker.cc:215
This class implements generic 2D bin-packing using a modified version of the Skyline Bottom-Left algo...
Definition: binpacker.h:36
BinPacker & operator=(const BinPacker &from)
Definition: binpacker.cc:207
Copyright 2016 Google Inc.
int width
#define DCHECK_LE(val1, val2)
Definition: logging.h:334
#define DCHECK_LT(val1, val2)
Definition: logging.h:335