~vcs-imports/tesseract-ocr/trunk

1 by tmbdev
top-skimming import from sf.net
1
/**********************************************************************
2
 * File:        fpchop.cpp  (Formerly fp_chop.c)
3
 * Description: Code to chop fixed pitch text into character cells.
4
 * Author:		Ray Smith
5
 * Created:		Thu Sep 16 11:14:15 BST 1993
6
 *
7
 * (C) Copyright 1993, Hewlett-Packard Ltd.
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
 *
18
 **********************************************************************/
19
20
#ifdef __UNIX__
21
#include          <assert.h>
22
#endif
23
#include          "stderr.h"
24
#include          "blobbox.h"
25
#include          "statistc.h"
26
#include          "drawtord.h"
27
#include          "tovars.h"
28
#include          "topitch.h"
29
#include          "fpchop.h"
30
351 by joregan
move include of config_auto.h to not conflict with local types. Not finished
31
// Include automatically generated configuration file if running autoconf.
32
#ifdef HAVE_CONFIG_H
33
#include "config_auto.h"
34
#endif
35
1 by tmbdev
top-skimming import from sf.net
36
#define EXTERN
37
38
EXTERN INT_VAR (textord_fp_chop_error, 2,
39
"Max allowed bending of chop cells");
40
EXTERN double_VAR (textord_fp_chop_snap, 0.5,
41
"Max distance of chop pt from vertex");
42
415 by theraysmith
Deleted lots of dead code, including PBLOB
43
ELISTIZE(C_OUTLINE_FRAG)
1 by tmbdev
top-skimming import from sf.net
44
//#undef ASSERT_HOST
45
//#define ASSERT_HOST(x) if (!(x)) AfxMessageBox(#x);
46
/**********************************************************************
47
 * fixed_pitch_words
48
 *
49
 * Make a ROW from a fixed pitch TO_ROW.
50
 **********************************************************************/
