package net.beadsproject.touch.old;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;

import math.geom2d.Point2D;
import processing.core.PApplet;


/**
 * Test code, used to work out the repulsive particle simulation.
 * 
 * @author ben
 */
public class ParticleTest extends PApplet {	
	static final int NUM_PARTICLES = 200;
	
	// particle physics models
	static final int MODE_CONSTANT = 0;
	static final int MODE_LINEAR = 1;
	static final int MODE_EXPONENTIAL = 2;
	
	// which model to use
	static final int MODE = MODE_EXPONENTIAL;	
	
	static class Particle
	{
		public Point2D position; 
		public float radius;
		
		public Particle(Point2D pos, float rad){position = pos; radius = rad;}
		public static float overlap(Particle a, Particle b)
		{
			return (float) (a.position.distance(b.position) - (a.radius+b.radius));
		}
	}	
	
	List<Particle> particles;
	
	public void setup()
	{
		size(1000,1000);
		initialiseParticles();		
		
		strokeWeight(500.f/NUM_PARTICLES);
		
		ellipseMode(PApplet.CENTER);
		smooth();
		
		frameRate(50);
	}
	
	public void draw()
	{
		background(225);		
		updateParticles(0.01f);
		for(Particle p: particles)
		{
			stroke(255);
			fill(113,151,185,150);
			ellipse((float)p.position.x,(float)p.position.y,2*p.radius,2*p.radius);
			
			noStroke();
			fill(0);			
			ellipse((float)p.position.x,(float)p.position.y,5,5);
		}
	}
	
	public void mousePressed()
	{
		particles.add(new Particle(new Point2D(mouseX,mouseY),(float) (width/((Math.random() + 1.75)*Math.sqrt(NUM_PARTICLES)))));		
	}
	
	public void initialiseParticles()
	{
		particles = new LinkedList<Particle>();
		final float minRadius = (float) (width/(2.5*Math.sqrt(NUM_PARTICLES)));
		final float maxRadius = (float) (width/(1.5*Math.sqrt(NUM_PARTICLES)));
		
		for(int i=0;i<NUM_PARTICLES;i++)
		{
			float radius = (float)(Math.random()*(maxRadius-minRadius) + minRadius);
			particles.add(new Particle(new Point2D(Math.random()*width,Math.random()*height), radius));
		}
	}
	
	public void updateParticles(float dt)
	{
		if (MODE==MODE_CONSTANT)
			updateParticlesConstant(dt);
		else if (MODE==MODE_LINEAR)
			updateParticlesLinear(dt);
		else if (MODE==MODE_EXPONENTIAL)
			updateParticlesExponential(dt);
	}
	
	
	public void updateParticlesExponential(float dt)
	{
		// for each overlapping particle, move it one "step" away, i.e., a little bit
		ListIterator<Particle> it = particles.listIterator();
		while(it.hasNext())
		{
			Particle p = it.next();
			ListIterator<Particle> it2 = particles.listIterator();
			while(it2.hasNext())
			{
				Particle q = it2.next();
				if (p==q) continue;
				
				float o = Particle.overlap(p,q);
				if (o<0)
				{
					// p and q away from each other
					
					if (p.position.distanceSq(q.position) < 2)
					{
						// then perturb each position randomly
						final double PERTURB = 2;
						p.position = p.position.plus(new Point2D(PERTURB*Math.random() - PERTURB/2,PERTURB*Math.random() - PERTURB/2));
						q.position = q.position.plus(new Point2D(PERTURB*Math.random() - PERTURB/2,PERTURB*Math.random() - PERTURB/2));						
					}
					else
					{
						Point2D v = p.position.minus(q.position);
						v = v.scale((o*o)*dt/v.distance(0,0)); // let the force of repulsion equal the overlapsquared
						p.position = p.position.plus(v);
						q.position = q.position.minus(v);
					}
				}
			}
			
			// also force the boundary constraint
			final double BOUNDFORCE = 0.2;
			if ((p.position.x-p.radius) < 0)
			{
				p.position.x += BOUNDFORCE*dt*(p.position.x-p.radius)*(p.position.x-p.radius);
			}
			else if ((p.position.x+p.radius) > width)
			{
				p.position.x -= BOUNDFORCE*dt*((width-p.position.x) - p.radius)*((width-p.position.x) - p.radius);
			}
			
			if ((p.position.y - p.radius) < 0)
			{
				p.position.y += BOUNDFORCE*dt*(p.position.y-p.radius)*(p.position.y-p.radius);	
			}
			else if ((p.position.y+p.radius) > height)
			{
				p.position.y -= BOUNDFORCE*dt*((height-p.position.y) - p.radius)*((height-p.position.y) - p.radius);
			}
			
		}
	}
	
	public void updateParticlesLinear(float dt)
	{
		// for each overlapping particle, move it one "step" away, i.e., a little bit
		ListIterator<Particle> it = particles.listIterator();
		while(it.hasNext())
		{
			Particle p = it.next();
			ListIterator<Particle> it2 = particles.listIterator();
			while(it2.hasNext())
			{
				Particle q = it2.next();
				if (p==q) continue;
				
				float o = Particle.overlap(p,q);
				if (o<0)
				{
					// p and q away from each other
					
					if (p.position.distanceSq(q.position) < 2)
					{
						// then perturb each position randomly
						final double PERTURB = 2;
						p.position = p.position.plus(new Point2D(PERTURB*Math.random() - PERTURB/2,PERTURB*Math.random() - PERTURB/2));
						q.position = q.position.plus(new Point2D(PERTURB*Math.random() - PERTURB/2,PERTURB*Math.random() - PERTURB/2));						
					}
					else
					{
						Point2D v = p.position.minus(q.position);
						v = v.scale(-o*dt/v.distance(0,0)); // let the force of repulsion equal the overlap
						p.position = p.position.plus(v);
						q.position = q.position.minus(v);
					}
				}
			}
			
			// also loosely force the boundary constraint
			if ((p.position.x-p.radius) < 0)
			{
				p.position.x -= dt*(p.position.x-p.radius);
			}
			else if ((p.position.x+p.radius) > width)
			{
				p.position.x += dt*((width-p.position.x) - p.radius);
			}
			
			if ((p.position.y - p.radius) < 0)
			{
				p.position.y -= dt*(p.position.y-p.radius);	
			}
			else if ((p.position.y+p.radius) > height)
			{
				p.position.y += dt*((height-p.position.y) - p.radius);
			}
			
		}
	}
	
	public void updateParticlesConstant(float dt)
	{
		// for each overlapping particle, move it one "step" away, i.e., a little bit
		ListIterator<Particle> it = particles.listIterator();
		while(it.hasNext())
		{
			Particle p = it.next();
			ListIterator<Particle> it2 = particles.listIterator();
			while(it2.hasNext())
			{
				Particle q = it2.next();
				if (p==q) continue;
				
				float o = Particle.overlap(p,q);
				if (o<0)
				{
					// p and q away from each other
					Point2D v = p.position.minus(q.position);
					v = v.scale(dt/v.getDistance(0,0)); // normalise
					p.position = p.position.plus(v);
					//q.position = q.position.plus(v);
				}
			}
		}
	}
}
