@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
187 lines (133 loc) • 5.24 kB
JavaScript
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;
}