view mesh.mm @ 15:d5758530a039 tip

oF0.84 Retina, and iPhone support
author Robert Tubb <rt300@eecs.qmul.ac.uk>
date Tue, 12 May 2015 15:48:52 +0100
parents 4ba81a12b008
children
line wrap: on
line source
/*
 *  mesh.cpp
 *  springstructure
 *
 *  Created by Robert Tubb on 07/06/2011.
 *  Copyright 2011 __MyCompanyName__. All rights reserved.
 *
 */

#include "mesh.h"
#include "testApp.h"
#include "lump.h"
#include <math.h>

extern GlobalForces globalForces;
extern ScanPath  scanPath; // now global

//--------------------------------------------------------------
Mesh::Mesh(){
	// construct an empty structure
    setReasonableDefaults();

    cout << "constructed mesh base" << endl;
}


Mesh::~Mesh(){
	cout << "destroying mesh" << endl;


}
//--------------------------------------------------------------
void Mesh::setReasonableDefaults(){
    numSprings = 0;
	numLumps = 0;
    
	syrup = false;
	GRAB_RANGE = 0.040;
    propagationSpeed = 0.5; // this will be changed by adjusting k/m
	
    
    m = 123.0;
    k = 0.1;
    f = 0.999;
    
    prevLump = -1;
    
    // some reasonable defaults
    setSpringConstant(0.8);
    setMass(1);
    setFriction(0.999991);
}
//--------------------------------------------------------------
//--------------------------------------------------------------
Mesh::Mesh(Json::Value& jmesh){
    setReasonableDefaults(); // sensible?
    
    meshType = (MeshType)jmesh.get("meshType",0).asInt();
    dim1 = jmesh.get("dim1",0).asInt();
    dim2 = jmesh.get("dim1",0).asInt();
    
    // need to know how many springs and lumps
    numLumps = jmesh.get("numLumps",0).asInt();
    numSprings = jmesh.get("numSprings",0).asInt();
    
    // make them
    makeComponents(numLumps, numSprings);
    setLumpPositionsFromJson(jmesh["lumpPositions"], jmesh["lumpRestPositions"]);
    makeConnectionsFromJson(jmesh["connections"]);
    makeScanPathFromJson(jmesh["scanPath"]);
    constrainFromJson(jmesh["constrainedLumps"]);

    // now set saved properties 
    setSpringConstant(jmesh["springConstant"].asDouble());
    setMass(jmesh["mass"].asDouble());
    setFriction(jmesh["friction" ].asDouble());
    // rest length?
    zeroRestLength();
}

//--------------------------------------------------------------
void Mesh::setLumpPositionsFromJson(Json::Value lumpPositions, Json::Value lumpRestPositions){
    int N = lumpPositions.size();
    
    for(int i=0;i<N;i++){
        lumps[i].position.x = lumpPositions[i]["x"].asFloat();
        lumps[i].position.y = lumpPositions[i]["y"].asFloat();
        
        lumps[i].zeroRefPos.x = lumpRestPositions[i]["x"].asFloat();
        lumps[i].zeroRefPos.y = lumpRestPositions[i]["y"].asFloat();
	}
}
//--------------------------------------------------------------
void Mesh::makeConnectionsFromJson(Json::Value connections){
    
    // format: pairs of lump indexes, and a connecting spring
    for (int i=0;i<connections.size();i++){
        connect(i, connections[i]["s"].asInt(),connections[i]["e"].asInt()); // will the springs be in the right order? they need to be!
        
    }
}

//--------------------------------------------------------------
void Mesh::makeScanPathFromJson(Json::Value jscanPath){
    
    for(int i=0;i<jscanPath.size();i++){
        scanPath.addElement(&lumps[jscanPath[i]["lumpNo"].asInt()], &springs[jscanPath[i]["springNo"].asInt()]);
    }
}
//--------------------------------------------------------------
void Mesh::constrainFromJson(Json::Value constrainedLumps){

    for(int i=0;i<constrainedLumps.size();i++){

        lumps[constrainedLumps[i].asInt()].constrain(); // TODO ignores XY thing

	}
}

//--------------------------------------------------------------
Json::Value Mesh::convertToJsonForSaving(){
    
    // needs to be good to put back into
    /*
     void setLumpPositionsFromJson(Json::Value lumpPositions);
     void makeConnectionsFromJson(Json::Value connections);
     void makeScanPathFromJson(Json::Value scanPathElements);
     void constrainFromJson(Json::Value constrainedLumps);
    */
    
    Json::Value jmesh;
    
    jmesh["meshType"] = meshType;
    jmesh["dim1"] = dim1; // these don't exist...
    jmesh["dim2"] = dim2;
    // need to know how many springs and lumps
    jmesh["numLumps"] = numLumps;
    jmesh["numSprings"] = numSprings;
    jmesh["springConstant"] = k;
    jmesh["mass"] = m;
    jmesh["friction" ] = f;
    for(int i=0;i<lumps.size();i++){
        jmesh["lumpPositions"][i]["x"] = lumps[i].position.x;
        jmesh["lumpPositions"][i]["y"] = lumps[i].position.y;
        // just use rest pos ... too much data! 
        jmesh["lumpRestPositions"][i]["x"] = lumps[i].zeroRefPos.x;
        jmesh["lumpRestPositions"][i]["y"] = lumps[i].zeroRefPos.y;
	}

    jmesh["connections"] = saveConnectionsAsJson();
    jmesh["scanPath"] = scanPath.convertToJsonForSaving();

    // save all indexes of constrained lumps
    int j = 0;
    for(int i=0;i<lumps.size();i++){
		if(lumps[i].constrained){
            
            jmesh["constrainedLumps"][j] = i;
            j++;
        }
	}
  
    
    return jmesh;
    
}
//--------------------------------------------------------------
Json::Value Mesh::saveConnectionsAsJson(){
    Json::Value connections;
    // loop all springs
    
    for(int i = 0; i<springs.size(); i++){
        connections[i]["s"]= springs[i].getStartLumpIndex();
        connections[i]["e"]= springs[i].getEndLumpIndex();
    }
    return connections;
    
    
}
//--------------------------------------------------------------
//--------------------------------------------------------------
///////////
// interface (called from testApp)
///////////
void Mesh::update(){
	// update all the springs and masses sequentially
    // INTERP HACK - this bit of code is to debug pops and clicks
  /*  
    static int updown = 0;
    updown++;
    if(updown > 24){
        for(int i=0;i<lumps.size();i++){
            lumps[i].position.y = 0.4;
        }	
    
    }else{
        
        for(int i=0;i<lumps.size();i++){
            lumps[i].position.y = 0.6;
        }	
     
    }
    if(updown > 48) updown = 0;
  	// update the ends of the springs to reflect lump movement
	
	for(int i=0;i<numSprings;i++){
		springs[i].updateEndPoints();
	}	  
    
    return;
    
   */
    
    globalForces.update();
	// calculate all the forces in the springs
	for(int i=0;i<springs.size();i++){
		springs[i].calculateForce();
	}	
    
	// accelerate the lumps
	for(int i=0;i<lumps.size();i++){
		lumps[i].applyForce();
	}	
    
    // move filters into apply force? not really, they're not forces.
    // apply homing filter
	for(int i=0;i<lumps.size();i++){
		lumps[i].homingFilter(globalForces.homingAmt);
	}	    
    // apply averaging filter
    // 
    averagingFilter(globalForces.avFilterAmt);
    
	// update the ends of the springs to reflect lump movement
	for(int i=0;i<numSprings;i++){
		springs[i].updateEndPoints();
	}	
    
    // scan the updated mesh
    scanPath.updateWavetables();
}
//--------------------------------------------------------------
void Mesh::averagingFilter(double amt){
    // we need a temporary store for average position, can't update in place with central diffs
    vector<TwoVector> avLumpPos = vector<TwoVector>(lumps.size());
    
    
    for(int i=0;i<lumps.size();i++){
		avLumpPos[i] = lumps[i].averageOfConnected();
	}	 
    // now we can set pos. needs to be separate loop because we don't want to average *moved* lumps
    for(int i=0;i<lumps.size();i++){
            // mix in the average with the 'real'

            lumps[i].position.x = (1 - amt)*lumps[i].position.x + amt*avLumpPos[i].x;
            lumps[i].position.y = (1 - amt)*lumps[i].position.y + amt*avLumpPos[i].y;

	}    
    
}
//--------------------------------------------------------------
void Mesh::draw(){
	// draw all the components belonging to mesh

	int i;
	for(i=0;i<numSprings;i++){
		springs[i].draw();
	}

	for(i=0;i<lumps.size();i++){
		lumps[i].draw();
	}




}
//--------------------------------------------------------------
TwoVector Mesh::calculateCentre(){
    // calculates average of all lump positions
    
    double x = 0, y = 0;
    for(int i = 0; i < lumps.size(); i++){
        x += lumps[i].position.x;
        y += lumps[i].position.y;
        
    }
    x = x/lumps.size();
    y = y/lumps.size();
    
    //cout << "centre : " << x << "," << y << endl;
    
    centre.setCoord(x,y);
    
    return centre;
}
//--------------------------------------------------------------
void Mesh::resetAll(){
	// it's this simple
	resetPositions();
    resetVelocities();
    unconstrain();
    clearScanPath();
    makeDefaultScanPath();
    constrain(0.0,0.0,CONSTRAIN_EDGES);
    constrain(0.0,0.0,CONSTRAIN_CORNERS);
    
	// unless we want to reset velocities...?
}
//--------------------------------------------------------------
void Mesh::resetPositions(){
	// it's this simple
	setLumpPositions();
	// unless we want to reset velocities...?
}
//--------------------------------------------------------------
void Mesh::resetVelocities(){
    for(int i = 0; i < lumps.size(); i++){
        lumps[i].setVelocity(0.0,0.0);
        
    }    
}
void Mesh::toggleHome(bool on){
    static double prev;
    
    if(!home && on){
        home = on;
        prev = globalForces.homingAmt;
        globalForces.homingAmt = 0.3;
        

    }else if (home && !on){
        home = on; // ie false
        globalForces.homingAmt = prev;        
    }
}
//--------------------------------------------------------------
void Mesh::resetEverything(){

}

