UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

255 lines (236 loc) 7.28 kB
import {Number4} from '../../../../types/GlobalTypes'; import {mapFirstKey} from '../../../MapUtils'; import { TetrahedronPoint, Tetrahedron, TetNeighbourData, TetNeighbourDatas, TetNeighbourDataWithSource, TET_FACE_POINT_INDICES, } from './TetCommon'; import {updateTetNeighboursFromNewTet} from './utils/tetNeighboursHelper'; import {Vector3, Triangle, Matrix4, Box3, Sphere} from 'three'; import {circumSphere} from './utils/tetSphere'; import {tetFaceTriangle} from './utils/tetTriangle'; import {logRedBg} from '../../../logger/Console'; import {objectCloneDeep} from '../../../ObjectUtils'; const _triangle = new Triangle(); const _triangleNormal = new Vector3(); const _newPointDelta = new Vector3(); export class TetGeometry { public readonly tetrahedrons: Map<number, Tetrahedron> = new Map(); public readonly points: Map<number, TetrahedronPoint> = new Map(); public readonly tetrahedronsByPointId: Map<number, Set<number>> = new Map(); private _nextPointId = -1; private _nextTetId = -1; private _pointsCount = 0; private _tetsCount = 0; private _lastAddedTetId: number | null = null; public userData: {[key: string]: any} = {}; addPoint(x: number, y: number, z: number) { this._nextPointId++; const id = this._nextPointId; const point: TetrahedronPoint = { id, position: new Vector3(x, y, z), }; this._pointsCount++; this.points.set(point.id, point); return id; } removePoint(pointId: number) { this.points.delete(pointId); this.tetrahedronsByPointId.delete(pointId); this._pointsCount--; } pointsCount() { return this._pointsCount; } tetsCount() { return this._tetsCount; } firstTetId() { return mapFirstKey(this.tetrahedrons); } lastAddedTetId() { return this._lastAddedTetId; } addTetrahedron(p0: number, p1: number, p2: number, p3: number) { if (p0 == p1 || p0 == p2 || p0 == p3 || p1 == p2 || p1 == p3 || p2 == p3) { console.warn('tetrahedron has duplicate points', p0, p1, p2, p3); return; } this._nextTetId++; const id = this._nextTetId; const _circumSphere = {center: new Vector3(), radius: 0}; circumSphere(this, p0, p1, p2, p3, _circumSphere); const tetrahedron: Tetrahedron = { id, pointIds: [p0, p1, p2, p3], neighbours: [null, null, null, null], sphere: _circumSphere, disposed: false, }; this.tetrahedrons.set(tetrahedron.id, tetrahedron); this._tetsCount++; this._lastAddedTetId = tetrahedron.id; // update point keys for (const p of tetrahedron.pointIds) { let tetrahedrons = this.tetrahedronsByPointId.get(p); if (!tetrahedrons) { tetrahedrons = new Set(); this.tetrahedronsByPointId.set(p, tetrahedrons); } tetrahedrons.add(tetrahedron.id); } // update neighbours updateTetNeighboursFromNewTet(this, tetrahedron); return id; } removeTets( tetIds: number[], sharedFacesNeighbourData?: Set<TetNeighbourDataWithSource>, newPointPosition?: Vector3 ) { if (sharedFacesNeighbourData && newPointPosition) { sharedFacesNeighbourData.clear(); // 1. store neighbours for future tet reconstruction for (const tetId of tetIds) { const tetrahedron = this.tetrahedrons.get(tetId); if (!tetrahedron) { continue; } let faceIndex = 0; for (const neighbourData of tetrahedron.neighbours) { // if the neighbour is not one of the removed tets, // we can add it if (neighbourData == null || !tetIds.includes(neighbourData.id)) { let faceAvailableOnSideOfNewPoint = true; if (neighbourData && neighbourData.id != null) { tetFaceTriangle(this, neighbourData.id, neighbourData.faceIndex, _triangle); _triangle.getNormal(_triangleNormal); _newPointDelta.copy(_triangle.a).sub(newPointPosition); if (_triangleNormal.dot(_newPointDelta) > 0) { // logRedBg(`nope:${tetId}`); faceAvailableOnSideOfNewPoint = false; } } if (faceAvailableOnSideOfNewPoint) { const pointIndices = TET_FACE_POINT_INDICES[faceIndex]; sharedFacesNeighbourData.add({ // faceIndex, pointIds: [ tetrahedron.pointIds[pointIndices[0]], tetrahedron.pointIds[pointIndices[1]], tetrahedron.pointIds[pointIndices[2]], ], }); } } faceIndex++; } } } // 2. remove tets for (const tetId of tetIds) { const tetrahedron = this.tetrahedrons.get(tetId); if (!tetrahedron) { logRedBg(`tet not found:${tetId} (${tetIds})`); throw `removeTets: tet not found ${tetId}`; continue; } // update point keys for (const pointId of tetrahedron.pointIds) { const tetrahedrons = this.tetrahedronsByPointId.get(pointId); if (tetrahedrons) { tetrahedrons.delete(tetrahedron.id); // DO not remove points here, // as they are still needed later // if (tetrahedrons.size == 0) { // this._removePoint(pointId); // } } } // remove neighbours for (const neighbourData of tetrahedron.neighbours) { if (neighbourData != null) { const neighbourTet = this.tetrahedrons.get(neighbourData.id); if (neighbourTet) { const neighbourFaceIndex = neighbourData.faceIndex; // remove from neighbour neighbourTet.neighbours[neighbourFaceIndex] = null; } } } // remove tetrahedron.disposed = true; this.tetrahedrons.delete(tetId); this._tetsCount--; } } clone(): this { const clonedGeometry = new TetGeometry(); this.points.forEach((point, id) => { clonedGeometry.points.set(id, { id: point.id, position: point.position.clone(), }); }); this.tetrahedrons.forEach((tetrahedron, id) => { clonedGeometry.tetrahedrons.set(id, { id: tetrahedron.id, pointIds: tetrahedron.pointIds.map((id) => id) as Number4, neighbours: tetrahedron.neighbours.map((d) => { if (!d) { return null; } const tetNeighbourData: TetNeighbourData = { id: d.id, faceIndex: d.faceIndex, }; return tetNeighbourData; }) as TetNeighbourDatas, sphere: { center: tetrahedron.sphere.center.clone(), radius: tetrahedron.sphere.radius, }, disposed: tetrahedron.disposed, }); }); this.tetrahedronsByPointId.forEach((tetrahedrons, id) => { clonedGeometry.tetrahedronsByPointId.set(id, new Set(tetrahedrons)); }); clonedGeometry._nextPointId = this._nextPointId; clonedGeometry._nextTetId = this._nextTetId; clonedGeometry._pointsCount = this._pointsCount; clonedGeometry._tetsCount = this._tetsCount; clonedGeometry._lastAddedTetId = this._lastAddedTetId; clonedGeometry.userData = objectCloneDeep(this.userData); return clonedGeometry as this; } applyMatrix4(matrix: Matrix4) { this.points.forEach((point) => { point.position.applyMatrix4(matrix); }); this.tetrahedrons.forEach((tetrahedron) => { circumSphere( this, tetrahedron.pointIds[0], tetrahedron.pointIds[1], tetrahedron.pointIds[2], tetrahedron.pointIds[3], tetrahedron.sphere ); }); } boundingBox(target: Box3): void { this.points.forEach((point) => { target.expandByPoint(point.position); }); } boundingSphere(target: Sphere): void { this.points.forEach((point) => { target.expandByPoint(point.position); }); } }