From 56ddf8c9c336e6012806aed7b6e07892feadb6e6 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 11 Nov 2014 17:14:48 -0800 Subject: [PATCH 1/6] First steps for collection mode --- src/tiledimage.js | 32 +++++++++++-- src/viewer.js | 23 +++++---- src/world.js | 87 +++++++++++++++++++++++++++-------- test/demo/collections/main.js | 15 +++++- 4 files changed, 124 insertions(+), 33 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index cfe4a0b3..cf55f40c 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -39,6 +39,7 @@ * or {@link OpenSeadragon.Viewer#addTiledImage} instead. * @class TiledImage * @memberof OpenSeadragon + * @extends OpenSeadragon.EventSource * @classdesc Handles rendering of tiles for an {@link OpenSeadragon.Viewer}. * A new instance is created for each TileSource opened. * @param {Object} options - Configuration for this TiledImage. @@ -68,6 +69,8 @@ $.TiledImage = function( options ) { $.console.assert( options.imageLoader, "[TiledImage] options.imageLoader is required" ); $.console.assert( options.source, "[TiledImage] options.source is required" ); + $.EventSource.call( this ); + this._tileCache = options.tileCache; delete options.tileCache; @@ -128,7 +131,7 @@ $.TiledImage = function( options ) { }, options ); }; -$.TiledImage.prototype = /** @lends OpenSeadragon.TiledImage.prototype */{ +$.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.TiledImage.prototype */{ /** * @returns {Boolean} Whether the TiledImage is scheduled for an update at the * soonest possible opportunity. @@ -175,8 +178,30 @@ $.TiledImage.prototype = /** @lends OpenSeadragon.TiledImage.prototype */{ */ getContentSize: function() { return new $.Point(this.source.dimensions.x, this.source.dimensions.y); + }, + + /** + * @fires OpenSeadragon.TiledImage.event:bounds-changed + */ + setPosition: function(position) { + this._worldX = position.x; + this._worldY = position.y; + this.updateAgain = true; + this._raiseBoundsChanged(); + }, + + _raiseBoundsChanged: function() { + /** + * Raised when the TiledImage's bounds are changed. + * @event bounds-changed + * @memberOf OpenSeadragon.TiledImage + * @type {object} + * @property {OpenSeadragon.World} eventSource - A reference to the TiledImage which raised the event. + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + this.raiseEvent('bounds-changed'); } -}; +}); /** * @private @@ -794,8 +819,7 @@ function drawTiles( tiledImage, lastDrawn ){ viewer, viewport, position, - tileSource, - collectionTileSource; + tileSource; // We need a callback to give image manipulation a chance to happen var drawingHandler = function(args) { diff --git a/src/viewer.js b/src/viewer.js index 727cbf9d..8a801cc0 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -384,10 +384,6 @@ $.Viewer = function( options ) { }); this.world.addHandler('add-item', function(event) { - if (_this.viewport) { - _this.viewport.setHomeBounds(_this.world.getHomeBounds(), _this.world.getContentFactor()); - } - // For backwards compatibility, we maintain the source property _this.source = _this.world.getItemAt(0).source; @@ -399,10 +395,6 @@ $.Viewer = function( options ) { }); this.world.addHandler('remove-item', function(event) { - if (_this.viewport) { - _this.viewport.setHomeBounds(_this.world.getHomeBounds(), _this.world.getContentFactor()); - } - // For backwards compatibility, we maintain the source property if (_this.world.getItemCount()) { _this.source = _this.world.getItemAt(0).source; @@ -413,6 +405,12 @@ $.Viewer = function( options ) { THIS[ _this.hash ].forceRedraw = true; }); + this.world.addHandler('home-bounds-changed', function(event) { + if (_this.viewport) { + _this.viewport.setHomeBounds(_this.world.getHomeBounds(), _this.world.getContentFactor()); + } + }); + this.world.addHandler('item-index-changed', function(event) { // For backwards compatibility, we maintain the source property _this.source = _this.world.getItemAt(0).source; @@ -1301,6 +1299,15 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, index: options.index }); + if (_this.collectionMode) { + _this.world.layout({ + rows: _this.collectionRows, + layout: _this.collectionLayout, + tileSize: _this.collectionTileSize, + tileMargin: _this.collectionTileMargin + }); + } + if (_this.world.getItemCount() === 1 && !_this.preserveViewport) { _this.viewport.goHome(true); } diff --git a/src/world.js b/src/world.js index 3e4d3924..94045a49 100644 --- a/src/world.js +++ b/src/world.js @@ -61,6 +61,8 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W * @fires OpenSeadragon.World.event:add-item */ addItem: function( item, options ) { + var _this = this; + $.console.assert(item, "[World.addItem] item is required"); $.console.assert(item instanceof $.TiledImage, "[World.addItem] only TiledImages supported at this time"); @@ -75,6 +77,11 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W this._figureSizes(); this._needsUpdate = true; + // TODO: remove handler when removing item from world + item.addHandler('bounds-changed', function(event) { + _this._figureSizes(); + }); + /** * Raised when an item is added to the World. * @event add-item @@ -242,39 +249,79 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W return this._contentFactor; }, + layout: function(config) { + var layout = config.layout || $.DEFAULT_SETTINGS.collectionLayout; + var rows = config.rows || $.DEFAULT_SETTINGS.collectionRows; + var wrap = Math.ceil(this._items.length / rows); + var x = 0; + var y = 0; + for (var i = 0; i < this._items.length; i++) { + if (i && (i % wrap) === 0) { + if (layout === 'horizontal') { + y += 1; + x = 0; + } else { + x += 1; + y = 0; + } + } + + this._items[i].setPosition(new $.Point(x, y)); + + if (layout === 'horizontal') { + x += 1; + } else { + y += 1; + } + } + }, + // private _figureSizes: function() { + var oldHomeBounds = this._homeBounds ? this._homeBounds.clone() : null; + if ( !this._items.length ) { this._homeBounds = new $.Rect(0, 0, 1, 1); this._contentSize = new $.Point(1, 1); - return; + } else { + 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; + var bottom = bounds.y + bounds.height; + 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 / box.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 ); + } + + this._homeBounds = new $.Rect( left, top, right - left, bottom - top ); + this._contentSize = new $.Point(this._homeBounds.width * this._contentFactor, + this._homeBounds.height * this._contentFactor); } - 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; - var bottom = bounds.y + bounds.height; - 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 / box.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 ); + if (!this._homeBounds.equals(oldHomeBounds)) { + /** + * Raised when the home bounds change. + * @event home-bounds-changed + * @memberOf OpenSeadragon.World + * @type {object} + * @property {OpenSeadragon.World} eventSource - A reference to the World which raised the event. + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + this.raiseEvent('home-bounds-changed'); } - - this._homeBounds = new $.Rect( left, top, right - left, bottom - top ); - this._contentSize = new $.Point(this._homeBounds.width * this._contentFactor, - this._homeBounds.height * this._contentFactor); }, // private _raiseRemoveItem: function(item) { /** - * Raised when a item is removed. + * Raised when an item is removed. * @event remove-item * @memberOf OpenSeadragon.World * @type {object} diff --git a/test/demo/collections/main.js b/test/demo/collections/main.js index 927b777f..7ca4d8b0 100644 --- a/test/demo/collections/main.js +++ b/test/demo/collections/main.js @@ -16,6 +16,9 @@ // debugMode: true, zoomPerScroll: 1.02, showNavigator: testNavigator, + collectionMode: true, + collectionRows: 3, + collectionLayout: 'vertical', // wrapHorizontal: true, // wrapVertical: true, id: "contentDiv", @@ -96,7 +99,7 @@ } // this.crossTest3(); - this.basicTest(); + this.collectionTest(); }, // ---------- @@ -189,6 +192,16 @@ }); }, + // ---------- + collectionTest: function() { + var tileSources = []; + for (var i = 0; i < 10; i++) { + tileSources.push('../../data/testpattern.dzi'); + } + + this.viewer.open(tileSources); + }, + // ---------- gridTest: function() { var self = this; From c4c17db045048fb5df5e6ecb5f27a21b4c8f2d64 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 12 Nov 2014 15:48:38 -0800 Subject: [PATCH 2/6] Supporting collectionTileSize and collectionTileMargin --- src/openseadragon.js | 3 +++ src/tiledimage.js | 45 ++++++++++++++++++++++++++++++----- src/world.js | 27 +++++++++++++++++---- test/demo/collections/main.js | 12 +++++++++- 4 files changed, 75 insertions(+), 12 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index 14221847..211fd10d 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -538,6 +538,8 @@ * * @property {Number} [collectionTileSize=800] * + * @property {Number} [collectionTileMargin=80] + * * @property {String|Boolean} [crossOriginPolicy=false] * Valid values are 'Anonymous', 'use-credentials', and false. If false, canvas requests will * not use CORS, and the canvas will be tainted. @@ -984,6 +986,7 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ collectionLayout: 'horizontal', //vertical collectionMode: false, collectionTileSize: 800, + collectionTileMargin: 80, //PERFORMANCE SETTINGS imageLoaderLimit: 0, diff --git a/src/tiledimage.js b/src/tiledimage.js index cf55f40c..6e5e4a2a 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -89,7 +89,7 @@ $.TiledImage = function( options ) { this.normHeight = options.source.dimensions.y / options.source.dimensions.x; if ( options.width ) { - this._scale = options.width; + this._setScale(options.width); delete options.width; if ( options.height ) { @@ -97,15 +97,12 @@ $.TiledImage = function( options ) { delete options.height; } } else if ( options.height ) { - this._scale = options.height / this.normHeight; + this._setScale(options.height / this.normHeight); delete options.height; } else { - this._scale = 1; + this._setScale(1); } - this._worldWidth = this._scale; - this._worldHeight = this.normHeight * this._scale; - $.extend( true, this, { //internal state properties @@ -184,12 +181,48 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag * @fires OpenSeadragon.TiledImage.event:bounds-changed */ setPosition: function(position) { + if (this._worldX === position.x && this._worldY === position.y) { + return; + } + this._worldX = position.x; this._worldY = position.y; this.updateAgain = true; this._raiseBoundsChanged(); }, + /** + * @fires OpenSeadragon.TiledImage.event:bounds-changed + */ + setWidth: function(width) { + if (this._worldWidth === width) { + return; + } + + this._setScale(width); + this.updateAgain = true; + this._raiseBoundsChanged(); + }, + + /** + * @fires OpenSeadragon.TiledImage.event:bounds-changed + */ + setHeight: function(height) { + if (this._worldHeight === height) { + return; + } + + this._setScale(height / this.normHeight); + this.updateAgain = true; + this._raiseBoundsChanged(); + }, + + _setScale: function(scale) { + this._scale = scale; + this._worldWidth = this._scale; + this._worldHeight = this.normHeight * this._scale; + }, + _raiseBoundsChanged: function() { /** * Raised when the TiledImage's bounds are changed. diff --git a/src/world.js b/src/world.js index 94045a49..f4b3c134 100644 --- a/src/world.js +++ b/src/world.js @@ -252,26 +252,43 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W layout: function(config) { var layout = config.layout || $.DEFAULT_SETTINGS.collectionLayout; var rows = config.rows || $.DEFAULT_SETTINGS.collectionRows; + var tileSize = config.tileSize || $.DEFAULT_SETTINGS.collectionTileSize; + var tileMargin = config.tileMargin || $.DEFAULT_SETTINGS.collectionTileMargin; + var increment = tileSize + tileMargin; var wrap = Math.ceil(this._items.length / rows); var x = 0; var y = 0; + var item, box, width, height, position; for (var i = 0; i < this._items.length; i++) { if (i && (i % wrap) === 0) { if (layout === 'horizontal') { - y += 1; + y += increment; x = 0; } else { - x += 1; + x += increment; y = 0; } } - this._items[i].setPosition(new $.Point(x, y)); + item = this._items[i]; + box = item.getWorldBounds(); + if (box.width > box.height) { + width = tileSize; + } else { + width = tileSize * (box.width / box.height); + } + + height = width * (box.height / box.width); + position = new $.Point(x + ((tileSize - width) / 2), + y + ((tileSize - height) / 2)); + + item.setPosition(position); + item.setWidth(width); if (layout === 'horizontal') { - x += 1; + x += increment; } else { - y += 1; + y += increment; } } }, diff --git a/test/demo/collections/main.js b/test/demo/collections/main.js index 7ca4d8b0..426015b2 100644 --- a/test/demo/collections/main.js +++ b/test/demo/collections/main.js @@ -19,6 +19,8 @@ collectionMode: true, collectionRows: 3, collectionLayout: 'vertical', + // collectionTileSize: 10, + // collectionTileMargin: 10, // wrapHorizontal: true, // wrapVertical: true, id: "contentDiv", @@ -195,8 +197,16 @@ // ---------- collectionTest: function() { var tileSources = []; + var random; for (var i = 0; i < 10; i++) { - tileSources.push('../../data/testpattern.dzi'); + random = Math.random(); + if (random < 0.33) { + tileSources.push('../../data/testpattern.dzi'); + } else if (random < 0.66) { + tileSources.push('../../data/tall.dzi'); + } else { + tileSources.push('../../data/wide.dzi'); + } } this.viewer.open(tileSources); From 1ed80b0d27b83e34079b7a493fa322e35487cc8f Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 12 Nov 2014 16:31:46 -0800 Subject: [PATCH 3/6] Docs and naming changes for collection mode --- changelog.txt | 4 +- src/navigator.js | 2 +- src/openseadragon.js | 7 +++ src/tiledimage.js | 34 ++++++++---- src/tilesourcecollection.js | 105 +----------------------------------- src/viewer.js | 6 +-- src/world.js | 41 +++++++++----- test/multi-image.js | 4 +- 8 files changed, 69 insertions(+), 134 deletions(-) diff --git a/changelog.txt b/changelog.txt index 7cd7b667..4751327f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -19,9 +19,11 @@ OPENSEADRAGON CHANGELOG * DEPRECATION: use Drawer.clear and World.update instead of Drawer.update * DEPRECATION: the layersAspectRatioEpsilon option is no longer necessary * DEPRECATION: Viewer's add-layer event is now World's add-item event - * DEPRECATION: Viewer's layer-level-changed event is now World's item-index-changed event + * DEPRECATION: Viewer's layer-level-changed event is now World's item-index-change event * DEPRECATION: Viewer's remove-layer event is now World's remove-item event * DEPRECATION: Viewer's add-layer-failed event is now add-item-failed + * DEPRECATION: TileSourceCollection has been retired in favor of World + * DEPRECATION: collectionMode no longer draws outlines or reflections for items * Drawer has been split into three classes: * TiledImage, tile management and positioning for a single tiled image * TileCache, tile caching for all images diff --git a/src/navigator.js b/src/navigator.js index 5f1767cc..df4b637a 100644 --- a/src/navigator.js +++ b/src/navigator.js @@ -239,7 +239,7 @@ $.Navigator = function( options ){ } }); - viewer.world.addHandler("item-index-changed", function(event) { + viewer.world.addHandler("item-index-change", function(event) { var item = _this.world.getItemAt(event.previousIndex); _this.world.setItemIndex(item, event.newIndex); }); diff --git a/src/openseadragon.js b/src/openseadragon.js index 211fd10d..70aeca84 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -531,14 +531,21 @@ * @property {Number} [referenceStripSizeRatio=0.2] * * @property {Boolean} [collectionMode=false] + * Set to true to have the viewer arrange your TiledImages in a grid or line. * * @property {Number} [collectionRows=3] + * If collectionMode is true, specifies how many rows the grid should have. Use 1 to make a line. + * If collectionLayout is 'vertical', specifies how many columns instead. * * @property {String} [collectionLayout='horizontal'] + * If collectionMode is true, specifies whether to arrange vertically or horizontally. * * @property {Number} [collectionTileSize=800] + * If collectionMode is true, specifies the size, in world coordinates, for each TiledImage to fit into. + * The TiledImage will be centered within a square of the specified size. * * @property {Number} [collectionTileMargin=80] + * If collectionMode is true, specifies the margin, in world coordinates, between each TiledImage. * * @property {String|Boolean} [crossOriginPolicy=false] * Valid values are 'Anonymous', 'use-credentials', and false. If false, canvas requests will diff --git a/src/tiledimage.js b/src/tiledimage.js index 6e5e4a2a..7e0ee5ee 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -166,10 +166,16 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag /** * @returns {OpenSeadragon.Rect} This TiledImage's bounds in world coordinates. */ - getWorldBounds: function() { + getBounds: function() { return new $.Rect( this._worldX, this._worldY, this._worldWidth, this._worldHeight ); }, + // deprecated + getWorldBounds: function() { + $.console.error('[TiledImage.getWorldBounds] is deprecated; use TiledImage.getBounds instead'); + return this.getBounds(); + }, + /** * @returns {OpenSeadragon.Point} This TiledImage's content size, in original pixels. */ @@ -178,7 +184,9 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag }, /** - * @fires OpenSeadragon.TiledImage.event:bounds-changed + * Sets the TiledImage's position in the world. + * @param {OpenSeadragon.Point} position - The new position, in world coordinates. + * @fires OpenSeadragon.TiledImage.event:bounds-change */ setPosition: function(position) { if (this._worldX === position.x && this._worldY === position.y) { @@ -188,11 +196,13 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag this._worldX = position.x; this._worldY = position.y; this.updateAgain = true; - this._raiseBoundsChanged(); + this._raiseBoundsChange(); }, /** - * @fires OpenSeadragon.TiledImage.event:bounds-changed + * Sets the TiledImage's width in the world, adjusting the height to match based on aspect ratio. + * @param {Number} width - The new width, in world coordinates. + * @fires OpenSeadragon.TiledImage.event:bounds-change */ setWidth: function(width) { if (this._worldWidth === width) { @@ -201,11 +211,13 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag this._setScale(width); this.updateAgain = true; - this._raiseBoundsChanged(); + this._raiseBoundsChange(); }, /** - * @fires OpenSeadragon.TiledImage.event:bounds-changed + * Sets the TiledImage's height in the world, adjusting the width to match based on aspect ratio. + * @param {Number} height - The new height, in world coordinates. + * @fires OpenSeadragon.TiledImage.event:bounds-change */ setHeight: function(height) { if (this._worldHeight === height) { @@ -214,25 +226,27 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag this._setScale(height / this.normHeight); this.updateAgain = true; - this._raiseBoundsChanged(); + this._raiseBoundsChange(); }, + // private _setScale: function(scale) { this._scale = scale; this._worldWidth = this._scale; this._worldHeight = this.normHeight * this._scale; }, - _raiseBoundsChanged: function() { + // private + _raiseBoundsChange: function() { /** * Raised when the TiledImage's bounds are changed. - * @event bounds-changed + * @event bounds-change * @memberOf OpenSeadragon.TiledImage * @type {object} * @property {OpenSeadragon.World} eventSource - A reference to the TiledImage which raised the event. * @property {?Object} userData - Arbitrary subscriber-defined object. */ - this.raiseEvent('bounds-changed'); + this.raiseEvent('bounds-change'); } }); diff --git a/src/tilesourcecollection.js b/src/tilesourcecollection.js index 52dcbd67..a180aa52 100644 --- a/src/tilesourcecollection.js +++ b/src/tilesourcecollection.js @@ -34,110 +34,9 @@ (function( $ ){ -/** - * @class TileSourceCollection - * @memberof OpenSeadragon - * @extends OpenSeadragon.TileSource - */ +// deprecated $.TileSourceCollection = function( tileSize, tileSources, rows, layout ) { - var options; - - if( $.isPlainObject( tileSize ) ){ - options = tileSize; - }else{ - options = { - tileSize: arguments[ 0 ], - tileSources: arguments[ 1 ], - rows: arguments[ 2 ], - layout: arguments[ 3 ] - }; - } - - if( !options.layout ){ - options.layout = 'horizontal'; - } - - var minLevel = 0, - levelSize = 1.0, - tilesPerRow = Math.ceil( options.tileSources.length / options.rows ), - longSide = tilesPerRow >= options.rows ? - tilesPerRow : - options.rows; - - if( 'horizontal' == options.layout ){ - options.width = ( options.tileSize ) * tilesPerRow; - options.height = ( options.tileSize ) * options.rows; - } else { - options.height = ( options.tileSize ) * tilesPerRow; - options.width = ( options.tileSize ) * options.rows; - } - - options.tileOverlap = -options.tileMargin; - options.tilesPerRow = tilesPerRow; - - //Set min level to avoid loading sublevels since collection is a - //different kind of abstraction - - while( levelSize < ( options.tileSize ) * longSide ){ - //$.console.log( '%s levelSize %s minLevel %s', options.tileSize * longSide, levelSize, minLevel ); - levelSize = levelSize * 2.0; - minLevel++; - } - options.minLevel = minLevel; - - //for( var name in options ){ - // $.console.log( 'Collection %s %s', name, options[ name ] ); - //} - - $.TileSource.apply( this, [ options ] ); - + $.console.error('TileSourceCollection is deprecated; use World instead'); }; -$.extend( $.TileSourceCollection.prototype, $.TileSource.prototype, /** @lends OpenSeadragon.TileSourceCollection.prototype */{ - - /** - * @function - * @param {Number} level - * @param {Number} x - * @param {Number} y - */ - getTileBounds: function( level, x, y ) { - var dimensionsScaled = this.dimensions.times( this.getLevelScale( level ) ), - px = this.tileSize * x - this.tileOverlap, - py = this.tileSize * y - this.tileOverlap, - sx = this.tileSize + 1 * this.tileOverlap, - sy = this.tileSize + 1 * this.tileOverlap, - scale = 1.0 / dimensionsScaled.x; - - sx = Math.min( sx, dimensionsScaled.x - px ); - sy = Math.min( sy, dimensionsScaled.y - py ); - - return new $.Rect( px * scale, py * scale, sx * scale, sy * scale ); - }, - - /** - * - * @function - */ - configure: function( data, url ){ - return; - }, - - - /** - * @function - * @param {Number} level - * @param {Number} x - * @param {Number} y - */ - getTileUrl: function( level, x, y ) { - //$.console.log([ level, '/', x, '_', y ].join( '' )); - return null; - } - - - -}); - - }( OpenSeadragon )); diff --git a/src/viewer.js b/src/viewer.js index 8a801cc0..633237ce 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -405,13 +405,13 @@ $.Viewer = function( options ) { THIS[ _this.hash ].forceRedraw = true; }); - this.world.addHandler('home-bounds-changed', function(event) { + this.world.addHandler('home-bounds-change', function(event) { if (_this.viewport) { _this.viewport.setHomeBounds(_this.world.getHomeBounds(), _this.world.getContentFactor()); } }); - this.world.addHandler('item-index-changed', function(event) { + this.world.addHandler('item-index-change', function(event) { // For backwards compatibility, we maintain the source property _this.source = _this.world.getItemAt(0).source; }); @@ -1300,7 +1300,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, }); if (_this.collectionMode) { - _this.world.layout({ + _this.world.arrange({ rows: _this.collectionRows, layout: _this.collectionLayout, tileSize: _this.collectionTileSize, diff --git a/src/world.js b/src/world.js index f4b3c134..1340b5ce 100644 --- a/src/world.js +++ b/src/world.js @@ -59,6 +59,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W * @param {OpenSeadragon.TiledImage} item - The item to add. * @param {Number} [options.index] - Index for the item. If not specified, goes at the top. * @fires OpenSeadragon.World.event:add-item + * @fires OpenSeadragon.World.event:home-bounds-change */ addItem: function( item, options ) { var _this = this; @@ -78,7 +79,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W this._needsUpdate = true; // TODO: remove handler when removing item from world - item.addHandler('bounds-changed', function(event) { + item.addHandler('bounds-change', function(event) { _this._figureSizes(); }); @@ -127,7 +128,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W * Change the index of a item so that it appears over or under others. * @param {OpenSeadragon.TiledImage} item - The item to move. * @param {Number} index - The new index. - * @fires OpenSeadragon.World.event:item-index-changed + * @fires OpenSeadragon.World.event:item-index-change */ setItemIndex: function( item, index ) { $.console.assert(item, "[World.setItemIndex] item is required"); @@ -149,7 +150,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W /** * Raised when the order of the indexes has been changed. - * @event item-index-changed + * @event item-index-change * @memberOf OpenSeadragon.World * @type {object} * @property {OpenSeadragon.World} eventSource - A reference to the World which raised the event. @@ -159,7 +160,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W * @property {Number} newIndex - The new index of the item * @property {?Object} userData - Arbitrary subscriber-defined object. */ - this.raiseEvent( 'item-index-changed', { + this.raiseEvent( 'item-index-change', { item: item, previousIndex: oldIndex, newIndex: index @@ -170,6 +171,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W * Remove an item. * @param {OpenSeadragon.TiledImage} item - The item to remove. * @fires OpenSeadragon.World.event:remove-item + * @fires OpenSeadragon.World.event:home-bounds-change */ removeItem: function( item ) { $.console.assert(item, "[World.removeItem] item is required"); @@ -188,6 +190,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W /** * Remove all items. * @fires OpenSeadragon.World.event:remove-item + * @fires OpenSeadragon.World.event:home-bounds-change */ removeAll: function() { var removedItems = this._items; @@ -249,11 +252,21 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W return this._contentFactor; }, - layout: function(config) { - var layout = config.layout || $.DEFAULT_SETTINGS.collectionLayout; - var rows = config.rows || $.DEFAULT_SETTINGS.collectionRows; - var tileSize = config.tileSize || $.DEFAULT_SETTINGS.collectionTileSize; - var tileMargin = config.tileMargin || $.DEFAULT_SETTINGS.collectionTileMargin; + /** + * Arranges all of the TiledImages with the specified settings. + * @param {Object} options - Specifies how to arrange. + * @param {String} [options.layout] - See collectionLayout in {@link OpenSeadragon.Options}. + * @param {Number} [options.rows] - See collectionRows in {@link OpenSeadragon.Options}. + * @param {Number} [options.tileSize] - See collectionTileSize in {@link OpenSeadragon.Options}. + * @param {Number} [options.tileMargin] - See collectionTileMargin in {@link OpenSeadragon.Options}. + * @fires OpenSeadragon.World.event:home-bounds-change + */ + arrange: function(options) { + options = options || {}; + var layout = options.layout || $.DEFAULT_SETTINGS.collectionLayout; + var rows = options.rows || $.DEFAULT_SETTINGS.collectionRows; + var tileSize = options.tileSize || $.DEFAULT_SETTINGS.collectionTileSize; + var tileMargin = options.tileMargin || $.DEFAULT_SETTINGS.collectionTileMargin; var increment = tileSize + tileMargin; var wrap = Math.ceil(this._items.length / rows); var x = 0; @@ -271,7 +284,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W } item = this._items[i]; - box = item.getWorldBounds(); + box = item.getBounds(); if (box.width > box.height) { width = tileSize; } else { @@ -301,7 +314,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W this._homeBounds = new $.Rect(0, 0, 1, 1); this._contentSize = new $.Point(1, 1); } else { - var bounds = this._items[0].getWorldBounds(); + var bounds = this._items[0].getBounds(); this._contentFactor = this._items[0].getContentSize().x / bounds.width; var left = bounds.x; var top = bounds.y; @@ -309,7 +322,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W var bottom = bounds.y + bounds.height; var box; for ( var i = 1; i < this._items.length; i++ ) { - box = this._items[i].getWorldBounds(); + box = this._items[i].getBounds(); this._contentFactor = Math.max(this._contentFactor, this._items[i].getContentSize().x / box.width); left = Math.min( left, box.x ); top = Math.min( top, box.y ); @@ -325,13 +338,13 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W if (!this._homeBounds.equals(oldHomeBounds)) { /** * Raised when the home bounds change. - * @event home-bounds-changed + * @event home-bounds-change * @memberOf OpenSeadragon.World * @type {object} * @property {OpenSeadragon.World} eventSource - A reference to the World which raised the event. * @property {?Object} userData - Arbitrary subscriber-defined object. */ - this.raiseEvent('home-bounds-changed'); + this.raiseEvent('home-bounds-change'); } }, diff --git a/test/multi-image.js b/test/multi-image.js index 4975b9b4..f67f5a37 100644 --- a/test/multi-image.js +++ b/test/multi-image.js @@ -63,9 +63,9 @@ equal( viewer.world.getItemAt( 2 ), item2, "The item at index 2 should be the second added item." ); - viewer.world.addHandler( "item-index-changed", + viewer.world.addHandler( "item-index-change", function itemIndexChangedHandler( event ) { - viewer.world.removeHandler( "item-index-changed", + viewer.world.removeHandler( "item-index-change", itemIndexChangedHandler ); equal( event.item, item2, "The item which changed index should be item2" ); From 9347cfe692da28d03fb789050c4531200632e9f7 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 12 Nov 2014 16:44:11 -0800 Subject: [PATCH 4/6] Event handler cleanup for tiled images in world --- src/world.js | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/world.js b/src/world.js index 1340b5ce..5bc885ab 100644 --- a/src/world.js +++ b/src/world.js @@ -43,6 +43,8 @@ * @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this World. **/ $.World = function( options ) { + var _this = this; + $.console.assert( options.viewer, "[World] options.viewer is required" ); $.EventSource.call( this ); @@ -50,6 +52,10 @@ $.World = function( options ) { this.viewer = options.viewer; this._items = []; this._needsUpdate = false; + this._delegatedFigureSizes = function(event) { + _this._figureSizes(); + }; + this._figureSizes(); }; @@ -62,8 +68,6 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W * @fires OpenSeadragon.World.event:home-bounds-change */ addItem: function( item, options ) { - var _this = this; - $.console.assert(item, "[World.addItem] item is required"); $.console.assert(item instanceof $.TiledImage, "[World.addItem] only TiledImages supported at this time"); @@ -78,10 +82,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W this._figureSizes(); this._needsUpdate = true; - // TODO: remove handler when removing item from world - item.addHandler('bounds-change', function(event) { - _this._figureSizes(); - }); + item.addHandler('bounds-change', this._delegatedFigureSizes); /** * Raised when an item is added to the World. @@ -181,6 +182,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W return; } + item.removeHandler('bounds-change', this._delegatedFigureSizes); this._items.splice( index, 1 ); this._figureSizes(); this._needsUpdate = true; @@ -193,13 +195,20 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W * @fires OpenSeadragon.World.event:home-bounds-change */ removeAll: function() { + var item; + for (var i = 0; i < this._items.length; i++) { + item = this._items[i]; + item.removeHandler('bounds-change', this._delegatedFigureSizes); + } + var removedItems = this._items; this._items = []; this._figureSizes(); this._needsUpdate = true; - for (var i = 0; i < removedItems.length; i++) { - this._raiseRemoveItem(removedItems[i]); + for (i = 0; i < removedItems.length; i++) { + item = removedItems[i]; + this._raiseRemoveItem(item); } }, From b371af712ef4c026b05af05bb1e3f23a6f1fdecb Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 14 Nov 2014 15:49:42 -0800 Subject: [PATCH 5/6] Fixed broken test --- src/viewer.js | 4 +++- src/world.js | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/viewer.js b/src/viewer.js index 633237ce..2b2ea1f1 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -405,7 +405,7 @@ $.Viewer = function( options ) { THIS[ _this.hash ].forceRedraw = true; }); - this.world.addHandler('home-bounds-change', function(event) { + this.world.addHandler('metrics-change', function(event) { if (_this.viewport) { _this.viewport.setHomeBounds(_this.world.getHomeBounds(), _this.world.getContentFactor()); } @@ -436,6 +436,8 @@ $.Viewer = function( options ) { margins: this.viewportMargins }); + this.viewport.setHomeBounds(this.world.getHomeBounds(), this.world.getContentFactor()); + // Create the image loader this.imageLoader = new $.ImageLoader(); diff --git a/src/world.js b/src/world.js index 5bc885ab..9dc47b45 100644 --- a/src/world.js +++ b/src/world.js @@ -65,7 +65,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W * @param {OpenSeadragon.TiledImage} item - The item to add. * @param {Number} [options.index] - Index for the item. If not specified, goes at the top. * @fires OpenSeadragon.World.event:add-item - * @fires OpenSeadragon.World.event:home-bounds-change + * @fires OpenSeadragon.World.event:metrics-change */ addItem: function( item, options ) { $.console.assert(item, "[World.addItem] item is required"); @@ -172,7 +172,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W * Remove an item. * @param {OpenSeadragon.TiledImage} item - The item to remove. * @fires OpenSeadragon.World.event:remove-item - * @fires OpenSeadragon.World.event:home-bounds-change + * @fires OpenSeadragon.World.event:metrics-change */ removeItem: function( item ) { $.console.assert(item, "[World.removeItem] item is required"); @@ -192,7 +192,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W /** * Remove all items. * @fires OpenSeadragon.World.event:remove-item - * @fires OpenSeadragon.World.event:home-bounds-change + * @fires OpenSeadragon.World.event:metrics-change */ removeAll: function() { var item; @@ -268,7 +268,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W * @param {Number} [options.rows] - See collectionRows in {@link OpenSeadragon.Options}. * @param {Number} [options.tileSize] - See collectionTileSize in {@link OpenSeadragon.Options}. * @param {Number} [options.tileMargin] - See collectionTileMargin in {@link OpenSeadragon.Options}. - * @fires OpenSeadragon.World.event:home-bounds-change + * @fires OpenSeadragon.World.event:metrics-change */ arrange: function(options) { options = options || {}; @@ -318,6 +318,8 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W // private _figureSizes: function() { var oldHomeBounds = this._homeBounds ? this._homeBounds.clone() : null; + var oldContentSize = this._contentSize ? this._contentSize.clone() : null; + var oldContentFactor = this._contentFactor || 0; if ( !this._items.length ) { this._homeBounds = new $.Rect(0, 0, 1, 1); @@ -344,16 +346,17 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W this._homeBounds.height * this._contentFactor); } - if (!this._homeBounds.equals(oldHomeBounds)) { + if (this._contentFactor !== oldContentFactor || !this._homeBounds.equals(oldHomeBounds) || + !this._contentSize.equals(oldContentSize)) { /** - * Raised when the home bounds change. - * @event home-bounds-change + * Raised when the home bounds, content size, or content factor change. + * @event metrics-change * @memberOf OpenSeadragon.World * @type {object} * @property {OpenSeadragon.World} eventSource - A reference to the World which raised the event. * @property {?Object} userData - Arbitrary subscriber-defined object. */ - this.raiseEvent('home-bounds-change'); + this.raiseEvent('metrics-change', {}); } }, From cccee8bdf15c481e7a1ce94645e4ed0352c5f981 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 14 Nov 2014 15:54:41 -0800 Subject: [PATCH 6/6] Changelog --- changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.txt b/changelog.txt index 4751327f..8b7a991c 100644 --- a/changelog.txt +++ b/changelog.txt @@ -6,6 +6,7 @@ OPENSEADRAGON CHANGELOG * True multi-image mode (#450) * BREAKING CHANGE: Navigator no longer sends an open event when its viewer opens * BREAKING CHANGE: Viewer.drawers and Viewer.drawersContainer no longer exist + * BREAKING CHANGE: A Viewer's Drawer and Viewport are now made once per Viewer and reused for every image that Viewer opens (rather than being recreated for every open); this means if you change Viewer options between opens, the behavior is different now. * DEPRECATION: use Viewer.addTiledImage instead of Viewer.addLayer * addTiledImage supports positioning config properties * DEPRECATION: use World.getItemAt instead of Viewer.getLayerAtLevel