In the previous chapters, (when we dealt with clouds of points) we saw how to read and write DXF files. There we were concerned only with the geometry of the points and not the connection of the faces. To accommodate the point-face-solid-group hierarchical structure, we need to do a few modifications of the previous code. In the next sections we will show how to write and read DXF and VRML files.
To write a DXF file we need to write the keywords “POLYLINE” and “SEQEND” to identify the beginning and end of a face. (Note: In the case of DXF face and solid are the same thing). The following code show how to write a DXF file:
/*********************************************************/
void
writeDXF(MyGroup group, String filename) throws IOException
{
System.out.println("Writing file
" + filename + "...");
FileOutputStream file = new
FileOutputStream(filename);
DataOutputStream stream = new
DataOutputStream(file);
try {
stream.writeBytes("999\r\n");
stream.writeBytes("Kostas\r\n");
// put some identification here
stream.writeBytes(" 0\r\n");
stream.writeBytes("SECTION\r\n");
stream.writeBytes(" 2\r\n");
stream.writeBytes("ENTITIES\r\n");
stream.writeBytes(" 0\r\n");
for(int k=0;
k<group.numSolids; k++){
//faces
for(int j=0;
j<group.solids[k].numFaces; j++) {
stream.writeBytes("POLYLINE\r\n");
stream.writeBytes(" 8\r\n");
stream.writeBytes("layer_1\r\n");
stream.writeBytes("
66\r\n");
stream.writeBytes(" 1\r\n");
stream.writeBytes("
70\r\n");
stream.writeBytes(" 9\r\n");
stream.writeBytes("
62\r\n");
stream.writeBytes(" 183\r\n");
stream.writeBytes(" 0\r\n");
//points
for(int i=0; i<group.solids[k].faces[j].numPoints; i++) {
stream.writeBytes("VERTEX\r\n");
stream.writeBytes("
8\r\n");
stream.writeBytes("layer_1\r\n");
stream.writeBytes("
62\r\n");
stream.writeBytes("183\r\n");
stream.writeBytes("10\r\n");
stream.writeBytes(Double.toString(
group.solids[k].faces[j].points[i].x)+"\r\n");
stream.writeBytes("
20\r\n");
stream.writeBytes(Double.toString(
group.solids[k].faces[j].points[i].y)+"\r\n");
stream.writeBytes("
30\r\n");
stream.writeBytes(Double.toString(
group.solids[k].faces[j].points[i].z)+"\r\n");
stream.writeBytes(" 70\r\n");
stream.writeBytes("
32\r\n");
stream.writeBytes("
0\r\n");
}
stream.writeBytes("SEQEND\r\n");
stream.writeBytes(" 0\r\n");
}
}
stream.writeBytes("ENDSEC\r\n");
stream.writeBytes(" 0\r\n");
stream.writeBytes("EOF\r\n");
//*** Finish
stream.flush();
stream.close();
}
catch(IOException e) {
System.err.println(e.toString() );
}
}
To read a DXF we do the same as in the Cloud section.
//********************************
public MyGroup readDXFFile(String fileIn)
throws IOException
{
Vector vpoints = new Vector();
Vector vfaces = new Vector();
System.out.println("Reading DXF file
" + fileIn + "...");
File is = new File(fileIn);
FileReader file = new FileReader(is);
StreamTokenizer st = new StreamTokenizer(file);
st.wordChars(' ','_');
boolean poly_flag = false;
boolean exit = false; //flag to exit
double x = 0.;
double y = 0.;
double z = 0.;
while (true) {
if(exit) break;
st.nextToken();
switch (st.ttype) {
case StreamTokenizer.TT_EOF:
exit = true;
break;
case StreamTokenizer.TT_WORD:
if("POLYLINE".equals(st.sval))poly_flag = true;
if("SEQEND".equals(st.sval)){
poly_flag = false;
int numPoints = vpoints.size();
MyPoint[] points = new MyPoint[numPoints];
for(int i=0; i<numPoints; i++)
points[i] =
(MyPoint)vpoints.elementAt(numPoints-1-i);
vfaces.addElement(new MyFace(points));
vpoints.removeAllElements();
}
break;
case StreamTokenizer.TT_NUMBER:
if (poly_flag==true && st.nval == 10){
if (st.nextToken() ==
StreamTokenizer.TT_NUMBER)
x = st.nval;
if (st.nextToken() ==
StreamTokenizer.TT_NUMBER)
if (st.nextToken() ==
StreamTokenizer.TT_NUMBER)
y = st.nval;
if (st.nextToken() ==
StreamTokenizer.TT_NUMBER)
if
(st.nextToken() == StreamTokenizer.TT_NUMBER)
z = st.nval;
vpoints.addElement(new
MyPoint(x, y, z));
}
break;
default:
break;
} /* switch */
}
/* while */
System.out.println("Finished ");
int numFaces = vfaces.size();
MyFace[] faces = new MyFace[numFaces];
for(int i=0; i<numFaces; i++)
faces[i] =
(MyFace)vfaces.elementAt(i);
MySolid[] solids = new MySolid[numFaces];
for(int i=0; i<numFaces; i++){
MyFace[] aFace = new MyFace[1];
aFace[0] = new
MyFace(faces[i].points);
solids[i] = new MySolid(aFace);
}
MyGroup group = new MyGroup(solids);
group.setScale(.3, .3, .3);
file.close();
return group;
}
Suppose that we create two cubes in formZ (or any other modeling program).

Lets save the files in a VRML format and call it 2cubes.wrl. If open the file in a text editor (i.e. notepad), we will see that the file is text and that it is structured in a special way. The syntax on the information in the file follows the specifications of VRML 2.0, which is documented at http://www.sdsc.edu/vrml or in the book “VRML Sourcebook” by A. Ames, D. Nadeau, and J. Moreland, Wiley and Sons, 1997.
#VRML
V1.0 ascii
#Creator
formZ v 3.6.0
#Date
03/07/01 21:34:08
#User
UCLA:Kostas Terzidis:Z-0200-0046276
DEF
Cameras Switch {
whichChild 0
DEF activeview OrthographicCamera {
position -13.8875 16.0359 -24.0538
orientation 0.0691842 0.963611
0.258199 -2.63623
focalDistance 5
height 37.0333
}
}
DEF
Color_1 Material {
ambientColor 0.5 0.5 0.5
diffuseColor 0.381476 0.30518 0.762951
transparency 0
}
DEF
light_1 DirectionalLight {
on TRUE
intensity 1
color 1 1 1
direction 0.762853 -0.560921 0.321595
}
DEF
object_1 Separator {
Coordinate3 {
point [
3.77694 0 -1.53537,
1.65063 0 -1.53537,
1.65063 0 -3.59264,
3.77694 0 -3.59264,
3.77694 3.04801 -1.53537,
1.65063 3.04801 -1.53537,
1.65063 3.04801 -3.59264,
3.77694 3.04801 -3.59264,
]
}
USE Color_1
IndexedFaceSet {
coordIndex [
0, 3, 2, 1, -1,
4, 5, 6, 7, -1,
0, 1, 5, 4, -1,
1, 2, 6, 5, -1,
2, 3, 7, 6, -1,
3, 0, 4, 7, -1,
]
}
}
DEF
object_2 Separator {
Coordinate3 {
point [
0.640228 0 1.56611,
-1.27292 0 1.56611,
-1.27292 0 -0.731554,
0.640228 0 -0.731554,
0.640228 1.2192 1.56611,
-1.27292 1.2192 1.56611,
-1.27292 1.2192 -0.731554,
0.640228 1.2192 -0.731554,
]
}
USE Color_1
IndexedFaceSet {
coordIndex [
0, 3, 2, 1, -1,
4, 5, 6, 7, -1,
0, 1, 5, 4, -1,
1, 2, 6, 5, -1,
2, 3, 7, 6, -1,
3, 0, 4, 7, -1,
]
}
}
As we can see, geometry-wise the syntax makes a distinction between the two objects (they are called object_1 and object_2). We can also see the coordinates of all points and the connections of the points. For example the line
0, 3, 2, 1, -1,
means start at point index 0 then go (or draw) to 3, then go to 2, then go to 1, and end when you encounter a –1. So to read the geometry of an object we need to collect the x,y,z coordinates and then connect them in the given order. We can use the keywords coords and coordIndex to find the beginning and end of these sections. The following code shows one way of reading a VRML file (just the geometry):
//********************************
public MyGroup
readVRMLFile(String fileIn) throws IOException
{
Vector vpoints = new
Vector();
Vector vfaces = new
Vector();
Vector vpointsindex =
new Vector();
System.out.println("Reading DXF file " + fileIn +
"...");
File is = new
File(fileIn);
FileReader file = new
FileReader(is);
StreamTokenizer st =
new StreamTokenizer(file);
boolean exit =
false; //flag to exit
boolean coord_flag =
false;
boolean
coordindex_flag = false;
double[] coord;
coord = new double[3];
int knt = 0;
while (true) {
if(exit) break;
st.nextToken();
switch (st.ttype) {
case
StreamTokenizer.TT_EOF:
exit = true;
break;
case
StreamTokenizer.TT_WORD:
if("coord".equals(st.sval)) {
coord_flag=true;
coordindex_flag = false;
vpoints.removeAllElements();
}
if("coordIndex".equals(st.sval))
{
coord_flag=false; coordindex_flag = true;
vpointsindex.removeAllElements();
}
break;
case
StreamTokenizer.TT_NUMBER:
if
(coord_flag==true){
coord[knt] = st.nval;
knt++;
if(knt==3){
knt = 0;
vpoints.addElement(new MyPoint(
coord[0]*20, coord[1]*20, coord[2]*20));
}
}
if (coordindex_flag==true){
if(st.nval
!= -1)
vpointsindex.addElement(new
Integer((int)st.nval));
else{
int
numPoints = vpointsindex.size();
MyPoint[]
points = new MyPoint[numPoints];
for(int
i=0; i<numPoints; i++){
Integer
index = (Integer)vpointsindex.elementAt(i);
points[i]
= (MyPoint)vpoints.elementAt(index.intValue());
}
vfaces.addElement(new MyFace(points));
}
}
break;
default:
break;
}
/* switch */
} /* while */
System.out.println("Finished ");
int numFaces =
vfaces.size();
MyFace[] faces = new
MyFace[numFaces];
for(int i=0;
i<numFaces; i++)
faces[i] =
(MyFace)vfaces.elementAt(i);
MySolid[] solids = new
MySolid[numFaces];
for(int i=0;
i<numFaces; i++){
MyFace[] aFace =
new MyFace[1];
aFace[0] = new
MyFace(faces[i].points);
solids[i] = new
MySolid(aFace);
}
MyGroup group = new
MyGroup(solids);
file.close();
return group;
}
The rationale in this code is simple: we tokenize the file and we look for keywords. If the word coords is found we set a flag (coord_flag). If the word coordIndex is found we set a flag (coordindes_flag). According to when the flags are set we know that the incoming information will be coordinate triads or connection sets. If coordinates are encountered, we store them in a Vector (vpoints). If connections are encountered, we store them in a Vector (vpointsindex). Once we are done with an object we create the appropriate arrays and populate them with the information contained in the twoVectors.