/**********************************************************************
 * File:        polyblob.cpp  (Formerly blob.c)
 * Description: Code for PBLOB class.
 * Author:		Ray Smith
 * Created:		Wed Oct 23 15:17:41 BST 1991
 *
 * (C) Copyright 1991, 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"
#include          "varable.h"
#include          "ocrrow.h"
#include          "polyblob.h"
//#include                                                      "lapoly.h"
#include          "polyaprx.h"

#define EXTERN

EXTERN BOOL_VAR (polygon_tess_approximation, TRUE,
"Do tess poly instead of greyscale");

ELISTIZE_S (PBLOB)
/**********************************************************************
 * position_outline
 *
 * Position the outline in the given list at the relevant place
 * according to its nesting.
 **********************************************************************/
static void position_outline(                        //put in place
                             OUTLINE *outline,       //thing to place
                             OUTLINE_LIST *destlist  //desstination list
                            ) {
  OUTLINE *dest_outline;         //outline from dest list
  OUTLINE_IT it = destlist;      //iterator
                                 //iterator on children
  OUTLINE_IT child_it = outline->child ();

  if (!it.empty ()) {
    do {
      dest_outline = it.data (); //get destination
                                 //encloses dest
      if (*dest_outline < *outline) {
                                 //take off list
        dest_outline = it.extract ();
                                 //put this in place
        it.add_after_then_move (outline);
                                 //make it a child
        child_it.add_to_end (dest_outline);
        while (!it.at_last ()) {
          it.forward ();         //do rest of list
                                 //check for other children
          dest_outline = it.data ();
          if (*dest_outline < *outline) {
                                 //take off list
            dest_outline = it.extract ();
            child_it.add_to_end (dest_outline);
            //make it a child
            if (it.empty ())
              break;
          }
        }
        return;                  //finished
      }
                                 //enclosed by dest
      else if (*outline < *dest_outline) {
        position_outline (outline, dest_outline->child ());
        //place in child list
        return;                  //finished
      }
      it.forward ();
    }
    while (!it.at_first ());
  }
  it.add_to_end (outline);       //at outer level
}


/**********************************************************************
 * plot_outline_list
 *
 * Draw a list of outlines in the given colour and their children
 * in the child colour.
 **********************************************************************/

#ifndef GRAPHICS_DISABLED
static void plot_outline_list(                     //draw outlines
                              OUTLINE_LIST *list,  //outline to draw
                              WINDOW window,       //window to draw in
                              COLOUR colour,       //colour to use
                              COLOUR child_colour  //colour of children
                             ) {
  OUTLINE *outline;              //current outline
  OUTLINE_IT it = list;          //iterator

  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) {
    outline = it.data ();
                                 //draw it
    outline->plot (window, colour);
    if (!outline->child ()->empty ())
      plot_outline_list (outline->child (), window,
        child_colour, child_colour);
  }
}
#endif


/**********************************************************************
 * PBLOB::PBLOB
 *
 * Constructor to build a PBLOB from a list of OUTLINEs.
 * The OUTLINEs are not copied so the source list is emptied.
 * The OUTLINEs are nested correctly in the blob.
 **********************************************************************/

PBLOB::PBLOB(                            //constructor
             OUTLINE_LIST *outline_list  //in random order
            ) {
  OUTLINE *outline;              //current outline
  OUTLINE_IT it = outline_list;  //iterator

  while (!it.empty ()) {         //grab the list
    outline = it.extract ();     //get off the list
                                 //put it in place
    position_outline(outline, &outlines); 
    if (!it.empty ())
      it.forward ();
  }
}


/**********************************************************************
 * approximate_outline_list
 *
 * Convert a list of outlines to polygonal form.
 **********************************************************************/

static void approximate_outline_list(                          //do list of outlines
                                     C_OUTLINE_LIST *srclist,  //list to convert
                                     OUTLINE_LIST *destlist,   //desstination list
                                     float xheight             //height of line
                                    ) {
  C_OUTLINE *src_outline;        //outline from src list
  OUTLINE *dest_outline;         //result
  C_OUTLINE_IT src_it = srclist; //source iterator
  OUTLINE_IT dest_it = destlist; //iterator

  do {
    src_outline = src_it.data ();
    //              if (polygon_tess_approximation)
    dest_outline = tesspoly_outline (src_outline, xheight);
    //              else
    //                      dest_outline=greypoly_outline(src_outline,xheight);
    if (dest_outline != NULL) {
      dest_it.add_after_then_move (dest_outline);
      if (!src_outline->child ()->empty ())
                                 //do child list
        approximate_outline_list (src_outline->child (), dest_outline->child (), xheight);
    }
    src_it.forward ();
  }
  while (!src_it.at_first ());
}


