tesseract  4.00.00dev
alignedblob.cpp
Go to the documentation of this file.
1 // File: alignedblob.cpp
3 // Description: Subclass of BBGrid to find vertically aligned blobs.
4 // Author: Ray Smith
5 // Created: Fri Mar 21 15:03:01 PST 2008
6 //
7 // (C) Copyright 2008, Google Inc.
8 // Licensed under the Apache License, Version 2.0 (the "License");
9 // you may not use this file except in compliance with the License.
10 // You may obtain a copy of the License at
11 // http://www.apache.org/licenses/LICENSE-2.0
12 // Unless required by applicable law or agreed to in writing, software
13 // distributed under the License is distributed on an "AS IS" BASIS,
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 // See the License for the specific language governing permissions and
16 // limitations under the License.
17 //
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config_auto.h"
22 #endif
23 
24 #include "alignedblob.h"
25 #include "ndminx.h"
26 
27 INT_VAR(textord_debug_tabfind, 0, "Debug tab finding");
28 INT_VAR(textord_debug_bugs, 0, "Turn on output related to bugs in tab finding");
29 INT_VAR(textord_testregion_left, -1, "Left edge of debug reporting rectangle");
30 INT_VAR(textord_testregion_top, -1, "Top edge of debug reporting rectangle");
31 INT_VAR(textord_testregion_right, MAX_INT32, "Right edge of debug rectangle");
32 INT_VAR(textord_testregion_bottom, MAX_INT32, "Bottom edge of debug rectangle");
33 BOOL_VAR(textord_debug_printable, false, "Make debug windows printable");
34 
35 namespace tesseract {
36 
37 // Fraction of resolution used as alignment tolerance for aligned tabs.
38 const double kAlignedFraction = 0.03125;
39 // Fraction of resolution used as alignment tolerance for ragged tabs.
40 const double kRaggedFraction = 2.5;
41 // Fraction of height used as a minimum gutter gap for aligned blobs.
42 const double kAlignedGapFraction = 0.75;
43 // Fraction of height used as a minimum gutter gap for ragged tabs.
44 const double kRaggedGapFraction = 1.0;
45 // Constant number of pixels used as alignment tolerance for line finding.
46 const int kVLineAlignment = 3;
47 // Constant number of pixels used as gutter gap tolerance for line finding.
48 const int kVLineGutter = 1;
49 // Constant number of pixels used as the search size for line finding.
50 const int kVLineSearchSize = 150;
51 // Min number of points to accept for a ragged tab stop.
52 const int kMinRaggedTabs = 5;
53 // Min number of points to accept for an aligned tab stop.
54 const int kMinAlignedTabs = 4;
55 // Constant number of pixels minimum height of a vertical line.
56 const int kVLineMinLength = 500;
57 // Minimum gradient for a vertical tab vector. Used to prune away junk
58 // tab vectors with what would be a ridiculously large skew angle.
59 // Value corresponds to tan(90 - max allowed skew angle)
60 const double kMinTabGradient = 4.0;
61 // Tolerance to skew on top of current estimate of skew. Divide x or y length
62 // by kMaxSkewFactor to get the y or x skew distance.
63 // If the angle is small, the angle in degrees is roughly 60/kMaxSkewFactor.
64 const int kMaxSkewFactor = 15;
65 
66 // Constructor to set the parameters for finding aligned and ragged tabs.
67 // Vertical_x and vertical_y are the current estimates of the true vertical
68 // direction (up) in the image. Height is the height of the starter blob.
69 // v_gap_multiple is the multiple of height that will be used as a limit
70 // on vertical gap before giving up and calling the line ended.
71 // resolution is the original image resolution, and align0 indicates the
72 // type of tab stop to be found.
73 AlignedBlobParams::AlignedBlobParams(int vertical_x, int vertical_y,
74  int height, int v_gap_multiple,
75  int min_gutter_width,
76  int resolution, TabAlignment align0)
77  : right_tab(align0 == TA_RIGHT_RAGGED || align0 == TA_RIGHT_ALIGNED),
78  ragged(align0 == TA_LEFT_RAGGED || align0 == TA_RIGHT_RAGGED),
79  alignment(align0),
80  confirmed_type(TT_CONFIRMED),
81  min_length(0) {
82  // Set the tolerances according to the type of line sought.
83  // For tab search, these are based on the image resolution for most, or
84  // the height of the starting blob for the maximum vertical gap.
85  max_v_gap = height * v_gap_multiple;
86  if (ragged) {
87  // In the case of a ragged edge, we are much more generous with the
88  // inside alignment fraction, but also require a much bigger gutter.
90  if (alignment == TA_RIGHT_RAGGED) {
91  l_align_tolerance = static_cast<int>(resolution * kRaggedFraction + 0.5);
92  r_align_tolerance = static_cast<int>(resolution * kAlignedFraction + 0.5);
93  } else {
94  l_align_tolerance = static_cast<int>(resolution * kAlignedFraction + 0.5);
95  r_align_tolerance = static_cast<int>(resolution * kRaggedFraction + 0.5);
96  }
98  } else {
100  l_align_tolerance = static_cast<int>(resolution * kAlignedFraction + 0.5);
101  r_align_tolerance = static_cast<int>(resolution * kAlignedFraction + 0.5);
103  }
104  min_gutter = static_cast<int>(height * gutter_fraction + 0.5);
105  if (min_gutter < min_gutter_width)
106  min_gutter = min_gutter_width;
107  // Fit the vertical vector into an ICOORD, which is 16 bit.
108  set_vertical(vertical_x, vertical_y);
109 }
110 
111 // Constructor to set the parameters for finding vertical lines.
112 // Vertical_x and vertical_y are the current estimates of the true vertical
113 // direction (up) in the image. Width is the width of the starter blob.
114 AlignedBlobParams::AlignedBlobParams(int vertical_x, int vertical_y,
115  int width)
116  : gutter_fraction(0.0),
117  right_tab(false),
118  ragged(false),
121  max_v_gap(kVLineSearchSize),
122  min_gutter(kVLineGutter),
123  min_points(1),
124  min_length(kVLineMinLength) {
125  // Compute threshold for left and right alignment.
126  l_align_tolerance = MAX(kVLineAlignment, width);
127  r_align_tolerance = MAX(kVLineAlignment, width);
128 
129  // Fit the vertical vector into an ICOORD, which is 16 bit.
130  set_vertical(vertical_x, vertical_y);
131 }
132 
133 // Fit the vertical vector into an ICOORD, which is 16 bit.
134 void AlignedBlobParams::set_vertical(int vertical_x, int vertical_y) {
135  int factor = 1;
136  if (vertical_y > MAX_INT16)
137  factor = vertical_y / MAX_INT16 + 1;
138  vertical.set_x(vertical_x / factor);
139  vertical.set_y(vertical_y / factor);
140 }
141 
142 
144  const ICOORD& bleft, const ICOORD& tright)
145  : BlobGrid(gridsize, bleft, tright) {
146 }
147 
149 }
150 
151 // Return true if the given coordinates are within the test rectangle
152 // and the debug level is at least the given detail level.
153 bool AlignedBlob::WithinTestRegion(int detail_level, int x, int y) {
154  if (textord_debug_tabfind < detail_level)
155  return false;
157  y <= textord_testregion_top && y >= textord_testregion_bottom;
158 }
159 
160 // Display the tab codes of the BLOBNBOXes in this grid.
161 ScrollView* AlignedBlob::DisplayTabs(const char* window_name,
162  ScrollView* tab_win) {
163 #ifndef GRAPHICS_DISABLED
164  if (tab_win == NULL)
165  tab_win = MakeWindow(0, 50, window_name);
166  // For every tab in the grid, display it.
168  gsearch.StartFullSearch();
169  BLOBNBOX* bbox;
170  while ((bbox = gsearch.NextFullSearch()) != NULL) {
171  const TBOX& box = bbox->bounding_box();
172  int left_x = box.left();
173  int right_x = box.right();
174  int top_y = box.top();
175  int bottom_y = box.bottom();
176  TabType tabtype = bbox->left_tab_type();
177  if (tabtype != TT_NONE) {
178  if (tabtype == TT_MAYBE_ALIGNED)
179  tab_win->Pen(ScrollView::BLUE);
180  else if (tabtype == TT_MAYBE_RAGGED)
181  tab_win->Pen(ScrollView::YELLOW);
182  else if (tabtype == TT_CONFIRMED)
183  tab_win->Pen(ScrollView::GREEN);
184  else
185  tab_win->Pen(ScrollView::GREY);
186  tab_win->Line(left_x, top_y, left_x, bottom_y);
187  }
188  tabtype = bbox->right_tab_type();
189  if (tabtype != TT_NONE) {
190  if (tabtype == TT_MAYBE_ALIGNED)
191  tab_win->Pen(ScrollView::MAGENTA);
192  else if (tabtype == TT_MAYBE_RAGGED)
193  tab_win->Pen(ScrollView::ORANGE);
194  else if (tabtype == TT_CONFIRMED)
195  tab_win->Pen(ScrollView::RED);
196  else
197  tab_win->Pen(ScrollView::GREY);
198  tab_win->Line(right_x, top_y, right_x, bottom_y);
199  }
200  }
201  tab_win->Update();
202 #endif
203  return tab_win;
204 }
205 
206 // Helper returns true if the total number of line_crossings of all the blobs
207 // in the list is at least 2.
208 static bool AtLeast2LineCrossings(BLOBNBOX_CLIST* blobs) {
209  BLOBNBOX_C_IT it(blobs);
210  int total_crossings = 0;
211  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
212  total_crossings += it.data()->line_crossings();
213  }
214  return total_crossings >= 2;
215 }
216 
217 // Finds a vector corresponding to a set of vertically aligned blob edges
218 // running through the given box. The type of vector returned and the
219 // search parameters are determined by the AlignedBlobParams.
220 // vertical_x and y are updated with an estimate of the real
221 // vertical direction. (skew finding.)
222 // Returns NULL if no decent vector can be found.
224  BLOBNBOX* bbox,
225  int* vertical_x,
226  int* vertical_y) {
227  int ext_start_y, ext_end_y;
228  BLOBNBOX_CLIST good_points;
229  // Search up and then down from the starting bbox.
230  TBOX box = bbox->bounding_box();
231  bool debug = WithinTestRegion(2, box.left(), box.bottom());
232  int pt_count = AlignTabs(align_params, false, bbox, &good_points, &ext_end_y);
233  pt_count += AlignTabs(align_params, true, bbox, &good_points, &ext_start_y);
234  BLOBNBOX_C_IT it(&good_points);
235  it.move_to_last();
236  box = it.data()->bounding_box();
237  int end_y = box.top();
238  int end_x = align_params.right_tab ? box.right() : box.left();
239  it.move_to_first();
240  box = it.data()->bounding_box();
241  int start_x = align_params.right_tab ? box.right() : box.left();
242  int start_y = box.bottom();
243  // Acceptable tab vectors must have a mininum number of points,
244  // have a minimum acceptable length, and have a minimum gradient.
245  // The gradient corresponds to the skew angle.
246  // Ragged tabs don't need to satisfy the gradient condition, as they
247  // will always end up parallel to the vertical direction.
248  bool at_least_2_crossings = AtLeast2LineCrossings(&good_points);
249  if ((pt_count >= align_params.min_points &&
250  end_y - start_y >= align_params.min_length &&
251  (align_params.ragged ||
252  end_y - start_y >= abs(end_x - start_x) * kMinTabGradient)) ||
253  at_least_2_crossings) {
254  int confirmed_points = 0;
255  // Count existing confirmed points to see if vector is acceptable.
256  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
257  bbox = it.data();
258  if (align_params.right_tab) {
259  if (bbox->right_tab_type() == align_params.confirmed_type)
260  ++confirmed_points;
261  } else {
262  if (bbox->left_tab_type() == align_params.confirmed_type)
263  ++confirmed_points;
264  }
265  }
266  // Ragged vectors are not allowed to use too many already used points.
267  if (!align_params.ragged ||
268  confirmed_points + confirmed_points < pt_count) {
269  const TBOX& box = bbox->bounding_box();
270  if (debug) {
271  tprintf("Confirming tab vector of %d pts starting at %d,%d\n",
272  pt_count, box.left(), box.bottom());
273  }
274  // Flag all the aligned neighbours as confirmed .
275  for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
276  bbox = it.data();
277  if (align_params.right_tab) {
278  bbox->set_right_tab_type(align_params.confirmed_type);
279  } else {
280  bbox->set_left_tab_type(align_params.confirmed_type);
281  }
282  if (debug) {
283  bbox->bounding_box().print();
284  }
285  }
286  // Now make the vector and return it.
287  TabVector* result = TabVector::FitVector(align_params.alignment,
288  align_params.vertical,
289  ext_start_y, ext_end_y,
290  &good_points,
291  vertical_x, vertical_y);
292  result->set_intersects_other_lines(at_least_2_crossings);
293  if (debug) {
294  tprintf("Box was %d, %d\n", box.left(), box.bottom());
295  result->Print("After fitting");
296  }
297  return result;
298  } else if (debug) {
299  tprintf("Ragged tab used too many used points: %d out of %d\n",
300  confirmed_points, pt_count);
301  }
302  } else if (debug) {
303  tprintf("Tab vector failed basic tests: pt count %d vs min %d, "
304  "length %d vs min %d, min grad %g\n",
305  pt_count, align_params.min_points, end_y - start_y,
306  align_params.min_length, abs(end_x - start_x) * kMinTabGradient);
307  }
308  return NULL;
309 }
310 
311 // Find a set of blobs that are aligned in the given vertical
312 // direction with the given blob. Returns a list of aligned
313 // blobs and the number in the list.
314 // For other parameters see FindAlignedBlob below.
315 int AlignedBlob::AlignTabs(const AlignedBlobParams& params,
316  bool top_to_bottom, BLOBNBOX* bbox,
317  BLOBNBOX_CLIST* good_points, int* end_y) {
318  int ptcount = 0;
319  BLOBNBOX_C_IT it(good_points);
320 
321  TBOX box = bbox->bounding_box();
322  bool debug = WithinTestRegion(2, box.left(), box.bottom());
323  if (debug) {
324  tprintf("Starting alignment run at blob:");
325  box.print();
326  }
327  int x_start = params.right_tab ? box.right() : box.left();
328  while (bbox != NULL) {
329  // Add the blob to the list if the appropriate side is a tab candidate,
330  // or if we are working on a ragged tab.
331  TabType type = params.right_tab ? bbox->right_tab_type()
332  : bbox->left_tab_type();
333  if (((type != TT_NONE && type != TT_MAYBE_RAGGED) || params.ragged) &&
334  (it.empty() || it.data() != bbox)) {
335  if (top_to_bottom)
336  it.add_before_then_move(bbox);
337  else
338  it.add_after_then_move(bbox);
339  ++ptcount;
340  }
341  // Find the next blob that is aligned with the current one.
342  // FindAlignedBlob guarantees that forward progress will be made in the
343  // top_to_bottom direction, and therefore eventually it will return NULL,
344  // making this while (bbox != NULL) loop safe.
345  bbox = FindAlignedBlob(params, top_to_bottom, bbox, x_start, end_y);
346  if (bbox != NULL) {
347  box = bbox->bounding_box();
348  if (!params.ragged)
349  x_start = params.right_tab ? box.right() : box.left();
350  }
351  }
352  if (debug) {
353  tprintf("Alignment run ended with %d pts at blob:", ptcount);
354  box.print();
355  }
356  return ptcount;
357 }
358 
359 // Search vertically for a blob that is aligned with the input bbox.
360 // The search parameters are determined by AlignedBlobParams.
361 // top_to_bottom tells whether to search down or up.
362 // The return value is NULL if nothing was found in the search box
363 // or if a blob was found in the gutter. On a NULL return, end_y
364 // is set to the edge of the search box or the leading edge of the
365 // gutter blob if one was found.
366 BLOBNBOX* AlignedBlob::FindAlignedBlob(const AlignedBlobParams& p,
367  bool top_to_bottom, BLOBNBOX* bbox,
368  int x_start, int* end_y) {
369  TBOX box = bbox->bounding_box();
370  // If there are separator lines, get the column edges.
371  int left_column_edge = bbox->left_rule();
372  int right_column_edge = bbox->right_rule();
373  // start_y is used to guarantee that forward progress is made and the
374  // search does not go into an infinite loop. New blobs must extend the
375  // line beyond start_y.
376  int start_y = top_to_bottom ? box.bottom() : box.top();
377  if (WithinTestRegion(2, x_start, start_y)) {
378  tprintf("Column edges for blob at (%d,%d)->(%d,%d) are [%d, %d]\n",
379  box.left(), box.top(), box.right(), box.bottom(),
380  left_column_edge, right_column_edge);
381  }
382  // Compute skew tolerance.
383  int skew_tolerance = p.max_v_gap / kMaxSkewFactor;
384  // Calculate xmin and xmax of the search box so that it contains
385  // all possibly relevant boxes up to p.max_v_gap above or below accoording
386  // to top_to_bottom.
387  // Start with a notion of vertical with the current estimate.
388  int x2 = (p.max_v_gap * p.vertical.x() + p.vertical.y()/2) / p.vertical.y();
389  if (top_to_bottom) {
390  x2 = x_start - x2;
391  *end_y = start_y - p.max_v_gap;
392  } else {
393  x2 = x_start + x2;
394  *end_y = start_y + p.max_v_gap;
395  }
396  // Expand the box by an additional skew tolerance
397  int xmin = MIN(x_start, x2) - skew_tolerance;
398  int xmax = MAX(x_start, x2) + skew_tolerance;
399  // Now add direction-specific tolerances.
400  if (p.right_tab) {
401  xmax += p.min_gutter;
402  xmin -= p.l_align_tolerance;
403  } else {
404  xmax += p.r_align_tolerance;
405  xmin -= p.min_gutter;
406  }
407  // Setup a vertical search for an aligned blob.
409  if (WithinTestRegion(2, x_start, start_y))
410  tprintf("Starting %s %s search at %d-%d,%d, search_size=%d, gutter=%d\n",
411  p.ragged ? "Ragged" : "Aligned", p.right_tab ? "Right" : "Left",
412  xmin, xmax, start_y, p.max_v_gap, p.min_gutter);
413  vsearch.StartVerticalSearch(xmin, xmax, start_y);
414  // result stores the best real return value.
415  BLOBNBOX* result = NULL;
416  // The backup_result is not a tab candidate and can be used if no
417  // real tab candidate result is found.
418  BLOBNBOX* backup_result = NULL;
419  // neighbour is the blob that is currently being investigated.
420  BLOBNBOX* neighbour = NULL;
421  while ((neighbour = vsearch.NextVerticalSearch(top_to_bottom)) != NULL) {
422  if (neighbour == bbox)
423  continue;
424  TBOX nbox = neighbour->bounding_box();
425  int n_y = (nbox.top() + nbox.bottom()) / 2;
426  if ((!top_to_bottom && n_y > start_y + p.max_v_gap) ||
427  (top_to_bottom && n_y < start_y - p.max_v_gap)) {
428  if (WithinTestRegion(2, x_start, start_y))
429  tprintf("Neighbour too far at (%d,%d)->(%d,%d)\n",
430  nbox.left(), nbox.bottom(), nbox.right(), nbox.top());
431  break; // Gone far enough.
432  }
433  // It is CRITICAL to ensure that forward progress is made, (strictly
434  // in/decreasing n_y) or the caller could loop infinitely, while
435  // waiting for a sequence of blobs in a line to end.
436  // NextVerticalSearch alone does not guarantee this, as there may be
437  // more than one blob in a grid cell. See comment in AlignTabs.
438  if ((n_y < start_y) != top_to_bottom || nbox.y_overlap(box))
439  continue; // Only look in the required direction.
440  if (result != NULL && result->bounding_box().y_gap(nbox) > gridsize())
441  return result; // This result is clear.
442  if (backup_result != NULL && p.ragged && result == NULL &&
443  backup_result->bounding_box().y_gap(nbox) > gridsize())
444  return backup_result; // This result is clear.
445 
446  // If the neighbouring blob is the wrong side of a separator line, then it
447  // "doesn't exist" as far as we are concerned.
448  int x_at_n_y = x_start + (n_y - start_y) * p.vertical.x() / p.vertical.y();
449  if (x_at_n_y < neighbour->left_crossing_rule() ||
450  x_at_n_y > neighbour->right_crossing_rule())
451  continue; // Separator line in the way.
452  int n_left = nbox.left();
453  int n_right = nbox.right();
454  int n_x = p.right_tab ? n_right : n_left;
455  if (WithinTestRegion(2, x_start, start_y))
456  tprintf("neighbour at (%d,%d)->(%d,%d), n_x=%d, n_y=%d, xatn=%d\n",
457  nbox.left(), nbox.bottom(), nbox.right(), nbox.top(),
458  n_x, n_y, x_at_n_y);
459  if (p.right_tab &&
460  n_left < x_at_n_y + p.min_gutter &&
461  n_right > x_at_n_y + p.r_align_tolerance &&
462  (p.ragged || n_left < x_at_n_y + p.gutter_fraction * nbox.height())) {
463  // In the gutter so end of line.
464  if (bbox->right_tab_type() >= TT_MAYBE_ALIGNED)
466  *end_y = top_to_bottom ? nbox.top() : nbox.bottom();
467  if (WithinTestRegion(2, x_start, start_y))
468  tprintf("gutter\n");
469  return NULL;
470  }
471  if (!p.right_tab &&
472  n_left < x_at_n_y - p.l_align_tolerance &&
473  n_right > x_at_n_y - p.min_gutter &&
474  (p.ragged || n_right > x_at_n_y - p.gutter_fraction * nbox.height())) {
475  // In the gutter so end of line.
476  if (bbox->left_tab_type() >= TT_MAYBE_ALIGNED)
478  *end_y = top_to_bottom ? nbox.top() : nbox.bottom();
479  if (WithinTestRegion(2, x_start, start_y))
480  tprintf("gutter\n");
481  return NULL;
482  }
483  if ((p.right_tab && neighbour->leader_on_right()) ||
484  (!p.right_tab && neighbour->leader_on_left()))
485  continue; // Neighbours of leaders are not allowed to be used.
486  if (n_x <= x_at_n_y + p.r_align_tolerance &&
487  n_x >= x_at_n_y - p.l_align_tolerance) {
488  // Aligned so keep it. If it is a marked tab save it as result,
489  // otherwise keep it as backup_result to return in case of later failure.
490  if (WithinTestRegion(2, x_start, start_y))
491  tprintf("aligned, seeking%d, l=%d, r=%d\n",
492  p.right_tab, neighbour->left_tab_type(),
493  neighbour->right_tab_type());
494  TabType n_type = p.right_tab ? neighbour->right_tab_type()
495  : neighbour->left_tab_type();
496  if (n_type != TT_NONE && (p.ragged || n_type != TT_MAYBE_RAGGED)) {
497  if (result == NULL) {
498  result = neighbour;
499  } else {
500  // Keep the closest neighbour by Euclidean distance.
501  // This prevents it from picking a tab blob in another column.
502  const TBOX& old_box = result->bounding_box();
503  int x_diff = p.right_tab ? old_box.right() : old_box.left();
504  x_diff -= x_at_n_y;
505  int y_diff = (old_box.top() + old_box.bottom()) / 2 - start_y;
506  int old_dist = x_diff * x_diff + y_diff * y_diff;
507  x_diff = n_x - x_at_n_y;
508  y_diff = n_y - start_y;
509  int new_dist = x_diff * x_diff + y_diff * y_diff;
510  if (new_dist < old_dist)
511  result = neighbour;
512  }
513  } else if (backup_result == NULL) {
514  if (WithinTestRegion(2, x_start, start_y))
515  tprintf("Backup\n");
516  backup_result = neighbour;
517  } else {
518  TBOX backup_box = backup_result->bounding_box();
519  if ((p.right_tab && backup_box.right() < nbox.right()) ||
520  (!p.right_tab && backup_box.left() > nbox.left())) {
521  if (WithinTestRegion(2, x_start, start_y))
522  tprintf("Better backup\n");
523  backup_result = neighbour;
524  }
525  }
526  }
527  }
528  return result != NULL ? result : backup_result;
529 }
530 
531 } // namespace tesseract.
532 
void Line(int x1, int y1, int x2, int y2)
Definition: scrollview.cpp:538
int textord_testregion_right
Definition: alignedblob.cpp:31
int textord_testregion_bottom
Definition: alignedblob.cpp:32
bool y_overlap(const TBOX &box) const
Definition: rect.h:418
bool textord_debug_printable
Definition: alignedblob.cpp:33
void set_right_tab_type(TabType new_type)
Definition: blobbox.h:265
int textord_testregion_left
Definition: alignedblob.cpp:29
#define MAX_INT32
Definition: host.h:62
void set_x(inT16 xin)
rewrite function
Definition: points.h:61
int right_crossing_rule() const
Definition: blobbox.h:316
const int kMinRaggedTabs
Definition: alignedblob.cpp:52
AlignedBlobParams(int vertical_x, int vertical_y, int height, int v_gap_multiple, int min_gutter_width, int resolution, TabAlignment alignment0)
Definition: alignedblob.cpp:73
ScrollView * MakeWindow(int x, int y, const char *window_name)
Definition: bbgrid.h:593
#define MAX_INT16
Definition: host.h:61
int left_rule() const
Definition: blobbox.h:298
inT16 x() const
access function
Definition: points.h:52
void set_intersects_other_lines(bool value)
Definition: tabvector.h:182
#define tprintf(...)
Definition: tprintf.h:31
int gridsize() const
Definition: bbgrid.h:64
const int kVLineSearchSize
Definition: alignedblob.cpp:50
const int kVLineAlignment
Definition: alignedblob.cpp:46
#define BOOL_VAR(name, val, comment)
Definition: params.h:279
void set_vertical(int vertical_x, int vertical_y)
int textord_testregion_top
Definition: alignedblob.cpp:30
inT16 left() const
Definition: rect.h:68
const int kMaxSkewFactor
Definition: alignedblob.cpp:64
TabType
Definition: blobbox.h:44
TabType left_tab_type() const
Definition: blobbox.h:256
const double kAlignedFraction
Definition: alignedblob.cpp:38
void StartVerticalSearch(int xmin, int xmax, int y)
Definition: bbgrid.h:792
int y_gap(const TBOX &box) const
Definition: rect.h:225
BBC * NextFullSearch()
Definition: bbgrid.h:679
int textord_debug_tabfind
Definition: alignedblob.cpp:27
bool leader_on_left() const
Definition: blobbox.h:343
TabType right_tab_type() const
Definition: blobbox.h:262
static void Update()
Definition: scrollview.cpp:715
inT16 y() const
access_function
Definition: points.h:56
#define INT_VAR(name, val, comment)
Definition: params.h:276
int textord_debug_bugs
Definition: alignedblob.cpp:28
bool leader_on_right() const
Definition: blobbox.h:349
static bool WithinTestRegion(int detail_level, int x, int y)
const int kVLineMinLength
Definition: alignedblob.cpp:56
BBC * NextVerticalSearch(bool top_to_bottom)
Definition: bbgrid.h:806
inT16 top() const
Definition: rect.h:54
#define MAX(x, y)
Definition: ndminx.h:24
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)
Definition: tabvector.cpp:182
int right_rule() const
Definition: blobbox.h:304
Definition: rect.h:30
AlignedBlob(int gridsize, const ICOORD &bleft, const ICOORD &tright)
const double kAlignedGapFraction
Definition: alignedblob.cpp:42
ScrollView * DisplayTabs(const char *window_name, ScrollView *tab_win)
#define MIN(x, y)
Definition: ndminx.h:28
const int kMinAlignedTabs
Definition: alignedblob.cpp:54
inT16 height() const
Definition: rect.h:104
inT16 right() const
Definition: rect.h:75
const double kMinTabGradient
Definition: alignedblob.cpp:60
void print() const
Definition: rect.h:270
void StartFullSearch()
Definition: bbgrid.h:669
inT16 bottom() const
Definition: rect.h:61
void Print(const char *prefix)
Definition: tabvector.cpp:525
const double kRaggedGapFraction
Definition: alignedblob.cpp:44
const double kRaggedFraction
Definition: alignedblob.cpp:40
void set_left_tab_type(TabType new_type)
Definition: blobbox.h:259
const int kVLineGutter
Definition: alignedblob.cpp:48
const TBOX & bounding_box() const
Definition: blobbox.h:215
void set_y(inT16 yin)
rewrite function
Definition: points.h:65
void Pen(Color color)
Definition: scrollview.cpp:726
integer coordinate
Definition: points.h:30
TabVector * FindVerticalAlignment(AlignedBlobParams align_params, BLOBNBOX *bbox, int *vertical_x, int *vertical_y)