//--------------
void Mesh::toggleSyrup(){
    if(syrup){
        toggleSyrup(false);
    }else{
        toggleSyrup(true);
    }
}
//--------------
void Mesh::toggleSyrup(bool on){
	if (!on){
		syrup = false;
		for(int i=0;i<lumps.size();i++) lumps[i].setFriction(0.998);
	}else{
		syrup = true;
		for(int i=0;i<lumps.size();i++) lumps[i].setFriction(0.90);
	}
}
//----------------------------------------------------------------
void Mesh::toggleGravity(){
    globalForces.gravityOn = !globalForces.gravityOn;
}
//----------------------------------------------------------------
void Mesh::toggleGravity(bool on){
	if (!on){
        globalForces.gravityOn = true;
	}else{
        globalForces.gravityOn = false;
	}
}

//----------------------------------------------------------------
void Mesh::toggleSpringForces(){

		Spring::forcesOn = !Spring::forcesOn;

}
//----------------------------------------------------------------
void Mesh::toggleSpringForces(bool on){
	if (!on){
		Spring::forcesOn = false;

	}else{
		Spring::forcesOn = true;

	}
}

//--------------------------------------------------------------
void Mesh::unconstrain(){
	
	for(int i = 0; i<lumps.size(); i++){
		
		lumps[i].unconstrain();
		
	}
}
//--------------------------------------------------------------
void Mesh::unconstrain(double ax, double ay, constrainMode aMode){
	int i = 0;
    TwoVector diff;
	// check if lump is within grab range
	switch (aMode) {
		case CONSTRAIN_GRAB_REGION:
			for(i = 0; i<lumps.size(); i++){
				
				diff.setCoord(lumps[i].position.x - ax, lumps[i].position.y - ay);
				
				if (diff.norm() < GRAB_RANGE){
					lumps[i].unconstrain();
				}
			}
			break;
		case CONSTRAIN_EDGES:
            // hmm
			break;
			
		default:
			break;
	}
}
//--------------------------------------------------------------
void Mesh::setRestLength(){
	for(int i = 0; i<numSprings; i++){
		springs[i].setRestLength();
	}

}
//--------------------------------------------------------------
void Mesh::setZeroAudioLine(){
	// set zero rest pos to where they are now
    for(int i = 0; i<lumps.size(); i++){
		
		lumps[i].setZeroRefPos();
		
	}
}
//--------------------------------------------------------------
void Mesh::setPropagationSpeed(double aSpeed){
    
    propagationSpeed = aSpeed;
    for(int i = 0; i < lumps.size(); i++){
        lumps[i].setInvMass(propagationSpeed*propagationSpeed); // inverse mass is c^2
    }
}
//--------------------------------------------------------------
void Mesh::increasePropagationSpeed(){
    
    propagationSpeed = propagationSpeed + 0.01;
    for(int i = 0; i < lumps.size(); i++){
        lumps[i].setInvMass(propagationSpeed*propagationSpeed); // inverse mass is c^2
    }
    cout << "prop speed  = " << propagationSpeed << endl;
}
//--------------------------------------------------------------
void Mesh::decreasePropagationSpeed(){
    
    propagationSpeed = propagationSpeed - 0.01;
    for(int i = 0; i < lumps.size(); i++){
        lumps[i].setInvMass(propagationSpeed*propagationSpeed); // inverse mass is c^2
    }
    cout << "prop speed  = " << propagationSpeed << endl;
}
//--------------------------------------------------------------
void Mesh::setSpringConstant(double aK){
    k = aK;
    if(sqrt(k/m) > 0.9){
        k = m * 0.9;
    }
    cout << "k/m = " << k/m << endl;
    for(int i = 0; i < springs.size(); i++){
        springs[i].setSpringConstant(k); // inverse mass is c^2
    }      
}
//--------------------------------------------------------------
void Mesh::setMass(double aM){

    m = aM;

    if(sqrt(k/m) > 0.9){
        m = k/0.9;
    }
    //cout << "k/m = " << k/m << endl;
    double im = 1/aM;
    for(int i = 0; i < lumps.size(); i++){
        lumps[i].setInvMass(im); 
    }    
}
//--------------------------------------------------------------
void Mesh::setFriction(double newF){
    f = newF;
    for(int i = 0; i < lumps.size(); i++){
        lumps[i].setFriction(f); 
    }        
}
//--------------------------------------------------------------
void Mesh::setMassAsym(double aAsym){
    // use m as bass and add asym linearly across mesh. might be wierd for some meshes!
    double newm,incr;
    newm = m;
    incr = aAsym*m/(lumps.size()-1);
    for(int i = 0; i < lumps.size(); i++){
        lumps[i].setInvMass(1/newm);
        newm += incr;
    }    
}
//--------------------------------------------------------------
void Mesh::setSpringConstantAsym(double aAsym){
    // from 0 to 1
    if (aAsym > 1.0){
        aAsym = 1.0;
    }
    if (aAsym < 0.0){
        aAsym = 0.0;
    }
    double newk, incr;
    newk = k;
    incr = (1-aAsym)*k/(numSprings-1);
    for(int i = 0; i < numSprings; i++){
        springs[i].setSpringConstant(newk);
        newk -= incr;
    }    
}

//--------------------------------------------------------------
void Mesh::setFrictionAsym(double aAsym){
    // multiplying it doesn't make sense!
    double newF,incr;
    newF = f;
    incr = aAsym*f/(lumps.size()-1);
    for(int i = 0; i < lumps.size(); i++){
        lumps[i].setFriction(newF); 
        newF += incr;
    }        
}
//--------------------------------------------------------------
void Mesh::checkHover(double ax, double ay){
	TwoVector diff;
	bool found = false;
	// check if lump is within grab range
	for(int i = 0; i<lumps.size(); i++){
		diff.setCoord(lumps[i].position.x - ax, lumps[i].position.y - ay);
		//cout << "diff " << diff.x << "," << diff.y << endl;
		if (diff.norm() < GRAB_RANGE){
			if(!found)	{
				lumps[i].highlight();
				found = true;
			}
		}else{
			lumps[i].unhighlight();
		}
	}	
	
}

//--------------------------------------------------------------
void Mesh::grab(double ax, double ay, int touchID){
	// attempt to grab a lump ax and ay should be already normalised
	
	TwoVector diff;
    
	// find closest
    int nearest = getNearestLump(ax,ay);
    //if(nearest = -1) return; // there are no lumps!
    diff.setCoord(lumps[nearest].position.x - ax, lumps[nearest].position.y - ay);
    //cout << "diff " << diff.x << "," << diff.y << endl;
    cout << GRAB_RANGE << endl;
    if (diff.norm() < GRAB_RANGE){
        
        cout << "grabbing lump " << nearest << endl;
        
        // move this lump to mouse pos
        
        lumps[nearest].grab(touchID);
    }

	
}
//--------------------------------------------------------------
void Mesh::unGrab(int touchID){
	
	for(int i=0; i<lumps.size();i++){
        if(lumps[i].grabID == touchID){ // only release the lumps that were grabbed by this finger
            lumps[i].unGrab();
            lumps[i].unhighlight();
        }
	}
}

//--------------------------------------------------------------
void Mesh::drag(double ax, double ay, int touchID){
	// maybe set up specific id's for grabbed lumps
	for(int i=0; i<lumps.size();i++){
        if(lumps[i].grabID == touchID){// only move the lumps that were grabbed by this finger
            
            lumps[i].drag(ax,ay,touchID);
        }
	}
}
//--------------------------------------------------------------
/* also could:
 add random velocities
 etc
 */
//////////////////////
// protected utils
////////////////////////
//---------------------------------------------------------------
void Mesh::connect(int aspringnum,int alumpnum){
	//cout << "connecting spring " << aspringnum << " to lump " << alumpnum << endl;
	// should we check valid indexes?
	lumps[alumpnum].attachSpring(&springs[aspringnum]);
	springs[aspringnum].attachLump(&lumps[alumpnum]);

}
//---------------------------------------------------------------
void Mesh::connect(int aspringnum,int alumpnum,int alumpnum2){
	// better version - just connects 2 lumps with a spring
	// should we check valid indexes?
	lumps[alumpnum].attachSpring(&springs[aspringnum]);
	springs[aspringnum].attachLump(&lumps[alumpnum]);
	lumps[alumpnum2].attachSpring(&springs[aspringnum]);
	springs[aspringnum].attachLump(&lumps[alumpnum2]);
    
}