51
ROW *fixed_pitch_words(                 //find lines
52
                       TO_ROW *row,     //row to do
53
                       FCOORD rotation  //for drawing
54
                      ) {
55
  BOOL8 bol;                     //start of line
112 by theraysmith
Fixed name collision with jpeg library
56
  uinT8 blanks;                  //in front of word
57
  uinT8 new_blanks;              //blanks in empty cell
58
  inT16 chop_coord;              //chop boundary
59
  inT16 prev_chop_coord;         //start of cell
60
  inT16 rep_left;                //left edge of rep word
1 by tmbdev
top-skimming import from sf.net
61
  ROW *real_row;                 //output row
62
  C_OUTLINE_LIST left_coutlines;
63
  C_OUTLINE_LIST right_coutlines;
64
  C_BLOB_LIST cblobs;
65
  C_BLOB_IT cblob_it = &cblobs;
66
  WERD_LIST words;
67
  WERD_IT word_it = &words;      //new words
68
                                 //repeated blobs
69
  WERD_IT rep_it = &row->rep_words;
70
  WERD *word;                    //new word
112 by theraysmith
Fixed name collision with jpeg library
71
  inT32 xstarts[2];              //row ends
72
  inT32 prev_x;                  //end of prev blob
1 by tmbdev
top-skimming import from sf.net
73
                                 //iterator
74
  BLOBNBOX_IT box_it = row->blob_list ();
75
                                 //boundaries
76
  ICOORDELT_IT cell_it = &row->char_cells;
77
78
#ifndef GRAPHICS_DISABLED
101 by theraysmith
Updated graphics output for new java-based display
79
  if (textord_show_page_cuts && to_win != NULL) {
80
    plot_row_cells (to_win, ScrollView::RED, row, 0, &row->char_cells);
1 by tmbdev
top-skimming import from sf.net
81
  }
82
#endif
83
84
  prev_x = -MAX_INT16;
85
  bol = TRUE;
86
  blanks = 0;
87
  if (rep_it.empty ())
88
    rep_left = MAX_INT16;
89
  else
90
    rep_left = rep_it.data ()->bounding_box ().left ();
91
  if (box_it.empty ())
92
    return NULL;                 //empty row
93
  xstarts[0] = box_it.data ()->bounding_box ().left ();
94
  if (rep_left < xstarts[0]) {
95
    xstarts[0] = rep_left;
96
  }
97
  if (cell_it.empty () || row->char_cells.singleton ()) {
98
    tprintf ("Row without enough char cells!\n");
99
    tprintf ("Leftmost blob is at (%d,%d)\n",
100
      box_it.data ()->bounding_box ().left (),
101
      box_it.data ()->bounding_box ().bottom ());
102
    return NULL;
103
  }
104
  ASSERT_HOST (!cell_it.empty () && !row->char_cells.singleton ());
105
  prev_chop_coord = cell_it.data ()->x ();
106
  word = NULL;
107
  while (rep_left < cell_it.data ()->x ()) {
108
    word = add_repeated_word (&rep_it, rep_left, prev_chop_coord,
109
      blanks, row->fixed_pitch, &word_it);
110
  }
111
  cell_it.mark_cycle_pt ();
112
  if (prev_chop_coord >= cell_it.data ()->x ())
113
    cell_it.forward ();
114
  for (; !cell_it.cycled_list (); cell_it.forward ()) {
115
    chop_coord = cell_it.data ()->x ();
116
    while (!box_it.empty ()
117
    && box_it.data ()->bounding_box ().left () <= chop_coord) {
118
      if (box_it.data ()->bounding_box ().right () > prev_x)
119
        prev_x = box_it.data ()->bounding_box ().right ();
120
      split_to_blob (box_it.extract (), chop_coord,
121
        textord_fp_chop_error + 0.5f,
415 by theraysmith
Deleted lots of dead code, including PBLOB
122
        &left_coutlines,
123
        &right_coutlines);
1 by tmbdev
top-skimming import from sf.net
124
      box_it.forward ();
415 by theraysmith
Deleted lots of dead code, including PBLOB
125
      while (!box_it.empty() && box_it.data()->cblob() == NULL) {
126
        delete box_it.extract();
127
        box_it.forward();
1 by tmbdev
top-skimming import from sf.net
128
      }
129
    }
415 by theraysmith
Deleted lots of dead code, including PBLOB
130
    if (!right_coutlines.empty() && left_coutlines.empty())
1 by tmbdev
top-skimming import from sf.net
131
      split_to_blob (NULL, chop_coord,
132
        textord_fp_chop_error + 0.5f,
415 by theraysmith
Deleted lots of dead code, including PBLOB
133
        &left_coutlines,
134
        &right_coutlines);
668 by theraysmith at gmail
Major refactor of beam search, elimination of dead code, misc bug fixes, updates to Makefile.am, Changelog etc.
135
    if (!left_coutlines.empty()) {
136
      cblob_it.add_after_then_move(new C_BLOB(&left_coutlines));
137
    } else {
1 by tmbdev
top-skimming import from sf.net
138
      if (rep_left < chop_coord) {
139
        if (rep_left > prev_chop_coord)
112 by theraysmith
Fixed name collision with jpeg library
140
          new_blanks = (uinT8) floor ((rep_left - prev_chop_coord)
1 by tmbdev
top-skimming import from sf.net
141
            / row->fixed_pitch + 0.5);
142
        else
143
          new_blanks = 0;
144
      }
145
      else {
146
        if (chop_coord > prev_chop_coord)
112 by theraysmith
Fixed name collision with jpeg library
147
          new_blanks = (uinT8) floor ((chop_coord - prev_chop_coord)
1 by tmbdev
top-skimming import from sf.net
148
            / row->fixed_pitch + 0.5);
149
        else
150
          new_blanks = 0;
151
      }
415 by theraysmith
Deleted lots of dead code, including PBLOB
152
      if (!cblob_it.empty()) {
1 by tmbdev
top-skimming import from sf.net
153
        if (blanks < 1 && word != NULL && !word->flag (W_REP_CHAR))
154
          blanks = 1;
415 by theraysmith
Deleted lots of dead code, including PBLOB
155
        word = new WERD (&cblobs, blanks, NULL);
156
        cblob_it.set_to_list (&cblobs);
1 by tmbdev
top-skimming import from sf.net
157
        word->set_flag (W_DONT_CHOP, TRUE);
158
        word_it.add_after_then_move (word);
159
        if (bol) {
160
          word->set_flag (W_BOL, TRUE);
161
          bol = FALSE;
162
        }
163
        blanks = new_blanks;
164
      }
165
      else
166
        blanks += new_blanks;
167
      while (rep_left < chop_coord) {
168
        word = add_repeated_word (&rep_it, rep_left, prev_chop_coord,
169
          blanks, row->fixed_pitch, &word_it);
170
      }
171
    }
172
    if (prev_chop_coord < chop_coord)
173
      prev_chop_coord = chop_coord;
174
  }
415 by theraysmith
Deleted lots of dead code, including PBLOB
175
  if (!cblob_it.empty()) {
176
    word = new WERD(&cblobs, blanks, NULL);
1 by tmbdev
top-skimming import from sf.net
177
    word->set_flag (W_DONT_CHOP, TRUE);
178
    word_it.add_after_then_move (word);
179
    if (bol)
180
      word->set_flag (W_BOL, TRUE);
181
  }
182
  ASSERT_HOST (word != NULL);
183
  while (!rep_it.empty ()) {
184
    add_repeated_word (&rep_it, rep_left, prev_chop_coord,
185
      blanks, row->fixed_pitch, &word_it);
186
  }
187
                                 //at end of line
188
  word_it.data ()->set_flag (W_EOL, TRUE);
189
  if (prev_chop_coord > prev_x)
190
    prev_x = prev_chop_coord;
191
  xstarts[1] = prev_x + 1;
112 by theraysmith
Fixed name collision with jpeg library
192
  real_row = new ROW (row, (inT16) row->kern_size, (inT16) row->space_size);
1 by tmbdev
top-skimming import from sf.net
193
  word_it.set_to_list (real_row->word_list ());
194
                                 //put words in row
195
  word_it.add_list_after (&words);
196
  real_row->recalc_bounding_box ();
197
  return real_row;
198
}
199
200
201
/**********************************************************************
202
 * add_repeated_word
203
 *
204
 * Add repeated word into the row at the given point.
205
 **********************************************************************/
