// 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 PANEL3D_h
#define PANEL3D_h


#include "general.h"
#include "pt3d.h"
#include "region2d.h"
#include "fastpts.h"
#include "table.h"



/************************************************************************/
struct panel3d {
  typedef pt3d::coord coord;	// coordinate type

  table<int>	ptNums;		// table of point numbers; last pt num is
 				// the same as the first pt num
  pt3d		normal;		// normal of the plane which contains the
  				// panel3d; if the panel3d is single-sided,
  				// points to the outside of the panel3d
  bool		doubleSided;	// true  -> panel3d is double-sided (i.e.
  				//          is visible from both sides)
  				// false -> panel3d is single-sided (i.e.
  				//          is a side of a solid object)
protected:
  bool		isConvex;	// true  -> panel3d is convex
  
public:
  	panel3d() {};
  	panel3d(int* &nums,pt3d pts[])
	       {Init(nums,pts);};
  	panel3d(const region2d& rgn, fastPts& fp,
		const pt3d& offset=pt3d(0,0,0), const ang3d& ang=ang3d(0),
		const pt3d& scale =pt3d(1,1,1), bool ds=0);
  	panel3d(const panel3d& p, pt3d panelPts[], fastPts& fp,
		const pt3d& offset=pt3d(0,0,0), const ang3d& ang=ang3d(0),
		const pt3d& scale =pt3d(1,1,1));

 protected:
  void	Init(int*&,pt3d[]);

 public:
  void	Clip(const pt3d src[],const pt2d prj[],pt2d dst[],coord,
	     const pt2d&,bool,int&);
  bool	Faces(pt3d pts[],const pt3d& p)
          {return ( (pts[ptNums[0]] - p).Dot(normal) <=0) or doubleSided;}
  bool	IsConvex() const {return isConvex;}
  
 private:
  void	SetNormalAndConvexity(const pt3d[]);
};


/*-------------------------------------------------------------------------*/
/* In:  ...
   Out: numDestPts= number of pts in the clipped panel3d
 
  Clipping algorithm:
   for each point
     if coord & prev coord are not on the same side of z==planez, then 
       find intersection w/ the z==planez plane
       copy intersection to new points
     if coord.z >=planez, then
       copy coord to new points
   if the new points form an unclosed polygon,
     add an extra new point to close the polygon

  Warning!  The number of points in the clipped panel3d can be, at most,
            exactly 50% *more* than the number of points in the original
            panel3d.  Make sure destPts[] is large enough to hold all
            the points in the clipped panel3d.

  This function is inlined because the arguments for 'greaterPts' and
  'planez' are likely to be constants, in which case an optimizing compiler
  would make this function small & fast.
*/
inline void panel3d::Clip(const pt3d srcPts[], const pt2d prjPts[],
			  pt2d destPts[],
			  coord planez, const pt2d& windowCenter,
			  bool greaterPts, int& numDestPts) {
  if (ptNums.Num()==0) {
    numDestPts= 0;
    return;
  }

  int *ptNum= ptNums.Array();
  int *ptNumsEnd= ptNum +ptNums.Num();
  pt2d *destPt= destPts;

  if ((srcPts[*ptNum].z >=planez and greaterPts) or 
      (srcPts[*ptNum].z <=planez and not greaterPts))
    *(destPt++)= prjPts[*ptNum];

  int *prevNum= ptNum++;

  while (ptNum != ptNumsEnd) {
    if ((srcPts[*ptNum].z >=planez) != (srcPts[*prevNum].z >=planez)) {
      pt3d temp;
      temp.GetZCrossing(srcPts[*prevNum], srcPts[*ptNum], planez);
      *(destPt++)= temp.Project() +windowCenter;
    }
    
    if ((srcPts[*ptNum].z >=planez and greaterPts) or 
        (srcPts[*ptNum].z <=planez and not greaterPts))
      *(destPt++)= prjPts[*ptNum];

    prevNum= ptNum++;
  }
  
  if (destPt != destPts and *(destPt-1) != destPts[0])
    *(destPt++)= destPts[0];
  
  numDestPts= destPt -destPts;
}


#endif
