package net.beadsproject.touch.old;

import java.awt.Color;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

import math.geom2d.AffineTransform2D;
import math.geom2d.Box2D;
import math.geom2d.Point2D;
import math.geom2d.line.LineSegment2D;
import math.geom2d.polygon.SimplePolygon2D;
import math.geom2d.polygon.convhull.GrahamScan2D;
import megamu.mesh.MPolygon;
import megamu.mesh.Voronoi;
import net.beadsproject.touch.SurfacePlayer;
import net.beadsproject.touch.examples.Example;
import net.beadsproject.touch.perform.PerformNode;
import net.beadsproject.touch.surface.PixelSurface;
import processing.core.PApplet;
import processing.core.PGraphics;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.operation.overlay.OverlayOp;


/**
 * Performs a hierarchical layout of a performtree. 
 * Basic algorithm:
 * - Define POLY = Rect(0,0,width,height)
 * - Layout children of root using particle repulsion (stopping after particles have achieved rest)
 * - Generate Voronoi regions 
 * - For each child, layout its children use RPM and confine to its v region
 * - Recurse in this manner until all children are laid out
 * 
 * @author ben
 */
public class HierarchicalParticleLayout extends PApplet {	
	/**
	 * Stop the simulation when the particles are not moving more than this amount
	 */
	static final float STOPDELTA = 0.001f;
	static final int NUMITERS = 1000;
	
	static final float TDELTA = 0.0001f;
	
	// particle physics models
	static final int MODE_EXPONENTIAL = 2;
	static final int MODE = MODE_EXPONENTIAL;	
	
	Voronoi voronoi;
	
	static class Particle
	{
		public Point2D position; 
		public Point2D oldPosition; // stores the last position of the particle, used to confine to the boundary
		public float radius;
		public PerformNode pn;
		
		public SimplePolygon2D region; // region that restricts child movement
		public Particle parent;
		public int depth; // depth down the hierarchy 
		
		public ArrayList<Particle> children;
		

		// cache the tree distance to each other particle
		public Map<Particle,Integer> treeDistanceMap;
				
		public Particle(Point2D pos, float rad)
		{
			position = pos; radius = rad;
			region = null;
			children = new ArrayList<Particle>();
			depth = 0;
			treeDistanceMap = new HashMap<Particle,Integer>();
		}
		
		public static float overlap(Particle a, Particle b)
		{
			return (float) (a.position.distance(b.position) - (a.radius+b.radius));
		}
	};
	
	static final int DEPTH = 3;
	static final int NARY = 3;
	static int NUM_PARTICLES = 1; //5*125;
	
	static final PerformNode pt = Example.example1();
	static final int ptDepth = pt.getDepth();
	
	// all particles
	Particle rootParticle = null;
	List<Particle> particles = new LinkedList<Particle>();
	Map<PerformNode,Particle> pnToParticleMap;
		
	List<Particle> leafParticles = new LinkedList<Particle>();
	
	boolean shouldIUpdateParticles = false;	
	boolean hasBeenBaked = false;
	PixelSurface ps;
	
	List<SimplePolygon2D> highlightPolys = new LinkedList<SimplePolygon2D>();
	
	PGraphics tempImg;
	
	float ox, oy; // old mousex, mousey;
	SurfacePlayer sp; // the user
	
	public void setup()
	{		
		size(900,900);
		
		ellipseMode(PApplet.CENTER);
		smooth();		
		
		sp = new SurfacePlayer();		
		pnToParticleMap = new HashMap<PerformNode,Particle>();
		pt.computeUniqueValue(0, 1);		
		loadTree(pt);
		NUM_PARTICLES = pt.getRoot().totalNumDescendents();
		doLayout2();
		
	}	
	
	public void printParticleTree(Particle p)
	{
		if (p==null) return;
		System.out.printf("(",p);
		for(Particle c: p.children)
		{
			printParticleTree(c);
		}
		System.out.printf(")");
	}
	
