/**********************************************************************
 * File:        poutline.cpp  (Formerly outline.c)
 * Description: Code for OUTLINE class.
 * Author:		Ray Smith
 * Created:		Wed Oct 23 10:52:04 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          "poutline.h"

ELISTIZE_S (OUTLINE)
/**********************************************************************
 * OUTLINE::OUTLINE
 *
 * Constructor to build a OUTLINE from a compact LOOP.
 **********************************************************************/
OUTLINE::OUTLINE (               //constructor
const ICOORD & startpt,          //start position
INT8 * compactloop,              //from Tess format
BOOL8 invert,                    //reverse it
ICOORD bot_left,                 //bounding box
ICOORD top_right):
box (bot_left, top_right),
start(startpt) { 
  ICOORD pos;                    //current point
  ICOORD vec;                    //vector to next
  POLYPT *polypt;                //new point
  INT8 *vector;                  //compact loop
  POLYPT_IT it = &outline;       //iterator

  pos = startpt;
  vector = compactloop;
  do {
                                 //vector to next
    vec = ICOORD (*vector, *(vector + 1));
                                 //make a new one
    polypt = new POLYPT (FCOORD (pos), FCOORD (vec));
                                 //add to list
    it.add_after_then_move (polypt);
    pos += vec;                  //move to next
    vector += 2;
  }
  while (pos != startpt);
  if (invert)
    reverse();  //now reverse it
}


/**********************************************************************
 * OUTLINE::OUTLINE
 *
 * Constructor to build an OUTLINE from a list of POLYPTs.
 **********************************************************************/

OUTLINE::OUTLINE(                    //constructor
                 POLYPT_IT *polypts  //input list
                ) {
  POLYPT_IT other_it = *polypts; //end of list

  polypts->move_to_first ();
  other_it.move_to_last ();
                                 //put in outline
  outline.assign_to_sublist (polypts, &other_it);
  compute_bb(); 
}


/**********************************************************************
 * OUTLINE::compute_bb
 *
 * Compute the bounding box from the outline points.
 **********************************************************************/

void OUTLINE::compute_bb() {  //constructor
  ICOORD ibl, itr;               //integer bb
  FCOORD botleft;                //bounding box
  FCOORD topright;
  FCOORD pos;                    //current pos;
  POLYPT_IT polypts = &outline;  //iterator

  botleft = polypts.data ()->pos;
  topright = botleft;
  start = ICOORD ((INT16) botleft.x (), (INT16) botleft.y ());
  do {
    pos = polypts.data ()->pos;
    if (pos.x () < botleft.x ())
                                 //get bounding box
      botleft = FCOORD (pos.x (), botleft.y ());
    if (pos.y () < botleft.y ())
      botleft = FCOORD (botleft.x (), pos.y ());
    if (pos.x () > topright.x ())
      topright = FCOORD (pos.x (), topright.y ());
    if (pos.y () > topright.y ())
      topright = FCOORD (topright.x (), pos.y ());
    polypts.forward ();
  }
  while (!polypts.at_first ());
  ibl = ICOORD ((INT16) botleft.x (), (INT16) botleft.y ());
  itr = ICOORD ((INT16) topright.x () + 1, (INT16) topright.y () + 1);
  box = BOX (ibl, itr);
}


/**********************************************************************
 * OUTLINE::area
 *
 * Compute the area from the outline points.
 **********************************************************************/

float OUTLINE::area() {  //constructor
  FCOORD origin;                 //startpt
  FCOORD prev_vec;               //previous value of vec
  FCOORD vec;                    //from start to current
  float total;                   //total area
  POLYPT_IT poly_it = polypts ();//iterator
                                 //child outline itertr
  OUTLINE_IT child_it(&children); 

  origin = poly_it.data ()->pos;
  poly_it.forward ();
  vec = poly_it.data ()->pos - origin;
  poly_it.forward ();
  total = 0.0f;
  while (!poly_it.at_first ()) {
    prev_vec = vec;
    vec = poly_it.data ()->pos - origin;
    total += prev_vec * vec;
    poly_it.forward ();
  }
  total /= 2;
  for (child_it.mark_cycle_pt (); !child_it.cycled_list ();
  child_it.forward ()) {
                                 //add ares of childrein
    total += child_it.data ()->area ();
  }
  return total;
}


