Coverage Report

Created: 2026-01-13 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tesseract/src/ccstruct/rect.h
Line
Count
Source
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:
39
  TBOX()
40
      : // empty constructor making a null box
41
209M
      bot_left(INT16_MAX, INT16_MAX)
42
209M
      , 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
  //*********************************************************************
53
  TBOX( // constructor
54
      TDimension left, TDimension bottom, TDimension right, TDimension top)
55
1.84G
      : bot_left(left, bottom), top_right(right, top) {}
56
57
  TBOX( // box around FCOORD
58
      const FCOORD pt);
59
60
381M
  bool null_box() const { // Is box null
61
381M
    return ((left() >= right()) || (top() <= bottom()));
62
381M
  }
63
64
0
  bool operator==(const TBOX &other) const {
65
0
    return bot_left == other.bot_left && top_right == other.top_right;
66
0
  }
67
68
4.26G
  TDimension top() const { // coord of top
69
4.26G
    return top_right.y();
70
4.26G
  }
71
524M
  void set_top(int y) {
72
524M
    top_right.set_y(y);
73
524M
  }
74
75
4.34G
  TDimension bottom() const { // coord of bottom
76
4.34G
    return bot_left.y();
77
4.34G
  }
78
520M
  void set_bottom(int y) {
79
520M
    bot_left.set_y(y);
80
520M
  }
81
82
4.81G
  TDimension left() const { // coord of left
83
4.81G
    return bot_left.x();
84
4.81G
  }
85
429M
  void set_left(int x) {
86
429M
    bot_left.set_x(x);
87
429M
  }
88
89
4.56G
  TDimension right() const { // coord of right
90
4.56G
    return top_right.x();
91
4.56G
  }
92
459M
  void set_right(int x) {
93
459M
    top_right.set_x(x);
94
459M
  }
95
3.09M
  int x_middle() const {
96
3.09M
    return (bot_left.x() + top_right.x()) / 2;
97
3.09M
  }
98
0
  int y_middle() const {
99
0
    return (bot_left.y() + top_right.y()) / 2;
100
0
  }
101
102
4.96M
  const ICOORD &botleft() const { // access function
103
4.96M
    return bot_left;
104
4.96M
  }
105
106
205k
  ICOORD botright() const { // ~ access function
107
205k
    return ICOORD(top_right.x(), bot_left.y());
108
205k
  }
109
110
411k
  ICOORD topleft() const { // ~ access function
111
411k
    return ICOORD(bot_left.x(), top_right.y());
112
411k
  }
113
114
3.00M
  const ICOORD &topright() const { // access function
115
3.00M
    return top_right;
116
3.00M
  }
117
118
45.9M
  TDimension height() const { // how high is it?
119
45.9M
    if (!null_box()) {
120
45.8M
      return top_right.y() - bot_left.y();
121
45.8M
    } else {
122
29.9k
      return 0;
123
29.9k
    }
124
45.9M
  }
125
126
333M
  TDimension width() const { // how wide is it?
127
333M
    if (!null_box()) {
128
332M
      return top_right.x() - bot_left.x();
129
332M
    } else {
130
608k
      return 0;
131
608k
    }
132
333M
  }
133
134
133k
  int32_t area() const { // what is the area?
135
133k
    if (!null_box()) {
136
133k
      return width() * height();
137
133k
    } else {
138
0
      return 0;
139
0
    }
140
133k
  }
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.
144
346k
  void pad(int xpad, int ypad) {
145
346k
    ICOORD pad(xpad, ypad);
146
346k
    bot_left -= pad;
147
346k
    top_right += pad;
148
346k
  }
149
150
  void move_bottom_edge( // move one edge
151
0
      const TDimension y) { // by +/- y
152
0
    bot_left += ICOORD(0, y);
153
0
  }
154
155
  void move_left_edge(   // move one edge
156
0
      const TDimension x) { // by +/- x
157
0
    bot_left += ICOORD(x, 0);
158
0
  }
159
160
  void move_right_edge(  // move one edge
161
0
      const TDimension x) { // by +/- x
162
0
    top_right += ICOORD(x, 0);
163
0
  }
164
165
  void move_top_edge(    // move one edge
166
0
      const TDimension y) { // by +/- y
167
0
    top_right += ICOORD(0, y);
168
0
  }
169
170
  void move(              // move box
171
0
      const ICOORD vec) { // by vector
172
0
    bot_left += vec;
173
0
    top_right += vec;
174
0
  }
