/***********************************************************************
* mt4j Copyright (c) 2008 - 2009 C.Ruff, Fraunhofer-Gesellschaft All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
***********************************************************************/
package org.mt4j.util.math;
/**
* Class to check whether a series of vectors which belong to
* a polygon are convex or concave.
* <p>C code from the article
* "Testing the Convexity of a Polygon"
* by Peter Schorn and Frederick Fisher,
* (schorn@inf.ethz.ch, fred@kpc.com)
* in "Graphics Gems IV", Academic Press, 1994
* @author C.Ruff
*/
public class ConvexityUtil {
/** The Constant NotConvex. */
public static final int NotConvex = 0;
/** The Constant NotConvexDegenerate. */
public static final int NotConvexDegenerate = 1;
/** The Constant ConvexDegenerate. */
public static final int ConvexDegenerate = 2;
/** The Constant ConvexCCW. */
public static final int ConvexCCW = 3;
/** The Constant ConvexCW. */
public static final int ConvexCW = 4;
/*
* C code from the article
* "Testing the Convexity of a Polygon"
* by Peter Schorn and Frederick Fisher,
* (schorn@inf.ethz.ch, fred@kpc.com)
* in "Graphics Gems IV", Academic Press, 1994
*/
/* Reasonably Optimized Routine to Classify a Polygon's Shape */
/*
.. code omitted which reads polygon, stores in an array, and calls
classifyPolygon2()
*/
// typedef enum { NotConvex, NotConvexDegenerate,
// ConvexDegenerate, ConvexCCW, ConvexCW } PolygonClass;
//
// typedef double Number; /* float or double */
//
// #define ConvexCompare(delta) \
// ( (delta[0] > 0) ? -1 : /* x coord diff, second pt > first pt */\
// (delta[0] < 0) ? 1 : /* x coord diff, second pt < first pt */\
// (delta[1] > 0) ? -1 : /* x coord same, second pt > first pt */\
// (delta[1] < 0) ? 1 : /* x coord same, second pt > first pt */\
// 0 ) /* second pt equals first point */
/**
* Convex compare.
*
* @param delta the delta
*
* @return the int
*/
private static int ConvexCompare(Vector3D delta){
int returnval = (
(delta.x > 0) ? -1 : /* x coord diff, second pt > first pt */
(delta.x < 0) ? 1 : /* x coord diff, second pt < first pt */
(delta.y > 0) ? -1 : /* x coord same, second pt > first pt */
(delta.y < 0) ? 1 : /* x coord same, second pt > first pt */
0 );
return returnval;
}
/**
* Convex get point delta.
*
* @param delta the delta
* @param pprev the pprev
* @param pcur the pcur
*/
private static void ConvexGetPointDelta(Vector3D delta, Vector3D pprev, Vector3D pcur) {
pcur = pVert[iread++];
delta.x = pcur.x - pprev.x;
delta.y = pcur.y - pprev.y;
}
/**
* Convex cross.
*
* @param p the p
* @param q the q
*
* @return the float
*/
private static float ConvexCross(Vector3D p, Vector3D q){
return (p.x * q.y - p.y * q.x);
}
//
// #define ConvexCheckTriple
// if ( (thisDir = ConvexCompare(dcur)) == -curDir ) {
// ++dirChanges;
// /* The following line will optimize for polygons that are */
// /* not convex because of classification condition 4, */
// /* otherwise, this will only slow down the classification. */
// /* if ( dirChanges > 2 ) return NotConvex; */
// }
// curDir = thisDir;
// cross = ConvexCross(dprev, dcur);
// if ( cross > 0 ) { if ( angleSign == -1 ) return NotConvex;
// angleSign = 1;
// }
// else if (cross < 0) { if (angleSign == 1) return NotConvex;
// angleSign = -1;
// }
// pSecond = pThird; /* Remember ptr to current point. */
// dprev[0] = dcur[0]; /* Remember current delta. */
// dprev[1] = dcur[1];
/**
* Convex check triple.
*
* @return the int
*/
private static int ConvexCheckTriple(){
if ( (thisDir = ConvexCompare(dcur)) == -curDir ) {
++dirChanges;
// System.out.println("Dirchange! " + dirChanges);
/* The following line will optimize for polygons that are */
/* not convex because of classification condition 4, */
/* otherwise, this will only slow down the classification. */
/* if ( dirChanges > 2 ) return NotConvex; */
}
curDir = thisDir;
cross = ConvexCross(dprev, dcur);
if ( cross > 0 ) {
if ( angleSign == -1 ){
return NotConvex;
}
angleSign = 1;
} else if (cross < 0) {
if (angleSign == 1){
return NotConvex; //TODO ander smachen
// return NotConvexDegenerate;
}
angleSign = -1;
}
pSecond = pThird; /* Remember ptr to current point. */
dprev.x = dcur.x; /* Remember current delta. */
dprev.y = dcur.y;
return -1;
}
/** The nvert. */
private static int nvert;
/** The p vert. */
private static Vector3D[] pVert;
/** The iread. */
private static int
curDir,
thisDir,
dirChanges = 0,
angleSign = 0,
iread ;
/** The cross. */
private static float cross;
/** The dprev. */
private static Vector3D dprev = new Vector3D(0,0,0);
/** The dcur. */
private static Vector3D dcur = new Vector3D(0,0,0);
/** The p second. */
private static Vector3D pSecond = new Vector3D(0,0,0);
/** The p third. */
private static Vector3D pThird = new Vector3D(0,0,0);
/** The p save second. */
private static Vector3D pSaveSecond = new Vector3D(0,0,0);
/**
* Classify polygon2.
*
* @param vertCount the vert count
* @param pVerts the verts
*
* @return the int
*/
public static int classifyPolygon2(int vertCount, Vector3D[] pVerts){
nvert = vertCount;
pVert = Vector3D.getDeepVertexArrayCopy(pVerts);
// pVert = pVerts;
/* if ( nvert <= 0 ) return error; if you care */
/* Get different point, return if less than 3 diff points. */
if ( nvert < 3 )
return ConvexDegenerate;
iread = 1;
while ( true ) {
ConvexGetPointDelta(dprev, pVert[0], pSecond);
if ( dprev.x != 0 || dprev.y != 0)
break;
/* Check if out of points. Check here to avoid slowing down cases
* without repeated points.
*/
if ( iread >= nvert )
return ConvexDegenerate;
}
pSaveSecond = pSecond;
curDir = ConvexCompare(dprev); /* Find initial direction */
while ( iread < nvert ) {
/* Get different point, break if no more points */
ConvexGetPointDelta(dcur, pSecond, pThird );
if ( dcur.x == 0.0 && dcur.y == 0.0 )
continue;
if (ConvexCheckTriple() == NotConvex ){ /* Check current three points */
return NotConvex;
}
}
/* Must check for direction changes from last vertex back to first */
pThird = pVert[0]; /* Prepare for 'ConvexCheckTriple' */
dcur.x = pThird.x - pSecond.x;
dcur.y = pThird.y - pSecond.y;
if ( ConvexCompare(dcur) != 0) {
if (ConvexCheckTriple() == NotConvex ){ /* Check current three points */
return NotConvex;
}
}
/* and check for direction changes back to second vertex */
dcur.x = pSaveSecond.x - pSecond.x;
dcur.y = pSaveSecond.y - pSecond.y;
if (ConvexCheckTriple() == NotConvex ){ /* Don't care about 'pThird' now */
return NotConvex;
}
/* Decide on polygon type given accumulated status */
if ( dirChanges > 2 )
return (angleSign != 0 ) ? NotConvex : NotConvexDegenerate;
if ( angleSign > 0 )
return ConvexCCW;
if ( angleSign < 0 )
return ConvexCW;
return ConvexDegenerate;
}
}