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). */