/* ------------------------------------------------------------------------- *
 * Clock Wipe Transition:
 * 	This transition wipes ibuf2 -> to ibuf1. 
 * 
 * Parameters:
 * 	Clockwise button: 
 *		If enabled wipe direction is clockwise else counter clock wise.
 * 
 *	X:
 * 		X translate. The middlepoint of the screen is (0,0) and X
 * 		can have values from -1.0 to 1.0.
 * 
 * 	Y:
 * 		Y translate. The middlepoint if the screen is (0,0) and Y
 * 		can have values from -1.0 to 1.0.
 * 
 * 	Start:
 * 		Angle for segments start boundary. Values from 0 to 360
 * 
 * 	Radius:
 * 		Radius for wipe circle. Values from 0 to 1.0, where 1.0 
 * 		is diagonal of the (0,0)-(sx,sy) rectangle.
 * 
 * 	Rounds:
 * 		This parameter is for future use. It will tell how many
 * 		rounds the wipe makes. This is implemented later.
 * 
 * Information:
 * 	If any problems or bugs found please inform unicorn@rieska.oulu.fi.
 * 
 * ------------------------------------------------------------------------- */


/* ------------------------------------------------------------------------- *
 * Include mandatory include files
 * ------------------------------------------------------------------------- */
#include "plugin.h"

/* ------------------------------------------------------------------------- *
 * Defines
 * ------------------------------------------------------------------------- */

#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif

/* ------------------------------------------------------------------------- *
 * Global variables
 * ------------------------------------------------------------------------- */

char name[24]= "circle";

VarStruct varstr[]= {
   { TOG|INT, "Clockwise", 0.0, 0.0, 1.0, "Direction CW or CCW"},
   { NUM|FLO, "X", 0.0, -1.0, 1.0, "X translate for (0,0)"},
   { NUM|FLO, "Y", 0.0, -1.0, 1.0, "Y translate for (0,0)"},
   { NUM|INT, "Start", 0.0, 0.0, 360.0, "Start angle"},
   { NUM|FLO, "Radius", 0.5, 0.0, 1.0, "Radius of the circle"},
   { NUM|INT, "Rounds", 1.0, 1.0, 10.0, "Number of rounds"},
};

/* ------------------------------------------------------------------------- *
 * Cast structure 
 * ------------------------------------------------------------------------- */

typedef struct Cast 
{
  int   direction;
  float centerx;
  float centery;
  int   sangle;
  float radius;
  int   rounds;
} Cast;

/* cfra: the current frame */
float cfra;
void plugin_seq_doit(Cast *, float, float, int, int, ImBuf *, ImBuf *, ImBuf *, ImBuf *);

/* ========================================================================= *
 * Fixed functions for plugins
 * ========================================================================= */
int plugin_seq_getversion(void) 
{
  return B_PLUGIN_VERSION;
}

void plugin_but_changed(int but) 
{
}

void plugin_init()
{
}

void plugin_getinfo(PluginInfo *info)
{
  info->name= name;
  info->nvars= sizeof(varstr)/sizeof(VarStruct);
  info->cfra= &cfra;
  
  info->varstr= varstr;
  
  info->init= plugin_init;
  info->seq_doit= (SeqDoit) plugin_seq_doit;
  info->callback= plugin_but_changed;
}

/* ========================================================================= *
 * Clock wipe transition
 * ========================================================================= */
void plugin_seq_doit(Cast *cast, float facf0, float facf1, int sx, int sy, ImBuf *ibuf1, 
		     ImBuf *ibuf2, ImBuf *out, ImBuf *use)
{
  /* Make variables */
  int   tx, ty;    	/* temporary x and y                                  */
  int   px, py;         /* point (x,y) which is tested                        */
  int   cx, cy;         /* moved center coordinates                           */
  float inside;       	/* is the point inside the circle                     */
  float startx, starty; /* start point on the circle                          */
  float endx, endy;     /* end point on the circle                            */
  float side;           /* end point side compared to (0,0) - (startx,starty) */
  int   sangle, eangle; /* temporary segment's start/end boundary angles      */
  float sside,eside;    /* point compared to end and start lines              */
  int   state = 0;      /* state 0 if not inside 1 if inside                  */ 
  int   angles;         /* count of angles if there are more rounds than 1    */
  int   radius;         /* radius in pixels, where max is diagonal distance   */
  int   direction;      /* counter clockwise or clockwise                     */
  
  /* Calculate center coordinates    */
  cx = (int)(ibuf1->x/2 + (ibuf1->x/2)*cast->centerx);
  cy = (int)(ibuf1->y/2 + (ibuf1->y/2)*cast->centery);

  /* Calculate angles                */
  direction = cast->direction ? -1 : 1;
  angles = cast->rounds * 360;
  sangle = cast->sangle;
  eangle = sangle + (facf0 * angles * direction);
  radius = sqrt(sx*sx + sy*sy) * cast->radius;
  
  /* Start creating the clock effect */
  for(ty=0;ty<sy;ty++)
  {
    for(tx=0;tx<sx;tx++)
    {
      /* Be sure the state is set out for default */
      /* and as well set correct place for point  */
      state = 0;
      
      px = tx - cx;
      py = ty - cy;
      
      /* Test if current point inside the circle  */
      /* if x^2 + y^2 <= r^2, then inside         */
      inside = sqrt(px*px + py*py);
      
      if(inside > radius && cast->direction)
	state = 1;
      
      if(inside <= radius)
      {
	/* Calculate points from both segment boundary lines (start and end lines) */
	/* x,y = r*cos(angle),r*sin(angle)                                         */
	/* more information in http://tcaep.co.uk/maths/cat/c/circle.htm           */
	startx = radius * cos(sangle * M_PI / 180);
	starty = radius * sin(sangle * M_PI / 180);
	endx   = radius * cos(eangle * M_PI / 180);
	endy   = radius * sin(eangle * M_PI / 180);
	
	/* Now check if the end point, we just calculated, is under or over the    */
	/* line which goes through (0,0) - (startx,starty). If under then then the */
	/* angle between lines (0,0) - (startx,starty) and (0,0) - (endx,endy) is  */
	/* bigger than 180 degrees. (if side < 0 then under else over).            */
	side = -starty*endx + startx*endy;
	
	/* Now start checking if the point is inside the segment. There is used    */
	/* simple boolean check. Code should be self commenting. It just checks    */
	/* if point, we are testing, is under or over lines start and end and      */
	/* depending on the "angle" between those lines we use operator OR or AND. */
	sside = -starty*px + startx*py;
	eside = -endy*px + endx*py;	
	
	if(side >= 0)
	{
	  if(sside > 0 && eside < 0)
	    state = 1;
	}
	else
	{
	  if(sside >0 || eside <0)
	    state = 1;
	}	
      }
      /* If clockwise */
      if(cast->direction)
	state = 1 - state;
                  
      /* If inside then copy the ibuf2 data else copy ibuf1 data      */
      if(state && facf0 && facf1)      
	out->rect[ty*sx+tx] = ibuf1->rect[ty*sx+tx];      
      else
	out->rect[ty*sx+tx] = ibuf2->rect[ty*sx+tx];
    }
  } 
}

