二维平面多边形的相交判定

一、类型定义

1、点

export type Point = {
    x: number;
    y: number;
}

2、线

export type Segment = [Point, Point];

3、面

export type Polygon = Array<Point>;

二、使用

判断两个四边形是否相交:

import { isPolygonsOverlap } from "./util.ts";

let A: Polygon = [{x: 0, y: 4}, {x: 4, y: 4}, {x: -4, y: 0}, {x: 0, y: 0}];
let B: Polygon = [{x: 2, y: 6}, {x: 6, y: 6}, {x: 2, y: 0}, {x: 6, y: 0}];
isPolygonsOverlap(A, B);

OR

import PolygonUtils from "./util.ts";

let A: Polygon = [{x: 0, y: 4}, {x: 4, y: 4}, {x: -4, y: 0}, {x: 0, y: 0}];
let B: Polygon = [{x: 2, y: 6}, {x: 6, y: 6}, {x: 2, y: 0}, {x: 6, y: 0}];
PolygonUtils.isPolygonsOverlap(A, B);

三、高阶

import { isPolygonsOverlaps } from "./util.ts";

let A: Polygon = [{x: 0, y: 4}, {x: 4, y: 4}, {x: -4, y: 0}, {x: 0, y: 0}];
let B: Polygon = [{x: 2, y: 6}, {x: 6, y: 6}, {x: 2, y: 0}, {x: 6, y: 0}];
let c: Polygon = [{x: 4, y: 8}, {x: 8, y: 8}, {x: 4, y: 2}, {x: 8, y: 2}];

1、判断A、B是否相交

isPolygonsOverlaps(A, B);

2、判断A与B、C是否相交

isPolygonsOverlaps(A, [B, C]);

3、判断A、B、C是否相交

isPolygonsOverlaps(A, B, C);

四、源代码

export interface Point {
    x: number;
    y: number;
}
export type Polygon = Array<Point>;
export type Segment = [Point, Point];

/**
 * 判断两多边形线段是否相交
 *
 * @param {Segment} segA-线段A
 * @param {Segment} segB-线段B
 * @returns {Boolean} 两条线段是否相交
 */
export function isSegmentsIntersectant(segA: Segment, segB: Segment): Boolean {
    //线线
    const abc = (segA[0].x - segB[0].x) * (segA[1].y - segB[0].y) - (segA[0].y - segB[0].y) * (segA[1].x - segB[0].x);
    const abd = (segA[0].x - segB[1].x) * (segA[1].y - segB[1].y) - (segA[0].y - segB[1].y) * (segA[1].x - segB[1].x);
    if (abc * abd >= 0) {
        return false;
    }
    const cda = (segB[0].x - segA[0].x) * (segB[1].y - segA[0].y) - (segB[0].y - segA[0].y) * (segB[1].x - segA[0].x);
    const cdb = cda + abc - abd;
    return !(cda * cdb >= 0);
}

/**
 * 判断两多边形边界是否相交
 *
 * @param {Polygon} plyA-多边形A
 * @param {Polygon} plyB-多边形B
 * @returns {Boolean} 两个多边形边界是否相交
 */
export function isPolygonsIntersectant(plyA: Polygon, plyB: Polygon): Boolean {
    //面面
    for (let i = 0, il = plyA.length; i < il; i++) {
        for (let j = 0, jl = plyB.length; j < jl; j++) {
            const segA: Segment = [plyA[i], plyA[i === il - 1 ? 0 : i + 1]];
            const segB: Segment = [plyB[j], plyB[j === jl - 1 ? 0 : j + 1]];
            if (isSegmentsIntersectant(segA, segB)) {
                return true;
            }
        }
    }
    return false;
}

/**
 * 判断点是否在另一平面图中
 *
 * @param {Point} point-点
 * @param {Polygon} polygon-多边形
 * @returns {Boolean} 是否在另一平面图中
 */
