Commit 45b2834c authored by gsavin's avatar gsavin

Isolate forces computation to provide several way for this computation : ntree, greedy ...

parent 85d7dced
......@@ -33,9 +33,7 @@ import java.util.LinkedList;
import org.graphstream.graph.implementations.AbstractGraph;
import org.graphstream.graph.implementations.AdjacencyListNode;
import org.miv.pherd.Particle;
import org.miv.pherd.geom.Point3;
import org.miv.pherd.geom.Vector3;
/**
* Represents a single bird-oid object.
......@@ -62,7 +60,6 @@ import org.miv.pherd.geom.Vector3;
public class Boid extends AdjacencyListNode {
protected final BoidSpecies species;
protected BoidParticle particle;
/** Parameters of this group of boids. */
......@@ -80,23 +77,22 @@ public class Boid extends AdjacencyListNode {
public Boid(AbstractGraph graph, BoidSpecies species, String id) {
super(graph, id);
this.particle = new BoidParticle((BoidGraph) graph);
this.species = species;
this.forces = getDefaultForces();
this.forces = null;
}
/**
* Force the position of the boid in space.
*/
public void setPosition(double x, double y, double z) {
particle.setPosition(x, y, z);
forces.setPosition(x, y, z);
}
/**
* Actual position of the boid in space.
*/
public Point3 getPosition() {
return particle.getPosition();
return forces.getPosition();
}
/**
......@@ -106,23 +102,16 @@ public class Boid extends AdjacencyListNode {
return species;
}
/**
* The underlying particle of the force system this boids is linked to.
*/
public BoidParticle getParticle() {
return particle;
public void setForces(BoidForces forces) {
this.forces = forces;
}
/**
* The forces acting on the boids, this is a set of vectors and parameters
* computed at each time step.
*/
public BoidForces getDefaultForces() {
return new BoidForces.BasicForces();
public BoidForces getForces() {
return forces;
}
protected void checkNeighborhood(BoidParticle... particles) {
if (particles != null) {
public void checkNeighborhood(Boid... boids) {
if (boids != null) {
Iterator<Boid> it = getNeighborNodeIterator();
LinkedList<Boid> toRemove = null;
......@@ -130,14 +119,14 @@ public class Boid extends AdjacencyListNode {
boolean found = false;
Boid b = it.next();
for (BoidParticle p : particles) {
if (p.getId().equals(b.getParticle().getId())) {
for (Boid b2 : boids) {
if (b == b2) {
found = true;
break;
}
}
if (!found && !forces.isVisible(b.particle, this.getPosition())) {
if (!found && !forces.isVisible(b, this.getPosition())) {
if (toRemove == null)
toRemove = new LinkedList<Boid>();
......@@ -152,11 +141,10 @@ public class Boid extends AdjacencyListNode {
toRemove.clear();
toRemove = null;
}
for (BoidParticle p : particles) {
if (getEdgeBetween(p.getBoid().getId()) == null) {
getGraph().addEdge(getEdgeId(this, p.getBoid()), getId(),
p.getBoid().getId());
}
for (Boid b2 : boids) {
if (getEdgeBetween(b2) == null)
getGraph().addEdge(getEdgeId(this, b2), this, b2);
}
}
}
......@@ -176,140 +164,4 @@ public class Boid extends AdjacencyListNode {
return String.format("%s--%s", b1.getId(), b2.getId());
}
/**
* Internal representation of the boid position, and direction in the forces
* system.
*
* @author Guilhelm Savin
* @author Antoine Dutot
*/
class BoidParticle extends Particle {
/**
* Direction of the boid.
*/
protected Vector3 dir;
/**
* Set of global parameters.
*/
protected BoidGraph ctx;
/**
* Number of boids in view at each step.
*/
protected int contacts = 0;
/**
* Number of boids of my group in view at each step.
*/
protected int mySpeciesContacts = 0;
/**
* New particle.
*
* @param ctx
* The set of global parameters.
*/
public BoidParticle(BoidGraph ctx) {
super(Boid.this.getId(), ctx.random.nextDouble() * (ctx.area * 2)
- ctx.area, ctx.random.nextDouble() * (ctx.area * 2)
- ctx.area, 0);
this.dir = new Vector3(ctx.random.nextDouble(), ctx.random
.nextDouble(), 0);
this.ctx = ctx;
}
@Override
public void move(int time) {
contacts = 0;
mySpeciesContacts = 0;
forces.compute(Boid.this, cell.getTree().getRootCell());
forces.direction.scalarMult(species.directionFactor);
forces.attraction.scalarMult(species.attractionFactor);
forces.repulsion.scalarMult(species.repulsionFactor);
dir.scalarMult(species.inertia);
dir.add(forces.direction);
dir.add(forces.attraction);
dir.add(forces.repulsion);
if (ctx.normalizeMode) {
double len = dir.normalize();
if (len <= species.minSpeed)
len = species.minSpeed;
else if (len >= species.maxSpeed)
len = species.maxSpeed;
dir.scalarMult(species.speedFactor * len);
} else {
dir.scalarMult(species.speedFactor);
}
if (ctx.storeForcesAttributes)
forces.store(this);
checkWalls();
nextPos.move(dir);
Boid.this.setAttribute("xyz", pos.x, pos.y, pos.z);
moved = true;
}
@Override
public void inserted() {
}
@Override
public void removed() {
}
public Boid getBoid() {
return Boid.this;
}
public void setPosition(double x, double y, double z) {
initPos(x, y, z);
}
/**
* Check the boid does not go out of the space walls.
*/
protected void checkWalls() {
// /float area = ctx.area;
float aarea = 0.000001f;
if (nextPos.x + dir.data[0] <= ctx.getSpace().getLoAnchor().x
+ aarea) {
nextPos.x = ctx.getSpace().getLoAnchor().x + aarea;
dir.data[0] = -dir.data[0];
} else if (nextPos.x + dir.data[0] >= ctx.getSpace().getHiAnchor().x
- aarea) {
nextPos.x = ctx.getSpace().getHiAnchor().x - aarea;
dir.data[0] = -dir.data[0];
}
if (nextPos.y + dir.data[1] <= ctx.getSpace().getLoAnchor().y
+ aarea) {
nextPos.y = ctx.getSpace().getLoAnchor().y + aarea;
dir.data[1] = -dir.data[1];
} else if (nextPos.y + dir.data[1] >= ctx.getSpace().getHiAnchor().y
- aarea) {
nextPos.y = ctx.getSpace().getHiAnchor().y - aarea;
dir.data[1] = -dir.data[1];
}
if (nextPos.z + dir.data[2] <= ctx.getSpace().getLoAnchor().z
+ aarea) {
nextPos.z = ctx.getSpace().getLoAnchor().z + aarea;
dir.data[2] = -dir.data[2];
} else if (nextPos.z + dir.data[2] >= ctx.getSpace().getHiAnchor().z
- aarea) {
nextPos.z = ctx.getSpace().getHiAnchor().z - aarea;
dir.data[2] = -dir.data[2];
}
}
}
}
\ No newline at end of file
......@@ -28,15 +28,8 @@
*/
package org.graphstream.boids;
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
import org.graphstream.boids.Boid.BoidParticle;
import org.miv.pherd.Particle;
import org.miv.pherd.geom.Point3;
import org.miv.pherd.geom.Vector3;
import org.miv.pherd.ntree.Cell;
/**
* Models the forces applied to a boid at each step.
......@@ -106,23 +99,7 @@ public abstract class BoidForces {
* @param startCell
* The start cell (usually the root cell of the n-tree).
*/
public abstract void compute(Boid source, Cell startCell);
/**
* Store each force vector as an attribute in the boid. This allows all
* listeners to retrieve the force vectors. This is particularly useful for
* the viewer for example. Each force is stored with the attribute name
* "force&lt;number&gt;" where &lt;number&gt; is a number that starts at
* zero.
*
* @param boid
* The boid to modify.
*/
protected void store(BoidParticle boid) {
boid.setAttribute("force0", direction);
boid.setAttribute("force1", attraction);
boid.setAttribute("force2", repulsion);
}
public abstract void compute();
/**
* Integrate a repulsion vector.
......@@ -158,262 +135,9 @@ public abstract class BoidForces {
/**
* Is the another point is space in the field of view?
*/
public abstract boolean isVisible(BoidParticle boid, Point3 other);
/**
* A basic definition of forces for a boid.
*
* <p>
* The kind of forces exercising on a boid can be changed to either use a
* n-tree or not, or to account for other kind of forces or another force
* model. This is the default force system that matches the basic boid
* definition as defined by Craig Reynolds.
* </p>
*
* @author Guilhelm Savin
* @author Antoine Dutot
*/
public static class BasicForces extends BoidForces {
@Override
public void compute(Boid source, Cell startCell) {
barycenter.set(0, 0, 0);
direction.fill(0);
attraction.fill(0);
repulsion.fill(0);
countAtt = 0;
countRep = 0;
Set<BoidParticle> contacts = new HashSet<BoidParticle>();
exploreTree(source, startCell, contacts);
source.checkNeighborhood(contacts.toArray(new BoidParticle[contacts
.size()]));
if (countAtt > 0) {
barycenter.scale(1f / countAtt, 1f / countAtt, 1f / countAtt);
direction.scalarDiv(countAtt);
attraction.set(barycenter.x - source.getPosition().x,
barycenter.y - source.getPosition().y, barycenter.z
- source.getPosition().z);
}
if (countRep > 0) {
repulsion.scalarDiv(countRep);
}
}
/**
* Recursively explore the n-tree to search for intersection cells, and
* the visible boids.
*
* @param source
* The boid the forces are computed on.
* @param cell
* The cell to explore recursively.
*/
protected void exploreTree(Boid source, Cell cell,
Set<BoidParticle> contacts) {
if (intersection(source, cell)) {
if (cell.isLeaf()) {
forcesFromCell(source.getParticle(), cell, contacts);
} else {
int n = cell.getSpace().getDivisions();
for (int i = 0; i < n; ++i)
exploreTree(source, cell.getSub(i), contacts);
}
}
}
/**
* A leaf cell has been found that is in intersection with the boid
* area, computes the forces from this cell.
*
* @param source
* The boid the forces are computed on.
* @param cell
* The cell.
*/
protected void forcesFromCell(BoidParticle source, Cell cell,
Set<BoidParticle> contacts) {
// BoidCellData data = (BoidCellData) cell.getData();
Iterator<? extends Particle> particles = cell.getParticles();
Vector3 rep = new Vector3();
// LinkedList<BoidParticle> contacts = null;
while (particles.hasNext()) {
Particle particle = particles.next();
if (particle instanceof BoidParticle) {
// if (contacts == null)
// contacts = new LinkedList<BoidParticle>();
if (source != particle
&& isVisible(source, particle.getPosition())) {
contacts.add((BoidParticle) particle);
actionWithNeighboor(source, (BoidParticle) particle,
rep);
}
}
}
public abstract boolean isVisible(Boid boid, Point3 other);
// barycenter.move( data.getCenter() );
// direction.add( data.getDirection() );
}
public abstract void setPosition(double x, double y, double z);
/**
* A rectangular intersection function, is the boid view area
* intersecting the given cell?. This provides a quick lookup function
* to test if a cell must be explored or not. Later, a better test
* according to a spherical view zone will be done.
*
* @param cell
* The cell to test for intersection with the boid
* rectangular view area.
* @return True if there is an intersection.
*/
protected boolean intersection(Boid source, Cell cell) {
double x1 = cell.getSpace().getLoAnchor().x;
double y1 = cell.getSpace().getLoAnchor().y;
double z1 = cell.getSpace().getLoAnchor().z;
double x2 = cell.getSpace().getHiAnchor().x;
double y2 = cell.getSpace().getHiAnchor().y;
double z2 = cell.getSpace().getHiAnchor().z;
double X1 = source.getPosition().x - source.getSpecies().viewZone;
double Y1 = source.getPosition().y - source.getSpecies().viewZone;
double Z1 = source.getPosition().z - source.getSpecies().viewZone;
double X2 = source.getPosition().x + source.getSpecies().viewZone;
double Y2 = source.getPosition().y + source.getSpecies().viewZone;
double Z2 = source.getPosition().z + source.getSpecies().viewZone;
// Only when the area is before or after the cell there cannot
// exist an intersection (case a and b). Else there must be an
// intersection (cases c, d, e and f).
//
// |-a-| +---------+ |-b-|
// | |
// |-c-| |-d-|
// | |
// | |-e-| |
// | |
// |-+----f----+-|
// | |
// +---------+
if (X2 < x1 || X1 > x2)
return false;
if (Y2 < y1 || Y1 > y2)
return false;
if (Z2 < z1 || Z1 > z2)
return false;
return true;
}
/**
* True if the given position is visible by the boid.
*
* <p>
* This method first check if the given point is under the max distance
* of view. If so, it checks if the point is in the angle of view. The
* angle of view is specified as the cosine of the angle between the
* boid direction vector and the vector between the boid and the given
* point. This means that -1 is equal to a 360 degree of vision (the
* angle of view test is deactivated in this case), 0 means 180 degree
* angle, and 0.5 a 90 degree angle for example.
* </p>
*
* @param source
* The source boid.
* @param point
* The point to consider.
*
* @return True if point is visible by source.
*/
@Override
public boolean isVisible(BoidParticle source, Point3 point) {
// Check both the distance and angle of view according to the
// direction
// of the source.
BoidSpecies species = source.getBoid().getSpecies();
Point3 pos = source.getPosition();
double d = pos.distance(point);
// At good distance.
if (d <= species.viewZone) {
// If there is an angle of view.
if (species.angleOfView > -1) {
Vector3 dir = new Vector3(source.dir);
Vector3 light = new Vector3(point.x - pos.x, point.y
- pos.y, point.z - pos.z);// (pos.x - point.x, pos.y
// - point.y, pos.z -
// point.z);
dir.normalize();
light.normalize();
double angle = dir.dotProduct(light);
// In the field of view.
if (angle > species.angleOfView)
return true;
} else {
return true;
}
}
// Not in view.
return false;
}
/**
* A boid particle p2 that is visible by p1 as been found, integrate it
* in the forces that apply to the boid p1.
*
* @param p1
* The source boid.
* @param p2
* the boid visible by p1.
* @param rep
* The repulsion to compute.
*/
protected void actionWithNeighboor(BoidParticle p1, BoidParticle p2,
Vector3 rep) {
Point3 pp = p2.getPosition();
BoidSpecies p1Species = p1.getBoid().getSpecies();
BoidSpecies p2Species = p2.getBoid().getSpecies();
double v = p1.getBoid().getSpecies().viewZone;
rep.set(p1.getPosition().x - pp.x, p1.getPosition().y - pp.y, p1
.getPosition().z
- pp.z);
double len = rep.length();
if (len != 0) {
if (p1Species != p2Species)
rep.scalarMult(1 / (len * len) * p2Species.getFearFactor());
else
rep.scalarMult(1 / (len * len));
}
double a = Math.log(Math.min(len, v)) / Math.log(v);
rep.scalarMult(a);
repulsion.add(rep);
countRep++;
if (p1Species == p2Species) {
barycenter.move(pp);
direction.add(p2.dir);
countAtt++;
}
}
}
public abstract Point3 getPosition();
}
\ No newline at end of file
......@@ -113,8 +113,8 @@ public class BoidGenerator extends SourceBase implements Generator {
*/
public void end() {
ctx.clearSinks();
ctx.pbox.removeAllParticles();
ctx.clear();
ctx = null;
}
......
......@@ -35,6 +35,7 @@ import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import org.graphstream.boids.forces.ntree.NTreeForcesFactory;
import org.graphstream.graph.Graph;
import org.graphstream.graph.NodeFactory;
import org.graphstream.graph.implementations.AbstractNode;
......@@ -42,10 +43,6 @@ import org.graphstream.graph.implementations.AdjacencyListGraph;
import org.graphstream.stream.file.FileSourceDGS;
import org.graphstream.ui.swingViewer.Viewer;
import org.graphstream.ui.swingViewer.util.Camera;
import org.miv.pherd.ParticleBox;
import org.miv.pherd.ntree.Anchor;
import org.miv.pherd.ntree.CellSpace;
import org.miv.pherd.ntree.OctreeCellSpace;
import java.util.Random;
......@@ -100,13 +97,6 @@ public class BoidGraph extends AdjacencyListGraph {
*/
protected long randomSeed;
protected CellSpace space;
/**
* The particles.
*/
protected ParticleBox pbox;
/**
* Species for boids.
*/
......@@ -127,6 +117,8 @@ public class BoidGraph extends AdjacencyListGraph {
*/
protected Random random;
protected BoidsForcesFactory forcesFactory;
/**
* New context.
*/
......@@ -134,8 +126,6 @@ public class BoidGraph extends AdjacencyListGraph {
super("boids-context");
setNodeFactory(new BoidFactory());
int maxParticlesPerCell = 10;
random = new Random();
randomSeed = random.nextLong();
random = new Random(randomSeed);
......@@ -147,9 +137,7 @@ public class BoidGraph extends AdjacencyListGraph {
area = 1;
maxSteps = 0;
boidSpecies = new HashMap<String, BoidSpecies>();
space = new OctreeCellSpace(new Anchor(-area, -area, -area),
new Anchor(area, area, area));
pbox = new ParticleBox(maxParticlesPerCell, space, new BoidCellData());
forcesFactory = new NTreeForcesFactory(this);
}
public BoidGraph(String dgsConfig) throws IOException {
......@@ -202,21 +190,13 @@ public class BoidGraph extends AdjacencyListGraph {
// Access
public CellSpace getSpace() {
return space;
}
public double getArea() {
return area;
}
public void setArea(double area) {