VisClient/org/dronus/gl/Convexiser.java

Go to the documentation of this file.
00001 package org.dronus.gl;
00002 
00018 import java.util.*;
00019 
00020 import org.lwjgl.util.vector.Vector3f;
00021 
00022 public class Convexiser {
00023 
00024         
00025         // the current polygon to cut  
00026         List<Vector3f>    currentPoly;  
00027         int n; //number of verticies in current remaining polygon
00028         float px[], py[];  //2d projected contour coordinates
00029         boolean pConvex[]; //convexity flag for every point
00030         
00031         //store list of triangles already cut off
00032         List<List<Vector3f>> earPoly = new ArrayList<List<Vector3f>>();
00033 
00034         /* rebuild px, py coords arrays after each cut. 
00035          dirty: does a simple projection to an abitrary defined skew plane.
00036          the hope is that a polygon will never be orthogonal to this plane.
00037          if it where, numerical accuracy quickly melt down and vanish with 
00038          perfect perpendicularity. then the whole thing fails.
00039          the plane is chosen skew to safely work on all base planes (x,y,z) 
00040          with one of x,y,z constant.    
00041          */
00042         void updatePoints() {
00043                 n = currentPoly.size();
00044                 int i = 0;
00045                 for (Vector3f v : currentPoly) { //do skew projection
00046                         px[i] = v.x - v.y;
00047                         py[i] = -v.z + v.y;
00048                         i++;
00049                 }
00050         }
00051 
00058         public List<List<Vector3f>> convexise(List<Vector3f> polygon) {
00059 
00060                 currentPoly = new ArrayList<Vector3f>(polygon);
00061 
00062                 
00063                 /*allocate coordinate arrays. for performance, 
00064                  this isn't repeated on updatePoints, just
00065                  the last index is updated in npoints.*/
00066                 n = polygon.size();
00067                 px = new float[n];
00068                 py = new float[n];
00069                 pConvex = new boolean[n];
00070 
00071                 //initially fill px,py
00072                 updatePoints();
00073 
00074                 //if polygon contour isn't in clockwise order, reverse it.
00075                 if (!polygonClockwise()) {
00076                         Collections.reverse(currentPoly);
00077                         updatePoints();
00078                 }
00079                 
00080                 //cut ears till a convex polygon or triangle (allways convex) is remaining
00081                 while (n > 3) {  
00082                         classifyConvexity();
00083 //                      if(classifyConvexity()) break; //if poly is already convex, quit.
00084                         if (!cutOneEar()) break; //if cutting fails, quit. 
00085                 }
00086 
00087                 earPoly.add(currentPoly); //add the remaining convex poly to the output
00088                 return earPoly;
00089         }
00090 
00091         /* tests a polygon for clockwise definition by angle sum */
00092         boolean polygonClockwise() {
00093                 
00094                 
00095                 double convex_sum = 0;
00096 
00097                 for (int i = 0; i < n; i++) {
00098 
00099                         //get indicies of three neighbouring verticies
00100                         int i0 = (i + n - 1) % n, i1 = i, i2 = (i + 1) % n;
00101 
00102                         float   aa = ((px[i2] - px[i0]) * (px[i2] - px[i0])) + ((-py[i2] + py[i0]) * (-py[i2] + py[i0])),
00103                                         bb = ((px[i1] - px[i0]) * (px[i1] - px[i0])) + ((-py[i1] + py[i0]) * (-py[i1] + py[i0])),
00104                                         cc = ((px[i2] - px[i1]) * (px[i2] - px[i1])) + ((-py[i2] + py[i1]) * (-py[i2] + py[i1]));
00105 
00106                         double  b = Math.sqrt(bb),
00107                                         c = Math.sqrt(cc),
00108                                         theta = Math.acos((bb + cc - aa) / (2 * b * c));
00109 
00110                         if (convex(px[i0], py[i0], px[i1], py[i1], px[i2], py[i2])) {
00111                                 convex_sum += Math.PI - theta;
00112                         } else {
00113                                 convex_sum -= Math.PI - theta;
00114                         }
00115                 }
00116 
00117                 return (convex_sum >= (2 * Math.PI - .001));
00118                         
00119         }
00120 
00121         /* classify verticies by convexity, thus filling pConvex. 
00122          * @returns true if all points proved convex.
00123          * */
00124         boolean classifyConvexity() {
00125 
00126                 boolean convex = true;
00127 
00128                 for (int i = 0; i < n; i++) {
00129 
00130                         int i0 = (i + n - 1) % n, i1 = i, i2 = (i + 1) % n;
00131 
00132                         pConvex[i] = convex(px[i0], py[i0], px[i1], py[i1], px[i2], py[i2]);
00133                         convex &= pConvex[i];
00134                 }
00135                 
00136                 return convex;
00137         }
00138 
00139         /* returns true if point (x2, y2) is convex */
00140         boolean convex(float x1, float y1, float x2, float y2, float x3, float y3) {
00141                 return (area(x1, y1, x2, y2, x3, y3) < 0);
00142         }
00143 
00144         /* area: determines the double area of triangle formed by three points */
00145         float area(float x1, float y1, float x2, float y2, float x3, float y3) {
00146                 float area = 0;
00147 
00148                 area += x1 * (y3 - y2);
00149                 area += x2 * (y1 - y3);
00150                 area += x3 * (y2 - y1);
00151         
00152                 return area;
00153         }
00154 
00155         /*
00156          * true if the triangle formed by three points contains another vertex
00157          */
00158         boolean triangleContainsPoint(float x1, float y1, float x2, float y2, float x3, float y3) {
00159                 
00160                 for (int i = 0; i < n; i++) {
00161                         if (!pConvex[i] && (
00162                                    ((px[i] != x1) && (py[i] != y1))
00163                                 || ((px[i] != x2) && (py[i] != y2)) 
00164                                 || ((px[i] != x3) && (py[i] != y3))
00165                         )) {
00166 
00167                                 float   a1 = area(x1, y1, x2, y2, px[i], py[i]),
00168                                                 a2 = area(x2, y2, x3, y3, px[i], py[i]),
00169                                                 a3 = area(x3, y3, x1, y1, px[i], py[i]);
00170 
00171                                 if ((a1 > 0 && a2 > 0 && a3 > 0)
00172                                                 || (a1 < 0 && a2 < 0 && a3 < 0))
00173                                         return true;
00174                         }
00175                 }
00176                 return false;
00177         }
00178 
00179         /* cut away "ear" vertex with given index */
00180         void cutEar(int index) {
00181 
00182                 int[] ind = new int[]{(index + n - 1) % n, index, (index + 1) % n};
00183                 
00184                 List<Vector3f> p = new ArrayList<Vector3f>();
00185 
00186                 for (int i = 0; i < 3; i++)
00187                         p.add(currentPoly.get(ind[i]));
00188 
00189                 earPoly.add(p);
00190                 currentPoly.remove(index);
00191                 updatePoints();
00192         }
00193 
00194         /* cut away abitrary "ear" vertex, that is any polygon vertex building
00195          * a triangle with its neighbours not containing any other vertex. */
00196         boolean cutOneEar() {
00197                 for (int i=0; i < n; i++) {
00198                         if (pConvex[i]) { /* point is convex */
00199                                 
00200                                 //      get indicies of three neighbouring verticies
00201                                 int i0 = (i + n - 1) % n, i1 = i, i2 = (i + 1) % n;                             
00202                         
00203                                 if (!triangleContainsPoint(px[i0], py[i0], px[i1], py[i1], px[i2], py[i2])) {
00204                                         cutEar(i);
00205                                         return true; //reclassify before next cut
00206                                 }                       
00207                         }
00208                 }
00209                 return false;
00210                 //throw new RuntimeException("Convexiser: could not cut bad ear. ");
00211         }
00212 
00213 }

Generated on Tue Apr 7 17:57:19 2009 for visclient by  doxygen 1.5.1