Files
esengine/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisionsPolygon.ts
2021-07-02 10:11:09 +08:00

103 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
module es {
export class ShapeCollisionsPolygon {
/**
* 检查两个多边形之间的碰撞
* @param first
* @param second
* @param result
*/
public static polygonToPolygon(first: Polygon, second: Polygon, result: CollisionResult): boolean {
let isIntersecting = true;
const firstEdges = first.edgeNormals;
const secondEdges = second.edgeNormals;
let minIntervalDistance = Number.POSITIVE_INFINITY;
let translationAxis = Vector2.zero;
let polygonOffset = first.position.sub(second.position);
let axis: Vector2;
// 循环穿过两个多边形的所有边
for (let edgeIndex = 0; edgeIndex < firstEdges.length + secondEdges.length; edgeIndex++) {
// 1. 找出当前多边形是否相交
// 多边形的归一化轴垂直于缓存给我们的当前边
axis = edgeIndex < firstEdges.length ? firstEdges[edgeIndex] : secondEdges[edgeIndex - firstEdges.length];
// 求多边形在当前轴上的投影
let intervalDist = 0;
let {min: minA, max: maxA} = this.getInterval(axis, first);
const {min: minB, max: maxB} = this.getInterval(axis, second);
// 将区间设为第二个多边形的空间。由轴上投影的位置差偏移。
const relativeIntervalOffset = polygonOffset.dot(axis);
minA += relativeIntervalOffset;
maxA += relativeIntervalOffset;
// 检查多边形投影是否正在相交
intervalDist = this.intervalDistance(minA, maxA, minB, maxB);
if (intervalDist > 0)
isIntersecting = false;
// 对于多对多数据类型转换添加一个Vector2?参数称为deltaMovement。为了提高速度我们这里不使用它
// TODO: 现在找出多边形是否会相交。只要检查速度就行了
// 如果多边形不相交,也不会相交,退出循环
if (!isIntersecting)
return false;
// 检查当前间隔距离是否为最小值。如果是,则存储间隔距离和当前距离。这将用于计算最小平移向量
intervalDist = Math.abs(intervalDist);
if (intervalDist < minIntervalDistance) {
minIntervalDistance = intervalDist;
translationAxis.setTo(axis.x, axis.y);
if (translationAxis.dot(polygonOffset) < 0)
translationAxis = translationAxis.scale(-1);
}
}
// 利用最小平移向量对多边形进行推入。
result.normal = translationAxis;
result.minimumTranslationVector = translationAxis.scale(-minIntervalDistance);
return true;
}
/**
* 计算一个多边形在一个轴上的投影,并返回一个[minmax]区间
* @param axis
* @param polygon
* @param min
* @param max
*/
public static getInterval(axis: Vector2, polygon: Polygon): {min: number, max: number} {
const res = {min: 0, max: 0};
let dot: number;
dot = polygon.points[0].dot(axis);
res.max = dot;
res.min = dot;
for (let i = 1; i < polygon.points.length; i++) {
dot = polygon.points[i].dot(axis);
if (dot < res.min) {
res.min = dot;
} else if (dot > res.max) {
res.max = dot;
}
}
return res;
}
/**
* 计算[minA, maxA]和[minB, maxB]之间的距离。如果间隔重叠,距离是负的
* @param minA
* @param maxA
* @param minB
* @param maxB
*/
public static intervalDistance(minA: number, maxA: number, minB: number, maxB: number) {
if (minA < minB)
return minB - maxA;
return minA - maxB;
}
}
}