Skip to content

Commit b4c6ec1

Browse files
dakerfinetjul
authored andcommitted
feat(PointLocator): add vtkPointLocator
1 parent e26260d commit b4c6ec1

File tree

12 files changed

+1702
-14
lines changed

12 files changed

+1702
-14
lines changed
4.07 KB
Loading

Documentation/content/examples/index.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ This will allow you to see the some live code running in your browser. Just pick
154154
[![WarpScalar Example][WarpScalargif]](./WarpScalar.html "WarpScalar")
155155
[![WindowedSincPolyDataFilter Example][WindowedSincPolyDataFilter]](./WindowedSincPolyDataFilter.html "WindowedSincPolyDataFilter")
156156

157-
<div>
157+
</div>
158+
158159
[ArcSource]: ../docs/gallery/ArcSource.jpg
159160
[ArrowSource]: ../docs/gallery/ArrowSource.jpg
160161
[CircleSource]: ../docs/gallery/CircleSource.jpg
@@ -343,6 +344,7 @@ This will allow you to see the some live code running in your browser. Just pick
343344
[![CellPicker Example][CellPicker]](./CellPicker.html "CPU cell picker/selector")
344345
[![PointPicker Example][PointPicker]](./PointPicker.html "CPU point picker/selector")
345346
[![HardwareSelector Example][HardwareSelector]](./HardwareSelector.html "GPU point/cell picker/selector with properties")
347+
[![PointLocator Example][PointLocator]](./PointLocator.html "PointLocator")
346348

347349
</div>
348350

@@ -358,6 +360,7 @@ This will allow you to see the some live code running in your browser. Just pick
358360
[CellPicker]: ../docs/gallery/CellPicker.jpg
359361
[PointPicker]: ../docs/gallery/PointPicker.jpg
360362
[HardwareSelector]: ../docs/gallery/HardwareSelector.jpg
363+
[PointLocator]: ../docs/gallery/PointLocator.jpg
361364

362365
# Widgets
363366

Sources/Common/Core/Points/index.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ export interface vtkPoints extends vtkDataArray {
8181
* @returns {Number} Index of the inserted point.
8282
*/
8383
insertNextPoint(x: number, y: number, z: number): number;
84+
85+
/**
86+
* Insert the [x,y,z] coordinates of a point at the given index.
87+
* @param {Number} ptId The index of point.
88+
* @param {Number[]} point The [x, y, z] coordinates of the point.
89+
* @returns {Number} The index of the inserted point.
90+
*/
91+
insertPoint(ptId: number, point: number[]): number;
8492
}
8593

