Bek1
(Bek)
May 19, 2025, 11:48am
1
Hi.
Here is minimal code:
function Modal(props: { stlBuffer: ArrayBuffer }) {
const vtkContext = useRef<any>({});
const vtkContainerRef = useRef(null);
useEffect(() => {
const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance({ rootContainer: vtkContainerRef.current });
const renderer = fullScreenRenderer.getRenderer();
const renderWindow = fullScreenRenderer.getRenderWindow();
const actor = vtkActor.newInstance();
const mapper = vtkMapper.newInstance({ scalarVisibility: false });
const smoothFilter = vtkWindowedSincPolyDataFilter.newInstance();
const normals = vtkPolyDataNormals.newInstance();
actor.setMapper(mapper);
renderer.addActor(actor);
const stlReader = vtkSTLReader.newInstance();
stlReader.setRemoveDuplicateVertices(1);
stlReader.parseAsArrayBuffer(props.stlBuffer);
const originalPolyData = stlReader.getOutputData()
vtkContext.current = { originalPolyData, fullScreenRenderer, renderer, renderWindow, actor, mapper, smoothFilter, normals };
}, []);
function makeNormalMode() {
const { mapper, renderWindow, renderer, originalPolyData } = vtkContext.current;
mapper.setInputData(originalPolyData);
renderer.resetCamera();
renderWindow.render();
}
function makeSmoothMode() {
const { smoothFilter, normals, mapper, renderWindow, renderer, originalPolyData } = vtkContext.current;
smoothFilter.setInputData(originalPolyData);
normals.setInputData(smoothFilter.getOutputData());
mapper.setInputData(normals.getOutputData());
renderer.resetCamera();
renderWindow.render();
}
return (
<div>
<Button onClick={makeNormalMode}>normal mode</Button>
<Button onClick={makeSmoothMode}>smooth mode</Button>
<div className="relative"><div ref={vtkContainerRef} className="relative h-[400px]"</div>
</div>
</div>
);
}
i clicked on “normal mode” button:
then clicked on “smooth mode” button and there is some issue:
But if i click on “smooth mode” button first it works good:
How to switch properly between them?
Thanks
finetjul
(Julien Finet)
May 19, 2025, 12:13pm
2
If you call setInputData()
that means you must update()
filters manually.
It is preferable to use setInputConnection()
instead.
Bek1
(Bek)
May 19, 2025, 12:27pm
3
Here is updated code:
function StlModal2(props: { stlBuffer: ArrayBuffer }) {
const vtkContext = useRef<any>({});
const vtkContainerRef = useRef(null);
useEffect(() => {
const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance({ rootContainer: vtkContainerRef.current });
const renderer = fullScreenRenderer.getRenderer();
const renderWindow = fullScreenRenderer.getRenderWindow();
const actor = vtkActor.newInstance();
const mapper = vtkMapper.newInstance({ scalarVisibility: false });
const smoothFilter = vtkWindowedSincPolyDataFilter.newInstance();
const normals = vtkPolyDataNormals.newInstance();
actor.setMapper(mapper);
renderer.addActor(actor);
const stlReader = vtkSTLReader.newInstance();
stlReader.setRemoveDuplicateVertices(1);
stlReader.parseAsArrayBuffer(props.stlBuffer);
const originalPolyData = stlReader.getOutputPort()
vtkContext.current = { originalPolyData, fullScreenRenderer, renderer, renderWindow, actor, mapper, smoothFilter, normals };
}, []);
function makeNormalMode() {
const { mapper, renderWindow, renderer, originalPolyData } = vtkContext.current;
mapper.setInputConnection(originalPolyData);
renderer.resetCamera();
renderWindow.render();
}
function makeSmoothMode() {
const { smoothFilter, normals, mapper, renderWindow, renderer, originalPolyData } = vtkContext.current;
smoothFilter.setInputConnection(originalPolyData);
normals.setInputConnection(smoothFilter.getOutputPort());
mapper.setInputConnection(normals.getOutputPort());
normals.update()
renderer.resetCamera();
renderWindow.render();
}
return (
<div>
<Button onClick={makeNormalMode}>normal mode</Button>
<Button onClick={makeSmoothMode}>smooth mode</Button>
<div className="relative">
<div ref={vtkContainerRef} className="relative h-[400px]"></div>
</div>
</div>
);
}
i’ve used setInputConnection everywhere and it did not help. The only thing that changed is that when switching to smooth mode it gets laggy (normals.update() from your previous answer has no effect for some reason )
finetjul
(Julien Finet)
May 19, 2025, 12:39pm
4
the following is incorrect:
mapper.setInputConnection(originalPolyData);
You can only do:
mapper.setInputConnection(reader.getOutputPort());
or
mapper.setInputData(originalPolyData);
You can’t use a polydata in place of an output port.
Bek1
(Bek)
May 19, 2025, 12:47pm
5
originalPolyData is getOutputPort, i just forgot to rename it in my code.
const originalPolyData = stlReader.getOutputPort()
Or i misunderstood you?
finetjul
(Julien Finet)
May 19, 2025, 12:50pm
6
My bad, I did not read carefully.
Not sure why it is laggy…
Can you share your code ?
Bek1
(Bek)
May 19, 2025, 12:58pm
7
I open the component above like this:
<DropdownMenuItem
onClick={async e => {
const stlRequest = await fetch(`http://localhost:3004/pacs/get-stl/regular`, {
method: 'POST',
body: OhifDicomSegblob,
});
const stlBlob = await stlRequest.blob();
const arrayBuffer = await stlBlob.arrayBuffer();
uiModalService.show({
content: Modal,
contentProps: { stlBuffer: arrayBuffer },
});
}}
>
STL
</DropdownMenuItem>
Thats it. Just get some stl blob, convert to array buffer and show modal. Should i share something else?
Being laggy is not the main problem. It would be great to have solution to issue of rendering when switching
finetjul
(Julien Finet)
May 19, 2025, 1:39pm
8
I meant code without React nor Ohif. Something in 100% VTK.js (with CodeSandBox for example).
Bek1
(Bek)
May 19, 2025, 2:14pm
9
Here is full index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/vtk.js"></script>
</head>
<body>
<div>
<section>
<label for="stlFile">Select STL File:</label>
<input type="file" id="stlFile" accept=".stl">
</section>
<section>
<button id="normalModeBtn">Normal Mode</button>
<button id="smoothModeBtn">Smooth Mode</button>
</section>
<div id="vtkContainer" style="width: 500px; height: 400px; position: relative;"></div>
</div>
<script>
let originalPortData = null;
let fullScreenRenderer = null;
let renderer = null;
let renderWindow = null;
let actor = null;
let mapper = null;
let smoothFilter = null;
let normals = null;
let stlReader = null;
let interactor = null;
const fileInput = document.getElementById('stlFile');
const normalModeBtn = document.getElementById('normalModeBtn');
const smoothModeBtn = document.getElementById('smoothModeBtn');
const vtkContainer = document.getElementById('vtkContainer');
fullScreenRenderer = vtk.Rendering.Misc.vtkFullScreenRenderWindow.newInstance({
rootContainer: vtkContainer,
});
renderer = fullScreenRenderer.getRenderer();
renderWindow = fullScreenRenderer.getRenderWindow();
interactor = renderWindow.getInteractor();
actor = vtk.Rendering.Core.vtkActor.newInstance();
mapper = vtk.Rendering.Core.vtkMapper.newInstance({ scalarVisibility: false });
smoothFilter = vtk.Filters.General.vtkWindowedSincPolyDataFilter.newInstance();
normals = vtk.Filters.Core.vtkPolyDataNormals.newInstance();
actor.setMapper(mapper);
renderer.addActor(actor);
stlReader = vtk.IO.Geometry.vtkSTLReader.newInstance();
stlReader.setRemoveDuplicateVertices(1);
fileInput.addEventListener('change', (event) => {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function (e) {
const fileBuffer = e.target.result;
stlReader.parseAsArrayBuffer(fileBuffer);
originalPortData = stlReader.getOutputPort();
};
reader.readAsArrayBuffer(file);
});
function makeNormalMode() {
if (!originalPortData) return;
mapper.setInputConnection(originalPortData);
renderer.resetCamera();
renderWindow.render();
}
function makeSmoothMode() {
if (!originalPortData) return;
smoothFilter.setInputConnection(originalPortData);
normals.setInputConnection(smoothFilter.getOutputPort());
mapper.setInputConnection(normals.getOutputPort());
renderer.resetCamera();
renderWindow.render();
}
normalModeBtn.addEventListener('click', makeNormalMode);
smoothModeBtn.addEventListener('click', makeSmoothMode);
</script>
</body>
</html>
also, here is STL for testing - https://drive.google.com/file/d/1wbOWwy2U4FiHTY9HlLP8YC0OIQQJSZOx/view?usp=sharing
finetjul
(Julien Finet)
May 19, 2025, 5:09pm
10
I fixed the slowness (due to infinite pipeline execution)
master
← finetjul:fix-stl-reader-remove-duplicates
opened 05:04PM - 19 May 25 UTC
### Context
https://discourse.vtk.org/t/how-to-switch-from-normal-mode-to-smoot… h-mode/15650
### Results
No infinite pipeline execution
### Changes
- [ ] Documentation and TypeScript definitions were updated to match those changes
### PR and Code Checklist
- [x] [semantic-release](https://github.com/semantic-release/semantic-release) commit messages
- [x] Run `npm run reformat` to have correctly formatted code
### Testing
- [ ] This change adds or fixes unit tests
- [x] Tested environment:
- **vtk.js**: master
- **OS**: Windows 11
- **Browser**: Chrome
Regarding the rendering glitch, it seems to be an issue from the WebGL rendering.
The vtkMapper does not seem to support changing the “input”.
It works fine when visualizing your application in WebGPU though (add ?viewAPI=WebGPU
in your url).
One quick solution is to use a different mapper when you change your mode.
FYI @sankhesh