// Class SwimmingIcon // Part of Swimming Icons 1 // David Tames, Version 1d, July 14, 2010 // Behavior code is based, in part, on Daniel Shiffman's Flocking demo at // http://processing.org/learning/topics/flocking.html, an implementation // of Craig Reynold's Boids program that simulates animals behavior. Each // SwimmingIcon steers itself based on rules of avoidance, alignment, and // coherence. // Copyright 2010 by David Tames, some rights reserved. This code is released // under the terms of a Creative Commons Share-Alike Attribution License, for // license text visit http://creativecommons.org/licenses/by-sa/2.5/ and for // attribution please link to: http://kino-eye.com/dmi/swimming-icons/ class SwimmingIcon { // Constants // float separationWeight = 1.5; float alignmentWeight = 1.0; float cohesionWeight = 1.0; float dampingFactor = 100.00; float desiredSeparation = 90.0; float neighborDistance = 50.0; // Movement related instance variables // PVector position; PVector velocity; PVector acceleration; float r; // our radius, can we get rid of this now? float maxForce; // Maximum steering force float maxSpeed; // Maximum speed // image related instance variables PImage thumbImage; PImage previewImage; // not used YET, but SOON I hope! int myHeight = 60; int myWidth = 60; boolean tagged = false; boolean hidden = false; boolean delete = false; // data from Flickr String imageIconURL; String imageName; String imagePageURL; String userName; String userPageURL; String imageTags; // SwimmingIcon constructor // SwimmingIcon(PVector l, float ms, float mf) { acceleration = new PVector(0,0); velocity = new PVector(random(-1,1),random(-1,1)); position = l.get(); r = 2.0; maxSpeed = ms; maxForce = mf; } // run the show // void run(ArrayList SwimmingIcons) { school(SwimmingIcons); update(); borders(); display(); } // school() // accumulate a new acceleration each time based on the rules // of separation, alignment, and cohesion // void school(ArrayList SwimmingIcons) { PVector separation = separate(SwimmingIcons); // Separation PVector alignment = align(SwimmingIcons); // Alignment PVector cohesion = cohere(SwimmingIcons); // Cohesion // Arbitrarily weight these forces separation.mult(separationWeight); alignment.mult(alignmentWeight); cohesion.mult(cohesionWeight); // Add the force vectors to acceleration acceleration.add(separation); acceleration.add(alignment); acceleration.add(cohesion); } // update() // // void update() { velocity.add(acceleration); // Update velocity of the icon velocity.limit(maxSpeed); // Limit speed so it does not swim out of control position.add(velocity); // Update our position acceleration.mult(0); // reset acceleration each frame } void seek(PVector target) { acceleration.add(steer(target,false)); } void arrive(PVector target) { acceleration.add(steer(target,true)); } // A method that calculates a steering vector towards a target // Takes a second argument, if true, it slows down as it approaches the target // PVector steer(PVector target, boolean slowdown) { PVector steer; // The steering vector PVector desired = target.sub(target,position); // A vector pointing from the position to the target float desiredMag = desired.mag(); // Distance from the target is the magnitude of the vector // If the distance is greater than 0, calc steering (otherwise return zero vector) if (desiredMag > 0) { // Normalize desired desired.normalize(); // Two options for desired vector magnitude // 1 -- based on distance, or // 2 -- maxSpeed if ((slowdown) && (desiredMag < dampingFactor)) { desired.mult(maxSpeed*(desiredMag/dampingFactor)); // This damping is somewhat arbitrary } else { desired.mult(maxSpeed); } // Steering = Desired minus Velocity steer = target.sub(desired,velocity); steer.limit(maxForce); // Limit to maximum steering force } else { steer = new PVector(0,0); } return steer; } void display() { // Draw a triangle rotated in the direction of velocity float theta = velocity.heading2D() + PI/2; fill(200,100); stroke(255); pushMatrix(); translate(position.x,position.y); image(thumbImage, 0, 0, myHeight, myWidth); if (tagged) { fill (100,255,100); stroke(128); strokeWeight(1); ellipse (50, 50, 9, 9); } else if (delete) { if (myHeight > 1) myHeight = myHeight - 2; if (myWidth > 1) myWidth = myWidth - 2; stroke(0); fill (0,0,0,100); rect(0, 0, myHeight, myWidth); } // rotate(theta); // beginShape(TRIANGLES); // vertex(0, -r*2); // vertex(-r, r*2); // vertex(r, r*2); // endShape(); popMatrix(); } /* // Wraparound void borders() { if (position.x < -r) position.x = width+r; if (position.y < -r) position.y = height+r; if (position.x > width+r) position.x = -r; if (position.y > height+r) position.y = -r; } */ void borders () { if (position.x > rightBorder) position.x = leftBorder; if (position.x < leftBorder) position.x = rightBorder; if (position.y > bottomBorder) position.y = topBorder; if (position.y < topBorder) position.y = bottomBorder; } PVector separate (ArrayList SwimmingIcons) { // Checks for nearby SwimmingIcons and steer away PVector steer = new PVector(0,0,0); int count = 0; // For every SwimmingIcon in the system, check if it's too close try { for (int i = 0 ; i < SwimmingIcons.size(); i++) { SwimmingIcon other = (SwimmingIcon) SwimmingIcons.get(i); float d = PVector.dist(position,other.position); // If the distance is greater than 0 and less than desired separation amount // which of course is 0 when you are yourself if ((d > 0) && (d < desiredSeparation)) { // Calculate vector pointing away from neighbor PVector diff = PVector.sub(position,other.position); diff.normalize(); diff.div(d); // Weight by distance steer.add(diff); count++; // Keep track of how many } } } catch(Exception e) { if (TRACE) println("separate: " + e.getMessage()); } // Average -- divide by how many if (count > 0) { steer.div((float)count); } // As long as the vector is greater than 0 if (steer.mag() > 0) { // Implement Reynolds: Steering = Desired - Velocity steer.normalize(); steer.mult(maxSpeed); steer.sub(velocity); steer.limit(maxForce); } return steer; } // Alignment // For every nearby SwimmingIcon in the system, calculate the average velocity PVector align (ArrayList SwimmingIcons) { PVector steer = new PVector(0,0,0); int count = 0; try { for (int i = 0 ; i < SwimmingIcons.size(); i++) { SwimmingIcon other = (SwimmingIcon) SwimmingIcons.get(i); float d = PVector.dist(position,other.position); if ((d > 0) && (d < neighborDistance)) { steer.add(other.velocity); count++; } } } catch(Exception e) { if (TRACE) println("align: " + e.getMessage()); } if (count > 0) { steer.div((float)count); } // As long as the vector is greater than 0 if (steer.mag() > 0) { // Implement Reynolds: Steering = Desired - Velocity steer.normalize(); steer.mult(maxSpeed); steer.sub(velocity); steer.limit(maxForce); } return steer; } // Cohesion // For the average position (i.e. center) of all nearby SwimmingIcons, calculate // steering vector towards that position PVector cohere (ArrayList SwimmingIcons) { PVector sum = new PVector(0,0); // Start with empty vector to accumulate all positions int count = 0; try { for (int i = 0 ; i < SwimmingIcons.size(); i++) { SwimmingIcon other = (SwimmingIcon) SwimmingIcons.get(i); float d = position.dist(other.position); if ((d > 0) && (d < neighborDistance)) { sum.add(other.position); // Add position count++; } } } catch(Exception e) { if (TRACE) println("cohere: " + e.getMessage()); } if (count > 0) { sum.div((float)count); return steer(sum,false); // Steer towards the position } return sum; } // -------------------------------------------------------- // all sorts of get and set methods boolean setImageIconURL(String u) { imageIconURL = u; thumbImage = loadImage(imageIconURL); if (thumbImage == null) { if (DEBUG) println ("error: could not open " + imageIconURL); return false; } else { return true; } } // ---------------------------- // over // is mouse clicking on me? boolean over (int mx, int my) { if (mx >= position.x && mx <= position.x+myWidth && my >= position.y && my <= position.y+myHeight) { return true; } else { return false; } } PVector getPosition() { return position.get(); } void tag() { tagged = true; } void untag() { tagged = false; } void hide() { hidden = true; } void unhide() { hidden = false; } boolean hidden() { return hidden; } void delete() { delete = true; } void undelete() { delete = false; } boolean deleted() { return delete; } boolean tagged() { return tagged; } String getImageIconURL() { return imageIconURL; } void setImagePageURL(String n) { imagePageURL = n; } String getImagePageURL() { return imagePageURL; } void setUserName(String n) { userName = n; } String getUserName() { return userName; } void setUserPageURL(String u) { userPageURL = u; } String getUserPageURL() { return userPageURL; } String getImageTags() { return imageTags; } void setImageTags(String t) { imageTags = t; } String getImageName() { return imageName; } void setImageName(String t) { imageName = t; } }