void Mesh::zeroRestLength(){
    // sets springs rest length to zero - collapses structure
    for(vector<Spring>::iterator spriter = springs.begin(); spriter < springs.end(); spriter++){
        (*spriter).setRestLength(0.0);
    }

}
//--------------------------------------------------------------
int Mesh::getNearestLump(double ax, double ay){
    // not used yet
	int nearestLumpIndex = -1;
	TwoVector diff;
	double distance = 1.0;
	double minDistance = 1.0;

	for(int i = 0; i<lumps.size(); i++){
		diff.setCoord(lumps[i].position.x - ax, lumps[i].position.y - ay);
		distance = diff.norm();
		if(distance < minDistance && !lumps[i].constrained){ // don't grab if constrained ?? 
			minDistance = distance;
			nearestLumpIndex = i;
		}
	}	
    //cout << "nearest lump : " << nearestLumpIndex << endl;
    
	return nearestLumpIndex;
}
//--------------------------------------------------------------
void Mesh::clearScanPath(){
	// in between having clicked and drawn we can't play anything
    scanPath.clear();
    prevLump = -1;
}
//--------------------------------------------------------------
void Mesh::disconnectDraw(){
    prevLump = -1;
}
//--------------------------------------------------------------
int Mesh::inscribeScanPath(double ax, double ay){
    bool foundPath = false;
    int targetLump, intermediateLump; 
    int count = 0;
    TwoVector halfWay;
    // is this ridiculously complicated for no reason?
    
    targetLump = getNearestLump(ax,ay); // target is the one closest to touch point, prev is last point already on our path
    
    while(!foundPath && count < 10000){
        count++;
        if(prevLump == -1){ // (scanPath.howManyElements() == 0){ //
            //first lump
            foundPath = true;
            prevLump = targetLump;
            //cout << "first lump" << endl;
            lumps[targetLump].isScanPathStart = true;
            return targetLump;
        }
        // check if this lump connected to last lump by a spring
        Spring * connectedToPrev  = lumps[targetLump].checkConnectedTo(&lumps[prevLump]);
        
        // if not connectedToLast will be null
        if (!connectedToPrev){
            //cout << "setting half way" << endl;
            // set coords to 1/2 way and recurse
            double halfdiffx = ((ax-lumps[prevLump].position.x)*0.5);
            double halfdiffy = ((ay - lumps[prevLump].position.y)*0.5);
            if (halfdiffx < 0.0001 && halfdiffy < 0.0001){
                // not found nuffun
                //cout << "not found a path, quitting" << endl;
                return -1;
            }
            halfWay.setCoord( ax - halfdiffx , ay -  halfdiffy);
            
            
            intermediateLump = inscribeScanPath(halfWay.x,halfWay.y);
            if(intermediateLump == -1){
                //cout << "finding intermediate didn't work" << endl;
                return -1;
            }
            // now check that the found intermediate targetLump is connected to the original targetlump 
            Spring * connectedToNext  = lumps[targetLump].checkConnectedTo(&lumps[prevLump]);
            if(!connectedToNext){
                // set prevLump to the interm and try again
                prevLump = intermediateLump;
                targetLump = inscribeScanPath(ax,ay);
                if(targetLump == -1){
                    //cout << "joining up next one didn't work" << endl;
                    return -1;
                }
            }
        }else{
            //cout << "CONNECTED! adding an element" << endl;
            
            // hurrah, add the spring and lump to the scanpath in the right order...
            // get pointer to lump and spring
            
            scanPath.addElement(&lumps[targetLump], connectedToPrev);
            
            // indicate end
            lumps[prevLump].isScanPathEnd = false;
            lumps[targetLump].isScanPathEnd = true;
            // and set prev lump to that one
            prevLump = targetLump;
            foundPath = true;
            return targetLump; // 
        }
        
    }

    cout << "reached end of inscribe func" << endl;

    return -1;
}

// ---------------------------------------------------------------------------------------



void Mesh::hit(double dax, double day,int velocity, GlobalForces::excitationTypes aEType, GlobalForces::excitationShapes aEShape){
    // set filter to something low
    globalForces.homingAmt = 0.0;
    
    double radius = 0.1, distance; // size of lump
    double impactStrength = globalForces.excitationStrength*velocity/1000; // velocity as in note loudness
    double amt = 0.0;
    TwoVector diff;
    // get all the lumps and smack em
    int xamt = globalForces.exciteShapeX; // harmonic number of sine
    int yamt = globalForces.exciteShapeY;

    switch(aEShape){
    
        case GlobalForces::SINE:
            
            // applies a sine wave to all lumps, x and y
            for(int i = 0; i < lumps.size(); i++){
                if(!lumps[i].constrained){
                    lumps[i].position.y = lumps[i].zeroRefPos.y + impactStrength*sin(PI* (2*(xamt-1) + 1) * lumps[i].position.x);
                    lumps[i].position.x = lumps[i].zeroRefPos.x + impactStrength*sin(PI* (2*(yamt-1) + 1) * lumps[i].position.y);
                }
            }
            
            break;
        case GlobalForces::GAUSS:
            // temporarily not a gauss...
            for(int i = 0; i < lumps.size(); i++){
                diff.setCoord(lumps[i].position.x - dax, lumps[i].position.y - day);
                distance = diff.norm();
                if(distance < radius){
                    amt = impactStrength*(1 + cos(PI*distance/radius)); // a 2d hamming.
                    lumps[i].position.y += amt;
                    
                }     
            }
            break;
        case GlobalForces::NOISE:
            // 2D noise
            for(int i = 0; i < lumps.size(); i++){
                    lumps[i].position.x += impactStrength*rand();
                    lumps[i].position.y += impactStrength*rand();
                    
            }     
            break;
        default:
            break;
    }
    
    
}

void Mesh::spatialHarmonic(int aharm, int aharm2){
    double magdr,theta;
    TwoVector r, nr, centre, rhat, displacement;
    centre.setCoord(0.5, 0.5);
    // applies a sine wave to all lumps, x and y
    if(radial){
        // distance from centre is tweaked
        for(int i = 0; i < lumps.size(); i++){
            if(!lumps[i].constrained ){ //&& lumps[i].isInScanPath

                // lots of sins and tans - might be simplifiable?
                r = lumps[i].position - centre;
                rhat = r.unitDir();
                theta = atan2(r.y,r.x);
                magdr = sin(aharm*theta);
                
                // new r for lump:
                displacement = rhat * 0.1*globalForces.touchStrength*magdr;
                lumps[i].position = lumps[i].position + displacement;
                
            }
        }
    }else{
        for(int i = 0; i < lumps.size(); i++){
            if(!lumps[i].constrained){
                lumps[i].position.y = lumps[i].zeroRefPos.y + globalForces.touchStrength/256.0*sin(PI* (2*(aharm-1) + 1) * lumps[i].position.x);
                lumps[i].position.x = lumps[i].zeroRefPos.x + globalForces.touchStrength/256.0*sin(PI* (2*(aharm2-1) + 1) * lumps[i].position.y);
            }
        }
    }
}

void Mesh::damp(){
    // set friction and filters to something high
    //globalForces.avFilterAmt = 0.0;
    globalForces.homingAmt = 0.3;    
}
void Mesh::forceField(double dax,double day,double strength){
    
    // 
}
//--------------------------------------------------------------
double Mesh::getNextSample(){

	return 0.0;

}

//--------------------------------------------------------------
// VIRTUAL
//--------------------------------------------------------------
void Mesh::makeDefaultScanPath(){
	// make 
	cout << "!!!!Mesh base class makeDefaultScanPath\n";
}

//--------------------------------------------------------------
void Mesh::setLumpPositions(){
    // places lumps in the right positions 
	cout << "!!!!Mesh base class setLumpPositions\n";
}
//--------------------------------------------------------------
void Mesh::makeConnections(){
    // connects the lmps and springs. the spring needs to know which lumps are at each end
    // and lumps need to know which springs attached
    // calls connect method that does this
	cout << "!!!!Mesh base class make connections\n";
}
//--------------------------------------------------------------
void Mesh::makeComponents(int numLumps, int numSprings){
    // does not need to know what shape we're in to set up the right number of lumps and springs
	cout << "!!!!Mesh base class makeComponents\n";
    
    //lumps = new Lump[numLumps];
    lumps = vector<Lump>(numLumps);
	cout << "made " << numLumps << " lumps\n";
	
    // one by one
    springs = vector<Spring>(numSprings);
    
	cout << "made " << numSprings << " springs\n";
    
    // tell them what index they are (for connections)
    for(int i = 0; i<lumps.size(); i++){
        lumps[i].myIndexInMesh = i;
    }
    for(int i = 0; i<springs.size(); i++){
        springs[i].myIndexInMesh = i;
    }
    
    
}
//--------------------------------------------------------------
void Mesh::constrain(double ax, double ay, constrainMode aMode){
	cout << "!!!!Mesh base class constrain\n";
}
//--------------------------------------------------------------
/****************************************/
//		SquareMesh					*/
/****************************************/
//--------------------------------------------------------------
SquareMesh::SquareMesh(): Mesh() {
    // the default is called from derived classes
    radial = false;
	cout << "default constructor SQUARE mesh\n";
}
//---------------------------------------------------------------
SquareMesh::SquareMesh(int height, int width): Mesh() {
    // this one called when no derived class
	meshType = SQUARE_MESH;
	makeComponents(height, width);
	
	setLumpPositions();
	
	makeConnections();
	
	makeDefaultScanPath();
    
    constrain(0,0,CONSTRAIN_EDGES_XY);
    
    zeroRestLength();
    
    radial = false;
	
	cout << "constructing SQUARE mesh\n";
}
SquareMesh::~SquareMesh(){
	
	cout << "destructing SQUARE mesh\n";
}

