Mercurial > hg > wabletios
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++; } }