From 517e1feae8fcfbef7830177a6ce256f32bc09d8b Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Sun, 24 Dec 2017 10:57:29 +0100 Subject: [PATCH 01/64] Fix getTileAtPoint epsilon precision Fix #1362 --- src/tilesource.js | 2 +- test/modules/tilesource.js | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/tilesource.js b/src/tilesource.js index 84aa5a82..66badc9c 100644 --- a/src/tilesource.js +++ b/src/tilesource.js @@ -360,7 +360,7 @@ $.TileSource.prototype = { if (point.x >= 1) { x = this.getNumTiles(level).x - 1; } - var EPSILON = 1e-16; + var EPSILON = 1e-15; if (point.y >= 1 / this.aspectRatio - EPSILON) { y = this.getNumTiles(level).y - 1; } diff --git a/test/modules/tilesource.js b/test/modules/tilesource.js index bac637c8..2c1a0f42 100644 --- a/test/modules/tilesource.js +++ b/test/modules/tilesource.js @@ -107,6 +107,17 @@ maxLevel: 0, }); assertTileAtPoint(0, new OpenSeadragon.Point(1, 1239 / 4036), new OpenSeadragon.Point(0, 0)); + + // Test for issue #1362 + tileSource = new OpenSeadragon.TileSource({ + width: 2000, + height: 3033, + tileWidth: 2000, + tileHeight: 3033, + tileOverlap: 0, + maxLevel: 0, + }); + assertTileAtPoint(0, new OpenSeadragon.Point(1, 3033 / 2000), new OpenSeadragon.Point(0, 0)); }); }()); From 027dac0d8e46e937e0309d6aa8da6d6e7314aeb3 Mon Sep 17 00:00:00 2001 From: Francesco Cretti Date: Wed, 7 Mar 2018 11:25:33 +0100 Subject: [PATCH 02/64] navigato minimap onClick vertical/horizontal pan fix --- src/navigator.js | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/navigator.js b/src/navigator.js index 4c9848cf..45b56b6c 100644 --- a/src/navigator.js +++ b/src/navigator.js @@ -405,10 +405,26 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /* * @function */ function onCanvasClick( event ) { - if ( event.quick && this.viewer.viewport ) { - this.viewer.viewport.panTo(this.viewport.pointFromPixel(event.position)); - this.viewer.viewport.applyConstraints(); + if (event.quick && this.viewer.viewport && (this.panVertical || this.panHorizontal)) { + var target, + posX, + posY; + if (!this.panVertical) { + // perform only horizonal pan + posX = this.viewport.pointFromPixel(event.position).x; + posY = this.viewport.getCenter().y; + target = new $.Point(posX, posY); + } else if (!this.panHorizontal) { + // perform only vertical pan + posX = this.viewport.getCenter().x; + posY = this.viewport.pointFromPixel(event.position).y; + target = new $.Point(posX, posY); + } else { + target = this.viewport.pointFromPixel(event.position); } + this.viewer.viewport.panTo(target); + this.viewer.viewport.applyConstraints(); + } } /** From 12be95c8c3526f99717dba728a347826da613fcd Mon Sep 17 00:00:00 2001 From: Francesco Cretti Date: Sun, 11 Mar 2018 13:05:19 +0100 Subject: [PATCH 03/64] getCenter fixed when clicking on minimap --- src/navigator.js | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/navigator.js b/src/navigator.js index 45b56b6c..84e3ad51 100644 --- a/src/navigator.js +++ b/src/navigator.js @@ -406,21 +406,13 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /* */ function onCanvasClick( event ) { if (event.quick && this.viewer.viewport && (this.panVertical || this.panHorizontal)) { - var target, - posX, - posY; + var target = this.viewport.pointFromPixel(event.position); if (!this.panVertical) { // perform only horizonal pan - posX = this.viewport.pointFromPixel(event.position).x; - posY = this.viewport.getCenter().y; - target = new $.Point(posX, posY); + target.y = this.viewer.viewport.getCenter(true).y; } else if (!this.panHorizontal) { // perform only vertical pan - posX = this.viewport.getCenter().x; - posY = this.viewport.pointFromPixel(event.position).y; - target = new $.Point(posX, posY); - } else { - target = this.viewport.pointFromPixel(event.position); + target.x = this.viewer.viewport.getCenter(true).x; } this.viewer.viewport.panTo(target); this.viewer.viewport.applyConstraints(); From 57654cf1cf768a3171ff95e614647b66965dbf4d Mon Sep 17 00:00:00 2001 From: Francesco Cretti Date: Sun, 11 Mar 2018 13:24:29 +0100 Subject: [PATCH 04/64] raise event for navigator click --- src/navigator.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/navigator.js b/src/navigator.js index 84e3ad51..392d9e15 100644 --- a/src/navigator.js +++ b/src/navigator.js @@ -417,6 +417,27 @@ function onCanvasClick( event ) { this.viewer.viewport.panTo(target); this.viewer.viewport.applyConstraints(); } + /** + * Raised when a click event occurs on the {@link OpenSeadragon.Viewer#navigator} element. + * + * @event navigator-click + * @memberof OpenSeadragon.Viewer + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event. + * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event. + * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element. + * @property {Boolean} quick - True only if the clickDistThreshold and clickTimeThreshold are both passed. Useful for differentiating between clicks and drags. + * @property {Boolean} shift - True if the shift key was pressed during this event. + * @property {Object} originalEvent - The original DOM event. + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + this.viewer.raiseEvent('navigator-click', { + tracker: event.eventSource, + position: event.position, + quick: event.quick, + shift: event.shift, + originalEvent: event.originalEvent + }); } /** From 7ba516b30aa2db16e070cec76967110488d784b7 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 27 Mar 2018 09:48:18 -0700 Subject: [PATCH 05/64] Fixed issue with square tile code on IE --- src/tilesource.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/tilesource.js b/src/tilesource.js index be5c1d5e..cc48aeec 100644 --- a/src/tilesource.js +++ b/src/tilesource.js @@ -392,9 +392,7 @@ $.TileSource.prototype = { sy = Math.min( sy, dimensionsScaled.y - py ); if (isSource) { - scale = 1; - px = 0; - py = 0; + return new $.Rect(0, 0, Math.floor(sx), Math.floor(sy)); } return new $.Rect( px * scale, py * scale, sx * scale, sy * scale ); From 02a397638c7cb5a3a8485ab6f759d265bf4c4822 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 2 Apr 2018 11:09:23 -0700 Subject: [PATCH 06/64] Fixed test errors --- src/tile.js | 17 +++++++++++------ src/tilesource.js | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/tile.js b/src/tile.js index 0cc836f0..de1eb40d 100644 --- a/src/tile.js +++ b/src/tile.js @@ -367,16 +367,21 @@ $.Tile.prototype = { // changes as we are rendering the image drawingHandler({context: context, tile: this, rendered: rendered}); - if (!this.sourceBounds) { // Just in case - this.sourceBounds = new $.Rect(0, 0, rendered.canvas.width, rendered.canvas.height); + var sourceWidth, sourceHeight; + if (this.sourceBounds) { + sourceWidth = Math.min(this.sourceBounds.width, rendered.canvas.width); + sourceHeight = Math.min(this.sourceBounds.height, rendered.canvas.height); + } else { + sourceWidth = rendered.canvas.width; + sourceHeight = rendered.canvas.height; } context.drawImage( rendered.canvas, - this.sourceBounds.x, - this.sourceBounds.y, - this.sourceBounds.width, - this.sourceBounds.height, + 0, + 0, + sourceWidth, + sourceHeight, position.x, position.y, size.x, diff --git a/src/tilesource.js b/src/tilesource.js index cc48aeec..a5b3897b 100644 --- a/src/tilesource.js +++ b/src/tilesource.js @@ -392,7 +392,7 @@ $.TileSource.prototype = { sy = Math.min( sy, dimensionsScaled.y - py ); if (isSource) { - return new $.Rect(0, 0, Math.floor(sx), Math.floor(sy)); + return new $.Rect(0, 0, sx, sy); } return new $.Rect( px * scale, py * scale, sx * scale, sy * scale ); From 71fd747051e14d77bc096dacf5019c70ed0b4701 Mon Sep 17 00:00:00 2001 From: Nelson Campos Date: Wed, 4 Apr 2018 11:31:18 +0100 Subject: [PATCH 07/64] Adds flip/mirror slides feature. This commit adds full support for a new OpenSeaDragon(OSD) feature - Mirror/Flip. In order to fully support this new feature, the following OSD objects had to be modified: - drawer.js: - navigator - openseadragon - strings - tiledImage - viewer Additionally, a new flip button was created (similar to the existing ones). Flip Logic Whenever the state is flip button is pressed, flip state is toogled, inverting all the controls and displays (the rotation direction is inverted as well). This means that all viewer coordinates (including user inputs) must me inverted too. Summary of modifications - drawer.js: modified _offsetForRotation to invert rotation angle on flipped state. Added a _flip method to scale/mirror canvas context. - navigator.js: adds full flip support and inverts nagivator inputs. - openseadragon.js: new buttons, flip state variable and showFlipControl variable. - strings.js: flip tool help tips. - tiledImage.js: flips the actual drawing canvas. - viewer.js: Added keyboardshortcuts to rotate 90degrees (r/R) and flip image (f/F). flip button state is stored here and flip order is set. The flipped state is stored on viewer object. --- images/flip_grouphover.png | Bin 0 -> 2179 bytes images/flip_hover.png | Bin 0 -> 2340 bytes images/flip_pressed.png | Bin 0 -> 2307 bytes images/flip_rest.png | Bin 0 -> 2300 bytes src/drawer.js | 25 ++++++++- src/navigator.js | 64 +++++++++++++++++++++-- src/openseadragon.js | 20 +++++++ src/strings.js | 3 +- src/tiledimage.js | 10 +++- src/viewer.js | 104 ++++++++++++++++++++++++++++++++----- src/viewport.js | 1 - 11 files changed, 205 insertions(+), 22 deletions(-) create mode 100644 images/flip_grouphover.png create mode 100644 images/flip_hover.png create mode 100644 images/flip_pressed.png create mode 100644 images/flip_rest.png diff --git a/images/flip_grouphover.png b/images/flip_grouphover.png new file mode 100644 index 0000000000000000000000000000000000000000..014ead6a384c9b97f041f1c9b0dcc3b52e5c5bc6 GIT binary patch literal 2179 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