/* Copyright (c) 2006, Sylvio Sell - maitag
 *
 * circdots_rgb.c
 * take care, this is only a testing beta version, colors will not be saved width your project and you can only 
 * change the color-values if the texture-preview is visible (will be fixed with future versions of blenders texture-plugin-api)
 *
 * blender plugin for generating circular aranged dots.
 *
 * source written/compiled by Sylvio Sell , 2006
 *
 * Contact:      semmi@maitag.de
 * Information:  http://www.maitag.de
 *
 * ***** BEGIN GPL LICENSE BLOCK *****
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software Foundation;
 * either version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program;
 * if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA 
 *
 * ***** END GPL LICENSE BLOCK *****
 */

#include "math.h" /*fold00*/
#include "plugin.h"

#define NR_TYPES 2
#define MAX_COL_INDEX 100

extern float roundf(float x); // because of problems to compile with gcc

float result[8];
float cfra;

/* plugin menu and variables******************************************* */

char name[]= "Circular dots (RGB)";
char stnames[NR_TYPES][16]={"noRGB","RGB"};

// globale Variablen um color indizes zu speichern
float c_rgb[MAX_COL_INDEX][3];
int c_range[MAX_COL_INDEX];
int last_col_index;

VarStruct varstr[]= { /*fold00*/
	/* butcode,	name,		default, min,   max	tooltips comment*/
	{   NUM|FLO,	"Dot ",		0.2,	0.01,	5.0,	"Size od the dots"},
	{   NUM|FLO,	"Soft",		0.3,	0.0,	1.0,	"Softness of the outer edge of the dots (0=hard,1=soft)"},
	{   NUM|FLO,	"Hole",		0.0,	0.0,	0.999,	"Size of the hole inside each dot"},
	{   NUM|FLO,	"Soft",		0.0,	0.0,	1.0,	"Softness of the inner edge of the dots (0=hard,1=soft)"},
	{   NUM|FLO,	"Circ",		0.2,	0.0,	10.0,	"Radius of the first circle in center"},
	{   NUM|FLO,	"Dist",		0.2,	0.001,	1.0,	"Distance between the dots on a circle"},

	{   NUM|FLO,	"x ",		0.0,	-20.0,	20.0,	"Move all in x direction"},
	{   NUM|FLO,	"y ",		0.0,	-20.0,	20.0,	"Move all in y direction"},

	{   NUM|FLO,	"ds1",		0,		-1.0,	1.0,	"Growing/shrinking of dot-size on outer circles"},
	{   NUM|FLO,	"cs1",		0.4,	0.01,	10.0,	"Growing/shrinking of circle-size on outer circles (linear)"},
	{   NUM|FLO,	"cs2",		0,		-1.0,	1.0,	"Growing/shrinking of circle-size on outer circles (exponential)"},
	{   NUM|FLO,	"da",		0,		-1.0,	1.0,		"Growing/shrinking of dot-distance on outer circles"},

/*	{   NUM|FLO,	"ds1",		0,		-10,	10,		"Growing/shrinking of dot-size on outer circles"},
	{   NUM|FLO,	"cs1",		0.4,	0.01,	10,		"Growing/shrinking of circle-size on outer circles (linear)"},
	{   NUM|FLO,	"cs2",		0,		-10,	10,		"Growing/shrinking of circle-size on outer circles (exponential)"},
	{   NUM|FLO,	"da",		0,		-5,		5,		"Growing/shrinking of dot-distance on outer circles"},
*/
	{   NUM|INT,	"ol1",		0,		0.0,	20,		"Overlap nearby dots (on the same circle)"},
	{   NUM|INT,	"ol2",		0,		0.0,	30,		"Overlap nearby dots (between circles)"},
	{   NUM|FLO,	"rot",		0.0,	0.0,	360.0,	"Rotation factor"},
	{   NUM|FLO,	"df1",		0.0,	-1.0,	1.0,	"Changes the form of a dot (make an apple seed)"},
	{   NUM|FLO,	"df2",		0.0,	0.0,	1.0,	"Changes the form of a dot"},
	{   NUM|FLO,	"df3",		1.0,	0.4,	3.0,	"Changes the form of a dot (sharpness if df2 > 0)"},

	{   NUM|FLO,	"sx ",		1.0,	0.01,	20.0,	"Scale factor"},
	{   LABEL,	"2006 by",		0,		0,		 0, 	"coded 2006 by Sylvio Sell (beta - colors can't be saved)"},
	{   LABEL,	"Sylvio Sell",	0,		0,		 0, 	"mail to semmi@maitag.de for questions"},

	{   NUM|INT,	"ci",		1,		1,		MAX_COL_INDEX,	"Color index. (Texture-preview must be visible if you change value!)"},
	{   NUM|INT,	"cd",		1,		1,		1000,	"Distance to next ci (Texture-preview must be visible if you change value!)"},
	{   COL|FLO,	"color",	1.0,	0.0, 	1.0,	"Color of these dots (Texture-preview must be visible if you change value!)"},

};
typedef struct Cast {
    float ds,da,hs,ha,cs,a,   x,y,dg1,cg1,cg2,a1;
	int ol1,ol2;
	float rot,kf,sf,sf1,   scale,dum6,dum7;
	int ci,ca;
	float r,g,b;
} Cast;

