Skip to content

Commit 86d0b0d

Browse files
bruyeretfinetjul
authored andcommitted
feat(cpr): Add stretched and projected CPR
Tweaking the settings of the mapper makes it possible to use the mapper in straightened, stretched or projected mode.
1 parent 1f46935 commit 86d0b0d

File tree

5 files changed

+79
-8
lines changed

5 files changed

+79
-8
lines changed

Sources/Common/DataModel/PolyLine/index.d.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { quat } from 'gl-matrix';
2-
import { Vector2, Vector3 } from '../../../types';
1+
import { quat, vec3 } from 'gl-matrix';
2+
import { Nullable, Vector2, Vector3 } from '../../../types';
33
import vtkCell, { ICellInitialValues } from '../Cell';
44
import { IIntersectWithLine } from '../Line';
55

@@ -67,6 +67,31 @@ export interface vtkPolyLine extends vtkCell {
6767
* @param distance The distance from the first point of the polyline
6868
*/
6969
findPointIdAtDistanceFromFirstPoint(distance: number): number;
70+
71+
/**
72+
* An array of quaternions used to orient the polyline at each of its point
73+
* The length of the array has to be the same size as the number of points
74+
* Defaults to null.
75+
*/
76+
getOrientations(): Nullable<quat[]>;
77+
78+
/**
79+
* @see getOrientations
80+
* @param orientations
81+
*/
82+
setOrientations(orientations: Nullable<quat[]>): boolean;
83+
84+
/**
85+
* The function used in getDistancesToFirstPoint and in findPointIdAtDistanceFromFirstPoint
86+
* Defaults to vec3.dist of gl-matrix
87+
*/
88+
getDistanceFunction(): (a: vec3, b: vec3) => number;
89+
90+
/**
91+
* @see getDistanceFunction
92+
* @param f
93+
*/
94+
setDistanceFunction(f: (a: vec3, b: vec3) => number): boolean;
7095
}
7196

7297
/**

Sources/Common/DataModel/PolyLine/index.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ function vtkPolyLine(publicAPI, model) {
8181
};
8282

8383
publicAPI.getDistancesToFirstPoint = () => {
84-
if (model.distancesTime.getMTime() < model.points.getMTime()) {
84+
const dTime = model.distancesTime.getMTime();
85+
if (dTime < model.points.getMTime() || dTime < publicAPI.getMTime()) {
8586
const numPoints = publicAPI.getNumberOfPoints();
8687
if (!model.distances) {
8788
model.distances = new Array(numPoints);
@@ -96,7 +97,7 @@ function vtkPolyLine(publicAPI, model) {
9697
model.points.getPoint(0, previousPoint);
9798
for (let i = 1; i < numPoints; ++i) {
9899
model.points.getPoint(i, currentPoint);
99-
totalDistance += vec3.dist(previousPoint, currentPoint);
100+
totalDistance += model.distanceFunction(previousPoint, currentPoint);
100101
model.distances[i] = totalDistance;
101102
vec3.copy(previousPoint, currentPoint);
102103
}
@@ -140,6 +141,7 @@ function vtkPolyLine(publicAPI, model) {
140141

141142
const DEFAULT_VALUES = {
142143
orientations: null, // an array of quat or null
144+
distanceFunction: vec3.dist,
143145
};
144146

145147
// ----------------------------------------------------------------------------
@@ -149,7 +151,7 @@ export function extend(publicAPI, model, initialValues = {}) {
149151

150152
vtkCell.extend(publicAPI, model, initialValues);
151153

152-
macro.setGet(publicAPI, model, ['orientations']);
154+
macro.setGet(publicAPI, model, ['orientations', 'distanceFunction']);
153155

154156
model.distancesTime = {};
155157
macro.obj(model.distancesTime, { mtime: 0 });

Sources/Rendering/Core/ImageCPRMapper/index.d.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,21 @@ export interface vtkImageCPRMapper extends vtkAbstractMapper3D {
6262
*/
6363
setUseUniformOrientation(useUniformOrientation: boolean): boolean;
6464

