// --------------------------------------------------------------------------
// This file is part of the pmpd software.
//
//    pmpd software 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 3 of the License, or
//    (at your option) any later version.
//
//    pmpd firmware 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 pmpd software. If not, see <http://www.gnu.org/licenses/>.
// -------------------------------------------------------------------------- 
//
// pmpd = physical modeling for pure data
// ch@chnry.net


#include "m_pd.h"
#include <math.h>

#include "pmpd_export.h"
#include "pmpd_version.h"

#define max(a,b) ( ((a) > (b)) ? (a) : (b) ) 
#define min(a,b) ( ((a) < (b)) ? (a) : (b) ) 

static t_class *mass3D_class;

typedef struct _mass3D {
  t_object  x_obj;
  t_float posX_old_1, posX_old_2, posY_old_1, posY_old_2, posZ_old_1, posZ_old_2;
  t_float Xinit, Yinit, Zinit, forceX, forceY, forceZ, VX, VY, VZ, dX, dY, dZ;
  t_float mass3D, seuil, onoff, damp;
  t_atom  pos_new[3], vitesse[4], force[4];
  t_float minX, maxX, minY, maxY, minZ, maxZ;
  t_outlet *position3D_new, *vitesse_out, *force_out;
  t_symbol *x_sym; // receive
  unsigned int x_state; // random
  t_float x_f; // random
} t_mass3D;

static int makeseed3D(void)
{
    static unsigned int random_nextseed = 1489853723;
    random_nextseed = random_nextseed * 435898247 + 938284287;
    return (random_nextseed & 0x7fffffff);
}

static t_float random_bang3D(t_mass3D *x)
{
    int nval;
    int range = 2000000;
	t_float rnd;
	unsigned int randval = x->x_state;
	x->x_state = randval = randval * 472940017 + 832416023;
    nval = ((double)range) * ((double)randval)
    	* (1./4294967296.);
    if (nval >= range) nval = range-1;

	rnd=nval;

	rnd-=1000000;
	rnd=rnd/1000000.;	//pour mettre entre -1 et 1;
    return (rnd);
}

void mass3D_on(t_mass3D *x)
{
  x->onoff = 1;
}

void mass3D_off(t_mass3D *x)
{
  x->onoff = 0;
}

void mass3D_minX(t_mass3D *x, t_floatarg f1)
{
  x->minX = f1;
}

void mass3D_maxX(t_mass3D *x, t_floatarg f1)
{
  x->maxX = f1;
}

void mass3D_minY(t_mass3D *x, t_floatarg f1)
{
  x->minY = f1;
}

void mass3D_maxY(t_mass3D *x, t_floatarg f1)
{
  x->maxY = f1;
}

void mass3D_minZ(t_mass3D *x, t_floatarg f1)
{
  x->minZ = f1;
}

void mass3D_maxZ(t_mass3D *x, t_floatarg f1)
{
  x->maxZ = f1;
}

void mass3D_seuil(t_mass3D *x, t_floatarg f1)
{
  x->seuil = f1;
}

void mass3D_damp(t_mass3D *x, t_floatarg f1)
{
  x->damp = f1;
}

void mass3D_loadbang(t_mass3D *x)
{
  outlet_anything(x->position3D_new, gensym("position3D"), 3, x->pos_new);
}

void mass3D_setX(t_mass3D *x, t_float posX)
{
  
  x->posX_old_2 = posX;
  x->posX_old_1 = posX;
  x->forceX=0;

  SETFLOAT(&(x->pos_new[0]), posX);

  outlet_anything(x->position3D_new, gensym("position3D"), 3, x->pos_new);

}

void mass3D_setY(t_mass3D *x, t_float posY)
{
  x->posY_old_2 = posY;
  x->posY_old_1 = posY;
  x->forceY=0;

  SETFLOAT(&(x->pos_new[1]), posY);

  outlet_anything(x->position3D_new, gensym("position3D"), 3, x->pos_new);

}

void mass3D_setZ(t_mass3D *x, t_float posZ)
{
  x->posZ_old_2 = posZ;
  x->posZ_old_1 = posZ;
  x->forceZ=0;

  SETFLOAT(&(x->pos_new[2]), posZ);

  outlet_anything(x->position3D_new, gensym("position3D"), 3, x->pos_new);

}

void mass3D_setXYZ(t_mass3D *x, t_float posX, t_float posY, t_float posZ)
{
  
  x->posX_old_2 = posX;
  x->posX_old_1 = posX;
  x->forceX=0;

  x->posY_old_2 = posY;
  x->posY_old_1 = posY;
  x->forceY=0;

  x->posZ_old_2 = posZ;
  x->posZ_old_1 = posZ;
  x->forceZ=0;

  SETFLOAT(&(x->pos_new[0]), posX);
  SETFLOAT(&(x->pos_new[1]), posY);
  SETFLOAT(&(x->pos_new[2]), posZ);

  outlet_anything(x->position3D_new, gensym("position3D"), 3, x->pos_new);
}

void mass3D_set_mass3D(t_mass3D *x, t_float mass)
{
  x->mass3D=mass;
}