	public void recursiveDraw(Particle p)
	{
		if (p==null) return;
		
		stroke(0,0,0,255);
		double d = 1.0 - (1.0*p.depth)/(ptDepth-1); // 0 = leaf node, 1 = root node  
		strokeWeight(10*(float) d);
		Color c = new Color(Color.HSBtoRGB(p.pn.uniqueValue(),.8f,(float)(1-d)));			
		//fill(c.getRed(),c.getGreen(),c.getBlue(),150);
		noFill();
		
		// draw its polygon
		/*
		if (p.region!=null && !p.region.isEmpty())
		{
			beginShape();
			for(Point2D v: p.region.getVertices())
				vertex((float)v.x,(float)v.y);
			endShape();
		}
		*/
		
		// also draw its centroid
		/*
		ellipseMode(CENTER);
		if (true && (p.children==null || p.children.isEmpty()))
		{
			noStroke();
			fill(255,255);
			
			// draw its polygon
			if (p.region!=null && !p.region.isEmpty())
			{
				Point2D c1 = p.region.getCentroid();
				if (c1!=null)
				{
					ellipse((float)c1.x,(float)c1.y,8,8);				
				}
			}
		}*/
		
		// draw a dot too
		strokeWeight(2);
		if (true && (p.children==null || p.children.isEmpty()))
		{	
			stroke(0,100);		
			c = new Color(Color.HSBtoRGB(p.pn.uniqueValue(), 0.75f, 0.75f));
			fill(c.getRed(),c.getGreen(),c.getBlue(),100);			
			ellipse((float)p.position.x,(float)p.position.y,p.radius*2,p.radius*2);
			
			stroke(0);
			fill(0,0);
			ellipse((float)p.position.x,(float)p.position.y,2,2);
		}
		
		for(Particle ch: p.children)
			recursiveDraw(ch);
	}
	
	public void draw()
	{		
		background(255);		
		recursiveDraw(rootParticle);
		
		// and update particle simulation
		updateParticles(0.01f);
	}
	
	public void updateParticles(float dt)
	{
		updateParticleTreeAttraction(dt);
		updateParticlesExponential(dt);
		updateParticlesBoundary(dt);
	}	
	
	public void updateParticleTreeAttraction(float dt)
	{
		// perform an attraction step between all pairs...
		// for each overlapping particle, move it one "step" away, i.e., a little bit
		ListIterator<Particle> it = leafParticles.listIterator();
		while(it.hasNext())
		{
			Particle p = it.next();
			ListIterator<Particle> it2 = leafParticles.listIterator();
			while(it2.hasNext())
			{
				Particle q = it2.next();
				if (p==q) continue;
				
				int treedist = p.treeDistanceMap.get(q);
				if (treedist<=1)
				{				
					Point2D v = p.position.minus(q.position);
					double mag = v.distance(0,0);				
					// force should be proportional to the distance from the centroid
					v = v.scale(-0.000001*dt*mag); // let the force of repulsion equal the overlapsquared
					p.position = p.position.plus(v);
					q.position = q.position.minus(v);
				}
			}
		}
	}		
	
