package edu.stanford.rsl.conrad.phantom.xcat;
import java.util.ArrayList;
import edu.stanford.rsl.conrad.geometry.General;
import edu.stanford.rsl.conrad.geometry.motion.CombinedBreathingHeartMotionField;
import edu.stanford.rsl.conrad.geometry.motion.MotionField;
import edu.stanford.rsl.conrad.geometry.motion.timewarp.ConstantTimeWarper;
import edu.stanford.rsl.conrad.geometry.motion.timewarp.HarmonicTimeWarper;
import edu.stanford.rsl.conrad.geometry.shapes.simple.PointND;
import edu.stanford.rsl.conrad.geometry.splines.SurfaceBSpline;
import edu.stanford.rsl.conrad.geometry.splines.TimeVariantSurfaceBSpline;
import edu.stanford.rsl.conrad.geometry.transforms.Translation;
import edu.stanford.rsl.conrad.numerics.SimpleVector;
import edu.stanford.rsl.conrad.physics.PhysicalObject;
import edu.stanford.rsl.conrad.rendering.PrioritizableScene;
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;
public class CombinedBreathingHeartScene extends WholeBodyScene {
/**
*
*/
private static final long serialVersionUID = -4691335601630948952L;
private BreathingScene breathing;
private HeartScene heart;
private Translation heartTranslation = new Translation(20,55,280);
MotionField motionField = null;
@Override
public void prepareForSerialization(){
heart.prepareForSerialization();
breathing.prepareForSerialization();
}
@Override
public MotionField getMotionField(){
return motionField;
}
public CombinedBreathingHeartScene() {
}
@Override
public void configure() throws Exception{
boolean dynamic = UserUtil.queryBoolean("Create dynamic scene?");
SurfaceBSpline catheterSpline = null;
if ( dynamic )
{
double breathCycles = UserUtil.queryDouble("Number of Breathing Cyles in Scene (acquistion time * breathing rate):", 1.0);
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];
double [] breathingStates = new double [dimz];
for (int i = 0; i < dimz; i++){
heartStates[i]= (i*heartCycles) / (dimz);
breathingStates[i] = (i*breathCycles) / (dimz);
}
config.getRegistry().put(RegKeys.BREATHING_PHASES, DoubleArrayUtil.toString(breathingStates));
config.getRegistry().put(RegKeys.HEART_PHASES, DoubleArrayUtil.toString(heartStates));
setName("Combined Breathing and Cardiac Motion Scene");
heart = new HeartScene();
heart.heartCycles = heartCycles;
heart.configure();
heart.setTimeWarper(new HarmonicTimeWarper(heartCycles));
breathing = new BreathingScene(false);
breathing.configure();
breathing.setTimeWarper(new HarmonicTimeWarper(breathCycles));
String catheterOption = Configuration.getGlobalConfiguration().getRegistry().get(RegKeys.XCAT_ADD_HEART_CATHETER);
if (catheterOption != null){
// remove Heart Catheter from variants in breathing and animate it according to heart and breathing motion.
for (int i=0; i< breathing.getVariants().size(); i++){
TimeVariantSurfaceBSpline spline = breathing.getVariants().get(i);
if (spline.getTitle().equals("Heart Catheter")){
breathing.getVariants().remove(spline);
catheterSpline = spline.getSplines().get(0);
catheterSpline.setTitle("Heart Catheter");
i--;
}
}
}
//createArteryTree();
//createHeartLesions();
} else {
setName("Combined Breathing and Cardiac Motion Scene");
// standard scene
double refHeartPhase = UserUtil.queryDouble("Reference heart phase", 0.75);
heart = new HeartScene();
heart.configure();
heart.setTimeWarper(new ConstantTimeWarper(refHeartPhase));
breathing = new BreathingScene(false);
breathing.configure();
breathing.setTimeWarper(new HarmonicTimeWarper(0.0));
//createArteryTree();
//createHeartLesions();
}
variants.addAll(heart.getVariants());
variants.addAll(breathing.getVariants());
// heart has only variants...
//splines.addAll(heart.getSplines());
splines.addAll(breathing.getSplines());
if (catheterSpline != null){
TimeVariantSurfaceBSpline movingCatheter = new TimeVariantSurfaceBSpline(catheterSpline, this, breathing.getNumberOfBSplineTimePoints(), true);
Translation inverseTranslation = getHeartTranslation().inverse();
movingCatheter.applyTransform(inverseTranslation);
heart.getVariants().add(movingCatheter);
variants.add(movingCatheter);
// todo tesselate as single scene!!!
}
createPhysicalObjects();
// standard scene
//min = new PointND(-62, 150, -26);
//max = new PointND(320, 400, 426);
// Large scene
//min = new PointND(-162, 50, -26);
//max = new PointND(420, 500, 626);
// projection scene
min = new PointND(-62, 150, 150);
max = new PointND(320, 400, 426);
motionField = new CombinedBreathingHeartMotionField(this, 5);
}
@Override
public PointND getPosition(PointND initialPosition, double initialTime,
double time) {
return motionField.getPosition(initialPosition, initialTime, time);
}
public PointND getPosition_old(PointND initialPosition, double initialTime,
double time) {
PointND newPoint;
if (initialTime != time){
Translation inverseTranslation = getHeartTranslation().inverse();
PointND test = initialPosition.clone();
test.applyTransform(inverseTranslation);
if (General.isWithinCuboid(test, heart.getMin(), heart.getMax())){
newPoint = heart.getPosition(test, initialTime, time);
newPoint.applyTransform(getHeartTranslation());
newPoint.applyTransform(new Translation(breathing.getDiaphragmMotionVector(initialTime, time)));
} else {
newPoint = breathing.getPosition(initialPosition, initialTime, time);
}
return newPoint;
} else {
return initialPosition;
}
}
@Override
public ArrayList<PointND> getPositions(PointND initialPosition,
double initialTime, double... times) {
return motionField.getPositions(initialPosition, initialTime, times);
}
public ArrayList<PointND> getPositions_old(PointND initialPosition,
double initialTime, double... times) {
Translation inverseTranslation = getHeartTranslation().inverse();
PointND test = initialPosition.clone();
test.applyTransform(inverseTranslation);
if (General.isWithinCuboid(test, heart.getMin(), heart.getMax())){
ArrayList<PointND> list = heart.getPositions(test, initialTime, times);
for (int i = 0; i < list.size(); i++){
PointND newPoint = list.get(i);
newPoint.applyTransform(getHeartTranslation());
newPoint.applyTransform(new Translation(breathing.getDiaphragmMotionVector(initialTime, times[i])));
}
return list;
}
return breathing.getPositions(initialPosition, initialTime, times);
}
@Override
public PrioritizableScene tessellateScene(double time){
PrioritizableScene scene = new PrioritizableScene();
PrioritizableScene breathingScene = breathing.tessellateScene(time);
PrioritizableScene heartScene = heart.tessellateScene(time);
SimpleVector diaphragmMotion =(breathing.getDiaphragmMotionVector(0, time));
for (PhysicalObject o: heartScene){
o.getShape().applyTransform(getHeartTranslation());
o.getShape().applyTransform(new Translation(diaphragmMotion));
}
//System.out.println(diaphragmMotion);
//System.out.println(heartTranslation);
//clear();
scene.addAll(breathingScene);
scene.addAll(heartScene);
return scene;
}
/**
* Renders cylinder-based regions into the atrium and the left ventricle
*/
public void createHeartLesions(){
heart.createLesions(heart);
}
/**
* Creates the coronary artery tree
*/
public void createArteryTree(){
heart.createArteryTree(heart);
}
@Override
public String getName() {
return "XCat Breathing Torso with Cardiac Motion";
}
/**
* @return the breathing
*/
public BreathingScene getBreathing() {
return breathing;
}
/**
* @param breathing the breathing to set
*/
public void setBreathing(BreathingScene breathing) {
this.breathing = breathing;
}
/**
* @return the heart
*/
public HeartScene getHeart() {
return heart;
}
/**
* @param heart the heart to set
*/
public void setHeart(HeartScene heart) {
this.heart = heart;
}
/**
* @param heartTranslation the heartTranslation to set
*/
public void setHeartTranslation(Translation heartTranslation) {
this.heartTranslation = heartTranslation;
}
/**
* @return the heartTranslation
*/
public Translation getHeartTranslation() {
return heartTranslation;
}
}
/*
* Copyright (C) 2010-2014 Andreas Maier
* CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
*/