mirror of
https://github.com/tesseract-ocr/tesseract.git
synced 2024-11-27 20:59:36 +08:00
4c6c58e6ea
git-svn-id: https://tesseract-ocr.googlecode.com/svn/trunk@158 d0cd1f9f-072b-0410-8dd7-cf729c803f20
1642 lines
58 KiB
C++
1642 lines
58 KiB
C++
/**********************************************************************
|
|
* 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 <assert.h>
|
|
#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 != NULL) {
|
|
plot_row_cells (to_win, ScrollView::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
|
|
TBOX 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<float>(textord_fp_chop_snap));
|
|
}
|
|
else {
|
|
chop_pos = prev_pt->pos - FCOORD (0.0f,
|
|
static_cast<float>(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
|
|
TBOX 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;
|
|
}
|