21 #include "config_auto.h" 75 int height,
int v_gap_multiple,
117 : gutter_fraction(0.0),
137 if (vertical_y > INT16_MAX)
138 factor = vertical_y / INT16_MAX + 1;
146 :
BlobGrid(gridsize, bleft, tright) {
161 #ifndef GRAPHICS_DISABLED 162 if (tab_win ==
nullptr)
170 int left_x = box.
left();
171 int right_x = box.
right();
172 int top_y = box.
top();
173 int bottom_y = box.
bottom();
184 tab_win->
Line(left_x, top_y, left_x, bottom_y);
196 tab_win->
Line(right_x, top_y, right_x, bottom_y);
206 static bool AtLeast2LineCrossings(BLOBNBOX_CLIST* blobs) {
207 BLOBNBOX_C_IT it(blobs);
208 int total_crossings = 0;
209 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
210 total_crossings += it.data()->line_crossings();
212 return total_crossings >= 2;
230 int ext_start_y, ext_end_y;
231 BLOBNBOX_CLIST good_points;
235 int pt_count = AlignTabs(align_params,
false, bbox, &good_points, &ext_end_y);
236 pt_count += AlignTabs(align_params,
true, bbox, &good_points, &ext_start_y);
237 BLOBNBOX_C_IT it(&good_points);
239 box = it.data()->bounding_box();
240 int end_y = box.
top();
243 box = it.data()->bounding_box();
245 int start_y = box.
bottom();
251 bool at_least_2_crossings = AtLeast2LineCrossings(&good_points);
256 at_least_2_crossings) {
257 int confirmed_points = 0;
259 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
270 if (!align_params.
ragged ||
271 confirmed_points + confirmed_points < pt_count) {
274 tprintf(
"Confirming tab vector of %d pts starting at %d,%d\n",
278 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
292 ext_start_y, ext_end_y,
294 vertical_x, vertical_y);
298 result->
Print(
"After fitting");
302 tprintf(
"Ragged tab used too many used points: %d out of %d\n",
303 confirmed_points, pt_count);
306 tprintf(
"Tab vector failed basic tests: pt count %d vs min %d, " 307 "length %d vs min %d, min grad %g\n",
308 pt_count, align_params.
min_points, end_y - start_y,
320 BLOBNBOX_CLIST* good_points,
int* end_y) {
322 BLOBNBOX_C_IT it(good_points);
327 tprintf(
"Starting alignment run at blob:");
331 while (bbox !=
nullptr) {
337 (it.empty() || it.data() != bbox)) {
339 it.add_before_then_move(bbox);
341 it.add_after_then_move(bbox);
348 bbox = FindAlignedBlob(params, top_to_bottom, bbox, x_start, end_y);
349 if (bbox !=
nullptr) {
356 tprintf(
"Alignment run ended with %d pts at blob:", ptcount);
369 BLOBNBOX* AlignedBlob::FindAlignedBlob(
const AlignedBlobParams& p,
371 int x_start,
int* end_y) {
374 int left_column_edge = bbox->
left_rule();
379 int start_y = top_to_bottom ? box.
bottom() : box.
top();
381 tprintf(
"Column edges for blob at (%d,%d)->(%d,%d) are [%d, %d]\n",
383 left_column_edge, right_column_edge);
391 int x2 = (p.max_v_gap * p.vertical.x() + p.vertical.y()/2) / p.vertical.y();
394 *end_y = start_y - p.max_v_gap;
397 *end_y = start_y + p.max_v_gap;
400 int xmin = std::min(x_start, x2) - skew_tolerance;
401 int xmax = std::max(x_start, x2) + skew_tolerance;
404 xmax += p.min_gutter;
405 xmin -= p.l_align_tolerance;
407 xmax += p.r_align_tolerance;
408 xmin -= p.min_gutter;
411 GridSearch<BLOBNBOX, BLOBNBOX_CLIST, BLOBNBOX_C_IT> vsearch(
this);
413 tprintf(
"Starting %s %s search at %d-%d,%d, search_size=%d, gutter=%d\n",
414 p.ragged ?
"Ragged" :
"Aligned", p.right_tab ?
"Right" :
"Left",
415 xmin, xmax, start_y, p.max_v_gap, p.min_gutter);
416 vsearch.StartVerticalSearch(xmin, xmax, start_y);
424 while ((neighbour = vsearch.NextVerticalSearch(top_to_bottom)) !=
nullptr) {
425 if (neighbour == bbox)
428 int n_y = (nbox.
top() + nbox.
bottom()) / 2;
429 if ((!top_to_bottom && n_y > start_y + p.max_v_gap) ||
430 (top_to_bottom && n_y < start_y - p.max_v_gap)) {
432 tprintf(
"Neighbour too far at (%d,%d)->(%d,%d)\n",
441 if ((n_y < start_y) != top_to_bottom || nbox.
y_overlap(box))
445 if (backup_result !=
nullptr && p.ragged && result ==
nullptr &&
447 return backup_result;
451 int x_at_n_y = x_start + (n_y - start_y) * p.vertical.x() / p.vertical.y();
452 if (x_at_n_y < neighbour->left_crossing_rule() ||
455 int n_left = nbox.
left();
456 int n_right = nbox.
right();
457 int n_x = p.right_tab ? n_right : n_left;
459 tprintf(
"neighbour at (%d,%d)->(%d,%d), n_x=%d, n_y=%d, xatn=%d\n",
463 n_left < x_at_n_y + p.min_gutter &&
464 n_right > x_at_n_y + p.r_align_tolerance &&
465 (p.ragged || n_left < x_at_n_y + p.gutter_fraction * nbox.
height())) {
469 *end_y = top_to_bottom ? nbox.
top() : nbox.
bottom();
475 n_left < x_at_n_y - p.l_align_tolerance &&
476 n_right > x_at_n_y - p.min_gutter &&
477 (p.ragged || n_right > x_at_n_y - p.gutter_fraction * nbox.
height())) {
481 *end_y = top_to_bottom ? nbox.
top() : nbox.
bottom();
489 if (n_x <= x_at_n_y + p.r_align_tolerance &&
490 n_x >= x_at_n_y - p.l_align_tolerance) {
494 tprintf(
"aligned, seeking%d, l=%d, r=%d\n",
500 if (result ==
nullptr) {
506 int x_diff = p.right_tab ? old_box.
right() : old_box.
left();
508 int y_diff = (old_box.
top() + old_box.
bottom()) / 2 - start_y;
509 int old_dist = x_diff * x_diff + y_diff * y_diff;
510 x_diff = n_x - x_at_n_y;
511 y_diff = n_y - start_y;
512 int new_dist = x_diff * x_diff + y_diff * y_diff;
513 if (new_dist < old_dist)
516 }
else if (backup_result ==
nullptr) {
519 backup_result = neighbour;
522 if ((p.right_tab && backup_box.
right() < nbox.
right()) ||
523 (!p.right_tab && backup_box.
left() > nbox.
left())) {
526 backup_result = neighbour;
531 return result !=
nullptr ? result : backup_result;
const int kMinAlignedTabs
ScrollView * MakeWindow(int x, int y, const char *window_name)
#define BOOL_VAR(name, val, comment)
int y_gap(const TBOX &box) const
void set_x(int16_t xin)
rewrite function
int right_crossing_rule() const
const int kVLineSearchSize
int textord_testregion_right
static bool WithinTestRegion(int detail_level, int x, int y)
TabVector * FindVerticalAlignment(AlignedBlobParams align_params, BLOBNBOX *bbox, int *vertical_x, int *vertical_y)
const double kRaggedFraction
AlignedBlobParams(int vertical_x, int vertical_y, int height, int v_gap_multiple, int min_gutter_width, int resolution, TabAlignment alignment0)
const double kAlignedGapFraction
const double kRaggedGapFraction
void set_right_tab_type(TabType new_type)
bool textord_debug_printable
const double kMinTabGradient
bool leader_on_right() const
void set_vertical(int vertical_x, int vertical_y)
int textord_debug_tabfind
TabType right_tab_type() const
TabType left_tab_type() const
int textord_testregion_top
DLLSYM void tprintf(const char *format,...)
ScrollView * DisplayTabs(const char *window_name, ScrollView *tab_win)
AlignedBlob(int gridsize, const ICOORD &bleft, const ICOORD &tright)
const int kVLineMinLength
const int kVLineAlignment
bool y_overlap(const TBOX &box) const
int textord_testregion_left
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)
const TBOX & bounding_box() const
bool leader_on_left() const
void set_intersects_other_lines(bool value)
void set_y(int16_t yin)
rewrite function
void set_left_tab_type(TabType new_type)
void Print(const char *prefix)
#define INT_VAR(name, val, comment)
void Line(int x1, int y1, int x2, int y2)
const double kAlignedFraction
int textord_testregion_bottom