二维平面多边形的相交判定
一、类型定义
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
};