//--------------------------------------------------------------
// make componets for square
void SquareMesh::makeComponents(int aheight, int awidth){
    
    
	height = aheight;
	width = awidth;
	numLumps = height * width;
    // horz             // vert             // criss cross
	numSprings = (width-1)*height + (height-1)*width + 2*(height-1)*(width-1);
	
    Mesh::makeComponents(numLumps, numSprings); // kinda wierd but works.
    
}

//--------------------------------------------------------------
void SquareMesh::setLumpPositions(){
    // numbering works line by line like text, analogue tv's etc
	double vspacing = 1.0/(height-1);
	double hspacing = 1.0/(width-1);
	int i =0;
	// set positions
	for(int row = 0; row < height; row++){ // go down column
		for(int col = 0;col < width; col++){ // go along row
            if(i >= lumps.size()){
                cout << " setLumpPosisions index error!\n";
            }
			lumps[i].setPosition(hspacing*col,vspacing*row);
			
			i++;
		}
	}
    
}
//--------------------------------------------------------------
void SquareMesh::makeConnections(){
	
	// could be cleverer in terms of numbering?
	int lumpnum = 0;
	int springnum = 0;
	// attach horizontal
	for ( int i=0;i<height;i++){
		for(int j=0;j<width-1;j++){
			connect(springnum,lumpnum);
			lumpnum++;
            if(springnum >= numSprings){
                cout << " makeConnections index error!\n";
            }
            if(lumpnum >= lumps.size()){
                cout << " makeConnections index error!\n";
            }
			connect(springnum,lumpnum);
			springnum++;
			
		}
		lumpnum++;
	}
    cout << "lumps attach horz: " << lumpnum-1 << ", spring " << springnum-1 << "\n";
	// attach vertical
	lumpnum = 0;
	for ( int i=0;i<width;i++){
		for(int j=0;j<height-1;j++){
			connect(springnum,lumpnum);
			lumpnum+=width;
            if(springnum >= numSprings){
                cout << " makeConnections index error!\n";
            }
            if(lumpnum >= lumps.size()){
                cout << " makeConnections index error!\n";
            }
			connect(springnum,lumpnum);
			springnum++;
			
		}
		lumpnum -= (width*(height-1)) - 1;
	}

}

//--------------------------------------------------------------
void SquareMesh::constrain(double ax, double ay, constrainMode aMode){
	
	TwoVector diff;
	int i = 0;
	
	// check if lump is within grab range
	switch (aMode) {
		case CONSTRAIN_GRAB_REGION:
			for(i = 0; i<lumps.size(); i++){
				
				diff.setCoord(lumps[i].position.x - ax, lumps[i].position.y - ay);
				
				if (diff.norm() < GRAB_RANGE){
                    if(i >= lumps.size()){
                        cout << "constrain index error\n";
                    }
					lumps[i].constrain();
				}
			}
			break;
		case CONSTRAIN_EDGES:
			i = 0;
			for(int row = 0; row < height; row++){ // go down column
				for(int col = 0;col < width; col++){ // go along row
					if(row == 0 || row == height-1 || col == 0 || col == width-1){
                        if(i >= lumps.size()){
                            cout << "constrain index error\n";
                        }
						lumps[i].constrain();
					}
					i++;
				}
			}
			break;
		case CONSTRAIN_EDGES_XY:
			i = 0;
			for(int row = 0; row < height; row++){ // go down column
				for(int col = 0;col < width; col++){ // go along row
					if(row == 0 || row == height-1){
                        if(i >= lumps.size()){
                            cout << "constrain index error\n";
                        }
						lumps[i].constrain(Lump::CONSTRAIN_Y);
					}else if( col == 0 || col == width-1){
                        if(i >= lumps.size()){
                            cout << "constrain index error\n";
                        }
                        lumps[i].constrain(Lump::CONSTRAIN_X);
                    }
					i++;
				}
			}
			break;
		case CONSTRAIN_CORNERS:
			i = 0;
			for(int row = 0; row < height; row++){ // go down column
				for(int col = 0;col < width; col++){ // go along row
					if(   (row == 0 && col == 0)
					   || (row == height-1 && col == 0)
					   || (row == 0 && col == width-1)
					   || (row == height-1 && col == width-1)){
                        if(i >= lumps.size()){
                            cout << "constrain index error\n";
                        }
						lumps[i].constrain();
					}
					i++;
				}
			}
			break;
			
		default:
			break;
	}
	
}

//--------------------------------------------------------------
void SquareMesh::makeDefaultScanPath(){
    if (height < 10 || width < 10) return; // not much point
    
    int vmarg = 5;
    int hmarg = 5;
    
    int lumpno = vmarg * width + hmarg; // top left corner
    int springno = vmarg * (width - 1) + hmarg;
    // do top horz
    for(int i = 0; i < width - 2*hmarg; i++){
        if(lumpno >= lumps.size()) cout << "makeDefaultScanPath index error\n";
        if(springno >= numSprings) cout << "makeDefaultScanPath index error\n";
        scanPath.addElement(&lumps[lumpno], &springs[springno]);
        lumpno++;
        springno++;
    }
    
    // do right vert, lumpno starts the same
    // all horz spr    // left marg rows      // top margin
    springno  = height*(width-1) + ((height-1) * (width - hmarg)) + vmarg;
    
    for(int i = 0; i < height - (2 * vmarg); i++){
        if(lumpno >= lumps.size()) cout << "makeDefaultScanPath index error\n";
        if(springno >= numSprings) cout << "makeDefaultScanPath index error\n";
        scanPath.addElement(&lumps[lumpno], &springs[springno]);
        springno++; // jump to next row
        lumpno += width;   // ditto
    }
    
    
    // do bottom horz right to left
    springno = (height - vmarg) * (width - 1) + (width - hmarg - 1);
    for(int i = 0; i < width - 2*hmarg; i++){
        if(lumpno >= lumps.size()) cout << "makeDefaultScanPath index error\n";
        if(springno >= numSprings) cout << "makeDefaultScanPath index error\n";
        scanPath.addElement(&lumps[lumpno], &springs[springno]);
        springno--; // jump to next row
        lumpno--;   // ditto
    }
    
    // all horz spr    // left marg rows    // top margin
    springno  = height*(width-1) + ((height-1) * hmarg) +  height - vmarg - 1;
    for(int i = 0; i < height - 2 * vmarg; i++){
        if(lumpno >= lumps.size()) cout << "makeDefaultScanPath index error\n";
        if(springno >= numSprings) cout << "makeDefaultScanPath index error\n";
        scanPath.addElement(&lumps[lumpno], &springs[springno]);
        springno--; // jump to next row
        lumpno -= width;   // ditto
    }
    
}
//--------------------------------------------------------------

/****************************************/
//		SquareCrossMesh					*/
/****************************************/
//--------------------------------------------------------------
SquareCrossMesh::SquareCrossMesh(int height, int width){
    
	meshType = SQUARE_CROSS_MESH;
	makeComponents(height, width);
	
	setLumpPositions();
	
	makeConnections();
	
	makeDefaultScanPath();
    
    constrain(0,0,CONSTRAIN_EDGES);
    
    zeroRestLength();
    
    radial = false;
	
	cout << "constructing SQUARECROSS mesh\n";
}
SquareCrossMesh::~SquareCrossMesh(){
	
	cout << "destructing SQUARECROSS mesh\n";
}

