20# include "config_auto.h"
31static INT_VAR(textord_testregion_left, -1,
32 "Left edge of debug reporting rectangle in Leptonica coords "
33 "(bottom=0/top=height), with horizontal lines x/y-flipped");
34static INT_VAR(textord_testregion_top, INT32_MAX,
35 "Top edge of debug reporting rectangle in Leptonica coords "
36 "(bottom=0/top=height), with horizontal lines x/y-flipped");
37static INT_VAR(textord_testregion_right, INT32_MAX,
38 "Right edge of debug rectangle in Leptonica coords "
39 "(bottom=0/top=height), with horizontal lines x/y-flipped");
40static INT_VAR(textord_testregion_bottom, -1,
41 "Bottom edge of debug rectangle in Leptonica coords "
42 "(bottom=0/top=height), with horizontal lines x/y-flipped");
82 int min_gutter_width,
int resolution,
TabAlignment align0)
122 : gutter_fraction(0.0)
142 if (vertical_y > INT16_MAX) {
143 factor = vertical_y / INT16_MAX + 1;
150 :
BlobGrid(gridsize, bleft, tright) {}
158 return x >= textord_testregion_left &&
x <= textord_testregion_right &&
159 y <= textord_testregion_top && y >= textord_testregion_bottom;
162#ifndef GRAPHICS_DISABLED
166 if (tab_win ==
nullptr) {
175 int left_x = box.
left();
176 int right_x = box.
right();
177 int top_y = box.
top();
178 int bottom_y = box.
bottom();
190 tab_win->
Line(left_x, top_y, left_x, bottom_y);
203 tab_win->
Line(right_x, top_y, right_x, bottom_y);
214static bool AtLeast2LineCrossings(BLOBNBOX_CLIST *blobs) {
215 BLOBNBOX_C_IT it(blobs);
216 int total_crossings = 0;
217 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
218 total_crossings += it.data()->line_crossings();
220 return total_crossings >= 2;
235 int *vertical_x,
int *vertical_y) {
236 int ext_start_y, ext_end_y;
237 BLOBNBOX_CLIST good_points;
241 int pt_count = AlignTabs(align_params,
false, bbox, &good_points, &ext_end_y);
242 pt_count += AlignTabs(align_params,
true, bbox, &good_points, &ext_start_y);
243 BLOBNBOX_C_IT it(&good_points);
245 box = it.data()->bounding_box();
246 int end_y = box.
top();
249 box = it.data()->bounding_box();
251 int start_y = box.
bottom();
257 bool at_least_2_crossings = AtLeast2LineCrossings(&good_points);
260 at_least_2_crossings) {
261 int confirmed_points = 0;
263 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
276 if (!align_params.
ragged || confirmed_points + confirmed_points < pt_count) {
279 tprintf(
"Confirming tab vector of %d pts starting at %d,%d\n", pt_count, box.
left(),
283 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
297 ext_end_y, &good_points, vertical_x, vertical_y);
301 result->
Print(
"After fitting");
305 tprintf(
"Ragged tab used too many used points: %d out of %d\n", confirmed_points, pt_count);
309 "Tab vector failed basic tests: pt count %d vs min %d, "
310 "length %d vs min %d, min grad %g\n",
322 BLOBNBOX_CLIST *good_points,
int *end_y) {
324 BLOBNBOX_C_IT it(good_points);
329 tprintf(
"Starting alignment run at blob:");
333 while (bbox !=
nullptr) {
338 (it.empty() || it.data() != bbox)) {
340 it.add_before_then_move(bbox);
342 it.add_after_then_move(bbox);
350 bbox = FindAlignedBlob(params, top_to_bottom, bbox, x_start, end_y);
351 if (bbox !=
nullptr) {
359 tprintf(
"Alignment run ended with %d pts at blob:", ptcount);
372BLOBNBOX *AlignedBlob::FindAlignedBlob(
const AlignedBlobParams &
p,
bool top_to_bottom,
373 BLOBNBOX *bbox,
int x_start,
int *end_y) {
374 TBOX box = bbox->bounding_box();
376 int left_column_edge = bbox->left_rule();
377 int right_column_edge = bbox->right_rule();
381 int start_y = top_to_bottom ? box.
bottom() : box.top();
383 tprintf(
"Column edges for blob at (%d,%d)->(%d,%d) are [%d, %d]\n", box.left(), box.top(),
384 box.right(), box.bottom(), left_column_edge, right_column_edge);
392 int x2 = (
p.max_v_gap *
p.vertical.x() +
p.vertical.y() / 2) /
p.vertical.y();
395 *end_y = start_y -
p.max_v_gap;
398 *end_y = start_y +
p.max_v_gap;
401 int xmin = std::min(x_start, x2) - skew_tolerance;
402 int xmax = std::max(x_start, x2) + skew_tolerance;
405 xmax +=
p.min_gutter;
406 xmin -=
p.l_align_tolerance;
408 xmax +=
p.r_align_tolerance;
409 xmin -=
p.min_gutter;
412 GridSearch<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT> vsearch(
this);
414 tprintf(
"Starting %s %s search at %d-%d,%d, search_size=%d, gutter=%d\n",
415 p.ragged ?
"Ragged" :
"Aligned",
p.right_tab ?
"Right" :
"Left", xmin, xmax, start_y,
416 p.max_v_gap,
p.min_gutter);
418 vsearch.StartVerticalSearch(xmin, xmax, start_y);
420 BLOBNBOX *result =
nullptr;
423 BLOBNBOX *backup_result =
nullptr;
425 BLOBNBOX *neighbour =
nullptr;
426 while ((neighbour = vsearch.NextVerticalSearch(top_to_bottom)) !=
nullptr) {
427 if (neighbour == bbox) {
430 TBOX nbox = neighbour->bounding_box();
431 int n_y = (nbox.top() + nbox.bottom()) / 2;
432 if ((!top_to_bottom && n_y > start_y +
p.max_v_gap) ||
433 (top_to_bottom && n_y < start_y -
p.max_v_gap)) {
435 tprintf(
"Neighbour too far at (%d,%d)->(%d,%d)\n", nbox.left(), nbox.bottom(), nbox.right(),
445 if ((n_y < start_y) != top_to_bottom || nbox.y_overlap(box)) {
448 if (result !=
nullptr && result->bounding_box().y_gap(nbox) >
gridsize()) {
451 if (backup_result !=
nullptr &&
p.ragged && result ==
nullptr &&
452 backup_result->bounding_box().y_gap(nbox) >
gridsize()) {
453 return backup_result;
458 int x_at_n_y = x_start + (n_y - start_y) *
p.vertical.x() /
p.vertical.y();
459 if (x_at_n_y < neighbour->left_crossing_rule() || x_at_n_y > neighbour->right_crossing_rule()) {
462 int n_left = nbox.left();
463 int n_right = nbox.right();
464 int n_x =
p.right_tab ? n_right : n_left;
466 tprintf(
"neighbour at (%d,%d)->(%d,%d), n_x=%d, n_y=%d, xatn=%d\n", nbox.left(),
467 nbox.bottom(), nbox.right(), nbox.top(), n_x, n_y, x_at_n_y);
469 if (
p.right_tab && n_left < x_at_n_y +
p.min_gutter &&
470 n_right > x_at_n_y +
p.r_align_tolerance &&
471 (
p.ragged || n_left < x_at_n_y +
p.gutter_fraction * nbox.height())) {
476 *end_y = top_to_bottom ? nbox.top() : nbox.bottom();
482 if (!
p.right_tab && n_left < x_at_n_y -
p.l_align_tolerance &&
483 n_right > x_at_n_y -
p.min_gutter &&
484 (
p.ragged || n_right > x_at_n_y -
p.gutter_fraction * nbox.height())) {
489 *end_y = top_to_bottom ? nbox.top() : nbox.bottom();
495 if ((
p.right_tab && neighbour->leader_on_right()) ||
496 (!
p.right_tab && neighbour->leader_on_left())) {
499 if (n_x <= x_at_n_y + p.r_align_tolerance && n_x >= x_at_n_y -
p.l_align_tolerance) {
503 tprintf(
"aligned, seeking%d, l=%d, r=%d\n",
p.right_tab, neighbour->left_tab_type(),
504 neighbour->right_tab_type());
506 TabType n_type =
p.right_tab ? neighbour->right_tab_type() : neighbour->left_tab_type();
508 if (result ==
nullptr) {
513 const TBOX &old_box = result->bounding_box();
514 int x_diff =
p.right_tab ? old_box.right() : old_box.left();
516 int y_diff = (old_box.top() + old_box.bottom()) / 2 - start_y;
517 int old_dist = x_diff * x_diff + y_diff * y_diff;
518 x_diff = n_x - x_at_n_y;
519 y_diff = n_y - start_y;
520 int new_dist = x_diff * x_diff + y_diff * y_diff;
521 if (new_dist < old_dist) {
525 }
else if (backup_result ==
nullptr) {
529 backup_result = neighbour;
531 TBOX backup_box = backup_result->bounding_box();
532 if ((
p.right_tab && backup_box.right() < nbox.right()) ||
533 (!
p.right_tab && backup_box.left() > nbox.left())) {
537 backup_result = neighbour;
542 return result !=
nullptr ? result : backup_result;
#define BOOL_VAR(name, val, comment)
#define INT_VAR(name, val, comment)
const int kVLineMinLength
const int kMinAlignedTabs
const int kVLineSearchSize
void tprintf(const char *format,...)
const double kAlignedGapFraction
bool textord_debug_printable
int textord_debug_tabfind
const double kRaggedGapFraction
const int kVLineAlignment
const double kAlignedFraction
const double kRaggedFraction
const double kMinTabGradient
const TBOX & bounding_box() const
void set_left_tab_type(TabType new_type)
TabType left_tab_type() const
void set_right_tab_type(TabType new_type)
TabType right_tab_type() const
void set_x(TDimension xin)
rewrite function
void set_y(TDimension yin)
rewrite function
TDimension bottom() const
void set_vertical(int vertical_x, int vertical_y)
AlignedBlobParams(int vertical_x, int vertical_y, int height, int v_gap_multiple, int min_gutter_width, int resolution, TabAlignment alignment0)
AlignedBlob(int gridsize, const ICOORD &bleft, const ICOORD &tright)
static bool WithinTestRegion(int detail_level, int x, int y)
TabVector * FindVerticalAlignment(AlignedBlobParams align_params, BLOBNBOX *bbox, int *vertical_x, int *vertical_y)
ScrollView * DisplayTabs(const char *window_name, ScrollView *tab_win)
ScrollView * MakeWindow(int x, int y, const char *window_name)
void set_intersects_other_lines(bool value)
static TabVector * FitVector(TabAlignment alignment, ICOORD vertical, int extended_start_y, int extended_end_y, BLOBNBOX_CLIST *good_points, int *vertical_x, int *vertical_y)
void Print(const char *prefix)
void Line(int x1, int y1, int x2, int y2)