package com.mygdx.game.utilities;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector3;
/**
* Class containing some geometry algorithms.
* Note that the implementation of some algorithms is not thread-safe.
*
* @author jsjolund
* @author davebaol
*/
public final class GeometryUtils {
private GeometryUtils() {
}
/**
* Projects a point to a line segment. This implementation is thread-safe.
*
* @param nearest Output for the nearest vector to the segment.
* @param start Segment start point
* @param end Segment end point
* @param point Point to project from
* @return Squared distance between point and nearest.
*/
public static float nearestSegmentPointSquareDistance
(Vector3 nearest, Vector3 start, Vector3 end, Vector3 point) {
nearest.set(start);
float abX = end.x - start.x;
float abY = end.y - start.y;
float abZ = end.z - start.z;
float abLen2 = abX * abX + abY * abY + abZ * abZ;
if (abLen2 > 0) { // Avoid NaN due to the indeterminate form 0/0
float t = ((point.x - start.x) * abX + (point.y - start.y) * abY + (point.z - start.z) * abZ) / abLen2;
float s = MathUtils.clamp(t, 0, 1);
nearest.x += abX * s;
nearest.y += abY * s;
nearest.z += abZ * s;
}
return nearest.dst2(point);
}
private static final Vector3 TMP_VEC_1 = new Vector3();
private static final Vector3 TMP_VEC_2 = new Vector3();
private static final Vector3 TMP_VEC_3 = new Vector3();
/**
* Find the closest point on the triangle, given a measure point.
* This is the optimized algorithm taken from the book "Real-Time Collision Detection".
* <p>
* This implementation is NOT thread-safe.
*
* @param a First vertex of the triangle
* @param b Second vertex of the triangle
* @param c Third vertex of the triangle
* @param p The measure point
* @param out Output for the closest point; can be {@code null}
* @return The closest distance squared, between the triangle's vertices and the point.
*/
public static float getClosestPointOnTriangle(Vector3 a, Vector3 b, Vector3 c, Vector3 p, Vector3 out) {
// Check if P in vertex region outside A
Vector3 ab = TMP_VEC_1.set(b).sub(a);
Vector3 ac = TMP_VEC_2.set(c).sub(a);
Vector3 ap = TMP_VEC_3.set(p).sub(a);
float d1 = ab.dot(ap);
float d2 = ac.dot(ap);
if (d1 <= 0.0f && d2 <= 0.0f) {
if (out != null)
out.set(a); // barycentric coordinates (1,0,0)
return p.dst2(a);
}
// Check if P in vertex region outside B
Vector3 bp = TMP_VEC_3.set(p).sub(b);
float d3 = ab.dot(bp);
float d4 = ac.dot(bp);
if (d3 >= 0.0f && d4 <= d3) {
if (out != null)
out.set(b); // barycentric coordinates (0,1,0)
return p.dst2(b);
}
// Check if P in edge region of AB, if so return projection of P onto AB
float vc = d1 * d4 - d3 * d2;
if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f) {
Vector3 ret = out != null ? out : TMP_VEC_3;
float v = d1 / (d1 - d3);
ret.set(a).mulAdd(ab, v); // barycentric coordinates (1-v,v,0)
return p.dst2(ret);
}
// Check if P in vertex region outside C
Vector3 cp = TMP_VEC_3.set(p).sub(c);
float d5 = ab.dot(cp);
float d6 = ac.dot(cp);
if (d6 >= 0.0f && d5 <= d6) {
if (out != null)
out.set(c); // barycentric coordinates (0,0,1)
return p.dst2(c);
}
// Check if P in edge region of AC, if so return projection of P onto AC
float vb = d5 * d2 - d1 * d6;
if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f) {
Vector3 ret = out != null ? out : TMP_VEC_3;
float w = d2 / (d2 - d6);
ret.set(a).mulAdd(ac, w); // barycentric coordinates (1-w,0,w)
return ret.dst2(p);
}
// Check if P in edge region of BC, if so return projection of P onto BC
float va = d3 * d6 - d5 * d4;
if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f) {
Vector3 ret = out != null ? out : TMP_VEC_3;
float w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
ret.set(b).mulAdd(TMP_VEC_1.set(c).sub(b), w); // barycentric coordinates (0,1-w,w)
return ret.dst2(p);
}
// P inside face region. Compute Q through its barycentric coordinates (u,v,w)
float denom = 1.0f / (va + vb + vc);
float v = vb * denom;
float w = vc * denom;
Vector3 ret = out != null ? out : TMP_VEC_3;
ret.set(a).mulAdd(ab, v).mulAdd(ac, w);
return ret.dst2(p);
}
}