25 #include "config_auto.h"
74 int AlignedBlob::debug_pix_index_ = 0;
82 snprintf(numbuf,
sizeof(numbuf),
"%d", debug_pix_index_);
83 textord_debug_pix_ += numbuf;
84 textord_debug_pix_ +=
".pix";
95 int height,
int v_gap_multiple,
137 : gutter_fraction(0.0),
166 :
BlobGrid(gridsize, bleft, tright) {
184 #ifndef GRAPHICS_DISABLED
193 int left_x = box.
left();
194 int right_x = box.
right();
195 int top_y = box.
top();
196 int bottom_y = box.
bottom();
207 tab_win->
Line(left_x, top_y, left_x, bottom_y);
219 tab_win->
Line(right_x, top_y, right_x, bottom_y);
229 static bool AtLeast2LineCrossings(BLOBNBOX_CLIST* blobs) {
230 BLOBNBOX_C_IT it(blobs);
231 int total_crossings = 0;
232 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
233 total_crossings += it.data()->line_crossings();
235 return total_crossings >= 2;
248 int ext_start_y, ext_end_y;
249 BLOBNBOX_CLIST good_points;
253 int pt_count = AlignTabs(align_params,
false, bbox, &good_points, &ext_end_y);
254 pt_count += AlignTabs(align_params,
true, bbox, &good_points, &ext_start_y);
255 BLOBNBOX_C_IT it(&good_points);
257 box = it.data()->bounding_box();
258 int end_y = box.
top();
261 box = it.data()->bounding_box();
263 int start_y = box.
bottom();
269 bool at_least_2_crossings = AtLeast2LineCrossings(&good_points);
274 at_least_2_crossings) {
275 int confirmed_points = 0;
277 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
288 if (!align_params.
ragged ||
289 confirmed_points + confirmed_points < pt_count) {
292 tprintf(
"Confirming tab vector of %d pts starting at %d,%d\n",
296 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
310 ext_start_y, ext_end_y,
312 vertical_x, vertical_y);
316 result->
Print(
"After fitting");
320 tprintf(
"Ragged tab used too many used points: %d out of %d\n",
321 confirmed_points, pt_count);
324 tprintf(
"Tab vector failed basic tests: pt count %d vs min %d, "
325 "length %d vs min %d, min grad %g\n",
326 pt_count, align_params.
min_points, end_y - start_y,
327 align_params.
min_length, abs(end_x - start_x) * kMinTabGradient);
338 BLOBNBOX_CLIST* good_points,
int* end_y) {
340 BLOBNBOX_C_IT it(good_points);
345 tprintf(
"Starting alignment run at blob:");
349 while (bbox !=
NULL) {
355 (it.empty() || it.data() != bbox)) {
357 it.add_before_then_move(bbox);
359 it.add_after_then_move(bbox);
366 bbox = FindAlignedBlob(params, top_to_bottom, bbox, x_start, end_y);
374 tprintf(
"Alignment run ended with %d pts at blob:", ptcount);
387 BLOBNBOX* AlignedBlob::FindAlignedBlob(
const AlignedBlobParams& p,
389 int x_start,
int* end_y) {
392 int left_column_edge = bbox->
left_rule();
397 int start_y = top_to_bottom ? box.
bottom() : box.
top();
399 tprintf(
"Column edges for blob at (%d,%d)->(%d,%d) are [%d, %d]\n",
401 left_column_edge, right_column_edge);
409 int x2 = (p.max_v_gap * p.vertical.x() + p.vertical.y()/2) / p.vertical.y();
412 *end_y = start_y - p.max_v_gap;
415 *end_y = start_y + p.max_v_gap;
418 int xmin =
MIN(x_start, x2) - skew_tolerance;
419 int xmax =
MAX(x_start, x2) + skew_tolerance;
422 xmax += p.min_gutter;
423 xmin -= p.l_align_tolerance;
425 xmax += p.r_align_tolerance;
426 xmin -= p.min_gutter;
429 GridSearch<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT> vsearch(
this);
431 tprintf(
"Starting %s %s search at %d-%d,%d, search_size=%d, gutter=%d\n",
432 p.ragged ?
"Ragged" :
"Aligned", p.right_tab ?
"Right" :
"Left",
433 xmin, xmax, start_y, p.max_v_gap, p.min_gutter);
434 vsearch.StartVerticalSearch(xmin, xmax, start_y);
442 while ((neighbour = vsearch.NextVerticalSearch(top_to_bottom)) !=
NULL) {
443 if (neighbour == bbox)
446 int n_y = (nbox.
top() + nbox.
bottom()) / 2;
447 if ((!top_to_bottom && n_y > start_y + p.max_v_gap) ||
448 (top_to_bottom && n_y < start_y - p.max_v_gap)) {
450 tprintf(
"Neighbour too far at (%d,%d)->(%d,%d)\n",
459 if ((n_y < start_y) != top_to_bottom || nbox.
y_overlap(box))
463 if (backup_result !=
NULL && p.ragged && result ==
NULL &&
465 return backup_result;
469 int x_at_n_y = x_start + (n_y - start_y) * p.vertical.x() / p.vertical.y();
470 if (x_at_n_y < neighbour->left_crossing_rule() ||
473 int n_left = nbox.
left();
474 int n_right = nbox.
right();
475 int n_x = p.right_tab ? n_right : n_left;
477 tprintf(
"neighbour at (%d,%d)->(%d,%d), n_x=%d, n_y=%d, xatn=%d\n",
481 n_left < x_at_n_y + p.min_gutter &&
482 n_right > x_at_n_y + p.r_align_tolerance &&
483 (p.ragged || n_left < x_at_n_y + p.gutter_fraction * nbox.
height())) {
487 *end_y = top_to_bottom ? nbox.
top() : nbox.
bottom();
493 n_left < x_at_n_y - p.l_align_tolerance &&
494 n_right > x_at_n_y - p.min_gutter &&
495 (p.ragged || n_right > x_at_n_y - p.gutter_fraction * nbox.
height())) {
499 *end_y = top_to_bottom ? nbox.
top() : nbox.
bottom();
507 if (n_x <= x_at_n_y + p.r_align_tolerance &&
508 n_x >= x_at_n_y - p.l_align_tolerance) {
512 tprintf(
"aligned, seeking%d, l=%d, r=%d\n",
518 if (result ==
NULL) {
524 int x_diff = p.right_tab ? old_box.
right() : old_box.
left();
526 int y_diff = (old_box.
top() + old_box.
bottom()) / 2 - start_y;
527 int old_dist = x_diff * x_diff + y_diff * y_diff;
528 x_diff = n_x - x_at_n_y;
529 y_diff = n_y - start_y;
530 int new_dist = x_diff * x_diff + y_diff * y_diff;
531 if (new_dist < old_dist)
534 }
else if (backup_result ==
NULL) {
537 backup_result = neighbour;
540 if ((p.right_tab && backup_box.
right() < nbox.
right()) ||
541 (!p.right_tab && backup_box.
left() > nbox.
left())) {
544 backup_result = neighbour;
549 return result !=
NULL ? result : backup_result;