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 =
new Vector2
();
public Vector2 contactPoint =
new Vector2
();
public int collidingEdgeIndex = -
1;
public void reset
()
{
isPenetrating =
false;
penetrationVector.
set(0,
0);
contactPoint.
set(0,
0);
collidingEdgeIndex = -
1;
}
}
public static class ProjectionResult
{
public double minimum =
Float.
MAX_VALUE;
public double maximum = -
Float.
MAX_VALUE;
public void reset
()
{
minimum =
Float.
MAX_VALUE;
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===========================================================================================
private static ProjectionResult p1 =
new ProjectionResult
();
private static ProjectionResult p2 =
new ProjectionResult
();
private static Vector2 tempA =
new Vector2
();
private static Vector2 tempB =
new Vector2
();
// 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 boolean calculatePenetration
(Line2
[] poly1, Line2
[] poly2, Vector2
[] axes1, Vector2
[] axes2, PenetrationResult result
)
{
double overlap =
Float.
MAX_VALUE;
Vector2 smallestAxis =
null;
boolean fromFirst =
true;
double flip =
1.0;
p1.
reset();
p2.
reset();
// project onto first axis list
for (int i =
0; i
< axes1.
length; i++
)
{
Vector2 axis = axes1
[i
];
axis.
normalize();
projectPoly
(poly1, axis, p1
);
projectPoly
(poly2, axis, p2
);
if (!p1.
isOverlapping(p2
))
return false;
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();
projectPoly
(poly1, axis, p1
);
projectPoly
(poly2, axis, p2
);
if (!p1.
isOverlapping(p2
))
return false;
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.
set((float)(smallestAxis.
x * overlap
),
(float)(smallestAxis.
y * overlap
));
// determine the vertex that is "deepest" in the other shape
int whichVertex = -
1;
// 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 ax = poly2
[i
].
a.
x;
float ay = poly2
[i
].
a.
y;
//float dp = Vector2.dotProduct(v, smallestAxis);
float dp = ax
*smallestAxis.
x + ay
*smallestAxis.
y;
if (dp
> maxDp
)
{
maxDp = dp
;
whichVertex = i
;
}
}
result.
contactPoint.
set(poly2
[whichVertex
].
a.
x, poly2
[whichVertex
].
a.
y);
}
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 ax = poly1
[i
].
a.
x;
float ay = poly1
[i
].
a.
y;
//float dp = Vector2.dotProduct(v, smallestAxis);
float dp = ax
*smallestAxis.
x + ay
*smallestAxis.
y;
if (dp
< maxDp
)
{
maxDp = dp
;
whichVertex = i
;
}
}
result.
contactPoint.
set(poly1
[whichVertex
].
a.
x, poly1
[whichVertex
].
a.
y);
}
result.
contactPoint.
addVector(result.
penetrationVector);
//result.contactPoint = contactPoint;
return true;
}
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 void projectPoly
(Line2
[] poly, Vector2 axis, ProjectionResult result
)
{
result.
reset();
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);
tempA.
set(poly
[i
].
a.
x, poly
[i
].
a.
y);
tempB.
set(poly
[i
].
b.
x, poly
[i
].
b.
y);
float dpA = Vector2.
dotProduct(tempA, axis
);
float dpB = Vector2.
dotProduct(tempB, 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
;
}
}
// Getters/Setters==================================================================================
}