/********************************************************************** * File: fpchop.cpp (Formerly fp_chop.c) * Description: Code to chop fixed pitch text into character cells. * Author: Ray Smith * Created: Thu Sep 16 11:14:15 BST 1993 * * (C) Copyright 1993, Hewlett-Packard Ltd. ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** http://www.apache.org/licenses/LICENSE-2.0 ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. * **********************************************************************/ #include "mfcpch.h" #ifdef __UNIX__ #include #endif #include "stderr.h" #include "blobbox.h" #include "lmedsq.h" #include "statistc.h" #include "drawtord.h" #include "tovars.h" #include "topitch.h" #include "fpchop.h" #include "notdll.h" #define EXTERN EXTERN INT_VAR (textord_fp_chop_error, 2, "Max allowed bending of chop cells"); EXTERN double_VAR (textord_fp_chop_snap, 0.5, "Max distance of chop pt from vertex"); ELISTIZE (OUTLINE_FRAG) ELISTIZE (C_OUTLINE_FRAG) //#undef ASSERT_HOST //#define ASSERT_HOST(x) if (!(x)) AfxMessageBox(#x); /********************************************************************** * fixed_pitch_words * * Make a ROW from a fixed pitch TO_ROW. **********************************************************************/ ROW *fixed_pitch_words( //find lines TO_ROW *row, //row to do FCOORD rotation //for drawing ) { BOOL8 bol; //start of line UINT8 blanks; //in front of word UINT8 new_blanks; //blanks in empty cell INT16 chop_coord; //chop boundary INT16 prev_chop_coord; //start of cell INT16 rep_left; //left edge of rep word ROW *real_row; //output row OUTLINE_LIST left_outlines; //in current blob OUTLINE_LIST right_outlines; //for next blob C_OUTLINE_LIST left_coutlines; C_OUTLINE_LIST right_coutlines; PBLOB_LIST blobs; //blobs in word C_BLOB_LIST cblobs; PBLOB_IT blob_it = &blobs; //iterator C_BLOB_IT cblob_it = &cblobs; WERD_LIST words; WERD_IT word_it = &words; //new words //repeated blobs WERD_IT rep_it = &row->rep_words; WERD *word; //new word INT32 xstarts[2]; //row ends double coeffs[3]; //quadratic INT32 prev_x; //end of prev blob //iterator BLOBNBOX_IT box_it = row->blob_list (); //boundaries ICOORDELT_IT cell_it = &row->char_cells; #ifndef GRAPHICS_DISABLED if (textord_show_page_cuts && to_win != NO_WINDOW) { plot_row_cells (to_win, RED, row, 0, &row->char_cells); } #endif prev_x = -MAX_INT16; bol = TRUE; blanks = 0; if (rep_it.empty ()) rep_left = MAX_INT16; else rep_left = rep_it.data ()->bounding_box ().left (); if (box_it.empty ()) return NULL; //empty row xstarts[0] = box_it.data ()->bounding_box ().left (); if (rep_left < xstarts[0]) { xstarts[0] = rep_left; } if (cell_it.empty () || row->char_cells.singleton ()) { tprintf ("Row without enough char cells!\n"); tprintf ("Leftmost blob is at (%d,%d)\n", box_it.data ()->bounding_box ().left (), box_it.data ()->bounding_box ().bottom ()); return NULL; } ASSERT_HOST (!cell_it.empty () && !row->char_cells.singleton ()); prev_chop_coord = cell_it.data ()->x (); word = NULL; while (rep_left < cell_it.data ()->x ()) { word = add_repeated_word (&rep_it, rep_left, prev_chop_coord, blanks, row->fixed_pitch, &word_it); } cell_it.mark_cycle_pt (); if (prev_chop_coord >= cell_it.data ()->x ()) cell_it.forward (); for (; !cell_it.cycled_list (); cell_it.forward ()) { chop_coord = cell_it.data ()->x (); while (!box_it.empty () && box_it.data ()->bounding_box ().left () <= chop_coord) { if (box_it.data ()->bounding_box ().right () > prev_x) prev_x = box_it.data ()->bounding_box ().right (); split_to_blob (box_it.extract (), chop_coord, textord_fp_chop_error + 0.5f, &left_outlines, &left_coutlines, &right_outlines, &right_coutlines); box_it.forward (); while (!box_it.empty () && box_it.data ()->blob () == NULL && box_it.data ()->cblob () == NULL) { delete box_it.extract (); box_it.forward (); } } if ((!right_outlines.empty () || !right_coutlines.empty ()) && left_outlines.empty () && left_coutlines.empty ()) split_to_blob (NULL, chop_coord, textord_fp_chop_error + 0.5f, &left_outlines, &left_coutlines, &right_outlines, &right_coutlines); if (!left_outlines.empty ()) blob_it.add_after_then_move (new PBLOB (&left_outlines)); else if (!left_coutlines.empty ()) cblob_it.add_after_then_move (new C_BLOB (&left_coutlines)); else { if (rep_left < chop_coord) { if (rep_left > prev_chop_coord) new_blanks = (UINT8) floor ((rep_left - prev_chop_coord) / row->fixed_pitch + 0.5); else new_blanks = 0; } else { if (chop_coord > prev_chop_coord) new_blanks = (UINT8) floor ((chop_coord - prev_chop_coord) / row->fixed_pitch + 0.5); else new_blanks = 0; } if (!blob_it.empty () || !cblob_it.empty ()) { if (blanks < 1 && word != NULL && !word->flag (W_REP_CHAR)) blanks = 1; if (!blob_it.empty ()) { //make real word word = new WERD (&blobs, blanks, NULL); blob_it.set_to_list (&blobs); } else { word = new WERD (&cblobs, blanks, NULL); cblob_it.set_to_list (&cblobs); } word->set_flag (W_DONT_CHOP, TRUE); word_it.add_after_then_move (word); if (bol) { word->set_flag (W_BOL, TRUE); bol = FALSE; } blanks = new_blanks; } else blanks += new_blanks; while (rep_left < chop_coord) { word = add_repeated_word (&rep_it, rep_left, prev_chop_coord, blanks, row->fixed_pitch, &word_it); } } if (prev_chop_coord < chop_coord) prev_chop_coord = chop_coord; } if (!blob_it.empty () || !cblob_it.empty ()) { if (!blob_it.empty ()) //last word on line word = new WERD (&blobs, blanks, NULL); else word = new WERD (&cblobs, blanks, NULL); word->set_flag (W_DONT_CHOP, TRUE); word_it.add_after_then_move (word); if (bol) word->set_flag (W_BOL, TRUE); } ASSERT_HOST (word != NULL); while (!rep_it.empty ()) { add_repeated_word (&rep_it, rep_left, prev_chop_coord, blanks, row->fixed_pitch, &word_it); } //at end of line word_it.data ()->set_flag (W_EOL, TRUE); if (prev_chop_coord > prev_x) prev_x = prev_chop_coord; xstarts[1] = prev_x + 1; coeffs[0] = 0; coeffs[1] = row->line_m (); coeffs[2] = row->line_c (); real_row = new ROW (row, (INT16) row->kern_size, (INT16) row->space_size); word_it.set_to_list (real_row->word_list ()); //put words in row word_it.add_list_after (&words); real_row->recalc_bounding_box (); return real_row; } /********************************************************************** * add_repeated_word * * Add repeated word into the row at the given point. **********************************************************************/ WERD *add_repeated_word( //move repeated word WERD_IT *rep_it, //repeated words INT16 &rep_left, //left edge of word INT16 &prev_chop_coord, //previous word end UINT8 &blanks, //no of blanks float pitch, //char cell size WERD_IT *word_it //list of words ) { WERD *word; //word to move INT16 new_blanks; //extra blanks if (rep_left > prev_chop_coord) { new_blanks = (UINT8) floor ((rep_left - prev_chop_coord) / pitch + 0.5); blanks += new_blanks; } word = rep_it->extract (); prev_chop_coord = word->bounding_box ().right (); word_it->add_after_then_move (word); word->set_blanks (blanks); rep_it->forward (); if (rep_it->empty ()) rep_left = MAX_INT16; else rep_left = rep_it->data ()->bounding_box ().left (); blanks = 0; return word; } /********************************************************************** * split_to_blob * * Split a BLOBNBOX across a vertical chop line and put the pieces * into a left outline list and a right outline list. **********************************************************************/ void split_to_blob( //split the blob BLOBNBOX *blob, //blob to split INT16 chop_coord, //place to chop float pitch_error, //allowed deviation OUTLINE_LIST *left_outlines, //left half of chop C_OUTLINE_LIST *left_coutlines, //for cblobs OUTLINE_LIST *right_outlines, //right half of chop C_OUTLINE_LIST *right_coutlines) { PBLOB *real_blob; //blob to chop C_BLOB *real_cblob; //cblob to chop if (blob != NULL) { real_blob = blob->blob (); real_cblob = blob->cblob (); } else { real_blob = NULL; real_cblob = NULL; } if (!right_outlines->empty () || real_blob != NULL) fixed_chop_blob(real_blob, chop_coord, pitch_error, left_outlines, right_outlines); else if (!right_coutlines->empty () || real_cblob != NULL) fixed_chop_cblob(real_cblob, chop_coord, pitch_error, left_coutlines, right_coutlines); if (blob != NULL) delete blob; //free it } /********************************************************************** * fixed_chop_blob * * Chop the given blob (if any) and the existing right outlines to * produce a list of outlines left of the chop point and more to the right. **********************************************************************/ void fixed_chop_blob( //split the blob PBLOB *blob, //blob to split INT16 chop_coord, //place to chop float pitch_error, //allowed deviation OUTLINE_LIST *left_outlines, //left half of chop OUTLINE_LIST *right_outlines //right half of chop ) { OUTLINE *old_right; //already there OUTLINE_LIST new_outlines; //new right ones //ouput iterator OUTLINE_IT left_it = left_outlines; //in/out iterator OUTLINE_IT right_it = right_outlines; OUTLINE_IT new_it = &new_outlines; OUTLINE_IT blob_it; //outlines in blob if (!right_it.empty ()) { while (!right_it.empty ()) { old_right = right_it.extract (); right_it.forward (); fixed_split_outline(old_right, chop_coord, pitch_error, &left_it, &new_it); } right_it.add_list_before (&new_outlines); } if (blob != NULL) { blob_it.set_to_list (blob->out_list ()); for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) fixed_split_outline (blob_it.extract (), chop_coord, pitch_error, &left_it, &right_it); delete blob; } } /********************************************************************** * fixed_split_outline * * Chop the given outline (if necessary) placing the fragments which * fall either side of the chop line into the appropriate list. **********************************************************************/ void fixed_split_outline( //chop the outline OUTLINE *srcline, //source outline INT16 chop_coord, //place to chop float pitch_error, //allowed deviation OUTLINE_IT *left_it, //left half of chop OUTLINE_IT *right_it //right half of chop ) { OUTLINE *child; //child outline BOX srcbox; //box of outline OUTLINE_LIST left_ch; //left children OUTLINE_LIST right_ch; //right children OUTLINE_FRAG_LIST left_frags; //chopped fragments OUTLINE_FRAG_LIST right_frags;; OUTLINE_IT left_ch_it = &left_ch; //for whole children OUTLINE_IT right_ch_it = &right_ch; //for holes OUTLINE_IT child_it = srcline->child (); srcbox = srcline->bounding_box (); //left of line if (srcbox.left () + srcbox.right () <= chop_coord * 2 //and not far over && srcbox.right () < chop_coord + pitch_error) //stick whole in left left_it->add_after_then_move (srcline); else if (srcbox.left () + srcbox.right () > chop_coord * 2 && srcbox.left () > chop_coord - pitch_error) //stick whole in right right_it->add_before_stay_put (srcline); else { //needs real chopping if (fixed_chop_outline (srcline, chop_coord, pitch_error, &left_frags, &right_frags)) { for (child_it.mark_cycle_pt (); !child_it.cycled_list (); child_it.forward ()) { child = child_it.extract (); srcbox = child->bounding_box (); if (srcbox.right () < chop_coord) left_ch_it.add_after_then_move (child); else if (srcbox.left () > chop_coord) right_ch_it.add_after_then_move (child); else { if (fixed_chop_outline (child, chop_coord, pitch_error, &left_frags, &right_frags)) delete child; else { if (srcbox.left () + srcbox.right () <= chop_coord * 2) left_ch_it.add_after_then_move (child); else right_ch_it.add_after_then_move (child); } } } close_chopped_fragments(&left_frags, &left_ch, left_it); close_chopped_fragments(&right_frags, &right_ch, right_it); ASSERT_HOST (left_ch.empty () && right_ch.empty ()); //no children left delete srcline; //smashed up } else { if (srcbox.left () + srcbox.right () <= chop_coord * 2) //stick whole in left left_it->add_after_then_move (srcline); else right_it->add_before_stay_put (srcline); } } } /********************************************************************** * fixed_chop_outline * * Chop the given outline (if necessary) placing the fragments which * fall either side of the chop line into the appropriate list. * If the outline lies too heavily to one side to chop, FALSE is returned. **********************************************************************/ BOOL8 fixed_chop_outline( //chop the outline OUTLINE *srcline, //source outline INT16 chop_coord, //place to chop float pitch_error, //allowed deviation OUTLINE_FRAG_LIST *left_frags, //left half of chop OUTLINE_FRAG_LIST *right_frags //right half of chop ) { BOOL8 not_first; //fragment BOOL8 test_valid; //test pt valid float left_edge; //of outline FCOORD chop_pos; //coords of chop float chop_starty; //test chop pt POLYPT *startpt; //in first fragment //general iterator POLYPT_IT poly_it = srcline->polypts (); POLYPT_IT head_it; //head of fragment POLYPT_IT tail_it; //tail of fragment POLYPT_IT test_tail; //possible chop pt left_edge = poly_it.data ()->pos.x (); tail_it = poly_it; for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); poly_it.forward ()) { if (poly_it.data ()->pos.x () < left_edge) { left_edge = poly_it.data ()->pos.x (); tail_it = poly_it; //find leftmost pt } } if (left_edge >= chop_coord - pitch_error) return FALSE; //not worth it startpt = tail_it.data (); not_first = FALSE; head_it = tail_it; chop_starty = tail_it.data ()->pos.y (); do { test_valid = FALSE; do { tail_it.forward (); if (test_valid && tail_it.data ()->pos.x () >= chop_coord && tail_it.data ()->pos.x () + tail_it.data ()->vec.x () <= chop_coord) { chop_pos = find_chop_coords (&tail_it, chop_coord); if (chop_pos.y () >= chop_starty) test_valid = FALSE; else { tail_it = test_tail; break; //must chop there } } if (tail_it.data ()->pos.x () <= chop_coord && tail_it.data ()->pos.x () + tail_it.data ()->vec.x () >= chop_coord) { chop_pos = find_chop_coords (&tail_it, chop_coord); chop_starty = chop_pos.y (); test_tail = tail_it; //save possible chop pt test_valid = TRUE; if (tail_it.data ()->vec.x () == 0 && tail_it.data ()->vec.y () < 0) break; //must chop here } } while (tail_it.data () != startpt && tail_it.data ()->pos.x () < chop_coord + pitch_error); //back to start if (tail_it.data () == startpt) { if (not_first) break; else return FALSE; //doesn't cross line } while (tail_it.data ()->pos.x () > chop_coord) tail_it.backward (); if (head_it.data () == tail_it.data ()) insert_extra_pt(&tail_it); insert_chop_pt(&tail_it, chop_coord); if (not_first) { save_chop_fragment(&head_it, &tail_it, left_frags); } else { tail_it.forward (); head_it = tail_it; } test_valid = FALSE; do { tail_it.forward (); if (test_valid && tail_it.data ()->pos.x () <= chop_coord && tail_it.data ()->pos.x () + tail_it.data ()->vec.x () >= chop_coord) { chop_pos = find_chop_coords (&tail_it, chop_coord); if (chop_pos.y () <= chop_starty) test_valid = FALSE; else { tail_it = test_tail; break; //must chop there } } if (tail_it.data ()->pos.x () >= chop_coord && tail_it.data ()->pos.x () + tail_it.data ()->vec.x () <= chop_coord) { chop_pos = find_chop_coords (&tail_it, chop_coord); chop_starty = chop_pos.y (); test_tail = tail_it; test_valid = TRUE; //save possible chop pt if (tail_it.data ()->vec.x () == 0 && tail_it.data ()->vec.y () > 0) break; //must chop here } } while (tail_it.data () != startpt && tail_it.data ()->pos.x () > chop_coord - pitch_error); while (tail_it.data ()->pos.x () < chop_coord) tail_it.backward (); if (head_it.data () == tail_it.data ()) insert_extra_pt(&tail_it); insert_chop_pt(&tail_it, chop_coord); save_chop_fragment(&head_it, &tail_it, right_frags); not_first = TRUE; } while (tail_it.data () != startpt); startpt = head_it.data_relative (-1); while (tail_it.data () != startpt) tail_it.forward (); save_chop_fragment(&head_it, &tail_it, left_frags); return TRUE; //did some chopping } /********************************************************************** * save_chop_fragment * * Store the given fragment in the given fragment list. **********************************************************************/ void save_chop_fragment( //chop the outline POLYPT_IT *head_it, //head of fragment POLYPT_IT *tail_it, //tail of fragment OUTLINE_FRAG_LIST *frags //fragment list ) { OUTLINE_FRAG *head; //head of fragment OUTLINE_FRAG *tail; //tail of fragment float tail_y; //ycoord of tail tail_y = tail_it->data ()->pos.y (); head = new OUTLINE_FRAG (head_it, tail_it); tail = new OUTLINE_FRAG (head, tail_y); head->other_end = tail; add_frag_to_list(head, frags); add_frag_to_list(tail, frags); head_it->forward (); tail_it->forward (); } /********************************************************************** * OUTLINE_FRAG::OUTLINE_FRAG * * Constructors for OUTLINE_FRAG. **********************************************************************/ OUTLINE_FRAG::OUTLINE_FRAG( //record fragment POLYPT_IT *head_it, //head of fragment POLYPT_IT *tail_it //tail of fragment ) { ycoord = head_it->data ()->pos.y (); other_end = NULL; polypts.assign_to_sublist (head_it, tail_it); } OUTLINE_FRAG::OUTLINE_FRAG( //record fragment OUTLINE_FRAG *head, //other end float tail_y) { ycoord = tail_y; other_end = head; } /********************************************************************** * add_frag_to_list * * Insert the fragment in the list at the appropriate place to keep * them in ascending ycoord order. **********************************************************************/ void add_frag_to_list( //ordered add OUTLINE_FRAG *frag, //fragment to add OUTLINE_FRAG_LIST *frags //fragment list ) { //output list OUTLINE_FRAG_IT frag_it = frags; if (!frags->empty ()) { for (frag_it.mark_cycle_pt (); !frag_it.cycled_list (); frag_it.forward ()) { if (frag_it.data ()->ycoord >= frag->ycoord) { frag_it.add_before_then_move (frag); return; } } } frag_it.add_to_end (frag); } /********************************************************************** * insert_chop_pt * * Decide whether or not to use the actual point as chop coord. * Insert either a duplicate of the current point or 2 copies * of the new chop point. Position the iterator at the first. **********************************************************************/ void insert_chop_pt( //make chop POLYPT_IT *it, //iterator INT16 chop_coord //required chop pt ) { POLYPT *prev_pt; //point befor chop POLYPT *chop_pt; //new vertex FCOORD chop_pos; //coords of chop FCOORD chop_vec; //vector to next prev_pt = it->data (); if (prev_pt->pos.x () + textord_fp_chop_snap >= chop_coord && prev_pt->pos.x () - textord_fp_chop_snap <= chop_coord) { chop_pt = new POLYPT (prev_pt->pos, prev_pt->vec); } else { chop_pos = FCOORD (chop_coord, prev_pt->pos.y () + prev_pt->vec.y () * (chop_coord - prev_pt->pos.x ()) / prev_pt->vec.x ()); chop_vec = it->data_relative (1)->pos - chop_pos; chop_pt = new POLYPT (chop_pos, chop_vec); it->add_after_then_move (chop_pt); chop_pt = new POLYPT (chop_pos, chop_vec); } it->add_after_stay_put (chop_pt); } /********************************************************************** * find_chop_coords * * Decide whether or not to use the actual point as chop coord. * Return the coords of the chop point. **********************************************************************/ FCOORD find_chop_coords( //make chop POLYPT_IT *it, //iterator INT16 chop_coord //required chop pt ) { POLYPT *prev_pt; //point befor chop FCOORD chop_pos; //coords of chop prev_pt = it->data (); if (prev_pt->pos.x () + textord_fp_chop_snap >= chop_coord && prev_pt->pos.x () - textord_fp_chop_snap <= chop_coord) { chop_pos = prev_pt->pos; } else { chop_pos = FCOORD (chop_coord, prev_pt->pos.y () + prev_pt->vec.y () * (chop_coord - prev_pt->pos.x ()) / prev_pt->vec.x ()); } return chop_pos; } /********************************************************************** * insert_extra_pt * * Add an extra pt to prevent single point fragments being made. **********************************************************************/ void insert_extra_pt( //make extra POLYPT_IT *it //iterator ) { POLYPT *prev_pt; //point befor chop POLYPT *chop_pt; //new vertex FCOORD chop_pos; //coords of chop FCOORD chop_vec; //vector to next prev_pt = it->data (); if (it->data_relative (1)->pos.y () > it->data_relative (-1)->pos.y ()) { chop_pos = prev_pt->pos + FCOORD (0.0f, static_cast(textord_fp_chop_snap)); } else { chop_pos = prev_pt->pos - FCOORD (0.0f, static_cast(textord_fp_chop_snap)); } chop_vec = it->data_relative (1)->pos - chop_pos; prev_pt->vec = chop_pos - prev_pt->pos; chop_pt = new POLYPT (chop_pos, chop_vec); it->add_after_then_move (chop_pt); } /********************************************************************** * close_chopped_fragments * * Clear the given list of fragments joining them up into outlines. * Each outline made soaks up any of the child outlines which it encloses. **********************************************************************/ void close_chopped_fragments( //chop the outline OUTLINE_FRAG_LIST *frags, //list to clear OUTLINE_LIST *children, //potential children OUTLINE_IT *dest_it //output list ) { //iterator OUTLINE_FRAG_IT frag_it = frags; OUTLINE_FRAG *bottom_frag; //bottom of cut OUTLINE_FRAG *top_frag; //top of cut OUTLINE *outline; //new outline OUTLINE *child; //current child OUTLINE_IT child_it = children; OUTLINE_IT olchild_it; //children of outline POLYPT_IT poly_it; //iterator for constr while (!frag_it.empty ()) { frag_it.move_to_first (); //get bottom one bottom_frag = frag_it.extract (); frag_it.forward (); //and one above it top_frag = frag_it.extract (); while (top_frag->other_end != bottom_frag) { do { frag_it.forward (); } //find other end while (frag_it.data () != top_frag->other_end); join_chopped_fragments(bottom_frag, top_frag); delete top_frag; delete frag_it.extract (); //remove middle section frag_it.forward (); top_frag = frag_it.extract (); } join_chopped_fragments(bottom_frag, top_frag); if (bottom_frag->polypts.empty ()) poly_it.set_to_list (&top_frag->polypts); else poly_it.set_to_list (&bottom_frag->polypts); outline = new OUTLINE (&poly_it); olchild_it.set_to_list (outline->child ()); for (child_it.mark_cycle_pt (); !child_it.cycled_list (); child_it.forward ()) { child = child_it.data (); if (*child < *outline) olchild_it.add_to_end (child_it.extract ()); } dest_it->add_after_then_move (outline); } while (!child_it.empty ()) { dest_it->add_after_then_move (child_it.extract ()); child_it.forward (); } } /********************************************************************** * join_chopped_fragments * * Join the two lists of POLYPTs such that the first OUTLINE_FRAG * operand keeps responsibility for the fragment. **********************************************************************/ void join_chopped_fragments( //join pieces OUTLINE_FRAG *bottom, //bottom of cut OUTLINE_FRAG *top //top of cut ) { POLYPT_IT master_it; //dest list POLYPT_IT slave_it; //src list POLYPT *cutpt; //vectors to change POLYPT *nextpt; //other end of cut if (bottom->polypts.empty ()) { master_it.set_to_list (&bottom->other_end->polypts); cutpt = master_it.data_relative (-1); ASSERT_HOST (!top->polypts.empty ()); slave_it.set_to_list (&top->polypts); nextpt = slave_it.data (); if (bottom->other_end != top) { master_it.move_to_last (); master_it.add_list_after (&top->polypts); } } else { master_it.set_to_list (&bottom->polypts); ASSERT_HOST (top->polypts.empty ()); slave_it.set_to_list (&top->other_end->polypts); cutpt = slave_it.data_relative (-1); nextpt = master_it.data (); if (bottom->other_end != top) master_it.add_list_before (&top->other_end->polypts); } cutpt->vec = nextpt->pos - cutpt->pos; } /********************************************************************** * fixed_chop_cblob * * Chop the given cblob (if any) and the existing right outlines to * produce a list of outlines left of the chop point and more to the right. **********************************************************************/ void fixed_chop_cblob( //split the blob C_BLOB *blob, //blob to split INT16 chop_coord, //place to chop float pitch_error, //allowed deviation C_OUTLINE_LIST *left_outlines, //left half of chop C_OUTLINE_LIST *right_outlines //right half of chop ) { C_OUTLINE *old_right; //already there C_OUTLINE_LIST new_outlines; //new right ones //ouput iterator C_OUTLINE_IT left_it = left_outlines; //in/out iterator C_OUTLINE_IT right_it = right_outlines; C_OUTLINE_IT new_it = &new_outlines; C_OUTLINE_IT blob_it; //outlines in blob if (!right_it.empty ()) { while (!right_it.empty ()) { old_right = right_it.extract (); right_it.forward (); fixed_split_coutline(old_right, chop_coord, pitch_error, &left_it, &new_it); } right_it.add_list_before (&new_outlines); } if (blob != NULL) { blob_it.set_to_list (blob->out_list ()); for (blob_it.mark_cycle_pt (); !blob_it.cycled_list (); blob_it.forward ()) fixed_split_coutline (blob_it.extract (), chop_coord, pitch_error, &left_it, &right_it); delete blob; } } /********************************************************************** * fixed_split_outline * * Chop the given outline (if necessary) placing the fragments which * fall either side of the chop line into the appropriate list. **********************************************************************/ void fixed_split_coutline( //chop the outline C_OUTLINE *srcline, //source outline INT16 chop_coord, //place to chop float pitch_error, //allowed deviation C_OUTLINE_IT *left_it, //left half of chop C_OUTLINE_IT *right_it //right half of chop ) { C_OUTLINE *child; //child outline BOX srcbox; //box of outline C_OUTLINE_LIST left_ch; //left children C_OUTLINE_LIST right_ch; //right children C_OUTLINE_FRAG_LIST left_frags;//chopped fragments C_OUTLINE_FRAG_LIST right_frags;; C_OUTLINE_IT left_ch_it = &left_ch; //for whole children C_OUTLINE_IT right_ch_it = &right_ch; //for holes C_OUTLINE_IT child_it = srcline->child (); srcbox = srcline->bounding_box (); //left of line if (srcbox.left () + srcbox.right () <= chop_coord * 2 //and not far over && srcbox.right () < chop_coord + pitch_error) //stick whole in left left_it->add_after_then_move (srcline); else if (srcbox.left () + srcbox.right () > chop_coord * 2 && srcbox.left () > chop_coord - pitch_error) //stick whole in right right_it->add_before_stay_put (srcline); else { //needs real chopping if (fixed_chop_coutline (srcline, chop_coord, pitch_error, &left_frags, &right_frags)) { for (child_it.mark_cycle_pt (); !child_it.cycled_list (); child_it.forward ()) { child = child_it.extract (); srcbox = child->bounding_box (); if (srcbox.right () < chop_coord) left_ch_it.add_after_then_move (child); else if (srcbox.left () > chop_coord) right_ch_it.add_after_then_move (child); else { if (fixed_chop_coutline (child, chop_coord, pitch_error, &left_frags, &right_frags)) delete child; else { if (srcbox.left () + srcbox.right () <= chop_coord * 2) left_ch_it.add_after_then_move (child); else right_ch_it.add_after_then_move (child); } } } close_chopped_cfragments(&left_frags, &left_ch, pitch_error, left_it); close_chopped_cfragments(&right_frags, &right_ch, pitch_error, right_it); ASSERT_HOST (left_ch.empty () && right_ch.empty ()); //no children left delete srcline; //smashed up } else { if (srcbox.left () + srcbox.right () <= chop_coord * 2) //stick whole in left left_it->add_after_then_move (srcline); else right_it->add_before_stay_put (srcline); } } } /********************************************************************** * fixed_chop_coutline * * Chop the given coutline (if necessary) placing the fragments which * fall either side of the chop line into the appropriate list. * If the coutline lies too heavily to one side to chop, FALSE is returned. **********************************************************************/ BOOL8 fixed_chop_coutline( //chop the outline C_OUTLINE *srcline, //source outline INT16 chop_coord, //place to chop float pitch_error, //allowed deviation C_OUTLINE_FRAG_LIST *left_frags, //left half of chop C_OUTLINE_FRAG_LIST *right_frags //right half of chop ) { BOOL8 first_frag; //fragment BOOL8 anticlock; //direction of loop INT16 left_edge; //of outline INT16 startindex; //in first fragment INT32 length; //of outline INT16 stepindex; //into outline INT16 head_index; //start of fragment ICOORD head_pos; //start of fragment INT16 tail_index; //end of fragment ICOORD tail_pos; //end of fragment ICOORD pos; //current point INT16 first_index = 0; //first tail ICOORD first_pos; //first tail length = srcline->pathlength (); pos = srcline->start_pos (); anticlock = srcline->turn_direction () > 0; left_edge = pos.x (); tail_index = 0; tail_pos = pos; for (stepindex = 0; stepindex < length; stepindex++) { if (pos.x () < left_edge) { left_edge = pos.x (); tail_index = stepindex; tail_pos = pos; } pos += srcline->step (stepindex); } if (left_edge >= chop_coord - pitch_error) return FALSE; //not worth it startindex = tail_index; first_frag = TRUE; head_index = tail_index; head_pos = tail_pos; do { do { tail_pos += srcline->step (tail_index); tail_index++; if (tail_index == length) tail_index = 0; } while (tail_pos.x () != chop_coord && tail_index != startindex); if (tail_index == startindex) { if (first_frag) return FALSE; //doesn't cross line else break; } //#ifdef __UNIX__ ASSERT_HOST (head_index != tail_index); //#endif if (!first_frag) { save_chop_cfragment(head_index, head_pos, tail_index, tail_pos, srcline, left_frags); } else { first_index = tail_index; first_pos = tail_pos; first_frag = FALSE; } while (srcline->step (tail_index).x () == 0) { tail_pos += srcline->step (tail_index); tail_index++; if (tail_index == length) tail_index = 0; } head_index = tail_index; head_pos = tail_pos; while (srcline->step (tail_index).x () > 0) { do { tail_pos += srcline->step (tail_index); tail_index++; if (tail_index == length) tail_index = 0; } while (tail_pos.x () != chop_coord); //#ifdef __UNIX__ ASSERT_HOST (head_index != tail_index); //#endif save_chop_cfragment(head_index, head_pos, tail_index, tail_pos, srcline, right_frags); while (srcline->step (tail_index).x () == 0) { tail_pos += srcline->step (tail_index); tail_index++; if (tail_index == length) tail_index = 0; } head_index = tail_index; head_pos = tail_pos; } } while (tail_index != startindex); save_chop_cfragment(head_index, head_pos, first_index, first_pos, srcline, left_frags); return TRUE; //did some chopping } /********************************************************************** * next_anti_left_seg * * Search the outline for a suitable point at which it crosses the * chop_coord from left to right. **********************************************************************/ INT16 next_anti_left_seg( //chop the outline C_OUTLINE *srcline, //source outline INT16 tail_index, //of tailpos INT16 startindex, //end of search INT32 length, //of outline INT16 chop_coord, //place to chop float pitch_error, //allowed deviation ICOORD *tail_pos //current position ) { BOOL8 test_valid; //test pt valid INT16 chop_starty; //test chop pt INT16 test_index; //possible chop pt ICOORD test_pos; //possible chop pt ICOORD prev_step; //in x to tail pos test_valid = FALSE; chop_starty = -MAX_INT16; test_index = tail_index; //stop warnings do { *tail_pos += srcline->step (tail_index); prev_step = srcline->step (tail_index); tail_index++; if (tail_index >= length) tail_index = 0; if (test_valid && tail_pos->x () == chop_coord && prev_step.x () < 0) { if (tail_pos->y () >= chop_starty) { chop_starty = -MAX_INT16; test_valid = FALSE; } else { *tail_pos = test_pos; tail_index = test_index; break; //must chop there } } if (tail_pos->x () == chop_coord && srcline->step (tail_index).x () > 0 && tail_pos->y () > chop_starty) { chop_starty = tail_pos->y (); test_index = tail_index; test_pos = *tail_pos; test_valid = TRUE; } else if (tail_pos->x () == chop_coord && srcline->step (tail_index).y () < 0 && prev_step.x () > 0 && tail_pos->y () > chop_starty) break; //must chop here } while (tail_index != startindex && tail_pos->x () < chop_coord + pitch_error); return tail_index; } /********************************************************************** * next_anti_right_seg * * Search the outline for a suitable point at which it crosses the * chop_coord from right to left. **********************************************************************/ INT16 next_anti_right_seg( //chop the outline C_OUTLINE *srcline, //source outline INT16 tail_index, //of tailpos INT16 startindex, //end of search INT32 length, //of outline INT16 chop_coord, //place to chop float pitch_error, //allowed deviation ICOORD *tail_pos //current position ) { BOOL8 test_valid; //test pt valid INT16 chop_starty; //test chop pt INT16 test_index; //possible chop pt ICOORD test_pos; //possible chop pt ICOORD prev_step; //in x to tail pos test_valid = FALSE; chop_starty = MAX_INT16; test_index = tail_index; //stop warnings do { //move forward *tail_pos += srcline->step (tail_index); prev_step = srcline->step (tail_index); tail_index++; if (tail_index >= length) tail_index = 0; if (test_valid && tail_pos->x () == chop_coord && prev_step.x () > 0) { if (tail_pos->y () <= chop_starty) { chop_starty = MAX_INT16; test_valid = FALSE; } else { *tail_pos = test_pos; tail_index = test_index; break; //must chop there } } if (tail_pos->x () == chop_coord && srcline->step (tail_index).x () < 0 && tail_pos->y () < chop_starty) { chop_starty = tail_pos->y (); test_index = tail_index; test_pos = *tail_pos; test_valid = TRUE; //save possible chop pt } else if (tail_pos->x () == chop_coord && srcline->step (tail_index).y () > 0 && prev_step.x () < 0 && tail_pos->y () < chop_starty) break; //must chop here } while (tail_index != startindex && tail_pos->x () > chop_coord - pitch_error); return tail_index; } /********************************************************************** * next_clock_left_seg * * Search the outline for a suitable point at which it crosses the * chop_coord from left to right. **********************************************************************/ INT16 next_clock_left_seg( //chop the outline C_OUTLINE *srcline, //source outline INT16 tail_index, //of tailpos INT16 startindex, //end of search INT32 length, //of outline INT16 chop_coord, //place to chop float pitch_error, //allowed deviation ICOORD *tail_pos //current position ) { BOOL8 test_valid; //test pt valid INT16 chop_starty; //test chop pt INT16 test_index; //possible chop pt ICOORD test_pos; //possible chop pt ICOORD prev_step; //in x to tail pos test_valid = FALSE; chop_starty = MAX_INT16; test_index = tail_index; //stop warnings do { *tail_pos += srcline->step (tail_index); prev_step = srcline->step (tail_index); tail_index++; if (tail_index >= length) tail_index = 0; if (test_valid && tail_pos->x () == chop_coord && prev_step.x () < 0) { if (tail_pos->y () <= chop_starty) { chop_starty = MAX_INT16; test_valid = FALSE; } else { *tail_pos = test_pos; tail_index = test_index; break; //must chop there } } if (tail_pos->x () == chop_coord && srcline->step (tail_index).x () > 0 && tail_pos->y () < chop_starty) { chop_starty = tail_pos->y (); test_index = tail_index; test_pos = *tail_pos; test_valid = TRUE; } else if (tail_pos->x () == chop_coord && srcline->step (tail_index).y () > 0 && prev_step.x () > 0 && tail_pos->y () < chop_starty) break; //must chop here } while (tail_index != startindex && tail_pos->x () < chop_coord + pitch_error); return tail_index; } /********************************************************************** * next_clock_right_seg * * Search the outline for a suitable point at which it crosses the * chop_coord from right to left. **********************************************************************/ INT16 next_clock_right_seg( //chop the outline C_OUTLINE *srcline, //source outline INT16 tail_index, //of tailpos INT16 startindex, //end of search INT32 length, //of outline INT16 chop_coord, //place to chop float pitch_error, //allowed deviation ICOORD *tail_pos //current position ) { BOOL8 test_valid; //test pt valid INT16 chop_starty; //test chop pt INT16 test_index; //possible chop pt ICOORD test_pos; //possible chop pt ICOORD prev_step; //in x to tail pos test_valid = FALSE; chop_starty = MAX_INT16; test_index = tail_index; //stop warnings do { //move forward *tail_pos += srcline->step (tail_index); prev_step = srcline->step (tail_index); tail_index++; if (tail_index >= length) tail_index = 0; if (test_valid && tail_pos->x () == chop_coord && prev_step.x () > 0) { if (tail_pos->y () >= chop_starty) { chop_starty = MAX_INT16; test_valid = FALSE; } else { *tail_pos = test_pos; tail_index = test_index; break; //must chop there } } if (tail_pos->x () == chop_coord && srcline->step (tail_index).x () < 0 && tail_pos->y () > chop_starty) { chop_starty = tail_pos->y (); test_index = tail_index; test_pos = *tail_pos; test_valid = TRUE; //save possible chop pt } else if (tail_pos->x () == chop_coord && srcline->step (tail_index).y () < 0 && prev_step.x () < 0 && tail_pos->y () > chop_starty) break; //must chop here } while (tail_index != startindex && tail_pos->x () > chop_coord - pitch_error); return tail_index; } /********************************************************************** * save_chop_cfragment * * Store the given fragment in the given fragment list. **********************************************************************/ void save_chop_cfragment( //chop the outline INT16 head_index, //head of fragment ICOORD head_pos, //head of fragment INT16 tail_index, //tail of fragment ICOORD tail_pos, //tail of fragment C_OUTLINE *srcline, //source of edgesteps C_OUTLINE_FRAG_LIST *frags //fragment list ) { INT16 jump; //gap across end INT16 stepcount; //total steps C_OUTLINE_FRAG *head; //head of fragment C_OUTLINE_FRAG *tail; //tail of fragment INT16 tail_y; //ycoord of tail ASSERT_HOST (tail_pos.x () == head_pos.x ()); ASSERT_HOST (tail_index != head_index); stepcount = tail_index - head_index; if (stepcount < 0) stepcount += srcline->pathlength (); jump = tail_pos.y () - head_pos.y (); if (jump < 0) jump = -jump; if (jump == stepcount) return; //its a nop tail_y = tail_pos.y (); head = new C_OUTLINE_FRAG (head_pos, tail_pos, srcline, head_index, tail_index); tail = new C_OUTLINE_FRAG (head, tail_y); head->other_end = tail; add_frag_to_list(head, frags); add_frag_to_list(tail, frags); } /********************************************************************** * C_OUTLINE_FRAG::C_OUTLINE_FRAG * * Constructors for C_OUTLINE_FRAG. **********************************************************************/ C_OUTLINE_FRAG::C_OUTLINE_FRAG( //record fragment ICOORD start_pt, //start coord ICOORD end_pt, //end coord C_OUTLINE *outline, //source of steps INT16 start_index, INT16 end_index) { start = start_pt; end = end_pt; ycoord = start_pt.y (); stepcount = end_index - start_index; if (stepcount < 0) stepcount += outline->pathlength (); ASSERT_HOST (stepcount > 0); steps = new DIR128[stepcount]; if (end_index > start_index) { for (int i = start_index; i < end_index; ++i) steps[i - start_index] = outline->step_dir(i); } else { int len = outline->pathlength(); int i = start_index; for (; i < len; ++i) steps[i - start_index] = outline->step_dir(i); if (end_index > 0) for (; i < end_index + len; ++i) steps[i - start_index] = outline->step_dir(i - len); } other_end = NULL; delete close(); } C_OUTLINE_FRAG::C_OUTLINE_FRAG( //record fragment C_OUTLINE_FRAG *head, //other end INT16 tail_y) { ycoord = tail_y; other_end = head; start = head->start; end = head->end; steps = NULL; stepcount = 0; } /********************************************************************** * add_frag_to_list * * Insert the fragment in the list at the appropriate place to keep * them in ascending ycoord order. **********************************************************************/ void add_frag_to_list( //ordered add C_OUTLINE_FRAG *frag, //fragment to add C_OUTLINE_FRAG_LIST *frags //fragment list ) { //output list C_OUTLINE_FRAG_IT frag_it = frags; if (!frags->empty ()) { for (frag_it.mark_cycle_pt (); !frag_it.cycled_list (); frag_it.forward ()) { if (frag_it.data ()->ycoord > frag->ycoord || frag_it.data ()->ycoord == frag->ycoord && frag->other_end->ycoord < frag->ycoord) { frag_it.add_before_then_move (frag); return; } } } frag_it.add_to_end (frag); } /********************************************************************** * close_chopped_cfragments * * Clear the given list of fragments joining them up into outlines. * Each outline made soaks up any of the child outlines which it encloses. **********************************************************************/ void close_chopped_cfragments( //chop the outline C_OUTLINE_FRAG_LIST *frags, //list to clear C_OUTLINE_LIST *children, //potential children float pitch_error, //allowed shrinkage C_OUTLINE_IT *dest_it //output list ) { //iterator C_OUTLINE_FRAG_IT frag_it = frags; C_OUTLINE_FRAG *bottom_frag; //bottom of cut C_OUTLINE_FRAG *top_frag; //top of cut C_OUTLINE *outline; //new outline C_OUTLINE *child; //current child C_OUTLINE_IT child_it = children; C_OUTLINE_IT olchild_it; //children of outline while (!frag_it.empty ()) { frag_it.move_to_first (); //get bottom one bottom_frag = frag_it.extract (); frag_it.forward (); top_frag = frag_it.data (); //look at next if (bottom_frag->steps == 0 && top_frag->steps == 0 || bottom_frag->steps != 0 && top_frag->steps != 0) { if (frag_it.data_relative (1)->ycoord == top_frag->ycoord) frag_it.forward (); } top_frag = frag_it.extract (); if (top_frag->other_end != bottom_frag) { outline = join_chopped_fragments (bottom_frag, top_frag); ASSERT_HOST (outline == NULL); } else { outline = join_chopped_fragments (bottom_frag, top_frag); ASSERT_HOST (outline != NULL); olchild_it.set_to_list (outline->child ()); for (child_it.mark_cycle_pt (); !child_it.cycled_list (); child_it.forward ()) { child = child_it.data (); if (*child < *outline) olchild_it.add_to_end (child_it.extract ()); } if (outline->bounding_box ().width () > pitch_error) dest_it->add_after_then_move (outline); else delete outline; //make it disappear } } while (!child_it.empty ()) { dest_it->add_after_then_move (child_it.extract ()); child_it.forward (); } } /********************************************************************** * join_chopped_fragments * * Join the two lists of POLYPTs such that neither OUTLINE_FRAG * operand keeps responsibility for the fragment. **********************************************************************/ C_OUTLINE *join_chopped_fragments( //join pieces C_OUTLINE_FRAG *bottom, //bottom of cut C_OUTLINE_FRAG *top //top of cut ) { C_OUTLINE *outline; //closed loop if (bottom->other_end == top) { if (bottom->steps == 0) outline = top->close (); //turn to outline else outline = bottom->close (); delete top; delete bottom; return outline; } if (bottom->steps == 0) { ASSERT_HOST (top->steps != 0); join_segments (bottom->other_end, top); } else { ASSERT_HOST (top->steps == 0); join_segments (top->other_end, bottom); } top->other_end->other_end = bottom->other_end; bottom->other_end->other_end = top->other_end; delete bottom; delete top; return NULL; } /********************************************************************** * join_segments * * Join the two edgestep fragments such that the second comes after * the first and the gap beween them is closed. **********************************************************************/ void join_segments( //join pieces C_OUTLINE_FRAG *bottom, //bottom of cut C_OUTLINE_FRAG *top //top of cut ) { DIR128 *steps; //new steps INT32 stepcount; //no of steps INT16 fake_count; //fake steps DIR128 fake_step; //step entry ASSERT_HOST (bottom->end.x () == top->start.x ()); fake_count = top->start.y () - bottom->end.y (); if (fake_count < 0) { fake_count = -fake_count; fake_step = 32; } else fake_step = 96; stepcount = bottom->stepcount + fake_count + top->stepcount; steps = new DIR128[stepcount]; memmove (steps, bottom->steps, bottom->stepcount); memset (steps + bottom->stepcount, fake_step.get_dir(), fake_count); memmove (steps + bottom->stepcount + fake_count, top->steps, top->stepcount); delete [] bottom->steps; bottom->steps = steps; bottom->stepcount = stepcount; bottom->end = top->end; bottom->other_end->end = top->end; } /********************************************************************** * C_OUTLINE_FRAG::close * * Join the ends of this fragment and turn it into an outline. **********************************************************************/ C_OUTLINE *C_OUTLINE_FRAG::close() { //join pieces DIR128 *new_steps; //new steps INT32 new_stepcount; //no of steps INT16 fake_count; //fake steps DIR128 fake_step; //step entry ASSERT_HOST (start.x () == end.x ()); fake_count = start.y () - end.y (); if (fake_count < 0) { fake_count = -fake_count; fake_step = 32; } else fake_step = 96; new_stepcount = stepcount + fake_count; new_steps = new DIR128[new_stepcount]; memmove(new_steps, steps, stepcount); memset (new_steps + stepcount, fake_step.get_dir(), fake_count); C_OUTLINE* result = new C_OUTLINE (start, new_steps, new_stepcount); delete [] new_steps; return result; } /********************************************************************** * C_OUTLINE_FRAG::operator= * * Copy this fragment. **********************************************************************/ //join pieces C_OUTLINE_FRAG & C_OUTLINE_FRAG::operator= ( const C_OUTLINE_FRAG & src //fragment to copy ) { if (steps != NULL) delete [] steps; stepcount = src.stepcount; steps = new DIR128[stepcount]; memmove (steps, src.steps, stepcount); start = src.start; end = src.end; ycoord = src.ycoord; return *this; }