Tesseract  3.02
tesseract-ocr/textord/fpchop.cpp
Go to the documentation of this file.
00001 /**********************************************************************
00002  * File:        fpchop.cpp  (Formerly fp_chop.c)
00003  * Description: Code to chop fixed pitch text into character cells.
00004  * Author:              Ray Smith
00005  * Created:             Thu Sep 16 11:14:15 BST 1993
00006  *
00007  * (C) Copyright 1993, Hewlett-Packard Ltd.
00008  ** Licensed under the Apache License, Version 2.0 (the "License");
00009  ** you may not use this file except in compliance with the License.
00010  ** You may obtain a copy of the License at
00011  ** http://www.apache.org/licenses/LICENSE-2.0
00012  ** Unless required by applicable law or agreed to in writing, software
00013  ** distributed under the License is distributed on an "AS IS" BASIS,
00014  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015  ** See the License for the specific language governing permissions and
00016  ** limitations under the License.
00017  *
00018  **********************************************************************/
00019 
00020 #include "mfcpch.h"
00021 #ifdef __UNIX__
00022 #include          <assert.h>
00023 #endif
00024 #include          "stderr.h"
00025 #include          "blobbox.h"
00026 #include          "statistc.h"
00027 #include          "drawtord.h"
00028 #include          "tovars.h"
00029 #include          "topitch.h"
00030 #include          "fpchop.h"
00031 #include          "notdll.h"
00032 
00033 // Include automatically generated configuration file if running autoconf.
00034 #ifdef HAVE_CONFIG_H
00035 #include "config_auto.h"
00036 #endif
00037 
00038 #define EXTERN
00039 
00040 EXTERN INT_VAR (textord_fp_chop_error, 2,
00041 "Max allowed bending of chop cells");
00042 EXTERN double_VAR (textord_fp_chop_snap, 0.5,
00043 "Max distance of chop pt from vertex");
00044 
00045 ELISTIZE(C_OUTLINE_FRAG)
00046 //#undef ASSERT_HOST
00047 //#define ASSERT_HOST(x) if (!(x)) AfxMessageBox(#x);
00048 /**********************************************************************
00049  * fixed_pitch_words
00050  *
00051  * Make a ROW from a fixed pitch TO_ROW.
00052  **********************************************************************/
00053 ROW *fixed_pitch_words(                 //find lines
00054                        TO_ROW *row,     //row to do
00055                        FCOORD rotation  //for drawing
00056                       ) {
00057   BOOL8 bol;                     //start of line
00058   uinT8 blanks;                  //in front of word
00059   uinT8 new_blanks;              //blanks in empty cell
00060   inT16 chop_coord;              //chop boundary
00061   inT16 prev_chop_coord;         //start of cell
00062   inT16 rep_left;                //left edge of rep word
00063   ROW *real_row;                 //output row
00064   C_OUTLINE_LIST left_coutlines;
00065   C_OUTLINE_LIST right_coutlines;
00066   C_BLOB_LIST cblobs;
00067   C_BLOB_IT cblob_it = &cblobs;
00068   WERD_LIST words;
00069   WERD_IT word_it = &words;      //new words
00070                                  //repeated blobs
00071   WERD_IT rep_it = &row->rep_words;
00072   WERD *word;                    //new word
00073   inT32 xstarts[2];              //row ends
00074   double coeffs[3];              //quadratic
00075   inT32 prev_x;                  //end of prev blob
00076                                  //iterator
00077   BLOBNBOX_IT box_it = row->blob_list ();
00078                                  //boundaries
00079   ICOORDELT_IT cell_it = &row->char_cells;
00080 
00081 #ifndef GRAPHICS_DISABLED
00082   if (textord_show_page_cuts && to_win != NULL) {
00083     plot_row_cells (to_win, ScrollView::RED, row, 0, &row->char_cells);
00084   }
00085 #endif
00086 
00087   prev_x = -MAX_INT16;
00088   bol = TRUE;
00089   blanks = 0;
00090   if (rep_it.empty ())
00091     rep_left = MAX_INT16;
00092   else
00093     rep_left = rep_it.data ()->bounding_box ().left ();
00094   if (box_it.empty ())
00095     return NULL;                 //empty row
00096   xstarts[0] = box_it.data ()->bounding_box ().left ();
00097   if (rep_left < xstarts[0]) {
00098     xstarts[0] = rep_left;
00099   }
00100   if (cell_it.empty () || row->char_cells.singleton ()) {
00101     tprintf ("Row without enough char cells!\n");
00102     tprintf ("Leftmost blob is at (%d,%d)\n",
00103       box_it.data ()->bounding_box ().left (),
00104       box_it.data ()->bounding_box ().bottom ());
00105     return NULL;
00106   }
00107   ASSERT_HOST (!cell_it.empty () && !row->char_cells.singleton ());
00108   prev_chop_coord = cell_it.data ()->x ();
00109   word = NULL;
00110   while (rep_left < cell_it.data ()->x ()) {
00111     word = add_repeated_word (&rep_it, rep_left, prev_chop_coord,
00112       blanks, row->fixed_pitch, &word_it);
00113   }
00114   cell_it.mark_cycle_pt ();
00115   if (prev_chop_coord >= cell_it.data ()->x ())
00116     cell_it.forward ();
00117   for (; !cell_it.cycled_list (); cell_it.forward ()) {
00118     chop_coord = cell_it.data ()->x ();
00119     while (!box_it.empty ()
00120     && box_it.data ()->bounding_box ().left () <= chop_coord) {
00121       if (box_it.data ()->bounding_box ().right () > prev_x)
00122         prev_x = box_it.data ()->bounding_box ().right ();
00123       split_to_blob (box_it.extract (), chop_coord,
00124         textord_fp_chop_error + 0.5f,
00125         &left_coutlines,
00126         &right_coutlines);
00127       box_it.forward ();
00128       while (!box_it.empty() && box_it.data()->cblob() == NULL) {
00129         delete box_it.extract();
00130         box_it.forward();
00131       }
00132     }
00133     if (!right_coutlines.empty() && left_coutlines.empty())
00134       split_to_blob (NULL, chop_coord,
00135         textord_fp_chop_error + 0.5f,
00136         &left_coutlines,
00137         &right_coutlines);
00138     if (!left_coutlines.empty ())
00139       cblob_it.add_after_then_move (new C_BLOB (&left_coutlines));
00140     else {
00141       if (rep_left < chop_coord) {
00142         if (rep_left > prev_chop_coord)
00143           new_blanks = (uinT8) floor ((rep_left - prev_chop_coord)
00144             / row->fixed_pitch + 0.5);
00145         else
00146           new_blanks = 0;
00147       }
00148       else {
00149         if (chop_coord > prev_chop_coord)
00150           new_blanks = (uinT8) floor ((chop_coord - prev_chop_coord)
00151             / row->fixed_pitch + 0.5);
00152         else
00153           new_blanks = 0;
00154       }
00155       if (!cblob_it.empty()) {
00156         if (blanks < 1 && word != NULL && !word->flag (W_REP_CHAR))
00157           blanks = 1;
00158         word = new WERD (&cblobs, blanks, NULL);
00159         cblob_it.set_to_list (&cblobs);
00160         word->set_flag (W_DONT_CHOP, TRUE);
00161         word_it.add_after_then_move (word);
00162         if (bol) {
00163           word->set_flag (W_BOL, TRUE);
00164           bol = FALSE;
00165         }
00166         blanks = new_blanks;
00167       }
00168       else
00169         blanks += new_blanks;
00170       while (rep_left < chop_coord) {
00171         word = add_repeated_word (&rep_it, rep_left, prev_chop_coord,
00172           blanks, row->fixed_pitch, &word_it);
00173       }
00174     }
00175     if (prev_chop_coord < chop_coord)
00176       prev_chop_coord = chop_coord;
00177   }
00178   if (!cblob_it.empty()) {
00179     word = new WERD(&cblobs, blanks, NULL);
00180     word->set_flag (W_DONT_CHOP, TRUE);
00181     word_it.add_after_then_move (word);
00182     if (bol)
00183       word->set_flag (W_BOL, TRUE);
00184   }
00185   ASSERT_HOST (word != NULL);
00186   while (!rep_it.empty ()) {
00187     add_repeated_word (&rep_it, rep_left, prev_chop_coord,
00188       blanks, row->fixed_pitch, &word_it);
00189   }
00190                                  //at end of line
00191   word_it.data ()->set_flag (W_EOL, TRUE);
00192   if (prev_chop_coord > prev_x)
00193     prev_x = prev_chop_coord;
00194   xstarts[1] = prev_x + 1;
00195   coeffs[0] = 0;
00196   coeffs[1] = row->line_m ();
00197   coeffs[2] = row->line_c ();
00198   real_row = new ROW (row, (inT16) row->kern_size, (inT16) row->space_size);
00199   word_it.set_to_list (real_row->word_list ());
00200                                  //put words in row
00201   word_it.add_list_after (&words);
00202   real_row->recalc_bounding_box ();
00203   return real_row;
00204 }
00205 
00206 
00207 /**********************************************************************
00208  * add_repeated_word
00209  *
00210  * Add repeated word into the row at the given point.
00211  **********************************************************************/
00212 
00213 WERD *add_repeated_word(                         //move repeated word
00214                         WERD_IT *rep_it,         //repeated words
00215                         inT16 &rep_left,         //left edge of word
00216                         inT16 &prev_chop_coord,  //previous word end
00217                         uinT8 &blanks,           //no of blanks
00218                         float pitch,             //char cell size
00219                         WERD_IT *word_it         //list of words
00220                        ) {
00221   WERD *word;                    //word to move
00222   inT16 new_blanks;              //extra blanks
00223 
00224   if (rep_left > prev_chop_coord) {
00225     new_blanks = (uinT8) floor ((rep_left - prev_chop_coord) / pitch + 0.5);
00226     blanks += new_blanks;
00227   }
00228   word = rep_it->extract ();
00229   prev_chop_coord = word->bounding_box ().right ();
00230   word_it->add_after_then_move (word);
00231   word->set_blanks (blanks);
00232   rep_it->forward ();
00233   if (rep_it->empty ())
00234     rep_left = MAX_INT16;
00235   else
00236     rep_left = rep_it->data ()->bounding_box ().left ();
00237   blanks = 0;
00238   return word;
00239 }
00240 
00241 
00242 /**********************************************************************
00243  * split_to_blob
00244  *
00245  * Split a BLOBNBOX across a vertical chop line and put the pieces
00246  * into a left outline list and a right outline list.
00247  **********************************************************************/
00248 
00249 void split_to_blob(                                 //split the blob
00250                    BLOBNBOX *blob,                  //blob to split
00251                    inT16 chop_coord,                //place to chop
00252                    float pitch_error,               //allowed deviation
00253                    C_OUTLINE_LIST *left_coutlines,  //for cblobs
00254                    C_OUTLINE_LIST *right_coutlines) {
00255   C_BLOB *real_cblob;            //cblob to chop
00256 
00257   if (blob != NULL) {
00258     real_cblob = blob->cblob();
00259   } else {
00260     real_cblob = NULL;
00261   }
00262   if (!right_coutlines->empty() || real_cblob != NULL)
00263     fixed_chop_cblob(real_cblob,
00264                      chop_coord,
00265                      pitch_error,
00266                      left_coutlines,
00267                      right_coutlines);
00268   if (blob != NULL)
00269     delete blob;                 //free it
00270 }
00271 
00272 /**********************************************************************
00273  * fixed_chop_cblob
00274  *
00275  * Chop the given cblob (if any) and the existing right outlines to
00276  * produce a list of outlines left of the chop point and more to the right.
00277  **********************************************************************/
00278 
00279 void fixed_chop_cblob(                                //split the blob
00280                       C_BLOB *blob,                   //blob to split
00281                       inT16 chop_coord,               //place to chop
00282                       float pitch_error,              //allowed deviation
00283                       C_OUTLINE_LIST *left_outlines,  //left half of chop
00284                       C_OUTLINE_LIST *right_outlines  //right half of chop
00285                      ) {
00286   C_OUTLINE *old_right;          //already there
00287   C_OUTLINE_LIST new_outlines;   //new right ones
00288                                  //ouput iterator
00289   C_OUTLINE_IT left_it = left_outlines;
00290                                  //in/out iterator
00291   C_OUTLINE_IT right_it = right_outlines;
00292   C_OUTLINE_IT new_it = &new_outlines;
00293   C_OUTLINE_IT blob_it;          //outlines in blob
00294 
00295   if (!right_it.empty ()) {
00296     while (!right_it.empty ()) {
00297       old_right = right_it.extract ();
00298       right_it.forward ();
00299       fixed_split_coutline(old_right,
00300                            chop_coord,
00301                            pitch_error,
00302                            &left_it,
00303                            &new_it);
00304     }
00305     right_it.add_list_before (&new_outlines);
00306   }
00307   if (blob != NULL) {
00308     blob_it.set_to_list (blob->out_list ());
00309     for (blob_it.mark_cycle_pt (); !blob_it.cycled_list ();
00310       blob_it.forward ())
00311     fixed_split_coutline (blob_it.extract (), chop_coord, pitch_error,
00312         &left_it, &right_it);
00313     delete blob;
00314   }
00315 }
00316 
00317 
00318 /**********************************************************************
00319  * fixed_split_outline
00320  *
00321  * Chop the given outline (if necessary) placing the fragments which
00322  * fall either side of the chop line into the appropriate list.
00323  **********************************************************************/
00324 
00325 void fixed_split_coutline(                        //chop the outline
00326                           C_OUTLINE *srcline,     //source outline
00327                           inT16 chop_coord,       //place to chop
00328                           float pitch_error,      //allowed deviation
00329                           C_OUTLINE_IT *left_it,  //left half of chop
00330                           C_OUTLINE_IT *right_it  //right half of chop
00331                          ) {
00332   C_OUTLINE *child;              //child outline
00333   TBOX srcbox;                    //box of outline
00334   C_OUTLINE_LIST left_ch;        //left children
00335   C_OUTLINE_LIST right_ch;       //right children
00336   C_OUTLINE_FRAG_LIST left_frags;//chopped fragments
00337   C_OUTLINE_FRAG_LIST right_frags;;
00338   C_OUTLINE_IT left_ch_it = &left_ch;
00339                                  //for whole children
00340   C_OUTLINE_IT right_ch_it = &right_ch;
00341                                  //for holes
00342   C_OUTLINE_IT child_it = srcline->child ();
00343 
00344   srcbox = srcline->bounding_box ();
00345                                  //left of line
00346   if (srcbox.left () + srcbox.right () <= chop_coord * 2
00347                                  //and not far over
00348     && srcbox.right () < chop_coord + pitch_error)
00349                                  //stick whole in left
00350     left_it->add_after_then_move (srcline);
00351   else if (srcbox.left () + srcbox.right () > chop_coord * 2
00352     && srcbox.left () > chop_coord - pitch_error)
00353                                  //stick whole in right
00354     right_it->add_before_stay_put (srcline);
00355   else {
00356                                  //needs real chopping
00357     if (fixed_chop_coutline (srcline, chop_coord, pitch_error,
00358     &left_frags, &right_frags)) {
00359       for (child_it.mark_cycle_pt (); !child_it.cycled_list ();
00360       child_it.forward ()) {
00361         child = child_it.extract ();
00362         srcbox = child->bounding_box ();
00363         if (srcbox.right () < chop_coord)
00364           left_ch_it.add_after_then_move (child);
00365         else if (srcbox.left () > chop_coord)
00366           right_ch_it.add_after_then_move (child);
00367         else {
00368           if (fixed_chop_coutline (child, chop_coord, pitch_error,
00369             &left_frags, &right_frags))
00370             delete child;
00371           else {
00372             if (srcbox.left () + srcbox.right () <= chop_coord * 2)
00373               left_ch_it.add_after_then_move (child);
00374             else
00375               right_ch_it.add_after_then_move (child);
00376           }
00377         }
00378       }
00379       close_chopped_cfragments(&left_frags, &left_ch, pitch_error, left_it);
00380       close_chopped_cfragments(&right_frags, &right_ch, pitch_error, right_it);
00381       ASSERT_HOST (left_ch.empty () && right_ch.empty ());
00382       //no children left
00383       delete srcline;            //smashed up
00384     }
00385     else {
00386       if (srcbox.left () + srcbox.right () <= chop_coord * 2)
00387                                  //stick whole in left
00388         left_it->add_after_then_move (srcline);
00389       else
00390         right_it->add_before_stay_put (srcline);
00391     }
00392   }
00393 }
00394 
00395 
00396 /**********************************************************************
00397  * fixed_chop_coutline
00398  *
00399  * Chop the given coutline (if necessary) placing the fragments which
00400  * fall either side of the chop line into the appropriate list.
00401  * If the coutline lies too heavily to one side to chop, FALSE is returned.
00402  **********************************************************************/
00403 
00404 BOOL8 fixed_chop_coutline(                                  //chop the outline
00405                           C_OUTLINE *srcline,               //source outline
00406                           inT16 chop_coord,                 //place to chop
00407                           float pitch_error,                //allowed deviation
00408                           C_OUTLINE_FRAG_LIST *left_frags,  //left half of chop
00409                           C_OUTLINE_FRAG_LIST *right_frags  //right half of chop
00410                          ) {
00411   BOOL8 first_frag;              //fragment
00412   BOOL8 anticlock;               //direction of loop
00413   inT16 left_edge;               //of outline
00414   inT16 startindex;              //in first fragment
00415   inT32 length;                  //of outline
00416   inT16 stepindex;               //into outline
00417   inT16 head_index;              //start of fragment
00418   ICOORD head_pos;               //start of fragment
00419   inT16 tail_index;              //end of fragment
00420   ICOORD tail_pos;               //end of fragment
00421   ICOORD pos;                    //current point
00422   inT16 first_index = 0;         //first tail
00423   ICOORD first_pos;              //first tail
00424 
00425   length = srcline->pathlength ();
00426   pos = srcline->start_pos ();
00427   anticlock = srcline->turn_direction () > 0;
00428   left_edge = pos.x ();
00429   tail_index = 0;
00430   tail_pos = pos;
00431   for (stepindex = 0; stepindex < length; stepindex++) {
00432     if (pos.x () < left_edge) {
00433       left_edge = pos.x ();
00434       tail_index = stepindex;
00435       tail_pos = pos;
00436     }
00437     pos += srcline->step (stepindex);
00438   }
00439   if (left_edge >= chop_coord - pitch_error)
00440     return FALSE;                //not worth it
00441 
00442   startindex = tail_index;
00443   first_frag = TRUE;
00444   head_index = tail_index;
00445   head_pos = tail_pos;
00446   do {
00447     do {
00448       tail_pos += srcline->step (tail_index);
00449       tail_index++;
00450       if (tail_index == length)
00451         tail_index = 0;
00452     }
00453     while (tail_pos.x () != chop_coord && tail_index != startindex);
00454     if (tail_index == startindex) {
00455       if (first_frag)
00456         return FALSE;            //doesn't cross line
00457       else
00458         break;
00459     }
00460     //#ifdef __UNIX__
00461     ASSERT_HOST (head_index != tail_index);
00462     //#endif
00463     if (!first_frag) {
00464       save_chop_cfragment(head_index,
00465                           head_pos,
00466                           tail_index,
00467                           tail_pos,
00468                           srcline,
00469                           left_frags);
00470     }
00471     else {
00472       first_index = tail_index;
00473       first_pos = tail_pos;
00474       first_frag = FALSE;
00475     }
00476     while (srcline->step (tail_index).x () == 0) {
00477       tail_pos += srcline->step (tail_index);
00478       tail_index++;
00479       if (tail_index == length)
00480         tail_index = 0;
00481     }
00482     head_index = tail_index;
00483     head_pos = tail_pos;
00484     while (srcline->step (tail_index).x () > 0) {
00485       do {
00486         tail_pos += srcline->step (tail_index);
00487         tail_index++;
00488         if (tail_index == length)
00489           tail_index = 0;
00490       }
00491       while (tail_pos.x () != chop_coord);
00492       //#ifdef __UNIX__
00493       ASSERT_HOST (head_index != tail_index);
00494       //#endif
00495       save_chop_cfragment(head_index,
00496                           head_pos,
00497                           tail_index,
00498                           tail_pos,
00499                           srcline,
00500                           right_frags);
00501       while (srcline->step (tail_index).x () == 0) {
00502         tail_pos += srcline->step (tail_index);
00503         tail_index++;
00504         if (tail_index == length)
00505           tail_index = 0;
00506       }
00507       head_index = tail_index;
00508       head_pos = tail_pos;
00509     }
00510   }
00511   while (tail_index != startindex);
00512   save_chop_cfragment(head_index,
00513                       head_pos,
00514                       first_index,
00515                       first_pos,
00516                       srcline,
00517                       left_frags);
00518   return TRUE;                   //did some chopping
00519 }
00520 
00521 
00522 /**********************************************************************
00523  * next_anti_left_seg
00524  *
00525  * Search the outline for a suitable point at which it crosses the
00526  * chop_coord from left to right.
00527  **********************************************************************/
00528 
00529 inT16 next_anti_left_seg(                     //chop the outline
00530                          C_OUTLINE *srcline,  //source outline
00531                          inT16 tail_index,    //of tailpos
00532                          inT16 startindex,    //end of search
00533                          inT32 length,        //of outline
00534                          inT16 chop_coord,    //place to chop
00535                          float pitch_error,   //allowed deviation
00536                          ICOORD *tail_pos     //current position
00537                         ) {
00538   BOOL8 test_valid;              //test pt valid
00539   inT16 chop_starty;             //test chop pt
00540   inT16 test_index;              //possible chop pt
00541   ICOORD test_pos;               //possible chop pt
00542   ICOORD prev_step;              //in x to tail pos
00543 
00544   test_valid = FALSE;
00545   chop_starty = -MAX_INT16;
00546   test_index = tail_index;       //stop warnings
00547   do {
00548     *tail_pos += srcline->step (tail_index);
00549     prev_step = srcline->step (tail_index);
00550     tail_index++;
00551     if (tail_index >= length)
00552       tail_index = 0;
00553     if (test_valid && tail_pos->x () == chop_coord && prev_step.x () < 0) {
00554       if (tail_pos->y () >= chop_starty) {
00555         chop_starty = -MAX_INT16;
00556         test_valid = FALSE;
00557       }
00558       else {
00559         *tail_pos = test_pos;
00560         tail_index = test_index;
00561         break;                   //must chop there
00562       }
00563     }
00564     if (tail_pos->x () == chop_coord
00565       && srcline->step (tail_index).x () > 0
00566     && tail_pos->y () > chop_starty) {
00567       chop_starty = tail_pos->y ();
00568       test_index = tail_index;
00569       test_pos = *tail_pos;
00570       test_valid = TRUE;
00571     }
00572     else if (tail_pos->x () == chop_coord
00573       && srcline->step (tail_index).y () < 0
00574       && prev_step.x () > 0 && tail_pos->y () > chop_starty)
00575       break;                     //must chop here
00576   }
00577   while (tail_index != startindex
00578     && tail_pos->x () < chop_coord + pitch_error);
00579   return tail_index;
00580 }
00581 
00582 
00583 /**********************************************************************
00584  * next_anti_right_seg
00585  *
00586  * Search the outline for a suitable point at which it crosses the
00587  * chop_coord from right to left.
00588  **********************************************************************/
00589 
00590 inT16 next_anti_right_seg(                     //chop the outline
00591                           C_OUTLINE *srcline,  //source outline
00592                           inT16 tail_index,    //of tailpos
00593                           inT16 startindex,    //end of search
00594                           inT32 length,        //of outline
00595                           inT16 chop_coord,    //place to chop
00596                           float pitch_error,   //allowed deviation
00597                           ICOORD *tail_pos     //current position
00598                          ) {
00599   BOOL8 test_valid;              //test pt valid
00600   inT16 chop_starty;             //test chop pt
00601   inT16 test_index;              //possible chop pt
00602   ICOORD test_pos;               //possible chop pt
00603   ICOORD prev_step;              //in x to tail pos
00604 
00605   test_valid = FALSE;
00606   chop_starty = MAX_INT16;
00607   test_index = tail_index;       //stop warnings
00608   do {
00609                                  //move forward
00610     *tail_pos += srcline->step (tail_index);
00611     prev_step = srcline->step (tail_index);
00612     tail_index++;
00613     if (tail_index >= length)
00614       tail_index = 0;
00615     if (test_valid && tail_pos->x () == chop_coord && prev_step.x () > 0) {
00616       if (tail_pos->y () <= chop_starty) {
00617         chop_starty = MAX_INT16;
00618         test_valid = FALSE;
00619       }
00620       else {
00621         *tail_pos = test_pos;
00622         tail_index = test_index;
00623         break;                   //must chop there
00624       }
00625     }
00626     if (tail_pos->x () == chop_coord
00627       && srcline->step (tail_index).x () < 0
00628     && tail_pos->y () < chop_starty) {
00629       chop_starty = tail_pos->y ();
00630       test_index = tail_index;
00631       test_pos = *tail_pos;
00632       test_valid = TRUE;         //save possible chop pt
00633     }
00634     else if (tail_pos->x () == chop_coord
00635       && srcline->step (tail_index).y () > 0
00636       && prev_step.x () < 0 && tail_pos->y () < chop_starty)
00637       break;                     //must chop here
00638   }
00639   while (tail_index != startindex
00640     && tail_pos->x () > chop_coord - pitch_error);
00641   return tail_index;
00642 }
00643 
00644 
00645 /**********************************************************************
00646  * next_clock_left_seg
00647  *
00648  * Search the outline for a suitable point at which it crosses the
00649  * chop_coord from left to right.
00650  **********************************************************************/
00651 
00652 inT16 next_clock_left_seg(                     //chop the outline
00653                           C_OUTLINE *srcline,  //source outline
00654                           inT16 tail_index,    //of tailpos
00655                           inT16 startindex,    //end of search
00656                           inT32 length,        //of outline
00657                           inT16 chop_coord,    //place to chop
00658                           float pitch_error,   //allowed deviation
00659                           ICOORD *tail_pos     //current position
00660                          ) {
00661   BOOL8 test_valid;              //test pt valid
00662   inT16 chop_starty;             //test chop pt
00663   inT16 test_index;              //possible chop pt
00664   ICOORD test_pos;               //possible chop pt
00665   ICOORD prev_step;              //in x to tail pos
00666 
00667   test_valid = FALSE;
00668   chop_starty = MAX_INT16;
00669   test_index = tail_index;       //stop warnings
00670   do {
00671     *tail_pos += srcline->step (tail_index);
00672     prev_step = srcline->step (tail_index);
00673     tail_index++;
00674     if (tail_index >= length)
00675       tail_index = 0;
00676     if (test_valid && tail_pos->x () == chop_coord && prev_step.x () < 0) {
00677       if (tail_pos->y () <= chop_starty) {
00678         chop_starty = MAX_INT16;
00679         test_valid = FALSE;
00680       }
00681       else {
00682         *tail_pos = test_pos;
00683         tail_index = test_index;
00684         break;                   //must chop there
00685       }
00686     }
00687     if (tail_pos->x () == chop_coord
00688       && srcline->step (tail_index).x () > 0
00689     && tail_pos->y () < chop_starty) {
00690       chop_starty = tail_pos->y ();
00691       test_index = tail_index;
00692       test_pos = *tail_pos;
00693       test_valid = TRUE;
00694     }
00695     else if (tail_pos->x () == chop_coord
00696       && srcline->step (tail_index).y () > 0
00697       && prev_step.x () > 0 && tail_pos->y () < chop_starty)
00698       break;                     //must chop here
00699   }
00700   while (tail_index != startindex
00701     && tail_pos->x () < chop_coord + pitch_error);
00702   return tail_index;
00703 }
00704 
00705 
00706 /**********************************************************************
00707  * next_clock_right_seg
00708  *
00709  * Search the outline for a suitable point at which it crosses the
00710  * chop_coord from right to left.
00711  **********************************************************************/
00712 
00713 inT16 next_clock_right_seg(                     //chop the outline
00714                            C_OUTLINE *srcline,  //source outline
00715                            inT16 tail_index,    //of tailpos
00716                            inT16 startindex,    //end of search
00717                            inT32 length,        //of outline
00718                            inT16 chop_coord,    //place to chop
00719                            float pitch_error,   //allowed deviation
00720                            ICOORD *tail_pos     //current position
00721                           ) {
00722   BOOL8 test_valid;              //test pt valid
00723   inT16 chop_starty;             //test chop pt
00724   inT16 test_index;              //possible chop pt
00725   ICOORD test_pos;               //possible chop pt
00726   ICOORD prev_step;              //in x to tail pos
00727 
00728   test_valid = FALSE;
00729   chop_starty = MAX_INT16;
00730   test_index = tail_index;       //stop warnings
00731   do {
00732                                  //move forward
00733     *tail_pos += srcline->step (tail_index);
00734     prev_step = srcline->step (tail_index);
00735     tail_index++;
00736     if (tail_index >= length)
00737       tail_index = 0;
00738     if (test_valid && tail_pos->x () == chop_coord && prev_step.x () > 0) {
00739       if (tail_pos->y () >= chop_starty) {
00740         chop_starty = MAX_INT16;
00741         test_valid = FALSE;
00742       }
00743       else {
00744         *tail_pos = test_pos;
00745         tail_index = test_index;
00746         break;                   //must chop there
00747       }
00748     }
00749     if (tail_pos->x () == chop_coord
00750       && srcline->step (tail_index).x () < 0
00751     && tail_pos->y () > chop_starty) {
00752       chop_starty = tail_pos->y ();
00753       test_index = tail_index;
00754       test_pos = *tail_pos;
00755       test_valid = TRUE;         //save possible chop pt
00756     }
00757     else if (tail_pos->x () == chop_coord
00758       && srcline->step (tail_index).y () < 0
00759       && prev_step.x () < 0 && tail_pos->y () > chop_starty)
00760       break;                     //must chop here
00761   }
00762   while (tail_index != startindex
00763     && tail_pos->x () > chop_coord - pitch_error);
00764   return tail_index;
00765 }
00766 
00767 
00768 /**********************************************************************
00769  * save_chop_cfragment
00770  *
00771  * Store the given fragment in the given fragment list.
00772  **********************************************************************/
00773 
00774 void save_chop_cfragment(                            //chop the outline
00775                          inT16 head_index,           //head of fragment
00776                          ICOORD head_pos,            //head of fragment
00777                          inT16 tail_index,           //tail of fragment
00778                          ICOORD tail_pos,            //tail of fragment
00779                          C_OUTLINE *srcline,         //source of edgesteps
00780                          C_OUTLINE_FRAG_LIST *frags  //fragment list
00781                         ) {
00782   inT16 jump;                    //gap across end
00783   inT16 stepcount;               //total steps
00784   C_OUTLINE_FRAG *head;          //head of fragment
00785   C_OUTLINE_FRAG *tail;          //tail of fragment
00786   inT16 tail_y;                  //ycoord of tail
00787 
00788   ASSERT_HOST (tail_pos.x () == head_pos.x ());
00789   ASSERT_HOST (tail_index != head_index);
00790   stepcount = tail_index - head_index;
00791   if (stepcount < 0)
00792     stepcount += srcline->pathlength ();
00793   jump = tail_pos.y () - head_pos.y ();
00794   if (jump < 0)
00795     jump = -jump;
00796   if (jump == stepcount)
00797     return;                      //its a nop
00798   tail_y = tail_pos.y ();
00799   head = new C_OUTLINE_FRAG (head_pos, tail_pos, srcline,
00800     head_index, tail_index);
00801   tail = new C_OUTLINE_FRAG (head, tail_y);
00802   head->other_end = tail;
00803   add_frag_to_list(head, frags);
00804   add_frag_to_list(tail, frags);
00805 }
00806 
00807 
00808 /**********************************************************************
00809  * C_OUTLINE_FRAG::C_OUTLINE_FRAG
00810  *
00811  * Constructors for C_OUTLINE_FRAG.
00812  **********************************************************************/
00813 
00814 C_OUTLINE_FRAG::C_OUTLINE_FRAG(                     //record fragment
00815                                ICOORD start_pt,     //start coord
00816                                ICOORD end_pt,       //end coord
00817                                C_OUTLINE *outline,  //source of steps
00818                                inT16 start_index,
00819                                inT16 end_index) {
00820   start = start_pt;
00821   end = end_pt;
00822   ycoord = start_pt.y ();
00823   stepcount = end_index - start_index;
00824   if (stepcount < 0)
00825     stepcount += outline->pathlength ();
00826   ASSERT_HOST (stepcount > 0);
00827   steps = new DIR128[stepcount];
00828   if (end_index > start_index) {
00829     for (int i = start_index; i < end_index; ++i)
00830       steps[i - start_index] = outline->step_dir(i);
00831   }
00832   else {
00833     int len = outline->pathlength();
00834     int i = start_index;
00835     for (; i < len; ++i)
00836       steps[i - start_index] = outline->step_dir(i);
00837     if (end_index > 0)
00838       for (; i < end_index + len; ++i)
00839         steps[i - start_index] = outline->step_dir(i - len);
00840   }
00841   other_end = NULL;
00842   delete close();
00843 }
00844 
00845 
00846 C_OUTLINE_FRAG::C_OUTLINE_FRAG(                       //record fragment
00847                                C_OUTLINE_FRAG *head,  //other end
00848                                inT16 tail_y) {
00849   ycoord = tail_y;
00850   other_end = head;
00851   start = head->start;
00852   end = head->end;
00853   steps = NULL;
00854   stepcount = 0;
00855 }
00856 
00857 
00858 /**********************************************************************
00859  * add_frag_to_list
00860  *
00861  * Insert the fragment in the list at the appropriate place to keep
00862  * them in ascending ycoord order.
00863  **********************************************************************/
00864 
00865 void add_frag_to_list(                            //ordered add
00866                       C_OUTLINE_FRAG *frag,       //fragment to add
00867                       C_OUTLINE_FRAG_LIST *frags  //fragment list
00868                      ) {
00869                                  //output list
00870   C_OUTLINE_FRAG_IT frag_it = frags;
00871 
00872   if (!frags->empty ()) {
00873     for (frag_it.mark_cycle_pt (); !frag_it.cycled_list ();
00874     frag_it.forward ()) {
00875       if (frag_it.data ()->ycoord > frag->ycoord
00876         || (frag_it.data ()->ycoord == frag->ycoord
00877          && frag->other_end->ycoord < frag->ycoord)) {
00878         frag_it.add_before_then_move (frag);
00879         return;
00880       }
00881     }
00882   }
00883   frag_it.add_to_end (frag);
00884 }
00885 
00886 
00887 /**********************************************************************
00888  * close_chopped_cfragments
00889  *
00890  * Clear the given list of fragments joining them up into outlines.
00891  * Each outline made soaks up any of the child outlines which it encloses.
00892  **********************************************************************/
00893 
00894 void close_chopped_cfragments(                             //chop the outline
00895                               C_OUTLINE_FRAG_LIST *frags,  //list to clear
00896                               C_OUTLINE_LIST *children,    //potential children
00897                               float pitch_error,           //allowed shrinkage
00898                               C_OUTLINE_IT *dest_it        //output list
00899                              ) {
00900                                  //iterator
00901   C_OUTLINE_FRAG_IT frag_it = frags;
00902   C_OUTLINE_FRAG *bottom_frag;   //bottom of cut
00903   C_OUTLINE_FRAG *top_frag;      //top of cut
00904   C_OUTLINE *outline;            //new outline
00905   C_OUTLINE *child;              //current child
00906   C_OUTLINE_IT child_it = children;
00907   C_OUTLINE_IT olchild_it;       //children of outline
00908 
00909   while (!frag_it.empty ()) {
00910     frag_it.move_to_first ();
00911                                  //get bottom one
00912     bottom_frag = frag_it.extract ();
00913     frag_it.forward ();
00914     top_frag = frag_it.data ();  //look at next
00915     if ((bottom_frag->steps == 0 && top_frag->steps == 0)
00916     || (bottom_frag->steps != 0 && top_frag->steps != 0)) {
00917       if (frag_it.data_relative (1)->ycoord == top_frag->ycoord)
00918         frag_it.forward ();
00919     }
00920     top_frag = frag_it.extract ();
00921     if (top_frag->other_end != bottom_frag) {
00922       outline = join_chopped_fragments (bottom_frag, top_frag);
00923       ASSERT_HOST (outline == NULL);
00924     }
00925     else {
00926       outline = join_chopped_fragments (bottom_frag, top_frag);
00927       ASSERT_HOST (outline != NULL);
00928       olchild_it.set_to_list (outline->child ());
00929       for (child_it.mark_cycle_pt (); !child_it.cycled_list ();
00930       child_it.forward ()) {
00931         child = child_it.data ();
00932         if (*child < *outline)
00933           olchild_it.add_to_end (child_it.extract ());
00934       }
00935       if (outline->bounding_box ().width () > pitch_error)
00936         dest_it->add_after_then_move (outline);
00937       else
00938         delete outline;          //make it disappear
00939     }
00940   }
00941   while (!child_it.empty ()) {
00942     dest_it->add_after_then_move (child_it.extract ());
00943     child_it.forward ();
00944   }
00945 }
00946 
00947 
00948 /**********************************************************************
00949  * join_chopped_fragments
00950  *
00951  * Join the two lists of POLYPTs such that neither OUTLINE_FRAG
00952  * operand keeps responsibility for the fragment.
00953  **********************************************************************/
00954 
00955 C_OUTLINE *join_chopped_fragments(                         //join pieces
00956                                   C_OUTLINE_FRAG *bottom,  //bottom of cut
00957                                   C_OUTLINE_FRAG *top      //top of cut
00958                                  ) {
00959   C_OUTLINE *outline;            //closed loop
00960 
00961   if (bottom->other_end == top) {
00962     if (bottom->steps == 0)
00963       outline = top->close ();   //turn to outline
00964     else
00965       outline = bottom->close ();
00966     delete top;
00967     delete bottom;
00968     return outline;
00969   }
00970   if (bottom->steps == 0) {
00971     ASSERT_HOST (top->steps != 0);
00972     join_segments (bottom->other_end, top);
00973   }
00974   else {
00975     ASSERT_HOST (top->steps == 0);
00976     join_segments (top->other_end, bottom);
00977   }
00978   top->other_end->other_end = bottom->other_end;
00979   bottom->other_end->other_end = top->other_end;
00980   delete bottom;
00981   delete top;
00982   return NULL;
00983 }
00984 
00985 
00986 /**********************************************************************
00987  * join_segments
00988  *
00989  * Join the two edgestep fragments such that the second comes after
00990  * the first and the gap beween them is closed.
00991  **********************************************************************/
00992 
00993 void join_segments(                         //join pieces
00994                    C_OUTLINE_FRAG *bottom,  //bottom of cut
00995                    C_OUTLINE_FRAG *top      //top of cut
00996                   ) {
00997   DIR128 *steps;                  //new steps
00998   inT32 stepcount;               //no of steps
00999   inT16 fake_count;              //fake steps
01000   DIR128 fake_step;               //step entry
01001 
01002   ASSERT_HOST (bottom->end.x () == top->start.x ());
01003   fake_count = top->start.y () - bottom->end.y ();
01004   if (fake_count < 0) {
01005     fake_count = -fake_count;
01006     fake_step = 32;
01007   }
01008   else
01009     fake_step = 96;
01010 
01011   stepcount = bottom->stepcount + fake_count + top->stepcount;
01012   steps = new DIR128[stepcount];
01013   memmove (steps, bottom->steps, bottom->stepcount);
01014   memset (steps + bottom->stepcount, fake_step.get_dir(), fake_count);
01015   memmove (steps + bottom->stepcount + fake_count, top->steps,
01016     top->stepcount);
01017   delete [] bottom->steps;
01018   bottom->steps = steps;
01019   bottom->stepcount = stepcount;
01020   bottom->end = top->end;
01021   bottom->other_end->end = top->end;
01022 }
01023 
01024 
01025 /**********************************************************************
01026  * C_OUTLINE_FRAG::close
01027  *
01028  * Join the ends of this fragment and turn it into an outline.
01029  **********************************************************************/
01030 
01031 C_OUTLINE *C_OUTLINE_FRAG::close() {  //join pieces
01032   DIR128 *new_steps;              //new steps
01033   inT32 new_stepcount;           //no of steps
01034   inT16 fake_count;              //fake steps
01035   DIR128 fake_step;               //step entry
01036 
01037   ASSERT_HOST (start.x () == end.x ());
01038   fake_count = start.y () - end.y ();
01039   if (fake_count < 0) {
01040     fake_count = -fake_count;
01041     fake_step = 32;
01042   }
01043   else
01044     fake_step = 96;
01045 
01046   new_stepcount = stepcount + fake_count;
01047   new_steps = new DIR128[new_stepcount];
01048   memmove(new_steps, steps, stepcount);
01049   memset (new_steps + stepcount, fake_step.get_dir(), fake_count);
01050   C_OUTLINE* result = new C_OUTLINE (start, new_steps, new_stepcount);
01051   delete [] new_steps;
01052   return result;
01053 }
01054 
01055 
01056 /**********************************************************************
01057  * C_OUTLINE_FRAG::operator=
01058  *
01059  * Copy this fragment.
01060  **********************************************************************/
01061 
01062                                  //join pieces
01063 C_OUTLINE_FRAG & C_OUTLINE_FRAG::operator= (
01064 const C_OUTLINE_FRAG & src       //fragment to copy
01065 ) {
01066   if (steps != NULL)
01067     delete [] steps;
01068 
01069   stepcount = src.stepcount;
01070   steps = new DIR128[stepcount];
01071   memmove (steps, src.steps, stepcount);
01072   start = src.start;
01073   end = src.end;
01074   ycoord = src.ycoord;
01075   return *this;
01076 }