/* ******************************************************************* */

int plugin_tex_doit(int, Cast*, float*, float*, float*); /*fold00*/

int plugin_tex_getversion(void)  /*fold00*/
{
	return B_PLUGIN_VERSION;
}

void plugin_but_changed(int but)  /*fold00*/
{
}

void plugin_init(void) /*fold00*/
{ int i;
  last_col_index=1;
  for (i=0; i<MAX_COL_INDEX; i++)
  { c_rgb[i][0]=1.0;
    c_rgb[i][1]=1.0;
	c_rgb[i][2]=1.0;
	c_range[i]=1;
  }
}

void plugin_getinfo(PluginInfo *info) /*fold00*/
{
	info->name= name;
	info->stypes= NR_TYPES;
	info->nvars= sizeof(varstr)/sizeof(VarStruct);

	info->snames= stnames[0];
	info->result= result;
	info->cfra= &cfra;
	info->varstr= varstr;

	info->init= plugin_init;
	info->tex_doit=  (TexDoit) plugin_tex_doit;
	info->callback= plugin_but_changed;
}
float punkt_abstand(float x,float y,float dotradius,float dotradiusinnen,float delta_dr,float aliasing,float aliasinginnen,float g0,float d,float delta_d,float a,float delta_a,float dw,float kernfaktor,float schmalfaktor,float schmalfaktor1,int ol_type,int delta_kreisbahn,int delta_kreisbahn1,int *kreisbahn)
{
  float k,g;
  float n; //die wievielte Kreisbahn (0 ist die erste bei g0)
  float aw; // winkel+abweichung eines teilstückes auf der kreisbahn
  float gw; // winkel am einheitskreis (der strecke von 0,0 zu x,y)  
  float drehung;
  float gk; // winkel der strecke des dichtesten punktes auf der dichtesten kreisbahn (noch ohne drehung)
  float xk,yk; // koordinaten des dichtesten punktes auf der dichtesten kreisbahn
  float ab; // abstand der beiden punkte
  float x_kern; // zwischenwert fuer kernfunktion (winkel zwischen ab und k .. geht von -1 -> oben bis 1 -> spitze)
  float intensitaet=0;
  
  // Abstand zum Mittelpunkt (0,0) bestimmen
  g=sqrtf(x*x+y*y);
  // dichteste kreisbahn bestimmen zu g(n) ... n= 0,1,2...

  if (delta_d ==0)
  {  n=roundf((fabsf(g-g0))/d);
  }
  else
  {  
     if (delta_d<0)
	 {n= -d/(2*delta_d) - sqrtf( (d*d)/(4*delta_d*delta_d) + fabsf(g-g0)/delta_d  );
	 }
	 else
	 {n= -d/(2*delta_d) + sqrtf( (d*d)/(4*delta_d*delta_d) + fabsf(g-g0)/delta_d  );
	 }
  }
  // n erstmal zur dichtester ganzzahl runden
  n=roundf(n);
  
  // ueberlappung benachbarter dots (zwischen den kreisbahnen)
  // da sich der dotradius je nach kreisbahn aendern kann,  auch andere kreisbahnen testbar machen (fkt. ruft mehrere auf und testet dann welcher was liefert an werten)
  if (n+delta_kreisbahn >=0)
  { n=n+delta_kreisbahn;
	*kreisbahn = (int) n; // kreisbahn zurueckgeben (fuer RGB)
  }
  else
  { // kleiner wohl nicht .. also kann da auch kein punkt mehr sein
    *kreisbahn = -1; // kreisbahn zurueckgeben (fuer RGB)
	return(0);
  }
  
  // kreisbahn nun mit dem entsprechendem n berechnen
  k=g0+n*d+n*n*delta_d;
 
  // zu bzw. abnahme der dot-size
  dotradius=fabsf(dotradius+delta_dr*n);
  // dotradius innen bestimmen (ist bisher ja nur der anteil , also 0 - 0.99)
  dotradiusinnen=dotradiusinnen*dotradius;
  
 // teilumfang bestimmen
  a=6.283185307179586476925286766559*a*0.2;
 // zu bzw. abnahme des abstandes der dots
  //a=a+delta_a*dotradius*n/2;
  a=a+2*delta_a*n;
  
  // damit keine division durch 0
  if (a==0) 
  {aw=360;
  }
  else
  {  
   aw=360/roundf(6.283185307179586476925286766559*k/a);
   // ueberlappung typ1 benachbarter dots (auf einer kreisbahn)
   if (ol_type==1) {aw=aw+aw*delta_kreisbahn1;}
  } 
 
  if (g==0) {gw=0;}
  else
  {
	  if (x>=0 && y>=0) // 1.Quadrant
	  {gw=asinf(y/g)*57.295779513082320876798154814105;
	  }
	  else if (x<0 && y>=0) // 2.Quadrant
	  {gw=180-asinf(y/g)*57.295779513082320876798154814105;
	  }
	  else if (x<0 && y<0) // 3.Quadrant
	  {gw=180+asinf(-y/g)*57.295779513082320876798154814105;
	  }
	  else //  4.Quadrant
	  { gw=360-asinf(-y/g)*57.295779513082320876798154814105;
	  }
  }
  
  // drehung bestimmen
  drehung=n*dw;
  
  gk=aw*roundf((gw-drehung)/aw)+drehung;
  // ueberlappung typ2 benachbarter dots (auf einer kreisbahn)
  if (ol_type>1) {gk=aw*(roundf((gw-drehung)/aw)+delta_kreisbahn1)+drehung;}
  
  gk=gk*0.017453292519943295769236907684886; // zurueck in Bogenmass fuer sin und cos
  xk=k*cosf(gk);
  yk=k*sinf(gk);
  
  ab=sqrtf((xk-x)*(xk-x)+(yk-y)*(yk-y));

  // ----------------------------------------------------------------------------------------------
  // nun den kreis zum kern machen ;)
  if (kernfaktor!=0 || schmalfaktor!=0) 
  {x_kern = acosf((k*k+ab*ab-g*g)/(2*k*ab))/3.1415926535897932384626433832795;
   // x_kern geht nun von 0 bis 1, muss aber fuer kernfunktion von -1 bis 0 gehen, darum:
   if (kernfaktor<0)
   { // je nach vorzeichen des kernfaktors die spitze umkehren
     x_kern = x_kern-1;
   }
   else
   { x_kern = 0-x_kern;
   }
   // x_kern = -fpow(-x_kern,1.6);
   kernfaktor=0.4*fabsf(kernfaktor)*(6*x_kern+12*x_kern*x_kern+6*x_kern*x_kern*x_kern)+1;
  
   // nun noch schmaler machen
   if (schmalfaktor!=0)
   { kernfaktor= kernfaktor*(-1*(schmalfaktor1+4)/5*schmalfaktor*(fpow(sin(sin(sin(sin(3.1415926535897932384626433832795*(1+x_kern))))),schmalfaktor1))+1);
   }
   dotradius=dotradius*kernfaktor;
   dotradiusinnen=dotradiusinnen*kernfaktor;
  }
  
  // ----------------------------------------------------------------------------------------------
  
  if (ab<=dotradius)
  { // zuerst so machen das die summer der beiden aliasing-werte immer <=100 !
    if (aliasing+aliasinginnen>100)
    { aliasing = aliasing -((aliasing+aliasinginnen-100)/2);
	  aliasinginnen = 100-aliasing;
	}
    // kreis-aliasing von aussen
    if (ab>dotradius-(dotradius-dotradiusinnen)*aliasing/100)
    {intensitaet = sinf((3.14*50*(dotradius-ab))/((dotradius-dotradiusinnen)*aliasing));
    }
    else
    {intensitaet = 1.0;
    }
	// innerer kreis
	if (ab>=dotradiusinnen)
	{  if (ab<dotradiusinnen+(dotradius-dotradiusinnen)*aliasinginnen/100)
       {intensitaet = sinf((3.14*50*(ab-dotradiusinnen))/((dotradius-dotradiusinnen)*aliasinginnen));
       }
	}
    else
    {intensitaet = 0.0;
    }
 	
  }
  else // garnicht nicht im kreis 
  {intensitaet = 0.0;
  }
  
  return(intensitaet);
}