206
207
WERD *add_repeated_word(                         //move repeated word
208
                        WERD_IT *rep_it,         //repeated words
112 by theraysmith
Fixed name collision with jpeg library
209
                        inT16 &rep_left,         //left edge of word
210
                        inT16 &prev_chop_coord,  //previous word end
211
                        uinT8 &blanks,           //no of blanks
1 by tmbdev
top-skimming import from sf.net
212
                        float pitch,             //char cell size
213
                        WERD_IT *word_it         //list of words
214
                       ) {
215
  WERD *word;                    //word to move
112 by theraysmith
Fixed name collision with jpeg library
216
  inT16 new_blanks;              //extra blanks
1 by tmbdev
top-skimming import from sf.net
217
218
  if (rep_left > prev_chop_coord) {
112 by theraysmith
Fixed name collision with jpeg library
219
    new_blanks = (uinT8) floor ((rep_left - prev_chop_coord) / pitch + 0.5);
1 by tmbdev
top-skimming import from sf.net
220
    blanks += new_blanks;
221
  }
222
  word = rep_it->extract ();
223
  prev_chop_coord = word->bounding_box ().right ();
224
  word_it->add_after_then_move (word);
225
  word->set_blanks (blanks);
226
  rep_it->forward ();
227
  if (rep_it->empty ())
228
    rep_left = MAX_INT16;
229
  else
230
    rep_left = rep_it->data ()->bounding_box ().left ();
231
  blanks = 0;
232
  return word;
233
}
234
235
236
/**********************************************************************
237
 * split_to_blob
238
 *
239
 * Split a BLOBNBOX across a vertical chop line and put the pieces
240
 * into a left outline list and a right outline list.
241
 **********************************************************************/
242
243
void split_to_blob(                                 //split the blob
244
                   BLOBNBOX *blob,                  //blob to split
112 by theraysmith
Fixed name collision with jpeg library
245
                   inT16 chop_coord,                //place to chop
1 by tmbdev
top-skimming import from sf.net
246
                   float pitch_error,               //allowed deviation
247
                   C_OUTLINE_LIST *left_coutlines,  //for cblobs
248
                   C_OUTLINE_LIST *right_coutlines) {
249
  C_BLOB *real_cblob;            //cblob to chop
250
251
  if (blob != NULL) {
415 by theraysmith
Deleted lots of dead code, including PBLOB
252
    real_cblob = blob->cblob();
253
  } else {
1 by tmbdev
top-skimming import from sf.net
254
    real_cblob = NULL;
255
  }
415 by theraysmith
Deleted lots of dead code, including PBLOB
256
  if (!right_coutlines->empty() || real_cblob != NULL)
1 by tmbdev
top-skimming import from sf.net
257
    fixed_chop_cblob(real_cblob,
258
                     chop_coord,
259
                     pitch_error,
260
                     left_coutlines,
261
                     right_coutlines);
262
  if (blob != NULL)
263
    delete blob;                 //free it
264
}
265
266
/**********************************************************************
267
 * fixed_chop_cblob
268
 *
269
 * Chop the given cblob (if any) and the existing right outlines to
270
 * produce a list of outlines left of the chop point and more to the right.
271
 **********************************************************************/
272
273
void fixed_chop_cblob(                                //split the blob
274
                      C_BLOB *blob,                   //blob to split
112 by theraysmith
Fixed name collision with jpeg library
275
                      inT16 chop_coord,               //place to chop
1 by tmbdev
top-skimming import from sf.net
276
                      float pitch_error,              //allowed deviation
277
                      C_OUTLINE_LIST *left_outlines,  //left half of chop
278
                      C_OUTLINE_LIST *right_outlines  //right half of chop
279
                     ) {
280
  C_OUTLINE *old_right;          //already there
281
  C_OUTLINE_LIST new_outlines;   //new right ones
282
                                 //ouput iterator
283
  C_OUTLINE_IT left_it = left_outlines;
284
                                 //in/out iterator
285
  C_OUTLINE_IT right_it = right_outlines;
286
  C_OUTLINE_IT new_it = &new_outlines;
287
  C_OUTLINE_IT blob_it;          //outlines in blob
288
289
  if (!right_it.empty ()) {
290
    while (!right_it.empty ()) {
291
      old_right = right_it.extract ();
292
      right_it.forward ();
293
      fixed_split_coutline(old_right,
294
                           chop_coord,
295
                           pitch_error,
296
                           &left_it,
297
                           &new_it);
298
    }
299
    right_it.add_list_before (&new_outlines);
300
  }
301
  if (blob != NULL) {
302
    blob_it.set_to_list (blob->out_list ());
303
    for (blob_it.mark_cycle_pt (); !blob_it.cycled_list ();
304
      blob_it.forward ())
305
    fixed_split_coutline (blob_it.extract (), chop_coord, pitch_error,
306
        &left_it, &right_it);
307
    delete blob;
308
  }
309
}
310
311
312
/**********************************************************************
313
 * fixed_split_outline
314
 *
315
 * Chop the given outline (if necessary) placing the fragments which
316
 * fall either side of the chop line into the appropriate list.
317
 **********************************************************************/
