@ -276,30 +276,45 @@ public class Face implements Comparator<Integer> {
List < Face > facesToTriangulate = new ArrayList < Face > ( Arrays . asList ( this . clone ( ) ) ) ;
while ( facesToTriangulate . size ( ) > 0 ) {
Face face = facesToTriangulate . remove ( 0 ) ;
int previousIndex1 = - 1 , previousIndex2 = - 1 , previousIndex3 = - 1 ;
while ( face . vertexCount ( ) > 0 ) {
indexes [ 0 ] = face . getIndex ( 0 ) ;
indexes [ 1 ] = face . findClosestVertex ( indexes [ 0 ] , - 1 ) ;
indexes [ 2 ] = face . findClosestVertex ( indexes [ 0 ] , indexes [ 1 ] ) ;
LOGGER . finer ( "Veryfying improper triangulation of the temporal mesh." ) ;
if ( indexes [ 0 ] < 0 | | indexes [ 1 ] < 0 | | indexes [ 2 ] < 0 ) {
throw new BlenderFileException ( "Unable to find two closest vertices while triangulating face in mesh: " + temporalMesh + "Please apply triangulation modifier in blender as a workaround and load again!" ) ;
}
if ( previousIndex1 = = indexes [ 0 ] & & previousIndex2 = = indexes [ 1 ] & & previousIndex3 = = indexes [ 2 ] ) {
throw new BlenderFileException ( "Infinite loop detected during triangulation of mesh: " + temporalMesh + "Please apply triangulation modifier in blender as a workaround and load again!" ) ;
}
previousIndex1 = indexes [ 0 ] ;
previousIndex2 = indexes [ 1 ] ;
previousIndex3 = indexes [ 2 ] ;
// two special cases will improve the computations speed
if ( face . getIndexes ( ) . size ( ) = = 3 ) {
triangulatedFaces . add ( face . getIndexes ( ) . clone ( ) ) ;
} else if ( face . getIndexes ( ) . size ( ) = = 4 ) {
// in case face has 4 verts we use the plain triangulation
indexes [ 0 ] = face . getIndex ( 0 ) ;
indexes [ 1 ] = face . getIndex ( 1 ) ;
indexes [ 2 ] = face . getIndex ( 2 ) ;
triangulatedFaces . add ( new IndexesLoop ( indexes ) ) ;
indexes [ 1 ] = face . getIndex ( 2 ) ;
indexes [ 2 ] = face . getIndex ( 3 ) ;
triangulatedFaces . add ( new IndexesLoop ( indexes ) ) ;
} else {
int previousIndex1 = - 1 , previousIndex2 = - 1 , previousIndex3 = - 1 ;
while ( face . vertexCount ( ) > 0 ) {
indexes [ 0 ] = face . getIndex ( 0 ) ;
indexes [ 1 ] = face . findClosestVertex ( indexes [ 0 ] , - 1 ) ;
indexes [ 2 ] = face . findClosestVertex ( indexes [ 0 ] , indexes [ 1 ] ) ;
LOGGER . finer ( "Veryfying improper triangulation of the temporal mesh." ) ;
if ( indexes [ 0 ] < 0 | | indexes [ 1 ] < 0 | | indexes [ 2 ] < 0 ) {
throw new BlenderFileException ( "Unable to find two closest vertices while triangulating face in mesh: " + temporalMesh + "Please apply triangulation modifier in blender as a workaround and load again!" ) ;
}
if ( previousIndex1 = = indexes [ 0 ] & & previousIndex2 = = indexes [ 1 ] & & previousIndex3 = = indexes [ 2 ] ) {
throw new BlenderFileException ( "Infinite loop detected during triangulation of mesh: " + temporalMesh + "Please apply triangulation modifier in blender as a workaround and load again!" ) ;
}
previousIndex1 = indexes [ 0 ] ;
previousIndex2 = indexes [ 1 ] ;
previousIndex3 = indexes [ 2 ] ;
Arrays . sort ( indexes , this ) ;
facesToTriangulate . addAll ( face . detachTriangle ( indexes ) ) ;
triangulatedFaces . add ( new IndexesLoop ( indexes ) ) ;
Arrays . sort ( indexes , this ) ;
facesToTriangulate . addAll ( face . detachTriangle ( indexes ) ) ;
triangulatedFaces . add ( new IndexesLoop ( indexes ) ) ;
}
}
}
} catch ( BlenderFileException e ) {
LOGGER . log ( Level . WARNING , "Errors occured during face triangulation: {0}. The face will be triangulated with the most direct algorithm, " + "but the results might not be identical to blender." , e . getLocalizedMessage ( ) ) ;
LOGGER . log ( Level . WARNING , "Errors occured during face triangulation: {0}. The face will be triangulated with the most direct algorithm, but the results might not be identical to blender." , e . getLocalizedMessage ( ) ) ;
indexes [ 0 ] = this . getIndex ( 0 ) ;
for ( int i = 1 ; i < this . vertexCount ( ) - 1 ; + + i ) {
indexes [ 1 ] = this . getIndex ( i ) ;
@ -308,7 +323,7 @@ public class Face implements Comparator<Integer> {
}
}
}
/ * *
* @return < b > true < / b > if the face is smooth and < b > false < / b > otherwise
* /
@ -335,17 +350,23 @@ public class Face implements Comparator<Integer> {
return "Face " + indexes ;
}
/ * *
* The method finds the closest vertex to the one specified by < b > index < / b > .
* If the vertexToIgnore is positive than it will be ignored in the result .
* The closes vertex must be able to create an edge that is fully contained within the face and does not cross
* any other edges .
* @param index
* the index of the vertex that needs to have found the nearest neighbour
* @param indexToIgnore
* the index to ignore in the result ( pass - 1 if none is to be ignored )
* @return the index of the closest vertex to the given one
* /
/ * *
* The method finds the closest vertex to the one specified by < b > index < / b > .
* If the vertexToIgnore is positive than it will be ignored in the result .
* The closest vertex must be able to create an edge that is fully contained
* within the face and does not cross any other edges . Also if the
* vertexToIgnore is not negative then the condition that the edge between
* the found index and the one to ignore is inside the face must also be
* met .
*
* @param index
* the index of the vertex that needs to have found the nearest
* neighbour
* @param indexToIgnore
* the index to ignore in the result ( pass - 1 if none is to be
* ignored )
* @return the index of the closest vertex to the given one
* /
private int findClosestVertex ( int index , int indexToIgnore ) {
int result = - 1 ;
List < Vector3f > vertices = temporalMesh . getVertices ( ) ;
@ -355,7 +376,7 @@ public class Face implements Comparator<Integer> {
if ( i ! = index & & i ! = indexToIgnore ) {
Vector3f v2 = vertices . get ( i ) ;
float d = v2 . distance ( v1 ) ;
if ( d < distance & & this . contains ( new Edge ( index , i , 0 , true , temporalMesh ) ) ) {
if ( d < distance & & this . contains ( new Edge ( index , i , 0 , true , temporalMesh ) ) & & ( indexToIgnore < 0 | | this . contains ( new Edge ( indexToIgnore , i , 0 , true , temporalMesh ) ) ) ) {
result = i ;
distance = d ;
}
@ -376,11 +397,9 @@ public class Face implements Comparator<Integer> {
int index2 = edge . getSecondIndex ( ) ;
// check if the line between the vertices is not a border edge of the face
if ( ! indexes . areNeighbours ( index1 , index2 ) ) {
List < Vector3f > vertices = temporalMesh . getVertices ( ) ;
for ( int i = 0 ; i < indexes . size ( ) ; + + i ) {
int i1 = this . getIndex ( i ) ;
int i2 = this . getIndex ( i + 1 ) ;
int i1 = this . getIndex ( i - 1 ) ;
int i2 = this . getIndex ( i ) ;
// check if the edges have no common verts (because if they do, they cannot cross)
if ( i1 ! = index1 & & i1 ! = index2 & & i2 ! = index1 & & i2 ! = index2 ) {
if ( edge . cross ( new Edge ( i1 , i2 , 0 , false , temporalMesh ) ) ) {
@ -389,35 +408,53 @@ public class Face implements Comparator<Integer> {
}
}
// the edge does NOT cross any of other edges, so now we need to verify if it is inside the face or outside
// we check it by comparing the angle that is created by vertices: [index1 - 1, index1, index1 + 1]
// with the one creaded by vertices: [index1 - 1, index1, index2]
// if the latter is greater than it means that the edge is outside the face
// IMPORTANT: we assume that all vertices are in one plane (this should be ensured before creating the Face)
int indexOfIndex1 = indexes . indexOf ( index1 ) ;
int indexMinus1 = this . getIndex ( indexOfIndex1 - 1 ) ; // indexOfIndex1 == 0 ? indexes.get(indexes.size() - 1) : indexes.get(indexOfIndex1 - 1);
int indexPlus1 = this . getIndex ( indexOfIndex1 + 1 ) ; // indexOfIndex1 == indexes.size() - 1 ? 0 : indexes.get(indexOfIndex1 + 1);
Vector3f edge1 = vertices . get ( indexMinus1 ) . subtract ( vertices . get ( index1 ) ) . normalizeLocal ( ) ;
Vector3f edge2 = vertices . get ( indexPlus1 ) . subtract ( vertices . get ( index1 ) ) . normalizeLocal ( ) ;
Vector3f newEdge = vertices . get ( index2 ) . subtract ( vertices . get ( index1 ) ) . normalizeLocal ( ) ;
// verify f the later computed angle is inside or outside the face
Vector3f direction1 = edge1 . cross ( edge2 ) . normalizeLocal ( ) ;
Vector3f direction2 = edge1 . cross ( newEdge ) . normalizeLocal ( ) ;
Vector3f normal = temporalMesh . getNormals ( ) . get ( index1 ) ;
boolean isAngle1Interior = normal . dot ( direction1 ) < 0 ;
boolean isAngle2Interior = normal . dot ( direction2 ) < 0 ;
float angle1 = isAngle1Interior ? edge1 . angleBetween ( edge2 ) : FastMath . TWO_PI - edge1 . angleBetween ( edge2 ) ;
float angle2 = isAngle2Interior ? edge1 . angleBetween ( newEdge ) : FastMath . TWO_PI - edge1 . angleBetween ( newEdge ) ;
return angle1 > = angle2 ;
// computing the edge's middle point
Vector3f edgeMiddlePoint = edge . computeCentroid ( ) ;
// computing the edge that is perpendicular to the given edge and has a length of 1 (length actually does not matter)
Vector3f edgeVector = edge . getSecondVertex ( ) . subtract ( edge . getFirstVertex ( ) ) ;
Vector3f edgeNormal = temporalMesh . getNormals ( ) . get ( index1 ) . cross ( edgeVector ) . normalizeLocal ( ) ;
Edge e = new Edge ( edgeMiddlePoint , edgeNormal . add ( edgeMiddlePoint ) ) ;
// compute the vectors from the middle point to the crossing between the extended edge 'e' and other edges of the face
List < Vector3f > crossingVectors = new ArrayList < Vector3f > ( ) ;
for ( int i = 0 ; i < indexes . size ( ) ; + + i ) {
int i1 = this . getIndex ( i ) ;
int i2 = this . getIndex ( i + 1 ) ;
Vector3f crossPoint = e . getCrossPoint ( new Edge ( i1 , i2 , 0 , false , temporalMesh ) , true , false ) ;
if ( crossPoint ! = null ) {
crossingVectors . add ( crossPoint . subtractLocal ( edgeMiddlePoint ) ) ;
}
}
if ( crossingVectors . size ( ) = = 0 ) {
return false ; // edges do not cross
}
// use only distinct vertices (doubles may appear if the crossing point is a vertex)
List < Vector3f > distinctCrossingVectors = new ArrayList < Vector3f > ( ) ;
for ( Vector3f cv : crossingVectors ) {
double minDistance = Double . MAX_VALUE ;
for ( Vector3f dcv : distinctCrossingVectors ) {
minDistance = Math . min ( minDistance , dcv . distance ( cv ) ) ;
}
if ( minDistance > FastMath . FLT_EPSILON ) {
distinctCrossingVectors . add ( cv ) ;
}
}
if ( distinctCrossingVectors . size ( ) = = 0 ) {
throw new IllegalStateException ( "There MUST be at least 2 crossing vertices!" ) ;
}
// checking if all crossing vectors point to the same direction (if yes then the edge is outside the face)
float direction = Math . signum ( distinctCrossingVectors . get ( 0 ) . dot ( edgeNormal ) ) ; // if at least one vector has different direction that this - it means that the edge is inside the face
for ( int i = 1 ; i < distinctCrossingVectors . size ( ) ; + + i ) {
if ( direction ! = Math . signum ( distinctCrossingVectors . get ( i ) . dot ( edgeNormal ) ) ) {
return true ;
}
}
return false ;
}
return true ;
}
@Override
public int hashCode ( ) {
final int prime = 31 ;