package prefuse.util.force;
import java.util.ArrayList;
import java.util.Iterator;
/**
* Manages a simulation of physical forces acting on bodies. To create a
* custom ForceSimulator, add the desired {@link Force} functions and choose an
* appropriate {@link Integrator}.
*
* @author <a href="http://jheer.org">jeffrey heer</a>
*/
public class ForceSimulator {
private ArrayList items;
private ArrayList springs;
private Force[] iforces;
private Force[] sforces;
private int iflen, sflen;
private Integrator integrator;
private float speedLimit = 1.0f;
/**
* Create a new, empty ForceSimulator. A RungeKuttaIntegrator is used
* by default.
*/
public ForceSimulator() {
this(new RungeKuttaIntegrator());
}
/**
* Create a new, empty ForceSimulator.
* @param integr the Integrator to use
*/
public ForceSimulator(Integrator integr) {
integrator = integr;
iforces = new Force[5];
sforces = new Force[5];
iflen = 0;
sflen = 0;
items = new ArrayList();
springs = new ArrayList();
}
/**
* Get the speed limit, or maximum velocity value allowed by this
* simulator.
* @return the "speed limit" maximum velocity value
*/
public float getSpeedLimit() {
return speedLimit;
}
/**
* Set the speed limit, or maximum velocity value allowed by this
* simulator.
* @param limit the "speed limit" maximum velocity value to use
*/
public void setSpeedLimit(float limit) {
speedLimit = limit;
}
/**
* Get the Integrator used by this simulator.
* @return the Integrator
*/
public Integrator getIntegrator() {
return integrator;
}
/**
* Set the Integrator used by this simulator.
* @param intgr the Integrator to use
*/
public void setIntegrator(Integrator intgr) {
integrator = intgr;
}
/**
* Clear this simulator, removing all ForceItem and Spring instances
* for the simulator.
*/
public void clear() {
items.clear();
Iterator siter = springs.iterator();
Spring.SpringFactory f = Spring.getFactory();
while ( siter.hasNext() )
f.reclaim((Spring)siter.next());
springs.clear();
}
/**
* Add a new Force function to the simulator.
* @param f the Force function to add
*/
public void addForce(Force f) {
if ( f.isItemForce() ) {
if ( iforces.length == iflen ) {
// resize necessary
Force[] newf = new Force[iflen+10];
System.arraycopy(iforces, 0, newf, 0, iforces.length);
iforces = newf;
}
iforces[iflen++] = f;
}
if ( f.isSpringForce() ) {
if ( sforces.length == sflen ) {
// resize necessary
Force[] newf = new Force[sflen+10];
System.arraycopy(sforces, 0, newf, 0, sforces.length);
sforces = newf;
}
sforces[sflen++] = f;
}
}
/**
* Get an array of all the Force functions used in this simulator.
* @return an array of Force functions
*/
public Force[] getForces() {
Force[] rv = new Force[iflen+sflen];
System.arraycopy(iforces, 0, rv, 0, iflen);
System.arraycopy(sforces, 0, rv, iflen, sflen);
return rv;
}
/**
* Add a ForceItem to the simulation.
* @param item the ForceItem to add
*/
public void addItem(ForceItem item) {
items.add(item);
}
/**
* Remove a ForceItem to the simulation.
* @param item the ForceItem to remove
*/
public boolean removeItem(ForceItem item) {
return items.remove(item);
}
/**
* Get an iterator over all registered ForceItems.
* @return an iterator over the ForceItems.
*/
public Iterator getItems() {
return items.iterator();
}
/**
* Add a Spring to the simulation.
* @param item1 the first endpoint of the spring
* @param item2 the second endpoint of the spring
* @return the Spring added to the simulation
*/
public Spring addSpring(ForceItem item1, ForceItem item2) {
return addSpring(item1, item2, -1.f, -1.f);
}
/**
* Add a Spring to the simulation.
* @param item1 the first endpoint of the spring
* @param item2 the second endpoint of the spring
* @param length the spring length
* @return the Spring added to the simulation
*/
public Spring addSpring(ForceItem item1, ForceItem item2, float length) {
return addSpring(item1, item2, -1.f, length);
}
/**
* Add a Spring to the simulation.
* @param item1 the first endpoint of the spring
* @param item2 the second endpoint of the spring
* @param coeff the spring coefficient
* @param length the spring length
* @return the Spring added to the simulation
*/
public Spring addSpring(ForceItem item1, ForceItem item2, float coeff, float length) {
if ( item1 == null || item2 == null )
throw new IllegalArgumentException("ForceItems must be non-null");
Spring s = Spring.getFactory().getSpring(item1, item2, coeff, length);
springs.add(s);
return s;
}
/**
* Get an iterator over all registered Springs.
* @return an iterator over the Springs.
*/
public Iterator getSprings() {
return springs.iterator();
}
/**
* Run the simulator for one timestep.
* @param timestep the span of the timestep for which to run the simulator
*/
public void runSimulator(long timestep) {
accumulate();
integrator.integrate(this, timestep);
}
/**
* Accumulate all forces acting on the items in this simulation
*/
public void accumulate() {
for ( int i = 0; i < iflen; i++ )
iforces[i].init(this);
for ( int i = 0; i < sflen; i++ )
sforces[i].init(this);
Iterator itemIter = items.iterator();
while ( itemIter.hasNext() ) {
ForceItem item = (ForceItem)itemIter.next();
item.force[0] = 0.0f; item.force[1] = 0.0f;
for ( int i = 0; i < iflen; i++ )
iforces[i].getForce(item);
}
Iterator springIter = springs.iterator();
while ( springIter.hasNext() ) {
Spring s = (Spring)springIter.next();
for ( int i = 0; i < sflen; i++ ) {
sforces[i].getForce(s);
}
}
}
} // end of class ForceSimulator