318
319
void fixed_split_coutline(                        //chop the outline
320
                          C_OUTLINE *srcline,     //source outline
112 by theraysmith
Fixed name collision with jpeg library
321
                          inT16 chop_coord,       //place to chop
1 by tmbdev
top-skimming import from sf.net
322
                          float pitch_error,      //allowed deviation
323
                          C_OUTLINE_IT *left_it,  //left half of chop
324
                          C_OUTLINE_IT *right_it  //right half of chop
325
                         ) {
326
  C_OUTLINE *child;              //child outline
112 by theraysmith
Fixed name collision with jpeg library
327
  TBOX srcbox;                    //box of outline
1 by tmbdev
top-skimming import from sf.net
328
  C_OUTLINE_LIST left_ch;        //left children
329
  C_OUTLINE_LIST right_ch;       //right children
330
  C_OUTLINE_FRAG_LIST left_frags;//chopped fragments
331
  C_OUTLINE_FRAG_LIST right_frags;;
332
  C_OUTLINE_IT left_ch_it = &left_ch;
333
                                 //for whole children
334
  C_OUTLINE_IT right_ch_it = &right_ch;
335
                                 //for holes
336
  C_OUTLINE_IT child_it = srcline->child ();
337
668 by theraysmith at gmail
Major refactor of beam search, elimination of dead code, misc bug fixes, updates to Makefile.am, Changelog etc.
338
  srcbox = srcline->bounding_box();
339
  if (srcbox.left() + srcbox.right() <= chop_coord * 2
340
      && srcbox.right() < chop_coord + pitch_error) {
341
    // Whole outline is in the left side or not far over the chop_coord,
342
    // so put the whole thing on the left.
343
    left_it->add_after_then_move(srcline);
344
  } else if (srcbox.left() + srcbox.right() > chop_coord * 2
345
             && srcbox.left () > chop_coord - pitch_error) {
346
    // Whole outline is in the right side or not far over the chop_coord,
347
    // so put the whole thing on the right.
348
   right_it->add_before_stay_put(srcline);
349
  } else {
350
    // Needs real chopping.
351
    if (fixed_chop_coutline(srcline, chop_coord, pitch_error,
352
        &left_frags, &right_frags)) {
353
      for (child_it.mark_cycle_pt(); !child_it.cycled_list();
354
           child_it.forward()) {
355
        child = child_it.extract();
356
        srcbox = child->bounding_box();
357
        if (srcbox.right() < chop_coord) {
358
          // Whole child is on the left.
359
          left_ch_it.add_after_then_move(child);
360
        } else if (srcbox.left() > chop_coord) {
361
          // Whole child is on the right.
1 by tmbdev
top-skimming import from sf.net
362
          right_ch_it.add_after_then_move (child);
668 by theraysmith at gmail
Major refactor of beam search, elimination of dead code, misc bug fixes, updates to Makefile.am, Changelog etc.
363
        } else {
364
          // No pitch_error is allowed when chopping children to prevent
365
          // impossible outlines from being created.
366
          if (fixed_chop_coutline(child, chop_coord, 0.0f,
367
              &left_frags, &right_frags)) {
1 by tmbdev
top-skimming import from sf.net
368
            delete child;
668 by theraysmith at gmail
Major refactor of beam search, elimination of dead code, misc bug fixes, updates to Makefile.am, Changelog etc.
369
          } else {
370
            if (srcbox.left() + srcbox.right() <= chop_coord * 2)
371
              left_ch_it.add_after_then_move(child);
1 by tmbdev
top-skimming import from sf.net
372
            else
668 by theraysmith at gmail
Major refactor of beam search, elimination of dead code, misc bug fixes, updates to Makefile.am, Changelog etc.
373
              right_ch_it.add_after_then_move(child);
1 by tmbdev
top-skimming import from sf.net
374
          }
375
        }
376
      }
112 by theraysmith
Fixed name collision with jpeg library
377
      close_chopped_cfragments(&left_frags, &left_ch, pitch_error, left_it);
378
      close_chopped_cfragments(&right_frags, &right_ch, pitch_error, right_it);
668 by theraysmith at gmail
Major refactor of beam search, elimination of dead code, misc bug fixes, updates to Makefile.am, Changelog etc.
379
      ASSERT_HOST(left_ch.empty() && right_ch.empty());
380
      // No children left.
381
      delete srcline;            // Smashed up.
382
    } else {
383
      // Chop failed. Just use middle coord.
384
      if (srcbox.left() + srcbox.right() <= chop_coord * 2)
385
        left_it->add_after_then_move(srcline);  // Stick whole in left.
1 by tmbdev
top-skimming import from sf.net
386
      else
668 by theraysmith at gmail
Major refactor of beam search, elimination of dead code, misc bug fixes, updates to Makefile.am, Changelog etc.
387
        right_it->add_before_stay_put(srcline);
1 by tmbdev
top-skimming import from sf.net
388
    }
389
  }
390
}
391
392
393
/**********************************************************************
394
 * fixed_chop_coutline
395
 *
396
 * Chop the given coutline (if necessary) placing the fragments which
397
 * fall either side of the chop line into the appropriate list.
398
 * If the coutline lies too heavily to one side to chop, FALSE is returned.
399
 **********************************************************************/
