Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of scoreLight_Advanced by
elasticLoop.cpp
- Committer:
- mbedalvaro
- Date:
- 2012-03-31
- Revision:
- 2:34157ebbf56b
- Parent:
- 1:a4050fee11f7
- Child:
- 3:b44ff6de81bd
File content as of revision 2:34157ebbf56b:
/*
* elasticLoop.cpp
* laserBlobPure
*
* Created by CASSINELLI ALVARO on 5/20/11.
* Copyright 2011 TOKYO UNIVERSITY. All rights reserved.
*
*/
#include "elasticLoop.h"
// SHOULD NOT BE HERE: (only because I am using AD_MIRRIOR... max and min in the set region function that should not be here)
#include "hardwareIO.h"
elasticLoop::elasticLoop() {
}
elasticLoop::~elasticLoop() {
/* // no need to do clear, this is done by default when deleting the vector container
massesLoop.clear();
loopSpringArray.clear();
hairVector.clear();
lightForce.clear();
centralSpringArray.clear();
displaySensingBuffer.lsdTrajectory.clear();
*/
}
void elasticLoop::createBlob(int _id, ElasticLoopMode _elasticBlobMode, vector2D _initPos) {
// (1) set ID:
identifier=_id;
// (2) Initialize common variables of all blobs (base class):
initCommonVariables();
// (3) initialize common variables for the elastic blob types:
slidingDirection=true; // (will change when touching wall)
// (3) Initialize secondary variables depending on the blob type and mode:
// NOTE (!): the mode does not affect the update method; in fact, all these elastic loops have different behaviours because of different parameters (but the booleans modes could
// actually be "condensed" in a mode...)
switch (_elasticBlobMode) {
case RELAX:
// Name of this kind of spot:
sprintf(spotName,"loop_relax"); //this is an relaxing elastic loop
// Color: (use parameter in the future):
setColor(0x07);//0x04+0x02>>i);
// default (initial) shape (the scafold belongs to the base class):
bluePrint.buildCircularScafold(400, _initPos, vector2D(0,0), 40); //(float _radius, vector2D _pos,vector2D _vel, int _numScafoldPoints);
// Numeric parameters for the simulated mechanical system:
massLoopParticle=0.25;
dampMotionMassesLoop=0.025;//0.17;
massAnchor=2.0;
dampMotionAnchorMass=0.001;
// Springs:
centralSpringK=0.3;
centralSpringRelax=bluePrint.radius; // use the radius of the scafold
interSpringK=0.46;
interSpringRelax=20;
// for "zack-like" blob:
interParticleRange=100;
factorInterParticleForce=18.0;
searchActive=false;
pseudopodesMode=false; // this is for contour following.
// Active/inactive forces:
springForcesOnLoop=true;
lightForcesOnLoop=true;
forceBorderOnLoop=false;
nuclearForceOnLoop=false;//true;
interParticleForceOnLoop=false;
forceInternalPressureOnLoop=false; // (when true, either constant force or calculated area using Green function or approximation by bounding box)
// Recentering vector:
angleCorrectionForceLoop=0;// in deg
recenteringForceOnLoop=false;
angleCorrectionForceNucleus=0;// in deg
recenteringForceOnNucleus=false;//true;
factorLightForce=4.0;//3.0;//8.0;
factorRecenteringAnchorMass=20.0/bluePrint.scafold.size(); // use number of points in the scafold
factorRecenteringLoopMass=0.3;
factorPressureLoopMass=1.0;
factorForceBorder=4.5;
break;
case CONTRACT:
sprintf(spotName,"loop_contract"); //this is an relaxing elastic loop
setColor(0x07);//0x04+0x02>>i);
// default (initial) shape:
bluePrint.buildCircularScafold(400, _initPos, vector2D(0,0), 40); //(float _radius, vector2D _pos,vector2D _vel, int _numScafoldPoints);
// Numeric parameters for the simulated mechanical system:
massLoopParticle=0.25;
dampMotionMassesLoop=0.024;//0.17;
massAnchor=2.0;
dampMotionAnchorMass=0.001;
// Springs:
centralSpringK=0.5;
centralSpringRelax=bluePrint.radius;
interSpringK=0.4;//46;
interSpringRelax=30;
// for "zack-like" blob:
interParticleRange=100;
factorInterParticleForce=18.0;
searchActive=false;
pseudopodesMode=false; // this is for contour following.
// Active/Inactive Forces:
springForcesOnLoop=true;
lightForcesOnLoop=true;
forceBorderOnLoop=false;
nuclearForceOnLoop=true;//true;
interParticleForceOnLoop=false;
forceInternalPressureOnLoop=false; // (when true, either constant force or calculated area using Green function or approximation by bounding box)
// Recentering vector:
angleCorrectionForceLoop=0;// in deg
recenteringForceOnLoop=false;
angleCorrectionForceNucleus=0;// in deg
recenteringForceOnNucleus=false;//true;
factorLightForce=6.0;//3.0;//8.0;
factorRecenteringAnchorMass=20.0/bluePrint.scafold.size();
factorRecenteringLoopMass=0.3;
factorPressureLoopMass=1.0;
factorForceBorder=4.5;
break;
case CONTRACT_CENTRAL:
sprintf(spotName,"contract_central");
setColor(0x07);//0x04+0x02>>i);
// default (initial) shape:
bluePrint.buildCircularScafold(400, _initPos, vector2D(0,0), 40); //(float _radius, vector2D _pos,vector2D _vel, int _numScafoldPoints);
// Numeric parameters for the simulated mechanical system:
massLoopParticle=0.3;
dampMotionMassesLoop=0.023;//0.17;
massAnchor=0.5;
dampMotionAnchorMass=0.001;
// Springs:
centralSpringK=0.3;
centralSpringRelax=bluePrint.radius;
interSpringK=0.54;//46;
interSpringRelax=30;
// for "zack-like" blob:
interParticleRange=100;
factorInterParticleForce=18.0;
searchActive=false;
pseudopodesMode=false; // this is for contour following.
// Active/Inactive Forces:
springForcesOnLoop= true;
lightForcesOnLoop= true;
forceBorderOnLoop=false;
nuclearForceOnLoop=false;//true;
interParticleForceOnLoop=false;
forceInternalPressureOnLoop=false; // (when true, either constant force or calculated area using Green function or approximation by bounding box)
// Recentering vector:
angleCorrectionForceLoop=0;// in deg
recenteringForceOnLoop=true;
angleCorrectionForceNucleus=0;// in deg
recenteringForceOnNucleus=false;//true;
factorLightForce=4.3;//3.0;//8.0;
factorRecenteringAnchorMass=20.0/bluePrint.scafold.size();
factorRecenteringLoopMass=0.045;
factorPressureLoopMass=1.5;
factorForceBorder=150;
break;
case CONTOUR_FOLLOWING:
sprintf(spotName,"following"); //this is a contour-following loop
setColor(0x07);//0x04+0x02>>i);
// default (initial) shape:
bluePrint.buildCircularScafold(100, _initPos, vector2D(0,0), 20); //(float _radius, vector2D _pos,vector2D _vel, int _numScafoldPoints);
// Numeric parameters for the simulated mechanical system:
massLoopParticle=0.05;
dampMotionMassesLoop=0.27;//0.17;
massAnchor=3.0;
dampMotionAnchorMass=0.03;
// Springs:
centralSpringK=0.4;
centralSpringRelax=bluePrint.radius;
interSpringK=0.4;//46;
interSpringRelax=0.7*bluePrint.radius*2*sin(1.0* PI/ bluePrint.scafold.size()); // if factor=1, this makes for a perfect polygon at relax for all springs...
// for "zack-like" blob:
interParticleRange=70;
factorInterParticleForce=4.0;
searchActive=true;
pseudopodesMode=true; // this is for contour following.
// Active/Inactive Forces:
springForcesOnLoop=true;
lightForcesOnLoop=true;
forceBorderOnLoop=false;
nuclearForceOnLoop=false;//true;
interParticleForceOnLoop=true;
forceInternalPressureOnLoop=false; // (when true, either constant force or calculated area using Green function or approximation by bounding box)
// Recentering vector:
angleCorrectionForceLoop=239;// in deg
recenteringForceOnLoop=true;
angleCorrectionForceNucleus=180;// in deg
recenteringForceOnNucleus=false;//true;
factorLightForce=2.3;//3.0;//8.0;
factorRecenteringAnchorMass=1.0;//20.0/scafold.size();
factorRecenteringLoopMass=0.09;
factorPressureLoopMass=1.5;
factorForceBorder=150;
break;
case CONTOUR_FOLLOWING_FAST:
sprintf(spotName,"following_fast");
setColor(0x07);//0x04+0x02>>i);
// default (initial) shape:
bluePrint.buildCircularScafold(100, _initPos, vector2D(0,0), 30); //(float _radius, vector2D _pos,vector2D _vel, int _numScafoldPoints);
// Numeric parameters for the simulated mechanical system:
massLoopParticle=0.05;
dampMotionMassesLoop=0.27;//0.17;
massAnchor=3.0;
dampMotionAnchorMass=0.03;
// Springs:
centralSpringK=-200;
centralSpringRelax=bluePrint.radius;
interSpringK=0.5;//46;
interSpringRelax=0.7*bluePrint.radius*2*sin(1.0* PI/bluePrint.scafold.size()); // if factor=1, this makes for a perfect polygon at relax for all springs...
// for "zack-like" blob:
interParticleRange=80;
factorInterParticleForce=4.0;
searchActive=false;
pseudopodesMode=true; // this is for contour following.
// Active/Inactive Forces:
springForcesOnLoop=true;
lightForcesOnLoop=true;
forceBorderOnLoop=false;
nuclearForceOnLoop=false;//true;
interParticleForceOnLoop=false;
forceInternalPressureOnLoop=false; // (when true, either constant force or calculated area using Green function or approximation by bounding box)
// Recentering vector:
angleCorrectionForceLoop=243;// in deg
recenteringForceOnLoop=true;
angleCorrectionForceNucleus=180;// in deg
recenteringForceOnNucleus=false;//true;
factorLightForce=2.3;//3.0;//8.0;
factorRecenteringAnchorMass=1.0;//20.0/bluePrint.scafold.size();
factorRecenteringLoopMass=0.09;
factorPressureLoopMass=1.5;
factorForceBorder=150;
break;
case BOUNCING:
sprintf(spotName,"bouncing");
setColor(0x07);//0x04+0x02>>i);
// default (initial) shape:
bluePrint.buildCircularScafold(70, _initPos, vector2D(0,0), 20); //(float _radius, vector2D _pos,vector2D _vel, int _numScafoldPoints);
// Numeric parameters for the simulated mechanical system:
massLoopParticle=5.0;
dampMotionMassesLoop=0.001;//0.17;
massAnchor=1.0;
dampMotionAnchorMass=0.002;
// Springs:
centralSpringK=1.0;
centralSpringRelax=bluePrint.radius;
interSpringK=0.4;//46;
interSpringRelax==1.0*bluePrint.radius*2*sin(1.0* PI/bluePrint.scafold.size()); // if factor=1, this makes for a perfect polygon at relax for all springs...
// for "zack-like" blob:
interParticleRange=100;
factorInterParticleForce=3.0;
searchActive=false;
pseudopodesMode=false; // this is for contour following.
// Active/Inactive Forces:
springForcesOnLoop=true;
lightForcesOnLoop=true;
forceBorderOnLoop=true;
nuclearForceOnLoop=true;//true;
interParticleForceOnLoop=false;
forceInternalPressureOnLoop=false; // (when true, either constant force or calculated area using Green function or approximation by bounding box)
// Recentering vector:
angleCorrectionForceLoop=0;// in deg
recenteringForceOnLoop=false;
angleCorrectionForceNucleus=0;// in deg
recenteringForceOnNucleus=false;//true;
factorLightForce=0.6;//3.0;//8.0;
factorRecenteringAnchorMass=100.0/bluePrint.scafold.size();
factorRecenteringLoopMass=5.0;
factorPressureLoopMass=2.0;
factorForceBorder=4.5;
break;
}
// Finally, we can create the loop using these parameters, and the positions given in the scafold:
createLoopFromScafold(); // this sets the number of masses
// Excursion limits (ATTN!!! this will set the limits for all the masses, so we need FIRT to call to createLoopFromScafold)
setRegionMotion(MIN_AD_MIRRORS, MIN_AD_MIRRORS, MAX_AD_MIRRORS, MAX_AD_MIRRORS);
// !!!!!!!!!!!!!!!!!!!!! ::
// The following is not nice here (should be in the classLaserSensingTrajectory, not even on the simpleLaserRenderer), but for the time being let's leave it here
setDelayMirrors(4); // ATTN!!! needs to be called AFTER setting the number of points!!! (note: 5 seemed good)
}
void elasticLoop::initSizeBlob(int _numMasses) {
// Iinitialize blob size (number of points for the loop, as well as other structures such as lsdTrajectory)
numMasses=_numMasses;
// Since this is an elastic loop object, let's create an elastic loop of masses:
massesLoop.resize(numMasses);
loopSpringArray.resize(numMasses); // springs connecting consecutive masses
// NOTE: to save memory, we can drop hairVector (use lightForce instead)
hairVector.resize(numMasses); // the perpendiculars to the loop
lightForce.resize(numMasses); // light force in each particle
//vector2D totalLightForce; // this belongs to the base class now
centralSpringArray.resize(numMasses); // springs connecting each mass to the anchorMass.
// Sensing and Display trajectory:
displaySensingBuffer.lsdTrajectory.resize(numMasses); // the lsdTrajectory and the elastic loop will have the same number of points (this could be different - decimation?).
}
// We will build the masses from the scafold shape (and maybe render it once on the lsdTrajectory to initialize this array?)
void elasticLoop::createLoopFromScafold(void) {
initSizeBlob(bluePrint.scafold.size()); // important: we will have here the same number of points in the scafold and the elastic loop (massLoop)
// Initial conditions for the loop masses:
float cx= bluePrint.center.x;
float cy= bluePrint.center.y;
for (int i = 0; i < numMasses; i++) {
massesLoop[i].setIntegrationStep(0.23);//18); // VERY IMPORTANT! in the case of verlet integration, we need to set dt BEFORE setting the initial speed.
massesLoop[i].setInitialCondition(bluePrint.scafold[i].x+cx, bluePrint.scafold[i].y+cy, bluePrint.speed.x, bluePrint.speed.y);
massesLoop[i].mass=massLoopParticle;
massesLoop[i].dampMotion=dampMotionMassesLoop;
}
// Springs for the loop:
for (int i = 0; i<numMasses; i++) {
loopSpringArray[i].distance =interSpringRelax;
// if we want an perfect polygon: =startRadius*2*sin(1.0* PI/ numMasses);
// loopSpringArray[i].distance = startRadius*2*sin(1.0* PI/ numMasses);
loopSpringArray[i].springiness = interSpringK;//*(i%5==0? .6 : 1);//0.4;//4f;
loopSpringArray[i].massA = & (massesLoop[i ]);
loopSpringArray[i].massB = & (massesLoop[(i+1) % numMasses]);
}
// Central (anchor mass):
anchorMass.setIntegrationStep(0.3); // VERY IMPORTANT! in the case of verlet integration, we need to set dt BEFORE setting the initial speed.
anchorMass.setInitialCondition(bluePrint.center.x, bluePrint.center.y,bluePrint.speed.x, bluePrint.speed.y);
anchorMass.mass=massAnchor;
anchorMass.dampMotion = dampMotionAnchorMass;
// Initial conditions for central springs:
for (int i = 0; i<numMasses; i++) {
centralSpringArray[i].distance =centralSpringRelax;// + 60* cos ( (1.0*i / numMasses) * 7* 2 * PI);
centralSpringArray[i].springiness =centralSpringK;// 0.4f;
centralSpringArray[i].massA = & (anchorMass);
centralSpringArray[i].massB = & (massesLoop[i]);
}
}
void elasticLoop::setRegionMotion(int mmix, int mmiy, int mmax, int mmay) { // Attention: the initial position should be INSIDE this...
for (int i = 0; i<numMasses; i++) {
massesLoop[i].setWallLimits(mmix, mmiy, mmax, mmay);
}
anchorMass.setWallLimits(mmix+10, mmiy+10, mmax-10, mmay-10);
}
void elasticLoop::update() {
// (I) Process loop geometry (compute "hair vectors", area and first order moment):
processLoopData();
// (II) Process sensing buffer and compute light forces
displaySensingBuffer.processSensedData();
// (III) Reset all forces:
for (int i = 0; i < numMasses; i++) {
massesLoop[i].resetForce();
}
anchorMass.resetForce();
// (IV) COMPUTE FORCES (motion is not update yet):
//== (1) Compute each particle light force as well as total light force (this will be stored separatedly from the final total particle force to send to OSC):
totalLightForce.set(0,0);
for (int i = 0; i < numMasses; i++) {
// NOTE: to save memory, we can drop hairVector...
lightForce[i]=hairVector[i]*factorLightForce*displaySensingBuffer.lsdTrajectory[i].lightZone;
// lightForce[i]=lightForce[i]*factorLightForce*displaySensingBuffer.lsdTrajectory[i].lightZone;
//compute total light force, not only on lighted zones, because it will mean AWAY from black zones:
totalLightForce+=lightForce[i]; // note: bad value choice (negative means TOUCH, and equal to -1), TO CHANGE this in future implementations
}
//== (2) Compute the "recentering vector" from the total light force, by rotating by the angleCorrection (this will give different behaviours):
recenteringVectorLoop= totalLightForce.getRotatedDeg(slidingDirection? angleCorrectionForceLoop : 140); // the hard coded value is a hack for the time being...
// Compute redundant quantities:
normRecenteringVector=recenteringVectorLoop.length();
angleRecenteringVector=recenteringVectorLoop.angleDegHoriz();
recenteringVectorNucleus=totalLightForce.getRotatedDeg(angleCorrectionForceNucleus);
//== (3) Compute forces on the loop:
//----(a) Nearest neighbour inter-particle springs on the loop (always? we can have still another mode, following the center mass only, etc...)
if (springForcesOnLoop) {
for (int i = 0; i < numMasses; i++) { // if putting -1, the loop is broken
loopSpringArray[i].update();// this add forces to the particles
}
}
//----(b) Direct forces from light pressure (COULD BE MERGED WITH FORCE RECENTERING!!)
if (pseudopodesMode) {
// special "patches" on blob membrane:
if (lightForcesOnLoop) {
int sign=1;
for (int i = 0; i < numMasses; i++) {
if ((i%2)==0) sign*=-1;
if (displaySensingBuffer.lsdTrajectory[i].lightZone>0) // this means touching something black: make SOME points attracted by it (pseudopodes!!) - but not all!
massesLoop[i].addForce(lightForce[i]*(sign<0? -1.24 : 1.4)); // sign<0 means this is a pseudopode attracted by dark zones
else // this means something white: do nothing, all forces are towards the exterior
massesLoop[i].addForce(lightForce[i]*1.6); // this force tends to make the blob "inflate", but is not "directional"
}
}
//----(c) Forces from the recentering vector on each particle (WITH PATCHES on the loop?):
if (recenteringForceOnLoop) {
vector2D auxForce= recenteringVectorLoop*factorRecenteringLoopMass*1.0;
vector2D auxForce2= totalLightForce.getRotatedDeg(80)*factorRecenteringLoopMass*(slidingDirection? 0 : 1)*1.8;
int sign=1;
for (int i = 0; i < numMasses; i++) {
if ((i%2)==0) sign*=-1;
if (displaySensingBuffer.lsdTrajectory[i].lightZone>0) {// this means touching something black: behaviour may depend on the pseudopode presence:
massesLoop[i].addForce((sign<0? auxForce2 : auxForce2)); // nothing, or sign, or corrected angle
} else
massesLoop[i].addForce(auxForce); // this force is responsible for the behaviour (contour following or not)
}
}
} else { // no special zones in the "cell membrane":
if (lightForcesOnLoop) {
for (int i = 0; i < numMasses; i++) {
massesLoop[i].addForce(lightForce[i]);
}
}
//----(c') Forces from the recentering vector on each particle:
if (recenteringForceOnLoop) {
vector2D auxForce= recenteringVectorLoop*factorRecenteringLoopMass;
for (int i = 0; i < numMasses; i++) massesLoop[i].addForce(auxForce);
}
}
//----(d) Forces from the anchorMass (depending on how we set the equilibrium position for each central spring, we can have a nice blob shape at equilibrium... like a gear for instance)
if (nuclearForceOnLoop) {
// Springs:
for (int i = 0; i < numMasses; i++) centralSpringArray[i].update();//assymetricUpdate();
// note: if using centralSpringArray[i].update(), we will add forces to the particles AND to the anchor mass...
// Inverse square (attractive):
//for (int i = 0; i < numMasses; i++) massesLoop[i].addInterInvSquareForce(anchorMass, 10, 300, centralSpringK);
}
//----(d) Inter loop-particles forces (Zach-Liebermann-like blob):
if (interParticleForceOnLoop) {
for (int i = 0; i < numMasses; i++) {
for (int j = 0; j < i-1; j++) massesLoop[i].addInterSpringForce(massesLoop[j], interParticleRange, factorInterParticleForce);
}
}
//----(e) Internal blob pressure force (my faster method to have a blob-like behaviour):
if (forceInternalPressureOnLoop) {
// NOTE on the Physics of the thing: the force on the membrane of a ballon is proportional to the DIFFERENCE of pressures (outside and inside):
// so: f= factor/area - cte, with cte=factor/area0, with area0 being the area at equilibrium.
// (And of course, to make it even more exact, we should do pressure*surface, but this will be considered constant)
// float area0=30000; // area in pixels when at equilibrium
//float factorPressureLoopMass=-0.1*(1.0/area-1.0/area0);
//float factorPressureLoopMass=500000.0*(1.0/(area*area)-1.0/(area0*area0));
//float factorPressureLoopMass=20000.0*(1.0/sqrt(area)-1.0/sqrt(area0));
// Constant force seems to work well too... but produces an annoying blob reversal (probably solved by using negative light forces instead of internal blob pressure):
//float factorPressureLoopMass=2.5;//4.8;
// Now, add the pressure force proportional to the inverse of the area to all particles, or just a signed constant:
int auxsign=(area>=0? -1: 1);
auxsign=-1;
for (int i = 0; i < numMasses; i++) massesLoop[i].addForce( hairVector[i] * factorPressureLoopMass* auxsign);
}
//----(f) force from border:
if (forceBorderOnLoop) {
for (int i = 0; i < numMasses; i++) {
if (massesLoop[i].bWallCollision) massesLoop[i].addForce(massesLoop[i].innerCollitionDirection*factorForceBorder);
}
}
//== (4) Compute forces on the anchor mass:
//----(a) Force from data send by OSC? (ex: from mouse?)
// anchorMass.addSpringForce(mx, my, 500, -10.2f);
// or direct control:
// anchorMass.pos.x=mx;anchorMass.pos.y=my;
//----(b) Force from the total light force (aka, the "recentering vector"!):
if (recenteringForceOnNucleus) {
anchorMass.addForce(recenteringVectorNucleus*factorRecenteringAnchorMass);
}
// when nothing is touching it for a while:
if (searchActive) {
if (!displaySensingBuffer.lightTouched) {
if (firstTimeNoTouch) {
firstTimeNoTouch=false;
computeBoundingBox();
randomForce.set(2000-cx,2000-cy);
randomForce.normalize();
randomForce= randomForce.getRotatedDeg(rand()%50-25);
}
if (noTouchedCounter>0) {
// add random force, modulated:
float aux=1.0*noTouchedCounter/1150;
vector2D randf=randomForce.getRotatedDeg(40.0*sin(aux*2*PI*2))*20.0;//*(1.0-aux)*0.3;
for (int i = 0; i < 1; i=i+1) { // only on some of the particles, and better if these are in the "black attractive" patch!
massesLoop[i].addForce(randf);
}
// and a special point?
//massesLoop[numMasses/2].addForce(randf);
// plus amoeba effect ?
// for (int i = 0; i < numMasses; i++) {
// massesLoop[i].addForce(hairVector[i]*18*cos( (0.0*noTouchedCounter/1000 + 1.0*i/(numMasses-1)*2*PI*3)));
//}
if ((noTouchedCounter>1150)||(blobWallCollision)) {
noTouchedCounter=0;
// compute force towards the center, slightly rotated to make the blob wander about:
computeBoundingBox();
randomForce.set(2000-cx,2000-cy);
randomForce.normalize();
randomForce= randomForce.getRotatedDeg(rand()%50-25);
}
}
} else {
firstTimeNoTouch=true;
noTouchedCounter=0;
}
noTouchedCounter++;
}
// (V) UPDATE DYNAMICS
//== (1) particules on the loop:
for (int i = 0; i < numMasses; i++) {
#ifndef VERLET_METHOD
massesLoop[i].addDampingForce(); // only in case of EULER method (damping in VERLET mode is done automatically when updating)
#endif
massesLoop[i].update(); // unconstrained
massesLoop[i].bounceOffWalls(); // constrain position (and compute wall "hit")
}
//== (2) For the anchorMass:
#ifndef VERLET_METHOD
anchorMass.addDampingForce(); // // only in case of EULER method (damping in VERLET mode is done automatically when updating)
#endif
anchorMass.update(); // unconstrained
anchorMass.bounceOffWalls(); // constrain position (and compute wall "hit")
// OTHER PARTICULAR THINGS:
// change sliding direction (for countour following):
if (blobWallCollision) {
if (wallCounter>10) {
slidingDirection=!slidingDirection;
wallCounter=0;
}
}
wallCounter++;
}
// Drawing the graphics - this will in fact use the graphic renderer - if any - and produce the trajectory to be displayed by the laser
void elasticLoop::draw() {
// for the time being, there is no "opengl" like renderer, so we just copy the coordinates of the mass into the lsdTrajectory:
for (int i = 0; i < numMasses; i++) {
displaySensingBuffer.lsdTrajectory[i].x= int( massesLoop[i].pos.x ); // note: it should be an integer
displaySensingBuffer.lsdTrajectory[i].y= int( massesLoop[i].pos.y );
//displaySensingBuffer.lsdTrajectory[i].color=blobColor; // perhaps per point color is not a good idea for the time being...
}
// global color for the whole loop:
displaySensingBuffer.displayColor=blobColor;
}
void elasticLoop::processLoopData() {
// (0) Check if the blob touched the borders:
blobWallCollision=false;
for (int i = 0; i < numMasses; i++) blobWallCollision= (blobWallCollision || massesLoop[i].bWallCollision);
// (1) Compute all the "hairvectors" for the loop (this is, the normals to the particles, pointing outwards).
// This will be approximated by taking the 90 deg rotated difference between contiguous particles positions.
for (int i = 0; i < numMasses; i++) {
vector2D diff;
diff.set(massesLoop[(i+1)%numMasses].pos-massesLoop[i].pos);
// normalize and rotate 90 deg:
// NOTE: to save memory, we can drop hairVector...
hairVector[i]=diff.getPerpendicularNormed(CW);
//lightForce[i]=diff.getPerpendicularNormed(CW);
}
// (2) Compute area:
// (a) using Green method:
/*
area=0;
float dx;
for (int i = 0; i < numMasses-1; i++){
dx=massesLoop[i].pos.x-massesLoop[i+1].pos.x;
area+=dx*massesLoop[i].pos.y;
}
// to avoid computation problems:
// if (area<=0) area=1; // or just norm: area CAN be negative! (a loop that is larger than the original blob...)
*/
// (b) Compute approximate area from enclosing rectangle:
computeBoundingBox();
}
void elasticLoop::computeBoundingBox() {
float minx=4096, maxx=-1, miny=4096, maxy=-1;
for (int i = 0; i < numMasses; i++) {
if (i == 0) {
minx = massesLoop[i].pos.x;
maxx = massesLoop[i].pos.x;
miny = massesLoop[i].pos.y;
maxy = massesLoop[i].pos.y;
} else {
minx = min(minx, massesLoop[i].pos.x);
maxx = max(maxx, massesLoop[i].pos.x);
miny = min(miny, massesLoop[i].pos.y);
maxy = max(maxy, massesLoop[i].pos.y);
}
}
// final results:
w = maxx - minx;
h = maxy - miny;
cx = minx+0.5*w; // note: center will be initialized with posX and posY when calling setInitialPos() of blobConfig
cy = miny+0.5*h;
// approx area:
approxArea=w*h;
}
void elasticLoop::sendDataSpecific() {
char auxstring[10];
myled2=1; // for tests...
// First, set the top address of the message to the ID of the blob (not the name):
sprintf(auxstring, "%d", identifier);
sendMes.setTopAddress("0");//auxstring);
// ===================== OSC ======================
if (sendOSC) {
// (a) Anchor mass:
if (sendingAnchorPosition) {
sendMes.setSubAddress("/apos");
long x, y; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!)
x=(long)(anchorMass.pos.x);
y=(long)(anchorMass.pos.y);
sendMes.setArgs( "ii", &x, &y);
osc.sendOsc( &sendMes );
}
if (sendingAnchorForce) {
sendMes.setSubAddress("/aforce");
long x, y; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!)
x=(long)(anchorMass.totalForce.x);
y=(long)(anchorMass.totalForce.y);
sendMes.setArgs( "ii", &x, &y);
osc.sendOsc( &sendMes );
}
if (sendingAnchorTouchWall) {// note: not an else (we can send different data simultaneously)
sendMes.setSubAddress("/awall");
long wall=(long)(anchorMass.bWallCollision? 1 : 0);
sendMes.setArgs( "i", &wall);
osc.sendOsc( &sendMes );
}
// (b) data from blob points:
if (sendingLoopPositions) {
#ifdef SEND_AS_POINTS
long x, y; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!)
for (int i = 0; i < numMasses; i++) {
sprintf(auxstring, "/p%d", i); // auxstring read as "/p1", "/p2", ...
sendMes.setSubAddress(auxstring); // ATTENTION: the host computer needs to know in advance how many points are in the loop (I did not implement "bundle" messages yet...)
x=(long)(massesLoop[i].pos.x);
y=(long)(massesLoop[i].pos.y);
sendMes.setArgs( "ii", &x, &y);
osc.sendOsc( &sendMes );
}
#endif
#ifdef SEND_AS_BLOB
sendMes.clearArgs(); // no need, we won't use osc.sendOsc()...
uint8_t blobdata[4*numMasses]; // 2 bytes per coordinate, and 2 coordinates
for (int i = 0; i < numMasses; i++ ) {
// note: massesLoop[i].pos.x is a "float"
uint16_t x=(uint16_t)(massesLoop[i].pos.x);
blobdata[4*i]=(uint8_t)x>>8; // BIG ENDIAN (send FIRST the MOST SIGNIFICANT BYTE)
blobdata[4*i+1]=(uint8_t)x;
uint16_t y=(uint16_t)(massesLoop[i].pos.y);
blobdata[4*i+2]=(uint8_t)y>>8; // BIG ENDIAN (send FIRST the MOST SIGNIFICANT BYTE)
blobdata[4*i+3]=(uint8_t)y;
}
osc.sendOscBlob(&(blobdata[0]), 4*numMasses, &sendMes ); // second parameter is osc blob size in bytes
#endif
#ifdef SEND_AS_STRING
sendMes.clearArgs(); // no need, we won't use osc.sendOsc()...
uint8_t blobdata[4*numMasses]; // 2 bytes per coordinate, and 2 coordinates
for (int i = 0; i < numMasses; i++ ) {
// note: massesLoop[i].pos.x is a "float"
uint16_t x=(uint16_t)(massesLoop[i].pos.x);
blobdata[4*i]=(uint8_t)x>>8; // BIG ENDIAN (send FIRST the MOST SIGNIFICANT BYTE)
blobdata[4*i+1]=(uint8_t)x;
uint16_t y=(uint16_t)(massesLoop[i].pos.y);
blobdata[4*i+2]=(uint8_t)y>>8; // BIG ENDIAN (send FIRST the MOST SIGNIFICANT BYTE)
blobdata[4*i+3]=(uint8_t)y;
}
osc.sendOscString(blobdata, 4*numMasses, &sendMes ); // second parameter is osc blob size in bytes
#endif
}
if (sendingLoopForces) { // ATTN: the force is the TOTAL force on the point (interesting perhaps for making sound...)
long x, y; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!)
for (int i = 0; i < numMasses; i++) {
sprintf(auxstring, "/f%d", i); // auxstring read as "/f1", "/f2", ...
sendMes.setSubAddress(auxstring); // ATTENTION: the host computer needs to know in advance how many points are in the loop (I did not implement "bundle" messages yet...)
x=(long)(massesLoop[i].totalForce.x);
y=(long)(massesLoop[i].totalForce.y);
sendMes.setArgs( "ii", &x, &y);
osc.sendOsc( &sendMes );
}
}
if (sendingLoopForcesLight) { // ATTN: the force is the TOTAL force on the point (interesting perhaps for making sound...)
long x, y; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!)
for (int i = 0; i < numMasses; i++) {
sprintf(auxstring, "/g%d", i); // auxstring read as "/f1", "/f2", ...
sendMes.setSubAddress(auxstring); // ATTENTION: the host computer needs to know in advance how many points are in the loop (I did not implement "bundle" messages yet...)
x=(long)(1000*lightForce[i].x);
y=(long)(1000*lightForce[i].y);
sendMes.setArgs( "ii", &x, &y);
osc.sendOsc( &sendMes );
}
}
if (sendingLoopRegions) {
long x; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!)
for (int i = 0; i < numMasses; i++) {
sprintf(auxstring, "/r%d", i); // auxstring read as "/f1", "/f2", ...
sendMes.setSubAddress(auxstring); // ATTENTION: the host computer needs to know in advance how many points are in the loop (I did not implement "bundle" messages yet...)
x=(long)(displaySensingBuffer.lsdTrajectory[i].lightZone>0? 1 : 0);
sendMes.setArgs( "i", &x);
osc.sendOsc( &sendMes );
}
}
if (sendingLoopTouchWall) { // global touch wall for the loop (not per point)
long wall; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!)
sprintf(auxstring, "/bWall");
sendMes.setSubAddress(auxstring);
wall=(long)(blobWallCollision? 1 : 0);
sendMes.setArgs( "i", &wall);
osc.sendOsc( &sendMes );
}
// (c) Blob geometry:
if (sendingBlobArea) {
sendMes.setSubAddress("/area");
long x; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!)
x=(long)(area);//approxArea); // area or approxArea
sendMes.setArgs( "i", &x);
osc.sendOsc( &sendMes );
}
if (sendingBlobNormals) {
long x, y; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!)
for (int i = 0; i < numMasses; i++) {
sprintf(auxstring, "nf%d", i); // auxstring read as "/f1", "/f2", ...
sendMes.setSubAddress(auxstring); // ATTENTION: the host computer needs to know in advance how many points are in the loop (I did not implement "bundle" messages yet...)
x=(long)(hairVector[i].x);
y=(long)(hairVector[i].y);
sendMes.setArgs( "ii", &x, &y);
osc.sendOsc( &sendMes );
}
}
if (sendingBlobAngles) {
long x; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!)
for (int i = 0; i < numMasses; i++) {
sprintf(auxstring, "/a%d", i); // auxstring read as "/f1", "/f2", ...
sendMes.setSubAddress(auxstring); // ATTENTION: the host computer needs to know in advance how many points are in the loop (I did not implement "bundle" messages yet...)
x=(long)(hairVector[i].angleDegHoriz());
sendMes.setArgs( "i", &x);
osc.sendOsc( &sendMes );
}
}
// (d) Light sensing statistics:
if (sendingBlobMaxMin) {
sendMes.setSubAddress("/maxmin");
long x, y; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!)
x=(long)(displaySensingBuffer.maxI);
y=(long)(displaySensingBuffer.minI);
sendMes.setArgs( "ii", &x, &y);
osc.sendOsc( &sendMes );
}
if (sendingLightForce) {
sendMes.setSubAddress("/lforce");
long x, y; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!)
x=(long)(totalLightForce.x);
y=(long)(totalLightForce.y);
sendMes.setArgs( "ii", &x, &y);
osc.sendOsc( &sendMes );
}
// (e) Recentering vector: (note: redundant with sendingLightForce, IF the correction angle is known).
if (sendingRecenteringVector) {
sendMes.setSubAddress("/rvector");
long x, y; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!)
x=(long)(recenteringVectorLoop.x);
y=(long)(recenteringVectorLoop.y);
sendMes.setArgs( "ii", &x, &y);
osc.sendOsc( &sendMes );
}
if (sendingRecenteringAngle) {
sendMes.setSubAddress("/rangle");
long x; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!)
x=(long)(angleRecenteringVector);
sendMes.setArgs( "i", &x);
osc.sendOsc( &sendMes );
}
if (sendingRecenteringNorm) {
sendMes.setSubAddress("/rnorm");
long x; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!)
x=(long)(normRecenteringVector);
sendMes.setArgs( "i", &x);
osc.sendOsc( &sendMes );
}
if (sendingTouched) {
if (displaySensingBuffer.lightTouched) {
sendMes.clearArgs(); // there are no arguments to send
sendMes.setSubAddress("/touched");
osc.sendOsc( &sendMes );
}
}
} // end of OSC sending per-spot
// ===================== SERIAL ======================
if (sendSerial) {
//.. to do
}
myled2=0; // for tests...
}
