Subversion Repositories AndroidProjects

Rev

Rev 1196 | Rev 1267 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

package com.gebauz.bauzoid.math.collision;

import com.gebauz.bauzoid.math.Line2;
import com.gebauz.bauzoid.math.Vector2;

public class CollisionUtil
{
        // Constants========================================================================================

        // Embedded Types===================================================================================
       
        public static class PenetrationResult
        {
                public boolean isPenetrating = false;
                public Vector2 penetrationVector = null;
                public Vector2 contactPoint = null;            
               
                public int collidingEdgeIndex = -1;
        }
       
        public static class ProjectionResult
        {
                public double minimum = Float.MAX_VALUE;
                public double maximum = -Float.MAX_VALUE;
               
                /** Returns true if the projection overlaps. */
                public boolean isOverlapping(ProjectionResult other)
                {
                        return ((maximum > other.minimum) && (minimum < other.maximum));
                }
                               
                /** Return the amount of overlap. */
                public double getOverlap(ProjectionResult other)
                {
                        if (!isOverlapping(other))
                                return 0.0f;
                       
                        double maxEdge = Math.min(maximum, other.maximum);
                        double minEdge = Math.max(minimum, other.minimum);
                       
                        return (maxEdge - minEdge);
                }
               
                public double flipPenetrationVector(ProjectionResult other)
                {
                        if (minimum <= other.minimum)
                                return -1.0;
                        return 1.0;
                }
               
                public boolean contains(ProjectionResult other)
                {
                        return ((minimum <= other.minimum) && (maximum >= other.maximum));                             
                }
        }

        // Fields===========================================================================================

        // Methods==========================================================================================
       
       

        /** Calculate collision and minimal penetration vector using the separating axis theorem, return penetration vector.
         * Returns null if there is no collision, or the penetration vector between the polygons.
         */

        public static PenetrationResult calculatePenetration(Line2[] poly1, Line2[] poly2, Vector2[] axes1, Vector2[] axes2)
        {
                PenetrationResult result = new PenetrationResult();
               
                double overlap = Float.MAX_VALUE;
                Vector2 smallestAxis = null;
                boolean fromFirst = true;
                double flip = 1.0;
               
                // project onto first axis list
                for (int i = 0; i < axes1.length; i++)
                {
                        Vector2 axis = axes1[i];
                       
                        axis.normalize();
                       
                        ProjectionResult p1 = projectPoly(poly1, axis);
                        ProjectionResult p2 = projectPoly(poly2, axis);
                       
                        if (!p1.isOverlapping(p2))
                                return result;
                        else
                        {
                                double o = p1.getOverlap(p2);
                                double f = p1.flipPenetrationVector(p2);
                               
                                if (p1.contains(p2) || p2.contains(p1))
                                {
                                        double mins = Math.abs(p1.minimum - p2.minimum);
                                        double maxs = Math.abs(p1.maximum - p2.maximum);
                                       
                                        if (mins < maxs)
                                                o += mins;
                                        else
                                                o += maxs;
                                }
                               
                                if (o < overlap)
                                {
                                        overlap = o;
                                        smallestAxis = axis;
                                        fromFirst = true;
                                        flip = f;
                                }
                        }
                }
               
                // project onto second axis list
                for (int i = 0; i < axes2.length; i++)
                {
                        Vector2 axis = axes2[i];
                       
                        axis.normalize();
                       
                        ProjectionResult p1 = projectPoly(poly1, axis);
                        ProjectionResult p2 = projectPoly(poly2, axis);
                       
                        if (!p1.isOverlapping(p2))
                                return result;
                        else
                        {
                                double o = p1.getOverlap(p2);
                                double f = p1.flipPenetrationVector(p2);
                               
                                if (p1.contains(p2) || p2.contains(p1))
                                {
                                        double mins = Math.abs(p1.minimum - p2.minimum);
                                        double maxs = Math.abs(p1.maximum - p2.maximum);
                                       
                                        if (mins < maxs)
                                                o += mins;
                                        else
                                                o += maxs;
                                }
                               
                                if (o < overlap)
                                {
                                        overlap = o;
                                        smallestAxis = axis;
                                        fromFirst = false;
                                        flip = f;
                                }
                        }
                }
               
                result.isPenetrating = true;
               
                smallestAxis.x *= flip;
                smallestAxis.y *= flip;
               
                result.penetrationVector = new Vector2((float)(smallestAxis.x * overlap), (float)(smallestAxis.y * overlap));

                // determine the vertex that is "deepest" in the other shape
                int whichVertex = -1;
               
                Vector2 contactPoint;

                // project vertices of poly that did not create the axis
                if (fromFirst)
                {
                        float maxDp = -Float.MAX_VALUE;
                       
                        for (int i = 0; i < poly2.length; i++)
                        {
                                Vector2 v = new Vector2(poly2[i].ax, poly2[i].ay);
                                float dp = Vector2.dotProduct(v, smallestAxis);

                                if (dp > maxDp)
                                {
                                        maxDp = dp;
                                        whichVertex = i;
                                }
                        }
                        contactPoint = new Vector2(poly2[whichVertex].ax, poly2[whichVertex].ay);
                }
                else
                {
                        float maxDp = Float.MAX_VALUE;
                       
                        for (int i = 0; i < poly1.length; i++)
                        {
                                Vector2 v = new Vector2(poly1[i].ax, poly1[i].ay);
                                float dp = Vector2.dotProduct(v, smallestAxis);
                               
                                if (dp < maxDp)
                                {
                                        maxDp = dp;
                                        whichVertex = i;
                                }
                        }
                        contactPoint = new Vector2(poly1[whichVertex].ax, poly1[whichVertex].ay);
                }

                contactPoint.addVector(result.penetrationVector);
                result.contactPoint = contactPoint;

                return result;
        }
       
