@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
182 lines (181 loc) • 6.93 kB
JavaScript
"use strict";
import { Vector3, Triangle } from "three";
import { TetGeometry } from "../TetGeometry";
import { TET_VERTICES_V_BASE } from "../TetCommon";
import { findTetContainingPosition } from "./findTetContainingPosition";
import { tetCenter } from "./tetCenter";
import { findNonDelaunayTetsFromSinglePointCheck } from "./findNonDelaunayTets";
import { isPositionInsideMesh } from "./tetInsideMesh";
import { setFirstValue } from "../../../../SetUtils";
import { tetRemoveUnusedPoints } from "./tetRemoveUnusedPoints";
import { jitterOffset } from "../../../operation/Jitter";
import { setToArray } from "../../../../SetUtils";
import { tetQuality } from "./tetQuality";
const _v = new Vector3();
const _jitterOffset = new Vector3();
const _bboxSize = new Vector3();
const _triangle = new Triangle();
const _faceNormal = new Vector3();
const _newPtDelta = new Vector3();
const _containingTetSearchRayOrigin = new Vector3();
const sharedFacesNeighbourData = /* @__PURE__ */ new Set();
const invalidTets = [];
const jitterMult = new Vector3(1, 1, 1);
function addPoint(tetGeometry, newPointPosition, searchStartPosition, tetIdOrigin, stage) {
const tetId = findTetContainingPosition(tetGeometry, newPointPosition, searchStartPosition, tetIdOrigin);
if (tetId == null) {
return;
}
findNonDelaunayTetsFromSinglePointCheck(tetGeometry, tetId, newPointPosition, invalidTets);
tetGeometry.removeTets(invalidTets, sharedFacesNeighbourData, newPointPosition);
_stage++;
if (stage != null && _stage > stage) {
return tetGeometry;
}
const pointId = tetGeometry.addPoint(newPointPosition.x, newPointPosition.y, newPointPosition.z);
sharedFacesNeighbourData.forEach((sharedFacesNeighbourData2) => {
_stage++;
if (stage != null && _stage > stage) {
return tetGeometry;
}
const id0 = sharedFacesNeighbourData2.pointIds[0];
const id1 = sharedFacesNeighbourData2.pointIds[1];
const id2 = sharedFacesNeighbourData2.pointIds[2];
const pt0 = tetGeometry.points.get(id0);
const pt1 = tetGeometry.points.get(id1);
const pt2 = tetGeometry.points.get(id2);
if (pt0 && pt1 && pt2) {
_triangle.a.copy(pt0.position);
_triangle.b.copy(pt1.position);
_triangle.c.copy(pt2.position);
_triangle.getNormal(_faceNormal);
_newPtDelta.copy(newPointPosition).sub(_triangle.a);
const dot = _newPtDelta.dot(_faceNormal);
if (dot > 0) {
tetGeometry.addTetrahedron(id0, id1, id2, pointId);
} else {
tetGeometry.addTetrahedron(pointId, id0, id1, id2);
}
}
});
if (stage != null && _stage > stage) {
return tetGeometry;
}
}
const _outsideTestPos = new Vector3();
const _tetIds = [];
function finalize(options) {
const { tetGeometry, mesh, deleteOutsideTets, minQuality } = options;
const idsToDelete = /* @__PURE__ */ new Set();
if (minQuality > 0) {
tetGeometry.tetrahedrons.forEach((tet, tetId) => {
if (tetQuality(tetGeometry, tetId) < minQuality) {
idsToDelete.add(tetId);
}
});
}
if (deleteOutsideTets) {
tetGeometry.tetrahedrons.forEach((tet, tetId) => {
tetCenter(tetGeometry, tetId, _outsideTestPos);
const isInside = isPositionInsideMesh(_outsideTestPos, mesh, 1e-3);
if (!isInside) {
idsToDelete.add(tetId);
}
});
}
setToArray(idsToDelete, _tetIds);
tetGeometry.removeTets(_tetIds);
return tetRemoveUnusedPoints(tetGeometry);
}
function prepareInputPoints(options) {
const { mesh, innerPointsResolution, jitterAmount } = options;
const { geometry } = mesh;
const inputPoints = [];
const geoPositionAttribute = geometry.attributes.position;
const pointsCount = geoPositionAttribute.count;
for (let i2 = 0; i2 < pointsCount; i2++) {
const newPos = new Vector3();
newPos.fromBufferAttribute(geoPositionAttribute, i2);
inputPoints.push(newPos);
}
geometry.computeBoundingBox();
if (!geometry.boundingBox) {
return inputPoints;
}
const { min } = geometry.boundingBox;
geometry.boundingBox.getSize(_bboxSize);
const minDim = Math.min(_bboxSize.x, _bboxSize.y, _bboxSize.z);
const minStep = minDim / innerPointsResolution;
let i = 0;
for (let xi = 0; xi < innerPointsResolution; xi++) {
for (let yi = 0; yi < innerPointsResolution; yi++) {
for (let zi = 0; zi < innerPointsResolution; zi++) {
jitterOffset(i, 11, jitterMult, jitterAmount, _jitterOffset);
_v.set(xi, yi, zi).divideScalar(innerPointsResolution).multiply(_bboxSize).add(min).add(_jitterOffset);
if (isPositionInsideMesh(_v, mesh, minStep)) {
inputPoints.push(_v.clone());
}
i++;
}
}
}
return inputPoints;
}
function getNearestPoint(inputPoints, inputPoint) {
let nearestPoint;
let nearestDistance = Infinity;
inputPoints.forEach((point) => {
const distance = point.distanceTo(inputPoint);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestPoint = point;
}
});
return nearestPoint;
}
let _stage = 0;
export function tetrahedralize(options) {
var _a;
_stage = 0;
const { mesh, stage, deleteOutsideTets, minQuality } = options;
const { geometry } = mesh;
const tetGeometry = new TetGeometry();
geometry.computeBoundingSphere();
const radius = ((_a = geometry.boundingSphere) == null ? void 0 : _a.radius) || 1;
const s = 5 * radius;
_v.copy(TET_VERTICES_V_BASE[0]).multiplyScalar(s);
const id0 = tetGeometry.addPoint(_v.x, _v.y, _v.z);
_v.copy(TET_VERTICES_V_BASE[1]).multiplyScalar(s);
const id1 = tetGeometry.addPoint(_v.x, _v.y, _v.z);
_v.copy(TET_VERTICES_V_BASE[2]).multiplyScalar(s);
const id2 = tetGeometry.addPoint(_v.x, _v.y, _v.z);
_v.copy(TET_VERTICES_V_BASE[3]).multiplyScalar(s);
const id3 = tetGeometry.addPoint(_v.x, _v.y, _v.z);
const firstTetId = tetGeometry.addTetrahedron(id0, id1, id2, id3);
if (firstTetId == null) {
return tetGeometry;
}
_stage++;
if (stage != null && _stage > stage) {
return finalize({ tetGeometry, mesh, deleteOutsideTets, minQuality });
}
const inputPoints = new Set(prepareInputPoints(options));
tetCenter(tetGeometry, firstTetId, _containingTetSearchRayOrigin);
let tetIdOrigin = firstTetId;
let inputPoint = setFirstValue(inputPoints);
while (inputPoint != null) {
addPoint(tetGeometry, inputPoint, _containingTetSearchRayOrigin, tetIdOrigin, stage);
const lastAddedTetId = tetGeometry.lastAddedTetId();
if (lastAddedTetId != null) {
tetCenter(tetGeometry, lastAddedTetId, _containingTetSearchRayOrigin);
tetIdOrigin = lastAddedTetId;
}
_stage++;
if (stage != null && _stage > stage) {
return finalize({ tetGeometry, mesh, deleteOutsideTets, minQuality });
}
inputPoints.delete(inputPoint);
inputPoint = getNearestPoint(inputPoints, inputPoint);
}
return finalize({ tetGeometry, mesh, deleteOutsideTets, minQuality });
}