Skip to content

Commit bfb9a22

Browse files
committed
feat(vtkimagereslice): add support of oriented vtkImageData
1 parent 593d7f5 commit bfb9a22

File tree

3 files changed

+284
-63
lines changed

3 files changed

+284
-63
lines changed

Sources/Imaging/Core/ImageReslice/index.js

Lines changed: 81 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { vec4, mat4 } from 'gl-matrix';
22

3-
import macro from 'vtk.js/Sources/macros';
3+
import macro, { vtkWarningMacro } from 'vtk.js/Sources/macros';
44
import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray';
5+
import vtkMath from 'vtk.js/Sources/Common/Core/Math';
6+
import vtkMatrixBuilder from 'vtk.js/Sources/Common/Core/MatrixBuilder';
57
import { VtkDataTypes } from 'vtk.js/Sources/Common/Core/DataArray/Constants';
68
import vtkBoundingBox from 'vtk.js/Sources/Common/DataModel/BoundingBox';
79
import vtkImageData from 'vtk.js/Sources/Common/DataModel/ImageData';
@@ -20,7 +22,7 @@ import Constants from 'vtk.js/Sources/Imaging/Core/ImageReslice/Constants';
2022

2123
const { SlabMode } = Constants;
2224

23-
const { capitalize, vtkErrorMacro } = macro;
25+
const { vtkErrorMacro } = macro;
2426

