using System;
using System.Collections.Generic;
namespace Netron.Diagramming.Core.Layout.Force {
///
/// Manages a simulation of physical forces acting on bodies. To create a
/// custom ForceSimulator, add the desired functions and choose an
/// appropriate .
///
public class ForceSimulator {
#region Fields
///
/// the force items
///
private List mItems;
///
/// the spring items
///
private List springs;
///
/// the forces
///
private IForce[] iforces;
///
/// the spring forces
///
private IForce[] sforces;
private int iflen;
private int sflen;
private IIntegrator mIntegrator;
///
/// the maximum speed allowed
///
private float mSpeedLimit = 1.0f;
#endregion
#region Properties
///
/// Get an iterator over all registered ForceItems.
///
/// the ForceItems.
public List Items {
get {
return mItems;
}
}
///
/// Get an array of all the IForce functions used in this simulator.
///
/// an array of IForce functions
public IForce[] Forces {
get {
IForce[] rv = new IForce[iflen + sflen];
Array.Copy(iforces, 0, rv, 0, iflen);
Array.Copy(sforces, 0, rv, iflen, sflen);
return rv;
}
}
///
/// Gets all registered Springs.
///
public List Springs {
get {
return springs;
}
}
///
/// Get or sets the speed limit, or maximum velocity value allowed by this
/// simulator.
///
/// the "speed limit" maximum velocity value
public float SpeedLimit {
get {
return mSpeedLimit;
}
set { mSpeedLimit = value; }
}
///
/// Get or sets the Integrator used by this simulator.
///
public IIntegrator Integrator {
get {
return mIntegrator;
}
set { mIntegrator = value; }
}
#endregion
#region Constructor
///
/// Create a new, empty ForceSimulator. A RungeKuttaIntegrator is used
/// by default.
///
public ForceSimulator()
: this(new RungeKuttaIntegrator()) {
}
///
/// Create a new, empty ForceSimulator.
///
/// the Integrator to use
public ForceSimulator(IIntegrator integr) {
mIntegrator = integr;
iforces = new IForce[5];
sforces = new IForce[5];
iflen = 0;
sflen = 0;
mItems = new List();
springs = new List();
}
#endregion
#region Methods
///
/// Clear this simulator, removing all ForceItem and Spring instances
/// for the simulator.
///
public void Clear() {
mItems.Clear();
Spring.SpringFactory f = Spring.Factory;
foreach (Spring spring in springs)
f.reclaim(spring);
springs.Clear();
}
///
/// Add a new IForce function to the simulator.
///
/// the IForce function to add
public void AddForce(IForce f) {
if (f.IsItemForce) {
if (iforces.Length == iflen) {
// resize necessary
IForce[] newf = new IForce[iflen + 10];
Array.Copy(iforces, 0, newf, 0, iforces.Length);
iforces = newf;
}
iforces[iflen++] = f;
}
if (f.IsSpringForce) {
if (sforces.Length == sflen) {
// resize necessary
IForce[] newf = new IForce[sflen + 10];
Array.Copy(sforces, 0, newf, 0, sforces.Length);
sforces = newf;
}
sforces[sflen++] = f;
}
}
///
/// Add a ForceItem to the simulation.
///
/// item the ForceItem to add.
public void addItem(ForceItem item) {
mItems.Add(item);
}
///
/// Remove a ForceItem to the simulation.
///
/// Item the ForceItem to remove.
///
public bool removeItem(ForceItem item) {
return mItems.Remove(item);
}
///
/// Add a Spring to the simulation.
///
/// the first endpoint of the spring
/// the second endpoint of the spring
/// the Spring added to the simulation
public Spring addSpring(ForceItem item1, ForceItem item2) {
return addSpring(item1, item2, -1.0F, -1.0F);
}
///
/// Add a Spring to the simulation.
///
/// the first endpoint of the spring
/// the second endpoint of the spring
/// the spring length
/// the Spring added to the simulation
public Spring addSpring(ForceItem item1, ForceItem item2, float length) {
return addSpring(item1, item2, -1.0F, length);
}
///
/// Add a Spring to the simulation.
///
/// the first endpoint of the spring
/// the second endpoint of the spring
/// the spring coefficient
/// the spring length
/// the Spring added to the simulation
public Spring addSpring(ForceItem item1, ForceItem item2, float coeff, float length) {
if (item1 == null || item2 == null)
throw new ArgumentException("ForceItems must be non-null");
Spring s = Spring.Factory.getSpring(item1, item2, coeff, length);
springs.Add(s);
return s;
}
///
/// Run the simulator for one timestep.
///
/// the span of the timestep for which to run the simulator
public void RunSimulator(long timestep) {
Accumulate();
mIntegrator.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);
foreach (ForceItem item in mItems) {
item.Force[0] = 0.0f; item.Force[1] = 0.0f;
for (int i = 0; i < iflen; i++)
iforces[i].GetForce(item);
}
foreach (Spring s in springs) {
for (int i = 0; i < sflen; i++) {
sforces[i].GetForce(s);
}
}
}
#endregion
}
}