int plugin_tex_doit(int stype, Cast *cast, float *texvec, float *dxt, float *dyt) /*fold00*/
{
  // colorindex aktualisieren
  int col_index=cast->ci;

  // erstmal konstanten, spaeter einstellbar
  float dotradius=(cast->ds)/2; // radius jedes dots
  float delta_dr=cast->dg1; // wie sich radius veraendert
  float aliasing=100*cast->da; // (0-100) wie unscharf es am rand des dots werden soll .. also wieviel prozent vom dotradius von aussen nach innen aliased werden soll

  //innerer kreis (dognout)
  float dotradiusinnen=cast->hs; // muss auf jeden fall kleiner sein als dotradius
  float aliasinginnen=100*cast->ha; // von 0-100
  
  float g0=cast->cs; // abstand des ersten ringes
  float d=cast->cg1; // zahlenfolge der ring-abstaende .. darf niemals 0 sein !
  float delta_d=cast->cg2; // zu oder abnahme von d (-1 bis +1 .. 0 ist gleichmaessig und default)

  float a=cast->a; // a=0.2;   abstand der dots auf dem umfang untereinander
  float delta_a=cast->a1; // zu oder abnahme von a (-1 bis +1 .. 0 ist gleichmaessig und default)
  
  float dw=cast->rot; // zahlenfolge:  um wieviel grad die naechstfolgende kreisbahn gedreht werden soll (0-360)

  float kernfaktor=cast->kf; // kernfaktor, (0->kreis, -1 schmaler kern, negativ: umgekehrte richtung der kernspitze)

  float schmalfaktor=cast->sf; // ob der kreis schmaler gemacht wird (ellypse)
  float schmalfaktor1=cast->sf1; // ob der kreis schmaler gemacht wird (ellypse)

  float x=(texvec[0] + -cast->x) * cast->scale;
  float y=(texvec[1] + -cast->y) * cast->scale;

  float intensity;
  float intensity_max=0;
  int get_kb;
  int kreisbahn=-1;
  //  dichteste kreisbahn
  int j1,j2;

  if (last_col_index!=col_index) 
    {cast->ca=c_range[col_index-1];
     cast->r=c_rgb[col_index-1][0];
     cast->g=c_rgb[col_index-1][1];
     cast->b=c_rgb[col_index-1][2];
     last_col_index=col_index;
    }
    else
    {
	  if (c_range[col_index-1]!=cast->ca || c_rgb[col_index-1][0]!=cast->r || c_rgb[col_index-1][1]!=cast->g || c_rgb[col_index-1][2]!=cast->b)
      {c_range[col_index-1]=cast->ca;
       c_rgb[col_index-1][0]=cast->r;
       c_rgb[col_index-1][1]=cast->g;
       c_rgb[col_index-1][2]=cast->b;
	  }
    }
 

  // -----------------------------------------------------------------------------------------------------------------------------
  for (j2=-(cast->ol2);j2<=(cast->ol2);j2++)
  {for (j1=-(cast->ol1);j1<=(cast->ol1);j1++)
   {intensity = punkt_abstand(x,y,dotradius,dotradiusinnen,delta_dr,aliasing,aliasinginnen,g0,d,delta_d,a,delta_a,dw,kernfaktor,schmalfaktor,schmalfaktor1,cast->ol1,j2,j1,&get_kb);
    if (intensity>intensity_max)
	{intensity_max=intensity;
	 kreisbahn=get_kb;
	}
   }
  }
  result[0]=intensity_max;

  if (stype==1 && intensity_max!=0) // nur wenn RGB berechnet werden soll
  {
	// farbe je nach kreisbahn bestimmen
	int range_summe = c_range[0];
	int range_index_next,range_index = 0;
	float r_min,g_min,b_min,r_max,g_max,b_max;

	while ((kreisbahn+1)>range_summe && range_index<(MAX_COL_INDEX-1))
	{ range_index +=1;
	range_summe += c_range[range_index];    
	}
	// im range_index ist nun der min-farbwert 
	r_min=c_rgb[range_index][0];
	g_min=c_rgb[range_index][1];
	b_min=c_rgb[range_index][2];
	// nun noch den naechsthoeheren index bestimmen
	range_index_next=range_index;
	if (range_index<(MAX_COL_INDEX-1))
	{range_index_next++;
	}

	r_max=c_rgb[range_index_next][0];
	g_max=c_rgb[range_index_next][1];
	b_max=c_rgb[range_index_next][2];

	if ((range_summe-c_range[range_index])==kreisbahn) // innere kreisbahn (bei auesserer muss range_summe-1 da stehen)
	{
		result[1] = r_min;
		result[2] = g_min;
		result[3] = b_min;
	}
	else
	{ 
		float f1 = (range_summe) - kreisbahn;
		float f2 = kreisbahn - (range_summe-c_range[range_index]);
		float f3 = (range_summe) - (range_summe-c_range[range_index]);
		result[1] = (r_min*f1+r_max*f2)/(f3);
		result[2] = (g_min*f1+g_max*f2)/(f3);
		result[3] = (b_min*f1+b_max*f2)/(f3);
	}
  } // end RGB - berechnung
  else
  {
  	result[1] = intensity_max;
	result[2] = intensity_max;
	result[3] = intensity_max;
  }
  
  result[4] = intensity_max; //alpha
  result[5] = result[5] * result[1]; // x normale
  result[6] = result[6] * result[2]; // y normale
  result[7] = result[7] * result[3]; // z normale

  return (stype+2);
}

