Skip to content

Commit 603d6f9

Browse files
dakerfloryst
authored andcommitted
feat(HDRReader): add vtkHDRReader
Fixes #3145
1 parent c7971eb commit 603d6f9

File tree

9 files changed

+730
-0
lines changed

9 files changed

+730
-0
lines changed
19.4 KB
Loading

Documentation/content/examples/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ This will allow you to see the some live code running in your browser. Just pick
185185
[![HttpSceneLoader Example][HttpSceneLoaderWithIcon]](./HttpSceneLoader.html "Import a VTK scene (data + representation)")
186186
[![OfflineLocalView Example][OfflineLocalViewWithIcon]](./OfflineLocalView.html "Load a serialized scene (VTKSZ)")
187187
[![G-Code Example][GCodeReaderWithIcon]](./GCodeReader.html "G-Code reader(gcode)")
188+
[![HDRReader Example][HDRReaderWithIcon]](./HDRReader.html "Load an HDR image")
188189

189190
</div>
190191

@@ -205,6 +206,7 @@ This will allow you to see the some live code running in your browser. Just pick
205206
[HttpSceneLoaderWithIcon]: ../docs/gallery/HttpSceneLoaderWithIcon.jpg
206207
[OfflineLocalViewWithIcon]: ../docs/gallery/OfflineLocalViewWithIcon.jpg
207208
[GCodeReaderWithIcon]: ../docs/gallery/GCodeReaderWithIcon.jpg
209+
[HDRReaderWithIcon]: ../docs/gallery/HDRReaderWithIcon.jpg
208210

