Skip to content

Commit d27f4e4

Browse files
Antti-Palolafloryst
authored andcommitted
feat(colortransferfunction, mapper): logarithmic scales support for ColorTransferFunction
Allows log scaling for ScalarBar as well as as Mappers using ColorTransferFunction. BREAKING CHANGE: Previous log scaling was disabled but functionality is now quite different from what it maybe was before it was disabled. ColorTransfer function now maps the model.nodes in logarithmic scales so there's an even spacing between powers of ten.
1 parent fb857d5 commit d27f4e4

File tree

6 files changed

+194
-118
lines changed

6 files changed

+194
-118
lines changed

Sources/Common/Core/ScalarsToColors/Constants.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@ export const ScalarMappingTarget = {
1111
RGBA: 4,
1212
};
1313

14+
export const Scale = {
15+
LINEAR: 0,
16+
LOG10: 1,
17+
};
18+
1419
export default {
1520
VectorMode,
1621
ScalarMappingTarget,
22+
Scale,
1723
};

Sources/Common/Core/ScalarsToColors/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray';
33
import Constants from 'vtk.js/Sources/Common/Core/ScalarsToColors/Constants';
44
import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper/Constants'; // Need to go inside Constants otherwise dependency loop
55

6-
const { ScalarMappingTarget, VectorMode } = Constants;
6+
const { ScalarMappingTarget, Scale, VectorMode } = Constants;
77
const { VtkDataTypes } = vtkDataArray;
88
const { ColorMode } = vtkMapper;
99
const { vtkErrorMacro } = macro;
@@ -553,6 +553,7 @@ const DEFAULT_VALUES = {
553553
annotationArray: null,
554554
annotatedValueMap: null,
555555
indexedLookup: false,
556+
scale: Scale.LINEAR,
556557
};
557558

558559
// ----------------------------------------------------------------------------

Sources/Rendering/Core/ColorTransferFunction/index.js

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -430,12 +430,22 @@ function vtkColorTransferFunction(publicAPI, model) {
430430
return rgb[2];
431431
};
432432

