content: /*
   * Copyright (C) 2010-2014 Andreas Maier
   * CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
   */
  package edu.stanford.rsl.conrad.io;
  
  import java.io.BufferedReader;
  import java.io.BufferedWriter;
  import java.io.File;
  import java.io.FileReader;
  import java.io.FileWriter;
  import java.io.IOException;
  
  import edu.stanford.rsl.conrad.geometry.AbstractShape;
  import edu.stanford.rsl.conrad.geometry.shapes.compound.CompoundShape;
  import edu.stanford.rsl.conrad.geometry.shapes.compound.TriangleMesh;
  import edu.stanford.rsl.conrad.geometry.shapes.simple.PointND;
  import edu.stanford.rsl.conrad.geometry.shapes.simple.Triangle;
  import edu.stanford.rsl.conrad.numerics.SimpleOperators;
  import edu.stanford.rsl.conrad.numerics.SimpleVector;
  import edu.stanford.rsl.conrad.phantom.AnalyticPhantom;
  import edu.stanford.rsl.conrad.phantom.MTFBeadPhantom;
  import edu.stanford.rsl.conrad.physics.PhysicalObject;
  import edu.stanford.rsl.conrad.rendering.PrioritizableScene;
  import edu.stanford.rsl.conrad.utils.FileUtil;
  
  public class STLFileUtil {
  
  
  	public static void writeToSTLFile(String filename, PrioritizableScene scene) throws IOException{
  		// Open file to write
  
  		FileWriter fw = new FileWriter(new File(filename));
  		BufferedWriter bw = new BufferedWriter(fw);
  
  		for (PhysicalObject o: scene){
  			writePhysicalObject(bw, o);
  		}
  		bw.flush();
  		bw.close();
  
  	}
  
  	private static void writePhysicalObject(BufferedWriter bw, PhysicalObject o) throws IOException{
  		bw.write("solid " + o.getNameString() + "\r\n");
  		writeShape(bw, o.getShape());
  		bw.write("endsolid "+ o.getNameString() + "\r\n");
  
  	}
  
  	private static void writeShape(BufferedWriter bw, AbstractShape shape) throws IOException{
  		if (shape instanceof CompoundShape){
  			for (AbstractShape s2: (CompoundShape)shape){
  				writeShape(bw, s2);
  			}
  		}
  		if (shape instanceof Triangle){
  			Triangle tri = (Triangle) shape;
  			bw.write("  facet normal " + vectorToString(tri.getNormal())+"\r\n");
  			bw.write("  outer loop\r\n");
  			bw.write("    vertex " + vectorToString(tri.getA().getAbstractVector())+"\r\n");
  			bw.write("    vertex " + vectorToString(tri.getB().getAbstractVector())+"\r\n");
  			bw.write("    vertex " + vectorToString(tri.getC().getAbstractVector())+"\r\n");
  			bw.write("  endloop\r\n");
  			bw.write("  endfacet\r\n");
  		}
  	}
  
  	private static String vectorToString(SimpleVector vec){
  		return "" + vec.getElement(0) + " " + vec.getElement(1) + " " + vec.getElement(2);
  	}
  
  	/**
  	 * @param args
  	 */
  	public static void main(String[] args) {
  		boolean testWrite = false;
  		if (testWrite){
  			AnalyticPhantom phantom = new MTFBeadPhantom();
  			try {
  				String filename = FileUtil.myFileChoose(".stl", true);
  				STLFileUtil.writeToSTLFile(filename, phantom.tessellatePhantom(0.5));
  			} catch (Exception e) {
  				// TODO Auto-generated catch block
  				e.printStackTrace();
  			}
  		} else {
  			try {
  				CompoundShape mesh = readSTLMesh(FileUtil.myFileChoose(".stl", false));
  
  				System.out.println("Read mesh with " + mesh.size() + " triangles");
  			} catch (IOException e) {
  				// TODO Auto-generated catch block
  				e.printStackTrace();
  			} catch (Exception e) {
  				// TODO Auto-generated catch block
  				e.printStackTrace();
  			}
  		}
  	}
  
  	/**
  	 * Method to read an ascii STL Mesh from a file.
  	 * @param filename the filename
  	 * @return the mesh
  	 * @throws IOException may occur
  	 */
  	public static CompoundShape readSTLMesh(String filename) throws IOException{
  		return readSTLMesh(filename, 100);
  	}
  
  	public static CompoundShape readSTLMesh(String filename, int subCompoundStep) throws IOException{
  		BufferedReader br = new BufferedReader(new FileReader(filename));
  		String lineString = br.readLine();
  		CompoundShape mesh = new TriangleMesh();
  		if (lineString.startsWith("solid")){
  			int currentCount = 0;
  			CompoundShape currentShape = new CompoundShape();
  			SimpleVector normal = null;
  			// read solid
  			lineString = br.readLine();
  			while (lineString !=null) {
  				if (lineString.contains("facet normal")){
  					normal = readVector(lineString);
  					lineString = br.readLine();
  				}
  				if (lineString.contains("outer loop")){
  					PointND one = new PointND(readVector(br.readLine()));
  					PointND two = new PointND(readVector(br.readLine()));
  					PointND three = new PointND(readVector(br.readLine()));
  					Triangle tri = new Triangle(one, two, three);
  					double test = SimpleOperators.multiplyInnerProd(normal, tri.getNormal());
  					if (test<0) tri.flipNormal();
  					currentShape.add(tri);
  					currentCount++;
  					if (currentCount >= subCompoundStep){
  						currentCount = 0;
  						mesh.add(currentShape);
  						currentShape = new CompoundShape();
  					}
  					lineString = br.readLine();
  					if (!lineString.contains("endloop")) throw new RuntimeException("Mesh did not contain triangular data!");
  					lineString = br.readLine();
  				}
  				if (!lineString.contains("endfacet")) throw new RuntimeException("Malformed facet detected!");
  				lineString = br.readLine();
  				if (lineString.contains("endsolid")) break;
  			}
  			if (currentShape.size() > 0) mesh.add(currentShape);
  		}
  		mesh.getMax();
  		mesh.getMin();
  		return mesh;
  	}
  
  	public static SimpleVector readVector(String lineString){
  		String [] substr = lineString.split("\\s+");
  		double one = Double.parseDouble(substr[substr.length-3]);
  		double two = Double.parseDouble(substr[substr.length-2]);
  		double three = Double.parseDouble(substr[substr.length-1]);
  		return new SimpleVector(one, two, three);
  	}
  
  
  }
  