2527
// ----------------------------------------------------------------------------
2628
// vtkImageReslice methods
@@ -29,6 +31,7 @@ const { capitalize, vtkErrorMacro } = macro;
2931
function vtkImageReslice(publicAPI, model) {
3032
// Set our className
3133
model.classHierarchy.push('vtkImageReslice');
34+
const superClass = { ...publicAPI };
3235

3336
let indexMatrix = null;
3437
let optimizedTransform = null;
@@ -99,6 +102,14 @@ function vtkImageReslice(publicAPI, model) {
99102
getImageResliceSlabTrap(tmpPtr, inComponents, sampleCount, f);
100103
}
101104

105+
publicAPI.getMTime = () => {
106+
let mTime = superClass.getMTime();
107+
if (model.resliceTransform) {
108+
mTime = Math.max(mTime, model.resliceTransform.getMTime());
109+
}
110+
return mTime;
111+
};
112+
102113
publicAPI.setResliceAxes = (resliceAxes) => {
103114
if (!model.resliceAxes) {
104115
model.resliceAxes = mat4.identity(new Float64Array(16));
@@ -136,11 +147,9 @@ function vtkImageReslice(publicAPI, model) {
136147
const outWholeExt = [0, 0, 0, 0, 0, 0];
137148
const outDims = [0, 0, 0];
138149

139-
let matrix = null;
150+
const matrix = mat4.identity(new Float64Array(16));
140151
if (model.resliceAxes) {
141-
matrix = model.resliceAxes;
142-
} else {
143-
matrix = mat4.identity(new Float64Array(16));
152+
mat4.multiply(matrix, matrix, model.resliceAxes);
144153
}
145154
const imatrix = new Float64Array(16);
146155
mat4.invert(imatrix, matrix);
@@ -250,6 +259,9 @@ function vtkImageReslice(publicAPI, model) {
250259
output.setDimensions(outDims);
251260
output.setOrigin(outOrigin);
252261
output.setSpacing(outSpacing);
262+
if (model.outputDirection) {
263+
output.setDirection(model.outputDirection);
264+
}
253265
output.getPointData().setScalars(outScalars);
254266

255267
publicAPI.getIndexMatrix(input, output);
@@ -385,7 +397,9 @@ function vtkImageReslice(publicAPI, model) {
385397
// allocate an output row of type double
386398
let floatPtr = null;
387399
if (!optimizeNearest) {
388-
floatPtr = new Float64Array(inComponents * (outExt[1] - outExt[0]));
400+
floatPtr = new Float64Array(
401+
inComponents * (outExt[1] - outExt[0] + nsamples)
402+
);
389403
}
390404

391405
const background = macro.newTypedArray(
@@ -711,7 +725,19 @@ function vtkImageReslice(publicAPI, model) {
711725
}
712726
}
713727
};
714-
728+
/**
729+
* The transform matrix supplied by the user converts output coordinates
730+
* to input coordinates.
731+
* To speed up the pixel lookup, the following function provides a
732+
* matrix which converts output pixel indices to input pixel indices.
733+
* This will also concatenate the ResliceAxes and the ResliceTransform
734+
* if possible (if the ResliceTransform is a 4x4 matrix transform).
735+
* If it does, this->OptimizedTransform will be set to nullptr, otherwise
736+
* this->OptimizedTransform will be equal to this->ResliceTransform.
737+
* @param {vtkPolyData} input
738+
* @param {vtkPolyData} output
739+
* @returns
740+
*/
715741
publicAPI.getIndexMatrix = (input, output) => {
716742
// first verify that we have to update the matrix
717743
if (indexMatrix === null) {
@@ -720,6 +746,7 @@ function vtkImageReslice(publicAPI, model) {
720746

721747
const inOrigin = input.getOrigin();
722748
const inSpacing = input.getSpacing();
749+
const inDirection = input.getDirection();
723750
const outOrigin = output.getOrigin();
724751
const outSpacing = output.getSpacing();
725752

@@ -735,19 +762,36 @@ function vtkImageReslice(publicAPI, model) {
735762
mat4.copy(transform, model.resliceAxes);
736763
}
737764
if (model.resliceTransform) {
738-
// TODO
765+
if (model.resliceTransform.isA('vtkHomogeneousTransform')) {
766+
// transform->PostMultiply();
767+
// transform->Concatenate(
768+
// mat4.multiply(transform, transform, model.resliceTransform.getMatrix());
769+
mat4.multiply(transform, model.resliceTransform.getMatrix(), transform);
770+
} else {
771+
// TODO
772+
vtkWarningMacro('Non homogeneous transform have not yet been ported');
773+
}
774+
}
775+
776+
if (!vtkMath.isIdentity3x3(inDirection)) {
777+
const imageTransform = vtkMatrixBuilder
778+
.buildFromRadian()
779+
.translate(inOrigin[0], inOrigin[1], inOrigin[2])
780+
.multiply3x3(inDirection)
781+
.translate(-inOrigin[0], -inOrigin[1], -inOrigin[2]);
782+
mat4.multiply(transform, imageTransform.getMatrix(), transform);
739783
}
740784

741785
// check to see if we have an identity matrix
742-
let isIdentity = publicAPI.isIdentityMatrix(transform);
786+
let isIdentity = vtkMath.isIdentity(transform);
743787

744788
// the outMatrix takes OutputData indices to OutputData coordinates,
745789
// the inMatrix takes InputData coordinates to InputData indices
746790
for (let i = 0; i < 3; i++) {
747791
if (
748-
(optimizedTransform === null &&
792+
(optimizedTransform == null &&
749793
(inSpacing[i] !== outSpacing[i] || inOrigin[i] !== outOrigin[i])) ||
750-
(optimizedTransform !== null &&
794+
(optimizedTransform != null &&
751795
(outSpacing[i] !== 1.0 || outOrigin[i] !== 0.0))
752796
) {
753797
isIdentity = false;
@@ -780,6 +824,7 @@ function vtkImageReslice(publicAPI, model) {
780824
publicAPI.getAutoCroppedOutputBounds = (input) => {
781825
const inOrigin = input.getOrigin();
782826
const inSpacing = input.getSpacing();
827+
const inDirection = input.getDirection();
783828
const dims = input.getDimensions();
784829
const inWholeExt = [0, dims[0] - 1, 0, dims[1] - 1, 0, dims[2] - 1];
785830

@@ -789,6 +834,20 @@ function vtkImageReslice(publicAPI, model) {
789834
} else {
790835
mat4.identity(matrix);
791836
}
837+
let transform = null;
838+
if (model.resliceTransform) {
839+
transform = model.resliceTransform.getInverse();
840+
}
841+
let imageTransform = null;
842+
if (!vtkMath.isIdentity3x3(inDirection)) {
843+
imageTransform = vtkMatrixBuilder
844+
.buildFromRadian()
845+
.translate(inOrigin[0], inOrigin[1], inOrigin[2])
846+
.multiply3x3(inDirection)
847+
.translate(-inOrigin[0], -inOrigin[1], -inOrigin[2])
848+
.invert()
849+
.getMatrix();
850+
}
792851

793852
const bounds = [
794853
Number.MAX_VALUE,
@@ -808,8 +867,12 @@ function vtkImageReslice(publicAPI, model) {
808867
inOrigin[2] + inWholeExt[4 + (Math.floor(i / 4) % 2)] * inSpacing[2];
809868
point[3] = 1.0;
810869

870+
if (imageTransform) {
871+
vec4.transformMat4(point, point, imageTransform);
872+
}
873+
811874
if (model.resliceTransform) {
812-
// TODO
875+
transform.transformPoint(point, point);
813876
}
814877

815878
vec4.transformMat4(point, point, matrix);
@@ -1048,24 +1111,6 @@ function vtkImageReslice(publicAPI, model) {
10481111
};
10491112
}
10501113

1051-
function setNullArray(publicAPI, model, fieldNames) {
1052-
fieldNames.forEach((field) => {
1053-
const setterName = `set${capitalize(field)}`;
1054-
const superSet = publicAPI[setterName];
1055-
publicAPI[setterName] = (...args) => {
1056-
if ((args.length === 1 && args[0] == null) || model[field] == null) {
1057-
if (args[0] !== model[field]) {
1058-
model[field] = args[0];
1059-
publicAPI.modified();
1060-
return true;
1061-
}
1062-
return null;
1063-
}
1064-
return superSet(...args);
1065-
};
1066-
});
1067-
}
1068-
10691114
// ----------------------------------------------------------------------------
10701115
// Object factory
10711116
// ----------------------------------------------------------------------------
@@ -1076,6 +1121,7 @@ const DEFAULT_VALUES = {
10761121
outputDimensionality: 3,
10771122
outputSpacing: null, // automatically computed if null
10781123
outputOrigin: null, // automatically computed if null
1124+
outputDirection: null, // identity if null
10791125
outputExtent: null, // automatically computed if null
10801126
outputScalarType: null,
10811127
wrap: false, // don't wrap
@@ -1091,7 +1137,7 @@ const DEFAULT_VALUES = {
10911137
scalarScale: 1,
10921138
backgroundColor: [0, 0, 0, 0],
10931139
resliceAxes: null,
1094-
resliceTransform: null,
1140+
// resliceTransform: null,
10951141
interpolator: vtkImageInterpolator.newInstance(),
10961142
usePermuteExecute: false, // no supported yet
10971143
};
@@ -1117,8 +1163,8 @@ export function extend(publicAPI, model, initialValues = {}) {
11171163
'wrap',
11181164
'mirror',
11191165
'border',
1120-
'backgroundColor',
11211166
'interpolationMode',
1167+
'resliceTransform',
11221168
'slabMode',
11231169
'slabTrapezoidIntegration',
11241170
'slabNumberOfSlices',
@@ -1127,12 +1173,8 @@ export function extend(publicAPI, model, initialValues = {}) {
11271173

11281174
macro.setGetArray(publicAPI, model, ['outputOrigin', 'outputSpacing'], 3);
11291175
macro.setGetArray(publicAPI, model, ['outputExtent'], 6);
1130-
1131-
setNullArray(publicAPI, model, [
1132-
'outputOrigin',
1133-
'outputSpacing',
1134-
'outputExtent',
1135-
]);
1176+
macro.setGetArray(publicAPI, model, ['outputDirection'], 9);
1177+
macro.setGetArray(publicAPI, model, ['backgroundColor'], 4);
11361178

11371179
macro.get(publicAPI, model, ['resliceAxes']);
11381180

0 commit comments

Comments
 (0)