/**********************************************************************
 * File:        edgloop.c  (Formerly edgeloop.c)
 * Description: Functions to clean up an outline before approximation.
 * Author:					Ray Smith
 * Created:					Tue Mar 26 16:56:25 GMT 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          "scanedg.h"
#include          "drawedg.h"
#include          "edgloop.h"

#define MINEDGELENGTH   8        //min decent length

#define EXTERN

EXTERN double_VAR (edges_threshold_greyfraction, 0.07,
"Min edge diff for grad vector");
EXTERN BOOL_VAR (edges_show_paths, FALSE, "Draw raw outlines");
EXTERN BOOL_VAR (edges_show_needles, FALSE, "Draw edge needles");
EXTERN INT_VAR (edges_maxedgelength, 16000, "Max steps in any outline");

#ifndef GRAPHICS_DISABLED
static ScrollView* edge_win;          //window
#endif
static C_OUTLINE_IT *outline_it; //iterator
static int short_edges;          //no of short ones
static int long_edges;           //no of long ones

/**********************************************************************
 * get_outlines
 *
 * Run the edge detector over the block and return a list of outlines.
 **********************************************************************/

DLLSYM void get_outlines(                      //edge detect
#ifndef GRAPHICS_DISABLED
                         ScrollView* window,        //window for output
#endif
                         IMAGE *image,         //image to scan
                         IMAGE *t_image,       //thresholded image
                         ICOORD page_tr,       //corner of page
                         PDBLK *block,         //block to scan
                         C_OUTLINE_IT *out_it  //output iterator
                        ) {
#ifndef GRAPHICS_DISABLED
  edge_win = window;             //set statics
#endif
  outline_it = out_it;
  block_edges(t_image, block, page_tr);
  out_it->move_to_first ();
#ifndef GRAPHICS_DISABLED
  if (window != NULL)
//    overlap_picture_ops(TRUE);  //update window
  ScrollView::Update();
#endif
}


/**********************************************************************
 * complete_edge
 *
 * Complete the edge by cleaning it up andapproximating it.
 **********************************************************************/

void complete_edge(                  //clean and approximate
                   CRACKEDGE *start  //start of loop
                  ) {
  ScrollView::Color colour;                 //colour to draw in
  inT16 looplength;              //steps in loop
  ICOORD botleft;                //bounding box
  ICOORD topright;
  C_OUTLINE *outline;            //new outline

                                 //check length etc.
  colour = check_path_legal (start);
#ifndef GRAPHICS_DISABLED
  if (edges_show_paths) {
                                 //in red
    draw_raw_edge(edge_win, start, colour);
  }
#endif

  if (colour == ScrollView::RED || colour == ScrollView::BLUE) {
    looplength = loop_bounding_box (start, botleft, topright);
    outline = new C_OUTLINE (start, botleft, topright, looplength);
                                 //add to list
    outline_it->add_after_then_move (outline);
  }
}


/**********************************************************************
 * check_path_legal
 *
 * Check that the outline is legal for length and for chaincode sum.
 * The return value is RED for a normal black-inside outline,
 * BLUE for a white-inside outline, MAGENTA if it is too short,
 * YELLOW if it is too long, and GREEN if it is illegal.
 * These colours are used to draw the raw outline.
 **********************************************************************/

ScrollView::Color check_path_legal(                  //certify outline
                        CRACKEDGE *start  //start of loop
                       ) {
  int lastchain;              //last chain code
  int chaindiff;               //chain code diff
  inT32 length;                  //length of loop
  inT32 chainsum;                //sum of chain diffs
  CRACKEDGE *edgept;             //current point
  const ERRCODE ED_ILLEGAL_SUM = "Illegal sum of chain codes";

  length = 0;
  chainsum = 0;                  //sum of chain codes
  edgept = start;
  lastchain = edgept->prev->stepdir; //previous chain code
  do {
    length++;
    if (edgept->stepdir != lastchain) {
                                 //chain code difference
      chaindiff = edgept->stepdir - lastchain;
      if (chaindiff > 2)
        chaindiff -= 4;
      else if (chaindiff < -2)
        chaindiff += 4;
      chainsum += chaindiff;     //sum differences
      lastchain = edgept->stepdir;
    }
    edgept = edgept->next;
  }
  while (edgept != start && length < edges_maxedgelength);

  if ((chainsum != 4 && chainsum != -4)
  || edgept != start || length < MINEDGELENGTH) {
    if (edgept != start) {
      long_edges++;
      return ScrollView::YELLOW;
    }
    else if (length < MINEDGELENGTH) {
      short_edges++;
      return ScrollView::MAGENTA;
    }
    else {
      ED_ILLEGAL_SUM.error ("check_path_legal", TESSLOG, "chainsum=%d",
        chainsum);
      return ScrollView::GREEN;
    }
  }
                                 //colour on inside
  return chainsum < 0 ? ScrollView::BLUE : ScrollView::RED;
}

/**********************************************************************
 * loop_bounding_box
 *
 * Find the bounding box of the edge loop.
 **********************************************************************/

inT16 loop_bounding_box(                    //get bounding box
                        CRACKEDGE *&start,  //edge loop
                        ICOORD &botleft,    //bounding box
                        ICOORD &topright) {
  inT16 length;                  //length of loop
  inT16 leftmost;                //on top row
  CRACKEDGE *edgept;             //current point
  CRACKEDGE *realstart;          //topleft start

  edgept = start;
  realstart = start;
  botleft = topright = ICOORD (edgept->pos.x (), edgept->pos.y ());
  leftmost = edgept->pos.x ();
  length = 0;                    //coutn length
  do {
    edgept = edgept->next;
    if (edgept->pos.x () < botleft.x ())
                                 //get bounding box
      botleft.set_x (edgept->pos.x ());
    else if (edgept->pos.x () > topright.x ())
      topright.set_x (edgept->pos.x ());
    if (edgept->pos.y () < botleft.y ())
                                 //get bounding box
      botleft.set_y (edgept->pos.y ());
    else if (edgept->pos.y () > topright.y ()) {
      realstart = edgept;
      leftmost = edgept->pos.x ();
      topright.set_y (edgept->pos.y ());
    }
    else if (edgept->pos.y () == topright.y ()
    && edgept->pos.x () < leftmost) {
                                 //leftmost on line
      leftmost = edgept->pos.x ();
      realstart = edgept;
    }
    length++;                    //count elements
  }
  while (edgept != start);
  start = realstart;             //shift it to topleft
  return length;
}