export function isPointInPolygon(point: Point, polygon: Polygon): Boolean {
    //下述代码来源:http://paulbourke.net/geometry/insidepoly/,进行了部分修改
    //基本思想是利用射线法,计算射线与多边形各边的交点,如果是偶数,则点在多边形外,否则
    //在多边形内。还会考虑一些特殊情况,如点在多边形顶点上,点在多边形边上等特殊情况。
    var N = polygon.length;
    var boundOrVertex = true; //如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true
    var intersectCount = 0; //cross points count of x
    var precision = 2e-10; //浮点类型计算时候与0比较时候的容差
    var p1, p2; //neighbour bound vertices
    var p = point; //测试点
    
    p1 = polygon[0]; //left vertex
    for (var i = 1; i <= N; ++i) {
        //check all rays
        if (p.x == p1.x && p.y == p1.y) {
            return boundOrVertex; //p is an vertex
        }
        p2 = polygon[i % N]; //right vertex
        if (p.y < Math.min(p1.y, p2.y) || p.y > Math.max(p1.y, p2.y)) {
            //ray is outside of our interests
            p1 = p2;
            continue; //next ray left point
        }
        if (p.y > Math.min(p1.y, p2.y) && p.y < Math.max(p1.y, p2.y)) {
            //ray is crossing over by the algorithm (common part of)
            if (p.x <= Math.max(p1.x, p2.x)) {
                //x is before of ray
                if (p1.y == p2.y && p.x >= Math.min(p1.x, p2.x)) {
                    //overlies on a horizontal ray
                    return boundOrVertex;
                }
                if (p1.x == p2.x) {
                    //ray is vertical
                    if (p1.x == p.x) {
                        //overlies on a vertical ray
                        return boundOrVertex;
                    } else {
                        //before ray
                        ++intersectCount;
                    }
                } else {
                    //cross point on the left side
                    var xinters = ((p.y - p1.y) * (p2.x - p1.x)) / (p2.y - p1.y) + p1.x; //cross point of x
                    if (Math.abs(p.x - xinters) < precision) {
                        //overlies on a ray
                        return boundOrVertex;
                    }

                    if (p.x < xinters) {
                        //before ray
                        ++intersectCount;
                    }
                }
            }
        } else {
            //special case when ray is crossing through the vertex
            if (p.y == p2.y && p.x <= p2.x) {
                //p crossing over p2
                var p3 = polygon[(i + 1) % N]; //next vertex
                if (p.y >= Math.min(p1.y, p3.y) && p.y <= Math.max(p1.y, p3.y))                 {
                    //p.y lies between p1.y & p3.y
                    ++intersectCount;
                } else {
                    intersectCount += 2;
                }
            }
        }
        p1 = p2; //next ray left point
    }
    
    if (intersectCount % 2 == 0) {
        //偶数在多边形外
        return false;
    } else {
        //奇数在多边形内
        return true;
    }
}
  
/**
 * 判断两多变形是否存在点与区域的包含关系(A的点在B的区域内或B的点在A的区域内)
 *
 * @param {Polygon} plyA-多边形A
 * @param {Polygon} plyB-多边形B
 * @returns {Boolean} 两多变形是否存在点与区域的包含关系
 */
export function isPointInPolygonBidirectional(plyA: Polygon, plyB: Polygon): Boolean {
    //面面
    let [a, b] = [false, false];
    a = plyA.some(item => isPointInPolygon(item, plyB));
    if (!a) {
        b = plyB.some(item => isPointInPolygon(item, plyA));
    }
    return a || b;
}

/**
 * 判断两个多边形是否相交
 *
 * @param {Polygon} plyA-多边形A
 * @param {Polygon} plyB-多边形B
 * @returns {Boolean} 两个多边形是否相交
 */
export function isPolygonsOverlap(plyA: Polygon, plyB: Polygon): Boolean {
    return isPolygonsIntersectant(plyA, plyB) || isPointInPolygonBidirectional(plyA, plyB);
}

/**
 * 判断两个多边形是否相交
 *
 * @param {Polygon} plyA-多边形A
 * @param {Polygon} plyB-多边形B
 * @returns {Boolean} 是否相交
 */
export function isPolygonsOverlaps(plyA: Polygon, plyB: Polygon): Boolean;

/**
 * 判断一个多边形是否与其他多边形是否相交
 *
 * @param {Polygon} plyA
 * @param {Array<Polygon>} plys
 * @returns {Boolean} 是否相交
 */
export function isPolygonsOverlaps(plyA: Polygon, plys: Array<Polygon>): Boolean;

/**
 * 判断多个多边形是否有相交
 *
 * @param {Polygon} plyA
 * @param {...Array<Polygon>} plys
 * @returns {Boolean} 是否相交
 */
export function isPolygonsOverlaps(...plys: Array<Polygon>): Boolean;

export function isPolygonsOverlaps(...plys: Array<Polygon> | [Polygon, Array<Polygon>] | [Polygon, Polygon]): Boolean {
    let flag = false;
    if (plys.length < 2) return flag;
    else if (plys.length > 2) {
        for (let i = 0; i < plys.length - 1; i++) {
            for (let j = i + 1; j < plys.length; j++) {
                if (isPolygonsOverlap(plys[i] as Polygon, plys[j] as Polygon)) flag = true;
            }
        }
        return flag;
    } else if (Array.isArray(plys[1])) {
        for (let i = 0; i < plys[1].length - 1; i++) {
            if (isPolygonsOverlap(plys[0] as Polygon, plys[1][i] as Polygon)) flag = true;
        }
        return flag;
    } else return isPolygonsOverlap(plys[0], plys[1]);
}

export default {
    isPolygonsOverlap,
    isPointInPolygonBidirectional,
    isPointInPolygon,
    isPolygonsIntersectant,
    isSegmentsIntersectant,
    isPolygonsOverlaps
};
posted @ 2023-06-12 11:33  一个人de雨天  阅读(417)  评论(0编辑  收藏  举报