175
176
  void move(              // move box
177
0
      const FCOORD vec) { // by float vector
178
0
    bot_left.set_x(static_cast<TDimension>(std::floor(bot_left.x() + vec.x())));
179
0
    // round left
180
0
    bot_left.set_y(static_cast<TDimension>(std::floor(bot_left.y() + vec.y())));
181
0
    // round down
182
0
    top_right.set_x(static_cast<TDimension>(std::ceil(top_right.x() + vec.x())));
183
0
    // round right
184
0
    top_right.set_y(static_cast<TDimension>(std::ceil(top_right.y() + vec.y())));
185
0
    // round up
186
0
  }
187
188
  void scale(          // scale box
189
0
      const float f) { // by multiplier
190
    // round left
191
0
    bot_left.set_x(static_cast<TDimension>(std::floor(bot_left.x() * f)));
192
    // round down
193
0
    bot_left.set_y(static_cast<TDimension>(std::floor(bot_left.y() * f)));
194
    // round right
195
0
    top_right.set_x(static_cast<TDimension>(std::ceil(top_right.x() * f)));
196
    // round up
197
0
    top_right.set_y(static_cast<TDimension>(std::ceil(top_right.y() * f)));
198
0
  }
199
  void scale(             // scale box
200
0
      const FCOORD vec) { // by float vector
201
0
    bot_left.set_x(static_cast<TDimension>(std::floor(bot_left.x() * vec.x())));
202
0
    bot_left.set_y(static_cast<TDimension>(std::floor(bot_left.y() * vec.y())));
203
0
    top_right.set_x(static_cast<TDimension>(std::ceil(top_right.x() * vec.x())));
204
0
    top_right.set_y(static_cast<TDimension>(std::ceil(top_right.y() * vec.y())));
205
0
  }
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.
210
67.2M
  void rotate(const FCOORD &vec) { // by vector
211
67.2M
    bot_left.rotate(vec);
212
67.2M
    top_right.rotate(vec);
213
67.2M
    *this = TBOX(bot_left, top_right);
214
67.2M
  }
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.
238
68.8M
  int x_gap(const TBOX &box) const {
239
68.8M
    return std::max(bot_left.x(), box.bot_left.x()) - std::min(top_right.x(), box.top_right.x());
240
68.8M
  }
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.
245
0
  int y_gap(const TBOX &box) const {
246
0
    return std::max(bot_left.y(), box.bot_left.y()) - std::min(top_right.y(), box.top_right.y());
247
0
  }
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.
282
0
  void set_to_given_coords(int x_min, int y_min, int x_max, int y_max) {
283
0
    bot_left.set_x(x_min);
284
0
    bot_left.set_y(y_min);
285
0
    top_right.set_x(x_max);
286
0
    top_right.set_y(y_max);
287
0
  }
288
289
0
  void print() const { // print
290
0
    tprintf("Bounding box=(%d,%d)->(%d,%d)\n", left(), bottom(), right(), top());
291
0
  }
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
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
330
inline TBOX::TBOX(  // constructor
331
    const FCOORD pt // floating centre
332
0
) {
333
0
  bot_left =
334
0
      ICOORD(static_cast<TDimension>(std::floor(pt.x())), static_cast<TDimension>(std::floor(pt.y())));
335
0
  top_right =
336
0
      ICOORD(static_cast<TDimension>(std::ceil(pt.x())), static_cast<TDimension>(std::ceil(pt.y())));
337
0
}
338
339
/**********************************************************************
340
 * TBOX::contains()  Is point within box
341
 *
342
 **********************************************************************/
343
344
11.3M
inline bool TBOX::contains(const FCOORD pt) const {
345
11.3M
  return ((pt.x() >= bot_left.x()) && (pt.x() <= top_right.x()) && (pt.y() >= bot_left.y()) &&
346
1.42M
          (pt.y() <= top_right.y()));
347
11.3M
}
348
349
/**********************************************************************
350
 * TBOX::contains()  Is box within box
351
 *
352
 **********************************************************************/
353
354
1.09M
inline bool TBOX::contains(const TBOX &box) const {
355
1.09M
  return (contains(box.bot_left) && contains(box.top_right));
356
1.09M
}
357
358
/**********************************************************************
359
 * TBOX::overlap()  Do two boxes overlap?
360
 *
361
 **********************************************************************/
362
363
inline bool TBOX::overlap( // do boxes overlap
364
40.9M
    const TBOX &box) const {
365
40.9M
  return ((box.bot_left.x() <= top_right.x()) && (box.top_right.x() >= bot_left.x()) &&
366
13.6M
          (box.bot_left.y() <= top_right.y()) && (box.top_right.y() >= bot_left.y()));
367
40.9M
}
368
369
/**********************************************************************
370
 * TBOX::major_overlap()  Do two boxes overlap by at least half of the smallest?
371
 *
372
 **********************************************************************/
