UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

187 lines (133 loc) 5.24 kB
import { noop } from "../../../function/noop.js"; import { compute_tetrahedron_volume } from "./compute_tetrahedron_volume.js"; import { is_tetrahedron_degenerate } from "./is_tetrahedron_degenerate.js"; import { INVALID_NEIGHBOUR } from "./TetrahedralMesh.js"; /** * * @param {TetrahedralMesh} mesh * @param {number} tet * @param {number} neighbour_id * @param {function(string):*} consumer * @return {boolean} */ export function validate_neighbour(mesh, tet, neighbour_id, consumer) { let valid = true; const neighbour = mesh.getNeighbour(tet, neighbour_id); if (neighbour === INVALID_NEIGHBOUR) { return true; } // decode neighbour const neighbour_tet = neighbour >> 2; const neighbour_vertex_id = neighbour & 3; if (!mesh.exists(neighbour_tet)) { consumer(`Tetrahedron ${tet} Neighbour[${neighbour_id}] ${neighbour_tet} does not exist in the mesh`); return false; } const back_link = mesh.getNeighbour(neighbour_tet, neighbour_vertex_id); if (back_link === INVALID_NEIGHBOUR) { consumer(`Tetrahedron ${tet} Neighbour[${neighbour_id}] ${neighbour_tet} links back to invalid neighbour (no neighbour), expected ${tet} instead`); return false; } const back_link_tet = back_link >> 2; const back_link_vertex_index = back_link & 3; if (back_link_tet !== tet) { valid = false; consumer(`Tetrahedron ${tet} Neighbour[${neighbour_id}] ${neighbour_tet} links back to a different tet, expected ${tet}, got ${back_link_tet}`) } else if (back_link_vertex_index !== neighbour_id) { valid = false; consumer(`Tetrahedron ${tet} Neighbour[${neighbour_id}] ${neighbour_tet} links back to a different vertex, expected ${neighbour_id}, got ${back_link_vertex_index}`); } for (let j = 0; j < 4; j++) { const v = mesh.getVertexIndex(tet, j); if (j === neighbour_id) { continue; } if (!mesh.tetContainsVertex(neighbour_tet, v)) { valid = false; consumer(`Tetrahedron ${tet} Neighbour[${neighbour_id}] ${neighbour_tet} does not share vertex ${v} with the tet`); } } return valid; } /** * * @param {TetrahedralMesh} mesh * @param {number} tet * @param {function(problem:string):*} consumer */ export function validate_tetrahedron_neighbourhood(mesh, tet, consumer = noop) { let valid = true; for (let i = 0; i < 4; i++) { if (!validate_neighbour(mesh, tet, i, consumer)) { valid = false; } } // validate all neighbours together for (let i = 0; i < 3; i++) { const neighbour_a = mesh.getNeighbour(tet, i); if (neighbour_a === INVALID_NEIGHBOUR) { continue; } const tet_a = neighbour_a >>> 2; for (let j = i+1; j < 4; j++) { const neighbour_b = mesh.getNeighbour(tet, j); if (neighbour_b === INVALID_NEIGHBOUR) { continue; } const tet_b = neighbour_b >>> 2; if (tet_a === tet_b) { consumer(`Tetrahedron ${tet} points to the same neighbour tetrahedron ${tet_a} for neighbour index ${i} and ${j}. Each neighbour tet must be unique`) valid = false; } } } return valid; } /** * * @param {TetrahedralMesh} mesh * @param {number} tet * @param {number} max_index * @param {function(problem:string):*} consumer */ function validate_tetrahedron_point_index_range(mesh, tet, max_index, consumer) { let valid = true; for (let i = 0; i < 4; i++) { const vertex_index = mesh.getVertexIndex(tet, i); if (vertex_index > max_index) { consumer(`Tetrahedron ${tet} points to non-existing vertex ${vertex_index} as it's ordinal vertex ${i}. Maximum allowed vertex index is ${max_index}`); valid = false; } } return valid; } /** * * @param {TetrahedralMesh} mesh * @param {ArrayLike<number>|number[]|Float32Array} points * @param {function(problem:string):*} [consumer] * @returns {boolean} */ export function validate_tetrahedral_mesh(mesh, points, consumer = noop) { let is_valid = true; const max_point_index = (points.length / 3) - 1; mesh.forEach((tet, mesh) => { if (!validate_tetrahedron_point_index_range(mesh, tet, max_point_index, consumer)) { is_valid = false; } const volume = compute_tetrahedron_volume(mesh, points, tet); if (volume < 0) { is_valid = false; consumer(`Tetrahedron ${tet} has negative volume ${volume}`); } // check for degeneracy if (is_tetrahedron_degenerate(mesh, tet)) { is_valid = false; consumer(`Tetrahedron ${tet} is degenerate (multiple vertices have the same index)`); } if (!validate_tetrahedron_neighbourhood(mesh, tet, consumer)) { is_valid = false; } }); return is_valid; }