/*
* Copyright (C) 2010-2014 Mathias Unberath
* 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.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.StringTokenizer;
import edu.stanford.rsl.conrad.geometry.shapes.mesh.Mesh;
import edu.stanford.rsl.conrad.geometry.shapes.simple.PointND;
import edu.stanford.rsl.conrad.numerics.SimpleMatrix;
/**
* Class to write legacy .vtk polydata meshes to a file.
* Only triangular meshes are supported at the moment.
* Extended to meshes featuring deformation vectors at each mesh vertex.
* FIXME Parser adds additional decimal values not present in the input file.
* @author Mathias Unberath, Tobias Geimer, Bastian Bier
*/
public class VTKMeshIO {
/**
* The filename of the output file.
*/
public String filename;
/**
* The mesh file to be written to <filename>.
*/
public Mesh mesh;
//==========================================================================================
// METHODS
//==========================================================================================\
/**
* Class to write legacy .vtk polydata files.
*/
public VTKMeshIO(){
}
/**
* Constructs a writer object and sets the output filename.
* @param filename The output file.
*/
public VTKMeshIO(String filename){
this.filename = filename;
}
/**
* Constructs a writer object and sets the output filename and mesh to be written.
* @param filename The output file.
* @param mesh The mesh to be written.
*/
public VTKMeshIO(String filename, Mesh mesh){
this.filename = filename;
this.mesh = mesh;
}
/**
* Sets the output filename.
* @param filename The output file.
*/
public void setFilename(String filename){
this.filename = filename;
}
/**
* Sets the mesh to be written.
* @param filename The mesh to be written.
*/
public void setMesh(Mesh mesh){
this.mesh = mesh;
}
/**
* Getter for the mesh.
* @return The mesh.
*/
public Mesh getMesh(){
return this.mesh;
}
/**
* Write the mesh to a file.
*/
public void write(){
assert(filename != null) : new Exception("Filename has not been set.");
assert(mesh != null) : new Exception("Mesh has not been set.");
System.out.println("Writing file: " + filename);
SimpleMatrix points = mesh.getPoints();
SimpleMatrix triangles = mesh.getConnectivity();
SimpleMatrix deformations = mesh.getDeformation();
if(points == null){
points = new SimpleMatrix(0,0);
}
if(triangles == null){
triangles = new SimpleMatrix(0,0);
}
try {
PrintWriter writer = new PrintWriter(filename,"UTF-8");
//write header information
writer.println("# vtk DataFile Version 3.0");
writer.println("vtk output");
writer.println("ASCII");
writer.println("DATASET POLYDATA");
// write number of points and then each point in one line
writer.println("POINTS "+points.getRows()+" float");
for(int i = 0; i < points.getRows(); i++){
writer.println(points.getElement(i, 0)+" "+points.getElement(i, 1)+" "+points.getElement(i, 2));
}
// write number of triangles, number of total entries and then each triangle in one line
writer.println("POLYGONS "+triangles.getRows()+" "+4*triangles.getRows());
for(int i = 0; i < triangles.getRows(); i++){
writer.println("3 " + Integer.toString((int)triangles.getElement(i, 0)) + " "
+ Integer.toString((int)triangles.getElement(i, 1)) + " "
+ Integer.toString((int)triangles.getElement(i, 2)));
}
writer.println();
// write deformations
if(deformations != null) {
writer.println("POINT_DATA " + points.getRows());
writer.println("FIELD FieldData 6");
writer.println("phiGlyph " + deformations.getCols() + " " + deformations.getRows() + " float");
for(int i = 0; i < deformations.getRows(); i++){
writer.println( deformations.getElement(i, 0) + " "
+ deformations.getElement(i, 1) + " "
+ deformations.getElement(i, 2));
}
writer.println();
}
writer.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Method to read a triangular mesh in legacy .vtk polydata format.
* @throws IOException if .vtk format does not match expected format
*/
public void read() throws IOException{
ArrayList<PointND> points = new ArrayList<PointND>();
ArrayList<PointND> triangles = new ArrayList<PointND>();
ArrayList<PointND> deformations = new ArrayList<PointND>();
FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr);
// read and discard header information
br.readLine();
br.readLine();
br.readLine();
br.readLine();
String line = br.readLine();
StringTokenizer tok = new StringTokenizer(line);
String t = tok.nextToken(); // skip "points"
t = tok.nextToken();
int numPoints = Integer.parseInt(t);
// read points
// the logic here allows more than one single point per line
for (int i = 0; i < numPoints;){
line = br.readLine();
tok = new StringTokenizer(line);
int nrPts = tok.countTokens();
nrPts /= 3;
for (int j = 0; j < nrPts; j++){
PointND p = new PointND(Float.parseFloat(tok.nextToken()), Float.parseFloat(tok.nextToken()), Float.parseFloat(tok.nextToken()));
points.add(p);
}
i += nrPts;
}
// read connectivity information
// assumes triangle mesh, hence first number in connectivity information needs to be 3
// logic allows more than one triangle per line
line = br.readLine();
if(line.isEmpty()){
line = br.readLine();
}
tok = new StringTokenizer(line);
tok.nextToken(); // skip "polygons"
int numTri = Integer.parseInt(tok.nextToken());
for(int i = 0; i < numTri;){
line = br.readLine();
tok = new StringTokenizer(line);
int nTri = tok.countTokens();
nTri /= 4;
for(int j = 0; j < nTri; j++){
t = tok.nextToken();
if(Integer.parseInt(t) != 3){
br.close();
fr.close();
throw new IOException("VTK-Polydata file: Format not yet supported.");
}else{
PointND triangle = new PointND(Integer.parseInt(tok.nextToken()),Integer.parseInt(tok.nextToken()),Integer.parseInt(tok.nextToken()));
triangles.add(triangle);
}
}
i += nTri;
}
// read deformation vectors
boolean hasDeform = true;
line = br.readLine();
if(line!=null && line.isEmpty()){
line = br.readLine();
}
// not every mesh features a deformation field
// check whether the file contains any more information
if(line == null) {
hasDeform = false;
} else {
tok = new StringTokenizer(line);
tok.nextToken(); // skip "Point_Data"
int numVec = Integer.parseInt(tok.nextToken());
line = br.readLine(); // skip "Field FieldData 6"
line = br.readLine(); // skip "Phi_Glyph numComp numTuples dataType"
for(int i = 0; i < numVec;){
line = br.readLine();
tok = new StringTokenizer(line);
int nVec = tok.countTokens();
nVec /= 3;
for(int j = 0; j < nVec; j++){
PointND vector = new PointND(Float.parseFloat(tok.nextToken()),Float.parseFloat(tok.nextToken()),Float.parseFloat(tok.nextToken()));
deformations.add(vector);
}
i += nVec;
}
}
br.close();
fr.close();
this.mesh = new Mesh();
this.mesh.numPoints = numPoints;
this.mesh.setPoints(toSimpleMatrix(points));
this.mesh.dimension = this.mesh.getPoints().getCols();
this.mesh.numConnections = numTri;
if(hasDeform) this.mesh.setDeformation(toSimpleMatrix(deformations));
if(triangles.size()!=0) this.mesh.setConnectivity(toSimpleMatrix(triangles));
}
/**
* Converts the ArrayList into a SimpleMatrix structure.
* @param list The ArrayList to be converted.
* @return The SimpleMatrix containing the ArrayList's entries.
*/
private SimpleMatrix toSimpleMatrix(ArrayList<PointND> list){
int rows = list.size();
int cols = list.get(0).getDimension();
SimpleMatrix pts = new SimpleMatrix(rows,cols);
for(int i = 0; i < rows; i++){
PointND point = list.get(i);
for(int j = 0; j < cols; j++){
pts.setElementValue(i, j, point.get(j));
}
}
return pts;
}
}