void mass3D_force(t_mass3D *x, t_floatarg f1, t_floatarg f2, t_floatarg f3)
{
  x->forceX += f1;
  x->forceY += f2;
  x->forceZ += f3;
}

void mass3D_dXYZ(t_mass3D *x, t_floatarg f1, t_floatarg f2, t_floatarg f3)
{
  x->dX += f1;
  x->dY += f2;
  x->dZ += f3;
}

void mass3D_dX(t_mass3D *x, t_floatarg f1 )
{
  x->dX += f1;
}

void mass3D_dY(t_mass3D *x, t_floatarg f1 )
{
  x->dY += f1;
}

void mass3D_dZ(t_mass3D *x, t_floatarg f1 )
{
  x->dZ += f1;
}

void mass3D_bang(t_mass3D *x)
{
  t_float posX_new, posY_new, posZ_new, vX=1, vY=1, vZ=1;
 if (x->onoff != 0)
 {

  if (x->seuil > 0)
  {
  if (x->posZ_old_1 == x->minZ) // si on est en dehors de la structure -> frottement sec sur les bords
  {
	  if (sqrt(x->forceX*x->forceX + x->forceY*x->forceY)<=(x->seuil * -(x->forceZ)))
	  {
		  vX = 0;		// on est a l'interieur du cone de frotement,  
		  vY = 0;		// on est a l'interieur du cone de frotement,  
	  }
  }

  if (x->posZ_old_1 == x->maxZ) // si on est en dehors de la structure -> frottement sec sur les bords
  {
	  if (sqrt(x->forceX*x->forceX + x->forceY*x->forceY)<=(x->seuil * (x->forceZ)))
 	  {
		  vX = 0;		// on est a l'interieur du cone de frotement,  
 		  vY = 0;		// on est a l'interieur du cone de frotement,  
 	  }
  }
 
  if (x->posY_old_1 == x->minY) // si on est en dehors de la structure -> frottement sec sur les bords
  {
	  if (sqrt(x->forceX*x->forceX + x->forceZ*x->forceZ)<=(x->seuil * -(x->forceY)))
	  {
		  vX = 0;		// on est a l'interieur du cone de frotement,  
 	      vZ = 0;		// on est a l'interieur du cone de frotement,  
 	  }
  }

  if (x->posY_old_1 == x->maxY) // si on est en dehors de la structure -> frottement sec sur les bords
  {
	  if (sqrt(x->forceX*x->forceX + x->forceZ*x->forceZ)<=(x->seuil * (x->forceY)))
	  {
		  vX = 0;		// on est a l'interieur du cone de frotement,  
 		  vZ = 0;		// on est a l'interieur du cone de frotement,  
  	  }
  }

  if (x->posX_old_1 == x->minX) // si on est en dehors de la structure -> frottement sec sur les bords
  {
	  if (sqrt(x->forceY*x->forceY + x->forceZ*x->forceZ)<=(x->seuil * -(x->forceX)))
	  {
		  vY = 0;		// on est a l'interieur du cone de frotement,  
 		  vZ = 0;		// on est a l'interieur du cone de frotement,  
  	  }
  }

  if (x->posX_old_1 == x->maxX) // si on est en dehors de la structure -> frottement sec sur les bords
  {
	  if (sqrt(x->forceY*x->forceY + x->forceZ*x->forceZ)<=(x->seuil * (x->forceX)))
	  {
		  vY = 0;		// on est a l'interieur du cone de frotement,  
  		  vZ = 0;		// on est a l'interieur du cone de frotement,  
 	  }
  }
  }

	x->forceX += x->damp * ((x->posX_old_2)-(x->posX_old_1));
	x->forceY += x->damp * ((x->posY_old_2)-(x->posY_old_1)); // damping
	x->forceZ += x->damp * ((x->posZ_old_2)-(x->posZ_old_1)); // damping

  if (!(x->mass3D == 0))
  {
  posX_new = x->forceX/x->mass3D + 2*x->posX_old_1 - x->posX_old_2;
  posY_new = x->forceY/x->mass3D + 2*x->posY_old_1 - x->posY_old_2;
  posZ_new = x->forceZ/x->mass3D + 2*x->posZ_old_1 - x->posZ_old_2;
  }
  else 
  {
  posX_new = x->posX_old_1;
  posY_new = x->posY_old_1;
  posZ_new = x->posY_old_1;
  }

 
	  if (vX==0)
	    posX_new = x->posX_old_1; // on n'a pas de mv qd on est a l'interieur du cone de frotement
	  if (vY==0)
	    posY_new = x->posY_old_1;
	  if (vZ==0)
	    posZ_new = x->posZ_old_1;

	  posX_new = max(min(x->maxX, posX_new), x->minX);
	  posY_new = max(min(x->maxY, posY_new), x->minY);
	  posZ_new = max(min(x->maxZ, posZ_new), x->minZ);


  posX_new += x->dX;
  posY_new += x->dY;
  posZ_new += x->dZ;

  x->posX_old_1 += x->dX;
  x->posY_old_1 += x->dY;
  x->posZ_old_1 += x->dZ;
	
  SETFLOAT(&(x->pos_new[0]), posX_new );
  SETFLOAT(&(x->pos_new[1]), posY_new );
  SETFLOAT(&(x->pos_new[2]), posZ_new );

  x->posX_old_2 = x->posX_old_1;
  x->posX_old_1 = posX_new;

  x->posY_old_2 = x->posY_old_1;
  x->posY_old_1 = posY_new;

  x->posZ_old_2 = x->posZ_old_1;
  x->posZ_old_1 = posZ_new;

  SETFLOAT(&(x->force[0]), x->forceX );
  SETFLOAT(&(x->force[1]), x->forceY );
  SETFLOAT(&(x->force[2]), x->forceZ );
  SETFLOAT(&(x->force[3]), sqrt( (x->forceX * x->forceX) + (x->forceY * x->forceY) + (x->forceZ * x->forceZ) ));
 
//  x->forceX=0;
//  x->forceY=0;
//  x->forceZ=0;

  x->forceX = random_bang3D(x)*1e-25;
  x->forceY = random_bang3D(x)*1e-25; // avoiding denormal problem by adding low amplitude noise
  x->forceZ = random_bang3D(x)*1e-25; 


  x->dX=0;
  x->dY=0;
  x->dZ=0;

  x->VX =  x->posX_old_1 -  x->posX_old_2;
  x->VY =  x->posY_old_1 -  x->posY_old_2;
  x->VZ =  x->posZ_old_1 -  x->posZ_old_2;
  
  SETFLOAT(&(x->vitesse[0]), x->VX );
  SETFLOAT(&(x->vitesse[1]), x->VY );
  SETFLOAT(&(x->vitesse[2]), x->VZ );
  SETFLOAT(&(x->vitesse[3]), sqrt( (x->VX * x->VX) + (x->VY * x->VY) + (x->VZ * x->VZ) ));
 
  outlet_anything(x->vitesse_out, gensym("velocity3D"), 4, x->vitesse);
  outlet_anything(x->force_out, gensym("force3D"), 4, x->force);
  outlet_anything(x->position3D_new, gensym("position3D"), 3, x->pos_new);
 }
}

