//=========================================================================== // FLOCKING BEHAVIORS SONIFIED - November 2006 // Beep() will stop programming action until it completes its sound // and it plays through the system speaker, not the amplifier // PlaySound() will stop playing when another PlaySound is activated // consequently, long .wav files may have a chance to play out // Midi will play notes when they are called // be careful of some instruments whose notes will sustain forever //=========================================================================== //--------------------------------------------------------------------------- #include #include #pragma hdrstop #include #include #include "Unit1.h" // The following two lines are needed to enable the randomizer #include "stdlib.h" #include "time.h" #include using namespace std; #include //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { // Double buffering eliminates some screen flicker... DoubleBuffered = true; } //--------------------------------------------------------------------------- //=========================================================================== // GLOBAL VARIABLES //=========================================================================== // ------------------------------------------------- your program begins here int midiport = 0; HMIDIOUT device; union { public: unsigned long word; unsigned char data[4]; } message; int soundType = 0; int instrument; int note; class anAgent { public: float velocity; double direction; double newDirection; double newDistance; double lastDistance; float x; float y; float pastX; float pastY; int inst; int lastNN; int dist; bool flocking; int bin; } agent[300]; int increment = 7; bool stop = true; bool wasStopped = false; int iterations; int i; int chosenAgent; int shapeDownX, shapeDownY; bool agentWasJustChosen = false; int dist; double vectorD, vectorV, vectorX, vectorY; // average directions & velocities int visibleN; //visible neighbors int visibleI; //visible inst int myNeighbor; int radius = 20; double x, y, z; int a[22]; //=========================================================================== // FUNCTIONS //=========================================================================== //-------------------------------------------------------------------instColor int instColor(int inst){ int red, green, blue; switch (inst) { case 0:{ red = 0; green = 105; blue = 0; //snare break; } case 1:{ red = 0; green = 155; blue = 0; break; } case 2:{ red = 0; green = 205; blue = 0; break; } case 3:{ red = 0; green = 255; blue = 0; break; } case 4:{ red = 0; green = 75; blue = 75; //toms break; } case 5:{ red = 0; green = 95; blue = 95; break; } case 6:{ red = 0; green = 115; blue = 115; break; } case 7:{ red = 0; green = 135; blue = 135; break; } case 8:{ red = 0; green = 155; blue = 155; break; } case 9:{ red = 0; green = 175; blue = 175; break; } case 10:{ red = 0; green = 195; blue = 195; //snare break; } case 11:{ red = 0; green = 215; blue = 215; break; } case 12:{ red = 0; green = 235; blue = 235; break; } case 13:{ red = 0; green = 255; blue = 255; break; } case 14:{ red = 105; green = 255; blue = 205; //HHC break; } case 15:{ red = 155; green = 255; blue = 155; //toms break; } case 16:{ red = 205; green = 255; blue = 105; break; } case 17:{ red = 255; green = 255; blue = 55; break; } case 18:{ red = 255; green = 205; blue = 0; //others CRBHHO break; } case 19:{ red = 255; green = 155; blue = 0; break; } case 20:{ red = 255; green = 105; blue = 0; break; } case 21:{ red = 255; green = 55; blue = 0; break; } //------------------------------------------------------------------------- /* case 22:{ red = 0; green = 175; blue = 175; break; } case 23:{ red = 0; green = 195; blue = 195; //snare break; } case 24:{ red = 0; green = 215; blue = 215; break; } case 25:{ red = 0; green = 235; blue = 235; break; } case 26:{ red = 0; green = 255; blue = 255; break; } case 27:{ red = 105; green = 255; blue = 205; //HHC break; } case 28:{ red = 155; green = 255; blue = 155; //toms break; } case 29:{ red = 205; green = 255; blue = 105; break; } case 30:{ red = 255; green = 255; blue = 55; break; } case 31:{ red = 255; green = 205; blue = 0; //others CRBHHO break; } case 32:{ red = 255; green = 155; blue = 0; break; } */ //------------------------------------------------------------------------- case 2000:{ red = 0; green = 0; blue = 0; break; } default: { red = 255; green = 0; blue = 0; break; } } return (RGB(red, green, blue)); } //---------------------------------------------------------------- color ramp int colorRamp(int range, int value) { int pixelDistanceAlongPath = (value * 1792) / range; int red, green, blue; // Which edge of the color cube are we on? if (pixelDistanceAlongPath < 256) { // Edge 1 from BLACK to BLUE red=0; green=0; blue=pixelDistanceAlongPath; } else if (pixelDistanceAlongPath < 512) { // Edge 2 from BLUE to CYAN red =0; green=pixelDistanceAlongPath-256; blue=255; } else if (pixelDistanceAlongPath < 768) { // Edge 3 from CYAN to GREEN red =0; green =255; blue= 255-(pixelDistanceAlongPath-512); } else if (pixelDistanceAlongPath < 1024) { // Edge 4 from GREEN to YELLOW red= (pixelDistanceAlongPath-768); green =255; blue =0; } else if (pixelDistanceAlongPath <1280) { // Edge 5 from YELLOW to RED red =255; green=255-(pixelDistanceAlongPath-1024); blue =0; } else if (pixelDistanceAlongPath < 1536) { // Edge 6 from RED to MAGENTA red =255; green=0; blue=pixelDistanceAlongPath -1280; } else { // Edge 7 from MAGENTA to WHITE red =255; green=pixelDistanceAlongPath-1536; blue =255; } return (RGB(red, green, blue)); } //------------------------------------------------------------------- shuffle void shuffle (void) { for (int i = 0; i < 300; i++) { float tmp; agent[i].x = random(400) + 30; agent[i].y = random(400) + 30; agent[i].direction = (float(random(1000)) / 500) * M_PI; agent[i].velocity = float(random(1000)) / 1500 + .1; agent[i].inst = int(random(21)); } } //--------------------------------------------------------------------- reset void reset (void) { iterations = 0; Form1->EditIterations->Text = iterations; shuffle(); } //---------------------------------------------------------- nearest neighbor int nearestNeighbor (int me) { vectorD = 0; // directions vectorV = 0; // velocities vectorX = 0; // x component vectorY = 0; // y component visibleN = 0; visibleI = 0; // number of visible neighbors myNeighbor = 0; // my nearest neighbor dist = 10000; // shortest distance for (int i = 0; i < 300; i++) { if (i == me) continue; if (agent[i].bin == agent[me].bin && agent[i].inst != 2000) { x = agent[i].x - agent[me].x; y = agent[i].y - agent[me].y; z = sqrt(x * x + y * y); if (z < dist) { // select nearest neighbor dist = z; myNeighbor = i; } if (z < radius) { // average all visible neighbors visibleN++; visibleI = visibleI + agent[i].inst; vectorY += agent[i].velocity * sin(agent[i].direction); vectorX += agent[i].velocity * cos(agent[i].direction); } } } if ((vectorY != 0) && (vectorX != 0) && (visibleN > 0)) { vectorD = atan(vectorY / vectorX); vectorV = sqrt(vectorX * vectorX + vectorY * vectorY) / visibleN; } return myNeighbor; } //--------------------------------------------------------------nearest same inst int nearestInst(int me) { int myNeighbor = 0; double d, e, f; int dist = 10000; for (int i = 0; i < 300; i++) { if (i == me) continue; if (agent[i].inst == 3 || agent[i].inst == 18 ){ d = agent[i].x - agent[me].x; e = agent[i].y - agent[me].y; f = sqrt(d * d + e * e); if (f < dist) { dist = f; myNeighbor = i;} } } return myNeighbor; } //-------------------------------------------------------------nearest leader int nearestLeader(int me){ int myNeighbor = 0; double d, e, f; int dist = 10000; for (int i = 0; i < 3; i++) { if (i == me) continue; d = agent[i].x - agent[me].x; e = agent[i].y - agent[me].y; f = sqrt(d * d + e * e); if (f < dist) { dist = f; myNeighbor = i;} } return myNeighbor; } //-----------------------------------------------------obtain places of agetnts void placebin(void){ float a,b; for (int i = 0; i < 300; i++) { a = agent[i].x; b = agent[i].y; if (b < 130 && a < 130 ) { agent[i].bin = 0; break; } else if (b < 130 && 130 < a && a < 230) { agent[i].bin = 1; break; } else if (b < 130 && 230 < a && a < 330) { agent[i].bin = 2; break; } else if (b < 130 && 330 < a && a< 430) { agent[i].bin = 3; break; } else if (130 < b && b < 230 && a < 130 ) { agent[i].bin = 4; break; } else if (130 < b && b < 230 && 130 < a && a < 230) { agent[i].bin = 5; break; } else if (130 < b && b < 230 && 230 < a && a < 330) { agent[i].bin = 6; break; } else if (130 < b && b < 130 && 330 < a && a< 430) { agent[i].bin = 7; break; } else if (230 < b && b < 330 && a < 130 ) { agent[i].bin = 8; break; } else if (230 < b && b < 330 && 130 < a && a < 230) { agent[i].bin = 9; break; } else if (230 < b && b < 330 && 230 < a && a < 330) { agent[i].bin = 10; break; } else if (230 < b && b < 330 && 330 < a && a< 430) { agent[i].bin = 11; break; } else if (330 < b && b < 430 && a < 130 ) { agent[i].bin = 12; break; } else if (330 < b && b < 430 && 130 < a && a < 230) { agent[i].bin = 13; break; } else if (330 < b && b < 430 && 230 < a && a < 330) { agent[i].bin = 14; break; } else if (330 < b && b < 430 && 330 < a && a< 430) { agent[i].bin = 15; break; } } } //-----------------------------------------------------------------------Kill void kill(void){ for (int i = 0; i < 20; i++) { a[i] = 0; } for (int i = 0; i < 20; i++) { for (int j = 0; j < 300; j++) { if (agent[j].inst == i) { a[i] = a[i] + 1; } } } int mci = 0; int mci2 = 0; for (int k; k < 20; k++) { if (a[k] > mci) { mci = a[k]; mci2 = k; } } for (int l = 0; l < 300; l++) { if (agent[l].inst == mci2) { agent[l].inst = 2000; } } } //----------------------------------------------------------------------GetS void getS(void){ int NOA = 0; for (int i = 0; i < 300; i++) { if (agent[i].inst != 2000) { NOA++; } } Form1->EditNOA->Text = NOA; } //---------------------------------------------------------------------- step void step (void) { iterations++; Form1->EditIterations->Text = iterations; increment = Form1->TrackBarIncrement->Position; Form1->Canvas->Pen->Color = clWhite; Form1->Canvas->MoveTo(430,30); Form1->Canvas->LineTo(430,430); Form1->Canvas->LineTo(30,430); placebin(); for (i = 0; i < 300; i++) { Form1->Canvas->Pen->Color = clBlack; Form1->Canvas->Brush->Color = clBlack; Form1->Canvas->Ellipse (agent[i].pastX, agent[i].pastY + 5, agent[i].pastX +5, agent[i].pastY); // calculate agent's new position based on velocity and direction agent[i].x += agent[i].velocity * cos(agent[i].direction) * increment; agent[i].y += agent[i].velocity * sin(agent[i].direction) * increment; /////////////////////////////////////////////////////////////////////// ///////////////////// FLOCKING BEHAVIORS BELOW //////////////////////// /////////////////////////////////////////////////////////////////////// if (Form1->RadioGroupFlockingBehavior->ItemIndex == 7) { if (i != 1 || i != 2) { double x, y, z, dir; int leader = nearestLeader(i); int neighbor = nearestNeighbor(i); if ((agent[i].x - agent[leader].x) < 30 && (agent[i].y - agent[leader].y) < 30) { agent[i].direction = -1 * (agent[neighbor].direction); } else { x = agent[i].x - agent[leader].x - 20; y = agent[i].y - agent[leader].y - 15; z = sqrt(x * x + y * y); dir = atan2(y/z, x/z) - M_PI; if (dir < 0) { dir = dir + 2 * M_PI; } agent[i].direction = dir; //agent[i].inst = agent[neighbor].inst; } } } //------------------------------------------------------------Depiction int ins = agent[i].inst; Form1->Canvas->Pen->Color = static_cast(instColor(ins)); Form1->Canvas->Brush->Color = static_cast(instColor(ins)); Form1->Canvas->Ellipse (agent[i].x, agent[i].y + 5, agent[i].x +5, agent[i].y); agent[i].pastX = agent[i].x; agent[i].pastY= agent[i].y; //---------------------------------------------------------------------------- // Your direction and velocity are the average of those // you can see within a given radius... if (Form1->RadioGroupFlockingBehavior->ItemIndex == 8) { nearestNeighbor(i); if (vectorV != 0 && vectorD != 0 && visibleN > 1) { agent[i].velocity = (vectorV + (agent[i].velocity * 199) )/200 ; // average the velocities agent[i].direction = vectorD; agent[i].flocking = TRUE; agent[i].inst = visibleN; } else if (agent[i].flocking == TRUE) { agent[i].direction = (float(random(1000)) / 500) * M_PI; agent[i].velocity = float(random(1000)) / 1500 + .1; agent[i].flocking = FALSE; }} //---------------------------------------------------------------------------- // Your direction and velocity are the average of those // you can see within a given radius... if (Form1->RadioGroupFlockingBehavior->ItemIndex == 9 && agent[i].inst != 2000) { nearestNeighbor(i); if (vectorV != 0 && vectorD != 0 && visibleN > 1) { agent[i].velocity = (vectorV + (agent[i].velocity * 199) )/200 ; // average the velocities agent[i].direction = vectorD; agent[i].flocking = TRUE; int diversity = Form1->TrackBarDiv->Position; int ram(random(2)); if (ram == 0) { agent[i].inst = (visibleI/visibleN) - int(random(diversity)); } else if (ram == 1) { agent[i].inst = (visibleI/visibleN) + int(random(diversity)); } if (agent[i].inst < 0 || agent[i].inst > 21) { agent[i].inst = int(random(20)); } } else if (agent[i].flocking == TRUE) { agent[i].direction = (float(random(1000)) / 500) * M_PI; agent[i].velocity = float(random(1000)) / 1500 + .1; agent[i].flocking = FALSE; }} //---------------------------------------------------------------------- // Your direction and velocity are the average of those // you can see within a given radius... if (Form1->RadioGroupFlockingBehavior->ItemIndex == 10) { agent[1].inst = 2000; agent[2].inst = 2000; if (i != 1 && i != 2) { nearestNeighbor(i); if (vectorV != 0 && vectorD != 0 && visibleN > 1) { agent[i].velocity = (vectorV + (agent[i].velocity * 99) )/100 ; // average the velocities agent[i].direction = vectorD; agent[i].flocking = TRUE; agent[i].inst = visibleN; } else if (agent[i].flocking == TRUE) { int leader = nearestLeader(i); double x, y, z, dir; x = agent[i].x - agent[leader].x - 20; y = agent[i].y - agent[leader].y - 15; z = sqrt(x * x + y * y); dir = atan2(y/z, x/z) - M_PI; if (dir < 0) { dir = dir + 2 * M_PI; } agent[i].direction = dir; }} } //------------------------------------------------------------------------------ // adopt nearest neighbor's direction (problematic with BOUNCE) if (Form1->RadioGroupFlockingBehavior->ItemIndex == 1) { agent[i].direction = agent[nearestNeighbor(i)].direction; } // increment direction by 1/50 // your nearest neighbor's if (Form1->RadioGroupFlockingBehavior->ItemIndex == 2) { agent[i].direction += agent[nearestNeighbor(i)].direction / 50; if (agent[i].direction > 2 * M_PI) { // modulo divide agent[i].direction = agent[i].direction - 2 * M_PI; } } // decrement direction by 1/100 your nearest neighbor's if (Form1->RadioGroupFlockingBehavior->ItemIndex == 3) { agent[i].direction -= agent[nearestNeighbor(i)].direction / 100; if (agent[i].direction > 2 * M_PI) { // modulo divide agent[i].direction = agent[i].direction - 2 * M_PI; } } // adopt nearest neighbor's velocity if (Form1->RadioGroupFlockingBehavior->ItemIndex == 4) { agent[i].velocity = agent[nearestNeighbor(i)].velocity; } // increment by half nearest neighbor's velocity if (Form1->RadioGroupFlockingBehavior->ItemIndex == 5) { agent[i].velocity += agent[nearestNeighbor(i)].velocity / 50; } // adopt nearest neighbor's direction and speed if (Form1->RadioGroupFlockingBehavior->ItemIndex == 6) { agent[i].velocity = agent[nearestNeighbor(i)].velocity; agent[i].direction = agent[nearestNeighbor(i)].direction; } /////////////////////////////////////////////////////////////////////// ///////////////////// FLOCKING BEHAVIORS ABOVE /////////////////////// /////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// BOUNCE if (Form1->RadioGroupBoundary->ItemIndex == 0) { if (agent[i].x > 430) { agent[i].direction = M_PI - agent[i].direction; } if (agent[i].y > 430) { agent[i].direction = 2 * M_PI - agent[i].direction; } if (agent[i].x < 30) { agent[i].direction = M_PI - agent[i].direction; } if (agent[i].y < 30) { agent[i].direction = 2 * M_PI - agent[i].direction; } // modular divide direction by one rotation if (agent[i].direction > 2 * M_PI) { agent[i].direction = agent[i].direction - 2 * M_PI; } if (agent[i].direction < 0) { agent[i].direction = agent[i].direction + 2 * M_PI; } } ///////////////////////////////////////////////////////////////// WRAP else { if (agent[i].x > 420) { agent[i].x = 40; } else if (agent[i].x < 30) { agent[i].x = 420; } else if (agent[i].y > 420) { agent[i].y = 40; } else if (agent[i].y < 35) { agent[i].y = 420; } } } } //--------------------------------------------------------------------run void run (void) { stop = false; message.data[0] = 0xC0; message.data[1] = 6; message.data[2] = 100; message.data[3] = 0; midiOutShortMsg(device, message.word); while (stop == false) { step(); Application->ProcessMessages(); } } //=========================================================================== // EVENT HANDLERS //=========================================================================== //------------------------------------------------------------ On Form Create void __fastcall TForm1::FormCreate(TObject *Sender) { randomize(); shuffle(); } //------------------------------------------------------- On Shape Mouse Down // An exception to the rule which says "Let Borland write event handlers." // This one you must type in yourself in addition to a reference to it // in Unit1.h (look at the source code in that unit). void __fastcall TForm1::ShapeMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { } //------------------------------------------------------- On Shape Mouse Move // An exception to the rule which says "Let Borland write event handlers." // This one you must type in yourself in addition to a reference to it // in Unit1.h (look at the source code in that unit). //--------------------------------------------------------- On Shape Mouse Up // An exception to the rule which says "Let Borland write event handlers." // This one you must type in yourself in addition to a reference to it // in Unit1.h (look at the source code in that unit). void __fastcall TForm1::ShapeMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { } //---------------------------------------------------------------- run button void __fastcall TForm1::ButtonRunClick(TObject *Sender) { run(); } //--------------------------------------------------------------- step button void __fastcall TForm1::ButtonStepClick(TObject *Sender) { stop = true; step(); } //--------------------------------------------------------------- stop button void __fastcall TForm1::ButtonStopClick(TObject *Sender) { stop = true; } //---------------------------------------------------------- randomize button void __fastcall TForm1::ButtonResetClick(TObject *Sender) { reset(); } void __fastcall TForm1::FormPaint(TObject *Sender) { Form1->Canvas->Pen->Color = clWhite; Form1->Canvas->MoveTo(430,30); Form1->Canvas->LineTo(430,430); Form1->Canvas->LineTo(30,430); for (int i=0; i < 300; i++) { int ins = agent[i].inst; Form1->Canvas->Pen->Color = static_cast(instColor(ins)); Form1->Canvas->Brush->Color = static_cast(instColor(ins)); Form1->Canvas->Ellipse (agent[i].x, agent[i].y + 5, agent[i].x +5, agent[i].y); agent[i].pastX = agent[i].x; agent[i].pastY= agent[i].y; } } //--------------------------------------------------------------------------- void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { for (int i = 0; i < 300; i++) { if (agent[i].x < X && agent[i].x > X-5 && agent[i].y < Y && agent[i].y > Y-5 ) { EditVelocity->Text = agent[i].velocity; EditDirection->Text = agent[i].direction; EditAgent->Text = i; EditX->Text = agent[i].x; EditY->Text = agent[i].y; EditInst->Text = agent[i].inst; } } } //--------------------------------------------------------------------------- void __fastcall TForm1::ButtonGetSClick(TObject *Sender) { getS(); } //--------------------------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { kill(); } //---------------------------------------------------------------------------