//--------------------------------------------------------------
void SquareCrossMesh::makeConnections(){
	
	// could be cleverer in terms of numbering?
	int lumpnum = 0;
	int springnum = 0;
	// attach horizontal 
	for ( int i=0;i<height;i++){
		for(int j=0;j<width-1;j++){
			connect(springnum,lumpnum);
			lumpnum++;
            if(springnum >= numSprings){
                cout << " makeConnections index error!\n";
            }
            if(lumpnum >= lumps.size()){
                cout << " makeConnections index error!\n";
            }
			connect(springnum,lumpnum);
			springnum++;
			
		}
		lumpnum++;
	}
    cout << "lumps attach horz: " << lumpnum-1 << ", spring " << springnum-1 << "\n";
	// attach vertical 
	lumpnum = 0;
	for ( int i=0;i<width;i++){
		for(int j=0;j<height-1;j++){
			connect(springnum,lumpnum);
			lumpnum+=width;
            if(springnum >= numSprings){
                cout << " makeConnections index error!\n";
            }
            if(lumpnum >= lumps.size()){
                cout << " makeConnections index error!\n";
            }
			connect(springnum,lumpnum);
			springnum++;
			
		}
		lumpnum -= (width*(height-1)) - 1;
	}	
	// attach diagonal
	lumpnum = 0;
	for ( int i=0;i<width-1;i++){
		for(int j=0;j<height-1;j++){
			connect(springnum,lumpnum);
			lumpnum+=(width);
            if(springnum >= numSprings){
                cout << " makeConnections index error!\n";
            }
            if(lumpnum >= lumps.size()){
                cout << " makeConnections index error!\n";
            }
			connect(springnum,lumpnum+1);
			springnum++;
			
		}
		lumpnum -= (width*(height-1)) - 1;
	}	
	// attach other diagonal
	lumpnum = 1;
	for ( int i=0;i<width-1;i++){
		for(int j=0;j<height-1;j++){
			connect(springnum,lumpnum);
			lumpnum+=(width);
            if(springnum >= numSprings){
                cout << " makeConnections index error!\n";
            }
            if(lumpnum >= lumps.size()){
                cout << " makeConnections index error!\n";
            }
			connect(springnum,lumpnum-1);
			springnum++;
			
		}
		lumpnum -= (width*(height-1)) - 1;
	}	
}

//--------------------------------------------------------------

//--------------------------------------------------------------


/****************************************/
//		SpiderMesh					*/
/****************************************/
SpiderMesh::SpiderMesh(int aNumSpokes,int aNumRings) : Mesh(){
    meshType = SPIDER_MESH;
    
	numSpokes = aNumSpokes;
	numRings = aNumRings;
    
    radial = true;
	
	makeComponents(numSpokes, numRings);
	
	setLumpPositions();
	
	makeConnections();

	makeDefaultScanPath();
    
    constrain(0.0,0.0,CONSTRAIN_EDGES);
    constrain(0.0,0.0,CONSTRAIN_CORNERS);
	
	cout << "constructed SPIDER mesh\n";
	
	
	
}
//--------------------------------------------------------------
void SpiderMesh::makeComponents(int aDimension1, int aDimension2){
    // make componets creates the correct number of lumps and springs according to dimensions and mesh type
	cout << "!!!spider makeComponents\n";
	numLumps = numSpokes * numRings + 1; // +1 cos one in the middle
	numSprings = 2 * numSpokes * numRings;
	
	//lumps = new Lump[numLumps];
	lumps = vector<Lump>(numLumps);
    cout << "made " << numLumps << " lumps\n";
	springs = vector<Spring>(numSprings);
    
	cout << "made " << numSprings << " springs\n";	
	
				
}
//--------------------------------------------------------------
void SpiderMesh::setLumpPositions(){
	cout << "!spider setLumpPositions\n";
	// here we're assuming that zero spoke is to right
	// work out sines / cosines for each spoke
	int l = 1;
	double hyplen,xpos,ypos;
	
	double * cosines = new double[numSpokes];
	double * sines = new double[numSpokes];
	double angle = 2 * PI / numSpokes;
	
	double ringSpacing = 1.0/(2*numRings);
	
	for(int spoke = 0; spoke < numSpokes; spoke++){
		cosines[spoke] = cos(angle*spoke);
		sines[spoke] = sin(angle*spoke);
	}
		
	lumps[0].setPosition(0.5,0.5);

	for(int ring = 0; ring < numRings; ring++){
		for(int spoke = 0; spoke < numSpokes; spoke++){
			hyplen = ringSpacing * (ring+1);
			xpos = hyplen * cosines[spoke] + 0.5;
			ypos = hyplen*sines[spoke] + 0.5;
			lumps[l].setPosition(xpos,ypos);
			l++;
			
		}
	}
	delete [] cosines;
	delete [] sines;
			
}
//--------------------------------------------------------------
void SpiderMesh::makeConnections(){
	// TODO needs to be rewritten to automatically 
	// add in lumps and springs AS we're connecting
	//using std vector
	// this is dumb
	cout << "!!!spider make connections\n";

	// UNLIKE a spider we're going to make rings first, to make indexing the scan path easier...

	for(int ring = 0; ring < numRings; ring++){
		for(int spoke = 0; spoke < numSpokes-1; spoke++){
			//		spring ,				lump
			connect(numSpokes*ring + spoke, ring*numSpokes + spoke + 1);
			connect(numSpokes*ring + spoke, ring*numSpokes + spoke + 2);
		}
		// remember the last one on the ring goes back to first
		//		spring ,						lump
		connect(numSpokes*ring + numSpokes - 1 , ring*numSpokes + numSpokes);
		connect(numSpokes*ring + numSpokes - 1,  ring*numSpokes + 1);
	}	
	// at which point we have used numSpokes*numRings springs
	int used = numSpokes*numRings;
	// first ring is different cos it connects to central point
	for(int spoke = 0; spoke < numSpokes; spoke++){
		connect(used,0); // inner radial on center
		connect(used,spoke+1);  // outer radial
		springs[used].setRestLength(0.0); // need tension on spokes
		used++;
	}
	// now do the rest of the radial spokes
	for(int ring = 0; ring < numRings-1; ring++){
		for(int spoke = 0; spoke < numSpokes; spoke++){
			connect(used,ring*numSpokes+1+spoke); // 
			connect(used,(ring+1)*numSpokes+1+spoke);  // going to next ring out
			springs[used].setRestLength(0.0);
			used++;
		}
	}
}

//--------------------------------------------------------------
void SpiderMesh::constrain(double ax, double ay, constrainMode aMode){
	cout << "!!spider constrain\n";
	
	TwoVector diff;
	int i = 0;
	
	// check if lump is within grab range
	switch (aMode) {
		case CONSTRAIN_GRAB_REGION:
			for(i = 0; i<lumps.size(); i++){
				
				diff.setCoord(lumps[i].position.x - ax, lumps[i].position.y - ay);
				
				if (diff.norm() < GRAB_RANGE){
					lumps[i].constrain();
				}
			}
			break;
		case CONSTRAIN_EDGES:
		{
			// constrain outer ring
			int startLump = numSpokes*(numRings-1) + 1;
			for(int l = startLump; l < startLump + numSpokes; l++){
				lumps[l].constrain();
			}
	
			break;
		}
		case CONSTRAIN_CORNERS:
			// er... constrain the centre
			lumps[0].constrain();
			
			break;
			
		default:
			break;
	}
}

//--------------------------------------------------------------

void SpiderMesh::makeDefaultScanPath(){
	
	cout << "!!spider makeDefaultScanPath\n";
    
    /*
    // add a all inner rings to the path
    
    
    int ringNo = ceil(numRings/2.0);
    int springno = 0, lumpno = 0;
    for(int i = 0; i < numSpokes; i++){
        lumpno = numSpokes*ringNo + 1 + i;
        springno = numSpokes*ringNo + i;
        scanPath.addElement(&lumps[lumpno], &springs[springno]);
    }
    cout << "!!spider scan path created on ring " << ringNo << "\n";
     */
    
    // add a spoke to the path

    int springno = 0, lumpno = 0;
    
    int springsinrings = numSpokes*numRings;
    for(int i = 0; i < numRings-1; i++){
        lumpno = numSpokes*i; // zero, numspokes etc
        springno = springsinrings + numSpokes*i -1; // starts from total rings, increments by numspokes etc
        scanPath.addElement(&lumps[lumpno], &springs[springno]);
    }
    
    for(int i = 0; i < numRings-1; i++){
        lumpno = numSpokes*i + 1; // zero, numspokes etc
        springno = springsinrings + numSpokes*i; // starts from total rings, increments by numspokes etc
        scanPath.addElement(&lumps[lumpno], &springs[springno]);
    }
    /*
    for(int i = numRings-2; i >= 0 ; i--){
        lumpno = numSpokes*i; // zero, numspokes etc
        springno = springsinrings + numSpokes*i; // starts from total rings, increments by numspokes etc
        scanPath.addElement(&lumps[lumpno], &springs[springno]);
    }
     */

 }


//--------------------------------------------------------------
/****************************************/
//		Holed Spider    Mesh            */
/****************************************/
//--------------------------------------------------------------
HoledSpiderMesh::HoledSpiderMesh(int aNumSpokes,int aNumRings) : Mesh(){
    meshType = HOLED_SPIDER_MESH;
	numSpokes = aNumSpokes;
	numRings = aNumRings;
    radial = true;
	
	makeComponents(numSpokes, numRings);
	
	setLumpPositions();
	
	makeConnections();
    
	makeDefaultScanPath();
    
    constrain(0.0,0.0,CONSTRAIN_EDGES);
    constrain(0.0,0.0,CONSTRAIN_CORNERS);
	
	cout << "constructed SPIDER mesh\n";
}

