From 2a5fd0b0f7ec1aa05a270e80692902272bcca4c7 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 21 Nov 2014 15:18:25 -0800 Subject: [PATCH 1/6] Fixed "toImage" converters --- src/viewer.js | 1 + src/viewport.js | 28 ++++++++++++++++++---------- test/demo/collections/main.js | 8 +++++++- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/viewer.js b/src/viewer.js index 2f1684de..2f5f697e 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -608,6 +608,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, if (successes) { if (_this._firstOpen || !_this.preserveViewport) { _this.viewport.goHome( true ); + _this.viewport.update(); } _this._firstOpen = false; diff --git a/src/viewport.js b/src/viewport.js index 1f07ebbe..167e0eca 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -953,7 +953,10 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ //they passed a point instead of individual components return this.viewportToImageCoordinates( viewerX.x, viewerX.y ); } - return new $.Point( viewerX * this.contentSize.x, viewerY * this.contentSize.y * this.contentAspectX ); + + var scale = this.homeBounds.width; + return new $.Point((viewerX - this.homeBounds.x) * (this.contentSize.x / scale), + (viewerY - this.homeBounds.y) * ((this.contentSize.y * this.contentAspectX) / scale)); }, /** @@ -971,7 +974,10 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ //they passed a point instead of individual components return this.imageToViewportCoordinates( imageX.x, imageX.y ); } - return new $.Point( imageX / this.contentSize.x, imageY / this.contentSize.y / this.contentAspectX ); + + var scale = this.homeBounds.width; + return new $.Point(this.homeBounds.x + ((imageX / this.contentSize.x) * scale), + this.homeBounds.y + ((imageY / this.contentSize.y / this.contentAspectX) * scale)); }, /** @@ -1003,13 +1009,13 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ imageX, imageY ); coordB = this.imageToViewportCoordinates( - pixelWidth, pixelHeight + imageX + pixelWidth, imageY + pixelHeight ); return new $.Rect( coordA.x, coordA.y, - coordB.x, - coordB.y + coordB.x - coordA.x, + coordB.y - coordA.y ); }, @@ -1039,12 +1045,12 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ ); } coordA = this.viewportToImageCoordinates( viewerX, viewerY ); - coordB = this.viewportToImageCoordinates( pointWidth, pointHeight ); + coordB = this.viewportToImageCoordinates(viewerX + pointWidth, viewerY + pointHeight); return new $.Rect( coordA.x, coordA.y, - coordB.x, - coordB.y + coordB.x - coordA.x, + coordB.y - coordA.y ); }, @@ -1148,7 +1154,8 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ viewportToImageZoom: function( viewportZoom ) { var imageWidth = this.viewer.source.dimensions.x; var containerWidth = this._containerInnerSize.x; - var viewportToImageZoomRatio = containerWidth / imageWidth; + var scale = this.homeBounds.width; + var viewportToImageZoomRatio = (containerWidth / imageWidth) * scale; return viewportZoom * viewportToImageZoomRatio; }, @@ -1166,7 +1173,8 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ imageToViewportZoom: function( imageZoom ) { var imageWidth = this.viewer.source.dimensions.x; var containerWidth = this._containerInnerSize.x; - var viewportToImageZoomRatio = imageWidth / containerWidth; + var scale = this.homeBounds.width; + var viewportToImageZoomRatio = (imageWidth / containerWidth) / scale; return imageZoom * viewportToImageZoomRatio; } }; diff --git a/test/demo/collections/main.js b/test/demo/collections/main.js index 669d4fdb..b2c7eeb8 100644 --- a/test/demo/collections/main.js +++ b/test/demo/collections/main.js @@ -7,7 +7,7 @@ var self = this; var testInitialOpen = true; - var testOverlays = true; + var testOverlays = false; var testMargins = false; var testNavigator = false; var margins; @@ -34,6 +34,12 @@ if (testInitialOpen) { config.tileSources = [ + { + tileSource: "../../data/testpattern.dzi", + x: 4, + y: 2, + width: 2 + }, { tileSource: "../../data/tall.dzi", x: 1.5, From d5c345970cbc18e546c52d9fa14edb7ddaa33069 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 21 Nov 2014 16:32:04 -0800 Subject: [PATCH 2/6] Coordinate conversion functions for TiledImage --- openseadragon.sublime-project | 5 +- src/tiledimage.js | 117 ++++++++++++++++++++++++++++++++++ src/viewport.js | 32 +++++++++- 3 files changed, 150 insertions(+), 4 deletions(-) diff --git a/openseadragon.sublime-project b/openseadragon.sublime-project index 1d6484e7..4feb36d8 100644 --- a/openseadragon.sublime-project +++ b/openseadragon.sublime-project @@ -8,7 +8,8 @@ "*.sublime-workspace" ], "folder_exclude_patterns": [ - "node_modules" + "node_modules", + "coverage" ] } ], @@ -17,5 +18,5 @@ "tab_size": 4, "translate_tabs_to_spaces": true, "trim_trailing_white_space_on_save": true - } + } } diff --git a/src/tiledimage.js b/src/tiledimage.js index 40726dbd..c345765b 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -87,6 +87,7 @@ $.TiledImage = function( options ) { // Ratio of zoomable image height to width. this.normHeight = options.source.dimensions.y / options.source.dimensions.x; + this.contentAspectX = options.source.dimensions.x / options.source.dimensions.y; if ( options.width ) { this._setScale(options.width); @@ -183,6 +184,122 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag return new $.Point(this.source.dimensions.x, this.source.dimensions.y); }, + /** + * Translates from OpenSeadragon viewer coordinate system to image coordinate system. + * This method can be called either by passing X,Y coordinates or an + * OpenSeadragon.Point + * @function + * @param {OpenSeadragon.Point} viewerX the point in viewport coordinate system. + * @param {Number} viewerX X coordinate in viewport coordinate system. + * @param {Number} viewerY Y coordinate in viewport coordinate system. + * @return {OpenSeadragon.Point} a point representing the coordinates in the image. + */ + viewportToImageCoordinates: function( viewerX, viewerY ) { + if ( arguments.length == 1 ) { + //they passed a point instead of individual components + return this.viewportToImageCoordinates( viewerX.x, viewerX.y ); + } + + var contentSize = this.source.dimensions; + return new $.Point((viewerX - this._worldX) * (contentSize.x / this._scale), + (viewerY - this._worldY) * ((contentSize.y * this.contentAspectX) / this._scale)); + }, + + /** + * Translates from image coordinate system to OpenSeadragon viewer coordinate system + * This method can be called either by passing X,Y coordinates or an + * OpenSeadragon.Point + * @function + * @param {OpenSeadragon.Point} imageX the point in image coordinate system. + * @param {Number} imageX X coordinate in image coordinate system. + * @param {Number} imageY Y coordinate in image coordinate system. + * @return {OpenSeadragon.Point} a point representing the coordinates in the viewport. + */ + imageToViewportCoordinates: function( imageX, imageY ) { + if ( arguments.length == 1 ) { + //they passed a point instead of individual components + return this.imageToViewportCoordinates( imageX.x, imageX.y ); + } + + var contentSize = this.source.dimensions; + return new $.Point(this._worldX + ((imageX / contentSize.x) * this._scale), + this._worldY + ((imageY / contentSize.y / this.contentAspectX) * this._scale)); + }, + + /** + * Translates from a rectangle which describes a portion of the image in + * pixel coordinates to OpenSeadragon viewport rectangle coordinates. + * This method can be called either by passing X,Y,width,height or an + * OpenSeadragon.Rect + * @function + * @param {OpenSeadragon.Rect} imageX the rectangle in image coordinate system. + * @param {Number} imageX the X coordinate of the top left corner of the rectangle + * in image coordinate system. + * @param {Number} imageY the Y coordinate of the top left corner of the rectangle + * in image coordinate system. + * @param {Number} pixelWidth the width in pixel of the rectangle. + * @param {Number} pixelHeight the height in pixel of the rectangle. + */ + imageToViewportRectangle: function( imageX, imageY, pixelWidth, pixelHeight ) { + var coordA, + coordB, + rect; + if( arguments.length == 1 ) { + //they passed a rectangle instead of individual components + rect = imageX; + return this.imageToViewportRectangle( + rect.x, rect.y, rect.width, rect.height + ); + } + coordA = this.imageToViewportCoordinates( + imageX, imageY + ); + coordB = this.imageToViewportCoordinates( + imageX + pixelWidth, imageY + pixelHeight + ); + return new $.Rect( + coordA.x, + coordA.y, + coordB.x - coordA.x, + coordB.y - coordA.y + ); + }, + + /** + * Translates from a rectangle which describes a portion of + * the viewport in point coordinates to image rectangle coordinates. + * This method can be called either by passing X,Y,width,height or an + * OpenSeadragon.Rect + * @function + * @param {OpenSeadragon.Rect} viewerX the rectangle in viewport coordinate system. + * @param {Number} viewerX the X coordinate of the top left corner of the rectangle + * in viewport coordinate system. + * @param {Number} imageY the Y coordinate of the top left corner of the rectangle + * in viewport coordinate system. + * @param {Number} pointWidth the width of the rectangle in viewport coordinate system. + * @param {Number} pointHeight the height of the rectangle in viewport coordinate system. + */ + viewportToImageRectangle: function( viewerX, viewerY, pointWidth, pointHeight ) { + var coordA, + coordB, + rect; + if ( arguments.length == 1 ) { + //they passed a rectangle instead of individual components + rect = viewerX; + return this.viewportToImageRectangle( + rect.x, rect.y, rect.width, rect.height + ); + } + coordA = this.viewportToImageCoordinates( viewerX, viewerY ); + coordB = this.viewportToImageCoordinates(viewerX + pointWidth, viewerY + pointHeight); + return new $.Rect( + coordA.x, + coordA.y, + coordB.x - coordA.x, + coordB.y - coordA.y + ); + }, + /** * Sets the TiledImage's position in the world. * @param {OpenSeadragon.Point} position - The new position, in world coordinates. diff --git a/src/viewport.js b/src/viewport.js index 167e0eca..c56ef7f0 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -942,6 +942,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ * Translates from OpenSeadragon viewer coordinate system to image coordinate system. * This method can be called either by passing X,Y coordinates or an * OpenSeadragon.Point + * Note: not accurate with multi-image; use TiledImage.viewportToImageCoordinates instead. * @function * @param {OpenSeadragon.Point} viewerX the point in viewport coordinate system. * @param {Number} viewerX X coordinate in viewport coordinate system. @@ -954,6 +955,10 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ return this.viewportToImageCoordinates( viewerX.x, viewerX.y ); } + if (this.viewer && this.viewer.world.getItemCount() > 1) { + $.console.error('[Viewport.viewportToImageCoordinates] is not accurate with multi-image; use TiledImage.viewportToImageCoordinates instead.'); + } + var scale = this.homeBounds.width; return new $.Point((viewerX - this.homeBounds.x) * (this.contentSize.x / scale), (viewerY - this.homeBounds.y) * ((this.contentSize.y * this.contentAspectX) / scale)); @@ -963,6 +968,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ * Translates from image coordinate system to OpenSeadragon viewer coordinate system * This method can be called either by passing X,Y coordinates or an * OpenSeadragon.Point + * Note: not accurate with multi-image; use TiledImage.imageToViewportCoordinates instead. * @function * @param {OpenSeadragon.Point} imageX the point in image coordinate system. * @param {Number} imageX X coordinate in image coordinate system. @@ -975,6 +981,10 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ return this.imageToViewportCoordinates( imageX.x, imageX.y ); } + if (this.viewer && this.viewer.world.getItemCount() > 1) { + $.console.error('[Viewport.imageToViewportCoordinates] is not accurate with multi-image; use TiledImage.imageToViewportCoordinates instead.'); + } + var scale = this.homeBounds.width; return new $.Point(this.homeBounds.x + ((imageX / this.contentSize.x) * scale), this.homeBounds.y + ((imageY / this.contentSize.y / this.contentAspectX) * scale)); @@ -985,6 +995,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ * pixel coordinates to OpenSeadragon viewport rectangle coordinates. * This method can be called either by passing X,Y,width,height or an * OpenSeadragon.Rect + * Note: not accurate with multi-image; use TiledImage.imageToViewportRectangle instead. * @function * @param {OpenSeadragon.Rect} imageX the rectangle in image coordinate system. * @param {Number} imageX the X coordinate of the top left corner of the rectangle @@ -1005,6 +1016,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ rect.x, rect.y, rect.width, rect.height ); } + coordA = this.imageToViewportCoordinates( imageX, imageY ); @@ -1024,6 +1036,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ * the viewport in point coordinates to image rectangle coordinates. * This method can be called either by passing X,Y,width,height or an * OpenSeadragon.Rect + * Note: not accurate with multi-image; use TiledImage.viewportToImageRectangle instead. * @function * @param {OpenSeadragon.Rect} viewerX the rectangle in viewport coordinate system. * @param {Number} viewerX the X coordinate of the top left corner of the rectangle @@ -1044,6 +1057,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ rect.x, rect.y, rect.width, rect.height ); } + coordA = this.viewportToImageCoordinates( viewerX, viewerY ); coordB = this.viewportToImageCoordinates(viewerX + pointWidth, viewerY + pointHeight); return new $.Rect( @@ -1057,6 +1071,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ /** * Convert pixel coordinates relative to the viewer element to image * coordinates. + * Note: not accurate with multi-image. * @param {OpenSeadragon.Point} pixel * @returns {OpenSeadragon.Point} */ @@ -1068,6 +1083,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ /** * Convert pixel coordinates relative to the image to * viewer element coordinates. + * Note: not accurate with multi-image. * @param {OpenSeadragon.Point} pixel * @returns {OpenSeadragon.Point} */ @@ -1078,6 +1094,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ /** * Convert pixel coordinates relative to the window to image coordinates. + * Note: not accurate with multi-image. * @param {OpenSeadragon.Point} pixel * @returns {OpenSeadragon.Point} */ @@ -1089,6 +1106,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ /** * Convert image coordinates to pixel coordinates relative to the window. + * Note: not accurate with multi-image. * @param {OpenSeadragon.Point} pixel * @returns {OpenSeadragon.Point} */ @@ -1146,13 +1164,18 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ * 1 means original image size, 0.5 half size... * Viewport zoom: ratio of the displayed image's width to viewport's width. * 1 means identical width, 2 means image's width is twice the viewport's width... + * Note: not accurate with multi-image. * @function * @param {Number} viewportZoom The viewport zoom * target zoom. * @returns {Number} imageZoom The image zoom */ viewportToImageZoom: function( viewportZoom ) { - var imageWidth = this.viewer.source.dimensions.x; + if (this.viewer && this.viewer.world.getItemCount() > 1) { + $.console.error('[Viewport.viewportToImageZoom] is not accurate with multi-image.'); + } + + var imageWidth = this.contentSize.x; var containerWidth = this._containerInnerSize.x; var scale = this.homeBounds.width; var viewportToImageZoomRatio = (containerWidth / imageWidth) * scale; @@ -1165,13 +1188,18 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ * 1 means original image size, 0.5 half size... * Viewport zoom: ratio of the displayed image's width to viewport's width. * 1 means identical width, 2 means image's width is twice the viewport's width... + * Note: not accurate with multi-image. * @function * @param {Number} imageZoom The image zoom * target zoom. * @returns {Number} viewportZoom The viewport zoom */ imageToViewportZoom: function( imageZoom ) { - var imageWidth = this.viewer.source.dimensions.x; + if (this.viewer && this.viewer.world.getItemCount() > 1) { + $.console.error('[Viewport.imageToViewportZoom] is not accurate with multi-image.'); + } + + var imageWidth = this.contentSize.x; var containerWidth = this._containerInnerSize.x; var scale = this.homeBounds.width; var viewportToImageZoomRatio = (imageWidth / containerWidth) / scale; From b8a1762a95a5e4375fe6cd2e452877d118cc36dc Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 21 Nov 2014 17:05:14 -0800 Subject: [PATCH 3/6] Unit tests for TiledImage unit conversion --- test/modules/tiledimage.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/test/modules/tiledimage.js b/test/modules/tiledimage.js index 186d6686..4901de01 100644 --- a/test/modules/tiledimage.js +++ b/test/modules/tiledimage.js @@ -1,4 +1,4 @@ -/* global module, asyncTest, $, ok, equal, notEqual, start, test, Util, testLog */ +/* global module, asyncTest, $, ok, equal, notEqual, start, test, Util, testLog, propEqual */ (function() { var viewer; @@ -45,6 +45,20 @@ checkBounds(image, new OpenSeadragon.Rect(5, 6, 10, 40), 'initial bounds'); + var scale = image.getContentSize().x / image.getBounds().width; + var viewportPoint = new OpenSeadragon.Point(10, 11); + var imagePoint = viewportPoint.minus(image.getBounds().getTopLeft()).times(scale); + + propEqual(image.viewportToImageCoordinates(viewportPoint), imagePoint, 'viewportToImageCoordinates'); + propEqual(image.imageToViewportCoordinates(imagePoint), viewportPoint, 'imageToViewportCoordinates'); + + var viewportRect = new OpenSeadragon.Rect(viewportPoint.x, viewportPoint.y, 6, 7); + var imageRect = new OpenSeadragon.Rect(imagePoint.x, imagePoint.y, + viewportRect.width * scale, viewportRect.height * scale); + + propEqual(image.viewportToImageRectangle(viewportRect), imageRect, 'viewportToImageRectangle'); + propEqual(image.imageToViewportRectangle(imageRect), viewportRect, 'imageToViewportRectangle'); + image.addHandler('bounds-change', function boundsChangeHandler(event) { image.removeHandler('bounds-change', boundsChangeHandler); handlerCount++; From 66517dab8de289c88592550b56ca9630f299527c Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 24 Nov 2014 11:46:33 -0800 Subject: [PATCH 4/6] Coordinate conversion rounding errors were causing test breakages; fixed --- src/tiledimage.js | 37 ++++++++++++++++++++++++------------- src/viewport.js | 37 +++++++++++++++++++++++++------------ 2 files changed, 49 insertions(+), 25 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index c345765b..446adb4a 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -184,6 +184,12 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag return new $.Point(this.source.dimensions.x, this.source.dimensions.y); }, + // private + _viewportToImageDelta: function( viewerX, viewerY ) { + return new $.Point(viewerX * (this.source.dimensions.x / this._scale), + viewerY * ((this.source.dimensions.y * this.contentAspectX) / this._scale)); + }, + /** * Translates from OpenSeadragon viewer coordinate system to image coordinate system. * This method can be called either by passing X,Y coordinates or an @@ -200,9 +206,13 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag return this.viewportToImageCoordinates( viewerX.x, viewerX.y ); } - var contentSize = this.source.dimensions; - return new $.Point((viewerX - this._worldX) * (contentSize.x / this._scale), - (viewerY - this._worldY) * ((contentSize.y * this.contentAspectX) / this._scale)); + return this._viewportToImageDelta(viewerX - this._worldX, viewerY - this._worldY); + }, + + // private + _imageToViewportDelta: function( imageX, imageY ) { + return new $.Point((imageX / this.source.dimensions.x) * this._scale, + (imageY / this.source.dimensions.y / this.contentAspectX) * this._scale); }, /** @@ -221,9 +231,10 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag return this.imageToViewportCoordinates( imageX.x, imageX.y ); } - var contentSize = this.source.dimensions; - return new $.Point(this._worldX + ((imageX / contentSize.x) * this._scale), - this._worldY + ((imageY / contentSize.y / this.contentAspectX) * this._scale)); + var point = this._imageToViewportDelta(imageX, imageY); + point.x += this._worldX; + point.y += this._worldY; + return point; }, /** @@ -254,14 +265,14 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag coordA = this.imageToViewportCoordinates( imageX, imageY ); - coordB = this.imageToViewportCoordinates( - imageX + pixelWidth, imageY + pixelHeight + coordB = this._imageToViewportDelta( + pixelWidth, pixelHeight ); return new $.Rect( coordA.x, coordA.y, - coordB.x - coordA.x, - coordB.y - coordA.y + coordB.x, + coordB.y ); }, @@ -291,12 +302,12 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag ); } coordA = this.viewportToImageCoordinates( viewerX, viewerY ); - coordB = this.viewportToImageCoordinates(viewerX + pointWidth, viewerY + pointHeight); + coordB = this._viewportToImageDelta(pointWidth, pointHeight); return new $.Rect( coordA.x, coordA.y, - coordB.x - coordA.x, - coordB.y - coordA.y + coordB.x, + coordB.y ); }, diff --git a/src/viewport.js b/src/viewport.js index c56ef7f0..a9edabe8 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -938,6 +938,13 @@ $.Viewport.prototype = /** @lends OpenSeadragon.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)); + }, + /** * Translates from OpenSeadragon viewer coordinate system to image coordinate system. * This method can be called either by passing X,Y coordinates or an @@ -959,9 +966,14 @@ $.Viewport.prototype = /** @lends OpenSeadragon.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); + }, + + // private + _imageToViewportDelta: function( imageX, imageY ) { var scale = this.homeBounds.width; - return new $.Point((viewerX - this.homeBounds.x) * (this.contentSize.x / scale), - (viewerY - this.homeBounds.y) * ((this.contentSize.y * this.contentAspectX) / scale)); + return new $.Point((imageX / this.contentSize.x) * scale, + (imageY / this.contentSize.y / this.contentAspectX) * scale); }, /** @@ -985,9 +997,10 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ $.console.error('[Viewport.imageToViewportCoordinates] is not accurate with multi-image; use TiledImage.imageToViewportCoordinates instead.'); } - var scale = this.homeBounds.width; - return new $.Point(this.homeBounds.x + ((imageX / this.contentSize.x) * scale), - this.homeBounds.y + ((imageY / this.contentSize.y / this.contentAspectX) * scale)); + var point = this._imageToViewportDelta(imageX, imageY); + point.x += this.homeBounds.x; + point.y += this.homeBounds.y; + return point; }, /** @@ -1020,14 +1033,14 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ coordA = this.imageToViewportCoordinates( imageX, imageY ); - coordB = this.imageToViewportCoordinates( - imageX + pixelWidth, imageY + pixelHeight + coordB = this._imageToViewportDelta( + pixelWidth, pixelHeight ); return new $.Rect( coordA.x, coordA.y, - coordB.x - coordA.x, - coordB.y - coordA.y + coordB.x, + coordB.y ); }, @@ -1059,12 +1072,12 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ } coordA = this.viewportToImageCoordinates( viewerX, viewerY ); - coordB = this.viewportToImageCoordinates(viewerX + pointWidth, viewerY + pointHeight); + coordB = this._viewportToImageDelta(pointWidth, pointHeight); return new $.Rect( coordA.x, coordA.y, - coordB.x - coordA.x, - coordB.y - coordA.y + coordB.x, + coordB.y ); }, From 2de44c752d2cad88d549ed5fbf3a7849a24f7b36 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 24 Nov 2014 11:59:06 -0800 Subject: [PATCH 5/6] Using "viewport coordinates" instead of "world coordinates" --- src/openseadragon.js | 4 ++-- src/tiledimage.js | 16 ++++++++-------- src/viewer.js | 8 ++++---- src/viewport.js | 8 ++++---- src/world.js | 6 +++--- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index 5a40c560..07abcd42 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -544,11 +544,11 @@ * 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. + * If collectionMode is true, specifies the size, in viewport 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. + * If collectionMode is true, specifies the margin, in viewport 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 446adb4a..a5cf2981 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -48,10 +48,10 @@ * @param {OpenSeadragon.TileCache} options.tileCache - The TileCache for this TiledImage to use. * @param {OpenSeadragon.Drawer} options.drawer - The Drawer for this TiledImage to draw onto. * @param {OpenSeadragon.ImageLoader} options.imageLoader - The ImageLoader for this TiledImage to use. - * @param {Number} [options.x=0] - Left position, in world coordinates. - * @param {Number} [options.y=0] - Top position, in world coordinates. - * @param {Number} [options.width=1] - Width, in world coordinates. - * @param {Number} [options.height] - Height, in world coordinates. + * @param {Number} [options.x=0] - Left position, in viewport coordinates. + * @param {Number} [options.y=0] - Top position, in viewport coordinates. + * @param {Number} [options.width=1] - Width, in viewport coordinates. + * @param {Number} [options.height] - Height, in viewport coordinates. * @param {Number} [options.minZoomImageRatio] - See {@link OpenSeadragon.Options}. * @param {Boolean} [options.wrapHorizontal] - See {@link OpenSeadragon.Options}. * @param {Boolean} [options.wrapVertical] - See {@link OpenSeadragon.Options}. @@ -165,7 +165,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag }, /** - * @returns {OpenSeadragon.Rect} This TiledImage's bounds in world coordinates. + * @returns {OpenSeadragon.Rect} This TiledImage's bounds in viewport coordinates. */ getBounds: function() { return new $.Rect( this._worldX, this._worldY, this._worldWidth, this._worldHeight ); @@ -313,7 +313,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag /** * Sets the TiledImage's position in the world. - * @param {OpenSeadragon.Point} position - The new position, in world coordinates. + * @param {OpenSeadragon.Point} position - The new position, in viewport coordinates. * @fires OpenSeadragon.TiledImage.event:bounds-change */ setPosition: function(position) { @@ -329,7 +329,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag /** * 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. + * @param {Number} width - The new width, in viewport coordinates. * @fires OpenSeadragon.TiledImage.event:bounds-change */ setWidth: function(width) { @@ -344,7 +344,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag /** * 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. + * @param {Number} height - The new height, in viewport coordinates. * @fires OpenSeadragon.TiledImage.event:bounds-change */ setHeight: function(height) { diff --git a/src/viewer.js b/src/viewer.js index 2f5f697e..e05bbba8 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1250,10 +1250,10 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, * named 'getTileUrl', it is treated as a custom TileSource. * @param {Number} [options.index] The index of the item. Added on top of * all other items if not specified. - * @param {Number} [options.x=0] The X position for the image in world coordinates. - * @param {Number} [options.y=0] The Y position for the image in world coordinates. - * @param {Number} [options.width=1] The width for the image in world coordinates. - * @param {Number} [options.height] The height for the image in world coordinates. + * @param {Number} [options.x=0] The X position for the image in viewport coordinates. + * @param {Number} [options.y=0] The Y position for the image in viewport coordinates. + * @param {Number} [options.width=1] The width for the image in viewport coordinates. + * @param {Number} [options.height] The height for the image in viewport coordinates. * @param {Function} [options.success] A function that gets called when the image is * successfully added. It's passed the event object which contains a single property: * "item", the resulting TiledImage. diff --git a/src/viewport.js b/src/viewport.js index a9edabe8..77893189 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -163,8 +163,8 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ /** * 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 + * @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 */ setHomeBounds: function(bounds, contentFactor) { @@ -303,7 +303,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ /** * @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 world coordinates. + * @returns {OpenSeadragon.Rect} The location you are zoomed/panned to, in viewport coordinates. */ getBounds: function( current ) { var center = this.getCenter( current ), @@ -322,7 +322,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ * @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 world coordinates. + * including the space taken by margins, in viewport coordinates. */ getBoundsWithMargins: function( current ) { var bounds = this.getBounds(current); diff --git a/src/world.js b/src/world.js index 2c7a3f7f..9591183b 100644 --- a/src/world.js +++ b/src/world.js @@ -245,7 +245,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W }, /** - * @returns {OpenSeadragon.Rect} The smallest rectangle that encloses all items, in world coordinates. + * @returns {OpenSeadragon.Rect} The smallest rectangle that encloses all items, in viewport coordinates. */ getHomeBounds: function() { return this._homeBounds.clone(); @@ -253,9 +253,9 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W /** * To facilitate zoom constraints, we keep track of the pixel density of the - * densest item in the World (i.e. the item whose content size to world size + * densest item in the World (i.e. the item whose content size to viewport size * ratio is the highest) and save it as this "content factor". - * @returns {Number} the number of content units per world unit. + * @returns {Number} the number of content units per viewport unit. */ getContentFactor: function() { return this._contentFactor; From bf9ccd5458953ff12df2cc0a583356cb43a5c9dc Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 24 Nov 2014 13:25:20 -0800 Subject: [PATCH 6/6] tile-drawing event now includes tiledImage --- src/drawer.js | 33 +++++++++------------------------ src/tile.js | 3 ++- src/tiledimage.js | 26 +++++++++++++++++++++++++- test/modules/drawer.js | 16 ---------------- test/modules/tiledimage.js | 12 +++++++++++- 5 files changed, 47 insertions(+), 43 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index 804f0693..3a02dad4 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -116,27 +116,6 @@ $.Drawer = function( options ) { // explicit left-align this.container.style.textAlign = "left"; this.container.appendChild( this.canvas ); - - // We need a callback to give image manipulation a chance to happen - this._drawingHandler = function(args) { - if (_this.viewer) { - /** - * This event is fired just before the tile is drawn giving the application a chance to alter the image. - * - * NOTE: This event is only fired when the drawer is using a . - * - * @event tile-drawing - * @memberof OpenSeadragon.Viewer - * @type {object} - * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. - * @property {OpenSeadragon.Tile} tile - The Tile being drawn. - * @property {OpenSeadragon.Tile} context - The HTML canvas context being drawn into. - * @property {OpenSeadragon.Tile} rendered - The HTML canvas context containing the tile imagery. - * @property {?Object} userData - Arbitrary subscriber-defined object. - */ - _this.viewer.raiseEvent('tile-drawing', args); - } - }; }; $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ @@ -249,17 +228,23 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ /** * Draws the given tile. * @param {OpenSeadragon.Tile} tile - The tile to draw. + * @param {Function} drawingHandler - Method for firing the drawing event if using canvas. + * drawingHandler({context, tile, rendered}) + * where rendered is the context with the pre-drawn image. */ - drawTile: function( tile ) { + drawTile: function( tile, drawingHandler ) { + $.console.assert(tile, '[Drawer.drawTile] tile is required'); + $.console.assert(drawingHandler, '[Drawer.drawTile] drawingHandler is required'); + if ( this.useCanvas ) { // TODO do this in a more performant way // specifically, don't save,rotate,restore every time we draw a tile if( this.viewport.degrees !== 0 ) { this._offsetForRotation( tile, this.viewport.degrees ); - tile.drawCanvas( this.context, this._drawingHandler ); + tile.drawCanvas( this.context, drawingHandler ); this._restoreRotationChanges( tile ); } else { - tile.drawCanvas( this.context, this._drawingHandler ); + tile.drawCanvas( this.context, drawingHandler ); } } else { tile.drawHTML( this.canvas ); diff --git a/src/tile.js b/src/tile.js index b54e9b32..30082315 100644 --- a/src/tile.js +++ b/src/tile.js @@ -231,7 +231,8 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{ * Renders the tile in a canvas-based context. * @function * @param {Canvas} context - * @param {Function} method for firing the drawing event. drawingHandler({context, tile, rendered}) + * @param {Function} drawingHandler - Method for firing the drawing event. + * drawingHandler({context, tile, rendered}) * where rendered is the context with the pre-drawn image. */ drawCanvas: function( context, drawingHandler ) { diff --git a/src/tiledimage.js b/src/tiledimage.js index a5cf2981..bb74abb4 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -63,6 +63,8 @@ * @param {String|Boolean} [options.crossOriginPolicy] - See {@link OpenSeadragon.Options}. */ $.TiledImage = function( options ) { + var _this = this; + $.console.assert( options.tileCache, "[TiledImage] options.tileCache is required" ); $.console.assert( options.drawer, "[TiledImage] options.drawer is required" ); $.console.assert( options.viewer, "[TiledImage] options.viewer is required" ); @@ -127,6 +129,28 @@ $.TiledImage = function( options ) { crossOriginPolicy: $.DEFAULT_SETTINGS.crossOriginPolicy }, options ); + + // We need a callback to give image manipulation a chance to happen + this._drawingHandler = function(args) { + /** + * This event is fired just before the tile is drawn giving the application a chance to alter the image. + * + * NOTE: This event is only fired when the drawer is using a . + * + * @event tile-drawing + * @memberof OpenSeadragon.Viewer + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. + * @property {OpenSeadragon.Tile} tile - The Tile being drawn. + * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn. + * @property {OpenSeadragon.Tile} context - The HTML canvas context being drawn into. + * @property {OpenSeadragon.Tile} rendered - The HTML canvas context containing the tile imagery. + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + _this.viewer.raiseEvent('tile-drawing', $.extend({ + tiledImage: _this + }, args)); + }; }; $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.TiledImage.prototype */{ @@ -1002,7 +1026,7 @@ function drawTiles( tiledImage, lastDrawn ){ for ( i = lastDrawn.length - 1; i >= 0; i-- ) { tile = lastDrawn[ i ]; - tiledImage._drawer.drawTile( tile ); + tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler ); tile.beingDrawn = true; if( tiledImage.debugMode ){ diff --git a/test/modules/drawer.js b/test/modules/drawer.js index e024c72e..80f9fa7f 100644 --- a/test/modules/drawer.js +++ b/test/modules/drawer.js @@ -39,22 +39,6 @@ start(); }); - // ---------- - asyncTest('tile-drawing event', function() { - createViewer({ - tileSources: '/test/data/testpattern.dzi' - }); - - viewer.addHandler('tile-drawing', function handler(event) { - viewer.removeHandler('tile-drawing', handler); - equal(event.eventSource, viewer, 'sender of tile-drawing event was viewer'); - ok(event.tile, 'tile-drawing event includes a tile'); - ok(event.context, 'tile-drawing event includes a context'); - ok(event.rendered, 'tile-drawing event includes a rendered'); - start(); - }); - }); - // ---------- asyncTest('rotation', function() { createViewer({ diff --git a/test/modules/tiledimage.js b/test/modules/tiledimage.js index 4901de01..703cc72a 100644 --- a/test/modules/tiledimage.js +++ b/test/modules/tiledimage.js @@ -116,6 +116,16 @@ ok(event.tile, 'update-tile event includes tile'); }); + viewer.addHandler('tile-drawing', function tileDrawingHandler(event) { + viewer.removeHandler('tile-drawing', tileDrawingHandler); + handlerCount++; + equal(event.eventSource, viewer, 'sender of tile-drawing event was viewer'); + equal(event.tiledImage, image, 'tiledImage of update-level event is correct'); + ok(event.tile, 'tile-drawing event includes a tile'); + ok(event.context, 'tile-drawing event includes a context'); + ok(event.rendered, 'tile-drawing event includes a rendered'); + }); + viewer.addHandler('tile-drawn', function tileDrawnHandler(event) { viewer.removeHandler('tile-drawn', tileDrawnHandler); handlerCount++; @@ -123,7 +133,7 @@ equal(event.tiledImage, image, 'tiledImage of update-level event is correct'); ok(event.tile, 'tile-drawn event includes tile'); - equal(handlerCount, 3, 'correct number of handlers called'); + equal(handlerCount, 4, 'correct number of handlers called'); start(); });