65+
/**
66+
* A point used to offset each line of pixel in the rendering
67+
* The line of pixel is offseted such as the center of the line is as close as possible to the center point
68+
* This can be used in combination with @see getUseUniformOrientation and a custom distance function for @see getOrientedCenterline to visualize a CPR in projected mode or stretched mode
69+
* Defaults to null.
70+
* @returns the center point
71+
*/
72+
getCenterPoint(): Nullable<vec3>;
73+
74+
/**
75+
* @see getCenterPoint
76+
* @param point
77+
*/
78+
setCenterPoint(point: Nullable<vec3>): boolean;
79+
6580
/**
6681
* This flag indicates wether the GPU should use half float or not
6782
* When true, will use half float
@@ -346,7 +361,10 @@ export function newInstance(initialValues?: IImageCPRMapperInitialValues): vtkIm
346361

347362
/**
348363
* CPR in vtkImageCPRMapper stands for Curved Planar Reformation. This mapper
349-
* can be used to visualize tubular structures such as blood vessels.
364+
* can be used to visualize tubular structures such as blood vessels. It can be
365+
* used in projected mode, stretched mode or straightened mode depending on the
366+
* settings @see getUseUniformOrientation , @see getCenterPoint and the distance
367+
* function of @see getOrientedCenterline .
350368
*
351369
* This specialised mapper takes as input a vtkImageData representing a volume
352370
* ( @see setImageData ) and a vtkPolyData representing a centerline

Sources/Rendering/Core/ImageCPRMapper/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ const DEFAULT_VALUES = {
297297
width: 10,
298298
uniformOrientation: [0, 0, 0, 1],
299299
useUniformOrientation: false,
300+
centerPoint: null,
300301
preferSizeOverAccuracy: false,
301302
orientationArrayName: null,
302303
tangentDirection: [1, 0, 0],
@@ -323,6 +324,7 @@ export function extend(publicAPI, model, initialValues = {}) {
323324
'width',
324325
'uniformOrientation',
325326
'useUniformOrientation',
327+
'centerPoint',
326328
'preferSizeOverAccuracy',
327329
'orientationArrayName',
328330
'tangentDirection',

Sources/Rendering/OpenGL/ImageCPRMapper/index.js

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -504,20 +504,28 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
504504
publicAPI.getNeedToRebuildShaders = (cellBO, ren, actor) => {
505505
// has something changed that would require us to recreate the shader?
506506
// candidates are
507+
// presence of centerPoint
508+
// value of useUniformOrientation
507509
// property modified (representation interpolation and lighting)
508510
// input modified
509511
// light complexity changed
510512
// render pass shader replacement changed
511513

512514
const tNumComp = model.volumeTexture.getComponents();
513515
const iComp = actor.getProperty().getIndependentComponents();
516+
const useCenterPoint = !!model.renderable.getCenterPoint();
517+
const useUniformOrientation = model.renderable.getUseUniformOrientation();
514518

515519
if (
516-
model.lastHaveSeenDepthRequest !== model.haveSeenDepthRequest ||
517520
cellBO.getProgram() === 0 ||
521+
model.lastUseCenterPoint !== useCenterPoint ||
522+
model.lastUseUniformOrientation !== useUniformOrientation ||
523+
model.lastHaveSeenDepthRequest !== model.haveSeenDepthRequest ||
518524
model.lastTextureComponents !== tNumComp ||
519525
model.lastIndependentComponents !== iComp
520526
) {
527+
model.lastUseCenterPoint = useCenterPoint;
528+
model.lastUseUniformOrientation = useUniformOrientation;
521529
model.lastHaveSeenDepthRequest = model.haveSeenDepthRequest;
522530
model.lastTextureComponents = tNumComp;
523531
model.lastIndependentComponents = iComp;
@@ -653,6 +661,10 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
653661
'in float centerlineAngleVSOutput;'
654662
);
655663
}
664+
const centerPoint = model.renderable.getCenterPoint();
665+
if (centerPoint) {
666+
tcoordFSDec.push('uniform vec3 globalCenterPoint;');
667+
}
656668
if (iComps) {
657669
for (let comp = 1; comp < tNumComp; comp++) {
658670
tcoordFSDec = tcoordFSDec.concat([
@@ -740,8 +752,16 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
740752
'interpolatedCenterlineDir = normalize(interpolatedCenterlineDir);'
741753
);
742754
}
755+
if (centerPoint) {
756+
tcoordFSImpl.push(
757+
'float baseOffset = dot(interpolatedCenterlineDir, globalCenterPoint - centerlinePosVSOutput);',
758+
'float horizontalOffset = quadOffsetVSOutput.x + baseOffset;'
759+
);
760+
} else {
761+
tcoordFSImpl.push('float horizontalOffset = quadOffsetVSOutput.x;');
762+
}
743763
tcoordFSImpl.push(
744-
'vec3 volumePosMC = centerlinePosVSOutput + quadOffsetVSOutput.x * interpolatedCenterlineDir;',
764+
'vec3 volumePosMC = centerlinePosVSOutput + horizontalOffset * interpolatedCenterlineDir;',
745765
'vec3 volumePosTC = (MCTCMatrix * vec4(volumePosMC, 1.0)).xyz;',
746766
'if (any(lessThan(volumePosTC, vec3(0.0))) || any(greaterThan(volumePosTC, vec3(1.0))))',
747767
'{',
@@ -972,6 +992,10 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
972992
.getProgram()
973993
.setUniform3fArray('centerlineDirection', uniformDirection);
974994
}
995+
if (cellBO.getProgram().isUniformUsed('globalCenterPoint')) {
996+
const centerPoint = model.renderable.getCenterPoint();
997+
cellBO.getProgram().setUniform3fArray('globalCenterPoint', centerPoint);
998+
}
975999

9761000
// Model coordinates to image space
9771001
// getWorldToIndex is badly named and is in fact modelToIndex

0 commit comments

Comments
 (0)