@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
255 lines (236 loc) • 7.28 kB
text/typescript
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);
});
}
}