void mass3D_reset(t_mass3D *x)
{
  
  x->posX_old_2 = x->Xinit;
  x->posX_old_1 = x->Xinit;
  x->forceX=0;

  x->posY_old_2 = x->Yinit;
  x->posY_old_1 = x->Yinit;
  x->forceY=0;

  x->posZ_old_2 = x->Zinit;
  x->posZ_old_1 = x->Zinit;
  x->forceZ=0;

  x->VX = 0;
  x->VY = 0;
  x->VZ = 0;

  x->dX=0;
  x->dY=0;
  x->dZ=0;

  x->seuil=0;

  x->onoff = 1;

  SETFLOAT(&(x->pos_new[0]), x->Xinit );
  SETFLOAT(&(x->pos_new[1]), x->Yinit );
  SETFLOAT(&(x->pos_new[2]), x->Zinit );

  SETFLOAT(&(x->force[0]), 0 );
  SETFLOAT(&(x->force[1]), 0 );
  SETFLOAT(&(x->force[2]), 0 );
  SETFLOAT(&(x->force[3]), 0 );
 
  SETFLOAT(&(x->vitesse[0]), 0 );
  SETFLOAT(&(x->vitesse[1]), 0 );
  SETFLOAT(&(x->vitesse[2]), 0 );
  SETFLOAT(&(x->vitesse[3]), 0 );

  outlet_anything(x->vitesse_out, gensym("velocity3D"), 4, x->vitesse);
  outlet_anything(x->force_out, gensym("force3D"), 4, x->force); 
  outlet_anything(x->position3D_new, gensym("position3D"), 3, x->pos_new);

}


void mass3D_resetf(t_mass3D *x)
{
  x->forceX=0;
  x->forceY=0;
  x->forceZ=0;

  x->dX=0;
  x->dY=0;
  x->dZ=0;
}

void mass3D_inter_ambient(t_mass3D *x, t_symbol *s, int argc, t_atom *argv)
{
	t_float tmp;

	if (argc == 17) 
		// 0 : FX
		// 1 : FY
		// 2 : FZ
		// 3 : RndX
		// 4 : RndY
		// 5 : RndZ
		// 6 : D2
		// 7 : rien
		// 8 : Xmin
		// 9 : Xmax
		// 10 : Ymin
		// 11 : Ymax
		// 12 : Zmin
		// 13 : Zmax
		// 14 : dX
		// 15 : dY
		// 16 : dZ
	{
		if (x->posX_old_1 > atom_getfloatarg(8, argc, argv))
		{
			if (x->posX_old_1 < atom_getfloatarg(9, argc, argv))
			{
				if (x->posY_old_1 > atom_getfloatarg(10, argc, argv))
				{
					if (x->posY_old_1 < atom_getfloatarg(11, argc, argv))
					{
					if (x->posZ_old_1 > atom_getfloatarg(12, argc, argv))
					{
					if (x->posZ_old_1 < atom_getfloatarg(13, argc, argv))
					{
						x->forceX += atom_getfloatarg(0, argc, argv);
						x->forceY += atom_getfloatarg(1, argc, argv); // constant
						x->forceZ += atom_getfloatarg(2, argc, argv); // constant

						x->forceX += random_bang3D(x)*atom_getfloatarg(3, argc, argv);
						x->forceY += random_bang3D(x)*atom_getfloatarg(4, argc, argv); // random
						x->forceZ += random_bang3D(x)*atom_getfloatarg(5, argc, argv); // random
	
						tmp = atom_getfloatarg(6, argc, argv);
						if (tmp != 0)
						{
							x->forceX += tmp * ((x->posX_old_2)-(x->posX_old_1));
							x->forceY += tmp * ((x->posY_old_2)-(x->posY_old_1)); // damping
							x->forceZ += tmp * ((x->posZ_old_2)-(x->posZ_old_1)); // damping
						}

						x->dX += atom_getfloatarg(14, argc, argv);
						x->dY += atom_getfloatarg(15, argc, argv); // constant
						x->dZ += atom_getfloatarg(16, argc, argv); // constant
					}
					}
					}
				}
			}
		}
	}
	else
	{
		logpost(x,1,"bad ambient interraction message");
	}
}

