view trunk/tests/ChipmunkDemos/samples/MagnetsElectric.d @ 25:5497d518b428

fixed memory corruption bug
author Extrawurst
date Mon, 13 Dec 2010 02:31:53 +0100
parents af2f61a96318
children
line wrap: on
line source


// written in the D programming language

module samples.MagnetsElectric;

import chipmunkd.chipmunk;

import samples.ChipmunkDemo;

import std.math;
import core.stdc.string:strcmp;
import core.stdc.stdio:sprintf;

enum WIDTH = 600;
enum HEIGHT = 400;

enum SINGMAX = 10; // Maximum number of singularities per body
enum NMAG = 10; // Number of magnets
enum NCHG = 10; // Number of charged bodies
enum NMIX = 10; // Number of charged magnets

enum COU_MKS = 8.987551787e9; // Some physical constants
enum MAG_MKS = 1e-7;

// Prototypes
alias void function(DataforForce* data)SingForceFunc;

// Structures
// Singularities
struct ActorSingularity{
	// Number of singularities 
	int Nsing; 
	// Value of the singularities 
	cpFloat value[SINGMAX];
	// Type of the singularities 
	char type[SINGMAX][100]; 
	// Global position of the singularities
	cpVect Gpos[SINGMAX];
	// Local position of the singularities
	cpVect position[SINGMAX];
	// Angle of the singularities measured in the body axes
	cpFloat angle[SINGMAX];
	// Angle of the singularities measured from x
	cpFloat Gangle[SINGMAX];
	// Force function
	SingForceFunc force_func[SINGMAX];
	// Force function
	SingForceFunc torque_func[SINGMAX];
}
alias ActorSingularity Sing;

// Data for the force functions
struct DataforForce{
	//Everything in global coordinates
	// Position of the source
	cpVect p0;
	// Observed position
	cpVect p;
	// Relative position source-observed
	cpVect relp;
	// distance, disntace^2, ditance ^3
	cpFloat r[3];
	// angle of the source
	cpFloat ang0;
	// angle of the observed singularity
	cpFloat ang;
	// Foce value
	cpVect F;
	// Torque value
	cpFloat T;
}
alias DataforForce ForceData;

// Global Varibales
static cpSpace *space;


// **** Forces ****** // 
// Calculate the forces between two bodies. all this functions requieres 
// a pointer to an structure with the necessary fields.

// forces between charges
static void
CoulombForce(ForceData* data){
	data.F=cpvmult(cpvnormalize(data.relp),COU_MKS/data.r[1]);
}

// forces between magnets
static void
MagDipoleForce(ForceData* data){
	static cpFloat phi,alpha,beta,Fr,Fphi;
	
	// Angle of the relative position vector
	phi=cpvtoangle(data.relp);
	alpha=data.ang0;
	beta=data.ang;		

	alpha =phi - alpha;
	beta = phi - beta;
	
	
	// Components in polar coordinates
	Fr=(2.0e0*cos(alpha)*cos(beta) - sin(alpha)*sin(beta));
	Fphi=sin(alpha+beta);
//	printf("%g %g %g %g %g\n",phi,alpha,beta,Fphi);
	
	// Cartesian coordinates
	data.F=cpv(Fr*cos(phi)-Fphi*sin(phi),Fr*sin(phi)+Fphi*cos(phi));
	data.F=cpvmult(data.F,-3.e0*MAG_MKS/(data.r[1]*data.r[1]));
}

static void
MagDipoleTorque(ForceData* data){
	static cpFloat phi,alpha,beta;
	
	phi=cpvtoangle(data.relp);
	alpha=data.ang0;
	beta=data.ang;		
	alpha =phi - alpha;
	beta = phi - beta;

	// Torque. Though we could use a component of F to save some space, 
	// we use another variables for the sake of clarity.
	
	data.T=(MAG_MKS/data.r[2])*(3.0e0*cos(alpha)*sin(beta) + sin(alpha-beta));
}
// ******* // 

