• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 ///////////////////////////////////////////////////////////////////////
2 // File:        bbgrid.h
3 // Description: Class to hold BLOBNBOXs in a grid for fast access
4 //              to neighbours.
5 // Author:      Ray Smith
6 // Created:     Wed Jun 06 17:22:01 PDT 2007
7 //
8 // (C) Copyright 2007, Google Inc.
9 // Licensed under the Apache License, Version 2.0 (the "License");
10 // you may not use this file except in compliance with the License.
11 // You may obtain a copy of the License at
12 // http://www.apache.org/licenses/LICENSE-2.0
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 //
19 ///////////////////////////////////////////////////////////////////////
20 
21 #ifndef TESSERACT_TEXTORD_BBGRID_H__
22 #define TESSERACT_TEXTORD_BBGRID_H__
23 
24 #include "clst.h"
25 #include "coutln.h"
26 #include "rect.h"
27 #include "scrollview.h"
28 
29 // Some code is dependent upon leptonica. If you don't have it,
30 // you don't get this functionality.
31 #ifdef HAVE_CONFIG_H
32 #include "config_auto.h"
33 #endif
34 #ifdef HAVE_LIBLEPT
35 #include "allheaders.h"
36 #endif
37 
38 class BLOCK;
39 
40 namespace tesseract {
41 
42 #ifdef HAVE_LIBLEPT
43 // Helper function to return a scaled Pix with one pixel per grid cell,
44 // set (black) where the given outline enters the corresponding grid cell,
45 // and clear where the outline does not touch the grid cell.
46 // Also returns the grid coords of the bottom-left of the Pix, in *left
47 // and *bottom, which corresponds to (0, 0) on the Pix.
48 // Note that the Pix is used upside-down, with (0, 0) being the bottom-left.
49 Pix* TraceOutlineOnReducedPix(C_OUTLINE* outline, int gridsize,
50                               ICOORD bleft, int* left, int* bottom);
51 // As TraceOutlineOnReducedPix above, but on a BLOCK instead of a C_OUTLINE.
52 Pix* TraceBlockOnReducedPix(BLOCK* block, int gridsize,
53                             ICOORD bleft, int* left, int* bottom);
54 #endif
55 
56 template<class BBC, class BBC_CLIST, class BBC_C_IT> class GridSearch;
57 
58 // The BBGrid class holds C_LISTs of template classes BBC (bounding box class)
59 // in a grid for fast neighbour access.
60 // The BBC class must have a member const TBOX& bounding_box() const.
61 // The BBC class must have been CLISTIZEH'ed elsewhere to make the
62 // list class BBC_CLIST and the iterator BBC_C_IT.
63 // Use of C_LISTs enables BBCs to exist in multiple cells simultaneously.
64 // As a consequence, ownership of BBCs is assumed to be elsewhere and
65 // persistent for at least the life of the BBGrid, or at least until Clear is
66 // called which removes all references to inserted objects without actually
67 // deleting them.
68 // Most uses derive a class from a specific instantiation of BBGrid,
69 // thereby making most of the ugly template notation go away.
70 // The friend class GridSearch, with the same template arguments, is
71 // used to search a grid efficiently in one of several search patterns.
72 template<class BBC, class BBC_CLIST, class BBC_C_IT> class BBGrid {
73   friend class GridSearch<BBC, BBC_CLIST, BBC_C_IT>;
74  public:
75   BBGrid();
76   BBGrid(int gridsize, const ICOORD& bleft, const ICOORD& tright);
77   virtual ~BBGrid();
78 
79   // (Re)Initialize the grid. The gridsize is the size in pixels of each cell,
80   // and bleft, tright are the bounding box of everything to go in it.
81   void Init(int gridsize, const ICOORD& bleft, const ICOORD& tright);
82 
83   // Empty all the lists but leave the grid itself intact.
84   void Clear();
85   // Deallocate the data in the lists but otherwise leave the lists and the grid
86   // intact.
87   void ClearGridData(void (*free_method)(BBC*));
88 
89   // Simple accessors.
gridsize()90   int gridsize() const {
91     return gridsize_;
92   }
gridwidth()93   int gridwidth() const {
94     return gridwidth_;
95   }
gridheight()96   int gridheight() const {
97     return gridheight_;
98   }
bleft()99   ICOORD bleft() const {
100     return bleft_;
101   }
tright()102   ICOORD tright() const {
103     return tright_;
104   }
105 
106   // Insert a bbox into the appropriate place in the grid.
107   // If h_spread, then all cells covered horizontally by the box are
108   // used, otherwise, just the bottom-left. Similarly for v_spread.
109   // WARNING: InsertBBox may invalidate an active GridSearch. Call
110   // RepositionIterator() on any GridSearches that are active on this grid.
111   void InsertBBox(bool h_spread, bool v_spread, BBC* bbox);
112 #ifdef HAVE_LIBLEPT
113   // Using a pix from TraceOutlineOnReducedPix or TraceBlockOnReducedPix, in
114   // which each pixel corresponds to a grid cell, insert a bbox into every
115   // place in the grid where the corresponding pixel is 1. The Pix is handled
116   // upside-down to match the Tesseract coordinate system. (As created by
117   // TraceOutlineOnReducedPix or TraceBlockOnReducedPix.)
118   // (0, 0) in the pix corresponds to (left, bottom) in the
119   // grid (in grid coords), and the pix works up the grid from there.
120   // WARNING: InsertPixPtBBox may invalidate an active GridSearch. Call
121   // RepositionIterator() on any GridSearches that are active on this grid.
122   void InsertPixPtBBox(int left, int bottom, Pix* pix, BBC* bbox);
123 #endif
124   // Remove the bbox from the grid.
125   // WARNING: Any GridSearch operating on this grid could be invalidated!
126   // If a GridSearch is operating, call GridSearch::RemoveBBox() instead.
127   void RemoveBBox(BBC* bbox);
128 
129   // Compute the given grid coordinates from image coords.
130   void GridCoords(int x, int y, int* grid_x, int* grid_y);
131 
132   // Clip the given grid coordinates to fit within the grid.
133   void ClipGridCoords(int* x, int* y);
134 
135   // Make a window of an appropriate size to display things in the grid.
136   ScrollView* MakeWindow(int x, int y, const char* window_name);
137 
138   // Display the bounding boxes of the BLOBNBOXes in this grid.
139   // Use of this function requires an additional member of the BBC class:
140   // ScrollView::Color BBC::BoxColor() const.
141   void DisplayBoxes(ScrollView* window);
142 
143   // ASSERT_HOST that every cell contains no more than one copy of each entry.
144   void AssertNoDuplicates();
145 
146   // Handle a click event in a display window.
147   virtual void HandleClick(int x, int y);
148 
149  protected:
150   int gridsize_;     // Pixel size of each grid cell.
151   int gridwidth_;    // Size of the grid in cells.
152   int gridheight_;
153   int gridbuckets_;  // Total cells in grid.
154   ICOORD bleft_;     // Pixel coords of bottom-left of grid.
155   ICOORD tright_;    // Pixel coords of top-right of grid.
156   BBC_CLIST* grid_;  // 2-d array of CLISTS of BBC elements.
157 
158  private:
159 };
160 
161 // The GridSearch class enables neighbourhood searching on a BBGrid.
162 template<class BBC, class BBC_CLIST, class BBC_C_IT> class GridSearch {
163  public:
GridSearch(BBGrid<BBC,BBC_CLIST,BBC_C_IT> * grid)164   GridSearch(BBGrid<BBC, BBC_CLIST, BBC_C_IT>* grid)
165       : grid_(grid), previous_return_(NULL), next_return_(NULL) {
166   }
167 
168   // Get the grid x, y coords of the most recently returned BBC.
GridX()169   int GridX() const {
170     return x_;
171   }
GridY()172   int GridY() const {
173     return y_;
174   }
175   // Apart from full search, all other searches return a box several
176   // times if the box is inserted with h_spread or v_spread.
177   // This method will return true for only one occurrance of each box
178   // that was inserted with both h_spread and v_spread as true.
179   // It will usually return false for boxes that were not inserted with
180   // both h_spread=true and v_spread=true
ReturnedSeedElement()181   bool ReturnedSeedElement() const {
182     TBOX box = previous_return_->bounding_box();
183     int x_center = (box.left()+box.right())/2;
184     int y_center = (box.top()+box.bottom())/2;
185     int grid_x, grid_y;
186     grid_->GridCoords(x_center, y_center, &grid_x, &grid_y);
187     return (x_ == grid_x) && (y_ == grid_y);
188   }
189 
190   // Various searching iterations... Note that these iterations
191   // all share data members, so you can't run more than one iteration
192   // in parallel in a single GridSearch instance, but multiple instances
193   // can search the same BBGrid in parallel.
194   // Note that all the searches can return blobs that may not exactly
195   // match the search conditions, since they return everything in the
196   // covered grid cells. It is up to the caller to check for
197   // appropriateness.
198 
199   // Start a new full search. Will iterate all stored blobs, from the top.
200   // If the blobs have been inserted using InsertBBox, (not InsertPixPtBBox)
201   // then the full search guarantees to return each blob in the grid once.
202   // Other searches may return a blob more than once if they have been
203   // inserted using h_spread or v_spread.
204   void StartFullSearch();
205   // Return the next bbox in the search or NULL if done.
206   BBC* NextFullSearch();
207 
208   // Start a new radius search. Will search in a spiral upto a
209   // given maximum radius in grid cells from the given center in pixels.
210   void StartRadSearch(int x, int y, int max_radius);
211   // Return the next bbox in the radius search or NULL if the
212   // maximum radius has been reached.
213   BBC* NextRadSearch();
214 
215   // Start a new left or right-looking search. Will search to the side
216   // for a box that vertically overlaps the given vertical line segment.
217   // CAVEAT: This search returns all blobs from the cells to the side
218   // of the start, and somewhat below, since there is no guarantee
219   // that there may not be a taller object in a lower cell. The
220   // blobs returned will include all those that vertically overlap and
221   // are no more than twice as high, but may also include some that do
222   // not overlap and some that are more than twice as high.
223   void StartSideSearch(int x, int ymin, int ymax);
224   // Return the next bbox in the side search or NULL if the
225   // edge has been reached. Searches left to right or right to left
226   // according to the flag.
227   BBC* NextSideSearch(bool right_to_left);
228 
229   // Start a vertical-looking search. Will search up or down
230   // for a box that horizontally overlaps the given line segment.
231   void StartVerticalSearch(int xmin, int xmax, int y);
232   // Return the next bbox in the vertical search or NULL if the
233   // edge has been reached. Searches top to bottom or bottom to top
234   // according to the flag.
235   BBC* NextVerticalSearch(bool top_to_bottom);
236 
237   // Start a rectangular search. Will search for a box that overlaps the
238   // given rectangle.
239   void StartRectSearch(const TBOX& rect);
240   // Return the next bbox in the rectangular search or NULL if complete.
241   BBC* NextRectSearch();
242 
243   // Remove the last returned BBC. Will not invalidate this. May invalidate
244   // any other concurrent GridSearch on the same grid. If any others are
245   // in use, call RepositionIterator on those, to continue without harm.
246   void RemoveBBox();
247   void RepositionIterator();
248 
249  private:
250   // Factored out helper to start a search.
251   void CommonStart(int x, int y);
252   // Factored out helper to complete a next search.
253   BBC* CommonNext();
254   // Factored out final return when search is exhausted.
255   BBC* CommonEnd();
256   // Factored out function to set the iterator to the current x_, y_
257   // grid coords and mark the cycle pt.
258   void SetIterator();
259 
260  private:
261   // The grid we are searching.
262   BBGrid<BBC, BBC_CLIST, BBC_C_IT>* grid_;
263   // For executing a search. The different search algorithms use these in
264   // different ways, but most use x_origin_ and y_origin_ as the start position.
265   int x_origin_;
266   int y_origin_;
267   int max_radius_;
268   int radius_;
269   int rad_index_;
270   int rad_dir_;
271   TBOX rect_;
272   int x_;  // The current location in grid coords, of the current search.
273   int y_;
274   BBC* previous_return_;  // Previous return from Next*.
275   BBC* next_return_;  // Current value of it_.data() used for repositioning.
276   // An iterator over the list at (x_, y_) in the grid_.
277   BBC_C_IT it_;
278 };
279 
280 // Sort function to sort a BBC by bounding_box().left().
281 template<class BBC>
SortByBoxLeft(const void * void1,const void * void2)282 int SortByBoxLeft(const void* void1, const void* void2) {
283   // The void*s are actually doubly indirected, so get rid of one level.
284   const BBC* p1 = *reinterpret_cast<const BBC* const *>(void1);
285   const BBC* p2 = *reinterpret_cast<const BBC* const *>(void2);
286   return p1->bounding_box().left() - p2->bounding_box().left();
287 }
288 
289 ///////////////////////////////////////////////////////////////////////
290 // BBGrid IMPLEMENTATION.
291 ///////////////////////////////////////////////////////////////////////
292 template<class BBC, class BBC_CLIST, class BBC_C_IT>
BBGrid()293 BBGrid<BBC, BBC_CLIST, BBC_C_IT>::BBGrid() : grid_(NULL) {
294 }
295 
296 template<class BBC, class BBC_CLIST, class BBC_C_IT>
BBGrid(int gridsize,const ICOORD & bleft,const ICOORD & tright)297 BBGrid<BBC, BBC_CLIST, BBC_C_IT>::BBGrid(
298   int gridsize, const ICOORD& bleft, const ICOORD& tright)
299     : grid_(NULL) {
300   Init(gridsize, bleft, tright);
301 }
302 
303 template<class BBC, class BBC_CLIST, class BBC_C_IT>
~BBGrid()304 BBGrid<BBC, BBC_CLIST, BBC_C_IT>::~BBGrid() {
305   if (grid_ != NULL)
306     delete [] grid_;
307 }
308 
309 // (Re)Initialize the grid. The gridsize is the size in pixels of each cell,
310 // and bleft, tright are the bounding box of everything to go in it.
311 template<class BBC, class BBC_CLIST, class BBC_C_IT>
Init(int gridsize,const ICOORD & bleft,const ICOORD & tright)312 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::Init(int gridsize,
313                                             const ICOORD& bleft,
314                                             const ICOORD& tright) {
315   gridsize_ = gridsize;
316   bleft_ = bleft;
317   tright_ = tright;
318   if (grid_ != NULL)
319     delete [] grid_;
320   if (gridsize_ == 0)
321     gridsize_ = 1;
322   gridwidth_ = (tright.x() - bleft.x() + gridsize_ - 1) / gridsize_;
323   gridheight_ = (tright.y() - bleft.y() + gridsize_ - 1) / gridsize_;
324   gridbuckets_ = gridwidth_ * gridheight_;
325   grid_ = new BBC_CLIST[gridbuckets_];
326 }
327 
328 // Clear all lists, but leave the array of lists present.
329 template<class BBC, class BBC_CLIST, class BBC_C_IT>
Clear()330 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::Clear() {
331   for (int i = 0; i < gridbuckets_; ++i) {
332     grid_[i].shallow_clear();
333   }
334 }
335 
336 // Deallocate the data in the lists but otherwise leave the lists and the grid
337 // intact.
338 template<class BBC, class BBC_CLIST, class BBC_C_IT>
ClearGridData(void (* free_method)(BBC *))339 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::ClearGridData(
340     void (*free_method)(BBC*)) {
341   if (grid_ == NULL) return;
342   GridSearch<BBC, BBC_CLIST, BBC_C_IT> search(this);
343   search.StartFullSearch();
344   BBC* bb;
345   BBC_CLIST bb_list;
346   BBC_C_IT it(&bb_list);
347   while ((bb = search.NextFullSearch()) != NULL) {
348     it.add_after_then_move(bb);
349   }
350   for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
351     free_method(it.data());
352   }
353 }
354 
355 // Insert a bbox into the appropriate place in the grid.
356 // If h_spread, then all cells covered horizontally by the box are
357 // used, otherwise, just the bottom-left. Similarly for v_spread.
358 // WARNING: InsertBBox may invalidate an active GridSearch. Call
359 // RepositionIterator() on any GridSearches that are active on this grid.
360 template<class BBC, class BBC_CLIST, class BBC_C_IT>
InsertBBox(bool h_spread,bool v_spread,BBC * bbox)361 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::InsertBBox(bool h_spread, bool v_spread,
362                                                   BBC* bbox) {
363   TBOX box = bbox->bounding_box();
364   int start_x, start_y, end_x, end_y;
365   GridCoords(box.left(), box.bottom(), &start_x, &start_y);
366   GridCoords(box.right(), box.top(), &end_x, &end_y);
367   if (!h_spread)
368     end_x = start_x;
369   if (!v_spread)
370     end_y = start_y;
371   int grid_index = start_y * gridwidth_;
372   for (int y = start_y; y <= end_y; ++y, grid_index += gridwidth_) {
373     for (int x = start_x; x <= end_x; ++x) {
374       grid_[grid_index + x].add_sorted(SortByBoxLeft<BBC>, true, bbox);
375     }
376   }
377 }
378 
379 #ifdef HAVE_LIBLEPT
380 // Using a pix from TraceOutlineOnReducedPix or TraceBlockOnReducedPix, in
381 // which each pixel corresponds to a grid cell, insert a bbox into every
382 // place in the grid where the corresponding pixel is 1. The Pix is handled
383 // upside-down to match the Tesseract coordinate system. (As created by
384 // TraceOutlineOnReducedPix or TraceBlockOnReducedPix.)
385 // (0, 0) in the pix corresponds to (left, bottom) in the
386 // grid (in grid coords), and the pix works up the grid from there.
387 // WARNING: InsertPixPtBBox may invalidate an active GridSearch. Call
388 // RepositionIterator() on any GridSearches that are active on this grid.
389 template<class BBC, class BBC_CLIST, class BBC_C_IT>
InsertPixPtBBox(int left,int bottom,Pix * pix,BBC * bbox)390 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::InsertPixPtBBox(int left, int bottom,
391                                                        Pix* pix, BBC* bbox) {
392   int width = pixGetWidth(pix);
393   int height = pixGetHeight(pix);
394   for (int y = 0; y < height; ++y) {
395     l_uint32* data = pixGetData(pix) + y * pixGetWpl(pix);
396     for (int x = 0; x < width; ++x) {
397       if (GET_DATA_BIT(data, x)) {
398         grid_[(bottom + y) * gridwidth_ + x + left].
399           add_sorted(SortByBoxLeft<BBC>, true, bbox);
400       }
401     }
402   }
403 }
404 #endif
405 
406 // Remove the bbox from the grid.
407 // WARNING: Any GridSearch operating on this grid could be invalidated!
408 // If a GridSearch is operating, call GridSearch::RemoveBBox() instead.
409 template<class BBC, class BBC_CLIST, class BBC_C_IT>
RemoveBBox(BBC * bbox)410 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::RemoveBBox(BBC* bbox) {
411   TBOX box = bbox->bounding_box();
412   int start_x, start_y, end_x, end_y;
413   GridCoords(box.left(), box.bottom(), &start_x, &start_y);
414   GridCoords(box.right(), box.top(), &end_x, &end_y);
415   int grid_index = start_y * gridwidth_;
416   for (int y = start_y; y <= end_y; ++y, grid_index += gridwidth_) {
417     for (int x = start_x; x <= end_x; ++x) {
418       BBC_C_IT it(&grid_[grid_index + x]);
419       for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) {
420         if (it.data() == bbox)
421           it.extract();
422       }
423     }
424   }
425 }
426 
427 // Compute the given grid coordinates from image coords.
428 template<class BBC, class BBC_CLIST, class BBC_C_IT>
GridCoords(int x,int y,int * grid_x,int * grid_y)429 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::GridCoords(int x, int y,
430                                                   int* grid_x, int* grid_y) {
431   *grid_x = (x - bleft_.x()) / gridsize_;
432   *grid_y = (y - bleft_.y()) / gridsize_;
433   ClipGridCoords(grid_x, grid_y);
434 }
435 
436 // Clip the given grid coordinates to fit within the grid.
437 template<class BBC, class BBC_CLIST, class BBC_C_IT>
ClipGridCoords(int * x,int * y)438 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::ClipGridCoords(int* x, int* y) {
439   if (*x < 0) *x = 0;
440   if (*x >= gridwidth_) *x = gridwidth_ - 1;
441   if (*y < 0) *y = 0;
442   if (*y >= gridheight_) *y = gridheight_ - 1;
443 }
444 
445 template<class G> class TabEventHandler : public SVEventHandler {
446  public:
TabEventHandler(G * grid)447   explicit TabEventHandler(G* grid) : grid_(grid) {
448   }
Notify(const SVEvent * sv_event)449   void Notify(const SVEvent* sv_event) {
450     if (sv_event->type == SVET_CLICK) {
451       grid_->HandleClick(sv_event->x, sv_event->y);
452     }
453   }
454  private:
455   G* grid_;
456 };
457 
458 // Make a window of an appropriate size to display things in the grid.
459 // Position the window at the given x,y.
460 template<class BBC, class BBC_CLIST, class BBC_C_IT>
MakeWindow(int x,int y,const char * window_name)461 ScrollView* BBGrid<BBC, BBC_CLIST, BBC_C_IT>::MakeWindow(
462     int x, int y, const char* window_name) {
463   ScrollView* tab_win = NULL;
464 #ifndef GRAPHICS_DISABLED
465   tab_win = new ScrollView(window_name, x, y,
466                            tright_.x() - bleft_.x(),
467                            tright_.y() - bleft_.y(),
468                            tright_.x() - bleft_.x(),
469                            tright_.y() - bleft_.y(),
470                            true);
471   TabEventHandler<BBGrid<BBC, BBC_CLIST, BBC_C_IT> >* handler =
472     new TabEventHandler<BBGrid<BBC, BBC_CLIST, BBC_C_IT> >(this);
473   tab_win->AddEventHandler(handler);
474   tab_win->Pen(ScrollView::GREY);
475   tab_win->Rectangle(0, 0, tright_.x(), tright_.y());
476 #endif
477   return tab_win;
478 }
479 
480 // Create a window at (x,y) and display the bounding boxes of the
481 // BLOBNBOXes in this grid.
482 // Use of this function requires an additional member of the BBC class:
483 // ScrollView::Color BBC::BoxColor() const.
484 template<class BBC, class BBC_CLIST, class BBC_C_IT>
DisplayBoxes(ScrollView * tab_win)485 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::DisplayBoxes(ScrollView* tab_win) {
486 #ifndef GRAPHICS_DISABLED
487   tab_win->Pen(ScrollView::BLUE);
488   tab_win->Brush(ScrollView::NONE);
489 
490   // For every bbox in the grid, display it.
491   GridSearch<BBC, BBC_CLIST, BBC_C_IT> gsearch(this);
492   gsearch.StartFullSearch();
493   BBC* bbox;
494   while ((bbox = gsearch.NextFullSearch()) != NULL) {
495     TBOX box = bbox->bounding_box();
496     int left_x = box.left();
497     int right_x = box.right();
498     int top_y = box.top();
499     int bottom_y = box.bottom();
500     ScrollView::Color box_color = bbox->BoxColor();
501     tab_win->Pen(box_color);
502     tab_win->Rectangle(left_x, bottom_y, right_x, top_y);
503   }
504   tab_win->Update();
505 #endif
506 }
507 
508 // ASSERT_HOST that every cell contains no more than one copy of each entry.
509 template<class BBC, class BBC_CLIST, class BBC_C_IT>
AssertNoDuplicates()510 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::AssertNoDuplicates() {
511   // Process all grid cells.
512   for (int i = gridwidth_ * gridheight_ - 1; i >= 0; --i) {
513     // Iterate over all elements excent the last.
514     for (BBC_C_IT it(&grid_[i]); !it.at_last(); it.forward()) {
515       BBC* ptr = it.data();
516       BBC_C_IT it2(it);
517       // None of the rest of the elements in the list should equal ptr.
518       for (it2.forward(); !it2.at_first(); it2.forward()) {
519         ASSERT_HOST(it2.data() != ptr);
520       }
521     }
522   }
523 }
524 
525 // Handle a click event in a display window.
526 template<class BBC, class BBC_CLIST, class BBC_C_IT>
HandleClick(int x,int y)527 void BBGrid<BBC, BBC_CLIST, BBC_C_IT>::HandleClick(int x, int y) {
528   tprintf("Click at (%d, %d)\n", x, y);
529 }
530 
531 ///////////////////////////////////////////////////////////////////////
532 // GridSearch IMPLEMENTATION.
533 ///////////////////////////////////////////////////////////////////////
534 
535 // Start a new full search. Will iterate all stored blobs.
536 template<class BBC, class BBC_CLIST, class BBC_C_IT>
StartFullSearch()537 void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::StartFullSearch() {
538   // Full search uses x_ and y_ as the current grid
539   // cell being searched.
540   CommonStart(grid_->bleft_.x(), grid_->tright_.y());
541 }
542 
543 // Return the next bbox in the search or NULL if done.
544 // The other searches will return a box that overlaps the grid cell
545 // thereby duplicating boxes, but NextFullSearch only returns each box once.
546 template<class BBC, class BBC_CLIST, class BBC_C_IT>
NextFullSearch()547 BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::NextFullSearch() {
548   int x;
549   int y;
550   do {
551     while (it_.cycled_list()) {
552       ++x_;
553       if (x_ >= grid_->gridwidth_) {
554         --y_;
555         if (y_ < 0)
556           return CommonEnd();
557         x_ = 0;
558       }
559       SetIterator();
560     }
561     CommonNext();
562     TBOX box = previous_return_->bounding_box();
563     grid_->GridCoords(box.left(), box.bottom(), &x, &y);
564   } while (x != x_ || y != y_);
565   return previous_return_;
566 }
567 
568 // Start a new radius search.
569 template<class BBC, class BBC_CLIST, class BBC_C_IT>
StartRadSearch(int x,int y,int max_radius)570 void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::StartRadSearch(int x, int y,
571                                                           int max_radius) {
572   // Rad search uses x_origin_ and y_origin_ as the center of the circle.
573   // The radius_ is the radius of the (diamond-shaped) circle and
574   // rad_index/rad_dir_ combine to determine the position around it.
575   max_radius_ = max_radius;
576   radius_ = 0;
577   rad_index_ = 0;
578   rad_dir_ = 3;
579   CommonStart(x, y);
580 }
581 
582 // Return the next bbox in the radius search or NULL if the
583 // maximum radius has been reached.
584 template<class BBC, class BBC_CLIST, class BBC_C_IT>
NextRadSearch()585 BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::NextRadSearch() {
586   while (it_.cycled_list()) {
587     ++rad_index_;
588     if (rad_index_ >= radius_) {
589       ++rad_dir_;
590       rad_index_ = 0;
591       if (rad_dir_ >= 4) {
592         ++radius_;
593         if (radius_ > max_radius_)
594           return CommonEnd();
595         rad_dir_ = 0;
596       }
597     }
598     ICOORD offset = C_OUTLINE::chain_step(rad_dir_);
599     offset *= radius_ - rad_index_;
600     offset += C_OUTLINE::chain_step(rad_dir_ + 1) * rad_index_;
601     x_ = x_origin_ + offset.x();
602     y_ = y_origin_ + offset.y();
603     if (x_ >= 0 && x_ < grid_->gridwidth_ &&
604         y_ >= 0 && y_ < grid_->gridheight_)
605       SetIterator();
606   }
607   return CommonNext();
608 }
609 
610 // Start a new left or right-looking search. Will search to the side
611 // for a box that vertically overlaps the given vertical line segment.
612 template<class BBC, class BBC_CLIST, class BBC_C_IT>
StartSideSearch(int x,int ymin,int ymax)613 void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::StartSideSearch(int x,
614                                                            int ymin, int ymax) {
615   // Right search records the x in x_origin_, the ymax in y_origin_
616   // and the size of the vertical strip to search in radius_.
617   // To guarantee finding overlapping objects of upto twice the
618   // given size, double the height.
619   radius_ = ((ymax - ymin) * 2 + grid_->gridsize_ - 1) / grid_->gridsize_;
620   rad_index_ = 0;
621   CommonStart(x, ymax);
622 }
623 
624 // Return the next bbox in the side search or NULL if the
625 // edge has been reached. Searches left to right or right to left
626 // according to the flag.
627 template<class BBC, class BBC_CLIST, class BBC_C_IT>
NextSideSearch(bool right_to_left)628 BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::NextSideSearch(bool right_to_left) {
629   while (it_.cycled_list()) {
630     ++rad_index_;
631     if (rad_index_ > radius_) {
632       if (right_to_left)
633         --x_;
634       else
635         ++x_;
636       rad_index_ = 0;
637       if (x_ < 0 || x_ >= grid_->gridwidth_)
638         return CommonEnd();
639     }
640     y_ = y_origin_ - rad_index_;
641     if (y_ >= 0 && y_ < grid_->gridheight_)
642       SetIterator();
643   }
644   return CommonNext();
645 }
646 
647 // Start a vertical-looking search. Will search up or down
648 // for a box that horizontally overlaps the given line segment.
649 template<class BBC, class BBC_CLIST, class BBC_C_IT>
StartVerticalSearch(int xmin,int xmax,int y)650 void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::StartVerticalSearch(int xmin,
651                                                                int xmax,
652                                                                int y) {
653   // Right search records the xmin in x_origin_, the y in y_origin_
654   // and the size of the horizontal strip to search in radius_.
655   radius_ = (xmax - xmin + grid_->gridsize_ - 1) / grid_->gridsize_;
656   rad_index_ = 0;
657   CommonStart(xmin, y);
658 }
659 
660 // Return the next bbox in the vertical search or NULL if the
661 // edge has been reached. Searches top to bottom or bottom to top
662 // according to the flag.
663 template<class BBC, class BBC_CLIST, class BBC_C_IT>
NextVerticalSearch(bool top_to_bottom)664 BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::NextVerticalSearch(
665     bool top_to_bottom) {
666   while (it_.cycled_list()) {
667     ++rad_index_;
668     if (rad_index_ > radius_) {
669       if (top_to_bottom)
670         --y_;
671       else
672         ++y_;
673       rad_index_ = 0;
674       if (y_ < 0 || y_ >= grid_->gridheight_)
675         return CommonEnd();
676     }
677     x_ = x_origin_ + rad_index_;
678     if (x_ >= 0 && x_ < grid_->gridwidth_)
679       SetIterator();
680   }
681   return CommonNext();
682 }
683 
684 // Start a rectangular search. Will search for a box that overlaps the
685 // given rectangle.
686 template<class BBC, class BBC_CLIST, class BBC_C_IT>
StartRectSearch(const TBOX & rect)687 void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::StartRectSearch(const TBOX& rect) {
688   // Rect search records the xmin in x_origin_, the ymin in y_origin_
689   // and the xmax in max_radius_.
690   // The search proceeds left to right, top to bottom.
691   rect_ = rect;
692   CommonStart(rect.left(), rect.top());
693   grid_->GridCoords(rect.right(), rect.bottom(),  // - rect.height(),
694                     &max_radius_, &y_origin_);
695 }
696 
697 // Return the next bbox in the rectangular search or NULL if complete.
698 template<class BBC, class BBC_CLIST, class BBC_C_IT>
NextRectSearch()699 BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::NextRectSearch() {
700   while (it_.cycled_list()) {
701     ++x_;
702     if (x_ > max_radius_) {
703       --y_;
704       x_ = x_origin_;
705       if (y_ < y_origin_)
706         return CommonEnd();
707     }
708     SetIterator();
709   }
710   return CommonNext();
711 }
712 
713 // Remove the last returned BBC. Will not invalidate this. May invalidate
714 // any other concurrent GridSearch on the same grid. If any others are
715 // in use, call RepositionIterator on those, to continue without harm.
716 template<class BBC, class BBC_CLIST, class BBC_C_IT>
RemoveBBox()717 void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::RemoveBBox() {
718   if (previous_return_ != NULL) {
719     // Remove all instances of previous_return_ from the list, so the iterator
720     // remains valid after removal from the rest of the grid cells.
721     // if previous_return_ is not on the list, then it has been removed already.
722     BBC* prev_data = NULL;
723     BBC* new_previous_return = NULL;
724     it_.move_to_first();
725     for (it_.mark_cycle_pt(); !it_.cycled_list();) {
726       if (it_.data() ==  previous_return_) {
727         new_previous_return = prev_data;
728         it_.extract();
729         it_.forward();
730         next_return_ = it_.cycled_list() ? NULL : it_.data();
731       } else {
732         prev_data = it_.data();
733         it_.forward();
734       }
735     }
736     grid_->RemoveBBox(previous_return_);
737     previous_return_ = new_previous_return;
738     RepositionIterator();
739   }
740 }
741 
742 template<class BBC, class BBC_CLIST, class BBC_C_IT>
RepositionIterator()743 void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::RepositionIterator() {
744   // Reset the iterator back to one past the previous return.
745   // If the previous_return_ is no longer in the list, then
746   // next_return_ serves as a backup.
747   it_.move_to_first();
748   for (it_.mark_cycle_pt(); !it_.cycled_list(); it_.forward()) {
749     if (it_.data() == previous_return_ ||
750         it_.data_relative(1) == next_return_) {
751       CommonNext();
752       return;
753     }
754   }
755   // We ran off the end of the list. Move to a new cell next time.
756   previous_return_ = NULL;
757   next_return_ = NULL;
758 }
759 
760 // Factored out helper to start a search.
761 template<class BBC, class BBC_CLIST, class BBC_C_IT>
CommonStart(int x,int y)762 void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::CommonStart(int x, int y) {
763   grid_->GridCoords(x, y, &x_origin_, &y_origin_);
764   x_ = x_origin_;
765   y_ = y_origin_;
766   SetIterator();
767   previous_return_ = NULL;
768   next_return_ = it_.empty() ? NULL : it_.data();
769 }
770 
771 // Factored out helper to complete a next search.
772 template<class BBC, class BBC_CLIST, class BBC_C_IT>
CommonNext()773 BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::CommonNext() {
774   previous_return_ = it_.data();
775   it_.forward();
776   next_return_ = it_.cycled_list() ? NULL : it_.data();
777   return previous_return_;
778 }
779 
780 // Factored out final return when search is exhausted.
781 template<class BBC, class BBC_CLIST, class BBC_C_IT>
CommonEnd()782 BBC* GridSearch<BBC, BBC_CLIST, BBC_C_IT>::CommonEnd() {
783   previous_return_ = NULL;
784   next_return_ = NULL;
785   return NULL;
786 }
787 
788 // Factored out function to set the iterator to the current x_, y_
789 // grid coords and mark the cycle pt.
790 template<class BBC, class BBC_CLIST, class BBC_C_IT>
SetIterator()791 void GridSearch<BBC, BBC_CLIST, BBC_C_IT>::SetIterator() {
792   it_= &(grid_->grid_[y_ * grid_->gridwidth_ + x_]);
793   it_.mark_cycle_pt();
794 }
795 
796 }  // namespace tesseract.
797 
798 #endif  // TESSERACT_TEXTORD_BBGRID_H__
799