/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 |