@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
193 lines (184 loc) • 6 kB
text/typescript
/**
* filters tetrahedrons based on their quality
*
*
*/
import {TetSopNode} from './_BaseTet';
import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig';
import {CoreGroup, Object3DWithGeometry} from '../../../core/geometry/Group';
import {SopType} from '../../poly/registers/nodes/types/Sop';
import {TetObject} from '../../../core/geometry/modules/tet/TetObject';
import {stringToIndices} from '../../../core/String';
import {isBooleanTrue} from '../../../core/Type';
import {Vector3, Mesh} from 'three';
import {tetCenter} from '../../../core/geometry/modules/tet/utils/tetCenter';
import {isPositionInsideMesh} from '../../../core/geometry/modules/tet/utils/tetInsideMesh';
import {MeshWithBVHGeometry, ThreeMeshBVHHelper} from '../../../core/geometry/bvh/ThreeMeshBVHHelper';
import {findNonDelaunayTetsFromMultiplePointsCheck} from '../../../core/geometry/modules/tet/utils/findNonDelaunayTets';
import {tetRemoveUnusedPoints} from '../../../core/geometry/modules/tet/utils/tetRemoveUnusedPoints';
import {tetQuality} from '../../../core/geometry/modules/tet/utils/tetQuality';
import {arrayPushItems} from '../../../core/ArrayUtils';
const _tetCenter = new Vector3();
const _indices: number[] = [];
class TetDeleteSopParamsConfig extends NodeParamsConfig {
byQuality = ParamConfig.BOOLEAN(0);
minQuality = ParamConfig.FLOAT(0.5, {
range: [0, 1],
rangeLocked: [true, true],
visibleIf: {byQuality: 1},
});
byIds = ParamConfig.BOOLEAN(0, {
separatorBefore: true,
});
ids = ParamConfig.STRING('0', {
visibleIf: {byIds: 1},
});
byIndex = ParamConfig.BOOLEAN(0, {
separatorBefore: true,
});
index = ParamConfig.INTEGER(-1, {
range: [0, 100],
rangeLocked: [true, false],
visibleIf: {byIndex: 1},
});
byIndexRange = ParamConfig.BOOLEAN(0, {
separatorBefore: true,
});
indexRangeStart = ParamConfig.INTEGER(0, {
range: [0, 2000],
rangeLocked: [true, false],
visibleIf: {byIndexRange: 1},
});
indexRangeEnd = ParamConfig.INTEGER(2000, {
range: [0, 2000],
rangeLocked: [true, false],
visibleIf: {byIndexRange: 1},
});
byDelaunay = ParamConfig.BOOLEAN(0, {
separatorBefore: true,
});
byBoundingObject = ParamConfig.BOOLEAN(0, {
separatorBefore: true,
});
invert = ParamConfig.BOOLEAN(0, {
separatorBefore: true,
});
}
const ParamsConfig = new TetDeleteSopParamsConfig();
export class TetDeleteSopNode extends TetSopNode<TetDeleteSopParamsConfig> {
override readonly paramsConfig = ParamsConfig;
static override type() {
return SopType.TET_DELETE;
}
protected override initializeNode() {
this.io.inputs.setCount(1, 2);
}
override async cook(inputCoreGroups: CoreGroup[]) {
const tetObjects = inputCoreGroups[0].tetObjects();
if (tetObjects) {
for (const tetObject of tetObjects) {
this._deleteTets(tetObject, inputCoreGroups);
}
this.setObjects(tetObjects);
} else {
this.setObjects([]);
}
}
_deleteTets(tetObject: TetObject, inputCoreGroups: CoreGroup[]) {
const selectedIds: number[] = [];
if (isBooleanTrue(this.pv.byQuality)) {
this._findTetsByQuality(tetObject, selectedIds);
}
if (isBooleanTrue(this.pv.byIds)) {
this._findTetsById(selectedIds);
}
if (isBooleanTrue(this.pv.byIndex)) {
this._findTetsByIndex(tetObject, selectedIds);
}
if (isBooleanTrue(this.pv.byIndexRange)) {
this._findTetsByIndexRange(tetObject, selectedIds);
}
if (isBooleanTrue(this.pv.byDelaunay)) {
this._findTetsByDelaunay(tetObject, selectedIds);
}
if (isBooleanTrue(this.pv.byBoundingObject)) {
const boundingObject = inputCoreGroups[1]?.threejsObjectsWithGeo()[0] as Mesh | null;
if (boundingObject) {
this._findTetsByBoundingObject(tetObject, boundingObject, selectedIds);
}
}
// invert
if (isBooleanTrue(this.pv.invert)) {
const nonSelectedIds: number[] = [];
const selectedIdsSet = new Set(selectedIds);
tetObject.geometry.tetrahedrons.forEach((_, tetId) => {
if (!selectedIdsSet.has(tetId)) {
nonSelectedIds.push(tetId);
}
});
tetObject.geometry.removeTets(nonSelectedIds);
} else {
tetObject.geometry.removeTets(selectedIds);
}
tetRemoveUnusedPoints(tetObject.geometry);
}
private _findTetsByQuality(tetObject: TetObject, selectedIds: number[]) {
tetObject.geometry.tetrahedrons.forEach((_, tetId) => {
if (tetQuality(tetObject.geometry, tetId) < this.pv.minQuality) {
selectedIds.push(tetId);
}
});
}
private _findTetsById(selectedIds: number[]) {
stringToIndices(this.pv.ids, _indices);
arrayPushItems(_indices, selectedIds);
}
private _findTetsByIndex(tetObject: TetObject, selectedIds: number[]) {
const index = this.pv.index;
if (index == -1) {
const lastId = tetObject.geometry.lastAddedTetId();
if (lastId != null) {
selectedIds.push(lastId);
}
} else {
let i = 0;
tetObject.geometry.tetrahedrons.forEach((_, tetId) => {
if (i == index) {
selectedIds.push(tetId);
}
i++;
});
}
}
private _findTetsByIndexRange(tetObject: TetObject, selectedIds: number[]) {
const min = this.pv.indexRangeStart;
const max = this.pv.indexRangeEnd;
let i = 0;
tetObject.geometry.tetrahedrons.forEach((_, tetId) => {
if (i >= min && i <= max) {
selectedIds.push(tetId);
}
i++;
});
}
private _findTetsByDelaunay(tetObject: TetObject, selectedIds: number[]) {
const invalidTets: number[] = [];
findNonDelaunayTetsFromMultiplePointsCheck(tetObject.geometry, invalidTets);
selectedIds.push(...invalidTets);
}
private _findTetsByBoundingObject(
tetObject: TetObject,
boundingObject: Object3DWithGeometry,
selectedIds: number[]
) {
ThreeMeshBVHHelper.assignDefaultBVHIfNone(boundingObject as Mesh);
const tetGeometry = tetObject.geometry;
tetGeometry.tetrahedrons.forEach((_, tetId) => {
tetCenter(tetGeometry, tetId, _tetCenter);
const isInside = isPositionInsideMesh(_tetCenter, boundingObject as MeshWithBVHGeometry, 0.001);
if (isInside) {
selectedIds.push(tetId);
}
});
}
}