400
401
BOOL8 fixed_chop_coutline(                                  //chop the outline
402
                          C_OUTLINE *srcline,               //source outline
112 by theraysmith
Fixed name collision with jpeg library
403
                          inT16 chop_coord,                 //place to chop
1 by tmbdev
top-skimming import from sf.net
404
                          float pitch_error,                //allowed deviation
405
                          C_OUTLINE_FRAG_LIST *left_frags,  //left half of chop
406
                          C_OUTLINE_FRAG_LIST *right_frags  //right half of chop
407
                         ) {
408
  BOOL8 first_frag;              //fragment
112 by theraysmith
Fixed name collision with jpeg library
409
  inT16 left_edge;               //of outline
410
  inT16 startindex;              //in first fragment
411
  inT32 length;                  //of outline
412
  inT16 stepindex;               //into outline
413
  inT16 head_index;              //start of fragment
1 by tmbdev
top-skimming import from sf.net
414
  ICOORD head_pos;               //start of fragment
112 by theraysmith
Fixed name collision with jpeg library
415
  inT16 tail_index;              //end of fragment
1 by tmbdev
top-skimming import from sf.net
416
  ICOORD tail_pos;               //end of fragment
417
  ICOORD pos;                    //current point
112 by theraysmith
Fixed name collision with jpeg library
418
  inT16 first_index = 0;         //first tail
1 by tmbdev
top-skimming import from sf.net
419
  ICOORD first_pos;              //first tail
420
421
  length = srcline->pathlength ();
422
  pos = srcline->start_pos ();
423
  left_edge = pos.x ();
424
  tail_index = 0;
425
  tail_pos = pos;
426
  for (stepindex = 0; stepindex < length; stepindex++) {
427
    if (pos.x () < left_edge) {
428
      left_edge = pos.x ();
429
      tail_index = stepindex;
430
      tail_pos = pos;
431
    }
432
    pos += srcline->step (stepindex);
433
  }
434
  if (left_edge >= chop_coord - pitch_error)
435
    return FALSE;                //not worth it
436
437
  startindex = tail_index;
438
  first_frag = TRUE;
439
  head_index = tail_index;
440
  head_pos = tail_pos;
441
  do {
442
    do {
443
      tail_pos += srcline->step (tail_index);
444
      tail_index++;
445
      if (tail_index == length)
446
        tail_index = 0;
447
    }
448
    while (tail_pos.x () != chop_coord && tail_index != startindex);
449
    if (tail_index == startindex) {
450
      if (first_frag)
451
        return FALSE;            //doesn't cross line
452
      else
453
        break;
454
    }
455
    //#ifdef __UNIX__
456
    ASSERT_HOST (head_index != tail_index);
457
    //#endif
458
    if (!first_frag) {
459
      save_chop_cfragment(head_index,
460
                          head_pos,
461
                          tail_index,
462
                          tail_pos,
463
                          srcline,
464
                          left_frags);
465
    }
466
    else {
467
      first_index = tail_index;
468
      first_pos = tail_pos;
469
      first_frag = FALSE;
470
    }
471
    while (srcline->step (tail_index).x () == 0) {
472
      tail_pos += srcline->step (tail_index);
473
      tail_index++;
474
      if (tail_index == length)
475
        tail_index = 0;
476
    }
477
    head_index = tail_index;
478
    head_pos = tail_pos;
479
    while (srcline->step (tail_index).x () > 0) {
480
      do {
481
        tail_pos += srcline->step (tail_index);
482
        tail_index++;
483
        if (tail_index == length)
484
          tail_index = 0;
485
      }
486
      while (tail_pos.x () != chop_coord);
487
      //#ifdef __UNIX__
488
      ASSERT_HOST (head_index != tail_index);
489
      //#endif
490
      save_chop_cfragment(head_index,
491
                          head_pos,
492
                          tail_index,
493
                          tail_pos,
494
                          srcline,
495
                          right_frags);
496
      while (srcline->step (tail_index).x () == 0) {
497
        tail_pos += srcline->step (tail_index);
498
        tail_index++;
499
        if (tail_index == length)
500
          tail_index = 0;
501
      }
502
      head_index = tail_index;
503
      head_pos = tail_pos;
504
    }
505
  }
506
  while (tail_index != startindex);
507
  save_chop_cfragment(head_index,
508
                      head_pos,
509
                      first_index,
510
                      first_pos,
511
                      srcline,
512
                      left_frags);
513
  return TRUE;                   //did some chopping
514
}
515
516
/**********************************************************************
517
 * save_chop_cfragment
518
 *
519
 * Store the given fragment in the given fragment list.
520
 **********************************************************************/
