From fc69c6568d8a27df04fd72b2e65fb15a464875fc Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Sat, 7 Jan 2017 19:24:29 +0100 Subject: [PATCH 1/3] Animate rotation --- src/drawer.js | 6 ++-- src/spring.js | 4 +++ src/tiledimage.js | 78 +++++++++++++++++++++++++++-------------------- 3 files changed, 52 insertions(+), 36 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index 8321b6fe..0289019a 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -500,9 +500,9 @@ $.Drawer.prototype = { if ( this.viewport.degrees !== 0 ) { this._offsetForRotation({degrees: this.viewport.degrees}); } - if (tiledImage.getRotation() !== 0) { + if (tiledImage.getRotation(true) !== 0) { this._offsetForRotation({ - degrees: tiledImage.getRotation(), + degrees: tiledImage.getRotation(true), point: tiledImage.viewport.pixelFromPointNoRotate( tiledImage._getRotationPoint(true), true) }); @@ -569,7 +569,7 @@ $.Drawer.prototype = { if ( this.viewport.degrees !== 0 ) { this._restoreRotationChanges(); } - if (tiledImage.getRotation() !== 0) { + if (tiledImage.getRotation(true) !== 0) { this._restoreRotationChanges(); } context.restore(); diff --git a/src/spring.js b/src/spring.js index c4342892..30a8956d 100644 --- a/src/spring.js +++ b/src/spring.js @@ -206,6 +206,7 @@ $.Spring.prototype = { /** * @function + * @returns true if the value got updated, false otherwise */ update: function() { this.current.time = $.now(); @@ -229,11 +230,14 @@ $.Spring.prototype = { ( this.target.time - this.start.time ) ); + var oldValue = this.current.value; if (this._exponential) { this.current.value = Math.exp(currentValue); } else { this.current.value = currentValue; } + + return oldValue != this.current.value; }, /** diff --git a/src/tiledimage.js b/src/tiledimage.js index 2fa48782..c6cdd7ec 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -132,7 +132,7 @@ $.TiledImage = function( options ) { var fitBoundsPlacement = options.fitBoundsPlacement || OpenSeadragon.Placement.CENTER; delete options.fitBoundsPlacement; - this._degrees = $.positiveModulo(options.degrees || 0, 360); + var degrees = $.positiveModulo(options.degrees || 0, 360); delete options.degrees; $.extend( true, this, { @@ -186,6 +186,12 @@ $.TiledImage = function( options ) { animationTime: this.animationTime }); + this._degreesSpring = new $.Spring({ + initial: degrees, + springStiffness: this.springStiffness, + animationTime: this.animationTime + }); + this._updateForScale(); if (fitBounds) { @@ -269,16 +275,12 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag * @returns {Boolean} Whether the TiledImage animated. */ update: function() { - var oldX = this._xSpring.current.value; - var oldY = this._ySpring.current.value; - var oldScale = this._scaleSpring.current.value; + var xUpdated = this._xSpring.update(); + var yUpdated = this._ySpring.update(); + var scaleUpdated = this._scaleSpring.update(); + var degreesUpdated = this._degreesSpring.update(); - this._xSpring.update(); - this._ySpring.update(); - this._scaleSpring.update(); - - if (this._xSpring.current.value !== oldX || this._ySpring.current.value !== oldY || - this._scaleSpring.current.value !== oldScale) { + if (xUpdated || yUpdated || scaleUpdated || degreesUpdated) { this._updateForScale(); this._needsDraw = true; return true; @@ -313,7 +315,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag */ getBounds: function(current) { return this.getBoundsNoRotate(current) - .rotate(this._degrees, this._getRotationPoint(current)); + .rotate(this.getRotation(current), this._getRotationPoint(current)); }, /** @@ -362,7 +364,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag clip.width, clip.height); } - return bounds.rotate(this._degrees, this._getRotationPoint(current)); + return bounds.rotate(this.getRotation(current), this._getRotationPoint(current)); }, /** @@ -397,7 +399,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag point = new $.Point(viewerX, viewerY); } - point = point.rotate(-this._degrees, this._getRotationPoint(current)); + point = point.rotate(-this.getRotation(current), this._getRotationPoint(current)); return current ? this._viewportToImageDelta( point.x - this._xSpring.current.value, @@ -439,7 +441,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag point.y += this._ySpring.target.value; } - return point.rotate(this._degrees, this._getRotationPoint(current)); + return point.rotate(this.getRotation(current), this._getRotationPoint(current)); }, /** @@ -453,7 +455,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag * @param {Boolean} [current=false] - Pass true to use the current location; false for target location. * @return {OpenSeadragon.Rect} A rect representing the coordinates in the viewport. */ - imageToViewportRectangle: function( imageX, imageY, pixelWidth, pixelHeight, current ) { + imageToViewportRectangle: function(imageX, imageY, pixelWidth, pixelHeight, current) { var rect = imageX; if (rect instanceof $.Rect) { //they passed a rect instead of individual components @@ -470,7 +472,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag coordA.y, coordB.x, coordB.y, - rect.degrees + this._degrees + rect.degrees + this.getRotation(current) ); }, @@ -502,7 +504,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag coordA.y, coordB.x, coordB.y, - rect.degrees - this._degrees + rect.degrees - this.getRotation(current) ); }, @@ -555,7 +557,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag // coordinates (x in [0, 1] and y in [0, aspectRatio]) _viewportToTiledImageRectangle: function(rect) { var scale = this._scaleSpring.current.value; - rect = rect.rotate(-this.getRotation(), this._getRotationPoint(true)); + rect = rect.rotate(-this.getRotation(true), this._getRotationPoint(true)); return new $.Rect( (rect.x - this._xSpring.current.value) / scale, (rect.y - this._ySpring.current.value) / scale, @@ -769,24 +771,34 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag }, /** - * Get the current rotation of this tiled image in degrees. - * @returns {Number} the current rotation of this tiled image in degrees. + * Get the rotation of this tiled image in degrees. + * @param {Boolean} [current=false] True for current rotation, false for target. + * @returns {Number} the rotation of this tiled image in degrees. */ - getRotation: function() { - return this._degrees; + getRotation: function(current) { + return current ? + this._degreesSpring.current.value : + this._degreesSpring.target.value; }, /** * Set the current rotation of this tiled image in degrees. * @param {Number} degrees the rotation in degrees. + * @param {Boolean} [immediately=false] Whether to animate to the new angle + * or rotate immediately. * @fires OpenSeadragon.TiledImage.event:bounds-change */ - setRotation: function(degrees) { + setRotation: function(degrees, immediately) { degrees = $.positiveModulo(degrees, 360); - if (this._degrees === degrees) { + if (this._degreesSpring.target.value === degrees && + this._degreesSpring.isAtTargetValue()) { return; } - this._degrees = degrees; + if (immediately) { + this._degreesSpring.resetTo(degrees); + } else { + this._degreesSpring.springTo(degrees); + } this._needsDraw = true; this._raiseBoundsChange(); }, @@ -1705,7 +1717,7 @@ function drawTiles( tiledImage, lastDrawn ) { if (lastDrawn.length > 1 && imageZoom > tiledImage.smoothTileEdgesMinZoom && !tiledImage.iOSDevice && - tiledImage.getRotation() === 0 && + tiledImage.getRotation(true) === 0 && $.supportsCanvas) { // When zoomed in a lot (>100%) the tile edges are visible. // So we have to composite them at ~100% and scale them up together. @@ -1739,9 +1751,9 @@ function drawTiles( tiledImage, lastDrawn ) { useSketch: useSketch }); } - if (tiledImage._degrees !== 0) { + if (tiledImage.getRotation(true) !== 0) { tiledImage._drawer._offsetForRotation({ - degrees: tiledImage._degrees, + degrees: tiledImage.getRotation(true), point: tiledImage.viewport.pixelFromPointNoRotate( tiledImage._getRotationPoint(true), true), useSketch: useSketch @@ -1754,7 +1766,7 @@ function drawTiles( tiledImage, lastDrawn ) { tiledImage._drawer.saveContext(useSketch); var box = tiledImage.imageToViewportRectangle(tiledImage._clip, true); - box = box.rotate(-tiledImage._degrees, tiledImage._getRotationPoint()); + box = box.rotate(-tiledImage.getRotation(true), tiledImage._getRotationPoint(true)); var clipRect = tiledImage._drawer.viewportToDrawerRectangle(box); if (sketchScale) { clipRect = clipRect.times(sketchScale); @@ -1816,7 +1828,7 @@ function drawTiles( tiledImage, lastDrawn ) { } if (!sketchScale) { - if (tiledImage._degrees !== 0) { + if (tiledImage.getRotation(true) !== 0) { tiledImage._drawer._restoreRotationChanges(useSketch); } if (tiledImage.viewport.degrees !== 0) { @@ -1832,9 +1844,9 @@ function drawTiles( tiledImage, lastDrawn ) { useSketch: false }); } - if (tiledImage._degrees !== 0) { + if (tiledImage.getRotation(true) !== 0) { tiledImage._drawer._offsetForRotation({ - degrees: tiledImage._degrees, + degrees: tiledImage.getRotation(true), point: tiledImage.viewport.pixelFromPointNoRotate( tiledImage._getRotationPoint(true), true), useSketch: false @@ -1849,7 +1861,7 @@ function drawTiles( tiledImage, lastDrawn ) { bounds: bounds }); if (sketchScale) { - if (tiledImage._degrees !== 0) { + if (tiledImage.getRotation(true) !== 0) { tiledImage._drawer._restoreRotationChanges(false); } if (tiledImage.viewport.degrees !== 0) { From b62d4a7bc15d0b01b076fac902209368ed487457 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Sun, 8 Jan 2017 18:36:37 +0100 Subject: [PATCH 2/3] Fix navigator with rotation and clip --- src/navigator.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/navigator.js b/src/navigator.js index 61d50734..45379fa1 100644 --- a/src/navigator.js +++ b/src/navigator.js @@ -369,9 +369,11 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /* // private _matchBounds: function(myItem, theirItem, immediately) { - var bounds = theirItem.getBounds(); + var bounds = theirItem.getBoundsNoRotate(); myItem.setPosition(bounds.getTopLeft(), immediately); myItem.setWidth(bounds.width, immediately); + myItem.setRotation(theirItem.getRotation(), immediately); + myItem.setClip(theirItem.getClip()); } }); From 63a8a2ffa6232a1c8a8e5495dc63b5d90198916b Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Sat, 21 Jan 2017 19:58:21 +0100 Subject: [PATCH 3/3] Allow tiled image rotation outside the 0 to 360 range --- src/drawer.js | 4 ++-- src/tiledimage.js | 15 +++++++-------- test/modules/tiledimage.js | 13 ++++++++++--- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index 0289019a..4485da98 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -500,7 +500,7 @@ $.Drawer.prototype = { if ( this.viewport.degrees !== 0 ) { this._offsetForRotation({degrees: this.viewport.degrees}); } - if (tiledImage.getRotation(true) !== 0) { + if (tiledImage.getRotation(true) % 360 !== 0) { this._offsetForRotation({ degrees: tiledImage.getRotation(true), point: tiledImage.viewport.pixelFromPointNoRotate( @@ -569,7 +569,7 @@ $.Drawer.prototype = { if ( this.viewport.degrees !== 0 ) { this._restoreRotationChanges(); } - if (tiledImage.getRotation(true) !== 0) { + if (tiledImage.getRotation(true) % 360 !== 0) { this._restoreRotationChanges(); } context.restore(); diff --git a/src/tiledimage.js b/src/tiledimage.js index c6cdd7ec..70859ec0 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -132,7 +132,7 @@ $.TiledImage = function( options ) { var fitBoundsPlacement = options.fitBoundsPlacement || OpenSeadragon.Placement.CENTER; delete options.fitBoundsPlacement; - var degrees = $.positiveModulo(options.degrees || 0, 360); + var degrees = options.degrees || 0; delete options.degrees; $.extend( true, this, { @@ -789,7 +789,6 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag * @fires OpenSeadragon.TiledImage.event:bounds-change */ setRotation: function(degrees, immediately) { - degrees = $.positiveModulo(degrees, 360); if (this._degreesSpring.target.value === degrees && this._degreesSpring.isAtTargetValue()) { return; @@ -1713,11 +1712,11 @@ function drawTiles( tiledImage, lastDrawn ) { var zoom = tiledImage.viewport.getZoom(true); var imageZoom = tiledImage.viewportToImageZoom(zoom); - // TODO: support tile edge smoothing with tiled image rotation. + if (lastDrawn.length > 1 && imageZoom > tiledImage.smoothTileEdgesMinZoom && !tiledImage.iOSDevice && - tiledImage.getRotation(true) === 0 && + tiledImage.getRotation(true) % 360 === 0 && // TODO: support tile edge smoothing with tiled image rotation. $.supportsCanvas) { // When zoomed in a lot (>100%) the tile edges are visible. // So we have to composite them at ~100% and scale them up together. @@ -1751,7 +1750,7 @@ function drawTiles( tiledImage, lastDrawn ) { useSketch: useSketch }); } - if (tiledImage.getRotation(true) !== 0) { + if (tiledImage.getRotation(true) % 360 !== 0) { tiledImage._drawer._offsetForRotation({ degrees: tiledImage.getRotation(true), point: tiledImage.viewport.pixelFromPointNoRotate( @@ -1828,7 +1827,7 @@ function drawTiles( tiledImage, lastDrawn ) { } if (!sketchScale) { - if (tiledImage.getRotation(true) !== 0) { + if (tiledImage.getRotation(true) % 360 !== 0) { tiledImage._drawer._restoreRotationChanges(useSketch); } if (tiledImage.viewport.degrees !== 0) { @@ -1844,7 +1843,7 @@ function drawTiles( tiledImage, lastDrawn ) { useSketch: false }); } - if (tiledImage.getRotation(true) !== 0) { + if (tiledImage.getRotation(true) % 360 !== 0) { tiledImage._drawer._offsetForRotation({ degrees: tiledImage.getRotation(true), point: tiledImage.viewport.pixelFromPointNoRotate( @@ -1861,7 +1860,7 @@ function drawTiles( tiledImage, lastDrawn ) { bounds: bounds }); if (sketchScale) { - if (tiledImage.getRotation(true) !== 0) { + if (tiledImage.getRotation(true) % 360 !== 0) { tiledImage._drawer._restoreRotationChanges(false); } if (tiledImage.viewport.degrees !== 0) { diff --git a/test/modules/tiledimage.js b/test/modules/tiledimage.js index 1b05b557..b2206341 100644 --- a/test/modules/tiledimage.js +++ b/test/modules/tiledimage.js @@ -319,10 +319,16 @@ function testDefaultRotation() { var image = viewer.world.getItemAt(0); - strictEqual(image.getRotation(), 0, 'image has default rotation'); + strictEqual(image.getRotation(true), 0, 'image has default current rotation'); + strictEqual(image.getRotation(false), 0, 'image has default target rotation'); image.setRotation(400); - strictEqual(image.getRotation(), 40, 'rotation is set correctly'); + strictEqual(image.getRotation(true), 0, 'current rotation is not changed'); + strictEqual(image.getRotation(false), 400, 'target rotation is set correctly'); + + image.setRotation(200, true); + strictEqual(image.getRotation(true), 200, 'current rotation is set correctly'); + strictEqual(image.getRotation(false), 200, 'target rotation is set correctly'); viewer.addOnceHandler('open', testTileSourceRotation); viewer.open({ @@ -333,7 +339,8 @@ function testTileSourceRotation() { var image = viewer.world.getItemAt(0); - strictEqual(image.getRotation(), 300, 'image has correct rotation'); + strictEqual(image.getRotation(true), -60, 'image has correct current rotation'); + strictEqual(image.getRotation(false), -60, 'image has correct target rotation'); start(); }