
// FlockBehavior.java
// Andrew Davison, April 2005, ad@fivedots.coe.psu.ac.th
// Sirinart Sakarin, March 2003, s4210315@calvin.coe.psu.ac.th


/* Třída FlockBehavior spravuje seznam typu BoidsList instancí podtříd třídy Boid a
   uzel typu BranchGroup uchovávající pro každého agenta uzly typu BranchGroup.

   BoidsList a BranchGroup inicializují podtřídy třídy FlockBehavior.

   Objekt tohoto typu se spouští každých DELAY milisekund a pro každého agenta
   ve svém seznamu typu ArrayList volá metodu animateBoid().

   Dále uchovává Reynoldovy rychlostní pravidla pro:
          - soudržnost
          - separaci
          - připojení
   Tato pravidla pak volá každý z agentů.

   Princip této třídy spočívá v tom, že objekt chování bude uchovávat
   pravidla, které vyžadují prohlídku spolu-agentů (nebo ostatních dynamicky se
   měnících prvků systému).

   Další rychlostní pravdila jsou uloženy v podtřídách třídy FlockBehavior.
*/


import java.awt.event.*;
import java.util.*;
import javax.media.j3d.*;
import javax.vecmath.*;


public class FlockBehavior extends Behavior
{
  protected final static float PROXIMITY = 2.0f;   
      // spolu-agenti jsou agenti, kteří jsou takto blízko (nebo blíž)

  // škálovací faktory pro rychlosti vypočítané pomocí pravidel
  private final static float COHESION_WEIGHT = 0.2f;
  private final static float SEPERATION_WEIGHT = 0.2f;
  private final static float ALIGNMENT_WEIGHT = 0.2f;

  private final static int DELAY = 50;

  protected BoidsList boidsList;   // uchovává instance podtříd třídy Boid
  protected BranchGroup boidsBG;   // uchovává uzly typu BGs jednotlivých agentů

  private WakeupCondition timeOut;

  // pro opakované výpočty
  private Vector3f avgPosn = new Vector3f();
  private Vector3f distFrom = new Vector3f();
  private Vector3f moveAway = new Vector3f();
  private Vector3f avgVel = new Vector3f();
  private Vector3f clearDist = new Vector3f();


  public FlockBehavior(int numBoids)
  { boidsList = new BoidsList(numBoids);
    boidsBG = new BranchGroup();
    timeOut = new WakeupOnElapsedTime(DELAY);
  } // konec metody: FlockBehavior()


  public void initialize()
  { wakeupOn(timeOut);  }


  public void processStimulus(Enumeration en)
  // pro každého viditelného agenta volá animateBoids() 
  {
    Boid b;
    int i = 0;
	while((b = boidsList.getBoid(i)) != null) {
	  b.animateBoid();
      i++;
    }
	wakeupOn(timeOut);   	// naplánuj další aktualizaci
  } // konec metody: processStimulus()


  public BoidsList getBoidsList()
  {  return boidsList;  }

  public BranchGroup getBoidsBG()
  // tuto metodu používá třída WrapFlocking3D pro přidávání agentů do scény
  {  return boidsBG;  }



 // ------------ pravidla pro soudržnost, separaci a připojení ---------


  public Vector3f cohesion(Vector3f boidmos)
  /* Agent se pokouší letět k průměrné poloze jeho spolu-agentů.

     Rychlostí je malý krůček od aktuální polohy agenta (boidPos) směrem k průměrné pozici.
   */
  { avgPosn.set(0,0,0);   // vynuluj a výchozí odpověď
    int numFlockMates = 0;
    Vector3f pos;
    Boid b;

    int i = 0;
	while((b = boidsList.getBoid(i)) != null) {
      distFrom.set(boidmos);
      pos = b.getBoidmos();
      distFrom.sub(pos);
	  if(distFrom.length() < PROXIMITY) {   // je agent spolu-agentem?
        avgPosn.add(pos);		// připočti pozici k záznamu
        numFlockMates++;
      }
      i++;
    }
	avgPosn.sub(boidmos);  // nepočítej agenta samotného
    numFlockMates--;

	if(numFlockMates > 0) {   // agent má spolu-agenty
	  avgPosn.scale(1.0f/numFlockMates);   // vypočítej průměrnou pozici
      // spočítej malý krok směrem k prům. pozici
       avgPosn.sub(boidmos);
       avgPosn.scale(COHESION_WEIGHT);
    }
    return avgPosn;
  } // konec metody: cohesion()
  


  public Vector3f separation(Vector3f boidmos)
  /* Agent se pokouší udžet malou vzdálenost od ostatních spoluagentů

     Rychlostí je průměrná vzdálenost spolu-agentů od agenta, škálovaná tak,
     aby se agent posunul jen o malý kousek, a ne o velký skok.
  */
  { moveAway.set(0,0,0);  // vynuluj
    int numFlockMates = 0;
    Vector3f pos;
    Boid b;

    int i = 0;
	while((b = boidsList.getBoid(i)) != null) {
      distFrom.set(boidmos);
      distFrom.sub( b.getBoidmos() );
	  if(distFrom.length() < PROXIMITY) {   // je agent spolu-agentem?
        moveAway.add(distFrom);	   // přidej vzdálenost k počítadlu
        numFlockMates++; 
      }	
      i++;
    }
    numFlockMates--;    // nepočítej agentovu vzdálenost od sama sebe
	if(numFlockMates > 0) {
	  moveAway.scale(1.0f/numFlockMates);   // spočítej průměrnou vzdálenost a
					        // vynásob pro snížení skoku
      moveAway.scale(SEPERATION_WEIGHT);
    } 
    return moveAway;	
  } // konec metody: separation()

  

  public Vector3f alignment(Vector3f boidmos, Vector3f boidVel)
  /* Agent se pokouší cestovat průměrnou rychlostí svých spolu-agentů.
     Rychlost je škálovaná tak, aby se měnila jen postupně, ne ve skocích.
  */
  { avgVel.set(0,0,0);  // vynuluj
    int numFlockMates = 0;
    Vector3f pos;
    Boid b;

    int i = 0;
	while((b = boidsList.getBoid(i)) != null) {
      distFrom.set(boidmos);
      pos = b.getBoidmos();
      distFrom.sub(pos);
	  if(distFrom.length() < PROXIMITY) {  // je agent spolu-agentem?
        avgVel.add(  ((Boid)boidsList.get(i)).getBoidVel() );  
							// přidej jeho rychlost do počítadla		
        numFlockMates++;
      }
      i++;
    }
	avgVel.sub(boidVel);  // nezapočítávej rychlost agenta samotného
    numFlockMates--;

	if(numFlockMates > 0) {
	  avgVel.scale(1.0f/numFlockMates);
      // vynásob pro snížení skokových změn rychlosti
      avgVel.scale(ALIGNMENT_WEIGHT); 
    } 
	return avgVel;	
  } // konec metody: alignment()


} // konec třídy: FlockBehavior