521
522
void save_chop_cfragment(                            //chop the outline
112 by theraysmith
Fixed name collision with jpeg library
523
                         inT16 head_index,           //head of fragment
1 by tmbdev
top-skimming import from sf.net
524
                         ICOORD head_pos,            //head of fragment
112 by theraysmith
Fixed name collision with jpeg library
525
                         inT16 tail_index,           //tail of fragment
1 by tmbdev
top-skimming import from sf.net
526
                         ICOORD tail_pos,            //tail of fragment
527
                         C_OUTLINE *srcline,         //source of edgesteps
528
                         C_OUTLINE_FRAG_LIST *frags  //fragment list
529
                        ) {
112 by theraysmith
Fixed name collision with jpeg library
530
  inT16 jump;                    //gap across end
531
  inT16 stepcount;               //total steps
1 by tmbdev
top-skimming import from sf.net
532
  C_OUTLINE_FRAG *head;          //head of fragment
533
  C_OUTLINE_FRAG *tail;          //tail of fragment
112 by theraysmith
Fixed name collision with jpeg library
534
  inT16 tail_y;                  //ycoord of tail
1 by tmbdev
top-skimming import from sf.net
535
536
  ASSERT_HOST (tail_pos.x () == head_pos.x ());
537
  ASSERT_HOST (tail_index != head_index);
538
  stepcount = tail_index - head_index;
539
  if (stepcount < 0)
540
    stepcount += srcline->pathlength ();
541
  jump = tail_pos.y () - head_pos.y ();
542
  if (jump < 0)
543
    jump = -jump;
544
  if (jump == stepcount)
545
    return;                      //its a nop
546
  tail_y = tail_pos.y ();
547
  head = new C_OUTLINE_FRAG (head_pos, tail_pos, srcline,
548
    head_index, tail_index);
549
  tail = new C_OUTLINE_FRAG (head, tail_y);
550
  head->other_end = tail;
112 by theraysmith
Fixed name collision with jpeg library
551
  add_frag_to_list(head, frags);
552
  add_frag_to_list(tail, frags);
1 by tmbdev
top-skimming import from sf.net
553
}
554
555
556
/**********************************************************************
557
 * C_OUTLINE_FRAG::C_OUTLINE_FRAG
558
 *
559
 * Constructors for C_OUTLINE_FRAG.
560
 **********************************************************************/
561
562
C_OUTLINE_FRAG::C_OUTLINE_FRAG(                     //record fragment
563
                               ICOORD start_pt,     //start coord
564
                               ICOORD end_pt,       //end coord
565
                               C_OUTLINE *outline,  //source of steps
112 by theraysmith
Fixed name collision with jpeg library
566
                               inT16 start_index,
567
                               inT16 end_index) {
1 by tmbdev
top-skimming import from sf.net
568
  start = start_pt;
569
  end = end_pt;
570
  ycoord = start_pt.y ();
571
  stepcount = end_index - start_index;
572
  if (stepcount < 0)
573
    stepcount += outline->pathlength ();
574
  ASSERT_HOST (stepcount > 0);
575
  steps = new DIR128[stepcount];
101 by theraysmith
Updated graphics output for new java-based display
576
  if (end_index > start_index) {
577
    for (int i = start_index; i < end_index; ++i)
578
      steps[i - start_index] = outline->step_dir(i);
1 by tmbdev
top-skimming import from sf.net
579
  }
101 by theraysmith
Updated graphics output for new java-based display
580
  else {
581
    int len = outline->pathlength();
582
    int i = start_index;
583
    for (; i < len; ++i)
1 by tmbdev
top-skimming import from sf.net
584
      steps[i - start_index] = outline->step_dir(i);
101 by theraysmith
Updated graphics output for new java-based display
585
    if (end_index > 0)
586
      for (; i < end_index + len; ++i)
1 by tmbdev
top-skimming import from sf.net
587
        steps[i - start_index] = outline->step_dir(i - len);
588
  }
589
  other_end = NULL;
112 by theraysmith
Fixed name collision with jpeg library
590
  delete close();
1 by tmbdev
top-skimming import from sf.net
591
}
592
593
594
C_OUTLINE_FRAG::C_OUTLINE_FRAG(                       //record fragment
595
                               C_OUTLINE_FRAG *head,  //other end
112 by theraysmith
Fixed name collision with jpeg library
596
                               inT16 tail_y) {
1 by tmbdev
top-skimming import from sf.net
597
  ycoord = tail_y;
598
  other_end = head;
599
  start = head->start;
600
  end = head->end;
601
  steps = NULL;
602
  stepcount = 0;
603
}
604
605
606
/**********************************************************************
607
 * add_frag_to_list
608
 *
609
 * Insert the fragment in the list at the appropriate place to keep
610
 * them in ascending ycoord order.
611
 **********************************************************************/
