diff --git a/FlockingArrows/Arrow.pde b/FlockingArrows/Arrow.pde new file mode 100644 index 0000000..3850566 --- /dev/null +++ b/FlockingArrows/Arrow.pde @@ -0,0 +1,186 @@ +// Arrow class implementing flocking behavior (Boids algorithm) +class Arrow { + PVector position; + PVector velocity; + PVector acceleration; + float maxForce; // Maximum steering force + float maxSpeed; // Maximum speed + float arrowSize; + color arrowColor; + + Arrow(float x, float y) { + position = new PVector(x, y); + velocity = PVector.random2D(); + velocity.setMag(random(2, 4)); + acceleration = new PVector(0, 0); + maxForce = 0.2; + maxSpeed = 4; + arrowSize = 12; + arrowColor = color(random(200, 255), random(100, 200), random(150, 255), 200); + } + + // Apply force to acceleration + void applyForce(PVector force) { + acceleration.add(force); + } + + // Main flocking behavior + void flock(ArrayList arrows) { + PVector separation = separate(arrows); + PVector alignment = align(arrows); + PVector cohesion = cohere(arrows); + + // Weight these forces + separation.mult(1.5); + alignment.mult(1.0); + cohesion.mult(1.0); + + // Apply all forces + applyForce(separation); + applyForce(alignment); + applyForce(cohesion); + } + + // Separation: steer to avoid crowding + PVector separate(ArrayList arrows) { + float desiredSeparation = 25.0; + PVector steer = new PVector(0, 0); + int count = 0; + + for (Arrow other : arrows) { + float d = PVector.dist(position, other.position); + if ((d > 0) && (d < desiredSeparation)) { + PVector diff = PVector.sub(position, other.position); + diff.normalize(); + diff.div(d); // Weight by distance + steer.add(diff); + count++; + } + } + + if (count > 0) { + steer.div(count); + } + + if (steer.mag() > 0) { + steer.setMag(maxSpeed); + steer.sub(velocity); + steer.limit(maxForce); + } + return steer; + } + + // Alignment: steer towards average heading + PVector align(ArrayList arrows) { + float neighborDist = 50; + PVector sum = new PVector(0, 0); + int count = 0; + + for (Arrow other : arrows) { + float d = PVector.dist(position, other.position); + if ((d > 0) && (d < neighborDist)) { + sum.add(other.velocity); + count++; + } + } + + if (count > 0) { + sum.div(count); + sum.setMag(maxSpeed); + PVector steer = PVector.sub(sum, velocity); + steer.limit(maxForce); + return steer; + } + return new PVector(0, 0); + } + + // Cohesion: steer towards average position + PVector cohere(ArrayList arrows) { + float neighborDist = 50; + PVector sum = new PVector(0, 0); + int count = 0; + + for (Arrow other : arrows) { + float d = PVector.dist(position, other.position); + if ((d > 0) && (d < neighborDist)) { + sum.add(other.position); + count++; + } + } + + if (count > 0) { + sum.div(count); + return seek(sum); + } + return new PVector(0, 0); + } + + // Seek a target position + PVector seek(PVector target) { + PVector desired = PVector.sub(target, position); + desired.setMag(maxSpeed); + PVector steer = PVector.sub(desired, velocity); + steer.limit(maxForce); + return steer; + } + + // Flee from a position (for mouse avoidance) + PVector flee(PVector target) { + PVector desired = PVector.sub(position, target); + float d = desired.mag(); + + if (d < 100) { // Only flee if mouse is close + desired.setMag(maxSpeed); + PVector steer = PVector.sub(desired, velocity); + steer.limit(maxForce); + return steer; + } + return new PVector(0, 0); + } + + // Update position + void update() { + velocity.add(acceleration); + velocity.limit(maxSpeed); + position.add(velocity); + acceleration.mult(0); // Reset acceleration + } + + // Wrap around screen edges + void borders() { + if (position.x < -arrowSize) position.x = width + arrowSize; + if (position.y < -arrowSize) position.y = height + arrowSize; + if (position.x > width + arrowSize) position.x = -arrowSize; + if (position.y > height + arrowSize) position.y = -arrowSize; + } + + // Display the arrow + void display() { + float theta = velocity.heading() + PI/2; + + pushMatrix(); + translate(position.x, position.y); + rotate(theta); + + fill(arrowColor); + noStroke(); + + // Draw arrow shape + beginShape(); + vertex(0, -arrowSize); + vertex(-arrowSize/3, arrowSize/2); + vertex(0, arrowSize/4); + vertex(arrowSize/3, arrowSize/2); + endShape(CLOSE); + + popMatrix(); + } + + // Run the full arrow behavior + void run(ArrayList arrows) { + flock(arrows); + update(); + borders(); + display(); + } +} diff --git a/FlockingArrows/FlockingArrows.pde b/FlockingArrows/FlockingArrows.pde new file mode 100644 index 0000000..40f4394 --- /dev/null +++ b/FlockingArrows/FlockingArrows.pde @@ -0,0 +1,103 @@ +/** + * Flocking Arrows + * + * A generative art application featuring flocking arrows that follow + * the Boids algorithm (separation, alignment, cohesion). + * + * CONTROLS: + * - Move mouse to interact with arrows (they will avoid the cursor) + * - Click mouse to attract arrows + * - Press SPACE to add more arrows + * - Press 'r' to reset + * - Press 's' to save screenshot + */ + +ArrayList flock; +boolean mouseAttract = false; + +void setup() { + size(1200, 800); + smooth(); + + // Initialize flock + flock = new ArrayList(); + for (int i = 0; i < 100; i++) { + flock.add(new Arrow(random(width), random(height))); + } +} + +void draw() { + // Create trailing effect + fill(10, 10, 15, 30); + noStroke(); + rect(0, 0, width, height); + + // Mouse interaction + PVector mousePos = new PVector(mouseX, mouseY); + + // Update and display all arrows + for (Arrow arrow : flock) { + arrow.run(flock); + + // Mouse interaction + if (mousePressed) { + // Left click: attract + if (mouseButton == LEFT) { + PVector attraction = arrow.seek(mousePos); + attraction.mult(0.5); + arrow.applyForce(attraction); + } + // Right click: repel + else if (mouseButton == RIGHT) { + PVector flee = arrow.flee(mousePos); + flee.mult(2.0); + arrow.applyForce(flee); + } + } else { + // Default: arrows avoid cursor when not clicking + PVector flee = arrow.flee(mousePos); + flee.mult(1.0); + arrow.applyForce(flee); + } + } + + // Display info + displayInfo(); +} + +void displayInfo() { + fill(255, 200); + textAlign(LEFT); + textSize(12); + text("Arrows: " + flock.size(), 10, 20); + text("FPS: " + int(frameRate), 10, 35); + text("Mouse: Avoid | Left Click: Attract | Right Click: Repel", 10, height - 10); + text("SPACE: Add arrows | R: Reset | S: Save", 10, height - 25); +} + +void keyPressed() { + // Add more arrows + if (key == ' ') { + for (int i = 0; i < 10; i++) { + flock.add(new Arrow(random(width), random(height))); + } + } + + // Reset + if (key == 'r' || key == 'R') { + flock.clear(); + for (int i = 0; i < 100; i++) { + flock.add(new Arrow(random(width), random(height))); + } + } + + // Save screenshot + if (key == 's' || key == 'S') { + saveFrame("flocking-arrows-####.png"); + println("Screenshot saved!"); + } +} + +void mousePressed() { + // Could add more interaction here +} diff --git a/FlockingArrows/README.md b/FlockingArrows/README.md new file mode 100644 index 0000000..d0d7709 --- /dev/null +++ b/FlockingArrows/README.md @@ -0,0 +1,69 @@ +# Flocking Arrows + +A generative art application featuring flocking arrows that simulate natural flocking behavior using the Boids algorithm. + +## Description + +This Processing sketch creates a mesmerizing display of arrow-shaped objects that move together in a flock, similar to birds or fish in nature. The arrows follow three simple rules: + +1. **Separation** - Avoid crowding nearby arrows +2. **Alignment** - Steer towards the average heading of nearby arrows +3. **Cohesion** - Move towards the average position of nearby arrows + +## Features + +- 🎨 Colorful arrows with trailing effects +- 🖱️ Interactive mouse controls +- ✨ Beautiful emergent flocking behavior +- 🎯 Dynamic adding/removal of arrows +- 📸 Screenshot capability + +## Controls + +| Input | Action | +|-------|--------| +| **Move Mouse** | Arrows avoid the cursor | +| **Left Click** | Attract arrows to cursor | +| **Right Click** | Strongly repel arrows from cursor | +| **SPACE** | Add 10 more arrows to the flock | +| **R** | Reset to default flock size (100 arrows) | +| **S** | Save screenshot | + +## How to Run + +1. Open `FlockingArrows.pde` in Processing +2. Click the Run button +3. Move your mouse to interact with the flock! + +## Technical Details + +- **Algorithm**: Boids (Craig Reynolds, 1986) +- **Default flock size**: 100 arrows +- **Canvas size**: 1200x800 pixels +- **Language**: Processing (Java mode) + +## Customization + +You can adjust these parameters in the code: + +- **Arrow count**: Change the loop in `setup()` (line 21) +- **Canvas size**: Modify `size()` in `setup()` (line 18) +- **Flocking weights**: Adjust multipliers in `flock()` method (Arrow.pde lines 31-33) +- **Arrow appearance**: Modify `arrowSize` and `arrowColor` in Arrow constructor + +## Algorithm Weights + +Current flocking behavior weights: +- Separation: 1.5x +- Alignment: 1.0x +- Cohesion: 1.0x +- Mouse avoidance: 1.0x +- Mouse attraction: 0.5x (when clicking) +- Mouse repulsion: 2.0x (when right-clicking) + +## Credits + +Based on Craig Reynolds' Boids algorithm (1986) +Implementation inspired by Daniel Shiffman's Nature of Code + +Enjoy the flocking arrows! 🎯✨