NOOPE is based on the following assumptions:
The toplevel class in the NOOPE hierarchy is noope.core.Physics. An instance of Physics contains a list of entities present in the simulation (instance of noope.core.Entities) and a list laws (an instance of noope.core.Laws). [Both Entities and Laws are subclasses of java.util.Vector - an object-oriented array.]
An Entity has got a set of fundamental properties: position, velocity, inertialMass.
What other properties are needed, depends, however, on which laws are being simulated. For this reason, each Entity holds an array of instances of subclasses of noope.laws.LawPropertyRecord, one for each Law; where the indeces of a LawPropertyRecord in the Entity and the corresponding Law in Laws are the same.
The basic NOOPE object hierarchy
Then, the forces by all laws on all entities, the corresponding accelerations, and the resulting new velocities and positions of all entities, are calculated.
For this, Entity provides the methods
Physics calls the act() method of every Law, which causes the Law to calculate the resulting force on each Entity and call Entity.addForce(this_law_s_force). Then, Physics calls the step(dt) method of every Entity to advance it in time by dt. N.B.: Entity.step(dt) also calls the getActiveForce() method of Entity. The result returned is added to the total force, before doing the calculations. This returns 0 for Entity, but subclasses may override it, in order to make simulations, which would otherwise be hard to describe in terms of physical laws, e.g. the motion of a Rocket. Note that this violates Newton's 3rd law (action - reaction principle).
A BlockReader is a generic data source that is used to construct all NOOPE objects. It is essentially a directory structure with String keys and values, that has to be accessed sequentially.
In more detail, a BlockReader contains
BlockReader.getEntryLocation() and BlockReader.getBlockLocation() ask the BlockReader to return a noope.input.BlockReaderLocation, that contains an identification of the position at which the error has occurred (not necessarily human-readable).
BlockReader.getContext(BlockReaderLocation loc) returns a noope.input.BlockReaderContext that should be a human-readable representation of the error position stored in loc. A BlockReaderContext can provide various useful data and methods for presenting the error to the user, these depend on the particular BlockReader implementation, as do the contents of BlockReaderLocation.
The BlockReader from which Physics will be constructed has the format explained in the noope.input.BlockReader source file.
Physics uses the BlockReader it is constructed with to dynamically load classes with their names given in the source file. This means that noope can work with laws implemented later than it was compiled.
public MyLaw(BlockReader br, int lawnumber, Physics phys) throws BRLoadingExceptionwhere br is the BlockReader this law should be constructed from, lawnumber is the number of this law in the collection of laws and phys is a reference to the Physics object this law belongs to.
public MyLaw(BlockReader br, int lawnumber, Physics phys) throws BRLoadingException
{
super(br, lawnumber, phys);
}
The abstract class noope.laws.ParticlePairLaw that is used for laws where the effect on each Entity is the sum of the effects of all other Entitys on that Entity has the following act() method:
public void act()
// for all i != j in e: actOnPair(i, j);
{
Entities e = physics.getEntities();
if (e.size() <= 1) return; // nothing to do
Entity ent, other;
int i, j;
for (i = 0; i < e.size(); i++)
{
ent = (Entity)e.elementAt(i);
for (j = i+1; j < e.size(); j++)
{
other = (Entity)e.elementAt(j);
actOnPair(ent, other);
}
}
}
/** Applies the law to the two parameters. This should be overridden in subclasses. */
abstract public void actOnPair(Entity ent1, Entity ent2);
In an output law, it outputs some sort of data, instead of exerting forces on the Entitys.
For this, you need to: