package org.jeecg.modules.opencv.utils;


import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.utils.Converters;

import java.util.List;

/**
 * 透视变换
 */

public class Perspective {
    public static void perpectiveImg(String backgroundImgPath, String phoneImgPath, String targetFileName) {
        try {
            System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

            Mat src = Imgcodecs.imread(phoneImgPath);
            Mat dst = Imgcodecs.imread(backgroundImgPath);
            Mat dst1 = dst.clone();
            //透视变换点位
            List<Point> perspectivePoints = CornerCheck.detectConerList(backgroundImgPath);
            //矩形点位
            List<Point> matPoints = CornerCheck.matCornerList(perspectivePoints);
            //读取图像到矩阵中
            if (src.empty()) {
                throw new Exception("no file");
            }

            List<Point> listSrcs = java.util.Arrays.asList(new Point(0, 0), new Point(src.cols(), 0), new Point(0, src.rows()), new Point(src.cols(), src.rows()));
            Mat srcPoints = Converters.vector_Point_to_Mat(listSrcs, CvType.CV_32F);
            Mat dstPoints = Converters.vector_Point_to_Mat(perspectivePoints, CvType.CV_32F);
            Mat perspectiveMmat = Imgproc.getPerspectiveTransform(srcPoints, dstPoints);
            Imgproc.warpPerspective(src, dst, perspectiveMmat, dst.size(), Imgproc.INTER_LINEAR);
            for (int i = 0; i < dst1.cols(); i++) {
                for (int j = 0; j < dst1.rows(); j++) {
                    Point point = new Point();
                    point.x = i;
                    point.y = j;
                    if (isPtInPoly(point, matPoints)) {
                        Imgproc.circle(dst1, point, 8, new Scalar(0, 0, 0), Imgproc.FILLED);
                    }
                }
            }
            Core.addWeighted(dst1, 1, dst, 1, 3, dst);
            Imgcodecs.imwrite(targetFileName, dst);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static boolean isInner(Point point, List<Point> corners) {
        int n = corners.size(); //n=4
        double sum = 0;
        for (int i = 0; i < n - 1; i++) {
            sum += area(corners.get(i), corners.get(i + 1), point);
        }
        sum += area(corners.get(n - 1), corners.get(0), point);

        double polyArea = area(corners.get(0), corners.get(1), corners.get(2)) + area(corners.get(1), corners.get(2), corners.get(3));
        if (sum == polyArea) {
            return true;
        } else {
            return false;
        }
    }

    //已知坐标，求三角形面积
    private static double area(Point p1, Point p2, Point p3) {
        return 0.5 * Math.abs((p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y));
    }

    public static boolean isPtInPolygon(Point point, List<Point> polygon) {
        int iSum, iIndex;
        double dLon1, dLon2, dLat1, dLat2, dLon;
        int size = polygon.size();
        iSum = 0;
        for (iIndex = 0; iIndex < size; iIndex++) {
            if (iIndex == size - 1) {
                dLon1 = polygon.get(iIndex).x;
                dLat1 = polygon.get(iIndex).y;
                dLon2 = polygon.get(0).x;
                dLat2 = polygon.get(0).y;
            } else {
                dLon1 = polygon.get(iIndex).x;
                dLat1 = polygon.get(iIndex).y;
                dLon2 = polygon.get(iIndex + 1).x;
                dLat2 = polygon.get(iIndex + 1).y;
            }
            // 以下语句判断A点是否在边的两端点的水平平行线之间，在则可能有交点，开始判断交点是否在左射线上
            if (((point.y >= dLat1) && (point.y < dLat2))
                    || ((point.y >= dLat2) && (point.y < dLat1))) {
                if (Math.abs(dLat1 - dLat2) > 0) {
                    //得到 A点向左射线与边的交点的x坐标：
                    dLon = dLon1 - ((dLon1 - dLon2) * (dLat1 - point.y)) / (dLat1 - dLat2);
                    // 如果交点在A点左侧（说明是做射线与 边的交点），则射线与边的全部交点数加一：
                    if (dLon < point.x) {
                        iSum++;
                    }
                }
            }
        }
        return (iSum % 2) != 0;
    }

    /**
     * 判断点是否在多边形内，如果点位于多边形的顶点或边上，也算做点在多边形内，直接返回true
     *
     * @param point   检测点
     * @param polygon 多边形的顶点
     * @return 点在多边形内返回true, 否则返回false
     */
    public static boolean isPtInPoly(Point point, List<Point> polygon) {

        int N = polygon.size();
        //如果点位于多边形的顶点或边上，也算做点在多边形内，直接返回true
        boolean boundOrVertex = true;
        //cross points count of x
        int intersectCount = 0;
        //浮点类型计算时候与0比较时候的容差
        double precision = 2e-10;
        //neighbour bound vertices
        Point p1, p2;
        //当前点
        Point p = point;

        //left vertex
        p1 = polygon.get(0);
        //check all rays
        for (int i = 1; i <= N; ++i) {
            if (p.equals(p1)) {
                //p is an vertex
                return boundOrVertex;
            }

            //right vertex
            p2 = polygon.get(i % N);
            //ray is outside of our interests
            if (p.x < Math.min(p1.x, p2.x) || p.x > Math.max(p1.x, p2.x)) {
                p1 = p2;
                //next ray left point
                continue;
            }

            //ray is crossing over by the algorithm (common part of)
            if (p.x > Math.min(p1.x, p2.x) && p.x < Math.max(p1.x, p2.x)) {
                //x is before of ray
                if (p.y <= Math.max(p1.y, p2.y)) {
                    //overlies on a horizontal ray
                    if (p1.x == p2.x && p.y >= Math.min(p1.y, p2.y)) {
                        return boundOrVertex;
                    }

                    //ray is vertical
                    if (p1.y == p2.y) {
                        //overlies on a vertical ray
                        if (p1.y == p.y) {
                            return boundOrVertex;
                            //before ray
                        } else {
                            ++intersectCount;
                        }
                        //cross point on the left side
                    } else {
                        //cross point of y
                        double xinters = (p.x - p1.x) * (p2.y - p1.y) / (p2.x - p1.x) + p1.y;
                        //overlies on a ray
                        if (Math.abs(p.y - xinters) < precision) {
                            return boundOrVertex;
                        }

                        //before ray
                        if (p.y < xinters) {
                            ++intersectCount;
                        }
                    }
                }
                //special case when ray is crossing through the vertex
            } else {
                //p crossing over p2
                if (p.x == p2.x && p.y <= p2.y) {
                    //next vertex
                    Point p3 = polygon.get((i + 1) % N);
                    //p.x lies between p1.x & p3.x
                    if (p.x >= Math.min(p1.x, p3.x) && p.x <= Math.max(p1.x, p3.x)) {
                        ++intersectCount;
                    } else {
                        intersectCount += 2;
                    }
                }
            }
            //next ray left point
            p1 = p2;
        }

        //偶数在多边形外
        if (intersectCount % 2 == 0) {
            return false;
            //奇数在多边形内
        } else {
            return true;
        }
    }
}
