/*
* Pharynx.java
*
* Created on December 21, 2000, 8:06 AM
*/
package pharynx;
import java.util.*;
/**
*
* @author leon@eatworms.swmed.edu
* @version 0.1
*/
public class Pharynx extends java.lang.Object implements Kickable {
private ArrayList sections;
private int length;
private int width;
private boolean repeat = false;
private double basetime = 0.0;
private double duration = 0.0;
private Simulator sim = null;
/*
* If firstUpdate >= 0, an update is pending on sections starting with
* firstUpdate.
*/
private int firstUpdate = -1;
/** Creates new Pharynx */
public Pharynx(List m) {
this(m, new Simulator());
}
public Pharynx(List m, Simulator sim) {
this(m, sim, false);
}
public Pharynx(List m, boolean repeat) {
this(m, new Simulator(), repeat);
}
public Pharynx(List m, Simulator sim, boolean repeat) {
this.sim = sim;
this.repeat = repeat;
LumenDims lds = new LumenDims();
double[] diams = lds.diameter();
length = diams.length;
width = 0;
// List m = lds.motion();
sections = new ArrayList();
int sn = 0;
Section last = null;
for(int i = 0; i < length; i++) {
if (diams[i] > width)
width = (int) Math.ceil(diams[i]);
MotionList ml = (MotionList) m.get(i);
Section s = new Section(1, diams[i], ml, this, sn);
if (
(null != last) &&
(last.diameter() == s.diameter()) &&
last.motion().equals(s.motion())
) {
// merge identical sections (makes simulation faster)
last.thickness(last.thickness() + s.thickness());
}
else {
sections.add(s);
last = s;
sn++;
double t = ml.getmp(ml.size() - 1).t();
if (duration < t) duration = t;
}
}
sections.trimToSize();
for(int i = 1; i < sections.size(); i++) {
Section s = (Section) sections.get(i);
MotionList ml = s.motion();
double t = ml.getmp(ml.size() - 1).t();
if (t < duration)
ml.add(new MotionPoint(duration, ml.getmp(ml.size() - 1).r()));
}
Section curr = null;
Section next = (Section) sections.get(0);
next.nextP(null);
for(int i = 1; i < sections.size(); i++) {
curr = next;
next = (Section) sections.get(i);
curr.nextA(next);
next.nextP(curr);
}
next.nextA(null);
firstUpdate = 0;
doUpdate(0.0F);
}
/** Run the simulation */
public void simulate() {
for(int i = sections.size() - 1; i >= 0; i--) {
Section s = (Section) sections.get(i);
s.start(sim, basetime);
}
if (repeat) sim.add(new Kick(duration, this));
sim.run();
}
public void halt() {
sim.halt();
}
public void kick(Kick k) {
if (!repeat) return;
basetime = k.time();
k.time(basetime + duration);
sim.add(k);
for(int i = sections.size() - 1; i >= 0; i--) {
Section s = (Section) sections.get(i);
s.start(sim, basetime);
}
}
/**
* Request an update of sections anterior to a certain point
*
* Something has occurred to change the flow through the anterior boundary
* of section s. requestUpdate is called (typically by the Section) to
* tell its containing Pharynx that the anterior Section needs to update
* its flow (which will have the same effect on the next anterior section,
* etc). requestUpdate doesn't actually cause the updates to be performed.
* The Pharynx saves them until things have settled -- this is more
* efficient.
*
* @param s The section whose flowA has changed
*/
protected void requestUpdate(int s) {
if ((firstUpdate < 0) || (s < firstUpdate)) firstUpdate = s;
}
/**
* Request an update of sections anterior to a certain point after Time t
*
* In this version of requestUpdate the time at which the flow change
* occurred is specified. Pharynx waits until all Kicks at that time have
* completed before carrying out the update.
*
* This optimization has been turned off: turned out to be far too much
* trouble to make sure updates were always completed before someone else
* needed them to be, and doesn't gain that much time to boot.
*
* @param s The section whose flowA has changed
* @param t Time after which update required
*/
private boolean updating = false;
protected /* synchronized */ void requestUpdate(int s, double t) {
requestUpdate(s);
if (updating) return;
/*
if (
(null != sim) && // no simulation in progress...
!sim.isEmpty()
) {
Kick k = (Kick) sim.peek();
if ( // if next kickee a Section...
(k.kickee() instanceof Section) &&
(k instanceof Section.SKick)
) {
Section.SKick sk = (Section.SKick) k;
if ( // and it's a motion change...
(sk.type() == Section.MOTION_CHANGE) &&
(sk.time() <= t) // at same time as this one...
) return; // postpone update
}
}
*/
updating = true; // all tests passed: do it
doUpdate(t);
updating = false;
}
/** Carry out pending Section flow updates */
protected /* synchronized */ void doUpdate(double t) {
while(firstUpdate >= 0) {
int sn = firstUpdate;
firstUpdate = -1;
if (sn >= sections.size()) break;
((Section) sections.get(sn+1)).updateFlowA(t);
}
}
public Simulator sim() { return sim; }
public List sections() { return sections; }
public int length() { return length; }
public int width() { return width; }
}