OSDN Git Service

[fixed] Issue 39
authorbadlogicgames <badlogicgames@6c4fd544-2939-11df-bb46-9574ba5d0bfa>
Wed, 22 Dec 2010 14:44:27 +0000 (14:44 +0000)
committerbadlogicgames <badlogicgames@6c4fd544-2939-11df-bb46-9574ba5d0bfa>
Wed, 22 Dec 2010 14:44:27 +0000 (14:44 +0000)
[added] patch by HexWave to include raycasting in the box2d wrapper.

gdx/jni/Box2D/World.cpp
gdx/jni/Box2D/World.h
gdx/src/com/badlogic/gdx/math/EarCutTriangulator.java
gdx/src/com/badlogic/gdx/physics/box2d/World.java

index 399b71c..bf4ecf0 100644 (file)
@@ -21,6 +21,27 @@ static jmethodID shouldCollideID = 0;
 static jmethodID beginContactID = 0;\r
 static jmethodID endContactID = 0;\r
 static jmethodID reportFixtureID = 0;\r
+static jmethodID reportRayFixtureID = 0;\r
+\r
+class CustomRayCastCallback: public b2RayCastCallback\r
+{\r
+private:\r
+       JNIEnv* env;\r
+       jobject obj;\r
+\r
+public:\r
+       CustomRayCastCallback( JNIEnv *env, jobject obj )\r
+       {\r
+               this->env = env;\r
+               this->obj = obj;\r
+       }\r
+\r
+       virtual float32 ReportFixture( b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float32 fraction)\r
+       {\r
+               return env->CallFloatMethod(obj, reportRayFixtureID, (jlong)fixture, (jfloat)point.x, (jfloat)point.y,\r
+                                                                                                                               (jfloat)normal.x, (jfloat)normal.y, (jfloat)fraction );\r
+       }\r
+};\r
 \r
 class CustomContactFilter: public b2ContactFilter\r
 {\r
@@ -103,6 +124,7 @@ JNIEXPORT jlong JNICALL Java_com_badlogic_gdx_physics_box2d_World_newWorld
        beginContactID = env->GetMethodID(worldClass, "beginContact", "(J)V" );\r
        endContactID = env->GetMethodID( worldClass, "endContact", "(J)V" );\r
        reportFixtureID = env->GetMethodID(worldClass, "reportFixture", "(J)Z" );\r
+       reportRayFixtureID = env->GetMethodID(worldClass, "reportRayFixture", "(JFFFFF)F" );\r
        shouldCollideID = env->GetMethodID( worldClass, "contactFilter", "(JJ)Z");\r
 \r
        b2World* world = new b2World( b2Vec2( gravityX, gravityY ), doSleep );\r
@@ -654,3 +676,17 @@ JNIEXPORT void JNICALL Java_com_badlogic_gdx_physics_box2d_World_jniDispose
        b2World* world = (b2World*)(addr);\r
        delete world;\r
 }\r
+\r
+/*\r
+ * Class:                      com_badlogic_gdx_physics_box2d_World\r
+ * Method:             jniRayCast\r
+ * Signature:  (JFFFF)V\r
+ */\r
+JNIEXPORT void JNICALL Java_com_badlogic_gdx_physics_box2d_World_jniRayCast\r
+       (JNIEnv *env, jobject obj, jlong addr, jfloat aX, jfloat aY, jfloat bX, jfloat bY)\r
+{\r
+       b2World *world = (b2World*)addr;\r
+\r
+       CustomRayCastCallback callback( env, obj );     \r
+       world->RayCast( &callback, b2Vec2(aX,aY), b2Vec2(bX,bY) );\r
+}
\ No newline at end of file
index d64af0e..3059b8a 100644 (file)
@@ -254,6 +254,14 @@ JNIEXPORT void JNICALL Java_com_badlogic_gdx_physics_box2d_World_jniGetContactLi
 JNIEXPORT void JNICALL Java_com_badlogic_gdx_physics_box2d_World_jniDispose\r
   (JNIEnv *, jobject, jlong);\r
 \r
