Skip to content

Commit 9949cff

Browse files
dakerfinetjul
authored andcommitted
feat(STLReader): use locator-based vertex merging
Replace STLReader duplicate-vertex removal logic with locator-based merging and improve the example. fixes #3052
1 parent 11d6f4b commit 9949cff

5 files changed

Lines changed: 303 additions & 67 deletions

File tree

Data/stl/suzanne.stl

196 KB
Binary file not shown.

Sources/IO/Geometry/STLReader/example/index.js

Lines changed: 64 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import '@kitware/vtk.js/favicon';
22

33
// Load the rendering pieces we want to use (for both WebGL and WebGPU)
44
import '@kitware/vtk.js/Rendering/Profiles/Geometry';
5+
import GUI from 'lil-gui';
56

67
import vtkActor from '@kitware/vtk.js/Rendering/Core/Actor';
78
import vtkFullScreenRenderWindow from '@kitware/vtk.js/Rendering/Misc/FullScreenRenderWindow';
@@ -13,7 +14,6 @@ import vtkPolyDataNormals from '@kitware/vtk.js/Filters/Core/PolyDataNormals';
1314
// ----------------------------------------------------------------------------
1415

1516
const reader = vtkSTLReader.newInstance();
16-
reader.setRemoveDuplicateVertices(5);
1717
const mapper = vtkMapper.newInstance({ scalarVisibility: false });
1818
const actor = vtkActor.newInstance();
1919

@@ -24,49 +24,91 @@ mapper.setInputConnection(normals.getOutputPort());
2424

2525
// ----------------------------------------------------------------------------
2626

