几何计算类
package solid.util;
import android.graphics.PointF;
import android.support.v4.util.Pair;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/**
* 几何计算类。
*/
public class GeometryUtils {
/**
* 求一点向任意一条直线做垂线的交点坐标。
* (x1, y1)为直线上的第一个点;(x2, y2)为直线上的第二个点;(x3, y3)为垂线上的点
*/
public static PointF getVerticalCrossPoint(float x1, float y1, float x2, float y2,
float x3, float y3) {
// 0 = ax +b -y; 对应垂线方程为 -x -ay + m = 0;(m为系数)
float a = getA(x1, y1, x2, y2);
float b = getB(x1, y1, a);
float m = x3 + a * y3;
// 求两直线交点坐标
float xCross = (m - a * b) / (a * a + 1);
float yCross = a * xCross + b;
return new PointF(xCross, yCross);
}
/**
* 求两点之间距离
*/
public static float getDistance(float x1, float y1, float x2, float y2) {
float d = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
return (float) Math.sqrt(d);
}
/**
* @return 返回数值x是否在数值a和数值b之间。
*/
public static boolean between(float x, float a, float b) {
return x >= a && x <= b || x >= b && x <= a;
}
/**
* 求 y=ax+b中的a
*/
public static float getB(float x1, float y1, float a) {
return y1 - a * x1;
}
/**
* 求 y=ax+b中的b
*/
public static float getA(float x1, float y1, float x2, float y2) {
return (y1 - y2) / (x1 - x2);
}
/**
* 判断一点是否在矩形中。
*/
public static boolean isInRect(float x, float y, float[] rect) {
// 只需循环两次
for (int i = 0; i < 4; i += 2) {
float x1 = rect[i % 8];
float y1 = rect[(i + 1) % 8];
float x2 = rect[(i + 2) % 8];
float y2 = rect[(i + 3) % 8];
float x3 = rect[(i + 4) % 8];
float y3 = rect[(i + 5) % 8];
float x4 = rect[(i + 6) % 8];
float y4 = rect[(i + 7) % 8];
float a1 = getA(x1, y1, x4, y4);
float b1 = getB(x1, y1, a1);
float a2 = getA(x2, y2, x3, y3);
float b2 = getB(x2, y2, a2);
if (!between(y, a1 * x + b1, a2 * x + b2)) {
return false;
}
}
return true;
}
/**
* 计算矩形外任意一点离该矩形最近的一个边,返回对应边的两个顶点。
*
* @param rect The order of the corners in the float array is:
* 0------->1
* ^ |
* | |
* | v
* 3<-------2
*/
public static Pair<PointF, PointF> getNearestLine(float x, float y, float[] rect) {
// 如果点位于矩形边的外投影区域,则点的最近边为该边。
// 判断外投影区域的方式:当前边相邻两边的外延长线区域内。
// 表达式为 a1*x+b1 < y < a4*x+b4 (假设a1*x+b1 < a4*x+b4)
for (int i = 0; i < 8; i += 2) {
float x1 = rect[i % 8];
float y1 = rect[(i + 1) % 8];
float x2 = rect[(i + 2) % 8];
float y2 = rect[(i + 3) % 8];
float x3 = rect[(i + 4) % 8];
float y3 = rect[(i + 5) % 8];
float x4 = rect[(i + 6) % 8];
float y4 = rect[(i + 7) % 8];
float a1 = getA(x1, y1, x4, y4);
float b1 = getB(x1, y1, a1);
float a2 = getA(x2, y2, x3, y3);
float b2 = getB(x2, y2, a2);
if (between(y, a1 * x + b1, a2 * x + b2)) {
// 处于两条外延长线的区域内,还要判断外延长线夹着的两条平行边哪个近
float d1 = getDistance(x, y, x1, y1) + getDistance(x, y, x2, y2);
float d2 = getDistance(x, y, x3, y3) + getDistance(x, y, x4, y4);
return d1 < d2 ? new Pair<>(new PointF(x1, y1), new PointF(x2, y2)) :
new Pair<>(new PointF(x3, y3), new PointF(x4, y4));
}
}
// 如果点位于矩形边的非外投影区域,则取点和边的两个端点的距离和中最小的那个边为最近边。
ArrayList<Pair<Float, Integer>> unsort = new ArrayList<>();
for (int i = 0; i < 8; i += 2) {
float x1 = rect[i % 8];
float y1 = rect[(i + 1) % 8];
float x2 = rect[(i + 2) % 8];
float y2 = rect[(i + 3) % 8];
float d = getDistance(x, y, x1, y1) + getDistance(x, y, x2, y2);
unsort.add(new Pair<>(d, i));
}
Collections.sort(unsort, new Comparator<Pair<Float, Integer>>() {
@Override
public int compare(Pair<Float, Integer> lhs, Pair<Float, Integer> rhs) {
return (int) (lhs.first - rhs.first);
}
});
int j = unsort.get(0).second;
return new Pair<>(new PointF(rect[j % 8], rect[(j + 1) % 8]), new PointF(rect[(j + 2) % 8], rect[(j + 3) % 8]));
}
}