373
374
inline bool TBOX::major_overlap( // Do boxes overlap more that half.
375
64.3M
    const TBOX &box) const {
376
64.3M
  int overlap = std::min(box.top_right.x(), top_right.x());
377
64.3M
  overlap -= std::max(box.bot_left.x(), bot_left.x());
378
64.3M
  overlap += overlap;
379
64.3M
  if (overlap < std::min(box.width(), width())) {
380
62.3M
    return false;
381
62.3M
  }
382
1.91M
  overlap = std::min(box.top_right.y(), top_right.y());
383
1.91M
  overlap -= std::max(box.bot_left.y(), bot_left.y());
384
1.91M
  overlap += overlap;
385
1.91M
  if (overlap < std::min(box.height(), height())) {
386
92.7k
    return false;
387
92.7k
  }
388
1.81M
  return true;
389
1.91M
}
390
391
/**********************************************************************
392
 * TBOX::overlap_fraction()  Fraction of area covered by the other box
393
 *
394
 **********************************************************************/
395
396
0
inline double TBOX::overlap_fraction(const TBOX &box) const {
397
0
  double fraction = 0.0;
398
0
  if (this->area()) {
399
0
    fraction = this->intersection(box).area() * 1.0 / this->area();
400
0
  }
401
0
  return fraction;
402
0
}
403
404
/**********************************************************************
405
 * TBOX::x_overlap()  Do two boxes overlap on x-axis
406
 *
407
 **********************************************************************/
408
409
0
inline bool TBOX::x_overlap(const TBOX &box) const {
410
0
  return ((box.bot_left.x() <= top_right.x()) && (box.top_right.x() >= bot_left.x()));
411
0
}
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
419
6.65M
inline bool TBOX::major_x_overlap(const TBOX &box) const {
420
6.65M
  TDimension overlap = box.width();
421
6.65M
  if (this->left() > box.left()) {
422
109k
    overlap -= this->left() - box.left();
423
109k
  }
424
6.65M
  if (this->right() < box.right()) {
425
4.95M
    overlap -= box.right() - this->right();
426
4.95M
  }
427
6.65M
  return (overlap >= box.width() / 2 || overlap >= this->width() / 2);
428
6.65M
}
429
430
/**********************************************************************
431
 * TBOX::y_overlap()  Do two boxes overlap on y-axis
432
 *
433
 **********************************************************************/
434
435
780M
inline bool TBOX::y_overlap(const TBOX &box) const {
436
780M
  return ((box.bot_left.y() <= top_right.y()) && (box.top_right.y() >= bot_left.y()));
437
780M
}
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
445
0
inline bool TBOX::major_y_overlap(const TBOX &box) const {
446
0
  TDimension overlap = box.height();
447
0
  if (this->bottom() > box.bottom()) {
448
0
    overlap -= this->bottom() - box.bottom();
449
0
  }
450
0
  if (this->top() < box.top()) {
451
0
    overlap -= box.top() - this->top();
452
0
  }
453
0
  return (overlap >= box.height() / 2 || overlap >= this->height() / 2);
454
0
}
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
463
0
inline double TBOX::x_overlap_fraction(const TBOX &other) const {
464
0
  int low = std::max(left(), other.left());
465
0
  int high = std::min(right(), other.right());
466
0
  int width = right() - left();
467
0
  if (width == 0) {
468
0
    int x = left();
469
0
    if (other.left() <= x && x <= other.right()) {
470
0
      return 1.0;
471
0
    } else {
472
0
      return 0.0;
473
0
    }
474
0
  } else {
475
0
    return std::max(0.0, static_cast<double>(high - low) / width);
476
0
  }
477
0
}
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
486
0
inline double TBOX::y_overlap_fraction(const TBOX &other) const {
487
0
  int low = std::max(bottom(), other.bottom());
488
0
  int high = std::min(top(), other.top());
489
0
  int height = top() - bottom();
490
0
  if (height == 0) {
491
0
    int y = bottom();
492
0
    if (other.bottom() <= y && y <= other.top()) {
493
0
      return 1.0;
494
0
    } else {
495
0
      return 0.0;
496
0
    }
497
0
  } else {
498
0
    return std::max(0.0, static_cast<double>(high - low) / height);
499
0
  }
500
0
}
501
502
} // namespace tesseract
503
504
#endif