package sim.physics2D.forceGenerator;
import sim.physics2D.physicalObject.MobileObject2D;
import sim.physics2D.PhysicsState;
//import sim.physics2D.util.Double2D;
import sim.util.Bag;
import sim.util.Double2D;
/** The ForceEngine manages the application of forces and torques to objects.
*/
public class ForceEngine
{
private Bag forceGenerators;
// NOTE - Resting contact is currently commented out. Hopefully it will
// be implemented soon...
// resting contact works like this:
// Determine a small relative velocity where objects are considered
// to have 0 relative velocity (STICKY_THRESHOLD). Since resting contact
// only works to cancel forces (it assumes 0 relative velocity), we need
// to get rid of the small relative velocity. Do this by setting the
// coefficient of restitution to 0 (collision.setSticky()).
// After the collision, calculate the relative velocity. If the collision
// was sticky (either because we set it or because one of the objects has
// a zero coefficient of restitution), the new relative velocity should be
// very very small (i.e. 1E-32). See if the new relvel is within (in either
// direction) of ZERO_VELOCITY from 0. If so, add this to the resting contact
// list.
private PhysicsState physicsState = null;
private Double2D forceFields;
private Bag mobileObjs = new Bag();
private static ForceEngine instance = null;
public static ForceEngine getInstance()
{
if (instance == null)
instance = new ForceEngine();
return instance;
}
public static ForceEngine reset()
{
instance = new ForceEngine();
return instance;
}
private ForceEngine()
{
forceFields = new Double2D(0,0);
physicsState = PhysicsState.getInstance();
forceGenerators = new Bag();
}
/** Registers a force generator with the force engine.
*/
public void registerForceGenerator(ForceGenerator forceGenerator)
{
this.forceGenerators.add(forceGenerator);
}
/** Registers a mobile object with the force generator. All mobile objects
* to whom force fields or friction should be applied need to be registered
* with the force engine.
*/
public void registerMobileObject(MobileObject2D objMO)
{
mobileObjs.add(objMO);
}
/** Adds a force that is applied to every object at
* every timestep. An example of a force field is gravity.
*/
public void addForceField(Double2D forceField)
{
forceFields = forceFields.add(forceField);
}
/** Clear all force fields */
public void clearForceField()
{
forceFields = new Double2D(0,0);
}
private void addForceField(MobileObject2D objMO)
{
objMO.addForce(forceFields);
}
/** Causes all force generators that are registered with the
* dynamics engine to add their forces to the appropriate objects
*/
public void addForces()
{
physicsState.clearAllForces();
Bag forceGenerators = this.forceGenerators;
for (int i = 0; i < forceGenerators.numObjs; i++)
{
ForceGenerator fg = (ForceGenerator)forceGenerators.objs[i];
fg.addForce();
}
Bag mobileObjs = this.mobileObjs;
for (int i = 0; i < mobileObjs.numObjs; i++)
{
MobileObject2D mobj = (MobileObject2D)mobileObjs.objs[i];
addForceField(mobj);
mobj.addFrictionForce();
}
/*
// Add resting contact forces
LCP lcp = physicsState.lcp;
if (lcp.contacts.numObjs > 0)
{
System.out.println("Computing Resting Contact Forces!!! ALMOST DONE!");
lcp.addPins();
Bag contactPairs = new Bag();
//System.out.println("HERE: " + lcp.contacts.numObjs);
for (int i = 0; i < lcp.contacts.numObjs; i++)
{
LCP.Contact pair = (LCP.Contact)lcp.contacts.objs[i];
// make sure they haven't started separating
double relVelNormal = pair.relVel.dotProduct(pair.normal);
if (relVelNormal > ZERO_VELOCITY)
lcp.contacts.remove(i);
else
contactPairs.add(pair);
}
if (lcp.contacts.numObjs > 0)
lcp.computeForces();
for (int i = 0; i < contactPairs.numObjs; i++)
{
LCP.Contact pair = (LCP.Contact)contactPairs.objs[i];
Double2D contactForce = pair.normal.scalarMult(lcp.f.get(i, 0));
// add in a small force to get rid of any relative velocity and to prevent interpenetration
double dist = pair.ra.add(pair.A.getPosition()).subtract(pair.rb.add(pair.B.getPosition())).dotProduct(pair.normal);
contactForce = contactForce.add(pair.normal.scalarMult(pair.relVel.dotProduct(pair.normal) * .3 + dist * .3));
if (pair.A instanceof MobileObject2D)
{
MobileObject2D mobjA = (MobileObject2D)pair.A;
Double2D ra = pair.ra; //rotate(mobjA.getOrientation().radians);
mobjA.addForce(contactForce);
mobjA.addTorque(ra.perpDot(contactForce));
}
if (pair.B instanceof MobileObject2D)
{
contactForce = contactForce.scalarMult(-1);
MobileObject2D mobjB = (MobileObject2D)pair.B;
Double2D rb = pair.rb; //rotate(mobjB.getOrientation().radians);
mobjB.addForce(contactForce);
mobjB.addTorque(rb.perpDot(contactForce));
}
}
}
*/
}
}