// This function fills the data structure for the force functions
// The structure Sing has the information about the singularity (charge or magnet)
static void
FillForceData(Sing* source,int inds, Sing* obs,int indo, ForceData* data)
{
	// Global Position and orientation of the source singularity
	 data.p0=source.Gpos[inds];
	 data.ang0=source.Gangle[inds]; 
	 
	// Global Position and orientation of the observed singularity
	 data.p=obs.Gpos[indo];
	 data.ang=obs.Gangle[indo];
	
	// Derived magnitudes
	 data.relp=cpvsub(data.p,data.p0); //Relative position
	 data.r[0]=cpvlength(data.relp); // Distance
	 data.r[1]=cpvlengthsq(data.relp); // Square Distance
	 data.r[2]=data.r[0]*data.r[1]; // Cubic distance
	 
     source.force_func[inds](data); // The value of the force
	 data.F= cpvmult(data.F,source.value[inds]*obs.value[indo]);
}

// Calculation of the interaction
static void
LRangeForceApply(cpBody *a, cpBody *b){
	
	Sing* aux = cast(Sing*)a.data;
	Sing* aux2 = cast(Sing*)b.data;
	cpVect delta;
	// General data needed to calculate interaction
	static ForceData fdata;
	fdata.F=cpvzero;
	
	// Calculate the forces between the charges of different bodies
	for (int i=0; i<aux.Nsing; i++)
	{
		for (int j=0; j<aux2.Nsing; j++)
		{
			if(!strcmp(aux.type[i].ptr,aux2.type[j].ptr))
			{
				//printf("%s %s\n",aux.type[i],aux2.type[j]);
				FillForceData (aux2,j,aux,i,&fdata);
				
				//Force applied to body A
				delta=cpvsub(aux.Gpos[i], a.p);
				cpBodyApplyForce(a,fdata.F, delta);
				
	 			if(aux.torque_func[i] != null)
				{
					//Torque on A
					aux.torque_func[i](&fdata);
					a.t += aux.value[i]*aux2.value[j]*fdata.T;
					
				}
			}
		}
	}
}

// function for the integration of the positions
// The following functions are variations to the starndrd integration in Chipmunk
// you can go ack to the standard ones by doing the appropiate changes.
static void
ChargedBodyUpdatePositionVerlet(cpBody *_body, cpFloat dt)
{
    // Long range interaction
    cpArray *bodies = space.bodies;
	static cpBody* B;
	Sing* aux=cast(Sing*)_body.data;
	Sing* aux2;

	// General data needed to calculate interaction
	static ForceData fdata;
	fdata.F=cpvzero;
	
	for(int i=0; i< bodies.num; i++)
	{
	  B=cast(cpBody*)bodies.arr[i];
	  aux2=cast(Sing*)B.data;
	  
	  if(B != _body)
	  {
        // Calculate the forces between the singularities of different bodies
        LRangeForceApply(_body, B);
	  }
	}
	
	cpVect dp = cpvmult(cpvadd(_body.v, _body.v_bias), dt);
	dp = cpvadd(dp,cpvmult(cpvmult(_body.f, _body.m_inv), 0.5e0*dt*dt));
	_body.p = cpvadd(_body.p, dp);

	cpBodySetAngle(_body, cast(float)(_body.a + (_body.w + _body.w_bias)*dt 
				   + 0.5*_body.t*_body.i_inv*dt*dt));

	// Update position of the singularities
	aux = cast(Sing*)_body.data;
	for (int i=0; i<aux.Nsing; i++)
	{
        aux.Gpos[i]=cpvadd(_body.p,cpvrotate(cpv(aux.position[i].x,
										  aux.position[i].y), _body.rot));
		aux.Gangle[i]= aux.angle[i] + _body.a;
	}
	
            
 	_body.v_bias = cpvzero;
	_body.w_bias = 0.0f;
}

// function for the integration of the velocities
static void
ChargedBodyUpdateVelocityVerlet(cpBody *_body, cpVect gravity, cpFloat damping, cpFloat dt)
{
	_body.v = cpvadd(_body.v, cpvmult(cpvadd(gravity, cpvmult(_body.f, _body.m_inv)), 0.5e0*dt));
	_body.w = _body.w + _body.t*_body.i_inv*0.5e0*dt;
	
	_body.f = cpvzero;
	_body.t = 0.0e0;
	
	// Long range interaction
    cpArray *bodies = space.bodies;
	static cpBody* B;

	// General data needed to calculate interaction
	static ForceData fdata;
	fdata.F=cpvzero;
	
	for(int i=0; i< bodies.num; i++)
	{
	  B=cast(cpBody*)bodies.arr[i];
	  
	  if(B != _body)
	  {
        // Calculate the forces between the singularities of different bodies
        LRangeForceApply(_body, B);
	  }
	}
	_body.v = cpvadd(cpvmult(_body.v,damping), cpvmult(cpvadd(gravity, cpvmult(_body.f, _body.m_inv)), 0.5e0*dt));
	_body.w = _body.w*damping + _body.t*_body.i_inv*0.5e0*dt;
}