433+
publicAPI.logScaleEnabled = () => model.scale === Scale.LOG10;
434+
435+
publicAPI.usingLogScale = () =>
436+
publicAPI.logScaleEnabled() && model.mappingRange[0] > 0.0;
437+
433438
//----------------------------------------------------------------------------
434439
// Returns a table of RGB colors at regular intervals along the function
435440
publicAPI.getTable = (xStart_, xEnd_, size, table) => {
441+
// Note: This requires range[0] <= range[1].
442+
const usingLogScale = publicAPI.usingLogScale();
443+
436444
// To handle BigInt limitation
437-
const xStart = Number(xStart_);
438-
const xEnd = Number(xEnd_);
445+
const xStart = usingLogScale
446+
? Math.log10(Number(xStart_))
447+
: Number(xStart_);
448+
const xEnd = usingLogScale ? Math.log10(Number(xEnd_)) : Number(xEnd_);
439449

440450
// Special case: If either the start or end is a NaN, then all any
441451
// interpolation done on them is also a NaN. Therefore, fill the table with
@@ -475,18 +485,12 @@ function vtkColorTransferFunction(publicAPI, model) {
475485
const tmpVec = [];
476486

477487
// If the scale is logarithmic, make sure the range is valid.
478-
let usingLogScale = model.scale === Scale.LOG10;
488+
let scaledMappingRange = model.mappingRange;
479489
if (usingLogScale) {
480-
// Note: This requires range[0] <= range[1].
481-
usingLogScale = model.mappingRange[0] > 0.0;
482-
}
483-
484-
let logStart = 0.0;
485-
let logEnd = 0.0;
486-
let logX = 0.0;
487-
if (usingLogScale) {
488-
logStart = Math.log10(xStart);
489-
logEnd = Math.log10(xEnd);
490+
scaledMappingRange = [
491+
Math.log10(model.mappingRange[0]),
492+
Math.log10(model.mappingRange[1]),
493+
];
490494
}
491495

492496
// For each table entry
@@ -498,15 +502,7 @@ function vtkColorTransferFunction(publicAPI, model) {
498502
// it halfway between start and end (usually start and end will
499503
// be the same in this case)
500504
if (size > 1) {
501-
if (usingLogScale) {
502-
logX = logStart + (i / (size - 1.0)) * (logEnd - logStart);
503-
x = 10.0 ** logX;
504-
} else {
505-
x = xStart + (i / (size - 1.0)) * (xEnd - xStart);
506-
}
507-
} else if (usingLogScale) {
508-
logX = 0.5 * (logStart + logEnd);
509-
x = 10.0 ** logX;
505+
x = xStart + (i / (size - 1.0)) * (xEnd - xStart);
510506
} else {
511507
x = 0.5 * (xStart + xEnd);
512508
}
@@ -515,7 +511,7 @@ function vtkColorTransferFunction(publicAPI, model) {
515511
// discretize (round down to the closest integer),
516512
// then map back to mappingRange
517513
if (model.discretize) {
518-
const range = model.mappingRange;
514+
const range = scaledMappingRange;
519515
if (x >= range[0] && x <= range[1]) {
520516
const numberOfValues = model.numberOfValues;
521517
const deltaRange = range[1] - range[0];
@@ -543,10 +539,6 @@ function vtkColorTransferFunction(publicAPI, model) {
543539
if (idx < numNodes) {
544540
x1 = model.nodes[idx - 1].x;
545541
x2 = model.nodes[idx].x;
546-
if (usingLogScale) {
547-
x1 = Math.log10(x1);
548-
x2 = Math.log10(x2);
549-
}
550542

551543
rgb1[0] = model.nodes[idx - 1].r;
552544
rgb2[0] = model.nodes[idx].r;
@@ -575,7 +567,7 @@ function vtkColorTransferFunction(publicAPI, model) {
575567
}
576568

577569
// Are we at or past the end? If so, just use the last value
578-
if (x > model.mappingRange[1]) {
570+
if (x > scaledMappingRange[1]) {
579571
table[tidx] = 0.0;
580572
table[tidx + 1] = 0.0;
581573
table[tidx + 2] = 0.0;
@@ -590,7 +582,7 @@ function vtkColorTransferFunction(publicAPI, model) {
590582
table[tidx + 2] = lastB;
591583
}
592584
}
593-
} else if (x < model.mappingRange[0] || (vtkMath.isInf(x) && x < 0)) {
585+
} else if (x < scaledMappingRange[0] || (vtkMath.isInf(x) && x < 0)) {
594586
// we are before the first node? If so, duplicate this node's values.
595587
// We have to deal with -inf here
596588
table[tidx] = 0.0;
@@ -627,11 +619,7 @@ function vtkColorTransferFunction(publicAPI, model) {
627619
// sharpness to get the curve shape we want and to have
628620
// it pass through (y1+y2)/2 at the midpoint.
629621
let s = 0.0;
630-
if (usingLogScale) {
631-
s = (logX - x1) / (x2 - x1);
632-
} else {
633-
s = (x - x1) / (x2 - x1);
634-
}
622+
s = (x - x1) / (x2 - x1);
635623

636624
// Readjust based on the midpoint - linear adjustment
637625
if (s < midpoint) {
@@ -1064,7 +1052,10 @@ function vtkColorTransferFunction(publicAPI, model) {
10641052
//----------------------------------------------------------------------------
10651053
publicAPI.setMappingRange = (min, max) => {
10661054
const range = [min, max];
1055+
const scaledRange = [min, max];
10671056
const originalRange = publicAPI.getRange();
1057+
const logScaleEnabled = publicAPI.logScaleEnabled();
1058+
10681059
if (originalRange[1] === range[1] && originalRange[0] === range[0]) {
10691060
return;
10701061
}
@@ -1074,8 +1065,20 @@ function vtkColorTransferFunction(publicAPI, model) {
10741065
return;
10751066
}
10761067

1077-
const scale = (range[1] - range[0]) / (originalRange[1] - originalRange[0]);
1078-
const shift = range[0] - originalRange[0] * scale;
1068+
if (logScaleEnabled) {
1069+
if (range[0] <= 0.0) {
1070+
console.warn(
1071+
'attempt to set log scale color range with non-positive minimum'
1072+
);
1073+
} else {
1074+
scaledRange[0] = Math.log10(range[0]);
1075+
scaledRange[1] = Math.log10(range[1]);
1076+
}
1077+
}
1078+
1079+
const scale =
1080+
(scaledRange[1] - scaledRange[0]) / (originalRange[1] - originalRange[0]);
1081+
const shift = scaledRange[0] - originalRange[0] * scale;
10791082

10801083
for (let i = 0; i < model.nodes.length; ++i) {
10811084
model.nodes[i].x = model.nodes[i].x * scale + shift;

Sources/Rendering/Core/Mapper/index.js

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ const colorTextureCoordinatesCache = new WeakMap();
138138
* @param {Range} range The range of the scalars
139139
* @param {Number} numberOfColorsInRange The number of colors that are used in the range
140140
* @param {vec3} dimensions The dimensions of the texture
141-
* @param {boolean} useLogScale If log scale should be used to transform input scalars
142141
* @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.
143142
* @returns A vtkDataArray containing the texture coordinates (2D or 3D)
144143
*/
@@ -148,7 +147,6 @@ function getOrCreateColorTextureCoordinates(
148147
range,
149148
numberOfColorsInRange,
150149
dimensions,
151-
useLogScale,
152150
useZigzagPattern
153151
) {
154152
// Caching using the "arguments" special object (because it is a pure function)
@@ -223,11 +221,6 @@ function getOrCreateColorTextureCoordinates(
223221
}
224222
inputIdx += numComps;
225223

226-
// Apply log scale if necessary
227-
if (useLogScale) {
228-
scalarValue = vtkLookupTable.applyLogScale(scalarValue, range, range);
229-
}
230-
231224
// Convert to texture coordinates and update output
232225
if (vtkMath.isNan(scalarValue)) {
233226
// Last texels are NaN colors (there is at least one NaN color)
@@ -452,11 +445,6 @@ function vtkMapper(publicAPI, model) {
452445
model.mapScalarsToTexture = (scalars, cellFlag, alpha) => {
453446
const range = model.lookupTable.getRange();
454447
const useLogScale = model.lookupTable.usingLogScale();
455-
if (useLogScale) {
456-
// convert range to log.
457-
vtkLookupTable.getLogRange(range, range);
458-
}
459-
460448
const origAlpha = model.lookupTable.getAlpha();
461449

462450
// Get rid of vertex color array. Only texture or vertex coloring
@@ -529,21 +517,27 @@ function vtkMapper(publicAPI, model) {
529517
const numberOfNonSpecialColors = model.numberOfColorsInRange;
530518
const numberOfNonNaNColors = numberOfNonSpecialColors + 2;
531519
const textureCoordinates = [0, 0, 0];
532-
const rangeMin = range[0];
533-
const rangeDifference = range[1] - range[0];
520+
521+
const scaledRange = useLogScale
522+
? [Math.log10(range[0]), Math.log10(range[1])]
523+
: range;
524+
const rangeMin = scaledRange[0];
525+
const rangeDifference = scaledRange[1] - scaledRange[0];
534526
for (let i = 0; i < numberOfNonNaNColors; ++i) {
535527
const scalarsArrayIndex = getIndexFromCoordinates(
536528
textureCoordinates,
537529
textureDimensions
538530
);
539531

540532
// Minus 1 start at min color
541-
const scalarValue =
533+
const intermediateValue =
542534
rangeMin +
543535
(rangeDifference * (i - 1)) / (numberOfNonSpecialColors - 1);
544-
scalarsArray[scalarsArrayIndex] = useLogScale
545-
? 10.0 ** scalarValue
546-
: scalarValue;
536+
const scalarValue = useLogScale
537+
? 10.0 ** intermediateValue
538+
: intermediateValue;
539+
540+
scalarsArray[scalarsArrayIndex] = scalarValue;
547541

548542
// Colors are zigzagging to allow interpolation between two neighbor colors when coloring cells
549543
updateZigzaggingCoordinates(textureCoordinates, textureDimensions);
@@ -585,7 +579,6 @@ function vtkMapper(publicAPI, model) {
585579
range,
586580
model.numberOfColorsInRange,
587581
model.colorTextureMap.getDimensions(),
588-
useLogScale,
589582
cellFlag
590583
);
591584
};

Sources/Rendering/Core/ScalarBarActor/example/controlPanel.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
<tr>
33
<td>Min:</td>
44
<td>
5-
<input type="number" id="min" value="0"></input>
5+
<input type="number" id="min" value="0.1"></input>
66
</td>
77
</tr>
88
<tr>
99
<td>Max:</td>
1010
<td>
11-
<input type="number" id="max" value="0.25">
11+
<input type="number" id="max" value="1000">
1212
</td>
1313
</tr>
1414
<tr>

0 commit comments

Comments
 (0)