# HG changeset patch # User Robert Tubb # Date 1353330042 0 # Node ID c667dfe12d47eec8f334dbdf612e0371618bfee8 OK. Ther real deal. diff -r 000000000000 -r c667dfe12d47 2dvector.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/2dvector.h Mon Nov 19 13:00:42 2012 +0000 @@ -0,0 +1,33 @@ +/* + * 2dvector.h + * simplespring + * + * Created by Robert Tubb on 01/06/2011. + * Copyright 2011 __MyCompanyName__. All rights reserved. + * + */ +#ifndef _2DVECTORH +#define _2DVECTORH + +class TwoVector{ +public: + double x, y; + TwoVector(); + TwoVector(double ax, double ay); + +// public methods + double norm(); + void setCoord(double ax, double ay); + TwoVector minus(TwoVector otherPoint); + TwoVector operator-(TwoVector otherPoint); + TwoVector operator+(TwoVector otherPoint); + + TwoVector operator*(TwoVector otherPoint); + TwoVector operator*(double scalar); // scalar is right operand + TwoVector unitDir(); + double distanceTo(TwoVector otherPoint); + + +}; + +#endif // #ifndef _2DVECTORH \ No newline at end of file diff -r 000000000000 -r c667dfe12d47 2dvector.mm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/2dvector.mm Mon Nov 19 13:00:42 2012 +0000 @@ -0,0 +1,82 @@ +/* + * 2dvector.cpp + * simplespring + * + * Created by Robert Tubb on 01/06/2011. + * Copyright 2011 __MyCompanyName__. All rights reserved. + * + */ + +#include "2dvector.h" +#include + +TwoVector::TwoVector(){ + x = 0.0; + y = 0.0; + //cout << "def constr set vector to zeros" << endl; +} + +TwoVector::TwoVector(double ax, double ay){ + x = ax; + y = ay; + //cout << "spec constr set vector to " << ax << "," << ay << endl; +} + +double TwoVector::norm(){ + double norm; + norm = sqrt(x * x + y * y); + return norm; + +} + +void TwoVector::setCoord(double ax, double ay){ + x = ax; + y = ay; + +} + +TwoVector TwoVector::minus(TwoVector otherPoint){ + TwoVector diff; + diff.setCoord(x - otherPoint.x, y - otherPoint.y); + return diff; +} + +TwoVector TwoVector::operator-(TwoVector otherPoint){ + TwoVector diff; + diff.setCoord(x - otherPoint.x, y - otherPoint.y); + return diff; +} + +TwoVector TwoVector::operator*(TwoVector otherPoint){ // if multiplying two vectors - elementwise + TwoVector diff; + diff.setCoord(x * otherPoint.x, y * otherPoint.y); + return diff; +} + +TwoVector TwoVector::operator*(double scalar){ // if multiplying two vectors - elementwise + TwoVector diff; + diff.setCoord(x * scalar, y * scalar); + return diff; +} + +TwoVector TwoVector::operator+(TwoVector otherPoint){ + TwoVector diff; + diff.setCoord(otherPoint.x + x, otherPoint.y + y); + return diff; +} + +TwoVector TwoVector::unitDir(){ + TwoVector unit; + double theNorm; + theNorm = this->norm(); + + unit.setCoord(x/theNorm, y/theNorm); + return unit; +} + +double TwoVector::distanceTo(TwoVector otherPoint){ + TwoVector diff; + diff.setCoord(otherPoint.x - x, otherPoint.y - y); + return diff.norm(); +} + diff -r 000000000000 -r c667dfe12d47 button.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/button.h Mon Nov 19 13:00:42 2012 +0000 @@ -0,0 +1,37 @@ +// +// button.h +// Wablet +// +// Created by Robert Tubb on 19/03/2012. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#ifndef Wablet_button_h +#define Wablet_button_h +#include +#include "ofMain.h" +#include "mesh.h" +#include "uifunctor.h" +class testApp; + +class ControlButton{ + +public: + ofColor bcolor, hicolor; + + bool pressed; + int width, height, xpos, ypos; + std::string name; + Mesh *theMesh; + int buttonId; + ControlButton(int abuttonId,int awidth,int aheight,int axpos,int aypos, std::string aname, Mesh *atheMeshPtr, ofColor acolor); + ~ControlButton(); + void passCallBack(UIFunctor callBack); + void draw(); + bool checkTouchArea(int axpos,int aypos); + void press(); + void release(); +}; + + +#endif diff -r 000000000000 -r c667dfe12d47 button.mm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/button.mm Mon Nov 19 13:00:42 2012 +0000 @@ -0,0 +1,160 @@ +// +// button.cpp +// Wablet +// +// Created by Robert Tubb on 19/03/2012. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// +// button on screen either side of mesh +#include +#include "button.h" +#include "globalUI.h" +#include "globalForces.h" +extern GlobalUI globalUI; +extern GlobalForces globalForces; + +ControlButton::ControlButton(int abuttonId, int awidth,int aheight,int axpos,int aypos, string aname, Mesh *atheMeshPtr, ofColor acolor){ + width = awidth; + height = aheight; + xpos = axpos; + ypos = aypos; + name = aname; + bcolor = acolor; + bcolor.setSaturation(150); + bcolor.setBrightness(150); + hicolor = acolor; + hicolor.setSaturation(230); + bcolor.setBrightness(230); + buttonId = abuttonId; + pressed = 0; + theMesh = atheMeshPtr; + + //callingApp = atestApp; + +} + +void ControlButton::draw(){ + //cout << "button draw " << hicolor.r << " \n"; + if(pressed){ + ofSetColor(hicolor); + }else{ + ofSetColor(bcolor); + } + + ofRect(xpos,ypos,width,height); + + ofSetLineWidth(2); + ofSetColor(hicolor); + ofLine(xpos,ypos,xpos+width,ypos); + ofLine(xpos,ypos,xpos,ypos+height); + ofLine(xpos+width,ypos,xpos+width,ypos+height); + ofLine(xpos,ypos+height,xpos+width,ypos+height); + + ofSetColor(0, 0, 0); + ofDrawBitmapString(name, xpos+10, ypos+20); + +} + +bool ControlButton::checkTouchArea(int axpos,int aypos){ + if ( (axpos > xpos) && (axpos < xpos+width) && (aypos > ypos) && (aypos < ypos+height)){ + return true; + }else{ + return false; + } + +} + +void ControlButton::press(){ + cout << name << " button has been pressed\n"; + + if (name == "still"){ + globalForces.homingAmt = 0.15; + } + + if (name == "drift"){ + theMesh->toggleSpringForces(false); + } + if (name == "tighter"){ + theMesh->increasePropagationSpeed(); + } + if (name == "slacker"){ + theMesh->decreasePropagationSpeed(); + } + + if (name == "reset"){ + theMesh->resetAll(); + } + if (name=="stickedge"){ + theMesh->constrain(.0,.0,Mesh::CONSTRAIN_EDGES); + } + + if (name == "free"){ + theMesh->unconstrain(); + } + + if (name == "drawscan"){ + theMesh->resetPositions(); + theMesh->resetVelocities(); + globalUI.touchMode = globalUI.INSCRIBE_PATH; + theMesh->clearScanPath(); + theMesh->update(); + } + if (name == "deleteMesh"){ + // cant + } + + if (name == "force"){ + globalUI.touchMode = globalUI.FORCE_FIELD; + } + if (name == "stick"){ + globalUI.touchMode = globalUI.CONSTRAIN; + } + if (name == "unstick"){ + globalUI.touchMode = globalUI.UNCONSTRAIN; + } + if (name == "free"){ + // nothing + //globalUI.touchMode = globalUI.UNCONSTRAIN; + } + if (name == "grab"){ + globalUI.touchMode = globalUI.GRAB; + } + if (name == "smooth"){ + globalForces.avFilterAmt = 0.11; + } + if (name == "harmonic"){ + globalUI.touchMode = globalUI.SPATIAL_HARMONIC; + } + if (name == "gravity"){ + globalForces.gravityAmt = 4.0; + } + if(name == "scanmode"){ + theMesh->scanPath->scanMode = theMesh->scanPath->SPEED; + } + pressed = true; +} + +void ControlButton::release(){ + + if (name == "homing"){ + globalForces.homingAmt = 0.0; + } + if (name == "drift"){ + theMesh->toggleSpringForces(true); + } + if (name == "smooth"){ + globalForces.avFilterAmt = 0.02; + } + if (name == "gravity"){ + globalForces.gravityAmt = 0.0; + } + if(name == "scanmode"){ + theMesh->scanPath->scanMode = theMesh->scanPath->DISPLACEMENT; + } + if (name == "freeAll"){ + theMesh->constrain(.0,.0,Mesh::CONSTRAIN_EDGES); + } + pressed = false; + + +} \ No newline at end of file diff -r 000000000000 -r c667dfe12d47 dsptools.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dsptools.h Mon Nov 19 13:00:42 2012 +0000 @@ -0,0 +1,32 @@ +// +// dsptools.h +// wablet +// +// Created by Robert Tubb on 21/06/2011. +// Copyright 2011 __MyCompanyName__. All rights reserved. +// +#ifndef _DSPTOOLSH +#define _DSPTOOLSH + + +class DSPTools{ +public: + double ax[3]; + double by[3]; + + + double xv[3]; + double yv[3]; + + DSPTools(); + double highpass1(double ax); + double lowpass1(double ax); + void getLPCoefficientsButterworth2Pole(const int samplerate, const double cutoff, double* const ax, double* const by); + void getHPCoefficientsButterworth2Pole(double* const ax, double* const by); + double butter(double sample); + +}; + + + +#endif \ No newline at end of file diff -r 000000000000 -r c667dfe12d47 dsptools.mm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dsptools.mm Mon Nov 19 13:00:42 2012 +0000 @@ -0,0 +1,78 @@ +// +// dsptools.cpp +// wablet +// +// Created by Robert Tubb on 21/06/2011. +// Copyright 2011 __MyCompanyName__. All rights reserved. +// + +#include "dsptools.h" +#include "ofMain.h" + +DSPTools::DSPTools(){ + for(int i = 0; i < 3; i++){ + xv[i] = 0.0; + yv[i] = 0.0; + } + getHPCoefficientsButterworth2Pole(ax, by); +} + +double DSPTools::highpass1(double ax){ + static double xm1 = 0.0; + double sample; + sample = 2*(ax - xm1); + xm1 = ax; + return sample; + +} +double DSPTools::lowpass1(double ax){ + static double xm1 = 0.0; + double sample; + sample = 0.5*(ax + xm1); + xm1 = ax; + return sample; + +} + +void DSPTools::getLPCoefficientsButterworth2Pole(const int samplerate, const double cutoff, double* const ax, double* const by) +{ + double sqrt2 = 1.4142135623730950488; + + double QcRaw = (2 * PI * cutoff) / samplerate; // Find cutoff frequency in [0..PI] + double QcWarp = tan(QcRaw); // Warp cutoff frequency + + double gain = 1 / (1+sqrt2/QcWarp + 2/(QcWarp*QcWarp)); + by[2] = (1 - sqrt2/QcWarp + 2/(QcWarp*QcWarp)) * gain; + by[1] = (2 - 2 * 2/(QcWarp*QcWarp)) * gain; + by[0] = 1; + ax[0] = 1 * gain; + ax[1] = 2 * gain; + ax[2] = 1 * gain; +} +void DSPTools::getHPCoefficientsButterworth2Pole(double* const ax, double* const by) +{ + ax[0] = 0.997987115675119; + ax[1] = -1.995974231350238; + ax[2] = 0.997987115675119; + + by[0] = 1.000000000000000; + by[1] = -1.995970179642828; + by[2] = 0.995978283057647; +} + +double DSPTools::butter(double sample) +{ + + xv[2] = xv[1]; + xv[1] = xv[0]; + xv[0] = sample; + yv[2] = yv[1]; + yv[1] = yv[0]; + + yv[0] = (ax[0] * xv[0] + ax[1] * xv[1] + ax[2] * xv[2] + - by[1] * yv[1] + - by[2] * yv[2]); + + return yv[0]; + +} \ No newline at end of file diff -r 000000000000 -r c667dfe12d47 globalForces.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/globalForces.h Mon Nov 19 13:00:42 2012 +0000 @@ -0,0 +1,63 @@ +// +// globalForces.h +// wablet +// +// Created by Robert Tubb on 18/07/2011. +// Copyright 2011 __MyCompanyName__. All rights reserved. +// +#ifndef _GLOBALFORCES +#define _GLOBALFORCES + +#include "2dvector.h" +#include "ofMain.h" +#include "ofxiPhone.h" +#include "ofxiPhoneExtras.h" +#include + +struct forceTouchPoint{ + double x, y; + int tid; + + //forceTouchPoint(double ax,double ay,int atid): x(ax),y(ay),tid(atid){}; + +}; + +class GlobalForces{ + +public: + + enum excitationTypes {POSITION,VELOCITY}; + enum excitationShapes {NOISE,GAUSS,SINE}; + + excitationTypes excitationType; + excitationShapes excitationShape; + + bool gravityOn, forceTouchOn, pressureOn; + // params for adjusting stuff + double pressureAmt, gravityAmt, touchStrength, excitationStrength, homingAmt, avFilterAmt; + int exciteShapeX, exciteShapeY; + // general params + double speedLimit, wallBounce; + double delt; // time step between frames + double volume; + + forceTouchPoint* forceTouchPoints; + TwoVector grav; + double pressure; + + int maxForcePoints; + + GlobalForces(); + ~GlobalForces(); + void update(); + void setPressure(double aP); + void createForceTouchPoint(double ax,double ay, int touchId); + void moveForceTouchPoint(double ax,double ay, int touchId); + void removeForceTouchPoint(int touchId); + TwoVector getAllForceAt(double ax, double ay); + + +}; + + +#endif diff -r 000000000000 -r c667dfe12d47 globalForces.mm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/globalForces.mm Mon Nov 19 13:00:42 2012 +0000 @@ -0,0 +1,122 @@ +// +// globalForces.cpp +// wablet +// +// Created by Robert Tubb on 18/07/2011. +// Copyright 2011 __MyCompanyName__. All rights reserved. +// + +#include "globalForces.h" + + +//----------------------------------------------------------- +GlobalForces::GlobalForces(): grav(0.0,0.0){ + //construct + gravityOn = true; + + speedLimit = 100.0; + wallBounce = 0.5; + + pressureAmt = 0.0; + gravityAmt = 0.2; + touchStrength = 1.0; + homingAmt = 0.0; + avFilterAmt = 0.0; + + pressure = 0.0; + volume = 1.0; + + maxForcePoints = 11; + excitationType = POSITION; + excitationShape = NOISE; + + forceTouchPoints = new forceTouchPoint[maxForcePoints]; + for(int i = 0; i< 11; i++){ + forceTouchPoints[i].x = 0.0; + forceTouchPoints[i].y = 0.0; + forceTouchPoints[i].tid = -1; + + } + + +} + +//----------------------------------------------------------- +GlobalForces::~GlobalForces(){ + //destruct + +} +void GlobalForces::update(){ + // gravity + grav.setCoord(ofxAccelerometer.getForce().y * 0.015 * gravityAmt, ofxAccelerometer.getForce().x * 0.015 * gravityAmt); + // time step + delt = 1/ofGetFrameRate(); + +} +//----------------------------------------------------------- +// this is actually only called from dropletmesh.update +void GlobalForces::setPressure(double aP){ + + pressure = pressureAmt*aP; +} +//----------------------------------------------------------- +void GlobalForces::createForceTouchPoint(double ax,double ay, int touchId){ + + forceTouchPoints[touchId].x = ax; + forceTouchPoints[touchId].y = ay; + forceTouchPoints[touchId].tid = touchId; + + + +} +//----------------------------------------------------------- +void GlobalForces::moveForceTouchPoint(double ax,double ay, int touchId){ + // + for(int i = 0;i +#include "globalUI.h" + +GlobalUI::GlobalUI(){ + // array of buttons, + + + numButtons = 12; + borderSize = 128; + buttonSize = 100; + + buttons = new ControlButton *[numButtons]; + + touchMode = GRAB; + +} + +void GlobalUI::passCallBack(UIFunctor specFuncA){ + +} +void GlobalUI::makeButtons(Mesh *atheMeshPtr){ + + // set up button size depending on screen resolution + + int w = ofGetWidth(); + cout << "width = " << w << "\n"; + if (w == 768){ // ipad + + }else if (w == 480){ // iphone + borderSize = 80; + buttonSize = 70; + + }else if (w == 1234){ // retinass? + + }else{ + // default to what? + + } + + theMeshPtr = atheMeshPtr; + + ofColor red(250,0,0); + ofColor blue(0,0,250); + ofColor green(0,250,0); + + buttons[0] = new ControlButton(2,buttonSize,buttonSize,(borderSize-buttonSize)*0.5,(borderSize-buttonSize)*0.5,"still",atheMeshPtr,green); + buttons[1] = new ControlButton(0,buttonSize,buttonSize,(borderSize-buttonSize)*0.5,borderSize+(borderSize-buttonSize)*0.5,"smooth",atheMeshPtr,green); + buttons[2] = new ControlButton(1,buttonSize,buttonSize,(borderSize-buttonSize)*0.5,borderSize*2+(borderSize-buttonSize)*0.5,"drift",atheMeshPtr,green); + buttons[3] = new ControlButton(3,buttonSize,buttonSize,(borderSize-buttonSize)*0.5,borderSize*3+(borderSize-buttonSize)*0.5,"gravity",atheMeshPtr,green); + buttons[4] = new ControlButton(4,buttonSize,buttonSize,(borderSize-buttonSize)*0.5,borderSize*4+(borderSize-buttonSize)*0.5,"free",atheMeshPtr,green); + buttons[5] = new ControlButton(5,buttonSize,buttonSize,(borderSize-buttonSize)*0.5,borderSize*5+(borderSize-buttonSize)*0.5,"something",atheMeshPtr,green); + + // touch mode buttons + buttons[6] = new ControlButton(6,buttonSize,buttonSize,borderSize+ofGetHeight()+(borderSize-buttonSize)*0.5,(borderSize-buttonSize)*0.5,"grab",atheMeshPtr, blue); + buttons[7] = new ControlButton(7,buttonSize,buttonSize,borderSize+ofGetHeight()+(borderSize-buttonSize)*0.5,borderSize+(borderSize-buttonSize)*0.5,"force",atheMeshPtr, blue); + buttons[8] = new ControlButton(8,buttonSize,buttonSize,borderSize+ofGetHeight()+(borderSize-buttonSize)*0.5,borderSize*2+(borderSize-buttonSize)*0.5,"stick",atheMeshPtr, blue); + buttons[9] = new ControlButton(9,buttonSize,buttonSize,borderSize+ofGetHeight()+(borderSize-buttonSize)*0.5,borderSize*3+(borderSize-buttonSize)*0.5,"unstick",atheMeshPtr, blue); + buttons[10] = new ControlButton(10,buttonSize,buttonSize,borderSize+ofGetHeight()+(borderSize-buttonSize)*0.5,borderSize*4+(borderSize-buttonSize)*0.5,"drawscan",atheMeshPtr, red); + buttons[11] = new ControlButton(11,buttonSize,buttonSize,borderSize+ofGetHeight()+(borderSize-buttonSize)*0.5,borderSize*5+(borderSize-buttonSize)*0.5,"reset",atheMeshPtr, red); +} + +//-------------------------------------------------------------- +void GlobalUI::draw(){ + + + for(int i=0; idraw(); + } + + //ofSetColor(blue); + //ofDrawBitmapString("touch mode", 670, 25); +} +//-------------------------------------------------------------- +bool GlobalUI::inUIZone(float x, float y){ + if (x < borderSize || x > borderSize + ofGetHeight()){ + return true; + + }else{ + return false; + } +} + +//-------------------------------------------------------------- +bool GlobalUI::handleTouchDown(int ax, int ay){ + // returns true if touch is not to be left to the mesh + if(inUIZone(ax,ay)){ + + /* not needed now we are using ofxUI + + for(int i=0; i < numButtons;i++){ + if(buttons[i]->checkTouchArea(ax,ay)){ + buttons[i]->press(); + } + } + */ + return true; + }else{ + + return false; + } +} +//-------------------------------------------------------------- +bool GlobalUI::handleTouchUp(int ax, int ay){ + // returns true if touch is not to be left to the mesh + if(inUIZone(ax,ay)){ + /* not needed now we are using ofxUI + + for(int i=0; i < numButtons;i++){ + if(buttons[i]->checkTouchArea(ax,ay)){ + buttons[i]->press(); + } + } + */ + return true; + }else{ + + return false; + } +} +//-------------------------------------------------------------- +bool GlobalUI::handleTouchMove(int ax, int ay){ + // returns true if touch is not to be left to the mesh + if(inUIZone(ax,ay)){ + + return true; + }else{ + + return false; + } +} + + diff -r 000000000000 -r c667dfe12d47 lump.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lump.h Mon Nov 19 13:00:42 2012 +0000 @@ -0,0 +1,87 @@ +/* + * lump.h + * simplespring + * + * Created by Robert Tubb on 01/06/2011. + * Copyright 2011 __MyCompanyName__. All rights reserved. + * + */ +#ifndef _LUMPH +#define _LUMPH +#include "2dvector.h" +#include "ofMain.h" +#include "spring.h" + + +class Spring; +class Lump { +private: + double mass, inverseMass; + bool grabbed, highlighted; + const int maxSprings; + + TwoVector velocity; + TwoVector accel; + double friction; + +public: + bool constrained; + Spring** attachedSprings; // pointers to all attached springs + int numAttachedSprings; + + enum ConstrainMode {NOT_CONSTRAINED,CONSTRAIN_X,CONSTRAIN_Y,CONSTRAIN_XY}; + ConstrainMode constrainMode; + + bool isInScanPath; + + TwoVector position; + TwoVector previousPosition; + double totalForceMag; + double size; + int grabID; // which touch is this lump grabbed by? + + Lump(); // default constructor + Lump(double aMass,double aFriction, double positionX, double positionY); + // also which spring is it attached to + ~Lump(); + + TwoVector applyForce(); + void averagingFilter(double amt); + void homingFilter(double amt); + TwoVector averageOfConnected(); + + TwoVector zeroRefPos; + void draw(); + + void attachSpring(Spring* aSpring); + void setPosition(double ax, double ay); + void setVelocity(double ax, double ay); + void constrain(); + void constrain(ConstrainMode aconstrainMode); + void unconstrain(); + + bool isGrabbed(); + void grab(int aGrabID); + void drag(double ax, double ay, int aGrabID); + void unGrab(); + void highlight(); + void unhighlight(); + void setInvMass(double aInvMass); + double getTotalForceMag(); + void setZeroRefPos(); + void setFriction(double aF); + + Spring * checkConnectedTo(Lump * otherLump); + + // interface to scan path + double scanDisplacement(); + double scanRadialDisplacement(); + double scanLumpSpeed(); + double scanYPos(); + double scanXPos(); + void addToScanPath(); + void removeFromScanPath(); + +}; + +#endif \ No newline at end of file diff -r 000000000000 -r c667dfe12d47 lump.mm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lump.mm Mon Nov 19 13:00:42 2012 +0000 @@ -0,0 +1,418 @@ +/* + * lump.cpp + * simplespring + * + * Created by Robert Tubb on 01/06/2011. + * Copyright 2011 __MyCompanyName__. All rights reserved. + * + */ +#include "lump.h" +#include "testApp.h" +#include +#include "globalForces.h" +#include "globalUI.h" +extern GlobalForces globalForces; +extern GlobalUI globalUI; +//int Lump::numLumps = 0; +//-------------------------------------------------------------- +// default constr +Lump::Lump() : maxSprings(100){ + //cout << "constructing a default lump" << endl; + mass = 10.0; + inverseMass = 1.0/mass; // not needed - used csquared for force + friction = 0.996; + position.x = 0.5; + position.y = 0.5; + velocity.x = 0.0; + velocity.y = 0.0; + accel.x = 0.0; + accel.y = 0.0; + numAttachedSprings = 0; + grabbed = false; + highlighted = false; + constrained = false; + totalForceMag = 0.0; + size = 3; //sqrt(mass/3.0); + isInScanPath = false; + previousPosition.setCoord(0.5,0.5); + zeroRefPos.setCoord(0.0,0.0); + constrainMode = NOT_CONSTRAINED; + + attachedSprings = new Spring*[maxSprings]; + + grabID = -1; + //myIndex = Lump::numLumps++; + //cout << numAttachedSprings << endl; + +} +//-------------------------------------------------------------- +// arg constructor +Lump::Lump(double aMass,double aFriction, double positionX, double positionY) : maxSprings(100){ + // set members + +} + +//-------------------------------------------------------------- +Lump::~Lump(){ + + delete [] attachedSprings; +} +//-------------------------------------------------------------- +void Lump::attachSpring(Spring* aSpring){ + + // stick pointer in array + if(numAttachedSprings < maxSprings){ + attachedSprings[numAttachedSprings] = aSpring; + numAttachedSprings++; + }else{ + cout << "cant attach another spring as mass already has " << maxSprings << endl; + return; + } +} +//-------------------------------------------------------------- +Spring * Lump::checkConnectedTo(Lump * otherLump){ + // loop thru all attached springs looking at other end + for(int i = 0; igetLumpOnOtherEnd(this) == otherLump){ + return attachedSprings[i]; + } + } + return NULL; +} +//-------------------------------------------------------------- +void Lump::constrain(){ + // TODO1 different constrain modes + constrained = true; + constrainMode = CONSTRAIN_XY; + setZeroRefPos(); + //cout << "constraining lump "<< endl; + +} +//-------------------------------------------------------------- +void Lump::constrain(ConstrainMode aconstrainMode){ + constrained = true; + constrainMode = aconstrainMode; + setZeroRefPos(); + //cout << "constraining lump "<< endl; + +} +void Lump::setInvMass(double aInvMass){ + // use csquared + inverseMass = aInvMass; +} +//-------------------------------------------------------------- + +void Lump::draw(){ +/* + + + */ + + if(grabbed){ + + int xpos = position.x * ofGetHeight() + globalUI.borderSize; + int ypos = position.y * ofGetHeight(); + // draw a circle round it + ofSetColor(255, 0, 0); + ofNoFill(); + ofCircle(xpos, ypos, 35.0); + ofFill(); + }else if(isInScanPath){ + ofSetColor(0, 200, 20); + int xpos = position.x * ofGetHeight() + globalUI.borderSize; + int ypos = position.y * ofGetHeight(); + ofEllipse(xpos,ypos, 6, 6); + + // code to display restpos and displacement + /* + ofSetColor(0, 0, 0); + int rxpos = zeroRefPos.x * ofGetHeight() + 128; + int rypos = zeroRefPos.y * ofGetHeight(); + ofEllipse(rxpos,rypos, 6, 6); + + ofSetColor(100, 100, 100); + + ofLine(rxpos,rypos,xpos,ypos); + */ + + }else if(highlighted){ + ofSetColor(200, 0, 0); + int xpos = position.x * ofGetHeight() + globalUI.borderSize; + int ypos = position.y * ofGetHeight(); + ofEllipse(xpos,ypos, 2, 2); + }else if (constrained){ + ofSetColor(200,23,23); + }else{ + // dont draw 'normal ' lumps + return; + //ofSetColor(23, 23, 200); + } +} + +//-------------------------------------------------------------- + +TwoVector Lump::applyForce(){ + + + if(grabbed || constrainMode == CONSTRAIN_XY){ + + // don't bother + return position; + } + // called LOTS so optimise + // use spring force to calc accel - vel - pos + + TwoVector springForce(0.0,0.0); + TwoVector totalForce(0.0,0.0); + + // sum up force from each attached spring + for(int i = 0;igetForce(this); + + //cout << "spring number " << i << " force x " << springForce.x << endl; + totalForce.x += springForce.x; + totalForce.y += springForce.y; + } + + // get the global forces, gravity and so on + totalForce.x += globalForces.getAllForceAt(position.x,position.y).x; + totalForce.y += globalForces.getAllForceAt(position.x,position.y).y; + + if (constrainMode != CONSTRAIN_X){ + accel.x = totalForce.x*inverseMass; + }else{ + accel.x = 0.0; + } + if(constrainMode != CONSTRAIN_Y){ + accel.y = totalForce.y*inverseMass; + }else{ + accel.y = 0.0; + } + + // DIFFERENCE EQUATIONS HERE. This is the bit that controls the movement! + + // Heun + + double pvx = velocity.x*friction + accel.x*2/3; + double pvy = velocity.y*friction + accel.y*2/3; + + + velocity.x = 0.75*pvx + 0.25*velocity.x; + velocity.y = 0.75*pvy + 0.25*velocity.y; + + double px = position.x + velocity.x*2/3; + double py = position.y + velocity.y*2/3; + + + position.x = 0.75*px + 0.25*position.x; + position.y = 0.75*py + 0.25*position.y; + + + // Newton's 2nd law +/* + velocity.x += accel.x; + velocity.x *= friction; + position.x += velocity.x; + + velocity.y += accel.y; + velocity.y *= friction; + position.y += velocity.y; +*/ + + + // WALLS + if (position.x < 0.0){ + position.x = -position.x; + velocity.x = -velocity.x * globalForces.wallBounce; + + } else if (position.x > 1.0){ + position.x = 2.0 - position.x; + velocity.x = -velocity.x * globalForces.wallBounce; + + } + if (position.y < 0.0){ + position.y = -position.y; + velocity.y = -velocity.y * globalForces.wallBounce; + + } else if (position.y > 1.0){ + position.y = 2.0 - position.y; + velocity.y = -velocity.y * globalForces.wallBounce; + + } + return position; + +} +//--------------------------------------------- +void Lump::homingFilter(double amt){ + // includes a little bit of the zero pos in the position, so sound will exponentially decay + + if (constrained || grabbed) return; + + position.x = (1 - amt)*position.x + amt*zeroRefPos.x; + position.y = (1 - amt)*position.y + amt*zeroRefPos.y; + +} +//--------------------------------------------- +void Lump::averagingFilter(double amt){ + // NOT USED AVERAGING FILTER NOW IN MESH + // amt is between 0 and 1 + if (constrained || grabbed) return; + double avx = 0.0, avy = 0.0; + // average the position of all the attached lumps + for(int i = 0;igetLumpOnOtherEnd(this); + avx += otherLump->position.x; + avy += otherLump->position.y; + } + avx /= numAttachedSprings; + avy /= numAttachedSprings; + + // mix in the average with the 'real' + position.x = (1 - amt)*position.x + amt*avx; + position.y = (1 - amt)*position.y + amt*avy; + +} +//--------------------------------------------- +TwoVector Lump::averageOfConnected(){ + TwoVector av; + if (constrained || grabbed) return position; // don't want constrained ones moving + //TODO what if edges unconstrained? this is why filtered unconstrained just ends up as a line... + + // average the position of all the attached lumps + for(int i = 0;igetLumpOnOtherEnd(this); + av.x += otherLump->position.x; + av.y += otherLump->position.y; + } + av.x /= numAttachedSprings; + av.y /= numAttachedSprings; + + return av; + +} +//-------------------------------------------------------------- +void Lump::setPosition(double ax, double ay){ + // set INITIAL position + // Called from mesh set up. not used for updates + + position.x = ax; + position.y = ay; + zeroRefPos.x = ax; + zeroRefPos.y = ay; +} +//-------------------------------------------------------------- +void Lump::setVelocity(double ax, double ay){ + + velocity.x = ax; + velocity.y = ay; + +} +//-------------------------------------------------------------- + +void Lump::setFriction(double aF){ + friction = aF; +} +//-------------------------------------------------------------- + +void Lump::setZeroRefPos(){ + // sets the reference point from which displacement is measured for scan amplitudes + zeroRefPos = position; +} +//-------------------------------------------------------------- +double Lump::getTotalForceMag(){ + return totalForceMag; +} +//-------------------------------------------------------------- +double Lump::scanDisplacement(){ + // returns the absolute distance from 'home' + return position.distanceTo(zeroRefPos); +} +//-------------------------------------------------------------- +double Lump::scanLumpSpeed(){ + // returns the absolute magnitude of the lumps velocity + return velocity.norm(); +} +//-------------------------------------------------------------- +double Lump::scanYPos(){ + // returns the y displ + return position.y - zeroRefPos.y; +} +//-------------------------------------------------------------- +double Lump::scanXPos(){ + // returns the x displ + return position.x - zeroRefPos.x; +} +// ------------------- +double Lump::scanRadialDisplacement(){ + // returns the distance from circle zero line + // need to know where the centre point of the circle is and default radius + + //return position.y - 0.5; + return position.distanceTo(zeroRefPos); + +} +//-------------------------------------------------------------- +void Lump::addToScanPath(){ + isInScanPath = true; + setZeroRefPos(); +} +//-------------------------------------------------------------- +void Lump::removeFromScanPath(){ + isInScanPath = false; + +} +//-------------------------------------------------------------- +void Lump::grab(int aGrabID){ + // hover hilight doesn't work on touchscreens if(highlighted) grabbed = true; + grabbed = true; + grabID = aGrabID; + velocity.x = 0.0; + velocity.y = 0.0; +} +//-------------------------------------------------------------- +void Lump::drag(double ax, double ay, int aGrabID){ + if(aGrabID == grabID){ + //cout << "dragging lump ID: " << grabID << endl; + position.x = ax; + position.y = ay; + velocity.x = previousPosition.x - position.x; + velocity.y = previousPosition.y - position.y; + previousPosition = position; // sets velocity too + } +} +//-------------------------------------------------------------- +void Lump::unGrab(){ + cout << "ungrabbed something\n"; + grabbed = false; + grabID = -1; + velocity.x = 0.0; + velocity.y = 0.0; +} +//-------------------------------------------------------------- +void Lump::unconstrain(){ + constrained = false; + constrainMode = NOT_CONSTRAINED; +} + + +//-------------------------------------------------------------- + +bool Lump::isGrabbed(){ + return grabbed; +} + +//-------------------------------------------------------------- + +void Lump::highlight(){ + highlighted = true; +} + +//-------------------------------------------------------------- + +void Lump::unhighlight(){ + highlighted = false; +} +//-------------------------------------------------------------- diff -r 000000000000 -r c667dfe12d47 main.mm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.mm Mon Nov 19 13:00:42 2012 +0000 @@ -0,0 +1,13 @@ +#include "ofMain.h" +#include "testApp.h" +#include "globalForces.h" + +GlobalForces globalForces; +GlobalUI globalUI; + +int main(){ + //ofSetupOpenGL(480,320, OF_FULLSCREEN); // <-------- setup the GL context + ofSetupOpenGL(1024,768, OF_FULLSCREEN); + ofRunApp(new testApp); + +} diff -r 000000000000 -r c667dfe12d47 mesh.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mesh.h Mon Nov 19 13:00:42 2012 +0000 @@ -0,0 +1,250 @@ +/* + * mesh.h + * springstructure + * + * Created by Robert Tubb on 07/06/2011. + * Copyright 2011 __MyCompanyName__. All rights reserved. + * + */ + +#ifndef _MESHH +#define _MESHH + +#include +#include "lump.h" +#include "scanpath.h" +#include "globalForces.h" + + +class Mesh{ +public: + + bool radial; // tells us if mesh is radial or not + // toggles + bool syrup; + bool home; + + double propagationSpeed; + + double k,m,f; // for stability check and asym + + int numLumps; + int numSprings; + + Lump *lumps; + Spring *springs; + ScanPath* scanPath; + + TwoVector centre; + + + // modes + enum constrainMode { CONSTRAIN_CORNERS, CONSTRAIN_EDGES,CONSTRAIN_EDGES_XY, CONSTRAIN_SINGLE_POINT, CONSTRAIN_SINGLE_POINT_X, CONSTRAIN_SINGLE_POINT_Y, CONSTRAIN_GRAB_REGION,CONSTRAIN_GRAB_REGION_X,CONSTRAIN_GRAB_REGION_Y, CONSTRAIN_ALL_X, CONSTRAIN_ALL_Y}; + double GRAB_RANGE; + + + + // MEMBER FUNCTIONS + Mesh(); + virtual ~Mesh(); + + void draw(); + + // INTERACTIONS + void resetAll(); + void resetPositions(); + void resetVelocities(); + void resetEverything(); + + // excite via position, velocity force + // using shapes : noise, hamming, sine, + void hit(double dax,double day,int velocity = 100, GlobalForces::excitationTypes aEType = GlobalForces::POSITION, + GlobalForces::excitationShapes aEShape = GlobalForces::NOISE); + void spatialHarmonic(int aharm1 = 1, int aharm2 = 0); + void damp(); + void forceField(double dax,double day,double strength); + void averagingFilter(double amt); + + void grab(double x, double y, int touchID); + void drag(double ax,double ay, int touchID); + void unGrab(int touchID); + + virtual void constrain(double x, double y, constrainMode aMode); // diff meshes will have diff edges + void unconstrain(); + void unconstrain(double x, double y, constrainMode aMode); + + + // PARAMETER SETTINGS + void toggleSyrup(); + void toggleSyrup(bool on); + void toggleSpringForces(); + void toggleSpringForces(bool on); + void toggleGravity(); + void toggleGravity(bool on); + void toggleHome(bool on); + + void setRestLength(); + void zeroRestLength(); + + void setPropagationSpeed(double aSpeed); // sets k and m to give a certain speed + void decreasePropagationSpeed(); + void increasePropagationSpeed(); + void setSpringConstant(double aK); + void setMass(double aM); + void setFriction(double aF); + + // create varying constants + void setSpringConstantAsym(double aAsym); + void setMassAsym(double aAsym); + void setFrictionAsym(double aAsym); + void setZeroAudioLine(); + + // only for mouse cursor + void checkHover(double x, double y); + +// SCANPATH STUFF + void clearScanPath(); + void disconnectDraw(); // enables you to draw scanpths anywhere + int inscribeScanPath(double ax, double ay); + // called by scanpath.update wavetables + double getNextSample(); + + virtual void update(); + +protected: + + int prevLump; + + // specific mesh shapes override these: + + virtual void makeComponents(int adimension1, int adimension2); + virtual void setLumpPositions(); + virtual void makeConnections(); + virtual void makeDefaultScanPath(); + + // UTILS + TwoVector calculateCentre(); + void connect(int springnum,int lumpnum); + void connect(int springnum,int lumpnum,int alumpnum2); + int getNearestLump(double ax,double ay); + +}; +//--------------------------------------------------------- +class SpiderMesh : public Mesh{ +public: + int numSpokes, numRings; + + SpiderMesh(int aNumSpokes,int aNumRings); + + virtual void constrain(double x, double y, constrainMode aMode); + virtual void makeComponents(int adimension1, int adimension2); + virtual void setLumpPositions(); + virtual void makeConnections(); + virtual void makeDefaultScanPath(); +}; +//--------------------------------------------------------- +class HoledSpiderMesh : public Mesh{ +public: + int numSpokes, numRings; + + HoledSpiderMesh(int aNumSpokes,int aNumRings); + void constrain(double x, double y, constrainMode aMode); + void makeComponents(int adimension1, int adimension2); + void setLumpPositions(); + void makeConnections(); + void makeDefaultScanPath(); +}; +//--------------------------------------------------------- +class SpiderCrossMesh : public Mesh{ +public: + int numSpokes, numRings; + + SpiderCrossMesh(int aNumSpokes,int aNumRings); + + void constrain(double x, double y, constrainMode aMode); + void makeComponents(int adimension1, int adimension2); // override + void setLumpPositions(); // same + void makeConnections(); // overrride + void makeDefaultScanPath(); // same +}; +//--------------------------------------------------------- +// square grid with all diagonals connected +class SquareCrossMesh : public Mesh{ +public: + // square specific + int height; + int width; + + SquareCrossMesh(int height, int width); + ~SquareCrossMesh(); + void constrain(double x, double y, constrainMode aMode); + void makeComponents(int adimension1, int adimension2); + void setLumpPositions(); + void makeConnections(); + void makeDefaultScanPath(); +}; +//--------------------------------------------------------- +// simple 1d line +class LineMesh : public Mesh{ +public: + // square specific + int numSegments; + + LineMesh(int aNumSegments); + void constrain(double x, double y, constrainMode aMode); + void makeComponents(int adimension1, int adimension2); + void setLumpPositions(); + void makeConnections(); + void makeDefaultScanPath(); +}; +//--------------------------------------------------------- +// simple 1d line attached to 'ground' (constrained lumps) by other springs... +class GroundedLineMesh : public Mesh{ +public: + int numSegments; + + GroundedLineMesh(int aNumSegments); + void constrain(double x, double y, constrainMode aMode); + void makeComponents(int adimension1, int adimension2); + void setLumpPositions(); + void makeConnections(); + void makeDefaultScanPath(); +}; + +//---------------------------------------------------------- +//--------------------------------------------------------- +// circular spring with internal pressure +class DropletMesh : public Mesh{ +public: + // specific + int numSegments; + double restArea; + + DropletMesh(int aNumSegments); + void constrain(double x, double y, constrainMode aMode); + void makeComponents(int adimension1, int adimension2); + void setLumpPositions(); + void makeConnections(); + void makeDefaultScanPath(); + double calculatePressure(); + double calculateArea(); + void update(); +}; +//--------------------------------------------------------- +// trangular mesh +class TriangleMesh : public Mesh{ +public: + // square specific + int height; + int width; + + TriangleMesh(int height, int width); + void constrain(double x, double y, constrainMode aMode); + void makeComponents(int adimension1, int adimension2); + void setLumpPositions(); + void makeConnections(); + void makeDefaultScanPath(); +}; +//---------------------------------------------------------- + +#endif diff -r 000000000000 -r c667dfe12d47 mesh.mm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mesh.mm Mon Nov 19 13:00:42 2012 +0000 @@ -0,0 +1,2050 @@ +/* + * 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 + +extern GlobalForces globalForces; + +//-------------------------------------------------------------- +Mesh::Mesh(){ + // construct an empty structure + numSprings = 0; + numLumps = 0; + + scanPath = new ScanPath(); + + syrup = false; + GRAB_RANGE = 0.040; + propagationSpeed = 0.5; // this will be changed by adjusting k/m + cout << "constructed mesh base" << endl; + + m = 123.0; + k = 0.1; + f = 0.999; + + prevLump = -1; + + // some reasonable defaults + setSpringConstant(0.8); + setMass(1); + setFriction(0.999991); + +} + + +Mesh::~Mesh(){ + cout << "destroying mesh" << endl; + delete [] lumps; + delete [] springs; + delete scanPath; +} +/////////// +// 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 48) updown = 0; + // update the ends of the springs to reflect lump movement + + for(int i=0;iupdateWavetables(); +} +//-------------------------------------------------------------- +void Mesh::averagingFilter(double amt){ + // we need a temporary store for average position, can't update in place with central diffs + static TwoVector * avLumpPos = new TwoVector[numLumps]; + + for(int i=0;idraw(); // uncomment if you want to see the output waveform + + +} +//-------------------------------------------------------------- +TwoVector Mesh::calculateCentre(){ + // calculates average of all lump positions + + double x = 0, y = 0; + for(int i = 0; i < numLumps; i++){ + x += lumps[i].position.x; + y += lumps[i].position.y; + + } + x = x/numLumps; + y = y/numLumps; + + //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 < numLumps; 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;iclear(); + 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 < 2048){ + count++; + if(prevLump == -1){ // (scanPath->howManyElements() == 0){ // + //first lump + foundPath = true; + prevLump = targetLump; + //cout << "first lump" << endl; + 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); + // 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; + int yamt = globalForces.exciteShapeY; + + switch(aEShape){ + + case GlobalForces::SINE: + + // applies a sine wave to all lumps, x and y + for(int i = 0; i < numLumps; 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 < numLumps; 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 < numLumps; 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 < numLumps; 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 < numLumps; i++){ + if(!lumps[i].constrained){ + lumps[i].position.y = lumps[i].zeroRefPos.y + globalForces.touchStrength/128.0*sin(PI* (2*(aharm-1) + 1) * lumps[i].position.x); + lumps[i].position.x = lumps[i].zeroRefPos.x + globalForces.touchStrength/128.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 componets creates the correct number of lumps and springs according to dimensions and mesh type + 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 aDimension1, int aDimension2){ + cout << "!!!!Mesh base class makeComponents\n"; +} +//-------------------------------------------------------------- +void Mesh::constrain(double ax, double ay, constrainMode aMode){ + cout << "!!!!Mesh base class constrain\n"; +} +//-------------------------------------------------------------- + + +/****************************************/ +// SquareCrossMesh */ +/****************************************/ +//-------------------------------------------------------------- +SquareCrossMesh::SquareCrossMesh(int height, int width): Mesh() { + + makeComponents(height, width); + + setLumpPositions(); + + makeConnections(); + + makeDefaultScanPath(); + + constrain(0,0,CONSTRAIN_EDGES_XY); + + zeroRestLength(); + + radial = false; + + cout << "constructing SQUARE mesh\n"; +} +SquareCrossMesh::~SquareCrossMesh(){ + + cout << "destructing SQUARECROSS mesh\n"; +} + +//-------------------------------------------------------------- +// make componets for square +void SquareCrossMesh::makeComponents(int aheight, int awidth){ + height = aheight; + width = awidth; + numLumps = height * width; + // horz // vert + numSprings = (width-1)*height + (height-1)*width + 2*(height-1)*(width-1); + + lumps = new Lump[numLumps]; + cout << "made " << numLumps << " lumps\n"; + springs = new Spring[numSprings]; + cout << "made " << numSprings << " springs\n"; +} + +//-------------------------------------------------------------- +void SquareCrossMesh::setLumpPositions(){ + 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 + lumps[i].setPosition(hspacing*col,vspacing*row); + + i++; + } + } +} +//-------------------------------------------------------------- +void SquareCrossMesh::makeConnections(){ + + // could be cleverer in terms of numbering? + int lumpnum = 0; + int springnum = 0; + // attach horizontal + for ( int i=0;iaddElement(&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++){ + 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++){ + 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++){ + scanPath->addElement(&lumps[lumpno], &springs[springno]); + springno--; // jump to next row + lumpno -= width; // ditto + } + +} +//-------------------------------------------------------------- + + +/****************************************/ +// SpiderMesh */ +/****************************************/ +SpiderMesh::SpiderMesh(int aNumSpokes,int aNumRings) : 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]; + cout << "made " << numLumps << " lumps\n"; + springs = new 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; iaddElement(&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; // starts from total rings, increments by numspokes etc + scanPath->addElement(&lumps[lumpno], &springs[springno]); + } +*/ + } + + +//-------------------------------------------------------------- +/****************************************/ +// Holed Spider Mesh */ +/****************************************/ +//-------------------------------------------------------------- +HoledSpiderMesh::HoledSpiderMesh(int aNumSpokes,int aNumRings) : 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 = new Lump[numLumps]; + cout << "made " << numLumps << " lumps\n"; + springs = new 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; iaddElement(&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(){ + 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); + + lumps = new Lump[numLumps]; + cout << "made " << numLumps << " lumps\n"; + springs = new Spring[numSprings]; + cout << "made " << numSprings << " springs\n"; + + +} +//-------------------------------------------------------------- +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; iaddElement(&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(){ + numSegments = aNumSegments; + radial = false; + + makeComponents(aNumSegments, 0); + + setLumpPositions(); + + makeConnections(); + + makeDefaultScanPath(); + + constrain(0.0,0.0,CONSTRAIN_EDGES); + + setPropagationSpeed(0.6); + + 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; + lumps = new Lump[numLumps]; + springs = new Spring[numSprings]; + +} +//-------------------------------------------------------------- +void LineMesh::setLumpPositions(){ + cout << "LineMesh class setLumpPositions\n"; + // + double hspacing = (1.0 - 0.05)/numSegments; + for(int i = 0; i < numLumps; 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[numLumps-1].constrain(); + }else if (aMode == CONSTRAIN_GRAB_REGION){ + TwoVector diff; + for(int i = 0; iaddElement(&lumps[i], &springs[i]); + } +} +//-------------------------------------------------------------- +/****************************************/ +// GROUNDED LINE Mesh */ +/****************************************/ + +GroundedLineMesh::GroundedLineMesh(int aNumSegments) : 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; + lumps = new Lump[numLumps]; + springs = new Spring[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[numLumps-1].constrain(); + + for(int i = numSegments; i < numLumps; i++){ + lumps[i].constrain(); + } + } + + if (aMode == CONSTRAIN_GRAB_REGION){ + TwoVector diff; + for(int i = 0; iaddElement(&lumps[i], &springs[i]); + } +} +//-------------------------------------------------------------- + +/****************************************/ +// DROPLET Mesh */ +/****************************************/ +// more of a balloon really - filled with compressible gas +DropletMesh::DropletMesh(int aNumSegments) : 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; + lumps = new Lump[numLumps]; + springs = new PressureSpring[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[numLumps-1].constrain(); + } else if (aMode == CONSTRAIN_GRAB_REGION){ + TwoVector diff; + for(int i = 0; iaddElement(&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 < numLumps; i++){ + TwoVector radius; + radius.setCoord(centre.x - lumps[i].position.x, centre.y - lumps[i].position.y ); + radiusMag = radius.norm(); + avRadiusMag += radiusMag; + } + avRadiusMag /= numLumps; + + // 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() { + 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); + + lumps = new Lump[numLumps]; + cout << "made " << numLumps << " lumps\n"; + springs = new Spring[numSprings]; + cout << "made " << numSprings << " springs\n"; +} + +//-------------------------------------------------------------- +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;coladdElement(&lumps[lumpno], &springs[springno]); + lumpno++; + springno++; + } + + +} + diff -r 000000000000 -r c667dfe12d47 scanpath.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scanpath.h Mon Nov 19 13:00:42 2012 +0000 @@ -0,0 +1,76 @@ +/* + * scanpath.h + * springstructure + * + * Created by Robert Tubb on 09/06/2011. + * Copyright 2011 __MyCompanyName__. All rights reserved. + * + */ + +#ifndef _SCANPATHH +#define _SCANPATHH +#include "lump.h" +#include "spring.h" +#include + + +class ScanPath{ +private: + int numElements; + int maxElements; + + bool audioAccessing; + bool updateAccessing; + + //Spring ** springPath; + // springpath - an array of pointers to the springs in the path + // lump path - ditto should be same number + Lump ** lumpPath; + Spring ** springPath; + + double * wavetableNew; + double * wavetableOld; + double * wavetableUpdate; + + double currentLength; + + double frameInterpolator; + double framesPerSample; + + + + +public: + enum scanModes {DISPLACEMENT,SPEED,SPRING_FORCE,YPOS}; + + scanModes scanMode; + double restLength; + + ScanPath(); + ~ScanPath(); + + + void inscribe(double ax,double ay); + void clear(); + + void draw(); + void drawCubic(); + + double getTotalLength(); + int howManyElements(); + + + void addSpring(); + void addLump(); + double getNextSample(); + double getNextSample(double aPhasor); + double getNextSampleCubic(double aPhasor); + // redone stuff + void initWavetables(); + void addElement(Lump* aLump, Spring * aSpring); + + void updateWavetables(); + +}; + +#endif \ No newline at end of file diff -r 000000000000 -r c667dfe12d47 scanpath.mm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scanpath.mm Mon Nov 19 13:00:42 2012 +0000 @@ -0,0 +1,336 @@ +/* + * scanpath.cpp + * springstructure + * + * Created by Robert Tubb on 09/06/2011. + * Copyright 2011 __MyCompanyName__. All rights reserved. + * + */ +#include +#include "scanpath.h" +#include "testApp.h" + +//---------------------------------------------------------------- +ScanPath::ScanPath() : maxElements(2048) { + // construct an empty scanpath + // an ' element ' consists of 1 lump and one spring + + + numElements = 0; // numElements is a dynamic running count of elements when building - a bit crap + springPath = new Spring*[maxElements]; + lumpPath = new Lump*[maxElements]; + + framesPerSample = ofGetFrameRate()/SAMPLE_RATE; + frameInterpolator = 0.0; + currentLength = 0.0; + restLength = 1.0; // ? + + scanMode = DISPLACEMENT; + + initWavetables(); + +} +//---------------------------------------------------------------- +ScanPath::~ScanPath(){ + delete [] springPath; + delete [] lumpPath; + delete [] wavetableNew; + delete [] wavetableOld; + delete [] wavetableUpdate; + cout << "destructed scanpath\n"; +} + +void ScanPath::clear(){ + for(int i = 0; iremoveFromScanPath(); + lumpPath[i]->removeFromScanPath(); + } + numElements = 0; + // cant work? +} +int ScanPath::howManyElements(){ + return numElements; +} +//---------------------------------------------------------------- +void ScanPath::inscribe(double ax, double ay){ + // look at coordinates, add the closest lump and it's connecting string + // if we're further away from current lump + + // check end points of connecting springs, pick closest +} +void ScanPath::draw(){ + // draw the actual waveform in the corner + + int width = 768; + int height = 128; + double sampval = 0.0; + int leftsampnum = 0; + int rightsampnum = 0; + float sampscale = 0.0, prevsampscale = 0.0, interp = 0.0; + + ofSetColor(0, 0, 0); + double step = double(numElements)/width; // how much we are stepping thru wave per pixel + for(int i = 0; i < width; i++){ + + leftsampnum = floor(i * step); // basic nearest neighbour interpolation + rightsampnum = ceil(i*step); + interp = (i*step)-leftsampnum; + if(rightsampnum < numElements){ + sampval = (1 - interp)*wavetableNew[leftsampnum] + interp*wavetableNew[rightsampnum]; + } + sampscale = (sampval * 700) + height/2.0; // centre and scale + ofSetLineWidth(2); + ofLine(sampscale, i, prevsampscale, i-1); // draw a line from pixel to pixel (?) + prevsampscale = sampscale; + } + +} +void drawCubic(){ + +} +//---------------------------------------------------------------- +// add spring +void ScanPath::addSpring(){ + // see add element +} +//---------------------------------------------------------------- +// add lump +void ScanPath::addLump(){ + // see add element +} +//---------------------------------------------------------------- +// add lump +double ScanPath::getTotalLength(){ + // for interesting modulations... + currentLength = 0.0; + for(int i = 0; i < numElements; i++){ + currentLength += springPath[i]->getLength(); + } + return currentLength; + +} +//---------------------------------------------------------------- +void ScanPath::initWavetables(){ + wavetableNew = new double[maxElements]; + wavetableOld = new double[maxElements]; + wavetableUpdate = new double[maxElements]; + + for(int i = 0; i < maxElements; i++){ + wavetableOld[i] = 0.0; + wavetableNew[i] = 0.0; + wavetableUpdate[i] = 0.0; + } + +} +//---------------------------------------------------------------- +void ScanPath::addElement(Lump* aLump, Spring * aSpring){ + // insert ptr to the lump and spring into array + if(numElements >= maxElements){ + cerr << "cannot add any more to scanpath - max elements: 2048\n"; + return; + } + lumpPath[numElements] = aLump; + springPath[numElements] = aSpring; + + aLump->addToScanPath(); + aSpring->addToScanPath(); + + numElements++; +} +//---------------------------------------------------------------- +void ScanPath::updateWavetables(){ + // swap old , new + double * temp; + + switch(scanMode){ + case DISPLACEMENT: + // now fill with new values + for(int i = 0; i < numElements; i++){ + + wavetableUpdate[i] = lumpPath[i]->scanRadialDisplacement()/1.5; + + } + break; + case SPEED: + for(int i = 0; i < numElements; i++){ + wavetableUpdate[i] = lumpPath[i]->scanLumpSpeed(); + } + break; + case SPRING_FORCE: + for(int i = 0; i < numElements; i++){ + wavetableUpdate[i] = springPath[i]->getForceMag(); + } + break; + case YPOS: + for(int i = 0; i < numElements; i++){ + wavetableUpdate[i] = lumpPath[i]->scanYPos(); + } + break; + default: + break; + + + } + + // reset the interp between frames + if(audioAccessing){ + cout << "buffers swapped while update!\n"; + } + updateAccessing = true; + temp = wavetableOld; + wavetableOld = wavetableNew; + wavetableNew = wavetableUpdate; + wavetableUpdate = temp; + updateAccessing = false; + + frameInterpolator = 0.0; + framesPerSample = 2.0*ofGetFrameRate()/SAMPLE_RATE; // attempt to get a reasonable est. of how fast to interp + +} +//---------------------------------------------------------------- +// get next sample +double ScanPath::getNextSample(double aPhasor){ + // move along path, interpolating between points + // move between frames too + double alongPath = aPhasor*double(numElements); + + // indexes for interpolated points + int n0 = floor(alongPath); + int n1 = n0+1; + if(n1 >= numElements){ + n1 = 0; + } + + double frac = alongPath - double(n0); + + audioAccessing = true; + if (updateAccessing){ + cout << "update is accessing while audio is\n"; + } + double oldsample = (1 - frac) * wavetableOld[n0] + frac * wavetableOld[n1]; + + double newsample = (1 - frac) * wavetableNew[n0] + frac * wavetableNew[n1]; + + audioAccessing = false; + + frameInterpolator += framesPerSample; + if(frameInterpolator >= 1.0){ + //cout << "frame interp > 1\n"; + frameInterpolator = 1.0; // just stays outputting new + } + + double sample = (1 - frameInterpolator)*oldsample + frameInterpolator*newsample; + //cout << sample << endl; + // keep within the bounds of acceptability + if(sample > 0.99){ + // cout << "OUCH\n"; + sample = 0.99; + }else if(sample < -0.99){ + sample = -0.99; + } + return sample; + +} +//---------------------------------------------------------------- +// get next sample +double ScanPath::getNextSample(){ + // move along wavetable, no interpolation ie: length of path is pitch + static int n = 0; + double oldsample = wavetableOld[n]; + + double newsample = wavetableNew[n]; + n++; + if (n >= numElements){ + n = 0; + } + + frameInterpolator += framesPerSample; + if(frameInterpolator >= 1.0){ + //cout << "frame interp > 1\n"; + frameInterpolator = 1.0; // just stays outputting new + } + + double sample = (1 - frameInterpolator)*oldsample + frameInterpolator*newsample; + return sample*3.0; + +} +//---------------------------------------------------------------- +//---------------------------------------------------------------- +// get next sample with cubic interpolation +double ScanPath::getNextSampleCubic(double aPhasor){ + // move along path, interpolating between points + // move between frames too + double alongPath = aPhasor*double(numElements); + + // indexes for interpolated points + int n1 = floor(alongPath); + + int n0 = n1-1; + if(n0 < 0){ + n0 = numElements; + } + int n2 = n1+1; + if(n2 >= numElements){ + n2 = 0; + } + int n3 = n2+1; + if(n3 >= numElements){ + n3 = 0; + } + double frac = alongPath - double(n0); + double fracSquared = frac * frac; + double fracCubed = fracSquared * frac; + double a0,a1,a2,a3; + //cout << n0 << endl; + // cubic interp + /* + double y0,double y1, + double y2,double y3, + double mu) + { + double a0,a1,a2,a3,mu2; + + mu2 = mu*mu; + a0 = y3 - y2 - y0 + y1; + a1 = y0 - y1 - a0; + a2 = y2 - y0; + a3 = y1; + + return(a0*mu*mu2+a1*mu2+a2*mu+a3); + */ + a0 = wavetableOld[n3] - wavetableOld[n2] - wavetableOld[n0] + wavetableOld[n1]; + a1 = wavetableOld[n0] - wavetableOld[n1] - a0; // y0 - y1 - a0; + a2 = wavetableOld[n2] - wavetableOld[n0]; // y2 - y0; + a3 = wavetableOld[n1]; + + double oldsample = a0*fracCubed + a1*fracSquared + a2*frac + a3; + + a0 = wavetableNew[n3] - wavetableNew[n2] - wavetableNew[n0] + wavetableNew[n1]; + a1 = wavetableNew[n0] - wavetableNew[n1] - a0; // y0 - y1 - a0; + a2 = wavetableNew[n2] - wavetableNew[n0]; // y2 - y0; + a3 = wavetableNew[n1]; + + double newsample = a0*fracCubed + a1*fracSquared + a2*frac + a3; + + frameInterpolator += framesPerSample; + if(frameInterpolator >= 1.0){ + frameInterpolator = 1.0; // just stays outputting new + } + + double sample = (1 - frameInterpolator)*oldsample + frameInterpolator*newsample; + //cout << sample << endl; + // keep within the bounds of acceptability + + // beef up + sample = sample*3.0; + + if(sample > 0.99){ + sample = 0.99; + }else if(sample < -0.99){ + sample = -0.99; + } + + return sample; + +} +//---------------------------------------------------------------- \ No newline at end of file diff -r 000000000000 -r c667dfe12d47 spring.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spring.h Mon Nov 19 13:00:42 2012 +0000 @@ -0,0 +1,78 @@ +/* + * spring.h + * simplespring + * + * Created by Robert Tubb on 02/06/2011. + * Copyright 2011 __MyCompanyName__. All rights reserved. + * + */ + + + +#ifndef _SPRINGH +#define _SPRINGH +#include "2dvector.h" +#include "ofMain.h" + +class Lump; + +class Spring{ +protected: + double restLength; + double springConst; + + static double maxForce; + static double minForce; + + static int numSprings; + + int myIndex; + + double csquared; + + TwoVector startPoint; + TwoVector endPoint; + + TwoVector force; + Lump * startLumpPtr; + Lump * endLumpPtr; + + bool isInScanPath; + + +public: + static bool forcesOn; + + Spring(); + Spring(double aStartx, double aStarty, double aEndx, double aEndy, double aK); + void updateEndPoints(); + void attachLump(Lump * aLump); + + void calculateForce(); + virtual TwoVector getForce(Lump * aLump); + + void draw(); + void setRestLength(); + void setRestLength(double aLength); + void setSpringConstant(double aK); + double getLength(); + + void addToScanPath(); + void removeFromScanPath(); + Lump * getLumpOnOtherEnd(Lump * alump); + //void checkStuff(); + + // interface for scanner + double getForceMag(); +}; + +class PressureSpring : public Spring{ +public: + PressureSpring(); + TwoVector getForce(Lump * aLump); + +}; + +#endif + + diff -r 000000000000 -r c667dfe12d47 spring.mm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spring.mm Mon Nov 19 13:00:42 2012 +0000 @@ -0,0 +1,225 @@ +/* + * spring.cpp + * simplespring + * + * Created by Robert Tubb on 02/06/2011. + * Copyright 2011 __MyCompanyName__. All rights reserved. + * + */ + +#include "spring.h" +#include "lump.h" +#include "globalForces.h" + +#include +#include "globalUI.h" +extern GlobalForces globalForces; +extern GlobalUI globalUI; +// statics +int Spring::numSprings = 0; +double Spring::maxForce = 0.0; +double Spring::minForce = 0.0; +bool Spring::forcesOn = true; + +//--------------------------------------------------------------- +Spring::Spring(){ + //cout << "constructing a default spring" << endl; + + startPoint.setCoord(0.0,0.0); // dummy values - shouldn't get used + endPoint.setCoord(0.0,0.0); + + springConst = 0.3; // not needed - assume it's one and use 1/c^2 for mass + + isInScanPath = false; + + startLumpPtr = 0; + endLumpPtr = 0; + restLength = 0.0; + myIndex = Spring::numSprings++; + maxForce = 0.005; + minForce = -0.005; + + force.x = 0.0; + force.y = 0.0; + + // width of a spring is proportional to how beefy it is + //ofSetLineWidth(springConst*2); + +} +//--------------------------------------------------------------- +Spring::Spring(double aStartx, double aStarty, double aEndx, double aEndy, double aK){ + // hfsjsfhfshsgr + + +} +//--------------------------------------------------------------- +void Spring::attachLump(Lump * aLump){ + // set position of spring start to mass position + if(startLumpPtr == 0){ + startLumpPtr = aLump; + + }else if(endLumpPtr == 0){ + endLumpPtr = aLump; + //cout << "----------------------------------------end point attached to " << endLumpPtr << endl; + updateEndPoints(); // now both ends attached - set the spring start and end points to the relevant lumps + setRestLength(); // the default length for the spring can be set + }else{ + cerr << "PERROR can't attach lump to this spring cos it's already attached both ends" << endl; + } +} +Lump * Spring::getLumpOnOtherEnd(Lump * alump){ + if (startLumpPtr == alump){ + return endLumpPtr; + + }else if (endLumpPtr == alump){ + return startLumpPtr; + } + cout << "getLumpOnOtherEnd: this should never happen" << endl; + + return NULL; +} +//--------------------------------------------------------------- +void Spring::setRestLength(){ + + //set rest length according to what the length is now + TwoVector diff(startPoint.x - endPoint.x, startPoint.y - endPoint.y); + + restLength = diff.norm(); + + //restLength = 0.0; + +} + +void Spring::setRestLength(double aLength){ + restLength = aLength; + +} +void Spring::setSpringConstant(double aK){ + springConst = aK; +} +double Spring::getLength(){ + return startPoint.distanceTo(endPoint); +} +//--------------------------------------------------------------- +void Spring::updateEndPoints(){ + // tweak the end points eg: from mass move + if (startLumpPtr ==0 || endLumpPtr == 0){ + cerr << "PERROR spring masses not attached!" << endl; + }else{ + startPoint = startLumpPtr->position; + //cout << "start pt x = " << startPoint.x << ", y = " << startPoint.y << endl; + endPoint = endLumpPtr->position; + } +} +//--------------------------------------------------------------- +void Spring::calculateForce(){ + + if(!Spring::forcesOn){ + force.x = 0.0; + force.y = 0.0; + return; + } + // get force from the extension + double xe = endPoint.x - startPoint.x; + double ye = endPoint.y - startPoint.y; + double extendedLength = sqrt(xe*xe + ye*ye); + + // doing this enables us to get away with only one division etc + double FF = springConst*(restLength/extendedLength - 1.0); + + force.x = FF*xe; + force.y = FF*ye; + +} +//--------------------------------------------------------------- +TwoVector Spring::getForce(Lump * aLump){ // pass in the lump that we want the force for + + // above force refers to force at END force at START negative of this... + if(aLump == startLumpPtr){ + TwoVector rForce; + rForce.setCoord(-force.x, -force.y); + return rForce; + }else if(aLump == endLumpPtr ){ + return force; + } + cout << "PERROR this lump is not attached to this spring\n"; + + return force; + +} + + +//--------------------------------------------------------------- +void Spring::draw(){ + + int sxpos = startPoint.x * ofGetHeight() + globalUI.borderSize; // TODO make this nicer + int sypos = startPoint.y * ofGetHeight(); + //cout << "start position = " << sxpos << "," << sypos << endl; + int expos = endPoint.x * ofGetHeight() + globalUI.borderSize; + int eypos = endPoint.y*ofGetHeight(); + //cout << "end position = " << expos << "," << eypos << endl; + + if(isInScanPath){ + ofSetLineWidth(4); + ofSetColor(0, 255, 0); + }else{ + ofSetLineWidth(1); + ofSetColor(0, 0, 255); + } + + ofLine(sxpos, sypos, expos, eypos); +} +//--------------------------------------------------------------- +double Spring::getForceMag(){ + return force.norm(); +} +//--------------------------------------------------------------- +void Spring::addToScanPath(){ + isInScanPath = true; +} +//--------------------------------------------------------------- +void Spring::removeFromScanPath(){ + isInScanPath = false; +} +//--------------------------------------------------------------- +/* +void Spring::checkStuff(){ + cout << "checking\n"; +} +*/ +PressureSpring::PressureSpring(){ + cout << "constructing a pressure spring for droplet mesh\n"; +} + +//--------------------------------------------------------------- +TwoVector PressureSpring::getForce(Lump * aLump){ // pass in the lump that we want the force for + + // PRESSURE STUFF + // by convention a positive pressure allways acts to the left as looking from start to end point + // ie: Droplet MUST be constructed in clockwise fashion + // we rotate the spring vector anti clockwise pi/2 radians this gives us the force vector + // x = -y , y = x except damn y coordinate points downwards so handedness is reversed + + // conveniently this is proportional to length just as pressure would be + //cout << "pressure spring get force\n"; + double xe = endPoint.x - startPoint.x; + double ye = endPoint.y - startPoint.y; + // above force refers to force at END force at START negative of this... + if(aLump == startLumpPtr){ + TwoVector rForce; + rForce.setCoord(-force.x, -force.y); + rForce.x += ye * globalForces.pressure * 0.5; + rForce.y -= xe * globalForces.pressure * 0.5; + return rForce; + }else if(aLump == endLumpPtr ){ + TwoVector rForce; + rForce.setCoord(force.x, force.y); + rForce.x += ye * globalForces.pressure * 0.5; + rForce.y -= xe * globalForces.pressure * 0.5; + return rForce; + } + cout << "PERROR this lump is not attached to this spring\n"; + + return force; + +} \ No newline at end of file diff -r 000000000000 -r c667dfe12d47 testApp.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/testApp.h Mon Nov 19 13:00:42 2012 +0000 @@ -0,0 +1,121 @@ +// oF 072 + +#ifndef _TEST_APP +#define _TEST_APP + +#include "ofMain.h" +#include "ofxiPhone.h" +#include "ofxiPhoneExtras.h" + +#include "2dvector.h" +#include "lump.h" +#include "spring.h" +#include +#include "mesh.h" +#include "dsptools.h" + +#include "ofxUI.h" +#include "ofxOsc.h" +#include "globalForces.h" +#include "globalUI.h" + +#define SAMPLE_RATE 44100 +// listen on port 12345 +#define INPORT 12345 +#define OUTPORT 54321 +#define HOST "192.168.1.3" +#define NUM_MSG_STRINGS 20 + +// defining this compiles it for ios otherwise osx +#define IPAD + + + +class testApp : public ofxiPhoneApp { + +public: + int timesOpened; + bool audioOn; + bool drawingOn; + + bool paused; + //bool inscribeScanPathMode; + bool meshWaitingToDelete; + Mesh *theMesh; + + DSPTools mydspTools; + + double timeStep; + + double pitch; + double phasorIncr; + + + + int numTouches; + + void setup(); + void update(); + void draw(); + void exit(); + + void loadLogXML(); + void saveLogXML(); + + void touchDown(ofTouchEventArgs &touch); + void touchMoved(ofTouchEventArgs &touch); + void touchUp(ofTouchEventArgs &touch); + void touchDoubleTap(ofTouchEventArgs &touch); + void addTouch(); + void removeTouch(); + + void UIcallBack(int buttID); + + + void keyPressed (int key); + void keyReleased(int key); + void mouseMoved(int x, int y ); + void mouseDragged(int x, int y, int button); + void mousePressed(int x, int y, int button); + void mouseReleased(int x, int y, int button); + + void restartAudioStream(); + void audioRequested (float * input, int bufferSize, int nChannels); + void audioReceived(float * input, int bufferSize, int nChannels); + + void handleMessages(); + void drawMessages(); + + void setupMesh(); + void regenerateMesh(string meshType, int dim1 = 3, int dim2 = 3); + void deleteMesh(); + + ofxOscReceiver receiver; + ofxOscSender sender; + ofTrueTypeFont font; + + int current_msg_string; + string msg_strings[NUM_MSG_STRINGS]; + float timers[NUM_MSG_STRINGS]; + + int mouseX, mouseY; + string mouseButtonState; + + ofxiPhoneKeyboard * keyboard; + + // ofxUI stuff + + ofxUICanvas *guiL; + ofxUICanvas *guiR; + void guiLEvent(ofxUIEventArgs &e); + void guiREvent(ofxUIEventArgs &e); + void setupGui(); + void drawSidePanels(); + + ofxUILabel *counter; + +}; + + + +#endif \ No newline at end of file diff -r 000000000000 -r c667dfe12d47 testApp.mm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/testApp.mm Mon Nov 19 13:00:42 2012 +0000 @@ -0,0 +1,1302 @@ +#include "testApp.h" + +extern GlobalForces globalForces; +extern GlobalUI globalUI; +//-------------------------------------------------------------- +void testApp::setup(){ + theMesh = NULL; + ofxiPhoneSetOrientation(OFXIPHONE_ORIENTATION_LANDSCAPE_RIGHT); + ofxAccelerometer.setup(); + + // listen on the given port + cout << "listening for osc messages on port " << INPORT << "\n"; + receiver.setup( INPORT ); + sender.setup( HOST, OUTPORT ); + + ofBackground(0, 0, 0); + ofSetFullscreen(true); + ofSetFrameRate(60); + + timeStep = 1/ofGetFrameRate(); + + ofEnableSmoothing(); + ofEnableAlphaBlending(); + + string url = "http://www.rootnot.co.uk/"; + ofURLFileLoader fileLoader; + ofHttpResponse resp; + resp = fileLoader.get(url); + cout << "HTTP STATUS " << resp.status << "\n"; + cout << "HTTP ERROR " << resp.error << "\n"; + cout << "HTTP DATA " << resp.data << "\n"; + + + + pitch = 60.0; + phasorIncr = pitch/SAMPLE_RATE; + globalForces.gravityAmt = 0.0; + globalForces.avFilterAmt = 0.01; + + // + ofSoundStreamSetup(2,0,this, SAMPLE_RATE,256, 2); + + // if we want things to start immediately + paused = false; + audioOn = true; + drawingOn = true; + + + setupGui(); + + + setupMesh(); + +} + +void testApp::setupMesh(){ + // SET UP THE MESH STRUCTURE + // + // + // ERRR + /* + if(audioOn){ + audioOn = false; + meshWaitingToDelete = true; + setupMesh(); + } + */ + static int type = 0; + + if (type % 4 == 0){ + // different for iphone + if(ofGetWidth() == 480){ //PHONE + theMesh = new SpiderCrossMesh(60,5); + }else{ //PAD + //theMesh = new SpiderCrossMesh(140,5); + delete theMesh; + theMesh = new SpiderCrossMesh(100,9); + } + }else if(type % 4 ==1){ + delete theMesh; + theMesh = new SpiderCrossMesh(30,30); + }else if(type % 4 ==2){ + delete theMesh; + theMesh = new SpiderCrossMesh(4,4); + }else if(type % 4 ==3){ + delete theMesh; + theMesh = new DropletMesh(100); + } + type++; + //theMesh = new LineMesh(123); + //theMesh = new GroundedLineMesh(400); + //theMesh = new TriangleMesh(40,40); + //theMesh = new LineMesh(100); + + // SET CONSTANTS UP FOR MESH + theMesh->setSpringConstant(0.8); + theMesh->setMass(1); + theMesh->setFriction(0.99991); + + globalUI.touchMode = globalUI.GRAB; + theMesh->scanPath->scanMode = theMesh->scanPath->DISPLACEMENT; + numTouches = 0; +} +//-------------------------------------------------------------- +//-------------------------------------------------------------- +// GUI stuff +//-------------------------------------------------------------- +//-------------------------------------------------------------- +void testApp::loadLogXML(){ + ofxXmlSettings XMLlog; + + string message; + if( XMLlog.loadFile(ofxiPhoneGetDocumentsDirectory() + "wabletlogs.xml") ){ + message = "mySettings.xml loaded from documents folder!"; + + }else if( XMLlog.loadFile("Logs/wabletlogs.xml") ){ + message = "mySettings.xml loaded from data folder!"; + + + }else{ + message = "unable to load mySettings.xml check data/ folder"; + return; + } + cout << message << "\n"; + + // get all the values from log + timesOpened = XMLlog.getValue("wabletlogs:timesOpened", 0); + timesOpened++; + + stringstream ss; + ss << timesOpened; + counter->setLabel(ss.str()); + +} +//-------------------------------------------------------------- +void testApp::saveLogXML(){ + ofxXmlSettings XMLlog; + + XMLlog.setValue("wabletlogs:timesOpened", timesOpened); + + if(XMLlog.saveFile(ofxiPhoneGetDocumentsDirectory() + "wabletlogs.xml")){ + cout << "Saved wabletlogs.XML in iphone documents OK"; + }else if(XMLlog.saveFile("Logs/wabletlogs.xml")){ + cout << "Saved wabletlogs.XML in data folder"; + }else{ + cout << "wabletlogs file did not save :("; + } + + + +} +//-------------------------------------------------------------- +void testApp::setupGui(){ + float xInit = OFX_UI_GLOBAL_WIDGET_SPACING; + float length = 128-xInit*2; + + + float dim = 42; + + // LEFT GUI ------------------ + guiL = new ofxUICanvas(0,0,128,ofGetHeight()); + guiL->addSpacer(length-xInit, 2); + guiL->addWidgetDown(new ofxUILabel("Physics", OFX_UI_FONT_LARGE)); + + ofxUIWidget *slider; + slider = guiL->addWidgetDown(new ofxUISlider(length,dim,0.0,0.8,0.4,"SPRING K")); + slider->setDrawPadding(true); + slider->setColorFill(ofColor(0,0,255)); + slider->setColorFillHighlight(ofColor(0,0,255)); + + slider = guiL->addWidgetDown(new ofxUISlider(length,dim, 0.0, 4.0, 0.0, "GRAVITY")); + slider->setDrawPadding(true); + slider->setColorFill(ofColor(0,0,255)); + slider->setColorFillHighlight(ofColor(0,0,255)); + + slider = guiL->addWidgetDown(new ofxUISlider(length,dim,0.0,0.3,0.0,"HOMING")); + slider->setDrawPadding(true); + slider->setColorFill(ofColor(0,0,255)); + slider->setColorFillHighlight(ofColor(0,0,255)); + + slider = guiL->addWidgetDown(new ofxUISlider(length,dim, 0.0, 0.5, 0.01, "SMOOTHING")); + slider->setDrawPadding(true); + slider->setColorFill(ofColor(0,0,255)); + slider->setColorFillHighlight(ofColor(0,0,255)); + + + + guiL->addSpacer(length-xInit, 2); + + + counter = guiL->addLabel("Counter"); + counter->setLabel("can set"); + + guiL->setWidgetPosition(OFX_UI_WIDGET_POSITION_DOWN); + slider = guiL->addSlider("PITCH", 20.0, 100.0, 80.0, length, 370); + slider->setDrawPadding(true); + slider->setColorFill(ofColor(0,0,255)); + slider->setColorFillHighlight(ofColor(0,0,255)); + + ofAddListener(guiL->newGUIEvent, this, &testApp::guiLEvent); + guiL->loadSettings("GUI/guiSettings.xml"); + + // RIGHT GUI ----------------------- + + guiR = new ofxUICanvas(ofGetWidth()-128, 0, 128, ofGetHeight()); + + + guiR->addSpacer(length-xInit, 2); + + vector names; + names.push_back("GRAB"); + names.push_back("FORCE"); + names.push_back("STICK"); + names.push_back("UNSTICK"); + names.push_back("NEWSCAN"); + names.push_back("SINE"); + + ofxUIRadio* radio; + radio = guiR->addRadio("TOUCH MODE", names, OFX_UI_ORIENTATION_VERTICAL, dim, dim); + radio->setDrawPadding(true); + radio->setColorFill(ofColor(0,0,255)); + radio->setColorFillHighlight(ofColor(0,0,255)); + + slider = guiR->addWidgetDown(new ofxUISlider(length,dim, -0.2, 4.0, 1.0, "TOUCH AMT")); + slider->setDrawPadding(true); + slider->setColorFill(ofColor(0,0,255)); + slider->setColorFillHighlight(ofColor(0,0,255)); + + guiR->addSpacer(length-xInit, 2); + + guiR->addButton("RESET", false, dim, dim); + guiR->addButton("MESH", false, dim, dim); + guiR->addToggle("PAUSE", false, dim, dim); + + ofAddListener(guiR->newGUIEvent, this, &testApp::guiREvent); + guiR->loadSettings(ofxiPhoneGetDocumentsDirectory() + "guiSettings.xml"); + radio->activateToggle("GRAB"); + + // SETUP MESH GUI--------------------------- + + + // load gui settings?? + loadLogXML(); +} +//-------------------------------------------------------------- +void testApp::UIcallBack(int buttID){ + cout << " BUTT ID " << buttID << "\n"; + +} +//-------------------------------------------------------------- +void testApp::guiREvent(ofxUIEventArgs &e) +{ + if(e.widget->getName() == "TOUCH MODE") + { + cout << "TOUCH MODE"; + + } + if(e.widget->getName() == "GRAB"){ + cout << "GRAB"; + globalUI.touchMode = globalUI.GRAB; + }else if(e.widget->getName() == "FORCE"){ + + globalUI.touchMode = globalUI.FORCE_FIELD; + }else if(e.widget->getName() == "STICK"){ + + globalUI.touchMode = globalUI.CONSTRAIN; + }else if(e.widget->getName() == "UNSTICK"){ + + globalUI.touchMode = globalUI.UNCONSTRAIN; + }else if(e.widget->getName() == "NEWSCAN"){ + + theMesh->resetPositions(); + theMesh->resetVelocities(); + globalUI.touchMode = globalUI.INSCRIBE_PATH; + theMesh->clearScanPath(); + theMesh->update(); + }else if(e.widget->getName() == "SINE"){ + globalUI.touchMode = globalUI.SPATIAL_HARMONIC; + }else if(e.widget->getName() == "RESET"){ + theMesh->resetAll(); + numTouches = 0; + }else if(e.widget->getName() == "MESH"){ + // ???? + cout << ((ofxUIButton *)e.widget)->getValue(); + + if( ((ofxUIButton *)e.widget)->getValue() == 1) setupMesh(); + }else if(e.widget->getName() == "PAUSE"){ + paused = !paused; + }else if(e.widget->getName() == "TOUCH AMT"){ + ofxUISlider *slider = (ofxUISlider *) e.widget; + slider->setDrawPadding(true); + globalForces.touchStrength = slider->getScaledValue(); + } + +} +//-------------------------------------------------------------- +void testApp::guiLEvent(ofxUIEventArgs &e) +{ + if(e.widget->getName() == "SPRING K") + { + ofxUISlider *slider = (ofxUISlider *) e.widget; + + theMesh->setSpringConstant( slider->getScaledValue() ); + }else if(e.widget->getName() == "GRAVITY") + { + ofxUISlider *slider = (ofxUISlider *) e.widget; + slider->setDrawPadding(true); + globalForces.gravityAmt = slider->getScaledValue(); + //radio->getScaledValue() ); + }else if(e.widget->getName() == "HOMING") + { + ofxUISlider *slider = (ofxUISlider *) e.widget; + globalForces.homingAmt = slider->getScaledValue(); + //radio->getScaledValue() ); + }else if(e.widget->getName() == "SMOOTHING") + { + + + ofxUISlider *slider = (ofxUISlider *) e.widget; + globalForces.avFilterAmt = slider->getScaledValue(); + //radio->getScaledValue() ); + }else if(e.widget->getName() == "FRICTION") + { + + + ofxUISlider *slider = (ofxUISlider *) e.widget; + theMesh->setFriction(slider->getScaledValue()); + //radio->getScaledValue() ); + }else if(e.widget->getName() == "PITCH") + { + + + ofxUISlider *slider = (ofxUISlider *) e.widget; + pitch = slider->getScaledValue(); + //radio->getScaledValue() ); + } + + +} +//-------------------------------------------------------------- +//-------------------------------------------------------------- +// App running stuff +//-------------------------------------------------------------- +//-------------------------------------------------------------- +//-------------------------------------------------------------- +void testApp::exit(){ + ofSoundStreamClose(); + + // save everything... + guiL->saveSettings(ofxiPhoneGetDocumentsDirectory() + "guiSettings.xml"); + delete guiL; + saveLogXML(); + + if(theMesh != NULL){ + delete theMesh; + } + +} + +//-------------------------------------------------------------- +void testApp::update(){ + + + + if(!paused){ + if (theMesh != NULL){ + theMesh->update(); + + } + // BASIC STRING basicString->update(); + } + handleMessages(); // !?!?? + //cout << "UPDATE frame rate = " << ofGetFrameRate() << " sec\n"; +} + +//-------------------------------------------------------------- +void testApp::draw(){ + if(drawingOn){ + if(theMesh != NULL) + theMesh->draw(); + + } + //cout << "frame rate = " << ofGetFrameRate() << " sec\n"; + //drawMessages(); + + drawSidePanels(); +} +//-------------------------------------------------------------- +// background for UI +void testApp::drawSidePanels(){ + ofSetColor(123, 123, 123); + ofRect(0, 0, 128, ofGetHeight()); + ofRect(ofGetWidth()-128, 0, 128, ofGetHeight()); +} +//-------------------------------------------------------------- +void testApp::drawMessages(){ + for ( int i=0; itoggleSyrup(true); + } + if (key == 'f'){ + theMesh->toggleSpringForces(false); + } + if (key == '='){ + theMesh->increasePropagationSpeed(); + } + if (key == '-'){ + theMesh->decreasePropagationSpeed(); + } + if (key == 'a'){ + if(audioOn){ + audioOn = false; + }else{ + audioOn = true; + } + } + if (key == 'r'){ + theMesh->resetPositions(); + theMesh->resetVelocities(); + + } + if (key == 'e'){ + theMesh->constrain(.0,.0,Mesh::CONSTRAIN_EDGES); + } + if (key == 'c'){ + theMesh->constrain(.0,.0,Mesh::CONSTRAIN_CORNERS); + } + if (key == 'u'){ + theMesh->unconstrain(); + } + if (key == 'l'){ + theMesh->setRestLength(); + } + if (key == '0'){ + theMesh->zeroRestLength(); + } + if (key == 'd'){ + drawingOn = !drawingOn; + } +} + +//--------------------------------------------------------------- + +void testApp::keyReleased(int key){ + if (key == 's'){ + theMesh->toggleSyrup(false); + } + if (key == 'f'){ + theMesh->toggleSpringForces(true); + } +} + + + +//-------------------------------------------------------------- +//-------------------------------------------------------------- +//------------------------TOUCH--------------------------------- +//-------------------------------------------------------------- +//-------------------------------------------------------------- + + + + +void testApp::touchDown(ofTouchEventArgs &touch){ + if(theMesh == NULL) return; + + double dax, day; + // touchDown + //cout << "touchDown ID: " << touch.id << endl; + if(globalUI.handleTouchDown(touch.x, touch.y)) return; + + + addTouch(); + + + + dax = (double(touch.x) - globalUI.borderSize)/ofGetHeight(); + day = double(touch.y)/ofGetHeight(); + + switch (globalUI.touchMode){ + case globalUI.GRAB: + theMesh->grab(dax,day,touch.id); + break; + case globalUI.INSCRIBE_PATH: + // start a new path, with touch id? ie:polyphonic paths!??! + break; + case globalUI.FORCE_FIELD: + globalForces.createForceTouchPoint(dax,day, touch.id); + break; + case globalUI.SPATIAL_HARMONIC: + // work out + theMesh->spatialHarmonic(numTouches, 0); + break; + case globalUI.CONSTRAIN: + theMesh->constrain(dax,day,Mesh::CONSTRAIN_GRAB_REGION); + break; + case globalUI.UNCONSTRAIN: + theMesh->unconstrain(dax,day,Mesh::CONSTRAIN_GRAB_REGION); + break; + case globalUI.VIBRATE: + break; + default: + + break; + } + + +} + +//-------------------------------------------------------------- +void testApp::touchMoved(ofTouchEventArgs &touch){ + if(theMesh == NULL) return; + if(globalUI.handleTouchMove(touch.x, touch.y)) return; + //cout << "touchMoved ID: " << touch.id << endl; + + + double dax, day; + dax = (double(touch.x) - globalUI.borderSize)/ofGetHeight(); + day = double(touch.y)/ofGetHeight(); + + + + /* + if(kslider->checkForTouch(touch.x, touch.y)){ + cout << "kslider touched"; + kslider->adjust(touch.x,touch.y); + return; + } + */ + + switch (globalUI.touchMode){ + case globalUI.GRAB: + theMesh->drag(dax,day,touch.id); + break; + case globalUI.INSCRIBE_PATH: + theMesh->inscribeScanPath(dax,day); + break; + case globalUI.FORCE_FIELD: + //theMesh->forceField(dax,day,touch.id); + cout << "move force tp\n"; + globalForces.moveForceTouchPoint(dax,day, touch.id); + break; + case globalUI.SPATIAL_HARMONIC: + // makes no sense + break; + case globalUI.CONSTRAIN: + theMesh->constrain(dax,day,Mesh::CONSTRAIN_GRAB_REGION); + break; + case globalUI.UNCONSTRAIN: + theMesh->unconstrain(dax,day,Mesh::CONSTRAIN_GRAB_REGION); + break; + case globalUI.VIBRATE: + break; + default: + + break; + } +} + +//-------------------------------------------------------------- +void testApp::touchUp(ofTouchEventArgs &touch){ + if(theMesh == NULL) return; + if(globalUI.handleTouchUp(touch.x, touch.y)) return; + + + removeTouch(); + + + switch (globalUI.touchMode){ + case globalUI.GRAB: + theMesh->unGrab(touch.id); + break; + case globalUI.INSCRIBE_PATH: + theMesh->disconnectDraw(); + break; + case globalUI.FORCE_FIELD: + globalForces.removeForceTouchPoint(touch.id); + break; + case globalUI.SPATIAL_HARMONIC: + break; + case globalUI.CONSTRAIN: + + break; + case globalUI.UNCONSTRAIN: + + break; + case globalUI.VIBRATE: + break; + default: + + break; + } + +} + +//-------------------------------------------------------------- +void testApp::touchDoubleTap(ofTouchEventArgs &touch){ + // pretty much useless. + /* + ofxOscMessage m; + m.setAddress( "/test" ); + m.addIntArg( 1 ); + m.addFloatArg( 3.5f ); + m.addStringArg( "hello" ); + m.addFloatArg( ofGetElapsedTimef() ); + sender.sendMessage( m ); + */ + + //ofxiPhoneScreenGrab(NULL); +} + + +//-------------------------------------------------------------- +void testApp::addTouch(){ + numTouches++; + if(numTouches >= 5){ + theMesh->toggleHome(true); + }else{ + theMesh->toggleHome(false); + } + cout << "numtouches " << numTouches << endl; +} +//-------------------------------------------------------------- +void testApp::removeTouch(){ + numTouches--; + +} + +//-------------------------------------------------------------- +//-------------------------------------------------------------- + // MOUSE - NOT NEEDED FOR IPAD +//-------------------------------------------------------------- +//-------------------------------------------------------------- +void testApp::mouseMoved(int x, int y ){ + //cout << "mouse moved " << x << 'y' << y << '\n'; + /* + double dax, day; + + // normalise + dax = double(x)/ofGetWidth(); + day = double(y)/ofGetHeight(); + + theMesh->checkHover(dax,day); + + */ +} + +//-------------------------------------------------------------- +void testApp::mouseDragged(int x, int y, int button){ + //cout << "mouse DRAGGED " << x << 'y' << y << '\n'; + /* + double dax, day; + dax = double(x)/ofGetWidth(); + day = double(y)/ofGetHeight(); + + if(button == 0){ + if(!paused){ + theMesh->drag(dax,day, 0); + }else{ + // pause mode - draw a scan path! + + theMesh->drawScanPath(dax,day); + audioOn = false; + } + }else if(button == 2){ + + } + */ + +} + +//-------------------------------------------------------------- +void testApp::mousePressed(int x, int y, int button){ + /* + double dax, day; + // normalise + dax = double(x)/ofGetWidth(); + day = double(y)/ofGetHeight(); + + if(button == 0){ + if(paused){ + // draw a path + audioOn = false; + theMesh->deleteScanPath(); + }else{ + theMesh->grab(dax,day, 0); + } + }else if(button == 2){ + theMesh->constrain(dax,day,Mesh::CONSTRAIN_GRAB_REGION); + }else{ + cout << "OTHER BUTTON?\n"; + } + */ + + +} + +//-------------------------------------------------------------- +void testApp::mouseReleased(int x, int y, int button){ + /* + if(button == 0){ + theMesh->unGrab(0); + }else if(button == 2){ + }else{ + cout << "butt other"; + } + */ + +} +//-------------------------------------------------------------- +//-------------------------------------------------------------- +//-------------------------AUDIO----------------------------- +//-------------------------------------------------------------- +//-------------------------------------------------------------- +void testApp::restartAudioStream(){ + ofSoundStreamStart(); + audioOn = true; + +} +//-------------------------------------------------------------- + +void testApp::audioReceived(float * input, int bufferSize, int nChannels){ + +} +//-------------------------------------------------------------- +void testApp::audioRequested (float * output, int bufferSize, int nChannels){ + float sample; + static double phasor; + static double sinePhasor; + + + phasorIncr = pitch/SAMPLE_RATE; + + if(meshWaitingToDelete){ + meshWaitingToDelete = false; + return; + } + + if(audioOn && theMesh != NULL){ + for (int i = 0; i < bufferSize; i++){ + sinePhasor = sin(2*PI*phasor); + + sample = float(theMesh->scanPath->getNextSample(phasor)); + //sample = float(theMesh->scanPath->getNextSample()); + // BASIC STRING sample = float(basicString->getNextSample(phasor)); + + // hipass to get rid of DC + //sample = mydspTools.highpass1(sample); + sample = mydspTools.butter(sample); + output[i*nChannels ] = sample*globalForces.volume; + output[i*nChannels + 1] = sample*globalForces.volume; + + phasor += phasorIncr; + + if(phasor >= 1.0){ + phasor -= 1.0; + } + } + }else{ + for (int i = 0; i < bufferSize; i++){ + output[i*nChannels ] = 0.0; + output[i*nChannels + 1] = 0.0; + } + } + + +} + +//-------------------------------------------------------------- +//-------------------------------------------------------------- +//-------------------------OSC MSGS-------------------------------- +//-------------------------------------------------------------- +//-------------------------------------------------------------- + +void testApp::handleMessages(){ + + // hide old messages + for ( int i=0; i 0){ + cout << "note pitch = " << pitch << endl; + pitch = 440.0/pow(2.0,((69.0 - double(note))/12.0)); + theMesh->hit(0.5,0.5,velocity,globalForces.excitationType,globalForces.excitationShape); + }else{ + theMesh->damp(); + } + + + } + else if ( m.getAddress() == "excitationStrength" ) + { + globalForces.excitationStrength = m.getArgAsFloat(0); + + + } + + else if ( m.getAddress() == "excitationType" ) + { + + // set MIDI twanger mode + if(m.getArgAsString(0) == "POSITION"){ + globalForces.excitationType = GlobalForces::POSITION; + + }else if (m.getArgAsString(0) == "VELOCITY"){ + cout << "excitationType = VELOCITY" << endl; + globalForces.excitationType = GlobalForces::VELOCITY; + } + } + else if ( m.getAddress() == "excitationShape" ) + { + + // set MIDI twanger twang shape + if(m.getArgAsString(0) == "NOISE"){ + globalForces.excitationShape = GlobalForces::NOISE; + + }else if (m.getArgAsString(0) == "GAUSS"){ + cout << "excitationType = GAUSS" << endl; + globalForces.excitationShape = GlobalForces::GAUSS; + }else if (m.getArgAsString(0) == "SINE"){ + cout << "excitationType = SINE" << endl; + globalForces.excitationShape = GlobalForces::SINE; + + } + // set size + globalForces.exciteShapeX = m.getArgAsInt32(1); + globalForces.exciteShapeY = m.getArgAsInt32(2); + } + + // touchModes: {GRAB,FORCE_FIELD,SPATIAL_HARMONIC,CONSTRAIN,VIBRATE,INSCRIBE_PATH}; + else if ( m.getAddress() == "touchMode" ) + { + + // set touch mode + if(m.getArgAsString(0) == "GRAB"){ + globalUI.touchMode = globalUI.GRAB; + + }else if (m.getArgAsString(0) == "FORCE_FIELD"){ + cout << "touchMode = FORCE_FIELD" << endl; + globalUI.touchMode = globalUI.FORCE_FIELD; + }else if (m.getArgAsString(0) == "SPATIAL_HARMONIC"){ + //cout << "touchMode = SPATIAL_HARMONIC" << endl; + globalUI.touchMode = globalUI.SPATIAL_HARMONIC; + }else if (m.getArgAsString(0) == "CONSTRAIN"){ + //cout << "touchMode = CONSTRAIN" << endl; + globalUI.touchMode = globalUI.CONSTRAIN; + }else if (m.getArgAsString(0) == "VIBRATE"){ + //cout << "touchMode = VIBRATE" << endl; + globalUI.touchMode = globalUI.VIBRATE; + }else if (m.getArgAsString(0) == "UNCONSTRAIN"){ + //cout << "touchMode = UNCONSTRAIN" << endl; + globalUI.touchMode = globalUI.UNCONSTRAIN; + }else if (m.getArgAsString(0) == "INSCRIBE_PATH"){ + //cout << "touchMode = INSCRIBE_PATH" << endl; + globalUI.touchMode = globalUI.INSCRIBE_PATH; + theMesh->clearScanPath(); + } + } + + else if ( m.getAddress() == "pause" ) + { + paused = !paused; + } + else if ( m.getAddress() == "touchStrength" ) + { + for ( int i=0; iscanPath->scanMode = theMesh->scanPath->DISPLACEMENT; + }else if (m.getArgAsString(0) == "SPEED"){ + + cout << "scanMode = SPEED" << endl; + theMesh->scanPath->scanMode = theMesh->scanPath->SPEED; + }else if (m.getArgAsString(0) == "SPRING_FORCE"){ + cout << "scanMode = SPRING_FORCE" << endl; + theMesh->scanPath->scanMode = theMesh->scanPath->SPRING_FORCE; + }else if (m.getArgAsString(0) == "YPOS"){ + cout << "scanMode = YPOS" << endl; + theMesh->scanPath->scanMode = theMesh->scanPath->YPOS; + }else if (m.getArgAsString(0) == "OTHER"){ + cout << "scanMode = OTHER" << endl; + //scanMode = OTHER; + } + } + else if ( m.getAddress() == "inscribeScanPath" ) + { + if (globalUI.touchMode != globalUI.INSCRIBE_PATH){ + theMesh->resetPositions(); + theMesh->resetVelocities(); + paused = true; + audioOn = false; + globalUI.touchMode = globalUI.INSCRIBE_PATH; + theMesh->clearScanPath(); + theMesh->update(); + }else{ + + paused = false; + audioOn = true; + globalUI.touchMode = globalUI.GRAB; + } + + } + else if ( m.getAddress() == "clearScanPath" ) + { + theMesh->clearScanPath(); + } + else if ( m.getAddress() == "toggleForce" ) + { + theMesh->toggleSpringForces(); + } + else if ( m.getAddress() == "toggleGravity" ) + { + theMesh->toggleGravity(); + } + else if ( m.getAddress() == "resetPositions" ) + { + theMesh->resetPositions(); + } + else if ( m.getAddress() == "resetVelocities" ) + { + theMesh->resetVelocities(); + } + + + else if ( m.getAddress() == "reset" ) + { + theMesh->resetPositions(); + theMesh->resetVelocities(); + theMesh->update(); + + } + else if ( m.getAddress() == "deleteMesh" ) + { + deleteMesh(); + } + else if ( m.getAddress() == "regenerate" ) + { + // ignore it - the mesh is not deleted + + + } + else if ( m.getAddress() == "zeroRestLength" ) + { + theMesh->zeroRestLength(); + } + else if ( m.getAddress() == "setThisRestLength" ) + { + theMesh->setRestLength(); + } + else if ( m.getAddress() == "setZeroAudioLine" ) + { + theMesh->setZeroAudioLine(); + } + else if ( m.getAddress() == "speedLimit" ) + { + for ( int i=0; itoggleSyrup(); + } + else if ( m.getAddress() == "toggleAudio" ) + { + if(audioOn){ + ofSoundStreamStop(); + audioOn = false; + }else{ + restartAudioStream(); + } + + } + + else if ( m.getAddress() == "constrainEdges" ) + { + theMesh->constrain(.0,.0,Mesh::CONSTRAIN_EDGES); + } + else if ( m.getAddress() == "constrainCorners" ) + { + theMesh->constrain(.0,.0,Mesh::CONSTRAIN_CORNERS ); + } + else if ( m.getAddress() == "unconstrain" ) + { + theMesh->unconstrain(); + } + else if ( m.getAddress() == "mass" ) + { + for ( int i=0; isetMass(m.getArgAsInt32( i )); + } + else if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT ) + { + theMesh->setMass(m.getArgAsFloat( i )<0.01?0.01:m.getArgAsFloat( i ) ); + } + + } + } + else if ( m.getAddress() == "springk" ) + { + for ( int i=0; isetSpringConstant(m.getArgAsInt32( i )); + } + else if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT ) + { + theMesh->setSpringConstant(m.getArgAsFloat( i )<0.01?0.01:m.getArgAsFloat( i ) ); + } + + } + } + else if ( m.getAddress() == "friction" ) + { + for ( int i=0; isetFriction(m.getArgAsInt32( i )); + } + else if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT ) + { + theMesh->setFriction(m.getArgAsFloat( i )<0.01?0.01:m.getArgAsFloat( i ) ); + } + + } + } + //////////////// asym + else if ( m.getAddress() == "massAsym" ) + { + for ( int i=0; isetMassAsym(m.getArgAsFloat( i )<0.01?0.01:m.getArgAsFloat( i ) ); + } + + } + } + else if ( m.getAddress() == "springkAsym" ) + { + for ( int i=0; isetSpringConstantAsym(m.getArgAsFloat( i )<0.00?0.00:m.getArgAsFloat( i ) ); + } + + } + } + else if ( m.getAddress() == "frictionAsym" ) + { + for ( int i=0; isetFrictionAsym(m.getArgAsFloat( i )<0.01?0.01:m.getArgAsFloat( i ) ); + } + + } + } + else if ( m.getAddress() == "volume" ) + { + for ( int i=0; i class UISpecificFunctor : public UIFunctor +{ +private: + void (testApp::*fpt)(int); // pointer to member function + testApp* pt2Object; // pointer to object + +public: + + // constructor - takes pointer to an object and pointer to a member and stores + // them in two private variables + + UISpecificFunctor( testApp* _pt2Object, void(testApp::*_fpt)(int)) + { + pt2Object = _pt2Object; + fpt=_fpt; + }; + + // override operator "()" + + virtual void operator()(int buttID) + { (*pt2Object.*fpt)(buttID);}; // execute member function + + // override function "Call" + virtual void Call(int buttID) + { (*pt2Object.*fpt)(buttID);}; // execute member function +}; + + +#endif