+/*\r
+ * Class:     com_badlogic_gdx_physics_box2d_World\r
+ * Method:    jniQueryAABB\r
+ * Signature: (JFFFF)V\r
+ */\r
+JNIEXPORT void JNICALL Java_com_badlogic_gdx_physics_box2d_World_jniRayCast\r
+  (JNIEnv *, jobject, jlong, jfloat, jfloat, jfloat, jfloat);\r
+\r
 #ifdef __cplusplus\r
 }\r
 #endif\r
index c90a3c7..9fabf96 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com)\r
+ * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com), Nicolas Gramlich\r
  * \r
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the\r
  * License. You may obtain a copy of the License at\r
@@ -16,282 +16,231 @@ package com.badlogic.gdx.math;
 import java.util.ArrayList;\r
 import java.util.Collections;\r
 import java.util.List;\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.List;\r
+\r
+import com.badlogic.gdx.math.Vector2;\r
 \r
 /**\r
- * A simple implementation of the ear cutting algorithm to triangulate simple polygons without holes. For more information see\r
+ * A simple implementation of the ear cutting algorithm to triangulate simple\r
+ * polygons without holes. For more information:\r
  * http://cgm.cs.mcgill.ca/~godfried/teaching/cg-projects/97/Ian/algorithm2.html\r
- * @author badlogicgames@gmail.com\r
+ * http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf\r
  * \r
+ * @author badlogicgames@gmail.com\r
+ * @author Nicolas Gramlich (Improved performance. Collinear edges are now supported.)\r
  */\r
 public final class EarCutTriangulator {\r
-       /**\r
-        * Triangulates the list of points and returns an array of {@link Vector3} triples that each form a single triangle.\r
-        * \r
-        * @param polygon The polygon to triangulate\r
-        * @return The list of triangle vertices.\r
-        */\r
-       public List<Vector2> triangulate (List<Vector2> polygon) {\r
-               List<Vector2> triangles = new ArrayList<Vector2>();\r
-               List<Vector2> tmp = new ArrayList<Vector2>(polygon.size());\r
-               tmp.addAll(polygon);\r
-               polygon = tmp;\r
-\r
-               if (polygon.size() == 3) {\r
-                       triangles.addAll(polygon);\r
-                       return triangles;\r
-               }\r
 \r
-               while (polygon.size() >= 3) {\r
-                       int ptType[] = classifyPoints(polygon);\r
+       private static final int CONCAVE = 1;\r
+       private static final int CONVEX = -1;\r
 \r
-                       for (int i = 0; i < polygon.size(); i++) {\r
-                               float x1 = polygon.get(i == 0 ? polygon.size() - 1 : i - 1).x;\r
-                               float y1 = polygon.get(i == 0 ? polygon.size() - 1 : i - 1).y;\r
-                               float x2 = polygon.get(i).x;\r
-                               float y2 = polygon.get(i).y;\r
-                               float x3 = polygon.get(i == polygon.size() - 1 ? 0 : i + 1).x;\r
-                               float y3 = polygon.get(i == polygon.size() - 1 ? 0 : i + 1).y;\r
+       private int concaveVertexCount;\r
 \r
-                               if (ear(polygon, ptType, x1, y1, x2, y2, x3, y3)) {\r
+       public List<Vector2> computeTriangles(final List<Vector2> pVertices) {\r
+               // TODO Check if LinkedList performs better\r
+               final ArrayList<Vector2> triangles = new ArrayList<Vector2>();\r
+               final ArrayList<Vector2> vertices = new ArrayList<Vector2>(pVertices.size());\r
+               vertices.addAll(pVertices);\r
 \r
-                                       cutEar(polygon, triangles, i);\r
-                                       updatePolygon(polygon, i);\r
+               if(vertices.size() == 3) {\r
+                       triangles.addAll(vertices);\r
+                       return triangles;\r
+               }\r
+\r
+               while(vertices.size() >= 3) {\r
+                       // TODO Usually(Always?) only the Types of the vertices next to the ear change! --> Improve\r
+                       final int vertexTypes[] = this.classifyVertices(vertices);\r
+\r
+                       final int vertexCount = vertices.size();\r
+                       for(int index = 0; index < vertexCount; index++) {\r
+                               if(this.isEarTip(vertices, index, vertexTypes)) {\r
+                                       this.cutEarTip(vertices, index, triangles);\r
                                        break;\r
                                }\r
                        }\r
                }\r
 \r
-// if( polygon.size() == 3 )\r
-// {\r
-// triangles.add( polygon.get(0) );\r
-// triangles.add( polygon.get(1) );\r
-// triangles.add( polygon.get(2) );\r
-// }\r
-\r
                return triangles;\r
        }\r
+       \r
+       private static boolean areVerticesClockwise(final ArrayList<Vector2> pVertices) {\r
+               final int vertexCount = pVertices.size();\r
 \r
-       /*\r
-        * polygonClockwise: Returns true if user inputted polygon in clockwise order, false if counterclockwise. The Law of Cosines is\r
-        * used to determine the angle.\r
-        */\r
-       public boolean polygonClockwise (List<Vector2> polygon) {\r
                float area = 0;\r
-               for (int i = 0; i < polygon.size(); i++) {\r
-                       Vector2 p1 = polygon.get(i);\r
-                       Vector2 p2 = polygon.get(i == polygon.size() - 1 ? 0 : i + 1);\r
+               for(int i = 0; i < vertexCount; i++) {\r
+                       final Vector2 p1 = pVertices.get(i);\r
+                       final Vector2 p2 = pVertices.get(EarCutTriangulator.computeNextIndex(pVertices, i));\r
                        area += p1.x * p2.y - p2.x * p1.y;\r
                }\r
 \r
-               if (area < 0)\r
+               if(area < 0) {\r
                        return true;\r
-               else\r
+               } else {\r
                        return false;\r
-\r
-// float aa, bb, cc, b, c, theta;\r
-// float convex_turn;\r
-// float convex_sum = 0;\r
-//\r
-// for (int i = 0; i < polygon.size() - 2; i++) {\r
-// aa = ((polygon.get(i+2).getX() - polygon.get(i).getX()) * (polygon.get(i+2).getX() - polygon.get(i).getX())) +\r
-// ((-polygon.get(i+2).getY() + polygon.get(i).getY()) * (-polygon.get(i+2).getY() + polygon.get(i).getY()));\r
-//\r
-// bb = ((polygon.get(i+1).getX() - polygon.get(i).getX()) * (polygon.get(i+1).getX() - polygon.get(i).getX()) +\r
-// ((-polygon.get(i+1).getY() + polygon.get(i).getY()) * (-polygon.get(i+1).getY() + polygon.get(i).getY())));\r
-//\r
-// cc = ((polygon.get(i+2).getX() - polygon.get(i+1).getX()) *\r
-// (polygon.get(i+2).getX() - polygon.get(i+1).getX())) +\r
-// ((-polygon.get(i+2).getY() + polygon.get(i+1).getY()) *\r
-// (-polygon.get(i+2).getY() + polygon.get(i+1).getY()));\r
-//\r
-// b = (float)Math.sqrt(bb);\r
-// c = (float)Math.sqrt(cc);\r
-// theta = (float)Math.acos((bb + cc - aa) / (2 * b * c));\r
-//\r
-// if (convex(polygon.get(i).getX(), polygon.get(i).getY(),\r
-// polygon.get(i+1).getX(), polygon.get(i+1).getY(),\r
-// polygon.get(i+2).getX(), polygon.get(i+2).getY())) {\r
-// convex_turn = (float)Math.PI - theta;\r
-// convex_sum += convex_turn;\r
-// }\r
-// else {\r
-// convex_sum -= Math.PI - theta;\r
-// }\r
-// }\r
-// aa = ((polygon.get(1).getX() - polygon.get(polygon.size()-1).getX()) *\r
-// (polygon.get(1).getX() - polygon.get(polygon.size()-1).getX())) +\r
-// ((-polygon.get(1).getY() + polygon.get(polygon.size()-1).getY()) *\r
-// (-polygon.get(1).getY() + polygon.get(polygon.size()-1).getY()));\r
-//\r
-// bb = ((polygon.get(0).getX() - polygon.get(polygon.size()-1).getX()) *\r
-// (polygon.get(0).getX() - polygon.get(polygon.size()-1).getX())) +\r
-// ((-polygon.get(0).getY() + polygon.get(polygon.size()-1).getY()) *\r
-// (-polygon.get(0).getY() + polygon.get(polygon.size()-1).getY()));\r
-//\r
-// cc = ((polygon.get(1).getX() - polygon.get(0).getX()) *\r
-// (polygon.get(1).getX() - polygon.get(0).getX())) +\r
-// ((-polygon.get(1).getY() + polygon.get(0).getY()) *\r
-// (-polygon.get(1).getY() + polygon.get(0).getY()));\r
-//\r
-// b = (float)Math.sqrt(bb);\r
-// c = (float)Math.sqrt(cc);\r
-// theta = (float)Math.acos((bb + cc - aa) / (2 * b * c));\r
-//\r
-// if (convex(polygon.get(polygon.size()-1).getX(), polygon.get(polygon.size()-1).getY(),\r
-// polygon.get(0).getX(), polygon.get(0).getY(),\r
-// polygon.get(1).getX(), polygon.get(1).getY())) {\r
-// convex_turn = (float)Math.PI - theta;\r
-// convex_sum += convex_turn;\r
-// }\r
-// else {\r
-// convex_sum -= Math.PI - theta;\r
-// }\r
-//\r
-// if (convex_sum >= (2 * 3.14159))\r
-// return true;\r
-// else\r
-// return false;\r
+               }\r
        }\r
 \r
-       /*\r
-        * classifyPoints: Classifies points as "convex" or "concave". Convex points are represented as a "1" in the ptType array;\r
-        * concave points are represented as a "-1" in the array.\r
+       /**\r
+        * @param pVertices\r
+        * @return An array of length <code>pVertices.size()</code> filled with either {@link EarCutTriangulator#CONCAVE} or\r
+        * {@link EarCutTriangulator#CONVEX}.\r
         */\r
-       int concaveCount = 0;\r
-\r
-       int[] classifyPoints (List<Vector2> polygon) {\r
-               int[] ptType = new int[polygon.size()];\r
-               concaveCount = 0;\r
-\r
-               /*\r
-                * Before cutting any ears, we must determine if the polygon was inputted in clockwise order or not, since the algorithm for\r
-                * cutting ears assumes that the polygon's points are in clockwise order. If the points are in counterclockwise order, they\r
-                * are simply reversed in the array.\r
-                */\r
-               if (!polygonClockwise(polygon)) {\r
-                       Collections.reverse(polygon);\r
+       private int[] classifyVertices(final ArrayList<Vector2> pVertices) {\r
+               final int vertexCount = pVertices.size();\r
+\r
+               final int[] vertexTypes = new int[vertexCount];\r
+               this.concaveVertexCount = 0;\r
+\r
+               /* Ensure vertices are in clockwise order. */\r
+               if(!EarCutTriangulator.areVerticesClockwise(pVertices)) {\r
+                       Collections.reverse(pVertices);\r
                }\r
 \r
-               for (int i = 0; i < polygon.size(); i++) {\r
-                       if (i == 0) {\r
-                               if (convex(polygon.get(polygon.size() - 1).x, polygon.get(polygon.size() - 1).y, polygon.get(i).x, polygon.get(i).y,\r
-                                       polygon.get(i + 1).x, polygon.get(i + 1).y)) {\r
-                                       ptType[i] = 1; /* point is convex */\r
-                               } else {\r
-                                       ptType[i] = -1; /* point is concave */\r
-                                       concaveCount++;\r
-                               }\r
-                       } else if (i == polygon.size() - 1) {\r
-                               if (convex(polygon.get(i - 1).x, polygon.get(i - 1).y, polygon.get(i).x, polygon.get(i).y, polygon.get(0).x,\r
-                                       polygon.get(0).y)) {\r
-                                       ptType[i] = 1; /* point is convex */\r
-                               } else {\r
-                                       ptType[i] = -1; /* point is concave */\r
-                                       concaveCount++;\r
-                               }\r
-                       } else { /* i > 0 */\r
-                               if (convex(polygon.get(i - 1).x, polygon.get(i - 1).y, polygon.get(i).x, polygon.get(i).y, polygon.get(i + 1).x,\r
-                                       polygon.get(i + 1).y)) {\r
-                                       ptType[i] = 1; /* point is convex */\r
-                               } else {\r
-                                       ptType[i] = -1; /* point is concave */\r
-                                       concaveCount++;\r
-                               }\r
+               for(int index = 0; index < vertexCount; index++) {\r
+                       final int previousIndex = EarCutTriangulator.computePreviousIndex(pVertices, index);\r
+                       final int nextIndex = EarCutTriangulator.computeNextIndex(pVertices, index);\r
+\r
+                       final Vector2 previousVertex = pVertices.get(previousIndex);\r
+                       final Vector2 currentVertex = pVertices.get(index);\r
+                       final Vector2 nextVertex = pVertices.get(nextIndex);\r
+\r
+                       if(EarCutTriangulator.isTriangleConvex(previousVertex.x, previousVertex.y, currentVertex.x, currentVertex.y, nextVertex.x, nextVertex.y)) {\r
+                               vertexTypes[index] = CONVEX;\r
+                       } else {\r
+                               vertexTypes[index] = CONCAVE;\r
+                               this.concaveVertexCount++;\r
                        }\r
                }\r
 \r
-               return ptType;\r
+               return vertexTypes;\r
        }\r
 \r
-       /*\r
-        * convex: returns true if point (x2, y2) is convex\r
-        */\r
-       boolean convex (float x1, float y1, float x2, float y2, float x3, float y3) {\r
-               if (area(x1, y1, x2, y2, x3, y3) < 0)\r
+       private static boolean isTriangleConvex(final float pX1, final float pY1, final float pX2, final float pY2, final float pX3, final float pY3) {\r
+               if(EarCutTriangulator.computeSpannedAreaSign(pX1, pY1, pX2, pY2, pX3, pY3) < 0) {\r
                        return false;\r
-               else\r
+               } else {\r
                        return true;\r
+               }\r
        }\r
 \r
-       /*\r
-        * area: determines area of triangle formed by three points\r
-        */\r
-       float area (float x1, float y1, float x2, float y2, float x3, float y3) {\r
-               float areaSum = 0;\r
-\r
-               areaSum += x1 * (y3 - y2);\r
-               areaSum += x2 * (y1 - y3);\r
-               areaSum += x3 * (y2 - y1);\r
+       private static int computeSpannedAreaSign(final float pX1, final float pY1, final float pX2, final float pY2, final float pX3, final float pY3) {\r
+               float area = 0;\r
 \r
-               /*\r
-                * for actual area, we need to multiple areaSum * 0.5, but we are only interested in the sign of the area (+/-)\r
-                */\r
+               area += pX1 * (pY3 - pY2);\r
+               area += pX2 * (pY1 - pY3);\r
+               area += pX3 * (pY2 - pY1);\r
 \r
-               return areaSum;\r
+               return (int)Math.signum(area);\r
        }\r
 \r
-       /*\r
-        * triangleContainsPoints: returns true if the triangle formed by three points contains another point\r
+       /**\r
+        * @return <code>true</code> when the Triangles contains one or more vertices, <code>false</code> otherwise.\r
         */\r
-       boolean triangleContainsPoint (List<Vector2> polygon, int[] ptType, float x1, float y1, float x2, float y2, float x3, float y3) {\r
+       private static boolean isAnyVertexInTriangle(final ArrayList<Vector2> pVertices, final int[] pVertexTypes, final float pX1, final float pY1, final float pX2, final float pY2, final float pX3, final float pY3) {\r
                int i = 0;\r
-               float area1, area2, area3;\r
-               boolean noPointInTriangle = true;\r
-\r
-               while ((i < polygon.size() - 1) && (noPointInTriangle)) {\r
-                       if ((ptType[i] == -1) /* point is concave */\r
-                               && (((polygon.get(i).x != x1) && (polygon.get(i).y != y1)) || ((polygon.get(i).x != x2) && (polygon.get(i).y != y2)) || ((polygon\r
-                                       .get(i).x != x3) && (polygon.get(i).y != y3)))) {\r
 \r
-                               area1 = area(x1, y1, x2, y2, polygon.get(i).x, polygon.get(i).y);\r
-                               area2 = area(x2, y2, x3, y3, polygon.get(i).x, polygon.get(i).y);\r
-                               area3 = area(x3, y3, x1, y1, polygon.get(i).x, polygon.get(i).y);\r
-\r
-                               if (area1 > 0) if ((area2 > 0) && (area3 > 0)) noPointInTriangle = false;\r
-                               if (area1 <= 0) if ((area2 <= 0) && (area3 <= 0)) noPointInTriangle = false;\r
+               final int vertexCount = pVertices.size();\r
+               while(i < vertexCount - 1) {\r
+                       if((pVertexTypes[i] == CONCAVE)) {\r
+                               final Vector2 currentVertex = pVertices.get(i);\r
+\r
+                               final float currentVertexX = currentVertex.x;\r
+                               final float currentVertexY = currentVertex.y;\r
+\r
+                               /* TODO The following condition fails for perpendicular, axis aligned triangles! \r
+                                * Removing it doesn't seem to cause problems. \r
+                                * Maybe it was an optimization?\r
+                                * Maybe it tried to handle collinear pieces ? */\r
+//                             if(((currentVertexX != pX1) && (currentVertexY != pY1)) || ((currentVertexX != pX2) && (currentVertexY != pY2)) || ((currentVertexX != pX3) && (currentVertexY != pY3))) {\r
+                                       final int areaSign1 = EarCutTriangulator.computeSpannedAreaSign(pX1, pY1, pX2, pY2, currentVertexX, currentVertexY);\r
+                                       final int areaSign2 = EarCutTriangulator.computeSpannedAreaSign(pX2, pY2, pX3, pY3, currentVertexX, currentVertexY);\r
+                                       final int areaSign3 = EarCutTriangulator.computeSpannedAreaSign(pX3, pY3, pX1, pY1, currentVertexX, currentVertexY);\r
+\r
+                                       if(areaSign1 > 0 && areaSign2 > 0 && areaSign3 > 0) {\r
+                                               return true;\r
+                                       } else if(areaSign1 <= 0 && areaSign2 <= 0 && areaSign3 <= 0) {\r
+                                               return true;\r
+                                       }\r
+//                             }\r
                        }\r
                        i++;\r
                }\r
-               return !noPointInTriangle;\r
+               return false;\r
        }\r
 \r
-       /*\r
-        * ear: returns true if the point (x2, y2) is an ear, false otherwise\r
-        */\r
-       boolean ear (List<Vector2> polygon, int[] ptType, float x1, float y1, float x2, float y2, float x3, float y3) {\r
-               if (concaveCount != 0)\r
-                       if (triangleContainsPoint(polygon, ptType, x1, y1, x2, y2, x3, y3))\r
+       private boolean isEarTip(final ArrayList<Vector2> pVertices, final int pEarTipIndex, final int[] pVertexTypes) {\r
+               if(this.concaveVertexCount != 0) {\r
+                       final Vector2 previousVertex = pVertices.get(EarCutTriangulator.computePreviousIndex(pVertices, pEarTipIndex));\r
+                       final Vector2 currentVertex = pVertices.get(pEarTipIndex);\r
+                       final Vector2 nextVertex = pVertices.get(EarCutTriangulator.computeNextIndex(pVertices, pEarTipIndex));\r
+\r
+                       if(EarCutTriangulator.isAnyVertexInTriangle(pVertices, pVertexTypes, previousVertex.x, previousVertex.y, currentVertex.x, currentVertex.y, nextVertex.x, nextVertex.y)) {\r
                                return false;\r
-                       else\r
+                       } else {\r
                                return true;\r
-               else\r
+                       }\r
+               } else {\r
                        return true;\r
+               }\r
        }\r
 \r
-       /*\r
-        * cutEar: creates triangle that represents ear for graphics purposes\r
-        */\r
-       void cutEar (List<Vector2> polygon, List<Vector2> triangles, int index) {\r
-               if (index == 0) {\r
-                       triangles.add(new Vector2(polygon.get(polygon.size() - 1)));\r
-                       triangles.add(new Vector2(polygon.get(index)));\r
-                       triangles.add(new Vector2(polygon.get(index + 1)));\r
-               } else if ((index > 0) && (index < polygon.size() - 1)) {\r
-                       triangles.add(new Vector2(polygon.get(index - 1)));\r
-                       triangles.add(new Vector2(polygon.get(index)));\r
-                       triangles.add(new Vector2(polygon.get(index + 1)));\r
-               } else if (index == polygon.size() - 1) {\r
-                       triangles.add(new Vector2(polygon.get(index - 1)));\r
-                       triangles.add(new Vector2(polygon.get(index)));\r
-                       triangles.add(new Vector2(polygon.get(0)));\r
+       private void cutEarTip(final ArrayList<Vector2> pVertices, final int pEarTipIndex, final ArrayList<Vector2> pTriangles) {\r
+               final int previousIndex = EarCutTriangulator.computePreviousIndex(pVertices, pEarTipIndex);\r
+               final int nextIndex = EarCutTriangulator.computeNextIndex(pVertices, pEarTipIndex);\r
+\r
+               if(!EarCutTriangulator.isCollinear(pVertices, previousIndex, pEarTipIndex, nextIndex)) {\r
+                       pTriangles.add(new Vector2(pVertices.get(previousIndex)));\r
+                       pTriangles.add(new Vector2(pVertices.get(pEarTipIndex)));\r
+                       pTriangles.add(new Vector2(pVertices.get(nextIndex)));\r
+               }\r
+\r
+               pVertices.remove(pEarTipIndex);\r
+               if(pVertices.size() >= 3) {\r
+                       EarCutTriangulator.removeCollinearNeighborEarsAfterRemovingEarTip(pVertices, pEarTipIndex);\r
                }\r
        }\r
 \r
-       /*\r
-        * updatePolygon: creates new polygon without the ear that was cut\r
-        */\r
-       void updatePolygon (List<Vector2> polygon, int index) {\r
-               polygon.remove(index);\r
+       private static void removeCollinearNeighborEarsAfterRemovingEarTip(final ArrayList<Vector2> pVertices, final int pEarTipCutIndex) {\r
+               final int collinearityCheckNextIndex = pEarTipCutIndex % pVertices.size();\r
+               int collinearCheckPreviousIndex = EarCutTriangulator.computePreviousIndex(pVertices, collinearityCheckNextIndex);\r
+\r
+               if(EarCutTriangulator.isCollinear(pVertices, collinearityCheckNextIndex)) {\r
+                       pVertices.remove(collinearityCheckNextIndex);\r
+\r
+                       if(pVertices.size() > 3) {\r
+                               /* Update */\r
+                               collinearCheckPreviousIndex = EarCutTriangulator.computePreviousIndex(pVertices, collinearityCheckNextIndex);\r
+                               if(EarCutTriangulator.isCollinear(pVertices, collinearCheckPreviousIndex)){\r
+                                       pVertices.remove(collinearCheckPreviousIndex);\r
+                               }\r
+                       }\r
+               } else if(EarCutTriangulator.isCollinear(pVertices, collinearCheckPreviousIndex)){\r
+                       pVertices.remove(collinearCheckPreviousIndex);\r
+               }\r
+       }\r
+\r
+       private static boolean isCollinear(final ArrayList<Vector2> pVertices, final int pIndex) {\r
+               final int previousIndex = EarCutTriangulator.computePreviousIndex(pVertices, pIndex);\r
+               final int nextIndex = EarCutTriangulator.computeNextIndex(pVertices, pIndex);\r
+\r
+               return EarCutTriangulator.isCollinear(pVertices, previousIndex, pIndex, nextIndex);\r
+       }\r
+\r
+       private static boolean isCollinear(final ArrayList<Vector2> pVertices, final int pPreviousIndex, final int pIndex, final int pNextIndex) {\r
+               final Vector2 previousVertex = pVertices.get(pPreviousIndex);\r
+               final Vector2 vertex = pVertices.get(pIndex);\r
+               final Vector2 nextVertex = pVertices.get(pNextIndex);\r
+\r
+               return EarCutTriangulator.computeSpannedAreaSign(previousVertex.x, previousVertex.y, vertex.x, vertex.y, nextVertex.x, nextVertex.y) == 0;\r
        }\r
 \r
-}\r
+       private static int computePreviousIndex(final List<Vector2> pVertices, final int pIndex) {\r
+               return pIndex == 0 ? pVertices.size() - 1 : pIndex - 1;\r
+       }\r
+\r
+       private static int computeNextIndex(final List<Vector2> pVertices, final int pIndex) {\r
+               return pIndex == pVertices.size() - 1 ? 0 : pIndex + 1;\r
+       }\r
+}
\ No newline at end of file
index 6a8cadf..8066377 100644 (file)
@@ -64,6 +64,33 @@ public class World {
        protected ContactListener contactListener = null;\r
 \r
        /**\r
+        * Ray-cast the world for all fixtures in the path of the ray.\r
+        * The ray-cast ignores shapes that contain the starting point.\r
+        * @param callback a user implemented callback class.\r
+        * @param point1 the ray starting point\r
+        * @param point2 the ray ending point\r
+        */\r
+       public void rayCast(RayCastCallback callback, Vector2 point1, Vector2 point2)\r
+       {\r
+               rayCastCallback = callback;\r
+               jniRayCast(addr, point1.x, point1.y, point2.x, point2.y);\r
+       }\r
+       \r
+       private RayCastCallback rayCastCallback = null;\r
+       \r
+       private native void jniRayCast (long addr, float aX, float aY, float bX, float bY);\r
+\r
+       private Vector2 rayPoint = new Vector2();\r
+       private Vector2 rayNormal = new Vector2();\r
+       \r
+       private float reportRayFixture (long addr, float pX, float pY, float nX, float nY, float fraction) {\r
+                       if (rayCastCallback != null)\r
+                               return rayCastCallback.reportRayFixture(fixtures.get(addr), rayPoint.set(pX, pY), rayNormal.set(nX, nY), fraction);\r
+                       else\r
+                               return 0.0f;\r
+       }\r
+       \r
+       /**\r
         * Construct a world object.\r
         * @param gravity the world gravity vector.\r
         * @param doSleep improve performance by not simulating inactive bodies.\r