@@ -37,6 +37,7 @@ import { useLutPresentationStore } from '../../stores/useLutPresentationStore';
3737import { usePositionPresentationStore } from '../../stores/usePositionPresentationStore' ;
3838import { useSynchronizersStore } from '../../stores/useSynchronizersStore' ;
3939import { useSegmentationPresentationStore } from '../../stores/useSegmentationPresentationStore' ;
40+ import getClosestOrientationFromIOP from '../../utils/isReferenceViewable' ;
4041
4142const EVENTS = {
4243 VIEWPORT_DATA_CHANGED : 'event::cornerstoneViewportService:viewportDataChanged' ,
@@ -634,67 +635,118 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
634635
635636 /**
636637 * Figures out which viewport to update when the viewport type needs to change.
637- * This may not be the active viewport if there is already a viewport showing
638- * the display set, but in the wrong orientation.
639- *
640- * The viewport will need to update the viewport type and/or display set to
641- * display the resulting data.
642- *
643- * The first choice will be a viewport already showing the correct display set,
644- * but showing it as a stack.
645- *
646- * Second choice is to see if there is a viewport already showing the right
647- * orientation for the image, but the wrong display set. This fixes the
648- * case where the user is in MPR and a viewport other than active should be
649- * the one to change to display the iamge.
650- *
651- * Final choice is to use the provide activeViewportId. This will cover
652- * changes to/from video and wsi viewports and other cases where no
653- * viewport is really even close to being able to display the measurement.
638+ * Orchestrates the search strategies in order of preference.
654639 */
655640 public findUpdateableViewportConfiguration ( activeViewportId : string , measurement ) {
656641 const { metadata, displaySetInstanceUID } = measurement ;
657- const { volumeId, referencedImageId } = metadata ;
658- const { displaySetService, viewportGridService } = this . servicesManager . services ;
642+ const { displaySetService } = this . servicesManager . services ;
659643 const displaySet = displaySetService . getDisplaySetByUID ( displaySetInstanceUID ) ;
660644
645+ // 1. Determine the target Viewport Type (Stack vs Volume)
646+ const viewportType = this . determineTargetViewportType ( displaySet , metadata ) ;
647+
648+ // 2. Strategy: Find viewport already showing this volume
649+ const volumeMatch = this . findViewportShowingVolume (
650+ metadata ,
651+ displaySetInstanceUID ,
652+ viewportType
653+ ) ;
654+ if ( volumeMatch ) {
655+ return volumeMatch ;
656+ }
657+
658+ // 3. Strategy: Find viewport with compatible orientation (even if different display set)
659+ const compatibleMatch = this . findViewportConvertibleToVolume (
660+ metadata ,
661+ displaySetInstanceUID ,
662+ viewportType
663+ ) ;
664+ if ( compatibleMatch ) {
665+ return compatibleMatch ;
666+ }
667+
668+ // 4. Strategy: Find viewport with matching orientation via IOP
669+ const orientationMatch = this . findViewportWithMatchingOrientation (
670+ displaySetInstanceUID ,
671+ viewportType
672+ ) ;
673+ if ( orientationMatch ) {
674+ return orientationMatch ;
675+ }
676+
677+ // 5. Fallback: Use the active viewport
678+ return {
679+ viewportId : activeViewportId ,
680+ displaySetInstanceUID,
681+ viewportOptions : { viewportType } ,
682+ } ;
683+ }
684+
685+ /**
686+ * Determines if the viewport should be what is specified in
687+ * the viewportType of the display set, or stack if the display
688+ * set isn't reconstructable and there is a referenced image id, otherwise
689+ * volume.
690+ *
691+ * Expect there to be more rules in the future for different types of annotations/settings
692+ * such as 3d annotations.
693+ */
694+ public determineTargetViewportType ( displaySet , metadata ) : string {
661695 let { viewportType } = displaySet ;
696+
662697 if ( ! viewportType ) {
663- if ( referencedImageId && ! displaySet . isReconstructable ) {
698+ if ( metadata . referencedImageId && ! displaySet . isReconstructable ) {
664699 viewportType = csEnums . ViewportType . STACK ;
665- } else if ( volumeId ) {
700+ } else if ( metadata . volumeId ) {
666701 viewportType = 'volume' ;
667702 }
668703 }
704+ return viewportType ;
705+ }
669706
670- // Find viewports that could be updated to be volumes to show this view
671- // That prefers a viewport already showing the right display set.
672- if ( volumeId ) {
673- for ( const id of this . viewportsById . keys ( ) ) {
674- const viewport = this . getCornerstoneViewport ( id ) ;
675- if ( viewport ?. isReferenceViewable ( metadata , { asVolume : true , withNavigation : true } ) ) {
676- return {
677- viewportId : id ,
678- displaySetInstanceUID,
679- viewportOptions : { viewportType } ,
680- } ;
681- }
707+ /**
708+ * Find viewports that could be updated to be volumes to show this view.
709+ * Prefers a viewport already showing the right display set.
710+ */
711+ public findViewportShowingVolume ( metadata , displaySetInstanceUID , viewportType ) {
712+ if ( ! metadata . volumeId ) {
713+ return null ;
714+ }
715+
716+ for ( const id of this . viewportsById . keys ( ) ) {
717+ const viewport = this . getCornerstoneViewport ( id ) ;
718+ if ( viewport ?. isReferenceViewable ( metadata , { asVolume : true , withNavigation : true } ) ) {
719+ return {
720+ viewportId : id ,
721+ displaySetInstanceUID,
722+ viewportOptions : { viewportType } ,
723+ } ;
682724 }
683725 }
726+ return null ;
727+ }
684728
685- // Find a viewport in the correct orientation showing a different display set
686- // which could be used to display the annotation.
729+ /**
730+ * Find a viewport that could be converted to a volume to show this annotation,
731+ * already showing the right display set.
732+ */
733+ public findViewportConvertibleToVolume ( metadata , displaySetInstanceUID , viewportType ) {
734+ const { viewportGridService } = this . servicesManager . services ;
687735 const altMetadata = { ...metadata , volumeId : null , referencedImageId : null } ;
736+
688737 for ( const id of this . viewportsById . keys ( ) ) {
689738 const viewport = this . getCornerstoneViewport ( id ) ;
690739 const viewportDisplaySetUID = viewportGridService . getDisplaySetsUIDsForViewport ( id ) ?. [ 0 ] ;
740+
691741 if ( ! viewportDisplaySetUID || ! viewport ) {
692742 continue ;
693743 }
694- if ( volumeId ) {
744+
745+ if ( metadata . volumeId ) {
695746 altMetadata . volumeId = viewportDisplaySetUID ;
696747 }
697748 altMetadata . FrameOfReferenceUID = this . _getFrameOfReferenceUID ( viewportDisplaySetUID ) ;
749+
698750 if ( viewport . isReferenceViewable ( altMetadata , { asVolume : true , withNavigation : true } ) ) {
699751 return {
700752 viewportId : id ,
@@ -703,13 +755,22 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
703755 } ;
704756 }
705757 }
758+ return null ;
759+ }
706760
707- // Just display in the active viewport
708- return {
709- viewportId : activeViewportId ,
710- displaySetInstanceUID,
711- viewportOptions : { viewportType } ,
712- } ;
761+ /**
762+ * Find a viewport with the closest orientation but on a different display set.
763+ */
764+ public findViewportWithMatchingOrientation ( metadata , displaySetInstanceUID , viewportType ) {
765+ const viewportAlignmentData = this . getViewportAlignmentData ( metadata ) ;
766+ if ( viewportAlignmentData ?. length ) {
767+ return {
768+ ...viewportAlignmentData [ 0 ] ,
769+ displaySetInstanceUID,
770+ viewportOptions : { viewportType } ,
771+ } ;
772+ }
773+ return null ;
713774 }
714775
715776 /**
0 commit comments