Duff Programming Notes
draft 9-14-06
This TWiki topic provides notes on use of Duff files (dfs).
Overview
The Duff format uses binary data to represent a three-dimensional triangulated mesh surface and associated attributes of that surface.
A Duff file contains:
- The vertices of a set of polygons which collectively define a surface in a three dimensional space.
- The triangular faces of the surface, defined by the indices of the vertices that compose the polygons.
- Collections of triangles that are efficiently encoded as a triangle strip.
- Colors, normals and uv-parameterization of the the vertices.
- Metadata, whose format is unstructured.
- Patient data, whose format is unstructured.
- A version of the file.
- A precision.
- An orientation.
- A magic number at the beginning of the file.
Format Specification
In the Duff format, the first 88 bytes of the file are a header, which indicates certain metadata and what, where and how much of each data type is present in the file. All characters are one byte ascii characters; all integers are 32-bit; all floats are 32-bit; all doubles are 64-bit. All encoding is little-endian. All offsets are from the beginning of the file. The header is formatted as follows:
8 x character : The magic number spelling "DUFFSURF" or "FRUSFFUD".
4 x character : The version of the form x.x.x.x .
1 x integer : The data offset.
1 x integer : The metadata offset.
1 x integer : The patient data offset.
1 x integer : The number of faces.
1 x integer : The number of vertices.
1 x integer : The number of triangle strips.
1 x integer : The size of the triangle strips.
1 x integer : The vertex normal offset.
1 x integer : The vertex uv offset.
1 x integer : The vertex color offset.
1 x integer : The precision.
4 x double : The orientation.
At the data offset, the faces, vertices and triangle strips are stored. First the faces are listed as groups of three integers, which correspond to the indices of the vertices that make the face. Second, the vertices are listed as groups of three floats, which specify the coordinates of the vertex. Third, the triangle strips are listed as groups of N integers, where N is the number of strips present (read from the header) and the integers refer to the indices of the vertices.
At the normal offset, the values are N groups of 3 floats, where N is the number of vertices in the shape, and the floats are the coordinates of the normal.
At the color offset, the values are N groups of 3 floats, where N is the number of vertices in the shape, and the floats are the color coordinates.
At the uv offset, the values are N groups of 2 floats, where N is the number of vertices in the shape, and the floats are the uv parameters.
If an attribute is not defined in the file, the offset is zero.
How a Duff is represented as a ShapeTools Shape
Overview
ShapeTools reads data from Duff files into a
Shape.
The Duff vertices are stored in the Shape's
PointSet.
The Duff faces are stored in the Shape's
FaceSet with an implementation
CGALFaceSet. Note that the triangles strips are
not yet supported by the ShapeTools library-- they are stored as an integer
array (encoded as a data attribute) whose values, if present, are written to a Duff output file .
The vertex attributes (color, normal, uv) are stored in
FloatTupleArrayAttributes in an
AttributeSet of the PointSet of the Shape. The ID's of the attributes are the public constant Strings of the DuffIO class. Note that some of these may not exist because the Duff file did not specify them.
The other data in the header are stored as IAttributes of the Shape's AttributeSet using the appropriate public constant string from the DuffIO class.
The precision is stored as an IntAttribute; the orientation is stored as a DoubleArrayAttribute; the version is a ByteArrayAttribute; the patient data is a ByteArrayAttribute; the metadata is a ByteArrayAttribute; and the triangle strips are stored as an IntTupleArray, where the size is the number of strips and the dimension is the size of a strip.
The ShapeTools Data Model
The basic data model used in ShapeTools is:
- Each point has a unique index.
- Points are stored in PointSet.
- A directed line from one point to another is an Edge.
- Each Edge has a unique index.
- Edges are stored in an EdgeSet.
- An ordered set of Edges comprise a Face (polygon).
- Each Face of a Shape has a unique index number.
- Faces are stored in a FaceSet.
- An ordered set of Faces comprise a Solid
- Each Solid has a unique number.
- Solids are stored in a SolidSet (not yet implemented - v1.1.0).
- Each SolidSet (will) refer to a FaceSet
- Each FaceSet refers to an EdgeSet
- Each EdgeSet refers to a PointSet
- A Shape is not required to contain any of PointSets, EdgeSets , FaceSets, or SolidSets, except as noted above.
- Shapes, PointSets, EdgeSets, FaceSets and SolidSets all contain attributes, stored in an AttributeSet.
Reading a Duff file using ShapeTools
The easiest way to to read a Duff is to use the ShapeTools ShapeToolsReader.read() method. This method will
determine which ShapeReader class can read the data file. This frees a ShapeTools programmer from having
to know the type of the input file. Here is how to do so:
import edu.ucla.loni.ccb.shape.About;
import edu.ucla.loni.ccb.shape.Shape;
import edu.ucla.loni.ccb.shapeio.util.ShapeToolsReader;
import java.io.IOException;
public class Example
{
// construct an Example
public Example ()
{
// check version of library
if (About.getMajorVersionNumber() != 1) {
System.err.println("Incompatible ShapeTools library.");
System.exit(1);
}
}
/**
* Read a Duff file into a shape.
*
* @param DuffFilename name of a Duff file.
*
* @returns a Shape read from a Duff.
* throws IOException if unable to read the Duff.
*/
public Shape read(String DuffFilename)
{
ShapeToolsReader reader = new ShapeToolsReader();
Shape duffShape = reader.readShape(duffFilename);
return duffShape;
}
}
An alternative (and more direct approach) is to use
the method in the edu.ucla.loni.ccb.shapeio.duff.DuffIO class.
The call
Shape duffShape = DuffIO.read(String filename)
will read a shape from the file located at the given filename. An IOException will be thrown if it cannot be read for some reason.
Getting data from a Duff Shape
Getting vertex coordinates
Vertex coordinates (points) are stored in the Shape's PointSet.
import edu.ucla.loni.ccb.shape.Shape;
import edu.ucla.loni.ccb.shape.geometry.IPointSet;
IPointSet pointSet = shape.getVertices();
Note: Points of any dimension are supported by ShapeTools.
If you need to know the number of dimensions of a point use
int numDimensions = pointSet.getPointDimensions();
this will always be 3 for a Duff file
To get the coordinates of a single point
Note: this method allocates a new float[] for each point's coordinates so obtained.
// index of desired point
int index = 23;
float [] coordinateTuple = pointSet.getPoint(index);
You'll often want the coordinates of the points as an array of floats.
To minimize memory use ShapeTools returns such requests as a group of
arrays in which
each array contains all point coordinates of a specific spatial dimension.
float [][] coordinates = pointSet.getPoints();
// print coordinates of all points
for (int pointIndex = 0; pointIndex < pointSet.size(); pointIndex++) {
System.out.print("Coords of point #" + pointIndex + ": [");
for (int dim = 0; dim< coordinates.length; dim ++) {
System.out.print(coordinates[dim][pointIndex] + " ");
}
System.out.println("]");
}
Getting triangle faces from a Duff Shape
The vertices of a particular face may be obtained from the FaceSet
// get number of the points of all faces in a Shape:
IFaceSet faceSet = shape.getFaces();
for (int faceIndex =0; faceIndex < faceSet.size(); faceIndex++) {
int [] verticesInAFace = getVertexIndices(int faceIndex)
}
The edges of a particular face may be obtained from the FaceSet
// get number of the points of all faces in a Shape:
IFaceSet faceSet = shape.getFaces();
for (int faceIndex =0; faceIndex < faceSet.size(); faceIndex++) {
int [] edgesInFace = getEdgeIndices(int faceIndex)
}
If the order of faces retrieved from a FaceSet does not matter you
can use a
FaceSetIterator
IFaceSet faces;
FaceSetIterator iter = new FaceSetIterator(faces);
while (iter.hasNext()) {
Face thisFace = (Face)iter.next();
}
Getting attribute data from a Duff Shape
Vertex attributes are stored in the AttributeSet of the PointSet; and likewise, all other attributes are stored in the AttributeSet of the Shape.
Each ShapeTools attribute has a string that identifies it. If you need attribute data from a Duff shape, you should refer to the public constant Strings in the DuffIO class-- they have names of the form $something_ATTRIBUTE_ID. This will be the ID of the ShapeTools attribute.
For example, if a file specified the normal of a vertex, then the shape would have a PointSet attribute with an ID "curvature". The attribute will be a FloatTupleArrayAttribute. Here is a call to get the attribute:
FloatTupleArrayAttribute attribute = shape.getVertices().getAttributeSet().getAttribute(DuffIO?.NORMAL_ATTRIBUTE_ID)
You could then check the dimensions of the attribute with the following calls:
int numberOfAttribute = attribute.size();
int dimension = attribute.getFloat(0).length;
You could get the values of the N-th attribute (the N-th vertex's) as a float[]:
float[] value = attribute.getFloat(N);
There are several special attributes used. There are two "common attributes", which are color and normals. If the Duff file specifies colors or normals, these are registered as common attributes of the FaceSet. Here is a call to get the attribute associated with the common color attribute (the same applies for normals):
String colorAttributeName = aShape.getFaces().getCommonAttributeName(ShapeComponent.COLOR_ATTRIBUTE);
if(colorAttributeName != null)
FloatTupleArrayAttribute = aShape.getFaces().getAttributeSet().getAttribute(colorAttributeName);
colorAttributeName should be the same as
DuffIO.COLOR_ATTRIBUTE_ID.
Writing a Duff file using ShapeTools
Writing a shape to a duff file is as simple as making the call:
DuffIO.write(aShape,aFilename)
where
aShape is a
Shape obtained elsewhere, and
aFilename is the name of the file to be written, which should end in ".dfs". If
aShape was originally created from a duff file, then all of the header data (including triangle strips) is reproduced. If it is was not a duff file, the default values will be used. If the file has either color or normal common attributes, they will always be written.