1
- import { vec3 } from 'gl-matrix' ;
1
+ import { vec3 , mat4 } from 'gl-matrix' ;
2
2
import { Events as EVENTS , MetadataModules } from '../enums' ;
3
3
import type {
4
4
WSIViewportProperties ,
@@ -9,7 +9,6 @@ import type {
9
9
CPUIImageData ,
10
10
ViewportInput ,
11
11
BoundsIJK ,
12
- CPUImageData ,
13
12
} from '../types' ;
14
13
import uuidv4 from '../utilities/uuidv4' ;
15
14
import * as metaData from '../metaData' ;
@@ -23,7 +22,9 @@ import { pointInShapeCallback } from '../utilities/pointInShapeCallback';
23
22
import microscopyViewportCss from '../constants/microscopyViewportCss' ;
24
23
import type { DataSetOptions } from '../types/IViewport' ;
25
24
25
+ let WSIUtilFunctions = null ;
26
26
const _map = Symbol . for ( 'map' ) ;
27
+ const affineSymbol = Symbol . for ( 'affine' ) ;
27
28
const EVENT_POSTRENDER = 'postrender' ;
28
29
/**
29
30
* A viewport which shows a microscopy view using the dicom-microscopy-viewer
@@ -232,6 +233,30 @@ class WSIViewport extends Viewport {
232
233
return null ;
233
234
}
234
235
236
+ public computeTransforms ( ) {
237
+ const indexToWorld = mat4 . create ( ) ;
238
+ const worldToIndex = mat4 . create ( ) ;
239
+
240
+ mat4 . fromTranslation ( indexToWorld , this . metadata . origin ) ;
241
+
242
+ indexToWorld [ 0 ] = this . metadata . direction [ 0 ] ;
243
+ indexToWorld [ 1 ] = this . metadata . direction [ 1 ] ;
244
+ indexToWorld [ 2 ] = this . metadata . direction [ 2 ] ;
245
+
246
+ indexToWorld [ 4 ] = this . metadata . direction [ 3 ] ;
247
+ indexToWorld [ 5 ] = this . metadata . direction [ 4 ] ;
248
+ indexToWorld [ 6 ] = this . metadata . direction [ 5 ] ;
249
+
250
+ indexToWorld [ 8 ] = this . metadata . direction [ 6 ] ;
251
+ indexToWorld [ 9 ] = this . metadata . direction [ 7 ] ;
252
+ indexToWorld [ 10 ] = this . metadata . direction [ 8 ] ;
253
+
254
+ mat4 . scale ( indexToWorld , indexToWorld , this . metadata . spacing ) ;
255
+
256
+ mat4 . invert ( worldToIndex , indexToWorld ) ;
257
+ return { indexToWorld, worldToIndex } ;
258
+ }
259
+
235
260
public getImageData ( ) : CPUIImageData {
236
261
const { metadata } = this ;
237
262
if ( ! metadata ) {
@@ -247,13 +272,10 @@ class WSIViewport extends Viewport {
247
272
getScalarData : ( ) => this . getScalarData ( ) ,
248
273
getSpacing : ( ) => metadata . spacing ,
249
274
worldToIndex : ( point : Point3 ) => {
250
- const canvasPoint = this . worldToCanvas ( point ) ;
251
- const pixelCoord = this . canvasToIndex ( canvasPoint ) ;
252
- return [ pixelCoord [ 0 ] , pixelCoord [ 1 ] , 0 ] as Point3 ;
275
+ return this . worldToIndex ( point ) ;
253
276
} ,
254
277
indexToWorld : ( point : Point3 ) => {
255
- const canvasPoint = this . indexToCanvas ( [ point [ 0 ] , point [ 1 ] ] ) ;
256
- return this . canvasToWorld ( canvasPoint ) ;
278
+ return this . indexToWorld ( point ) ;
257
279
} ,
258
280
} ;
259
281
const imageDataReturn = {
@@ -372,7 +394,11 @@ class WSIViewport extends Viewport {
372
394
this . refreshRenderValues ( ) ;
373
395
const { resolution, xSpacing, centerIndex } = this . internalCamera ;
374
396
const canvasToWorldRatio = resolution * xSpacing ;
375
- const canvasCenter = this . indexToCanvas ( centerIndex . slice ( 0 , 2 ) as Point2 ) ;
397
+ const canvasCenter = this . indexToCanvas ( [
398
+ centerIndex [ 0 ] ,
399
+ centerIndex [ 1 ] ,
400
+ 0 ,
401
+ ] ) ;
376
402
const focalPoint = this . canvasToWorld ( canvasCenter ) ;
377
403
378
404
return {
@@ -428,6 +454,54 @@ class WSIViewport extends Viewport {
428
454
this . refreshRenderValues ( ) ;
429
455
} ;
430
456
457
+ /**
458
+ * Converts a slide coordinate to a image coordinate using WSI utils functions
459
+ * @param point
460
+ * @returns
461
+ */
462
+ public worldToIndexWSI ( point : Point3 ) : Point2 {
463
+ if ( ! WSIUtilFunctions ) {
464
+ return ;
465
+ }
466
+ const affine = this . viewer [ affineSymbol ] ;
467
+ const pixelCoords = WSIUtilFunctions . applyInverseTransform ( {
468
+ coordinate : [ point [ 0 ] , point [ 1 ] ] ,
469
+ affine,
470
+ } ) ;
471
+ return [ pixelCoords [ 0 ] , pixelCoords [ 1 ] ] as Point2 ;
472
+ }
473
+
474
+ /**
475
+ * Converts a image coordinate to a slide coordinate using WSI utils functions
476
+ * @param point
477
+ * @returns
478
+ */
479
+ public indexToWorldWSI ( point : Point2 ) : Point3 {
480
+ if ( ! WSIUtilFunctions ) {
481
+ return ;
482
+ }
483
+ const sliceCoords = WSIUtilFunctions . applyTransform ( {
484
+ coordinate : [ point [ 0 ] , point [ 1 ] ] ,
485
+ affine : this . viewer [ affineSymbol ] ,
486
+ } ) ;
487
+ return [ sliceCoords [ 0 ] , sliceCoords [ 1 ] , 0 ] as Point3 ;
488
+ }
489
+
490
+ public worldToIndex ( point : Point3 ) : Point3 {
491
+ const { worldToIndex : worldToIndexMatrix } = this . computeTransforms ( ) ;
492
+ const imageCoord = vec3 . create ( ) ;
493
+ vec3 . transformMat4 ( imageCoord , point , worldToIndexMatrix ) ;
494
+ return imageCoord as Point3 ;
495
+ }
496
+
497
+ public indexToWorld ( point : Point3 ) : Point3 {
498
+ const { indexToWorld : indexToWorldMatrix } = this . computeTransforms ( ) ;
499
+ const worldPos = vec3 . create ( ) ;
500
+ const point3D = vec3 . fromValues ( ...point ) ;
501
+ vec3 . transformMat4 ( worldPos , point3D , indexToWorldMatrix ) ;
502
+ return [ worldPos [ 0 ] , worldPos [ 1 ] , worldPos [ 2 ] ] as Point3 ;
503
+ }
504
+
431
505
/**
432
506
* Converts a VideoViewport canvas coordinate to a video coordinate.
433
507
*
@@ -439,21 +513,9 @@ class WSIViewport extends Viewport {
439
513
return ;
440
514
}
441
515
// compute the pixel coordinate in the image
442
- const [ px , py ] = this . canvasToIndex ( canvasPos ) ;
443
- // convert pixel coordinate to world coordinate
444
- const { origin, spacing, direction } = this . getImageData ( ) ;
445
-
446
- const worldPos = vec3 . fromValues ( 0 , 0 , 0 ) ;
447
-
448
- // Calculate size of spacing vector in normal direction
449
- const iVector = direction . slice ( 0 , 3 ) as Point3 ;
450
- const jVector = direction . slice ( 3 , 6 ) as Point3 ;
451
-
452
- // Calculate the world coordinate of the pixel
453
- vec3 . scaleAndAdd ( worldPos , origin , iVector , px * spacing [ 0 ] ) ;
454
- vec3 . scaleAndAdd ( worldPos , worldPos , jVector , py * spacing [ 1 ] ) ;
455
-
456
- return [ worldPos [ 0 ] , worldPos [ 1 ] , worldPos [ 2 ] ] as Point3 ;
516
+ const indexPoint = this . canvasToIndex ( canvasPos ) ;
517
+ indexPoint [ 1 ] = - indexPoint [ 1 ] ; // flip y axis to match canvas coordinates
518
+ return this . indexToWorld ( indexPoint ) ;
457
519
} ;
458
520
459
521
/**
@@ -466,20 +528,11 @@ class WSIViewport extends Viewport {
466
528
if ( ! this . metadata ) {
467
529
return ;
468
530
}
469
- const { spacing, direction, origin } = this . metadata ;
470
-
471
- const iVector = direction . slice ( 0 , 3 ) as Point3 ;
472
- const jVector = direction . slice ( 3 , 6 ) as Point3 ;
473
-
474
- const diff = vec3 . subtract ( [ 0 , 0 , 0 ] , worldPos , origin ) ;
475
-
476
- const indexPoint : Point2 = [
477
- vec3 . dot ( diff , iVector ) / spacing [ 0 ] ,
478
- vec3 . dot ( diff , jVector ) / spacing [ 1 ] ,
479
- ] ;
531
+ const indexPoint = this . worldToIndex ( worldPos ) ;
532
+ indexPoint [ 1 ] = - indexPoint [ 1 ] ; // flip y axis to match canvas coordinates
480
533
481
534
// pixel to canvas
482
- const canvasPoint = this . indexToCanvas ( indexPoint ) ;
535
+ const canvasPoint = this . indexToCanvas ( [ indexPoint [ 0 ] , indexPoint [ 1 ] , 0 ] ) ;
483
536
return canvasPoint ;
484
537
} ;
485
538
@@ -514,6 +567,7 @@ class WSIViewport extends Viewport {
514
567
this . microscopyElement . innerText = 'Loading' ;
515
568
this . imageIds = imageIds ;
516
569
const DicomMicroscopyViewer = await WSIViewport . getDicomMicroscopyViewer ( ) ;
570
+ WSIUtilFunctions ||= DicomMicroscopyViewer . utils ;
517
571
this . frameOfReferenceUID = null ;
518
572
519
573
const metadataDicomweb = this . imageIds . map ( ( imageId ) => {
@@ -608,18 +662,19 @@ class WSIViewport extends Viewport {
608
662
609
663
public getRotation = ( ) => 0 ;
610
664
611
- protected canvasToIndex = ( canvasPos : Point2 ) : Point2 => {
665
+ protected canvasToIndex = ( canvasPos : Point2 ) : Point3 => {
612
666
const transform = this . getTransform ( ) ;
613
667
transform . invert ( ) ;
614
- return transform . transformPoint (
668
+ const indexPoint = transform . transformPoint (
615
669
canvasPos . map ( ( it ) => it * devicePixelRatio ) as Point2
616
670
) ;
671
+ return [ indexPoint [ 0 ] , indexPoint [ 1 ] , 0 ] as Point3 ;
617
672
} ;
618
673
619
- protected indexToCanvas = ( indexPos : Point2 ) : Point2 => {
674
+ protected indexToCanvas = ( indexPos : Point3 ) : Point2 => {
620
675
const transform = this . getTransform ( ) ;
621
676
return transform
622
- . transformPoint ( indexPos )
677
+ . transformPoint ( [ indexPos [ 0 ] , indexPos [ 1 ] ] )
623
678
. map ( ( it ) => it / devicePixelRatio ) as Point2 ;
624
679
} ;
625
680
0 commit comments