Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
gs-boids
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
graphstream
gs-boids
Commits
45b2834c
Commit
45b2834c
authored
Mar 28, 2012
by
gsavin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Isolate forces computation to provide several way for this computation : ntree, greedy ...
parent
85d7dced
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
618 additions
and
492 deletions
+618
-492
src/org/graphstream/boids/Boid.java
src/org/graphstream/boids/Boid.java
+16
-164
src/org/graphstream/boids/BoidForces.java
src/org/graphstream/boids/BoidForces.java
+4
-280
src/org/graphstream/boids/BoidGenerator.java
src/org/graphstream/boids/BoidGenerator.java
+2
-2
src/org/graphstream/boids/BoidGraph.java
src/org/graphstream/boids/BoidGraph.java
+17
-44
src/org/graphstream/boids/BoidsForcesFactory.java
src/org/graphstream/boids/BoidsForcesFactory.java
+40
-0
src/org/graphstream/boids/forces/ntree/BoidCellData.java
src/org/graphstream/boids/forces/ntree/BoidCellData.java
+2
-2
src/org/graphstream/boids/forces/ntree/NTreeForces.java
src/org/graphstream/boids/forces/ntree/NTreeForces.java
+275
-0
src/org/graphstream/boids/forces/ntree/NTreeForcesFactory.java
...rg/graphstream/boids/forces/ntree/NTreeForcesFactory.java
+262
-0
No files found.
src/org/graphstream/boids/Boid.java
View file @
45b2834c
...
...
@@ -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
;
}
p
rotected
void
checkNeighborhood
(
BoidParticle
...
particle
s
)
{
if
(
particle
s
!=
null
)
{
p
ublic
void
checkNeighborhood
(
Boid
...
boid
s
)
{
if
(
boid
s
!=
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
(
Boid
Particle
p
:
particle
s
)
{
if
(
p
.
getId
().
equals
(
b
.
getParticle
().
getId
())
)
{
for
(
Boid
b2
:
boid
s
)
{
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
src/org/graphstream/boids/BoidForces.java
View file @
45b2834c
...
...
@@ -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<number>" where <number> 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
(
1
f
/
countAtt
,
1
f
/
countAtt
,
1
f
/
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
src/org/graphstream/boids/BoidGenerator.java
View file @
45b2834c
...
...
@@ -113,8 +113,8 @@ public class BoidGenerator extends SourceBase implements Generator {
*/
public
void
end
()
{
ctx
.
clearSinks
();
ctx
.
pbox
.
removeAllParticles
();
ctx
.
clear
();
ctx
=
null
;
}
...
...
src/org/graphstream/boids/BoidGraph.java
View file @
45b2834c
...
...
@@ -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
()
{