//=========================================================================== // FLOCKING POLYGONS - Version 23a // AutoRun with Multiple Timers // 26 March 2013 //=========================================================================== // // EVERYTHING RUNS MORE SLOWLY IN WINDOWS 7 // // This application expands the display to the size of the monitor: // Form1->AutoSize = false; // Form1->BorderStyle = bsNone; // Form1 and PaintBox1 sizes are set to MonitorHeight and MonitorWidth. // // Since the standard Windows controls do not appear: // Form1->Close() is executed when the label "[X]" is clicked. // Right-Click makes a PopUp Menu appear which controls a panel. // // To make the application run with continuously varying parameters: // More than 13 timers change positions of the TrackBars. // // Directions are reckoned in Radians, not degrees as follows: // // North // -pi/2 // -1.57 // // West East // pi + 0 // +/-3.14 0 // // South // +pi/2 // +1.57 // //=========================================================================== // // FOR 3D: // We should optimize by not computing z when it is not required // We should make agents smaller as a function of their depths #include #include #include // enables MIDI #pragma hdrstop #include "Unit1.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; #define POP 1000 // maximum population // still violation if 800 // ========================================================================== // ====================================================== VARIABLES & CLASSES // ========================================================================== // for back gravity long double sumSidesSquared; long double gravForce; long double gravHypot; // for good gravity long double GM, R, centerX, centerY; // temporary for debugging int dirX, velX; bool stop = true; bool move = false; bool anAgentHasBeenChosen = false; bool fixCursorXY = false; bool timers = false; bool anaglyph = false; bool solo = false; bool obstacles = false; bool readyToDrag = false; bool setDirAll = false; bool setVelAll = false; bool agent0FollowsCursor = false; bool box = false; // draw a triangle or a box // for throwing an agent double downX, downY; double upX, upY; double downUpX, downUpY; double timeUp, timeDown; double timeDownUp; double weightedAveDirection; double tangent; double sumXdistancesFromWho, sumYdistancesFromWho, sumZdistancesFromWho; double adjustedAveSumXdistances, adjustedAveSumYdistances; double adjustedAveSumZdistances; double xDistanceFromWho, yDistanceFromWho, zDistanceFromWho; double ave_abs_dX, ave_abs_dY; double weightedAveVelocity, sum_dH, my_H; double noiseX, noiseY; int variance; double directionVariance; int iterations; int delay = 0; int depth = 150; int i; int nn; int dist; int size = 10; int who; int chosen = 0; int lastChosen = 15; int lastLastChosen; //int chosenMidi; int pop = 300; // running population <= POP int lastPop = 100; int rule = 2; int lastRule = 0; int radius = 200; int numberOfNeighbors; int maxNeighbors, lastMaxNeighbors; int independence = 300; int cursorX, cursorY, cursorZ; int monitorWidth = 900; int monitorHeight = 900; int leftClick = 0; int keyboard = 1; int colorChoice = 3; int lastColorChoice = 1; int controlDownX, controlDownY; TColor back = clBlack; TColor fore = clWhite; TColor userColor = clWhite; // user chosen color TColor dynaColor = clWhite; // dynamically chosen color TColor lastDynaColor = dynaColor; // set Pen to color of last Brush TColor defaultColor = clWhite; TColor cyanColor = clAqua; // to allow adjusting the shade of cyan // -------------------------------------------------------------- For the MIDI int midiport = 0; HMIDIOUT device; union { public: unsigned long word; unsigned char data[4]; } message; int instrument1 = 10; // music box int instrument2 = 4; // rhodes piano int note, note1, note2, lastNote1, lastNote2, part, whole; //------------------------------------------------------- 2010.01.20 midiRamp int midiRamp(int part, int whole) { // there are 47 good MIDI musicbox notes ranging from 35 to 81 if (whole == 0) whole++; part = part % whole; note = (part * 47) / whole + 35; return note; } //--------------------------------------------------- Color Ramp Version 2011 TColor colorRamp(int part, int whole) { if (whole == 0) whole++; // prevent divide by zero part = part % whole; // keep part <= whole int pixelDistanceAlongEdges = (part * 1792) / whole; int red, green, blue; // Which edge of the color cube are we on? if (pixelDistanceAlongEdges < 256) { // from BLACK to BLUE red = 0; green = 0; blue = pixelDistanceAlongEdges; } else if (pixelDistanceAlongEdges < 512) { // from BLUE to CYAN red = 0; green = pixelDistanceAlongEdges - 256; blue = 255; } else if (pixelDistanceAlongEdges < 768) { // from CYAN to GREEN red = 0; green = 255; blue = 255 - (pixelDistanceAlongEdges - 512); } else if (pixelDistanceAlongEdges < 1024) { // from GREEN to YELLOW red = (pixelDistanceAlongEdges - 768); green = 255; blue = 0; } else if (pixelDistanceAlongEdges < 1280) { // from YELLOW to RED red = 255; green= 255-(pixelDistanceAlongEdges - 1024); blue = 0; } else if (pixelDistanceAlongEdges < 1536) { // from RED to MAGENTA red = 255; green= 0; blue = pixelDistanceAlongEdges - 1280; } else { // from MAGENTA to WHITE red = 255; green = pixelDistanceAlongEdges - 1537; blue = 255; } return static_cast(RGB(red, green, blue)); } //-------------------------------------------------------- GRAY RAMP FUNCTION TColor grayRamp(int part, int whole) { if (whole == 0) whole = 1; // prevent divide by zero part = part % whole; // keep part <= whole int distance = ( part * 255 ) / whole; return static_cast(RGB(distance, distance, distance)); } //------------------------ declaration of an abstract class called agentClass class agentClass { public: long double x, y, z ; long double dx, dy, dz; long double dxx, dyy, dzz; TPoint twoDPoints[3]; TPoint threeDPoints[3]; TPoint boxPoints[5]; TColor color; float height, width; int tag; int seeking; void erase (void) { // size is dependent upon speed if (anaglyph) { // 3d Form1->PaintBox1->Canvas->Pen->Color = back; Form1->PaintBox1->Canvas->Brush->Color = back; Form1->PaintBox1->Canvas->Brush->Style = bsSolid; //bsClear; Form1->PaintBox1->Canvas->Polygon(twoDPoints, 2); Form1->PaintBox1->Canvas->Polygon(threeDPoints, 2); } else { if (box == false) { // 2d triangle Form1->PaintBox1->Canvas->Pen->Color = back; Form1->PaintBox1->Canvas->Brush->Color = back; Form1->PaintBox1->Canvas->Brush->Style = bsSolid; Form1->PaintBox1->Canvas->Polygon(twoDPoints, 2); } if (box == true) { // 2d box Form1->PaintBox1->Canvas->Pen->Color = back; Form1->PaintBox1->Canvas->Brush->Color = back; Form1->PaintBox1->Canvas->Brush->Style = bsSolid; Form1->PaintBox1->Canvas->Polygon(boxPoints, 3); } } } void draw (void) { // speed determines size twoDPoints[0].x = x + dx * size; twoDPoints[0].y = y + dy * size; twoDPoints[1].x = x - dx * size + size / 2 * dy; twoDPoints[1].y = y - dy * size - size / 2 * dx; twoDPoints[2].x = x - dx * size - size / 2 * dy; twoDPoints[2].y = y - dy * size + size / 2 * dx; // uniform box boxPoints[0].x = x + size; boxPoints[0].y = y + size; boxPoints[1].x = x - size; boxPoints[1].y = y + size; boxPoints[2].x = x - size; boxPoints[2].y = y - size; boxPoints[3].x = x + size; boxPoints[3].y = y - size; //boxPoints[4].x = x + size; boxPoints[4].y = y + size; if (!anaglyph) { // 2D COLORING BY // agent-specific colors if (Form1->RadioGroupColor->ItemIndex == 0) { dynaColor = color; } // speed in colors if (Form1->RadioGroupColor->ItemIndex == 1) { dynaColor = colorRamp(getVelocity() * 300, 1000); } // speed in grays if (Form1->RadioGroupColor->ItemIndex == 2) { dynaColor = grayRamp(getVelocity() * 300, 1000); } // iterations in colors synchronous if (Form1->RadioGroupColor->ItemIndex == 3) { dynaColor = colorRamp(iterations + 10, 3000); } // iterations in grays synchronous if (Form1->RadioGroupColor->ItemIndex == 4) { dynaColor = grayRamp(iterations + 10, 3000); } // iterations in colors asynchronous if (Form1->RadioGroupColor->ItemIndex == 5) { dynaColor = colorRamp(iterations + 5*tag, 5 * pop); } // iterations in grays asynchronous if (Form1->RadioGroupColor->ItemIndex == 6) { dynaColor = grayRamp(iterations + 5*tag, 5 * pop); } // direction in colors if (Form1->RadioGroupColor->ItemIndex == 7) { dynaColor = colorRamp((getDirection() + 3) * 100, 628); } // direction in grays if (Form1->RadioGroupColor->ItemIndex == 8) { dynaColor = grayRamp((getDirection() + 3) * 100, 628); } // numberOfNeighbors in colors if (Form1->RadioGroupColor->ItemIndex == 9) { dynaColor = colorRamp(numberOfNeighbors + 5, maxNeighbors); } // numberOfNeighbors in grays if (Form1->RadioGroupColor->ItemIndex == 10) { dynaColor = grayRamp(numberOfNeighbors + 5, maxNeighbors); } // agent ID color by solorRamp if (Form1->RadioGroupColor->ItemIndex == 12) { dynaColor = colorRamp(tag, pop); } // color trails and outline in black if (Form1->RadioGroupTrail->ItemIndex == 1) { Form1->PaintBox1->Canvas->Pen->Color = clBlack; } // color trails and outline in opposite color // Why, sincd dynaColor is used for both Brush and Pen? if (Form1->RadioGroupTrail->ItemIndex == 2) { Form1->PaintBox1->Canvas->Pen->Color = lastDynaColor; } // color trails and outline in same color if (Form1->RadioGroupTrail->ItemIndex == 3) { Form1->PaintBox1->Canvas->Pen->Color = dynaColor; } lastDynaColor = dynaColor; // remember color of last Brush Form1->PaintBox1->Canvas->Brush->Color = dynaColor; if (tag == chosen || tag == lastChosen) { Form1->PaintBox1->Canvas->Brush->Color = clWhite; } if (box == false) Form1->PaintBox1->Canvas->Polygon(twoDPoints, 2); else Form1->PaintBox1->Canvas->Polygon(boxPoints, 3); } else { // RED is twoDPoints and is OK Form1->PaintBox1->Canvas->Pen->Color = clRed; Form1->PaintBox1->Canvas->Brush->Style = bsClear; Form1->PaintBox1->Canvas->Polygon(twoDPoints, 2); // 3D COLORING // compute CYAN threeDPoints[0].x = x + dx * size + (monitorHeight / 2 - z) / depth;; threeDPoints[0].y = y + dy * size; threeDPoints[1].x = x - dx * size + size / 2 * dy + (monitorHeight / 2 - z) / depth; threeDPoints[1].y = y - dy * size - size / 2 * dx; threeDPoints[2].x = x - dx * size - size / 2 * dy + (monitorHeight / 2 - z) / depth; threeDPoints[2].y = y - dy * size + size / 2 * dx; // CYAN is threeDPoints and OK Form1->PaintBox1->Canvas->Pen->Color = cyanColor; Form1->PaintBox1->Canvas->Brush->Style = bsClear; Form1->PaintBox1->Canvas->Polygon(threeDPoints, 2); } Application->ProcessMessages(); } void move (void) { x += dx; y += dy; if (anaglyph) z += dz; if (Form1->RadioGroupSpace->ItemIndex == 0) { // Bounce if ((x > monitorWidth) || (x < 0)) { // was 885, 155 dx = -dx; } if ((y > monitorHeight) || (y < 0)) { dy = -dy; } if (anaglyph && (z > monitorHeight || z < 0)) { dz = -dz; } } if (Form1->RadioGroupSpace->ItemIndex == 1) { // Wrap if (x > monitorWidth) x = 0; // was 895, 5 if (x < 0) x = monitorWidth; if (y > monitorHeight) y = 0; if (y < 0) y = monitorHeight; if (anaglyph && (z > monitorHeight)) z = 0; if (anaglyph && (z < 0)) z = monitorHeight; } // if (Form1->RadioGroupSpace->ItemIndex == 2 { // Infinite // do nothing // } draw(); } long double getDirection (void) { // ok //if (dx == 0.0) dx = .000000001; // doesn't matter // return atan2(dy, dx); // original return ((dx == 0) && (dy == 0)) ? 0 : atan2(dy,dx); // doesn't matter } long double getVelocity (void) { // ok long double velo = sqrtl(pow(dx, 2.0) + pow(dy, 2.0)); //long double velo = hypotl(dx, dy); // doesn't matter //if (anaglyph) velo = sqrt(pow(velo, 2) + pow(dz, 2)); return velo; } // velocities skew towards minimal at +/- pi/2 (N & S) // all continue to shrink on repeated changes and don't recover void setDirection (long double dir) { dx = getVelocity() * cosl(dir); dy = getVelocity() * sinl(dir); } // directions skew towards the horizontal as velocities increase // all continue to skew on repated changes and don't recover void setVelocity (long double vel) { dx = vel * cosl(getDirection()); dy = vel * sinl(getDirection()); // dx *= vel; // this works OK // dy *= vel; } }; agentClass agent[POP]; // creates an array "agent" of type "agentClass" agentClass circles[4]; agentClass cursor; int me; // ========================================================================== // ================================================================ FUNCTIONS // ========================================================================== // =============================================================== MAKE VALID int mv (int i) { if (i < 0) i = pop; if (i < pop) i = 0; return i; } // ==================================================================== RESET void reset (void) { Form1->Refresh(); //Application->ProcessMessages(); // get depth for 3d if (anaglyph) depth = Form1->TrackBarDepth->Position; iterations = 0; leftClick = 0; maxNeighbors = 0; lastMaxNeighbors = 0; for (int i = 0; i < pop; i++) { agent[i].tag = i; agent[i].erase(); agent[i].height = 10; agent[i].width = 10; //agent[i].seeking = random(pop); //agent[i].color = clGray; //agent[i].direction = 0; //double(i) / 3; computed from dy, dx agent[i].x = random(monitorWidth) - 1; agent[i].y = random(monitorHeight) - 1; agent[i].z = random(monitorHeight) - 1; agent[i].dy = double(random(1000) - 500) / 250.00; // -2 to +2 agent[i].dx = double(random(1000) - 500) / 250.00; // -2 to +2 agent[i].dz = double(random(1000) - 500) / 250.00; // -2 to +2 agent[i].draw(); } } // =============================================================== INITIALIZE void initialize (void) { for (int i = 0; i < POP; i++) { agent[i].tag = i; agent[i].erase(); agent[i].height = 10; agent[i].width = 10; agent[i].color = clGray; //agent[i].direction = 0; // double(i) / 3; computed from dy, dx agent[i].x = random(monitorWidth) - 1; agent[i].y = random(monitorHeight) - 1; agent[i].z = random(monitorHeight) - 1; agent[i].dy = double(random(100) - 50) / 25.00; // -2 to +2 agent[i].dx = double(random(100) - 50) / 25.00; // -2 to +2 agent[i].dz = double(random(100) - 50) / 25.00; // -2 to +2 agent[i].draw(); } Form1->TrackBarSimSpeed->Position = Form1->TrackBarSimSpeed->Max; } // ========================================================= NEAREST NEIGHBOR int nneighbor (int me) { // suspect nn; int minDist = 10000; for (int i = 0; i < pop; i++) { if (i == me) continue; // don't count yourself dist = sqrt( // true Pythagorean distance pow((agent[i].x - agent[me].x), 2) + pow((agent[i].y - agent[me].y), 2) ); if (anaglyph) { dist = sqrt( pow((agent[i].z - agent[me].z), 2) + pow(dist, 2) ); } if (dist < minDist) { minDist = dist; nn = i;} } return nn; } // ======================================================= DISTANCE TO CURSOR double distanceToCursor(int i) { int dist = sqrt(pow((agent[i].x - cursorX), 2) + pow((agent[i].y - cursorY), 2)); return dist; } // =================== SUM THE NEIGHBORS VECTORS WITHIN A RADIUS FROM SOMEONE void sumNeighborsVectorsFrom (int who) { // square, not Pythagorean, radius numberOfNeighbors = 0; sumXdistancesFromWho = 0; sumYdistancesFromWho = 0; sumZdistancesFromWho = 0; sum_dH = 0; xDistanceFromWho = 0; yDistanceFromWho = 0; zDistanceFromWho = 0; for (int neigh = 0; neigh < pop; neigh++) { // look at everyone else if (neigh == who) continue; // but skip who xDistanceFromWho = abs(agent[neigh].x - agent[who].x); yDistanceFromWho = abs(agent[neigh].y - agent[who].y); zDistanceFromWho = abs(agent[neigh].z - agent[who].z); if ((xDistanceFromWho < radius) && (yDistanceFromWho < radius) && (zDistanceFromWho < radius)) { numberOfNeighbors++; sumXdistancesFromWho += agent[neigh].dx; sumYdistancesFromWho += agent[neigh].dy; sumZdistancesFromWho += agent[neigh].dz; } } } // ======================================== ADJUST INFLUENCE BY INDEPENDENCE void adjustNeighborsVectorsByIndependence (void) { adjustedAveSumXdistances = (independence * agent[me].dx + sumXdistancesFromWho) / (numberOfNeighbors + independence); adjustedAveSumYdistances = (independence * agent[me].dy + sumYdistancesFromWho) / (numberOfNeighbors + independence); adjustedAveSumZdistances = (independence * agent[me].dz + sumZdistancesFromWho) / (numberOfNeighbors + independence); if (adjustedAveSumXdistances == 0) adjustedAveSumXdistances = .00001; if (adjustedAveSumYdistances == 0) adjustedAveSumYdistances = .00001; if (adjustedAveSumZdistances == 0) adjustedAveSumZdistances = .00001; } // ================================================================ ADD NOISE void addNoise (void) { directionVariance = 0.001 * ((random(variance + 1)) - 0.5 * variance); weightedAveDirection = atan2(adjustedAveSumYdistances, adjustedAveSumXdistances) + directionVariance; my_H = agent[me].getVelocity(); weightedAveVelocity = (independence * my_H + sum_dH) / (numberOfNeighbors + independence); agent[me].dx = my_H * cos(weightedAveDirection); agent[me].dy = my_H * sin(weightedAveDirection); } // ===================================================================== STEP void step (void) { iterations ++; lastMaxNeighbors = maxNeighbors; maxNeighbors = 0; for (me = 0; me < pop; me++) { if (numberOfNeighbors > maxNeighbors) maxNeighbors = numberOfNeighbors; // draw trails or not if (Form1->RadioGroupTrail->ItemIndex == 0) { agent[me].erase(); } //######################### beginning of rules ################## //######################### beginning of rules ################## // ####################################################### RULE 1 if (rule == 1) { // adopt nearest neighbors direction and speed agent[me].dx = agent[nneighbor(me)].dx; agent[me].dy = agent[nneighbor(me)].dy; } // ####################################################### RULE 2 // adopt weighted average direction and speed of those // within a square radius if (rule == 2) { // calculates numberOfNeighbors and... sumNeighborsVectorsFrom(me); // also calculates numberOfNeighbors // adjusts vector sums by independence adjustNeighborsVectorsByIndependence(); // adds noise addNoise(); } // ####################################################### RULE 3 if (rule == 3) { // flock to cursor sumXdistancesFromWho = cursorX - agent[me].x; sumYdistancesFromWho = cursorY - agent[me].y; sumZdistancesFromWho = cursorZ - agent[me].z; numberOfNeighbors = 1; // adjusts vector sums by independence adjustNeighborsVectorsByIndependence(); // adds noise addNoise(); } // ####################################################### RULE 4 if (rule == 4) { // flee from cursor sumXdistancesFromWho = agent[me].x - cursorX; sumYdistancesFromWho = agent[me].y - cursorY; sumYdistancesFromWho = agent[me].z - cursorZ; numberOfNeighbors = 1; // adjusts vector sums by independence adjustNeighborsVectorsByIndependence(); // adds noise addNoise(); } // ####################################################### RULE 5 if (rule == 5) { // assimilate to nearest neighbor's color agent[me].color = agent[nneighbor(me)].color; } // ####################################################### RULE 6 if (rule == 6) { // follow assigned individual if (agent0FollowsCursor && me == 0) { // if 0 follows cursor sumXdistancesFromWho = cursorX - agent[me].x; sumYdistancesFromWho = cursorY - agent[me].y; sumZdistancesFromWho = cursorZ - agent[me].z; } else { sumXdistancesFromWho = agent[agent[me].seeking].x - agent[me].x; sumYdistancesFromWho = agent[agent[me].seeking].y - agent[me].y; sumZdistancesFromWho = agent[agent[me].seeking].z - agent[me].z; } numberOfNeighbors = 1; // adjusts vector sums by independence adjustNeighborsVectorsByIndependence(); // adds noise addNoise(); } // ####################################################### RULE 7 if (rule == 7) { // follow in line, seek in order sumXdistancesFromWho = agent[mv(me+1)].x - agent[me].x; sumYdistancesFromWho = agent[mv(me+1)].y - agent[me].y; sumZdistancesFromWho = agent[mv(me+1)].z - agent[me].z; numberOfNeighbors = 1; // adjusts vector sums by independence adjustNeighborsVectorsByIndependence(); // adds noise addNoise(); } // ####################################################### RULE 8 if(rule == 8) { // Seek nearest neighbor nneighbor(me); // calculates nn and dist sumXdistancesFromWho = agent[nn].x - agent[me].x; sumYdistancesFromWho = agent[nn].y - agent[me].y; sumZdistancesFromWho = agent[nn].z - agent[me].z; numberOfNeighbors = 1; // adjusts vector sums by independence adjustNeighborsVectorsByIndependence(); // adds noise addNoise(); } // ####################################################### RULE 9 if (rule == 9) { //flee nearest neighbor nneighbor(me); // calculates nn and dist sumXdistancesFromWho = agent[me].x - agent[nn].x; sumYdistancesFromWho = agent[me].y - agent[nn].y; sumZdistancesFromWho = agent[me].z - agent[nn].z; numberOfNeighbors = 1; // adjusts vector sums by independence adjustNeighborsVectorsByIndependence(); // adds noise addNoise(); } // ###################################################### RULE 10 if (rule == 10) { // Odd (.y and .x switched from seek cursor) sumXdistancesFromWho = cursorX - agent[me].y; sumYdistancesFromWho = cursorY - agent[me].x; sumZdistancesFromWho = cursorZ - agent[me].z; numberOfNeighbors = 1; // adjusts vector sums by independence adjustNeighborsVectorsByIndependence(); // adds noise addNoise(); } // ###################################################### RULE 11 if (rule == 11) { // Sarah Van Name's Four Corners if (agent[me].x < 840 && agent[me].y < 525) { sumXdistancesFromWho = 420 - agent[me].x; sumYdistancesFromWho = 262.5 - agent[me].y; sumZdistancesFromWho = 100 - agent[me].z; } if (agent[me].x < 840 && agent[me].y > 525) { sumXdistancesFromWho = 420 - agent[me].x; sumYdistancesFromWho = 787.5 - agent[me].y; sumZdistancesFromWho = 100 - agent[me].z; } if (agent[me].x > 840 && agent[me].y < 525) { sumXdistancesFromWho = 1260 - agent[me].x; sumYdistancesFromWho = 262.5 - agent[me].y; sumZdistancesFromWho = 100 - agent[me].z; } if (agent[me].x > 840 && agent[me].y > 525) { sumXdistancesFromWho = 1260 - agent[me].x; sumYdistancesFromWho = 787.5 - agent[me].y; sumZdistancesFromWho = 100 - agent[me].z; } dynaColor = colorRamp(125, 255); numberOfNeighbors = 1; // adjusts vector sums by independence adjustNeighborsVectorsByIndependence(); // adds noise addNoise(); } // ###################################################### RULE 12 if (rule == 12) { // Gravity: Sun at Center // GM is symonymous with independence // z is ignored R = sqrt(pow(centerX - agent[me].x, 2) + pow(centerY - agent[me].y, 2)); agent[me].dx -= (GM) / pow(R, 3) * (agent[me].x - centerX); agent[me].dy -= (GM) / pow(R, 3) * (agent[me].y - centerY); //agent[me].dZ = - (GM) / pow(R, 3) * agent[me].z); } // ###################################################### RULE 13 if (rule == 13) { // Sarah Van Name's Lissajou Figure agent[mv(me + 1)].x = 840 + .45 * tan(me * 20) * 512; agent[mv(me + 1)].y = 525 + 0.3 * sin(me * 20) * 512; sumXdistancesFromWho = agent[mv(me + 1)].x - agent[me].x; sumYdistancesFromWho = agent[mv(me + 1)].y - agent[me].y; sumZdistancesFromWho = agent[mv(me + 1)].z - agent[me].z; numberOfNeighbors = 1; // weight vectors by independence adjustNeighborsVectorsByIndependence(); // add noise addNoise(); } // ###################################################### RULE 14 if(rule == 14) { // Flee Larger, Seek Smaller if(agent[nneighbor(me)].getVelocity() > agent[me].getVelocity()) { sumXdistancesFromWho = -1 * (agent[nneighbor(me)].x - agent[me].x); sumYdistancesFromWho = -1 * (agent[nneighbor(me)].y - agent[me].y); sumZdistancesFromWho = -1 * (agent[nneighbor(me)].z - agent[me].z); } else { sumXdistancesFromWho = agent[nneighbor(me)].x - agent[me].x; sumYdistancesFromWho = agent[nneighbor(me)].y - agent[me].y; sumZdistancesFromWho = agent[nneighbor(me)].z - agent[me].z; } numberOfNeighbors = 1; // weight vectors by independence adjustNeighborsVectorsByIndependence(); // add noise addNoise(); } // ###################################################### RULE 15 if (rule == 15) { // circle: seek radius from cursor if (distanceToCursor(me) < radius) { // flee sumXdistancesFromWho = agent[me].x - cursorX; sumYdistancesFromWho = agent[me].y - cursorY; } else { // flock sumXdistancesFromWho = cursorX - agent[me].x; sumYdistancesFromWho = cursorY - agent[me].y; } numberOfNeighbors = 1; // weight vectors by independence adjustNeighborsVectorsByIndependence(); // add noise addNoise(); } // ###################################################### RULE 16 if (rule == 16) { // Gravitational attraction to cursor sumSidesSquared = pow(cursorX - agent[me].x, 2) + pow(cursorY -agent[me].y, 2); gravForce = (radius * 10000) / sumSidesSquared; gravHypot = sqrt(sumSidesSquared); sumXdistancesFromWho = (gravForce / gravHypot) * (cursorX - agent[me].x); sumYdistancesFromWho = (gravForce / gravHypot) * (cursorY - agent[me].y); numberOfNeighbors = 1; // adjusts vector sums by independence adjustNeighborsVectorsByIndependence(); // adds noise addNoise(); } // ###################################################### RULE 17 if(rule == 17) { // Flee near, seek far nneighbor(me); // calculates nn and dist if (dist > radius) { // seek far sumXdistancesFromWho = agent[nn].x - agent[me].x; sumYdistancesFromWho = agent[nn].y - agent[me].y; sumZdistancesFromWho = agent[nn].z - agent[me].z; } if (dist < radius) { // flee near sumXdistancesFromWho = agent[me].x - agent[nn].x; sumYdistancesFromWho = agent[me].y - agent[nn].y; sumZdistancesFromWho = agent[me].z - agent[nn].z; } numberOfNeighbors = 1; // adjusts vector sums by independence adjustNeighborsVectorsByIndependence(); // adds noise addNoise(); } //############################### end of rules ################## //############################### end of rules ################## if (obstacles) { if (Form1->Shape1->Visible && (((agent[me].x - Form1->Shape1->Left < 10) && (agent[me].x - Form1->Shape1->Left > 0)) || ((agent[me].x - (Form1->Shape1->Left + Form1->Shape1->Width) > -10) && (agent[me].x - (Form1->Shape1->Left + Form1->Shape1->Width) < 0))) && ((agent[me].y > Form1->Shape1->Top) && (agent[me].y < Form1->Shape1->Top + Form1->Shape1->Height))) { agent[me].dx = -agent[me].dx; } if (Form1->Shape1->Visible && (((agent[me].y - Form1->Shape1->Top < 10) && (agent[me].y - Form1->Shape1->Top > 0)) || ((agent[me].y - (Form1->Shape1->Top + Form1->Shape1->Height) > -10) && (agent[me].y - (Form1->Shape1->Top + Form1->Shape1->Height) < 0))) && ((agent[me].x > Form1->Shape1->Left) && (agent[me].x < Form1->Shape1->Left + Form1->Shape1->Width))) { agent[me].dy = -agent[me].dy; } if (Form1->Shape2->Visible && (((agent[me].x - Form1->Shape2->Left < 10) && (agent[me].x - Form1->Shape2->Left > 0)) || ((agent[me].x - (Form1->Shape2->Left + Form1->Shape2->Width) > -10) && (agent[me].x - (Form1->Shape2->Left + Form1->Shape2->Width) < 0))) && ((agent[me].y > Form1->Shape2->Top) && (agent[me].y < Form1->Shape2->Top + Form1->Shape2->Height))) { agent[me].dx = -agent[me].dx; } if (Form1->Shape2->Visible && (((agent[me].y - Form1->Shape2->Top < 10) && (agent[me].y - Form1->Shape2->Top > 0)) || ((agent[me].y - (Form1->Shape2->Top + Form1->Shape2->Height) > -10) && (agent[me].y - (Form1->Shape2->Top + Form1->Shape2->Height) < 0))) && ((agent[me].x > Form1->Shape2->Left) && (agent[me].x < Form1->Shape2->Left + Form1->Shape2->Width))) { agent[me].dy = -agent[me].dy; } if (Form1->Shape3->Visible && (((agent[me].x - Form1->Shape3->Left < 10) && (agent[me].x - Form1->Shape3->Left > 0)) || ((agent[me].x - (Form1->Shape3->Left + Form1->Shape3->Width) > -10) && (agent[me].x - (Form1->Shape3->Left + Form1->Shape3->Width) < 0))) && ((agent[me].y > Form1->Shape3->Top) && (agent[me].y < Form1->Shape3->Top + Form1->Shape3->Height))) { agent[me].dx = -agent[me].dx; } if (Form1->Shape3->Visible && (((agent[me].y - Form1->Shape3->Top < 10) && (agent[me].y - Form1->Shape3->Top > 0)) || ((agent[me].y - (Form1->Shape3->Top + Form1->Shape3->Height) > -10) && (agent[me].y - (Form1->Shape3->Top + Form1->Shape3->Height) < 0))) && ((agent[me].x > Form1->Shape3->Left) && (agent[me].x < Form1->Shape3->Left + Form1->Shape3->Width))) { agent[me].dy = -agent[me].dy; } } // end of all rules agent[me].move(); // any color specified here is overridden if (rule == 12) { Form1->PaintBox1->Canvas->Ellipse (centerX -10, centerY -10, centerX +10, centerY +10); } } Form1->Shape1->BringToFront(); Form1->Shape2->BringToFront(); Form1->Shape3->BringToFront(); Sleep(delay); } // ================================================================== REFRESH void refresh (void) { Form1->PaintBox1->Refresh(); for (int i = 0; i < pop; i++) { agent[i].draw(); } } // ====================================================================== RUN void run (void) { stop = false; while (stop == false) { Application->ProcessMessages(); // get depth for 3d if (anaglyph) depth = 220 - Form1->TrackBarDepth->Position; iterations++; step(); } } //================================================================== AUTO RUN void autoRun (void) { } // ========================================================================== // =========================================================== EVENT HANDLERS // ========================================================================== //---------------------------------------------------------- FORM CONSTRUCTOR __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { Randomize(); DoubleBuffered = true; // MakeFullyVisible(); // See also Screen->Height, Width // See also Monitor->Height, Width monitorHeight = Monitor->Height; centerY = monitorHeight / 2; Form1->Height = monitorHeight; Form1->PaintBox1->Height = monitorHeight; cursorY = monitorHeight / 2; monitorWidth = Monitor->Width; centerX = monitorWidth / 2; Form1->Width = monitorWidth; Form1->PaintBox1->Width = monitorWidth; cursorX = monitorWidth / 2; randomize(); initialize(); Form1->PaintBox1->Canvas->Pen->Width = 1; // for the MIDI system midiOutOpen(&device, midiport, 0, 0, CALLBACK_NULL); message.data[0] = 0xC0; // choose instrument command message.data[1] = instrument1; // instrument message.data[2] = 100; // once set, no need to repeat message.data[3] = 0; // once set, no need to repeat midiOutShortMsg(device, message.word); reset(); } //----------------------------------------------------------------- SHOW FORM void __fastcall TForm1::FormShow(TObject *Sender) { refresh(); } //------------------------------------------------------------- ACTIVATE FORM void __fastcall TForm1::FormActivate(TObject *Sender) { refresh(); } //------------------------------------------------------ PAINT BOX MOUSE DOWN void __fastcall TForm1::PaintBox1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { int shift = Shift.ToInt(); // shft values: | Left Right Wheel // -------------------------------------------------------------- // no key | 8 cursor 16 popup 32 // Shift | 9 musicians 17 33 probe // Alt | 10 color 18 34 // Ctrl | 12 obstacles 20 36 // -------------------------------------------------------------- // Left button #0 to set and unset, cursor X and Y // Clicks alternate between a fixed & moving cursor if (shift == 8) { fixCursorXY = !fixCursorXY; if (fixCursorXY == true) { cursorX = X; cursorY = Y; cursorZ = monitorHeight / 2; } } // Right Button #1 (shft 16) is reserved for the Popup Menu // Shift left button to set the nearest agent as a musician // Two musical agents will be colored white if (shift == 9) { int shortestDistance = 1000; lastLastChosen = lastChosen; lastChosen = chosen; for (i = 0; i < pop; i++) { int distance = abs(X - agent[i].x) + abs(Y - agent[i].y); if (distance < shortestDistance) { shortestDistance = distance; chosen = i; } } agent[lastLastChosen].draw(); agent[chosen].draw(); } // Alt left button to color the nearest agent // This only shows with color rule 0, "color by agent color" if (shift == 10) { int shortestDistance = 1000; for (i = 0; i < pop; i++) { int distance = abs(X - agent[i].x) + abs(Y - agent[i].y); if (distance < shortestDistance) { shortestDistance = distance; who = i; } } agent[who].color = userColor; agent[who].draw(); } // Control Left Click to enter obstacles in order // Obstacles must be enabled if (shift == 12) { leftClick = leftClick % 4; circles[leftClick].x = X; circles[leftClick].y = Y; if (leftClick == 0) { Form1->Shape1->Left = X - 15; Form1->Shape1->Top = Y - 15; Form1->Shape1->Visible = true; } if (leftClick == 1) { Form1->Shape2->Left = X - 15; Form1->Shape2->Top = Y - 15; Form1->Shape2->Visible = true; } if (leftClick == 2) { Form1->Shape3->Left = X - 15; Form1->Shape3->Top = Y - 15; Form1->Shape3->Visible = true; } if (leftClick == 3) { Form1->Shape1->Visible = false; Form1->Shape2->Visible = false; Form1->Shape3->Visible = false; } leftClick++; } // Shift Wheel Click to probe nearest agent if (shift == 33) { int shortestDistance = 1000; for (i = 0; i < pop; i++) { int distance = abs(X - agent[i].x) + abs(Y - agent[i].y); if (distance < shortestDistance) { shortestDistance = distance; who = i; } } // Edit->Text cannot convert a long double to a string double tempDX = agent[who].dx; double tempDY = agent[who].dy; Form1->EditWho->Text = agent[who].tag; Form1->EditDX->Text = tempDX; Form1->EditDY->Text = tempDY; double tempDirection = agent[who].getDirection(); double tempVelocity = agent[who].getVelocity(); Form1->EditDir->Text = tempDirection; Form1->EditVel->Text = tempVelocity; } } //------------------------------------------------------ PAINT BOX MOUSE MOVE void __fastcall TForm1::PaintBox1MouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { if (fixCursorXY == false) { cursorX = X; // useful for some rules cursorY = Y; } } //---------------------------------------------------------------- RUN BUTTON void __fastcall TForm1::ButtonRunClick(TObject *Sender) { Refresh(); // without this initial refresh, the sim runs slowly run(); } //--------------------------------------------------------------- STEP BUTTON void __fastcall TForm1::ButtonStepClick(TObject *Sender) { stop = true; step(); } //--------------------------------------------------------------- STOP BUTTON void __fastcall TForm1::ButtonStopClick(TObject *Sender) { stop = true; } //-------------------------------------------------------------- RESET BUTTON void __fastcall TForm1::ButtonResetClick(TObject *Sender) { reset(); } //------------------------------------------------------- SELECT COLOR BUTTON void __fastcall TForm1::ButtonSelectColorClick(TObject *Sender) { if(Form1->ColorDialog1->Execute()) { userColor = ColorDialog1->Color; Form1->ShapeColor->Brush->Color = userColor; } } //---------------------------------------------------- EQUALIZE SPEED BUTTON void __fastcall TForm1::ButtonEqualizeSpeedRandomizeDirectionClick (TObject *Sender) { for (int i = 0; i < POP; i++) { double dir = (random(M_PI * 2000)/1000 - double(M_PI)); agent[i].dx = cos(dir) * 2; agent[i].dy = sin(dir) * 2; } } //------------------------------------------------------- CLEAR SCREEN BUTTON void __fastcall TForm1::ButtonClearClick(TObject *Sender) { Form1->Refresh(); } //---------------------------------------------------------------- RUN POPUP void __fastcall TForm1::Exit1Click(TObject *Sender) { run(); } //--------------------------------------------------------------- STEP POPUP void __fastcall TForm1::Step1Click(TObject *Sender) { stop = true; } //--------------------------------------------------------------- STOP POPUP void __fastcall TForm1::Run1Click(TObject *Sender) { stop = true; step(); } //--------------------------------------------------------------- CLOSE POPUP void __fastcall TForm1::Exit2Click(TObject *Sender) { stop = true; Form1->Close(); } //------------------------------------------------------------- VISIBLE POPUP void __fastcall TForm1::Visible1Click(TObject *Sender) { Form1->GroupBoxControls->Visible = true; } //-------------------------------------------------------------- HIDDEN POPUP void __fastcall TForm1::Hidden1Click(TObject *Sender) { Form1->GroupBoxControls->Visible = false; } //--------------------------------------------------------------- RESET POPUP void __fastcall TForm1::Reset1Click(TObject *Sender) { reset(); Form1->Refresh(); } //-------------------------------------------------------- CLEAR SCREEN POPUP void __fastcall TForm1::ClearScreen1Click(TObject *Sender) { Form1->Refresh(); } //--------------------------------------------------------- TRAIL RADIO GROUP void __fastcall TForm1::RadioGroupTrailClick(TObject *Sender) { //Form1->Refresh(); } //--------------------------------------------------------- RULES RADIO GROUP void __fastcall TForm1::RadioGroupRulesClick(TObject *Sender) { rule = RadioGroupRules->ItemIndex; // if gravitational orbits set space to infinite if (rule == 12) { Form1->RadioGroupSpace->ItemIndex = 2; // infinite Form1->Label5->Caption = "Gravitation"; // change label Form1->RadioGroupShape->ItemIndex = 1; // box Form1->TrackBarSize->Position = 1; // size = 1 Form1->RadioGroupTrail->ItemIndex = 3; // trail in same color Form1->TrackBarIndependence->Position = 700; // a good setting Form1->RadioGroupColor->ItemIndex = 12; // color by agent tag } else { Form1->RadioGroupSpace->ItemIndex = 1; // wrapped Form1->Label5->Caption = "Independence"; // change label Form1->RadioGroupShape->ItemIndex = 0; // triangle Form1->TrackBarSize->Position = 10; // size = 10 Form1->RadioGroupTrail->ItemIndex = 0; // trail off Form1->RadioGroupColor->ItemIndex = 1; // color by speed } } //--------------------------------------------------------- COLOR RADIO GROUP void __fastcall TForm1::RadioGroupColorClick(TObject *Sender) { Form1->Refresh(); colorChoice = RadioGroupColor->ItemIndex; if (colorChoice == 11) anaglyph = true; else anaglyph = false; for (int i = 0; i < pop; i++) { agent[i].erase(); agent[i].draw(); } } //----------------------------------------------------- PEN WIDTH RADIO GROUP void __fastcall TForm1::RadioGroupPenWidthClick(TObject *Sender) { Form1->PaintBox1->Canvas->Pen->Width = RadioGroupPenWidth->ItemIndex + 1; } //--------------------------------------------------------- SOUND RADIO GROUP void __fastcall TForm1::RadioGroupSoundClick(TObject *Sender) { if (RadioGroupSound->ItemIndex == 0) { Form1->TimerSound->Enabled = false; } else Form1->TimerSound->Enabled = true; } //-------------------------------------------------- INSTRUMENT 1 RADIO GROUP void __fastcall TForm1::RadioGroupInstrument1Click(TObject *Sender) { if (RadioGroupInstrument1->ItemIndex == 0) instrument1 = 10; if (RadioGroupInstrument1->ItemIndex == 1) instrument1 = 9; if (RadioGroupInstrument1->ItemIndex == 2) instrument1 = 4; if (RadioGroupInstrument1->ItemIndex == 3) instrument1 = 121; } //-------------------------------------------------- INSTRUMENT 2 RADIO GROUP void __fastcall TForm1::RadioGroupInstrument2Click(TObject *Sender) { if (RadioGroupInstrument2->ItemIndex == 0) instrument2 = 10; if (RadioGroupInstrument2->ItemIndex == 1) instrument2 = 9; if (RadioGroupInstrument2->ItemIndex == 2) instrument2 = 4; if (RadioGroupInstrument2->ItemIndex == 3) instrument2 = 121; } //-------------------------------------------------------- Voices Radio Group void __fastcall TForm1::RadioGroupVoicesClick(TObject *Sender) { if (RadioGroupVoices->ItemIndex == 0) { solo = true; } else solo = false; } //------------------------------------------------------ Keyboard Radio Group void __fastcall TForm1::RadioGroupKeyboardClick(TObject *Sender) { keyboard = RadioGroupKeyboard->ItemIndex; } //------------------------------------------------- ON/OFF TIMERS RADIO GROUP void __fastcall TForm1::RadioGroupTimersClick(TObject *Sender) { timers = !timers; cursorX = random(monitorWidth - 10); cursorY = random(monitorHeight - 10); if (timers == true) { Form1->GroupBoxControls->Visible = false; Form1->TimerColor->Enabled = true; Form1->TimerRules->Enabled = true; Form1->TimerRadius->Enabled = true; Form1->TimerIndependence->Enabled = true; Form1->TimerVariance->Enabled = true; Form1->TimerReset->Enabled = true; Form1->TimerTrails->Enabled = true; Form1->TimerCursor->Enabled = true; Form1->TimerPopulation->Enabled = true; Form1->TimerOneSecond->Enabled = true; Form1->TimerRefresh->Enabled = true; Form1->TimerSize->Enabled = true; Form1->TimerBeatsPerSecond->Enabled = true; } else { Form1->GroupBoxControls->Visible = true; Form1->TimerColor->Enabled = false; Form1->TimerRules->Enabled = false; Form1->TimerRadius->Enabled = false; Form1->TimerIndependence->Enabled = false; Form1->TimerVariance->Enabled = false; Form1->TimerReset->Enabled = false; Form1->TimerTrails->Enabled = false; Form1->TimerCursor->Enabled = false; Form1->TimerPopulation->Enabled = false; Form1->TimerOneSecond->Enabled = false; Form1->TimerRefresh->Enabled = false; Form1->TimerSize->Enabled = false; Form1->TimerBeatsPerSecond->Enabled = false; } run(); } //----------------------------------------------------- OBSTACLES RADIO GROUP void __fastcall TForm1::RadioGroupObstaclesClick(TObject *Sender) { obstacles = RadioGroupObstacles->ItemIndex; if (RadioGroupObstacles->ItemIndex == 0) { Form1->Shape1->Visible = false; Form1->Shape2->Visible = false; Form1->Shape3->Visible = false; } } //---------------------------------------------------- DEPTH CHANGE TRACK BAR void __fastcall TForm1::TrackBarDepthChange(TObject *Sender) { Form1->EditDepth->Text = TrackBarDepth->Position; } //------------------------------------------------ SIMULATION SPEED TRACK BAR void __fastcall TForm1::TrackBarSimSpeedChange(TObject *Sender) { delay = TrackBarSimSpeed->Max - TrackBarSimSpeed->Position; Form1->EditDelay->Text = delay; } //------------------------------------------------------ POPULATION TRACK BAR void __fastcall TForm1::TrackBarPopulationChange(TObject *Sender) { // remember previous population lastPop = pop; pop = TrackBarPopulation->Position; Form1->EditPop->Text = pop; // if new pop smaller, erase excess agents if (pop < lastPop) Form1->PaintBox1->Refresh(); reset(); } //----------------------------------------------------------- RADIUS TRACKBAR void __fastcall TForm1::TrackBarRadiusChange(TObject *Sender) { radius = TrackBarRadius->Position; Form1->EditRadius->Text = radius; } //----------------------------------------------------- INDEPENDENCE TRACKBAR void __fastcall TForm1::TrackBarIndependenceChange(TObject *Sender) { independence = TrackBarIndependence->Position; GM = independence; Form1->EditStubbornness->Text = independence; } //--------------------------------------------------------- VARIANCE TRACKBAR void __fastcall TForm1::TrackBarVarianceChange(TObject *Sender) { variance = TrackBarVariance->Position; Form1->EditVariance->Text = variance; } //------------------------------------------------------ CHANGE SIZE TRACKBAR void __fastcall TForm1::TrackBarSizeChange(TObject *Sender) { size = TrackBarSize->Position; Form1->EditSize->Text = size; } //--------------------------------------------------------------- COLOR TIMER void __fastcall TForm1::TimerColorTimer(TObject *Sender) { Form1->RadioGroupColor->ItemIndex = random(10) + 1; } //--------------------------------------------------------------- RULES TIMER void __fastcall TForm1::TimerRulesTimer(TObject *Sender) { Form1->RadioGroupRules->ItemIndex = random(15) + 1; } //-------------------------------------------------------------- RADIUS TIMER void __fastcall TForm1::TimerRadiusTimer(TObject *Sender) { Form1->TrackBarRadius->Position = random(400); } //-------------------------------------------------------- INDEPENDENCE TIMER void __fastcall TForm1::TimerIndependenceTimer(TObject *Sender) { Form1->TrackBarIndependence->Position = random(2000) + 1; } //------------------------------------------------------------ VARIANCE TIMER void __fastcall TForm1::TimerVarianceTimer(TObject *Sender) { Form1->TrackBarVariance->Position = random(200); } //--------------------------------------------------------------- RESET TIMER void __fastcall TForm1::TimerResetTimer(TObject *Sender) { reset(); } //-------------------------------------------------------------- TRAILS TIMER void __fastcall TForm1::TimerTrailsTimer(TObject *Sender) { Form1->RadioGroupTrail->ItemIndex = random(4); } //-------------------------------------------------------------- CURSOR TIMER void __fastcall TForm1::TimerCursorTimer(TObject *Sender) { cursorX = random(monitorWidth - 20) + 10; cursorY = random(monitorHeight - 20) + 10; } //---------------------------------------------------------- POPULATION TIMER void __fastcall TForm1::TimerPopulationTimer(TObject *Sender) { Form1->TrackBarPopulation->Position = 20 + random(675); // Access Violation if pop < 100 } //---------------------------------------------------------- ONE SECOND TIMER REFRESH TIMER void __fastcall TForm1::TimerRefreshTimer(TObject *Sender) { Form1->Refresh(); } //---------------------------------------------------------------- SIZE TIMER void __fastcall TForm1::TimerSizeTimer(TObject *Sender) { Form1->TrackBarSize->Position = random(47) + 2; } //--------------------------------------------------------------- SOUND TIMER void __fastcall TForm1::TimerSoundTimer(TObject *Sender) { // direction ranges from -3.14 to +3.14 // note = midiRamp(agent[0].getDirection(),32); // note ranges from 33-37 if (keyboard == 0) { // 7 note keyboard note1 = midiRamp(agent[chosen].getDirection() + 3.14159, 7); note2 = midiRamp(agent[lastChosen].getDirection() + 3.14159, 7); } if (keyboard == 1) { // 47 note keyboard note1 = midiRamp((agent[chosen].getDirection() + 3) * 100, 628); note2 = midiRamp((agent[lastChosen].getDirection() + 3) * 100, 628); } if (note1 != lastNote1) { Form1->MemoProbe->Lines->Append(IntToStr(chosen) + " " + IntToStr(note1) + " " + FloatToStr(agent[chosen].getDirection())); message.data[0] = 0xC0; // choose instrument command message.data[1] = instrument1; // instrument midiOutShortMsg(device, message.word); message.data[0] = 0x90; // note on message.data[1] = note; // note # midiOutShortMsg(device, message.word); // tell midi } lastNote1 = note1; if (solo == false) { if (note2 != lastNote2) { Form1->MemoProbe->Lines->Append(IntToStr(lastChosen) + " " + IntToStr(note2) + " " + FloatToStr(agent[lastChosen].getDirection())); message.data[0] = 0xC0; // choose instrument command message.data[1] = instrument2; // instrument midiOutShortMsg(device, message.word); message.data[0] = 0x90; // note on message.data[1] = note; // note # midiOutShortMsg(device, message.word); // tell midi } lastNote2 = note2; } } //--------------------------------------------------------------- CLOSE LABEL void __fastcall TForm1::LabelCloseClick(TObject *Sender) { stop = true; Form1->Close(); } //---------------------------------------------------------------- FORM CLOSE void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) { midiOutClose(&device); } //--------------------------------------------------------------------------- void __fastcall TForm1::RadioGroupBeatsPerSecondClick(TObject *Sender) { int beats = RadioGroupBeatsPerSecond->ItemIndex; Form1->TimerSound->Interval = 1000 / (beats + 1); } //--------------------------------------------------------------------------- void __fastcall TForm1::TimerBeatsPerSecondTimer(TObject *Sender) { int choice = random(6); if (choice == 5) Form1->RadioGroupSound->ItemIndex = !Form1->RadioGroupSound->ItemIndex; else Form1->RadioGroupBeatsPerSecond->ItemIndex = random(5); } //--------------------------------------------------------------------------- void __fastcall TForm1::GroupBoxControlsMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { readyToDrag = true; controlDownX = X; controlDownY = Y; } //--------------------------------------------------------------------------- void __fastcall TForm1::GroupBoxControlsMouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { if (readyToDrag) { GroupBoxControls->Left = GroupBoxControls->Left + X - controlDownX; GroupBoxControls->Top = GroupBoxControls->Top + Y - controlDownY; } } //--------------------------------------------------------------------------- void __fastcall TForm1::GroupBoxControlsMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { readyToDrag = false; } //--------------------------------------------------------------------------- void __fastcall TForm1::ButtonSetAllToCMYGClick(TObject *Sender) { for (int i = 0; i < pop; i++) { if (i % 4 == 0) agent[i].color = clAqua; if (i % 4 == 1) agent[i].color = clFuchsia; if (i % 4 == 2) agent[i].color = clYellow; if (i % 4 == 3) agent[i].color = clGray; RadioGroupColor->ItemIndex = 0; colorChoice = 0; agent[i].draw(); } } //--------------------------------------------------------------------------- void __fastcall TForm1::ButtonSetAllToGrayClick(TObject *Sender) { for (int i = 0; i < pop; i++) { agent[i].color = clGray; RadioGroupColor->ItemIndex = 0; colorChoice = 0; agent[i].draw(); } } //--------------------------------------------------------------------------- void __fastcall TForm1::TrackBarCyanChange(TObject *Sender) { int darkness = 255 - TrackBarCyan->Position; cyanColor = static_cast(RGB(0, darkness, darkness)); Form1->EditCyan->Text = darkness; } //--------------------------------------------------------------------------- void __fastcall TForm1::TrackBarSetDirectionChange(TObject *Sender) { //setDirAll = true; dirX++; Form1->EditDirX->Text = dirX; long double ldSetDir; for (int i = 0; i < POP; i++) { ldSetDir = TrackBarSetDirection->Position / 10000.00; agent[i].setDirection(ldSetDir); //agent[i].draw(); } double dSetDir = ldSetDir; Form1->EditSetDirection->Text = String(dSetDir); } //--------------------------------------------------------------------------- void __fastcall TForm1::EditSetDirectionClick(TObject *Sender) { for (int i = 0; i < POP; i++) { agent[i].setDirection(-0.4); } step(); Sleep(500); for (int i = 0; i < POP; i++) { agent[i].setDirection(-0.8); } step(); Sleep(500); for (int i = 0; i < POP; i++) { agent[i].setDirection(-1.2); } step(); Sleep(500); for (int i = 0; i < POP; i++) { agent[i].setDirection(-1.6); } step(); Sleep(500); for (int i = 0; i < POP; i++) { agent[i].setDirection(-2.0); } step(); Sleep(500); for (int i = 0; i < POP; i++) { agent[i].setDirection(-2.4); } step(); Sleep(500); for (int i = 0; i < POP; i++) { agent[i].setDirection(-2.8); } step(); Sleep(500); for (int i = 0; i < POP; i++) { agent[i].setDirection(-2.4); } step(); Sleep(500); for (int i = 0; i < POP; i++) { agent[i].setDirection(-2.0); } step(); Sleep(500); for (int i = 0; i < POP; i++) { agent[i].setDirection(-1.6); } step(); Sleep(500); for (int i = 0; i < POP; i++) { agent[i].setDirection(-1.2); } step(); Sleep(500); for (int i = 0; i < POP; i++) { agent[i].setDirection(-0.8); } step(); Sleep(500); for (int i = 0; i < POP; i++) { agent[i].setDirection(-0.4); } step(); } //--------------------------------------------------------------------------- // How do we prevent this from firing more than once? void __fastcall TForm1::TrackBarSetVelocityChange(TObject *Sender) { //setVelAll = true; velX++; Form1->EditVelX->Text = velX; long double ldSetVel; for (int i = 0; i < POP; i++) { ldSetVel = TrackBarSetVelocity->Position / 1000.00; agent[i].setVelocity(ldSetVel); //agent[i].draw(); } double dSetVel = ldSetVel; Form1->EditSetVelocity->Text = String(dSetVel); } //--------------------------------------------------------------------------- // this works OK but the skewing still exists void __fastcall TForm1::EditSetVelocityClick(TObject *Sender) { for (int i = 0; i < POP; i++) { agent[i].setVelocity(.8); } step(); Sleep(500); for (int i = 0; i < POP; i++) { agent[i].setVelocity(2.0); } step(); Sleep(500); for (int i = 0; i < POP; i++) { agent[i].setVelocity(4.0); } step(); Sleep(500); for (int i = 0; i < POP; i++) { agent[i].setVelocity(8.0); } step(); Sleep(500); for (int i = 0; i < POP; i++) { agent[i].setVelocity(.8); } } //--------------------------------------------------------------------------- void __fastcall TForm1::ButtonFollowRandomAgentClick(TObject *Sender) { for (int i = 0; i < pop; i++) { agent[i].seeking = random(pop); } } //--------------------------------------------------------------------------- void __fastcall TForm1::ButtonFollowNextClick(TObject *Sender) { for (int i = 0; i < pop - 1; i++) { agent[i].seeking = i + 1; } agent[pop - 1].seeking = 0; } //--------------------------------------------------------------------------- void __fastcall TForm1::ButtonFollowThreeClick(TObject *Sender) { for (int i = 0; i < pop; i++) { agent[i].seeking = random(3); } } //--------------------------------------------------------------------------- void __fastcall TForm1::ButtonFollow139HierarchyClick(TObject *Sender) { // to initialize 1:3:9:27... hierarchical seeking for (int i = pop - 1; i >= 0; i--) { agent[i].seeking = (i - 1) / 3; } agent[0].seeking = random(pop); } //---------------------------------------------------------- That's all folks void __fastcall TForm1::RadioGroupAgent0FollowsClick(TObject *Sender) { if (RadioGroupAgent0Follows->ItemIndex == 0) { agent0FollowsCursor = true; } } //--------------------------------------------------------------------------- void __fastcall TForm1::RadioGroupShapeClick(TObject *Sender) { if (RadioGroupShape->ItemIndex == 1) { box = true; } else box = false; } //---------------------------------------------------------------------------