jakke-graphics-ts
Version:
My common graphics utils for building my aec apps.
229 lines • 14.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CollisionUtils = void 0;
const vectorUtils_1 = require("./vectorUtils");
const lineEvaluationUtils_1 = require("./lineEvaluationUtils");
const ZERO_VECTOR_TOLERANCE = 1e-6;
const UNIT_VECTOR_TOLERANCE = 1e-6;
const COLLISION_TOLERANCE = 1e-6;
function hasBoundingBoxCollision2d(boxA, boxB, includeContating = false) {
// Filter the case when zero vectors are given.
if (vectorUtils_1.VectorUtils.getSize(Object.assign(Object.assign({}, boxA.uAxis), { z: 0 })) <= ZERO_VECTOR_TOLERANCE || vectorUtils_1.VectorUtils.getSize(Object.assign(Object.assign({}, boxB.uAxis), { z: 0 })) <= ZERO_VECTOR_TOLERANCE) {
return { result: false, hasError: true, message: "The vector of each boundingbox should not be zero." };
}
const uAxisOfA = vectorUtils_1.VectorUtils.normalize(Object.assign(Object.assign({}, boxA.uAxis), { z: 0 }));
const vAxisOfA = vectorUtils_1.VectorUtils.rotateOnXY(Object.assign(Object.assign({}, uAxisOfA), { z: 0 }), Math.PI * 0.5);
const uAxisOfB = vectorUtils_1.VectorUtils.normalize(Object.assign(Object.assign({}, boxB.uAxis), { z: 0 }));
const vAxisOfB = vectorUtils_1.VectorUtils.rotateOnXY(Object.assign(Object.assign({}, uAxisOfB), { z: 0 }), Math.PI * 0.5);
const axes = [uAxisOfA, vAxisOfA, uAxisOfB, vAxisOfB];
// Get all pts of A
const p0OfA = Object.assign(Object.assign({}, boxA.anchor), { z: 0 });
const p1OfA = vectorUtils_1.VectorUtils.add(Object.assign(Object.assign({}, boxA.anchor), { z: 0 }), vectorUtils_1.VectorUtils.scale(Object.assign(Object.assign({}, uAxisOfA), { z: 0 }), boxA.length.u));
const p3OfA = vectorUtils_1.VectorUtils.add(Object.assign(Object.assign({}, boxA.anchor), { z: 0 }), vectorUtils_1.VectorUtils.scale(Object.assign(Object.assign({}, vAxisOfA), { z: 0 }), boxA.length.v));
const p2OfA = vectorUtils_1.VectorUtils.add(p1OfA, vectorUtils_1.VectorUtils.scale(Object.assign(Object.assign({}, vAxisOfA), { z: 0 }), boxA.length.v));
// Get all pts of B
const p0OfB = Object.assign(Object.assign({}, boxB.anchor), { z: 0 });
const p1OfB = vectorUtils_1.VectorUtils.add(Object.assign(Object.assign({}, boxB.anchor), { z: 0 }), vectorUtils_1.VectorUtils.scale(Object.assign(Object.assign({}, uAxisOfB), { z: 0 }), boxB.length.u));
const p3OfB = vectorUtils_1.VectorUtils.add(Object.assign(Object.assign({}, boxB.anchor), { z: 0 }), vectorUtils_1.VectorUtils.scale(Object.assign(Object.assign({}, vAxisOfB), { z: 0 }), boxB.length.v));
const p2OfB = vectorUtils_1.VectorUtils.add(p1OfB, vectorUtils_1.VectorUtils.scale(Object.assign(Object.assign({}, vAxisOfB), { z: 0 }), boxB.length.v));
const collisionResult = axes.map(axis => {
// Get all foot point on axis of A
const ptsOnAxisFromA = [
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p0OfA),
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p1OfA),
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p2OfA),
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p3OfA),
];
const ptsOnAxisFromB = [
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p0OfB),
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p1OfB),
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p2OfB),
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p3OfB),
];
// Filter the case when pts has undefined;
const checkUndefinedPtsA = ptsOnAxisFromA.every(p => p !== undefined);
const checkUndefinedPtsB = ptsOnAxisFromB.every(p => p !== undefined);
if (!checkUndefinedPtsA || !checkUndefinedPtsB) {
return { hasError: true, hasCollision: false };
}
const sortedParamsFromA = ptsOnAxisFromA
.sort((a, b) => a.t - b.t)
.map(a => a.t);
const sortedParamsFromB = ptsOnAxisFromB
.sort((a, b) => a.t - b.t)
.map(a => a.t);
const sectionByA = [sortedParamsFromA[0], sortedParamsFromA[3]];
const sectionByB = [sortedParamsFromB[0], sortedParamsFromB[3]];
let isSeparated;
if (includeContating) {
isSeparated = sectionByA[1] < sectionByB[0] - COLLISION_TOLERANCE || sectionByB[1] < sectionByA[0] - COLLISION_TOLERANCE;
}
else {
isSeparated = sectionByA[1] < sectionByB[0] + COLLISION_TOLERANCE || sectionByB[1] < sectionByA[0] + COLLISION_TOLERANCE;
}
return { hasError: false, hasCollision: !isSeparated, sectionByA, sectionByB };
});
return {
result: collisionResult.every(result => !result.hasError && result.hasCollision),
args: collisionResult.map(result => {
return {
setionByA: result.sectionByA,
setionByB: result.sectionByB,
};
})
};
}
function hasBoundingBoxCollision3d(boxA, boxB, includeContating = false) {
// Filter the case when zero vectors are given.
const hasZeroVectors = [
boxA.uAxis,
boxA.vAxis,
boxB.uAxis,
boxB.vAxis,
].some(axis => vectorUtils_1.VectorUtils.getSize(axis) <= ZERO_VECTOR_TOLERANCE);
if (hasZeroVectors) {
return { result: false, hasError: true, message: "Some axes are zero vector." };
}
// Filter the case when u, v axes are not perpendicular.
const uAxisA = vectorUtils_1.VectorUtils.normalize(boxA.uAxis);
const vAxisA = vectorUtils_1.VectorUtils.normalize(boxA.vAxis);
const uAxisB = vectorUtils_1.VectorUtils.normalize(boxB.uAxis);
const vAxisB = vectorUtils_1.VectorUtils.normalize(boxB.vAxis);
const isPerpendicularA = Math.abs(vectorUtils_1.VectorUtils.dot(uAxisA, vAxisA)) < UNIT_VECTOR_TOLERANCE;
const isPerpendicularB = Math.abs(vectorUtils_1.VectorUtils.dot(uAxisB, vAxisB)) < UNIT_VECTOR_TOLERANCE;
if (!isPerpendicularA || !isPerpendicularB) {
return { result: false, hasError: true, message: "U, V axis of each box should be perpendicular to each other." };
}
const nAxisA = vectorUtils_1.VectorUtils.normalize(vectorUtils_1.VectorUtils.cross(uAxisA, vAxisA));
const nAxisB = vectorUtils_1.VectorUtils.normalize(vectorUtils_1.VectorUtils.cross(uAxisB, vAxisB));
const p0A = Object.assign({}, boxA.anchor);
const p1A = vectorUtils_1.VectorUtils.add(Object.assign({}, p0A), vectorUtils_1.VectorUtils.scale(uAxisA, boxA.length.u));
const p2A = vectorUtils_1.VectorUtils.add(Object.assign({}, p1A), vectorUtils_1.VectorUtils.scale(vAxisA, boxA.length.v));
const p3A = vectorUtils_1.VectorUtils.add(Object.assign({}, p0A), vectorUtils_1.VectorUtils.scale(vAxisA, boxA.length.v));
const p4A = vectorUtils_1.VectorUtils.add(Object.assign({}, p0A), vectorUtils_1.VectorUtils.scale(nAxisA, boxA.length.n));
const p5A = vectorUtils_1.VectorUtils.add(Object.assign({}, p1A), vectorUtils_1.VectorUtils.scale(nAxisA, boxA.length.n));
const p6A = vectorUtils_1.VectorUtils.add(Object.assign({}, p2A), vectorUtils_1.VectorUtils.scale(nAxisA, boxA.length.n));
const p7A = vectorUtils_1.VectorUtils.add(Object.assign({}, p3A), vectorUtils_1.VectorUtils.scale(nAxisA, boxA.length.n));
const p0B = Object.assign({}, boxB.anchor);
const p1B = vectorUtils_1.VectorUtils.add(Object.assign({}, p0B), vectorUtils_1.VectorUtils.scale(uAxisB, boxB.length.u));
const p2B = vectorUtils_1.VectorUtils.add(Object.assign({}, p1B), vectorUtils_1.VectorUtils.scale(vAxisB, boxB.length.v));
const p3B = vectorUtils_1.VectorUtils.add(Object.assign({}, p0B), vectorUtils_1.VectorUtils.scale(vAxisB, boxB.length.v));
const p4B = vectorUtils_1.VectorUtils.add(Object.assign({}, p0B), vectorUtils_1.VectorUtils.scale(nAxisB, boxB.length.n));
const p5B = vectorUtils_1.VectorUtils.add(Object.assign({}, p1B), vectorUtils_1.VectorUtils.scale(nAxisB, boxB.length.n));
const p6B = vectorUtils_1.VectorUtils.add(Object.assign({}, p2B), vectorUtils_1.VectorUtils.scale(nAxisB, boxB.length.n));
const p7B = vectorUtils_1.VectorUtils.add(Object.assign({}, p3B), vectorUtils_1.VectorUtils.scale(nAxisB, boxB.length.n));
const axes = [uAxisA, vAxisA, nAxisA, uAxisB, vAxisB, nAxisB];
const collisionResult = axes.map(axis => {
const ptsOnAxisFromA = [
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p0A),
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p1A),
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p2A),
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p3A),
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p4A),
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p5A),
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p6A),
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p7A),
];
const ptsOnAxisFromB = [
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p0B),
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p1B),
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p2B),
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p3B),
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p4B),
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p5B),
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p6B),
lineEvaluationUtils_1.LineEvaluation.getFootPointOnDirection(axis, p7B),
];
// Filter the case when pts has undefined;
const checkUndefinedPtsA = ptsOnAxisFromA.every(p => p !== undefined);
const checkUndefinedPtsB = ptsOnAxisFromB.every(p => p !== undefined);
if (!checkUndefinedPtsA || !checkUndefinedPtsB) {
return { hasError: true, hasCollision: false };
}
const sortedParamsFromA = ptsOnAxisFromA
.sort((a, b) => a.t - b.t)
.map(a => a.t);
const sortedParamsFromB = ptsOnAxisFromB
.sort((a, b) => a.t - b.t)
.map(a => a.t);
const sectionByA = [sortedParamsFromA[0], sortedParamsFromA[7]];
const sectionByB = [sortedParamsFromB[0], sortedParamsFromB[7]];
let isSeparated;
if (includeContating) {
isSeparated = sectionByA[1] < sectionByB[0] - COLLISION_TOLERANCE || sectionByB[1] < sectionByA[0] - COLLISION_TOLERANCE;
}
else {
isSeparated = sectionByA[1] < sectionByB[0] + COLLISION_TOLERANCE || sectionByB[1] < sectionByA[0] + COLLISION_TOLERANCE;
}
return { hasError: false, hasCollision: !isSeparated, sectionByA, sectionByB };
});
const containsError = collisionResult.some(r => r.hasError);
const containSeparated = collisionResult.some(r => !r.hasCollision);
return {
result: !containSeparated,
hasError: containsError,
args: collisionResult.map(result => {
return {
setionByA: result.sectionByA,
setionByB: result.sectionByB,
};
})
};
}
function getCollisionLineWithTriangleEdges(line, triangle) {
const AB = { p0: triangle.p0, p1: triangle.p1 };
const BC = { p0: triangle.p1, p1: triangle.p2 };
const CA = { p0: triangle.p2, p1: triangle.p0 };
const intersectionOnAB = lineEvaluationUtils_1.LineEvaluation.getIntersection(line, AB);
const intersectionOnBC = lineEvaluationUtils_1.LineEvaluation.getIntersection(line, BC);
const intersectionOnCA = lineEvaluationUtils_1.LineEvaluation.getIntersection(line, CA);
const pts = [];
if (intersectionOnAB.result && intersectionOnAB.pt)
pts.push(intersectionOnAB.pt);
if (intersectionOnBC.result && intersectionOnBC.pt)
pts.push(intersectionOnBC.pt);
if (intersectionOnCA.result && intersectionOnCA.pt)
pts.push(intersectionOnCA.pt);
const sortedPts = pts.map(p => {
return { t: lineEvaluationUtils_1.LineEvaluation.getParameterOnLine(line.p0, line.p1, p), p };
}).sort((a, b) => a.t - b.t);
return sortedPts;
}
function getCollisionLineWithTriangleSurface(line, triangle) {
if (isDetZero(line, triangle))
return;
const v0 = triangle.p0;
const v1 = triangle.p1;
const v2 = triangle.p2;
const p0 = line.p0;
const p1 = line.p1;
const v0v1 = vectorUtils_1.VectorUtils.subtract(v1, v0);
const v0v2 = vectorUtils_1.VectorUtils.subtract(v2, v0);
const v0p0 = vectorUtils_1.VectorUtils.subtract(p0, v0);
const d = vectorUtils_1.VectorUtils.chain(p1).subtract(p0).normalize().value();
const det = vectorUtils_1.VectorUtils.dot(v0v1, vectorUtils_1.VectorUtils.cross(d, v0v2));
const v = vectorUtils_1.VectorUtils.dot(d, vectorUtils_1.VectorUtils.cross(v0v2, v0p0)) / det;
const w = vectorUtils_1.VectorUtils.dot(d, vectorUtils_1.VectorUtils.cross(v0p0, v0v1)) / det;
const u = 1 - (v + w);
const result = vectorUtils_1.VectorUtils.chain(v0)
.scale(u)
.add(vectorUtils_1.VectorUtils.scale(v1, v))
.add(vectorUtils_1.VectorUtils.scale(v2, w))
.value();
const validParams = [u, v, w].every(val => val >= 0 && val <= 1);
return validParams ? result : undefined;
}
function isDetZero(line, triangle) {
const v0v1 = vectorUtils_1.VectorUtils.subtract(triangle.p1, triangle.p0);
const v0v2 = vectorUtils_1.VectorUtils.subtract(triangle.p2, triangle.p0);
const d = vectorUtils_1.VectorUtils.subtract(line.p1, line.p0);
const det = vectorUtils_1.VectorUtils.dot(v0v1, vectorUtils_1.VectorUtils.cross(d, v0v2));
return det === 0;
}
exports.CollisionUtils = {
hasBoundingBoxCollision2d,
hasBoundingBoxCollision3d,
getCollisionLineWithTriangleEdges,
getCollisionLineWithTriangleSurface
};
//# sourceMappingURL=collisionUtils.js.map