void mass3D_inter_plane(t_mass3D *x, t_symbol *s, int argc, t_atom *argv)
{
	t_float a, b, c, d, profondeur, tmp, profondeur_old;

	if (argc == 12) 
		// 0 : Xvector
		// 1 : Yvector
		// 2 : Zvector
		// 3 : Xcenter
		// 4 : Ycenter
		// 5 : Zcenter
		// 6 : FNCt
		// 7 : KN
		// 8 : damping de liaison (profondeur)
		// 9 : Profondeur maximum
		// 10 : deplacement normal X
		// 11 : deplacement proportionel a P

	{

// ax+by+cz-d=0
// a = Xvector / |V|
// b = Yvector ...
// d est tel que aXcenter +bYcenter + cYcenter = d

		a = atom_getfloatarg(0, argc, argv);
		b = atom_getfloatarg(1, argc, argv);
		c = atom_getfloatarg(2, argc, argv);

		tmp = sqrt (a*a + b*b + c*c);
	if (tmp != 0)
	{
		a /= tmp;
		b /= tmp;
		c /= tmp;

	}
	else
	{
		a=1;
		b=0;
		c=0;
	}

		d = a * atom_getfloatarg(3, argc, argv) + b * atom_getfloatarg(4, argc, argv) + c * atom_getfloatarg(5, argc, argv);
//C a optimiser : envoyer  directement les coef directeur et l'offset
//C a faire pour les autres obj aussi
		
		profondeur = a * x->posX_old_1 + b * x->posY_old_1 + c * x->posZ_old_1 - d;

		if ( (profondeur < 0) & (profondeur > -atom_getfloatarg(9, argc, argv)) )
		{

			tmp = atom_getfloatarg(6, argc, argv); // force normal constante

			x->forceX += tmp * a;
			x->forceY += tmp * b;
			x->forceZ += tmp * c;
	
			tmp = atom_getfloatarg(7, argc, argv); // force normal proportionelle a la profondeur
			tmp *= profondeur;
			x->forceX -= tmp * a;
			x->forceY -= tmp * b;
			x->forceZ -= tmp * c;

			tmp = atom_getfloatarg(8, argc, argv); // force normal proportionelle a la profondeur

			profondeur_old = a * x->posX_old_2 + b * x->posY_old_2 + c * x->posZ_old_2 - d;		
			tmp *= (profondeur - profondeur_old);
			x->forceX -= tmp * a;
			x->forceY -= tmp * b;
			x->forceZ -= tmp * c;

			
			tmp = atom_getfloatarg(10, argc, argv); // deplacement normal constant

			x->dX += tmp * a;
			x->dY += tmp * b;
			x->dZ += tmp * c;

			tmp = atom_getfloatarg(11, argc, argv); // deplacement normal proportionel
			tmp *= profondeur;

			x->dX -= tmp * a;
			x->dY -= tmp * b;
			x->dZ -= tmp * c;
		}

	}	
	else
	{
		logpost(x,1,"bad plane interraction message");
	}
}


