// -*- C++ -*-
// $RCSfile: amtriangleiterator.C,v $
// $Revision: 1.8 $
// $Author: langer $
// $Date: 2000/11/01 17:07:44 $


/* This software was produced by NIST, an agency of the U.S. government,
 * and by statute is not subject to copyright in the United States.
 * Recipients of this software assume all responsibilities associated
 * with its operation, modification and maintenance. However, to
 * facilitate maintenance we ask that before distributing modifed
 * versions of this software, you first contact the authors at
 * oof_manager@ctcms.nist.gov. 
 */

// Iterate over pixels underlying a triangle
// Usage:
//  Triangle tri = ...;
//  for(AMTriangleIterator i(tri); !i.end(); ++i) {
//     Cell_coordinate pixel = tri[i];
//     ...
//  }

#include "adaptmesh.h"
#include "amtriangle.h"
#include "amtriangleiterator.h"
#include "goof.h"

// Divide the triangle into two triangles with a horizontal division
// through one vertex. Loop over the bottom half first.

AMTriangleIterator::AMTriangleIterator(const AMTriangle &triangle)
  : btm_done(0),		// has the bottom half been done?
    top_done(0),		// has the top half been done?
    maxx(triangle.mesh->goof->query_width()-1),
    maxy(triangle.mesh->goof->query_height()-1)
{
  // find botttom node
  btmnode = triangle.node[0];
  double btmy = btmnode->coord().y;
  int btmi = 0;
  for(int i=1; i<3; i++) {
    double y = triangle.node[i]->coord().y;
    if(y < btmy) {
      btmy = y;
      btmnode = triangle.node[i];
      btmi = i;
    }
  }
  // find middle node and top nodes
  int n1 = (btmi+1)%3;
  int n2 = (btmi+2)%3;
  if(triangle.node[n1]->coord().y < triangle.node[n2]->coord().y) {
    midnode = triangle.node[n1];
    topnode = triangle.node[n2];
  }
  else {
    midnode = triangle.node[n2];
    topnode = triangle.node[n1];
  }

  // is the middle node to the right or left of the line joining top and btm?
  midright = ((midnode->coord() - btmnode->coord()) %
	      (topnode->coord() - btmnode->coord()) > 0);

  // find equations for sides
  if(btmnode->coord().y == midnode->coord().y)
    btm_done = 1;		// there is no bottom triangle
  else {
    // compute slopes of edges, assuming that the midpoint is on the left side
    lslope_btm = (midnode->coord().x - btmnode->coord().x)
      / (midnode->coord().y - btmnode->coord().y);
    rslope_btm = (topnode->coord().x - btmnode->coord().x)
      / (topnode->coord().y - btmnode->coord().y);
    if(midright) {		// wrong assumption!
      double temp = lslope_btm;
      lslope_btm = rslope_btm;
      rslope_btm = temp;
    }
    if(rslope_btm == lslope_btm)
      btm_done = 1;
  }
  if(midnode->coord().y == topnode->coord().y)
    top_done = 1;		// there is no top triangle
  else {
    // compute slopes of edges, assuming that the midpoint is on the left side
    lslope_top = (topnode->coord().x - midnode->coord().x)
      / (topnode->coord().y - midnode->coord().y);
    rslope_top = (topnode->coord().x - btmnode->coord().x)
      / (topnode->coord().y - btmnode->coord().y);
    if(midright) {		// wrong assumption
      double temp = rslope_top;
      rslope_top = lslope_top;
      lslope_top = temp;
    }
    if(rslope_top == lslope_top)
      top_done = 1;
  }
  if(!btm_done) {
    current.y = (short) btmnode->coord().y;
    current.x = (short) xmin_btm(current.y);
    current_xmax = (int) xmax_btm(current.y);
  }
  else {
    current.y = (short) midnode->coord().y;
    current.x = (short) xmin_top(current.y);
    current_xmax = (int) xmax_top(current.y);
  }
}

double AMTriangleIterator::xmin_btm(double y) const {
  // Find the minimum x value for a pixel within the bottom triangle
  // at this y. Use the equation for the left edge of the triangle,
  // but be careful if the edge has an end within this row of pixels
  // (ie between y and y+1).
  double min;
  double yint;			// y at minimum value of x

  if(lslope_btm > 0) {
//                edge
//   y+1  ---------/------------------------------------/-------
//                /  yint is the lowest y              /
//               /   for the edge in this row         B
//     y  ------/-------------------------------------+---------
//            xmin                                   xmin when btm node
//                                                   is within the row
    yint = y;
    if(y < btmnode->coord().y)	// bottom node is in this row of pixels
      yint = btmnode->coord().y;
  }
  else {			// lslope_btm < 0
    yint = y + 1;
    if(!midright && midnode->coord().y < yint) // left edge ends within this row
      yint = midnode->coord().y;
  }
  
  min = floor(btmnode->coord().x + lslope_btm*(yint - btmnode->coord().y));
  if(min < 0) min = 0;
  if(min > maxx) min = maxx;
  return min;
}

double AMTriangleIterator::xmax_btm(double y) const {
  double max;
  double yint;
  if(rslope_btm > 0) {
    yint = y + 1;
    if(midright && midnode->coord().y < yint) // midnode is within this row
	yint = midnode->coord().y;
  }
  else {			// rslope_btm < 0
    yint = y;
    if(y < btmnode->coord().y)	// bottom node is in this row of pixels
      yint = btmnode->coord().y;
  }
  max = floor(btmnode->coord().x + rslope_btm*(yint - btmnode->coord().y));
  if(max > maxx) max = maxx;
  if(max < 0) max = 0;
  return max;
}

double AMTriangleIterator::xmin_top(double y) const {
  double min;
  double yint;
  if(lslope_top > 0) {
    yint = y;
    if(!midright && midnode->coord().y > yint) // middle node is within this row
	yint = midnode->coord().y;
  }
  else {			// lslope_top < 0
    yint = y + 1;
    if(topnode->coord().y < y + 1)
      yint = topnode->coord().y;
  }
  min = floor(topnode->coord().x + lslope_top*(yint - topnode->coord().y));
  if(min < 0) min = 0;
  if(min > maxx) min = maxx;
  return min;
}

double AMTriangleIterator::xmax_top(double y) const {
  double max;
  double yint;
  if(rslope_top > 0) {
    yint = y + 1;
    if(topnode->coord().y < y + 1)
      yint = topnode->coord().y;
  }
  else {			// rslope_top < 0
    yint = y;
    if(midright && midnode->coord().y > y)
      yint = midnode->coord().y;
  }
  max = floor(topnode->coord().x + rslope_top*(yint - topnode->coord().y));
  if(max > maxx) max = maxx;
  if(max < 0) max = 0;
  return max;
}

void AMTriangleIterator::operator++() {
  if(!btm_done) {
    current.x++;		// next pixel in this row
    if(current.x > current_xmax) { // done with this row?
      current.y++;		// go up to next row
      if(current.y > maxy || current.y > topnode->coord().y) {
	top_done = btm_done = 1;
      }
      else if(current.y > midnode->coord().y) { // done with btm triangle?
	btm_done = 1;
	// initial settings for top triangle
	current.x = (short) xmin_top(current.y);
	current_xmax = (int) xmax_top(current.y);
      }
      else {			// new row in bottom triangle
	current.x = (short) xmin_btm(current.y);
	current_xmax = (int) xmax_btm(current.y);
      }
    }
  }
  else if(!top_done) {
    current.x++;
    if(current.x > current_xmax) { // done with this row?
      current.y++;
      if(current.y > maxy || current.y > topnode->coord().y)
	top_done = 1;
      else {
	current.x = (short) xmin_top(current.y);
	current_xmax = (int) xmax_top(current.y);
      }
    }
  }
}

Cell_coordinate AMTriangle::operator[](AMTriangleIterator &iter) const {
  return iter.current;
}
