1 /**********************************************************************
2 * File: rect.h (Formerly box.h)
3 * Description: Bounding box class definition.
4 * Author: Phil Cheatle
5 *
6 * (C) Copyright 1991, Hewlett-Packard Ltd.
7 ** Licensed under the Apache License, Version 2.0 (the "License");
8 ** you may not use this file except in compliance with the License.
9 ** You may obtain a copy of the License at
10 ** http://www.apache.org/licenses/LICENSE-2.0
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 *
17 **********************************************************************/
18
19 #ifndef RECT_H
20 #define RECT_H
21
22 #include "points.h" // for ICOORD, FCOORD
23 #include "scrollview.h" // for ScrollView, ScrollView::Color
24 #include "tesstypes.h" // for TDimension
25 #include "tprintf.h" // for tprintf
26
27 #include <tesseract/export.h> // for DLLSYM
28
29 #include <algorithm> // for std::max, std::min
30 #include <cmath> // for std::ceil, std::floor
31 #include <cstdint> // for INT16_MAX
32 #include <cstdio> // for FILE
33 #include <string> // for std::string
34
35 namespace tesseract {
36
37 class TESS_API TBOX { // bounding box
38 public:
TBOX()39 TBOX()
40 : // empty constructor making a null box
41 bot_left(INT16_MAX, INT16_MAX)
42 , top_right(-INT16_MAX, -INT16_MAX) {}
43
44 TBOX( // constructor
45 const ICOORD pt1, // one corner
46 const ICOORD pt2); // the other corner
47
48 //*********************************************************************
49 // TBOX::TBOX() Constructor from 4 integer values.
50 // Note: It is caller's responsibility to provide values
51 // in the right order.
52 //*********************************************************************
TBOX(TDimension left,TDimension bottom,TDimension right,TDimension top)53 TBOX( // constructor
54 TDimension left, TDimension bottom, TDimension right, TDimension top)
55 : bot_left(left, bottom), top_right(right, top) {}
56
57 TBOX( // box around FCOORD
58 const FCOORD pt);
59
null_box()60 bool null_box() const { // Is box null
61 return ((left() >= right()) || (top() <= bottom()));
62 }
63
64 bool operator==(const TBOX &other) const {
65 return bot_left == other.bot_left && top_right == other.top_right;
66 }
67
top()68 TDimension top() const { // coord of top
69 return top_right.y();
70 }
set_top(int y)71 void set_top(int y) {
72 top_right.set_y(y);
73 }
74
bottom()75 TDimension bottom() const { // coord of bottom
76 return bot_left.y();
77 }
set_bottom(int y)78 void set_bottom(int y) {
79 bot_left.set_y(y);
80 }
81
left()82 TDimension left() const { // coord of left
83 return bot_left.x();
84 }
set_left(int x)85 void set_left(int x) {
86 bot_left.set_x(x);
87 }
88
right()89 TDimension right() const { // coord of right
90 return top_right.x();
91 }
set_right(int x)92 void set_right(int x) {
93 top_right.set_x(x);
94 }
x_middle()95 int x_middle() const {
96 return (bot_left.x() + top_right.x()) / 2;
97 }
y_middle()98 int y_middle() const {
99 return (bot_left.y() + top_right.y()) / 2;
100 }
101
botleft()102 const ICOORD &botleft() const { // access function
103 return bot_left;
104 }
105
botright()106 ICOORD botright() const { // ~ access function
107 return ICOORD(top_right.x(), bot_left.y());
108 }
109
topleft()110 ICOORD topleft() const { // ~ access function
111 return ICOORD(bot_left.x(), top_right.y());
112 }
113
topright()114 const ICOORD &topright() const { // access function
115 return top_right;
116 }
117
height()118 TDimension height() const { // how high is it?
119 if (!null_box()) {
120 return top_right.y() - bot_left.y();
121 } else {
122 return 0;
123 }
124 }
125
width()126 TDimension width() const { // how high is it?
127 if (!null_box()) {
128 return top_right.x() - bot_left.x();
129 } else {
130 return 0;
131 }
132 }
133
area()134 int32_t area() const { // what is the area?
135 if (!null_box()) {
136 return width() * height();
137 } else {
138 return 0;
139 }
140 }
141
142 // Pads the box on either side by the supplied x,y pad amounts.
143 // NO checks for exceeding any bounds like 0 or an image size.
pad(int xpad,int ypad)144 void pad(int xpad, int ypad) {
145 ICOORD pad(xpad, ypad);
146 bot_left -= pad;
147 top_right += pad;
148 }
149
move_bottom_edge(const TDimension y)150 void move_bottom_edge( // move one edge
151 const TDimension y) { // by +/- y
152 bot_left += ICOORD(0, y);
153 }
154
move_left_edge(const TDimension x)155 void move_left_edge( // move one edge
156 const TDimension x) { // by +/- x
157 bot_left += ICOORD(x, 0);
158 }
159
move_right_edge(const TDimension x)160 void move_right_edge( // move one edge
161 const TDimension x) { // by +/- x
162 top_right += ICOORD(x, 0);
163 }
164
move_top_edge(const TDimension y)165 void move_top_edge( // move one edge
166 const TDimension y) { // by +/- y
167 top_right += ICOORD(0, y);
168 }
169
move(const ICOORD vec)170 void move( // move box
171 const ICOORD vec) { // by vector
172 bot_left += vec;
173 top_right += vec;
174 }
175
move(const FCOORD vec)176 void move( // move box
177 const FCOORD vec) { // by float vector
178 bot_left.set_x(static_cast<TDimension>(std::floor(bot_left.x() + vec.x())));
179 // round left
180 bot_left.set_y(static_cast<TDimension>(std::floor(bot_left.y() + vec.y())));
181 // round down
182 top_right.set_x(static_cast<TDimension>(std::ceil(top_right.x() + vec.x())));
183 // round right
184 top_right.set_y(static_cast<TDimension>(std::ceil(top_right.y() + vec.y())));
185 // round up
186 }
187
scale(const float f)188 void scale( // scale box
189 const float f) { // by multiplier
190 // round left
191 bot_left.set_x(static_cast<TDimension>(std::floor(bot_left.x() * f)));
192 // round down
193 bot_left.set_y(static_cast<TDimension>(std::floor(bot_left.y() * f)));
194 // round right
195 top_right.set_x(static_cast<TDimension>(std::ceil(top_right.x() * f)));
196 // round up
197 top_right.set_y(static_cast<TDimension>(std::ceil(top_right.y() * f)));
198 }
scale(const FCOORD vec)199 void scale( // scale box
200 const FCOORD vec) { // by float vector
201 bot_left.set_x(static_cast<TDimension>(std::floor(bot_left.x() * vec.x())));
202 bot_left.set_y(static_cast<TDimension>(std::floor(bot_left.y() * vec.y())));
203 top_right.set_x(static_cast<TDimension>(std::ceil(top_right.x() * vec.x())));
204 top_right.set_y(static_cast<TDimension>(std::ceil(top_right.y() * vec.y())));
205 }
206
207 // rotate doesn't enlarge the box - it just rotates the bottom-left
208 // and top-right corners. Use rotate_large if you want to guarantee
209 // that all content is contained within the rotated box.
rotate(const FCOORD & vec)210 void rotate(const FCOORD &vec) { // by vector
211 bot_left.rotate(vec);
212 top_right.rotate(vec);
213 *this = TBOX(bot_left, top_right);
214 }
215 // rotate_large constructs the containing bounding box of all 4
216 // corners after rotating them. It therefore guarantees that all
217 // original content is contained within, but also slightly enlarges the box.
218 void rotate_large(const FCOORD &vec);
219
220 bool contains( // is pt inside box
221 const FCOORD pt) const;
222
223 bool contains( // is box inside box
224 const TBOX &box) const;
225
226 bool overlap( // do boxes overlap
227 const TBOX &box) const;
228
229 bool major_overlap( // do boxes overlap more than half
230 const TBOX &box) const;
231
232 // Do boxes overlap on x axis.
233 bool x_overlap(const TBOX &box) const;
234
235 // Return the horizontal gap between the boxes. If the boxes
236 // overlap horizontally then the return value is negative, indicating
237 // the amount of the overlap.
x_gap(const TBOX & box)238 int x_gap(const TBOX &box) const {
239 return std::max(bot_left.x(), box.bot_left.x()) - std::min(top_right.x(), box.top_right.x());
240 }
241
242 // Return the vertical gap between the boxes. If the boxes
243 // overlap vertically then the return value is negative, indicating
244 // the amount of the overlap.
y_gap(const TBOX & box)245 int y_gap(const TBOX &box) const {
246 return std::max(bot_left.y(), box.bot_left.y()) - std::min(top_right.y(), box.top_right.y());
247 }
248
249 // Do boxes overlap on x axis by more than
250 // half of the width of the narrower box.
251 bool major_x_overlap(const TBOX &box) const;
252
253 // Do boxes overlap on y axis.
254 bool y_overlap(const TBOX &box) const;
255
256 // Do boxes overlap on y axis by more than
257 // half of the height of the shorter box.
258 bool major_y_overlap(const TBOX &box) const;
259
260 // fraction of current box's area covered by other
261 double overlap_fraction(const TBOX &box) const;
262
263 // fraction of the current box's projected area covered by the other's
264 double x_overlap_fraction(const TBOX &box) const;
265
266 // fraction of the current box's projected area covered by the other's
267 double y_overlap_fraction(const TBOX &box) const;
268
269 // Returns true if the boxes are almost equal on x axis.
270 bool x_almost_equal(const TBOX &box, int tolerance) const;
271
272 // Returns true if the boxes are almost equal
273 bool almost_equal(const TBOX &box, int tolerance) const;
274
275 TBOX intersection( // shared area box
276 const TBOX &box) const;
277
278 TBOX bounding_union( // box enclosing both
279 const TBOX &box) const;
280
281 // Sets the box boundaries to the given coordinates.
set_to_given_coords(int x_min,int y_min,int x_max,int y_max)282 void set_to_given_coords(int x_min, int y_min, int x_max, int y_max) {
283 bot_left.set_x(x_min);
284 bot_left.set_y(y_min);
285 top_right.set_x(x_max);
286 top_right.set_y(y_max);
287 }
288
print()289 void print() const { // print
290 tprintf("Bounding box=(%d,%d)->(%d,%d)\n", left(), bottom(), right(), top());
291 }
292 // Appends the bounding box as (%d,%d)->(%d,%d) to a string.
293 void print_to_str(std::string &str) const;
294
295 #ifndef GRAPHICS_DISABLED
plot(ScrollView * fd)296 void plot( // use current settings
297 ScrollView *fd) const { // where to paint
298 fd->Rectangle(bot_left.x(), bot_left.y(), top_right.x(), top_right.y());
299 }
300
301 void plot( // paint box
302 ScrollView *fd, // where to paint
303 ScrollView::Color fill_colour, // colour for inside
304 ScrollView::Color border_colour) const; // colour for border
305 #endif
306 // Writes to the given file. Returns false in case of error.
307 bool Serialize(FILE *fp) const;
308 bool Serialize(TFile *fp) const;
309
310 // Reads from the given file. Returns false in case of error.
311 // If swap is true, assumes a big/little-endian swap is needed.
312 bool DeSerialize(bool swap, FILE *fp);
313 bool DeSerialize(TFile *fp);
314
315 friend TBOX &operator+=(TBOX &, const TBOX &);
316 // in place union
317 friend TBOX &operator&=(TBOX &, const TBOX &);
318 // in place intersection
319
320 private:
321 ICOORD bot_left; // bottom left corner
322 ICOORD top_right; // top right corner
323 };
324
325 /**********************************************************************
326 * TBOX::TBOX() Constructor from 1 FCOORD
327 *
328 **********************************************************************/
329
TBOX(const FCOORD pt)330 inline TBOX::TBOX( // constructor
331 const FCOORD pt // floating centre
332 ) {
333 bot_left =
334 ICOORD(static_cast<TDimension>(std::floor(pt.x())), static_cast<TDimension>(std::floor(pt.y())));
335 top_right =
336 ICOORD(static_cast<TDimension>(std::ceil(pt.x())), static_cast<TDimension>(std::ceil(pt.y())));
337 }
338
339 /**********************************************************************
340 * TBOX::contains() Is point within box
341 *
342 **********************************************************************/
343
contains(const FCOORD pt)344 inline bool TBOX::contains(const FCOORD pt) const {
345 return ((pt.x() >= bot_left.x()) && (pt.x() <= top_right.x()) && (pt.y() >= bot_left.y()) &&
346 (pt.y() <= top_right.y()));
347 }
348
349 /**********************************************************************
350 * TBOX::contains() Is box within box
351 *
352 **********************************************************************/
353
contains(const TBOX & box)354 inline bool TBOX::contains(const TBOX &box) const {
355 return (contains(box.bot_left) && contains(box.top_right));
356 }
357
358 /**********************************************************************
359 * TBOX::overlap() Do two boxes overlap?
360 *
361 **********************************************************************/
362
overlap(const TBOX & box)363 inline bool TBOX::overlap( // do boxes overlap
364 const TBOX &box) const {
365 return ((box.bot_left.x() <= top_right.x()) && (box.top_right.x() >= bot_left.x()) &&
366 (box.bot_left.y() <= top_right.y()) && (box.top_right.y() >= bot_left.y()));
367 }
368
369 /**********************************************************************
370 * TBOX::major_overlap() Do two boxes overlap by at least half of the smallest?
371 *
372 **********************************************************************/
373
major_overlap(const TBOX & box)374 inline bool TBOX::major_overlap( // Do boxes overlap more that half.
375 const TBOX &box) const {
376 int overlap = std::min(box.top_right.x(), top_right.x());
377 overlap -= std::max(box.bot_left.x(), bot_left.x());
378 overlap += overlap;
379 if (overlap < std::min(box.width(), width())) {
380 return false;
381 }
382 overlap = std::min(box.top_right.y(), top_right.y());
383 overlap -= std::max(box.bot_left.y(), bot_left.y());
384 overlap += overlap;
385 if (overlap < std::min(box.height(), height())) {
386 return false;
387 }
388 return true;
389 }
390
391 /**********************************************************************
392 * TBOX::overlap_fraction() Fraction of area covered by the other box
393 *
394 **********************************************************************/
395
overlap_fraction(const TBOX & box)396 inline double TBOX::overlap_fraction(const TBOX &box) const {
397 double fraction = 0.0;
398 if (this->area()) {
399 fraction = this->intersection(box).area() * 1.0 / this->area();
400 }
401 return fraction;
402 }
403
404 /**********************************************************************
405 * TBOX::x_overlap() Do two boxes overlap on x-axis
406 *
407 **********************************************************************/
408
x_overlap(const TBOX & box)409 inline bool TBOX::x_overlap(const TBOX &box) const {
410 return ((box.bot_left.x() <= top_right.x()) && (box.top_right.x() >= bot_left.x()));
411 }
412
413 /**********************************************************************
414 * TBOX::major_x_overlap() Do two boxes overlap by more than half the
415 * width of the narrower box on the x-axis
416 *
417 **********************************************************************/
418
major_x_overlap(const TBOX & box)419 inline bool TBOX::major_x_overlap(const TBOX &box) const {
420 TDimension overlap = box.width();
421 if (this->left() > box.left()) {
422 overlap -= this->left() - box.left();
423 }
424 if (this->right() < box.right()) {
425 overlap -= box.right() - this->right();
426 }
427 return (overlap >= box.width() / 2 || overlap >= this->width() / 2);
428 }
429
430 /**********************************************************************
431 * TBOX::y_overlap() Do two boxes overlap on y-axis
432 *
433 **********************************************************************/
434
y_overlap(const TBOX & box)435 inline bool TBOX::y_overlap(const TBOX &box) const {
436 return ((box.bot_left.y() <= top_right.y()) && (box.top_right.y() >= bot_left.y()));
437 }
438
439 /**********************************************************************
440 * TBOX::major_y_overlap() Do two boxes overlap by more than half the
441 * height of the shorter box on the y-axis
442 *
443 **********************************************************************/
444
major_y_overlap(const TBOX & box)445 inline bool TBOX::major_y_overlap(const TBOX &box) const {
446 TDimension overlap = box.height();
447 if (this->bottom() > box.bottom()) {
448 overlap -= this->bottom() - box.bottom();
449 }
450 if (this->top() < box.top()) {
451 overlap -= box.top() - this->top();
452 }
453 return (overlap >= box.height() / 2 || overlap >= this->height() / 2);
454 }
455
456 /**********************************************************************
457 * TBOX::x_overlap_fraction() Calculates the horizontal overlap of the
458 * given boxes as a fraction of this boxes
459 * width.
460 *
461 **********************************************************************/
462
x_overlap_fraction(const TBOX & other)463 inline double TBOX::x_overlap_fraction(const TBOX &other) const {
464 int low = std::max(left(), other.left());
465 int high = std::min(right(), other.right());
466 int width = right() - left();
467 if (width == 0) {
468 int x = left();
469 if (other.left() <= x && x <= other.right()) {
470 return 1.0;
471 } else {
472 return 0.0;
473 }
474 } else {
475 return std::max(0.0, static_cast<double>(high - low) / width);
476 }
477 }
478
479 /**********************************************************************
480 * TBOX::y_overlap_fraction() Calculates the vertical overlap of the
481 * given boxes as a fraction of this boxes
482 * height.
483 *
484 **********************************************************************/
485
y_overlap_fraction(const TBOX & other)486 inline double TBOX::y_overlap_fraction(const TBOX &other) const {
487 int low = std::max(bottom(), other.bottom());
488 int high = std::min(top(), other.top());
489 int height = top() - bottom();
490 if (height == 0) {
491 int y = bottom();
492 if (other.bottom() <= y && y <= other.top()) {
493 return 1.0;
494 } else {
495 return 0.0;
496 }
497 } else {
498 return std::max(0.0, static_cast<double>(high - low) / height);
499 }
500 }
501
502 } // namespace tesseract
503
504 #endif
505