From 3106d8f85b49a2e74e00bbe614b0a47428f79fb9 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Sat, 23 Apr 2016 08:29:32 -0400 Subject: [PATCH 1/7] Fix viewport.fitBounds tests. --- test/modules/viewport.js | 51 +++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/test/modules/viewport.js b/test/modules/viewport.js index 0b3ca020..e40f82be 100644 --- a/test/modules/viewport.js +++ b/test/modules/viewport.js @@ -405,26 +405,7 @@ viewer.open(DZI_PATH); }); - asyncTest('fitBounds', function(){ - var openHandler = function(event) { - viewer.removeHandler('open', openHandler); - var viewport = viewer.viewport; - - for(var i = 0; i < testRects.length; i++){ - var rect = testRects[i].times(viewport.getContainerSize()); - viewport.fitBounds(rect, true); - propEqual( - viewport.getBounds(), - rect, - "Fit bounds correctly." - ); - } - start(); - }; - viewer.addHandler('open', openHandler); - viewer.open(DZI_PATH); - }); - + // Fit bounds tests var testRectsFitBounds = [ new OpenSeadragon.Rect(0, -0.75, 0.5, 1), new OpenSeadragon.Rect(0.5, 0, 0.5, 0.8), @@ -433,12 +414,39 @@ ]; var expectedRectsFitBounds = [ + new OpenSeadragon.Rect(-0.25, -0.75, 1, 1), + new OpenSeadragon.Rect(0.35, 0, 0.8, 0.8), + new OpenSeadragon.Rect(0.75, 0.75, 0.5, 0.5), + new OpenSeadragon.Rect(-0.3, -0.3, 0.5, 0.5) + ]; + + var expectedRectsFitBoundsWithConstraints = [ new OpenSeadragon.Rect(-0.25, -0.5, 1, 1), new OpenSeadragon.Rect(0.35, 0, 0.8, 0.8), new OpenSeadragon.Rect(0.75, 0.75, 0.5, 0.5), new OpenSeadragon.Rect(-0.25, -0.25, 0.5, 0.5) ]; + asyncTest('fitBounds', function(){ + var openHandler = function(event) { + viewer.removeHandler('open', openHandler); + var viewport = viewer.viewport; + + for(var i = 0; i < testRectsFitBounds.length; i++){ + var rect = testRectsFitBounds[i]; + viewport.fitBounds(rect, true); + propEqual( + viewport.getBounds(), + expectedRectsFitBounds[i], + "Fit bounds correctly." + ); + } + start(); + }; + viewer.addHandler('open', openHandler); + viewer.open(DZI_PATH); + }); + asyncTest('fitBoundsWithConstraints', function(){ var openHandler = function(event) { viewer.removeHandler('open', openHandler); @@ -450,7 +458,7 @@ viewport.fitBoundsWithConstraints(rect, true); propEqual( viewport.getBounds(), - expectedRectsFitBounds[i], + expectedRectsFitBoundsWithConstraints[i], "Fit bounds correctly." ); } @@ -491,6 +499,7 @@ viewer.addHandler('open', openHandler); viewer.open(WIDE_PATH); }); + // End fitBounds tests. asyncTest('panBy', function(){ var openHandler = function(event) { From 521e020b9a1e927ab4e62d9a4fc46bb9f7789df3 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Wed, 4 May 2016 22:26:33 -0400 Subject: [PATCH 2/7] Viewport getBounds and fitBounds methods now take rotation into account. Fix #924 --- src/navigator.js | 4 +- src/tiledimage.js | 13 +--- src/viewer.js | 10 +-- src/viewport.js | 131 +++++++++++++++++++++++++-------------- test/modules/viewport.js | 79 +++++++++++++++++++++-- 5 files changed, 169 insertions(+), 68 deletions(-) diff --git a/src/navigator.js b/src/navigator.js index 4b3a98d2..e4b1a6c0 100644 --- a/src/navigator.js +++ b/src/navigator.js @@ -306,8 +306,8 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /* this.updateSize(); } - if( viewport && this.viewport ) { - bounds = viewport.getBounds( true ); + if (viewport && this.viewport) { + bounds = viewport.getBoundsNoRotate(true); topleft = this.viewport.pixelFromPointNoRotate(bounds.getTopLeft(), false); bottomright = this.viewport.pixelFromPointNoRotate(bounds.getBottomRight(), false) .minus( this.totalBorderWidths ); diff --git a/src/tiledimage.js b/src/tiledimage.js index 9d4db782..8d4b64b1 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -795,6 +795,7 @@ function updateViewport( tiledImage ) { levelOpacity, levelVisibility; + viewportBounds = viewportBounds.getBoundingBox(); viewportBounds.x -= tiledImage._xSpring.current.value; viewportBounds.y -= tiledImage._ySpring.current.value; @@ -804,18 +805,6 @@ function updateViewport( tiledImage ) { tile.beingDrawn = false; } - //Change bounds for rotation - if (degrees === 90 || degrees === 270) { - viewportBounds = viewportBounds.rotate( degrees ); - } else if (degrees !== 0 && degrees !== 180) { - // This is just an approximation. - var orthBounds = viewportBounds.rotate(90); - viewportBounds.x -= orthBounds.width / 2; - viewportBounds.y -= orthBounds.height / 2; - viewportBounds.width += orthBounds.width; - viewportBounds.height += orthBounds.height; - } - var viewportTL = viewportBounds.getTopLeft(); var viewportBR = viewportBounds.getBottomRight(); diff --git a/src/viewer.js b/src/viewer.js index da4b51ee..c9f6a98b 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -2981,11 +2981,13 @@ function updateOnce( viewer ) { if ( !containerSize.equals( THIS[ viewer.hash ].prevContainerSize ) ) { if ( viewer.preserveImageSizeOnResize ) { var prevContainerSize = THIS[ viewer.hash ].prevContainerSize; - var bounds = viewer.viewport.getBounds(true); + var bounds = viewer.viewport.getBoundsNoRotate(true); var deltaX = (containerSize.x - prevContainerSize.x); var deltaY = (containerSize.y - prevContainerSize.y); - var viewportDiff = viewer.viewport.deltaPointsFromPixels(new OpenSeadragon.Point(deltaX, deltaY), true); - viewer.viewport.resize(new OpenSeadragon.Point(containerSize.x, containerSize.y), false); + var viewportDiff = viewer.viewport.deltaPointsFromPixels( + new $.Point(deltaX, deltaY), true); + viewer.viewport.resize( + new $.Point(containerSize.x, containerSize.y), false); // Keep the center of the image in the center and just adjust the amount of image shown bounds.width += viewportDiff.x; @@ -2996,7 +2998,7 @@ function updateOnce( viewer ) { } else { // maintain image position - var oldBounds = viewer.viewport.getBounds(); + var oldBounds = viewer.viewport.getBoundsNoRotate(); var oldCenter = viewer.viewport.getCenter(); resizeViewportAndRecenter(viewer, containerSize, oldBounds, oldCenter); } diff --git a/src/viewport.js b/src/viewport.js index ad65f573..8a2ba22d 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -237,6 +237,17 @@ $.Viewport.prototype = { * @returns {OpenSeadragon.Rect} The home bounds in vewport coordinates. */ getHomeBounds: function() { + return this.getHomeBoundsNoRotate().rotate(-this.getRotation()); + }, + + /** + * Returns the home bounds in viewport coordinates. + * This method ignores the viewport rotation. Use + * {@link OpenSeadragon.Viewport#getHomeBounds} to take it into account. + * @function + * @returns {OpenSeadragon.Rect} The home bounds in vewport coordinates. + */ + getHomeBoundsNoRotate: function() { var center = this._contentBounds.getCenter(); var width = 1.0 / this.getHomeZoom(); var height = width / this.getAspectRatio(); @@ -254,8 +265,8 @@ $.Viewport.prototype = { * @param {Boolean} immediately * @fires OpenSeadragon.Viewer.event:home */ - goHome: function( immediately ) { - if( this.viewer ){ + goHome: function(immediately) { + if (this.viewer) { /** * Raised when the "home" operation occurs (see {@link OpenSeadragon.Viewport#goHome}). * @@ -266,11 +277,11 @@ $.Viewport.prototype = { * @property {Boolean} immediately * @property {?Object} userData - Arbitrary subscriber-defined object. */ - this.viewer.raiseEvent( 'home', { + this.viewer.raiseEvent('home', { immediately: immediately }); } - return this.fitBounds( this.getHomeBounds(), immediately ); + return this.fitBounds(this.getHomeBounds(), immediately); }, /** @@ -352,14 +363,26 @@ $.Viewport.prototype = { * @param {Boolean} current - Pass true for the current location; defaults to false (target location). * @returns {OpenSeadragon.Rect} The location you are zoomed/panned to, in viewport coordinates. */ - getBounds: function( current ) { - var center = this.getCenter( current ), - width = 1.0 / this.getZoom( current ), - height = width / this.getAspectRatio(); + getBounds: function(current) { + return this.getBoundsNoRotate(current).rotate(-this.getRotation()); + }, + + /** + * Returns the bounds of the visible area in viewport coordinates. + * This method ignores the viewport rotation. Use + * {@link OpenSeadragon.Viewport#getBounds} to take it into account. + * @function + * @param {Boolean} current - Pass true for the current location; defaults to false (target location). + * @returns {OpenSeadragon.Rect} The location you are zoomed/panned to, in viewport coordinates. + */ + getBoundsNoRotate: function(current) { + var center = this.getCenter(current); + var width = 1.0 / this.getZoom(current); + var height = width / this.getAspectRatio(); return new $.Rect( - center.x - ( width / 2.0 ), - center.y - ( height / 2.0 ), + center.x - (width / 2.0), + center.y - (height / 2.0), width, height ); @@ -371,8 +394,19 @@ $.Viewport.prototype = { * @returns {OpenSeadragon.Rect} The location you are zoomed/panned to, * including the space taken by margins, in viewport coordinates. */ - getBoundsWithMargins: function( current ) { - var bounds = this.getBounds(current); + getBoundsWithMargins: function(current) { + return this.getBoundsNoRotateWithMargins(current).rotate( + -this.getRotation(), this.getCenter(current)); + }, + + /** + * @function + * @param {Boolean} current - Pass true for the current location; defaults to false (target location). + * @returns {OpenSeadragon.Rect} The location you are zoomed/panned to, + * including the space taken by margins, in viewport coordinates. + */ + getBoundsNoRotateWithMargins: function(current) { + var bounds = this.getBoundsNoRotate(current); var factor = this._containerInnerSize.x * this.getZoom(current); bounds.x -= this._margins.left / factor; bounds.y -= this._margins.top / factor; @@ -538,7 +572,7 @@ $.Viewport.prototype = { this.zoomTo( constrainedZoom, this.zoomPoint, immediately ); } - bounds = this.getBounds(); + bounds = this.getBoundsNoRotate(); constrainedBounds = this._applyBoundaryConstraints( bounds, immediately ); @@ -564,39 +598,38 @@ $.Viewport.prototype = { * @param {Object} options (immediately=false, constraints=false) * @return {OpenSeadragon.Viewport} Chainable. */ - _fitBounds: function( bounds, options ) { + _fitBounds: function(bounds, options) { options = options || {}; var immediately = options.immediately || false; var constraints = options.constraints || false; var aspect = this.getAspectRatio(); var center = bounds.getCenter(); + + // Compute width and height of bounding box. var newBounds = new $.Rect( bounds.x, bounds.y, bounds.width, - bounds.height - ); + bounds.height, + bounds.degrees + this.getRotation()) + .getBoundingBox(); - if ( newBounds.getAspectRatio() >= aspect ) { - newBounds.height = bounds.width / aspect; - newBounds.y = center.y - newBounds.height / 2; + if (newBounds.getAspectRatio() >= aspect) { + newBounds.height = newBounds.width / aspect; } else { - newBounds.width = bounds.height * aspect; - newBounds.x = center.x - newBounds.width / 2; + newBounds.width = newBounds.height * aspect; } - this.panTo( this.getCenter( true ), true ); - this.zoomTo( this.getZoom( true ), null, true ); - - var oldBounds = this.getBounds(); - var oldZoom = this.getZoom(); - var newZoom = 1.0 / newBounds.width; + // Compute x and y from width, height and center position + newBounds.x = center.x - newBounds.width / 2; + newBounds.y = center.y - newBounds.height / 2; + var newZoom = 1.0 / newBounds.width; if (constraints) { var newBoundsAspectRatio = newBounds.getAspectRatio(); var newConstrainedZoom = Math.max( - Math.min(newZoom, this.getMaxZoom() ), + Math.min(newZoom, this.getMaxZoom()), this.getMinZoom() ); @@ -608,32 +641,32 @@ $.Viewport.prototype = { newBounds.y = center.y - newBounds.height / 2; } - newBounds = this._applyBoundaryConstraints( newBounds, immediately ); + newBounds = this._applyBoundaryConstraints(newBounds, immediately); center = newBounds.getCenter(); } if (immediately) { - this.panTo( center, true ); + this.panTo(center, true); return this.zoomTo(newZoom, null, true); } + this.panTo(this.getCenter(true), true); + this.zoomTo(this.getZoom(true), null, true); + + var oldBounds = this.getBounds(); + var oldZoom = this.getZoom(); + if (Math.abs(newZoom - oldZoom) < 0.00000001 || Math.abs(newBounds.width - oldBounds.width) < 0.00000001) { - return this.panTo( center, immediately ); + return this.panTo(center, immediately); } - var referencePoint = oldBounds.getTopLeft().times( - this._containerInnerSize.x / oldBounds.width - ).minus( - newBounds.getTopLeft().times( - this._containerInnerSize.x / newBounds.width - ) - ).divide( - this._containerInnerSize.x / oldBounds.width - - this._containerInnerSize.x / newBounds.width - ); + newBounds = newBounds.rotate(-this.getRotation()); + var referencePoint = newBounds.getTopLeft().divide(newBounds.width) + .minus(oldBounds.getTopLeft().divide(oldBounds.width)) + .divide(1 / newBounds.width - 1 / oldBounds.width); - return this.zoomTo( newZoom, referencePoint, immediately ); + return this.zoomTo(newZoom, referencePoint, immediately); }, /** @@ -754,7 +787,12 @@ $.Viewport.prototype = { }, /** + * Zooms to the specified zoom level * @function + * @param {Number} zoom The zoom level to zoom to. + * @param {OpenSeadragon.Point} [refPoint] The point which will stay at + * the same screen location. Defaults to the viewport center. + * @param {Boolean} [immediately=false] * @return {OpenSeadragon.Viewport} Chainable. * @fires OpenSeadragon.Viewer.event:zoom */ @@ -844,7 +882,7 @@ $.Viewport.prototype = { * @fires OpenSeadragon.Viewer.event:resize */ resize: function( newContainerSize, maintain ) { - var oldBounds = this.getBounds(), + var oldBounds = this.getBoundsNoRotate(), newBounds = oldBounds, widthDeltaFactor; @@ -996,7 +1034,8 @@ $.Viewport.prototype = { * @returns {OpenSeadragon.Point} */ pixelFromPointNoRotate: function(point, current) { - return this._pixelFromPointNoRotate(point, this.getBounds(current)); + return this._pixelFromPointNoRotate( + point, this.getBoundsNoRotate(current)); }, /** @@ -1007,7 +1046,7 @@ $.Viewport.prototype = { * @returns {OpenSeadragon.Point} */ pixelFromPoint: function(point, current) { - return this._pixelFromPoint(point, this.getBounds(current)); + return this._pixelFromPoint(point, this.getBoundsNoRotate(current)); }, // private @@ -1038,7 +1077,7 @@ $.Viewport.prototype = { * @returns {OpenSeadragon.Point} */ pointFromPixelNoRotate: function(pixel, current) { - var bounds = this.getBounds( current ); + var bounds = this.getBoundsNoRotate(current); return pixel.minus( new $.Point(this._margins.left, this._margins.top) ).divide( diff --git a/test/modules/viewport.js b/test/modules/viewport.js index e40f82be..0b06b93c 100644 --- a/test/modules/viewport.js +++ b/test/modules/viewport.js @@ -5,6 +5,7 @@ var VIEWER_ID = "example"; var PREFIX_URL = "/build/openseadragon/images/"; var SPRING_STIFFNESS = 100; // Faster animation = faster tests + var EPSILON = 0.0000000001; module("viewport", { setup: function () { @@ -218,7 +219,27 @@ }); }); - asyncTest('getHomeBoundsWithRotation', function() { + asyncTest('getHomeBoundsNoRotate with rotation', function() { + function openHandler() { + viewer.removeHandler('open', openHandler); + var viewport = viewer.viewport; + viewport.setRotation(-675); + Util.assertRectangleEquals( + viewport.getHomeBoundsNoRotate(), + new OpenSeadragon.Rect( + (1 - Math.sqrt(2)) / 2, + (1 - Math.sqrt(2)) / 2, + Math.sqrt(2), + Math.sqrt(2)), + 0.00000001, + "Test getHomeBoundsNoRotate with degrees = -675"); + start(); + } + viewer.addHandler('open', openHandler); + viewer.open(DZI_PATH); + }); + + asyncTest('getHomeBounds with rotation', function() { function openHandler() { viewer.removeHandler('open', openHandler); var viewport = viewer.viewport; @@ -226,10 +247,11 @@ Util.assertRectangleEquals( viewport.getHomeBounds(), new OpenSeadragon.Rect( - (1 - Math.sqrt(2)) / 2, - (1 - Math.sqrt(2)) / 2, + 0.5, + -0.5, Math.sqrt(2), - Math.sqrt(2)), + Math.sqrt(2), + 45), 0.00000001, "Test getHomeBounds with degrees = -675"); start(); @@ -420,6 +442,33 @@ new OpenSeadragon.Rect(-0.3, -0.3, 0.5, 0.5) ]; + var expectedRectsFitBoundsWithRotation = [ + new OpenSeadragon.Rect( + 0.25, + -1, + Math.sqrt(0.125) + Math.sqrt(0.5), + Math.sqrt(0.125) + Math.sqrt(0.5), + 45), + new OpenSeadragon.Rect( + 0.75, + -0.25, + Math.sqrt(0.125) + Math.sqrt(8 / 25), + Math.sqrt(0.125) + Math.sqrt(8 / 25), + 45), + new OpenSeadragon.Rect( + 1, + 0.5, + Math.sqrt(0.125) * 2, + Math.sqrt(0.125) * 2, + 45), + new OpenSeadragon.Rect( + -0.05, + -0.55, + Math.sqrt(0.125) * 2, + Math.sqrt(0.125) * 2, + 45) + ]; + var expectedRectsFitBoundsWithConstraints = [ new OpenSeadragon.Rect(-0.25, -0.5, 1, 1), new OpenSeadragon.Rect(0.35, 0, 0.8, 0.8), @@ -447,6 +496,28 @@ viewer.open(DZI_PATH); }); + asyncTest('fitBounds with viewport rotation', function(){ + var openHandler = function(event) { + viewer.removeHandler('open', openHandler); + var viewport = viewer.viewport; + viewport.setRotation(45); + + for(var i = 0; i < testRectsFitBounds.length; i++){ + var rect = testRectsFitBounds[i]; + viewport.fitBounds(rect, true); + Util.assertRectangleEquals( + viewport.getBounds(), + expectedRectsFitBoundsWithRotation[i], + EPSILON, + "Fit bounds correctly." + ); + } + start(); + }; + viewer.addHandler('open', openHandler); + viewer.open(DZI_PATH); + }); + asyncTest('fitBoundsWithConstraints', function(){ var openHandler = function(event) { viewer.removeHandler('open', openHandler); From e4c29d649b5b1752c9eca86eb2051859a2722a37 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Tue, 10 May 2016 18:49:55 -0400 Subject: [PATCH 3/7] Remove code duplication in Viewport.applyConstraints. --- src/viewport.js | 63 ++++++++++++++-------------- test/modules/events.js | 90 +++++++++++++++++++++++----------------- test/modules/viewport.js | 41 +++++++++++++++++- 3 files changed, 122 insertions(+), 72 deletions(-) diff --git a/src/viewport.js b/src/viewport.js index 8a2ba22d..3c08c594 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -555,40 +555,27 @@ $.Viewport.prototype = { }, /** + * Enforces the minZoom, maxZoom and visibilityRatio constraints by + * zooming and panning to the closest acceptable zoom and location. * @function + * @param {Boolean} [immediately=false] * @return {OpenSeadragon.Viewport} Chainable. * @fires OpenSeadragon.Viewer.event:constrain */ - applyConstraints: function( immediately ) { - var actualZoom = this.getZoom(), - constrainedZoom = Math.max( - Math.min( actualZoom, this.getMaxZoom() ), - this.getMinZoom() - ), - bounds, - constrainedBounds; - - if ( actualZoom != constrainedZoom ) { - this.zoomTo( constrainedZoom, this.zoomPoint, immediately ); - } - - bounds = this.getBoundsNoRotate(); - - constrainedBounds = this._applyBoundaryConstraints( bounds, immediately ); - - if ( bounds.x !== constrainedBounds.x || bounds.y !== constrainedBounds.y || immediately ){ - this.fitBounds( constrainedBounds, immediately ); - } - + applyConstraints: function(immediately) { + this.fitBoundsWithConstraints(this.getBounds(), immediately); return this; }, /** + * Equivalent to {@link OpenSeadragon.Viewport#applyConstraints} * @function - * @param {Boolean} immediately + * @param {Boolean} [immediately=false] + * @return {OpenSeadragon.Viewport} Chainable. + * @fires OpenSeadragon.Viewer.event:constrain */ - ensureVisible: function( immediately ) { - return this.applyConstraints( immediately ); + ensureVisible: function(immediately) { + return this.applyConstraints(immediately); }, /** @@ -670,29 +657,41 @@ $.Viewport.prototype = { }, /** + * Makes the viewport zoom and pan so that the specified bounds take + * as much space as possible in the viewport. + * Note: this method ignores the constraints (minZoom, maxZoom and + * visibilityRatio). + * Use {@link OpenSeadragon.Viewport#fitBoundsWithConstraints} to enforce + * them. * @function * @param {OpenSeadragon.Rect} bounds - * @param {Boolean} immediately + * @param {Boolean} [immediately=false] * @return {OpenSeadragon.Viewport} Chainable. */ - fitBounds: function( bounds, immediately ) { - return this._fitBounds( bounds, { + fitBounds: function(bounds, immediately) { + return this._fitBounds(bounds, { immediately: immediately, constraints: false - } ); + }); }, /** + * Makes the viewport zoom and pan so that the specified bounds take + * as much space as possible in the viewport while enforcing the constraints + * (minZoom, maxZoom and visibilityRatio). + * Note: because this method enforces the constraints, part of the + * provided bounds may end up outside of the viewport. + * Use {@link OpenSeadragon.Viewport#fitBounds} to ignore them. * @function * @param {OpenSeadragon.Rect} bounds - * @param {Boolean} immediately + * @param {Boolean} [immediately=false] * @return {OpenSeadragon.Viewport} Chainable. */ - fitBoundsWithConstraints: function( bounds, immediately ) { - return this._fitBounds( bounds, { + fitBoundsWithConstraints: function(bounds, immediately) { + return this._fitBounds(bounds, { immediately: immediately, constraints: true - } ); + }); }, /** diff --git a/test/modules/events.js b/test/modules/events.js index 18f50c37..2d435fc0 100644 --- a/test/modules/events.js +++ b/test/modules/events.js @@ -678,45 +678,20 @@ } ); // ---------- - asyncTest( 'Viewer: preventDefaultAction', function () { - var $canvas = $( viewer.element ).find( '.openseadragon-canvas' ).not( '.navigator .openseadragon-canvas' ), - tracker = viewer.innerTracker, - origClickHandler, - origDragHandler, - dragCount = 10, - originalZoom = 0, - originalBounds = null; - - var onOpen = function ( event ) { - viewer.removeHandler( 'open', onOpen ); - - // Hook viewer events to set preventDefaultAction - origClickHandler = tracker.clickHandler; - tracker.clickHandler = function ( event ) { - event.preventDefaultAction = true; - return origClickHandler( event ); - }; - origDragHandler = tracker.dragHandler; - tracker.dragHandler = function ( event ) { - event.preventDefaultAction = true; - return origDragHandler( event ); - }; - - originalZoom = viewer.viewport.getZoom(); - originalBounds = viewer.viewport.getBounds(); - - var event = { - clientX:1, - clientY:1 - }; + asyncTest('Viewer: preventDefaultAction', function() { + var $canvas = $(viewer.element).find('.openseadragon-canvas') + .not('.navigator .openseadragon-canvas'); + var tracker = viewer.innerTracker; + var epsilon = 0.0000001; + function simulateClickAndDrag() { $canvas.simulate( 'focus', event ); // Drag to pan Util.simulateViewerClickWithDrag( { viewer: viewer, widthFactor: 0.25, heightFactor: 0.25, - dragCount: dragCount, + dragCount: 10, dragDx: 1, dragDy: 1 } ); @@ -730,20 +705,57 @@ dragDy: 0 } ); $canvas.simulate( 'blur', event ); + } - var zoom = viewer.viewport.getZoom(), - bounds = viewer.viewport.getBounds(); + var onOpen = function() { + viewer.removeHandler('open', onOpen); - equal( zoom, originalZoom, "Zoom prevented" ); - ok( bounds.x == originalBounds.x && bounds.y == originalBounds.y, 'Pan prevented' ); + // Hook viewer events to set preventDefaultAction + var origClickHandler = tracker.clickHandler; + tracker.clickHandler = function(event) { + event.preventDefaultAction = true; + return origClickHandler(event); + }; + var origDragHandler = tracker.dragHandler; + tracker.dragHandler = function(event) { + event.preventDefaultAction = true; + return origDragHandler(event); + }; + + var originalZoom = viewer.viewport.getZoom(); + var originalBounds = viewer.viewport.getBounds(); + + simulateClickAndDrag(); + + var zoom = viewer.viewport.getZoom(); + var bounds = viewer.viewport.getBounds(); + Util.assessNumericValue(zoom, originalZoom, epsilon, + "Zoom should be prevented"); + Util.assertRectangleEquals(bounds, originalBounds, epsilon, + 'Pan should be prevented'); + + tracker.clickHandler = origClickHandler; + tracker.dragHandler = origDragHandler; + + simulateClickAndDrag(); + + var zoom = viewer.viewport.getZoom(); + var bounds = viewer.viewport.getBounds(); + Util.assessNumericValue(zoom, 0.002, epsilon, + "Zoom should not be prevented"); + Util.assertRectangleEquals( + bounds, + new OpenSeadragon.Rect(-250, -0.25, 500, 0.5), + epsilon, + 'Pan should not be prevented'); viewer.close(); start(); }; - viewer.addHandler( 'open', onOpen ); - viewer.open( '/test/data/testpattern.dzi' ); - } ); + viewer.addHandler('open', onOpen); + viewer.open('/test/data/testpattern.dzi'); + }); // ---------- asyncTest( 'EventSource/MouseTracker/Viewer: event.originalEvent event.userData canvas-drag canvas-drag-end canvas-release canvas-click', function () { diff --git a/test/modules/viewport.js b/test/modules/viewport.js index 0b06b93c..8412a007 100644 --- a/test/modules/viewport.js +++ b/test/modules/viewport.js @@ -409,7 +409,7 @@ viewer.open(DZI_PATH); }); - asyncTest('ensureVisible', function(){ + asyncTest('ensureVisible', function() { var openHandler = function(event) { viewer.removeHandler('open', openHandler); var viewport = viewer.viewport; @@ -427,6 +427,45 @@ viewer.open(DZI_PATH); }); + asyncTest('applyConstraints', function() { + var openHandler = function() { + viewer.removeHandler('open', openHandler); + var viewport = viewer.viewport; + + viewport.fitBounds(new OpenSeadragon.Rect(1, 1, 1, 1), true); + viewport.visibilityRatio = 0.3; + viewport.applyConstraints(true); + var bounds = viewport.getBounds(); + Util.assertRectangleEquals( + bounds, + new OpenSeadragon.Rect(0.7, 0.7, 1, 1), + EPSILON, + "Viewport.applyConstraints should move viewport."); + start(); + }; + viewer.addHandler('open', openHandler); + viewer.open(DZI_PATH); + }); + + asyncTest('applyConstraints with rotation', function() { + var openHandler = function() { + viewer.removeHandler('open', openHandler); + var viewport = viewer.viewport; + viewport.setRotation(45); + viewport.fitBounds(new OpenSeadragon.Rect(1, 1, 1, 1), true); + viewport.applyConstraints(true); + var bounds = viewport.getBounds(); + Util.assertRectangleEquals( + bounds, + new OpenSeadragon.Rect(1, 0, Math.sqrt(2), Math.sqrt(2), 45), + EPSILON, + "Viewport.applyConstraints with rotation should move viewport."); + start(); + }; + viewer.addHandler('open', openHandler); + viewer.open(DZI_PATH); + }); + // Fit bounds tests var testRectsFitBounds = [ new OpenSeadragon.Rect(0, -0.75, 0.5, 1), From 0c398eacdbb5f95116cdb56d23aae8e9f44d5f32 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Tue, 10 May 2016 21:19:33 -0400 Subject: [PATCH 4/7] Add test for fitBounds with a rotated rectangle. --- test/modules/viewport.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/test/modules/viewport.js b/test/modules/viewport.js index 8412a007..c3618be9 100644 --- a/test/modules/viewport.js +++ b/test/modules/viewport.js @@ -471,14 +471,16 @@ new OpenSeadragon.Rect(0, -0.75, 0.5, 1), new OpenSeadragon.Rect(0.5, 0, 0.5, 0.8), new OpenSeadragon.Rect(0.75, 0.75, 0.5, 0.5), - new OpenSeadragon.Rect(-0.3, -0.3, 0.5, 0.5) + new OpenSeadragon.Rect(-0.3, -0.3, 0.5, 0.5), + new OpenSeadragon.Rect(0.5, 0.25, Math.sqrt(0.125), Math.sqrt(0.125), 45) ]; var expectedRectsFitBounds = [ new OpenSeadragon.Rect(-0.25, -0.75, 1, 1), new OpenSeadragon.Rect(0.35, 0, 0.8, 0.8), new OpenSeadragon.Rect(0.75, 0.75, 0.5, 0.5), - new OpenSeadragon.Rect(-0.3, -0.3, 0.5, 0.5) + new OpenSeadragon.Rect(-0.3, -0.3, 0.5, 0.5), + new OpenSeadragon.Rect(0.25, 0.25, 0.5, 0.5) ]; var expectedRectsFitBoundsWithRotation = [ @@ -505,6 +507,12 @@ -0.55, Math.sqrt(0.125) * 2, Math.sqrt(0.125) * 2, + 45), + new OpenSeadragon.Rect( + 0.5, + 0.25, + Math.sqrt(0.125), + Math.sqrt(0.125), 45) ]; @@ -512,7 +520,8 @@ new OpenSeadragon.Rect(-0.25, -0.5, 1, 1), new OpenSeadragon.Rect(0.35, 0, 0.8, 0.8), new OpenSeadragon.Rect(0.75, 0.75, 0.5, 0.5), - new OpenSeadragon.Rect(-0.25, -0.25, 0.5, 0.5) + new OpenSeadragon.Rect(-0.25, -0.25, 0.5, 0.5), + new OpenSeadragon.Rect(0.25, 0.25, 0.5, 0.5) ]; asyncTest('fitBounds', function(){ From 07d66ce655a1ac33398d359d928b6c63f86457e5 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Thu, 12 May 2016 18:01:18 -0400 Subject: [PATCH 5/7] Restore applyConstraints to avoid panning when clicking at max zoom. --- src/viewport.js | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/viewport.js b/src/viewport.js index 3c08c594..ba65048b 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -474,6 +474,13 @@ $.Viewport.prototype = { } }, + // private + _applyZoomConstraints: function(zoom) { + return Math.max( + Math.min(zoom, this.getMaxZoom()), + this.getMinZoom()); + }, + /** * @function * @private @@ -563,7 +570,23 @@ $.Viewport.prototype = { * @fires OpenSeadragon.Viewer.event:constrain */ applyConstraints: function(immediately) { - this.fitBoundsWithConstraints(this.getBounds(), immediately); + var actualZoom = this.getZoom(); + var constrainedZoom = this._applyZoomConstraints(actualZoom); + + if (actualZoom !== constrainedZoom) { + this.zoomTo(constrainedZoom, this.zoomPoint, immediately); + } + + var bounds = this.getBoundsNoRotate(); + var constrainedBounds = this._applyBoundaryConstraints( + bounds, immediately); + + if (bounds.x !== constrainedBounds.x || + bounds.y !== constrainedBounds.y || + immediately) { + this.fitBounds(constrainedBounds.rotate(this.getRotation()), + immediately); + } return this; }, @@ -615,10 +638,7 @@ $.Viewport.prototype = { if (constraints) { var newBoundsAspectRatio = newBounds.getAspectRatio(); - var newConstrainedZoom = Math.max( - Math.min(newZoom, this.getMaxZoom()), - this.getMinZoom() - ); + var newConstrainedZoom = this._applyZoomConstraints(newZoom); if (newZoom !== newConstrainedZoom) { newZoom = newConstrainedZoom; From 14069a64e1ab20bc6dcf92bb25a50cb1dbec55fb Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Thu, 12 May 2016 18:47:35 -0400 Subject: [PATCH 6/7] Fix applyConstraints with rotation. --- src/viewport.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/viewport.js b/src/viewport.js index ba65048b..e5c4f51a 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -584,7 +584,8 @@ $.Viewport.prototype = { if (bounds.x !== constrainedBounds.x || bounds.y !== constrainedBounds.y || immediately) { - this.fitBounds(constrainedBounds.rotate(this.getRotation()), + this.fitBounds( + constrainedBounds.rotate(-this.getRotation()), immediately); } return this; From 1014d5767cdc50b080ec4d5c3271874cd9c95ff2 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Thu, 12 May 2016 19:23:09 -0400 Subject: [PATCH 7/7] Fix resize handling. --- src/viewer.js | 66 ++++++++++++++------------------------------------- 1 file changed, 18 insertions(+), 48 deletions(-) diff --git a/src/viewer.js b/src/viewer.js index c9f6a98b..5fdb1c58 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -2975,35 +2975,26 @@ function updateOnce( viewer ) { return; } - var containerSize; - if ( viewer.autoResize ) { - containerSize = _getSafeElemSize( viewer.container ); - if ( !containerSize.equals( THIS[ viewer.hash ].prevContainerSize ) ) { - if ( viewer.preserveImageSizeOnResize ) { - var prevContainerSize = THIS[ viewer.hash ].prevContainerSize; - var bounds = viewer.viewport.getBoundsNoRotate(true); - var deltaX = (containerSize.x - prevContainerSize.x); - var deltaY = (containerSize.y - prevContainerSize.y); - var viewportDiff = viewer.viewport.deltaPointsFromPixels( - new $.Point(deltaX, deltaY), true); - viewer.viewport.resize( - new $.Point(containerSize.x, containerSize.y), false); - - // Keep the center of the image in the center and just adjust the amount of image shown - bounds.width += viewportDiff.x; - bounds.height += viewportDiff.y; - bounds.x -= (viewportDiff.x / 2); - bounds.y -= (viewportDiff.y / 2); - viewer.viewport.fitBoundsWithConstraints(bounds, true); - } - else { + if (viewer.autoResize) { + var containerSize = _getSafeElemSize(viewer.container); + var prevContainerSize = THIS[viewer.hash].prevContainerSize; + if (!containerSize.equals(prevContainerSize)) { + var viewport = viewer.viewport; + if (viewer.preserveImageSizeOnResize) { + var resizeRatio = prevContainerSize.x / containerSize.x; + var zoom = viewport.getZoom() * resizeRatio; + var center = viewport.getCenter(); + viewport.resize(containerSize, false); + viewport.zoomTo(zoom, null, true); + viewport.panTo(center, true); + } else { // maintain image position - var oldBounds = viewer.viewport.getBoundsNoRotate(); - var oldCenter = viewer.viewport.getCenter(); - resizeViewportAndRecenter(viewer, containerSize, oldBounds, oldCenter); + var oldBounds = viewport.getBounds(); + viewport.resize(containerSize, true); + viewport.fitBoundsWithConstraints(oldBounds, true); } - THIS[ viewer.hash ].prevContainerSize = containerSize; - THIS[ viewer.hash ].forceRedraw = true; + THIS[viewer.hash].prevContainerSize = containerSize; + THIS[viewer.hash].forceRedraw = true; } } @@ -3088,27 +3079,6 @@ function updateOnce( viewer ) { //viewer.profiler.endUpdate(); } -// This function resizes the viewport and recenters the image -// as it was before resizing. -// TODO: better adjust width and height. The new width and height -// should depend on the image dimensions and on the dimensions -// of the viewport before and after switching mode. -function resizeViewportAndRecenter( viewer, containerSize, oldBounds, oldCenter ) { - var viewport = viewer.viewport; - - viewport.resize( containerSize, true ); - - var newBounds = new $.Rect( - oldCenter.x - ( oldBounds.width / 2.0 ), - oldCenter.y - ( oldBounds.height / 2.0 ), - oldBounds.width, - oldBounds.height - ); - - // let the viewport decide if the bounds are too big or too small - viewport.fitBoundsWithConstraints( newBounds, true ); -} - function drawWorld( viewer ) { viewer.imageLoader.clear(); viewer.drawer.clear();