/src/tesseract/src/ccstruct/polyblk.cpp
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * File: polyblk.cpp (Formerly poly_block.c) |
3 | | * Description: Polygonal blocks |
4 | | * |
5 | | * (C) Copyright 1993, Hewlett-Packard Ltd. |
6 | | ** Licensed under the Apache License, Version 2.0 (the "License"); |
7 | | ** you may not use this file except in compliance with the License. |
8 | | ** You may obtain a copy of the License at |
9 | | ** http://www.apache.org/licenses/LICENSE-2.0 |
10 | | ** Unless required by applicable law or agreed to in writing, software |
11 | | ** distributed under the License is distributed on an "AS IS" BASIS, |
12 | | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | ** See the License for the specific language governing permissions and |
14 | | ** limitations under the License. |
15 | | * |
16 | | **********************************************************************/ |
17 | | |
18 | | // Include automatically generated configuration file if running autoconf. |
19 | | #ifdef HAVE_CONFIG_H |
20 | | # include "config_auto.h" |
21 | | #endif |
22 | | |
23 | | #include "polyblk.h" |
24 | | |
25 | | #include "elst.h" |
26 | | |
27 | | #include <cctype> |
28 | | #include <cinttypes> // PRId32 |
29 | | #include <cmath> |
30 | | #include <cstdio> |
31 | | #include <memory> // std::unique_ptr |
32 | | |
33 | | namespace tesseract { |
34 | | |
35 | 0 | #define INTERSECTING INT16_MAX |
36 | | |
37 | 0 | POLY_BLOCK::POLY_BLOCK(ICOORDELT_LIST *points, PolyBlockType t) { |
38 | 0 | ICOORDELT_IT v = &vertices; |
39 | |
|
40 | 0 | vertices.clear(); |
41 | 0 | v.move_to_first(); |
42 | 0 | v.add_list_before(points); |
43 | 0 | compute_bb(); |
44 | 0 | type = t; |
45 | 0 | } |
46 | | |
47 | | // Initialize from box coordinates. |
48 | 12.3k | POLY_BLOCK::POLY_BLOCK(const TBOX &tbox, PolyBlockType t) { |
49 | 12.3k | vertices.clear(); |
50 | 12.3k | ICOORDELT_IT v = &vertices; |
51 | 12.3k | v.move_to_first(); |
52 | 12.3k | v.add_to_end(new ICOORDELT(tbox.left(), tbox.top())); |
53 | 12.3k | v.add_to_end(new ICOORDELT(tbox.left(), tbox.bottom())); |
54 | 12.3k | v.add_to_end(new ICOORDELT(tbox.right(), tbox.bottom())); |
55 | 12.3k | v.add_to_end(new ICOORDELT(tbox.right(), tbox.top())); |
56 | 12.3k | compute_bb(); |
57 | 12.3k | type = t; |
58 | 12.3k | } |
59 | | |
60 | | /** |
61 | | * @name POLY_BLOCK::compute_bb |
62 | | * |
63 | | * Compute the bounding box from the outline points. |
64 | | */ |
65 | | |
66 | 12.3k | void POLY_BLOCK::compute_bb() { // constructor |
67 | 12.3k | ICOORD ibl, itr; // integer bb |
68 | 12.3k | ICOORD botleft; // bounding box |
69 | 12.3k | ICOORD topright; |
70 | 12.3k | ICOORD pos; // current pos; |
71 | 12.3k | ICOORDELT_IT pts = &vertices; // iterator |
72 | | |
73 | 12.3k | botleft = *pts.data(); |
74 | 12.3k | topright = botleft; |
75 | 49.3k | do { |
76 | 49.3k | pos = *pts.data(); |
77 | 49.3k | if (pos.x() < botleft.x()) { |
78 | | // get bounding box |
79 | 0 | botleft = ICOORD(pos.x(), botleft.y()); |
80 | 0 | } |
81 | 49.3k | if (pos.y() < botleft.y()) { |
82 | 12.3k | botleft = ICOORD(botleft.x(), pos.y()); |
83 | 12.3k | } |
84 | 49.3k | if (pos.x() > topright.x()) { |
85 | 12.3k | topright = ICOORD(pos.x(), topright.y()); |
86 | 12.3k | } |
87 | 49.3k | if (pos.y() > topright.y()) { |
88 | 0 | topright = ICOORD(topright.x(), pos.y()); |
89 | 0 | } |
90 | 49.3k | pts.forward(); |
91 | 49.3k | } while (!pts.at_first()); |
92 | 12.3k | ibl = ICOORD(botleft.x(), botleft.y()); |
93 | 12.3k | itr = ICOORD(topright.x(), topright.y()); |
94 | 12.3k | box = TBOX(ibl, itr); |
95 | 12.3k | } |
96 | | |
97 | | /** |
98 | | * @name POLY_BLOCK::winding_number |
99 | | * |
100 | | * Return the winding number of the outline around the given point. |
101 | | * @param point point to wind around |
102 | | */ |
103 | | |
104 | 0 | int16_t POLY_BLOCK::winding_number(const ICOORD &point) { |
105 | 0 | int16_t count; // winding count |
106 | 0 | ICOORD pt; // current point |
107 | 0 | ICOORD vec; // point to current point |
108 | 0 | ICOORD vvec; // current point to next point |
109 | 0 | int32_t cross; // cross product |
110 | 0 | ICOORDELT_IT it = &vertices; // iterator |
111 | |
|
112 | 0 | count = 0; |
113 | 0 | do { |
114 | 0 | pt = *it.data(); |
115 | 0 | vec = pt - point; |
116 | 0 | vvec = *it.data_relative(1) - pt; |
117 | | // crossing the line |
118 | 0 | if (vec.y() <= 0 && vec.y() + vvec.y() > 0) { |
119 | 0 | cross = vec * vvec; // cross product |
120 | 0 | if (cross > 0) { |
121 | 0 | count++; // crossing right half |
122 | 0 | } else if (cross == 0) { |
123 | 0 | return INTERSECTING; // going through point |
124 | 0 | } |
125 | 0 | } else if (vec.y() > 0 && vec.y() + vvec.y() <= 0) { |
126 | 0 | cross = vec * vvec; |
127 | 0 | if (cross < 0) { |
128 | 0 | count--; // crossing back |
129 | 0 | } else if (cross == 0) { |
130 | 0 | return INTERSECTING; // illegal |
131 | 0 | } |
132 | 0 | } else if (vec.y() == 0 && vec.x() == 0) { |
133 | 0 | return INTERSECTING; |
134 | 0 | } |
135 | 0 | it.forward(); |
136 | 0 | } while (!it.at_first()); |
137 | 0 | return count; // winding number |
138 | 0 | } |
139 | | |
140 | | /// @return true if other is inside this. |
141 | 0 | bool POLY_BLOCK::contains(POLY_BLOCK *other) { |
142 | 0 | int16_t count; // winding count |
143 | 0 | ICOORDELT_IT it = &vertices; // iterator |
144 | 0 | ICOORD vertex; |
145 | |
|
146 | 0 | if (!box.overlap(*(other->bounding_box()))) { |
147 | 0 | return false; // can't be contained |
148 | 0 | } |
149 | | |
150 | | /* check that no vertex of this is inside other */ |
151 | | |
152 | 0 | do { |
153 | 0 | vertex = *it.data(); |
154 | | // get winding number |
155 | 0 | count = other->winding_number(vertex); |
156 | 0 | if (count != INTERSECTING) { |
157 | 0 | if (count != 0) { |
158 | 0 | return false; |
159 | 0 | } |
160 | 0 | } |
161 | 0 | it.forward(); |
162 | 0 | } while (!it.at_first()); |
163 | | |
164 | | /* check that all vertices of other are inside this */ |
165 | | |
166 | | // switch lists |
167 | 0 | it.set_to_list(other->points()); |
168 | 0 | do { |
169 | 0 | vertex = *it.data(); |
170 | | // try other way round |
171 | 0 | count = winding_number(vertex); |
172 | 0 | if (count != INTERSECTING) { |
173 | 0 | if (count == 0) { |
174 | 0 | return false; |
175 | 0 | } |
176 | 0 | } |
177 | 0 | it.forward(); |
178 | 0 | } while (!it.at_first()); |
179 | 0 | return true; |
180 | 0 | } |
181 | | |
182 | | /** |
183 | | * @name POLY_BLOCK::rotate |
184 | | * |
185 | | * Rotate the POLY_BLOCK. |
186 | | * @param rotation cos, sin of angle |
187 | | */ |
188 | | |
189 | 0 | void POLY_BLOCK::rotate(FCOORD rotation) { |
190 | 0 | FCOORD pos; // current pos; |
191 | 0 | ICOORDELT *pt; // current point |
192 | 0 | ICOORDELT_IT pts = &vertices; // iterator |
193 | |
|
194 | 0 | do { |
195 | 0 | pt = pts.data(); |
196 | 0 | pos.set_x(pt->x()); |
197 | 0 | pos.set_y(pt->y()); |
198 | 0 | pos.rotate(rotation); |
199 | 0 | pt->set_x(static_cast<TDimension>(floor(pos.x() + 0.5))); |
200 | 0 | pt->set_y(static_cast<TDimension>(floor(pos.y() + 0.5))); |
201 | 0 | pts.forward(); |
202 | 0 | } while (!pts.at_first()); |
203 | 0 | compute_bb(); |
204 | 0 | } |
205 | | |
206 | | /** |
207 | | * @name POLY_BLOCK::reflect_in_y_axis |
208 | | * |
209 | | * Reflect the coords of the polygon in the y-axis. (Flip the sign of x.) |
210 | | */ |
211 | | |
212 | 0 | void POLY_BLOCK::reflect_in_y_axis() { |
213 | 0 | ICOORDELT *pt; // current point |
214 | 0 | ICOORDELT_IT pts = &vertices; // Iterator. |
215 | |
|
216 | 0 | do { |
217 | 0 | pt = pts.data(); |
218 | 0 | pt->set_x(-pt->x()); |
219 | 0 | pts.forward(); |
220 | 0 | } while (!pts.at_first()); |
221 | 0 | compute_bb(); |
222 | 0 | } |
223 | | |
224 | | /** |
225 | | * POLY_BLOCK::move |
226 | | * |
227 | | * Move the POLY_BLOCK. |
228 | | * @param shift x,y translation vector |
229 | | */ |
230 | | |
231 | 0 | void POLY_BLOCK::move(ICOORD shift) { |
232 | 0 | ICOORDELT *pt; // current point |
233 | 0 | ICOORDELT_IT pts = &vertices; // iterator |
234 | |
|
235 | 0 | do { |
236 | 0 | pt = pts.data(); |
237 | 0 | *pt += shift; |
238 | 0 | pts.forward(); |
239 | 0 | } while (!pts.at_first()); |
240 | 0 | compute_bb(); |
241 | 0 | } |
242 | | |
243 | | #ifndef GRAPHICS_DISABLED |
244 | | void POLY_BLOCK::plot(ScrollView *window, int32_t num) { |
245 | | ICOORDELT_IT v = &vertices; |
246 | | |
247 | | window->Pen(ColorForPolyBlockType(type)); |
248 | | |
249 | | v.move_to_first(); |
250 | | |
251 | | if (num > 0) { |
252 | | window->TextAttributes("Times", 80, false, false, false); |
253 | | char temp_buff[34]; |
254 | | # if !defined(_WIN32) || defined(__MINGW32__) |
255 | | snprintf(temp_buff, sizeof(temp_buff), "%" PRId32, num); |
256 | | # else |
257 | | _ltoa(num, temp_buff, 10); |
258 | | # endif |
259 | | window->Text(v.data()->x(), v.data()->y(), temp_buff); |
260 | | } |
261 | | |
262 | | window->SetCursor(v.data()->x(), v.data()->y()); |
263 | | for (v.mark_cycle_pt(); !v.cycled_list(); v.forward()) { |
264 | | window->DrawTo(v.data()->x(), v.data()->y()); |
265 | | } |
266 | | v.move_to_first(); |
267 | | window->DrawTo(v.data()->x(), v.data()->y()); |
268 | | } |
269 | | |
270 | | void POLY_BLOCK::fill(ScrollView *window, ScrollView::Color colour) { |
271 | | ICOORDELT_IT s_it; |
272 | | |
273 | | std::unique_ptr<PB_LINE_IT> lines(new PB_LINE_IT(this)); |
274 | | window->Pen(colour); |
275 | | |
276 | | for (auto y = this->bounding_box()->bottom(); y <= this->bounding_box()->top(); y++) { |
277 | | const std::unique_ptr</*non-const*/ ICOORDELT_LIST> segments(lines->get_line(y)); |
278 | | if (!segments->empty()) { |
279 | | s_it.set_to_list(segments.get()); |
280 | | for (s_it.mark_cycle_pt(); !s_it.cycled_list(); s_it.forward()) { |
281 | | // Note different use of ICOORDELT, x coord is x coord of pixel |
282 | | // at the start of line segment, y coord is length of line segment |
283 | | // Last pixel is start pixel + length. |
284 | | auto width = s_it.data()->y(); |
285 | | window->SetCursor(s_it.data()->x(), y); |
286 | | window->DrawTo(s_it.data()->x() + static_cast<float>(width), y); |
287 | | } |
288 | | } |
289 | | } |
290 | | } |
291 | | #endif |
292 | | |
293 | | /// @return true if the polygons of other and this overlap. |
294 | 0 | bool POLY_BLOCK::overlap(POLY_BLOCK *other) { |
295 | 0 | int16_t count; // winding count |
296 | 0 | ICOORDELT_IT it = &vertices; // iterator |
297 | 0 | ICOORD vertex; |
298 | |
|
299 | 0 | if (!box.overlap(*(other->bounding_box()))) { |
300 | 0 | return false; // can't be any overlap. |
301 | 0 | } |
302 | | |
303 | | /* see if a vertex of this is inside other */ |
304 | | |
305 | 0 | do { |
306 | 0 | vertex = *it.data(); |
307 | | // get winding number |
308 | 0 | count = other->winding_number(vertex); |
309 | 0 | if (count != INTERSECTING) { |
310 | 0 | if (count != 0) { |
311 | 0 | return true; |
312 | 0 | } |
313 | 0 | } |
314 | 0 | it.forward(); |
315 | 0 | } while (!it.at_first()); |
316 | | |
317 | | /* see if a vertex of other is inside this */ |
318 | | |
319 | | // switch lists |
320 | 0 | it.set_to_list(other->points()); |
321 | 0 | do { |
322 | 0 | vertex = *it.data(); |
323 | | // try other way round |
324 | 0 | count = winding_number(vertex); |
325 | 0 | if (count != INTERSECTING) { |
326 | 0 | if (count != 0) { |
327 | 0 | return true; |
328 | 0 | } |
329 | 0 | } |
330 | 0 | it.forward(); |
331 | 0 | } while (!it.at_first()); |
332 | 0 | return false; |
333 | 0 | } |
334 | | |
335 | 276k | ICOORDELT_LIST *PB_LINE_IT::get_line(TDimension y) { |
336 | 276k | ICOORDELT_IT v, r; |
337 | 276k | ICOORDELT_LIST *result; |
338 | 276k | ICOORDELT *x, *current, *previous; |
339 | 276k | float fy = y + 0.5f; |
340 | 276k | result = new ICOORDELT_LIST(); |
341 | 276k | r.set_to_list(result); |
342 | 276k | v.set_to_list(block->points()); |
343 | | |
344 | 1.38M | for (v.mark_cycle_pt(); !v.cycled_list(); v.forward()) { |
345 | 1.10M | if (((v.data_relative(-1)->y() > y) && (v.data()->y() <= y)) || |
346 | 845k | ((v.data_relative(-1)->y() <= y) && (v.data()->y() > y))) { |
347 | 520k | previous = v.data_relative(-1); |
348 | 520k | current = v.data(); |
349 | 520k | float fx = |
350 | 520k | 0.5f + previous->x() + |
351 | 520k | (current->x() - previous->x()) * (fy - previous->y()) / (current->y() - previous->y()); |
352 | 520k | x = new ICOORDELT(static_cast<TDimension>(fx), 0); |
353 | 520k | r.add_to_end(x); |
354 | 520k | } |
355 | 1.10M | } |
356 | | |
357 | 276k | if (!r.empty()) { |
358 | 260k | r.sort([](const ICOORDELT *p1, const ICOORDELT *p2) { |
359 | 260k | if (p1->x() < p2->x()) { |
360 | 0 | return (-1); |
361 | 260k | } else if (p1->x() > p2->x()) { |
362 | 260k | return (1); |
363 | 260k | } else { |
364 | 0 | return (0); |
365 | 0 | } |
366 | 260k | }); |
367 | 780k | for (r.mark_cycle_pt(); !r.cycled_list(); r.forward()) { |
368 | 520k | x = r.data(); |
369 | 520k | } |
370 | 520k | for (r.mark_cycle_pt(); !r.cycled_list(); r.forward()) { |
371 | 260k | r.data()->set_y(r.data_relative(1)->x() - r.data()->x()); |
372 | 260k | r.forward(); |
373 | 260k | delete (r.extract()); |
374 | 260k | } |
375 | 260k | } |
376 | | |
377 | 276k | return result; |
378 | 276k | } |
379 | | |
380 | | #ifndef GRAPHICS_DISABLED |
381 | | /// Returns a color to draw the given type. |
382 | | ScrollView::Color POLY_BLOCK::ColorForPolyBlockType(PolyBlockType type) { |
383 | | // Keep kPBColors in sync with PolyBlockType. |
384 | | const ScrollView::Color kPBColors[PT_COUNT] = { |
385 | | ScrollView::WHITE, // Type is not yet known. Keep as the 1st element. |
386 | | ScrollView::BLUE, // Text that lives inside a column. |
387 | | ScrollView::CYAN, // Text that spans more than one column. |
388 | | ScrollView::MEDIUM_BLUE, // Text that is in a cross-column pull-out |
389 | | // region. |
390 | | ScrollView::AQUAMARINE, // Partition belonging to an equation region. |
391 | | ScrollView::SKY_BLUE, // Partition belonging to an inline equation |
392 | | // region. |
393 | | ScrollView::MAGENTA, // Partition belonging to a table region. |
394 | | ScrollView::GREEN, // Text-line runs vertically. |
395 | | ScrollView::LIGHT_BLUE, // Text that belongs to an image. |
396 | | ScrollView::RED, // Image that lives inside a column. |
397 | | ScrollView::YELLOW, // Image that spans more than one column. |
398 | | ScrollView::ORANGE, // Image in a cross-column pull-out region. |
399 | | ScrollView::BROWN, // Horizontal Line. |
400 | | ScrollView::DARK_GREEN, // Vertical Line. |
401 | | ScrollView::GREY // Lies outside of any column. |
402 | | }; |
403 | | if (type < PT_COUNT) { |
404 | | return kPBColors[type]; |
405 | | } |
406 | | return ScrollView::WHITE; |
407 | | } |
408 | | #endif // !GRAPHICS_DISABLED |
409 | | |
410 | | } // namespace tesseract |