209211
# Actors
210212

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ export function swapColumnsMatrix_nxn(
5151
*/
5252
export function Pi(): number;
5353

54+
/**
55+
* Calculates x times (2 to the power of exponent).
56+
*/
57+
export function ldexp(x: number, exponent: number): number;
58+
5459
/**
5560
* Convert degrees to radians.
5661
* @param {Number} deg The value in degrees.

Sources/Common/Core/Math/index.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,16 @@ export function createArray(size = 3) {
5757

5858
export const Pi = () => Math.PI;
5959

60+
export function ldexp(x, exponent) {
61+
if (exponent > 1023) {
62+
return x * 2 ** 1023 * 2 ** (exponent - 1023);
63+
}
64+
if (exponent < -1074) {
65+
return x * 2 ** -1074 * 2 ** (exponent + 1074);
66+
}
67+
return x * 2 ** exponent;
68+
}
69+
6070
export function radiansFromDegrees(deg) {
6171
return (deg / 180) * Math.PI;
6272
}
@@ -2227,6 +2237,7 @@ export function float2CssRGBA(rgbArray) {
22272237

22282238
export default {
22292239
Pi,
2240+
ldexp,
22302241
radiansFromDegrees,
22312242
degreesFromRadians,
22322243
round,

Sources/IO/Image/HDRReader/Utils.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';
2+
3+
/**
4+
* Read a line from a Uint8Array
5+
* @param {Uint8Array} uint8array
6+
* @param {number} startIndex
7+
* @returns string
8+
*/
9+
function readLine(uint8array, startIndex) {
10+
let line = '';
11+
let character = '';
12+
for (let i = startIndex; i < uint8array.length - startIndex; i++) {
13+
character = String.fromCharCode(uint8array[i]);
14+
if (character === '\n') {
15+
break;
16+
}
17+
line += character;
18+
}
19+
return line;
20+
}
21+
22+
/**
23+
* Convert rgbe to float
24+
* @param {Array} rgbe The rgbe array
25+
* @param {Array} floats The output array
26+
* @param {number} exposure The exposure value
27+
*/
28+
function rgbe2float(rgbe, exposure, floats = []) {
29+
if (rgbe[3] > 0) {
30+
/* nonzero pixel */
31+
const f = vtkMath.ldexp(1.0, rgbe[3] - (128 + 8)) / exposure;
32+
floats[0] = rgbe[0] * f;
33+
floats[1] = rgbe[1] * f;
34+
floats[2] = rgbe[2] * f;
35+
} else {
36+
floats[0] = 0;
37+
floats[1] = 0;
38+
floats[2] = 0;
39+
}
40+
return floats;
41+
}
42+
43+
export { readLine, rgbe2float };
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import '@kitware/vtk.js/favicon';
2+
3+
// Load the rendering pieces we want to use (for both WebGL and WebGPU)
4+
import '@kitware/vtk.js/Rendering/Profiles/Geometry';
5+
6+
import vtkFullScreenRenderWindow from '@kitware/vtk.js/Rendering/Misc/FullScreenRenderWindow';
7+
import vtkActor from '@kitware/vtk.js/Rendering/Core/Actor';
8+
import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper';
9+
import vtkPlaneSource from '@kitware/vtk.js/Filters/Sources/PlaneSource';
10+
import vtkHDRReader from '@kitware/vtk.js/IO/Image/HDRReader';
11+
import vtkTexture from '@kitware/vtk.js/Rendering/Core/Texture';
12+
import vtkURLExtract from '@kitware/vtk.js/Common/Core/URLExtract';
13+
14+
// ----------------------------------------------------------------------------
15+
// Example code
16+
// ----------------------------------------------------------------------------
17+
const userParams = vtkURLExtract.extractURLParameters();
18+
19+
const reader = vtkHDRReader.newInstance();
20+
const texture = vtkTexture.newInstance();
21+
const planeSource = vtkPlaneSource.newInstance();
22+
const mapper = vtkMapper.newInstance();
23+
const actor = vtkActor.newInstance();
24+
mapper.setInputConnection(planeSource.getOutputPort());
25+
actor.setMapper(mapper);
26+
27+
// ----------------------------------------------------------------------------
28+
// Use a file reader to load a local file
29+
// ----------------------------------------------------------------------------
30+
31+
const myContainer = document.querySelector('body');
32+
const fileContainer = document.createElement('div');
33+
fileContainer.innerHTML =
34+
'<div>Select a hdr file.<br/><input type="file" class="file"/></div>';
35+
myContainer.appendChild(fileContainer);
36+
37+
const fileInput = fileContainer.querySelector('input');
38+
39+
function zoomCameraToFitPlane(camera, planeWidth, planeHeight) {
40+
const fov = 60; // Field of view in degrees
41+
42+
// Calculate the distance needed to fit the plane in view
43+
const distance =
44+
Math.max(planeWidth, planeHeight) /
45+
(2 * Math.tan((fov * Math.PI) / 180 / 2));
46+
47+
// Set camera position
48+
camera.setPosition(planeWidth / 2, planeHeight / 2, distance);
49+
camera.setFocalPoint(planeWidth / 2, planeHeight / 2, 0);
50+
camera.setViewUp(0, 1, 0);
51+
52+
// Set parallel scale for orthographic projection
53+
camera.setParallelScale(planeHeight / 2);
54+
}
55+
56+
function update() {
57+
// Get the vtkImageData from the reader
58+
const imageData = reader.getOutputData();
59+
60+
// Set the vtkImageData as the texture input
61+
texture.setInputData(imageData);
62+
63+
// Get the image's extent and spacing
64+
const [xMin, xMax, yMin, yMax] = imageData.getExtent();
65+
const [spacingX, spacingY] = imageData.getSpacing();
66+
67+
// Calculate the plane's width and height based on the image's dimensions
68+
const planeWidth = (xMax - xMin + 1) * spacingX;
69+
const planeHeight = (yMax - yMin + 1) * spacingY;
70+
71+
// Set the plane's origin and corners based on calculated width and height
72+
planeSource.setOrigin(0, 0, 0);
73+
planeSource.setPoint1(planeWidth, 0, 0); // Horizontal edge
74+
planeSource.setPoint2(0, planeHeight, 0); // Vertical edge
75+
76+
actor.addTexture(texture);
77+
78+
const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance();
79+
const renderer = fullScreenRenderer.getRenderer();
80+
const renderWindow = fullScreenRenderer.getRenderWindow();
81+
const camera = renderer.getActiveCamera();
82+
const interactor = renderWindow.getInteractor();
83+
84+
// Disable default interactor style
85+
interactor.setInteractorStyle(null);
86+
87+
renderer.addActor(actor);
88+
89+
// Adjust the camera to fit the plane in the view
90+
zoomCameraToFitPlane(camera, planeWidth, planeHeight);
91+
renderer.resetCameraClippingRange();
92+
93+
renderWindow.render();
94+
}
95+
96+
function handleFile(event) {
97+
event.preventDefault();
98+
const dataTransfer = event.dataTransfer;
99+
const files = event.target.files || dataTransfer.files;
100+
if (files.length === 1) {
101+
const file = files[0];
102+
const fileReader = new FileReader();
103+
fileReader.onload = () => {
104+
reader.parse(fileReader.result);
105+
update();
106+
};
107+
fileReader.readAsArrayBuffer(file);
108+
}
109+
}
110+
111+
fileInput.addEventListener('change', handleFile);
112+
113+
// ----------------------------------------------------------------------------
114+
// Use the reader to download a file
115+
// ----------------------------------------------------------------------------
116+
if (userParams.fileURL) {
117+
reader.setUrl(userParams.fileURL).then(() => {
118+
reader.loadData().then(() => {
119+
update();
120+
});
121+
});
122+
}

Sources/IO/Image/HDRReader/index.d.ts

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { vtkAlgorithm, vtkObject } from '../../../interfaces';
2+
import HtmlDataAccessHelper from '../../Core/DataAccessHelper/HtmlDataAccessHelper';
3+
import HttpDataAccessHelper from '../../Core/DataAccessHelper/HttpDataAccessHelper';
4+
import JSZipDataAccessHelper from '../../Core/DataAccessHelper/JSZipDataAccessHelper';
5+
import LiteHttpDataAccessHelper from '../../Core/DataAccessHelper/LiteHttpDataAccessHelper';
6+
7+
interface IHDRReaderOptions {
8+
compression?: string;
9+
progressCallback?: any;
10+
}
11+
12+
/**
13+
*
14+
*/
15+
export interface IHDRReaderInitialValues {}
16+
17+
type vtkHDRReaderBase = vtkObject &
18+
Omit<
19+
vtkAlgorithm,
20+
| 'getInputData'
21+
| 'setInputData'
22+
| 'setInputConnection'
23+
| 'getInputConnection'
24+
| 'addInputConnection'
25+
| 'addInputData'
26+
>;
27+
28+
export interface vtkHDRReader extends vtkHDRReaderBase {
29+
/**
30+
* Get the base url.
31+
*/
32+
getBaseURL(): string;
33+
34+
/**
35+
* Get the dataAccess helper.
36+
*/
37+
getDataAccessHelper():
38+
| HtmlDataAccessHelper
39+
| HttpDataAccessHelper
40+
| JSZipDataAccessHelper
41+
| LiteHttpDataAccessHelper;
42+
43+
/**
44+
* Get the url of the object to load.
45+
*/
46+
getUrl(): string;
47+
48+
/**
49+
* Load the object data.
50+
* @param {IHDRReaderOptions} [options]
51+
*/
52+
loadData(options?: IHDRReaderOptions): Promise<any>;
53+
54+
/**
55+
* Parse data.
56+
* @param {ArrayBuffer} content The content to parse.
57+
*/
58+
parse(content: ArrayBuffer): void;
59+
60+
/**
61+
* Parse data as ArrayBuffer.
62+
* @param {ArrayBuffer} content The content to parse.
63+
*/
64+
parseAsArrayBuffer(content: ArrayBuffer): void;
65+
66+
/**
67+
*
68+
* @param inData
69+
* @param outData
70+
*/
71+
requestData(inData: any, outData: any): void;
72+
73+
/**
74+
*
75+
* @param dataAccessHelper
76+
*/
77+
setDataAccessHelper(
78+
dataAccessHelper:
79+
| HtmlDataAccessHelper
80+
| HttpDataAccessHelper
81+
| JSZipDataAccessHelper
82+
| LiteHttpDataAccessHelper
83+
): boolean;
84+
85+
/**
86+
* Set the url of the object to load.
87+
* @param {String} url the url of the object to load.
88+
* @param {IHDRReaderOptions} [option] The PLY reader options.
89+
*/
90+
setUrl(url: string, option?: IHDRReaderOptions): Promise<string | any>;
91+
}
92+
93+
/**
94+
* Method used to decorate a given object (publicAPI+model) with vtkHDRReader characteristics.
95+
*
96+
* @param publicAPI object on which methods will be bounds (public)
97+
* @param model object on which data structure will be bounds (protected)
98+
* @param {IHDRReaderInitialValues} [initialValues] (default: {})
99+
*/
100+
export function extend(
101+
publicAPI: object,
102+
model: object,
103+
initialValues?: IHDRReaderInitialValues
104+
): void;
105+
106+
/**
107+
* Method used to create a new instance of vtkHDRReader
108+
* @param {IHDRReaderInitialValues} [initialValues] for pre-setting some of its content
109+
*/
110+
export function newInstance(
111+
initialValues?: IHDRReaderInitialValues
112+
): vtkHDRReader;
113+
114+
/**
115+
* vtkHDRReader is a source object that reads Radiance HDR files.
116+
*/
117+
export declare const vtkHDRReader: {
118+
newInstance: typeof newInstance;
119+
extend: typeof extend;
120+
};
121+
export default vtkHDRReader;

0 commit comments

Comments
 (0)