	public void updateParticlesExponential(float dt)
	{
		// for each overlapping particle, move it one "step" away, i.e., a little bit
		ListIterator<Particle> it = leafParticles.listIterator();
		while(it.hasNext())
		{
			Particle p = it.next();
			ListIterator<Particle> it2 = leafParticles.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);
					}
				}
			}
		}
	}
	
	void updateParticlesBoundary(float dt)
	{
		// for each overlapping particle, move it one "step" away, i.e., a little bit
		ListIterator<Particle> it = leafParticles.listIterator();
		while(it.hasNext())
		{
			Particle p = it.next();		
		
			// 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);
			}
			
		}
	}	
	
	// NOTE: doLayout() is an internal PApplet method!
	public void doLayout2()
	{
		/*
		* - Define POLY = Rect(0,0,width,height)
		 * - Layout children of root using particle repulsion (stopping after particles have achieved rest)
		 * - Generate Voronoi regions 
		 * - For each child, layout its children use RPM and confine to its v region
		 * - Recurse in this manner until all children are laid out
		 **/
		
		if (rootParticle!=null)
		{
			Box2D window = new Box2D(0,width,0,height);
			GrahamScan2D jm = new GrahamScan2D();
			rootParticle.region = (SimplePolygon2D)(jm.convexHull(window.getVertices()));
			doLayout2(rootParticle);
		}
	}
	
	/* Do a recursive layout on this particles children */
	public void doLayout2(Particle p)
	{	
		if (p==null || p.children==null || p.children.isEmpty() || p.region==null || p.region.isEmpty())
		{
			if (!(p.children==null || p.children.isEmpty() || p.children.size()<3) && (p.region==null || p.region.isEmpty()))
			{
				System.err.println("Argh!");
			}
			return;
		}
		
		// special cases, 1 child
		if (p.children.size()==1)
		{
			Particle child = p.children.get(0);
			child.position = p.position.clone();
			child.oldPosition = child.position;
			child.radius = p.radius;
			child.region = p.region;
			doLayout2(child);
			return;
		}		
		
		// first layout the particles within the particle region
		// we do this by finding the centroid of the region and applying a small perturbation to each particle
		SimplePolygon2D sp2d = p.region;		
		Point2D centroid = sp2d.getCentroid();
		
		// compute the equal radius given to each child
		float area = (float) sp2d.getArea();
		float areaPerChild = area/p.children.size();
		float radiusPerChild = (float) Math.sqrt(areaPerChild/Math.PI);
		
		if (centroid==null)
		{
			centroid = new Point2D(0,0);
			for(Point2D v: p.region.getVertices())
			{
				centroid = centroid.plus(v);
			}
			centroid = centroid.scale(1./p.region.getVertexNumber());
		}
		Box2D b2d = p.region.getBoundingBox();
		float dr = (float) (Math.min(b2d.getWidth(),b2d.getHeight())/5f);
		
		// lay children on a circle
		Point2D r = new Point2D(dr,0);
		double dtheta = Math.PI*2 / p.children.size(); 
		
		for(Particle c: p.children)
		{
			c.position = centroid.plus(r).plus(new Point2D(Math.random()*dr*0.1,Math.random()*dr*0.1));
			r = r.transform(AffineTransform2D.createRotation(dtheta));
			c.oldPosition = c.position;
			c.radius = radiusPerChild;
			
			//System.out.printf("p %s\n", c.position);
		}
				
		/*
		int gw = (int)Math.ceil(Math.sqrt(p.children.size()));	
		int row = 0;
		float col = 0;
		float stepsize = ((float)gw*gw)/p.children.size();
		
		Point2D tl = centroid.minus(new Point2D(dw*gw,dh*gw));
		
		for(Particle c: p.children)
		{
			c.position = tl.plus(new Point2D(col*dw,row*dh));
			col += stepsize;
			while (col >= gw)
			{
				col -= gw;
				row++;
			}
			c.oldPosition = c.position;
			c.radius = radiusPerChild;
			
			System.out.printf("p %s\n", c.position);
		}
		*/
		
		/*
		
		int iter = 0;
		while(true)
		{
			System.err.print(".");
			
			stepParticles(p.children,TDELTA);
			
			// check to see if they've come to rest
			double maxmove = -1;
			for(Particle ch: p.children)
			{
				double d = ch.position.distanceSq(ch.oldPosition);
				maxmove = Math.max(maxmove,d);
			}
			if (maxmove < STOPDELTA)
				break;
			iter++;
			if (iter > NUMITERS)
				break;
		}
		System.err.print("\n");
		*/
		
		// Simulation is done, so compute the voronoi boundaries of each placed particle
		buildVoronoi(p.children);
		
		// now recursively simulate all of their children
		for(Particle c: p.children)
		{
			doLayout2(c);
		}
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	public void stepParticles(List<Particle> particles, float dt)
	{
		stepParticlesExponential(particles,dt);
		constrainToBoundary(particles,dt);
		
		repaint();
	}
	
	public void stepParticlesExponential(List<Particle> particles, 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();
			p.oldPosition = p.position;
			
			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);
					}
				}
			}
		}
	}
	
	public void constrainToBoundary(List<Particle> particles, 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();
			if (!p.parent.region.contains(p.position))
				p.position = p.oldPosition;		
		}
	}
		
	/*
	public void updateParticleRegionAttraction(float dt)
	{
		// perform an attraction step		
		// 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;
				
				int treedist = p.treeDistanceMap.get(q);
				if (treedist<=1)
				{				
					Point2D v = p.position.minus(q.position);
					double mag = v.distance(0,0);				
					// force should be proportional to the distance from the centroid
					v = v.scale(-0.000001*dt*mag); // let the force of repulsion equal the overlapsquared
					p.position = p.position.plus(v);
					q.position = q.position.minus(v);
				}
			}
		}
	}	
	*/	
	
	void loadTree(PerformNode pt)
	{		
		PerformNode root = pt.getRoot();
		if (root==null) return;
		rootParticle = new Particle(new Point2D(width/2,height/2),width/2);
		rootParticle.pn = root;	
		rootParticle.parent = null;
		particles.add(rootParticle);
		pnToParticleMap.put(root,rootParticle);
		
		// recursively generate all particles
		loadNode(rootParticle);
		
		// cache tree dist
		// 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;
				
				p.treeDistanceMap.put(q, PerformNode.getTreeDistance(p.pn, q.pn));				
			}
		}
	}
	
	void loadNode(Particle p)
	{
		for (PerformNode pn: p.pn.getChildren())
		{
			Particle ch = new Particle(new Point2D(0,0),0);
			ch.pn = pn;
			ch.parent = p;
			ch.depth = p.depth + 1;
			p.children.add(ch);
			particles.add(ch);
			pnToParticleMap.put(ch.pn,ch);
			loadNode(ch);
		}
		
		if (p.pn.getChildren()==null || p.pn.getChildren().isEmpty())
		{
			leafParticles.add(p);
		}
	}
	
	public SimplePolygon2D p2dFromMply(MPolygon m)
	{
		List<Point2D> soup = new LinkedList<Point2D>();
		for(float[] f: m.getCoords())
			soup.add(new Point2D(f[0],f[1]));
		GrahamScan2D jm = new GrahamScan2D();
		return (SimplePolygon2D)(jm.convexHull(soup));
	}
	

	public SimplePolygon2D intersect2(SimplePolygon2D a, SimplePolygon2D b)
	{
		Coordinate[] ac = new Coordinate[1+a.getVertexNumber()];
		int i = 0;
		for(Point2D p: a.getVertices())
			ac[i++] = new Coordinate(p.x,p.y);
		ac[i] = new Coordinate(a.getVertex(0).x,a.getVertex(0).y);
		i = 0;
		Coordinate[] bc = new Coordinate[1+b.getVertexNumber()];
		for(Point2D p: b.getVertices())
			bc[i++] = new Coordinate(p.x,p.y);
		bc[i] = new Coordinate(b.getVertex(0).x,b.getVertex(0).y);
		
		List<Coordinate[]> c = new LinkedList<Coordinate[]>();
		c.add(ac);
		c.add(bc);
		
		GeometryFactory gf = new GeometryFactory(); //new PrecisionModel(PrecisionModel.FIXED));		
		Polygon pa = gf.createPolygon(gf.createLinearRing(ac),null); // new Polygon(lr[0],null,null);
		Polygon pb = gf.createPolygon(gf.createLinearRing(bc),null);
		
		assert (pa.isSimple());
		assert (pb.isSimple());
		
		try
		{
			Geometry res = OverlayOp.overlayOp(pa,pb,OverlayOp.INTERSECTION);
		
			SimplePolygon2D sp2d = new SimplePolygon2D();
			for (Coordinate co: res.convexHull().getCoordinates())
			{
				sp2d.addVertex(new Point2D(co.x,co.y));
			}
			return sp2d;
		}		
		catch(Exception e)
		{
			highlightPolys.add(a);
			highlightPolys.add(b);
			
			System.out.printf("polya: %s", pa.toText());
			
			
			System.out.print("a ");
			for(Point2D va: a.getVertices())
			{
				System.out.printf("(%f,%f) ", va.x, va.y);
			}
			System.out.println();
			
			System.out.printf("polyb: %s", pb.toText());
			
			System.out.print("b ");
			for(Point2D vb: b.getVertices())
			{
				System.out.printf("(%f,%f) ", vb.x, vb.y);
			}
			System.out.println();
			
			e.printStackTrace();

			
			System.out.println("OOPS, WE'VE ENCOUNTERED AN ERROR WITH THE POLYGON INTERSECTION DOOBAH...\nEXITTING.");
			System.exit(0);
			return null;
		}			
	}
	
	/**
	 * Intersect boundary exceeding poly a, with mother poly m
	 * @return
	 */
	public SimplePolygon2D intersect3(SimplePolygon2D a, SimplePolygon2D m)
	{
		System.out.printf("Intersecting %s with %s\n",a,m);
		
		final int border = 10;
		Box2D thisWindow = new Box2D(-border,width+border,-border,height+border);
		
		// Let P = points in a outside of this window
		List<Point2D> P = new LinkedList<Point2D>();		
		for(Point2D pa: a.getVertices())
		{
			if (!thisWindow.contains(pa))
			{
				P.add(pa);
			}
		}
		
		// Find lines l1,l2 that escape this window
		LineSegment2D l1 = null, l2 = null;
		for(LineSegment2D l: a.getEdges())
		{
			boolean b1 = thisWindow.contains(l.getFirstPoint());
			boolean b2 = thisWindow.contains(l.getLastPoint()); 
			if (b1 && !b2)
			{
				l1 = l;
			}
			else if (!b1 && b2)
			{
				l2 = l;
			}
		}
		
		if (l1==null || l2==null)
		{
			System.err.println("Error intersecting poly a with boundary!");
			return null;
		}
		// else continue!
		
		// Find points v1 and v2 where l1 and l2 intersect poly m
		Point2D v1 = null, v2 = null;
		for(LineSegment2D l: m.getEdges())	
		{
			if (v1==null)
			{
				Point2D pv1 = l.getIntersection(l1);
				if (pv1!=null)
					v1 = pv1;			
			}
			if (v2==null)
			{
				Point2D pv2 = l.getIntersection(l2);
				if (pv2!=null)
					v2 = pv2;			
			}
			if (v1!=null && v2!=null) break;			
		}
		
		if (v1==null || v2==null)
		{
			System.err.println("Error intersecting poly a with poly m!");
			return null;
		}
		
		// Construct set Q, where Q = (a.points - P) + v1 + v2
		List<Point2D> Q = new LinkedList<Point2D>();
		Q.addAll(a.getVertices());
		Q.removeAll(P);
		Q.add(v1);
		Q.add(v2);
		
		GrahamScan2D jm = new GrahamScan2D();
		return (SimplePolygon2D)(jm.convexHull(Q));
	}
	
	public SimplePolygon2D intersect(SimplePolygon2D a, SimplePolygon2D b)
	{
		//System.out.printf("Intersecting %s with %s\n",a,b);
		
		// Let P = A union B union L
		// where A is the set of points in polygon a which are contained in polygon b
		// and B is the set of points in poly b which are contained in poly a
		// and L is the set of all intersection points of all lines of a and b
		List<Point2D> points = new LinkedList<Point2D>();
		for(Point2D pa: a.getVertices())
		{
			//System.out.printf("%s ",pa);
			if (b.contains(pa)) points.add(pa);
		}
		//System.out.println();
		for(Point2D pb: b.getVertices())
		{
			//System.out.printf("%s is contained in a? %s\n",pb,a.contains(pb));
			if (a.contains(pb)) points.add(pb);
		}
		for(LineSegment2D la: a.getEdges())
		{
			for(LineSegment2D lb: b.getEdges())
			{
				Point2D p = la.getIntersection(lb);
				if (p!=null)
					points.add(p);
			}
		}
		
		if (points.isEmpty()) return null;
		
		GrahamScan2D jm = new GrahamScan2D();
		return (SimplePolygon2D)(jm.convexHull(points));
	}
	
	public void buildVoronoi(ArrayList<Particle> ps)
	{
		if (ps.size()<3)
		{
			return;
		}
		
		float[][] pts = new float[ps.size()][2];
		int index = 0;
		
		//System.err.printf("voronoi: ");
		for(Particle p: ps)
		{
			pts[ps.size()-1-index][0] = (float) p.position.x;
			pts[ps.size()-1-index][1] = (float) p.position.y;
			index++;			
			//System.err.printf("(%.2f,%.2f) ",p.position.x,p.position.y);			
		}
		//System.err.println();
		
		Voronoi v = new Voronoi(pts);		
		
		SimplePolygon2D motherPoly = ps.get(0).parent.region;
		//motherPoly = motherPoly.transform(AffineTransform2D.createScaling(motherPoly.getCentroid(), .99,.99));
		
		MPolygon[] polys = v.getRegions();
		for(int i=0;i<ps.size();i++)
		{
			// convert MPolygon into Polygon2D
			// and clip it against the mother polygon
			SimplePolygon2D p = p2dFromMply(polys[i]);
			// p = p.transform(AffineTransform2D.createScaling(p.getCentroid(), 1.1,1.1));
			SimplePolygon2D q = intersect(p,motherPoly);
			if (q==null)
			{
				System.err.printf("Error intersecting %s with %s\n",p,motherPoly);
				q = p;
			}
			ps.get(i).region = q;			
		}
	}
	
	public void mouseDragged()
	{
		didMouseDragged(mouseX, mouseY);
	}

	
	int count = 0;
	long previousTime = 0;
	
	public void didMouseDragged(int mouseX, int mouseY)
	{
		if (hasBeenBaked)
		{
			float nx = mouseXToWorldX(mouseX);
			float ny = mouseYToWorldY(mouseY);
			
			// notify surface
			// cs.mouseMoved(ox,oy,nx,ny);
			ps.playerMoved(sp,ox,oy,nx,ny);
			
			// update old position
			ox = nx;
			oy = ny;
			
			long newTime = System.currentTimeMillis();
//			System.out.println("drag " + count++ + " " + (int)(newTime - previousTime));
			previousTime = newTime;
		}
	}
	
	public void mouseReleased()
	{
		if (hasBeenBaked)
		{
			float nx = mouseXToWorldX(mouseX);
			float ny = mouseYToWorldY(mouseY);
			ps.playerReleased(sp,nx,ny);		
			ox = nx;
			oy = ny;
		}
	}
	
	public void mousePressed()
	{
		if (!hasBeenBaked)
		{
			shouldIUpdateParticles = !shouldIUpdateParticles;
		}
		else
		{
			float nx = mouseXToWorldX(mouseX);
			float ny = mouseYToWorldY(mouseY);
			ps.playerPressed(sp,nx,ny);
			ox = nx;
			oy = ny;
		}
	}
	
	
	public float mouseXToWorldX(int x)
	{
		return (float) (1.0*x/width);
	}
	
	public float mouseYToWorldY(int y)
	{
		return (float) (1.0*y/height);
	}
	
}