void HoledSpiderMesh::makeComponents(int aDimension1, int aDimension2){
    // make componets creates the correct number of lumps and springs according to dimensions and mesh type
	cout << "HOLED spider makeComponents\n";
	numLumps = numSpokes * numRings; 
	numSprings = numSpokes * numRings + numSpokes * (numRings - 1);
	
	lumps = vector<Lump>(numLumps);
    cout << "made " << numLumps << " lumps\n";
	springs = vector<Spring>(numSprings);
	cout << "made " << numSprings << " springs\n";	
	
    
}
//--------------------------------------------------------------
void HoledSpiderMesh::setLumpPositions(){
	cout << "HOLED spider setLumpPositions\n";
	// here we're assuming that zero spoke is to right
	// work out sines / cosines for each spoke
	int l = 0;
	double hyplen,xpos,ypos;
	
	double * cosines = new double[numSpokes];
	double * sines = new double[numSpokes];
	double angle = 2 * PI / numSpokes;
	
	double ringSpacing = 1.0/(2*numRings);
	
	for(int spoke = 0; spoke < numSpokes; spoke++){
		cosines[spoke] = cos(angle*spoke);
		sines[spoke] = sin(angle*spoke);
	}
    
	for(int ring = 0; ring < numRings; ring++){
		for(int spoke = 0; spoke < numSpokes; spoke++){
			hyplen = ringSpacing * (ring+1);
			xpos = hyplen * cosines[spoke] + 0.5;
			ypos = hyplen*sines[spoke] + 0.5;
			lumps[l].setPosition(xpos,ypos);
			l++;
			
		}
	}
	delete [] cosines;
	delete [] sines;
    
}
//--------------------------------------------------------------
void HoledSpiderMesh::makeConnections(){
	// TODO needs to be rewritten to automatically 
	// add in lumps and springs AS we're connecting
	//using std vector
	// this is dumb
	cout << "HOLED spider make connections\n";
    
	// UNLIKE a spider we're going to make rings first, to make indexing the scan path easier...
    
	for(int ring = 0; ring < numRings; ring++){
		for(int spoke = 0; spoke < numSpokes-1; spoke++){
			//		spring ,				lump
			connect(numSpokes*ring + spoke, ring*numSpokes + spoke + 0);
			connect(numSpokes*ring + spoke, ring*numSpokes + spoke + 1);
		}
		// remember the last one on the ring goes back to first
		//		spring ,						lump
		connect(numSpokes*ring + numSpokes - 1 , ring*numSpokes + numSpokes - 1);
		connect(numSpokes*ring + numSpokes - 1,  ring*numSpokes);
	}	
	// at which point we have used numSpokes*numRings springs
	int used = numSpokes*numRings;
	// now do the rest of the radial spokes
	for(int ring = 0; ring < numRings-1; ring++){
		for(int spoke = 0; spoke < numSpokes; spoke++){
			connect(used,ring*numSpokes+spoke); // 
			connect(used,(ring+1)*numSpokes+spoke);  // going to next ring out
			springs[used].setRestLength(0.0);
			used++;
		}
	}
}

//--------------------------------------------------------------
void HoledSpiderMesh::constrain(double ax, double ay, constrainMode aMode){
	cout << "HOLED spider constrain\n";
	
	TwoVector diff;
	int i = 0;
	int startLump = 0;
	// check if lump is within grab range
	switch (aMode) {
		case CONSTRAIN_GRAB_REGION:
			for(i = 0; i<lumps.size(); i++){
				
				diff.setCoord(lumps[i].position.x - ax, lumps[i].position.y - ay);
				
				if (diff.norm() < GRAB_RANGE){
					lumps[i].constrain();
				}
			}
			break;
		case CONSTRAIN_EDGES:
		{
			// constrain outer ring
			startLump = numSpokes*(numRings-1);
			for(int l = startLump; l < startLump + numSpokes; l++){
				lumps[l].constrain();
			}
            
			break;
		}
		case CONSTRAIN_CORNERS:
			// constrain inner ring
			
			for(int l = startLump; l < startLump + numSpokes; l++){
				lumps[l].constrain();
			}
            
			break;
			
		default:
			break;
	}
}
//--------------------------------------------------------------

void HoledSpiderMesh::makeDefaultScanPath(){
	
	cout << "HOLED spider makeDefaultScanPath\n";
    
     // add a ring to the path
     int ringNo = floor(numRings/2.0);
     int springno = 0, lumpno = 0;
     for(int i = 0; i < numSpokes; i++){
         lumpno = numSpokes*ringNo + i;
         springno = numSpokes*ringNo + i;
         
         scanPath.addElement(&lumps[lumpno], &springs[springno]);
     }
     cout << "HOLED spider scan path created on ring " << ringNo << "\n";
     
    // add a spoke to the path
    /*
    int springno = 0, lumpno = 0;
    
    int springsinrings = numSpokes*numRings;
    for(int i = 0; i < numRings-1; i++){
        lumpno = numSpokes*i; // zero, numspokes etc
        springno = springsinrings + numSpokes*i; // starts from total rings, increments by numspokes etc
        scanPath.addElement(&lumps[lumpno], &springs[springno]);
    } 
     */
}

//--------------------------------------------------------------
/****************************************/
//		Holed Spider Cross   Mesh            */
/****************************************/
//--------------------------------------------------------------
SpiderCrossMesh::SpiderCrossMesh(int aNumSpokes,int aNumRings) : Mesh(){
    meshType = SPIDER_CROSS_MESH;
	numSpokes = aNumSpokes;
	numRings = aNumRings;
    radial = true;
	
	makeComponents(numSpokes, numRings);
	
	setLumpPositions();
	
	makeConnections();
    
	makeDefaultScanPath();
    
    constrain(0.0,0.0,CONSTRAIN_EDGES);
    constrain(0.0,0.0,CONSTRAIN_CORNERS);
	
	cout << "constructed SpiderCrossMesh mesh\n";
}

void SpiderCrossMesh::makeComponents(int aDimension1, int aDimension2){
    // make componets creates the correct number of lumps and springs according to dimensions and mesh type
	cout << "SpiderCrossMesh spider makeComponents\n";
	numLumps = numSpokes * numRings; 
                // rings                // spokes  & diags
	numSprings = numSpokes * numRings + 3 * numSpokes * (numRings - 1);
	
	Mesh::makeComponents(numLumps, numSprings);
	
    
}
//--------------------------------------------------------------
void SpiderCrossMesh::setLumpPositions(){
	cout << "SpiderCrossMesh spider setLumpPositions\n";
	// here we're assuming that zero spoke is to right
	// work out sines / cosines for each spoke
	int l = 0;
	double hyplen,xpos,ypos;
	
	double * cosines = new double[numSpokes];
	double * sines = new double[numSpokes];
	double angle = 2 * PI / numSpokes;
	
	double ringSpacing = 1.0/(2*numRings);
	
	for(int spoke = 0; spoke < numSpokes; spoke++){
		cosines[spoke] = cos(angle*spoke);
		sines[spoke] = sin(angle*spoke);
	}
    
	for(int ring = 0; ring < numRings; ring++){
		for(int spoke = 0; spoke < numSpokes; spoke++){
			hyplen = ringSpacing * (ring+1);
			xpos = hyplen * cosines[spoke] + 0.5;
			ypos = hyplen*sines[spoke] + 0.5;
			lumps[l].setPosition(xpos,ypos);
			l++;
			
		}
	}
	delete [] cosines;
	delete [] sines;
    
}
//--------------------------------------------------------------
void SpiderCrossMesh::makeConnections(){

	cout << "SpiderCrossMesh spider make connections\n";
    
	// UNLIKE a spider we're going to make rings first, to make indexing the scan path easier...
    
	for(int ring = 0; ring < numRings; ring++){
		for(int spoke = 0; spoke < numSpokes-1; spoke++){
			//		spring ,				lump
			connect(numSpokes*ring + spoke, ring*numSpokes + spoke + 0);
			connect(numSpokes*ring + spoke, ring*numSpokes + spoke + 1);
		}
		// remember the last one on the ring goes back to first
		//		spring ,						lump
		connect(numSpokes*ring + numSpokes - 1 , ring*numSpokes + numSpokes - 1);
		connect(numSpokes*ring + numSpokes - 1,  ring*numSpokes);
	}	
	// at which point we have used numSpokes*numRings springs
	int used = numSpokes*numRings;
    
	// now do radial spokes
	for(int ring = 0; ring < numRings-1; ring++){
		for(int spoke = 0; spoke < numSpokes; spoke++){
			connect(used,ring*numSpokes+spoke); // 
			connect(used,(ring+1)*numSpokes+spoke);  // going to next ring out
			springs[used].setRestLength(0.0);
			used++;
		}
	}
    // now do diag /
    for(int ring = 0; ring < numRings-1; ring++){
		for(int spoke = 0; spoke < numSpokes-1; spoke++){
			connect(used,ring*numSpokes+spoke,(ring+1)*numSpokes+spoke+1); // 
			springs[used].setRestLength(0.0);
			used++;
		}
        connect(used,ring*numSpokes+numSpokes-1,(ring+1)*numSpokes); // wrap to first 
        springs[used].setRestLength(0.0);
        used++;
	}
    
    // now do \ diag
    for(int ring = 0; ring < numRings-1; ring++){
        connect(used,ring*numSpokes,(ring+1)*numSpokes+numSpokes-1); // wrap first to last 
        springs[used].setRestLength(0.0);
        used++;
		for(int spoke = 1; spoke < numSpokes; spoke++){
			connect(used,ring*numSpokes+spoke,(ring+1)*numSpokes+spoke-1); // 
			springs[used].setRestLength(0.0);
			used++;
		}

	}
    cout << "used: " << used << " numSprings: " << numSprings << endl;
    
}

