From bd62d56a37c4d9f73e4266af6b2e59a20329a27d Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Fri, 1 Apr 2016 13:29:09 -0400 Subject: [PATCH] Fix Overlays.getBounds with rotation. --- src/overlay.js | 49 ++++---- test/modules/overlays.js | 239 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 268 insertions(+), 20 deletions(-) diff --git a/src/overlay.js b/src/overlay.js index fa5ddefa..97b5b80a 100644 --- a/src/overlay.js +++ b/src/overlay.js @@ -131,7 +131,6 @@ /** @lends OpenSeadragon.Overlay.prototype */ $.Overlay.prototype = { - // private _init: function(options) { this.location = options.location; @@ -157,7 +156,6 @@ this.placement = $.Placement.TOP_LEFT; } }, - /** * Internal function to adjust the position of an overlay * depending on it size and placement. @@ -181,7 +179,6 @@ position.y -= size.y; } }, - /** * @function */ @@ -225,7 +222,6 @@ style[transformProp] = ""; } }, - /** * @function * @param {Element} container @@ -283,7 +279,6 @@ } } }, - // private _getOverlayPositionAndSize: function(viewport) { var position = viewport.pixelFromPoint(this.location, true); @@ -312,7 +307,6 @@ rotate: rotate }; }, - // private _getSizeInPixels: function(viewport) { var width = this.size.x; @@ -339,26 +333,29 @@ } return new $.Point(width, height); }, - // private _getBoundingBox: function(rect, degrees) { - var refPoint = new $.Point(rect.x, rect.y); + var refPoint = this._getPlacementPoint(rect); + return rect.rotate(degrees, refPoint).getBoundingBox(); + }, + // private + _getPlacementPoint: function(rect) { + var result = new $.Point(rect.x, rect.y); var properties = $.Placement.properties[this.placement]; if (properties) { if (properties.isHorizontallyCentered) { - refPoint.x += rect.width / 2; + result.x += rect.width / 2; } else if (properties.isRight) { - refPoint.x += rect.width; + result.x += rect.width; } if (properties.isVerticallyCentered) { - refPoint.y += rect.height / 2; + result.y += rect.height / 2; } else if (properties.isBottom) { - refPoint.y += rect.height; + result.y += rect.height; } } - return rect.rotate(degrees, refPoint).getBoundingBox(); + return result; }, - // private _getTransformOrigin: function() { var result = ""; @@ -378,7 +375,6 @@ } return result; }, - /** * Changes the overlay settings. * @function @@ -403,7 +399,6 @@ rotationMode: options.rotationMode || this.rotationMode }); }, - /** * Returns the current bounds of the overlay in viewport coordinates * @function @@ -411,11 +406,11 @@ * @returns {OpenSeadragon.Rect} overlay bounds */ getBounds: function(viewport) { + $.console.assert(!viewport, 'Calling Overlay.getBounds withouth ' + + 'specifying a viewport is deprecated.'); var width = this.width; var height = this.height; if (width === null || height === null) { - $.console.assert(!viewport, 'The viewport must be specified to' + - ' get the bounds of a not entirely scaling overlay'); var size = viewport.deltaPointsFromPixelsNoRotate(this.size, true); if (width === null) { width = size.x; @@ -426,7 +421,23 @@ } var location = this.location.clone(); this.adjust(location, new $.Point(width, height)); - return new $.Rect(location.x, location.y, width, height); + return this._adjustBoundsForRotation( + viewport, new $.Rect(location.x, location.y, width, height)); + }, + + _adjustBoundsForRotation: function(viewport, bounds) { + if (!viewport || + viewport.degrees === 0 || + this.rotationMode === $.OverlayRotationMode.EXACT) { + return bounds; + } + // If overlay not fully scalable, BOUNDING_BOX falls back to EXACT + if (this.rotationMode === $.OverlayRotationMode.BOUNDING_BOX && + (this.width === null || this.height === null)) { + return bounds; + } + return bounds.rotate(-viewport.degrees, + this._getPlacementPoint(bounds)); } }; diff --git a/test/modules/overlays.js b/test/modules/overlays.js index 496c4f89..0183d61f 100644 --- a/test/modules/overlays.js +++ b/test/modules/overlays.js @@ -658,6 +658,7 @@ }); }); + // ---------- asyncTest('Overlay.getBounds', function() { viewer = OpenSeadragon({ id: 'example-overlays', @@ -710,7 +711,7 @@ viewer._drawOverlays(); var actualBounds = viewer.getOverlayById("fully-scaled-overlay") - .getBounds(); + .getBounds(viewer.viewport); var expectedBounds = new OpenSeadragon.Rect(0, 0, 1, 1); ok(expectedBounds.equals(actualBounds), "The fully scaled overlay should have bounds " + @@ -745,4 +746,240 @@ }); }); + // ---------- + asyncTest('Fully scaled overlay rotation mode NO_ROTATION', function() { + viewer = OpenSeadragon({ + id: 'example-overlays', + prefixUrl: '/build/openseadragon/images/', + tileSources: '/test/data/testpattern.dzi', + springStiffness: 100, // Faster animation = faster tests + degrees: 45, + overlays: [{ + id: "fully-scaled-overlay", + x: 1, + y: 1, + width: 1, + height: 1, + placement: OpenSeadragon.Placement.BOTTOM_RIGHT, + rotationMode: OpenSeadragon.OverlayRotationMode.NO_ROTATION + }] + }); + + viewer.addOnceHandler('open', function() { + var viewport = viewer.viewport; + + var $overlay = $("#fully-scaled-overlay"); + var expectedSize = viewport.deltaPixelsFromPointsNoRotate( + new OpenSeadragon.Point(1, 1)); + var expectedPosition = viewport.viewportToViewerElementCoordinates( + new OpenSeadragon.Point(1, 1)) + .minus(expectedSize); + var actualPosition = $overlay.position(); + Util.assessNumericValue(actualPosition.left, expectedPosition.x, epsilon, + "Scaled overlay position.x should adjust to rotation."); + Util.assessNumericValue(actualPosition.top, expectedPosition.y, epsilon, + "Scaled overlay position.y should adjust to rotation."); + + var actualWidth = $overlay.width(); + var actualHeight = $overlay.height(); + Util.assessNumericValue(actualWidth, expectedSize.x, epsilon, + "Scaled overlay width should not adjust to rotation."); + Util.assessNumericValue(actualHeight, expectedSize.y, epsilon, + "Scaled overlay height should not adjust to rotation."); + + var actualBounds = viewer.getOverlayById("fully-scaled-overlay") + .getBounds(viewport); + var expectedBounds = new OpenSeadragon.Rect(0, 0, 1, 1) + .rotate(-45, new OpenSeadragon.Point(1, 1)); + ok(expectedBounds.equals(actualBounds), + "The fully scaled overlay should have bounds " + + expectedBounds.toString() + " but found " + actualBounds); + + start(); + }); + }); + + // ---------- + asyncTest('Horizontally scaled overlay rotation mode NO_ROTATION', function() { + viewer = OpenSeadragon({ + id: 'example-overlays', + prefixUrl: '/build/openseadragon/images/', + tileSources: '/test/data/testpattern.dzi', + springStiffness: 100, // Faster animation = faster tests + degrees: 45, + overlays: [{ + id: "horizontally-scaled-overlay", + x: 0.5, + y: 0.5, + width: 1, + placement: OpenSeadragon.Placement.CENTER, + rotationMode: OpenSeadragon.OverlayRotationMode.NO_ROTATION + }] + }); + + viewer.addOnceHandler('open', function() { + var $overlay = $("#horizontally-scaled-overlay"); + var notScaledWidth = 100; + var notScaledHeight = 100; + $overlay.get(0).style.height = notScaledHeight + "px"; + + var viewport = viewer.viewport; + var notScaledSize = viewport.deltaPointsFromPixelsNoRotate( + new OpenSeadragon.Point(notScaledWidth, notScaledHeight)); + + // Force refresh to takes new dimensions into account. + viewer._drawOverlays(); + + var expectedWidth = viewport.deltaPixelsFromPointsNoRotate( + new OpenSeadragon.Point(1, 1)).x; + var expectedPosition = viewport.viewportToViewerElementCoordinates( + new OpenSeadragon.Point(0.5, 0.5)) + .minus(new OpenSeadragon.Point(expectedWidth / 2, notScaledHeight / 2)); + var actualPosition = $overlay.position(); + Util.assessNumericValue(actualPosition.left, expectedPosition.x, epsilon, + "Horizontally scaled overlay position.x should adjust to rotation."); + Util.assessNumericValue(actualPosition.top, expectedPosition.y, epsilon, + "Horizontally scaled overlay position.y should adjust to rotation."); + + var actualWidth = $overlay.width(); + var actualHeight = $overlay.height(); + Util.assessNumericValue(actualWidth, expectedWidth, epsilon, + "Horizontally scaled overlay width should not adjust to rotation."); + Util.assessNumericValue(actualHeight, notScaledHeight, epsilon, + "Horizontally scaled overlay height should not adjust to rotation."); + + var actualBounds = viewer.getOverlayById("horizontally-scaled-overlay") + .getBounds(viewport); + var expectedBounds = new OpenSeadragon.Rect( + 0, 0.5 - notScaledSize.y / 2, 1, notScaledSize.y) + .rotate(-45, new OpenSeadragon.Point(0.5, 0.5)); + ok(expectedBounds.equals(actualBounds), + "The horizontally scaled overlay should have bounds " + + expectedBounds.toString() + " but found " + actualBounds); + + start(); + }); + }); + + // ---------- + asyncTest('Vertically scaled overlay rotation mode NO_ROTATION', function() { + viewer = OpenSeadragon({ + id: 'example-overlays', + prefixUrl: '/build/openseadragon/images/', + tileSources: '/test/data/testpattern.dzi', + springStiffness: 100, // Faster animation = faster tests + degrees: 45, + overlays: [{ + id: "vertically-scaled-overlay", + x: 0, + y: 0.5, + height: 1, + placement: OpenSeadragon.Placement.LEFT, + rotationMode: OpenSeadragon.OverlayRotationMode.NO_ROTATION + }] + }); + + viewer.addOnceHandler('open', function() { + var $overlay = $("#vertically-scaled-overlay"); + var notScaledWidth = 100; + var notScaledHeight = 100; + $overlay.get(0).style.width = notScaledWidth + "px"; + + var viewport = viewer.viewport; + var notScaledSize = viewport.deltaPointsFromPixelsNoRotate( + new OpenSeadragon.Point(notScaledWidth, notScaledHeight)); + + // Force refresh to takes new dimensions into account. + viewer._drawOverlays(); + + var expectedHeight = viewport.deltaPixelsFromPointsNoRotate( + new OpenSeadragon.Point(1, 1)).y; + var expectedPosition = viewport.viewportToViewerElementCoordinates( + new OpenSeadragon.Point(0, 0.5)) + .minus(new OpenSeadragon.Point(0, expectedHeight / 2)); + var actualPosition = $overlay.position(); + Util.assessNumericValue(actualPosition.left, expectedPosition.x, epsilon, + "Vertically scaled overlay position.x should adjust to rotation."); + Util.assessNumericValue(actualPosition.top, expectedPosition.y, epsilon, + "Vertically scaled overlay position.y should adjust to rotation."); + + var actualWidth = $overlay.width(); + var actualHeight = $overlay.height(); + Util.assessNumericValue(actualWidth, notScaledWidth, epsilon, + "Vertically scaled overlay width should not adjust to rotation."); + Util.assessNumericValue(actualHeight, expectedHeight, epsilon, + "Vertically scaled overlay height should not adjust to rotation."); + + var actualBounds = viewer.getOverlayById("vertically-scaled-overlay") + .getBounds(viewport); + var expectedBounds = new OpenSeadragon.Rect( + 0, 0, notScaledSize.x, 1) + .rotate(-45, new OpenSeadragon.Point(0, 0.5)); + ok(expectedBounds.equals(actualBounds), + "The vertically scaled overlay should have bounds " + + expectedBounds.toString() + " but found " + actualBounds); + + start(); + }); + }); + + // ---------- + asyncTest('Not scaled overlay rotation mode NO_ROTATION', function() { + viewer = OpenSeadragon({ + id: 'example-overlays', + prefixUrl: '/build/openseadragon/images/', + tileSources: '/test/data/testpattern.dzi', + springStiffness: 100, // Faster animation = faster tests + degrees: 45, + overlays: [{ + id: "not-scaled-overlay", + x: 1, + y: 0, + placement: OpenSeadragon.Placement.TOP_RIGHT, + rotationMode: OpenSeadragon.OverlayRotationMode.NO_ROTATION + }] + }); + + viewer.addOnceHandler('open', function() { + var $overlay = $("#not-scaled-overlay"); + var notScaledWidth = 100; + var notScaledHeight = 100; + $overlay.get(0).style.width = notScaledWidth + "px"; + $overlay.get(0).style.height = notScaledHeight + "px"; + + var viewport = viewer.viewport; + var notScaledSize = viewport.deltaPointsFromPixelsNoRotate( + new OpenSeadragon.Point(notScaledWidth, notScaledHeight)); + + // Force refresh to takes new dimensions into account. + viewer._drawOverlays(); + + var expectedPosition = viewport.viewportToViewerElementCoordinates( + new OpenSeadragon.Point(1, 0)) + .minus(new OpenSeadragon.Point(notScaledWidth, 0)); + var actualPosition = $overlay.position(); + Util.assessNumericValue(actualPosition.left, expectedPosition.x, epsilon, + "Not scaled overlay position.x should adjust to rotation."); + Util.assessNumericValue(actualPosition.top, expectedPosition.y, epsilon, + "Not scaled overlay position.y should adjust to rotation."); + + var actualWidth = $overlay.width(); + var actualHeight = $overlay.height(); + Util.assessNumericValue(actualWidth, notScaledWidth, epsilon, + "Not scaled overlay width should not adjust to rotation."); + Util.assessNumericValue(actualHeight, notScaledHeight, epsilon, + "Not scaled overlay height should not adjust to rotation."); + + var actualBounds = viewer.getOverlayById("not-scaled-overlay") + .getBounds(viewport); + var expectedBounds = new OpenSeadragon.Rect( + 1 - notScaledSize.x, 0, notScaledSize.x, notScaledSize.y) + .rotate(-45, new OpenSeadragon.Point(1, 0)); + ok(expectedBounds.equals(actualBounds), + "Not scaled overlay should have bounds " + + expectedBounds.toString() + " but found " + actualBounds); + + start(); + }); + }); })();