27-
function update() {
28-
const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance();
29-
const renderer = fullScreenRenderer.getRenderer();
30-
const renderWindow = fullScreenRenderer.getRenderWindow();
31-
32-
const resetCamera = renderer.resetCamera;
33-
const render = renderWindow.render;
27+
const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance();
28+
const renderer = fullScreenRenderer.getRenderer();
29+
const renderWindow = fullScreenRenderer.getRenderWindow();
30+
renderer.addActor(actor);
3431

35-
renderer.addActor(actor);
36-
resetCamera();
37-
render();
32+
function update() {
33+
renderer.resetCamera();
34+
renderWindow.render();
3835
}
3936

4037
// ----------------------------------------------------------------------------
41-
// Use a file reader to load a local file
38+
// File handling
4239
// ----------------------------------------------------------------------------
4340

44-
const myContainer = document.querySelector('body');
45-
const fileContainer = document.createElement('div');
46-
fileContainer.innerHTML = '<input type="file" class="file"/>';
47-
myContainer.appendChild(fileContainer);
41+
let lastLoadedArrayBuffer = null;
42+
43+
function parseCurrentBuffer() {
44+
if (!lastLoadedArrayBuffer) {
45+
return;
46+
}
47+
reader.parseAsArrayBuffer(lastLoadedArrayBuffer.slice(0));
48+
update();
49+
}
4850

49-
const fileInput = fileContainer.querySelector('input');
51+
const hiddenFileInput = document.createElement('input');
52+
hiddenFileInput.type = 'file';
53+
hiddenFileInput.accept = '.stl';
54+
hiddenFileInput.style.display = 'none';
55+
document.body.appendChild(hiddenFileInput);
5056

5157
function handleFile(event) {
5258
event.preventDefault();
5359
const dataTransfer = event.dataTransfer;
5460
const files = event.target.files || dataTransfer.files;
5561
if (files.length === 1) {
56-
myContainer.removeChild(fileContainer);
5762
const fileReader = new FileReader();
5863
fileReader.onload = function onLoad(e) {
59-
reader.parseAsArrayBuffer(fileReader.result);
60-
update();
64+
lastLoadedArrayBuffer = fileReader.result;
65+
parseCurrentBuffer();
6166
};
6267
fileReader.readAsArrayBuffer(files[0]);
6368
}
6469
}
6570

66-
fileInput.addEventListener('change', handleFile);
71+
hiddenFileInput.addEventListener('change', handleFile);
72+
73+
// ----------------------------------------------------------------------------
74+
// GUI
75+
// ----------------------------------------------------------------------------
76+
77+
const settings = {
78+
merging: true,
79+
removeDuplicateVertices: -1,
80+
openFile() {
81+
hiddenFileInput.click();
82+
},
83+
};
84+
85+
reader.setMerging(settings.merging);
86+
reader.setRemoveDuplicateVertices(settings.removeDuplicateVertices);
87+
88+
const gui = new GUI({ title: 'Controls' });
89+
gui
90+
.add(settings, 'merging')
91+
.name('merging')
92+
.onChange((value) => {
93+
reader.setMerging(value);
94+
parseCurrentBuffer();
95+
});
96+
gui
97+
.add(settings, 'removeDuplicateVertices', -1, 12, 1)
98+
.name('removeDuplicateVertices')
99+
.onChange((value) => {
100+
reader.setRemoveDuplicateVertices(value);
101+
parseCurrentBuffer();
102+
});
103+
gui.add(settings, 'openFile').name('Open STL...');
67104

68105
// ----------------------------------------------------------------------------
69106
// Use the reader to download a file
70107
// ----------------------------------------------------------------------------
71108

72-
// reader.setUrl(`${__BASE_PATH__}/data/stl/segmentation.stl`, { binary: true }).then(update);
109+
fetch(`${__BASE_PATH__}/data/stl/suzanne.stl`)
110+
.then((response) => response.arrayBuffer())
111+
.then((arrayBuffer) => {
112+
lastLoadedArrayBuffer = arrayBuffer;
113+
parseCurrentBuffer();
114+
});

Sources/IO/Geometry/STLReader/index.d.ts

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,34 @@ interface ISTLReaderOptions {
1313
/**
1414
*
1515
*/
16-
export interface ISTLReaderInitialValues {}
16+
export interface ISTLReaderInitialValues {
17+
/**
18+
* Data access helper used for loading remote resources.
19+
*/
20+
dataAccessHelper?:
21+
| HtmlDataAccessHelper
22+
| HttpDataAccessHelper
23+
| JSZipDataAccessHelper
24+
| LiteHttpDataAccessHelper;
25+
26+
/**
27+
* Point locator used for vertex merging.
28+
*/
29+
locator?: any;
30+
31+
/**
32+
* Enable/disable point merging.
33+
* Default is true.
34+
*/
35+
merging?: boolean;
36+
37+
/**
38+
* Merge tolerance power.
39+
* If >= 0, points within 10^-tolerance are merged.
40+
* Default is -1 (disabled).
41+
*/
42+
removeDuplicateVertices?: number;
43+
}
1744

1845
type vtkSTLReaderBase = vtkObject &
1946
Omit<
@@ -41,6 +68,16 @@ export interface vtkSTLReader extends vtkSTLReaderBase {
4168
| JSZipDataAccessHelper
4269
| LiteHttpDataAccessHelper;
4370

71+
/**
72+
* Get the point locator used for vertex merging.
73+
*/
74+
getLocator(): any;
75+
76+
/**
77+
* Get whether point merging is enabled.
78+
*/
79+
getMerging(): boolean;
80+
4481
/**
4582
* Get the url of the object to load.
4683
*/
@@ -74,6 +111,7 @@ export interface vtkSTLReader extends vtkSTLReaderBase {
74111
* @param {String} content The content to parse.
75112
*/
76113
parseAsText(content: string): void;
114+
77115
/**
78116
*
79117
* @param inData
@@ -93,6 +131,16 @@ export interface vtkSTLReader extends vtkSTLReaderBase {
93131
| LiteHttpDataAccessHelper
94132
): boolean;
95133

134+
/**
135+
* Set a point locator used for vertex merging.
136+
*/
137+
setLocator(locator: any): boolean;
138+
139+
/**
140+
* Enable/disable point merging.
141+
*/
142+
setMerging(merging: boolean): boolean;
143+
96144
/**
97145
* Set the url of the object to load.
98146
* @param {String} url the url of the object to load.

0 commit comments

Comments
 (0)