/*
* Copyright (C) 2010-2014 Chris Schwemmer
* CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
*/
package edu.stanford.rsl.conrad.phantom;
import java.util.ArrayList;
import edu.stanford.rsl.conrad.geometry.motion.RotationMotionField;
import edu.stanford.rsl.conrad.geometry.motion.timewarp.HarmonicTimeWarper;
import edu.stanford.rsl.conrad.geometry.motion.timewarp.IdentityTimeWarper;
import edu.stanford.rsl.conrad.geometry.motion.timewarp.PeriodicTimeWarper;
import edu.stanford.rsl.conrad.geometry.motion.timewarp.RestPhaseTimeWarper;
import edu.stanford.rsl.conrad.geometry.motion.timewarp.SigmoidTimeWarper;
import edu.stanford.rsl.conrad.geometry.shapes.simple.PointND;
import edu.stanford.rsl.conrad.geometry.shapes.simple.Sphere;
import edu.stanford.rsl.conrad.numerics.SimpleVector;
import edu.stanford.rsl.conrad.physics.PhysicalObject;
import edu.stanford.rsl.conrad.physics.materials.database.MaterialsDB;
import edu.stanford.rsl.conrad.rendering.PrioritizableScene;
import edu.stanford.rsl.conrad.utils.CONRAD;
import edu.stanford.rsl.conrad.utils.Configuration;
import edu.stanford.rsl.conrad.utils.DoubleArrayUtil;
import edu.stanford.rsl.conrad.utils.RegKeys;
import edu.stanford.rsl.jpop.utils.UserUtil;
/**
* A simple 4D phantom similar to a swinging pendulum - spherical version.
*
* @author Chris Schwemmer
*
*/
public class MovingBallPhantom extends AnalyticPhantom4D {
private static final long serialVersionUID = 8308889276453752657L;
private RotationMotionField mf;
private boolean dynamic;
private PointND refPosition;
private PeriodicTimeWarper periWarper;
private HarmonicTimeWarper harmoWarper;
private SigmoidTimeWarper sigWarper;
private RestPhaseTimeWarper restWarper;
private double radius, angle;
private PointND initialCentre;
public MovingBallPhantom(){
}
public void configure() throws Exception {
radius = UserUtil.queryDouble("Radius of sphere", 10);
angle = UserUtil.queryDouble("Angle of deflection (degrees)", 20) * Math.PI / 180.0;
double accFac = UserUtil.queryDouble("Acceleration factor", 4.0);
double rp = UserUtil.queryDouble("Rest phase (%)", 20);
double heartCycles = UserUtil.queryDouble("Number of \"heart beats\" in Scene (acquisition time * heart rate):", 4.0);
Configuration config = Configuration.getGlobalConfiguration();
int dimz = config.getGeometry().getProjectionStackSize() * config.getNumSweeps();
double [] heartStates = new double [dimz];
for (int i = 0; i < dimz; i++){
heartStates[i]= (i*heartCycles) / (dimz);
}
config.getRegistry().put(RegKeys.HEART_PHASES, DoubleArrayUtil.toString(heartStates));
dynamic = UserUtil.queryBoolean("Create dynamic scene?");
mf = new RotationMotionField(new PointND(0, 0, 0), new SimpleVector(0, 1, 0), 2.0 * angle);
harmoWarper = new HarmonicTimeWarper(heartCycles);
sigWarper = new SigmoidTimeWarper();
periWarper = new PeriodicTimeWarper();
restWarper = new RestPhaseTimeWarper();
this.warper = new IdentityTimeWarper();
sigWarper.setAcc(accFac);
restWarper.setRestPhase(rp / 100);
PointND range = mf.getPosition(new PointND(0, 0, 5.0 * radius), 0, 0.5);
initialCentre = new PointND(-range.get(0), range.get(1), range.get(2));
PointND endPos = new PointND(range.get(0), range.get(1), range.get(2));
double maxExt = Math.max(endPos.get(0), endPos.get(2));
this.max = new PointND(1.7 * maxExt, 1.7 * maxExt, 1.7 * maxExt);
this.min = new PointND(-1.7 * maxExt, -1.7 * maxExt, -1.7 * maxExt);
if (!dynamic) {
double refHeartPhase = UserUtil.queryDouble("Reference heart phase", 0.75);
refPosition = mf.getPosition(initialCentre, 0, sigWarper.warpTime(periWarper.warpTime(restWarper.warpTime(refHeartPhase))));
}
}
@Override
public PointND getPosition(PointND initialPosition, double initialTime,
double time) {
double nT = sigWarper.warpTime(periWarper.warpTime(restWarper.warpTime(harmoWarper.warpTime(time))));
System.out.print(nT);
System.out.print(", ");
return mf.getPosition(initialPosition, initialTime, nT);
}
@Override
public ArrayList<PointND> getPositions(PointND initialPosition,
double initialTime, double... times) {
ArrayList<PointND> list = new ArrayList<PointND>();
for (int i=0; i< times.length; i++){
list.add(getPosition(initialPosition, initialTime, times[i]));
}
return list;
}
@Override
public String getBibtexCitation() {
return CONRAD.CONRADBibtex;
}
@Override
public String getMedlineCitation() {
return CONRAD.CONRADMedline;
}
@Override
public PrioritizableScene getScene(double time) {
PrioritizableScene scene = new PrioritizableScene();
PhysicalObject obj = new PhysicalObject();
obj.setMaterial(MaterialsDB.getMaterial("I"));
obj.setNameString("Contrast Bolus");
System.out.print(time);
System.out.print(", ");
PointND centre;
if (dynamic)
centre = getPosition(initialCentre, 0, time);
else
centre = refPosition.clone();
System.out.println(centre.toString());
scene.setMin(min);
scene.setMax(max);
Sphere sp = new Sphere(radius, centre);
sp.setName("Moving Ball");
obj.setShape(sp);
scene.add(obj);
return scene;
}
@Override
public String getName() {
return "Moving Ball Phantom";
}
}