//--------------------------------------------------------------
void SpiderCrossMesh::constrain(double ax, double ay, constrainMode aMode){
	cout << "SpiderCrossMesh spider constrain\n";
	
	TwoVector diff;
	int i = 0;
	int startLump = 0;
	// check if lump is within grab range
	switch (aMode) {
		case CONSTRAIN_GRAB_REGION:
			for(i = 0; i<lumps.size(); i++){
				
				diff.setCoord(lumps[i].position.x - ax, lumps[i].position.y - ay);
				
				if (diff.norm() < GRAB_RANGE){
					lumps[i].constrain();
				}
			}
			break;
		case CONSTRAIN_EDGES:
		{
			// constrain outer ring
			startLump = numSpokes*(numRings-1);
			for(int l = startLump; l < startLump + numSpokes; l++){
				lumps[l].constrain();
			}
            
			break;
		}
		case CONSTRAIN_CORNERS:
			// constrain inner ring
			
			for(int l = startLump; l < startLump + numSpokes; l++){
				lumps[l].constrain();
			}
            
			break;
			
		default:
			break;
	}
}
//--------------------------------------------------------------

void SpiderCrossMesh::makeDefaultScanPath(){
	
	cout << "SpiderCrossMesh spider makeDefaultScanPath\n";
    
    // add a ring to the path
    int ringNo = floor(numRings/2.0);
    int springno = 0, lumpno = 0;
    for(int i = 0; i < numSpokes; i++){
        lumpno = numSpokes*ringNo + i;
        springno = numSpokes*ringNo + i;
        scanPath.addElement(&lumps[lumpno], &springs[springno]);
    }
    cout << "SpiderCrossMesh spider scan path created on ring " << ringNo << "\n";
    
    // add a spoke to the path
    /*
     int springno = 0, lumpno = 0;
     
     int springsinrings = numSpokes*numRings;
     for(int i = 0; i < numRings-1; i++){
     lumpno = numSpokes*i; // zero, numspokes etc
     springno = springsinrings + numSpokes*i; // starts from total rings, increments by numspokes etc
     scanPath.addElement(&lumps[lumpno], &springs[springno]);
     } 
     */
}

/****************************************/
//		Line    Mesh					*/
/****************************************/

LineMesh::LineMesh(int aNumSegments) : Mesh(){
    meshType = LINE_MESH;
	numSegments = aNumSegments;
    radial = false;
    
	makeComponents(aNumSegments, 0);
	
	setLumpPositions();
	
	makeConnections();
    
	makeDefaultScanPath();
    
    
    
    setPropagationSpeed(0.6);
	
    for(int i = 0; i < lumps.size(); i++){
        lumps[i].constrain(Lump::CONSTRAIN_X);
    }
    
    constrain(0.0,0.0,CONSTRAIN_EDGES);
	cout << "constructed LINE mesh\n";
	
}
//--------------------------------------------------------------
void LineMesh::makeComponents(int aDimension1, int aDimension2){
    // make componets creates the correct number of lumps and springs according to dimensions and mesh type
	cout << "Line class makeComponents\n";
    // 
    numLumps = numSegments+1;
    numSprings = numSegments;
    Mesh::makeComponents(numLumps, numSprings);
    
}
//--------------------------------------------------------------
void LineMesh::setLumpPositions(){
	cout << "LineMesh class setLumpPositions\n";
    // 
    double hspacing = (1.0 - 0.05)/numSegments;
    for(int i = 0; i < lumps.size(); i++){
        lumps[i].setPosition(0.025+hspacing*i, 0.5);
    }
}
//--------------------------------------------------------------
void LineMesh::makeConnections(){
    // connects the lmps and springs. the spring needs to know which lumps are at each end
    // and lumps need to know which springs attached
    // calls connect method that does this
	cout << "LineMesh class make connections\n";
    for(int i = 0; i < numSegments; i++){
        connect(i,i);
        connect(i,i+1);
    }

}

//--------------------------------------------------------------
void LineMesh::constrain(double ax, double ay, constrainMode aMode){
	cout << "LineMesh class constrain\n";
    if (aMode == CONSTRAIN_EDGES || aMode == CONSTRAIN_CORNERS){
        lumps[0].constrain();
        lumps[lumps.size()-1].constrain();
    }else if (aMode == CONSTRAIN_GRAB_REGION){
        TwoVector diff;
        for(int i = 0; i<lumps.size(); i++){
            
            diff.setCoord(lumps[i].position.x - ax, lumps[i].position.y - ay);
            
            if (diff.norm() < GRAB_RANGE){
                lumps[i].constrain();
            }
        }
    }
}
//--------------------------------------------------------------
//--------------------------------------------------------------
void LineMesh::makeDefaultScanPath(){
	
	cout << "LineMesh class makeDefaultScanPath\n";
    for(int i = 1; i < numSegments-1; i++){
        scanPath.addElement(&lumps[i], &springs[i]);
    }
}
//--------------------------------------------------------------
/****************************************/
//		GROUNDED LINE  Mesh					*/
/****************************************/

GroundedLineMesh::GroundedLineMesh(int aNumSegments) : Mesh(){
    meshType = GROUNDED_LINE_MESH;
	numSegments = aNumSegments;
    radial = false;
	makeComponents(aNumSegments, 0);
	
	setLumpPositions();
	
	makeConnections();
    
	makeDefaultScanPath();
    
    constrain(0.0,0.0,CONSTRAIN_ALL_X);
    
    setPropagationSpeed(0.6);
	
	cout << "constructed GROUNDED LINE mesh\n";
	
}
//--------------------------------------------------------------
void GroundedLineMesh::makeComponents(int aDimension1, int aDimension2){
    // make componets creates the correct number of lumps and springs according to dimensions and mesh type
    
    // THIS IS CRAP should be able to just create new ones and attach them...?
	cout << "Line class makeComponents\n";
    // 
    numLumps = (numSegments+1)*2;
    numSprings = numSegments*2;
    Mesh::makeComponents(numLumps, numSprings);
    
}
//--------------------------------------------------------------
void GroundedLineMesh::setLumpPositions(){
	cout << "LineMesh class setLumpPositions\n";
    // 

    double hspacing = (1.0 - 0.05)/numSegments;
    for(int i = 0; i < (numSegments+1); i++){
        // moving ones
        lumps[i].setPosition(0.025+hspacing*i, 0.5);
        //grounded ones
        lumps[i + numSegments+1].setPosition(0.025+hspacing*i,0.5*cos(i*hspacing*PI) + 0.5 );
    }
    


}
//--------------------------------------------------------------
void GroundedLineMesh::makeConnections(){
    // connects the lmps and springs. the spring needs to know which lumps are at each end
    // and lumps need to know which springs attached
    // calls connect method that does this
	cout << "LineMesh class make connections\n";
    for(int i = 0; i < numSegments; i++){
        connect(i,i,i+1);
        connect(i + numSegments,i , i + numSegments+1);

    }

    setRestLength();
}

//--------------------------------------------------------------
void GroundedLineMesh::constrain(double ax, double ay, constrainMode aMode){
	cout << "LineMesh class constrain\n";
    if (aMode == CONSTRAIN_EDGES || aMode == CONSTRAIN_CORNERS){
        lumps[0].constrain();
        lumps[lumps.size()-1].constrain();
        
        for(int i = numSegments; i < lumps.size(); i++){
            lumps[i].constrain();
        }
    }

    if (aMode == CONSTRAIN_GRAB_REGION){
        TwoVector diff;
        for(int i = 0; i<lumps.size(); i++){
            
            diff.setCoord(lumps[i].position.x - ax, lumps[i].position.y - ay);
            
            if (diff.norm() < GRAB_RANGE){
                lumps[i].constrain();
            }
        }
    }
    if (aMode == CONSTRAIN_ALL_X){

        
        for(int i = 0; i<numSegments; i++){

            lumps[i].constrain(Lump::CONSTRAIN_X);
        }
        for(int i = numSegments; i < lumps.size(); i++){
            lumps[i].constrain();
        }
        lumps[0].constrain();
        lumps[lumps.size()-1].constrain();
    }
        
}
//--------------------------------------------------------------
//--------------------------------------------------------------
void GroundedLineMesh::makeDefaultScanPath(){
	
	cout << "LineMesh class makeDefaultScanPath\n";
    for(int i = 0; i < numSegments; i++){
        scanPath.addElement(&lumps[i], &springs[i]);
    }
}
//--------------------------------------------------------------

/****************************************/
//		DROPLET    Mesh					*/
/****************************************/
// more of a balloon really - filled with compressible gas
DropletMesh::DropletMesh(int aNumSegments) : Mesh(){
    meshType = DROPLET_MESH;
    restArea = 1.0;
    radial = true;
	numSegments = aNumSegments;
    
	makeComponents(aNumSegments, 0);
	
	setLumpPositions();
	
	makeConnections();
    
	makeDefaultScanPath();
    
    //constrain(0.0,0.0,CONSTRAIN_EDGES);
    
    setPropagationSpeed(0.8);
    
    zeroRestLength();
    globalForces.pressureAmt = 0.4;
	
	cout << "constructed DropletMesh mesh\n";
	
}
//--------------------------------------------------------------
void DropletMesh::makeComponents(int aDimension1, int aDimension2){
    // make componets creates the correct number of lumps and springs according to dimensions and mesh type
	cout << "Line class makeComponents\n";
    // 
    numLumps = numSegments;
    numSprings = numSegments;
    Mesh::makeComponents(numLumps, numSprings);
}
//--------------------------------------------------------------
void DropletMesh::setLumpPositions(){
	cout << "DropletMesh class setLumpPositions\n";
    // make a circle centred around 0.5,0.5 radius 0.4
	int l = 0;
	double hyplen,xpos,ypos;
	
	double * cosines = new double[numSegments];
	double * sines = new double[numSegments];
	double angle = 2 * PI / numSegments;
    
	for(int spoke = 0; spoke < numSegments; spoke++){
		cosines[spoke] = cos(angle*spoke);
		sines[spoke] = sin(angle*spoke);
	}
    
    hyplen = 0.2; // aka radius
    
    // 
	//lumps[0].setPosition(0.5,0.5);
    
    for(int spoke = 0; spoke < numSegments; spoke++){
        
        xpos = hyplen * cosines[spoke] + 0.5;
        ypos = hyplen*sines[spoke] + 0.5;
        lumps[l].setPosition(xpos,ypos);
        l++;
        
    }
    calculateCentre();
    restArea = calculateArea() * 3; // this is like the amount of air in there originally, inflate a bit !
    
	delete [] cosines;
	delete [] sines;
}
//--------------------------------------------------------------
void DropletMesh::makeConnections(){
    // connects the lmps and springs. the spring needs to know which lumps are at each end
    // and lumps need to know which springs attached
    // calls connect method that does this
	cout << "DropletMesh class make connections\n";
    for(int i = 0; i < numSegments-1; i++){
        connect(i,i,i+1);
    }
    // finally connect end spring to start lump
    connect(numSegments-1,numSegments-1);
    connect(numSegments-1,0);
}

//--------------------------------------------------------------
void DropletMesh::constrain(double ax, double ay, constrainMode aMode){
	cout << "DropletMesh class constrain\n";
    if (aMode == CONSTRAIN_EDGES || aMode == CONSTRAIN_CORNERS){
        lumps[0].constrain();
        lumps[lumps.size()-1].constrain();
    }  else  if (aMode == CONSTRAIN_GRAB_REGION){
        TwoVector diff;
        for(int i = 0; i<lumps.size(); i++){
            
            diff.setCoord(lumps[i].position.x - ax, lumps[i].position.y - ay);
            
            if (diff.norm() < GRAB_RANGE){
                lumps[i].constrain();
            }
        }
    }
}
//--------------------------------------------------------------
//--------------------------------------------------------------
void DropletMesh::makeDefaultScanPath(){
	
	cout << "DropletMesh class makeDefaultScanPath\n";
    for(int i = 0; i < numSegments; i++){
        scanPath.addElement(&lumps[i], &springs[i]);
    }
    cout << "DropletMesh class makeDefaultScanPath\n";
}
//--------------------------------------------------------------
double DropletMesh::calculateArea(){
    
    // how do we calculate area?? answer: average all radii !
    
    // calc average radius magnitude
    double radiusMag = 0.0, avRadiusMag = 0.0;
    for(int i = 0; i < lumps.size(); i++){
        TwoVector radius;
        radius.setCoord(centre.x - lumps[i].position.x, centre.y - lumps[i].position.y );
        radiusMag = radius.norm();
        avRadiusMag += radiusMag;
    }
    avRadiusMag /= lumps.size();
    
    // calc area
    double area = 2 * PI * avRadiusMag*avRadiusMag;
    
    return area;
    
}

double DropletMesh::calculatePressure(){
    // work out pressure on the springs. 
    // this will be proportional to the difference in the rest area to the current area
    //this will exert a force proportional to length
    
    double da = restArea - calculateArea();
    // if current area is smaller this will create positive pressure
    
    globalForces.setPressure(da);
    return globalForces.pressure;
    
}
//--------------------------------------------------------------
void DropletMesh::update(){

    calculateCentre();
    calculatePressure();
    Mesh::update(); // schweet
    
}
//--------------------------------------------------------------

/****************************************/
//		TriangleMesh					*/
/****************************************/
//--------------------------------------------------------------
TriangleMesh::TriangleMesh(int height, int width): Mesh() {
    meshType = TRIANGLE_MESH;
	radial = false;
	makeComponents(height, width);
	
	setLumpPositions();
	
	makeConnections();
	
	makeDefaultScanPath();
    
    constrain(0,0,CONSTRAIN_EDGES);
    
    zeroRestLength();
    
	
	cout << "constructing TRIANGLE mesh\n";
}
//--------------------------------------------------------------
// make componets for tri
void TriangleMesh::makeComponents(int aheight, int awidth){
	height = aheight;
	width = awidth;
	numLumps = height * width;
                /* horz   ---            \ diag                  / diag              */
	numSprings = (width-1) * (height-1) +  width * (height - 1) + (width-1) * (height - 1);
	
	Mesh::makeComponents(numLumps, numSprings);
}

//--------------------------------------------------------------
void TriangleMesh::setLumpPositions(){
	double vspacing = 1.0/(height-1);
	double hspacing = 1.0/(width - 0.5);
	int i =0;
	// set positions
	for(int row = 0; row < height; row++){ // go down column
		for(int col = 0;col < width; col++){ // go along row
            // if even row start from wall
            if(row%2){ // odd
                lumps[i].setPosition(hspacing*col + 0.5*hspacing,vspacing*row);
			}else{ // even
                lumps[i].setPosition(hspacing*col,vspacing*row);
            }
			i++;
		}
	}
}
//--------------------------------------------------------------
void TriangleMesh::makeConnections(){
	
	// could be cleverer in terms of numbering?
	int lumpnum = 0;
	int springnum = 0;
    
	// attach horizontal a
	for ( int row=1;row< height;row++){
		for(int col=0;col<width-1;col++){
			connect(springnum,lumpnum,lumpnum+1);
            lumpnum++;
            springnum++;
		}
        lumpnum++;
	}
	
    /* now do /\/\/\/\/\/\/\ */
	lumpnum = 0;
	for ( int row=0;row<height-1;row++){
        
        if(row%2 == 0){ 
            // even ie 0
            /* start with \  diag */
            for(int col=0;col<width-1;col++){
                /* \ */
                connect(springnum++, lumpnum, lumpnum+ width);
                lumpnum++;
                /* / */
                connect(springnum++, lumpnum, lumpnum+(width-1));
                
            }
            connect(springnum++, lumpnum, lumpnum+width);
            lumpnum++;
            
        }else{         // odd
            for(int col=0;col<width-1;col++){
                /* / */
                connect(springnum++, lumpnum, lumpnum+ width);
                /* \ */
                connect(springnum++, lumpnum, lumpnum+(width+1));
                lumpnum++;
            }
            // last r to l
            /* / */
            connect(springnum++, lumpnum, lumpnum+(width));
            lumpnum++;
            
        }
    }
    
}	



//--------------------------------------------------------------
void TriangleMesh::constrain(double ax, double ay, constrainMode aMode){
	// exactly the same as square
	TwoVector diff;
	int i = 0;
	
	// check if lump is within grab range
	switch (aMode) {
		case CONSTRAIN_GRAB_REGION:
			for(i = 0; i<lumps.size(); i++){
				
				diff.setCoord(lumps[i].position.x - ax, lumps[i].position.y - ay);
				
				if (diff.norm() < GRAB_RANGE){
					lumps[i].constrain();
				}
			}
			break;
		case CONSTRAIN_EDGES:
			i = 0;
			for(int row = 0; row < height; row++){ // go down column
				for(int col = 0;col < width; col++){ // go along row
					if(row == 0 || row == height-1 || col == 0 || col == width-1){
						lumps[i].constrain();
					}
					i++;
				}
			}	
			break;
		case CONSTRAIN_CORNERS:
			i = 0;
			for(int row = 0; row < height; row++){ // go down column
				for(int col = 0;col < width; col++){ // go along row
					if(   (row == 0 && col == 0)
					   || (row == height-1 && col == 0) 
					   || (row == 0 && col == width-1) 
					   || (row == height-1 && col == width-1)){
						lumps[i].constrain();
					}
					i++;
				}
			}
			break;
			
		default:
			break;
	}
	
}
//--------------------------------------------------------------
//--------------------------------------------------------------
void TriangleMesh::makeDefaultScanPath(){

    int vmarg = 4;
    int hmarg = 4;
    
    int lumpno = vmarg * width + hmarg; // top left corner
    int springno = vmarg * (width - 1) + hmarg;
    // do top horz
    for(int i = 0; i < width - 2*hmarg; i++){
        scanPath.addElement(&lumps[lumpno], &springs[springno]);
        lumpno++;
        springno++;
    }

    
}