tesseract  3.03
/usr/local/google/home/jbreiden/tesseract-ocr-read-only/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 #ifdef __UNIX__
00021 #include          <assert.h>
00022 #endif
00023 #include          "stderr.h"
00024 #include          "blobbox.h"
00025 #include          "statistc.h"
00026 #include          "drawtord.h"
00027 #include          "tovars.h"
00028 #include          "topitch.h"
00029 #include          "fpchop.h"
00030 
00031 // Include automatically generated configuration file if running autoconf.
00032 #ifdef HAVE_CONFIG_H
00033 #include "config_auto.h"
00034 #endif
00035 
00036 #define EXTERN
00037 
00038 EXTERN INT_VAR (textord_fp_chop_error, 2,
00039 "Max allowed bending of chop cells");
00040 EXTERN double_VAR (textord_fp_chop_snap, 0.5,
00041 "Max distance of chop pt from vertex");
00042 
00043 ELISTIZE(C_OUTLINE_FRAG)
00044 //#undef ASSERT_HOST
00045 //#define ASSERT_HOST(x) if (!(x)) AfxMessageBox(#x);
00046 /**********************************************************************
00047  * fixed_pitch_words
00048  *
00049  * Make a ROW from a fixed pitch TO_ROW.
00050  **********************************************************************/
00051 ROW *fixed_pitch_words(                 //find lines
00052                        TO_ROW *row,     //row to do
00053                        FCOORD rotation  //for drawing
00054                       ) {
00055   BOOL8 bol;                     //start of line
00056   uinT8 blanks;                  //in front of word
00057   uinT8 new_blanks;              //blanks in empty cell
00058   inT16 chop_coord;              //chop boundary
00059   inT16 prev_chop_coord;         //start of cell
00060   inT16 rep_left;                //left edge of rep word
00061   ROW *real_row;                 //output row
00062   C_OUTLINE_LIST left_coutlines;
00063   C_OUTLINE_LIST right_coutlines;
00064   C_BLOB_LIST cblobs;
00065   C_BLOB_IT cblob_it = &cblobs;
00066   WERD_LIST words;
00067   WERD_IT word_it = &words;      //new words
00068                                  //repeated blobs
00069   WERD_IT rep_it = &row->rep_words;
00070   WERD *word;                    //new word
00071   inT32 xstarts[2];              //row ends
00072   inT32 prev_x;                  //end of prev blob
00073                                  //iterator
00074   BLOBNBOX_IT box_it = row->blob_list ();
00075                                  //boundaries
00076   ICOORDELT_IT cell_it = &row->char_cells;
00077 
00078 #ifndef GRAPHICS_DISABLED
00079   if (textord_show_page_cuts && to_win != NULL) {
00080     plot_row_cells (to_win, ScrollView::RED, row, 0, &row->char_cells);
00081   }
00082 #endif
00083 
00084   prev_x = -MAX_INT16;
00085   bol = TRUE;
00086   blanks = 0;
00087   if (rep_it.empty ())
00088     rep_left = MAX_INT16;
00089   else
00090     rep_left = rep_it.data ()->bounding_box ().left ();
00091   if (box_it.empty ())
00092     return NULL;                 //empty row
00093   xstarts[0] = box_it.data ()->bounding_box ().left ();
00094   if (rep_left < xstarts[0]) {
00095     xstarts[0] = rep_left;
00096   }
00097   if (cell_it.empty () || row->char_cells.singleton ()) {
00098     tprintf ("Row without enough char cells!\n");
00099     tprintf ("Leftmost blob is at (%d,%d)\n",
00100       box_it.data ()->bounding_box ().left (),
00101       box_it.data ()->bounding_box ().bottom ());
00102     return NULL;
00103   }
00104   ASSERT_HOST (!cell_it.empty () && !row->char_cells.singleton ());
00105   prev_chop_coord = cell_it.data ()->x ();
00106   word = NULL;
00107   while (rep_left < cell_it.data ()->x ()) {
00108     word = add_repeated_word (&rep_it, rep_left, prev_chop_coord,
00109       blanks, row->fixed_pitch, &word_it);
00110   }
00111   cell_it.mark_cycle_pt ();
00112   if (prev_chop_coord >= cell_it.data ()->x ())
00113     cell_it.forward ();
00114   for (; !cell_it.cycled_list (); cell_it.forward ()) {
00115     chop_coord = cell_it.data ()->x ();
00116     while (!box_it.empty ()
00117     && box_it.data ()->bounding_box ().left () <= chop_coord) {
00118       if (box_it.data ()->bounding_box ().right () > prev_x)
00119         prev_x = box_it.data ()->bounding_box ().right ();
00120       split_to_blob (box_it.extract (), chop_coord,
00121         textord_fp_chop_error + 0.5f,
00122         &left_coutlines,
00123         &right_coutlines);
00124       box_it.forward ();
00125       while (!box_it.empty() && box_it.data()->cblob() == NULL) {
00126         delete box_it.extract();
00127         box_it.forward();
00128       }
00129     }
00130     if (!right_coutlines.empty() && left_coutlines.empty())
00131       split_to_blob (NULL, chop_coord,
00132         textord_fp_chop_error + 0.5f,
00133         &left_coutlines,
00134         &right_coutlines);
00135     if (!left_coutlines.empty()) {
00136       cblob_it.add_after_then_move(new C_BLOB(&left_coutlines));
00137     } else {
00138       if (rep_left < chop_coord) {
00139         if (rep_left > prev_chop_coord)
00140           new_blanks = (uinT8) floor ((rep_left - prev_chop_coord)
00141             / row->fixed_pitch + 0.5);
00142         else
00143           new_blanks = 0;
00144       }
00145       else {
00146         if (chop_coord > prev_chop_coord)
00147           new_blanks = (uinT8) floor ((chop_coord - prev_chop_coord)
00148             / row->fixed_pitch + 0.5);
00149         else
00150           new_blanks = 0;
00151       }
00152       if (!cblob_it.empty()) {
00153         if (blanks < 1 && word != NULL && !word->flag (W_REP_CHAR))
00154           blanks = 1;
00155         word = new WERD (&cblobs, blanks, NULL);
00156         cblob_it.set_to_list (&cblobs);
00157         word->set_flag (W_DONT_CHOP, TRUE);
00158         word_it.add_after_then_move (word);
00159         if (bol) {
00160           word->set_flag (W_BOL, TRUE);
00161           bol = FALSE;
00162         }
00163         blanks = new_blanks;
00164       }
00165       else
00166         blanks += new_blanks;
00167       while (rep_left < chop_coord) {
00168         word = add_repeated_word (&rep_it, rep_left, prev_chop_coord,
00169           blanks, row->fixed_pitch, &word_it);
00170       }
00171     }
00172     if (prev_chop_coord < chop_coord)
00173       prev_chop_coord = chop_coord;
00174   }
00175   if (!cblob_it.empty()) {
00176     word = new WERD(&cblobs, blanks, NULL);
00177     word->set_flag (W_DONT_CHOP, TRUE);
00178     word_it.add_after_then_move (word);
00179     if (bol)
00180       word->set_flag (W_BOL, TRUE);
00181   }
00182   ASSERT_HOST (word != NULL);
00183   while (!rep_it.empty ()) {
00184     add_repeated_word (&rep_it, rep_left, prev_chop_coord,
00185       blanks, row->fixed_pitch, &word_it);
00186   }
00187                                  //at end of line
00188   word_it.data ()->set_flag (W_EOL, TRUE);
00189   if (prev_chop_coord > prev_x)
00190     prev_x = prev_chop_coord;
00191   xstarts[1] = prev_x + 1;
00192   real_row = new ROW (row, (inT16) row->kern_size, (inT16) row->space_size);
00193   word_it.set_to_list (real_row->word_list ());
00194                                  //put words in row
00195   word_it.add_list_after (&words);
00196   real_row->recalc_bounding_box ();
00197   return real_row;
00198 }
00199 
00200 
00201 /**********************************************************************
00202  * add_repeated_word
00203  *
00204  * Add repeated word into the row at the given point.
00205  **********************************************************************/
00206 
00207 WERD *add_repeated_word(                         //move repeated word
00208                         WERD_IT *rep_it,         //repeated words
00209                         inT16 &rep_left,         //left edge of word
00210                         inT16 &prev_chop_coord,  //previous word end
00211                         uinT8 &blanks,           //no of blanks
00212                         float pitch,             //char cell size
00213                         WERD_IT *word_it         //list of words
00214                        ) {
00215   WERD *word;                    //word to move
00216   inT16 new_blanks;              //extra blanks
00217 
00218   if (rep_left > prev_chop_coord) {
00219     new_blanks = (uinT8) floor ((rep_left - prev_chop_coord) / pitch + 0.5);
00220     blanks += new_blanks;
00221   }
00222   word = rep_it->extract ();
00223   prev_chop_coord = word->bounding_box ().right ();
00224   word_it->add_after_then_move (word);
00225   word->set_blanks (blanks);
00226   rep_it->forward ();
00227   if (rep_it->empty ())
00228     rep_left = MAX_INT16;
00229   else
00230     rep_left = rep_it->data ()->bounding_box ().left ();
00231   blanks = 0;
00232   return word;
00233 }
00234 
00235 
00236 /**********************************************************************
00237  * split_to_blob
00238  *
00239  * Split a BLOBNBOX across a vertical chop line and put the pieces
00240  * into a left outline list and a right outline list.
00241  **********************************************************************/
00242 
00243 void split_to_blob(                                 //split the blob
00244                    BLOBNBOX *blob,                  //blob to split
00245                    inT16 chop_coord,                //place to chop
00246                    float pitch_error,               //allowed deviation
00247                    C_OUTLINE_LIST *left_coutlines,  //for cblobs
00248                    C_OUTLINE_LIST *right_coutlines) {
00249   C_BLOB *real_cblob;            //cblob to chop
00250 
00251   if (blob != NULL) {
00252     real_cblob = blob->cblob();
00253   } else {
00254     real_cblob = NULL;
00255   }
00256   if (!right_coutlines->empty() || real_cblob != NULL)
00257     fixed_chop_cblob(real_cblob,
00258                      chop_coord,
00259                      pitch_error,
00260                      left_coutlines,
00261                      right_coutlines);
00262   if (blob != NULL)
00263     delete blob;                 //free it
00264 }
00265 
00266 /**********************************************************************
00267  * fixed_chop_cblob
00268  *
00269  * Chop the given cblob (if any) and the existing right outlines to
00270  * produce a list of outlines left of the chop point and more to the right.
00271  **********************************************************************/
00272 
00273 void fixed_chop_cblob(                                //split the blob
00274                       C_BLOB *blob,                   //blob to split
00275                       inT16 chop_coord,               //place to chop
00276                       float pitch_error,              //allowed deviation
00277                       C_OUTLINE_LIST *left_outlines,  //left half of chop
00278                       C_OUTLINE_LIST *right_outlines  //right half of chop
00279                      ) {
00280   C_OUTLINE *old_right;          //already there
00281   C_OUTLINE_LIST new_outlines;   //new right ones
00282                                  //ouput iterator
00283   C_OUTLINE_IT left_it = left_outlines;
00284                                  //in/out iterator
00285   C_OUTLINE_IT right_it = right_outlines;
00286   C_OUTLINE_IT new_it = &new_outlines;
00287   C_OUTLINE_IT blob_it;          //outlines in blob
00288 
00289   if (!right_it.empty ()) {
00290     while (!right_it.empty ()) {
00291       old_right = right_it.extract ();
00292       right_it.forward ();
00293       fixed_split_coutline(old_right,
00294                            chop_coord,
00295                            pitch_error,
00296                            &left_it,
00297                            &new_it);
00298     }
00299     right_it.add_list_before (&new_outlines);
00300   }
00301   if (blob != NULL) {
00302     blob_it.set_to_list (blob->out_list ());
00303     for (blob_it.mark_cycle_pt (); !blob_it.cycled_list ();
00304       blob_it.forward ())
00305     fixed_split_coutline (blob_it.extract (), chop_coord, pitch_error,
00306         &left_it, &right_it);
00307     delete blob;
00308   }
00309 }
00310 
00311 
00312 /**********************************************************************
00313  * fixed_split_outline
00314  *
00315  * Chop the given outline (if necessary) placing the fragments which
00316  * fall either side of the chop line into the appropriate list.
00317  **********************************************************************/
00318 
00319 void fixed_split_coutline(                        //chop the outline
00320                           C_OUTLINE *srcline,     //source outline
00321                           inT16 chop_coord,       //place to chop
00322                           float pitch_error,      //allowed deviation
00323                           C_OUTLINE_IT *left_it,  //left half of chop
00324                           C_OUTLINE_IT *right_it  //right half of chop
00325                          ) {
00326   C_OUTLINE *child;              //child outline
00327   TBOX srcbox;                    //box of outline
00328   C_OUTLINE_LIST left_ch;        //left children
00329   C_OUTLINE_LIST right_ch;       //right children
00330   C_OUTLINE_FRAG_LIST left_frags;//chopped fragments
00331   C_OUTLINE_FRAG_LIST right_frags;;
00332   C_OUTLINE_IT left_ch_it = &left_ch;
00333                                  //for whole children
00334   C_OUTLINE_IT right_ch_it = &right_ch;
00335                                  //for holes
00336   C_OUTLINE_IT child_it = srcline->child ();
00337 
00338   srcbox = srcline->bounding_box();
00339   if (srcbox.left() + srcbox.right() <= chop_coord * 2
00340       && srcbox.right() < chop_coord + pitch_error) {
00341     // Whole outline is in the left side or not far over the chop_coord,
00342     // so put the whole thing on the left.
00343     left_it->add_after_then_move(srcline);
00344   } else if (srcbox.left() + srcbox.right() > chop_coord * 2
00345              && srcbox.left () > chop_coord - pitch_error) {
00346     // Whole outline is in the right side or not far over the chop_coord,
00347     // so put the whole thing on the right.
00348    right_it->add_before_stay_put(srcline);
00349   } else {
00350     // Needs real chopping.
00351     if (fixed_chop_coutline(srcline, chop_coord, pitch_error,
00352         &left_frags, &right_frags)) {
00353       for (child_it.mark_cycle_pt(); !child_it.cycled_list();
00354            child_it.forward()) {
00355         child = child_it.extract();
00356         srcbox = child->bounding_box();
00357         if (srcbox.right() < chop_coord) {
00358           // Whole child is on the left.
00359           left_ch_it.add_after_then_move(child);
00360         } else if (srcbox.left() > chop_coord) {
00361           // Whole child is on the right.
00362           right_ch_it.add_after_then_move (child);
00363         } else {
00364           // No pitch_error is allowed when chopping children to prevent
00365           // impossible outlines from being created.
00366           if (fixed_chop_coutline(child, chop_coord, 0.0f,
00367               &left_frags, &right_frags)) {
00368             delete child;
00369           } else {
00370             if (srcbox.left() + srcbox.right() <= chop_coord * 2)
00371               left_ch_it.add_after_then_move(child);
00372             else
00373               right_ch_it.add_after_then_move(child);
00374           }
00375         }
00376       }
00377       close_chopped_cfragments(&left_frags, &left_ch, pitch_error, left_it);
00378       close_chopped_cfragments(&right_frags, &right_ch, pitch_error, right_it);
00379       ASSERT_HOST(left_ch.empty() && right_ch.empty());
00380       // No children left.
00381       delete srcline;            // Smashed up.
00382     } else {
00383       // Chop failed. Just use middle coord.
00384       if (srcbox.left() + srcbox.right() <= chop_coord * 2)
00385         left_it->add_after_then_move(srcline);  // Stick whole in left.
00386       else
00387         right_it->add_before_stay_put(srcline);
00388     }
00389   }
00390 }
00391 
00392 
00393 /**********************************************************************
00394  * fixed_chop_coutline
00395  *
00396  * Chop the given coutline (if necessary) placing the fragments which
00397  * fall either side of the chop line into the appropriate list.
00398  * If the coutline lies too heavily to one side to chop, FALSE is returned.
00399  **********************************************************************/
00400 
00401 BOOL8 fixed_chop_coutline(                                  //chop the outline
00402                           C_OUTLINE *srcline,               //source outline
00403                           inT16 chop_coord,                 //place to chop
00404                           float pitch_error,                //allowed deviation
00405                           C_OUTLINE_FRAG_LIST *left_frags,  //left half of chop
00406                           C_OUTLINE_FRAG_LIST *right_frags  //right half of chop
00407                          ) {
00408   BOOL8 first_frag;              //fragment
00409   inT16 left_edge;               //of outline
00410   inT16 startindex;              //in first fragment
00411   inT32 length;                  //of outline
00412   inT16 stepindex;               //into outline
00413   inT16 head_index;              //start of fragment
00414   ICOORD head_pos;               //start of fragment
00415   inT16 tail_index;              //end of fragment
00416   ICOORD tail_pos;               //end of fragment
00417   ICOORD pos;                    //current point
00418   inT16 first_index = 0;         //first tail
00419   ICOORD first_pos;              //first tail
00420 
00421   length = srcline->pathlength ();
00422   pos = srcline->start_pos ();
00423   left_edge = pos.x ();
00424   tail_index = 0;
00425   tail_pos = pos;
00426   for (stepindex = 0; stepindex < length; stepindex++) {
00427     if (pos.x () < left_edge) {
00428       left_edge = pos.x ();
00429       tail_index = stepindex;
00430       tail_pos = pos;
00431     }
00432     pos += srcline->step (stepindex);
00433   }
00434   if (left_edge >= chop_coord - pitch_error)
00435     return FALSE;                //not worth it
00436 
00437   startindex = tail_index;
00438   first_frag = TRUE;
00439   head_index = tail_index;
00440   head_pos = tail_pos;
00441   do {
00442     do {
00443       tail_pos += srcline->step (tail_index);
00444       tail_index++;
00445       if (tail_index == length)
00446         tail_index = 0;
00447     }
00448     while (tail_pos.x () != chop_coord && tail_index != startindex);
00449     if (tail_index == startindex) {
00450       if (first_frag)
00451         return FALSE;            //doesn't cross line
00452       else
00453         break;
00454     }
00455     //#ifdef __UNIX__
00456     ASSERT_HOST (head_index != tail_index);
00457     //#endif
00458     if (!first_frag) {
00459       save_chop_cfragment(head_index,
00460                           head_pos,
00461                           tail_index,
00462                           tail_pos,
00463                           srcline,
00464                           left_frags);
00465     }
00466     else {
00467       first_index = tail_index;
00468       first_pos = tail_pos;
00469       first_frag = FALSE;
00470     }
00471     while (srcline->step (tail_index).x () == 0) {
00472       tail_pos += srcline->step (tail_index);
00473       tail_index++;
00474       if (tail_index == length)
00475         tail_index = 0;
00476     }
00477     head_index = tail_index;
00478     head_pos = tail_pos;
00479     while (srcline->step (tail_index).x () > 0) {
00480       do {
00481         tail_pos += srcline->step (tail_index);
00482         tail_index++;
00483         if (tail_index == length)
00484           tail_index = 0;
00485       }
00486       while (tail_pos.x () != chop_coord);
00487       //#ifdef __UNIX__
00488       ASSERT_HOST (head_index != tail_index);
00489       //#endif
00490       save_chop_cfragment(head_index,
00491                           head_pos,
00492                           tail_index,
00493                           tail_pos,
00494                           srcline,
00495                           right_frags);
00496       while (srcline->step (tail_index).x () == 0) {
00497         tail_pos += srcline->step (tail_index);
00498         tail_index++;
00499         if (tail_index == length)
00500           tail_index = 0;
00501       }
00502       head_index = tail_index;
00503       head_pos = tail_pos;
00504     }
00505   }
00506   while (tail_index != startindex);
00507   save_chop_cfragment(head_index,
00508                       head_pos,
00509                       first_index,
00510                       first_pos,
00511                       srcline,
00512                       left_frags);
00513   return TRUE;                   //did some chopping
00514 }
00515 
00516 /**********************************************************************
00517  * save_chop_cfragment
00518  *
00519  * Store the given fragment in the given fragment list.
00520  **********************************************************************/
00521 
00522 void save_chop_cfragment(                            //chop the outline
00523                          inT16 head_index,           //head of fragment
00524                          ICOORD head_pos,            //head of fragment
00525                          inT16 tail_index,           //tail of fragment
00526                          ICOORD tail_pos,            //tail of fragment
00527                          C_OUTLINE *srcline,         //source of edgesteps
00528                          C_OUTLINE_FRAG_LIST *frags  //fragment list
00529                         ) {
00530   inT16 jump;                    //gap across end
00531   inT16 stepcount;               //total steps
00532   C_OUTLINE_FRAG *head;          //head of fragment
00533   C_OUTLINE_FRAG *tail;          //tail of fragment
00534   inT16 tail_y;                  //ycoord of tail
00535 
00536   ASSERT_HOST (tail_pos.x () == head_pos.x ());
00537   ASSERT_HOST (tail_index != head_index);
00538   stepcount = tail_index - head_index;
00539   if (stepcount < 0)
00540     stepcount += srcline->pathlength ();
00541   jump = tail_pos.y () - head_pos.y ();
00542   if (jump < 0)
00543     jump = -jump;
00544   if (jump == stepcount)
00545     return;                      //its a nop
00546   tail_y = tail_pos.y ();
00547   head = new C_OUTLINE_FRAG (head_pos, tail_pos, srcline,
00548     head_index, tail_index);
00549   tail = new C_OUTLINE_FRAG (head, tail_y);
00550   head->other_end = tail;
00551   add_frag_to_list(head, frags);
00552   add_frag_to_list(tail, frags);
00553 }
00554 
00555 
00556 /**********************************************************************
00557  * C_OUTLINE_FRAG::C_OUTLINE_FRAG
00558  *
00559  * Constructors for C_OUTLINE_FRAG.
00560  **********************************************************************/
00561 
00562 C_OUTLINE_FRAG::C_OUTLINE_FRAG(                     //record fragment
00563                                ICOORD start_pt,     //start coord
00564                                ICOORD end_pt,       //end coord
00565                                C_OUTLINE *outline,  //source of steps
00566                                inT16 start_index,
00567                                inT16 end_index) {
00568   start = start_pt;
00569   end = end_pt;
00570   ycoord = start_pt.y ();
00571   stepcount = end_index - start_index;
00572   if (stepcount < 0)
00573     stepcount += outline->pathlength ();
00574   ASSERT_HOST (stepcount > 0);
00575   steps = new DIR128[stepcount];
00576   if (end_index > start_index) {
00577     for (int i = start_index; i < end_index; ++i)
00578       steps[i - start_index] = outline->step_dir(i);
00579   }
00580   else {
00581     int len = outline->pathlength();
00582     int i = start_index;
00583     for (; i < len; ++i)
00584       steps[i - start_index] = outline->step_dir(i);
00585     if (end_index > 0)
00586       for (; i < end_index + len; ++i)
00587         steps[i - start_index] = outline->step_dir(i - len);
00588   }
00589   other_end = NULL;
00590   delete close();
00591 }
00592 
00593 
00594 C_OUTLINE_FRAG::C_OUTLINE_FRAG(                       //record fragment
00595                                C_OUTLINE_FRAG *head,  //other end
00596                                inT16 tail_y) {
00597   ycoord = tail_y;
00598   other_end = head;
00599   start = head->start;
00600   end = head->end;
00601   steps = NULL;
00602   stepcount = 0;
00603 }
00604 
00605 
00606 /**********************************************************************
00607  * add_frag_to_list
00608  *
00609  * Insert the fragment in the list at the appropriate place to keep
00610  * them in ascending ycoord order.
00611  **********************************************************************/
00612 
00613 void add_frag_to_list(                            //ordered add
00614                       C_OUTLINE_FRAG *frag,       //fragment to add
00615                       C_OUTLINE_FRAG_LIST *frags  //fragment list
00616                      ) {
00617                                  //output list
00618   C_OUTLINE_FRAG_IT frag_it = frags;
00619 
00620   if (!frags->empty ()) {
00621     for (frag_it.mark_cycle_pt (); !frag_it.cycled_list ();
00622     frag_it.forward ()) {
00623       if (frag_it.data ()->ycoord > frag->ycoord
00624         || (frag_it.data ()->ycoord == frag->ycoord
00625          && frag->other_end->ycoord < frag->ycoord)) {
00626         frag_it.add_before_then_move (frag);
00627         return;
00628       }
00629     }
00630   }
00631   frag_it.add_to_end (frag);
00632 }
00633 
00634 
00635 /**********************************************************************
00636  * close_chopped_cfragments
00637  *
00638  * Clear the given list of fragments joining them up into outlines.
00639  * Each outline made soaks up any of the child outlines which it encloses.
00640  **********************************************************************/
00641 
00642 void close_chopped_cfragments(                             //chop the outline
00643                               C_OUTLINE_FRAG_LIST *frags,  //list to clear
00644                               C_OUTLINE_LIST *children,    //potential children
00645                               float pitch_error,           //allowed shrinkage
00646                               C_OUTLINE_IT *dest_it        //output list
00647                              ) {
00648                                  //iterator
00649   C_OUTLINE_FRAG_IT frag_it = frags;
00650   C_OUTLINE_FRAG *bottom_frag;   //bottom of cut
00651   C_OUTLINE_FRAG *top_frag;      //top of cut
00652   C_OUTLINE *outline;            //new outline
00653   C_OUTLINE *child;              //current child
00654   C_OUTLINE_IT child_it = children;
00655   C_OUTLINE_IT olchild_it;       //children of outline
00656 
00657   while (!frag_it.empty()) {
00658     frag_it.move_to_first();
00659                                  // get bottom one
00660     bottom_frag = frag_it.extract();
00661     frag_it.forward();
00662     top_frag = frag_it.data();  // look at next
00663     if ((bottom_frag->steps == 0 && top_frag->steps == 0)
00664     || (bottom_frag->steps != 0 && top_frag->steps != 0)) {
00665       if (frag_it.data_relative(1)->ycoord == top_frag->ycoord)
00666         frag_it.forward();
00667     }
00668     top_frag = frag_it.extract();
00669     if (top_frag->other_end != bottom_frag) {
00670       outline = join_chopped_fragments(bottom_frag, top_frag);
00671       ASSERT_HOST(outline == NULL);
00672     } else {
00673       outline = join_chopped_fragments(bottom_frag, top_frag);
00674       if (outline != NULL) {
00675         olchild_it.set_to_list(outline->child());
00676         for (child_it.mark_cycle_pt(); !child_it.cycled_list();
00677              child_it.forward()) {
00678           child = child_it.data();
00679           if (*child < *outline)
00680             olchild_it.add_to_end(child_it.extract());
00681         }
00682         if (outline->bounding_box().width() > pitch_error)
00683           dest_it->add_after_then_move(outline);
00684         else
00685           delete outline;          // Make it disappear.
00686       }
00687     }
00688   }
00689   while (!child_it.empty ()) {
00690     dest_it->add_after_then_move (child_it.extract ());
00691     child_it.forward ();
00692   }
00693 }
00694 
00695 
00696 /**********************************************************************
00697  * join_chopped_fragments
00698  *
00699  * Join the two lists of POLYPTs such that neither OUTLINE_FRAG
00700  * operand keeps responsibility for the fragment.
00701  **********************************************************************/
00702 
00703 C_OUTLINE *join_chopped_fragments(                         //join pieces
00704                                   C_OUTLINE_FRAG *bottom,  //bottom of cut
00705                                   C_OUTLINE_FRAG *top      //top of cut
00706                                  ) {
00707   C_OUTLINE *outline;            //closed loop
00708 
00709   if (bottom->other_end == top) {
00710     if (bottom->steps == 0)
00711       outline = top->close ();   //turn to outline
00712     else
00713       outline = bottom->close ();
00714     delete top;
00715     delete bottom;
00716     return outline;
00717   }
00718   if (bottom->steps == 0) {
00719     ASSERT_HOST (top->steps != 0);
00720     join_segments (bottom->other_end, top);
00721   }
00722   else {
00723     ASSERT_HOST (top->steps == 0);
00724     join_segments (top->other_end, bottom);
00725   }
00726   top->other_end->other_end = bottom->other_end;
00727   bottom->other_end->other_end = top->other_end;
00728   delete bottom;
00729   delete top;
00730   return NULL;
00731 }
00732 
00733 
00734 /**********************************************************************
00735  * join_segments
00736  *
00737  * Join the two edgestep fragments such that the second comes after
00738  * the first and the gap beween them is closed.
00739  **********************************************************************/
00740 
00741 void join_segments(                         //join pieces
00742                    C_OUTLINE_FRAG *bottom,  //bottom of cut
00743                    C_OUTLINE_FRAG *top      //top of cut
00744                   ) {
00745   DIR128 *steps;                  //new steps
00746   inT32 stepcount;               //no of steps
00747   inT16 fake_count;              //fake steps
00748   DIR128 fake_step;               //step entry
00749 
00750   ASSERT_HOST (bottom->end.x () == top->start.x ());
00751   fake_count = top->start.y () - bottom->end.y ();
00752   if (fake_count < 0) {
00753     fake_count = -fake_count;
00754     fake_step = 32;
00755   }
00756   else
00757     fake_step = 96;
00758 
00759   stepcount = bottom->stepcount + fake_count + top->stepcount;
00760   steps = new DIR128[stepcount];
00761   memmove (steps, bottom->steps, bottom->stepcount);
00762   memset (steps + bottom->stepcount, fake_step.get_dir(), fake_count);
00763   memmove (steps + bottom->stepcount + fake_count, top->steps,
00764     top->stepcount);
00765   delete [] bottom->steps;
00766   bottom->steps = steps;
00767   bottom->stepcount = stepcount;
00768   bottom->end = top->end;
00769   bottom->other_end->end = top->end;
00770 }
00771 
00772 
00773 /**********************************************************************
00774  * C_OUTLINE_FRAG::close
00775  *
00776  * Join the ends of this fragment and turn it into an outline.
00777  **********************************************************************/
00778 
00779 C_OUTLINE *C_OUTLINE_FRAG::close() {  //join pieces
00780   DIR128 *new_steps;              //new steps
00781   inT32 new_stepcount;           //no of steps
00782   inT16 fake_count;              //fake steps
00783   DIR128 fake_step;               //step entry
00784 
00785   ASSERT_HOST (start.x () == end.x ());
00786   fake_count = start.y () - end.y ();
00787   if (fake_count < 0) {
00788     fake_count = -fake_count;
00789     fake_step = 32;
00790   }
00791   else
00792     fake_step = 96;
00793 
00794   new_stepcount = stepcount + fake_count;
00795   if (new_stepcount > C_OUTLINE::kMaxOutlineLength)
00796     return NULL;  // Can't join them
00797   new_steps = new DIR128[new_stepcount];
00798   memmove(new_steps, steps, stepcount);
00799   memset (new_steps + stepcount, fake_step.get_dir(), fake_count);
00800   C_OUTLINE* result = new C_OUTLINE (start, new_steps, new_stepcount);
00801   delete [] new_steps;
00802   return result;
00803 }
00804 
00805 
00806 /**********************************************************************
00807  * C_OUTLINE_FRAG::operator=
00808  *
00809  * Copy this fragment.
00810  **********************************************************************/
00811 
00812                                  //join pieces
00813 C_OUTLINE_FRAG & C_OUTLINE_FRAG::operator= (
00814 const C_OUTLINE_FRAG & src       //fragment to copy
00815 ) {
00816   if (steps != NULL)
00817     delete [] steps;
00818 
00819   stepcount = src.stepcount;
00820   steps = new DIR128[stepcount];
00821   memmove (steps, src.steps, stepcount);
00822   start = src.start;
00823   end = src.end;
00824   ycoord = src.ycoord;
00825   return *this;
00826 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines