// Copyright (c) 1999 Philip A. Hardin (pahardin@cs.utexas.edu)
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License v2 or later.


#ifndef PT2D_h
#define PT2D_h


#include <math.h>    // to get sqrt(), M_SQRT2, sin(), etc.
#include <iostream.h>
#include "general.h"
#include "ang2d.h"


/*======================================================================*/
struct pt2d {
  enum {numDims=2};
  typedef double coord;
  coord	x,y;

  	pt2d() {};
  	pt2d(coord nx, coord ny) : x(nx), y(ny) {}
  	pt2d(const coord* ptr) {x= *ptr++; y= *ptr++;}
  	pt2d(coord*& ptr)      {x= *ptr++; y= *ptr++;}
  	pt2d(const pt2d*& ptr) {*this= *ptr++;}
  	pt2d(pt2d*& ptr)       {*this= *ptr++;}

  // inline functions
  bool	operator==(const pt2d& p) const	{return (x==p.x and y==p.y);}
  bool	operator!=(const pt2d& p) const	{return (x!=p.x or y!=p.y);}
  void	operator+=(const pt2d& p)	{x+=p.x; y+=p.y;}
  void	operator-=(const pt2d& p)	{x-=p.x; y-=p.y;}
  pt2d	operator+ (const pt2d& p) const {return pt2d(x+p.x,y+p.y);}
  pt2d	operator- (const pt2d& p) const {return pt2d(x-p.x,y-p.y);}
  void	operator*=(const pt2d& sc)	{x*=sc.x; y*=sc.y;}
  void	operator*=(coord sc)		{x*=sc; y*=sc;}
  pt2d	operator* (coord sc) const	{return pt2d(x*sc, y*sc);}
  pt2d	operator* (const pt2d& p) const	{return pt2d(x*p.x, y*p.y);}
  coord	Dot       (const pt2d& p) const	{return x*p.x +y*p.y;}
  void	operator/=(coord dv)		{x/=dv; y/=dv;}
  pt2d	operator/ (coord sc) const	{return pt2d(x/sc, y/sc);}
  pt2d	operator/ (const pt2d& d) const {return pt2d(x/d.x,y/d.y);}
  coord& operator[](int i)              {if (i==0) return x; else return y;}
  const
  coord& operator[](int i) const        {if (i==0) return x; else return y;}
  coord	Dist() const		{return sqrt((double)(sqr(x) +sqr(y)));}
  coord	FastDist() const;
  pt2d	operator>>(ang2d) const;
  void	operator>>=(ang2d);
  pt2d	Left90() const		{return pt2d(-y,x);}
  pt2d	Right90() const		{return pt2d(y,-x);}

  // non-inline functions
  ang2d	Ang2d() const;
  void	Interpolate(const pt2d&,const pt2d&,int,int);
  pt2d  Normalized(coord=1) const;
  void	Normalize(coord);
  void	FastNormalize(coord);
  pt2d	operator>>(double) const;
  void	operator>>=(double);
};


/*----------------------------------------------------------------------*/
inline pt2d::coord pt2d::FastDist() const
{ pt2d absPt;

  absPt.x= ABS(x);
  absPt.y= ABS(y);
  if (absPt.x >absPt.y)
    return absPt.x +(M_SQRT2-1)*absPt.y;
  else
    return absPt.y +(M_SQRT2-1)*absPt.x;
}


/*----------------------------------------------------------------------*/
inline pt2d pt2d::operator>>(ang2d ang) const
{ double sinAng= sinTbl[ang&(2*MA_PI-1)];
  double cosAng= cosTbl[ang&(2*MA_PI-1)];
  return pt2d((coord)(cosAng*x - sinAng*y),(coord)(cosAng*y + sinAng*x));
}


/*----------------------------------------------------------------------*/
inline void pt2d::operator>>=(ang2d ang)
{ double sinAng= sinTbl[ang&(2*MA_PI-1)];
  double cosAng= cosTbl[ang&(2*MA_PI-1)];
  coord	tempx= (coord)(cosAng*x - sinAng*y);
  y= (coord)(cosAng*y + sinAng*x);
  x= tempx;
}


/*======================================================================*/
inline ostream& operator<<(ostream& out, const pt2d& p) {
  return out << p[0] << "," << p[1];
}


// Destination point is pt1 for function below
inline void Get2dXCrossing(pt2d& pt1,const pt2d& pt2,pt2d::coord planex)
{ pt1.y= pt1.y +(planex - pt1.x) * (pt2.y - pt1.y) / (pt2.x - pt1.x);
  pt1.x= planex;
}


// Destination point is pt1 for function below
inline void Get2dYCrossing(pt2d& pt1,const pt2d& pt2,pt2d::coord planey)
{ pt1.x= pt1.x +(planey - pt1.y) * (pt2.x - pt1.x) / (pt2.y - pt1.y);
  pt1.y= planey;
}


/*----------------------------------------------------------------------*/
/* In:	numPts = number of pts in pts[] array
   Out:	numPts = number of pts in destPts[] array

   Clipping algorithm:
   for each point
     if coord & prev coord are not on the same side of x==planex, then 
       find intersection w/ the x==planex plane
       copy intersection to new points
     if coord.x >=planex, then
       copy coord to new points
   if the new points form an unclosed polygon,
     add an extra new point to close the polygon
   
   Presumably, the arguments for the parameters 'greaterPts' and 'axis'
   will usually be constants, in which case an optimizing compiler should
   be able to make this function small & fast.  (This is the
   reason for declaring this function 'inline'.)
*/

inline void ClipPts(const pt2d *srcPt, pt2d destPts[], int& numPts,
		    double plane, bool greaterPts, int axis) {
  const pt2d *srcPtsEnd= srcPt +numPts;
  pt2d *destPt= destPts;
#define axis2 (not axis)

  if (numPts==0) return;
  if (((*srcPt)[axis] >= plane and greaterPts) or 
      ((*srcPt)[axis] <= plane and not greaterPts))
    *(destPt++)= *srcPt;
    
  const pt2d *prevPt= srcPt;
  srcPt++;

  while (srcPt != srcPtsEnd) {
    if (((*srcPt)[axis] >=plane) != ((*prevPt)[axis] >=plane)) {
      (*destPt)[axis]= plane;
      (*destPt)[axis2]= (*prevPt)[axis2] + (plane - (*prevPt)[axis]) * 
	((*srcPt)[axis2] - (*prevPt)[axis2]) /
	((*srcPt)[axis]  - (*prevPt)[axis]);
      destPt++;
    }
    if (((*srcPt)[axis] >=plane and greaterPts) or 
        ((*srcPt)[axis] <=plane and not greaterPts))
      *(destPt++)= *srcPt;
    prevPt= srcPt++;
  }
  
  if (destPt != destPts and *(destPt-1) != *destPts)
    *(destPt++)= *destPts;
  
  numPts= destPt -destPts;
}

#endif
