diff --git a/changelog.txt b/changelog.txt index 94ed8217..e2de9bf0 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,10 @@ OPENSEADRAGON CHANGELOG 2.1.1: (in progress) +* BREAKING CHANGE: Viewport.homeBounds, Viewport.contentSize, Viewport.contentAspectX and +Viewport.contentAspectY have been removed. +* DEPRECATION: Viewport.setHomeBounds has been deprecated +* DEPRECATION: the Viewport constructor is now ignoring the contentSize option * Tile edge smoothing at high zoom (#764) * Fixed issue with reference strip popping up virtual keyboard on mobile devices (#779) * Now supporting rotation in the Rect class (#782) @@ -15,6 +19,7 @@ OPENSEADRAGON CHANGELOG * Added globalCompositeOperation option for tiledImage, to allow for different transfer modes (#814) * Added Viewer.addSimpleImage method for easily adding non-tiled images (#827) * DziTileSource now works properly with DZI files that have no extension (#835) +* Fixed content clipping with rotation (#463, #567 and #833) * Fixed navigator not being rotated when viewport rotation is set in constructor (#840) * Fixed: Viewer.setMouseNavEnabled wasn't affecting all of the viewer's trackers (#845) diff --git a/src/viewer.js b/src/viewer.js index b1342e88..136040d4 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -330,7 +330,7 @@ $.Viewer = function( options ) { this.world.addHandler('metrics-change', function(event) { if (_this.viewport) { - _this.viewport.setHomeBounds(_this.world.getHomeBounds(), _this.world.getContentFactor()); + _this.viewport._setContentBounds(_this.world.getHomeBounds(), _this.world.getContentFactor()); } }); @@ -359,7 +359,7 @@ $.Viewer = function( options ) { margins: this.viewportMargins }); - this.viewport.setHomeBounds(this.world.getHomeBounds(), this.world.getContentFactor()); + this.viewport._setContentBounds(this.world.getHomeBounds(), this.world.getContentFactor()); // Create the image loader this.imageLoader = new $.ImageLoader({ diff --git a/src/viewport.js b/src/viewport.js index a9b12eb7..63b4dcaf 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -134,13 +134,9 @@ $.Viewport = function( options ) { this._oldCenterY = this.centerSpringY.current.value; this._oldZoom = this.zoomSpring.current.value; - if (this.contentSize) { - this.resetContentSize( this.contentSize ); - } else { - this.setHomeBounds(new $.Rect(0, 0, 1, 1), 1); - } + this._setContentBounds(new $.Rect(0, 0, 1, 1), 1); - this.goHome( true ); + this.goHome(true); this.update(); }; @@ -153,93 +149,101 @@ $.Viewport.prototype = { * @return {OpenSeadragon.Viewport} Chainable. * @fires OpenSeadragon.Viewer.event:reset-size */ - resetContentSize: function( contentSize ){ + resetContentSize: function(contentSize) { $.console.assert(contentSize, "[Viewport.resetContentSize] contentSize is required"); $.console.assert(contentSize instanceof $.Point, "[Viewport.resetContentSize] contentSize must be an OpenSeadragon.Point"); $.console.assert(contentSize.x > 0, "[Viewport.resetContentSize] contentSize.x must be greater than 0"); $.console.assert(contentSize.y > 0, "[Viewport.resetContentSize] contentSize.y must be greater than 0"); - this.setHomeBounds(new $.Rect(0, 0, 1, contentSize.y / contentSize.x), contentSize.x); + this._setContentBounds(new $.Rect(0, 0, 1, contentSize.y / contentSize.x), contentSize.x); return this; }, - /** - * Updates the viewport's home bounds and constraints. - * @function - * @param {OpenSeadragon.Rect} bounds - the new bounds in viewport coordinates - * @param {Number} contentFactor - how many content units per viewport unit - * @fires OpenSeadragon.Viewer.event:reset-size - */ + // deprecated setHomeBounds: function(bounds, contentFactor) { - $.console.assert(bounds, "[Viewport.setHomeBounds] bounds is required"); - $.console.assert(bounds instanceof $.Rect, "[Viewport.setHomeBounds] bounds must be an OpenSeadragon.Rect"); - $.console.assert(bounds.width > 0, "[Viewport.setHomeBounds] bounds.width must be greater than 0"); - $.console.assert(bounds.height > 0, "[Viewport.setHomeBounds] bounds.height must be greater than 0"); + $.console.error("[Viewport.setHomeBounds] this function is deprecated; The content bounds should not be set manually."); + this._setContentBounds(bounds, contentFactor); + }, - this.homeBounds = bounds.clone().rotate(this.degrees).getBoundingBox(); - this.contentSize = this.homeBounds.getSize().times(contentFactor); - this.contentAspectX = this.contentSize.x / this.contentSize.y; - this.contentAspectY = this.contentSize.y / this.contentSize.x; + // Set the viewport's content bounds + // @param {OpenSeadragon.Rect} bounds - the new bounds in viewport coordinates + // without rotation + // @param {Number} contentFactor - how many content units per viewport unit + // @fires OpenSeadragon.Viewer.event:reset-size + // @private + _setContentBounds: function(bounds, contentFactor) { + $.console.assert(bounds, "[Viewport._setContentBounds] bounds is required"); + $.console.assert(bounds instanceof $.Rect, "[Viewport._setContentBounds] bounds must be an OpenSeadragon.Rect"); + $.console.assert(bounds.width > 0, "[Viewport._setContentBounds] bounds.width must be greater than 0"); + $.console.assert(bounds.height > 0, "[Viewport._setContentBounds] bounds.height must be greater than 0"); + + this._contentBoundsNoRotate = bounds.clone(); + this._contentSizeNoRotate = this._contentBoundsNoRotate.getSize().times( + contentFactor); + + this._contentBounds = bounds.rotate(this.degrees).getBoundingBox(); + this._contentSize = this._contentBounds.getSize().times(contentFactor); + this._contentAspectRatio = this._contentSize.x / this._contentSize.y; if (this.viewer) { /** * Raised when the viewer's content size or home bounds are reset - * (see {@link OpenSeadragon.Viewport#resetContentSize}, - * {@link OpenSeadragon.Viewport#setHomeBounds}). + * (see {@link OpenSeadragon.Viewport#resetContentSize}). * * @event reset-size * @memberof OpenSeadragon.Viewer * @type {object} * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event. * @property {OpenSeadragon.Point} contentSize - * @property {OpenSeadragon.Rect} homeBounds + * @property {OpenSeadragon.Rect} contentBounds - Content bounds. + * @property {OpenSeadragon.Rect} homeBounds - Content bounds. + * Deprecated use contentBounds instead. * @property {Number} contentFactor * @property {?Object} userData - Arbitrary subscriber-defined object. */ this.viewer.raiseEvent('reset-size', { - contentSize: this.contentSize.clone(), + contentSize: this._contentSizeNoRotate.clone(), contentFactor: contentFactor, - homeBounds: this.homeBounds.clone() + homeBounds: this._contentBoundsNoRotate.clone(), + contentBounds: this._contentBounds.clone() }); } }, /** + * Returns the home zoom in "viewport zoom" value. * @function + * @returns {Number} The home zoom in "viewport zoom". */ getHomeZoom: function() { - if( this.defaultZoomLevel ){ + if (this.defaultZoomLevel) { return this.defaultZoomLevel; - } else { - var aspectFactor = - this.contentAspectX / this.getAspectRatio(); - - var output; - if( this.homeFillsViewer ){ // fill the viewer and clip the image - output = ( aspectFactor >= 1) ? - aspectFactor : - 1; - } else { - output = ( aspectFactor >= 1 ) ? - 1 : - aspectFactor; - } - - return output / this.homeBounds.width; } + + var aspectFactor = this._contentAspectRatio / this.getAspectRatio(); + var output; + if (this.homeFillsViewer) { // fill the viewer and clip the image + output = aspectFactor >= 1 ? aspectFactor : 1; + } else { + output = aspectFactor >= 1 ? 1 : aspectFactor; + } + + return output / this._contentBounds.width; }, /** + * Returns the home bounds in viewport coordinates. * @function + * @returns {OpenSeadragon.Rect} The home bounds in vewport coordinates. */ getHomeBounds: function() { - var center = this.homeBounds.getCenter( ), - width = 1.0 / this.getHomeZoom( ), - height = width / this.getAspectRatio(); + var center = this._contentBounds.getCenter(); + var width = 1.0 / this.getHomeZoom(); + 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 ); @@ -287,8 +291,8 @@ $.Viewport.prototype = { getMaxZoom: function() { var zoom = this.maxZoomLevel; if (!zoom) { - zoom = this.contentSize.x * this.maxZoomPixelRatio / this._containerInnerSize.x; - zoom /= this.homeBounds.width; + zoom = this._contentSize.x * this.maxZoomPixelRatio / this._containerInnerSize.x; + zoom /= this._contentBounds.width; } return Math.max( zoom, this.getHomeZoom() ); @@ -341,6 +345,7 @@ $.Viewport.prototype = { }, /** + * Returns the bounds of the visible area in viewport coordinates. * @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. @@ -440,65 +445,61 @@ $.Viewport.prototype = { * @param {Boolean} immediately * @return {OpenSeadragon.Rect} constrained bounds. */ - _applyBoundaryConstraints: function( bounds, immediately ) { - var dx = 0, - dy = 0, - newBounds = new $.Rect( + _applyBoundaryConstraints: function(bounds, immediately) { + var newBounds = new $.Rect( bounds.x, bounds.y, bounds.width, - bounds.height - ); + bounds.height); var horizontalThreshold = this.visibilityRatio * newBounds.width; var verticalThreshold = this.visibilityRatio * newBounds.height; - if ( this.wrapHorizontal ) { + if (this.wrapHorizontal) { //do nothing } else { + var dx = 0; var thresholdLeft = newBounds.x + (newBounds.width - horizontalThreshold); - if (this.homeBounds.x > thresholdLeft) { - dx = this.homeBounds.x - thresholdLeft; + if (this._contentBoundsNoRotate.x > thresholdLeft) { + dx = this._contentBoundsNoRotate.x - thresholdLeft; } - var homeRight = this.homeBounds.x + this.homeBounds.width; + var contentRight = this._contentBoundsNoRotate.x + this._contentBoundsNoRotate.width; var thresholdRight = newBounds.x + horizontalThreshold; - if (homeRight < thresholdRight) { - var newDx = homeRight - thresholdRight; + if (contentRight < thresholdRight) { + var newDx = contentRight - thresholdRight; if (dx) { dx = (dx + newDx) / 2; } else { dx = newDx; } } + newBounds.x += dx; } - if ( this.wrapVertical ) { + if (this.wrapVertical) { //do nothing } else { + var dy = 0; var thresholdTop = newBounds.y + (newBounds.height - verticalThreshold); - if (this.homeBounds.y > thresholdTop) { - dy = this.homeBounds.y - thresholdTop; + if (this._contentBoundsNoRotate.y > thresholdTop) { + dy = this._contentBoundsNoRotate.y - thresholdTop; } - var homeBottom = this.homeBounds.y + this.homeBounds.height; + var contentBottom = this._contentBoundsNoRotate.y + this._contentBoundsNoRotate.height; var thresholdBottom = newBounds.y + verticalThreshold; - if (homeBottom < thresholdBottom) { - var newDy = homeBottom - thresholdBottom; + if (contentBottom < thresholdBottom) { + var newDy = contentBottom - thresholdBottom; if (dy) { dy = (dy + newDy) / 2; } else { dy = newDy; } } - } - - if ( dx || dy ) { - newBounds.x += dx; newBounds.y += dy; } - if( this.viewer ){ + if (this.viewer) { /** * Raised when the viewport constraints are applied (see {@link OpenSeadragon.Viewport#applyConstraints}). * @@ -566,20 +567,14 @@ $.Viewport.prototype = { var immediately = options.immediately || false; var constraints = options.constraints || false; - var aspect = this.getAspectRatio(), - center = bounds.getCenter(), - newBounds = new $.Rect( - bounds.x, - bounds.y, - bounds.width, - bounds.height - ), - oldBounds, - oldZoom, - newZoom, - referencePoint, - newBoundsAspectRatio, - newConstrainedZoom; + var aspect = this.getAspectRatio(); + var center = bounds.getCenter(); + var newBounds = new $.Rect( + bounds.x, + bounds.y, + bounds.width, + bounds.height + ); if ( newBounds.getAspectRatio() >= aspect ) { newBounds.height = bounds.width / aspect; @@ -589,19 +584,16 @@ $.Viewport.prototype = { newBounds.x = center.x - newBounds.width / 2; } - if ( constraints ) { - newBoundsAspectRatio = newBounds.getAspectRatio(); - } - this.panTo( this.getCenter( true ), true ); this.zoomTo( this.getZoom( true ), null, true ); - oldBounds = this.getBounds(); - oldZoom = this.getZoom(); - newZoom = 1.0 / newBounds.width; + var oldBounds = this.getBounds(); + var oldZoom = this.getZoom(); + var newZoom = 1.0 / newBounds.width; - if ( constraints ) { - newConstrainedZoom = Math.max( + if (constraints) { + var newBoundsAspectRatio = newBounds.getAspectRatio(); + var newConstrainedZoom = Math.max( Math.min(newZoom, this.getMaxZoom() ), this.getMinZoom() ); @@ -628,7 +620,7 @@ $.Viewport.prototype = { return this.panTo( center, immediately ); } - referencePoint = oldBounds.getTopLeft().times( + var referencePoint = oldBounds.getTopLeft().times( this._containerInnerSize.x / oldBounds.width ).minus( newBounds.getTopLeft().times( @@ -673,11 +665,13 @@ $.Viewport.prototype = { * @param {Boolean} immediately * @return {OpenSeadragon.Viewport} Chainable. */ - fitVertically: function( immediately ) { - var box = new $.Rect(this.homeBounds.x + (this.homeBounds.width / 2), this.homeBounds.y, - 0, this.homeBounds.height); - - return this.fitBounds( box, immediately ); + fitVertically: function(immediately) { + var box = new $.Rect( + this._contentBounds.x + (this._contentBounds.width / 2), + this._contentBounds.y, + 0, + this._contentBounds.height); + return this.fitBounds(box, immediately); }, /** @@ -685,11 +679,13 @@ $.Viewport.prototype = { * @param {Boolean} immediately * @return {OpenSeadragon.Viewport} Chainable. */ - fitHorizontally: function( immediately ) { - var box = new $.Rect(this.homeBounds.x, this.homeBounds.y + (this.homeBounds.height / 2), - this.homeBounds.width, 0); - - return this.fitBounds( box, immediately ); + fitHorizontally: function(immediately) { + var box = new $.Rect( + this._contentBounds.x, + this._contentBounds.y + (this._contentBounds.height / 2), + this._contentBounds.width, + 0); + return this.fitBounds(box, immediately); }, @@ -812,7 +808,7 @@ $.Viewport.prototype = { degrees += 360; } this.degrees = degrees; - this.setHomeBounds( + this._setContentBounds( this.viewer.world.getHomeBounds(), this.viewer.world.getContentFactor()); this.viewer.forceRedraw(); @@ -1066,9 +1062,10 @@ $.Viewport.prototype = { // private _viewportToImageDelta: function( viewerX, viewerY ) { - var scale = this.homeBounds.width; - return new $.Point(viewerX * (this.contentSize.x / scale), - viewerY * ((this.contentSize.y * this.contentAspectX) / scale)); + var scale = this._contentBoundsNoRotate.width; + return new $.Point( + viewerX * this._contentSizeNoRotate.x / scale, + viewerY * this._contentSizeNoRotate.x / scale); }, /** @@ -1092,14 +1089,17 @@ $.Viewport.prototype = { $.console.error('[Viewport.viewportToImageCoordinates] is not accurate with multi-image; use TiledImage.viewportToImageCoordinates instead.'); } - return this._viewportToImageDelta(viewerX - this.homeBounds.x, viewerY - this.homeBounds.y); + return this._viewportToImageDelta( + viewerX - this._contentBoundsNoRotate.x, + viewerY - this._contentBoundsNoRotate.y); }, // private _imageToViewportDelta: function( imageX, imageY ) { - var scale = this.homeBounds.width; - return new $.Point((imageX / this.contentSize.x) * scale, - (imageY / this.contentSize.y / this.contentAspectX) * scale); + var scale = this._contentBoundsNoRotate.width; + return new $.Point( + imageX / this._contentSizeNoRotate.x * scale, + imageY / this._contentSizeNoRotate.x * scale); }, /** @@ -1124,8 +1124,8 @@ $.Viewport.prototype = { } var point = this._imageToViewportDelta(imageX, imageY); - point.x += this.homeBounds.x; - point.y += this.homeBounds.y; + point.x += this._contentBoundsNoRotate.x; + point.y += this._contentBoundsNoRotate.y; return point; }, @@ -1301,9 +1301,9 @@ $.Viewport.prototype = { $.console.error('[Viewport.viewportToImageZoom] is not accurate with multi-image.'); } - var imageWidth = this.contentSize.x; + var imageWidth = this._contentSizeNoRotate.x; var containerWidth = this._containerInnerSize.x; - var scale = this.homeBounds.width; + var scale = this._contentBoundsNoRotate.width; var viewportToImageZoomRatio = (containerWidth / imageWidth) * scale; return viewportZoom * viewportToImageZoomRatio; }, @@ -1325,9 +1325,9 @@ $.Viewport.prototype = { $.console.error('[Viewport.imageToViewportZoom] is not accurate with multi-image.'); } - var imageWidth = this.contentSize.x; + var imageWidth = this._contentSizeNoRotate.x; var containerWidth = this._containerInnerSize.x; - var scale = this.homeBounds.width; + var scale = this._contentBoundsNoRotate.width; var viewportToImageZoomRatio = (imageWidth / containerWidth) / scale; return imageZoom * viewportToImageZoomRatio; } diff --git a/test/modules/navigator.js b/test/modules/navigator.js index a3e929e3..c58560a6 100644 --- a/test/modules/navigator.js +++ b/test/modules/navigator.js @@ -78,9 +78,11 @@ if (navigator === null) { navigator = $(".navigator"); - navigatorScaleFactor = Math.min(navigator.width() / viewer.viewport.contentSize.x, navigator.height() / viewer.viewport.contentSize.y); - displayRegionWidth = viewer.viewport.contentSize.x * navigatorScaleFactor; - displayRegionHeight = viewer.viewport.contentSize.y * navigatorScaleFactor; + navigatorScaleFactor = Math.min( + navigator.width() / viewer.viewport._contentSize.x, + navigator.height() / viewer.viewport._contentSize.y); + displayRegionWidth = viewer.viewport._contentSize.x * navigatorScaleFactor; + displayRegionHeight = viewer.viewport._contentSize.y * navigatorScaleFactor; contentStartFromLeft = (navigator.width() - displayRegionWidth) / 2; contentStartFromTop = (navigator.height() - displayRegionHeight) / 2; } @@ -91,8 +93,8 @@ regionBoundsInPoints = new OpenSeadragon.Rect(expectedDisplayRegionXLocation, expectedDisplayRegionYLocation, expectedDisplayRegionWidth, expectedDisplayRegionHeight); if (debug) { - console.log('Image width: ' + viewer.viewport.contentSize.x + '\n' + - 'Image height: ' + viewer.viewport.contentSize.y + '\n' + + console.log('Image width: ' + viewer.viewport._contentSize.x + '\n' + + 'Image height: ' + viewer.viewport._contentSize.y + '\n' + 'navigator.width(): ' + navigator.width() + '\n' + 'navigator.height(): ' + navigator.height() + '\n' + 'navigatorScaleFactor: ' + navigatorScaleFactor + '\n' + diff --git a/test/modules/viewport.js b/test/modules/viewport.js index ff485395..70e980ed 100644 --- a/test/modules/viewport.js +++ b/test/modules/viewport.js @@ -305,7 +305,7 @@ var rect = testRects[i].times(viewport.getContainerSize()); viewport.resetContentSize(rect.getSize()); propEqual( - viewport.contentSize, + viewport._contentSize, rect.getSize(), "Reset content size correctly." );