content: package edu.stanford.rsl.conrad.phantom.workers;
  
  import ij.process.FloatProcessor;
  
  import java.util.ArrayList;
  
  import edu.stanford.rsl.conrad.data.numeric.Grid2D;
  import edu.stanford.rsl.conrad.geometry.shapes.simple.Edge;
  import edu.stanford.rsl.conrad.geometry.shapes.simple.PointND;
  import edu.stanford.rsl.conrad.geometry.shapes.simple.StraightLine;
  import edu.stanford.rsl.conrad.geometry.trajectories.Trajectory;
  import edu.stanford.rsl.conrad.geometry.transforms.Translation;
  import edu.stanford.rsl.conrad.numerics.SimpleVector;
  import edu.stanford.rsl.conrad.phantom.AnalyticPhantom;
  import edu.stanford.rsl.conrad.phantom.AnalyticPhantom4D;
  import edu.stanford.rsl.conrad.physics.PhysicalObject;
  import edu.stanford.rsl.conrad.physics.materials.utils.AttenuationType;
  import edu.stanford.rsl.conrad.rendering.AbstractRayTracer;
  import edu.stanford.rsl.conrad.rendering.PrioritizableScene;
  import edu.stanford.rsl.conrad.rendering.Priority1DRayTracer;
  import edu.stanford.rsl.conrad.utils.CONRAD;
  import edu.stanford.rsl.conrad.utils.Configuration;
  import edu.stanford.rsl.conrad.utils.RegKeys;
  import edu.stanford.rsl.conrad.utils.UserUtil;
  
  
  /**
   * Renders arbitrarily defined phantoms
   * Works now with the correct origin computations.
   * 
   * @author Rotimi X Ojo
   * 
   */
  public class AnalyticPhantom3DVolumeRenderer extends SliceWorker {
  	protected boolean renderAttenuation = false;
  	protected AnalyticPhantom phantom = null;
  	protected PrioritizableScene currentScene = null;
  	private static double DEFAULT_ATTENUATION = -1024; // air(HU)
  	protected double xrayEnergy = 80;
  	protected AttenuationType attType = AttenuationType.TOTAL_WITH_COHERENT_ATTENUATION;
  
  	@Override
  	public String getProcessName() {
  		return "Generic Phantom Renderer";
  	}
  
  	@Override
  	public String getBibtexCitation() {
  		return CONRAD.CONRADBibtex;
  	}
  
  	@Override
  	public String getMedlineCitation() {
  		return CONRAD.CONRADMedline;
  	}
  
  	@Override
  	public void workOnSlice(int sliceNumber) {
  		Trajectory geom = Configuration.getGlobalConfiguration().getGeometry();
  		double originIndexX = geom.getOriginInPixelsX();
  		double originIndexY = geom.getOriginInPixelsY();
  		double originIndexZ = geom.getOriginInPixelsZ();
  		double voxelSizeX = geom.getVoxelSpacingX();
  		double voxelSizeY = geom.getVoxelSpacingY();
  		double voxelSizeZ = geom.getVoxelSpacingZ();
  		FloatProcessor slice = new FloatProcessor(geom.getReconDimensionX(), geom.getReconDimensionY());
  
  		PrioritizableScene phantomScene = phantom;
  		AbstractRayTracer tracer = new Priority1DRayTracer();
  		if (phantom instanceof AnalyticPhantom4D){
  			if (currentScene == null) {
  				AnalyticPhantom4D scene = (AnalyticPhantom4D) phantom;
  				phantomScene = scene.getScene(0);
  				String key = Configuration.getGlobalConfiguration().getRegistry().get(RegKeys.RENDER_PHANTOM_VOLUME_AUTO_CENTER);
  				if (key != null){
  					if (key.equals("true")) {
  						Translation translate = phantom.computeCenterTranslation();
  						for (PhysicalObject o : phantomScene){
  							o.applyTransform(translate);
  						}
  					}
  				}
  				currentScene = phantomScene;
  			}
  			phantomScene = currentScene;
  		}
  		tracer.setScene(phantomScene);
  		slice.add(DEFAULT_ATTENUATION);
  		double z  = (sliceNumber - originIndexZ) * voxelSizeZ;
  		double xFirst = (-originIndexX) * voxelSizeX;
  		double xLast = (slice.getWidth()-originIndexX) * voxelSizeX;
  		
  		if (renderAttenuation && xrayEnergy > 0)
  			slice.setValue(phantomScene.getBackgroundMaterial().getAttenuation(xrayEnergy,attType));
  		else
  			slice.setValue(phantomScene.getBackgroundMaterial().getDensity());
  			
  			
  		slice.fill();
  		for (int i = 0; i < slice.getHeight(); i ++){
  			double y  = (i - originIndexY) * voxelSizeY;
  			PointND pointLeft = new PointND(xFirst, y, z);
  			PointND pointRight = new PointND(xLast, y, z);
  			long basetime = System.currentTimeMillis();
  			StraightLine line = new StraightLine(pointLeft, pointRight);
  			line.normalize();
  			ArrayList<PhysicalObject> segments = tracer.castRay(line);
  			long castTime = System.currentTimeMillis() - basetime;
  			basetime = System.currentTimeMillis();
  			if (segments != null) {
  				for (PhysicalObject o: segments){
  					Edge lineSegment = (Edge) o.getShape(); 
  					PointND p1 = lineSegment.getPoint();
  					PointND p2 = lineSegment.getEnd();
  					int ix1 = (int) Math.round((p1.get(0) / voxelSizeX) + originIndexX);
  					int ix2 = (int) Math.round((p2.get(0) / voxelSizeX) + originIndexX);
  					if (ix2 <= ix1){
  						continue;
  					}
  					int iy = (int) Math.round((y / voxelSizeY) + originIndexY);
  					
  					if (renderAttenuation && xrayEnergy > 0)
  						slice.setValue(o.getMaterial().getAttenuation(xrayEnergy, attType));
  					else
  						slice.setValue(o.getMaterial().getDensity());
  						
  						
  					slice.drawLine(ix1, iy, ix2-1, iy);
  				}
  				long renderTime = System.currentTimeMillis() - basetime;
  				if (sliceNumber == 123) System.out.println("Cast time " + castTime + " render time " + renderTime + " " + segments.size());
  			}
  		}
  		Grid2D grid = new Grid2D((float[])slice.getPixels(), slice.getWidth(), slice.getHeight());
  		this.imageBuffer.add(grid, sliceNumber);
  	}
  
  	public SliceWorker clone() {
  		AnalyticPhantom3DVolumeRenderer newRend = new AnalyticPhantom3DVolumeRenderer();
  		newRend.phantom = this.phantom;
  		newRend.attType=attType;
  		newRend.xrayEnergy = xrayEnergy;
  		newRend.renderAttenuation = renderAttenuation;
  		newRend.showStatus = showStatus;
  		return newRend;
  	}
  
  	@Override
  	public void configure() throws Exception {
  		super.configure();
  		phantom = UserUtil.queryPhantom("Select Phantom", "Select Phantom");
  		phantom.configure();
  		renderAttenuation = UserUtil.queryBoolean("Render energy dependent attenuation?");
  		if(renderAttenuation)
  			xrayEnergy = UserUtil.queryDouble("Monochromatic Xray energy [keV]", xrayEnergy);
  	}
  
  
  
  
  
  
  }
  /*
   * Copyright (C) 2010-2014 Andreas Maier, Rotimi X Ojo
   * CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
  */