Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 | 428x 80x 3608x 3608x 432960x 3608x 3608x 3608x 130780x 130780x 94524x 36256x 56528x 32684x 3572x 3608x 36x 3572x 3572x 3560x 3560x 3560x 7120x 3572x 3572x 3596x 3596x 3596x 3596x 3560x 36x 72x 72x 72x 72x 72x 36x 3596x 3596x 3596x 3596x 3596x 3596x 7300x 3632x 3596x | import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'; import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction'; import vtkPiecewiseFunction from '@kitware/vtk.js/Common/DataModel/PiecewiseFunction'; import type { ColormapPublic, ColormapRegistration } from '../types'; import isEqual from './isEqual'; import { actorIsA } from './actorCheck'; const _colormaps = new Map(); /** * Register a colormap * @param name - name of the colormap * @param colormap - colormap object */ function registerColormap(colormap: ColormapRegistration) { colormap.name = colormap.name || colormap.Name; _colormaps.set(colormap.name, colormap); } /** * Get a colormap by name * @param name - name of the colormap * @returns colormap object */ function getColormap(name) { return _colormaps.get(name); } /** * Get all registered colormap names * @returns array of colormap names * */ function getColormapNames() { return Array.from(_colormaps.keys()); } /** * Finds a colormap that matches the given RGB points. * * @param rgbPoints - The RGB points to match against the colormaps. * @returns The matched colormap object or null if no match is found. */ function findMatchingColormap(rgbPoints, actor): ColormapPublic | null { const colormapsVTK = vtkColorMaps.rgbPresetNames.map((presetName) => vtkColorMaps.getPresetByName(presetName) ); const colormapsCS3D = getColormapNames().map((colormapName) => getColormap(colormapName) ); const colormaps = colormapsVTK.concat(colormapsCS3D); // Find the colormap that matches the given RGB points const matchedColormap = colormaps.find((colormap) => { const { RGBPoints: presetRGBPoints } = colormap; if (presetRGBPoints.length !== rgbPoints.length) { return false; } for (let i = 0; i < presetRGBPoints.length; i += 4) { if ( !isEqual( presetRGBPoints.slice(i + 1, i + 4), rgbPoints.slice(i + 1, i + 4) ) ) { return false; } } return true; }); if (!matchedColormap) { return null; } const opacity = []; if (actorIsA(actor, 'vtkVolume')) { const opacityPoints = actor .getProperty() .getScalarOpacity(0) .getDataPointer(); Iif (!opacityPoints) { return { name: matchedColormap.Name, }; } for (let i = 0; i < opacityPoints.length; i += 2) { opacity.push({ value: opacityPoints[i], opacity: opacityPoints[i + 1], }); } } const result = { name: matchedColormap.Name, ...(Array.isArray(opacity) && opacity.length > 0 && { opacity }), ...(typeof opacity === 'number' && { opacity }), }; return result; } export function setColorMapTransferFunctionForVolumeActor(volumeInfo) { const { volumeActor, preset, opacity = 0.9, threshold = null, colorRange = [0, 5], } = volumeInfo; const mapper = volumeActor.getMapper(); mapper.setSampleDistance(1.0); // Set up color transfer function const cfun = vtkColorTransferFunction.newInstance(); const presetToUse = preset || vtkColorMaps.getPresetByName('hsv'); cfun.applyColorMap(presetToUse); cfun.setMappingRange(colorRange[0], colorRange[1]); volumeActor.getProperty().setRGBTransferFunction(0, cfun); // Set up opacity function with threshold updateOpacityWithThreshold(volumeActor, opacity, threshold); } /** * Updates only the opacity value while preserving threshold */ export function updateOpacity(volumeActor, newOpacity) { const currentThreshold = getThresholdValue(volumeActor); updateOpacityWithThreshold(volumeActor, newOpacity, currentThreshold); } /** * Updates only the threshold while preserving opacity */ export function updateThreshold(volumeActor, newThreshold) { const currentOpacity = getMaxOpacity(volumeActor); updateOpacityWithThreshold(volumeActor, currentOpacity, newThreshold); } /** * Helper function to update opacity function with threshold * @param {Object} volumeActor - The volume actor to update * @param {number} opacity - The opacity value to set (0-1) * @param {number|null} threshold - The absolute threshold value (not normalized) */ function updateOpacityWithThreshold(volumeActor, opacity, threshold) { // there is always a voxel manager for each volume actor const meta = volumeActor.getMapper().getInputData().get('voxelManager'); if (!meta?.voxelManager) { throw new Error( 'No voxel manager was found for the volume actor, or you cannot yet update opacity with a threshold using stacked images' ); } const range = meta.voxelManager.getRange(); const ofun = vtkPiecewiseFunction.newInstance(); if (threshold !== null) { // Small delta for sharp threshold transition const delta = Math.abs(range[1] - range[0]) * 0.001; // Make sure threshold is within range const thresholdValue = Math.max(range[0], Math.min(range[1], threshold)); // Create points for the piecewise function ofun.addPoint(range[0], 0); ofun.addPoint(thresholdValue - delta, 0); ofun.addPoint(thresholdValue, opacity); ofun.addPoint(range[1], opacity); } else { // Simple uniform opacity without threshold ofun.addPoint(range[0], opacity); ofun.addPoint(range[1], opacity); } volumeActor.getProperty().setScalarOpacity(0, ofun); } /** * Extract threshold value from the actor's opacity function * @returns {number|null} The absolute threshold value or null if no threshold */ function getThresholdValue(volumeActor) { const opacityFunction = volumeActor.getProperty().getScalarOpacity(0); Iif (!opacityFunction) { return null; } const dataArray = opacityFunction.getDataPointer(); if (!dataArray || dataArray.length <= 4) { return null; // No threshold if simple opacity function } // Find transition from 0 to non-zero opacity for (let i = 0; i < dataArray.length - 2; i += 2) { const x1 = dataArray[i]; const y1 = dataArray[i + 1]; const x2 = dataArray[i + 2]; const y2 = dataArray[i + 3]; if (y1 === 0 && y2 > 0) { // Found threshold point - return the actual value return x2; } } return null; // No threshold found } /** * Extract maximum opacity value from actor's opacity function */ function getMaxOpacity(volumeActor) { const opacityFunction = volumeActor.getProperty().getScalarOpacity(0); Iif (!opacityFunction) { return 1.0; } const dataArray = opacityFunction.getDataPointer(); Iif (!dataArray || dataArray.length === 0) { return 1.0; } // Find maximum opacity value let maxOpacity = 0; for (let i = 1; i < dataArray.length; i += 2) { if (dataArray[i] > maxOpacity) { maxOpacity = dataArray[i]; } } return maxOpacity; } export { getColormap, getColormapNames, registerColormap, findMatchingColormap, getThresholdValue, getMaxOpacity, }; |