@kitware/vtk.js 34.4.0 → 34.5.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.
|
@@ -106,15 +106,21 @@ export interface vtkImageProperty extends vtkObject {
|
|
|
106
106
|
getUseLabelOutline(): boolean;
|
|
107
107
|
|
|
108
108
|
/**
|
|
109
|
-
* Set
|
|
110
|
-
*
|
|
109
|
+
* Set opacity of the label outline.
|
|
110
|
+
*
|
|
111
|
+
* Opacity must be between 0 and 1.
|
|
112
|
+
* If the given opacity is a number, the opacity will apply to all outline segments.
|
|
113
|
+
* If the given opacity is an array of numbers, each opacity value will apply to the
|
|
114
|
+
* label equal to the opacity value's index + 1. (This is the same behavior as setLabelOutlineThickness).
|
|
115
|
+
*
|
|
116
|
+
* @param {Number | Number[]} opacity
|
|
111
117
|
*/
|
|
112
|
-
setLabelOutlineOpacity(opacity: number): boolean;
|
|
118
|
+
setLabelOutlineOpacity(opacity: number | number[]): boolean;
|
|
113
119
|
|
|
114
120
|
/**
|
|
115
121
|
* Get the 0 to 1 opacity of the label outline.
|
|
116
122
|
*/
|
|
117
|
-
getLabelOutlineOpacity(): number;
|
|
123
|
+
getLabelOutlineOpacity(): number | number[];
|
|
118
124
|
|
|
119
125
|
/**
|
|
120
126
|
* gets the label outline thickness
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { m as macro } from '../../macros2.js';
|
|
2
2
|
import vtkAbstractMapper from './AbstractMapper.js';
|
|
3
|
+
import vtkDataArray from '../../Common/Core/DataArray.js';
|
|
4
|
+
import vtkImageData from '../../Common/DataModel/ImageData.js';
|
|
3
5
|
import vtkLookupTable from '../../Common/Core/LookupTable.js';
|
|
6
|
+
import vtkScalarsToColors from '../../Common/Core/ScalarsToColors/Constants.js';
|
|
7
|
+
import { i as isNan } from '../../Common/Core/Math/index.js';
|
|
4
8
|
import Constants from './Mapper/Constants.js';
|
|
5
9
|
|
|
6
10
|
const {
|
|
@@ -8,6 +12,237 @@ const {
|
|
|
8
12
|
ScalarMode,
|
|
9
13
|
GetArray
|
|
10
14
|
} = Constants;
|
|
15
|
+
const {
|
|
16
|
+
VectorMode
|
|
17
|
+
} = vtkScalarsToColors;
|
|
18
|
+
const {
|
|
19
|
+
VtkDataTypes
|
|
20
|
+
} = vtkDataArray;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Increase by one the 3D coordinates
|
|
24
|
+
* It will follow a zigzag pattern so that each coordinate is the neighbor of the next coordinate
|
|
25
|
+
* This enables interpolation between two texels without issues
|
|
26
|
+
* Note: texture coordinates can't be interpolated using this pattern
|
|
27
|
+
* @param {vec3} coordinates The 3D coordinates using integers for each coordinate
|
|
28
|
+
* @param {vec3} dimensions The 3D dimensions of the volume
|
|
29
|
+
*/
|
|
30
|
+
function updateZigzaggingCoordinates(coordinates, dimensions) {
|
|
31
|
+
const directionX = coordinates[1] % 2 === 0 ? 1 : -1;
|
|
32
|
+
coordinates[0] += directionX;
|
|
33
|
+
if (coordinates[0] >= dimensions[0] || coordinates[0] < 0) {
|
|
34
|
+
const directionY = coordinates[2] % 2 === 0 ? 1 : -1;
|
|
35
|
+
coordinates[0] -= directionX;
|
|
36
|
+
coordinates[1] += directionY;
|
|
37
|
+
if (coordinates[1] >= dimensions[1] || coordinates[1] < 0) {
|
|
38
|
+
coordinates[1] -= directionY;
|
|
39
|
+
coordinates[2]++;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Returns the index in the array representing the volume from a 3D coordinate
|
|
46
|
+
* @param {vec3} coordinates The 3D integer coordinates
|
|
47
|
+
* @param {vec3} dimensions The 3D dimensions of the volume
|
|
48
|
+
* @returns The index in a flat array representing the volume
|
|
49
|
+
*/
|
|
50
|
+
function getIndexFromCoordinates(coordinates, dimensions) {
|
|
51
|
+
return coordinates[0] + dimensions[0] * (coordinates[1] + dimensions[1] * coordinates[2]);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Write texture coordinates for the given `texelIndexPosition` in `textureCoordinate`.
|
|
56
|
+
* The `texelIndexPosition` is a floating point number that represents the distance in index space
|
|
57
|
+
* from the center of the first texel to the final output position.
|
|
58
|
+
* The output is given in texture coordinates and not in index coordinates (this is done at the very end of the function)
|
|
59
|
+
* @param {vec3} textureCoordinate The output texture coordinates (to avoid allocating a new Array)
|
|
60
|
+
* @param {Number} texelIndexPosition The floating point distance from the center of the first texel, following a zigzag pattern
|
|
61
|
+
* @param {vec3} dimensions The 3D dimensions of the volume
|
|
62
|
+
*/
|
|
63
|
+
function getZigZagTextureCoordinatesFromTexelPosition(textureCoordinate, texelIndexPosition, dimensions) {
|
|
64
|
+
// First compute the integer textureCoordinate
|
|
65
|
+
const intTexelIndex = Math.floor(texelIndexPosition);
|
|
66
|
+
const xCoordBeforeWrap = intTexelIndex % (2 * dimensions[0]);
|
|
67
|
+
let xDirection;
|
|
68
|
+
let xEndFlag;
|
|
69
|
+
if (xCoordBeforeWrap < dimensions[0]) {
|
|
70
|
+
textureCoordinate[0] = xCoordBeforeWrap;
|
|
71
|
+
xDirection = 1;
|
|
72
|
+
xEndFlag = textureCoordinate[0] === dimensions[0] - 1;
|
|
73
|
+
} else {
|
|
74
|
+
textureCoordinate[0] = 2 * dimensions[0] - 1 - xCoordBeforeWrap;
|
|
75
|
+
xDirection = -1;
|
|
76
|
+
xEndFlag = textureCoordinate[0] === 0;
|
|
77
|
+
}
|
|
78
|
+
const intRowIndex = Math.floor(intTexelIndex / dimensions[0]);
|
|
79
|
+
const yCoordBeforeWrap = intRowIndex % (2 * dimensions[1]);
|
|
80
|
+
let yDirection;
|
|
81
|
+
let yEndFlag;
|
|
82
|
+
if (yCoordBeforeWrap < dimensions[1]) {
|
|
83
|
+
textureCoordinate[1] = yCoordBeforeWrap;
|
|
84
|
+
yDirection = 1;
|
|
85
|
+
yEndFlag = textureCoordinate[1] === dimensions[1] - 1;
|
|
86
|
+
} else {
|
|
87
|
+
textureCoordinate[1] = 2 * dimensions[1] - 1 - yCoordBeforeWrap;
|
|
88
|
+
yDirection = -1;
|
|
89
|
+
yEndFlag = textureCoordinate[1] === 0;
|
|
90
|
+
}
|
|
91
|
+
textureCoordinate[2] = Math.floor(intRowIndex / dimensions[1]);
|
|
92
|
+
|
|
93
|
+
// Now add the remainder either in x, y or z
|
|
94
|
+
const remainder = texelIndexPosition - intTexelIndex;
|
|
95
|
+
if (xEndFlag) {
|
|
96
|
+
if (yEndFlag) {
|
|
97
|
+
textureCoordinate[2] += remainder;
|
|
98
|
+
} else {
|
|
99
|
+
textureCoordinate[1] += yDirection * remainder;
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
textureCoordinate[0] += xDirection * remainder;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// textureCoordinates are in index space, convert to texture space
|
|
106
|
+
textureCoordinate[0] = (textureCoordinate[0] + 0.5) / dimensions[0];
|
|
107
|
+
textureCoordinate[1] = (textureCoordinate[1] + 0.5) / dimensions[1];
|
|
108
|
+
textureCoordinate[2] = (textureCoordinate[2] + 0.5) / dimensions[2];
|
|
109
|
+
}
|
|
110
|
+
// Associate an input vtkDataArray to an object { stringHash, textureCoordinates }
|
|
111
|
+
// A single dataArray only caches one array of texture coordinates, so this cache is useless when
|
|
112
|
+
// the input data array is used with two different lookup tables (which is very unlikely)
|
|
113
|
+
const colorTextureCoordinatesCache = new WeakMap();
|
|
114
|
+
/**
|
|
115
|
+
* The minimum of the range is mapped to the center of the first texel excluding min texel (texel at index distance 1)
|
|
116
|
+
* The maximum of the range is mapped to the center of the last texel excluding max and NaN texels (texel at index distance numberOfColorsInRange)
|
|
117
|
+
* The result is cached, and is reused if the arguments are the same and the input doesn't change
|
|
118
|
+
* @param {vtkDataArray} input The input data array used for coloring
|
|
119
|
+
* @param {Number} component The component of the input data array that is used for coloring (-1 for magnitude of the vectors)
|
|
120
|
+
* @param {Range} range The range of the scalars
|
|
121
|
+
* @param {boolean} useLogScale Should the values be transformed to logarithmic scale. When true, the range must already be in logarithmic scale.
|
|
122
|
+
* @param {Number} numberOfColorsInRange The number of colors that are used in the range
|
|
123
|
+
* @param {vec3} dimensions The dimensions of the texture
|
|
124
|
+
* @param {boolean} useZigzagPattern If a zigzag pattern should be used. Otherwise 1 row for colors (including min and max) and 1 row for NaN are used.
|
|
125
|
+
* @returns A vtkDataArray containing the texture coordinates (2D or 3D)
|
|
126
|
+
*/
|
|
127
|
+
function getOrCreateColorTextureCoordinates(input, component, range, useLogScale, numberOfColorsInRange, dimensions, useZigzagPattern) {
|
|
128
|
+
// Caching using the "arguments" special object (because it is a pure function)
|
|
129
|
+
const argStrings = new Array(arguments.length);
|
|
130
|
+
for (let argIndex = 0; argIndex < arguments.length; ++argIndex) {
|
|
131
|
+
// eslint-disable-next-line prefer-rest-params
|
|
132
|
+
const arg = arguments[argIndex];
|
|
133
|
+
argStrings[argIndex] = arg.getMTime?.() ?? arg;
|
|
134
|
+
}
|
|
135
|
+
const stringHash = argStrings.join('/');
|
|
136
|
+
const cachedResult = colorTextureCoordinatesCache.get(input);
|
|
137
|
+
if (cachedResult && cachedResult.stringHash === stringHash) {
|
|
138
|
+
return cachedResult.textureCoordinates;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// The range used for computing coordinates have to change
|
|
142
|
+
// slightly to accommodate the special above- and below-range
|
|
143
|
+
// colors that are the first and last texels, respectively.
|
|
144
|
+
const scalarTexelWidth = (range[1] - range[0]) / (numberOfColorsInRange - 1);
|
|
145
|
+
const [paddedRangeMin, paddedRangeMax] = [range[0] - scalarTexelWidth, range[1] + scalarTexelWidth];
|
|
146
|
+
|
|
147
|
+
// Use the center of the voxel
|
|
148
|
+
const textureSOrigin = paddedRangeMin - 0.5 * scalarTexelWidth;
|
|
149
|
+
const textureSCoeff = 1.0 / (paddedRangeMax - paddedRangeMin + scalarTexelWidth);
|
|
150
|
+
|
|
151
|
+
// Compute in index space first
|
|
152
|
+
const texelIndexOrigin = paddedRangeMin;
|
|
153
|
+
const texelIndexCoeff = (numberOfColorsInRange + 1) / (paddedRangeMax - paddedRangeMin);
|
|
154
|
+
const inputV = input.getData();
|
|
155
|
+
const numScalars = input.getNumberOfTuples();
|
|
156
|
+
const numComps = input.getNumberOfComponents();
|
|
157
|
+
const useMagnitude = component < 0 || component >= numComps;
|
|
158
|
+
const numberOfOutputComponents = dimensions[2] <= 1 ? 2 : 3;
|
|
159
|
+
const output = vtkDataArray.newInstance({
|
|
160
|
+
numberOfComponents: numberOfOutputComponents,
|
|
161
|
+
values: new Float32Array(numScalars * numberOfOutputComponents)
|
|
162
|
+
});
|
|
163
|
+
const outputV = output.getData();
|
|
164
|
+
const nanTextureCoordinate = [0, 0, 0];
|
|
165
|
+
// Distance of NaN from the beginning:
|
|
166
|
+
// min: 0, ...colorsInRange, max: numberOfColorsInRange + 1, NaN = numberOfColorsInRange + 2
|
|
167
|
+
getZigZagTextureCoordinatesFromTexelPosition(nanTextureCoordinate, numberOfColorsInRange + 2, dimensions);
|
|
168
|
+
|
|
169
|
+
// Set a texture coordinate in the output for each tuple in the input
|
|
170
|
+
let inputIdx = 0;
|
|
171
|
+
let outputIdx = 0;
|
|
172
|
+
const textureCoordinate = [0.5, 0.5, 0.5];
|
|
173
|
+
for (let scalarIdx = 0; scalarIdx < numScalars; ++scalarIdx) {
|
|
174
|
+
// Get scalar value from magnitude or a single component
|
|
175
|
+
let scalarValue;
|
|
176
|
+
if (useMagnitude) {
|
|
177
|
+
let sum = 0;
|
|
178
|
+
for (let compIdx = 0; compIdx < numComps; ++compIdx) {
|
|
179
|
+
const compValue = inputV[inputIdx + compIdx];
|
|
180
|
+
sum += compValue * compValue;
|
|
181
|
+
}
|
|
182
|
+
scalarValue = Math.sqrt(sum);
|
|
183
|
+
} else {
|
|
184
|
+
scalarValue = inputV[inputIdx + component];
|
|
185
|
+
}
|
|
186
|
+
if (useLogScale) {
|
|
187
|
+
scalarValue = Math.log10(scalarValue);
|
|
188
|
+
}
|
|
189
|
+
inputIdx += numComps;
|
|
190
|
+
|
|
191
|
+
// Convert to texture coordinates and update output
|
|
192
|
+
if (isNan(scalarValue)) {
|
|
193
|
+
// Last texels are NaN colors (there is at least one NaN color)
|
|
194
|
+
textureCoordinate[0] = nanTextureCoordinate[0];
|
|
195
|
+
textureCoordinate[1] = nanTextureCoordinate[1];
|
|
196
|
+
textureCoordinate[2] = nanTextureCoordinate[2];
|
|
197
|
+
} else if (useZigzagPattern) {
|
|
198
|
+
// Texel position is in [0, numberOfColorsInRange + 1]
|
|
199
|
+
let texelIndexPosition = (scalarValue - texelIndexOrigin) * texelIndexCoeff;
|
|
200
|
+
if (texelIndexPosition < 1) {
|
|
201
|
+
// Use min color when smaller than range
|
|
202
|
+
texelIndexPosition = 0;
|
|
203
|
+
} else if (texelIndexPosition > numberOfColorsInRange) {
|
|
204
|
+
// Use max color when greater than range
|
|
205
|
+
texelIndexPosition = numberOfColorsInRange + 1;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Convert the texel position into texture coordinate following a zigzag pattern
|
|
209
|
+
getZigZagTextureCoordinatesFromTexelPosition(textureCoordinate, texelIndexPosition, dimensions);
|
|
210
|
+
} else {
|
|
211
|
+
// 0.0 in t coordinate means not NaN. So why am I setting it to 0.49?
|
|
212
|
+
// Because when you are mapping scalars and you have a NaN adjacent to
|
|
213
|
+
// anything else, the interpolation everywhere should be NaN. Thus, I
|
|
214
|
+
// want the NaN color everywhere except right on the non-NaN neighbors.
|
|
215
|
+
// To simulate this, I set the t coord for the real numbers close to
|
|
216
|
+
// the threshold so that the interpolation almost immediately looks up
|
|
217
|
+
// the NaN value.
|
|
218
|
+
textureCoordinate[1] = 0.49;
|
|
219
|
+
|
|
220
|
+
// Some implementations apparently don't handle relatively large
|
|
221
|
+
// numbers (compared to the range [0.0, 1.0]) very well. In fact,
|
|
222
|
+
// values above 1122.0f appear to cause texture wrap-around on
|
|
223
|
+
// some systems even when edge clamping is enabled. Why 1122.0f? I
|
|
224
|
+
// don't know. For safety, we'll clamp at +/- 1000. This will
|
|
225
|
+
// result in incorrect images when the texture value should be
|
|
226
|
+
// above or below 1000, but I don't have a better solution.
|
|
227
|
+
const textureS = (scalarValue - textureSOrigin) * textureSCoeff;
|
|
228
|
+
if (textureS > 1000.0) {
|
|
229
|
+
textureCoordinate[0] = 1000.0;
|
|
230
|
+
} else if (textureS < -1000.0) {
|
|
231
|
+
textureCoordinate[0] = -1000.0;
|
|
232
|
+
} else {
|
|
233
|
+
textureCoordinate[0] = textureS;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
for (let i = 0; i < numberOfOutputComponents; ++i) {
|
|
237
|
+
outputV[outputIdx++] = textureCoordinate[i];
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
colorTextureCoordinatesCache.set(input, {
|
|
241
|
+
stringHash,
|
|
242
|
+
textureCoordinates: output
|
|
243
|
+
});
|
|
244
|
+
return output;
|
|
245
|
+
}
|
|
11
246
|
|
|
12
247
|
// ---------------------------------------------------------------------------
|
|
13
248
|
// vtkMapper2D methods
|
|
@@ -35,7 +270,7 @@ function vtkMapper2D(publicAPI, model) {
|
|
|
35
270
|
if (!input || !model.scalarVisibility) {
|
|
36
271
|
return {
|
|
37
272
|
scalars: null,
|
|
38
|
-
|
|
273
|
+
cellFlag: false
|
|
39
274
|
};
|
|
40
275
|
}
|
|
41
276
|
let scalars = null;
|
|
@@ -96,8 +331,14 @@ function vtkMapper2D(publicAPI, model) {
|
|
|
96
331
|
return mt;
|
|
97
332
|
};
|
|
98
333
|
publicAPI.mapScalars = (input, alpha) => {
|
|
99
|
-
const
|
|
334
|
+
const {
|
|
335
|
+
scalars,
|
|
336
|
+
cellFlag
|
|
337
|
+
} = publicAPI.getAbstractScalars(input, model.scalarMode, model.arrayAccessMode, model.arrayId, model.colorByArrayName);
|
|
338
|
+
model.areScalarsMappedFromCells = cellFlag;
|
|
100
339
|
if (!scalars) {
|
|
340
|
+
model.colorCoordinates = null;
|
|
341
|
+
model.colorTextureMap = null;
|
|
101
342
|
model.colorMapColors = null;
|
|
102
343
|
return;
|
|
103
344
|
}
|
|
@@ -108,14 +349,124 @@ function vtkMapper2D(publicAPI, model) {
|
|
|
108
349
|
if (!model.useLookupTableScalarRange) {
|
|
109
350
|
publicAPI.getLookupTable().setRange(model.scalarRange[0], model.scalarRange[1]);
|
|
110
351
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
model.
|
|
352
|
+
if (publicAPI.canUseTextureMapForColoring(scalars, cellFlag)) {
|
|
353
|
+
model.mapScalarsToTexture(scalars, cellFlag, alpha);
|
|
354
|
+
} else {
|
|
355
|
+
model.colorCoordinates = null;
|
|
356
|
+
model.colorTextureMap = null;
|
|
357
|
+
const lut = publicAPI.getLookupTable();
|
|
358
|
+
if (lut) {
|
|
359
|
+
// Ensure that the lookup table is built
|
|
360
|
+
lut.build();
|
|
361
|
+
model.colorMapColors = lut.mapScalars(scalars, model.colorMode, model.fieldDataTupleId);
|
|
362
|
+
}
|
|
116
363
|
}
|
|
117
364
|
model.colorBuildString = `${publicAPI.getMTime()}${scalars.getMTime()}${alpha}`;
|
|
118
365
|
};
|
|
366
|
+
publicAPI.canUseTextureMapForColoring = (scalars, cellFlag) => {
|
|
367
|
+
if (cellFlag && !(model.colorMode === ColorMode.DIRECT_SCALARS)) {
|
|
368
|
+
return true; // cell data always use textures.
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// index color does not use textures
|
|
372
|
+
if (model.lookupTable && model.lookupTable.getIndexedLookup()) {
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
if (!scalars) {
|
|
376
|
+
// no scalars on this dataset, we don't care if texture is used at all.
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
if (model.colorMode === ColorMode.DEFAULT && scalars.getDataType() === VtkDataTypes.UNSIGNED_CHAR || model.colorMode === ColorMode.DIRECT_SCALARS) {
|
|
380
|
+
// Don't use texture if direct coloring using RGB unsigned chars is
|
|
381
|
+
// requested.
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
return true;
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
// Protected method
|
|
388
|
+
model.mapScalarsToTexture = (scalars, cellFlag, alpha) => {
|
|
389
|
+
const range = model.lookupTable.getRange();
|
|
390
|
+
const useLogScale = model.lookupTable.usingLogScale();
|
|
391
|
+
const origAlpha = model.lookupTable.getAlpha();
|
|
392
|
+
const scaledRange = useLogScale ? [Math.log10(range[0]), Math.log10(range[1])] : range;
|
|
393
|
+
|
|
394
|
+
// Get rid of vertex color array. Only texture or vertex coloring
|
|
395
|
+
// can be active at one time. The existence of the array is the
|
|
396
|
+
// signal to use that technique.
|
|
397
|
+
model.colorMapColors = null;
|
|
398
|
+
|
|
399
|
+
// If the lookup table has changed, then recreate the color texture map.
|
|
400
|
+
// Set a new lookup table changes this->MTime.
|
|
401
|
+
if (model.colorTextureMap == null || publicAPI.getMTime() > model.colorTextureMap.getMTime() || model.lookupTable.getMTime() > model.colorTextureMap.getMTime() || model.lookupTable.getAlpha() !== alpha) {
|
|
402
|
+
model.lookupTable.setAlpha(alpha);
|
|
403
|
+
model.colorTextureMap = null;
|
|
404
|
+
|
|
405
|
+
// Get the texture map from the lookup table.
|
|
406
|
+
// Create a dummy ramp of scalars.
|
|
407
|
+
// In the future, we could extend vtkScalarsToColors.
|
|
408
|
+
model.lookupTable.build();
|
|
409
|
+
const numberOfAvailableColors = model.lookupTable.getNumberOfAvailableColors();
|
|
410
|
+
|
|
411
|
+
// Maximum dimensions and number of colors in range
|
|
412
|
+
const maxTextureWidthForCells = 2048;
|
|
413
|
+
const maxColorsInRangeForCells = maxTextureWidthForCells ** 3 - 3; // 3D but keep a color for min, max and NaN
|
|
414
|
+
const maxTextureWidthForPoints = 4096;
|
|
415
|
+
const maxColorsInRangeForPoints = maxTextureWidthForPoints - 2; // 1D but keep a color for min and max (NaN is in a different row)
|
|
416
|
+
// Minimum number of colors in range (excluding special colors like minColor, maxColor and NaNColor)
|
|
417
|
+
const minColorsInRange = 2;
|
|
418
|
+
// Maximum number of colors, limited by the maximum possible texture size
|
|
419
|
+
const maxColorsInRange = cellFlag ? maxColorsInRangeForCells : maxColorsInRangeForPoints;
|
|
420
|
+
model.numberOfColorsInRange = Math.min(Math.max(numberOfAvailableColors, minColorsInRange), maxColorsInRange);
|
|
421
|
+
const numberOfColorsForCells = model.numberOfColorsInRange + 3; // Add min, max and NaN
|
|
422
|
+
const numberOfColorsInUpperRowForPoints = model.numberOfColorsInRange + 2; // Add min and max ; the lower row will be used for NaN color
|
|
423
|
+
const textureDimensions = cellFlag ? [Math.min(Math.ceil(numberOfColorsForCells / maxTextureWidthForCells ** 0), maxTextureWidthForCells), Math.min(Math.ceil(numberOfColorsForCells / maxTextureWidthForCells ** 1), maxTextureWidthForCells), Math.min(Math.ceil(numberOfColorsForCells / maxTextureWidthForCells ** 2), maxTextureWidthForCells)] : [numberOfColorsInUpperRowForPoints, 2, 1];
|
|
424
|
+
const textureSize = textureDimensions[0] * textureDimensions[1] * textureDimensions[2];
|
|
425
|
+
const scalarsArray = new Float64Array(textureSize);
|
|
426
|
+
|
|
427
|
+
// Colors for NaN by default
|
|
428
|
+
scalarsArray.fill(NaN);
|
|
429
|
+
|
|
430
|
+
// Colors in range
|
|
431
|
+
// Add 2 to also get color for min and max
|
|
432
|
+
const numberOfNonSpecialColors = model.numberOfColorsInRange;
|
|
433
|
+
const numberOfNonNaNColors = numberOfNonSpecialColors + 2;
|
|
434
|
+
const textureCoordinates = [0, 0, 0];
|
|
435
|
+
const rangeMin = scaledRange[0];
|
|
436
|
+
const rangeDifference = scaledRange[1] - scaledRange[0];
|
|
437
|
+
for (let i = 0; i < numberOfNonNaNColors; ++i) {
|
|
438
|
+
const scalarsArrayIndex = getIndexFromCoordinates(textureCoordinates, textureDimensions);
|
|
439
|
+
|
|
440
|
+
// Minus 1 start at min color
|
|
441
|
+
const intermediateValue = rangeMin + rangeDifference * (i - 1) / (numberOfNonSpecialColors - 1);
|
|
442
|
+
const scalarValue = useLogScale ? 10.0 ** intermediateValue : intermediateValue;
|
|
443
|
+
scalarsArray[scalarsArrayIndex] = scalarValue;
|
|
444
|
+
|
|
445
|
+
// Colors are zigzagging to allow interpolation between two neighbor colors when coloring cells
|
|
446
|
+
updateZigzaggingCoordinates(textureCoordinates, textureDimensions);
|
|
447
|
+
}
|
|
448
|
+
const scalarsDataArray = vtkDataArray.newInstance({
|
|
449
|
+
numberOfComponents: 1,
|
|
450
|
+
values: scalarsArray
|
|
451
|
+
});
|
|
452
|
+
const colorsDataArray = model.lookupTable.mapScalars(scalarsDataArray, model.colorMode, 0);
|
|
453
|
+
model.colorTextureMap = vtkImageData.newInstance();
|
|
454
|
+
model.colorTextureMap.setDimensions(textureDimensions);
|
|
455
|
+
model.colorTextureMap.getPointData().setScalars(colorsDataArray);
|
|
456
|
+
model.lookupTable.setAlpha(origAlpha);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Although I like the feature of applying magnitude to single component
|
|
460
|
+
// scalars, it is not how the old MapScalars for vertex coloring works.
|
|
461
|
+
const scalarComponent = model.lookupTable.getVectorMode() === VectorMode.MAGNITUDE && scalars.getNumberOfComponents() > 1 ? -1 : model.lookupTable.getVectorComponent();
|
|
462
|
+
|
|
463
|
+
// Create new coordinates if necessary, this function uses cache if possible.
|
|
464
|
+
// A zigzag pattern can't be used with point data, as interpolation of texture coordinates will be wrong
|
|
465
|
+
// A zigzag pattern can be used with cell data, as there will be no texture coordinates interpolation
|
|
466
|
+
// The texture generated using a zigzag pattern in one dimension is the same as without zigzag
|
|
467
|
+
// Therefore, the same code can be used for texture generation of point/cell data but not for texture coordinates
|
|
468
|
+
model.colorCoordinates = getOrCreateColorTextureCoordinates(scalars, scalarComponent, scaledRange, useLogScale, model.numberOfColorsInRange, model.colorTextureMap.getDimensions(), cellFlag);
|
|
469
|
+
};
|
|
119
470
|
publicAPI.getPrimitiveCount = () => {
|
|
120
471
|
const input = publicAPI.getInputData();
|
|
121
472
|
const pcount = {
|
|
@@ -143,6 +494,9 @@ const DEFAULT_VALUES = {
|
|
|
143
494
|
arrayAccessMode: 1,
|
|
144
495
|
// By_NAME
|
|
145
496
|
|
|
497
|
+
colorMapColors: null,
|
|
498
|
+
// Same as this->Colors
|
|
499
|
+
areScalarsMappedFromCells: false,
|
|
146
500
|
renderTime: 0,
|
|
147
501
|
colorByArrayName: null,
|
|
148
502
|
transformCoordinate: null,
|
|
@@ -157,7 +511,7 @@ function extend(publicAPI, model) {
|
|
|
157
511
|
|
|
158
512
|
// Inheritance
|
|
159
513
|
vtkAbstractMapper.extend(publicAPI, model, initialValues);
|
|
160
|
-
macro.get(publicAPI, model, ['colorMapColors']);
|
|
514
|
+
macro.get(publicAPI, model, ['areScalarsMappedFromCells', 'colorCoordinates', 'colorTextureMap', 'colorMapColors']);
|
|
161
515
|
macro.setGet(publicAPI, model, ['arrayAccessMode', 'colorByArrayName', 'colorMode', 'lookupTable', 'renderTime', 'scalarMode', 'scalarVisibility', 'static', 'transformCoordinate', 'useLookupTableScalarRange', 'viewSpecificProperties', 'customShaderAttributes' // point data array names that will be transferred to the VBO
|
|
162
516
|
]);
|
|
163
517
|
|
|
@@ -54,7 +54,7 @@ function vtkOpenGLImageMapper(publicAPI, model) {
|
|
|
54
54
|
// The openGLTexture is not shared
|
|
55
55
|
model.openGLTexture.releaseGraphicsResources(renderWindow);
|
|
56
56
|
// All these other resources are shared
|
|
57
|
-
[model._colorTransferFunc, model._pwFunc, model._labelOutlineThicknessArray].forEach(coreObject => renderWindow.unregisterGraphicsResourceUser(coreObject, publicAPI));
|
|
57
|
+
[model._colorTransferFunc, model._pwFunc, model._labelOutlineThicknessArray, model._labelOutlineOpacity].forEach(coreObject => renderWindow.unregisterGraphicsResourceUser(coreObject, publicAPI));
|
|
58
58
|
}
|
|
59
59
|
publicAPI.buildPass = prepass => {
|
|
60
60
|
if (prepass) {
|
|
@@ -131,7 +131,14 @@ function vtkOpenGLImageMapper(publicAPI, model) {
|
|
|
131
131
|
// color shift and scale
|
|
132
132
|
'uniform float cshift0;', 'uniform float cscale0;',
|
|
133
133
|
// pwf shift and scale
|
|
134
|
-
'uniform float pwfshift0;', 'uniform float pwfscale0;', 'uniform sampler2D texture1;', 'uniform sampler2D colorTexture1;', 'uniform sampler2D pwfTexture1;', 'uniform
|
|
134
|
+
'uniform float pwfshift0;', 'uniform float pwfscale0;', 'uniform sampler2D texture1;', 'uniform sampler2D colorTexture1;', 'uniform sampler2D pwfTexture1;', 'uniform float opacity;'];
|
|
135
|
+
if (actor.getProperty().getUseLabelOutline()) {
|
|
136
|
+
tcoordDec = tcoordDec.concat([
|
|
137
|
+
// outline thickness
|
|
138
|
+
'uniform sampler2D labelOutlineTexture1;',
|
|
139
|
+
// outline opacity
|
|
140
|
+
'uniform sampler2D labelOutlineOpacityTexture1;']);
|
|
141
|
+
}
|
|
135
142
|
if (iComps) {
|
|
136
143
|
for (let comp = 1; comp < tNumComp; comp++) {
|
|
137
144
|
tcoordDec = tcoordDec.concat([
|
|
@@ -167,7 +174,7 @@ function vtkOpenGLImageMapper(publicAPI, model) {
|
|
|
167
174
|
// check for the outline thickness and opacity
|
|
168
175
|
const vtkImageLabelOutline = actor.getProperty().getUseLabelOutline();
|
|
169
176
|
if (vtkImageLabelOutline === true) {
|
|
170
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::LabelOutline::Dec', ['uniform
|
|
177
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::LabelOutline::Dec', ['uniform float vpWidth;', 'uniform float vpHeight;', 'uniform float vpOffsetX;', 'uniform float vpOffsetY;', 'uniform mat4 PCWCMatrix;', 'uniform mat4 vWCtoIDX;', 'uniform ivec3 imageDimensions;', 'uniform int sliceAxis;']).result;
|
|
171
178
|
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::ImageLabelOutlineOn', '#define vtkImageLabelOutlineOn').result;
|
|
172
179
|
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::LabelOutlineHelperFunction', ['#ifdef vtkImageLabelOutlineOn', 'vec3 fragCoordToIndexSpace(vec4 fragCoord) {', ' vec4 pcPos = vec4(', ' (fragCoord.x / vpWidth - vpOffsetX - 0.5) * 2.0,', ' (fragCoord.y / vpHeight - vpOffsetY - 0.5) * 2.0,', ' (fragCoord.z - 0.5) * 2.0,', ' 1.0);', '', ' vec4 worldCoord = PCWCMatrix * pcPos;', ' vec4 vertex = (worldCoord/worldCoord.w);', '', ' vec3 index = (vWCtoIDX * vertex).xyz;', '', ' // half voxel fix for labelmapOutline', ' return (index + vec3(0.5)) / vec3(imageDimensions);', '}', 'vec2 getSliceCoords(vec3 coord, int axis) {', ' if (axis == 0) return coord.yz;', ' if (axis == 1) return coord.xz;', ' if (axis == 2) return coord.xy;', '}', '#endif']).result;
|
|
173
180
|
}
|
|
@@ -209,6 +216,7 @@ function vtkOpenGLImageMapper(publicAPI, model) {
|
|
|
209
216
|
int segmentIndex = int(centerValue * 255.0);
|
|
210
217
|
float textureCoordinate = float(segmentIndex - 1) / 1024.0;
|
|
211
218
|
float textureValue = texture2D(labelOutlineTexture1, vec2(textureCoordinate, 0.5)).r;
|
|
219
|
+
float outlineOpacity = texture2D(labelOutlineOpacityTexture1, vec2(textureCoordinate, 0.5)).r;
|
|
212
220
|
int actualThickness = int(textureValue * 255.0);
|
|
213
221
|
|
|
214
222
|
if (segmentIndex == 0){
|
|
@@ -301,7 +309,7 @@ function vtkOpenGLImageMapper(publicAPI, model) {
|
|
|
301
309
|
if (!model.currentRenderPass && model.lastRenderPassShaderReplacement || model.currentRenderPass && model.currentRenderPass.getShaderReplacement() !== model.lastRenderPassShaderReplacement) {
|
|
302
310
|
needRebuild = true;
|
|
303
311
|
}
|
|
304
|
-
if (needRebuild || model.lastHaveSeenDepthRequest !== model.haveSeenDepthRequest || cellBO.getProgram()?.getHandle() === 0 || model.lastTextureComponents !== tNumComp || model.lastIndependentComponents !== iComp) {
|
|
312
|
+
if (needRebuild || model.lastHaveSeenDepthRequest !== model.haveSeenDepthRequest || cellBO.getProgram()?.getHandle() === 0 || cellBO.getShaderSourceTime().getMTime() < model.renderable.getMTime() || cellBO.getShaderSourceTime().getMTime() < model.currentInput.getMTime() || cellBO.getShaderSourceTime().getMTime() < actor.getProperty().getMTime() || model.lastTextureComponents !== tNumComp || model.lastIndependentComponents !== iComp) {
|
|
305
313
|
model.lastHaveSeenDepthRequest = model.haveSeenDepthRequest;
|
|
306
314
|
model.lastTextureComponents = tNumComp;
|
|
307
315
|
model.lastIndependentComponents = iComp;
|
|
@@ -417,8 +425,12 @@ function vtkOpenGLImageMapper(publicAPI, model) {
|
|
|
417
425
|
cellBO.getProgram().setUniformi('colorTexture1', texColorUnit);
|
|
418
426
|
const texOpacityUnit = model.pwfTexture.getTextureUnit();
|
|
419
427
|
cellBO.getProgram().setUniformi('pwfTexture1', texOpacityUnit);
|
|
420
|
-
|
|
421
|
-
|
|
428
|
+
if (actor.getProperty().getUseLabelOutline()) {
|
|
429
|
+
const outlineThicknessUnit = model.labelOutlineThicknessTexture.getTextureUnit();
|
|
430
|
+
cellBO.getProgram().setUniformi('labelOutlineTexture1', outlineThicknessUnit);
|
|
431
|
+
const texOutlineOpacityUnit = model.labelOutlineOpacityTexture.getTextureUnit();
|
|
432
|
+
cellBO.getProgram().setUniformi('labelOutlineOpacityTexture1', texOutlineOpacityUnit);
|
|
433
|
+
}
|
|
422
434
|
if (model.renderable.getNumberOfClippingPlanes()) {
|
|
423
435
|
// add all the clipping planes
|
|
424
436
|
let numClipPlanes = model.renderable.getNumberOfClippingPlanes();
|
|
@@ -449,13 +461,6 @@ function vtkOpenGLImageMapper(publicAPI, model) {
|
|
|
449
461
|
cellBO.getProgram().setUniformi('numClipPlanes', numClipPlanes);
|
|
450
462
|
cellBO.getProgram().setUniform4fv('clipPlanes', planeEquations);
|
|
451
463
|
}
|
|
452
|
-
|
|
453
|
-
// outline thickness and opacity
|
|
454
|
-
const vtkImageLabelOutline = actor.getProperty().getUseLabelOutline();
|
|
455
|
-
if (vtkImageLabelOutline === true) {
|
|
456
|
-
const outlineOpacity = actor.getProperty().getLabelOutlineOpacity();
|
|
457
|
-
cellBO.getProgram().setUniformf('outlineOpacity', outlineOpacity);
|
|
458
|
-
}
|
|
459
464
|
};
|
|
460
465
|
publicAPI.setCameraShaderParameters = (cellBO, ren, actor) => {
|
|
461
466
|
const program = cellBO.getProgram();
|
|
@@ -516,7 +521,10 @@ function vtkOpenGLImageMapper(publicAPI, model) {
|
|
|
516
521
|
// activate the texture
|
|
517
522
|
model.openGLTexture.activate();
|
|
518
523
|
model.colorTexture.activate();
|
|
519
|
-
|
|
524
|
+
if (actor.getProperty().getUseLabelOutline()) {
|
|
525
|
+
model.labelOutlineThicknessTexture.activate();
|
|
526
|
+
model.labelOutlineOpacityTexture.activate();
|
|
527
|
+
}
|
|
520
528
|
model.pwfTexture.activate();
|
|
521
529
|
|
|
522
530
|
// draw polygons
|
|
@@ -528,7 +536,10 @@ function vtkOpenGLImageMapper(publicAPI, model) {
|
|
|
528
536
|
}
|
|
529
537
|
model.openGLTexture.deactivate();
|
|
530
538
|
model.colorTexture.deactivate();
|
|
531
|
-
|
|
539
|
+
if (actor.getProperty().getUseLabelOutline()) {
|
|
540
|
+
model.labelOutlineThicknessTexture.deactivate();
|
|
541
|
+
model.labelOutlineOpacityTexture.deactivate();
|
|
542
|
+
}
|
|
532
543
|
model.pwfTexture.deactivate();
|
|
533
544
|
};
|
|
534
545
|
publicAPI.renderPieceFinish = (ren, actor) => {};
|
|
@@ -560,7 +571,7 @@ function vtkOpenGLImageMapper(publicAPI, model) {
|
|
|
560
571
|
publicAPI.buildBufferObjects(ren, actor);
|
|
561
572
|
}
|
|
562
573
|
};
|
|
563
|
-
publicAPI.getNeedToRebuildBufferObjects = (ren, actor) => model.VBOBuildTime.getMTime() < publicAPI.getMTime() || model.VBOBuildTime.getMTime() < actor.getMTime() || model.VBOBuildTime.getMTime() < model.renderable.getMTime() || model.VBOBuildTime.getMTime() < actor.getProperty().getMTime() || model.VBOBuildTime.getMTime() < model.currentInput.getMTime() || !model.openGLTexture?.getHandle() || !model.colorTexture?.getHandle() || !model.labelOutlineThicknessTexture?.getHandle() || !model.pwfTexture?.getHandle();
|
|
574
|
+
publicAPI.getNeedToRebuildBufferObjects = (ren, actor) => model.VBOBuildTime.getMTime() < publicAPI.getMTime() || model.VBOBuildTime.getMTime() < actor.getMTime() || model.VBOBuildTime.getMTime() < model.renderable.getMTime() || model.VBOBuildTime.getMTime() < actor.getProperty().getMTime() || model.VBOBuildTime.getMTime() < model.currentInput.getMTime() || !model.openGLTexture?.getHandle() || !model.colorTexture?.getHandle() || actor.getProperty().getUseLabelOutline() && (!model.labelOutlineThicknessTexture?.getHandle() || !model.labelOutlineOpacityTexture?.getHandle()) || !model.pwfTexture?.getHandle();
|
|
564
575
|
publicAPI.buildBufferObjects = (ren, actor) => {
|
|
565
576
|
const image = model.currentInput;
|
|
566
577
|
if (!image) {
|
|
@@ -740,9 +751,11 @@ function vtkOpenGLImageMapper(publicAPI, model) {
|
|
|
740
751
|
} else {
|
|
741
752
|
model.pwfTexture = pwfTex.oglObject;
|
|
742
753
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
754
|
+
if (actor.getProperty().getUseLabelOutline()) {
|
|
755
|
+
// Build outline thickness + opacity buffers
|
|
756
|
+
publicAPI.updatelabelOutlineThicknessTexture(actor);
|
|
757
|
+
publicAPI.updateLabelOutlineOpacityTexture(actor);
|
|
758
|
+
}
|
|
746
759
|
|
|
747
760
|
// Find what IJK axis and what direction to slice along
|
|
748
761
|
const {
|
|
@@ -955,6 +968,63 @@ function vtkOpenGLImageMapper(publicAPI, model) {
|
|
|
955
968
|
model.VBOBuildString = toString;
|
|
956
969
|
}
|
|
957
970
|
};
|
|
971
|
+
publicAPI.updateLabelOutlineOpacityTexture = image => {
|
|
972
|
+
let labelOutlineOpacity = image.getProperty().getLabelOutlineOpacity();
|
|
973
|
+
|
|
974
|
+
// when the labelOutlineOpacity is a number, we use _cachedLabelOutlineOpacityObj
|
|
975
|
+
// as a stable object reference for `[labelOutlineOpacity]`.
|
|
976
|
+
if (typeof labelOutlineOpacity === 'number') {
|
|
977
|
+
if (model._cachedLabelOutlineOpacityObj?.[0] === labelOutlineOpacity) {
|
|
978
|
+
labelOutlineOpacity = model._cachedLabelOutlineOpacityObj;
|
|
979
|
+
} else {
|
|
980
|
+
labelOutlineOpacity = [labelOutlineOpacity];
|
|
981
|
+
}
|
|
982
|
+
model._cachedLabelOutlineOpacityObj = labelOutlineOpacity;
|
|
983
|
+
}
|
|
984
|
+
const lTex = model._openGLRenderWindow.getGraphicsResourceForObject(labelOutlineOpacity);
|
|
985
|
+
const toString = `${labelOutlineOpacity.join('-')}`;
|
|
986
|
+
const reBuildL = !lTex?.oglObject?.getHandle() || lTex?.hash !== toString;
|
|
987
|
+
if (reBuildL) {
|
|
988
|
+
let lWidth = model.renderable.getLabelOutlineTextureWidth();
|
|
989
|
+
if (lWidth <= 0) {
|
|
990
|
+
lWidth = model.context.getParameter(model.context.MAX_TEXTURE_SIZE);
|
|
991
|
+
}
|
|
992
|
+
const lHeight = 1;
|
|
993
|
+
const lSize = lWidth * lHeight;
|
|
994
|
+
const lTable = new Float32Array(lSize);
|
|
995
|
+
for (let i = 0; i < lWidth; ++i) {
|
|
996
|
+
// Retrieve the opacity value for the current segment index.
|
|
997
|
+
// If the value is undefined, use the first element's value as a default, otherwise use the value (even if 0)
|
|
998
|
+
lTable[i] = labelOutlineOpacity[i] ?? labelOutlineOpacity[0];
|
|
999
|
+
}
|
|
1000
|
+
model.labelOutlineOpacityTexture = vtkOpenGLTexture.newInstance({
|
|
1001
|
+
resizable: false
|
|
1002
|
+
});
|
|
1003
|
+
model.labelOutlineOpacityTexture.setOpenGLRenderWindow(model._openGLRenderWindow);
|
|
1004
|
+
model.labelOutlineOpacityTexture.resetFormatAndType();
|
|
1005
|
+
model.labelOutlineOpacityTexture.setMinificationFilter(Filter.NEAREST);
|
|
1006
|
+
model.labelOutlineOpacityTexture.setMagnificationFilter(Filter.NEAREST);
|
|
1007
|
+
|
|
1008
|
+
// Create a 2D texture (acting as 1D) from the raw data
|
|
1009
|
+
model.labelOutlineOpacityTexture.create2DFromRaw({
|
|
1010
|
+
width: lWidth,
|
|
1011
|
+
height: lHeight,
|
|
1012
|
+
numComps: 1,
|
|
1013
|
+
dataType: VtkDataTypes.FLOAT,
|
|
1014
|
+
data: lTable
|
|
1015
|
+
});
|
|
1016
|
+
if (labelOutlineOpacity) {
|
|
1017
|
+
model._openGLRenderWindow.setGraphicsResourceForObject(labelOutlineOpacity, model.labelOutlineOpacityTexture, toString);
|
|
1018
|
+
if (labelOutlineOpacity !== model._labelOutlineOpacity) {
|
|
1019
|
+
model._openGLRenderWindow.registerGraphicsResourceUser(labelOutlineOpacity, publicAPI);
|
|
1020
|
+
model._openGLRenderWindow.unregisterGraphicsResourceUser(model._labelOutlineOpacity, publicAPI);
|
|
1021
|
+
}
|
|
1022
|
+
model._labelOutlineOpacity = labelOutlineOpacity;
|
|
1023
|
+
}
|
|
1024
|
+
} else {
|
|
1025
|
+
model.labelOutlineOpacityTexture = lTex.oglObject;
|
|
1026
|
+
}
|
|
1027
|
+
};
|
|
958
1028
|
publicAPI.updatelabelOutlineThicknessTexture = image => {
|
|
959
1029
|
const labelOutlineThicknessArray = image.getProperty().getLabelOutlineThicknessByReference();
|
|
960
1030
|
const lTex = model._openGLRenderWindow.getGraphicsResourceForObject(labelOutlineThicknessArray);
|
|
@@ -1046,7 +1116,7 @@ const DEFAULT_VALUES = {
|
|
|
1046
1116
|
colorTexture: null,
|
|
1047
1117
|
pwfTexture: null,
|
|
1048
1118
|
labelOutlineThicknessTexture: null,
|
|
1049
|
-
|
|
1119
|
+
labelOutlineOpacityTexture: null,
|
|
1050
1120
|
lastHaveSeenDepthRequest: false,
|
|
1051
1121
|
haveSeenDepthRequest: false,
|
|
1052
1122
|
lastTextureComponents: 0
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { mat4 } from 'gl-matrix';
|
|
2
2
|
import { n as newInstance$1, e as setGet, o as obj, c as macro } from '../../macros2.js';
|
|
3
3
|
import vtkHelper from './Helper.js';
|
|
4
|
-
import vtkMapper2D from '../Core/Mapper2D.js';
|
|
5
4
|
import vtkPoints from '../../Common/Core/Points.js';
|
|
6
5
|
import { v as vtkPolyData2DFS } from './glsl/vtkPolyData2DFS.glsl.js';
|
|
7
6
|
import { v as vtkPolyData2DVS } from './glsl/vtkPolyData2DVS.glsl.js';
|
|
8
7
|
import vtkReplacementShaderMapper from './ReplacementShaderMapper.js';
|
|
9
8
|
import vtkShaderProgram from './ShaderProgram.js';
|
|
10
9
|
import vtkViewNode from '../SceneGraph/ViewNode.js';
|
|
10
|
+
import vtkOpenGLTexture from './Texture.js';
|
|
11
11
|
import { P as round } from '../../Common/Core/Math/index.js';
|
|
12
12
|
import { DisplayLocation } from '../Core/Property2D/Constants.js';
|
|
13
13
|
import { registerOverride } from './ViewNodeFactory.js';
|
|
@@ -17,8 +17,9 @@ const {
|
|
|
17
17
|
primTypes
|
|
18
18
|
} = vtkHelper;
|
|
19
19
|
const {
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
Filter,
|
|
21
|
+
Wrap
|
|
22
|
+
} = vtkOpenGLTexture;
|
|
22
23
|
const {
|
|
23
24
|
vtkErrorMacro
|
|
24
25
|
} = macro;
|
|
@@ -82,6 +83,11 @@ function vtkOpenGLPolyDataMapper2D(publicAPI, model) {
|
|
|
82
83
|
if (!model.currentInput.getPoints || !model.currentInput.getPoints().getNumberOfValues()) {
|
|
83
84
|
return;
|
|
84
85
|
}
|
|
86
|
+
|
|
87
|
+
// cull back face to avoid double drawing
|
|
88
|
+
const gl = model.context;
|
|
89
|
+
model._openGLRenderWindow.enableCullFace();
|
|
90
|
+
gl.cullFace(gl.BACK);
|
|
85
91
|
publicAPI.renderPieceStart(ren, actor);
|
|
86
92
|
publicAPI.renderPieceDraw(ren, actor);
|
|
87
93
|
publicAPI.renderPieceFinish(ren, actor);
|
|
@@ -94,6 +100,13 @@ function vtkOpenGLPolyDataMapper2D(publicAPI, model) {
|
|
|
94
100
|
model._openGLRenderer.getSelector().renderProp(actor);
|
|
95
101
|
}
|
|
96
102
|
}
|
|
103
|
+
|
|
104
|
+
// If we are coloring by texture, then load the texture map.
|
|
105
|
+
// Use Map as indicator, because texture hangs around.
|
|
106
|
+
if (model.renderable.getColorTextureMap()) {
|
|
107
|
+
model.internalColorTexture.activate();
|
|
108
|
+
}
|
|
109
|
+
|
|
97
110
|
// make sure the BOs are up to date
|
|
98
111
|
publicAPI.updateBufferObjects(ren, actor);
|
|
99
112
|
|
|
@@ -133,19 +146,44 @@ function vtkOpenGLPolyDataMapper2D(publicAPI, model) {
|
|
|
133
146
|
}
|
|
134
147
|
model.renderable.mapScalars(poly, actor.getProperty().getOpacity());
|
|
135
148
|
const c = model.renderable.getColorMapColors();
|
|
136
|
-
model.haveCellScalars = false;
|
|
137
|
-
const scalarMode = model.renderable.getScalarMode();
|
|
138
|
-
if (model.renderable.getScalarVisibility()) {
|
|
139
|
-
// We must figure out how the scalars should be mapped to the polydata.
|
|
140
|
-
if ((scalarMode === ScalarMode.USE_CELL_DATA || scalarMode === ScalarMode.USE_CELL_FIELD_DATA || scalarMode === ScalarMode.USE_FIELD_DATA || !poly.getPointData().getScalars()) && scalarMode !== ScalarMode.USE_POINT_FIELD_DATA && c) {
|
|
141
|
-
model.haveCellScalars = true;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
149
|
const representation = actor.getProperty().getRepresentation();
|
|
145
150
|
let tcoords = poly.getPointData().getTCoords();
|
|
146
151
|
if (!model.openGLActor2D.getActiveTextures()) {
|
|
147
152
|
tcoords = null;
|
|
148
153
|
}
|
|
154
|
+
|
|
155
|
+
// Flag to check if tcoords are per cell instead of per point
|
|
156
|
+
let useTCoordsPerCell = false;
|
|
157
|
+
// handle color mapping via texture
|
|
158
|
+
if (model.renderable.getColorCoordinates()) {
|
|
159
|
+
tcoords = model.renderable.getColorCoordinates();
|
|
160
|
+
useTCoordsPerCell = model.renderable.getAreScalarsMappedFromCells();
|
|
161
|
+
if (!model.internalColorTexture) {
|
|
162
|
+
model.internalColorTexture = vtkOpenGLTexture.newInstance({
|
|
163
|
+
resizable: true
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
const tex = model.internalColorTexture;
|
|
167
|
+
// the following 4 lines allow for NPOT textures
|
|
168
|
+
tex.setMinificationFilter(Filter.NEAREST);
|
|
169
|
+
tex.setMagnificationFilter(Filter.NEAREST);
|
|
170
|
+
tex.setWrapS(Wrap.CLAMP_TO_EDGE);
|
|
171
|
+
tex.setWrapT(Wrap.CLAMP_TO_EDGE);
|
|
172
|
+
tex.setOpenGLRenderWindow(model._openGLRenderWindow);
|
|
173
|
+
const input = model.renderable.getColorTextureMap();
|
|
174
|
+
const ext = input.getExtent();
|
|
175
|
+
const inScalars = input.getPointData().getScalars();
|
|
176
|
+
tex.create2DFromRaw({
|
|
177
|
+
width: ext[1] - ext[0] + 1,
|
|
178
|
+
height: ext[3] - ext[2] + 1,
|
|
179
|
+
numComps: inScalars.getNumberOfComponents(),
|
|
180
|
+
dataType: inScalars.getDataType(),
|
|
181
|
+
data: inScalars.getData()
|
|
182
|
+
});
|
|
183
|
+
tex.activate();
|
|
184
|
+
tex.sendParameters();
|
|
185
|
+
tex.deactivate();
|
|
186
|
+
}
|
|
149
187
|
const transformCoordinate = model.renderable.getTransformCoordinate();
|
|
150
188
|
const view = ren.getRenderWindow().getViews()[0];
|
|
151
189
|
const vsize = view.getViewportSize(ren);
|
|
@@ -171,7 +209,8 @@ function vtkOpenGLPolyDataMapper2D(publicAPI, model) {
|
|
|
171
209
|
tcoords,
|
|
172
210
|
colors: c,
|
|
173
211
|
cellOffset: 0,
|
|
174
|
-
|
|
212
|
+
useTCoordsPerCell,
|
|
213
|
+
haveCellScalars: model.renderable.getAreScalarsMappedFromCells(),
|
|
175
214
|
customAttributes: model.renderable.getCustomShaderAttributes().map(arrayName => poly.getPointData().getArrayByName(arrayName))
|
|
176
215
|
};
|
|
177
216
|
options.cellOffset += model.primitives[primTypes.Points].getCABO().createVBO(poly.getVerts(), 'verts', representation, options);
|
|
@@ -201,6 +240,9 @@ function vtkOpenGLPolyDataMapper2D(publicAPI, model) {
|
|
|
201
240
|
if (model.lastBoundBO) {
|
|
202
241
|
model.lastBoundBO.getVAO().release();
|
|
203
242
|
}
|
|
243
|
+
if (model.renderable.getColorTextureMap()) {
|
|
244
|
+
model.internalColorTexture.deactivate();
|
|
245
|
+
}
|
|
204
246
|
};
|
|
205
247
|
publicAPI.replaceShaderValues = (shaders, ren, actor) => {
|
|
206
248
|
publicAPI.replaceShaderColor(shaders, ren, actor);
|
|
@@ -212,21 +254,28 @@ function vtkOpenGLPolyDataMapper2D(publicAPI, model) {
|
|
|
212
254
|
let VSSource = shaders.Vertex;
|
|
213
255
|
let GSSource = shaders.Geometry;
|
|
214
256
|
let FSSource = shaders.Fragment;
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
257
|
+
|
|
258
|
+
// create the color property declarations
|
|
259
|
+
// these are always defined
|
|
260
|
+
let colorDec = ['uniform vec3 diffuseColorUniform;', 'uniform float opacityUniform;'];
|
|
261
|
+
|
|
262
|
+
// now handle the more complex fragment shader implementation
|
|
263
|
+
let colorImpl = ['vec3 diffuseColor = diffuseColorUniform;', 'float opacity = opacityUniform;'];
|
|
264
|
+
|
|
265
|
+
// add scalar vertex colors
|
|
219
266
|
if (model.lastBoundBO.getCABO().getColorComponents() !== 0) {
|
|
220
|
-
|
|
221
|
-
VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::Color::
|
|
222
|
-
|
|
223
|
-
GSSource = vtkShaderProgram.substitute(GSSource, '//VTK::Color::
|
|
224
|
-
|
|
225
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::Color::Impl', ['
|
|
226
|
-
} else {
|
|
227
|
-
|
|
228
|
-
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::Color::Impl', ['gl_FragData[0] = diffuseColor;']).result;
|
|
267
|
+
colorDec = colorDec.concat(['varying vec4 vertexColorVSOutput;']);
|
|
268
|
+
VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::Color::Dec', ['attribute vec4 scalarColor;', 'varying vec4 vertexColorVSOutput;']).result;
|
|
269
|
+
VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::Color::Impl', ['vertexColorVSOutput = scalarColor;']).result;
|
|
270
|
+
GSSource = vtkShaderProgram.substitute(GSSource, '//VTK::Color::Dec', ['in vec4 vertexColorVSOutput[];', 'out vec4 vertexColorGSOutput;']).result;
|
|
271
|
+
GSSource = vtkShaderProgram.substitute(GSSource, '//VTK::Color::Impl', ['vertexColorGSOutput = vertexColorVSOutput[i];']).result;
|
|
272
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::Color::Impl', colorImpl.concat([' diffuseColor = vertexColorVSOutput.rgb;', ' opacity = opacity*vertexColorVSOutput.a;'])).result;
|
|
273
|
+
} else if (model.renderable.getAreScalarsMappedFromCells()) {
|
|
274
|
+
colorImpl = colorImpl.concat([' vec4 texColor = texture2D(texture1, tcoordVCVSOutput.st);', ' diffuseColor = texColor.rgb;', ' opacity = opacity*texColor.a;']);
|
|
229
275
|
}
|
|
276
|
+
colorImpl = colorImpl.concat(['gl_FragData[0] = vec4(diffuseColor, opacity);']);
|
|
277
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::Color::Dec', colorDec).result;
|
|
278
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::Color::Impl', colorImpl).result;
|
|
230
279
|
shaders.Vertex = VSSource;
|
|
231
280
|
shaders.Geometry = GSSource;
|
|
232
281
|
shaders.Fragment = FSSource;
|
|
@@ -252,7 +301,7 @@ function vtkOpenGLPolyDataMapper2D(publicAPI, model) {
|
|
|
252
301
|
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::TCoord::Dec', ['in vec2 tcoordVCVSOutput;', 'uniform sampler2D texture1;']).result;
|
|
253
302
|
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::TCoord::Impl', ['gl_FragData[0] = gl_FragData[0]*texture2D(texture1, tcoordVCVSOutput.st);']).result;
|
|
254
303
|
}
|
|
255
|
-
if (model.
|
|
304
|
+
if (model.renderable.getAreScalarsMappedFromCells()) {
|
|
256
305
|
GSSource = vtkShaderProgram.substitute(GSSource, '//VTK::PrimID::Impl', ['gl_PrimitiveID = gl_PrimitiveIDIn;']).result;
|
|
257
306
|
}
|
|
258
307
|
shaders.Vertex = VSSource;
|
|
@@ -303,8 +352,18 @@ function vtkOpenGLPolyDataMapper2D(publicAPI, model) {
|
|
|
303
352
|
} else {
|
|
304
353
|
cellBO.getVAO().removeAttributeArray('tcoordMC');
|
|
305
354
|
}
|
|
355
|
+
if (cellBO.getProgram().isAttributeUsed('scalarColor') && cellBO.getCABO().getColorComponents()) {
|
|
356
|
+
if (!cellBO.getVAO().addAttributeArray(cellBO.getProgram(), cellBO.getCABO().getColorBO(), 'scalarColor', cellBO.getCABO().getColorOffset(), cellBO.getCABO().getColorBOStride(), model.context.UNSIGNED_BYTE, 4, true)) {
|
|
357
|
+
vtkErrorMacro('Error setting scalarColor in shader VAO.');
|
|
358
|
+
}
|
|
359
|
+
} else {
|
|
360
|
+
cellBO.getVAO().removeAttributeArray('scalarColor');
|
|
361
|
+
}
|
|
306
362
|
if (model.internalColorTexture && cellBO.getProgram().isUniformUsed('texture1')) {
|
|
307
|
-
|
|
363
|
+
const texUnit = model.internalColorTexture.getTextureUnit();
|
|
364
|
+
if (texUnit > -1) {
|
|
365
|
+
cellBO.getProgram().setUniformi('texture1', model.internalColorTexture.getTextureUnit());
|
|
366
|
+
}
|
|
308
367
|
}
|
|
309
368
|
const tus = model.openGLActor2D.getActiveTextures();
|
|
310
369
|
if (tus) {
|
|
@@ -331,9 +390,9 @@ function vtkOpenGLPolyDataMapper2D(publicAPI, model) {
|
|
|
331
390
|
const program = cellBO.getProgram();
|
|
332
391
|
const ppty = actor.getProperty();
|
|
333
392
|
const opacity = ppty.getOpacity();
|
|
393
|
+
program.setUniformf('opacityUniform', opacity);
|
|
334
394
|
const dColor = ppty.getColor();
|
|
335
|
-
|
|
336
|
-
program.setUniform4f('diffuseColor', diffuseColor);
|
|
395
|
+
program.setUniform3fArray('diffuseColorUniform', dColor);
|
|
337
396
|
}
|
|
338
397
|
};
|
|
339
398
|
publicAPI.setLightingShaderParameters = (cellBO, ren, actor) => {
|