package org.critterai.nmgen;
/**
* Provides shared computational geometry operations.
* <p>
* This is a temporary class. Its functionality will eventually be merged into
* classes in the utility library.
* </p>
*/
public final class Geometry {
/*
* Design Notes:
*
* Until computational geometry functions are moved to the utilities
* library, they will slowly be migrated here as needed for easier
* unit testing.
*/
// TODO: GENERALIZATION: Move these algorithms to the utility library.
private Geometry() {}
/**
* Returns the distance squared from the point to the line segment.
* <p>
* Behavior is undefined if the the closest distance is outside the line
* segment.
* </p>
*
* @param px
* The x-value of point (px, py).
* @param py
* The y-value of point (px, py)
* @param ax
* The x-value of the line segment's vertex A.
* @param ay
* The y-value of the line segment's vertex A.
* @param bx
* The x-value of the line segment's vertex B.
* @param by
* The y-value of the line segment's vertex B.
* @return The distance squared from the point (px, py) to line segment AB.
*/
public static float getPointSegmentDistanceSq(final int px, final int py, final int ax,
final int ay, final int bx, final int by) {
/*
* Reference: http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/
*
* The goal of the algorithm is to find the point on line segment AB
* that is closest to P and then calculate the distance between P
* and that point.
*/
final float deltaABx = bx - ax;
final float deltaABy = by - ay;
final float deltaAPx = px - ax;
final float deltaAPy = py - ay;
final float segmentABLengthSq = (deltaABx * deltaABx) + (deltaABy * deltaABy);
if (segmentABLengthSq == 0) {
// AB is not a line segment. So just return
// distanceSq from P to A
return (deltaAPx * deltaAPx) + (deltaAPy * deltaAPy);
}
final float u = ((deltaAPx * deltaABx) + (deltaAPy * deltaABy)) / segmentABLengthSq;
if (u < 0) {
// Closest point on line AB is outside outside segment AB and
// closer to A. So return distanceSq from P to A.
return (deltaAPx * deltaAPx) + (deltaAPy * deltaAPy);
} else if (u > 1) {
// Closest point on line AB is outside segment AB and closer to B.
// So return distanceSq from P to B.
return ((px - bx) * (px - bx)) + ((py - by) * (py - by));
}
// Closest point on lineAB is inside segment AB. So find the exact
// point on AB and calculate the distanceSq from it to P.
// The calculation in parenthesis is the location of the point on
// the line segment.
final float deltaX = (ax + (u * deltaABx)) - px;
final float deltaY = (ay + (u * deltaABy)) - py;
return (deltaX * deltaX) + (deltaY * deltaY);
}
public static float getPointSegmentDistanceSq(final float px, final float py, final float pz,
final float ax, final float ay, final float az, final float bx, final float by,
final float bz) {
final float deltaABx = bx - ax;
final float deltaABy = by - ay;
final float deltaABz = bz - az;
final float deltaAPx = px - ax;
final float deltaAPy = py - ay;
final float deltaAPz = pz - az;
final float segmentABDistSq =
(deltaABx * deltaABx) + (deltaABy * deltaABy) + (deltaABz * deltaABz);
if (segmentABDistSq == 0) {
// AB is not a line segment. So just return
// distanceSq from P to A.
return (deltaAPx * deltaAPx) + (deltaAPy * deltaAPy) + (deltaAPz * deltaAPz);
}
final float u =
((deltaABx * deltaAPx) + (deltaABy * deltaAPy) + (deltaABz * deltaAPz)) /
segmentABDistSq;
if (u < 0) {
// Closest point on line AB is outside outside segment AB and
// closer to A. So return distanceSq from P to A.
return (deltaAPx * deltaAPx) + (deltaAPy * deltaAPy) + (deltaAPz * deltaAPz);
} else if (u > 1) {
// Closest point on line AB is outside segment AB and closer to B.
// So return distanceSq from P to B.
return ((px - bx) * (px - bx)) + ((py - by) * (py - by)) + ((pz - bz) * (pz - bz));
}
// Closest point on lineAB is inside segment AB. So find the exact
// point on AB and calculate the distanceSq from it to P.
// The calculation in parenthesis is the location of the point on
// the line segment.
final float deltaX = (ax + (u * deltaABx)) - px;
final float deltaY = (ay + (u * deltaABy)) - py;
final float deltaZ = (az + (u * deltaABz)) - pz;
return (deltaX * deltaX) + (deltaY * deltaY) + (deltaZ * deltaZ);
}
/**
* Returns TRUE if line segment AB intersects with line segment CD in any
* manner. Either collinear or at a single point.
*
* @param ax
* The x-value for point (ax, ay) in line segment AB.
* @param ay
* The y-value for point (ax, ay) in line segment AB.
* @param bx
* The x-value for point (bx, by) in line segment AB.
* @param by
* The y-value for point (bx, by) in line segment AB.
* @param cx
* The x-value for point (cx, cy) in line segment CD.
* @param cy
* The y-value for point (cx, cy) in line segment CD.
* @param dx
* The x-value for point (dx, dy) in line segment CD.
* @param dy
* The y-value for point (dx, dy) in line segment CD.
* @return TRUE if line segment AB intersects with line segment CD in any
* manner.
*/
public static boolean segmentsIntersect(final int ax, final int ay, final int bx, final int by,
final int cx, final int cy, final int dx, final int dy) {
/*
* This is modified 2D line-line intersection/segment-segment
* intersection test.
*/
final int deltaABx = bx - ax;
final int deltaABy = by - ay;
final int deltaCAx = ax - cx;
final int deltaCAy = ay - cy;
final int deltaCDx = dx - cx;
final int deltaCDy = dy - cy;
final int numerator = (deltaCAy * deltaCDx) - (deltaCAx * deltaCDy);
final int denominator = (deltaABx * deltaCDy) - (deltaABy * deltaCDx);
// Perform early exit tests.
if ((denominator == 0) && (numerator != 0)) {
// If numerator is zero, then the lines are colinear.
// Since it isn't, then the lines must be parallel.
return false;
}
// Lines intersect. But do the segments intersect?
// Forcing float division on both of these via casting of the
// denominator.
final float factorAB = numerator / (float) denominator;
final float factorCD =
((deltaCAy * deltaABx) - (deltaCAx * deltaABy)) / (float) denominator;
// Determine the type of intersection
if ((factorAB >= 0.0f) && (factorAB <= 1.0f) && (factorCD >= 0.0f) && (factorCD <= 1.0f)) {
return true; // The two segments intersect.
}
// The lines intersect, but segments to not.
return false;
}
}