content: package edu.stanford.rsl.conrad.phantom.xcat;
  
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.Iterator;
  
  import edu.stanford.rsl.conrad.geometry.AbstractShape;
  import edu.stanford.rsl.conrad.geometry.AbstractSurface;
  import edu.stanford.rsl.conrad.geometry.motion.MotionField;
  import edu.stanford.rsl.conrad.geometry.motion.PointBasedMotionField;
  import edu.stanford.rsl.conrad.geometry.motion.timewarp.IdentityTimeWarper;
  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.parallel.ParallelThreadExecutor;
  import edu.stanford.rsl.conrad.rendering.PrioritizableScene;
  import edu.stanford.rsl.conrad.utils.CONRAD;
  
  
  /**
   * Simple scene for XCat to display the whole body. This scene does not define any motion.
   * 
   * @author akmaier
   *
   */
  public class WholeBodyScene extends XCatScene {
  
  	/**
  	 * 
  	 */
  	private static final long serialVersionUID = 7514545759333848517L;
  	protected boolean autoResize = false;
  	public WholeBodyScene (){
  		warper = new IdentityTimeWarper();
  	}
  
  	@Override
  	public void configure() throws Exception{
  		setName("Whole Body Scene");
  		init();
  		createPhysicalObjects();
  	}
  
  
  
  	public PrioritizableScene tessellateScene(double time){
  		// remove previous state scene objects.
  		PrioritizableScene scene = new PrioritizableScene();
  
  
  
  		ArrayList<AbstractSurface> list = new ArrayList<AbstractSurface>();
  		list.addAll(splines);
  		list.addAll(variants);
  		
  		int numberOfThreads = Math.min(CONRAD.getNumberOfThreads(),list.size());
  		TessellationThread[] threads = new TessellationThread[numberOfThreads];
  
  		Iterator<?> iter = Collections.synchronizedList(list).iterator();
  		for (int splineNum = 0; splineNum < numberOfThreads; splineNum++) {
  			threads[splineNum] = new TessellationThread(iter, warper.warpTime(time));
  		}
  		ParallelThreadExecutor executor = new ParallelThreadExecutor(threads);
  		try {
  			executor.execute();
  		} catch (InterruptedException e) {
  			e.printStackTrace();
  		}
  		for (int splineNum = 0; splineNum < numberOfThreads; splineNum++) {
  			for (AbstractShape s: threads[splineNum].getObjects()) {
  				add(scene, s, s.getName());
  			}
  		}
  		return scene;
  	}
  
  	/**
  	 * Determines the bounds of the scene.
  	 */
  	protected void updateSceneLimits(){
  		max = new PointND(-Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE);
  		min = new PointND(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE);
  		int splineNum = splines.size();
  		for (int i=splineNum-1; i >= 0; i--){
  			if ((getSplineNameMaterialNameLUT().get(splines.get(i).getTitle()) != null) &&
  					(getSplinePriorityLUT().get(splines.get(i).getTitle()) != null)) {
  				max.updateIfHigher(splines.get(i).getMax());
  				min.updateIfLower(splines.get(i).getMin());
  			} else {
  				splines.remove(i);
  			}
  		}
  	}
  	
  	/**
  	 * Determines the bounds of the scene.
  	 */
  	protected void init(){
  		init(true);
  	}
  
  	/**
  	 * Determines the bounds of the scene.
  	 */
  	protected void init(boolean keepIntersectingSplines){
  
  		splines = readSplines();
  		if (max == null){ 
  			updateSceneLimits();
  		}
  
  		// remove splines outside the volume of interest using z axis
  		int splineNum = splines.size();
  		if (keepIntersectingSplines) {
  			for (int i=splineNum-1; i >= 0; i--){
  				if (splines.get(i).getMax().get(2) 
  						< min.get(2)){// below VOI
  					splines.remove(i);
  				} else {
  					if (splines.get(i).getMin().get(2) > max.get(2)) { // above VOI
  						splines.remove(i);
  					}
  				}
  			}
  		}
  		else{
  			// remove splines outside the volume of interest using z axis
  			for (int i=splineNum-1; i >= 0; i--){
  				if (splines.get(i).getMax().get(2) 
  						> max.get(2)){// below VOI
  					splines.remove(i);
  				} else {
  					if (splines.get(i).getMin().get(2) < min.get(2)) { // above VOI
  						splines.remove(i);
  					}
  				}
  			}
  		}
  	}
  
  	/**
  	 * Reads XCat from file.
  	 * @return a list of splines
  	 */
  	public ArrayList<SurfaceBSpline> readSplines(){
  		try {
  			String filename = "mtorso";
  			if (!maleGender) {
  				filename = "ftorso";
  			}
  			return SurfaceBSpline.readSplinesFromFile(XCatDirectory + "/"+ filename + ".nrb");
  		} catch (IOException e) {
  			// TODO Auto-generated catch block
  			e.printStackTrace();
  			return null;
  		}
  	}
  
  	/**
  	 * Creates a motion field from the scene motion plus a sequence of points in time.
  	 * @param numberOfBSplineTimePoints
  	 * @param additionalMotion
  	 * @param context
  	 * @return the motion field
  	 */
  	public MotionField getSceneMotion(int numberOfBSplineTimePoints, ArrayList<ArrayList<PointND>> additionalMotion, int context){
  		for (TimeVariantSurfaceBSpline tSpline:variants){
  			for (int t=0; t<numberOfBSplineTimePoints; t++){
  				additionalMotion.get(t).addAll(tSpline.getControlPoints(t));
  			}
  		}
  		PointND[][] array = new PointND[additionalMotion.size()][additionalMotion.get(0).size()];
  		for (int t = 0; t < additionalMotion.size(); t++){
  			array[t] = additionalMotion.get(t).toArray(array[t]);
  		}
  		MotionField motion = new PointBasedMotionField(array, context);
  		return motion;
  	}
  
  	/**
  	 * Creates a motion field from a sequence of points in time.
  	 * @param numberOfBSplineTimePoints
  	 * @param additionalMotion
  	 * @param context 
  	 * @return the motion field
  	 */
  	public MotionField getCompoundMotion(int numberOfBSplineTimePoints, ArrayList<ArrayList<PointND>> additionalMotion, int context){
  		PointND[][] array = new PointND[additionalMotion.size()][additionalMotion.get(0).size()];
  		for (int t = 0; t < additionalMotion.size(); t++){
  			array[t] = additionalMotion.get(t).toArray(array[t]);
  		}
  		MotionField motion = new PointBasedMotionField(array, context);
  		return motion;
  	}
  
  
  	@Override
  	public PointND getPosition(PointND initialPosition, double initialTime,
  			double time) {
  		return initialPosition;
  	}
  
  	@Override
  	public ArrayList<PointND> getPositions(PointND initialPosition,
  			double initialTime, double... times) {
  		ArrayList<PointND> newList = new ArrayList<PointND>();
  		for (int i = 0; i<times.length; i++){
  			newList.add(initialPosition);
  		}
  		return newList;
  	}
  
  	@Override
  	public String getName() {
  		return "XCat Whole Body";
  	}
  
  	@Override
  	public String getBibtexCitation() {
  		return "@article{Segars08-RCS,\n" +
  				"  author={Segars WP, Mahesh M, Beck TJ, Frey EC, Tsui BMW},\n" +
  				"  title={Realistic CT simulation using the 4D XCAT phantom},\n" +
  				"  journal={Med. Phys.},\n" +
  				"  volume={35},\n" +
  				"  pages={3800-3808},\n" +
  				"  year={2008}\n" +
  				"}";
  	}
  
  	@Override
  	public String getMedlineCitation() {
  		return "Segars WP, Mahesh M, Beck TJ, Frey EC, Tsui BMW. Realistic CT simulation using the 4D XCAT phantom. Med. Phys. 35:3800-3808 (2008);";
  	}
  
  	@Override
  	public float[] getBinaryRepresentation() {
  		// TODO Auto-generated method stub
  		return null;
  	}
  }
  
  /*
   * Copyright (C) 2010-2014 Andreas Maier
   * CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
  */
  