static void 
update(int ticks)
{
	enum int steps = 10;
	enum cpFloat dt = 1.0/60.0/cast(cpFloat)steps;
	
	cpArray *bodies = space.bodies;

	for(int i=0; i< bodies.num; i++)
		cpBodyResetForces(cast(cpBody*)bodies.arr[i]);
	
	for(int i=0; i<steps; i++){
		cpSpaceStep(space, dt);
	}
	
}

static void
make_mag(cpVect p, cpFloat ang, cpFloat mag)
{
	int nverts=6;
	cpVect verts[] = [
		cpv(-10,-10),
		cpv(-10, 10),
		cpv( 10, 10),
		cpv( 15, 5),
		cpv( 15, -5),
		cpv( 10,-10)
	];

	cpBody *_body = cpBodyNew(1.0, cpMomentForPoly(1.0f, nverts, verts.ptr, cpvzero));
	_body.p = p;
	_body.v = cpvzero;
	cpBodySetAngle(_body, ang);
	_body.w = 0.0e0;
	
    // Load the singularities
    Sing *magnet=cast(Sing*)cpmalloc(Sing.sizeof);
	magnet.Nsing=1; 
	magnet.value[0]=mag;
	sprintf(magnet.type[0].ptr,"magdipole"); 

	// The position and angle could be different form the one of the body
	magnet.position[0]=cpvzero;
	magnet.Gpos[0]=cpvadd(p,magnet.position[0]);
	magnet.angle[0]=0.0f;
	magnet.Gangle[0]=ang;
	
	magnet.force_func[0]=&MagDipoleForce;
	magnet.torque_func[0]=&MagDipoleTorque;

	_body.data=magnet;
	
    _body.position_func=&ChargedBodyUpdatePositionVerlet;
    _body.velocity_func=&ChargedBodyUpdateVelocityVerlet;
	cpSpaceAddBody(space, _body);
	
	cpShape *shape = cpPolyShapeNew(_body, nverts, verts.ptr, cpvzero);
	shape.e = 0.0; shape.u = 0.7;
	cpSpaceAddShape(space, shape);
}

static void
make_charged(cpVect p, cpFloat chg)
{
	int nverts=4;
	cpVect verts[] = [
		cpv(-10,-10),
		cpv(-10, 10),
		cpv( 10, 10),
		cpv( 10,-10)
	];

	cpBody *_body = cpBodyNew(1.0, cpMomentForPoly(1.0, nverts, verts.ptr, cpvzero));
	_body.p = p;
	_body.v = cpvzero;
	cpBodySetAngle(_body, 0.0f);
	_body.w = 0.0e0;
	
    // Load the singularities
    Sing *charge=cast(Sing*)cpmalloc(Sing.sizeof);;
	charge.Nsing=1; 
	charge.value[0]=chg;
	sprintf(charge.type[0].ptr,"electrical\0"); 

	// The position and angle could be different form the one of the body
	charge.position[0]=cpvzero;
	charge.Gpos[0]=cpvadd(p,charge.position[0]);
	charge.Gangle[0]=0;
	charge.angle[0]=0.0f;
	
	charge.force_func[0]=&CoulombForce;
	charge.torque_func[0]=null;
	
	_body.data=charge;
	
    _body.position_func=&ChargedBodyUpdatePositionVerlet;
    _body.velocity_func=&ChargedBodyUpdateVelocityVerlet;
	cpSpaceAddBody(space, _body);
	
	cpShape *shape = cpPolyShapeNew(_body, nverts, verts.ptr, cpvzero);
	shape.e = 0.0; shape.u = 0.7;
	cpSpaceAddShape(space, shape);
}
void 
make_mix(cpVect p, cpFloat ang, cpFloat mag,cpFloat chg)
{
	int nverts=5;
	cpVect verts[] = [
		cpv(-10,-10),
		cpv(-10, 10),
		cpv( 10, 10),
		cpv( 20, 0),		
		cpv( 10,-10)
	];

	cpBody *_body = cpBodyNew(1.0, cpMomentForPoly(1.0, nverts, verts.ptr, cpvzero));
	_body.p = p;
	_body.v = cpvzero;
	cpBodySetAngle(_body, ang);
	_body.w = 0.0e0;
	
    // Load the singularities
    Sing *mix=cast(Sing*)cpmalloc(Sing.sizeof);;
	mix.Nsing=2; 
	mix.value[0]=mag;
	mix.value[1]=chg;
	sprintf(mix.type[0].ptr,"magdipole\0");
	sprintf(mix.type[1].ptr,"electrical\0");  

	// The position and angle could be different form the one of the body
	mix.position[0]=cpvzero;
	mix.Gpos[0]=cpvadd(p,mix.position[0]);
	mix.position[1]=cpvzero;
	mix.Gpos[1]=cpvadd(p,mix.position[1]);
	mix.Gangle[0]=ang;
	mix.Gangle[1]=ang;	
	mix.angle[0]=0.0f;
	mix.angle[1]=0.0f;
	
	mix.force_func[0]=&MagDipoleForce;
	mix.force_func[1]=&CoulombForce;
	mix.torque_func[0]=&MagDipoleTorque;
	mix.torque_func[1]=null;	
	
	_body.data=mix;
	
    _body.position_func=&ChargedBodyUpdatePositionVerlet;
    _body.velocity_func=&ChargedBodyUpdateVelocityVerlet;
	cpSpaceAddBody(space, _body);
	
	cpShape *shape = cpPolyShapeNew(_body, nverts, verts.ptr, cpvzero);
	shape.e = 0.0; shape.u = 0.7;
	cpSpaceAddShape(space, shape);
}


