vtk.js 29.7.3 → 29.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.
- package/ComputeHistogram.worker-lite.worker.js.LICENSE.txt +1 -1
- package/ComputeHistogram.worker.worker.js.LICENSE.txt +1 -1
- package/PaintFilter.worker-lite.worker.js.LICENSE.txt +1 -1
- package/PaintFilter.worker.worker.js.LICENSE.txt +1 -1
- package/Sources/Rendering/Core/ImageCPRMapper/Constants.d.ts +10 -0
- package/Sources/Rendering/Core/ImageCPRMapper/Constants.js +9 -0
- package/Sources/Rendering/Core/ImageCPRMapper/index.d.ts +46 -0
- package/Sources/Rendering/Core/ImageCPRMapper/index.js +13 -1
- package/Sources/Rendering/OpenGL/ImageCPRMapper/index.js +242 -129
- package/package.json +1 -1
- package/vtk-bundle.html +1 -1
- package/vtk-lite-bundle.html +1 -1
- package/vtk-lite.js.LICENSE.txt +1 -1
- package/vtk.js.LICENSE.txt +1 -1
|
@@ -6,6 +6,7 @@ import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray';
|
|
|
6
6
|
import vtkImageData from 'vtk.js/Sources/Common/DataModel/ImageData';
|
|
7
7
|
import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData';
|
|
8
8
|
import vtkPolyLine from 'vtk.js/Sources/Common/DataModel/PolyLine';
|
|
9
|
+
import { ProjectionMode } from 'vtk.js/Sources/Rendering/Core/ImageCPRMapper/Constants';
|
|
9
10
|
|
|
10
11
|
interface ICoincidentTopology {
|
|
11
12
|
factor: number;
|
|
@@ -157,6 +158,51 @@ export interface vtkImageCPRMapper extends vtkAbstractMapper3D {
|
|
|
157
158
|
*/
|
|
158
159
|
setDirectionMatrix(mat: mat3): boolean;
|
|
159
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Thickness of the projection slab in image coordinates (NOT in voxels)
|
|
163
|
+
* Usually in millimeters if the spacing of the input image is set from a DICOM
|
|
164
|
+
*/
|
|
165
|
+
getProjectionSlabThickness(): number;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @see getProjectionSlabThickness
|
|
169
|
+
* @param projectionSlabThickness
|
|
170
|
+
*/
|
|
171
|
+
setProjectionSlabThickness(ProjectionSlabThickness: number): boolean;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Total number of samples of the volume done by the projection mode
|
|
175
|
+
* If this number is equal or less than 1, projection is disabled
|
|
176
|
+
* Using an odd number is advised
|
|
177
|
+
* If this number is even, the center of the slab will not be sampled
|
|
178
|
+
*/
|
|
179
|
+
getProjectionSlabNumberOfSamples(): number;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* @see getProjectionSlabNumberOfSamples
|
|
183
|
+
* @param projectionSlabNumberOfSamples
|
|
184
|
+
*/
|
|
185
|
+
setProjectionSlabNumberOfSamples(projectionSlabNumberOfSamples: number): boolean;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Returns wether projection is enabled
|
|
189
|
+
* It is based on the number of samples
|
|
190
|
+
* @see getProjectionSlabNumberOfSamples
|
|
191
|
+
*/
|
|
192
|
+
isProjectionEnabled(): boolean;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* The different modes of projection
|
|
196
|
+
* Available modes include MIP, MinIP and AverageIP
|
|
197
|
+
*/
|
|
198
|
+
getProjectionMode(): ProjectionMode;
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @see getProjectionMode
|
|
202
|
+
* @param projectionMode
|
|
203
|
+
*/
|
|
204
|
+
setProjectionMode(projectionMode: ProjectionMode): boolean;
|
|
205
|
+
|
|
160
206
|
/**
|
|
161
207
|
* Find the data array to use for orientation in the input polydata ( @see getOrientationArrayName )
|
|
162
208
|
*/
|
|
@@ -4,6 +4,7 @@ import vtkAbstractImageMapper from 'vtk.js/Sources/Rendering/Core/AbstractImageM
|
|
|
4
4
|
import macro from 'vtk.js/Sources/macros';
|
|
5
5
|
import vtkPoints from 'vtk.js/Sources/Common/Core/Points';
|
|
6
6
|
import vtkPolyLine from 'vtk.js/Sources/Common/DataModel/PolyLine';
|
|
7
|
+
import { ProjectionMode } from './Constants';
|
|
7
8
|
|
|
8
9
|
const { vtkErrorMacro } = macro;
|
|
9
10
|
|
|
@@ -103,7 +104,10 @@ function vtkImageCPRMapper(publicAPI, model) {
|
|
|
103
104
|
: orientationDataArray.getNumberOfComponents();
|
|
104
105
|
switch (numComps) {
|
|
105
106
|
case 16:
|
|
106
|
-
convert =
|
|
107
|
+
convert = (outQuat, inMat) => {
|
|
108
|
+
mat4.getRotation(outQuat, inMat);
|
|
109
|
+
quat.normalize(outQuat, outQuat);
|
|
110
|
+
};
|
|
107
111
|
break;
|
|
108
112
|
case 9:
|
|
109
113
|
convert = (outQuat, inMat) => {
|
|
@@ -303,6 +307,8 @@ function vtkImageCPRMapper(publicAPI, model) {
|
|
|
303
307
|
});
|
|
304
308
|
};
|
|
305
309
|
|
|
310
|
+
publicAPI.isProjectionEnabled = () => model.projectionSlabNumberOfSamples > 1;
|
|
311
|
+
|
|
306
312
|
publicAPI.setCenterlineData = (centerlineData) =>
|
|
307
313
|
publicAPI.setInputData(centerlineData, 1);
|
|
308
314
|
|
|
@@ -335,6 +341,9 @@ const DEFAULT_VALUES = {
|
|
|
335
341
|
tangentDirection: [1, 0, 0],
|
|
336
342
|
bitangentDirection: [0, 1, 0],
|
|
337
343
|
normalDirection: [0, 0, 1],
|
|
344
|
+
projectionSlabThickness: 1,
|
|
345
|
+
projectionSlabNumberOfSamples: 1,
|
|
346
|
+
projectionMode: ProjectionMode.MAX,
|
|
338
347
|
};
|
|
339
348
|
|
|
340
349
|
// ----------------------------------------------------------------------------
|
|
@@ -362,6 +371,9 @@ export function extend(publicAPI, model, initialValues = {}) {
|
|
|
362
371
|
'tangentDirection',
|
|
363
372
|
'bitangentDirection',
|
|
364
373
|
'normalDirection',
|
|
374
|
+
'projectionSlabThickness',
|
|
375
|
+
'projectionSlabNumberOfSamples',
|
|
376
|
+
'projectionMode',
|
|
365
377
|
]);
|
|
366
378
|
CoincidentTopologyHelper.implementCoincidentTopologyMethods(publicAPI, model);
|
|
367
379
|
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import macro from 'vtk.js/Sources/macros';
|
|
2
2
|
import { mat4, vec3 } from 'gl-matrix';
|
|
3
|
-
import
|
|
3
|
+
import { Filter } from 'vtk.js/Sources/Rendering/OpenGL/Texture/Constants';
|
|
4
|
+
import { InterpolationType } from 'vtk.js/Sources/Rendering/Core/ImageProperty/Constants';
|
|
5
|
+
import { ProjectionMode } from 'vtk.js/Sources/Rendering/Core/ImageCPRMapper/Constants';
|
|
6
|
+
import { Representation } from 'vtk.js/Sources/Rendering/Core/Property/Constants';
|
|
7
|
+
import { VtkDataTypes } from 'vtk.js/Sources/Common/Core/DataArray/Constants';
|
|
8
|
+
import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray';
|
|
4
9
|
import vtkHelper from 'vtk.js/Sources/Rendering/OpenGL/Helper';
|
|
10
|
+
import vtkOpenGLTexture from 'vtk.js/Sources/Rendering/OpenGL/Texture';
|
|
5
11
|
import vtkReplacementShaderMapper from 'vtk.js/Sources/Rendering/OpenGL/ReplacementShaderMapper';
|
|
6
12
|
import vtkShaderProgram from 'vtk.js/Sources/Rendering/OpenGL/ShaderProgram';
|
|
7
|
-
import
|
|
8
|
-
import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray';
|
|
9
|
-
import { VtkDataTypes } from 'vtk.js/Sources/Common/Core/DataArray/Constants';
|
|
10
|
-
import { Representation } from 'vtk.js/Sources/Rendering/Core/Property/Constants';
|
|
11
|
-
import { Filter } from 'vtk.js/Sources/Rendering/OpenGL/Texture/Constants';
|
|
12
|
-
import { InterpolationType } from 'vtk.js/Sources/Rendering/Core/ImageProperty/Constants';
|
|
13
|
+
import vtkViewNode from 'vtk.js/Sources/Rendering/SceneGraph/ViewNode';
|
|
13
14
|
|
|
14
15
|
import vtkPolyDataVS from 'vtk.js/Sources/Rendering/OpenGL/glsl/vtkPolyDataVS.glsl';
|
|
15
16
|
import vtkPolyDataFS from 'vtk.js/Sources/Rendering/OpenGL/glsl/vtkPolyDataFS.glsl';
|
|
@@ -451,46 +452,39 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
451
452
|
const customAttributes = [centerlinePosition, quadIndex];
|
|
452
453
|
|
|
453
454
|
if (!model.renderable.getUseUniformOrientation()) {
|
|
454
|
-
// For each
|
|
455
|
-
// Send
|
|
456
|
-
// The interpolation will occur in the fragment shader (slerp)
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
//
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
centerlineTopDirectionArray[offset + 2] =
|
|
472
|
-
directions[baseDirectionIdx + 2];
|
|
473
|
-
// Bot array
|
|
474
|
-
centerlineBotDirectionArray[offset + 0] =
|
|
475
|
-
directions[baseDirectionIdx + 3];
|
|
476
|
-
centerlineBotDirectionArray[offset + 1] =
|
|
477
|
-
directions[baseDirectionIdx + 4];
|
|
478
|
-
centerlineBotDirectionArray[offset + 2] =
|
|
479
|
-
directions[baseDirectionIdx + 5];
|
|
480
|
-
offset += 3;
|
|
455
|
+
// For each quad (i.e. centerline segment), a top and bottom quaternion give the orientation
|
|
456
|
+
// Send both quaternions to each vertex and use flat interpolation to get them "as is" in the fragment shader
|
|
457
|
+
// The interpolation of the quaternions will occur in the fragment shader (slerp)
|
|
458
|
+
const orientationQuats =
|
|
459
|
+
model.renderable.getOrientedCenterline().getOrientations() ?? [];
|
|
460
|
+
const centerlineTopOrientationArray = new Float32Array(4 * nPts);
|
|
461
|
+
const centerlineBotOrientationArray = new Float32Array(4 * nPts);
|
|
462
|
+
for (let quadIdx = 0; quadIdx < nLines; ++quadIdx) {
|
|
463
|
+
// All vertices of a given quad have the same topDir and botDir
|
|
464
|
+
// Polyline goes from top to bottom
|
|
465
|
+
const topQuat = orientationQuats[quadIdx];
|
|
466
|
+
const botQuat = orientationQuats[quadIdx + 1];
|
|
467
|
+
for (let pointInQuadIdx = 0; pointInQuadIdx < 4; ++pointInQuadIdx) {
|
|
468
|
+
const pointIdx = pointInQuadIdx + 4 * quadIdx;
|
|
469
|
+
const quaternionArrayOffset = 4 * pointIdx;
|
|
470
|
+
centerlineTopOrientationArray.set(topQuat, quaternionArrayOffset);
|
|
471
|
+
centerlineBotOrientationArray.set(botQuat, quaternionArrayOffset);
|
|
481
472
|
}
|
|
482
473
|
}
|
|
483
|
-
const
|
|
484
|
-
numberOfComponents:
|
|
485
|
-
values:
|
|
486
|
-
name: '
|
|
474
|
+
const centerlineTopOrientation = vtkDataArray.newInstance({
|
|
475
|
+
numberOfComponents: 4,
|
|
476
|
+
values: centerlineTopOrientationArray,
|
|
477
|
+
name: 'centerlineTopOrientation',
|
|
487
478
|
});
|
|
488
|
-
const
|
|
489
|
-
numberOfComponents:
|
|
490
|
-
values:
|
|
491
|
-
name: '
|
|
479
|
+
const centerlineBotOrientation = vtkDataArray.newInstance({
|
|
480
|
+
numberOfComponents: 4,
|
|
481
|
+
values: centerlineBotOrientationArray,
|
|
482
|
+
name: 'centerlineBotOrientation',
|
|
492
483
|
});
|
|
493
|
-
customAttributes.push(
|
|
484
|
+
customAttributes.push(
|
|
485
|
+
centerlineTopOrientation,
|
|
486
|
+
centerlineBotOrientation
|
|
487
|
+
);
|
|
494
488
|
}
|
|
495
489
|
|
|
496
490
|
model.tris.getCABO().createVBO(cells, 'polys', Representation.SURFACE, {
|
|
@@ -515,17 +509,22 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
515
509
|
const iComp = actor.getProperty().getIndependentComponents();
|
|
516
510
|
const useCenterPoint = !!model.renderable.getCenterPoint();
|
|
517
511
|
const useUniformOrientation = model.renderable.getUseUniformOrientation();
|
|
512
|
+
const projectionMode =
|
|
513
|
+
model.renderable.isProjectionEnabled() &&
|
|
514
|
+
model.renderable.getProjectionMode();
|
|
518
515
|
|
|
519
516
|
if (
|
|
520
517
|
cellBO.getProgram() === 0 ||
|
|
521
518
|
model.lastUseCenterPoint !== useCenterPoint ||
|
|
522
519
|
model.lastUseUniformOrientation !== useUniformOrientation ||
|
|
520
|
+
model.lastProjectionMode !== projectionMode ||
|
|
523
521
|
model.lastHaveSeenDepthRequest !== model.haveSeenDepthRequest ||
|
|
524
522
|
model.lastTextureComponents !== tNumComp ||
|
|
525
523
|
model.lastIndependentComponents !== iComp
|
|
526
524
|
) {
|
|
527
525
|
model.lastUseCenterPoint = useCenterPoint;
|
|
528
526
|
model.lastUseUniformOrientation = useUniformOrientation;
|
|
527
|
+
model.lastProjectionMode = projectionMode;
|
|
529
528
|
model.lastHaveSeenDepthRequest = model.haveSeenDepthRequest;
|
|
530
529
|
model.lastTextureComponents = tNumComp;
|
|
531
530
|
model.lastIndependentComponents = iComp;
|
|
@@ -544,6 +543,26 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
544
543
|
let VSSource = shaders.Vertex;
|
|
545
544
|
let FSSource = shaders.Fragment;
|
|
546
545
|
|
|
546
|
+
// https://glmatrix.net/docs/vec3.js.html#line522
|
|
547
|
+
const applyQuaternionToVecShaderFunction = [
|
|
548
|
+
'vec3 applyQuaternionToVec(vec4 q, vec3 v) {',
|
|
549
|
+
' float uvx = q.y * v.z - q.z * v.y;',
|
|
550
|
+
' float uvy = q.z * v.x - q.x * v.z;',
|
|
551
|
+
' float uvz = q.x * v.y - q.y * v.x;',
|
|
552
|
+
' float uuvx = q.y * uvz - q.z * uvy;',
|
|
553
|
+
' float uuvy = q.z * uvx - q.x * uvz;',
|
|
554
|
+
' float uuvz = q.x * uvy - q.y * uvx;',
|
|
555
|
+
' float w2 = q.w * 2.0;',
|
|
556
|
+
' uvx *= w2;',
|
|
557
|
+
' uvy *= w2;',
|
|
558
|
+
' uvz *= w2;',
|
|
559
|
+
' uuvx *= 2.0;',
|
|
560
|
+
' uuvy *= 2.0;',
|
|
561
|
+
' uuvz *= 2.0;',
|
|
562
|
+
' return vec3(v.x + uvx + uuvx, v.y + uvy + uuvy, v.z + uvz + uuvz);',
|
|
563
|
+
'}',
|
|
564
|
+
];
|
|
565
|
+
|
|
547
566
|
// Vertex shader main replacements
|
|
548
567
|
VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::Camera::Dec', [
|
|
549
568
|
'uniform mat4 MCPCMatrix;',
|
|
@@ -561,19 +580,27 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
561
580
|
'out vec2 quadOffsetVSOutput;',
|
|
562
581
|
'out vec3 centerlinePosVSOutput;',
|
|
563
582
|
];
|
|
583
|
+
const useProjection = model.renderable.isProjectionEnabled();
|
|
564
584
|
const isDirectionUniform = model.renderable.getUseUniformOrientation();
|
|
565
585
|
if (isDirectionUniform) {
|
|
566
586
|
vsColorDec.push(
|
|
567
|
-
'out vec3
|
|
568
|
-
'uniform
|
|
587
|
+
'out vec3 samplingDirVSOutput;',
|
|
588
|
+
'uniform vec4 centerlineOrientation;',
|
|
589
|
+
'uniform vec3 tangentDirection;',
|
|
590
|
+
...applyQuaternionToVecShaderFunction
|
|
569
591
|
);
|
|
592
|
+
if (useProjection) {
|
|
593
|
+
vsColorDec.push(
|
|
594
|
+
'out vec3 projectionDirVSOutput;',
|
|
595
|
+
'uniform vec3 bitangentDirection;'
|
|
596
|
+
);
|
|
597
|
+
}
|
|
570
598
|
} else {
|
|
571
599
|
vsColorDec.push(
|
|
572
|
-
'out
|
|
573
|
-
'out
|
|
574
|
-
'
|
|
575
|
-
'attribute
|
|
576
|
-
'attribute vec3 centerlineBotDirection;'
|
|
600
|
+
'out vec4 centerlineTopOrientationVSOutput;',
|
|
601
|
+
'out vec4 centerlineBotOrientationVSOutput;',
|
|
602
|
+
'attribute vec4 centerlineTopOrientation;',
|
|
603
|
+
'attribute vec4 centerlineBotOrientation;'
|
|
577
604
|
);
|
|
578
605
|
}
|
|
579
606
|
VSSource = vtkShaderProgram.substitute(
|
|
@@ -589,35 +616,18 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
589
616
|
'centerlinePosVSOutput = centerlinePosition;',
|
|
590
617
|
];
|
|
591
618
|
if (isDirectionUniform) {
|
|
592
|
-
vsColorImpl.push(
|
|
619
|
+
vsColorImpl.push(
|
|
620
|
+
'samplingDirVSOutput = applyQuaternionToVec(centerlineOrientation, tangentDirection);'
|
|
621
|
+
);
|
|
622
|
+
if (useProjection) {
|
|
623
|
+
vsColorImpl.push(
|
|
624
|
+
'projectionDirVSOutput = applyQuaternionToVec(centerlineOrientation, bitangentDirection);'
|
|
625
|
+
);
|
|
626
|
+
}
|
|
593
627
|
} else {
|
|
594
628
|
vsColorImpl.push(
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
// When x > 0: atan2(y, x) = atan(y/x)
|
|
598
|
-
// Thus: dirAngle = 2 * atan(|| topDir - botDir || / || topDir + botDir ||)
|
|
599
|
-
// This is more stable and should not be to slow compared to acos(dot(u, v))
|
|
600
|
-
'vec3 sumVec = centerlineTopDirection + centerlineBotDirection;',
|
|
601
|
-
'float sumLen2 = dot(sumVec, sumVec);',
|
|
602
|
-
'float diffLen2 = 4.0 - sumLen2;',
|
|
603
|
-
'if (diffLen2 < 0.001) {',
|
|
604
|
-
' // vectors are too close to each other, use lerp',
|
|
605
|
-
' centerlineAngleVSOutput = -1.0; // use negative angle as a flag for lerp',
|
|
606
|
-
' centerlineTopDirVSOutput = centerlineTopDirection;',
|
|
607
|
-
' centerlineBotDirVSOutput = centerlineBotDirection;',
|
|
608
|
-
'} else if (sumLen2 == 0.0) {',
|
|
609
|
-
" // vector are opposite to each other, don't make a choice for the user",
|
|
610
|
-
' // use slerp without direction, it will display the centerline color on each row of pixel',
|
|
611
|
-
' centerlineAngleVSOutput = 0.0;',
|
|
612
|
-
' centerlineTopDirVSOutput = vec3(0.0);',
|
|
613
|
-
' centerlineBotDirVSOutput = vec3(0.0);',
|
|
614
|
-
'} else {',
|
|
615
|
-
' // use slerp',
|
|
616
|
-
' centerlineAngleVSOutput = 2.0 * atan(sqrt(diffLen2/sumLen2));',
|
|
617
|
-
' float sinAngle = sin(centerlineAngleVSOutput);',
|
|
618
|
-
' centerlineTopDirVSOutput = centerlineTopDirection / sinAngle;',
|
|
619
|
-
' centerlineBotDirVSOutput = centerlineBotDirection / sinAngle;',
|
|
620
|
-
'}'
|
|
629
|
+
'centerlineTopOrientationVSOutput = centerlineTopOrientation;',
|
|
630
|
+
'centerlineBotOrientationVSOutput = centerlineBotOrientation;'
|
|
621
631
|
);
|
|
622
632
|
}
|
|
623
633
|
VSSource = vtkShaderProgram.substitute(
|
|
@@ -652,14 +662,29 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
652
662
|
`uniform float pwfshift0;`,
|
|
653
663
|
`uniform float pwfscale0;`,
|
|
654
664
|
];
|
|
665
|
+
if (useProjection) {
|
|
666
|
+
tcoordFSDec.push(
|
|
667
|
+
'uniform vec3 spacing;',
|
|
668
|
+
'uniform int projectionSlabNumberOfSamples;',
|
|
669
|
+
'uniform float projectionConstantOffset;',
|
|
670
|
+
'uniform float projectionStepLength;'
|
|
671
|
+
);
|
|
672
|
+
}
|
|
655
673
|
if (isDirectionUniform) {
|
|
656
|
-
tcoordFSDec.push('in vec3
|
|
674
|
+
tcoordFSDec.push('in vec3 samplingDirVSOutput;');
|
|
675
|
+
if (useProjection) {
|
|
676
|
+
tcoordFSDec.push('in vec3 projectionDirVSOutput;');
|
|
677
|
+
}
|
|
657
678
|
} else {
|
|
658
679
|
tcoordFSDec.push(
|
|
659
|
-
'
|
|
660
|
-
'in
|
|
661
|
-
'in
|
|
680
|
+
'uniform vec3 tangentDirection;',
|
|
681
|
+
'in vec4 centerlineTopOrientationVSOutput;',
|
|
682
|
+
'in vec4 centerlineBotOrientationVSOutput;',
|
|
683
|
+
...applyQuaternionToVecShaderFunction
|
|
662
684
|
);
|
|
685
|
+
if (useProjection) {
|
|
686
|
+
tcoordFSDec.push('uniform vec3 bitangentDirection;');
|
|
687
|
+
}
|
|
663
688
|
}
|
|
664
689
|
const centerPoint = model.renderable.getCenterPoint();
|
|
665
690
|
if (centerPoint) {
|
|
@@ -730,47 +755,103 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
730
755
|
|
|
731
756
|
let tcoordFSImpl = [];
|
|
732
757
|
if (isDirectionUniform) {
|
|
733
|
-
tcoordFSImpl.push(
|
|
734
|
-
|
|
735
|
-
|
|
758
|
+
tcoordFSImpl.push('vec3 samplingDirection = samplingDirVSOutput;');
|
|
759
|
+
if (useProjection) {
|
|
760
|
+
tcoordFSImpl.push('vec3 projectionDirection = projectionDirVSOutput;');
|
|
761
|
+
}
|
|
736
762
|
} else {
|
|
737
763
|
// Slerp or lerp between centerlineTopDirVSOutput and centerlineBotDirVSOutput
|
|
738
764
|
// We use quadOffsetVSOutput.y: bottom = 0.0; top = 1.0;
|
|
739
765
|
tcoordFSImpl.push(
|
|
740
|
-
|
|
741
|
-
'
|
|
742
|
-
'
|
|
743
|
-
'
|
|
766
|
+
// Slerp / Lerp
|
|
767
|
+
'vec4 q0 = centerlineBotOrientationVSOutput;',
|
|
768
|
+
'vec4 q1 = centerlineTopOrientationVSOutput;',
|
|
769
|
+
'float qCosAngle = dot(q0, q1);',
|
|
770
|
+
'vec4 interpolatedOrientation;',
|
|
771
|
+
'if (qCosAngle > 0.999 || qCosAngle < -0.999) {',
|
|
772
|
+
' // Use LERP instead of SLERP when the two quaternions are close or opposite',
|
|
773
|
+
' interpolatedOrientation = normalize(mix(q0, q1, quadOffsetVSOutput.y));',
|
|
744
774
|
'} else {',
|
|
745
|
-
'
|
|
746
|
-
'
|
|
747
|
-
' float botInterpolationAngle = centerlineAngleVSOutput - topInterpolationAngle;',
|
|
748
|
-
' interpolatedCenterlineDir = sin(topInterpolationAngle) * centerlineTopDirVSOutput + sin(botInterpolationAngle) * centerlineBotDirVSOutput;',
|
|
775
|
+
' float omega = acos(qCosAngle);',
|
|
776
|
+
' interpolatedOrientation = normalize(sin((1.0 - quadOffsetVSOutput.y) * omega) * q0 + sin(quadOffsetVSOutput.y * omega) * q1);',
|
|
749
777
|
'}',
|
|
750
|
-
'
|
|
751
|
-
'// Normalize for both lerp and slerp',
|
|
752
|
-
'interpolatedCenterlineDir = normalize(interpolatedCenterlineDir);'
|
|
778
|
+
'vec3 samplingDirection = applyQuaternionToVec(interpolatedOrientation, tangentDirection);'
|
|
753
779
|
);
|
|
780
|
+
if (useProjection) {
|
|
781
|
+
tcoordFSImpl.push(
|
|
782
|
+
'vec3 projectionDirection = applyQuaternionToVec(interpolatedOrientation, bitangentDirection);'
|
|
783
|
+
);
|
|
784
|
+
}
|
|
754
785
|
}
|
|
755
786
|
if (centerPoint) {
|
|
756
787
|
tcoordFSImpl.push(
|
|
757
|
-
'float baseOffset = dot(
|
|
788
|
+
'float baseOffset = dot(samplingDirection, globalCenterPoint - centerlinePosVSOutput);',
|
|
758
789
|
'float horizontalOffset = quadOffsetVSOutput.x + baseOffset;'
|
|
759
790
|
);
|
|
760
791
|
} else {
|
|
761
792
|
tcoordFSImpl.push('float horizontalOffset = quadOffsetVSOutput.x;');
|
|
762
793
|
}
|
|
763
794
|
tcoordFSImpl.push(
|
|
764
|
-
'vec3 volumePosMC = centerlinePosVSOutput + horizontalOffset *
|
|
795
|
+
'vec3 volumePosMC = centerlinePosVSOutput + horizontalOffset * samplingDirection;',
|
|
765
796
|
'vec3 volumePosTC = (MCTCMatrix * vec4(volumePosMC, 1.0)).xyz;',
|
|
766
797
|
'if (any(lessThan(volumePosTC, vec3(0.0))) || any(greaterThan(volumePosTC, vec3(1.0))))',
|
|
767
798
|
'{',
|
|
768
799
|
' // set the background color and exit',
|
|
769
800
|
' gl_FragData[0] = backgroundColor;',
|
|
770
801
|
' return;',
|
|
771
|
-
'}'
|
|
772
|
-
'vec4 tvalue = texture(volumeTexture, volumePosTC);'
|
|
802
|
+
'}'
|
|
773
803
|
);
|
|
804
|
+
|
|
805
|
+
if (useProjection) {
|
|
806
|
+
const projectionMode = model.renderable.getProjectionMode();
|
|
807
|
+
switch (projectionMode) {
|
|
808
|
+
case ProjectionMode.MIN:
|
|
809
|
+
tcoordFSImpl.push(
|
|
810
|
+
'const vec4 initialProjectionTextureValue = vec4(1.0);'
|
|
811
|
+
);
|
|
812
|
+
break;
|
|
813
|
+
case ProjectionMode.MAX:
|
|
814
|
+
case ProjectionMode.AVERAGE:
|
|
815
|
+
default:
|
|
816
|
+
tcoordFSImpl.push(
|
|
817
|
+
'const vec4 initialProjectionTextureValue = vec4(0.0);'
|
|
818
|
+
);
|
|
819
|
+
break;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// Loop on all the samples of the projection
|
|
823
|
+
tcoordFSImpl.push(
|
|
824
|
+
'vec3 projectionScaledDirection = projectionDirection / spacing;',
|
|
825
|
+
'vec3 projectionStep = projectionStepLength * projectionScaledDirection;',
|
|
826
|
+
'vec3 projectionStartPosition = volumePosTC + projectionConstantOffset * projectionScaledDirection;',
|
|
827
|
+
'vec4 tvalue = initialProjectionTextureValue;',
|
|
828
|
+
'for (int projectionSampleIdx = 0; projectionSampleIdx < projectionSlabNumberOfSamples; ++projectionSampleIdx) {',
|
|
829
|
+
' vec3 projectionSamplePosition = projectionStartPosition + float(projectionSampleIdx) * projectionStep;',
|
|
830
|
+
' vec4 sampledTextureValue = texture(volumeTexture, projectionSamplePosition);'
|
|
831
|
+
);
|
|
832
|
+
switch (projectionMode) {
|
|
833
|
+
case ProjectionMode.MAX:
|
|
834
|
+
tcoordFSImpl.push(' tvalue = max(tvalue, sampledTextureValue);');
|
|
835
|
+
break;
|
|
836
|
+
case ProjectionMode.MIN:
|
|
837
|
+
tcoordFSImpl.push(' tvalue = min(tvalue, sampledTextureValue);');
|
|
838
|
+
break;
|
|
839
|
+
case ProjectionMode.AVERAGE:
|
|
840
|
+
default:
|
|
841
|
+
tcoordFSImpl.push(' tvalue = tvalue + sampledTextureValue;');
|
|
842
|
+
break;
|
|
843
|
+
}
|
|
844
|
+
tcoordFSImpl.push('}');
|
|
845
|
+
|
|
846
|
+
// Process the total if needed
|
|
847
|
+
if (projectionMode === ProjectionMode.AVERAGE) {
|
|
848
|
+
tcoordFSImpl.push(
|
|
849
|
+
'tvalue = tvalue / float(projectionSlabNumberOfSamples);'
|
|
850
|
+
);
|
|
851
|
+
}
|
|
852
|
+
} else {
|
|
853
|
+
tcoordFSImpl.push('vec4 tvalue = texture(volumeTexture, volumePosTC);');
|
|
854
|
+
}
|
|
774
855
|
if (iComps) {
|
|
775
856
|
const rgba = ['r', 'g', 'b', 'a'];
|
|
776
857
|
for (let comp = 0; comp < tNumComp; ++comp) {
|
|
@@ -923,23 +1004,25 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
923
1004
|
};
|
|
924
1005
|
|
|
925
1006
|
publicAPI.setMapperShaderParameters = (cellBO, ren, actor) => {
|
|
1007
|
+
const program = cellBO.getProgram();
|
|
1008
|
+
const cellArrayBufferObject = cellBO.getCABO();
|
|
926
1009
|
if (
|
|
927
|
-
|
|
1010
|
+
cellArrayBufferObject.getElementCount() &&
|
|
928
1011
|
(model.VBOBuildTime.getMTime() >
|
|
929
1012
|
cellBO.getAttributeUpdateTime().getMTime() ||
|
|
930
1013
|
cellBO.getShaderSourceTime().getMTime() >
|
|
931
1014
|
cellBO.getAttributeUpdateTime().getMTime())
|
|
932
1015
|
) {
|
|
933
|
-
if (
|
|
1016
|
+
if (program.isAttributeUsed('vertexMC')) {
|
|
934
1017
|
if (
|
|
935
1018
|
!cellBO
|
|
936
1019
|
.getVAO()
|
|
937
1020
|
.addAttributeArray(
|
|
938
|
-
|
|
939
|
-
|
|
1021
|
+
program,
|
|
1022
|
+
cellArrayBufferObject,
|
|
940
1023
|
'vertexMC',
|
|
941
|
-
|
|
942
|
-
|
|
1024
|
+
cellArrayBufferObject.getVertexOffset(),
|
|
1025
|
+
cellArrayBufferObject.getStride(),
|
|
943
1026
|
model.context.FLOAT,
|
|
944
1027
|
3,
|
|
945
1028
|
model.context.FALSE
|
|
@@ -956,15 +1039,15 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
956
1039
|
.forEach((data) => {
|
|
957
1040
|
if (
|
|
958
1041
|
data &&
|
|
959
|
-
|
|
1042
|
+
program.isAttributeUsed(data.name) &&
|
|
960
1043
|
!cellBO
|
|
961
1044
|
.getVAO()
|
|
962
1045
|
.addAttributeArray(
|
|
963
|
-
|
|
964
|
-
|
|
1046
|
+
program,
|
|
1047
|
+
cellArrayBufferObject,
|
|
965
1048
|
data.name,
|
|
966
1049
|
data.offset,
|
|
967
|
-
|
|
1050
|
+
cellArrayBufferObject.getStride(),
|
|
968
1051
|
model.context.FLOAT,
|
|
969
1052
|
data.components,
|
|
970
1053
|
model.context.FALSE
|
|
@@ -977,24 +1060,53 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
977
1060
|
}
|
|
978
1061
|
|
|
979
1062
|
const texUnit = model.volumeTexture.getTextureUnit();
|
|
980
|
-
|
|
981
|
-
|
|
1063
|
+
program.setUniformi('volumeTexture', texUnit);
|
|
1064
|
+
program.setUniformf('width', model.renderable.getWidth());
|
|
982
1065
|
cellBO
|
|
983
1066
|
.getProgram()
|
|
984
|
-
.
|
|
985
|
-
'backgroundColor',
|
|
986
|
-
...model.renderable.getBackgroundColor()
|
|
987
|
-
);
|
|
1067
|
+
.setUniform4fv('backgroundColor', model.renderable.getBackgroundColor());
|
|
988
1068
|
|
|
989
|
-
if (
|
|
990
|
-
const
|
|
1069
|
+
if (program.isUniformUsed('tangentDirection')) {
|
|
1070
|
+
const tangentDirection = model.renderable.getTangentDirection();
|
|
991
1071
|
cellBO
|
|
992
1072
|
.getProgram()
|
|
993
|
-
.setUniform3fArray('
|
|
1073
|
+
.setUniform3fArray('tangentDirection', tangentDirection);
|
|
994
1074
|
}
|
|
995
|
-
if (
|
|
1075
|
+
if (program.isUniformUsed('bitangentDirection')) {
|
|
1076
|
+
const bitangentDirection = model.renderable.getBitangentDirection();
|
|
1077
|
+
cellBO
|
|
1078
|
+
.getProgram()
|
|
1079
|
+
.setUniform3fArray('bitangentDirection', bitangentDirection);
|
|
1080
|
+
}
|
|
1081
|
+
if (program.isUniformUsed('centerlineOrientation')) {
|
|
1082
|
+
const uniformOrientation = model.renderable.getUniformOrientation();
|
|
1083
|
+
cellBO
|
|
1084
|
+
.getProgram()
|
|
1085
|
+
.setUniform4fv('centerlineOrientation', uniformOrientation);
|
|
1086
|
+
}
|
|
1087
|
+
if (program.isUniformUsed('globalCenterPoint')) {
|
|
996
1088
|
const centerPoint = model.renderable.getCenterPoint();
|
|
997
|
-
|
|
1089
|
+
program.setUniform3fArray('globalCenterPoint', centerPoint);
|
|
1090
|
+
}
|
|
1091
|
+
// Projection uniforms
|
|
1092
|
+
if (model.renderable.isProjectionEnabled()) {
|
|
1093
|
+
const image = model.currentImageDataInput;
|
|
1094
|
+
const spacing = image.getSpacing();
|
|
1095
|
+
const projectionSlabThickness =
|
|
1096
|
+
model.renderable.getProjectionSlabThickness();
|
|
1097
|
+
const projectionSlabNumberOfSamples =
|
|
1098
|
+
model.renderable.getProjectionSlabNumberOfSamples();
|
|
1099
|
+
|
|
1100
|
+
program.setUniform3fArray('spacing', spacing);
|
|
1101
|
+
program.setUniformi(
|
|
1102
|
+
'projectionSlabNumberOfSamples',
|
|
1103
|
+
projectionSlabNumberOfSamples
|
|
1104
|
+
);
|
|
1105
|
+
const constantOffset = -0.5 * projectionSlabThickness;
|
|
1106
|
+
program.setUniformf('projectionConstantOffset', constantOffset);
|
|
1107
|
+
const stepLength =
|
|
1108
|
+
projectionSlabThickness / (projectionSlabNumberOfSamples - 1);
|
|
1109
|
+
program.setUniformf('projectionStepLength', stepLength);
|
|
998
1110
|
}
|
|
999
1111
|
|
|
1000
1112
|
// Model coordinates to image space
|
|
@@ -1008,7 +1120,7 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
1008
1120
|
vec3.inverse([], image.getDimensions())
|
|
1009
1121
|
);
|
|
1010
1122
|
const MCTCMatrix = mat4.mul(ICTCMatrix, ICTCMatrix, MCICMatrix);
|
|
1011
|
-
|
|
1123
|
+
program.setUniformMatrix('MCTCMatrix', MCTCMatrix);
|
|
1012
1124
|
|
|
1013
1125
|
if (model.haveSeenDepthRequest) {
|
|
1014
1126
|
cellBO
|
|
@@ -1024,9 +1136,10 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
1024
1136
|
numClipPlanes = 6;
|
|
1025
1137
|
}
|
|
1026
1138
|
|
|
1027
|
-
const shiftScaleEnabled =
|
|
1139
|
+
const shiftScaleEnabled =
|
|
1140
|
+
cellArrayBufferObject.getCoordShiftAndScaleEnabled();
|
|
1028
1141
|
const inverseShiftScaleMatrix = shiftScaleEnabled
|
|
1029
|
-
?
|
|
1142
|
+
? cellArrayBufferObject.getInverseShiftAndScaleMatrix()
|
|
1030
1143
|
: null;
|
|
1031
1144
|
const mat = inverseShiftScaleMatrix
|
|
1032
1145
|
? mat4.copy(model.imagematinv, actor.getMatrix())
|
|
@@ -1057,17 +1170,17 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
1057
1170
|
planeEquations.push(planeEquation[j]);
|
|
1058
1171
|
}
|
|
1059
1172
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1173
|
+
program.setUniformi('numClipPlanes', numClipPlanes);
|
|
1174
|
+
program.setUniform4fv('clipPlanes', planeEquations);
|
|
1062
1175
|
}
|
|
1063
1176
|
|
|
1064
1177
|
// handle coincident
|
|
1065
|
-
if (
|
|
1178
|
+
if (program.isUniformUsed('coffset')) {
|
|
1066
1179
|
const cp = publicAPI.getCoincidentParameters(ren, actor);
|
|
1067
|
-
|
|
1180
|
+
program.setUniformf('coffset', cp.offset);
|
|
1068
1181
|
// cfactor isn't always used when coffset is.
|
|
1069
|
-
if (
|
|
1070
|
-
|
|
1182
|
+
if (program.isUniformUsed('cfactor')) {
|
|
1183
|
+
program.setUniformf('cfactor', cp.factor);
|
|
1071
1184
|
}
|
|
1072
1185
|
}
|
|
1073
1186
|
};
|
package/package.json
CHANGED
package/vtk-bundle.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8"/>
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
6
|
-
<title>vtk.js [
|
|
6
|
+
<title>vtk.js [7 Mar 2024 at 13:06]</title>
|
|
7
7
|
<link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAABrVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+O1foceMD///+J0/qK1Pr7/v8Xdr/9///W8P4UdL7L7P0Scr2r4Pyj3vwad8D5/f/2/f+55f3E6f34+/2H0/ojfMKpzOd0rNgQcb3F3O/j9f7c8v6g3Pz0/P/w+v/q+P7n9v6T1/uQ1vuE0vqLut/y+v+Z2fvt+f+15Pzv9fuc2/vR7v2V2Pvd6/bg9P7I6/285/2y4/yp3/zp8vk8i8kqgMT7/P31+fyv4vxGkcz6/P6/6P3j7vfS5PNnpNUxhcbO7f7F6v3O4vHK3/DA2u631Ouy0eqXweKJud5wqthfoNMMbLvY8f73+v2dxeR8sNtTmdDx9/zX6PSjyeaCtd1YnNGX2PuQveCGt95Nls42h8dLlM3F4vBtAAAAM3RSTlMAAyOx0/sKBvik8opWGBMOAe3l1snDm2E9LSb06eHcu5JpHbarfHZCN9CBb08zzkdNS0kYaptYAAAFV0lEQVRYw92X51/aYBDHHS2O2qqttVbrqNq9m+TJIAYIShBkWwqIiCgoWvfeq7Z2/s29hyQNyUcR7LveGwVyXy6XH8/9rqxglLfUPLxVduUor3h0rfp2TYvpivk37929TkG037hffoX0+peVtZQc1589rigVUdXS/ABSAyEmGIO/1XfvldSK8vs3OqB6u3m0nxmIrvgB0dj7rr7Y9IbuF68hnfFaiHA/sxqm0wciIG43P60qKv9WXWc1RXGh/mFESFABTSBi0sNAKzqet17eCtOb3kZIDwxEEU0oAIJGYxNBDhBND29e0rtXXbcpuPmED9IhEAAQ/AXEaF8EPmnrrKsv0LvWR3fg5sWDNAFZOgAgaKvZDogHNU9MFwnnYROkc56RD5CjAbQX9Ow4g7upCsvYu55aSI/Nj0H1akgKQEUM94dwK65hYRmFU9MIcH/fqJYOZYcnuJSU/waKDgTOEVaVKhwrTRP5XzgSpAITYzom7UvkhFX5VutmxeNnWDjjswTKTyfgluNDGbUpWissXhF3s7mlSml+czWkg3D0l1nNjGNjz3myOQOa1KM/jOS6ebdbAVTCi4gljHSFrviza7tOgRWcS0MOUX9zdNgag5w7rRqA44Lzw0hr1WqES36dFliSJFlh2rXIae3FFcDDgKdxrUIDePr8jGcSClV1u7A9xeN0ModY/pHMxmR1EzRh8TJiwqsHmKW0l4FCEZI+jHio+JdPPE9qwQtTRxku2D8sIeRL2LnxWSllANCQGOIiqVHAz2ye2JR0DcH+HoxDkaADLjgxjKQ+AwCX/g0+DNgdG0ukYCONAe+dbc2IAc6fwt1ARoDSezNHxV2Cmzwv3O6lDMV55edBGwGK9n1+x2F8EDfAGCxug8MhpsMEcTEAWf3rx2vZhe/LAmtIn/6apE6PN0ULKgywD9mmdxbmFl3OvD5AS5fW5zLbv/YHmcsBTjf/afDz3MaZTVCfAP9z6/Bw6ycv8EUBWJIn9zYcoAWWlW9+OzO3vkTy8H+RANLmdrpOuYWdZYEXpo+TlCJrW5EARb7fF+bWdqf3hhyZI1nWJQHgznErZhbjoEsWqi8dQNoE294aldzFurwSABL2XXMf9+H1VQGke9exw5P/AnA5Pv5ngMul7LOvO922iwACu8WkCwLCafvM4CeWPxfA8lNHcWZSoi8EwMAIciKX2Z4SWCMAa3snCZ/G4EA8D6CMLNFsGQhkkz/gQNEBbPCbWsxGUpYVu3z8IyNAknwJkfPMEhLyrdi5RTyUVACkw4GSFRNWJNEW+fgPGwHD8/JxnRuLabN4CGNRkAE23na2+VmEAUmrYymSGjMAYqH84YUIyzgzs3XC7gNgH36Vcc4zKY9o9fgPBXUAiHHwVboBHGLiX6Zcjp1f2wu4tvzZKo0ecPnDtQYDQvJXaBeNzce45Fp28ZQLrEZVuFqgBwOalArKXnW1UzlnSusQKJqKYNuz4tOnI6sZG4zanpemv+7ySU2jbA9h6uhcgpfy6G2PahirDZ6zvq6zDduMVFTKvzw8wgyEdelwY9in3XkEPs3osJuwRQ4qTkfzifndg9Gfc4pdsu82+tTnHZTBa2EAMrqr2t43pguc8tNm7JQVQ2S0ukj2d22dhXYP0/veWtwKrCkNoNimAN5+Xr/oLrxswKbVJjteWrX7eR63o4j9q0GxnaBdWgGA5VStpanIjQmEhV0/nVt5VOFUvix6awJhPcAaTEShgrG+iGyvb5a0Ndb1YGHFPEwoqAinoaykaID1o1pdPNu7XsnCKQ3R+hwWIIhGvORcJUBYXe3Xa3vq/mF/N9V13ugufMkfXn+KHsRD0B8AAAAASUVORK5CYII=" type="image/x-icon" />
|
|
8
8
|
|
|
9
9
|
<script>
|
package/vtk-lite-bundle.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8"/>
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
6
|
-
<title>vtk.js [
|
|
6
|
+
<title>vtk.js [7 Mar 2024 at 13:06]</title>
|
|
7
7
|
<link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAABrVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+O1foceMD///+J0/qK1Pr7/v8Xdr/9///W8P4UdL7L7P0Scr2r4Pyj3vwad8D5/f/2/f+55f3E6f34+/2H0/ojfMKpzOd0rNgQcb3F3O/j9f7c8v6g3Pz0/P/w+v/q+P7n9v6T1/uQ1vuE0vqLut/y+v+Z2fvt+f+15Pzv9fuc2/vR7v2V2Pvd6/bg9P7I6/285/2y4/yp3/zp8vk8i8kqgMT7/P31+fyv4vxGkcz6/P6/6P3j7vfS5PNnpNUxhcbO7f7F6v3O4vHK3/DA2u631Ouy0eqXweKJud5wqthfoNMMbLvY8f73+v2dxeR8sNtTmdDx9/zX6PSjyeaCtd1YnNGX2PuQveCGt95Nls42h8dLlM3F4vBtAAAAM3RSTlMAAyOx0/sKBvik8opWGBMOAe3l1snDm2E9LSb06eHcu5JpHbarfHZCN9CBb08zzkdNS0kYaptYAAAFV0lEQVRYw92X51/aYBDHHS2O2qqttVbrqNq9m+TJIAYIShBkWwqIiCgoWvfeq7Z2/s29hyQNyUcR7LveGwVyXy6XH8/9rqxglLfUPLxVduUor3h0rfp2TYvpivk37929TkG037hffoX0+peVtZQc1589rigVUdXS/ABSAyEmGIO/1XfvldSK8vs3OqB6u3m0nxmIrvgB0dj7rr7Y9IbuF68hnfFaiHA/sxqm0wciIG43P60qKv9WXWc1RXGh/mFESFABTSBi0sNAKzqet17eCtOb3kZIDwxEEU0oAIJGYxNBDhBND29e0rtXXbcpuPmED9IhEAAQ/AXEaF8EPmnrrKsv0LvWR3fg5sWDNAFZOgAgaKvZDogHNU9MFwnnYROkc56RD5CjAbQX9Ow4g7upCsvYu55aSI/Nj0H1akgKQEUM94dwK65hYRmFU9MIcH/fqJYOZYcnuJSU/waKDgTOEVaVKhwrTRP5XzgSpAITYzom7UvkhFX5VutmxeNnWDjjswTKTyfgluNDGbUpWissXhF3s7mlSml+czWkg3D0l1nNjGNjz3myOQOa1KM/jOS6ebdbAVTCi4gljHSFrviza7tOgRWcS0MOUX9zdNgag5w7rRqA44Lzw0hr1WqES36dFliSJFlh2rXIae3FFcDDgKdxrUIDePr8jGcSClV1u7A9xeN0ModY/pHMxmR1EzRh8TJiwqsHmKW0l4FCEZI+jHio+JdPPE9qwQtTRxku2D8sIeRL2LnxWSllANCQGOIiqVHAz2ye2JR0DcH+HoxDkaADLjgxjKQ+AwCX/g0+DNgdG0ukYCONAe+dbc2IAc6fwt1ARoDSezNHxV2Cmzwv3O6lDMV55edBGwGK9n1+x2F8EDfAGCxug8MhpsMEcTEAWf3rx2vZhe/LAmtIn/6apE6PN0ULKgywD9mmdxbmFl3OvD5AS5fW5zLbv/YHmcsBTjf/afDz3MaZTVCfAP9z6/Bw6ycv8EUBWJIn9zYcoAWWlW9+OzO3vkTy8H+RANLmdrpOuYWdZYEXpo+TlCJrW5EARb7fF+bWdqf3hhyZI1nWJQHgznErZhbjoEsWqi8dQNoE294aldzFurwSABL2XXMf9+H1VQGke9exw5P/AnA5Pv5ngMul7LOvO922iwACu8WkCwLCafvM4CeWPxfA8lNHcWZSoi8EwMAIciKX2Z4SWCMAa3snCZ/G4EA8D6CMLNFsGQhkkz/gQNEBbPCbWsxGUpYVu3z8IyNAknwJkfPMEhLyrdi5RTyUVACkw4GSFRNWJNEW+fgPGwHD8/JxnRuLabN4CGNRkAE23na2+VmEAUmrYymSGjMAYqH84YUIyzgzs3XC7gNgH36Vcc4zKY9o9fgPBXUAiHHwVboBHGLiX6Zcjp1f2wu4tvzZKo0ecPnDtQYDQvJXaBeNzce45Fp28ZQLrEZVuFqgBwOalArKXnW1UzlnSusQKJqKYNuz4tOnI6sZG4zanpemv+7ySU2jbA9h6uhcgpfy6G2PahirDZ6zvq6zDduMVFTKvzw8wgyEdelwY9in3XkEPs3osJuwRQ4qTkfzifndg9Gfc4pdsu82+tTnHZTBa2EAMrqr2t43pguc8tNm7JQVQ2S0ukj2d22dhXYP0/veWtwKrCkNoNimAN5+Xr/oLrxswKbVJjteWrX7eR63o4j9q0GxnaBdWgGA5VStpanIjQmEhV0/nVt5VOFUvix6awJhPcAaTEShgrG+iGyvb5a0Ndb1YGHFPEwoqAinoaykaID1o1pdPNu7XsnCKQ3R+hwWIIhGvORcJUBYXe3Xa3vq/mF/N9V13ugufMkfXn+KHsRD0B8AAAAASUVORK5CYII=" type="image/x-icon" />
|
|
8
8
|
|
|
9
9
|
<script>
|
package/vtk-lite.js.LICENSE.txt
CHANGED