diff --git a/changelog.txt b/changelog.txt index b201bbf5..ea669fa1 100644 --- a/changelog.txt +++ b/changelog.txt @@ -6,7 +6,7 @@ OPENSEADRAGON CHANGELOG * NEW BEHAVIOR: Setting the viewport rotation now animates by default (pass false for the new immediately parameter to disable) (#2136 @jonasengelmann) * DEPRECATION: Don't access the viewport's degrees property directly anymore; instead use setRotation and getRotation (#2136 @jonasengelmann) * New gesture: Double-click and drag to zoom (on by default for touch) (#2225 @HamzaTatheer) -* You can now provide a pivot point when rotating the viewport (#2233 @ pearcetm) +* You can now provide a pivot point when rotating the viewport (#2233 #2253 @pearcetm) * Improved the constraints that keep the image in the viewer, specifically when zoomed out a lot (#2160 @joedf, #2246 @pearcetm) * You can now provide an element for the navigator (as an alternative to an ID) (#1303 @cameronbaney, #2166 #2175 @joedf) * Now supporting IIIF "id" and "identifier" in addition to "@id" (#2173 @ahankinson) @@ -15,6 +15,7 @@ OPENSEADRAGON CHANGELOG * The viewer now emits before-destroy and destroy events (#2239 @pearcetm) * Improved documentation (#2211 @shyamkumaryadav) * Fixed: Cropping tiled images with polygons was broken (#2183 @altert) +* Fixed: IIIF tile sizes would be calculated wrong on rare occasions (#2206 @filak) * Fixed: Disabling buttons only changed their appearance, but they were still clickable (#2187 @pearcetm) * Fixed: ImageTileSource produced an error having to do with getTileHashKey (#2190 @Aiosa) * Fixed: On startup you would get an unnecessary "Viewer.buttons is deprecated" warning (#2201 @joedf, #2219 @jssullivan, #2212 @joedf) diff --git a/package-lock.json b/package-lock.json index b810a4e1..ca799e74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -695,6 +695,19 @@ "node": ">=0.10.0" } }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "dev": true, @@ -2050,6 +2063,20 @@ "node": ">= 4.0.0" } }, + "node_modules/get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-stream": { "version": "5.2.0", "dev": true, @@ -3023,6 +3050,18 @@ "node": ">=4" } }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-value": { "version": "1.0.0", "dev": true, @@ -4292,6 +4331,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-visit": { "version": "1.0.1", "dev": true, @@ -4856,9 +4904,13 @@ } }, "node_modules/qs": { - "version": "6.9.4", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, - "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.4" + }, "engines": { "node": ">=0.6" }, @@ -5657,6 +5709,20 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/snapdragon": { "version": "0.8.2", "dev": true, @@ -6901,6 +6967,16 @@ } } }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, "callsites": { "version": "3.1.0", "dev": true @@ -7800,6 +7876,17 @@ "globule": "^1.0.0" } }, + "get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, "get-stream": { "version": "5.2.0", "dev": true, @@ -8443,6 +8530,12 @@ "version": "3.0.0", "dev": true }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, "has-value": { "version": "1.0.0", "dev": true, @@ -9288,6 +9381,12 @@ } } }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true + }, "object-visit": { "version": "1.0.1", "dev": true, @@ -9635,8 +9734,13 @@ } }, "qs": { - "version": "6.9.4", - "dev": true + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } }, "querystring": { "version": "0.2.0", @@ -10194,6 +10298,17 @@ "version": "3.0.0", "dev": true }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "snapdragon": { "version": "0.8.2", "dev": true, diff --git a/src/iiiftilesource.js b/src/iiiftilesource.js index 632974a6..08932b56 100644 --- a/src/iiiftilesource.js +++ b/src/iiiftilesource.js @@ -134,7 +134,7 @@ $.IIIFTileSource = function( options ){ if (!options.maxLevel && !this.emulateLegacyImagePyramid) { if (!this.scale_factors) { - options.maxLevel = Number(Math.ceil(Math.log(Math.max(this.width, this.height), 2))); + options.maxLevel = Number(Math.round(Math.log(Math.max(this.width, this.height), 2))); } else { var maxScaleFactor = Math.max.apply(null, this.scale_factors); options.maxLevel = Math.round(Math.log(maxScaleFactor) * Math.LOG2E); @@ -377,8 +377,8 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea scale = Math.pow( 0.5, this.maxLevel - level ), //# image dimensions at this level - levelWidth = Math.ceil( this.width * scale ), - levelHeight = Math.ceil( this.height * scale ), + levelWidth = Math.round( this.width * scale ), + levelHeight = Math.round( this.height * scale ), //## iiif region tileWidth, @@ -398,8 +398,8 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea tileWidth = this.getTileWidth(level); tileHeight = this.getTileHeight(level); - iiifTileSizeWidth = Math.ceil( tileWidth / scale ); - iiifTileSizeHeight = Math.ceil( tileHeight / scale ); + iiifTileSizeWidth = Math.round( tileWidth / scale ); + iiifTileSizeHeight = Math.round( tileHeight / scale ); if (this.version === 1) { iiifQuality = "native." + this.tileFormat; } else { @@ -426,8 +426,8 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea } else { iiifRegion = [ iiifTileX, iiifTileY, iiifTileW, iiifTileH ].join( ',' ); } - iiifSizeW = Math.ceil( iiifTileW * scale ); - iiifSizeH = Math.ceil( iiifTileH * scale ); + iiifSizeW = Math.round( iiifTileW * scale ); + iiifSizeH = Math.round( iiifTileH * scale ); if ( this.version === 2 && iiifSizeW === this.width ) { iiifSize = "full"; } else if ( this.version === 3 && iiifSizeW === this.width && iiifSizeH === this.height ) { diff --git a/src/viewport.js b/src/viewport.js index 4a68849c..77d02f2c 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -913,17 +913,15 @@ $.Viewport.prototype = { }, /** - * Rotates this viewport to the angle specified. Alias for rotateTo. + * Rotates this viewport to the angle specified. * @function * @param {Number} degrees The degrees to set the rotation to. - * @param {OpenSeadragon.Point} [pivot] (Optional) point in viewport coordinates - * around which the rotation should be performed. Defaults to the center of the viewport. * @param {Boolean} [immediately=false] Whether to animate to the new angle * or rotate immediately. * * @returns {OpenSeadragon.Viewport} Chainable. */ - setRotation: function(degrees, pivot, immediately) { - return this.rotateTo(degrees, pivot, immediately); + setRotation: function(degrees, immediately) { + return this.rotateTo(degrees, null, immediately); }, /** @@ -938,6 +936,20 @@ $.Viewport.prototype = { this.degreesSpring.target.value; }, + /** + * Rotates this viewport to the angle specified around a pivot point. Alias for rotateTo. + * @function + * @param {Number} degrees The degrees to set the rotation to. + * @param {OpenSeadragon.Point} [pivot] (Optional) point in viewport coordinates + * around which the rotation should be performed. Defaults to the center of the viewport. + * @param {Boolean} [immediately=false] Whether to animate to the new angle + * or rotate immediately. + * * @returns {OpenSeadragon.Viewport} Chainable. + */ + setRotationWithPivot: function(degrees, pivot, immediately) { + return this.rotateTo(degrees, pivot, immediately); + }, + /** * Rotates this viewport to the angle specified. * @function diff --git a/test/modules/drawer.js b/test/modules/drawer.js index e39b48b5..d40c47c1 100644 --- a/test/modules/drawer.js +++ b/test/modules/drawer.js @@ -46,7 +46,7 @@ }); viewer.addHandler('open', function handler(event) { - viewer.viewport.setRotation(30, null, true); + viewer.viewport.setRotation(30, true); Util.spyOnce(viewer.drawer.context, 'rotate', function() { assert.ok(true, 'drawing with new rotation'); done(); diff --git a/test/modules/navigator.js b/test/modules/navigator.js index e4636108..0859abb9 100644 --- a/test/modules/navigator.js +++ b/test/modules/navigator.js @@ -248,14 +248,8 @@ }; var dragNavigatorBackToCenter = function () { - var start = viewer.viewport.getBounds().getTopLeft(), - target = new OpenSeadragon.Point(0.5 - viewer.viewport.getBounds().width / 2, - 1 / viewer.source.aspectRatio / 2 - viewer.viewport.getBounds().height / 2), - delta = target.minus(start); - if (viewer.source.aspectRatio < 1) { - delta.y *= viewer.source.aspectRatio; - } - simulateNavigatorDrag(viewer.navigator, delta.x * displayRegionWidth, delta.y * displayRegionHeight); + var delta = viewer.viewport.getHomeBounds().getCenter().minus(viewer.viewport.getCenter()).times(displayRegionWidth); + simulateNavigatorDrag(viewer.navigator, delta.x, delta.y); }; var resizeElement = function ($element, width, height) { diff --git a/test/modules/tiledimage.js b/test/modules/tiledimage.js index 4a075328..a6b962f7 100644 --- a/test/modules/tiledimage.js +++ b/test/modules/tiledimage.js @@ -9,6 +9,7 @@ testLog.reset(); + // eslint-disable-next-line new-cap viewer = OpenSeadragon({ id: 'example', prefixUrl: '/build/openseadragon/images/', @@ -517,12 +518,17 @@ var image = viewer.world.getItemAt(0); assert.equal(image.getFullyLoaded(), false, 'not fully loaded at first'); + // Zoom out enough that we don't start out with all the tiles loaded. + viewer.viewport.zoomBy(0.5, null, true); + var count = 0; var fullyLoadedChangeHandler = function(event) { if (count === 0) { assert.equal(event.fullyLoaded, true, 'event includes true fullyLoaded property'); assert.equal(image.getFullyLoaded(), true, 'image is fully loaded after event'); + + // Zoom in enough that it needs to load some new tiles. viewer.viewport.zoomBy(5, null, true); } else if (count === 1) { assert.equal(event.fullyLoaded, false, 'event includes false fullyLoaded property'); diff --git a/test/modules/units.js b/test/modules/units.js index 9f350130..ee47ebe1 100644 --- a/test/modules/units.js +++ b/test/modules/units.js @@ -212,13 +212,13 @@ checkPoint(assert, ' after zoom and pan'); //Restore rotation - viewer.viewport.setRotation(0, null, true); + viewer.viewport.setRotation(0, true); done(); }); viewer.viewport.zoomTo(0.8).panTo(new OpenSeadragon.Point(0.1, 0.2)); }); - viewer.viewport.setRotation(45, null, true); + viewer.viewport.setRotation(45, true); viewer.open([{ tileSource: "/test/data/testpattern.dzi" }, { diff --git a/test/modules/viewport.js b/test/modules/viewport.js index 1e4c91f8..e8267efc 100644 --- a/test/modules/viewport.js +++ b/test/modules/viewport.js @@ -247,7 +247,7 @@ function openHandler() { viewer.removeHandler('open', openHandler); var viewport = viewer.viewport; - viewport.setRotation(-675, null, true); + viewport.setRotation(-675, true); Util.assertRectangleEquals( assert, viewport.getHomeBoundsNoRotate(), @@ -269,7 +269,7 @@ function openHandler() { viewer.removeHandler('open', openHandler); var viewport = viewer.viewport; - viewport.setRotation(-675, null, true); + viewport.setRotation(-675, true); Util.assertRectangleEquals( assert, viewport.getHomeBounds(), @@ -533,7 +533,7 @@ var openHandler = function() { viewer.removeHandler('open', openHandler); var viewport = viewer.viewport; - viewport.setRotation(45, null, true); + viewport.setRotation(45, true); viewport.fitBounds(new OpenSeadragon.Rect(1, 1, 1, 1), true); viewport.applyConstraints(true); var bounds = viewport.getBounds(); @@ -557,7 +557,7 @@ var viewport = viewer.viewport; viewport.setFlip(true); - viewport.setRotation(45, null, true); + viewport.setRotation(45, true); viewport.fitBounds(new OpenSeadragon.Rect(1, 1, 1, 1), true); viewport.applyConstraints(true); @@ -659,7 +659,7 @@ var openHandler = function(event) { viewer.removeHandler('open', openHandler); var viewport = viewer.viewport; - viewport.setRotation(45, null, true); + viewport.setRotation(45, true); for(var i = 0; i < testRectsFitBounds.length; i++){ var rect = testRectsFitBounds[i]; @@ -1066,12 +1066,12 @@ var viewport = viewer.viewport; assert.propEqual(viewport.getRotation, 0, "Original rotation should be 0 degrees"); - viewport.setRotation(90, null, true); + viewport.setRotation(90, true); assert.propEqual(viewport.getRotation, 90, "Rotation should be 90 degrees"); - viewport.setRotation(-75, null, true); + viewport.setRotation(-75, true); assert.propEqual(viewport.getRotation, -75, "Rotation should be -75 degrees"); - viewport.setRotation(0, null, true); + viewport.setRotation(0, true); assert.strictEqual(viewport.getRotation(true), 0, 'viewport has default current rotation'); assert.strictEqual(viewport.getRotation(false), 0, 'viewport has default target rotation'); @@ -1079,7 +1079,7 @@ assert.strictEqual(viewport.getRotation(true), 0, 'current rotation is not changed'); assert.strictEqual(viewport.getRotation(false), 33, 'target rotation is set correctly'); - viewport.setRotation(200, null, true); + viewport.setRotation(200, true); assert.strictEqual(viewport.getRotation(true), 200, 'current rotation is set correctly'); assert.strictEqual(viewport.getRotation(false), 200, 'target rotation is set correctly'); @@ -1099,9 +1099,9 @@ viewport.setFlip(true); assert.propEqual(viewport.getRotation, 0, "Original flipped rotation should be 0 degrees"); - viewport.setRotation(90, null, true); + viewport.setRotation(90, true); assert.propEqual(viewport.getRotation, 90, "Flipped rotation should be 90 degrees"); - viewport.setRotation(-75, null, true); + viewport.setRotation(-75, true); assert.propEqual(viewport.getRotation, -75, "Flipped rotation should be -75 degrees"); done(); diff --git a/test/test.html b/test/test.html index 7a1a6b1a..8b85a123 100644 --- a/test/test.html +++ b/test/test.html @@ -50,6 +50,6 @@ - +