import macro from 'vtk.js/Sources/macros'; import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';
const { vtkErrorMacro } = macro;
const OCTREENODE_INSERTPOINT = [ (points, pointIdx, coords) => pointIdx, (points, pointIdx, coords) => { points.setTuple(pointIdx, coords); return pointIdx; }, (points, pointIdx, coords) => points.insertNextTuple(coords), ];
const OCTREE_CHILD_BOUNDS_LUT = [ [ [0, 1], [0, 1], [0, 1], ], [ [1, 2], [0, 1], [0, 1], ], [ [0, 1], [1, 2], [0, 1], ], [ [1, 2], [1, 2], [0, 1], ],
[ [0, 1], [0, 1], [1, 2], ], [ [1, 2], [0, 1], [1, 2], ], [ [0, 1], [1, 2], [1, 2], ], [ [1, 2], [1, 2], [1, 2], ], ];
function vtkIncrementalOctreeNode(publicAPI, model) { model.classHierarchy.push('vtkIncrementalOctreeNode');
publicAPI.createPointIdSet = (initSize, growSize) => { if (model.pointIdSet == null) { model.pointIdSet = []; } };
publicAPI.setBounds = (x1, x2, y1, y2, z1, z2) => { if (model.minBounds == null) model.minBounds = []; if (model.maxBounds == null) model.maxBounds = []; if (model.minDataBounds == null) model.minDataBounds = []; if (model.maxDataBounds == null) model.maxDataBounds = [];
model.minBounds[0] = x1; model.maxBounds[0] = x2; model.minBounds[1] = y1; model.maxBounds[1] = y2; model.minBounds[2] = z1; model.maxBounds[2] = z2;
model.minDataBounds[0] = x2; model.maxDataBounds[0] = x1; model.minDataBounds[1] = y2; model.maxDataBounds[1] = y1; model.minDataBounds[2] = z2; model.maxDataBounds[2] = z1; };
publicAPI.getBounds = (bounds) => { bounds[0] = model.minBounds[0]; bounds[1] = model.maxBounds[0]; bounds[2] = model.minBounds[1]; bounds[3] = model.maxBounds[1]; bounds[4] = model.minBounds[2]; bounds[5] = model.maxBounds[2]; };
publicAPI.getChildIndex = (point) => Number(point[0] > model.children[0].getMaxBoundsByReference()[0]) + (Number(point[1] > model.children[0].getMaxBoundsByReference()[1]) << 1) + (Number(point[2] > model.children[0].getMaxBoundsByReference()[2]) << 2);
publicAPI.containsPoint = (pnt) => model.minBounds[0] < pnt[0] && pnt[0] <= model.maxBounds[0] && model.minBounds[1] < pnt[1] && pnt[1] <= model.maxBounds[1] && model.minBounds[2] < pnt[2] && pnt[2] <= model.maxBounds[2] ? 1 : 0;
publicAPI.containsPointByData = (pnt) => model.minDataBounds[0] <= pnt[0] && pnt[0] <= model.maxDataBounds[0] && model.minDataBounds[1] <= pnt[1] && pnt[1] <= model.maxDataBounds[1] && model.minDataBounds[2] <= pnt[2] && pnt[2] <= model.maxDataBounds[2] ? 1 : 0;
publicAPI.updateCounterAndDataBounds = (point, nHits, updateData) => { model.numberOfPoints += nHits;
if (!updateData) return false;
let updated = false;
if (point[0] < model.minDataBounds[0]) { updated = true; model.minDataBounds[0] = point[0]; } if (point[0] > model.maxDataBounds[0]) { updated = true; model.maxDataBounds[0] = point[0]; }
if (point[1] < model.minDataBounds[1]) { updated = true; model.minDataBounds[1] = point[1]; } if (point[1] > model.maxDataBounds[1]) { updated = true; model.maxDataBounds[1] = point[1]; }
if (point[2] < model.minDataBounds[2]) { updated = true; model.minDataBounds[2] = point[2]; } if (point[2] > model.maxDataBounds[2]) { updated = true; model.maxDataBounds[2] = point[2]; }
return updated; };
publicAPI.updateCounterAndDataBoundsRecursively = ( point, nHits, updateData, endNode ) => { const updated = publicAPI.updateCounterAndDataBounds( point, nHits, updateData );
return model.parent === endNode ? updated : model.parent.updateCounterAndDataBoundsRecursively( point, nHits, updated, endNode ); };
publicAPI.containsDuplicatePointsOnly = (point) => model.minDataBounds[0] === point[0] && point[0] === model.maxDataBounds[0] && model.minDataBounds[1] === point[1] && point[1] === model.maxDataBounds[1] && model.minDataBounds[2] === point[2] && point[2] === model.maxDataBounds[2];
publicAPI.isLeaf = () => model.children == null;
publicAPI.getChild = (i) => model.children[i];
publicAPI.separateExactlyDuplicatePointsFromNewInsertion = ( points, pntIds, newPnt, pntIdx, maxPts, ptMode ) => { let pointIdx = pntIdx; let i; const dupPnt = [0.0, 0.0, 0.0]; const octMin = [0.0, 0.0, 0.0]; const octMid = [0.0, 0.0, 0.0]; const octMax = [0.0, 0.0, 0.0]; const boxPtr = [null, null, null]; let ocNode = null; let duplic = publicAPI; let single = publicAPI;
points.getPoint(pntIds[0], dupPnt);
while (duplic === single) { ocNode = duplic; octMid[0] = (ocNode.minBounds[0] + ocNode.maxBounds[0]) * 0.5; octMid[1] = (ocNode.minBounds[1] + ocNode.maxBounds[1]) * 0.5; octMid[2] = (ocNode.minBounds[2] + ocNode.maxBounds[2]) * 0.5; boxPtr[0] = ocNode.minBounds; boxPtr[1] = octMid; boxPtr[2] = ocNode.maxBounds;
ocNode.children = [ newInstance(), newInstance(), newInstance(), newInstance(), newInstance(), newInstance(), newInstance(), newInstance(), ]; for (i = 0; i < 8; i++) { octMin[0] = boxPtr[OCTREE_CHILD_BOUNDS_LUT[i][0][0]][0]; octMax[0] = boxPtr[OCTREE_CHILD_BOUNDS_LUT[i][0][1]][0];
octMin[1] = boxPtr[OCTREE_CHILD_BOUNDS_LUT[i][1][0]][1]; octMax[1] = boxPtr[OCTREE_CHILD_BOUNDS_LUT[i][1][1]][1];
octMin[2] = boxPtr[OCTREE_CHILD_BOUNDS_LUT[i][2][0]][2]; octMax[2] = boxPtr[OCTREE_CHILD_BOUNDS_LUT[i][2][1]][2];
ocNode.children[i] = newInstance(); ocNode.children[i].setParent(ocNode); ocNode.children[i].setBounds( octMin[0], octMax[0], octMin[1], octMax[1], octMin[2], octMax[2] ); }
duplic = ocNode.children[ocNode.getChildIndex(dupPnt)]; single = ocNode.children[ocNode.getChildIndex(newPnt)]; }
pointIdx = OCTREENODE_INSERTPOINT[ptMode](points, pointIdx, newPnt); single.createPointIdSet(maxPts >> 2, maxPts >> 1); single.getPointIdSet().push(pointIdx); single.updateCounterAndDataBoundsRecursively(newPnt, 1, 1, null);
duplic.setPointIdSet(pntIds); duplic.updateCounterAndDataBoundsRecursively( dupPnt, pntIds.length, 1, publicAPI ); return pointIdx; };
publicAPI.createChildNodes = ( points, pntIds, newPnt, pntIdx, maxPts, ptMode, numberOfNodes ) => {
let nbNodes = numberOfNodes; let pointIdx = pntIdx; const sample = []; points.getPoint(pntIds[0], sample); if (publicAPI.containsDuplicatePointsOnly(sample)) { pointIdx = publicAPI.separateExactlyDuplicatePointsFromNewInsertion( points, pntIds, newPnt, pointIdx, maxPts, ptMode ); return { success: false, nbNodes, pointIdx }; }
let i; let target; let dvidId = -1; let fullId = -1; const numIds = [0, 0, 0, 0, 0, 0, 0, 0]; const octMin = []; const octMax = []; const tempPt = []; let tempId;
const octMid = [ (model.minBounds[0] + model.maxBounds[0]) * 0.5, (model.minBounds[1] + model.maxBounds[1]) * 0.5, (model.minBounds[2] + model.maxBounds[2]) * 0.5, ]; const boxPtr = [model.minBounds, octMid, model.maxBounds];
model.children = []; for (i = 0; i < 8; i++) { octMin[0] = boxPtr[OCTREE_CHILD_BOUNDS_LUT[i][0][0]][0]; octMax[0] = boxPtr[OCTREE_CHILD_BOUNDS_LUT[i][0][1]][0];
octMin[1] = boxPtr[OCTREE_CHILD_BOUNDS_LUT[i][1][0]][1]; octMax[1] = boxPtr[OCTREE_CHILD_BOUNDS_LUT[i][1][1]][1];
octMin[2] = boxPtr[OCTREE_CHILD_BOUNDS_LUT[i][2][0]][2]; octMax[2] = boxPtr[OCTREE_CHILD_BOUNDS_LUT[i][2][1]][2];
model.children[i] = newInstance(); model.children[i].setParent(publicAPI); model.children[i].setBounds( octMin[0], octMax[0], octMin[1], octMax[1], octMin[2], octMax[2] );
model.children[i].createPointIdSet(maxPts >> 2, maxPts >> 1); } boxPtr[0] = null; boxPtr[1] = null; boxPtr[2] = null;
for (i = 0; i < maxPts; i++) { tempId = pntIds[i]; points.getPoint(tempId, tempPt); target = publicAPI.getChildIndex(tempPt); model.children[target].getPointIdSet().push(tempId); model.children[target].updateCounterAndDataBounds(tempPt); numIds[target]++; }
for (i = 0; i < 8; i++) { if (numIds[i] === maxPts) { fullId = i; break; } }
target = publicAPI.getChildIndex(newPnt); if (fullId === target) { ({ numberOfNodes: nbNodes, pointIdx } = model.children[ target ].createChildNodes( points, pntIds, newPnt, pointIdx, maxPts, ptMode, nbNodes )); dvidId = fullId; } else { pointIdx = OCTREENODE_INSERTPOINT[ptMode](points, pointIdx, newPnt); model.children[target].getPointIdSet().push(pointIdx); model.children[target].updateCounterAndDataBoundsRecursively( newPnt, 1, 1, null );
numIds[target]++; }
for (i = 0; i < 8; i++) { if (numIds[i] === 0 || i === dvidId) { model.children[i].getPointIdSet().length = 0; } }
return { success: true, numberOfNodes: nbNodes, pointIdx }; };
publicAPI.insertPoint = ( points, newPnt, maxPts, pntId, ptMode, numberOfNodes ) => { let nbNodes = 0; let pointIdx = pntId;
if (model.pointIdSet) { if ( model.pointIdSet.length < maxPts || publicAPI.containsDuplicatePointsOnly(newPnt) ) { pointIdx = OCTREENODE_INSERTPOINT[ptMode](points, pointIdx, newPnt); model.pointIdSet.push(pointIdx); publicAPI.updateCounterAndDataBoundsRecursively(newPnt, 1, 1, null); } else { ({ numberOfNodes: nbNodes, pointIdx } = publicAPI.createChildNodes( points, model.pointIdSet, newPnt, pointIdx, maxPts, ptMode, numberOfNodes )); model.pointIdSet = null; } } else { pointIdx = OCTREENODE_INSERTPOINT[ptMode](points, pointIdx, newPnt); model.pointIdSet = []; model.pointIdSet.push(pointIdx); publicAPI.updateCounterAndDataBoundsRecursively(newPnt, 1, 1, null); }
return { numberOfNodes: numberOfNodes + nbNodes, pointIdx }; };
publicAPI.getDistance2ToBoundary = ( point, closest, innerOnly, rootNode, checkData ) => { let thisMin = null; let thisMax = null; let rootMin = null; let rootMax = null; let minDist = Number.MAX_VALUE; if (checkData) { thisMin = publicAPI.getMinDataBounds(); thisMax = publicAPI.getMaxDataBounds(); rootMin = rootNode.getMinDataBounds(); rootMax = rootNode.getMaxDataBounds(); } else { thisMin = model.minBounds; thisMax = model.maxBounds; rootMin = rootNode.getMinBounds(); rootMax = rootNode.getMaxBounds(); }
let minFace = 0; const beXless = Number(point[0] < thisMin[0]); const beXmore = Number(point[0] > thisMax[0]); const beYless = Number(point[1] < thisMin[1]); const beYmore = Number(point[1] > thisMax[1]); const beZless = Number(point[2] < thisMin[2]); const beZmore = Number(point[2] > thisMax[2]); const withinX = Number(!beXless && !beXmore); const withinY = Number(!beYless && !beYmore); const withinZ = Number(!beZless && !beZmore); const xyzFlag = (withinZ << 2) + (withinY << 1) + withinX;
switch (xyzFlag) { case 0: {
closest[0] = beXless ? thisMin[0] : thisMax[0]; closest[1] = beYless ? thisMin[1] : thisMax[1]; closest[2] = beZless ? thisMin[2] : thisMax[2]; minDist = vtkMath.distance2BetweenPoints(point, closest); break; }
case 1: {
closest[0] = point[0]; closest[1] = beYless ? thisMin[1] : thisMax[1]; closest[2] = beZless ? thisMin[2] : thisMax[2]; minDist = vtkMath.distance2BetweenPoints(point, closest); break; }
case 2: {
closest[0] = beXless ? thisMin[0] : thisMax[0]; closest[1] = point[1]; closest[2] = beZless ? thisMin[2] : thisMax[2]; minDist = vtkMath.distance2BetweenPoints(point, closest); break; }
case 3: {
if (beZless) { minDist = thisMin[2] - point[2]; closest[2] = thisMin[2]; } else { minDist = point[2] - thisMax[2]; closest[2] = thisMax[2]; }
minDist *= minDist; closest[0] = point[0]; closest[1] = point[1]; break; }
case 4: {
closest[0] = beXless ? thisMin[0] : thisMax[0]; closest[1] = beYless ? thisMin[1] : thisMax[1]; closest[2] = point[2]; minDist = vtkMath.distance2BetweenPoints(point, closest); break; }
case 5: {
if (beYless) { minDist = thisMin[1] - point[1]; closest[1] = thisMin[1]; } else { minDist = point[1] - thisMax[1]; closest[1] = thisMax[1]; }
minDist *= minDist; closest[0] = point[0]; closest[2] = point[2]; break; }
case 6: {
if (beXless) { minDist = thisMin[0] - point[0]; closest[0] = thisMin[0]; } else { minDist = point[0] - thisMax[0]; closest[0] = thisMax[0]; }
minDist *= minDist; closest[1] = point[1]; closest[2] = point[2]; break; }
case 7: {
if (innerOnly) { let faceDst;
faceDst = point[0] - thisMin[0]; if (thisMin[0] !== rootMin[0] && faceDst < minDist) { minFace = 0; minDist = faceDst; }
faceDst = thisMax[0] - point[0]; if (thisMax[0] !== rootMax[0] && faceDst < minDist) { minFace = 1; minDist = faceDst; }
faceDst = point[1] - thisMin[1]; if (thisMin[1] !== rootMin[1] && faceDst < minDist) { minFace = 2; minDist = faceDst; }
faceDst = thisMax[1] - point[1]; if (thisMax[1] !== rootMax[1] && faceDst < minDist) { minFace = 3; minDist = faceDst; }
faceDst = point[2] - thisMin[2]; if (thisMin[2] !== rootMin[2] && faceDst < minDist) { minFace = 4; minDist = faceDst; }
faceDst = thisMax[2] - point[2]; if (thisMax[2] !== rootMax[2] && faceDst < minDist) { minFace = 5; minDist = faceDst; } } else { const tmpDist = [ point[0] - thisMin[0], thisMax[0] - point[0], point[1] - thisMin[1], thisMax[1] - point[1], point[2] - thisMin[2], thisMax[2] - point[2], ];
for (let i = 0; i < 6; i++) { if (tmpDist[i] < minDist) { minFace = i; minDist = tmpDist[i]; } } }
if (minDist !== Number.MAX_VALUE) { minDist *= minDist; }
closest[0] = point[0]; closest[1] = point[1]; closest[2] = point[2];
const pMinMax = [thisMin, thisMax]; const xyzIndx = minFace >> 1; closest[xyzIndx] = pMinMax[minFace & 1][xyzIndx];
break; } default: vtkErrorMacro('unexpected case in getDistance2ToBoundary'); }
return minDist; };
publicAPI.getDistance2ToInnerBoundary = (point, rootNode) => { const dummy = []; return publicAPI.getDistance2ToBoundary(point, dummy, 0, rootNode, 0); }; }
const DEFAULT_VALUES = { pointIdSet: null, minBounds: null, maxBounds: null, minDataBounds: null, maxDataBounds: null, parent: null, children: null, };
export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues);
macro.obj(publicAPI, model);
macro.setGetArray( publicAPI, model, ['minBounds', 'maxBounds', 'minDataBounds', 'maxDataBounds'], 6 );
macro.get(publicAPI, model, ['pointIdSet', 'numberOfPoints']);
macro.set(publicAPI, model, ['parent']);
vtkIncrementalOctreeNode(publicAPI, model); }
export const newInstance = macro.newInstance( extend, 'vtkIncrementalOctreeNode' );
export default { newInstance, extend };
|