luxiaoyu
4/30/2016 - 9:05 AM

几何计算类

几何计算类

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]));
    }
}