static cpSpace* 
init()
{
	cpResetShapeIdCounter();
	space = cpSpaceNew();
	space.iterations = 5;
	space.gravity = cpvzero; //cpv(0,-100);
	
	cpSpaceResizeActiveHash(space, 30.0, 2999);

	// Screen border
/*	shape = cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(-320,240), 0.0f);
	shape.e = 1.0; shape.u = 1.0;
	cpSpaceAddShape(space, shape);

	shape = cpSegmentShapeNew(staticBody, cpv(320,-240), cpv(320,240), 0.0f);
	shape.e = 1.0; shape.u = 1.0;
	cpSpaceAddShape(space, shape);

	shape = cpSegmentShapeNew(staticBody, cpv(-320,-240), cpv(320,-240), 0.0f);
	shape.e = 1.0; shape.u = 1.0;
	cpSpaceAddShape(space, shape);

	// Reference line
	// Does not collide with other objects, we just want to draw it.
	shape = cpSegmentShapeNew(staticBody, cpv(-320,0), cpv(320,0), 0.0f);
	shape.collision_type = 1;
	cpSpaceAddShape(space, shape);
	// Add a collision pair function to filter collisions
	cpSpaceAddCollisionPairFunc(space, 0, 1, null, null);
*/		
	
	//srand(cast(uint) time(null));
    cpVect p;
	cpFloat ang;
	
	// Create magnets
	for(int i=0; i<NMAG; i++)
	{
	  p.x=(2.0e0*frand() - 1.0e0)*WIDTH/2.0f;
  	  p.y=(2.0e0*frand() - 1.0e0)*HEIGHT/2.0f;
  	  ang=(2.0e0*frand() - 1.0e0)*3.1415;
	  make_mag(p, ang,1.0e7);
	}
	
	// Create charged objects
	for(int i=0; i<NCHG; i++)
	{
	  p.x=(2.0e0*frand() - 1.0e0)*WIDTH/2.0f;
  	  p.y=(2.0e0*frand() - 1.0e0)*HEIGHT/2.0f;
  	  ang=(2.0e0*frand() - 1.0e0)*3.1415;
	  make_charged(p,1.0e-3*pow(-1.0,i%2));
	}
		
	// Create charged magnets objects
	for(int i=0; i<NMIX; i++)
	{
      p.x=(2.0e0*frand() - 1.0e0)*WIDTH/2.0f;
  	  p.y=(2.0e0*frand() - 1.0e0)*HEIGHT/2.0f;
  	  ang=(2.0e0*frand() - 1.0e0)*3.1415;
	  make_mix(p, ang,1.0e7*pow(-1.0,i%2), 1.0e-3*pow(-1.0,i%2));
	}
	
	return space;
}

static void
destroy()
{
	cpSpaceFreeChildren(space);
	cpSpaceFree(space);
}

chipmunkDemo MagnetsElectric = {
	"Magnets and Electric Charges (By: Juan Pablo Carbajal)",
	null,
	&init,
	&update,
	&destroy,
};