项目文件夹

文件

223 行
8.3 KiB
JavaScript

/**
* Triangle Collision Detection Module
* Implements algorithms for detecting overlap between triangles
*/
/**
* Represents a triangle with three vertices
* @typedef {Object} Triangle
* @property {number} x1 - x-coordinate of first vertex
* @property {number} y1 - y-coordinate of first vertex
* @property {number} x2 - x-coordinate of second vertex
* @property {number} y2 - y-coordinate of second vertex
* @property {number} x3 - x-coordinate of third vertex
* @property {number} y3 - y-coordinate of third vertex
*/
/**
* Determines if a point is inside a triangle using barycentric coordinates
* @param {number} px - x-coordinate of the point
* @param {number} py - y-coordinate of the point
* @param {Triangle} triangle - The triangle to check against
* @returns {boolean} - True if the point is inside the triangle
*/
export function isPointInTriangle(px, py, triangle) {
const { x1, y1, x2, y2, x3, y3 } = triangle;
// Calculate barycentric coordinates
const denominator = ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3));
// Avoid division by zero
if (Math.abs(denominator) < 0.0001) return false;
const a = ((y2 - y3) * (px - x3) + (x3 - x2) * (py - y3)) / denominator;
const b = ((y3 - y1) * (px - x3) + (x1 - x3) * (py - y3)) / denominator;
const c = 1 - a - b;
// Point is inside if all barycentric coordinates are between 0 and 1
return a >= 0 && a <= 1 && b >= 0 && b <= 1 && c >= 0 && c <= 1;
}
/**
* Checks if a line segment intersects with another line segment
* @param {number} x1 - x-coordinate of first point of first line
* @param {number} y1 - y-coordinate of first point of first line
* @param {number} x2 - x-coordinate of second point of first line
* @param {number} y2 - y-coordinate of second point of first line
* @param {number} x3 - x-coordinate of first point of second line
* @param {number} y3 - y-coordinate of first point of second line
* @param {number} x4 - x-coordinate of second point of second line
* @param {number} y4 - y-coordinate of second point of second line
* @returns {boolean} - True if the line segments intersect
*/
export function doLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4) {
// Special case: Check if the lines are actually the same line segment
if ((x1 === x3 && y1 === y3 && x2 === x4 && y2 === y4) ||
(x1 === x4 && y1 === y4 && x2 === x3 && y2 === y3)) {
return true;
}
// Check for the case where one of the endpoints of one line is on the other line
// This is a more robust approach for detecting intersections
// Function to check if point (px,py) is on line segment (x1,y1)-(x2,y2)
function isPointOnLine(px, py, lx1, ly1, lx2, ly2) {
// Check if point is within the bounding box of the line segment
if (px < Math.min(lx1, lx2) - 0.0001 || px > Math.max(lx1, lx2) + 0.0001 ||
py < Math.min(ly1, ly2) - 0.0001 || py > Math.max(ly1, ly2) + 0.0001) {
return false;
}
// Check if point is on the line using cross product
const cross = (py - ly1) * (lx2 - lx1) - (px - lx1) * (ly2 - ly1);
return Math.abs(cross) < 0.0001;
}
// Check if any endpoint of one line is on the other line
if (isPointOnLine(x1, y1, x3, y3, x4, y4) ||
isPointOnLine(x2, y2, x3, y3, x4, y4) ||
isPointOnLine(x3, y3, x1, y1, x2, y2) ||
isPointOnLine(x4, y4, x1, y1, x2, y2)) {
return true;
}
// Calculate the direction of the lines
const d1x = x2 - x1;
const d1y = y2 - y1;
const d2x = x4 - x3;
const d2y = y4 - y3;
// Calculate the determinant
const denominator = d1y * d2x - d1x * d2y;
// Lines are parallel if denominator is close to 0
if (Math.abs(denominator) < 0.0001) return false;
// Calculate the parameters for the intersection point
const d3x = x1 - x3;
const d3y = y1 - y3;
const t1 = (d2x * d3y - d2y * d3x) / denominator;
const t2 = (d1x * d3y - d1y * d3x) / denominator;
// Check if the intersection point is within both line segments
return t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1;
}
/**
* Checks if two triangles overlap using vertex inclusion and edge intersection tests
* @param {Triangle} triangle1 - First triangle
* @param {Triangle} triangle2 - Second triangle
* @returns {boolean} - True if the triangles overlap
*/
export function doTrianglesOverlap(triangle1, triangle2) {
// Null check for triangles
if (!triangle1 || !triangle2) return false;
// Ensure all properties exist
const t1Props = ['x1', 'y1', 'x2', 'y2', 'x3', 'y3'];
const t2Props = ['x1', 'y1', 'x2', 'y2', 'x3', 'y3'];
for (const prop of t1Props) {
if (triangle1[prop] === undefined) return false;
}
for (const prop of t2Props) {
if (triangle2[prop] === undefined) return false;
}
// Check if any vertex of triangle1 is inside triangle2
if (isPointInTriangle(triangle1.x1, triangle1.y1, triangle2) ||
isPointInTriangle(triangle1.x2, triangle1.y2, triangle2) ||
isPointInTriangle(triangle1.x3, triangle1.y3, triangle2)) {
return true;
}
// Check if any vertex of triangle2 is inside triangle1
if (isPointInTriangle(triangle2.x1, triangle2.y1, triangle1) ||
isPointInTriangle(triangle2.x2, triangle2.y2, triangle1) ||
isPointInTriangle(triangle2.x3, triangle2.y3, triangle1)) {
return true;
}
// Check if any edge of triangle1 intersects with any edge of triangle2
const edges1 = [
[triangle1.x1, triangle1.y1, triangle1.x2, triangle1.y2],
[triangle1.x2, triangle1.y2, triangle1.x3, triangle1.y3],
[triangle1.x3, triangle1.y3, triangle1.x1, triangle1.y1]
];
const edges2 = [
[triangle2.x1, triangle2.y1, triangle2.x2, triangle2.y2],
[triangle2.x2, triangle2.y2, triangle2.x3, triangle2.y3],
[triangle2.x3, triangle2.y3, triangle2.x1, triangle2.y1]
];
for (const [x1, y1, x2, y2] of edges1) {
for (const [x3, y3, x4, y4] of edges2) {
if (doLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4)) {
return true;
}
}
}
return false;
}
/**
* Creates a triangle with the given center point and size
* @param {number} x - x-coordinate of the center
* @param {number} y - y-coordinate of the center
* @param {number} size - Size of the triangle (distance from center to vertices)
* @param {number} rotation - Rotation angle in radians
* @returns {Triangle} - A triangle object
*/
export function createTriangle(x, y, size, rotation = 0) {
// Create an equilateral triangle with a slightly larger size for better collision detection
// Using 1.1 * size to make the collision area slightly larger than the visual triangle
const collisionSize = size * 1.1; // Slightly larger collision area
const angle1 = rotation;
const angle2 = rotation + (2 * Math.PI / 3);
const angle3 = rotation + (4 * Math.PI / 3);
return {
x1: x + collisionSize * Math.cos(angle1),
y1: y + collisionSize * Math.sin(angle1),
x2: x + collisionSize * Math.cos(angle2),
y2: y + collisionSize * Math.sin(angle2),
x3: x + collisionSize * Math.cos(angle3),
y3: y + collisionSize * Math.sin(angle3)
};
}
/**
* Calculates the area of a triangle
* @param {Triangle} triangle - The triangle
* @returns {number} - The area of the triangle
*/
export function calculateTriangleArea(triangle) {
const { x1, y1, x2, y2, x3, y3 } = triangle;
return Math.abs((x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2);
}
/**
* Checks if a point is inside a convex polygon
* @param {number} x - x-coordinate of the point
* @param {number} y - y-coordinate of the point
* @param {Array<{x: number, y: number}>} polygon - Array of polygon vertices
* @returns {boolean} - True if the point is inside the polygon
*/
export function isPointInPolygon(x, y, polygon) {
let inside = false;
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
const xi = polygon[i].x, yi = polygon[i].y;
const xj = polygon[j].x, yj = polygon[j].y;
const intersect = ((yi > y) !== (yj > y)) &&
(x < (xj - xi) * (y - yi) / (yj - yi) + xi);
if (intersect) inside = !inside;
}
return inside;
}