8694
/**

Sources/Common/Core/Points/index.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ function vtkPoints(publicAPI, model) {
3535

3636
publicAPI.insertNextPoint = (x, y, z) => publicAPI.insertNextTuple([x, y, z]);
3737

38+
publicAPI.insertPoint = (ptId, point) => publicAPI.insertTuple(ptId, point);
39+
3840
publicAPI.getBounds = () => {
3941
if (publicAPI.getNumberOfComponents() === 3) {
4042
const xRange = publicAPI.getRange(0);
@@ -50,8 +52,9 @@ function vtkPoints(publicAPI, model) {
5052
}
5153

5254
if (publicAPI.getNumberOfComponents() !== 2) {
53-
vtkErrorMacro(`getBounds called on an array with components of
54-
${publicAPI.getNumberOfComponents()}`);
55+
vtkErrorMacro(
56+
`getBounds called on an array with components of ${publicAPI.getNumberOfComponents()}`
57+
);
5558
return INVALID_BOUNDS;
5659
}
5760

Sources/Common/DataModel/AbstractPointLocator/index.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { vtkObject } from '../../../interfaces';
22
import { Bounds } from '../../../types';
3-
import { ILocatorInitialValues } from '../Locator';
3+
import vtkLocator, { ILocatorInitialValues } from '../Locator';
44

55
/**
66
*
@@ -11,7 +11,7 @@ export interface IAbstractPointLocatorInitialValues
1111
numberOfBuckets: number;
1212
}
1313

14-
export interface vtkAbstractPointLocator extends vtkObject {
14+
export interface vtkAbstractPointLocator extends vtkLocator {
1515
/**
1616
* Set the bounds of this object.
1717
* @param {Bounds} input

Sources/Common/DataModel/BoundingBox/index.d.ts

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,37 @@ export function cutWithPlane(
325325
normal: Vector3
326326
): boolean;
327327

328+
/**
329+
* Clamp the divisions to ensure the total number doesn't exceed targetBins
330+
* @param {Number} targetBins - Maximum number of bins allowed
331+
* @param {Number[]} divs - Divisions array to adjust [divX, divY, divZ]
332+
*/
333+
export function clampDivisions(targetBins: number, divs: number[]): void;
334+
335+
/**
336+
* Compute the number of divisions given the current bounding box and a
337+
* target number of buckets/bins. Handles degenerate bounding boxes properly.
338+
* @param {Bounds} bounds - The bounding box
339+
* @param {Number} totalBins - Target number of bins
340+
* @param {Number[]} divs - Output array to store divisions [divX, divY, divZ]
341+
* @param {Bounds} [adjustedBounds] - Output array to store adjusted bounds if needed
342+
* @returns {Number} The actual total number of bins
343+
*/
344+
export function computeDivisions(
345+
bounds: Bounds,
346+
totalBins: number,
347+
divs: number[],
348+
adjustedBounds?: Bounds
349+
): number;
350+
351+
/**
352+
* Calculate the squared distance from point x to the specified bounds.
353+
* @param {Vector3} x The point coordinates
354+
* @param {Bounds} bounds The bounding box coordinates
355+
* @returns {Number} The squared distance to the bounds
356+
*/
357+
export function distance2ToBounds(x: Vector3, bounds: Bounds): number;
358+
328359
declare class BoundingBox {
329360
getBounds(): Bounds;
330361
/**
@@ -409,10 +440,9 @@ declare class BoundingBox {
409440

410441
/**
411442
* Inflates a bounding box.
412-
* @param {Bounds} bounds
413-
* @param {number} delta
443+
* @param {number} [delta] The amount to inflate the bounding box by.
414444
*/
415-
inflate(bounds: Bounds, delta: number): Bounds;
445+
inflate(delta?: number): Bounds;
416446

417447
/**
418448
* Scales a bounding box.
@@ -611,6 +641,14 @@ declare class BoundingBox {
611641
* @param {Vector3} normal
612642
*/
613643
cutWithPlane(bounds: Bounds, origin: Vector3, normal: Vector3): boolean;
644+
645+
/**
646+
* Calculate the squared distance from point x to the specified bounds.
647+
* @param {Vector3} x The point coordinates
648+
* @param {Bounds} bounds The bounding box coordinates
649+
* @returns {Number} The squared distance to the bounds
650+
*/
651+
distance2ToBounds(x: Vector3, bounds: Bounds): number;
614652
}
615653