612
613
void add_frag_to_list(                            //ordered add
614
                      C_OUTLINE_FRAG *frag,       //fragment to add
615
                      C_OUTLINE_FRAG_LIST *frags  //fragment list
616
                     ) {
617
                                 //output list
618
  C_OUTLINE_FRAG_IT frag_it = frags;
619
620
  if (!frags->empty ()) {
621
    for (frag_it.mark_cycle_pt (); !frag_it.cycled_list ();
622
    frag_it.forward ()) {
623
      if (frag_it.data ()->ycoord > frag->ycoord
101 by theraysmith
Updated graphics output for new java-based display
624
        || (frag_it.data ()->ycoord == frag->ycoord
625
         && frag->other_end->ycoord < frag->ycoord)) {
1 by tmbdev
top-skimming import from sf.net
626
        frag_it.add_before_then_move (frag);
627
        return;
628
      }
629
    }
630
  }
631
  frag_it.add_to_end (frag);
632
}
633
634
635
/**********************************************************************
636
 * close_chopped_cfragments
637
 *
638
 * Clear the given list of fragments joining them up into outlines.
639
 * Each outline made soaks up any of the child outlines which it encloses.
640
 **********************************************************************/
641
642
void close_chopped_cfragments(                             //chop the outline
643
                              C_OUTLINE_FRAG_LIST *frags,  //list to clear
644
                              C_OUTLINE_LIST *children,    //potential children
645
                              float pitch_error,           //allowed shrinkage
646
                              C_OUTLINE_IT *dest_it        //output list
647
                             ) {
648
                                 //iterator
649
  C_OUTLINE_FRAG_IT frag_it = frags;
650
  C_OUTLINE_FRAG *bottom_frag;   //bottom of cut
651
  C_OUTLINE_FRAG *top_frag;      //top of cut
652
  C_OUTLINE *outline;            //new outline
653
  C_OUTLINE *child;              //current child
654
  C_OUTLINE_IT child_it = children;
655
  C_OUTLINE_IT olchild_it;       //children of outline
656
668 by theraysmith at gmail
Major refactor of beam search, elimination of dead code, misc bug fixes, updates to Makefile.am, Changelog etc.
657
  while (!frag_it.empty()) {
658
    frag_it.move_to_first();
659
                                 // get bottom one
660
    bottom_frag = frag_it.extract();
661
    frag_it.forward();
662
    top_frag = frag_it.data();  // look at next
101 by theraysmith
Updated graphics output for new java-based display
663
    if ((bottom_frag->steps == 0 && top_frag->steps == 0)
664
    || (bottom_frag->steps != 0 && top_frag->steps != 0)) {
668 by theraysmith at gmail
Major refactor of beam search, elimination of dead code, misc bug fixes, updates to Makefile.am, Changelog etc.
665
      if (frag_it.data_relative(1)->ycoord == top_frag->ycoord)
666
        frag_it.forward();
1 by tmbdev
top-skimming import from sf.net
667
    }
668 by theraysmith at gmail
Major refactor of beam search, elimination of dead code, misc bug fixes, updates to Makefile.am, Changelog etc.
668
    top_frag = frag_it.extract();
1 by tmbdev
top-skimming import from sf.net
669
    if (top_frag->other_end != bottom_frag) {
668 by theraysmith at gmail
Major refactor of beam search, elimination of dead code, misc bug fixes, updates to Makefile.am, Changelog etc.
670
      outline = join_chopped_fragments(bottom_frag, top_frag);
671
      ASSERT_HOST(outline == NULL);
672
    } else {
673
      outline = join_chopped_fragments(bottom_frag, top_frag);
674
      if (outline != NULL) {
675
        olchild_it.set_to_list(outline->child());
676
        for (child_it.mark_cycle_pt(); !child_it.cycled_list();
677
             child_it.forward()) {
678
          child = child_it.data();
679
          if (*child < *outline)
680
            olchild_it.add_to_end(child_it.extract());
681
        }
682
        if (outline->bounding_box().width() > pitch_error)
683
          dest_it->add_after_then_move(outline);
684
        else
685
          delete outline;          // Make it disappear.
1 by tmbdev
top-skimming import from sf.net
686
      }
687
    }
688
  }
689
  while (!child_it.empty ()) {
690
    dest_it->add_after_then_move (child_it.extract ());
691
    child_it.forward ();
692
  }
693
}
694
695
696
/**********************************************************************
697
 * join_chopped_fragments
698
 *
699
 * Join the two lists of POLYPTs such that neither OUTLINE_FRAG
700
 * operand keeps responsibility for the fragment.
701
 **********************************************************************/
