UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

261 lines (243 loc) 9.56 kB
import { m as macro } from '../../macros2.js'; import vtkImplicitFunction from './ImplicitFunction.js'; import { f as distance2BetweenPoints } from '../Core/Math/index.js'; // ---------------------------------------------------------------------------- // Global methods // ---------------------------------------------------------------------------- function evaluate(radius, center, xyz) { if (!Array.isArray(radius)) { const retVal = (xyz[0] - center[0]) * (xyz[0] - center[0]) + (xyz[1] - center[1]) * (xyz[1] - center[1]) + (xyz[2] - center[2]) * (xyz[2] - center[2]) - radius * radius; return retVal; } const r = [(xyz[0] - center[0]) / radius[0], (xyz[1] - center[1]) / radius[1], (xyz[2] - center[2]) / radius[2]]; return r[0] * r[0] + r[1] * r[1] + r[2] * r[2] - 1; } function setPointFromArray(dst, pts, pointId) { const offset = 3 * pointId; dst[0] = pts[offset]; dst[1] = pts[offset + 1]; dst[2] = pts[offset + 2]; } function copyPoint(dst, src) { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; } function copySphere(dst, src) { dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3]; } // Inspired by Graphics Gems Vol. I ("An Efficient Bounding Sphere" by Jack Ritter). function computeBoundingSphere(pts, numPts, hints) { const actualNumPts = numPts ?? Math.floor(pts.length / 3); const sphere = [0, 0, 0, 0]; if (actualNumPts < 1) { return sphere; } const d1 = [0, 0, 0]; const d2 = [0, 0, 0]; if (hints && hints.length >= 2) { setPointFromArray(d1, pts, hints[0]); setPointFromArray(d2, pts, hints[1]); } else { const xMin = [Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE]; const yMin = [Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE]; const zMin = [Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE]; const xMax = [-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE]; const yMax = [-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE]; const zMax = [-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE]; const p = [0, 0, 0]; for (let i = 0; i < actualNumPts; i++) { setPointFromArray(p, pts, i); if (p[0] < xMin[0]) copyPoint(xMin, p); if (p[0] > xMax[0]) copyPoint(xMax, p); if (p[1] < yMin[1]) copyPoint(yMin, p); if (p[1] > yMax[1]) copyPoint(yMax, p); if (p[2] < zMin[2]) copyPoint(zMin, p); if (p[2] > zMax[2]) copyPoint(zMax, p); } const xSpan = distance2BetweenPoints(xMax, xMin); const ySpan = distance2BetweenPoints(yMax, yMin); const zSpan = distance2BetweenPoints(zMax, zMin); if (xSpan > ySpan) { if (xSpan > zSpan) { copyPoint(d1, xMin); copyPoint(d2, xMax); } else { copyPoint(d1, zMin); copyPoint(d2, zMax); } } else if (ySpan > zSpan) { copyPoint(d1, yMin); copyPoint(d2, yMax); } else { copyPoint(d1, zMin); copyPoint(d2, zMax); } } sphere[0] = (d1[0] + d2[0]) / 2; sphere[1] = (d1[1] + d2[1]) / 2; sphere[2] = (d1[2] + d2[2]) / 2; let r2 = distance2BetweenPoints(d1, d2) / 4; sphere[3] = Math.sqrt(r2); const p = [0, 0, 0]; for (let i = 0; i < actualNumPts; i++) { setPointFromArray(p, pts, i); const dist2 = distance2BetweenPoints(p, sphere); if (dist2 > r2) { const dist = Math.sqrt(dist2); sphere[3] = (sphere[3] + dist) / 2; r2 = sphere[3] * sphere[3]; const delta = dist - sphere[3]; sphere[0] = (sphere[3] * sphere[0] + delta * p[0]) / dist; sphere[1] = (sphere[3] * sphere[1] + delta * p[1]) / dist; sphere[2] = (sphere[3] * sphere[2] + delta * p[2]) / dist; } } return sphere; } function computeBoundingSphereFromSpheres(spheres, numSpheres, hints) { const actualNumSpheres = numSpheres ?? spheres.length; const sphere = [0, 0, 0, 0]; if (actualNumSpheres < 1) { return sphere; } if (actualNumSpheres === 1) { copySphere(sphere, spheres[0]); return sphere; } const s1 = [0, 0, 0, 0]; const s2 = [0, 0, 0, 0]; if (hints && hints.length >= 2) { copySphere(s1, spheres[hints[0]]); copySphere(s2, spheres[hints[1]]); } else { const xMin = [Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE, 0]; const yMin = [Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE, 0]; const zMin = [Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE, 0]; const xMax = [-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE, 0]; const yMax = [-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE, 0]; const zMax = [-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE, 0]; for (let i = 0; i < actualNumSpheres; i++) { const s = spheres[i]; if (s[0] - s[3] < xMin[0] - xMin[3]) copySphere(xMin, s); if (s[0] + s[3] > xMax[0] + xMax[3]) copySphere(xMax, s); if (s[1] - s[3] < yMin[1] - yMin[3]) copySphere(yMin, s); if (s[1] + s[3] > yMax[1] + yMax[3]) copySphere(yMax, s); if (s[2] - s[3] < zMin[2] - zMin[3]) copySphere(zMin, s); if (s[2] + s[3] > zMax[2] + zMax[3]) copySphere(zMax, s); } const xSpan = (xMax[0] + xMax[3] - (xMin[0] - xMin[3])) * (xMax[0] + xMax[3] - (xMin[0] - xMin[3])) + (xMax[1] + xMax[3] - (xMin[1] - xMin[3])) * (xMax[1] + xMax[3] - (xMin[1] - xMin[3])) + (xMax[2] + xMax[3] - (xMin[2] - xMin[3])) * (xMax[2] + xMax[3] - (xMin[2] - xMin[3])); const ySpan = (yMax[0] + yMax[3] - (yMin[0] - yMin[3])) * (yMax[0] + yMax[3] - (yMin[0] - yMin[3])) + (yMax[1] + yMax[3] - (yMin[1] - yMin[3])) * (yMax[1] + yMax[3] - (yMin[1] - yMin[3])) + (yMax[2] + yMax[3] - (yMin[2] - yMin[3])) * (yMax[2] + yMax[3] - (yMin[2] - yMin[3])); const zSpan = (zMax[0] + zMax[3] - (zMin[0] - zMin[3])) * (zMax[0] + zMax[3] - (zMin[0] - zMin[3])) + (zMax[1] + zMax[3] - (zMin[1] - zMin[3])) * (zMax[1] + zMax[3] - (zMin[1] - zMin[3])) + (zMax[2] + zMax[3] - (zMin[2] - zMin[3])) * (zMax[2] + zMax[3] - (zMin[2] - zMin[3])); if (xSpan > ySpan) { if (xSpan > zSpan) { copySphere(s1, xMin); copySphere(s2, xMax); } else { copySphere(s1, zMin); copySphere(s2, zMax); } } else if (ySpan > zSpan) { copySphere(s1, yMin); copySphere(s2, yMax); } else { copySphere(s1, zMin); copySphere(s2, zMax); } } let r2 = distance2BetweenPoints(s1, s2) / 4; sphere[3] = r2 > 0 ? Math.sqrt(r2) : s1[3]; const t1 = -s1[3] / (2 * sphere[3]); const t2 = 1 + s2[3] / (2 * sphere[3]); const v = [0, 0, 0]; for (let i = 0; i < 3; i++) { v[i] = s2[i] - s1[i]; const tmp = s1[i] + t1 * v[i]; s2[i] = s1[i] + t2 * v[i]; s1[i] = tmp; sphere[i] = (s1[i] + s2[i]) / 2; } r2 = distance2BetweenPoints(s1, s2) / 4; if (r2 > 0) { sphere[3] = Math.sqrt(r2); } else { sphere[3] = s1[3]; r2 = sphere[3] * sphere[3]; } for (let i = 0; i < actualNumSpheres; i++) { const s = spheres[i]; const sR2 = s[3] * s[3]; let dist2 = distance2BetweenPoints(s, sphere); if (dist2 <= 0) { dist2 = s[3]; } const fac = sR2 > dist2 ? 2 * sR2 : 2 * dist2; if (dist2 + fac + sR2 > r2) { const dist = Math.sqrt(dist2); if ((dist + s[3]) * (dist + s[3]) > r2) { for (let j = 0; j < 3; j++) { v[j] = s[j] - sphere[j]; s1[j] = sphere[j] - sphere[3] / dist * v[j]; s2[j] = sphere[j] + (1 + s[3] / dist) * v[j]; sphere[j] = (s1[j] + s2[j]) / 2; } r2 = distance2BetweenPoints(s1, s2) / 4; if (r2 > 0) { sphere[3] = Math.sqrt(r2); } else { sphere[3] = Math.max(s1[3], sphere[3]); r2 = sphere[3] * sphere[3]; } } } } return sphere; } // ---------------------------------------------------------------------------- // Static API // ---------------------------------------------------------------------------- const STATIC = { evaluate, computeBoundingSphere, computeBoundingSphereFromSpheres }; // ---------------------------------------------------------------------------- // vtkSphere methods // ---------------------------------------------------------------------------- function vtkSphere(publicAPI, model) { // Set our className model.classHierarchy.push('vtkSphere'); publicAPI.evaluateFunction = xyz => evaluate(model.radius, model.center, xyz); publicAPI.evaluateGradient = xyz => { const retVal = [2.0 - (xyz[0] - model.center[0]), 2.0 - (xyz[1] - model.center[1]), 2.0 - (xyz[2] - model.center[2])]; return retVal; }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- const DEFAULT_VALUES = { radius: 0.5, center: [0.0, 0.0, 0.0] }; // ---------------------------------------------------------------------------- function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues); // Object methods vtkImplicitFunction.extend(publicAPI, model, initialValues); macro.setGet(publicAPI, model, ['radius']); macro.setGetArray(publicAPI, model, ['center'], 3); vtkSphere(publicAPI, model); } // ---------------------------------------------------------------------------- const newInstance = macro.newInstance(extend, 'vtkSphere'); // ---------------------------------------------------------------------------- var vtkSphere$1 = { newInstance, extend, ...STATIC }; export { STATIC, vtkSphere$1 as default, extend, newInstance };