![](/media/cache/profiles/68a15b5278e4f7c4c056df9d5f1d3b1f.jpg.50x50_q85.jpg)
save loops
elasticLoop.cpp
- Committer:
- mbedalvaro
- Date:
- 2014-12-02
- Revision:
- 0:df6fdd9b99f0
File content as of revision 0:df6fdd9b99f0:
/* * 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::showChildParameters() { // pc.printf("Mirror delay :%d\n", displaySensingBuffer.delayMirrorSamples); // pc.printf("Angle correction force :%d\n", angleCorrectionForceLoop); pc.printf("Integration Step Loop :%f\n", integrationStepLoop); pc.printf("Integration Step Anchor :%f\n", integrationStepAnchor); } 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: integrationStepLoop=0.22; integrationStepAnchor=0.4; slidingDirection=true; // (will change when touching wall) // Sending data: periodSendingData=15; // in ms sendingLoopPositions=false; sendingBlobArea=true; sendingKineticEnergy=true; sendingBlobMaxMin=true; // send ALWAYS, regardless of the fact the blob is being touched or not, in case of elastic loops: sendingOnlyWhenTouch=false; // (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); blueTouch=true; // 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); blueTouch=true; // 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: // this is the "big mouth" integrationStepLoop=0.4; integrationStepAnchor=0.4; sprintf(spotName,"contract_central"); //setColor(0x07);//0x04+0x02>>i); setColor(0x04); blueTouch=true; // 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=25;//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=8.0;//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(1); break; case CONTRACT_CENTRAL_FAST: //setColor(0x07);//0x04+0x02>>i); setColor(0x04); blueTouch=true; // 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(1); break; case CONTOUR_FOLLOWING: sprintf(spotName,"following"); //this is a contour-following loop integrationStepLoop=0.22; integrationStepAnchor=0.4; //setColor(0x07);//0x04+0x02>>i); setColor(0x04); blueTouch=true; // 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; 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=0;// in deg recenteringForceOnLoop=true; angleCorrectionForceNucleus=180;// in deg recenteringForceOnNucleus=false;//true; factorLightForce=2.4;//3.0;//8.0; factorRecenteringAnchorMass=1.0;//20.0/scafold.size(); factorRecenteringLoopMass=0.2; 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 CONTOUR_FOLLOWING_FAST: sprintf(spotName,"following_fast"); setColor(0x07);//0x04+0x02>>i); blueTouch=true; // 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); blueTouch=true; // 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(integrationStepLoop);//22);//19); // 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(vector2Df referencePos) { // (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].rotateDeg(angleCorrectionForceLoop); // correction by hand (interactive) // 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 } recenteringVectorLoop=totalLightForce;//.getRotated(angleCorrectionForceLoop); //== (2) Compute the "recentering vector" from the total light force: // 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, to "ATTACH" like a zip to the black drawing: if (lightForcesOnLoop) { int sign=1; for (int i = 0; i < numMasses; i++) { if ((i%2)==0) sign*=-1; //sign=5*cos(6*2*PI*1.0*i/(numMasses-1))-2; 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]*2.3); // 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?): THIS IS RESPONSIBLE FOR THE "FOLLOWING" BEHAVIOUR if (recenteringForceOnLoop) { vector2Df auxForce= (slidingDirection? recenteringVectorLoop.getRotatedDeg(-145) : recenteringVectorLoop.getRotatedDeg(145))*factorRecenteringLoopMass*1; //vector2Df auxForce2= (slidingDirection? totalLightForce.getRotatedDeg(-90) : totalLightForce.getRotatedDeg(90))*factorRecenteringLoopMass*1.5; //vector2Df auxForce3= (slidingDirection? totalLightForce.getRotatedDeg(-90) : totalLightForce.getRotatedDeg(90))*factorRecenteringLoopMass*1.5; vector2Df auxForce2= (slidingDirection? recenteringVectorLoop.getRotatedDeg(-50) : recenteringVectorLoop.getRotatedDeg(50))*factorRecenteringLoopMass*0.5;//*1.8; vector2Df auxForce3= (slidingDirection? recenteringVectorLoop.getRotatedDeg(-30) : recenteringVectorLoop.getRotatedDeg(30))*factorRecenteringLoopMass*0.6;//1.2; 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 : auxForce3)); // auxForce3: 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= (slidingDirection? recenteringVectorLoop.getRotatedDeg(-90) : recenteringVectorLoop.getRotatedDeg(90))*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 (or only one...), 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... }