702
703
C_OUTLINE *join_chopped_fragments(                         //join pieces
704
                                  C_OUTLINE_FRAG *bottom,  //bottom of cut
705
                                  C_OUTLINE_FRAG *top      //top of cut
706
                                 ) {
707
  C_OUTLINE *outline;            //closed loop
708
709
  if (bottom->other_end == top) {
710
    if (bottom->steps == 0)
711
      outline = top->close ();   //turn to outline
712
    else
713
      outline = bottom->close ();
714
    delete top;
715
    delete bottom;
716
    return outline;
717
  }
718
  if (bottom->steps == 0) {
719
    ASSERT_HOST (top->steps != 0);
720
    join_segments (bottom->other_end, top);
721
  }
722
  else {
723
    ASSERT_HOST (top->steps == 0);
724
    join_segments (top->other_end, bottom);
725
  }
726
  top->other_end->other_end = bottom->other_end;
727
  bottom->other_end->other_end = top->other_end;
728
  delete bottom;
729
  delete top;
730
  return NULL;
731
}
732
733
734
/**********************************************************************
735
 * join_segments
736
 *
737
 * Join the two edgestep fragments such that the second comes after
738
 * the first and the gap beween them is closed.
739
 **********************************************************************/
740
741
void join_segments(                         //join pieces
742
                   C_OUTLINE_FRAG *bottom,  //bottom of cut
743
                   C_OUTLINE_FRAG *top      //top of cut
744
                  ) {
745
  DIR128 *steps;                  //new steps
112 by theraysmith
Fixed name collision with jpeg library
746
  inT32 stepcount;               //no of steps
747
  inT16 fake_count;              //fake steps
1 by tmbdev
top-skimming import from sf.net
748
  DIR128 fake_step;               //step entry
749
750
  ASSERT_HOST (bottom->end.x () == top->start.x ());
751
  fake_count = top->start.y () - bottom->end.y ();
752
  if (fake_count < 0) {
753
    fake_count = -fake_count;
754
    fake_step = 32;
755
  }
756
  else
757
    fake_step = 96;
758
759
  stepcount = bottom->stepcount + fake_count + top->stepcount;
760
  steps = new DIR128[stepcount];
761
  memmove (steps, bottom->steps, bottom->stepcount);
762
  memset (steps + bottom->stepcount, fake_step.get_dir(), fake_count);
763
  memmove (steps + bottom->stepcount + fake_count, top->steps,
764
    top->stepcount);
765
  delete [] bottom->steps;
766
  bottom->steps = steps;
767
  bottom->stepcount = stepcount;
768
  bottom->end = top->end;
769
  bottom->other_end->end = top->end;
770
}
771
772
773
/**********************************************************************
774
 * C_OUTLINE_FRAG::close
775
 *
776
 * Join the ends of this fragment and turn it into an outline.
777
 **********************************************************************/
778
779
C_OUTLINE *C_OUTLINE_FRAG::close() {  //join pieces
780
  DIR128 *new_steps;              //new steps
112 by theraysmith
Fixed name collision with jpeg library
781
  inT32 new_stepcount;           //no of steps
782
  inT16 fake_count;              //fake steps
1 by tmbdev
top-skimming import from sf.net
783
  DIR128 fake_step;               //step entry
784
785
  ASSERT_HOST (start.x () == end.x ());
786
  fake_count = start.y () - end.y ();
787
  if (fake_count < 0) {
788
    fake_count = -fake_count;
789
    fake_step = 32;
790
  }
791
  else
792
    fake_step = 96;
793
794
  new_stepcount = stepcount + fake_count;
668 by theraysmith at gmail
Major refactor of beam search, elimination of dead code, misc bug fixes, updates to Makefile.am, Changelog etc.
795
  if (new_stepcount > C_OUTLINE::kMaxOutlineLength)
796
    return NULL;  // Can't join them
1 by tmbdev
top-skimming import from sf.net
797
  new_steps = new DIR128[new_stepcount];
112 by theraysmith
Fixed name collision with jpeg library
798
  memmove(new_steps, steps, stepcount);
101 by theraysmith
Updated graphics output for new java-based display
799
  memset (new_steps + stepcount, fake_step.get_dir(), fake_count);
800
  C_OUTLINE* result = new C_OUTLINE (start, new_steps, new_stepcount);
801
  delete [] new_steps;
1 by tmbdev
top-skimming import from sf.net
802
  return result;
803
}
804
805
806
/**********************************************************************
807
 * C_OUTLINE_FRAG::operator=
808
 *
809
 * Copy this fragment.
810
 **********************************************************************/
811
812
                                 //join pieces
813
C_OUTLINE_FRAG & C_OUTLINE_FRAG::operator= (
814
const C_OUTLINE_FRAG & src       //fragment to copy
815
) {
816
  if (steps != NULL)
112 by theraysmith
Fixed name collision with jpeg library
817
    delete [] steps;
1 by tmbdev
top-skimming import from sf.net
818
819
  stepcount = src.stepcount;
820
  steps = new DIR128[stepcount];
821
  memmove (steps, src.steps, stepcount);
822
  start = src.start;
823
  end = src.end;
824
  ycoord = src.ycoord;
825
  return *this;
826
}