com.sun.j3d.utils.geometry.Stripifier Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java3d-core Show documentation
Show all versions of java3d-core Show documentation
Java3D Core And Java3D Util Libraries
The newest version!
/*
* Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
*/
// ----------------------------------------------------------------------
//
// The reference to Fast Industrial Strength Triangulation (FIST) code
// in this release by Sun Microsystems is related to Sun's rewrite of
// an early version of FIST. FIST was originally created by Martin
// Held and Joseph Mitchell at Stony Brook University and is
// incorporated by Sun under an agreement with The Research Foundation
// of SUNY (RFSUNY). The current version of FIST is available for
// commercial use under a license agreement with RFSUNY on behalf of
// the authors and Stony Brook University. Please contact the Office
// of Technology Licensing at Stony Brook, phone 631-632-9009, for
// licensing information.
//
// ----------------------------------------------------------------------
package com.sun.j3d.utils.geometry;
import java.util.ArrayList;
import com.sun.j3d.internal.J3dUtilsI18N;
/**
* The Stripifier utility will change the primitive of the GeometryInfo
* object to Triangle Strips. The strips are made by analyzing the
* triangles in the original data and connecting them together.
*
* Normal Generation should be performed on the GeometryInfo object
* before Stripification, for best results. Example:
*
*
* GeometryInfo gi = new GeometryInfo(TRIANGLE_ARRAY);
* gi.setCoordinates(coordinateData);
*
* NormalGenerator ng = new NormalGenerator();
* ng.generateNormals(gi);
*
* Stripifier st = new Stripifier()
* st.stripify(gi);
*
* Shape3D part = new Shape3D();
* part.setAppearance(appearance);
* part.setGeometry(gi.getGeometryArray());
*
*/
public class Stripifier {
final boolean DEBUG = false;
final boolean CHECK_ORIENT = false;
static final int EMPTY = -1;
boolean hasNormals = false;
boolean hasTextures = false;
int texSetCount = 0;
boolean hasColors = false;
boolean colorStrips = false;
StripifierStats stats;
int[] numNhbrs;
/**
* Indicates to the stripifier to collect statistics on the data
*/
public static final int COLLECT_STATS = 0x01;
/**
* Creates the Stripifier object.
*/
public Stripifier() {
}
/**
* Creates the Stripifier object.
* @param flags Flags
* @since Java 3D 1.2.1
*/
public Stripifier(int flags) {
if ((flags & COLLECT_STATS) != 0) {
stats = new StripifierStats();
}
}
/**
* Converts the geometry contained in the GeometryInfo object into an
* array of triangle strips.
*/
public void stripify(GeometryInfo gi) {
// System.out.println("stripify");
long time = System.currentTimeMillis();
// setup
gi.convertToIndexedTriangles();
gi.forgetOldPrim();
// write out the gi object
// System.out.println("write out the object");
// gi.writeObj();
Face[] faces = createFaceArray(gi);
Edge[] edges = createEdgeArray(faces);
buildAdjacencies(edges, faces);
// print out the adjacency information
if (DEBUG) {
for (int i = 0; i < faces.length; i++) {
faces[i].printVertices();
}
System.out.println("");
for (int i = 0; i < faces.length; i++) {
faces[i].printAdjacency();
}
System.out.println("");
}
Node[] faceNodes = new Node[faces.length];
// Node[] queue = hybridSearch(faces, faceNodes);
Node[] queue = dfSearch(faces, faceNodes);
// print out the queue
if (DEBUG) {
for (int i = 0; i < queue.length; i++) {
queue[i].print();
}
System.out.println("");
}
// int "pointers" for the numbers of strips and patches from
// hamiliton
int[] ns = new int[1];
int[] np = new int[1];
ArrayList hamiltons = hamilton(queue, ns, np);
int numStrips = ns[0];
int numPatches = np[0];
// print out the hamiltonians
if (DEBUG) {
for (int i = 0; i < hamiltons.size(); i++) {
System.out.println("Hamiltonian: " + i);
ArrayList list = (ArrayList)hamiltons.get(i);
for (int j = 0; j < list.size(); j++) {
Face face = (Face)list.get(j);
face.printVertices();
}
System.out.println("");
}
}
// now make strips out of the hamiltonians
ArrayList strips = stripe(hamiltons);
// print out the strips
if (DEBUG) {
for (int i = 0; i < strips.size(); i++) {
System.out.println("Strip: " + i);
Istream istream = (Istream)strips.get(i);
for (int j = 0; j < istream.length; j++) {
System.out.println("vertex: " + istream.istream[j].index);
}
System.out.println("");
}
}
// concatenate the strips
concatenate(strips, faces);
// print out the new strips
if (DEBUG) {
System.out.println("");
System.out.println("concatenated strips: (" +
(strips.size()) + ")");
System.out.println("");
for (int i = 0; i < strips.size(); i++) {
System.out.println("Strip: " + i);
Istream istream = (Istream)strips.get(i);
for (int j = 0; j < istream.length; j++) {
System.out.println("vertex: " + istream.istream[j].index);
}
System.out.println("");
}
}
// put the stripified data into the GeometryInfo object
putBackData(gi, strips);
// System.out.println("time: " + (System.currentTimeMillis()-time));
// System.out.println("");
// add to stats
if (stats != null) {
stats.updateInfo(System.currentTimeMillis()-time, strips,
faces.length);
}
// Stat.printInfo();
// print out strip count info
// System.out.println("numStrips = " + strips.size());
// System.out.println("stripCounts:");
// int avg = 0;
// for (int i = 0; i < strips.size(); i++) {
// System.out.print(((Istream)strips.get(i)).length + " ");
// avg += ((Istream)strips.get(i)).length;
// }
// System.out.println("Avg: " + ((double)avg/(double)strips.size()));
}
/**
* Prints out statistical information for the stripifier: the number of
* original triangles, the number of original vertices, the number of
* strips created, the number of vertices, the total number of triangles,
* the minimum strip length (in # of tris) the maximum strip length
* (in number of tris), the average strip length (in # of tris), the
* average number of vertices per triangle, the total time it took to
* stripify, and the strip length (how many strips of a given length.
* The data is cumulative over all the times the stripifier is called
* until the stats are printed, and then they are reset.
*/
// public static void printStats() {
// // stats.toString();
// }
/**
* Returns the stripifier stats object.
* @exception IllegalStateException if the Stripfier has not
* been constructed
* with the COLLECT_STATS flag
* @since Java 3D 1.2.1
*/
public StripifierStats getStripifierStats() {
if (stats == null) {
throw new IllegalStateException(J3dUtilsI18N.getString("Stripifier0"));
}
return stats;
}
/**
* Creates an array of faces from the geometry in the GeometryInfo object.
*/
Face[] createFaceArray(GeometryInfo gi) {
int[] vertices = gi.getCoordinateIndices();
int[] normals = gi.getNormalIndices();
int[][] textures = null;
int[] t1 = null;
int[] t2 = null;
int[] t3 = null;
texSetCount = gi.getTexCoordSetCount();
if (texSetCount > 0) {
hasTextures = true;
textures = new int[texSetCount][];
for (int i = 0; i < texSetCount; i++) {
textures[i] = gi.getTextureCoordinateIndices(i);
}
t1 = new int[texSetCount];
t2 = new int[texSetCount];
t3 = new int[texSetCount];
} else hasTextures = false;
int[] colors = gi.getColorIndices();
Face[] faces = new Face[vertices.length/3];
int n1, n2, n3, c1, c2, c3;
Vertex v1, v2, v3;
int count = 0;
for (int i = 0; i < vertices.length;) {
if (normals != null) {
// System.out.println("hasNormals");
hasNormals = true;
n1 = normals[i];
n2 = normals[i+1];
n3 = normals[i+2];
}
else {
// System.out.println("doesn't have normals");
hasNormals = false;
n1 = EMPTY;
n2 = EMPTY;
n3 = EMPTY;
}
if (hasTextures) {
for (int j = 0; j < texSetCount; j++) {
t1[j] = textures[j][i];
t2[j] = textures[j][(i+1)];
t3[j] = textures[j][(i+2)];
}
}
if (colors != null) {
hasColors = true;
c1 = colors[i];
c2 = colors[i+1];
c3 = colors[i+2];
}
else {
hasColors = false;
c1 = EMPTY;
c2 = EMPTY;
c3 = EMPTY;
}
v1 = new Vertex(vertices[i], n1, texSetCount, t1, c1);
v2 = new Vertex(vertices[i+1], n2, texSetCount, t2, c2);
v3 = new Vertex(vertices[i+2], n3, texSetCount, t3, c3);
if (!v1.equals(v2) && !v2.equals(v3) && !v3.equals(v1)) {
faces[count] = new Face(count, v1, v2, v3);
count++;
}
i+=3;
}
if (faces.length > count) {
Face[] temp = faces;
faces = new Face[count];
System.arraycopy(temp, 0, faces, 0, count);
}
return faces;
}
/**
* Creates an array of edges from the Face array.
*/
Edge[] createEdgeArray(Face[] faces) {
Edge[] edges = new Edge[faces.length*3];
Face face;
for (int i = 0; i < faces.length; i++) {
face = faces[i];
edges[i*3] = new Edge(face.verts[0], face.verts[1], face.key);
edges[i*3+1] = new Edge(face.verts[1], face.verts[2], face.key);
edges[i*3+2] = new Edge(face.verts[2], face.verts[0], face.key);
}
return edges;
}
/**
* Builds the adjacency graph by finding the neighbors of the edges
*/
void buildAdjacencies(Edge[] edges, Face[] faces) {
// sortEdges(edges);
quickSortEdges(edges, 0, edges.length-1);
// int i = 1;
// set up the edge list of each face
Edge edge;
Face face;
Vertex[] verts;
boolean flag;
int k;
for (int i = 0; i < edges.length; i++) {
// edges are kept in order s.t. the ith edge is the opposite
// edge of the ith vertex
edge = edges[i];
face = faces[edge.face];
verts = face.verts;
flag = true;
if ((!verts[0].equals(edge.v1)) && (!verts[0].equals(edge.v2))) {
face.edges[0] = edge;
face.numNhbrs--;
flag = false;
}
else if ((!verts[1].equals(edge.v1)) &&
(!verts[1].equals(edge.v2))) {
face.edges[1] = edge;
face.numNhbrs--;
flag = false;
}
else if ((!verts[2].equals(edge.v1)) &&
(!verts[2].equals(edge.v2))) {
face.edges[2] = edge;
face.numNhbrs--;
flag = false;
}
else {
if (DEBUG) System.out.println("error!!! Stripifier.buildAdj");
}
// handle degenerencies
if (flag) {
Vertex i1;
// triangle degenerated to a point
if ((edge.v1).equals(edge.v2)) {
face.edges[--face.numNhbrs] = edge;
}
// triangle degenerated to an edge
else {
if (verts[0].equals(verts[1])) {
i1 = verts[1];
}
else {
i1 = verts[2];
}
if (verts[0].equals(i1) && face.edges[0] == null) {
face.edges[0] = edge;
face.numNhbrs--;
}
else if (verts[1].equals(i1) && face.edges[1] == null) {
face.edges[1] = edge;
face.numNhbrs--;
}
else {
face.edges[2] = edge;
face.numNhbrs--;
}
}
}
}
// build the adjacency information by pairing up every two triangles
// that share the same edge
int i = 0; int j = 0;
int j1, j2;
while (i < (edges.length-1)) {
j = i+1;
if (edges[i].equals(edges[j])) {
// determine the orientations of the common edge in the two
// adjacent triangles. Only set them to be adjacent if they
// are opposite
j1 = edges[i].face;
j2 = edges[j].face;
if (j1 != j2) { // set up the two faces as neighbors
edge = edges[i];
face = faces[j1];
k = face.getEdgeIndex(edge);
if ((edge.v1.equals(face.verts[(k+1)%3])) &&
(edge.v2.equals(face.verts[(k+2)%3]))) {
flag = false;
}
else flag = true;
edge = edges[j];
face = faces[j2];
k = face.getEdgeIndex(edge);
if ((edge.v1.equals(face.verts[(k+1)%3])) &&
(edge.v2.equals(face.verts[(k+2)%3]))) {
flag = flag;
}
else flag = (!flag);
if (flag) {
edges[i].face = j2;
edges[j].face = j1;
(faces[j1].numNhbrs)++;
(faces[j2].numNhbrs)++;
j++;
}
else edges[i].face = EMPTY;
}
else edges[i].face = EMPTY;
}
else edges[i].face = EMPTY;
i=j;
}
if (i <= (edges.length-1)) edges[i].face = EMPTY;
// check, for each face, if it is duplicated. For a face that
// neighbors its duplicate in the adjacency graph, it's possible
// that two or more of its neighbors are the same (the duplicate).
// This will be corrected to avoid introducing redundant faces
// later on
for (i = 0; i < faces.length; i++) {
face = faces[i];
if (face.numNhbrs == 3) {
if ((j1 = face.edges[1].face) == face.edges[0].face) {
face.edges[1].face = EMPTY;
face.numNhbrs--;
faces[j1].counterEdgeDel(face.edges[1]);
}
if ((j2 = face.edges[2].face) == face.edges[0].face) {
face.edges[2].face = EMPTY;
face.numNhbrs--;
faces[j2].counterEdgeDel(face.edges[2]);
}
if ((face.edges[1].face != EMPTY) && (j1 == j2)) {
face.edges[2].face = EMPTY;
face.numNhbrs--;
faces[j1].counterEdgeDel(face.edges[2]);
}
}
}
}
/**
* Sorts the edges using BubbleSort
*/
void sortEdges(Edge[] edges) {
int i = edges.length;
boolean sorted = false;
Edge temp = null;
while ((i > 1) && !sorted) {
sorted = true;
for (int j = 1; j < i; j++) {
if (edges[j].lessThan(edges[j-1])) {
temp = edges[j-1];
edges[j-1] = edges[j];
edges[j] = temp;
sorted = false;
}
}
i--;
}
}
/**
* uses quicksort to sort the edges
*/
void quickSortEdges(Edge[] edges, int l, int r) {
if (edges.length > 0) {
int i = l;
int j = r;
Edge k = edges[(l+r) / 2];
do {
while (edges[i].lessThan(k)) i++;
while (k.lessThan(edges[j])) j--;
if (i <= j) {
Edge tmp = edges[i];
edges[i] = edges[j];
edges[j] = tmp;
i++;
j--;
}
} while (i <= j);
if (l < j) quickSortEdges(edges, l, j);
if (l < r) quickSortEdges(edges, i, r);
}
}
/**
* Takes a list of faces as input and performs a hybrid search, a
* variated depth first search that returns to the highest level node
* not yet fully explored. Returns an array of pointers to the faces
* found in order from the search. The faceNodes parameter is an
* array of the Nodes created for the faces.
*/
Node[] hybridSearch(Face[] faces, Node[] faceNodes) {
int numFaces = faces.length;
int i = 0, j = 0, k = 0, ind = 0;
// keep # of faces with certain # of neighbors
int[] count = {0, 0, 0, 0};
// faces sorted by number of neighbors
int[] index = new int[numFaces];
// the index of a certain face in the sorted array
int[] rindex = new int[numFaces];
// Control list pop up operation
boolean popFlag = false;
// queue of pointers to faces found in search
Node[] queue = new Node[numFaces];
// root of depth first tree
Node source;
// for the next node
Node nnode;
// a face
Face face;
// starting position for insertion into the list
int start = 0;
// list for search
SortedList dlist;
// count how many faces have a certain # of neighbors and
// create a Node for each face
for (i = 0; i < numFaces; i++) {
j = faces[i].numNhbrs;
count[j]++;
faceNodes[i] = new Node(faces[i]);
}
// to help with sorting
for (i = 1; i < 4; i++) {
count[i] += count[i-1];
}
// decreasing i to make sorting stable
for (i = numFaces - 1; i >= 0; i--) {
j = faces[i].numNhbrs;
count[j]--;
index[count[j]] = i;
rindex[i] = count[j];
}
// start the hybrid search
for (i = 0; i < numFaces; i++) {
if (index[i] != EMPTY) {
dlist = new SortedList();
source = faceNodes[index[i]];
source.setRoot();
queue[ind] = source;
ind++;
index[i] = EMPTY;
while (source != null) {
nnode = null;
// use the first eligible for continuing search
face = source.face;
for (j = 0; j < 3; j++) {
k = face.getNeighbor(j);
if ((k != EMPTY) &&
(faceNodes[k].notAccessed())) {
nnode = faceNodes[k];
break;
}
}
if (nnode != null) {
// insert the new node
nnode.insert(source);
if (!popFlag) {
start = dlist.sortedInsert(source, start);
}
else popFlag = false;
source = nnode;
queue[ind] = source;
ind++;
index[rindex[k]] = EMPTY;
}
else {
source.processed();
source = dlist.pop();
popFlag = true;
start = 0;
}
} // while -- does popFlag need to be set to false here?
}
}
return queue;
}
Node[] dfSearch(Face[] faces, Node[] faceNodes) {
int numFaces = faces.length;
int i = 0, j = 0, k = 0, ind = 0;
// keep certain # of faces with certain # of neighbors
int[] count = {0, 0, 0, 0};
// faces sorted by # of neighbors
int[] index = new int[numFaces];
// index of a certain face in the sorted array
int[] rindex = new int[numFaces];
// queue of pointers to faces found in the search
Node[] queue = new Node[numFaces];
// root of the depth first tree
Node source;
// the current node
Node node;
// for the next Node
Node nnode;
// a face
Face face;
// count how many faces have a certain # of neighbors and create
// a Node for each face
for (i = 0; i < numFaces; i++) {
j = faces[i].numNhbrs;
count[j]++;
faceNodes[i] = new Node(faces[i]);
}
// to help with sorting
for (i = 1; i < 4; i++) count[i] += count[i-1];
// dec i to make sorting stable
for (i = numFaces-1; i >= 0; i--) {
j = faces[i].numNhbrs;
count[j]--;
index[count[j]] = i;
rindex[i] = count[j];
}
setNumNhbrs(faces);
// start the dfs
for (i = 0; i < numFaces; i++) {
if (index[i] != EMPTY) {
source = faceNodes[index[i]];
source.setRoot();
queue[ind] = source;
ind++;
index[i] = EMPTY;
node = source;
do {
// if source has been done, stop
if ((node == source) && (node.right != null)) break;
nnode = null;
face = node.face;
// for (j = 0; j < 3; j++) {
// if (((k = face.getNeighbor(j)) != EMPTY) &&
// (faceNodes[k].notAccessed())) {
// nnode = faceNodes[k];
// break;
// }
// }
k = findNext(node, faceNodes, faces);
if (k != EMPTY) nnode = faceNodes[k];
if (nnode != null) updateNumNhbrs(nnode);
if (nnode != null) {
// insert new node
nnode.insert(node);
node = nnode;
queue[ind] = node;
ind++;
index[rindex[k]] = EMPTY;
}
else {
node.processed();
node = node.parent;
}
} while (node != source.parent);
}
}
freeNhbrTable();
return queue;
}
int findNext(Node node, Node[] faceNodes, Face[] faces) {
Face face = node.face;
// this face has no neighbors so return
if (face.numNhbrs == 0) return EMPTY;
int i, j, count;
int[] n = new int[3]; // num neighbors of neighboring face
int[] ind = {-1, -1, -1}; // neighboring faces
// find the number of neighbors for each neighbor
count = 0;
for (i = 0; i < 3; i++) {
if (((j = face.getNeighbor(i)) != EMPTY) &&
(faceNodes[j].notAccessed())) {
ind[count] = j;
n[count] = numNhbrs[j];
count++;
}
}
// this face has no not accessed faces
if (count == 0) return EMPTY;
// this face has only one neighbor
if (count == 1) return ind[0];
if (count == 2) {
// if the number of neighbors are the same, try reseting
if ((n[0] == n[1]) && (n[0] != 0)) {
n[0] = resetNhbr(ind[0], faces, faceNodes);
n[1] = resetNhbr(ind[1], faces, faceNodes);
}
// if one neighbor has fewer neighbors, return that neighbor
if (n[0] < n[1]) return ind[0];
if (n[1] < n[0]) return ind[1];
// neighbors tie. pick the sequential one
Node pnode, ppnode;
Face pface, ppface;
if ((pnode = node.parent) != null) {
pface = pnode.face;
i = pface.findSharedEdge(face.key);
if ((ppnode = pnode.parent) != null) {
ppface = ppnode.face;
if (pface.getNeighbor((i+1)%3) == ppface.key) {
j = pface.verts[(i+2)%3].index;
}
else {
j = pface.verts[(i+1)%3].index;
}
}
else {
j = pface.verts[(i+1)%3].index;
}
i = face.findSharedEdge(ind[0]);
if (face.verts[i].index == j) return ind[0];
else return ind[1];
}
else return ind[0];
}
// three neighbors
else {
if ((n[0] < n[1]) && (n[0] < n[2])) return ind[0];
else if ((n[1] < n[0]) && (n[1] < n[2])) return ind[1];
else if ((n[2] < n[0]) && (n[2] < n[1])) return ind[2];
else if ((n[0] == n[1]) && (n[0] < n[2])) {
if (n[0] != 0) {
n[0] = resetNhbr(ind[0], faces, faceNodes);
n[1] = resetNhbr(ind[1], faces, faceNodes);
}
if (n[0] <= n[1]) return ind[0];
else return ind[1];
}
else if ((n[1] == n[2]) && n[1] < n[0]) {
if (n[1] != 0) {
n[1] = resetNhbr(ind[1], faces, faceNodes);
n[2] = resetNhbr(ind[2], faces, faceNodes);
}
if (n[1] <= n[2]) return ind[1];
else return ind[2];
}
else if ((n[2] == n[0]) && (n[2] < n[1])) {
if (n[0] != 0) {
n[0] = resetNhbr(ind[0], faces, faceNodes);
n[2] = resetNhbr(ind[2], faces, faceNodes);
}
if (n[0] <= n[2]) return ind[0];
else return ind[2];
}
else {
if (n[0] != 0) {
n[0] = resetNhbr(ind[0], faces, faceNodes);
n[1] = resetNhbr(ind[1], faces, faceNodes);
n[2] = resetNhbr(ind[2], faces, faceNodes);
}
if ((n[0] <= n[1]) && (n[0] <= n[2])) return ind[0];
else if (n[1] <= n[2]) return ind[1];
else return ind[2];
}
}
}
void setNumNhbrs(Face[] faces) {
int numFaces = faces.length;
numNhbrs = new int[numFaces];
for (int i = 0; i < numFaces; i++) {
numNhbrs[i] = faces[i].numNhbrs;
}
}
void freeNhbrTable() {
numNhbrs = null;
}
void updateNumNhbrs(Node node) {
Face face = node.face;
int i;
if ((i = face.getNeighbor(0)) != EMPTY) numNhbrs[i]--;
if ((i = face.getNeighbor(1)) != EMPTY) numNhbrs[i]--;
if ((i = face.getNeighbor(2)) != EMPTY) numNhbrs[i]--;
}
int resetNhbr(int y, Face[] faces, Node[] faceNodes) {
int x = EMPTY;
Face nface = faces[y];
int i;
for (int j = 0; j < 3; j++) {
if (((i = nface.getNeighbor(j)) != EMPTY) &&
(faceNodes[i].notAccessed())) {
if ((x == EMPTY) || (x > numNhbrs[i])) x = numNhbrs[i];
}
}
return x;
}
/**
* generates hamiltonian strips from the derived binary spanning tree
* using the path peeling algorithm to peel off any node wiht double
* children in a bottom up fashion. Returns a Vector of strips. Also
* return the number of strips and patches in the numStrips and
* numPatches "pointers"
*/
ArrayList hamilton(Node[] sTree, int[] numStrips, int[] numPatches) {
// the number of nodes in the tree
int numNodes = sTree.length;
// number of strips
int ns = 0;
// number of patches
int np = 0;
// some tree node variables
Node node, pnode, cnode;
// the Vector of strips
ArrayList strips = new ArrayList();
// the current strip
ArrayList currStrip;
// the tree nodes are visited in such a bottom-up fashion that
// any node is visited prior to its parent
for (int i = numNodes - 1; i >= 0; i--) {
cnode = sTree[i];
// if cnode is the root of a tree create a strip
if (cnode.isRoot()) {
// each patch is a single tree
np++;
// create a new strip
currStrip = new ArrayList();
// insert the current node into the list
currStrip.add(0, cnode.face);
// add the left "wing" of the parent node to the strip
node = cnode.left;
while (node != null) {
currStrip.add(0, node.face);
node = node.left;
}
// add the right "wing" of the parent node to the strip
node = cnode.right;
while (node != null) {
currStrip.add(currStrip.size(), node.face);
node = node.left;
}
// increase the number of strips
ns++;
// add the strip to the Vector
strips.add(currStrip);
}
// if the number of children of this node is 2, create a strip
else if (cnode.numChildren == 2) {
// if the root has a single child with double children, it
// could be left over as a singleton. However, the following
// rearrangement reduces the chances
pnode = cnode.parent;
if (pnode.isRoot() && (pnode.numChildren == 1)) {
pnode = cnode.right;
if (pnode.left != null) cnode = pnode;
else cnode = cnode.left;
}
// handle non-root case
// remove the node
cnode.remove();
// create a new strip
currStrip = new ArrayList();
// insert the current node into the list
currStrip.add(0, cnode.face);
// add the left "wing" of cnode to the list
node = cnode.left;
while (node != null) {
currStrip.add(0, node.face);
node = node.left;
}
// add the right "wing" of cnode to the list
node = cnode.right;
while (node != null) {
currStrip.add(currStrip.size(), node.face);
node = node.left;
}
// increase the number of strips
ns++;
// add the strip to the Vector
strips.add(currStrip);
}
}
// put the ns and np in the "pointers to return
numStrips[0] = ns;
numPatches[0] = np;
// return the strips
return strips;
}
/**
* creates the triangle strips
*/
ArrayList stripe(ArrayList strips) {
int numStrips = strips.size(); // the number of strips
int count; // where we are in the hamiltonian
Face face; // the face we are adding to the stream
Face prev; // the previous face added to the stream
boolean done; // whether we are done with the current strip
boolean cont; // whether we should continue the current stream
ArrayList currStrip; // the current hamiltonian
Istream currStream; // the stream we are building
ArrayList istreams = new ArrayList(); // the istreams to return
boolean ccw = true;; // counter-clockwise
int share; // the shared edge
Vertex[] buf = new Vertex[4]; // a vertex array to start the stream
// create streams for each hamiltonian
for (int i = 0; i < numStrips; i++) {
currStrip = (ArrayList)strips.get(i);
count = 0;
done = false;
face = getNextFace(currStrip, count++);
// while we are not done with the current hamiltonian
while (!done) {
cont = true;
// if the current face is the only one left in the current
// hamiltonian
if (stripDone(currStrip, count)) {
// create a new istream with the current face
currStream = new Istream(face.verts, 3, false);
// set the head of the strip to this face
currStream.head = face.key;
done = true;
// since we are done with the strip, set the tail to this
// face
currStream.tail = face.key;
}
else {
prev = face;
face = getNextFace(currStrip, count++);
// put the prev vertices in the correct order
// to add the next tri on
share = prev.findSharedEdge(face.key);
buf[0] = prev.verts[share];
buf[1] = prev.verts[(share+1)%3];
buf[2] = prev.verts[(share+2)%3];
// find the fourth vertex
if (CHECK_ORIENT) {
// check for clockwise orientation
if (checkOrientCWSeq(buf[2], buf[1], face)) {
share = face.findSharedEdge(prev.key);
buf[3] = face.verts[share];
currStream = new Istream(buf, 4, false);
// set the head of this strip to the prev face
currStream.head = prev.key;
// if this was the last tri in the strip, then
// we are done
if (stripDone(currStrip, count)) {
done = true;
// set the tail for the strip to current face
currStream.tail = face.key;
}
}
else {
cont = false;
currStream = new Istream(buf, 3, false);
// set the head to the prev face
currStream.head = prev.key;
// since we are not continuing, set
// the tail to prev also
currStream.tail = prev.key;
}
// orientation starts counter-clockwise for 3rd face
ccw = true;
}
else {
share = face.findSharedEdge(prev.key);
buf[3] = face.verts[share];
currStream = new Istream(buf, 4, false);
// set the head of this strip to the prev face
currStream.head = prev.key;
// if this was the last tri in the strip, then
// we are done
if (stripDone(currStrip, count)) {
done = true;
// set the tail for the strip to current face
currStream.tail = face.key;
}
}
// while continue and the strip isn't finished
// add more faces to the stream
while (cont && !stripDone(currStrip, count)) {
prev = face;
face = getNextFace(currStrip, count++);
share = face.findSharedEdge(prev.key);
// if we can add the face without adding any
// zero area triangles
if (seq(currStream, face, share)) {
if (CHECK_ORIENT) {
// if we can add the next face with the correct
// orientation
if (orientSeq(ccw, currStream, face)) {
// append the vertex opposite the
//shared edge
currStream.append(face.verts[share]);
// next face must have opposite orientation
ccw = (!ccw);
// if this was the last tri in the
//strip, then we are done
if (stripDone(currStrip, count)) {
done = true;
// since we are done with this strip,
// set the tail to the current face
currStream.tail = face.key;
}
}
// if we cannot add the face with the correct
// orientation, do not continue with this
// stream
else {
cont = false;
// since we cannot continue with this strip
// set the tail to prev
currStream.tail = prev.key;
}
}
else {
// append the vertex opposite the
//shared edge
currStream.append(face.verts[share]);
// if this was the last tri in the
//strip, then we are done
if (stripDone(currStrip, count)) {
done = true;
// since we are done with this strip,
// set the tail to the current face
currStream.tail = face.key;
}
}
}
// need zero area tris to add continue the strip
else {
if (CHECK_ORIENT) {
// check the orientation for adding a zero
// area tri and this face
if (orientZAT(ccw, currStream, face)) {
// swap the end of the current stream to
// add a zero area triangle
currStream.swapEnd();
// append the vertex opposite the
// shared edge
currStream.append(face.verts[share]);
// if this was the last tri in the
// strip then we are done
if (stripDone(currStrip, count)) {
done = true;
// set the tail because we are done
currStream.tail = face.key;
}
}
// if we cannot add the face with the correct
// orientation, do not continue with this
// stream
else {
cont = false;
// since we cannot continue with this face,
// set the tail to the prev face
currStream.tail = prev.key;
}
}
else {
// swap the end of the current stream to
// add a zero area triangle
currStream.swapEnd();
// append the vertex opposite the
// shared edge
currStream.append(face.verts[share]);
// if this was the last tri in the
// strip then we are done
if (stripDone(currStrip, count)) {
done = true;
// set the tail because we are done
currStream.tail = face.key;
}
}
}
} // while (cont && !stripDone)
} // else
// add the current strip to the strips to be returned
istreams.add(currStream);
} // while !done
} // for each hamiltonian
return istreams;
} // stripe
boolean stripDone(ArrayList strip, int count) {
if (count < strip.size()) {
return false;
}
else return true;
}
boolean seq(Istream stream, Face face, int share) {
int length = stream.length;
Vertex v1 = face.edges[share].v1;
Vertex v2 = face.edges[share].v2;
Vertex last = stream.istream[length-1];
Vertex prev = stream.istream[length-2];
if (((v1.equals(prev)) && (v2.equals(last))) ||
((v1.equals(last)) && (v2.equals(prev)))) {
return true;
}
else return false;
}
boolean orientSeq(boolean ccw, Istream stream, Face face) {
int length = stream.length;
Vertex last = stream.istream[length-1];
Vertex prev = stream.istream[length-2];
if ((ccw && checkOrientCCWSeq(last, prev, face)) ||
((!ccw) && checkOrientCWSeq(last, prev, face))) {
return true;
}
else return false;
}
boolean orientZAT(boolean ccw, Istream stream, Face face) {
int length = stream.length;
Vertex last = stream.istream[length-1];
Vertex swap = stream.istream[length-3];
if ((ccw && checkOrientCWSeq(last, swap, face)) ||
((!ccw) && checkOrientCCWSeq(last, swap, face))) {
return true;
}
else return false;
}
boolean checkOrientCWSeq(Vertex last, Vertex prev, Face face) {
System.out.println("checkOrientCWSeq");
System.out.println("last = " + last.index);
System.out.println("prev = " + prev.index);
System.out.print("face = ");
face.printVertices();
if (last.equals(face.verts[0])) {
if (!prev.equals(face.verts[1])) {
if (DEBUG) System.out.println("ORIENTATION PROBLEM!");
return false;
}
}
else if (last.equals(face.verts[1])) {
if (!prev.equals(face.verts[2])) {
if (DEBUG) System.out.println("ORIENTATION PROBLEM!");
return false;
}
}
else if (last.equals(face.verts[2])) {
if (!prev.equals(face.verts[0])) {
if (DEBUG) System.out.println("ORIENTATION PROBLEM!");
return false;
}
}
return true;
}
boolean checkOrientCCWSeq(Vertex last, Vertex prev, Face face) {
System.out.println("checkOrientCCWSeq");
System.out.println("last = " + last.index);
System.out.println("prev = " + prev.index);
System.out.print("face = ");
face.printVertices();
if (prev.equals(face.verts[0])) {
if (!last.equals(face.verts[1])) {
System.out.println("ORIENTATION PROBLEM!");
return false;
}
}
else if (prev.equals(face.verts[1])) {
if (!last.equals(face.verts[2])) {
System.out.println("ORIENTATION PROBLEM!");
return false;
}
}
else if (prev.equals(face.verts[2])) {
if (!last.equals(face.verts[0])) {
System.out.println("ORIENTATION PROBLEM!");
return false;
}
}
return true;
}
Face getNextFace(ArrayList currStrip, int index) {
if (currStrip.size() > index) return (Face)currStrip.get(index);
else return null;
}
/**
* joins tristrips if their end triangles neighbor each other. The
* priority is performed in three stages: strips are concatenated to
* save 2, 1, or no vertices
*/
void concatenate(ArrayList strips, Face[] faces) {
int numFaces = faces.length;
int[] faceTable = new int[numFaces];
Istream strm;
// initialize the face table to empty
for (int i = 0; i < numFaces; i++) {
faceTable[i] = EMPTY;
}
// set up the faceTable so that a face index relates to a strip
// that owns the face as one of its end faces
for (int i = 0; i < strips.size(); i++) {
strm = (Istream)strips.get(i);
faceTable[strm.head] = i;
faceTable[strm.tail] = i;
}
if (DEBUG) {
System.out.println("");
System.out.println("faceTable:");
for (int i = 0; i < faceTable.length; i++) {
System.out.println(faceTable[i]);
}
System.out.println("");
}
reduceCostByTwo(strips, faces, faceTable);
reduceCostByOne(strips, faces, faceTable);
reduceCostByZero(strips, faces, faceTable);
}
/**
* find all the links that reduce the cost by 2
*/
void reduceCostByTwo(ArrayList strips, Face[] faces, int[] faceTable) {
// System.out.println("reduceCostByTwo");
// number of faces in the face array
int numFaces = faces.length;
// possible adjacent strips
int id, id1, id2;
// Istreams
Istream strm, strm1;
// the length of the Istrem
int len, len1;
// vertex sequences for tristrips
Vertex[] seq, seq1;
// a face
Face face;
// the list of vertices for the face
Vertex[] verts;
// used to syncronize the orientation
boolean sync, sync1;
// a swap variable
Vertex swap;
for (int i = 0; i < numFaces; i++) {
id = faceTable[i];
if (id != EMPTY) {
sync = false; sync1 = false;
strm = (Istream)strips.get(id);
len = strm.length;
seq = strm.istream;
face = faces[i];
verts = face.verts;
// sequential strips
if (!strm.fan) {
// a singleton strip
if (len == 3) {
// check all three neighbors
for (int j = 0; j < 3; j++) {
int k = face.getNeighbor(j);
if ((k != EMPTY) &&
((id1 = faceTable[k]) != EMPTY) &&
(id1 != id)) {
// reassign the sequence
seq[0] = verts[j];
seq[1] = verts[(j+1)%3];
seq[2] = verts[(j+2)%3];
// the neighboring stream
strm1 = (Istream)strips.get(id1);
len1 = strm1.length;
if (k != strm1.head) {
strm1.invert();
// if the length is odd set sync1 to true
if ((len1 % 2) != 0) sync1 = true;
}
seq1 = strm1.istream;
// append a singleton strip
if (len1 == 3) {
// System.out.println("reduce2");
int m = faces[k].findSharedEdge(i);
strm.append(faces[k].verts[m]);
strm1.length = 0;
strm1.istream = null;
strm.tail = k;
faceTable[k] = id;
i--;
break;
}
// append a strip of length over 2
else {
if ((len1 == 4) &&
(seq[1].index == seq1[0].index) &&
(seq[2].index == seq1[2].index)) {
// swap seq1[1] and seq1[2] so that
// seq[1] == seq1[0] and
// seq[1] == seq1[1]
swap = seq1[1];
seq1[1] = seq1[2];
seq1[2] = swap;
}
// see if we can join the strips
if ((seq[1].index == seq1[0].index) &&
(seq[2].index == seq1[1].index)) {
// System.out.println("reduce2");
// add the stream in
strm.addStream(strm1);
faceTable[k] = EMPTY;
faceTable[strm.tail] = id;
i--;
break;
}
else if (sync1) {
strm1.invert();
sync1 = false;
}
}
}
}
}
// not a singleton strip
// can append a stream where the current face is the tail
// or is an even length so we can invert it
else if ((i == strm.tail) || ((len % 2) == 0)) {
// if the current face isn't the tail, then
// have to invert the strip
if (i != strm.tail) {
strm.invert();
seq = strm.istream;
}
// System.out.println("seq.length = " + seq.length);
// System.out.println("len = " + len);
// System.out.print("seq = ");
// for (int l = 0; l < seq.length; l++) {
// if (seq[l] == null) System.out.print(" null");
// else System.out.print(" " + seq[l].index);
// }
// System.out.println("");
swap = seq[len - 3];
// find the neighboring strip
int m = EMPTY;
if (verts[0].index == swap.index) m = 0;
else if (verts[1].index == swap.index) m = 1;
else if (verts[2].index == swap.index) m = 2;
if (m == EMPTY) {
if (DEBUG) System.out.println("problem finding neighbor strip");
}
int j = face.getNeighbor(m);
if (j == EMPTY) id1 = j;
else id1 = faceTable[j];
if ((id1 != EMPTY) &&
(((Istream)strips.get(id1)).fan !=
strm.fan)) {
id1 = EMPTY;
}
if ((id1 != EMPTY) && (id1 != id)) {
strm1 = (Istream)strips.get(id1);
len1 = strm1.length;
// if the shared face isn't the head, invert
// the stream
if (j != strm1.head) {
strm1.invert();
// set the sync var if the length is odd
if ((len1 % 2) != 0) sync1 = true;
}
seq1 = strm1.istream;
// append a singleton strip
if (len1 == 3) {
// System.out.println("reduce2");
m = faces[j].findSharedEdge(i);
strm.append(faces[j].verts[m]);
strm1.length = 0;
strm1.istream = null;
strm.tail = j;
faceTable[i] = EMPTY;
faceTable[j] = id;
}
// append a non-singleton strip
else {
if ((len1 == 4) &&
(seq[len-2].index == seq1[0].index) &&
(seq[len-1].index == seq1[2].index)) {
// swap seq1[1] and seq1[2] so that
// seq[len-2] == seq1[0] and
// seq[len-1] == seq1[1]
swap = seq1[1];
seq1[1] = seq1[2];
seq1[2] = swap;
}
// see if we can append the strip
if ((seq[len-2].index == seq1[0].index) &&
(seq[len-1].index == seq1[1].index)) {
// System.out.println("reduce2");
strm.addStream(strm1);
faceTable[i] = EMPTY;
faceTable[strm.tail] = id;
faceTable[j] = EMPTY;
}
else if (sync1) strm1.invert();
}
}
}
}
}
}
}
/**
* find all links that reduce cost by 1
*/
void reduceCostByOne(ArrayList strips, Face[] faces, int[] faceTable) {
// System.out.println("reduceCostByOne");
// number of faces in the face array
int numFaces = faces.length;
// possible adjacent strips
int id, id1, id2;
// Istreams
Istream strm, strm1;
// the length of the Istream
int len, len1;
// vertex sequences for tristrips
Vertex[] seq, seq1;
// a face
Face face;
// the list of vertices for the face
Vertex[] verts;
// used to synchronize the orientation
boolean sync, sync1;
// a swap variable
Vertex swap;
for (int i = 0; i < numFaces; i++) {
id = faceTable[i];
if ((id != EMPTY) && !((Istream)strips.get(id)).fan) {
sync = false;
strm = (Istream)strips.get(id);
seq = strm.istream;
face = faces[i];
verts = face.verts;
len = strm.length;
// a singleton strip
if (len == 3) {
// consider the three neighboring triangles
for (int j = 0; j < 3; j++) {
int k = face.getNeighbor(j);
if ((k != EMPTY) &&
((id1 = faceTable[k]) != EMPTY) &&
(id1 != id) &&
(!((Istream)strips.get(id1)).fan)) {
// reassign the sequence
seq[0] = verts[j];
seq[1] = verts[(j+1)%3];
seq[2] = verts[(j+2)%3];
// the neighboring stream
strm1 = (Istream)strips.get(id1);
len1 = strm1.length;
if (k != strm1.head) {
strm1.invert();
if ((len1 % 2) != 0) sync = true;
}
seq1 = strm1.istream;
// see if we can join the strips
if ((len1 == 4) &&
(((seq[1].index == seq1[2].index) &&
(seq[2].index == seq1[0].index)) ||
((seq[1].index == seq1[0].index) &&
(seq[2].index == seq1[2].index)))) {
swap = seq1[1];
seq1[1] = seq1[2];
seq1[2] = swap;
}
if ((seq[1].index == seq1[0].index) &&
(seq[2].index == seq1[1].index)) {
// System.out.println("reduce1");
strm.addStream(strm1);
faceTable[k] = EMPTY;
faceTable[strm.tail] = id;
i--;
break;
}
if ((seq[1].index == seq1[1].index) &&
(seq[2].index == seq1[0].index)) {
// System.out.println("reduce1");
strm.append(seq1[1]);
strm.addStream(strm1);
faceTable[k] = EMPTY;
faceTable[strm.tail] = id;
i--;
break;
}
if ((seq[1].index == seq1[0].index) &&
(seq[2].index == seq1[2].index)) {
// System.out.println("reduce1");
seq1[0] = seq1[2];
strm.append(seq1[1]);
strm.addStream(strm1);
faceTable[k] = EMPTY;
faceTable[strm.tail] = id;
i--;
break;
}
if (sync) {
strm1.invert();
sync = false;
}
}
}
}
// non-singleton strip
else if ((i == strm.tail) || ((len % 2) == 0)) {
// make sure the face i ends the id-th strip
if (i != strm.tail) {
strm.invert();
seq = strm.istream;
}
swap = seq[len-3];
// find the neighboring strip
int m = EMPTY;
if (verts[0].index == swap.index) m = 0;
else if (verts[1].index == swap.index) m = 1;
else if (verts[2].index == swap.index) m = 2;
if (m == EMPTY) {
if (DEBUG) System.out.println("problem finding neighbor strip");
}
int j = face.getNeighbor(m);
if (j == EMPTY) id1 = j;
else id1 = faceTable[j];
if ((id1 != EMPTY) &&
(((Istream)strips.get(id1)).fan != strm.fan)) {
id1 = EMPTY;
}
// find another neighboring strip
swap = seq[len-2];
m = EMPTY;
if (verts[0].index == swap.index) m = 0;
else if (verts[1].index == swap.index) m = 1;
else if (verts[2].index == swap.index) m = 2;
if (m == EMPTY) {
if (DEBUG) System.out.println("problem finding neighbor strip.");
}
int k = face.getNeighbor(m);
if (k == EMPTY) id2 = k;
else id2 = faceTable[k];
if ((id2 != EMPTY) &&
(((Istream)strips.get(id2)).fan != strm.fan)) {
id2 = EMPTY;
}
// consider strip id1
boolean success = false;
if ((id1 != EMPTY) && (id1 != id)) {
strm1 = (Istream)strips.get(id1);
len1 = strm1.length;
if (j != strm1.head) {
strm1.invert();
if ((len1 % 2) != 0) sync = true;
}
seq1 = strm1.istream;
if ((len1 == 4) &&
(((seq[len-2].index == seq1[2].index) &&
(seq[len-1].index == seq1[0].index)) ||
(seq[len-2].index == seq1[0].index) &&
(seq[len-1].index == seq1[2].index))) {
swap = seq1[1];
seq1[1] = seq1[2];
seq1[2] = swap;
}
// find matches
if ((seq[len-2].index == seq1[0].index) &&
(seq[len-1].index == seq1[1].index)) {
// System.out.println("reduce1");
strm.addStream(strm1);
faceTable[i] = EMPTY;
faceTable[strm.tail] = id;
faceTable[j] = EMPTY;
success = true;
}
else if ((seq[len-2].index == seq1[1].index) &&
(seq[len-1].index == seq1[0].index)) {
// System.out.println("reduce1");
strm.append(seq1[1]);
strm.addStream(strm1);
faceTable[i] = EMPTY;
faceTable[strm.tail] = id;
faceTable[j] = EMPTY;
success = true;
}
else if ((seq[len-2].index == seq1[0].index) &&
(seq[len-1].index == seq1[2].index)) {
// System.out.println("reduce1");
seq1[0] = seq1[2];
strm.append(seq1[1]);
strm.addStream(strm1);
faceTable[i] = EMPTY;
faceTable[strm.tail] = id;
faceTable[j] = EMPTY;
success = true;
}
else if (sync) {
strm1.invert();
sync = false;
}
}
// now consider strip id2
if (!success &&
(id2 != EMPTY) && (id2 != id)) {
strm1 = (Istream)strips.get(id2);
len1 = strm1.length;
if (k != strm1.head) {
strm1.invert();
if ((len1 % 2) != 0) sync = true;
}
seq1 = strm1.istream;
if ((len1 == 4) &&
(seq[len-3].index == seq1[0].index) &&
(seq[len-1].index == seq1[2].index)) {
swap = seq1[1];
seq1[1] = seq1[2];
seq1[2] = swap;
}
// find matches
if ((seq[len-3].index == seq1[0].index) &&
(seq[len-1].index == seq1[1].index)) {
// System.out.println("reduce1");
strm.swapEnd();
strm.addStream(strm1);
faceTable[i] = EMPTY;
faceTable[strm.tail] = id;
faceTable[k] = EMPTY;
success = true;
}
if (!success && sync) strm1.invert();
}
}
}
}
}
/**
* find all the links that reduce the cost by 0
*/
void reduceCostByZero(ArrayList strips, Face[] faces, int[] faceTable) {
// System.out.println("reduceCostByZero");
// number of faces in the face array
int numFaces = faces.length;
// possible adjacent strips
int id, id1, id2;
// Istreams
Istream strm, strm1;
// the length of the Istream
int len, len1;
// vertex sequences for tristrips
Vertex[] seq, seq1;
// a face
Face face;
// the list of vertices for the face
Vertex[] verts;
// used to synchronize the orientation
boolean sync, sync1;
// a swap variable
Vertex swap;
for (int i = 0; i < numFaces; i++) {
id = faceTable[i];
if ((id != EMPTY) && !((Istream)strips.get(id)).fan) {
sync = false;
strm = (Istream)strips.get(id);
seq = strm.istream;
len = strm.length;
face = faces[i];
verts = face.verts;
if (len == 3) {
for (int j = 0; j < 3; j++) {
int k = face.getNeighbor(j);
if ((k != EMPTY) && ((id1 = faceTable[k]) != EMPTY) &&
(id1 != id) &&
!((Istream)strips.get(id1)).fan) {
// reassign the sequence
seq[0] = verts[j];
seq[1] = verts[(j+1)%3];
seq[2] = verts[(j+2)%3];
// the neighboring stream
strm1 = (Istream)strips.get(id1);
len1 = strm1.length;
if (k != strm1.head) {
strm1.invert();
if ((len1 % 2) != 0) sync = true;
}
seq1 = strm1.istream;
// see if we can join the strips
if ((seq[1].index == seq1[2].index) &&
(seq[2].index == seq1[0].index)) {
// System.out.println("reduce0");
seq1[0] = seq1[2];
strm.append(seq1[0]);
strm.append(seq1[1]);
strm.addStream(strm1);
faceTable[k] = EMPTY;
faceTable[strm.tail] = id;
i--;
break;
}
else if (sync) {
strm1.invert();
sync = false;
}
}
}
}
else if ((i == strm.tail) || ((len % 2) == 0)) {
if (i != strm.tail) {
strm.invert();
seq = strm.istream;
}
swap = seq[len-3];
// find neighboring strip
int m = EMPTY;
if (verts[0].index == swap.index) m = 0;
else if (verts[1].index == swap.index) m = 1;
else if (verts[2].index == swap.index) m = 2;
if (m == EMPTY) {
if (DEBUG) System.out.println("problem finding neighbor strip");
}
int j = face.getNeighbor(m);
if (j == EMPTY) id1 = j;
else id1 = faceTable[j];
if ((id1 != EMPTY) &&
(((Istream)strips.get(id1)).fan != strm.fan)) {
id1 = EMPTY;
}
// find another neighboring strip
swap = seq[len-2];
m = EMPTY;
if (verts[0].index == swap.index) m = 0;
else if (verts[1].index == swap.index) m = 1;
else if (verts[2].index == swap.index) m = 2;
if (m == EMPTY) {
if (DEBUG) System.out.println("problem finding neighbor strip.");
}
int k = face.getNeighbor(m);
if (k == EMPTY) id2 = k;
else id2 = faceTable[k];
if ((id2 != EMPTY) &&
(((Istream)strips.get(id2)).fan != strm.fan)) {
id2 = EMPTY;
}
// consider strip id1
boolean success = false;
if ((id1 != EMPTY) && (id1 != id)) {
strm1 = (Istream)strips.get(id1);
len1 = strm1.length;
if (j != strm1.head) {
strm1.invert();
if ((len1 % 2) != 0) sync = true;
}
seq1 = strm1.istream;
// find matches
if ((seq[len-2].index == seq1[2].index) &&
(seq[len-1].index == seq1[0].index)) {
// System.out.println("reduce0");
seq1[0] = seq1[2];
strm.append(seq1[0]);
strm.append(seq1[1]);
strm.addStream(strm1);
faceTable[i] = EMPTY;
faceTable[strm.tail] = id;
faceTable[j] = EMPTY;
success = true;
}
else if (sync) {
strm1.invert();
sync = false;
}
}
// consider strip id2
if (!success && (id2 != EMPTY) && (id2 != id)) {
strm1 = (Istream)strips.get(id2);
len1 = strm1.length;
if (k != strm1.head) {
strm1.invert();
if ((len1 % 2) != 0) sync = true;
}
seq1 = strm1.istream;
if ((len1 == 4) &&
(((seq[len-3].index == seq1[2].index) &&
(seq[len-1].index == seq1[0].index)) ||
((seq[len-3].index == seq1[0].index) &&
(seq[len-1].index == seq1[2].index)))) {
swap = seq1[1];
seq1[1] = seq1[2];
seq1[2] = swap;
}
// find matches
if ((seq[len-3].index == seq1[1].index) &&
(seq[len-1].index == seq1[0].index)) {
// System.out.println("reduce0");
strm.swapEnd();
strm.append(seq1[1]);
strm.addStream(strm1);
faceTable[i] = EMPTY;
faceTable[strm.tail] = id;
faceTable[k] = EMPTY;
}
else if ((seq[len-3].index == seq1[0].index) &&
(seq[len-1].index == seq1[2].index)) {
// System.out.println("reduce0");
seq1[0] = seq1[2];
strm.swapEnd();
strm.append(seq1[1]);
strm.addStream(strm1);
faceTable[i] = EMPTY;
faceTable[strm.tail] = id;
faceTable[k] = EMPTY;
}
else if ((seq[len-3].index == seq1[0].index) &&
(seq[len-1].index == seq1[1].index)) {
// System.out.println("reduce0");
strm.swapEnd();
strm.addStream(strm1);
faceTable[i] = EMPTY;
faceTable[strm.tail] = id;
faceTable[k] = EMPTY;
}
else if (sync) strm1.invert();
}
}
}
}
}
/**
* puts the stripified data back into the GeometryInfo object
*/
void putBackData(GeometryInfo gi, ArrayList strips) {
int[] tempStripCounts = new int[strips.size()];
int ciSize = 0;
int stripLength;
for (int i = 0; i < strips.size();) {
stripLength = ((Istream)strips.get(i)).length;
if (stripLength != 0) {
tempStripCounts[i] = stripLength;
ciSize += stripLength;
i++;
}
else {
strips.remove(i);
}
}
if (ciSize > 3) {
gi.setPrimitive(gi.TRIANGLE_STRIP_ARRAY);
int[] stripCounts = new int[strips.size()];
System.arraycopy(tempStripCounts, 0, stripCounts, 0, strips.size());
gi.setStripCounts(stripCounts);
// create one array with all the strips
int[] coords = new int[ciSize];
// create arrays for normals, textures and colors if necessary
int[] normals = null;
int[][] textures = null;
int[] colors = null;
javax.vecmath.Color3b[] stripColors = null;
if (hasNormals) normals = new int[ciSize];
if (hasTextures) {
textures = new int[texSetCount][ciSize];
}
if (hasColors) colors = new int[ciSize];
if (colorStrips) {
stripColors = new javax.vecmath.Color3b[ciSize];
colors = new int[ciSize];
}
int count = 0;
Istream currStrip;
for (int i = 0; i < strips.size(); i++) {
currStrip = (Istream)strips.get(i);
if (currStrip.length < 3) {
throw new RuntimeException("currStrip.length = " +
currStrip.length);
}
java.awt.Color stripColor = null;
if (colorStrips) {
int r = ((int)(Math.random()*1000))%255;
int g = ((int)(Math.random()*1000))%255;
int b = ((int)(Math.random()*1000))%255;
stripColor = new java.awt.Color(r, g, b);
}
for (int j = 0; j < currStrip.length; j++) {
coords[count] = currStrip.istream[j].index;
if (hasNormals) normals[count] = currStrip.istream[j].normal;
if (hasTextures) {
for (int k = 0; k < texSetCount; k++) {
textures[k][count] =
currStrip.istream[j].texture[k];
}
}
if (hasColors) colors[count] = currStrip.istream[j].color;
if (colorStrips) stripColors[count] =
new javax.vecmath.Color3b(stripColor);
count++;
}
}
gi.setCoordinateIndices(coords);
if (hasNormals) gi.setNormalIndices(normals);
if (hasTextures) {
for (int i = 0; i < texSetCount; i++) {
gi.setTextureCoordinateIndices(i, textures[i]);
}
}
if (hasColors) gi.setColorIndices(colors);
if (colorStrips) {
gi.setColors(stripColors);
colors = gi.getListIndices(stripColors);
gi.setColorIndices(colors);
}
}
}
/**
* Stores the infomration about a vertex
*/
class Vertex {
int index;
int normal = EMPTY;
int numTexSets = 0;
int[] texture = null;
int color = EMPTY;
Vertex(int vertIndex) {
this(vertIndex, EMPTY, 0, null, EMPTY);
}
Vertex(int vertIndex, int vertNormal,
int vertNumTexSets, int[] vertTexture, int vertColor) {
index = vertIndex;
normal = vertNormal;
numTexSets = vertNumTexSets;
if (numTexSets > 0) {
texture = new int[numTexSets];
System.arraycopy(vertTexture, 0, texture, 0, numTexSets);
}
color = vertColor;
}
boolean equals(Vertex v) {
for (int i = 0; i < numTexSets; i++) {
if (texture[i] != v.texture[i]) {
return false;
}
}
return ((v.index == index) &&
(v.normal == normal) &&
(v.color == color));
}
// will this yield same results as c code ???
boolean lessThan(Vertex v) {
if (index < v.index) return true;
if (index > v.index) return false;
if (normal < v.normal) return true;
if (normal > v.normal) return false;
for (int i = 0; i < numTexSets; i++) {
if (texture[i] < v.texture[i]) return true;
if (texture[i] > v.texture[i]) return false;
}
if (color < v.color) return true;
if (color > v.color) return false;
return false;
}
}
/**
* Stores the information about an edge of a triangle
*/
class Edge {
Vertex v1, v2;
int face;
Edge(Vertex vertex1, Vertex vertex2, int faceIndex) {
face = faceIndex;
// this could be causing wrapping problem
if (vertex1.lessThan(vertex2)) {
v1 = vertex1;
v2 = vertex2;
} else {
v1 = vertex2;
v2 = vertex1;
}
}
/**
* Determine whether the edges have the same vertices
*/
boolean equals(Edge edge) {
return ((v1.equals(edge.v1)) && (v2.equals(edge.v2)));
}
/**
* Used to sort the edges. If this is less than the edge parameter,
* return true. First check if vertex1 is less than vertex1 of the
* edge provided. If so, return true. If the first vertices are equal
* then check vertex2.
*/
boolean lessThan(Edge edge) {
if (v1.lessThan(edge.v1)) return true;
else if (v1.equals(edge.v1)) return (v2.lessThan(edge.v2));
else return false;
}
}
/**
* Stores the information about the face of a triangle
*/
class Face {
int key;
int numNhbrs = 0;
Vertex[] verts = null;
// edges are kept in order s.t. the ith edge is the opposite
// edge of the ith vertex
Edge[] edges = null;
/**
* Creates a new Face with the three given vertices
*/
Face(int index, Vertex v1, Vertex v2, Vertex v3) {
key = index;
verts = new Vertex[3];
verts[0] = v1;
verts[1] = v2;
verts[2] = v3;
edges = new Edge[3];
edges[0] = null;
edges[1] = null;
edges[2] = null;
numNhbrs = 3;
}
/**
* returns the index of the face that neighbors the edge supplied
* by the parameter
*/
int getNeighbor(int edge) {
return edges[edge].face;
}
/**
* returns the index of the edge that is shared by the triangle
* specified by the key parameter
*/
int findSharedEdge(int key) {
if (edges[0].face == key) return 0;
else if (edges[1].face == key) return 1;
else if (edges[2].face == key) return 2;
else return -1; /* error */
}
int getEdgeIndex(Edge edge) {
if (edges[0].equals(edge)) return 0;
else if (edges[1].equals(edge)) return 1;
else return 2;
}
void counterEdgeDel(Edge edge) {
if (DEBUG) {
System.out.println("counterEdgeDel");
}
if ((edges[0]).equals(edge)) {
edges[0].face = EMPTY;
numNhbrs--;
}
else if ((edges[1]).equals(edge)) {
edges[1].face = EMPTY;
numNhbrs--;
}
else if ((edges[2]).equals(edge)) {
edges[2].face = EMPTY;
numNhbrs--;
}
else {
if (DEBUG) {
System.out.println("error in counterEdgeDel");
}
}
}
void printAdjacency() {
System.out.println("Face " + key + ": ");
System.out.println("\t numNhbrs = " + numNhbrs);
System.out.println("\t edge 0: Face " + edges[0].face);
System.out.println("\t edge 1: Face " + edges[1].face);
System.out.println("\t edge 2: Face " + edges[2].face);
}
void printVertices() {
System.out.println("Face " + key + ": (" + verts[0].index + ", " +
verts[1].index + ", " + verts[2].index + ")");
}
}
/**
* stores the information for a face node
*/
class Node {
Face face; // the data: the face
Node parent; // the parent node
Node left; // the left child
Node right; // the right child
int depth; // the topological distance of the node from the root
int numChildren; // the number of children
int attrib; // characteristic of the node eg. color
// the attributes - 3 states for the Node
static final int WHITE = 0; // not being accessed yet
static final int GREY = 1; // being accessed but not done yet
static final int BLACK = 2; // done
Node(Face f) {
face = f;
}
/**
* inserts this node below the parent supplied.
*/
void insert(Node p) {
parent = p;
depth = p.depth + 1;
attrib = GREY;
if (parent.left == null) parent.left = this;
else parent.right = this;
(parent.numChildren)++;
}
/**
* remove this node from its parent
*/
void remove() {
if (parent != null) {
if (parent.left == this) {
parent.left = parent.right;
parent.right = null;
}
else {
parent.right = null;
}
(parent.numChildren)--;
}
}
/**
* sets the depth to 0 and the attrib to GREY
*/
void setRoot() {
depth = 0;
attrib = GREY;
}
/**
* returns true if the attrib is WHITE
*/
boolean notAccessed() {
return (attrib == WHITE);
}
/**
* sets the color to BLACK
*/
void processed() {
attrib = BLACK;
}
/**
* a node is the root if it doesn't have a parent
*/
boolean isRoot() {
return (parent == null);
}
/**
* prints the information in this Node
*/
void print() {
System.out.println(this);
System.out.println("Node depth: " + depth);
face.printVertices();
System.out.print("parent: ");
if (parent != null) parent.face.printVertices();
else System.out.println("null");
System.out.print("left: ");
if (left != null) left.face.printVertices();
else System.out.println("null");
System.out.print("right: ");
if (right != null) right.face.printVertices();
else System.out.println("null");
System.out.println("attrib: " + attrib);
System.out.println("");
}
}
/**
* sorts the Nodes by depth
*/
class SortedList {
ArrayList list;
/**
* create a new SortedList
*/
SortedList() {
list = new ArrayList();
}
/**
* insert into the list sorted by depth. start looking at start
* to save some time. Returns the index of the next item of the
* inserted element
*/
int sortedInsert(Node data, int start) {
// adjust start to where insert sorted
while ((start < list.size()) &&
(((Node)list.get(start)).depth <= data.depth)) {
start++;
}
// insert at start index
list.add(start, data);
// return start+1 -- the index of the next element
return (start+1);
}
/**
* remove and return 1st element
*/
Node pop() {
if (!list.isEmpty()) return (Node)list.remove(0);
else return null;
}
}
class Istream {
// fan encoding
boolean fan = false;
// length of the strip
int length = 0;
// array that specifies triangle strip
Vertex[] istream;
// indices of the head and tail vertices
int head, tail;
/**
* creates a new Istream to store the triangle strip
*/
Istream(Vertex[] list, int size, boolean isFan) {
if (size == 0) throw new RuntimeException("size is 0");
fan = isFan;
length = size;
istream = new Vertex[length];
int i;
System.arraycopy(list, 0, istream, 0, length);
}
/**
* adds a new vertex to the end of the stream
* makes the int array bigger, if necessary
*/
void append(Vertex vertex) {
growArray();
// add in new vertex
istream[length] = vertex;
length++;
}
/**
* turns the encoding (..., -3, -2, -1) into (.... -3, -2, -3, -1)
* so that zero-area triangle (-3, -2. -3) is added
*/
void swapEnd() {
growArray();
istream[length] = istream[length-1];
istream[length-1] = istream[length-3];
length++;
}
/**
* makes the array bigger, if necessary
*/
void growArray() {
if (length >= istream.length) {
Vertex[] old = istream;
// for now add enough space to add three more vertices
// may change this later
istream = new Vertex[length + 3];
System.arraycopy(old, 0, istream, 0, length);
}
}
/**
* inverts the istream
*/
void invert() {
Vertex[] tmp = new Vertex[istream.length];
// reverse the stream
for (int i = 0; i < length; i++) {
tmp[i] = istream[length - i - 1];
}
// copy it back
System.arraycopy(tmp, 0, istream, 0, istream.length);
tmp = null;
// swap the head and the tail
int swap = head;
head = tail;
tail = swap;
}
/**
* concats two streams into one big stream
*/
void addStream(Istream strm) {
// System.out.println("addStream");
int strmLen = strm.length;
int size = strmLen + length - 2;
// make the istream bigger
if (size >= istream.length) {
Vertex[] old = istream;
istream = new Vertex[size];
System.arraycopy(old, 0, istream, 0, length);
}
// add the strm to istream
System.arraycopy(strm.istream, 2, istream, length, strmLen-2);
tail = strm.tail;
length = size;
strm.length = 0;
strm.istream = null;
}
}
}