diff --git a/changelog.txt b/changelog.txt index e9c895f1..babc5979 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,8 @@ OPENSEADRAGON CHANGELOG ======================= +2.0.0: (in progress) + 1.2.0: (in progress) * New combined IIIF TileSource for 1.0 through 2.0 (#441) diff --git a/src/point.js b/src/point.js index 38aad1f1..0004426b 100644 --- a/src/point.js +++ b/src/point.js @@ -60,6 +60,13 @@ $.Point = function( x, y ) { }; $.Point.prototype = /** @lends OpenSeadragon.Point.prototype */{ + /** + * @function + * @returns {OpenSeadragon.Point} a duplicate of this Point + */ + clone: function() { + return new $.Point(this.x, this.y); + }, /** * Add another Point to this point and return a new Point. diff --git a/src/rectangle.js b/src/rectangle.js index 99172e7d..3a3ba547 100644 --- a/src/rectangle.js +++ b/src/rectangle.js @@ -75,6 +75,13 @@ $.Rect = function( x, y, width, height ) { }; $.Rect.prototype = /** @lends OpenSeadragon.Rect.prototype */{ + /** + * @function + * @returns {OpenSeadragon.Rect} a duplicate of this Rect + */ + clone: function() { + return new $.Rect(this.x, this.y, this.width, this.height); + }, /** * The aspect ratio is simply the ratio of width to height. diff --git a/src/tiledimage.js b/src/tiledimage.js index 46f1e3e3..31b30a0f 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -155,6 +155,10 @@ $.TiledImage.prototype = /** @lends OpenSeadragon.TiledImage.prototype */{ getWorldBounds: function() { return new $.Rect( this._worldX, this._worldY, this._worldWidth, this._worldHeight ); + }, + + getContentSize: function() { + return new $.Point(this.source.dimensions.x, this.source.dimensions.y); } }; diff --git a/src/viewer.js b/src/viewer.js index e872ee03..d8c99b52 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -333,7 +333,7 @@ $.Viewer = function( options ) { _this.viewport.applyConstraints(); return false; case 48://0|) - _this.goHome(); + _this.viewport.goHome(); _this.viewport.applyConstraints(); return false; case 119://w @@ -622,29 +622,6 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, this.element = null; }, - /** - * @function - * @param {Boolean} immediately - * @fires OpenSeadragon.Viewer.event:home - */ - goHome: function(immediately) { - /** - * Raised when the "home" operation occurs (see {@link OpenSeadragon.Viewport#goHome}). - * - * @event home - * @memberof OpenSeadragon.Viewer - * @type {object} - * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event. - * @property {Boolean} immediately - * @property {?Object} userData - Arbitrary subscriber-defined object. - */ - this.raiseEvent( 'home', { - immediately: immediately - }); - - this.viewport.fitBounds( this.world.getHomeBounds(), immediately ); - }, - /** * @function * @return {Boolean} @@ -1137,6 +1114,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, debugGridColor: _this.debugGridColor }); _this.world.addItem( tiledImage ); + _this.viewport.setHomeBounds(_this.world.getHomeBounds(), _this.world.getContentFactor()); if ( options.level !== undefined ) { _this.world.setItemLevel( tiledImage, options.level ); } @@ -1868,7 +1846,6 @@ function openTileSource( viewer, source, options ) { collectionMode: true, collectionTileSource: _this.source, containerSize: THIS[ _this.hash ].prevContainerSize, - contentSize: _this.source.dimensions, springStiffness: _this.springStiffness, animationTime: _this.animationTime, showNavigator: false, @@ -1886,7 +1863,6 @@ function openTileSource( viewer, source, options ) { } _this.viewport = _this.viewport ? _this.viewport : new $.Viewport({ containerSize: THIS[ _this.hash ].prevContainerSize, - contentSize: _this.source.dimensions, springStiffness: _this.springStiffness, animationTime: _this.animationTime, minZoomImageRatio: _this.minZoomImageRatio, @@ -1903,9 +1879,10 @@ function openTileSource( viewer, source, options ) { }); } - if( _this.preserveViewport ){ - _this.viewport.resetContentSize( _this.source.dimensions ); - } + // TODO: what to do about this? + // if( _this.preserveViewport ){ + // _this.viewport.resetContentSize( _this.source.dimensions ); + // } _this.source.overlays = _this.source.overlays || []; @@ -1952,7 +1929,8 @@ function openTileSource( viewer, source, options ) { }); _this.world.addItem( tiledImage ); - _this.goHome( true ); + _this.viewport.setHomeBounds(_this.world.getHomeBounds(), _this.world.getContentFactor()); + _this.viewport.goHome( true ); // Now that we have a drawer, see if it supports rotate. If not we need to remove the rotate buttons if (!_this.drawer.canRotate()) { @@ -2818,7 +2796,7 @@ function lightUp() { function onHome() { - this.goHome(); + this.viewport.goHome(); } diff --git a/src/viewport.js b/src/viewport.js index 48fa11a2..6a4c4156 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -104,43 +104,82 @@ $.Viewport = function( options ) { animationTime: this.animationTime }); - this.resetContentSize( this.contentSize ); + if (this.contentSize) { + this.resetContentSize( this.contentSize ); + } else { + this.setHomeBounds(new $.Rect(0, 0, 1, 1), 1); + } + + this.goHome( true ); this.update(); }; $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ - /** + * Updates the viewport's home bounds and constraints for the given content size. * @function + * @param {OpenSeadragon.Point} contentSize - size of the content in content units * @return {OpenSeadragon.Viewport} Chainable. * @fires OpenSeadragon.Viewer.event:reset-size */ resetContentSize: function( contentSize ){ - this.contentSize = 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); + return this; + }, + + /** + * Updates the viewport's home bounds and constraints. + * @function + * @param {OpenSeadragon.Rect} bounds - the new bounds in world coordinates + * @param {Number} contentFactor - how many content units per world unit + * @fires OpenSeadragon.Viewer.event:reset-size + */ + 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"); + + this.homeBounds = bounds.clone(); + this.contentSize = this.homeBounds.getSize().times(contentFactor); this.contentAspectX = this.contentSize.x / this.contentSize.y; this.contentAspectY = this.contentSize.y / this.contentSize.x; - this.fitWidthBounds = new $.Rect( 0, 0, 1, this.contentAspectY ); - this.fitHeightBounds = new $.Rect( 0, 0, this.contentAspectY, this.contentAspectY); - this.homeBounds = new $.Rect( 0, 0, 1, this.contentAspectY ); + // TODO: seems like fitWidthBounds and fitHeightBounds should be thin slices + // across the appropriate axis, centered in the image, rather than what we have + // here. + this.fitWidthBounds = new $.Rect(this.homeBounds.x, this.homeBounds.y, + this.homeBounds.width, this.homeBounds.width); + + this.fitHeightBounds = new $.Rect(this.homeBounds.x, this.homeBounds.y, + this.homeBounds.height, this.homeBounds.height); if( this.viewer ){ /** - * Raised when the viewer's content size is reset (see {@link OpenSeadragon.Viewport#resetContentSize}). + * Raised when the viewer's content size or home bounds are reset + * (see {@link OpenSeadragon.Viewport#resetContentSize}, + * {@link OpenSeadragon.Viewport#setHomeBounds}). * * @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 {Number} contentFactor * @property {?Object} userData - Arbitrary subscriber-defined object. */ this.viewer.raiseEvent( 'reset-size', { - contentSize: contentSize + contentSize: this.contentSize.clone(), + contentFactor: contentFactor, + homeBounds: this.homeBounds.clone() }); } - - return this; }, /** @@ -153,9 +192,11 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ if( this.defaultZoomLevel ){ return this.defaultZoomLevel; } else { - return ( aspectFactor >= 1 ) ? + var output = ( aspectFactor >= 1 ) ? 1 : aspectFactor; + + return output / this.homeBounds.width; } }, @@ -163,29 +204,31 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ * @function */ getHomeBounds: function() { - var center = this.homeBounds.getCenter( ), - width = 1.0 / this.getHomeZoom( ), - height = width / this.getAspectRatio(); - - return new $.Rect( - center.x - ( width / 2.0 ), - center.y - ( height / 2.0 ), - width, - height - ); + return this.homeBounds.clone(); }, /** * @function - * @private + * @param {Boolean} immediately + * @fires OpenSeadragon.Viewer.event:home */ goHome: function( immediately ) { - $.console.error("[Viewport.goHome] this function is deprecated; use Viewer.goHome instead"); if( this.viewer ){ - this.viewer.goHome(immediately); + /** + * Raised when the "home" operation occurs (see {@link OpenSeadragon.Viewport#goHome}). + * + * @event home + * @memberof OpenSeadragon.Viewer + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event. + * @property {Boolean} immediately + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + this.viewer.raiseEvent( 'home', { + immediately: immediately + }); } - - return this; + return this.fitBounds( this.getHomeBounds(), immediately ); }, /** @@ -204,9 +247,11 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ * @function */ getMaxZoom: function() { - var zoom = this.maxZoomLevel ? - this.maxZoomLevel : - ( this.contentSize.x * this.maxZoomPixelRatio / this.containerSize.x ); + var zoom = this.maxZoomLevel; + if (!zoom) { + zoom = this.contentSize.x * this.maxZoomPixelRatio / this.containerSize.x; + zoom /= this.homeBounds.width; + } return Math.max( zoom, this.getHomeZoom() ); }, @@ -335,9 +380,9 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ verticalThreshold = this.visibilityRatio * newBounds.height; left = newBounds.x + newBounds.width; - right = 1 - newBounds.x; + right = this.homeBounds.width - newBounds.x; top = newBounds.y + newBounds.height; - bottom = this.contentAspectY - newBounds.y; + bottom = this.homeBounds.height - newBounds.y; if ( this.wrapHorizontal ) { //do nothing @@ -368,11 +413,11 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ if ( dx || dy || immediately ) { newBounds.x += dx; newBounds.y += dy; - if( newBounds.width > 1 ){ - newBounds.x = 0.5 - newBounds.width/2; + if( newBounds.width > this.homeBounds.width ){ + newBounds.x = this.homeBounds.width / 2 - newBounds.width/2; } - if( newBounds.height > this.contentAspectY ){ - newBounds.y = this.contentAspectY/2 - newBounds.height/2; + if( newBounds.height > this.homeBounds.height){ + newBounds.y = this.homeBounds.height / 2 - newBounds.height/2; } } @@ -401,10 +446,6 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ * @fires OpenSeadragon.Viewer.event:constrain */ applyConstraints: function( immediately ) { - if (true) { - return; // TEMP - } - var actualZoom = this.getZoom(), constrainedZoom = Math.max( Math.min( actualZoom, this.getMaxZoom() ), diff --git a/src/world.js b/src/world.js index 8eeb3141..1b2245b3 100644 --- a/src/world.js +++ b/src/world.js @@ -43,11 +43,13 @@ $.World = function( options ) { this.viewer = options.viewer; this._items = []; + this._figureSizes(); }; $.World.prototype = /** @lends OpenSeadragon.World.prototype */{ addItem: function( item ) { this._items.push( item ); + this._figureSizes(); }, /** @@ -132,6 +134,8 @@ $.World.prototype = /** @lends OpenSeadragon.World.prototype */{ } this._items.splice( index, 1 ); + this._figureSizes(); + /** * Raised when a layer is removed. * @event remove-layer @@ -167,11 +171,26 @@ $.World.prototype = /** @lends OpenSeadragon.World.prototype */{ }, getHomeBounds: function() { + return this._homeBounds.clone(); + }, + + getContentSize: function() { + return this._contentSize.clone(); + }, + + getContentFactor: function() { + return this._contentFactor; + }, + + _figureSizes: function() { if ( !this._items.length ) { - return new $.Rect(0, 0, 1, 1); + this._homeBounds = new $.Rect(0, 0, 1, 1); + this._contentSize = new $.Point(1, 1); + return; } var bounds = this._items[0].getWorldBounds(); + this._contentFactor = this._items[0].getContentSize().x / bounds.width; var left = bounds.x; var top = bounds.y; var right = bounds.x + bounds.width; @@ -179,13 +198,16 @@ $.World.prototype = /** @lends OpenSeadragon.World.prototype */{ var box; for ( var i = 1; i < this._items.length; i++ ) { box = this._items[i].getWorldBounds(); + this._contentFactor = Math.max(this._contentFactor, this._items[i].getContentSize().x / bounds.width); left = Math.min( left, box.x ); top = Math.min( top, box.y ); right = Math.max( right, box.x + box.width ); bottom = Math.max( bottom, box.y + box.height ); } - return new $.Rect( left, top, right - left, bottom - top ); + this._homeBounds = new $.Rect( left, top, right - left, bottom - top ); + this._contentSize = new $.Point(this._homeBounds.width * this._contentFactor, + this._homeBounds.height * this._contentFactor); } }; diff --git a/test/demo/collections/main.js b/test/demo/collections/main.js index 0f78ab4f..03547079 100644 --- a/test/demo/collections/main.js +++ b/test/demo/collections/main.js @@ -1,6 +1,8 @@ +/* globals $, App */ + (function() { - var App = { + window.App = { init: function() { var self = this; @@ -38,7 +40,7 @@ var addLayerHandler = function( event ) { if ( event.options === options ) { self.viewer.removeHandler( "add-layer", addLayerHandler ); - self.viewer.goHome(); + self.viewer.viewport.goHome(); } }; this.viewer.addHandler( "add-layer", addLayerHandler );