Alvaro Cassinelli
/
skinGames_forktest
just a test
Fork of scoreLight_Advanced by
Diff: elasticLoop.cpp
- Revision:
- 30:d8af03f01cd4
- Parent:
- 27:1ce994629ffc
- Child:
- 31:5f039cbddee8
--- a/elasticLoop.cpp Wed Jun 20 03:25:49 2012 +0000 +++ b/elasticLoop.cpp Fri Sep 21 10:02:35 2012 +0000 @@ -1,1000 +1,1001 @@ -/* - * 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 clearing the vector container? - massesLoop.clear(); - loopSpringArray.clear(); - hairVector.clear(); - lightForce.clear(); - centralSpringArray.clear(); - displaySensingBuffer.lsdTrajectory.clear(); -} - - -void elasticLoop::createBlob(int _id, ElasticLoopMode _elasticBlobMode, vector2Df _initPos, vector2Df _initSpeed) { - // (1) set ID: - identifier=_id; - - startCenter=_initPos; - startSpeed=_initSpeed; - -// (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) - // Sending data: - periodSendingData=15; // in ms - sendingLoopPositions=false; - sendingBlobArea=true; - sendingKineticEnergy=true; - sendingOnlyWhenTouch=false; // send ALWAYS, regardless of the fact the blob is being touched or not. - -// (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); - setColor(0x04); - - // default (initial) shape (the scafold belongs to the base class): - startRadius=400; - bluePrint.buildCircularScafold(startRadius, vector2Dd(0,0), 40); //(float _radius, vector2Dd _pos, 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=startRadius;// 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; - - // per-blob mirror delay (if things were well adjusted - in particular mirror waiting times, then this could be 0. - //But in case of unique blobs, it may be interesting to accelerate display AND correct the delay by software): - displaySensingBuffer.setDelayMirrors(2); - - break; - - case CONTRACT: - - sprintf(spotName,"loop_contract"); //this is an relaxing elastic loop - - setColor(0x07);//0x04+0x02>>i); - - // default (initial) shape: - startRadius =400; - bluePrint.buildCircularScafold(startRadius, vector2Dd(0,0), 40); //(float _radius, vector2Dd _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=startRadius; - 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; - - // per-blob mirror delay (if things were well adjusted - in particular mirror waiting times, then this could be 0. - //But in case of unique blobs, it may be interesting to accelerate display AND correct the delay by software): - displaySensingBuffer.setDelayMirrors(2); // per-blob mirror delay (if things were well adjusted - in particular mirror waiting times, then this could be 0. - - break; - case CONTRACT_CENTRAL: - - sprintf(spotName,"contract_central"); - - //setColor(0x07);//0x04+0x02>>i); - setColor(0x04); - - // default (initial) shape: - startRadius=400; - bluePrint.buildCircularScafold(startRadius, vector2Dd(0,0), 45); //(float _radius, vector2Dd _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=startRadius; - 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=false ; //true; !!!!!!!!!!!!!!! - angleCorrectionForceNucleus=0;// in deg - recenteringForceOnNucleus=false;//true; - - factorLightForce=6.3;//4.3; - factorRecenteringAnchorMass= 20.0/bluePrint.scafold.size(); - factorRecenteringLoopMass=0.045; - factorPressureLoopMass=1.5; - factorForceBorder=150; - - // per-blob mirror delay (if things were well adjusted - in particular mirror waiting times, then this could be 0. - //But in case of unique blobs, it may be interesting to accelerate display AND correct the delay by software): - displaySensingBuffer.setDelayMirrors(4); - - break; - - case CONTRACT_CENTRAL_FAST: - - //setColor(0x07);//0x04+0x02>>i); - setColor(0x04); - - // default (initial) shape: - startRadius=150; - bluePrint.buildCircularScafold(startRadius, vector2Dd(0,0), 40); //(float _radius, vector2Dd _pos,vector2D _vel, int _numScafoldPoints); - - // Numeric parameters for the simulated mechanical system: - massLoopParticle=0.06; - dampMotionMassesLoop=0.021;//0.17; - massAnchor=0.5; - dampMotionAnchorMass=0.01; - // Springs: - centralSpringK=0.3; - centralSpringRelax=startRadius; - interSpringK=0.54;//46; - interSpringRelax=40; - // for "zack-like" blob: - interParticleRange=150; - factorInterParticleForce=160.0; - - searchActive=false; - pseudopodesMode=false; // this is for contour following. - - // Active/Inactive Forces: - springForcesOnLoop= true; - lightForcesOnLoop= true; - forceBorderOnLoop=false; - nuclearForceOnLoop=false; - interParticleForceOnLoop=true; //!!! - forceInternalPressureOnLoop=false; // (when true, either constant force or calculated area using Green function or approximation by bounding box) - // Recentering vector: - angleCorrectionForceLoop=90;// in deg - recenteringForceOnLoop=true; - angleCorrectionForceNucleus=0;// in deg - recenteringForceOnNucleus=false;//true; - - factorLightForce=-4;//3.0;//8.0; - factorRecenteringAnchorMass= 20.0/bluePrint.scafold.size(); - factorRecenteringLoopMass=0.06; - factorPressureLoopMass=1.5; - factorForceBorder=150; - - displaySensingBuffer.setDelayMirrors(70); - break; - - case CONTOUR_FOLLOWING: - sprintf(spotName,"following"); //this is a contour-following loop - - //setColor(0x07);//0x04+0x02>>i); - setColor(0x04); - - // default (initial) shape: - startRadius=100; - bluePrint.buildCircularScafold(startRadius, vector2Dd(0,0), 20); //(float _radius, vector2Dd _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=100;//bluePrint.radius; - interSpringK=0.4;//46; - interSpringRelax=0.7*startRadius*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=240;//239;// in deg - recenteringForceOnLoop=true; - angleCorrectionForceNucleus=180;// in deg - recenteringForceOnNucleus=false;//true; - - factorLightForce=2.23;//3.0;//8.0; - factorRecenteringAnchorMass=1.0;//20.0/scafold.size(); - factorRecenteringLoopMass=0.09; - factorPressureLoopMass=1.5; - factorForceBorder=150; - // per-blob mirror delay (if things were well adjusted - in particular mirror waiting times, then this could be 0. - //But in case of unique blobs, it may be interesting to accelerate display AND correct the delay by software): - displaySensingBuffer.setDelayMirrors(4); - - break; - case CONTOUR_FOLLOWING_FAST: - sprintf(spotName,"following_fast"); - - setColor(0x07);//0x04+0x02>>i); - - // default (initial) shape: - startRadius=100; - bluePrint.buildCircularScafold(startRadius, vector2Dd(0,0), 30); //(float _radius, vector2Dd _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=100;//bluePrint.radius; - interSpringK=0.5;//46; - interSpringRelax=0.7*startRadius*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; - - // per-blob mirror delay (if things were well adjusted - in particular mirror waiting times, then this could be 0. - //But in case of unique blobs, it may be interesting to accelerate display AND correct the delay by software): - displaySensingBuffer.setDelayMirrors(2); - break; - case BOUNCING: - sprintf(spotName,"bouncing"); - - setColor(0x07);//0x04+0x02>>i); - - // default (initial) shape: - startRadius=70; - bluePrint.buildCircularScafold(startRadius, vector2Dd(0,0), 20); //(float _radius, vector2Dd _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=70;//bluePrint.radius; - interSpringK=0.4;//46; - interSpringRelax==1.0*startRadius*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; - - // per-blob mirror delay (if things were well adjusted - in particular mirror waiting times, then this could be 0. - //But in case of unique blobs, it may be interesting to accelerate display AND correct the delay by software): - displaySensingBuffer.setDelayMirrors(2); - 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 - NO NEEDED ANYMORE: now calling to static member method of pointMass...) - setRegionMotion(MIN_AD_MIRRORS, MIN_AD_MIRRORS, MAX_AD_MIRRORS, MAX_AD_MIRRORS); - - // draw it once on the display buffer for good initialization: - draw(); -} - -void elasticLoop::speedFactor(float speedfactor) { - // This method is more appropiate for rigid loop, but we can "simulate" speed up in case of elastic loop by changing some parameters, even if the loop is not - // set in "contour following" mode. - factorRecenteringLoopMass*=speedfactor; -} - -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: - 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(startCenter.x+bluePrint.scafold[i].x,startCenter.y+bluePrint.scafold[i].y, startSpeed.x, startSpeed.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(startCenter, startSpeed); - 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(float mmix, float mmiy, float mmax, float 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); - */ - - // Use the static method of the class pointMass: - pointMass::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) { - - vector2Df auxForce= recenteringVectorLoop*factorRecenteringLoopMass*1.0; - vector2Df 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) { - vector2Df 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; - vector2Df 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: - // (1) current color: change with touch? NO - // if (displaySensingBuffer.lightTouched) - // transientBlobColor=blobColor|0x02; // set green ON on the trajectory, regardless of the initial color - // else - transientBlobColor=blobColor; // just the original blob color - - // 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= (unsigned short)( massesLoop[i].pos.x ); // note: it should be an unsigned short - displaySensingBuffer.lsdTrajectory[i].y= (unsigned short)( massesLoop[i].pos.y ); - - //displaySensingBuffer.lsdTrajectory[i]= massesLoop[i].pos.y; // NOTE: doing this means converting from unsigned short to float (vector2Dd to vector2Df) - - //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=transientBlobColor; -} - - -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++) { - vector2Df 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(); - - // (c) Compute kinetic energy: - totalKineticEnergy=0; - for (int i = 0; i < numMasses; i++){ - totalKineticEnergy+=massesLoop[i].getSpeed().squareLength(); - } -} - - -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) { - - // (new) Total kinetic energy: - if (sendingKineticEnergy) { - sprintf(auxstring, "/k %d",identifier); - sendMes.setSubAddress(auxstring); - long x; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!) - x=(long)(totalKineticEnergy); - sendMes.setArgs( "i", &x); - osc.sendOsc( &sendMes ); - } - // (a) Anchor mass: - if (sendingAnchorPosition) { - sprintf(auxstring, "/p %d",identifier); - sendMes.setSubAddress(auxstring); - 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("/a"); - long x; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!) - // x=(long)(area);//approxArea); // area or approxArea - x=(long)(area>0? approxArea : -approxArea); - sendMes.setArgs( "i", &x); // ATTENTION: AREA CAN BE NEGATIVE!!! (does MAX handles this well? test this!) - */ - // HACK for the time being (for Daito): - sendMes.setSubAddress("/a"); - long x, y; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!) - // x=(long)(area);//approxArea); // area or approxArea - x=(long)(w); y=(long)(h); - sendMes.setArgs( "ii", &x, &y); // ATTENTION: AREA CAN BE NEGATIVE!!! (does MAX handles this well? test this!) - - 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... -} - +/* + * 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 clearing the vector container? + massesLoop.clear(); + loopSpringArray.clear(); + hairVector.clear(); + lightForce.clear(); + centralSpringArray.clear(); + displaySensingBuffer.lsdTrajectory.clear(); +} + + +void elasticLoop::createBlob(int _id, ElasticLoopMode _elasticBlobMode, vector2Df _initPos, vector2Df _initSpeed) { + // (1) set ID: + identifier=_id; + + startCenter=_initPos; + startSpeed=_initSpeed; + +// (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) + // Sending data: + periodSendingData=15; // in ms + sendingLoopPositions=false; + sendingBlobArea=true; + sendingKineticEnergy=true; + sendingOnlyWhenTouch=false; // send ALWAYS, regardless of the fact the blob is being touched or not. + +// (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); + setColor(0x04); + + // default (initial) shape (the scafold belongs to the base class): + startRadius=400; + bluePrint.buildCircularScafold(startRadius, vector2Dd(0,0), 40); //(float _radius, vector2Dd _pos, 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=startRadius;// 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; + + // per-blob mirror delay (if things were well adjusted - in particular mirror waiting times, then this could be 0. + //But in case of unique blobs, it may be interesting to accelerate display AND correct the delay by software): + displaySensingBuffer.setDelayMirrors(2); + + break; + + case CONTRACT: + + sprintf(spotName,"loop_contract"); //this is an relaxing elastic loop + + setColor(0x07);//0x04+0x02>>i); + + // default (initial) shape: + startRadius =400; + bluePrint.buildCircularScafold(startRadius, vector2Dd(0,0), 40); //(float _radius, vector2Dd _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=startRadius; + 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; + + // per-blob mirror delay (if things were well adjusted - in particular mirror waiting times, then this could be 0. + //But in case of unique blobs, it may be interesting to accelerate display AND correct the delay by software): + displaySensingBuffer.setDelayMirrors(2); // per-blob mirror delay (if things were well adjusted - in particular mirror waiting times, then this could be 0. + + break; + case CONTRACT_CENTRAL: + + sprintf(spotName,"contract_central"); + + //setColor(0x07);//0x04+0x02>>i); + setColor(0x04); + + // default (initial) shape: + startRadius=400; + bluePrint.buildCircularScafold(startRadius, vector2Dd(0,0), 45); //(float _radius, vector2Dd _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=startRadius; + 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=false ; //true; !!!!!!!!!!!!!!! + angleCorrectionForceNucleus=0;// in deg + recenteringForceOnNucleus=false;//true; + + factorLightForce=6.3;//4.3; + factorRecenteringAnchorMass= 20.0/bluePrint.scafold.size(); + factorRecenteringLoopMass=0.045; + factorPressureLoopMass=1.5; + factorForceBorder=150; + + // per-blob mirror delay (if things were well adjusted - in particular mirror waiting times, then this could be 0. + //But in case of unique blobs, it may be interesting to accelerate display AND correct the delay by software): + displaySensingBuffer.setDelayMirrors(4); + + break; + + case CONTRACT_CENTRAL_FAST: + + //setColor(0x07);//0x04+0x02>>i); + setColor(0x04); + + // default (initial) shape: + startRadius=150; + bluePrint.buildCircularScafold(startRadius, vector2Dd(0,0), 40); //(float _radius, vector2Dd _pos,vector2D _vel, int _numScafoldPoints); + + // Numeric parameters for the simulated mechanical system: + massLoopParticle=0.06; + dampMotionMassesLoop=0.021;//0.17; + massAnchor=0.5; + dampMotionAnchorMass=0.01; + // Springs: + centralSpringK=0.3; + centralSpringRelax=startRadius; + interSpringK=0.54;//46; + interSpringRelax=40; + // for "zack-like" blob: + interParticleRange=150; + factorInterParticleForce=160.0; + + searchActive=false; + pseudopodesMode=false; // this is for contour following. + + // Active/Inactive Forces: + springForcesOnLoop= true; + lightForcesOnLoop= true; + forceBorderOnLoop=false; + nuclearForceOnLoop=false; + interParticleForceOnLoop=true; //!!! + forceInternalPressureOnLoop=false; // (when true, either constant force or calculated area using Green function or approximation by bounding box) + // Recentering vector: + angleCorrectionForceLoop=90;// in deg + recenteringForceOnLoop=true; + angleCorrectionForceNucleus=0;// in deg + recenteringForceOnNucleus=false;//true; + + factorLightForce=-4;//3.0;//8.0; + factorRecenteringAnchorMass= 20.0/bluePrint.scafold.size(); + factorRecenteringLoopMass=0.06; + factorPressureLoopMass=1.5; + factorForceBorder=150; + + displaySensingBuffer.setDelayMirrors(70); + break; + + case CONTOUR_FOLLOWING: + sprintf(spotName,"following"); //this is a contour-following loop + + //setColor(0x07);//0x04+0x02>>i); + setColor(0x04); + + // default (initial) shape: + startRadius=100; + bluePrint.buildCircularScafold(startRadius, vector2Dd(0,0), 20); //(float _radius, vector2Dd _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=100;//bluePrint.radius; + interSpringK=0.4;//46; + interSpringRelax=0.7*startRadius*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=240;//239;// in deg + recenteringForceOnLoop=true; + angleCorrectionForceNucleus=180;// in deg + recenteringForceOnNucleus=false;//true; + + factorLightForce=2.23;//3.0;//8.0; + factorRecenteringAnchorMass=1.0;//20.0/scafold.size(); + factorRecenteringLoopMass=0.09; + factorPressureLoopMass=1.5; + factorForceBorder=150; + // per-blob mirror delay (if things were well adjusted - in particular mirror waiting times, then this could be 0. + //But in case of unique blobs, it may be interesting to accelerate display AND correct the delay by software): + displaySensingBuffer.setDelayMirrors(4); + + break; + case CONTOUR_FOLLOWING_FAST: + sprintf(spotName,"following_fast"); + + setColor(0x07);//0x04+0x02>>i); + + // default (initial) shape: + startRadius=100; + bluePrint.buildCircularScafold(startRadius, vector2Dd(0,0), 30); //(float _radius, vector2Dd _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=100;//bluePrint.radius; + interSpringK=0.5;//46; + interSpringRelax=0.7*startRadius*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; + + // per-blob mirror delay (if things were well adjusted - in particular mirror waiting times, then this could be 0. + //But in case of unique blobs, it may be interesting to accelerate display AND correct the delay by software): + displaySensingBuffer.setDelayMirrors(2); + break; + case BOUNCING: + sprintf(spotName,"bouncing"); + + setColor(0x07);//0x04+0x02>>i); + + // default (initial) shape: + startRadius=70; + bluePrint.buildCircularScafold(startRadius, vector2Dd(0,0), 20); //(float _radius, vector2Dd _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=70;//bluePrint.radius; + interSpringK=0.4;//46; + interSpringRelax==1.0*startRadius*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; + + // per-blob mirror delay (if things were well adjusted - in particular mirror waiting times, then this could be 0. + //But in case of unique blobs, it may be interesting to accelerate display AND correct the delay by software): + displaySensingBuffer.setDelayMirrors(2); + 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 - NO NEEDED ANYMORE: now calling to static member method of pointMass...) + setRegionMotion(MIN_AD_MIRRORS, MIN_AD_MIRRORS, MAX_AD_MIRRORS, MAX_AD_MIRRORS); + + // draw it once on the display buffer for good initialization: + draw(); +} + +void elasticLoop::speedFactor(float speedfactor) { + // This method is more appropiate for rigid loop, but we can "simulate" speed up in case of elastic loop by changing some parameters, even if the loop is not + // set in "contour following" mode. + factorRecenteringLoopMass*=speedfactor; +} + +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: + 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(startCenter.x+bluePrint.scafold[i].x,startCenter.y+bluePrint.scafold[i].y, startSpeed.x, startSpeed.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(startCenter, startSpeed); + 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(float mmix, float mmiy, float mmax, float 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); + */ + + // Use the static method of the class pointMass: + // pointMass::setWallLimits(mmix+10, mmiy+10, mmax-10, mmay-10); + pointMass::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) { + + vector2Df auxForce= recenteringVectorLoop*factorRecenteringLoopMass*1.0; + vector2Df 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) { + vector2Df 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; + vector2Df 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: + // (1) current color: change with touch? NO + // if (displaySensingBuffer.lightTouched) + // transientBlobColor=blobColor|0x02; // set green ON on the trajectory, regardless of the initial color + // else + transientBlobColor=blobColor; // just the original blob color + + // 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= (unsigned short)( massesLoop[i].pos.x ); // note: it should be an unsigned short + displaySensingBuffer.lsdTrajectory[i].y= (unsigned short)( massesLoop[i].pos.y ); + + //displaySensingBuffer.lsdTrajectory[i]= massesLoop[i].pos.y; // NOTE: doing this means converting from unsigned short to float (vector2Dd to vector2Df) + + //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=transientBlobColor; +} + + +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++) { + vector2Df 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(); + + // (c) Compute kinetic energy: + totalKineticEnergy=0; + for (int i = 0; i < numMasses; i++){ + totalKineticEnergy+=massesLoop[i].getSpeed().squareLength(); + } +} + + +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) { + + // (new) Total kinetic energy: + if (sendingKineticEnergy) { + sprintf(auxstring, "/k %d",identifier); + sendMes.setSubAddress(auxstring); + long x; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!) + x=(long)(totalKineticEnergy); + sendMes.setArgs( "i", &x); + osc.sendOsc( &sendMes ); + } + // (a) Anchor mass: + if (sendingAnchorPosition) { + sprintf(auxstring, "/p %d",identifier); + sendMes.setSubAddress(auxstring); + 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("/a"); + long x; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!) + // x=(long)(area);//approxArea); // area or approxArea + x=(long)(area>0? approxArea : -approxArea); + sendMes.setArgs( "i", &x); // ATTENTION: AREA CAN BE NEGATIVE!!! (does MAX handles this well? test this!) + */ + // HACK for the time being (for Daito): + sendMes.setSubAddress("/a"); + long x, y; //ATTENTION: parameters to setArgs should be long or unsigned long only (not int!!) + // x=(long)(area);//approxArea); // area or approxArea + x=(long)(w); y=(long)(h); + sendMes.setArgs( "ii", &x, &y); // ATTENTION: AREA CAN BE NEGATIVE!!! (does MAX handles this well? test this!) + + 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... +} +