Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(colormap): Resolve bugs related to colormap reset and colorbar #1225

Merged
merged 16 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions common/reviews/api/core.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type ActorEntry = {
};

// @public (undocumented)
function actorIsA(actorEntry: Types.ActorEntry, actorType: actorTypes): boolean;
function actorIsA(actorEntry: Types.ActorEntry | Types.Actor, actorType: actorTypes): boolean;

// @public (undocumented)
type ActorSliceRange = {
Expand Down Expand Up @@ -146,6 +146,8 @@ export abstract class BaseVolumeViewport extends Viewport implements IVolumeView
// (undocumented)
getViewReference(viewRefSpecifier?: ViewReferenceSpecifier): ViewReference;
// (undocumented)
protected getVOIModifiedEventDetail(volumeId: string): VoiModifiedEventDetail;
// (undocumented)
protected getVolumeId(specifier: ViewReferenceSpecifier): string;
// (undocumented)
hasImageURI: (imageURI: string) => boolean;
Expand Down Expand Up @@ -287,7 +289,8 @@ declare namespace colormap {
export {
getColormap,
getColormapNames,
registerColormap
registerColormap,
findMatchingColormap
}
}

Expand All @@ -311,7 +314,7 @@ type ColormapPublic = {
type ColormapRegistration = {
ColorSpace: string;
Name: string;
RGBPoints: RGB[];
RGBPoints: RGB[] | number[];
};

// @public (undocumented)
Expand Down Expand Up @@ -929,6 +932,9 @@ declare namespace EventTypes {
}
}

// @public (undocumented)
function findMatchingColormap(rgbPoints: any, actor: any): ColormapPublic | null;

// @public (undocumented)
type FlipDirection = {
flipHorizontal?: boolean;
Expand Down Expand Up @@ -4037,6 +4043,7 @@ type VoiModifiedEventDetail = {
VOILUTFunction?: VOILUTFunctionType;
invert?: boolean;
invertStateChanged?: boolean;
colormap?: ColormapPublic;
};

// @public (undocumented)
Expand Down
137 changes: 69 additions & 68 deletions packages/core/src/RenderingEngine/BaseVolumeViewport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ import {
invertRgbTransferFunction,
triggerEvent,
colormap as colormapUtils,
isEqual,
} from '../utilities';
import { createVolumeActor } from './helpers';
import volumeNewImageEventDispatcher, {
Expand All @@ -61,8 +60,8 @@ import Viewport from './Viewport';
import type { vtkSlabCamera as vtkSlabCameraType } from './vtkClasses/vtkSlabCamera';
import vtkSlabCamera from './vtkClasses/vtkSlabCamera';
import transformWorldToIndex from '../utilities/transformWorldToIndex';
import { findMatchingColormap } from '../utilities/colormap';
import { getTransferFunctionNodes } from '../utilities/transferFunctionUtils';
import { getColormap, getColormapNames } from '../utilities/colormap';
/**
* Abstract base class for volume viewports. VolumeViewports are used to render
* 3D volumes from which various orientations can be viewed. Since VolumeViewports
Expand Down Expand Up @@ -357,24 +356,51 @@ abstract class BaseVolumeViewport extends Viewport implements IVolumeViewport {
const cfun = this._getOrCreateColorTransferFunction(volumeIdToUse);
invertRgbTransferFunction(cfun);

const { voiRange, VOILUTFunction } = this.getProperties(volumeIdToUse);

this.viewportProperties.invert = inverted;

if (!suppressEvents) {
const eventDetail: VoiModifiedEventDetail = {
viewportId: this.id,
range: voiRange,
volumeId: volumeIdToUse,
VOILUTFunction: VOILUTFunction,
invert: inverted,
...this.getVOIModifiedEventDetail(volumeIdToUse),
invertStateChanged: true,
};

triggerEvent(this.element, Events.VOI_MODIFIED, eventDetail);
}
}

protected getVOIModifiedEventDetail(
volumeId: string
): VoiModifiedEventDetail {
const applicableVolumeActorInfo = this._getApplicableVolumeActor(volumeId);

if (!applicableVolumeActorInfo) {
throw new Error(`No actor found for the given volumeId: ${volumeId}`);
}

const volumeActor = applicableVolumeActorInfo.volumeActor;

const transferFunction = volumeActor
.getProperty()
.getRGBTransferFunction(0);

const range = transferFunction.getMappingRange();

const matchedColormap = this.getColormap(volumeId);
const { VOILUTFunction, invert } = this.getProperties(volumeId);

return {
viewportId: this.id,
range: {
lower: range[0],
upper: range[1],
},
volumeId: applicableVolumeActorInfo.volumeId,
VOILUTFunction: VOILUTFunction,
colormap: matchedColormap,
invert,
};
}

private _getOrCreateColorTransferFunction(
volumeId: string
): vtkColorTransferFunction {
Expand Down Expand Up @@ -468,22 +494,11 @@ abstract class BaseVolumeViewport extends Viewport implements IVolumeViewport {
.getProperty()
.getRGBTransferFunction(0)
.setRange(lower, upper);

if (!this.initialTransferFunctionNodes) {
const transferFunction = volumeActor
.getProperty()
.getRGBTransferFunction(0);
this.initialTransferFunctionNodes =
getTransferFunctionNodes(transferFunction);
}
}

if (!suppressEvents) {
const eventDetail: VoiModifiedEventDetail = {
viewportId: this.id,
range: voiRange,
volumeId: volumeIdToUse,
VOILUTFunction: VOILUTFunction,
...this.getVOIModifiedEventDetail(volumeIdToUse),
};

triggerEvent(this.element, Events.VOI_MODIFIED, eventDetail);
Expand Down Expand Up @@ -850,7 +865,7 @@ abstract class BaseVolumeViewport extends Viewport implements IVolumeViewport {
? voiRanges.find((range) => range.volumeId === volumeId)?.voiRange
: voiRanges[0]?.voiRange;

const volumeColormap = this.getColormap(applicableVolumeActorInfo);
const volumeColormap = this.getColormap(volumeId);

const colormap =
volumeId && volumeColormap ? volumeColormap : latestColormap;
Expand Down Expand Up @@ -880,61 +895,24 @@ abstract class BaseVolumeViewport extends Viewport implements IVolumeViewport {
* @param applicableVolumeActorInfo - The volume actor information for the volume
* @returns colormap information for the volume if identified
*/
private getColormap = (applicableVolumeActorInfo) => {
private getColormap = (volumeId) => {
const applicableVolumeActorInfo = this._getApplicableVolumeActor(volumeId);

if (!applicableVolumeActorInfo) {
return;
}

const { volumeActor } = applicableVolumeActorInfo;
const cfun = volumeActor.getProperty().getRGBTransferFunction(0);
const { nodes } = cfun.getState();
const RGBPoints = nodes.reduce((acc, node) => {
acc.push(node.x, node.r, node.g, node.b);
return acc;
}, []);
const colormapsVTK = vtkColorMaps.rgbPresetNames.map((presetName) =>
vtkColorMaps.getPresetByName(presetName)
);
const colormapsCS3D = getColormapNames().map((colormapName) =>
getColormap(colormapName)
);
const colormaps = colormapsVTK.concat(colormapsCS3D);
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 opacityPoints = volumeActor
.getProperty()
.getScalarOpacity(0)
.getDataPointer();

const opacity = [];
for (let i = 0; i < opacityPoints.length; i += 2) {
opacity.push({ value: opacityPoints[i], opacity: opacityPoints[i + 1] });
}

const colormap = {
name: matchedColormap.Name,
opacity: opacity,
};
const matchedColormap = findMatchingColormap(RGBPoints, volumeActor);

return colormap;
return matchedColormap;
};

/**
Expand Down Expand Up @@ -996,6 +974,8 @@ abstract class BaseVolumeViewport extends Viewport implements IVolumeViewport {
this._setVolumeActors(volumeActors);
this.viewportStatus = ViewportStatus.PRE_RENDER;

this.initializeColorTransferFunction(volumeInputArray);

triggerEvent(this.element, Events.VOLUME_VIEWPORT_NEW_VOLUME, {
viewportId: this.id,
volumeActors,
Expand Down Expand Up @@ -1070,6 +1050,8 @@ abstract class BaseVolumeViewport extends Viewport implements IVolumeViewport {

this.addActors(volumeActors);

this.initializeColorTransferFunction(volumeInputArray);

if (immediate) {
// render
this.render();
Expand Down Expand Up @@ -1106,6 +1088,25 @@ abstract class BaseVolumeViewport extends Viewport implements IVolumeViewport {
console.warn('Method "setOrientation" needs implementation');
}

/**
* Initializes the color transfer function nodes for a given volume.
*
* @param volumeInputArray - Array of volume inputs.
* @param getTransferFunctionNodes - Function to get the transfer function nodes.
* @returns void
*/
private initializeColorTransferFunction(volumeInputArray) {
const selectedVolumeId = volumeInputArray[0].volumeId;
const colorTransferFunction =
this._getOrCreateColorTransferFunction(selectedVolumeId);

if (!this.initialTransferFunctionNodes) {
this.initialTransferFunctionNodes = getTransferFunctionNodes(
colorTransferFunction
);
}
}

private _getApplicableVolumeActor(volumeId?: string) {
if (volumeId !== undefined && !this.getActor(volumeId)) {
return;
Expand Down
13 changes: 13 additions & 0 deletions packages/core/src/RenderingEngine/StackViewport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ import {
import correctShift from './helpers/cpuFallback/rendering/correctShift';
import resetCamera from './helpers/cpuFallback/rendering/resetCamera';
import { Transform } from './helpers/cpuFallback/rendering/transform';
import { findMatchingColormap } from '../utilities/colormap';

const EPSILON = 1; // Slice Thickness

Expand Down Expand Up @@ -867,6 +868,18 @@ class StackViewport extends Viewport implements IStackViewport, IImagesLoader {
transferFunction,
this.initialTransferFunctionNodes
);

const nodes = getTransferFunctionNodes(transferFunction);

const RGBPoints = nodes.reduce((acc, node) => {
acc.push(node[0], node[1], node[2], node[3]);
return acc;
}, []);

const defaultActor = this.getDefaultActor();
const matchedColormap = findMatchingColormap(RGBPoints, defaultActor.actor);

this.setColormap(matchedColormap);
}

public resetToDefaultProperties(): void {
Expand Down
20 changes: 6 additions & 14 deletions packages/core/src/RenderingEngine/VolumeViewport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -443,26 +443,18 @@ class VolumeViewport extends BaseVolumeViewport {
setDefaultVolumeVOI(volumeActor.actor as vtkVolume, imageVolume, false);

if (isImageActor(volumeActor)) {
const transferFunction = (volumeActor.actor as ImageActor)
.getProperty()
.getRGBTransferFunction(0);

setTransferFunctionNodes(
(volumeActor.actor as ImageActor)
.getProperty()
.getRGBTransferFunction(0),
transferFunction,
this.initialTransferFunctionNodes
);
}

const range = (volumeActor.actor as vtkVolume)
.getProperty()
.getRGBTransferFunction(0)
.getMappingRange();

const eventDetails = {
viewportId: volumeActor.uid,
range: {
lower: range[0],
upper: range[1],
},
volumeId: volumeActor.uid,
...super.getVOIModifiedEventDetail(volumeId),
};

const resetPan = true;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/types/Colormap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import RGB from './RGB';
type ColormapRegistration = {
ColorSpace: string;
Name: string;
RGBPoints: RGB[];
RGBPoints: RGB[] | number[];
};

type OpacityMapping = {
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/types/EventTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ type VoiModifiedEventDetail = {
invert?: boolean;
/** Indicates if the 'invert' state has changed from the previous state */
invertStateChanged?: boolean;
/** color map */
colormap?: ColormapPublic;
};

type ColormapModifiedEventDetail = {
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/utilities/actorCheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ export function isImageActor(actorEntry: Types.ActorEntry): boolean {
}

export function actorIsA(
actorEntry: Types.ActorEntry,
actorEntry: Types.ActorEntry | Types.Actor,
actorType: actorTypes
): boolean {
const actor = actorEntry.actor;
const actorToCheck = 'isA' in actorEntry ? actorEntry : actorEntry.actor;

return !!actor.isA(actorType);
return !!actorToCheck.isA(actorType);
}
Loading