void mass3D_inter_sphere(t_mass3D *x, t_symbol *s, int argc, t_atom *argv)
{
t_float posx1, posy1, posz1, Nx, Ny, Nz, distance, Dmax, tmp;

	if (argc == 17) 
		// 0 : Xcentre
		// 1 : Ycendre
		// 2 : Zcentre
		// 3 : Rmin
		// 4 : Rmax
		// 5 : F normal
		// 6 : K normal
		// 7 : F normal proportionel a 1/R
		// 8 : Damp de liaison normal 
		// 9 : deplacement N Ct
		// 10 : position ancienne de l'interacteur en X
		// 11 : position abcienne de l'interacteur en Y
		// 12 : position abcienne de l'interacteur en Z
		// 13 : d dormal proportionel a R
		// 14 : force normal proportionel a 1/R2
		// 15 : d dormal proportionel a 1/R
		// 16 : d dormal proportionel a 1/R*R

	{
		posx1 = atom_getfloatarg(0, argc, argv);
		posy1 = atom_getfloatarg(1, argc, argv);
		posz1 = atom_getfloatarg(2, argc, argv);
		Nx = (x->posX_old_1)-posx1;					// vecteur deplacement X
		Ny = (x->posY_old_1)-posy1;					// vecteur deplacement Y
		Nz = (x->posZ_old_1)-posz1;					// vecteur deplacement Y

		distance = sqrt((Nx * Nx)+(Ny * Ny)+(Nz * Nz));		// distance entre le centre de l'interaction, et le pts

		Nx = Nx/distance;							// composante X de la normal (normalisé)
		Ny = Ny/distance;							// composante Y de la normal.
		Nz = Nz/distance;							// composante Y de la normal.

		Dmax= atom_getfloatarg(4, argc, argv);		// distance max de l'interaction
		if ( (distance > atom_getfloatarg(3, argc, argv)) && (distance < Dmax) )
		{
			tmp = atom_getfloatarg(5, argc, argv); // force constante normal
			x->forceX += tmp * Nx;
			x->forceY += tmp * Ny;
			x->forceZ += tmp * Nz;

			tmp = atom_getfloatarg(6, argc, argv); // force variable (K) normal
			tmp *= ( Dmax-distance );
			x->forceX += tmp * Nx ;
			x->forceY += tmp * Ny ;
			x->forceZ += tmp * Nz ;

		    tmp = atom_getfloatarg(7, argc, argv); // force normal proportionel a 1/r
			if ( (distance != 0) & (tmp != 0) )
			{
				tmp /= distance;
				x->forceX += tmp * Nx;
				x->forceY += tmp * Ny;
				x->forceZ += tmp * Nz ;
			}

			tmp = atom_getfloatarg(8, argc, argv); // damping2 normal
			tmp *= ( x->VX * Nx + x->VY * Ny + x->VZ * Nz );
			x->forceX -= tmp * Nx ;
			x->forceY -= tmp * Ny ;
			x->forceZ -= tmp * Nz ;

			tmp = atom_getfloatarg(9, argc, argv); // d normal
			x->dX += tmp * Nx ;
			x->dY += tmp * Ny ;
			x->dZ += tmp * Nz ;

			tmp = atom_getfloatarg(13, argc, argv); // force normal proportionel a 1/r2
			if ( (distance != 0) & (tmp != 0) )
			{
				tmp /= (distance * distance);
				x->forceX += tmp * Nx ;
				x->forceY += tmp * Ny ;
				x->forceZ += tmp * Nz ;
			}

			tmp = atom_getfloatarg(14, argc, argv); // deplacement variable (K) normal
			tmp *= ( Dmax-distance );
			x->dX += tmp * Nx ;
			x->dY += tmp * Ny ;
			x->dZ += tmp * Nz ;

			tmp = atom_getfloatarg(15, argc, argv); // deplacement normal proportionel a 1/r
			if ( (distance != 0) & (tmp != 0) )
			{
				tmp /= distance;
			    x->dX += tmp * Nx ;
			    x->dY += tmp * Ny ;
			    x->dZ += tmp * Nz ;
			}

			tmp = atom_getfloatarg(16, argc, argv); // deplacement normal proportionel a 1/r2
			if ( (distance != 0) & (tmp != 0) )
			{
				tmp /= (distance * distance);
			    x->dX += tmp * Nx;
			    x->dY += tmp * Ny;
			    x->dZ += tmp * Nz;
			}

		}
	}
	else
	{
		logpost(x,1,"bad interact_3D_sphere message");
	}
}


void mass3D_inter_circle(t_mass3D *x, t_symbol *s, int argc, t_atom *argv)
{
	t_float a, b, c, d, profondeur, tmp, profondeur_old, rayon;

	if (argc == 14) 
		// 0 : Xvector
		// 1 : Yvector
		// 2 : Zvector
		// 3 : Xcenter
		// 4 : Ycenter
		// 5 : Zcenter
		// 6 : Rmin
		// 7 : RMax
		// 8 : FNCt
		// 9 : KN
		// 10 : damping de liaison (profondeur)
		// 11 : Profondeur maximum
		// 12 : dN
		// 13 : dKN

	{
// ax+by+cz-d=0
// a = Xvector / |V|
// b = Yvector ...
// d est tel que aXcenter +bYcenter + cYcenter = d

		a = atom_getfloatarg(0, argc, argv);
		b = atom_getfloatarg(1, argc, argv);
		c = atom_getfloatarg(2, argc, argv);

		tmp = sqrt (a*a + b*b + c*c);
	if (tmp != 0)
	{
		a /= tmp;
		b /= tmp;
		c /= tmp;
	}
	else
	{
		a=1;
		b=0;
		c=0;
	}

		d = a * atom_getfloatarg(3, argc, argv) + b * atom_getfloatarg(4, argc, argv) + c * atom_getfloatarg(5, argc, argv);

		profondeur = a * x->posX_old_1 + b * x->posY_old_1 + c * x->posZ_old_1 - d;

		rayon = sqrt ( pow(x->posX_old_1-atom_getfloatarg(3, argc, argv), 2) + pow(x->posY_old_1-atom_getfloatarg(4, argc, argv) , 2) + pow(x->posZ_old_1 - atom_getfloatarg(5, argc, argv) , 2) - profondeur*profondeur );

		if ( (profondeur < 0) && (profondeur > - atom_getfloatarg(11, argc, argv)) && (rayon > atom_getfloatarg(6, argc, argv)) && (rayon < atom_getfloatarg(7, argc, argv)))
		{

			tmp = atom_getfloatarg(8, argc, argv); // force normal constante

			x->forceX += tmp * a;
			x->forceY += tmp * b;
			x->forceZ += tmp * c;
	
			tmp = atom_getfloatarg(9, argc, argv); // force normal proportionelle a la profondeur
			tmp *= profondeur;
			x->forceX -= tmp * a;
			x->forceY -= tmp * b;
			x->forceZ -= tmp * c;

			tmp = atom_getfloatarg(10, argc, argv); // force normal proportionelle a la profondeur

			profondeur_old = a * x->posX_old_2 + b * x->posY_old_2 + c * x->posZ_old_2 - d;		
			tmp *= (profondeur - profondeur_old);

			x->forceX -= tmp * a;
			x->forceY -= tmp * b;
			x->forceZ -= tmp * c;

			tmp = atom_getfloatarg(12, argc, argv); // deplacement normal constante
			x->dX += tmp * a;
			x->dY += tmp * b;
			x->dZ += tmp * c;
	
			tmp = atom_getfloatarg(13, argc, argv); // deplacement normal proportionelle a la profondeur
			tmp *= profondeur;
			x->dX -= tmp * a;
			x->dY -= tmp * b;
			x->dZ -= tmp * c;
		}
	}	
	else
	{
		logpost(x,1,"bad circle interraction message");
	}
}


