Skip to content.
CCB > CCBSIGS > ShapeToolLibraryProgram > ShapeToolsLibrary > DuffProgrammingNotes

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.