616654
export interface IBoundingBoxInitialValues {
@@ -653,6 +691,7 @@ declare const vtkBoundingBox: {
653691
intersects: typeof intersects;
654692
containsPoint: typeof containsPoint;
655693
contains: typeof contains;
694+
distance2ToBounds: typeof distance2ToBounds;
656695
INIT_BOUNDS: Bounds;
657696
};
658697

Sources/Common/DataModel/BoundingBox/index.js

Lines changed: 196 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,11 @@ export function setMaxPoint(bounds, x, y, z) {
119119
return xMax !== x || yMax !== y || zMax !== z;
120120
}
121121

122-
export function inflate(bounds, delta) {
122+
function inflate(bounds, delta) {
123+
if (delta == null) {
124+
// eslint-disable-next-line no-use-before-define
125+
return minInflate(bounds);
126+
}
123127
bounds[0] -= delta;
124128
bounds[1] += delta;
125129
bounds[2] -= delta;
@@ -129,6 +133,35 @@ export function inflate(bounds, delta) {
129133
return bounds;
130134
}
131135

136+
function minInflate(bounds) {
137+
const nonZero = [0, 0, 0];
138+
let maxIdx = -1;
139+
let max = 0.0;
140+
let w = 0.0;
141+
for (let i = 0; i < 3; ++i) {
142+
w = bounds[i * 2 + 1] - bounds[i * 2];
143+
if (w > max) {
144+
max = w;
145+
maxIdx = i;
146+
}
147+
nonZero[i] = w > 0.0 ? 1 : 0;
148+
}
149+
150+
if (maxIdx < 0) {
151+
return inflate(bounds, 0.5);
152+
}
153+
154+
// Any zero width sides are bumped out 1% of max side
155+
for (let i = 0; i < 3; ++i) {
156+
if (!nonZero[i]) {
157+
const d = 0.005 * max;
158+
bounds[i * 2] -= d;
159+
bounds[i * 2 + 1] += d;
160+
}
161+
}
162+
return bounds;
163+
}
164+
132165
export function scale(bounds, sx, sy, sz) {
133166
if (!isValid(bounds)) {
134167
return false;
@@ -616,6 +649,157 @@ export function cutWithPlane(bounds, origin, normal) {
616649
return true;
617650
}
618651

652+
/**
653+
* Clamp the divisions to ensure the total number doesn't exceed targetBins
654+
* @param {Number} targetBins - Maximum number of bins allowed
655+
* @param {Array} divs - Divisions array to adjust [divX, divY, divZ]
656+
*/
657+
export function clampDivisions(targetBins, divs) {
658+
for (let i = 0; i < 3; ++i) {
659+
divs[i] = divs[i] < 1 ? 1 : divs[i];
660+
}
661+
662+
let numBins = divs[0] * divs[1] * divs[2];
663+
while (numBins > targetBins) {
664+
for (let i = 0; i < 3; ++i) {
665+
divs[i] = divs[i] > 1 ? divs[i] - 1 : 1;
666+
}
667+
numBins = divs[0] * divs[1] * divs[2];
668+
}
669+
}
670+
671+
/**
672+
* Compute the number of divisions given the current bounding box and a
673+
* target number of buckets/bins. Handles degenerate bounding boxes properly.
674+
* @param {Bounds} bounds - The bounding box
675+
* @param {Number} totalBins - Target number of bins
676+
* @param {Array} divs - Output array to store divisions [divX, divY, divZ]
677+
* @param {Array} [adjustedBounds] - Output array to store adjusted bounds if needed
678+
* @returns {Number} The actual total number of bins
679+
*/
680+
export function computeDivisions(bounds, totalBins, divs, adjustedBounds = []) {
681+
// This will always produce at least one bin
682+
// eslint-disable-next-line no-param-reassign
683+
totalBins = totalBins <= 0 ? 1 : totalBins;
684+
685+
// First determine the maximum length of the side of the bounds. Keep track
686+
// of zero width sides of the bounding box.
687+
let numNonZero = 0;
688+
const nonZero = [0, 0, 0];
689+
let maxIdx = -1;
690+
let max = 0.0;
691+
const lengths = getLengths(bounds);
692+
693+
// Use a finite tolerance when detecting zero width sides
694+
const totLen = lengths[0] + lengths[1] + lengths[2];
695+
const zeroDetectionTolerance = totLen * (0.001 / 3.0);
696+
697+
for (let i = 0; i < 3; ++i) {
698+
if (lengths[i] > max) {
699+
maxIdx = i;
700+
max = lengths[i];
701+
}
702+
if (lengths[i] > zeroDetectionTolerance) {
703+
nonZero[i] = 1;
704+
numNonZero++;
705+
} else {
706+
nonZero[i] = 0;
707+
}
708+
}
709+
710+
// Get min and max points
711+
const minPoint = getMinPoint(bounds);
712+
const maxPoint = getMaxPoint(bounds);
713+
714+
// If the bounding box is degenerate, then one bin of arbitrary size
715+
if (numNonZero < 1) {
716+
divs[0] = 1;
717+
divs[1] = 1;
718+
divs[2] = 1;
719+
adjustedBounds[0] = minPoint[0] - 0.5;
720+
adjustedBounds[1] = maxPoint[0] + 0.5;
721+
adjustedBounds[2] = minPoint[1] - 0.5;
722+
adjustedBounds[3] = maxPoint[1] + 0.5;
723+
adjustedBounds[4] = minPoint[2] - 0.5;
724+
adjustedBounds[5] = maxPoint[2] + 0.5;
725+
return 1;
726+
}
727+
728+
// Compute the divisions roughly in proportion to the bounding box edge lengths
729+
let f = totalBins;
730+
f /= nonZero[0] ? lengths[0] / totLen : 1.0;
731+
f /= nonZero[1] ? lengths[1] / totLen : 1.0;
732+
f /= nonZero[2] ? lengths[2] / totLen : 1.0;
733+
f **= 1.0 / numNonZero;
734+
735+
for (let i = 0; i < 3; ++i) {
736+
divs[i] = nonZero[i] ? Math.floor((f * lengths[i]) / totLen) : 1;
737+
divs[i] = divs[i] < 1 ? 1 : divs[i];
738+
}
739+
740+
// Make sure that we do not exceed the totalBins
741+
clampDivisions(totalBins, divs);
742+
743+
// Now compute the final bounds, making sure it is a non-zero volume
744+
const delta = (0.5 * lengths[maxIdx]) / divs[maxIdx];
745+
for (let i = 0; i < 3; ++i) {
746+
if (nonZero[i]) {
747+
adjustedBounds[2 * i] = minPoint[i];
748+
adjustedBounds[2 * i + 1] = maxPoint[i];
749+
} else {
750+
adjustedBounds[2 * i] = minPoint[i] - delta;
751+
adjustedBounds[2 * i + 1] = maxPoint[i] + delta;
752+
}
753+
}
754+
755+
return divs[0] * divs[1] * divs[2];
756+
}
757+
758+
/**
759+
* Calculate the squared distance from point x to the specified bounds.
760+
* @param {Vector3} x The point coordinates
761+
* @param {Bounds} bounds The bounding box coordinates
762+
* @returns {Number} The squared distance to the bounds
763+
*/
764+
export function distance2ToBounds(x, bounds) {
765+
// Are we within the bounds?
766+
if (
767+
x[0] >= bounds[0] &&
768+
x[0] <= bounds[1] &&
769+
x[1] >= bounds[2] &&
770+
x[1] <= bounds[3] &&
771+
x[2] >= bounds[4] &&
772+
x[2] <= bounds[5]
773+
) {
774+
return 0.0;
775+
}
776+
777+
const deltas = [0.0, 0.0, 0.0];
778+
779+
// dx
780+
if (x[0] < bounds[0]) {
781+
deltas[0] = bounds[0] - x[0];
782+
} else if (x[0] > bounds[1]) {
783+
deltas[0] = x[0] - bounds[1];
784+
}
785+
786+
// dy
787+
if (x[1] < bounds[2]) {
788+
deltas[1] = bounds[2] - x[1];
789+
} else if (x[1] > bounds[3]) {
790+
deltas[1] = x[1] - bounds[3];
791+
}
792+
793+
// dz
794+
if (x[2] < bounds[4]) {
795+
deltas[2] = bounds[4] - x[2];
796+
} else if (x[2] > bounds[5]) {
797+
deltas[2] = x[2] - bounds[5];
798+
}
799+
800+
return vtkMath.dot(deltas, deltas);
801+
}
802+
619803
// ----------------------------------------------------------------------------
620804
// Light Weight class
621805
// ----------------------------------------------------------------------------
@@ -763,6 +947,14 @@ class BoundingBox {
763947
contains(otherBounds) {
764948
return intersects(this.bounds, otherBounds);
765949
}
950+
951+
computeDivisions(totalBins, divs, adjustedBounds = []) {
952+
return computeDivisions(this.bounds, totalBins, divs, adjustedBounds);
953+
}
954+
955+
distance2ToBounds(x) {
956+
return distance2ToBounds(x, this.bounds);
957+
}
766958
}
767959

768960
function newInstance(initialValues) {
@@ -809,6 +1001,9 @@ export const STATIC = {
8091001
intersects,
8101002
containsPoint,
8111003
contains,
1004+
computeDivisions,
1005+
clampDivisions,
1006+
distance2ToBounds,
8121007
INIT_BOUNDS,
8131008
};
8141009

0 commit comments

Comments
 (0)