void mass3D_inter_cylinder(t_mass3D *x, t_symbol *s, int argc, t_atom *argv)
{
	t_float a, b, c, d, profondeur, profondeur_old, tmp, rayon_old, rayon;
	t_float Xb, Yb, Zb, Ta, Tb, Tc, Xb_old, Yb_old, Zb_old;

	if (argc == 21) 
		// 0 : Xvector
		// 1 : Yvector
		// 2 : Zvector
		// 3 : Xcenter
		// 4 : Ycenter
		// 5 : Zcenter
		// 6 : Rmin
		// 7 : Rmax
		// 8 : FNCt
		// 9 : KN
		// 10 : damping de liaison (rayon)
		// 11 : FN 1/R
		// 12 : FN 1/R2
		// 13 : Pmin
		// 14 : Pmax
		// 15 : FTct
		// 16 : KT
		// 17 : dNct
		// 18 : dTct
		// 19 : dKN
		// 20 : dKT

	{

// ax+by+cz-d=0
// a = Xvector / |V|
// b = Yvector ...
// d est tel que aXcenter +bYcenter + cYcenter = d

		a = atom_getfloatarg(0, argc, argv);
		b = atom_getfloatarg(1, argc, argv);
		c = atom_getfloatarg(2, argc, argv);

		tmp = sqrt (a*a + b*b + c*c);
	if (tmp != 0)
	{
		a /= tmp;
		b /= tmp;
		c /= tmp;
	}
	else
	{
		a=1;
		b=0;
		c=0;
	}

		d = a * atom_getfloatarg(3, argc, argv) + b * atom_getfloatarg(4, argc, argv) + c * atom_getfloatarg(5, argc, argv);

		profondeur = a * x->posX_old_1 + b * x->posY_old_1 + c * x->posZ_old_1 - d;

		Xb = x->posX_old_1 - atom_getfloatarg(3, argc, argv) - profondeur * a;
		Yb = x->posY_old_1 - atom_getfloatarg(4, argc, argv) - profondeur * b;
		Zb = x->posZ_old_1 - atom_getfloatarg(5, argc, argv) - profondeur * c;

		rayon = sqrt ( pow(Xb, 2) + pow(Yb, 2)  + pow(Zb, 2) );

		if (rayon != 0)
		{
			Xb /= rayon;  // normalisation
			Yb /= rayon;
			Zb /= rayon;
		}
		else
		{
			Xb = 0;  // normalisation
			Yb = 0;
			Zb = 0;
		}


		Ta = b*Zb - c*Yb; // vecteur tengentiel = vecteur vectoriel rayon
		Tb = c*Xb - a*Zb;
		Tc = a*Yb - b*Xb;
		
		if ( (profondeur < atom_getfloatarg(14, argc, argv)) && (profondeur > atom_getfloatarg(13, argc, argv)) && (rayon < atom_getfloatarg(7, argc, argv)) && (rayon > atom_getfloatarg(6, argc, argv)) )
		{

			tmp = atom_getfloatarg(8, argc, argv); // force normal constante

			x->forceX += tmp * Xb;
			x->forceY += tmp * Yb;
			x->forceZ += tmp * Zb;
	
			tmp = atom_getfloatarg(9, argc, argv); // rigidité normal proportionelle
			tmp *= ( atom_getfloatarg(7, argc, argv) - rayon ) ;
			x->forceX += tmp * Xb;
			x->forceY += tmp * Yb;
			x->forceZ += tmp * Zb;
						
			tmp = atom_getfloatarg(10, argc, argv); // damping normal proportionelle a la profondeur

			profondeur_old = a * x->posX_old_2 + b * x->posY_old_2 + c * x->posZ_old_2 - d;

			Xb_old = x->posX_old_2 - atom_getfloatarg(3, argc, argv) - profondeur_old * a;
			Yb_old = x->posY_old_2 - atom_getfloatarg(4, argc, argv) - profondeur_old * b;
			Zb_old = x->posZ_old_2 - atom_getfloatarg(5, argc, argv) - profondeur_old * c;

			rayon_old = sqrt ( pow(Xb_old, 2) + pow(Yb_old, 2)  + pow(Zb_old, 2) );		

			tmp *= (rayon - rayon_old);

			x->forceX -= tmp * Xb;
			x->forceY -= tmp * Yb;
			x->forceZ -= tmp * Zb;

			tmp = atom_getfloatarg(11, argc, argv); // force normal proportionne a 1/R
			if (rayon != 0)
			{	
				tmp /= rayon;
			    x->forceX += tmp * Xb;
				x->forceY += tmp * Yb;
				x->forceZ += tmp * Zb;
			}

			tmp = atom_getfloatarg(12, argc, argv); // force normal proportionne a 1/R*R
			if (rayon != 0)
			{	
				tmp /= (rayon*rayon);
			    x->forceX += tmp * Xb;
				x->forceY += tmp * Yb;
				x->forceZ += tmp * Zb;
			}

			tmp = atom_getfloatarg(15, argc, argv); // force tengente constante
			x->forceX -= tmp * Ta;
			x->forceY -= tmp * Tb;
			x->forceZ -= tmp * Tc;

			tmp = atom_getfloatarg(16, argc, argv); // rigidité tengentiel proportionelle
			tmp *= ( atom_getfloatarg(7, argc, argv) - rayon ) ;
			x->forceX += tmp * Ta;
			x->forceY += tmp * Tb;
			x->forceZ += tmp * Tc;

			tmp = atom_getfloatarg(17, argc, argv); // deplacement normal constante

			x->dX += tmp * Xb;
			x->dY += tmp * Yb;
			x->dZ += tmp * Zb;
	
			tmp = atom_getfloatarg(19, argc, argv); // deplacement normal proportionelle
			tmp *= ( atom_getfloatarg(7, argc, argv) - rayon ) ;
			x->dX += tmp * Xb;
			x->dY += tmp * Yb;
			x->dZ += tmp * Zb;

			tmp = atom_getfloatarg(18, argc, argv); // deplacement tengente constante
			x->dX += tmp * Ta;
			x->dY += tmp * Tb;
			x->dZ += tmp * Tc;

			tmp = atom_getfloatarg(20, argc, argv); // deplacement tengentiel proportionelle
			tmp *= ( atom_getfloatarg(7, argc, argv) - rayon ) ;
			x->dX += tmp * Ta;
			x->dY += tmp * Tb;
			x->dZ += tmp * Tc;
		}
	}	
	else
	{
		logpost(x,1,"bad cylinder interraction message");
	}
}

