UNPKG

jakke-graphics-ts

Version:

My common graphics utils for building my aec apps.

229 lines 14.2 kB
"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