        public static boolean isLeft(Vector2 a, Vector2 b, Vector2 c)
        {
             return ((b.x - a.x)*(c.y - a.y) - (b.y - a.y)*(c.x - a.x)) >= 0.0f;
        }
       
        /** Get the center point of a poly. */
        public static Vector2 getCenter(Line2[] poly)
        {
                Vector2 result = new Vector2(0, 0);
                for (int i = 0; i < poly.length; i++)
                {
                        // need just one point because it should be a closed polygon
                        result.x += poly[i].ax;
                        result.y += poly[i].ay;
                }
                result.x /= (float)poly.length;
                result.y /= (float)poly.length;
               
                return result;
        }
       
       
        /** Project the polygon onto an axis. */
        public static ProjectionResult projectPoly(Line2[] poly, Vector2 axis)
        {
                ProjectionResult result = new ProjectionResult();
               
                for (int i = 0; i < poly.length; i++)
                {
                        // project the vector from origin to each of the line segment's end points onto b
                        // the result is the section on the axis
                       
                        Vector2 a = new Vector2(poly[i].ax, poly[i].ay);
                        Vector2 b = new Vector2(poly[i].bx, poly[i].by);
                       
                        float dpA = Vector2.dotProduct(a, axis);
                        float dpB = Vector2.dotProduct(b, axis);
                       
                        //Gdx.app.log(Consts.LOG_TAG, "a: " + a.x + ", " + a.y + " (" + dpA + ") - b: " + b.x + ", " + b.y + " (" + dpB + ")");
                       
                        if (dpA < result.minimum)
                                result.minimum = dpA;
                       
                        if (dpB < result.minimum)
                                result.minimum = dpB;
                       
                        if (dpA > result.maximum)
                                result.maximum = dpA;
                       
                        if (dpB > result.maximum)
                                result.maximum = dpB;
                }
               
                return result;         
        }
       

        // Getters/Setters==================================================================================

}