void *mass3D_new(t_symbol *s, int argc, t_atom *argv)
{
  t_mass3D *x = (t_mass3D *)pd_new(mass3D_class);

  x->x_sym = atom_getsymbolarg(0, argc, argv);
  x->x_state = makeseed3D();

  pd_bind(&x->x_obj.ob_pd, atom_getsymbolarg(0, argc, argv));

  x->position3D_new=outlet_new(&x->x_obj, 0);
  x->force_out=outlet_new(&x->x_obj, 0);
  x->vitesse_out=outlet_new(&x->x_obj, 0);

  x->forceX=0;
  x->forceY=0;
  x->forceZ=0;

  if (argc >= 2)
    x->mass3D = atom_getfloatarg(1, argc, argv) ;
  else
    x->mass3D = 1;

  x->onoff = 1;

  x->VX = 0;
  x->VY = 0;
  x->VZ = 0;

  x->dX=0;
  x->dY=0;
  x->dZ=0;

   if (argc >= 3)
		x->Xinit = atom_getfloatarg(2, argc, argv);
	else
		x->Xinit = 0 ;

	x->posX_old_1 = x->Xinit ;
	x->posX_old_2 = x->Xinit;
	SETFLOAT(&(x->pos_new[0]),  x->Xinit);

	if (argc >= 4)
		x->Yinit = atom_getfloatarg(3, argc, argv);
	else
		x->Yinit = 0 ;

	x->posY_old_1 = x->Yinit ;
	x->posY_old_2 = x->Yinit;
	SETFLOAT(&(x->pos_new[1]),  x->Yinit);

	if (argc >= 5)
		x->Zinit = atom_getfloatarg(4, argc, argv);
	else
		x->Zinit = 0 ;

	x->posZ_old_1 = x->Zinit ;
	x->posZ_old_2 = x->Zinit;
	SETFLOAT(&(x->pos_new[2]),  x->Zinit);


	  if (argc >= 6)
		x->minX = atom_getfloatarg(5, argc, argv) ;
	  else 
		x->minX = -100000;

	  if (argc >= 7)
		x->maxX = atom_getfloatarg(6, argc, argv) ;
	  else 
		x->maxX = 100000;
	
	  if (argc >= 8)
		x->minY = atom_getfloatarg(7, argc, argv) ;
	  else 
		x->minY = -100000;

	  if (argc >= 9)
		x->maxY = atom_getfloatarg(8, argc, argv) ;
	  else 
		x->maxY = 100000;

	  if (argc >= 10)
		x->minZ = atom_getfloatarg(9, argc, argv) ;
	  else 
		x->minZ = -100000;

	  if (argc >= 11)
		x->maxZ = atom_getfloatarg(10, argc, argv) ;
	  else 
		x->maxZ = 100000;

	  if (argc >= 12)
		x->seuil = atom_getfloatarg(11, argc, argv) ;
	  else 
		x->seuil = 0;

	  if (argc >= 13)
		x->damp = atom_getfloatarg(12, argc, argv) ;
	  else 
		x->damp = 0;

  return (void *)x;
}