/**********************************************************************
 * OUTLINE::operator<
 *
 * Return TRUE if the left operand is inside the right one.
 **********************************************************************/

BOOL8
OUTLINE::operator< (             //winding number
OUTLINE & other                  //other outline
) {
  INT16 count;                   //winding count
  POLYPT_IT it = &outline;       //iterator

  if (!box.overlap (other.box))
    return FALSE;                //can't be contained

  do {
    count = other.winding_number (FCOORD (it.data ()->pos));
    //get winding number
    if (count != INTERSECTING)
      return count != 0;
    it.forward ();
  }
  while (!it.at_first ());

                                 //switch lists
  it.set_to_list (&other.outline);
  do {
                                 //try other way round
    count = winding_number (FCOORD (it.data ()->pos));
    if (count != INTERSECTING)
      return count == 0;
    it.forward ();
  }
  while (!it.at_first ());
  return TRUE;
}


/**********************************************************************
 * OUTLINE::winding_number
 *
 * Return the winding number of the outline around the given point.
 **********************************************************************/

INT16 OUTLINE::winding_number(                     //winding number
                              const FCOORD &point  //point to wind around
                             ) {
  INT16 count;                   //winding count
  POLYPT *polypt;                //current point
  FCOORD vec;                    //to current point
  float cross;                   //cross product
  POLYPT_IT it = &outline;       //iterator

  count = 0;
  do {
    polypt = it.data ();
    vec = polypt->pos - point;
                                 //crossing the line
    if (vec.y () <= 0 && vec.y () + polypt->vec.y () > 0) {
      cross = vec * polypt->vec; //cross product
      if (cross > 0)
        count++;                 //crossing right half
      else if (cross == 0)
        return INTERSECTING;     //going through point
    }
    else if (vec.y () > 0 && vec.y () + polypt->vec.y () <= 0) {
      cross = vec * polypt->vec;
      if (cross < 0)
        count--;                 //crossing back
      else if (cross == 0)
        return INTERSECTING;     //illegal
    }
    it.forward ();
  }
  while (!it.at_first ());
  return count;                  //winding number
}


/**********************************************************************
 * OUTLINE::reverse
 *
 * Reverse the direction of an outline.
 **********************************************************************/

void OUTLINE::reverse() {  //reverse direction
  POLYPT_LIST back_list;         //reversed list
  POLYPT_IT dest_it = &back_list;//destination
  POLYPT_IT src_it = &outline;   //source list
  POLYPT *polypt;                //current point

  do {
    polypt = src_it.extract ();
                                 //copy in reverse
    dest_it.add_after_then_move (polypt);
    src_it.backward ();
  }
  while (!src_it.empty ());
  dest_it.move_to_first ();
  do {
    polypt = dest_it.data ();
    polypt->vec = dest_it.data_relative (1)->pos - polypt->pos;
    //vector to next
    dest_it.forward ();
  }
  while (!dest_it.at_first ());
  dest_it.backward ();
  src_it.set_to_list (&back_list);
                                 //put it back
  outline.assign_to_sublist (&src_it, &dest_it);
}


/**********************************************************************
 * OUTLINE::move
 *
 * Move OUTLINE by vector
 **********************************************************************/

