topical media & game development

talk show tell print

student-twitter-swarm-Boid.ax

student-twitter-swarm-Boid.ax (swf ) [ flash ] flex


  package {
          import flash.events.Event;
          import flash.utils.setTimeout;
          
          import mx.containers.Panel;
          import mx.controls.Image;
          import mx.controls.Label;
  
          public class @ax-student-twitter-swarm-Boid {
                  private var x:int, y:int;
                  private var maxWidth:int = 900, maxHeight:int = 600, immunityTimer:int = 0;
                  private var xVelocity:Number, yVelocity:Number, maxVelocity:Number = 5.0, 
                                          nestingThreshold:Number, nestingFraction:Number, maxNestingThreshold:Number = 0.2,
                                          minNestingFraction:Number = 0.90, nestingDegradationFactor:Number = 0.9999,
                                          flockingProb:Number = 1.0;
                  private var image:student_twitter_swarm_AnimatedImage, panel:Panel, tweet:student_twitter_swarm_Tweet;
                  private var selected:Boolean = false;
                  private var similarities:Array;
                  
                  public function @ax-student-twitter-swarm-Boid(image:Image, panel:Panel, tweet:student_twitter_swarm_Tweet, anim:Array):void {
                      addRandomImpulse(2.0);
                          this.x = random(this.maxWidth);
                      this.y = random(this.maxHeight);
                          this.nestingThreshold = this.maxNestingThreshold;
                          this.nestingFraction  = this.minNestingFraction;
                      
                      this.image = new student_twitter_swarm_AnimatedImage(image, anim);
                      this.tweet = tweet;
                      this.image.setColor(this.tweet.getColor());
                      this.panel = panel;
                  }
                  
                  public function addRandomImpulse (max:Number):void {
                          // Sets the boid flying of in a random direction at a random speed.
  
                          this.xVelocity = random(max * 2) - max;
                          this.yVelocity = random(max * 2) - max;
                          this.nestingFraction = 1;
                  }
                  
                  private        function random (maxNum:int):int {
                          return Math.ceil(Math.random() * maxNum);
                  }
                  
                  public function calculateSimilarities (boids:Array):void {
                          // Asks the Tweet to calculate the similarity with all the boids.
                          
                          this.similarities = new Array(boids.length);
                          
                          for (var i:int = 0; i < boids.length; i++) {
                                  this.similarities[i] = this.tweet.calculateSimilarity(boids[i].tweet);
                          }
                  }
                  
          // Interaction
                  public function hideTweet ():void {
                          this.image.deselect();
                  }
                  
                  public function showTweet (event:Event):void {
                          setTimeout(showLabel, 50);
                  }
                  
                  private function showLabel ():void {
                          this.panel.title = this.tweet.getScreenName();
                          var pic:Image = this.panel.getChildByName("profilePic") as Image;
                          pic.source = this.tweet.getPic();
                          var label:Label = this.panel.getChildByName("tweetLabel") as Label;
                          label.text = this.tweet.getText();
                          var tagLabel:Label = this.panel.getChildByName("moodLabel") as Label;
                          tagLabel.text = "Moods: " + this.tweet.getMoods();
                          this.panel.visible = true;
                          this.image.select();
                  }
                  
          // Render
          
                  public function moveImage (screenWidth:int, screenHeight:int):void {
                          this.image.move(this.x, this.y);
                          this.image.rotate(direction());
                  }
                  
                  public function direction ():Number {
                          // Determines the angle (in degrees) that the image should be rotated 
                          // to, given the current vector.
  
                          var result:Number, distance:Number;
                          
                          distance = Math.sqrt(this.xVelocity * this.xVelocity + this.yVelocity * this.yVelocity);
                          result = Math.acos(this.xVelocity/distance);
                          
                          if (this.yVelocity < 0) {
                                  result *= -1;
                          }
                          
                          result *= 180 / Math.PI;
                          return result;
                  }
                  
                  public function getX ():int {
                          return this.x;
                  }
                  
                  public function getY ():int {
                          return this.y;
                  }
                  
                  public function getImage ():Image {
                          return this.image.getImage();
                  }
                  
          // Boid behaviour
                  public function move ():void {
                          // Calculates the new position using the current vector and forces the 
                          // boids to turn around if they get to the edge of the screen.
  
                          this.x += this.xVelocity;
                          this.y += this.yVelocity;
                          
                          var border:int = 20, width:int = this.maxWidth, height:int = this.maxHeight;
                          var maxX:int = width;
                          var minX:int = 0;
                          var maxY:int = height;
                          var minY:int = 0;
                          
                          if (this.x <= minX + border) {
                                  this.xVelocity += 1.0;
                                  this.yVelocity *= (this.yVelocity < 5) ? 1.1 : 1.0;
                          } else if (this.x >= maxX - border) {
                                  this.xVelocity -= 1.0;
                                  this.yVelocity *= (this.yVelocity < 5) ? 1.1 : 1.0;
                          }
                          if (this.y <= minY + border) {
                                  this.yVelocity += 1.0;
                                  this.xVelocity *= (this.xVelocity < 5) ? 1.1 : 1.0;
                          } else if (this.y >= maxY - border) {
                                  this.yVelocity -= 1.0;
                                  this.xVelocity *= (this.xVelocity < 5) ? 1.1 : 1.0;
                          }
                          
                          if (this.x < minX - 2 * border || this.x > (maxX + 2 * border) || 
                                  this.y < minY - 2 * border || this.y > (maxY + 2 * border)) {
                                  this.x = 100;
                                  this.y = 100;
                                  addRandomImpulse(2.0);                                        
                          }
                  }
              
                  public function distance (boid:student_twitter_swarm_Boid):int {
                          var distX:int = this.x - boid.x,
                                  distY:int = this.y - boid.y;
                          return Math.sqrt(distX * distX + distY * distY);
                  }
                  
                  public function separation (boids:Array, minDistance:int):void {
                          // This algorithm pushes the boids away from each other.
                          
                          var distanceX:int = 0,
                                  distanceY:int = 0,
                                  numClose:int = 0;
                          
                          for(var i:int = 0; i < boids.length; i++) {
                                  var boid:student_twitter_swarm_Boid = boids[i];
                                  var similarityFactor:Number = (1 - this.flockingProb) + this.flockingProb * this.similarities[i];
                                  
                                  if(boid.x == this.x && boid.y == this.y) continue;
          
                                  var distance:int = this.distance(boid)
                                  if(distance < minDistance) {
                                          numClose++;
                                          var xdiff:int = (this.x - boid.x),
                                                  ydiff:int = (this.y - boid.y);
                          
                                          if (xdiff >= 0) xdiff = Math.sqrt(minDistance) - xdiff;
                                          else if (xdiff < 0) xdiff = -Math.sqrt(minDistance) - xdiff;
          
                                          if (ydiff >= 0) ydiff = Math.sqrt(minDistance) - ydiff;
                                          else if (ydiff < 0) ydiff = -Math.sqrt(minDistance) - ydiff;
                                          
                                          distanceX += xdiff * similarityFactor;
                                          distanceY += ydiff * similarityFactor;
                                          boid = null; 
                                  }
                          }
          
                          if(numClose == 0) return;
          
                          this.xVelocity -= distanceX / 5;
                          this.yVelocity -= distanceY / 5;
                  }
          
                  public function cohesion (boids:Array, distance:int):void {
                          // This algorithm brings the boids towards each other.
                          
                          if(boids.length < 1) return;                                
                          
                          // Calculate the average velocity of the other boids
                          var avgX:int = 0, avgY:int = 0, avgZ:int = 0;
                          for(var i:int = 0; i < boids.length; i++) {
                                  var boid:student_twitter_swarm_Boid = boids[i];
                                  if(boid.x == this.x && boid.y == this.y) continue;
                                  if(this.distance(boid) > distance) continue;
                                  
                                  var similarityFactor:Number = (1 - this.flockingProb) + this.flockingProb * this.similarities[i];
                                  
                                  avgX += (this.x - boid.x) * similarityFactor;
                                  avgY += (this.y - boid.y) * similarityFactor;
                                  boid = null;
                          }
  
                          avgX /= boids.length;
                          avgY /= boids.length;
                          avgZ /= boids.length;
          
                          distance = Math.sqrt((avgX * avgX) + (avgY * avgY) + (avgZ * avgZ)) * -1.0;
                          if(distance == 0) return;
          
                          this.xVelocity = Math.min(this.xVelocity + (avgX / distance) * 0.15, this.maxVelocity);
                          this.yVelocity = Math.min(this.yVelocity + (avgY / distance) * 0.15, this.maxVelocity);
                  }
                  
                  public function alignment (boids:Array, distance:int):void {
                          // This algorithm makes the boid turn toward the same 
                          // direction as the average of the boids around it.
                          
                          if(boids.length < 1) return;
          
                          // Calculate the average velocity of the other boids
                          var avgX:int = 0, avgY:int = 0, avgZ:int = 0;
                          for(var i:int = 0; i < boids.length; i++) {
                                  var boid:student_twitter_swarm_Boid = boids[i];
                                  if(boid.x == this.x && boid.y == this.y) continue;
                                  if(this.distance(boid) > distance) continue;
                                  
                                  var similarityFactor:Number = (1 - this.flockingProb) + this.flockingProb * this.similarities[i];
          
                                  avgX += boid.xVelocity * similarityFactor;
                                  avgY += boid.yVelocity * similarityFactor;
                                  boid = null;
                          }
                          avgX /= boids.length;
                          avgY /= boids.length;
                          avgZ /= boids.length;
          
                          distance = Math.sqrt((avgX * avgX) + (avgY * avgY) + (avgZ * avgZ)) * 1.0;
                          if(distance == 0) return;
          
                          this.xVelocity = Math.min(this.xVelocity + (avgX / distance) * 0.05, this.maxVelocity);
                          this.yVelocity = Math.min(this.yVelocity + (avgY / distance) * 0.05, this.maxVelocity);
                  }
                  
                  public function treeSeeking (trees:Array, distance:int):void {
                          // This algorithm makes the boid turn toward the same 
                          // direction as the average of the trees around it.
                          
                          if(trees.length < 1) return;
          
                          // Calculate the average direction of the trees that are in range
                          var diffX:int = 0, diffY:int = 0;
                          for(var i:int = 0; i < trees.length; i++) {
                                  var tree:student_twitter_swarm_Tree = trees[i];
                                  if ( this.tweet.containsMood(tree.getMood()) ) { 
                                          if(tree.distanceToBoid(this) > distance) continue;
                                          
                                          diffX += (tree.getX() - this.x);
                                          diffY += (tree.getY() - this.y);
                                  }
                                  tree = null;
                          }
          
                          var dist:Number = Math.sqrt((diffX * diffX) * 1.0 + (diffY * diffY) * 1.0);
                          if (dist == 0) return;
  
                          this.xVelocity = Math.min(this.xVelocity + (diffX / dist) * 0.05, this.maxVelocity);
                          this.yVelocity = Math.min(this.yVelocity + (diffY / dist) * 0.05, this.maxVelocity);
                  }
                  
                  public function nesting (trees:Array):void {
                          // This algorithm slowes down the boid when it is over a tree.
                          // If it sits on the tree for a while the bird will become immune
                          // to the effects of this algorithm.  
                          
                          var onTree:Boolean = false;
                          if(trees.length < 1) return;
                          
                          for(var i:int = 0; i < trees.length; i++) {
                                  var tree:student_twitter_swarm_Tree = trees[i];
                                  if ( this.tweet.containsMood(tree.getMood()) ) {
                                          if(tree.distanceToBoid(this) < tree.getRadius() - 10) {
                                                  onTree = true;
                                                  if (immunityTimer == 0) {
                                                          if (this.xVelocity * this.xVelocity > this.nestingThreshold) {
                                                                  this.xVelocity *= this.nestingFraction;
                                                          } else {
                                                                  this.xVelocity = 0;
                                                          }
                                                          if (this.yVelocity * this.yVelocity > this.nestingThreshold) {
                                                                  this.yVelocity *= this.nestingFraction;
                                                          } else {
                                                                  this.yVelocity = 0;
                                                          }
                                                          if (this.xVelocity == 0 && this.yVelocity == 0) {
                                                                  this.immunityTimer = 200;
                                                          }
                                                          
                                                          if (this.nestingThreshold > 0.05) {
                                                                  this.nestingThreshold *= this.nestingDegradationFactor;
                                                          } else {
                                                                  this.nestingThreshold = 0;
                                                          }
                                                          if (this.nestingFraction < 0.995) {
                                                                  this.nestingFraction  /= this.nestingDegradationFactor;                                                
                                                          } else {
                                                                  this.nestingFraction = 1;
                                                          }
                                                  }
                                          } else {
                                                  this.immunityTimer = (this.immunityTimer > 0) ? this.immunityTimer - 1 : 0;
                                                  if (this.nestingThreshold < this.maxNestingThreshold) {
                                                          this.nestingThreshold /= this.nestingDegradationFactor;
                                                  } else {
                                                          this.nestingThreshold = this.maxNestingThreshold;
                                                  }
                                                  if (this.nestingFraction < this.minNestingFraction) {
                                                          this.nestingFraction  *= this.nestingDegradationFactor;                                                
                                                  } else {
                                                          this.nestingFraction = this.minNestingFraction;
                                                  }
                                          }
                                  }
                          }
                  }
          }
  }
  


(C) Æliens 20/2/2008

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.