static void mass3D_free(t_mass3D *x)
{
    pd_unbind(&x->x_obj.ob_pd, x->x_sym);
}


PMPD_EXPORT void mass3D_setup(void)
{

  mass3D_class = class_new(gensym("mass3D"),
        (t_newmethod)mass3D_new,
        (t_method)mass3D_free,
		sizeof(t_mass3D),
        CLASS_DEFAULT, A_GIMME, 0);

  if(!mass3D_class)
      return;

  //verbose(4, "mass3D version %s (%s)", pmpd_tag(), pmpd_sha());

  class_addcreator((t_newmethod)mass3D_new, gensym("masse3D"), A_GIMME, 0);

  class_addmethod(mass3D_class, (t_method)mass3D_force, gensym("force3D"),A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0);
  class_addbang(mass3D_class, mass3D_bang);

  class_addmethod(mass3D_class, (t_method)mass3D_dX, gensym("dX"), A_DEFFLOAT, 0);
  class_addmethod(mass3D_class, (t_method)mass3D_dY, gensym("dY"), A_DEFFLOAT, 0);
  class_addmethod(mass3D_class, (t_method)mass3D_dZ, gensym("dZ"), A_DEFFLOAT, 0);
  class_addmethod(mass3D_class, (t_method)mass3D_dXYZ, gensym("dXYZ"), A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0);
  class_addmethod(mass3D_class, (t_method)mass3D_setX, gensym("setX"), A_DEFFLOAT, 0);
  class_addmethod(mass3D_class, (t_method)mass3D_setY, gensym("setY"), A_DEFFLOAT, 0);
  class_addmethod(mass3D_class, (t_method)mass3D_setZ, gensym("setZ"), A_DEFFLOAT, 0);
  class_addmethod(mass3D_class, (t_method)mass3D_setXYZ, gensym("setXYZ"), A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0);
  class_addmethod(mass3D_class, (t_method)mass3D_minX, gensym("setXmin"), A_DEFFLOAT, 0);
  class_addmethod(mass3D_class, (t_method)mass3D_minY, gensym("setYmin"), A_DEFFLOAT, 0);
  class_addmethod(mass3D_class, (t_method)mass3D_maxX, gensym("setXmax"), A_DEFFLOAT, 0);
  class_addmethod(mass3D_class, (t_method)mass3D_maxY, gensym("setYmax"), A_DEFFLOAT, 0);
  class_addmethod(mass3D_class, (t_method)mass3D_minZ, gensym("setZmin"), A_DEFFLOAT, 0);
  class_addmethod(mass3D_class, (t_method)mass3D_maxZ, gensym("setZmax"), A_DEFFLOAT, 0);
  class_addmethod(mass3D_class, (t_method)mass3D_set_mass3D, gensym("setM"), A_DEFFLOAT, 0);
  class_addmethod(mass3D_class, (t_method)mass3D_reset, gensym("reset"), 0);
  class_addmethod(mass3D_class, (t_method)mass3D_resetf, gensym("resetF"), 0);
  class_addmethod(mass3D_class, (t_method)mass3D_reset, gensym("loadbang"), 0);
  class_addmethod(mass3D_class, (t_method)mass3D_on, gensym("on"), 0);
  class_addmethod(mass3D_class, (t_method)mass3D_off, gensym("off"), 0);
  class_addmethod(mass3D_class, (t_method)mass3D_seuil, gensym("setT"), A_DEFFLOAT, 0);
  class_addmethod(mass3D_class, (t_method)mass3D_damp, gensym("setD"), A_DEFFLOAT, 0);

  class_addmethod(mass3D_class, (t_method)mass3D_inter_ambient, gensym("interactor_ambient_3D"), A_GIMME, 0);
  class_addmethod(mass3D_class, (t_method)mass3D_inter_sphere, gensym("interactor_sphere_3D"), A_GIMME, 0);
  class_addmethod(mass3D_class, (t_method)mass3D_inter_plane, gensym("interactor_plane_3D"), A_GIMME, 0);
  class_addmethod(mass3D_class, (t_method)mass3D_inter_circle, gensym("interactor_circle_3D"), A_GIMME, 0);
  class_addmethod(mass3D_class, (t_method)mass3D_inter_cylinder, gensym("interactor_cylinder_3D"), A_GIMME, 0);

}
