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 } }