@kitware/vtk.js 32.7.1 → 32.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -408,6 +408,10 @@ export function extend(
408
408
 
409
409
  /**
410
410
  * Method use to create a new instance of vtkDataArray
411
+ *
412
+ * If the provided `values` is a plain Array and `dataType` is not explicitly provided,
413
+ * then the vtkDataArray data type will be a Float32Array.
414
+ *
411
415
  * @param {object} [initialValues] for pre-setting some of its content
412
416
  */
413
417
  export function newInstance(initialValues?: object): vtkDataArray;
@@ -497,6 +497,9 @@ const DEFAULT_VALUES = {
497
497
  function extend(publicAPI, model) {
498
498
  let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
499
499
  Object.assign(model, DEFAULT_VALUES, initialValues);
500
+ if (Array.isArray(initialValues.values) && initialValues.dataType === undefined) {
501
+ console.warn('vtkDataArray.newInstance: no dataType provided, converting to Float32Array');
502
+ }
500
503
  if (!model.empty && !model.values && !model.size) {
501
504
  throw new TypeError('Cannot create vtkDataArray object without: size > 0, values');
502
505
  }
@@ -133,7 +133,7 @@ function createAnimationSampler(glTFSampler) {
133
133
  result = cubicSplineInterpolate(path, t0, t1, i0, i1, time);
134
134
  break;
135
135
  default:
136
- throw new Error(`Unknown interpolation method: ${glTFSampler.interpolation}`);
136
+ vtkWarningMacro(`Unknown interpolation method: ${glTFSampler.interpolation}`);
137
137
  }
138
138
  return result;
139
139
  }
@@ -101,7 +101,7 @@ class GLTFParser {
101
101
  if (mesh.primitives) {
102
102
  mesh.primitives = mesh.primitives.map((primitive, idx) => {
103
103
  const attributes = primitive.attributes;
104
- primitive.name = `submesh-${idx}`;
104
+ primitive.name = `primitive-${idx}`;
105
105
  primitive.attributes = {};
106
106
  for (const attribute in attributes) {
107
107
  const attr = SEMANTIC_ATTRIBUTE_MAP[attribute];
@@ -30,18 +30,15 @@ async function parseGLTF(gltf, options) {
30
30
  }
31
31
 
32
32
  /**
33
- * Creates VTK polydata from a GLTF mesh
34
- * @param {GLTFMesh} mesh - The GLTF mesh
33
+ * Creates VTK polydata from a GLTF mesh primitive
34
+ * @param {GLTFPrimitive} primitive - The GLTF mesh primitive
35
35
  * @returns {vtkPolyData} The created VTK polydata
36
36
  */
37
- async function createPolyDataFromGLTFMesh(mesh) {
38
- const primitive = mesh.primitives[0]; // For simplicity, we'll just use the first primitive
39
-
37
+ async function createPolyDataFromGLTFMesh(primitive) {
40
38
  if (!primitive || !primitive.attributes) {
41
- vtkWarningMacro('Mesh has no position data, skipping');
39
+ vtkWarningMacro('Primitive has no position data, skipping');
42
40
  return null;
43
41
  }
44
- const mode = primitive.mode;
45
42
  if (primitive.extensions?.KHR_draco_mesh_compression) {
46
43
  return handleKHRDracoMeshCompression(primitive.extensions.KHR_draco_mesh_compression);
47
44
  }
@@ -118,10 +115,10 @@ async function createPolyDataFromGLTFMesh(mesh) {
118
115
  });
119
116
 
120
117
  // Handle indices if available
121
- if (primitive.indices !== undefined) {
118
+ if (primitive.indices != null) {
122
119
  const indices = primitive.indices.value;
123
120
  const nCells = indices.length - 2;
124
- switch (mode) {
121
+ switch (primitive.mode) {
125
122
  case MODES.GL_LINE_STRIP:
126
123
  case MODES.GL_TRIANGLE_STRIP:
127
124
  case MODES.GL_LINE_LOOP:
@@ -135,7 +132,7 @@ async function createPolyDataFromGLTFMesh(mesh) {
135
132
  }
136
133
  }
137
134
  }
138
- switch (mode) {
135
+ switch (primitive.mode) {
139
136
  case MODES.GL_TRIANGLES:
140
137
  case MODES.GL_TRIANGLE_FAN:
141
138
  polyData.setPolys(cells);
@@ -159,7 +156,7 @@ async function createPolyDataFromGLTFMesh(mesh) {
159
156
 
160
157
  /**
161
158
  * Creates a VTK property from a GLTF material
162
- * @param {*} model - The vtk model object
159
+ * @param {object} model - The vtk model object
163
160
  * @param {GLTFMaterial} material - The GLTF material
164
161
  * @param {vtkActor} actor - The VTK actor
165
162
  */
@@ -169,15 +166,15 @@ async function createPropertyFromGLTFMaterial(model, material, actor) {
169
166
  const emissiveFactor = material.emissiveFactor;
170
167
  const property = actor.getProperty();
171
168
  const pbr = material.pbrMetallicRoughness;
172
- if (pbr !== undefined) {
169
+ if (pbr != null) {
173
170
  if (!pbr?.metallicFactor || pbr?.metallicFactor <= 0 || pbr?.metallicFactor >= 1) {
174
- vtkWarningMacro('Invalid material.pbrMetallicRoughness.metallicFactor value. Using default value instead.');
171
+ vtkDebugMacro('Invalid material.pbrMetallicRoughness.metallicFactor value. Using default value instead.');
175
172
  } else metallicFactor = pbr.metallicFactor;
176
173
  if (!pbr?.roughnessFactor || pbr?.roughnessFactor <= 0 || pbr?.roughnessFactor >= 1) {
177
- vtkWarningMacro('Invalid material.pbrMetallicRoughness.roughnessFactor value. Using default value instead.');
174
+ vtkDebugMacro('Invalid material.pbrMetallicRoughness.roughnessFactor value. Using default value instead.');
178
175
  } else roughnessFactor = pbr.roughnessFactor;
179
176
  const color = pbr.baseColorFactor;
180
- if (color !== undefined) {
177
+ if (color != null) {
181
178
  property.setDiffuseColor(color[0], color[1], color[2]);
182
179
  property.setOpacity(color[3]);
183
180
  }
@@ -187,7 +184,7 @@ async function createPropertyFromGLTFMaterial(model, material, actor) {
187
184
  if (pbr.baseColorTexture) {
188
185
  pbr.baseColorTexture.extensions;
189
186
  const tex = pbr.baseColorTexture.texture;
190
- if (tex.extensions !== undefined) {
187
+ if (tex.extensions != null) {
191
188
  const extensionsNames = Object.keys(tex.extensions);
192
189
  extensionsNames.forEach(extensionName => {
193
190
  // TODO: Handle KHR_texture_basisu extension
@@ -243,7 +240,7 @@ async function createPropertyFromGLTFMaterial(model, material, actor) {
243
240
  property.setEmissionTexture(emissiveTex);
244
241
 
245
242
  // Handle mutiple Uvs
246
- if (material.emissiveTexture.texCoord !== undefined) {
243
+ if (material.emissiveTexture.texCoord != null) {
247
244
  const pd = actor.getMapper().getInputData().getPointData();
248
245
  pd.setActiveTCoords(`TEXCOORD_${material.emissiveTexture.texCoord}`);
249
246
  }
@@ -257,14 +254,14 @@ async function createPropertyFromGLTFMaterial(model, material, actor) {
257
254
  const normalImage = await loadImage(tex.source);
258
255
  const normalTex = createVTKTextureFromGLTFTexture(normalImage, sampler);
259
256
  property.setNormalTexture(normalTex);
260
- if (material.normalTexture.scale !== undefined) {
257
+ if (material.normalTexture.scale != null) {
261
258
  property.setNormalStrength(material.normalTexture.scale);
262
259
  }
263
260
  }
264
261
  }
265
262
 
266
263
  // Material extensions
267
- if (material.extensions !== undefined) {
264
+ if (material.extensions != null) {
268
265
  const extensionsNames = Object.keys(material.extensions);
269
266
  extensionsNames.forEach(extensionName => {
270
267
  const extension = material.extensions[extensionName];
@@ -291,17 +288,17 @@ async function createPropertyFromGLTFMaterial(model, material, actor) {
291
288
 
292
289
  /**
293
290
  * Handles primitive extensions
291
+ * @param {string} nodeId The GLTF node id
294
292
  * @param {*} extensions The extensions object
295
293
  * @param {*} model The vtk model object
296
- * @param {GLTFNode} node The GLTF node
297
294
  */
298
- function handlePrimitiveExtensions(extensions, model, node) {
295
+ function handlePrimitiveExtensions(nodeId, extensions, model) {
299
296
  const extensionsNames = Object.keys(extensions);
300
297
  extensionsNames.forEach(extensionName => {
301
298
  const extension = extensions[extensionName];
302
299
  switch (extensionName) {
303
300
  case 'KHR_materials_variants':
304
- model.variantMappings.set(node.id, extension.mappings);
301
+ model.variantMappings.set(nodeId, extension.mappings);
305
302
  break;
306
303
  default:
307
304
  vtkWarningMacro(`Unhandled extension: ${extensionName}`);
@@ -314,33 +311,43 @@ function handlePrimitiveExtensions(extensions, model, node) {
314
311
  * @param {GLTFMesh} mesh - The GLTF mesh
315
312
  * @returns {vtkActor} The created VTK actor
316
313
  */
317
- async function createActorFromGTLFNode(model, node, worldMatrix) {
314
+ async function createActorFromGTLFNode(worldMatrix) {
318
315
  const actor = vtkActor.newInstance();
319
316
  const mapper = vtkMapper.newInstance();
320
317
  mapper.setColorModeToDirectScalars();
321
318
  actor.setMapper(mapper);
322
319
  actor.setUserMatrix(worldMatrix);
323
- if (node.mesh !== undefined) {
324
- const polyData = await createPolyDataFromGLTFMesh(node.mesh);
325
- mapper.setInputData(polyData);
326
- const primitive = node.mesh.primitives[0]; // the first one for now
320
+ const polydata = vtkPolyData.newInstance();
321
+ mapper.setInputData(polydata);
322
+ return actor;
323
+ }
327
324
 
328
- // Support for materials
329
- if (primitive.material !== undefined) {
330
- await createPropertyFromGLTFMaterial(model, primitive.material, actor);
331
- }
332
- if (primitive.extensions !== undefined) {
333
- handlePrimitiveExtensions(primitive.extensions, model, node);
334
- }
335
- } else {
336
- const polyData = vtkPolyData.newInstance();
337
- mapper.setInputData(polyData);
325
+ /**
326
+ * Creates a VTK actor from a GLTF mesh
327
+ * @param {GLTFMesh} mesh - The GLTF mesh
328
+ * @returns {vtkActor} The created VTK actor
329
+ */
330
+ async function createActorFromGTLFPrimitive(model, primitive, worldMatrix) {
331
+ const actor = vtkActor.newInstance();
332
+ const mapper = vtkMapper.newInstance();
333
+ mapper.setColorModeToDirectScalars();
334
+ actor.setMapper(mapper);
335
+ actor.setUserMatrix(worldMatrix);
336
+ const polydata = await createPolyDataFromGLTFMesh(primitive);
337
+ mapper.setInputData(polydata);
338
+
339
+ // Support for materials
340
+ if (primitive.material != null) {
341
+ await createPropertyFromGLTFMaterial(model, primitive.material, actor);
342
+ }
343
+ if (primitive.extensions != null) {
344
+ handlePrimitiveExtensions(`${primitive.name}`, primitive.extensions, model);
338
345
  }
339
346
  return actor;
340
347
  }
341
348
 
342
349
  /**
343
- *
350
+ * Creates a GLTF animation object
344
351
  * @param {GLTFAnimation} animation
345
352
  * @returns
346
353
  */
@@ -366,7 +373,7 @@ function getTransformationMatrix(node) {
366
373
  const translation = node.translation ?? vec3.create();
367
374
  const rotation = node.rotation ?? quat.create();
368
375
  const scale = node.scale ?? vec3.fromValues(1.0, 1.0, 1.0);
369
- const matrix = node.matrix !== undefined ? mat4.clone(node.matrix) : mat4.fromRotationTranslationScale(mat4.create(), rotation, translation, scale);
376
+ const matrix = node.matrix != null ? mat4.clone(node.matrix) : mat4.fromRotationTranslationScale(mat4.create(), rotation, translation, scale);
370
377
  return matrix;
371
378
  }
372
379
 
@@ -384,13 +391,17 @@ async function processNode(node, model) {
384
391
  const worldMatrix = mat4.multiply(mat4.create(), parentMatrix, node.transform);
385
392
 
386
393
  // Create actor for the current node
387
- const actor = await createActorFromGTLFNode(model, node, worldMatrix);
388
- if (actor) {
389
- actor.setUserMatrix(worldMatrix);
394
+ if (node.mesh != null) {
395
+ const nodeActor = await createActorFromGTLFNode(worldMatrix);
390
396
  if (parentActor) {
391
- actor.setParentProp(parentActor);
397
+ nodeActor.setParentProp(parentActor);
392
398
  }
393
- model.actors.set(node.id, actor);
399
+ model.actors.set(`${node.id}`, nodeActor);
400
+ await Promise.all(node.mesh.primitives.map(async (primitive, i) => {
401
+ const actor = await createActorFromGTLFPrimitive(model, primitive, worldMatrix);
402
+ actor.setParentProp(nodeActor);
403
+ model.actors.set(`${node.id}_${primitive.name}`, actor);
404
+ }));
394
405
  }
395
406
 
396
407
  // Handle KHRLightsPunctual extension
@@ -153,7 +153,7 @@ function createVTKTextureFromGLTFTexture(image, sampler, extensions) {
153
153
  texture.setInterpolate(true);
154
154
  }
155
155
  } else {
156
- texture.MipmapOn();
156
+ texture.setMipLevel(8);
157
157
  texture.setInterpolate(true);
158
158
  texture.setEdgeClamp(true);
159
159
  }
@@ -0,0 +1,163 @@
1
+ import { vtkAlgorithm, vtkObject } from './../../interfaces';
2
+ import vtkRenderer from './../../Rendering/Core/Renderer';
3
+ import HtmlDataAccessHelper from './../Core/DataAccessHelper/HtmlDataAccessHelper';
4
+ import HttpDataAccessHelper from './../Core/DataAccessHelper/HttpDataAccessHelper';
5
+ import JSZipDataAccessHelper from './../Core/DataAccessHelper/JSZipDataAccessHelper';
6
+ import LiteHttpDataAccessHelper from './../Core/DataAccessHelper/LiteHttpDataAccessHelper';
7
+
8
+ interface IIFCImporterOptions {
9
+ compression?: string;
10
+ progressCallback?: any;
11
+ }
12
+
13
+ /**
14
+ *
15
+ */
16
+ export interface IIFCImporterInitialValues {
17
+ mergeGeometries?: boolean;
18
+ }
19
+
20
+ type vtkIFCImporterBase = vtkObject &
21
+ Omit<
22
+ vtkAlgorithm,
23
+ | 'getInputData'
24
+ | 'setInputData'
25
+ | 'setInputConnection'
26
+ | 'getInputConnection'
27
+ | 'addInputConnection'
28
+ | 'addInputData'
29
+ >;
30
+
31
+ export interface vtkIFCImporter extends vtkIFCImporterBase {
32
+ /**
33
+ *
34
+ */
35
+ getBaseURL(): string;
36
+
37
+ /**
38
+ *
39
+ */
40
+ getDataAccessHelper():
41
+ | HtmlDataAccessHelper
42
+ | HttpDataAccessHelper
43
+ | JSZipDataAccessHelper
44
+ | LiteHttpDataAccessHelper;
45
+
46
+ /**
47
+ * Get the url of the object to load.
48
+ */
49
+ getUrl(): string;
50
+
51
+ /**
52
+ * Import actors into the renderer.
53
+ * @param {vtkRenderer} renderer The vtkRenderer to import the actors into.
54
+ */
55
+ importActors(renderer: vtkRenderer): void;
56
+
57
+ /**
58
+ * Load the object data.
59
+ * @param {IIFCImporterOptions} [options]
60
+ */
61
+ loadData(options?: IIFCImporterOptions): Promise<any>;
62
+
63
+ /**
64
+ * Parse data.
65
+ * @param {String | ArrayBuffer} content The content to parse.
66
+ */
67
+ parse(content: string | ArrayBuffer): void;
68
+
69
+ /**
70
+ * Parse data as ArrayBuffer.
71
+ * @param {ArrayBuffer} content The content to parse.
72
+ */
73
+ parseAsArrayBuffer(content: ArrayBuffer): void;
74
+
75
+ /**
76
+ *
77
+ * @param inData
78
+ * @param outData
79
+ */
80
+ requestData(inData: any, outData: any): void;
81
+
82
+ /**
83
+ *
84
+ * @param dataAccessHelper
85
+ */
86
+ setDataAccessHelper(
87
+ dataAccessHelper:
88
+ | HtmlDataAccessHelper
89
+ | HttpDataAccessHelper
90
+ | JSZipDataAccessHelper
91
+ | LiteHttpDataAccessHelper
92
+ ): boolean;
93
+
94
+ /**
95
+ * Set the url of the object to load.
96
+ * @param {String} url the url of the object to load.
97
+ * @param {IIFCImporterOptions} [option] The PLY reader options.
98
+ */
99
+ setUrl(url: string, option?: IIFCImporterOptions): Promise<string | any>;
100
+ }
101
+
102
+ /**
103
+ * Set WebIFC api to be used by vtkIFCImporter
104
+ * @param {object} ifcApi
105
+ */
106
+ export function setIFCAPI(ifcApi: any): void;
107
+
108
+ /**
109
+ * Method used to decorate a given object (publicAPI+model) with vtkIFCImporter characteristics.
110
+ *
111
+ * @param publicAPI object on which methods will be bounds (public)
112
+ * @param model object on which data structure will be bounds (protected)
113
+ * @param {IIFCImporterInitialValues} [initialValues] (default: {})
114
+ */
115
+ export function extend(
116
+ publicAPI: object,
117
+ model: object,
118
+ initialValues?: IIFCImporterInitialValues
119
+ ): void;
120
+
121
+ /**
122
+ * Method used to create a new instance of vtkIFCImporter
123
+ * @param {IIFCImporterInitialValues} [initialValues] for pre-setting some of its content
124
+ */
125
+ export function newInstance(
126
+ initialValues?: IIFCImporterInitialValues
127
+ ): vtkIFCImporter;
128
+
129
+ /**
130
+ * vtkIFCImporter is a source object that reads Industry Foundation Class(IFC) files.
131
+ *
132
+ * The vtkIFCImporter is using web-ifc library to parse the IFC file.
133
+ *
134
+ * @example
135
+ * ```js
136
+ * import vtkResourceLoader from '@kitware/vtk.js/IO/Core/ResourceLoader';
137
+ * import vtkIFCImporter from '@kitware/vtk.js/IO/Geometry/IFCImporter';
138
+ *
139
+ * function update() {
140
+ * importer.onReady(() => {
141
+ * importer.importActors(renderer);
142
+ * renderer.resetCamera();
143
+ * renderWindow.render();
144
+ * });
145
+ * }
146
+ *
147
+ * vtkResourceLoader
148
+ * .loadScript('https://cdn.jsdelivr.net/npm/web-ifc@0.0.55/web-ifc-api-iife.js')
149
+ * .then(() => {
150
+ * // Pass WebIFC api to vtkIFCImporter
151
+ * vtkIFCImporter.setIFCAPI(window.WebIFC);
152
+ *
153
+ * // Trigger data download
154
+ * importer.setUrl(`${__BASE_PATH__}/data/ifc/house.ifc`).then(update);
155
+ * });
156
+ * ```
157
+ */
158
+ export declare const vtkIFCImporter: {
159
+ newInstance: typeof newInstance;
160
+ extend: typeof extend;
161
+ setIFCAPI: typeof setIFCAPI;
162
+ };
163
+ export default vtkIFCImporter;
@@ -0,0 +1,267 @@
1
+ import { m as macro } from '../../macros2.js';
2
+ import '../Core/DataAccessHelper/LiteHttpDataAccessHelper.js';
3
+ import DataAccessHelper from '../Core/DataAccessHelper.js';
4
+ import vtkActor from '../../Rendering/Core/Actor.js';
5
+ import vtkMapper from '../../Rendering/Core/Mapper.js';
6
+ import vtkDataArray from '../../Common/Core/DataArray.js';
7
+ import vtkPolyData from '../../Common/DataModel/PolyData.js';
8
+ import vtkCellArray from '../../Common/Core/CellArray.js';
9
+ import vtkAppendPolyData from '../../Filters/General/AppendPolyData.js';
10
+ import vtkMatrixBuilder from '../../Common/Core/MatrixBuilder.js';
11
+
12
+ const {
13
+ vtkErrorMacro
14
+ } = macro;
15
+ let WebIFC;
16
+
17
+ /**
18
+ * Set WebIFC api to be used by vtkIFCImporter
19
+ * @param {object} ifcApi
20
+ */
21
+ function setIFCAPI(ifcApi) {
22
+ WebIFC = ifcApi;
23
+ }
24
+ function vtkIFCImporter(publicAPI, model) {
25
+ model.classHierarchy.push('vtkIFCImporter');
26
+ const meshes = [];
27
+
28
+ /**
29
+ * Create a vtkPolyData from an IFC mesh object
30
+ * @param {object} mesh the IFC web mesh object
31
+ * @returns vtkPolyData
32
+ */
33
+ function createPolyDataFromIFCMesh(mesh) {
34
+ const {
35
+ vertices,
36
+ indices
37
+ } = mesh;
38
+ const pd = vtkPolyData.newInstance();
39
+ const cells = vtkCellArray.newInstance();
40
+ const pointValues = new Float32Array(vertices.length / 2);
41
+ const normalsArray = new Float32Array(vertices.length / 2);
42
+ for (let i = 0; i < vertices.length; i += 6) {
43
+ pointValues[i / 2] = vertices[i];
44
+ pointValues[i / 2 + 1] = vertices[i + 1];
45
+ pointValues[i / 2 + 2] = vertices[i + 2];
46
+ normalsArray[i / 2] = vertices[i + 3];
47
+ normalsArray[i / 2 + 1] = vertices[i + 4];
48
+ normalsArray[i / 2 + 2] = vertices[i + 5];
49
+ }
50
+ const nCells = indices.length;
51
+ cells.resize(3 * nCells / 3);
52
+ for (let cellId = 0; cellId < nCells; cellId += 3) {
53
+ const cell = indices.slice(cellId, cellId + 3);
54
+ cells.insertNextCell(cell);
55
+ }
56
+ pd.getPoints().setData(pointValues, 3);
57
+ pd.setStrips(cells);
58
+ pd.getPointData().setNormals(vtkDataArray.newInstance({
59
+ name: 'Normals',
60
+ values: normalsArray,
61
+ numberOfComponents: 3
62
+ }));
63
+ return pd;
64
+ }
65
+
66
+ /**
67
+ * Create a colored vtkPolyData from an IFC mesh object
68
+ * @param {object} mesh the IFC mesh object
69
+ * @returns vtkPolyData
70
+ */
71
+ function createColoredPolyDataFromIFCMesh(mesh) {
72
+ const {
73
+ vertices,
74
+ indices,
75
+ color,
76
+ userMatrix
77
+ } = mesh;
78
+ const pd = vtkPolyData.newInstance();
79
+ const cells = vtkCellArray.newInstance();
80
+ const pointValues = new Float32Array(vertices.length / 2);
81
+ const normalsArray = new Float32Array(vertices.length / 2);
82
+ const colorArray = new Float32Array(vertices.length / 2);
83
+ if (userMatrix) {
84
+ const transformMatrix = vtkMatrixBuilder.buildFromRadian().setMatrix(userMatrix);
85
+ for (let i = 0; i < vertices.length; i += 6) {
86
+ const point = [vertices[i], vertices[i + 1], vertices[i + 2]];
87
+ const normal = [vertices[i + 3], vertices[i + 4], vertices[i + 5]];
88
+ transformMatrix.apply(point).apply(normal);
89
+ pointValues[i / 2] = point[0];
90
+ pointValues[i / 2 + 1] = point[1];
91
+ pointValues[i / 2 + 2] = point[2];
92
+ normalsArray[i / 2] = normal[0];
93
+ normalsArray[i / 2 + 1] = normal[1];
94
+ normalsArray[i / 2 + 2] = normal[2];
95
+ const colorIndex = i / 2;
96
+ colorArray[colorIndex] = color.x;
97
+ colorArray[colorIndex + 1] = color.y;
98
+ colorArray[colorIndex + 2] = color.z;
99
+ }
100
+ } else {
101
+ for (let i = 0; i < vertices.length; i += 6) {
102
+ pointValues[i / 2] = vertices[i];
103
+ pointValues[i / 2 + 1] = vertices[i + 1];
104
+ pointValues[i / 2 + 2] = vertices[i + 2];
105
+ normalsArray[i / 2] = vertices[i + 3];
106
+ normalsArray[i / 2 + 1] = vertices[i + 4];
107
+ normalsArray[i / 2 + 2] = vertices[i + 5];
108
+ const colorIndex = i / 2;
109
+ colorArray[colorIndex] = color.x;
110
+ colorArray[colorIndex + 1] = color.y;
111
+ colorArray[colorIndex + 2] = color.z;
112
+ }
113
+ }
114
+ const nCells = indices.length;
115
+ cells.resize(3 * nCells / 3);
116
+ for (let cellId = 0; cellId < nCells; cellId += 3) {
117
+ const cell = indices.slice(cellId, cellId + 3);
118
+ cells.insertNextCell(cell);
119
+ }
120
+ pd.getPoints().setData(pointValues, 3);
121
+ pd.setPolys(cells);
122
+ pd.getPointData().setNormals(vtkDataArray.newInstance({
123
+ name: 'Normals',
124
+ values: normalsArray,
125
+ numberOfComponents: 3
126
+ }));
127
+ pd.getPointData().setScalars(vtkDataArray.newInstance({
128
+ name: 'Colors',
129
+ values: colorArray,
130
+ numberOfComponents: 3
131
+ }));
132
+ return pd;
133
+ }
134
+ function parseIfc(content) {
135
+ const modelID = model._ifcApi.OpenModel(new Uint8Array(content), {
136
+ COORDINATE_TO_ORIGIN: true,
137
+ USE_FAST_BOOLS: true
138
+ });
139
+ model._ifcApi.StreamAllMeshes(modelID, mesh => {
140
+ const placedGeometries = mesh.geometries;
141
+ for (let i = 0; i < placedGeometries.size(); i++) {
142
+ const placedGeometry = placedGeometries.get(i);
143
+ const ifcGeometryData = model._ifcApi.GetGeometry(modelID, placedGeometry.geometryExpressID);
144
+ const ifcVertices = model._ifcApi.GetVertexArray(ifcGeometryData.GetVertexData(), ifcGeometryData.GetVertexDataSize());
145
+ const ifcIndices = model._ifcApi.GetIndexArray(ifcGeometryData.GetIndexData(), ifcGeometryData.GetIndexDataSize());
146
+ meshes.push({
147
+ vertices: ifcVertices,
148
+ indices: ifcIndices,
149
+ color: placedGeometry.color,
150
+ userMatrix: placedGeometry.flatTransformation
151
+ });
152
+ }
153
+ });
154
+ model._ifcApi.CloseModel(modelID);
155
+ }
156
+ if (!model.dataAccessHelper) {
157
+ model.dataAccessHelper = DataAccessHelper.get('http');
158
+ }
159
+ function fetchData(url) {
160
+ const {
161
+ compression,
162
+ progressCallback
163
+ } = model;
164
+ return model.dataAccessHelper.fetchBinary(url, {
165
+ compression,
166
+ progressCallback
167
+ });
168
+ }
169
+ publicAPI.setUrl = function (url) {
170
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
171
+ binary: true
172
+ };
173
+ model.url = url;
174
+ model.baseURL = url.split('/').slice(0, -1).join('/');
175
+ model.compression = options.compression;
176
+ return publicAPI.loadData(options);
177
+ };
178
+ publicAPI.loadData = function () {
179
+ return fetchData(model.url).then(publicAPI.parse);
180
+ };
181
+ publicAPI.parse = content => {
182
+ publicAPI.parseAsArrayBuffer(content);
183
+ };
184
+ publicAPI.parseAsArrayBuffer = content => {
185
+ if (!content) {
186
+ vtkErrorMacro('No content to parse.');
187
+ return;
188
+ }
189
+ if (!WebIFC) {
190
+ vtkErrorMacro('vtkIFCImporter requires WebIFC API to be set.');
191
+ return;
192
+ }
193
+ model._ifcApi = new WebIFC.IfcAPI();
194
+ model._ifcApi.Init().then(() => {
195
+ parseIfc(content);
196
+ publicAPI.invokeReady();
197
+ });
198
+ };
199
+ publicAPI.importActors = renderer => {
200
+ if (model.mergeGeometries) {
201
+ const opaqueMeshes = meshes.filter(mesh => mesh.color.w === 1);
202
+ let apd = vtkAppendPolyData.newInstance();
203
+ opaqueMeshes.forEach(mesh => {
204
+ const pd = createColoredPolyDataFromIFCMesh(mesh);
205
+ apd.addInputData(pd);
206
+ });
207
+ let mapper = vtkMapper.newInstance();
208
+ mapper.setColorModeToDirectScalars();
209
+ mapper.setInputConnection(apd.getOutputPort());
210
+ let actor = vtkActor.newInstance();
211
+ actor.setMapper(mapper);
212
+ renderer.addActor(actor);
213
+ const transparentMeshes = meshes.filter(mesh => mesh.color.w < 1);
214
+ apd = vtkAppendPolyData.newInstance();
215
+ transparentMeshes.forEach(mesh => {
216
+ const pd = createColoredPolyDataFromIFCMesh(mesh);
217
+ apd.addInputData(pd);
218
+ });
219
+ mapper = vtkMapper.newInstance();
220
+ mapper.setColorModeToDirectScalars();
221
+ mapper.setInputConnection(apd.getOutputPort());
222
+ actor = vtkActor.newInstance();
223
+ actor.setMapper(mapper);
224
+ actor.getProperty().setOpacity(0.5);
225
+ renderer.addActor(actor);
226
+ } else {
227
+ meshes.forEach(mesh => {
228
+ const pd = createPolyDataFromIFCMesh(mesh);
229
+ const mapper = vtkMapper.newInstance();
230
+ mapper.setInputData(pd);
231
+ const actor = vtkActor.newInstance();
232
+ actor.setMapper(mapper);
233
+ const {
234
+ x,
235
+ y,
236
+ z,
237
+ w
238
+ } = mesh.color;
239
+ actor.getProperty().setColor(x, y, z);
240
+ actor.getProperty().setOpacity(w);
241
+ actor.setUserMatrix(mesh.userMatrix);
242
+ renderer.addActor(actor);
243
+ });
244
+ }
245
+ };
246
+ }
247
+ const DEFAULT_VALUES = {
248
+ mergeGeometries: false
249
+ };
250
+ function extend(publicAPI, model) {
251
+ let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
252
+ Object.assign(model, DEFAULT_VALUES, initialValues);
253
+ macro.obj(publicAPI, model);
254
+ macro.get(publicAPI, model, ['url', 'baseURL']);
255
+ macro.setGet(publicAPI, model, ['dataAccessHelper', 'mergeGeometries']);
256
+ macro.event(publicAPI, model, 'ready');
257
+ macro.algo(publicAPI, model, 0, 1);
258
+ vtkIFCImporter(publicAPI, model);
259
+ }
260
+ const newInstance = macro.newInstance(extend, 'vtkIFCImporter');
261
+ var vtkIFCImporter$1 = {
262
+ newInstance,
263
+ extend,
264
+ setIFCAPI
265
+ };
266
+
267
+ export { vtkIFCImporter$1 as default, extend, newInstance };
package/IO/Geometry.js CHANGED
@@ -4,6 +4,7 @@ import vtkDracoReader from './Geometry/DracoReader.js';
4
4
  import vtkSTLWriter from './Geometry/STLWriter.js';
5
5
  import vtkPLYWriter from './Geometry/PLYWriter.js';
6
6
  import vtkGLTFImporter from './Geometry/GLTFImporter.js';
7
+ import vtkIFCImporter from './Geometry/IFCImporter.js';
7
8
 
8
9
  var Geometry = {
9
10
  vtkSTLReader,
@@ -11,7 +12,8 @@ var Geometry = {
11
12
  vtkDracoReader,
12
13
  vtkSTLWriter,
13
14
  vtkPLYWriter,
14
- vtkGLTFImporter
15
+ vtkGLTFImporter,
16
+ vtkIFCImporter
15
17
  };
16
18
 
17
19
  export { Geometry as default };
@@ -0,0 +1,133 @@
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 ITIFFReaderOptions {
8
+ compression?: string;
9
+ progressCallback?: any;
10
+ flipY?: boolean;
11
+ }
12
+
13
+ /**
14
+ *
15
+ */
16
+ export interface ITIFFReaderInitialValues {}
17
+
18
+ type vtkTIFFReaderBase = vtkObject &
19
+ Omit<
20
+ vtkAlgorithm,
21
+ | 'getInputData'
22
+ | 'setInputData'
23
+ | 'setInputConnection'
24
+ | 'getInputConnection'
25
+ | 'addInputConnection'
26
+ | 'addInputData'
27
+ >;
28
+
29
+ export interface vtkTIFFReader extends vtkTIFFReaderBase {
30
+ /**
31
+ * Get the base url.
32
+ */
33
+ getBaseURL(): string;
34
+
35
+ /**
36
+ * Get if the image is flipped vertically.
37
+ */
38
+ getFlipY(): boolean;
39
+
40
+ /**
41
+ * Get the dataAccess helper.
42
+ */
43
+ getDataAccessHelper():
44
+ | HtmlDataAccessHelper
45
+ | HttpDataAccessHelper
46
+ | JSZipDataAccessHelper
47
+ | LiteHttpDataAccessHelper;
48
+
49
+ /**
50
+ * Get the url of the object to load.
51
+ */
52
+ getUrl(): string;
53
+
54
+ /**
55
+ * Load the object data.
56
+ * @param {ITIFFReaderOptions} [options]
57
+ */
58
+ loadData(options?: ITIFFReaderOptions): Promise<any>;
59
+
60
+ /**
61
+ * Parse data.
62
+ * @param {ArrayBuffer} content The content to parse.
63
+ */
64
+ parse(content: ArrayBuffer): void;
65
+
66
+ /**
67
+ * Parse data as ArrayBuffer.
68
+ * @param {ArrayBuffer} content The content to parse.
69
+ */
70
+ parseAsArrayBuffer(content: ArrayBuffer): void;
71
+
72
+ /**
73
+ *
74
+ * @param inData
75
+ * @param outData
76
+ */
77
+ requestData(inData: any, outData: any): void;
78
+
79
+ /**
80
+ * Flip the image vertically.
81
+ * @param {String} flipY If true, flip the image vertically.
82
+ */
83
+ setFlipY(flipY: boolean): boolean;
84
+
85
+ /**
86
+ *
87
+ * @param dataAccessHelper
88
+ */
89
+ setDataAccessHelper(
90
+ dataAccessHelper:
91
+ | HtmlDataAccessHelper
92
+ | HttpDataAccessHelper
93
+ | JSZipDataAccessHelper
94
+ | LiteHttpDataAccessHelper
95
+ ): boolean;
96
+
97
+ /**
98
+ * Set the url of the object to load.
99
+ * @param {String} url the url of the object to load.
100
+ * @param {ITIFFReaderOptions} [option] The PLY reader options.
101
+ */
102
+ setUrl(url: string, option?: ITIFFReaderOptions): Promise<string | any>;
103
+ }
104
+
105
+ /**
106
+ * Method used to decorate a given object (publicAPI+model) with vtkTIFFReader characteristics.
107
+ *
108
+ * @param publicAPI object on which methods will be bounds (public)
109
+ * @param model object on which data structure will be bounds (protected)
110
+ * @param {ITIFFReaderInitialValues} [initialValues] (default: {})
111
+ */
112
+ export function extend(
113
+ publicAPI: object,
114
+ model: object,
115
+ initialValues?: ITIFFReaderInitialValues
116
+ ): void;
117
+
118
+ /**
119
+ * Method used to create a new instance of vtkTIFFReader
120
+ * @param {ITIFFReaderInitialValues} [initialValues] for pre-setting some of its content
121
+ */
122
+ export function newInstance(
123
+ initialValues?: ITIFFReaderInitialValues
124
+ ): vtkTIFFReader;
125
+
126
+ /**
127
+ * vtkTIFFReader is a source object that reads TIFF files.
128
+ */
129
+ export declare const vtkTIFFReader: {
130
+ newInstance: typeof newInstance;
131
+ extend: typeof extend;
132
+ };
133
+ export default vtkTIFFReader;
@@ -0,0 +1,144 @@
1
+ import { m as macro } from '../../macros2.js';
2
+ import '../Core/DataAccessHelper/LiteHttpDataAccessHelper.js';
3
+ import DataAccessHelper from '../Core/DataAccessHelper.js';
4
+ import vtkImageData from '../../Common/DataModel/ImageData.js';
5
+ import vtkDataArray from '../../Common/Core/DataArray.js';
6
+ import UTIF from 'utif';
7
+
8
+ // ----------------------------------------------------------------------------
9
+ // vtkTIFFReader methods
10
+ // ----------------------------------------------------------------------------
11
+
12
+ function vtkTIFFReader(publicAPI, model) {
13
+ // Set our className
14
+ model.classHierarchy.push('vtkTIFFReader');
15
+
16
+ // Create default dataAccessHelper if not available
17
+ if (!model.dataAccessHelper) {
18
+ model.dataAccessHelper = DataAccessHelper.get('http');
19
+ }
20
+
21
+ // Internal method to fetch Array
22
+ function fetchData(url) {
23
+ const {
24
+ compression,
25
+ progressCallback
26
+ } = model;
27
+ return model.dataAccessHelper.fetchBinary(url, {
28
+ compression,
29
+ progressCallback
30
+ });
31
+ }
32
+
33
+ // Set DataSet url
34
+ publicAPI.setUrl = function (url) {
35
+ let option = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
36
+ binary: true
37
+ };
38
+ model.url = url;
39
+
40
+ // Remove the file in the URL
41
+ const path = url.split('/');
42
+ path.pop();
43
+ model.baseURL = path.join('/');
44
+ model.compression = option.compression;
45
+
46
+ // Fetch metadata
47
+ return publicAPI.loadData({
48
+ progressCallback: option.progressCallback
49
+ });
50
+ };
51
+
52
+ // Fetch the actual data arrays
53
+ publicAPI.loadData = function () {
54
+ const promise = fetchData(model.url);
55
+ promise.then(publicAPI.parse);
56
+ return promise;
57
+ };
58
+ publicAPI.parse = content => {
59
+ publicAPI.parseAsArrayBuffer(content);
60
+ };
61
+ publicAPI.parseAsArrayBuffer = content => {
62
+ if (!content) {
63
+ return;
64
+ }
65
+
66
+ // Read Header
67
+ const ifds = UTIF.decode(content);
68
+ UTIF.decodeImage(content, ifds[0]);
69
+ const data = UTIF.toRGBA8(ifds[0]);
70
+ const width = ifds[0].width;
71
+ const height = ifds[0].height;
72
+ const output = new Uint8Array(data.length);
73
+ if (model.flipY) {
74
+ for (let y = 0; y < height; y++) {
75
+ for (let x = 0; x < width; x++) {
76
+ const srcIndex = (y * width + x) * 4;
77
+ const destIndex = ((height - y - 1) * width + x) * 4;
78
+ output[destIndex] = data[srcIndex]; // R
79
+ output[destIndex + 1] = data[srcIndex + 1]; // G
80
+ output[destIndex + 2] = data[srcIndex + 2]; // B
81
+ output[destIndex + 3] = data[srcIndex + 3]; // A
82
+ }
83
+ }
84
+ }
85
+
86
+ const dataExtent = [0, width - 1, 0, height - 1];
87
+ const dataSpacing = [1, 1, 1];
88
+ const imageData = vtkImageData.newInstance();
89
+ imageData.setDimensions(width, height, 1);
90
+ imageData.setExtent(dataExtent);
91
+ imageData.setSpacing(dataSpacing);
92
+ const dataArray = vtkDataArray.newInstance({
93
+ name: 'TIFFImage',
94
+ numberOfComponents: 4,
95
+ values: output
96
+ });
97
+ imageData.getPointData().setScalars(dataArray);
98
+ model.output[0] = imageData;
99
+ };
100
+ publicAPI.requestData = (inData, outData) => {
101
+ publicAPI.parse(model.parseData);
102
+ };
103
+ }
104
+
105
+ // ----------------------------------------------------------------------------
106
+ // Object factory
107
+ // ----------------------------------------------------------------------------
108
+
109
+ const DEFAULT_VALUES = {
110
+ flipY: true,
111
+ compression: null,
112
+ progressCallback: null
113
+ };
114
+
115
+ // ----------------------------------------------------------------------------
116
+
117
+ function extend(publicAPI, model) {
118
+ let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
119
+ Object.assign(model, DEFAULT_VALUES, initialValues);
120
+
121
+ // Make this a VTK object
122
+ macro.obj(publicAPI, model);
123
+
124
+ // Also make it an algorithm with one input and one output
125
+ macro.algo(publicAPI, model, 0, 1);
126
+ macro.get(publicAPI, model, ['url', 'baseURL']);
127
+ macro.setGet(publicAPI, model, ['dataAccessHelper', 'flipY']);
128
+
129
+ // Object specific methods
130
+ vtkTIFFReader(publicAPI, model);
131
+ }
132
+
133
+ // ----------------------------------------------------------------------------
134
+
135
+ const newInstance = macro.newInstance(extend, 'vtkTIFFReader');
136
+
137
+ // ----------------------------------------------------------------------------
138
+
139
+ var vtkTIFFReader$1 = {
140
+ newInstance,
141
+ extend
142
+ };
143
+
144
+ export { vtkTIFFReader$1 as default, extend, newInstance };
package/IO/Image.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import vtkHDRReader from './Image/HDRReader.js';
2
2
  import vtkTGAReader from './Image/TGAReader.js';
3
+ import vtkTIFFReader from './Image/TIFFReader.js';
3
4
 
4
5
  var index = {
5
6
  vtkHDRReader,
6
- vtkTGAReader
7
+ vtkTGAReader,
8
+ vtkTIFFReader
7
9
  };
8
10
 
9
11
  export { index as default };
@@ -1,5 +1,6 @@
1
1
  import { m as macro } from '../../../macros2.js';
2
2
  import { k as add } from '../../../Common/Core/Math/index.js';
3
+ import vtkBoundingBox from '../../../Common/DataModel/BoundingBox.js';
3
4
  import vtkPointPicker from '../../../Rendering/Core/PointPicker.js';
4
5
 
5
6
  const MAX_POINTS = 3;
@@ -8,6 +9,7 @@ function widgetBehavior(publicAPI, model) {
8
9
  model._isDragging = false;
9
10
  const picker = vtkPointPicker.newInstance();
10
11
  picker.setPickFromList(1);
12
+ publicAPI.getBounds = () => model.widgetState.getHandleList().reduce((bounds, handle) => vtkBoundingBox.inflate(vtkBoundingBox.addPoint(bounds, ...handle.getOrigin()), publicAPI.getScaleInPixels() ? 0 : handle.getScale1() / 2), [...vtkBoundingBox.INIT_BOUNDS]);
11
13
 
12
14
  // --------------------------------------------------------------------------
13
15
  // Display 2D
@@ -342,6 +342,23 @@ function widgetBehavior(publicAPI, model) {
342
342
  };
343
343
  };
344
344
 
345
+ /**
346
+ * Rotate a line by a specified angle
347
+ * @param {string} lineName The line name to rotate (e.g. YinX, ZinX, XinY, ZinY, XinZ, YinZ)
348
+ * @param {Number} radianAngle Applied angle in radian
349
+ */
350
+ publicAPI.setViewPlane = (viewType, normal, viewUp) => {
351
+ let newViewUp = viewUp;
352
+ if (newViewUp == null) {
353
+ newViewUp = model.widgetState.getPlanes()[viewType].viewUp;
354
+ }
355
+ model.widgetState.getPlanes()[viewType] = {
356
+ normal,
357
+ viewUp: newViewUp
358
+ };
359
+ updateState(model.widgetState, model._factory.getScaleInPixels(), model._factory.getRotationHandlePosition());
360
+ };
361
+
345
362
  // --------------------------------------------------------------------------
346
363
  // initialization
347
364
  // --------------------------------------------------------------------------
@@ -113,6 +113,7 @@ function rotateVector(vectorToBeRotated, axis, angle) {
113
113
  }
114
114
 
115
115
  /**
116
+ * Return ['X'] if there are only 1 plane defined in the widget state.
116
117
  * Return ['X', 'Y'] if there are only 2 planes defined in the widget state.
117
118
  * Return ['X', 'Y', 'Z'] if there are 3 planes defined in the widget state.
118
119
  * @param {object} widgetState the state of the widget
@@ -290,6 +290,9 @@ function widgetBehavior(publicAPI, model) {
290
290
  const up = model._camera.getViewUp();
291
291
  const right = [];
292
292
  vec3.cross(right, up, normal);
293
+ vtkMath.normalize(right);
294
+ vec3.cross(up, normal, right);
295
+ vtkMath.normalize(up);
293
296
  model.shapeHandle.setUp(up);
294
297
  model.shapeHandle.setRight(right);
295
298
  model.shapeHandle.setDirection(normal);
package/index.d.ts CHANGED
@@ -93,12 +93,14 @@
93
93
  /// <reference path="./IO/Core/WSLinkClient.d.ts" />
94
94
  /// <reference path="./IO/Geometry/DracoReader.d.ts" />
95
95
  /// <reference path="./IO/Geometry/GLTFImporter.d.ts" />
96
+ /// <reference path="./IO/Geometry/IFCImporter.d.ts" />
96
97
  /// <reference path="./IO/Geometry/PLYReader.d.ts" />
97
98
  /// <reference path="./IO/Geometry/PLYWriter.d.ts" />
98
99
  /// <reference path="./IO/Geometry/STLReader.d.ts" />
99
100
  /// <reference path="./IO/Geometry/STLWriter.d.ts" />
100
101
  /// <reference path="./IO/Image/HDRReader.d.ts" />
101
102
  /// <reference path="./IO/Image/TGAReader.d.ts" />
103
+ /// <reference path="./IO/Image/TIFFReader.d.ts" />
102
104
  /// <reference path="./IO/Misc/ElevationReader.d.ts" />
103
105
  /// <reference path="./IO/Misc/GCodeReader.d.ts" />
104
106
  /// <reference path="./IO/Misc/ITKImageReader.d.ts" />
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitware/vtk.js",
3
- "version": "32.7.1",
3
+ "version": "32.8.0",
4
4
  "description": "Visualization Toolkit for the Web",
5
5
  "keywords": [
6
6
  "3d",
@@ -43,6 +43,7 @@
43
43
  "shelljs": "0.8.5",
44
44
  "spark-md5": "3.0.2",
45
45
  "stream-browserify": "3.0.0",
46
+ "utif": "3.1.0",
46
47
  "webworker-promise": "0.5.0",
47
48
  "worker-loader": "3.0.8",
48
49
  "xmlbuilder2": "3.0.2"