From 3e79ba7d41d143ad7d65c30b16900cfae86a8212 Mon Sep 17 00:00:00 2001 From: naglepuff Date: Thu, 20 Jan 2022 14:29:15 -0500 Subject: [PATCH 1/4] Make image overlays respond to global opacity When rendering image overlay element annotations, use the global opacity to help set the initial opactity. Also make image overlay layers respond to changes in the global opacity. --- .../web_client/views/imageViewerWidget/geojs.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/girder_annotation/girder_large_image_annotation/web_client/views/imageViewerWidget/geojs.js b/girder_annotation/girder_large_image_annotation/web_client/views/imageViewerWidget/geojs.js index 7932a581b..14cf97c88 100644 --- a/girder_annotation/girder_large_image_annotation/web_client/views/imageViewerWidget/geojs.js +++ b/girder_annotation/girder_large_image_annotation/web_client/views/imageViewerWidget/geojs.js @@ -140,6 +140,7 @@ var GeojsImageViewerWidgetExtension = function (viewer) { params.layer.renderer = 'canvas'; } params.layer.opacity = overlay.opacity || 1; + params.layer.opacity *= this.featureLayer.opacity(); if (this.levels !== overlayImageMetadata.levels) { const levelDifference = this.levels - overlayImageMetadata.levels; @@ -660,6 +661,14 @@ var GeojsImageViewerWidgetExtension = function (viewer) { feature.layer().opacity(opacity); } })); + _.each(this._annotations, (annotation) => { + _.each(annotation.overlays, (overlay) => { + const overlayLayer = this.viewer.layers().find((layer) => layer.id() === overlay.id); + if (overlayLayer) { + overlayLayer.opacity(opacity * overlay.opacity); + } + }); + }); return this; }, From fc4641fc425cd428b0b4cd745a896e7ed94f8ec6 Mon Sep 17 00:00:00 2001 From: naglepuff Date: Thu, 20 Jan 2022 16:38:08 -0500 Subject: [PATCH 2/4] Make overlays respect highlighted annotations --- .../web_client/views/imageViewerWidget/geojs.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/girder_annotation/girder_large_image_annotation/web_client/views/imageViewerWidget/geojs.js b/girder_annotation/girder_large_image_annotation/web_client/views/imageViewerWidget/geojs.js index 14cf97c88..5867c1cf7 100644 --- a/girder_annotation/girder_large_image_annotation/web_client/views/imageViewerWidget/geojs.js +++ b/girder_annotation/girder_large_image_annotation/web_client/views/imageViewerWidget/geojs.js @@ -140,7 +140,7 @@ var GeojsImageViewerWidgetExtension = function (viewer) { params.layer.renderer = 'canvas'; } params.layer.opacity = overlay.opacity || 1; - params.layer.opacity *= this.featureLayer.opacity(); + params.layer.opacity *= this._globalAnnotationOpacity; if (this.levels !== overlayImageMetadata.levels) { const levelDifference = this.levels - overlayImageMetadata.levels; @@ -501,6 +501,18 @@ var GeojsImageViewerWidgetExtension = function (viewer) { feature.updateStyleFromArray('strokeOpacity', strokeOpacityArray); feature._lastFeatureProp = prop; }); + // Also modify opacity of image overlay layers + const overlays = this._annotations[annotationId].overlays || null; + if (overlays) { + _.each(overlays, (overlay) => { + const overlayLayer = this.viewer.layers().find((layer) => layer.id() === overlay.id); + let newOpacity = (overlay.opacity || 1) * this._globalAnnotationOpacity; + if (this._highlightAnnotation && annotationId !== this._highlightAnnotation) { + newOpacity = newOpacity * 0.25; + } + overlayLayer.opacity(newOpacity); + }); + } }, /** @@ -665,7 +677,8 @@ var GeojsImageViewerWidgetExtension = function (viewer) { _.each(annotation.overlays, (overlay) => { const overlayLayer = this.viewer.layers().find((layer) => layer.id() === overlay.id); if (overlayLayer) { - overlayLayer.opacity(opacity * overlay.opacity); + const overlayOpacity = overlay.opacity || 1; + overlayLayer.opacity(opacity * overlayOpacity); } }); }); From b37de2f1e096a7f7f46772f71314812274d76b3a Mon Sep 17 00:00:00 2001 From: naglepuff Date: Fri, 21 Jan 2022 12:51:46 -0500 Subject: [PATCH 3/4] Allow `additionalProperties` for image overlays This supports deleting individual elements from an annotation containing overlay elements (e.g. from the HistomicsUI edit annotation widget. --- .../girder_large_image_annotation/models/annotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/girder_annotation/girder_large_image_annotation/models/annotation.py b/girder_annotation/girder_large_image_annotation/models/annotation.py index b0f158ea9..eb8f8df00 100644 --- a/girder_annotation/girder_large_image_annotation/models/annotation.py +++ b/girder_annotation/girder_large_image_annotation/models/annotation.py @@ -508,7 +508,7 @@ class AnnotationSchema: 'group': groupSchema, }, 'required': ['girderId', 'type'], - 'additionalProperties': False, + 'additionalProperties': True, 'description': 'An image to overlay onto another like an ' 'annotation.' } From 6bf913fb133fd47a65e85706d817ceaa7bf065ea Mon Sep 17 00:00:00 2001 From: naglepuff Date: Fri, 21 Jan 2022 14:46:59 -0500 Subject: [PATCH 4/4] Prevent orphaned image overlay layers on redraw If re-drawing an annotation with image overlay elements, compare the updated results of `annotation.overlays()` to the overlays stored in `this._annotations`. This captures changes to the `annotation._elements` collection between the initial render of the annotation and the redraw. --- .../views/imageViewerWidget/geojs.js | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/girder_annotation/girder_large_image_annotation/web_client/views/imageViewerWidget/geojs.js b/girder_annotation/girder_large_image_annotation/web_client/views/imageViewerWidget/geojs.js index 5867c1cf7..b5a2ad9f3 100644 --- a/girder_annotation/girder_large_image_annotation/web_client/views/imageViewerWidget/geojs.js +++ b/girder_annotation/girder_large_image_annotation/web_client/views/imageViewerWidget/geojs.js @@ -190,6 +190,7 @@ var GeojsImageViewerWidgetExtension = function (viewer) { var geo = window.geo; options = _.defaults(options || {}, {fetch: true}); var geojson = annotation.geojson(); + const overlays = annotation.overlays() || []; var present = _.has(this._annotations, annotation.id); var centroidFeature; if (present) { @@ -204,8 +205,20 @@ var GeojsImageViewerWidgetExtension = function (viewer) { centroidFeature = feature; } }); + if (this._annotations[annotation.id].overlays) { + // Ensure that overlay elements that have been deleted are not rendered on a re-draw + _.each(this._annotations[annotation.id].overlays, (overlay) => { + const oldOverlayIds = this._annotations[annotation.id].overlays.map((overlay) => overlay.id); + const updatedOverlayIds = overlays.map((overlay) => overlay.id); + _.each(oldOverlayIds, (id) => { + if (!updatedOverlayIds.includes(id)) { + const overlayLayer = this.viewer.layers().find((layer) => layer.id() === id); + this.viewer.deleteLayer(overlayLayer); + } + }); + }); + } } - const overlays = annotation.overlays() || []; this._annotations[annotation.id] = { features: centroidFeature ? [centroidFeature] : [], options: options, @@ -506,11 +519,13 @@ var GeojsImageViewerWidgetExtension = function (viewer) { if (overlays) { _.each(overlays, (overlay) => { const overlayLayer = this.viewer.layers().find((layer) => layer.id() === overlay.id); - let newOpacity = (overlay.opacity || 1) * this._globalAnnotationOpacity; - if (this._highlightAnnotation && annotationId !== this._highlightAnnotation) { - newOpacity = newOpacity * 0.25; + if (overlayLayer) { + let newOpacity = (overlay.opacity || 1) * this._globalAnnotationOpacity; + if (this._highlightAnnotation && annotationId !== this._highlightAnnotation) { + newOpacity = newOpacity * 0.25; + } + overlayLayer.opacity(newOpacity); } - overlayLayer.opacity(newOpacity); }); } },