void OUTLINE::move(                  // reposition OUTLINE
                   const FCOORD vec  // by vector
                  ) {
                                 //child outline itertr
  OUTLINE_IT child_it(&children); 
  POLYPT_IT poly_it(&outline);  //outline point itertr

  box.move (vec);

  start.set_x ((INT16) floor (start.x () + vec.x () + 0.5));
  // ?? Why ICOORD?
  start.set_y ((INT16) floor (start.y () + vec.y () + 0.5));
  // ?? Why ICOORD?

  for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); poly_it.forward ())
    poly_it.data ()->pos += vec;

  for (child_it.mark_cycle_pt (); !child_it.cycled_list ();
    child_it.forward ())
  child_it.data ()->move (vec);  // move child outlines
}


/**********************************************************************
 * OUTLINE::scale
 *
 * Scale OUTLINE by vector
 **********************************************************************/

void OUTLINE::scale(               // scale OUTLINE
                    const float f  // by multiplier
                   ) {
                                 //child outline itertr
  OUTLINE_IT child_it(&children); 
  POLYPT_IT poly_it(&outline);  //outline point itertr
  POLYPT *pt;

  box.scale (f);

                                 // ?? Why ICOORD?
  start.set_x ((INT16) floor (start.x () * f + 0.5));
                                 // ?? Why ICOORD?
  start.set_y ((INT16) floor (start.y () * f + 0.5));

  for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); poly_it.forward ()) {
    pt = poly_it.data ();
    pt->pos *= f;
    pt->vec *= f;
  }

  for (child_it.mark_cycle_pt (); !child_it.cycled_list ();
    child_it.forward ())
  child_it.data ()->scale (f);   //scale child outlines
}


/**********************************************************************
 * OUTLINE::scale
 *
 * Scale OUTLINE by vector
 **********************************************************************/

void OUTLINE::scale(                     // scale OUTLINE
                    const FCOORD vector  //by fcoord
                   ) {
                                 //child outline itertr
  OUTLINE_IT child_it(&children); 
  POLYPT_IT poly_it(&outline);  //outline point itertr
  POLYPT *pt;

  box.scale (vector);

  start.set_x ((INT16) floor (start.x () * vector.x () + 0.5));
  // ?? Why ICOORD?
  start.set_y ((INT16) floor (start.y () * vector.y () + 0.5));
  // ?? Why ICOORD?

  for (poly_it.mark_cycle_pt (); !poly_it.cycled_list (); poly_it.forward ()) {
    pt = poly_it.data ();
    pt->pos =
      FCOORD (pt->pos.x () * vector.x (), pt->pos.y () * vector.y ());
    pt->vec =
      FCOORD (pt->vec.x () * vector.x (), pt->vec.y () * vector.y ());
  }

  for (child_it.mark_cycle_pt (); !child_it.cycled_list ();
    child_it.forward ())
                                 //scale child outlines
  child_it.data ()->scale (vector);
}


/**********************************************************************
 * OUTLINE::plot
 *
 * Draw the outline in the given colour.
 **********************************************************************/

#ifndef GRAPHICS_DISABLED
void OUTLINE::plot(                //draw it
                   WINDOW window,  //window to draw in
                   COLOUR colour   //colour to draw in
                  ) {
  POLYPT *polypt;                //current point
  POLYPT_IT it = &outline;       //iterator

  line_color_index(window, colour); 
  polypt = it.data ();
  move2d (window, polypt->pos.x (), polypt->pos.y ());
  do {
    it.forward ();
    polypt = it.data ();
    draw2d (window, polypt->pos.x (), polypt->pos.y ());
  }
  while (!it.at_first ());
}
#endif


/**********************************************************************
 * OUTLINE::operator=
 *
 * Assignment - deep copy data
 **********************************************************************/

OUTLINE & OUTLINE::operator= (   //assignment
const OUTLINE & source           //from this
) {
  box = source.box;
  start = source.start;
  if (!outline.empty ())
    outline.clear ();
  outline.deep_copy (&source.outline);
  if (!children.empty ())
    children.clear ();
  children.deep_copy (&source.children);
  return *this;
}
