View Javadoc
1   package org.newdawn.slick.geom;
2   
3   
4   import javax.annotation.Nonnull;
5   import javax.annotation.Nullable;
6   
7   /**
8    * A second triangulator that seems slightly more robust
9    * 
10   * @author Online examples
11   */
12  public class NeatTriangulator implements Triangulator
13  {
14      /**
15       * 
16       */
17      private static final long serialVersionUID = 1L;
18  
19      /** The error factor */
20      private static final float EPSILON = 1E-006F;
21      
22      /** The x coordinates */
23      private float pointsX[];
24      /** The y coordiantes */
25      private float pointsY[];
26      /** The number of points that have been added */
27      private int numPoints;
28      /** The edges defines by triangulation */
29      private Edge edges[];
30      /** Voroni */
31      @Nullable
32      private int V[];
33      /** The number of edges found */
34      private int numEdges;
35      /** The triangles that have been found */
36      private Triangle triangles[];
37      /** The number of triangles found */
38      private int numTriangles;
39      /** The current offset */
40      private float offset = EPSILON;
41      
42      /**
43       * Create a new triangulator
44       */
45      public NeatTriangulator()
46      {
47          pointsX = new float[100];
48          pointsY = new float[100];
49          numPoints = 0;
50          edges = new Edge[100];
51          numEdges = 0;
52          triangles = new Triangle[100];
53          numTriangles = 0;
54      }
55  
56      /**
57       * Clear the triangulator status 
58       */
59      public void clear()
60      {
61          numPoints = 0;
62          numEdges = 0;
63          numTriangles = 0;
64      }
65      
66      /**
67       * Find an edge between two verts 
68       * 
69       * @param i The index of the first vert
70       * @param j The index of the second vert
71       * @return The index of the dge
72       */
73      private int findEdge(int i, int j)
74      {
75          int k;
76          int l;
77          if(i < j)
78          {
79              k = i;
80              l = j;
81          } else
82          {
83              k = j;
84              l = i;
85          }
86          for(int i1 = 0; i1 < numEdges; i1++)
87              if(edges[i1].v0 == k && edges[i1].v1 == l)
88                  return i1;
89  
90          return -1;
91      }
92  
93      /**
94       * Add a discovered edge
95       * 
96       * @param i The index of the first vert
97       * @param j The index of the second vert
98       * @param k The index of the spread vert
99       */
100     private void addEdge(int i, int j, int k)
101     {
102         int l1 = findEdge(i, j);
103         int j1;
104         int k1;
105         Edge edge;
106         if(l1 < 0)
107         {
108             if(numEdges == edges.length)
109             {
110                 Edge aedge[] = new Edge[edges.length * 2];
111                 System.arraycopy(edges, 0, aedge, 0, numEdges);
112                 edges = aedge;
113             }
114             j1 = -1;
115             k1 = -1;
116             l1 = numEdges++;
117             edge = edges[l1] = new Edge();
118         } else
119         {
120             edge = edges[l1];
121             j1 = edge.t0;
122             k1 = edge.t1;
123         }
124         int l;
125         int i1;
126         if(i < j)
127         {
128             l = i;
129             i1 = j;
130             j1 = k;
131         } else
132         {
133             l = j;
134             i1 = i;
135             k1 = k;
136         }
137         edge.v0 = l;
138         edge.v1 = i1;
139         edge.t0 = j1;
140         edge.t1 = k1;
141         edge.suspect = true;
142     }
143 
144     /**
145      * Mark an edge as either a suspect or not
146      * 
147      * @param i The index of the first vert
148      * @param j The index of the second vert
149      * @param flag True if the edge is a suspect
150      * @throws InternalException Indicates the edge didn't exist
151      */
152     void markSuspect(int i, int j, boolean flag) throws InternalException
153     {
154         int k;
155         if(0 > (k = findEdge(i, j)))
156         {
157             throw new InternalException("Attempt to mark unknown edge");
158         } else
159         {
160             edges[k].suspect = flag;
161         }
162     }
163 
164     /**
165      * Check if the point P is inside the triangle defined by
166      * the points A,B,C
167      *
168      * @param f Point A x-coordinate
169      * @param f1 Point A y-coordinate
170      * @param f2 Point B x-coordinate
171      * @param f3 Point B y-coordinate
172      * @param f4 Point C x-coordinate
173      * @param f5 Point C y-coordinate
174      * @param f6 Point P x-coordinate
175      * @param f7 Point P y-coordinate
176      * @return True if the point specified is within the triangle
177      */
178     private static boolean insideTriangle(float f, float f1, float f2, float f3, float f4, float f5, float f6, float f7)
179     {
180         float f8 = f4 - f2;
181         float f9 = f5 - f3;
182         float f10 = f - f4;
183         float f11 = f1 - f5;
184         float f12 = f2 - f;
185         float f13 = f3 - f1;
186         float f14 = f6 - f;
187         float f15 = f7 - f1;
188         float f16 = f6 - f2;
189         float f17 = f7 - f3;
190         float f18 = f6 - f4;
191         float f19 = f7 - f5;
192         float f22 = f8 * f17 - f9 * f16;
193         float f20 = f12 * f15 - f13 * f14;
194         float f21 = f10 * f19 - f11 * f18;
195         return f22 >= 0.0D && f21 >= 0.0D && f20 >= 0.0D;
196     }
197 
198     /**
199      * Cut a the contour and add a triangle into V to describe the
200      * location of the cut
201      *
202      * @param i The index of the first point
203      * @param j The index of the second point
204      * @param k The index of the third point
205      * @param l ?
206      * @return True if a triangle was found
207      */
208     private boolean snip(int i, int j, int k, int l)
209     {
210         float f = pointsX[V[i]];
211         float f1 = pointsY[V[i]];
212         float f2 = pointsX[V[j]];
213         float f3 = pointsY[V[j]];
214         float f4 = pointsX[V[k]];
215         float f5 = pointsY[V[k]];
216         if(1E-006F > (f2 - f) * (f5 - f1) - (f3 - f1) * (f4 - f))
217             return false;
218         for(int i1 = 0; i1 < l; i1++)
219             if(i1 != i && i1 != j && i1 != k)
220             {
221                 float f6 = pointsX[V[i1]];
222                 float f7 = pointsY[V[i1]];
223                 if(insideTriangle(f, f1, f2, f3, f4, f5, f6, f7))
224                     return false;
225             }
226 
227         return true;
228     }
229 
230     /**
231      * Get the area defined by the points
232      * 
233      * @return The area defined by the points
234      */
235     private float area()
236     {
237         float f = 0.0F;
238         int i = numPoints - 1;
239         for(int j = 0; j < numPoints;)
240         {
241             f += pointsX[i] * pointsY[j] - pointsY[i] * pointsX[j];
242             i = j++;
243         }
244 
245         return f * 0.5F;
246     }
247 
248     /**
249      * Perform simple triangulation
250      * 
251      * @throws InternalException Indicates a polygon that can't be triangulated
252      */
253     void basicTriangulation() throws InternalException
254     {
255         int i = numPoints;
256         if(i < 3)
257             return;
258         numEdges = 0;
259         numTriangles = 0;
260         V = new int[i];
261         
262         if(0.0D < area())
263         {
264             for(int k = 0; k < i; k++)
265                 V[k] = k;
266 
267         } else
268         {
269             for(int l = 0; l < i; l++)
270                 V[l] = numPoints - 1 - l;
271 
272         }
273         int k1 = 2 * i;
274         int i1 = i - 1;
275         while(i > 2) 
276         {
277             if(0 >= k1--) {
278                 throw new InternalException("Bad polygon");
279             }
280             
281             int j = i1;
282             if(i <= j)
283                 j = 0;
284             i1 = j + 1;
285             if(i <= i1)
286                 i1 = 0;
287             int j1 = i1 + 1;
288             if(i <= j1)
289                 j1 = 0;
290             if(snip(j, i1, j1, i))
291             {
292                 int l1 = V[j];
293                 int i2 = V[i1];
294                 int j2 = V[j1];
295                 if(numTriangles == triangles.length)
296                 {
297                     Triangle atriangle[] = new Triangle[triangles.length * 2];
298                     System.arraycopy(triangles, 0, atriangle, 0, numTriangles);
299                     triangles = atriangle;
300                 }
301                 triangles[numTriangles] = new Triangle(l1, i2, j2);
302                 addEdge(l1, i2, numTriangles);
303                 addEdge(i2, j2, numTriangles);
304                 addEdge(j2, l1, numTriangles);
305                 numTriangles++;
306                 int k2 = i1;
307                 for(int l2 = i1 + 1; l2 < i; l2++)
308                 {
309                     V[k2] = V[l2];
310                     k2++;
311                 }
312 
313                 i--;
314                 k1 = 2 * i;
315             }
316         }
317         V = null;
318     }
319 
320     /**
321      * Upate the triangles
322      */
323     public boolean triangulate()
324     {
325         try
326         {
327             basicTriangulation();
328             //optimize();
329             return true;
330         }
331         catch (InternalException e)
332         {
333             numEdges = 0;
334         }
335         return false;
336     }
337 
338     /** 
339      * Add a point to the polygon
340      */
341     public void addPolyPoint(float x, float y)
342     {
343         for (int i=0;i<numPoints;i++) {
344             if ((pointsX[i] == x) && (pointsY[i] == y)) {
345                 //return;
346                 y += offset;
347                 offset += EPSILON;
348             }
349         }
350 
351         if(numPoints == pointsX.length)
352         {
353             float af[] = new float[numPoints * 2];
354             System.arraycopy(pointsX, 0, af, 0, numPoints);
355             pointsX = af;
356             af = new float[numPoints * 2];
357             System.arraycopy(pointsY, 0, af, 0, numPoints);
358             pointsY = af;
359         }
360         
361         pointsX[numPoints] = x;
362         pointsY[numPoints] = y;
363         numPoints++;
364     }
365 
366     /**
367      * A single triangle
368      *
369      * @author Online Source
370      */
371     class Triangle
372     {
373         /** The vertices index */
374         final int[] v;
375 
376         /**
377          * Create a new triangle
378          * 
379          * @param i The index of vert 1
380          * @param j The index of vert 2
381          * @param k The index of vert 3
382          */
383         Triangle(int i, int j, int k)
384         {
385             v = new int[3];
386             v[0] = i;
387             v[1] = j;
388             v[2] = k;
389         }
390     }
391 
392     /**
393      * A single edge between two points
394      * 
395      * @author Online Source
396      */
397     class Edge
398     {
399         /** The start vert */
400         int v0;
401         /** The end vert */
402         int v1;
403         /** The start tangent vert */
404         int t0;
405         /** The end tangent vert */
406         int t1;
407         /** True if the edge is marked as a suspect */
408         boolean suspect;
409 
410         /**
411          * Create a new empty edge
412          */
413         Edge()
414         {
415             v0 = -1;
416             v1 = -1;
417             t0 = -1;
418             t1 = -1;
419         }
420     }
421     
422     /**
423      * A failure to triangulate, hidden from outside and handled
424      * 
425      * @author Online Source
426      */
427     class InternalException extends Exception {
428         /**
429          * 
430          */
431         private static final long serialVersionUID = 1L;
432 
433         /**
434          * Create an internal exception
435          *
436          * @param msg The message describing the exception
437          */
438         public InternalException(String msg) {
439             super(msg);
440         }
441     }
442 
443     /**
444      * @see org.newdawn.slick.geom.Triangulator#getTriangleCount()
445      */
446     public int getTriangleCount() {
447         return numTriangles;
448     }
449 
450     /**
451      * @see org.newdawn.slick.geom.Triangulator#getTrianglePoint(int, int)
452      */
453     @Nonnull
454     public float[] getTrianglePoint(int tri, int i) {
455         float xp = pointsX[triangles[tri].v[i]];
456         float yp = pointsY[triangles[tri].v[i]];
457 
458         return new float[] {xp,yp};
459     }
460 
461     /**
462      * @see org.newdawn.slick.geom.Triangulator#startHole()
463      */
464     public void startHole() {
465     }
466 }