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