1
1
import * as macro from 'vtk.js/Sources/macros' ;
2
2
3
- import { mat4 , vec3 } from 'gl-matrix' ;
3
+ import { mat4 , mat3 , vec3 } from 'gl-matrix' ;
4
4
5
5
import vtkClosedPolyLineToSurfaceFilter from 'vtk.js/Sources/Filters/General/ClosedPolyLineToSurfaceFilter' ;
6
6
import vtkCubeSource from 'vtk.js/Sources/Filters/Sources/CubeSource' ;
@@ -1067,6 +1067,52 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
1067
1067
return [ false , 2 ] ;
1068
1068
}
1069
1069
1070
+ function transformPoints ( points , transform ) {
1071
+ const tmp = [ 0 , 0 , 0 ] ;
1072
+ for ( let i = 0 ; i < points . length ; i += 3 ) {
1073
+ const p = points . subarray ( i , i + 3 ) ;
1074
+ if ( transform . length === 9 ) {
1075
+ vec3 . transformMat3 ( tmp , p , transform ) ;
1076
+ } else {
1077
+ vec3 . transformMat4 ( tmp , p , transform ) ;
1078
+ }
1079
+ p [ 0 ] = tmp [ 0 ] ;
1080
+ p [ 1 ] = tmp [ 1 ] ;
1081
+ p [ 2 ] = tmp [ 2 ] ;
1082
+ }
1083
+ }
1084
+
1085
+ function imageToCubePolyData ( image , outPD ) {
1086
+ // First create a cube polydata in the index-space of the image.
1087
+ const sext = image ?. getSpatialExtent ( ) ;
1088
+
1089
+ if ( sext ) {
1090
+ model . cubeSource . setXLength ( sext [ 1 ] - sext [ 0 ] ) ;
1091
+ model . cubeSource . setYLength ( sext [ 3 ] - sext [ 2 ] ) ;
1092
+ model . cubeSource . setZLength ( sext [ 5 ] - sext [ 4 ] ) ;
1093
+ } else {
1094
+ model . cubeSource . setXLength ( 1 ) ;
1095
+ model . cubeSource . setYLength ( 1 ) ;
1096
+ model . cubeSource . setZLength ( 1 ) ;
1097
+ }
1098
+
1099
+ model . cubeSource . setCenter (
1100
+ model . cubeSource . getXLength ( ) / 2.0 ,
1101
+ model . cubeSource . getYLength ( ) / 2.0 ,
1102
+ model . cubeSource . getZLength ( ) / 2.0
1103
+ ) ;
1104
+
1105
+ model . cubeSource . update ( ) ;
1106
+ const out = model . cubeSource . getOutputData ( ) ;
1107
+ outPD . getPoints ( ) . setData ( Float32Array . from ( out . getPoints ( ) . getData ( ) ) , 3 ) ;
1108
+ outPD . getPolys ( ) . setData ( Uint32Array . from ( out . getPolys ( ) . getData ( ) ) , 1 ) ;
1109
+
1110
+ // Now, transform the cube polydata points in-place
1111
+ // using the image's indexToWorld transformation.
1112
+ const points = outPD . getPoints ( ) . getData ( ) ;
1113
+ transformPoints ( points , image . getIndexToWorld ( ) ) ;
1114
+ }
1115
+
1070
1116
publicAPI . updateResliceGeometry = ( ) => {
1071
1117
let resGeomString = '' ;
1072
1118
const image = model . currentInput ;
@@ -1084,7 +1130,15 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
1084
1130
resGeomString = resGeomString . concat ( `Image${ image . getMTime ( ) } ` ) ;
1085
1131
}
1086
1132
// Check to see if we can bypass oblique slicing related bounds computation
1087
- [ orthoSlicing , orthoAxis ] = isVectorAxisAligned ( slicePlane . getNormal ( ) ) ;
1133
+ // Compute a world-to-image-orientation matrix.
1134
+ // Ignore the translation component since we are
1135
+ // using it on vectors rather than positions.
1136
+ const w2io = mat3 . fromValues ( image ?. getDirection ( ) ) ;
1137
+ mat3 . invert ( w2io , w2io ) ;
1138
+ // transform the cutting plane normal to image local coords
1139
+ const imageLocalNormal = [ ...slicePlane . getNormal ( ) ] ;
1140
+ vec3 . transformMat3 ( imageLocalNormal , imageLocalNormal , w2io ) ;
1141
+ [ orthoSlicing , orthoAxis ] = isVectorAxisAligned ( imageLocalNormal ) ;
1088
1142
} else {
1089
1143
// Create a default slice plane here
1090
1144
const plane = vtkPlane . newInstance ( ) ;
@@ -1112,27 +1166,18 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
1112
1166
. getPointData ( )
1113
1167
. setNormals ( slicePD . getPointData ( ) . getNormals ( ) ) ;
1114
1168
} else if ( slicePlane ) {
1115
- const bounds = image ? imageBounds : [ 0 , 1 , 0 , 1 , 0 , 1 ] ;
1116
1169
if ( ! orthoSlicing ) {
1117
- const cube = vtkCubeSource . newInstance ( ) ;
1118
- cube . setCenter (
1119
- 0.5 * ( bounds [ 0 ] + bounds [ 1 ] ) ,
1120
- 0.5 * ( bounds [ 2 ] + bounds [ 3 ] ) ,
1121
- 0.5 * ( bounds [ 4 ] + bounds [ 5 ] )
1170
+ imageToCubePolyData ( image , model . cubePolyData ) ;
1171
+ model . cutter . setInputData ( model . cubePolyData ) ;
1172
+ model . cutter . setCutFunction ( slicePlane ) ;
1173
+ model . lineToSurfaceFilter . setInputConnection (
1174
+ model . cutter . getOutputPort ( )
1122
1175
) ;
1123
- cube . setXLength ( bounds [ 1 ] - bounds [ 0 ] ) ;
1124
- cube . setYLength ( bounds [ 3 ] - bounds [ 2 ] ) ;
1125
- cube . setZLength ( bounds [ 5 ] - bounds [ 4 ] ) ;
1126
- const cutter = vtkCutter . newInstance ( ) ;
1127
- cutter . setInputConnection ( cube . getOutputPort ( ) ) ;
1128
- cutter . setCutFunction ( slicePlane ) ;
1129
- const pds = vtkClosedPolyLineToSurfaceFilter . newInstance ( ) ;
1130
- pds . setInputConnection ( cutter . getOutputPort ( ) ) ;
1131
- pds . update ( ) ;
1176
+ model . lineToSurfaceFilter . update ( ) ;
1132
1177
if ( ! model . resliceGeom ) {
1133
1178
model . resliceGeom = vtkPolyData . newInstance ( ) ;
1134
1179
}
1135
- const planePD = pds . getOutputData ( ) ;
1180
+ const planePD = model . lineToSurfaceFilter . getOutputData ( ) ;
1136
1181
model . resliceGeom
1137
1182
. getPoints ( )
1138
1183
. setData ( planePD . getPoints ( ) . getData ( ) , 3 ) ;
@@ -1158,18 +1203,26 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
1158
1203
} ) ;
1159
1204
model . resliceGeom . getPointData ( ) . setNormals ( normals ) ;
1160
1205
} else {
1206
+ // Since the image-local normal is axis-aligned, we
1207
+ // can quickly construct the cutting plane using indexToWorld transforms.
1161
1208
const ptsArray = new Float32Array ( 12 ) ;
1162
- const o = slicePlane . getOrigin ( ) ;
1209
+ const indexSpacePlaneOrigin = image . worldToIndex (
1210
+ slicePlane . getOrigin ( ) ,
1211
+ [ 0 , 0 , 0 ]
1212
+ ) ;
1163
1213
const otherAxes = [ ( orthoAxis + 1 ) % 3 , ( orthoAxis + 2 ) % 3 ] . sort ( ) ;
1214
+ const dim = image . getDimensions ( ) ;
1215
+ const ext = [ 0 , dim [ 0 ] - 1 , 0 , dim [ 1 ] - 1 , 0 , dim [ 2 ] - 1 ] ;
1164
1216
let ptIdx = 0 ;
1165
1217
for ( let i = 0 ; i < 2 ; ++ i ) {
1166
1218
for ( let j = 0 ; j < 2 ; ++ j ) {
1167
- ptsArray [ ptIdx + orthoAxis ] = o [ orthoAxis ] ;
1168
- ptsArray [ ptIdx + otherAxes [ 0 ] ] = bounds [ 2 * otherAxes [ 0 ] + j ] ;
1169
- ptsArray [ ptIdx + otherAxes [ 1 ] ] = bounds [ 2 * otherAxes [ 1 ] + i ] ;
1219
+ ptsArray [ ptIdx + orthoAxis ] = indexSpacePlaneOrigin [ orthoAxis ] ;
1220
+ ptsArray [ ptIdx + otherAxes [ 0 ] ] = ext [ 2 * otherAxes [ 0 ] + j ] ;
1221
+ ptsArray [ ptIdx + otherAxes [ 1 ] ] = ext [ 2 * otherAxes [ 1 ] + i ] ;
1170
1222
ptIdx += 3 ;
1171
1223
}
1172
1224
}
1225
+ transformPoints ( ptsArray , image . getIndexToWorld ( ) ) ;
1173
1226
1174
1227
const cellArray = new Uint16Array ( 8 ) ;
1175
1228
cellArray [ 0 ] = 3 ;
@@ -1273,9 +1326,14 @@ export function extend(publicAPI, model, initialValues = {}) {
1273
1326
model . VBOBuildTime = { } ;
1274
1327
macro . obj ( model . VBOBuildTime ) ;
1275
1328
1276
- // model.modelToView = mat4.identity(new Float64Array(16));
1277
1329
model . tmpMat4 = mat4 . identity ( new Float64Array ( 16 ) ) ;
1278
1330
1331
+ // Implicit plane to polydata related cache:
1332
+ model . cubeSource = vtkCubeSource . newInstance ( ) ;
1333
+ model . cubePolyData = vtkPolyData . newInstance ( ) ;
1334
+ model . cutter = vtkCutter . newInstance ( ) ;
1335
+ model . lineToSurfaceFilter = vtkClosedPolyLineToSurfaceFilter . newInstance ( ) ;
1336
+
1279
1337
macro . get ( publicAPI , model , [ 'openGLTexture' ] ) ;
1280
1338
1281
1339
// Object methods
0 commit comments