/**********************************************************************
 * PBLOB::PBLOB
 *
 * Constructor to build a PBLOB from a C_BLOB by polygonal approximation.
 **********************************************************************/

PBLOB::PBLOB(                //constructor
             C_BLOB *cblob,  //compact blob
             float xheight   //height of line
            ) {
  BOX bbox;                      //bounding box

  if (!cblob->out_list ()->empty ()) {
                                 //get bounding box
    bbox = cblob->bounding_box ();
    if (bbox.height () > xheight)
      xheight = bbox.height ();  //max of line and blob
                                 //copy it
    approximate_outline_list (cblob->out_list (), &outlines, xheight);
  }
}


/**********************************************************************
 * PBLOB::bounding_box
 *
 * Return the bounding box of the blob.
 **********************************************************************/

BOX PBLOB::bounding_box() {  //bounding box
  OUTLINE *outline;              //current outline
  OUTLINE_IT it = &outlines;     //outlines of blob
  BOX box;                       //bounding box

  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) {
    outline = it.data ();
    box += outline->bounding_box ();
  }
  return box;
}


/**********************************************************************
 * PBLOB::area
 *
 * Return the area of the blob.
 **********************************************************************/

float PBLOB::area() {  //area
  OUTLINE *outline;              //current outline
  OUTLINE_IT it = &outlines;     //outlines of blob
  float total;                   //total area

  total = 0.0f;
  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ()) {
    outline = it.data ();
    total += outline->area ();
  }
  return total;
}


/**********************************************************************
 * PBLOB::baseline_normalise
 *
 * Baseline normalize a blob
 **********************************************************************/

PBLOB *PBLOB::baseline_normalise(                //normalize blob
                                 ROW *row,       //row it came from
                                 DENORM *denorm  //inverse mapping
                                ) {
  BOX blob_box = bounding_box ();
  float x_centre = (blob_box.left () + blob_box.right ()) / 2.0;
  PBLOB *bn_blob;                //copied blob

  *denorm = DENORM (x_centre, bln_x_height / row->x_height (), row);
  bn_blob = new PBLOB;           //get one
  *bn_blob = *this;              //deep copy
  bn_blob->move (FCOORD (-denorm->origin (), -row->base_line (x_centre)));
  bn_blob->scale (denorm->scale ());
  bn_blob->move (FCOORD (0.0, bln_baseline_offset));
  return bn_blob;
}


/**********************************************************************
 * PBLOB::baseline_denormalise
 *
 * DeBaseline Normalise the blob properly with the given denorm.
 **********************************************************************/

void PBLOB::baseline_denormalise(                      // Tess style BL Norm
                                 const DENORM *denorm  //antidote
                                ) {
  float blob_x_left;           // Left edge of blob.
  BOX blob_box;                  //blob bounding box

  move(FCOORD (0.0f, 0.0f - bln_baseline_offset));
  blob_box = bounding_box ();
  blob_x_left = blob_box.left ();
  scale (1.0 / denorm->scale_at_x (blob_x_left));
  move (FCOORD (denorm->origin (),
    denorm->yshift_at_x (blob_x_left)));
}


/**********************************************************************
 * PBLOB::move
 *
 * Move PBLOB by vector
 **********************************************************************/

void PBLOB::move(                  // reposition blob
                 const FCOORD vec  // by vector
                ) {
  OUTLINE_IT it(&outlines);  // iterator

  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
    it.data ()->move (vec);      // move each outline
}


/**********************************************************************
 * PBLOB::scale
 *
 * Scale PBLOB by float multiplier
 **********************************************************************/

void PBLOB::scale(               // scale blob
                  const float f  // by multiplier
                 ) {
  OUTLINE_IT it(&outlines);  // iterator

  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
    it.data ()->scale (f);       // scale each outline
}


/**********************************************************************
 * PBLOB::scale
 *
 * Scale PBLOB by float multiplier
 **********************************************************************/

void PBLOB::scale(                  // scale blob
                  const FCOORD vec  // by multiplier
                 ) {
  OUTLINE_IT it(&outlines);  // iterator

  for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
    it.data ()->scale (vec);     // scale each outline
}


/**********************************************************************
 * PBLOB::plot
 *
 * Draw the PBLOB in the given colour.
 **********************************************************************/

#ifndef GRAPHICS_DISABLED
void PBLOB::plot(                     //draw it
                 WINDOW window,       //window to draw in
                 COLOUR blob_colour,  //main colour
                 COLOUR child_colour  //for holes
                ) {
  plot_outline_list(&outlines, window, blob_colour, child_colour); 
}
#endif
