mirror of
https://github.com/openseadragon/openseadragon.git
synced 2024-11-22 05:06:09 +03:00
commit
eb8b9ccd50
@ -328,8 +328,7 @@ $.Drawer.prototype = {
|
|||||||
// the viewport get rotated later on, we will need to resize it.
|
// the viewport get rotated later on, we will need to resize it.
|
||||||
if (this.viewport.getRotation() === 0) {
|
if (this.viewport.getRotation() === 0) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.viewer.addHandler('rotate', function resizeSketchCanvas() {
|
this.viewer.addOnceHandler('rotate', function resizeSketchCanvas() {
|
||||||
self.viewer.removeHandler('rotate', resizeSketchCanvas);
|
|
||||||
var sketchCanvasSize = self._calculateSketchCanvasSize();
|
var sketchCanvasSize = self._calculateSketchCanvasSize();
|
||||||
self.sketchCanvas.width = sketchCanvasSize.x;
|
self.sketchCanvas.width = sketchCanvasSize.x;
|
||||||
self.sketchCanvas.height = sketchCanvasSize.y;
|
self.sketchCanvas.height = sketchCanvasSize.y;
|
||||||
@ -482,7 +481,7 @@ $.Drawer.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// private
|
// private
|
||||||
drawDebugInfo: function( tile, count, i ){
|
drawDebugInfo: function(tile, count, i, tiledImage) {
|
||||||
if ( !this.useCanvas ) {
|
if ( !this.useCanvas ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -495,7 +494,14 @@ $.Drawer.prototype = {
|
|||||||
context.fillStyle = this.debugGridColor;
|
context.fillStyle = this.debugGridColor;
|
||||||
|
|
||||||
if ( this.viewport.degrees !== 0 ) {
|
if ( this.viewport.degrees !== 0 ) {
|
||||||
this._offsetForRotation(this.viewport.degrees);
|
this._offsetForRotation({degrees: this.viewport.degrees});
|
||||||
|
}
|
||||||
|
if (tiledImage.getRotation() !== 0) {
|
||||||
|
this._offsetForRotation({
|
||||||
|
degrees: tiledImage.getRotation(),
|
||||||
|
point: tiledImage.viewport.pixelFromPointNoRotate(
|
||||||
|
tiledImage._getRotationPoint(true), true)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
context.strokeRect(
|
context.strokeRect(
|
||||||
@ -559,6 +565,9 @@ $.Drawer.prototype = {
|
|||||||
if ( this.viewport.degrees !== 0 ) {
|
if ( this.viewport.degrees !== 0 ) {
|
||||||
this._restoreRotationChanges();
|
this._restoreRotationChanges();
|
||||||
}
|
}
|
||||||
|
if (tiledImage.getRotation() !== 0) {
|
||||||
|
this._restoreRotationChanges();
|
||||||
|
}
|
||||||
context.restore();
|
context.restore();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -592,17 +601,22 @@ $.Drawer.prototype = {
|
|||||||
return new $.Point(canvas.width, canvas.height);
|
return new $.Point(canvas.width, canvas.height);
|
||||||
},
|
},
|
||||||
|
|
||||||
// private
|
getCanvasCenter: function() {
|
||||||
_offsetForRotation: function(degrees, useSketch) {
|
return new $.Point(this.canvas.width / 2, this.canvas.height / 2);
|
||||||
var cx = this.canvas.width / 2;
|
},
|
||||||
var cy = this.canvas.height / 2;
|
|
||||||
|
|
||||||
var context = this._getContext(useSketch);
|
// private
|
||||||
|
_offsetForRotation: function(options) {
|
||||||
|
var point = options.point ?
|
||||||
|
options.point.times($.pixelDensityRatio) :
|
||||||
|
this.getCanvasCenter();
|
||||||
|
|
||||||
|
var context = this._getContext(options.useSketch);
|
||||||
context.save();
|
context.save();
|
||||||
|
|
||||||
context.translate(cx, cy);
|
context.translate(point.x, point.y);
|
||||||
context.rotate(Math.PI / 180 * degrees);
|
context.rotate(Math.PI / 180 * options.degrees);
|
||||||
context.translate(-cx, -cy);
|
context.translate(-point.x, -point.y);
|
||||||
},
|
},
|
||||||
|
|
||||||
// private
|
// private
|
||||||
|
@ -341,9 +341,12 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
|
|||||||
myItem._originalForNavigator = original;
|
myItem._originalForNavigator = original;
|
||||||
_this._matchBounds(myItem, original, true);
|
_this._matchBounds(myItem, original, true);
|
||||||
|
|
||||||
original.addHandler('bounds-change', function() {
|
function matchBounds() {
|
||||||
_this._matchBounds(myItem, original);
|
_this._matchBounds(myItem, original);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
original.addHandler('bounds-change', matchBounds);
|
||||||
|
original.addHandler('clip-change', matchBounds);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1375,6 +1375,21 @@ function OpenSeadragon( options ){
|
|||||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the modulo of a number but makes sure to always return
|
||||||
|
* a positive value.
|
||||||
|
* @param {Number} number the number to computes the modulo of
|
||||||
|
* @param {Number} modulo the modulo
|
||||||
|
* @returns {Number} the result of the modulo of number
|
||||||
|
*/
|
||||||
|
positiveModulo: function(number, modulo) {
|
||||||
|
var result = number % modulo;
|
||||||
|
if (result < 0) {
|
||||||
|
result += modulo;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if a point is within the bounding rectangle of the given element (hit-test).
|
* Determines if a point is within the bounding rectangle of the given element (hit-test).
|
||||||
* @function
|
* @function
|
||||||
|
@ -202,10 +202,7 @@ $.Point.prototype = {
|
|||||||
var sin;
|
var sin;
|
||||||
// Avoid float computations when possible
|
// Avoid float computations when possible
|
||||||
if (degrees % 90 === 0) {
|
if (degrees % 90 === 0) {
|
||||||
var d = degrees % 360;
|
var d = $.positiveModulo(degrees, 360);
|
||||||
if (d < 0) {
|
|
||||||
d += 360;
|
|
||||||
}
|
|
||||||
switch (d) {
|
switch (d) {
|
||||||
case 0:
|
case 0:
|
||||||
cos = 1;
|
cos = 1;
|
||||||
|
@ -81,10 +81,7 @@ $.Rect = function(x, y, width, height, degrees) {
|
|||||||
this.degrees = typeof(degrees) === "number" ? degrees : 0;
|
this.degrees = typeof(degrees) === "number" ? degrees : 0;
|
||||||
|
|
||||||
// Normalizes the rectangle.
|
// Normalizes the rectangle.
|
||||||
this.degrees = this.degrees % 360;
|
this.degrees = $.positiveModulo(this.degrees, 360);
|
||||||
if (this.degrees < 0) {
|
|
||||||
this.degrees += 360;
|
|
||||||
}
|
|
||||||
var newTopLeft, newWidth;
|
var newTopLeft, newWidth;
|
||||||
if (this.degrees >= 270) {
|
if (this.degrees >= 270) {
|
||||||
newTopLeft = this.getTopRight();
|
newTopLeft = this.getTopRight();
|
||||||
@ -442,19 +439,21 @@ $.Rect.prototype = {
|
|||||||
* @return {OpenSeadragon.Rect}
|
* @return {OpenSeadragon.Rect}
|
||||||
*/
|
*/
|
||||||
rotate: function(degrees, pivot) {
|
rotate: function(degrees, pivot) {
|
||||||
degrees = degrees % 360;
|
degrees = $.positiveModulo(degrees, 360);
|
||||||
if (degrees === 0) {
|
if (degrees === 0) {
|
||||||
return this.clone();
|
return this.clone();
|
||||||
}
|
}
|
||||||
if (degrees < 0) {
|
|
||||||
degrees += 360;
|
|
||||||
}
|
|
||||||
|
|
||||||
pivot = pivot || this.getCenter();
|
pivot = pivot || this.getCenter();
|
||||||
var newTopLeft = this.getTopLeft().rotate(degrees, pivot);
|
var newTopLeft = this.getTopLeft().rotate(degrees, pivot);
|
||||||
var newTopRight = this.getTopRight().rotate(degrees, pivot);
|
var newTopRight = this.getTopRight().rotate(degrees, pivot);
|
||||||
|
|
||||||
var diff = newTopRight.minus(newTopLeft);
|
var diff = newTopRight.minus(newTopLeft);
|
||||||
|
// Handle floating point error
|
||||||
|
diff = diff.apply(function(x) {
|
||||||
|
var EPSILON = 1e-15;
|
||||||
|
return Math.abs(x) < EPSILON ? 0 : x;
|
||||||
|
});
|
||||||
var radians = Math.atan(diff.y / diff.x);
|
var radians = Math.atan(diff.y / diff.x);
|
||||||
if (diff.x < 0) {
|
if (diff.x < 0) {
|
||||||
radians += Math.PI;
|
radians += Math.PI;
|
||||||
|
@ -132,6 +132,9 @@ $.TiledImage = function( options ) {
|
|||||||
var fitBoundsPlacement = options.fitBoundsPlacement || OpenSeadragon.Placement.CENTER;
|
var fitBoundsPlacement = options.fitBoundsPlacement || OpenSeadragon.Placement.CENTER;
|
||||||
delete options.fitBoundsPlacement;
|
delete options.fitBoundsPlacement;
|
||||||
|
|
||||||
|
this._degrees = $.positiveModulo(options.degrees || 0, 360);
|
||||||
|
delete options.degrees;
|
||||||
|
|
||||||
$.extend( true, this, {
|
$.extend( true, this, {
|
||||||
|
|
||||||
//internal state properties
|
//internal state properties
|
||||||
@ -160,7 +163,6 @@ $.TiledImage = function( options ) {
|
|||||||
placeholderFillStyle: $.DEFAULT_SETTINGS.placeholderFillStyle,
|
placeholderFillStyle: $.DEFAULT_SETTINGS.placeholderFillStyle,
|
||||||
opacity: $.DEFAULT_SETTINGS.opacity,
|
opacity: $.DEFAULT_SETTINGS.opacity,
|
||||||
compositeOperation: $.DEFAULT_SETTINGS.compositeOperation
|
compositeOperation: $.DEFAULT_SETTINGS.compositeOperation
|
||||||
|
|
||||||
}, options );
|
}, options );
|
||||||
|
|
||||||
this._fullyLoaded = false;
|
this._fullyLoaded = false;
|
||||||
@ -290,7 +292,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
draw: function() {
|
draw: function() {
|
||||||
if (this.opacity !== 0) {
|
if (this.opacity !== 0) {
|
||||||
this._midDraw = true;
|
this._midDraw = true;
|
||||||
updateViewport(this);
|
this._updateViewport();
|
||||||
this._midDraw = false;
|
this._midDraw = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -303,17 +305,35 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Get this TiledImage's bounds in viewport coordinates.
|
||||||
|
* @param {Boolean} [current=false] - Pass true for the current location;
|
||||||
|
* false for target location.
|
||||||
* @returns {OpenSeadragon.Rect} This TiledImage's bounds in viewport coordinates.
|
* @returns {OpenSeadragon.Rect} This TiledImage's bounds in viewport coordinates.
|
||||||
* @param {Boolean} [current=false] - Pass true for the current location; false for target location.
|
|
||||||
*/
|
*/
|
||||||
getBounds: function(current) {
|
getBounds: function(current) {
|
||||||
if (current) {
|
return this.getBoundsNoRotate(current)
|
||||||
return new $.Rect( this._xSpring.current.value, this._ySpring.current.value,
|
.rotate(this._degrees, this._getRotationPoint(current));
|
||||||
this._worldWidthCurrent, this._worldHeightCurrent );
|
},
|
||||||
}
|
|
||||||
|
|
||||||
return new $.Rect( this._xSpring.target.value, this._ySpring.target.value,
|
/**
|
||||||
this._worldWidthTarget, this._worldHeightTarget );
|
* Get this TiledImage's bounds in viewport coordinates without taking
|
||||||
|
* rotation into account.
|
||||||
|
* @param {Boolean} [current=false] - Pass true for the current location;
|
||||||
|
* false for target location.
|
||||||
|
* @returns {OpenSeadragon.Rect} This TiledImage's bounds in viewport coordinates.
|
||||||
|
*/
|
||||||
|
getBoundsNoRotate: function(current) {
|
||||||
|
return current ?
|
||||||
|
new $.Rect(
|
||||||
|
this._xSpring.current.value,
|
||||||
|
this._ySpring.current.value,
|
||||||
|
this._worldWidthCurrent,
|
||||||
|
this._worldHeightCurrent) :
|
||||||
|
new $.Rect(
|
||||||
|
this._xSpring.target.value,
|
||||||
|
this._ySpring.target.value,
|
||||||
|
this._worldWidthTarget,
|
||||||
|
this._worldHeightTarget);
|
||||||
},
|
},
|
||||||
|
|
||||||
// deprecated
|
// deprecated
|
||||||
@ -329,9 +349,11 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
* @returns {$.Rect} The clipped bounds in viewport coordinates.
|
* @returns {$.Rect} The clipped bounds in viewport coordinates.
|
||||||
*/
|
*/
|
||||||
getClippedBounds: function(current) {
|
getClippedBounds: function(current) {
|
||||||
var bounds = this.getBounds(current);
|
var bounds = this.getBoundsNoRotate(current);
|
||||||
if (this._clip) {
|
if (this._clip) {
|
||||||
var ratio = this._worldWidthCurrent / this.source.dimensions.x;
|
var worldWidth = current ?
|
||||||
|
this._worldWidthCurrent : this._worldWidthTarget;
|
||||||
|
var ratio = worldWidth / this.source.dimensions.x;
|
||||||
var clip = this._clip.times(ratio);
|
var clip = this._clip.times(ratio);
|
||||||
bounds = new $.Rect(
|
bounds = new $.Rect(
|
||||||
bounds.x + clip.x,
|
bounds.x + clip.x,
|
||||||
@ -339,7 +361,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
clip.width,
|
clip.width,
|
||||||
clip.height);
|
clip.height);
|
||||||
}
|
}
|
||||||
return bounds;
|
return bounds.rotate(this._degrees, this._getRotationPoint(current));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -364,21 +386,24 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
* @param {Boolean} [current=false] - Pass true to use the current location; false for target location.
|
* @param {Boolean} [current=false] - Pass true to use the current location; false for target location.
|
||||||
* @return {OpenSeadragon.Point} A point representing the coordinates in the image.
|
* @return {OpenSeadragon.Point} A point representing the coordinates in the image.
|
||||||
*/
|
*/
|
||||||
viewportToImageCoordinates: function( viewerX, viewerY, current ) {
|
viewportToImageCoordinates: function(viewerX, viewerY, current) {
|
||||||
|
var point;
|
||||||
if (viewerX instanceof $.Point) {
|
if (viewerX instanceof $.Point) {
|
||||||
//they passed a point instead of individual components
|
//they passed a point instead of individual components
|
||||||
current = viewerY;
|
current = viewerY;
|
||||||
viewerY = viewerX.y;
|
point = viewerX;
|
||||||
viewerX = viewerX.x;
|
} else {
|
||||||
|
point = new $.Point(viewerX, viewerY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current) {
|
point = point.rotate(-this._degrees, this._getRotationPoint(current));
|
||||||
return this._viewportToImageDelta(viewerX - this._xSpring.current.value,
|
return current ?
|
||||||
viewerY - this._ySpring.current.value);
|
this._viewportToImageDelta(
|
||||||
}
|
point.x - this._xSpring.current.value,
|
||||||
|
point.y - this._ySpring.current.value) :
|
||||||
return this._viewportToImageDelta(viewerX - this._xSpring.target.value,
|
this._viewportToImageDelta(
|
||||||
viewerY - this._ySpring.target.value);
|
point.x - this._xSpring.target.value,
|
||||||
|
point.y - this._ySpring.target.value);
|
||||||
},
|
},
|
||||||
|
|
||||||
// private
|
// private
|
||||||
@ -396,7 +421,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
* @param {Boolean} [current=false] - Pass true to use the current location; false for target location.
|
* @param {Boolean} [current=false] - Pass true to use the current location; false for target location.
|
||||||
* @return {OpenSeadragon.Point} A point representing the coordinates in the viewport.
|
* @return {OpenSeadragon.Point} A point representing the coordinates in the viewport.
|
||||||
*/
|
*/
|
||||||
imageToViewportCoordinates: function( imageX, imageY, current ) {
|
imageToViewportCoordinates: function(imageX, imageY, current) {
|
||||||
if (imageX instanceof $.Point) {
|
if (imageX instanceof $.Point) {
|
||||||
//they passed a point instead of individual components
|
//they passed a point instead of individual components
|
||||||
current = imageY;
|
current = imageY;
|
||||||
@ -413,7 +438,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
point.y += this._ySpring.target.value;
|
point.y += this._ySpring.target.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return point;
|
return point.rotate(this._degrees, this._getRotationPoint(current));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -444,7 +469,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
coordA.y,
|
coordA.y,
|
||||||
coordB.x,
|
coordB.x,
|
||||||
coordB.y,
|
coordB.y,
|
||||||
rect.degrees
|
rect.degrees + this._degrees
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -476,7 +501,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
coordA.y,
|
coordA.y,
|
||||||
coordB.x,
|
coordB.x,
|
||||||
coordB.y,
|
coordB.y,
|
||||||
rect.degrees
|
rect.degrees - this._degrees
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -524,6 +549,20 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
OpenSeadragon.getElementPosition( this.viewer.element ));
|
OpenSeadragon.getElementPosition( this.viewer.element ));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// private
|
||||||
|
// Convert rectangle in viewport coordinates to this tiled image point
|
||||||
|
// 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));
|
||||||
|
return new $.Rect(
|
||||||
|
(rect.x - this._xSpring.current.value) / scale,
|
||||||
|
(rect.y - this._ySpring.current.value) / scale,
|
||||||
|
rect.width / scale,
|
||||||
|
rect.height / scale,
|
||||||
|
rect.degrees);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a viewport zoom to an image zoom.
|
* Convert a viewport zoom to an image zoom.
|
||||||
* Image zoom: ratio of the original image size to displayed image size.
|
* Image zoom: ratio of the original image size to displayed image size.
|
||||||
@ -688,6 +727,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
* @param {OpenSeadragon.Rect|null} newClip - An area, in image pixels, to clip to
|
* @param {OpenSeadragon.Rect|null} newClip - An area, in image pixels, to clip to
|
||||||
* (portions of the image outside of this area will not be visible). Only works on
|
* (portions of the image outside of this area will not be visible). Only works on
|
||||||
* browsers that support the HTML5 canvas.
|
* browsers that support the HTML5 canvas.
|
||||||
|
* @fires OpenSeadragon.TiledImage.event:clip-change
|
||||||
*/
|
*/
|
||||||
setClip: function(newClip) {
|
setClip: function(newClip) {
|
||||||
$.console.assert(!newClip || newClip instanceof $.Rect,
|
$.console.assert(!newClip || newClip instanceof $.Rect,
|
||||||
@ -700,6 +740,16 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._needsDraw = true;
|
this._needsDraw = true;
|
||||||
|
/**
|
||||||
|
* Raised when the TiledImage's clip is changed.
|
||||||
|
* @event clip-change
|
||||||
|
* @memberOf OpenSeadragon.TiledImage
|
||||||
|
* @type {object}
|
||||||
|
* @property {OpenSeadragon.TiledImage} eventSource - A reference to the
|
||||||
|
* TiledImage which raised the event.
|
||||||
|
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||||
|
*/
|
||||||
|
this.raiseEvent('clip-change');
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -717,6 +767,39 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
this._needsDraw = true;
|
this._needsDraw = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current rotation of this tiled image in degrees.
|
||||||
|
* @returns {Number} the current rotation of this tiled image in degrees.
|
||||||
|
*/
|
||||||
|
getRotation: function() {
|
||||||
|
return this._degrees;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current rotation of this tiled image in degrees.
|
||||||
|
* @param {Number} degrees the rotation in degrees.
|
||||||
|
* @fires OpenSeadragon.TiledImage.event:bounds-change
|
||||||
|
*/
|
||||||
|
setRotation: function(degrees) {
|
||||||
|
degrees = $.positiveModulo(degrees, 360);
|
||||||
|
if (this._degrees === degrees) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._degrees = degrees;
|
||||||
|
this._needsDraw = true;
|
||||||
|
this._raiseBoundsChange();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* Get the point around which this tiled image is rotated
|
||||||
|
* @param {Boolean} current True for current rotation point, false for target.
|
||||||
|
* @returns {OpenSeadragon.Point}
|
||||||
|
*/
|
||||||
|
_getRotationPoint: function(current) {
|
||||||
|
return this.getBoundsNoRotate(current).getCenter();
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {String} The TiledImage's current compositeOperation.
|
* @returns {String} The TiledImage's current compositeOperation.
|
||||||
*/
|
*/
|
||||||
@ -775,7 +858,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
* @event bounds-change
|
* @event bounds-change
|
||||||
* @memberOf OpenSeadragon.TiledImage
|
* @memberOf OpenSeadragon.TiledImage
|
||||||
* @type {object}
|
* @type {object}
|
||||||
* @property {OpenSeadragon.World} eventSource - A reference to the TiledImage which raised the event.
|
* @property {OpenSeadragon.TiledImage} eventSource - A reference to the
|
||||||
|
* TiledImage which raised the event.
|
||||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||||
*/
|
*/
|
||||||
this.raiseEvent('bounds-change');
|
this.raiseEvent('bounds-change');
|
||||||
@ -784,187 +868,144 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
// private
|
// private
|
||||||
_isBottomItem: function() {
|
_isBottomItem: function() {
|
||||||
return this.viewer.world.getItemAt(0) === this;
|
return this.viewer.world.getItemAt(0) === this;
|
||||||
|
},
|
||||||
|
|
||||||
|
// private
|
||||||
|
_getLevelsInterval: function() {
|
||||||
|
var lowestLevel = Math.max(
|
||||||
|
this.source.minLevel,
|
||||||
|
Math.floor(Math.log(this.minZoomImageRatio) / Math.log(2))
|
||||||
|
);
|
||||||
|
var currentZeroRatio = this.viewport.deltaPixelsFromPointsNoRotate(
|
||||||
|
this.source.getPixelRatio(0), true).x *
|
||||||
|
this._scaleSpring.current.value;
|
||||||
|
var highestLevel = Math.min(
|
||||||
|
Math.abs(this.source.maxLevel),
|
||||||
|
Math.abs(Math.floor(
|
||||||
|
Math.log(currentZeroRatio / this.minPixelRatio) / Math.log(2)
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculations for the interval of levels to draw
|
||||||
|
// can return invalid intervals; fix that here if necessary
|
||||||
|
lowestLevel = Math.min(lowestLevel, highestLevel);
|
||||||
|
return {
|
||||||
|
lowestLevel: lowestLevel,
|
||||||
|
highestLevel: highestLevel
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
// private
|
||||||
|
_updateViewport: function() {
|
||||||
|
this._needsDraw = false;
|
||||||
|
|
||||||
|
// Reset tile's internal drawn state
|
||||||
|
while (this.lastDrawn.length > 0) {
|
||||||
|
var tile = this.lastDrawn.pop();
|
||||||
|
tile.beingDrawn = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var viewport = this.viewport;
|
||||||
|
var drawArea = this._viewportToTiledImageRectangle(
|
||||||
|
viewport.getBoundsWithMargins(true));
|
||||||
|
|
||||||
|
if (!this.wrapHorizontal && !this.wrapVertical) {
|
||||||
|
var tiledImageBounds = this._viewportToTiledImageRectangle(
|
||||||
|
this.getClippedBounds(true));
|
||||||
|
drawArea = drawArea.intersection(tiledImageBounds);
|
||||||
|
if (drawArea === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var levelsInterval = this._getLevelsInterval();
|
||||||
|
var lowestLevel = levelsInterval.lowestLevel;
|
||||||
|
var highestLevel = levelsInterval.highestLevel;
|
||||||
|
var bestTile = null;
|
||||||
|
var haveDrawn = false;
|
||||||
|
var currentTime = $.now();
|
||||||
|
|
||||||
|
// Update any level that will be drawn
|
||||||
|
for (var level = highestLevel; level >= lowestLevel; level--) {
|
||||||
|
var drawLevel = false;
|
||||||
|
|
||||||
|
//Avoid calculations for draw if we have already drawn this
|
||||||
|
var currentRenderPixelRatio = viewport.deltaPixelsFromPointsNoRotate(
|
||||||
|
this.source.getPixelRatio(level),
|
||||||
|
true
|
||||||
|
).x * this._scaleSpring.current.value;
|
||||||
|
|
||||||
|
if (level === lowestLevel ||
|
||||||
|
(!haveDrawn && currentRenderPixelRatio >= this.minPixelRatio)) {
|
||||||
|
drawLevel = true;
|
||||||
|
haveDrawn = true;
|
||||||
|
} else if (!haveDrawn) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Perform calculations for draw if we haven't drawn this
|
||||||
|
var targetRenderPixelRatio = viewport.deltaPixelsFromPointsNoRotate(
|
||||||
|
this.source.getPixelRatio(level),
|
||||||
|
false
|
||||||
|
).x * this._scaleSpring.current.value;
|
||||||
|
|
||||||
|
var targetZeroRatio = viewport.deltaPixelsFromPointsNoRotate(
|
||||||
|
this.source.getPixelRatio(
|
||||||
|
Math.max(
|
||||||
|
this.source.getClosestLevel(viewport.containerSize) - 1,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
),
|
||||||
|
false
|
||||||
|
).x * this._scaleSpring.current.value;
|
||||||
|
|
||||||
|
var optimalRatio = this.immediateRender ? 1 : targetZeroRatio;
|
||||||
|
var levelOpacity = Math.min(1, (currentRenderPixelRatio - 0.5) / 0.5);
|
||||||
|
var levelVisibility = optimalRatio / Math.abs(
|
||||||
|
optimalRatio - targetRenderPixelRatio
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update the level and keep track of 'best' tile to load
|
||||||
|
bestTile = updateLevel(
|
||||||
|
this,
|
||||||
|
haveDrawn,
|
||||||
|
drawLevel,
|
||||||
|
level,
|
||||||
|
levelOpacity,
|
||||||
|
levelVisibility,
|
||||||
|
drawArea,
|
||||||
|
currentTime,
|
||||||
|
bestTile
|
||||||
|
);
|
||||||
|
|
||||||
|
// Stop the loop if lower-res tiles would all be covered by
|
||||||
|
// already drawn tiles
|
||||||
|
if (providesCoverage(this.coverage, level)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the actual drawing
|
||||||
|
drawTiles(this, this.lastDrawn);
|
||||||
|
|
||||||
|
// Load the new 'best' tile
|
||||||
|
if (bestTile && !bestTile.context2D) {
|
||||||
|
loadTile(this, bestTile, currentTime);
|
||||||
|
this._needsDraw = true;
|
||||||
|
this._setFullyLoaded(false);
|
||||||
|
} else {
|
||||||
|
this._setFullyLoaded(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
function updateLevel(tiledImage, haveDrawn, drawLevel, level, levelOpacity,
|
||||||
* @private
|
levelVisibility, drawArea, currentTime, best) {
|
||||||
* @inner
|
|
||||||
* Pretty much every other line in this needs to be documented so it's clear
|
|
||||||
* how each piece of this routine contributes to the drawing process. That's
|
|
||||||
* why there are so many TODO's inside this function.
|
|
||||||
*/
|
|
||||||
function updateViewport( tiledImage ) {
|
|
||||||
|
|
||||||
tiledImage._needsDraw = false;
|
var topLeftBound = drawArea.getBoundingBox().getTopLeft();
|
||||||
|
var bottomRightBound = drawArea.getBoundingBox().getBottomRight();
|
||||||
|
|
||||||
var tile,
|
if (tiledImage.viewer) {
|
||||||
level,
|
|
||||||
best = null,
|
|
||||||
haveDrawn = false,
|
|
||||||
currentTime = $.now(),
|
|
||||||
viewportBounds = tiledImage.viewport.getBoundsWithMargins( true ),
|
|
||||||
zeroRatioC = tiledImage.viewport.deltaPixelsFromPointsNoRotate(
|
|
||||||
tiledImage.source.getPixelRatio( 0 ),
|
|
||||||
true
|
|
||||||
).x * tiledImage._scaleSpring.current.value,
|
|
||||||
lowestLevel = Math.max(
|
|
||||||
tiledImage.source.minLevel,
|
|
||||||
Math.floor(
|
|
||||||
Math.log( tiledImage.minZoomImageRatio ) /
|
|
||||||
Math.log( 2 )
|
|
||||||
)
|
|
||||||
),
|
|
||||||
highestLevel = Math.min(
|
|
||||||
Math.abs(tiledImage.source.maxLevel),
|
|
||||||
Math.abs(Math.floor(
|
|
||||||
Math.log( zeroRatioC / tiledImage.minPixelRatio ) /
|
|
||||||
Math.log( 2 )
|
|
||||||
))
|
|
||||||
),
|
|
||||||
renderPixelRatioC,
|
|
||||||
renderPixelRatioT,
|
|
||||||
zeroRatioT,
|
|
||||||
optimalRatio,
|
|
||||||
levelOpacity,
|
|
||||||
levelVisibility;
|
|
||||||
|
|
||||||
// Reset tile's internal drawn state
|
|
||||||
while (tiledImage.lastDrawn.length > 0) {
|
|
||||||
tile = tiledImage.lastDrawn.pop();
|
|
||||||
tile.beingDrawn = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tiledImage.wrapHorizontal && !tiledImage.wrapVertical) {
|
|
||||||
var tiledImageBounds = tiledImage.getClippedBounds(true);
|
|
||||||
var intersection = viewportBounds.intersection(tiledImageBounds);
|
|
||||||
if (intersection === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
viewportBounds = intersection;
|
|
||||||
}
|
|
||||||
viewportBounds = viewportBounds.getBoundingBox();
|
|
||||||
viewportBounds.x -= tiledImage._xSpring.current.value;
|
|
||||||
viewportBounds.y -= tiledImage._ySpring.current.value;
|
|
||||||
|
|
||||||
var viewportTL = viewportBounds.getTopLeft();
|
|
||||||
var viewportBR = viewportBounds.getBottomRight();
|
|
||||||
|
|
||||||
//Don't draw if completely outside of the viewport
|
|
||||||
if ( !tiledImage.wrapHorizontal && (viewportBR.x < 0 || viewportTL.x > tiledImage._worldWidthCurrent ) ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !tiledImage.wrapVertical && ( viewportBR.y < 0 || viewportTL.y > tiledImage._worldHeightCurrent ) ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate viewport rect / bounds
|
|
||||||
if ( !tiledImage.wrapHorizontal ) {
|
|
||||||
viewportTL.x = Math.max( viewportTL.x, 0 );
|
|
||||||
viewportBR.x = Math.min( viewportBR.x, tiledImage._worldWidthCurrent );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !tiledImage.wrapVertical ) {
|
|
||||||
viewportTL.y = Math.max( viewportTL.y, 0 );
|
|
||||||
viewportBR.y = Math.min( viewportBR.y, tiledImage._worldHeightCurrent );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculations for the interval of levels to draw
|
|
||||||
// (above in initial var statement)
|
|
||||||
// can return invalid intervals; fix that here if necessary
|
|
||||||
lowestLevel = Math.min( lowestLevel, highestLevel );
|
|
||||||
|
|
||||||
// Update any level that will be drawn
|
|
||||||
var drawLevel; // FIXME: drawLevel should have a more explanatory name
|
|
||||||
for ( level = highestLevel; level >= lowestLevel; level-- ) {
|
|
||||||
drawLevel = false;
|
|
||||||
|
|
||||||
//Avoid calculations for draw if we have already drawn this
|
|
||||||
renderPixelRatioC = tiledImage.viewport.deltaPixelsFromPointsNoRotate(
|
|
||||||
tiledImage.source.getPixelRatio( level ),
|
|
||||||
true
|
|
||||||
).x * tiledImage._scaleSpring.current.value;
|
|
||||||
|
|
||||||
if ( ( !haveDrawn && renderPixelRatioC >= tiledImage.minPixelRatio ) ||
|
|
||||||
( level == lowestLevel ) ) {
|
|
||||||
drawLevel = true;
|
|
||||||
haveDrawn = true;
|
|
||||||
} else if ( !haveDrawn ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Perform calculations for draw if we haven't drawn this
|
|
||||||
renderPixelRatioT = tiledImage.viewport.deltaPixelsFromPointsNoRotate(
|
|
||||||
tiledImage.source.getPixelRatio( level ),
|
|
||||||
false
|
|
||||||
).x * tiledImage._scaleSpring.current.value;
|
|
||||||
|
|
||||||
zeroRatioT = tiledImage.viewport.deltaPixelsFromPointsNoRotate(
|
|
||||||
tiledImage.source.getPixelRatio(
|
|
||||||
Math.max(
|
|
||||||
tiledImage.source.getClosestLevel( tiledImage.viewport.containerSize ) - 1,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
),
|
|
||||||
false
|
|
||||||
).x * tiledImage._scaleSpring.current.value;
|
|
||||||
|
|
||||||
optimalRatio = tiledImage.immediateRender ?
|
|
||||||
1 :
|
|
||||||
zeroRatioT;
|
|
||||||
|
|
||||||
levelOpacity = Math.min( 1, ( renderPixelRatioC - 0.5 ) / 0.5 );
|
|
||||||
|
|
||||||
levelVisibility = optimalRatio / Math.abs(
|
|
||||||
optimalRatio - renderPixelRatioT
|
|
||||||
);
|
|
||||||
|
|
||||||
// Update the level and keep track of 'best' tile to load
|
|
||||||
best = updateLevel(
|
|
||||||
tiledImage,
|
|
||||||
haveDrawn,
|
|
||||||
drawLevel,
|
|
||||||
level,
|
|
||||||
levelOpacity,
|
|
||||||
levelVisibility,
|
|
||||||
viewportTL,
|
|
||||||
viewportBR,
|
|
||||||
currentTime,
|
|
||||||
best
|
|
||||||
);
|
|
||||||
|
|
||||||
// Stop the loop if lower-res tiles would all be covered by
|
|
||||||
// already drawn tiles
|
|
||||||
if ( providesCoverage( tiledImage.coverage, level ) ) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform the actual drawing
|
|
||||||
drawTiles( tiledImage, tiledImage.lastDrawn );
|
|
||||||
|
|
||||||
// Load the new 'best' tile
|
|
||||||
if (best && !best.context2D) {
|
|
||||||
loadTile( tiledImage, best, currentTime );
|
|
||||||
tiledImage._needsDraw = true;
|
|
||||||
tiledImage._setFullyLoaded(false);
|
|
||||||
} else {
|
|
||||||
tiledImage._setFullyLoaded(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function updateLevel( tiledImage, haveDrawn, drawLevel, level, levelOpacity, levelVisibility, viewportTL, viewportBR, currentTime, best ){
|
|
||||||
|
|
||||||
var x, y,
|
|
||||||
tileTL,
|
|
||||||
tileBR,
|
|
||||||
numberOfTiles,
|
|
||||||
viewportCenter = tiledImage.viewport.pixelFromPoint( tiledImage.viewport.getCenter() );
|
|
||||||
|
|
||||||
|
|
||||||
if( tiledImage.viewer ){
|
|
||||||
/**
|
/**
|
||||||
* <em>- Needs documentation -</em>
|
* <em>- Needs documentation -</em>
|
||||||
*
|
*
|
||||||
@ -977,45 +1018,59 @@ function updateLevel( tiledImage, haveDrawn, drawLevel, level, levelOpacity, lev
|
|||||||
* @property {Object} level
|
* @property {Object} level
|
||||||
* @property {Object} opacity
|
* @property {Object} opacity
|
||||||
* @property {Object} visibility
|
* @property {Object} visibility
|
||||||
* @property {Object} topleft
|
* @property {OpenSeadragon.Rect} drawArea
|
||||||
* @property {Object} bottomright
|
* @property {Object} topleft deprecated, use drawArea instead
|
||||||
|
* @property {Object} bottomright deprecated, use drawArea instead
|
||||||
* @property {Object} currenttime
|
* @property {Object} currenttime
|
||||||
* @property {Object} best
|
* @property {Object} best
|
||||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||||
*/
|
*/
|
||||||
tiledImage.viewer.raiseEvent( 'update-level', {
|
tiledImage.viewer.raiseEvent('update-level', {
|
||||||
tiledImage: tiledImage,
|
tiledImage: tiledImage,
|
||||||
havedrawn: haveDrawn,
|
havedrawn: haveDrawn,
|
||||||
level: level,
|
level: level,
|
||||||
opacity: levelOpacity,
|
opacity: levelOpacity,
|
||||||
visibility: levelVisibility,
|
visibility: levelVisibility,
|
||||||
topleft: viewportTL,
|
drawArea: drawArea,
|
||||||
bottomright: viewportBR,
|
topleft: topLeftBound,
|
||||||
|
bottomright: bottomRightBound,
|
||||||
currenttime: currentTime,
|
currenttime: currentTime,
|
||||||
best: best
|
best: best
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//OK, a new drawing so do your calculations
|
//OK, a new drawing so do your calculations
|
||||||
tileTL = tiledImage.source.getTileAtPoint( level, viewportTL.divide( tiledImage._scaleSpring.current.value ));
|
var topLeftTile = tiledImage.source.getTileAtPoint(level, topLeftBound);
|
||||||
tileBR = tiledImage.source.getTileAtPoint( level, viewportBR.divide( tiledImage._scaleSpring.current.value ));
|
var bottomRightTile = tiledImage.source.getTileAtPoint(level, bottomRightBound);
|
||||||
numberOfTiles = tiledImage.source.getNumTiles( level );
|
var numberOfTiles = tiledImage.source.getNumTiles(level);
|
||||||
|
|
||||||
resetCoverage( tiledImage.coverage, level );
|
resetCoverage(tiledImage.coverage, level);
|
||||||
|
|
||||||
if ( tiledImage.wrapHorizontal ) {
|
if (!tiledImage.wrapHorizontal) {
|
||||||
tileTL.x -= 1; // left invisible column (othervise we will have empty space after scroll at left)
|
// Adjust for floating point error
|
||||||
} else {
|
topLeftTile.x = Math.max(topLeftTile.x, 0);
|
||||||
tileBR.x = Math.min( tileBR.x, numberOfTiles.x - 1 );
|
bottomRightTile.x = Math.min(bottomRightTile.x, numberOfTiles.x - 1);
|
||||||
}
|
}
|
||||||
if ( tiledImage.wrapVertical ) {
|
if (!tiledImage.wrapVertical) {
|
||||||
tileTL.y -= 1; // top invisible row (othervise we will have empty space after scroll at top)
|
// Adjust for floating point error
|
||||||
} else {
|
topLeftTile.y = Math.max(topLeftTile.y, 0);
|
||||||
tileBR.y = Math.min( tileBR.y, numberOfTiles.y - 1 );
|
bottomRightTile.y = Math.min(bottomRightTile.y, numberOfTiles.y - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( x = tileTL.x; x <= tileBR.x; x++ ) {
|
var viewportCenter = tiledImage.viewport.pixelFromPoint(
|
||||||
for ( y = tileTL.y; y <= tileBR.y; y++ ) {
|
tiledImage.viewport.getCenter());
|
||||||
|
for (var x = topLeftTile.x; x <= bottomRightTile.x; x++) {
|
||||||
|
for (var y = topLeftTile.y; y <= bottomRightTile.y; y++) {
|
||||||
|
|
||||||
|
// Optimisation disabled with wrapping because getTileBounds does not
|
||||||
|
// work correctly with x and y outside of the number of tiles
|
||||||
|
if (!tiledImage.wrapHorizontal && !tiledImage.wrapVertical) {
|
||||||
|
var tileBounds = tiledImage.source.getTileBounds(level, x, y);
|
||||||
|
if (drawArea.intersection(tileBounds) === null) {
|
||||||
|
// This tile is outside of the viewport, no need to draw it
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
best = updateTile(
|
best = updateTile(
|
||||||
tiledImage,
|
tiledImage,
|
||||||
@ -1476,7 +1531,9 @@ function drawTiles( tiledImage, lastDrawn ) {
|
|||||||
|
|
||||||
var zoom = tiledImage.viewport.getZoom(true);
|
var zoom = tiledImage.viewport.getZoom(true);
|
||||||
var imageZoom = tiledImage.viewportToImageZoom(zoom);
|
var imageZoom = tiledImage.viewportToImageZoom(zoom);
|
||||||
if (imageZoom > tiledImage.smoothTileEdgesMinZoom && !tiledImage.iOSDevice) {
|
// TODO: support tile edge smoothing with tiled image rotation.
|
||||||
|
if (imageZoom > tiledImage.smoothTileEdgesMinZoom && !tiledImage.iOSDevice &&
|
||||||
|
tiledImage.getRotation() === 0) {
|
||||||
// When zoomed in a lot (>100%) the tile edges are visible.
|
// When zoomed in a lot (>100%) the tile edges are visible.
|
||||||
// So we have to composite them at ~100% and scale them up together.
|
// So we have to composite them at ~100% and scale them up together.
|
||||||
// Note: Disabled on iOS devices per default as it causes a native crash
|
// Note: Disabled on iOS devices per default as it causes a native crash
|
||||||
@ -1500,10 +1557,23 @@ function drawTiles( tiledImage, lastDrawn ) {
|
|||||||
tiledImage._drawer._clear(true, bounds);
|
tiledImage._drawer._clear(true, bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When scaling, we must rotate only when blending the sketch canvas to avoid
|
// When scaling, we must rotate only when blending the sketch canvas to
|
||||||
// interpolation
|
// avoid interpolation
|
||||||
if (tiledImage.viewport.degrees !== 0 && !sketchScale) {
|
if (!sketchScale) {
|
||||||
tiledImage._drawer._offsetForRotation(tiledImage.viewport.degrees, useSketch);
|
if (tiledImage.viewport.degrees !== 0) {
|
||||||
|
tiledImage._drawer._offsetForRotation({
|
||||||
|
degrees: tiledImage.viewport.degrees,
|
||||||
|
useSketch: useSketch
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (tiledImage._degrees !== 0) {
|
||||||
|
tiledImage._drawer._offsetForRotation({
|
||||||
|
degrees: tiledImage._degrees,
|
||||||
|
point: tiledImage.viewport.pixelFromPointNoRotate(
|
||||||
|
tiledImage._getRotationPoint(true), true),
|
||||||
|
useSketch: useSketch
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var usedClip = false;
|
var usedClip = false;
|
||||||
@ -1511,6 +1581,7 @@ function drawTiles( tiledImage, lastDrawn ) {
|
|||||||
tiledImage._drawer.saveContext(useSketch);
|
tiledImage._drawer.saveContext(useSketch);
|
||||||
|
|
||||||
var box = tiledImage.imageToViewportRectangle(tiledImage._clip, true);
|
var box = tiledImage.imageToViewportRectangle(tiledImage._clip, true);
|
||||||
|
box = box.rotate(-tiledImage._degrees, tiledImage._getRotationPoint());
|
||||||
var clipRect = tiledImage._drawer.viewportToDrawerRectangle(box);
|
var clipRect = tiledImage._drawer.viewportToDrawerRectangle(box);
|
||||||
if (sketchScale) {
|
if (sketchScale) {
|
||||||
clipRect = clipRect.times(sketchScale);
|
clipRect = clipRect.times(sketchScale);
|
||||||
@ -1571,14 +1642,31 @@ function drawTiles( tiledImage, lastDrawn ) {
|
|||||||
tiledImage._drawer.restoreContext( useSketch );
|
tiledImage._drawer.restoreContext( useSketch );
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tiledImage.viewport.degrees !== 0 && !sketchScale) {
|
if (!sketchScale) {
|
||||||
tiledImage._drawer._restoreRotationChanges(useSketch);
|
if (tiledImage._degrees !== 0) {
|
||||||
|
tiledImage._drawer._restoreRotationChanges(useSketch);
|
||||||
|
}
|
||||||
|
if (tiledImage.viewport.degrees !== 0) {
|
||||||
|
tiledImage._drawer._restoreRotationChanges(useSketch);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useSketch) {
|
if (useSketch) {
|
||||||
var offsetForRotation = tiledImage.viewport.degrees !== 0 && sketchScale;
|
if (sketchScale) {
|
||||||
if (offsetForRotation) {
|
if (tiledImage.viewport.degrees !== 0) {
|
||||||
tiledImage._drawer._offsetForRotation(tiledImage.viewport.degrees, false);
|
tiledImage._drawer._offsetForRotation({
|
||||||
|
degrees: tiledImage.viewport.degrees,
|
||||||
|
useSketch: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (tiledImage._degrees !== 0) {
|
||||||
|
tiledImage._drawer._offsetForRotation({
|
||||||
|
degrees: tiledImage._degrees,
|
||||||
|
point: tiledImage.viewport.pixelFromPointNoRotate(
|
||||||
|
tiledImage._getRotationPoint(true), true),
|
||||||
|
useSketch: false
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
tiledImage._drawer.blendSketch({
|
tiledImage._drawer.blendSketch({
|
||||||
opacity: tiledImage.opacity,
|
opacity: tiledImage.opacity,
|
||||||
@ -1587,8 +1675,13 @@ function drawTiles( tiledImage, lastDrawn ) {
|
|||||||
compositeOperation: tiledImage.compositeOperation,
|
compositeOperation: tiledImage.compositeOperation,
|
||||||
bounds: bounds
|
bounds: bounds
|
||||||
});
|
});
|
||||||
if (offsetForRotation) {
|
if (sketchScale) {
|
||||||
tiledImage._drawer._restoreRotationChanges(false);
|
if (tiledImage._degrees !== 0) {
|
||||||
|
tiledImage._drawer._restoreRotationChanges(false);
|
||||||
|
}
|
||||||
|
if (tiledImage.viewport.degrees !== 0) {
|
||||||
|
tiledImage._drawer._restoreRotationChanges(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
drawDebugInfo( tiledImage, lastDrawn );
|
drawDebugInfo( tiledImage, lastDrawn );
|
||||||
@ -1599,7 +1692,8 @@ function drawDebugInfo( tiledImage, lastDrawn ) {
|
|||||||
for ( var i = lastDrawn.length - 1; i >= 0; i-- ) {
|
for ( var i = lastDrawn.length - 1; i >= 0; i-- ) {
|
||||||
var tile = lastDrawn[ i ];
|
var tile = lastDrawn[ i ];
|
||||||
try {
|
try {
|
||||||
tiledImage._drawer.drawDebugInfo( tile, lastDrawn.length, i );
|
tiledImage._drawer.drawDebugInfo(
|
||||||
|
tile, lastDrawn.length, i, tiledImage);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
$.console.error(e);
|
$.console.error(e);
|
||||||
}
|
}
|
||||||
|
@ -344,12 +344,20 @@ $.TileSource.prototype = {
|
|||||||
* @param {Number} level
|
* @param {Number} level
|
||||||
* @param {OpenSeadragon.Point} point
|
* @param {OpenSeadragon.Point} point
|
||||||
*/
|
*/
|
||||||
getTileAtPoint: function( level, point ) {
|
getTileAtPoint: function(level, point) {
|
||||||
var numTiles = this.getNumTiles( level );
|
var widthScaled = this.dimensions.x * this.getLevelScale(level);
|
||||||
return new $.Point(
|
var pixelX = $.positiveModulo(point.x, 1) * widthScaled;
|
||||||
Math.floor( (point.x * numTiles.x) / 1 ),
|
var pixelY = $.positiveModulo(point.y, 1 / this.aspectRatio) * widthScaled;
|
||||||
Math.floor( (point.y * numTiles.y * this.dimensions.x) / this.dimensions.y )
|
|
||||||
);
|
var x = Math.floor(pixelX / this.getTileWidth());
|
||||||
|
var y = Math.floor(pixelY / this.getTileHeight());
|
||||||
|
|
||||||
|
// Fix for wrapping
|
||||||
|
var numTiles = this.getNumTiles(level);
|
||||||
|
x += numTiles.x * Math.floor(point.x);
|
||||||
|
y += numTiles.y * Math.floor(point.y * this.aspectRatio);
|
||||||
|
|
||||||
|
return new $.Point(x, y);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1228,6 +1228,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
* (portions of the image outside of this area will not be visible). Only works on
|
* (portions of the image outside of this area will not be visible). Only works on
|
||||||
* browsers that support the HTML5 canvas.
|
* browsers that support the HTML5 canvas.
|
||||||
* @param {Number} [options.opacity] Opacity the tiled image should be drawn at by default.
|
* @param {Number} [options.opacity] Opacity the tiled image should be drawn at by default.
|
||||||
|
* @param {Number} [options.degrees=0] Initial rotation of the tiled image around
|
||||||
|
* its top left corner in degrees.
|
||||||
* @param {String} [options.compositeOperation] How the image is composited onto other images.
|
* @param {String} [options.compositeOperation] How the image is composited onto other images.
|
||||||
* @param {String} [options.crossOriginPolicy] The crossOriginPolicy for this specific image,
|
* @param {String} [options.crossOriginPolicy] The crossOriginPolicy for this specific image,
|
||||||
* overriding viewer.crossOriginPolicy.
|
* overriding viewer.crossOriginPolicy.
|
||||||
@ -1369,6 +1371,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
clip: queueItem.options.clip,
|
clip: queueItem.options.clip,
|
||||||
placeholderFillStyle: queueItem.options.placeholderFillStyle,
|
placeholderFillStyle: queueItem.options.placeholderFillStyle,
|
||||||
opacity: queueItem.options.opacity,
|
opacity: queueItem.options.opacity,
|
||||||
|
degrees: queueItem.options.degrees,
|
||||||
compositeOperation: queueItem.options.compositeOperation,
|
compositeOperation: queueItem.options.compositeOperation,
|
||||||
springStiffness: _this.springStiffness,
|
springStiffness: _this.springStiffness,
|
||||||
animationTime: _this.animationTime,
|
animationTime: _this.animationTime,
|
||||||
|
@ -852,11 +852,7 @@ $.Viewport.prototype = {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
degrees = degrees % 360;
|
this.degrees = $.positiveModulo(degrees, 360);
|
||||||
if (degrees < 0) {
|
|
||||||
degrees += 360;
|
|
||||||
}
|
|
||||||
this.degrees = degrees;
|
|
||||||
this._setContentBounds(
|
this._setContentBounds(
|
||||||
this.viewer.world.getHomeBounds(),
|
this.viewer.world.getHomeBounds(),
|
||||||
this.viewer.world.getContentFactor());
|
this.viewer.world.getContentFactor());
|
||||||
|
@ -94,6 +94,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W
|
|||||||
this._needsDraw = true;
|
this._needsDraw = true;
|
||||||
|
|
||||||
item.addHandler('bounds-change', this._delegatedFigureSizes);
|
item.addHandler('bounds-change', this._delegatedFigureSizes);
|
||||||
|
item.addHandler('clip-change', this._delegatedFigureSizes);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raised when an item is added to the World.
|
* Raised when an item is added to the World.
|
||||||
@ -194,6 +195,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W
|
|||||||
}
|
}
|
||||||
|
|
||||||
item.removeHandler('bounds-change', this._delegatedFigureSizes);
|
item.removeHandler('bounds-change', this._delegatedFigureSizes);
|
||||||
|
item.removeHandler('clip-change', this._delegatedFigureSizes);
|
||||||
item.destroy();
|
item.destroy();
|
||||||
this._items.splice( index, 1 );
|
this._items.splice( index, 1 );
|
||||||
this._figureSizes();
|
this._figureSizes();
|
||||||
@ -213,6 +215,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W
|
|||||||
for (var i = 0; i < this._items.length; i++) {
|
for (var i = 0; i < this._items.length; i++) {
|
||||||
item = this._items[i];
|
item = this._items[i];
|
||||||
item.removeHandler('bounds-change', this._delegatedFigureSizes);
|
item.removeHandler('bounds-change', this._delegatedFigureSizes);
|
||||||
|
item.removeHandler('clip-change', this._delegatedFigureSizes);
|
||||||
item.destroy();
|
item.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -383,7 +386,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W
|
|||||||
var item = this._items[0];
|
var item = this._items[0];
|
||||||
var bounds = item.getBounds();
|
var bounds = item.getBounds();
|
||||||
this._contentFactor = item.getContentSize().x / bounds.width;
|
this._contentFactor = item.getContentSize().x / bounds.width;
|
||||||
var clippedBounds = item.getClippedBounds();
|
var clippedBounds = item.getClippedBounds().getBoundingBox();
|
||||||
var left = clippedBounds.x;
|
var left = clippedBounds.x;
|
||||||
var top = clippedBounds.y;
|
var top = clippedBounds.y;
|
||||||
var right = clippedBounds.x + clippedBounds.width;
|
var right = clippedBounds.x + clippedBounds.width;
|
||||||
@ -393,7 +396,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W
|
|||||||
bounds = item.getBounds();
|
bounds = item.getBounds();
|
||||||
this._contentFactor = Math.max(this._contentFactor,
|
this._contentFactor = Math.max(this._contentFactor,
|
||||||
item.getContentSize().x / bounds.width);
|
item.getContentSize().x / bounds.width);
|
||||||
clippedBounds = item.getClippedBounds();
|
clippedBounds = item.getClippedBounds().getBoundingBox();
|
||||||
left = Math.min(left, clippedBounds.x);
|
left = Math.min(left, clippedBounds.x);
|
||||||
top = Math.min(top, clippedBounds.y);
|
top = Math.min(top, clippedBounds.y);
|
||||||
right = Math.max(right, clippedBounds.x + clippedBounds.width);
|
right = Math.max(right, clippedBounds.x + clippedBounds.width);
|
||||||
|
@ -222,6 +222,27 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ----------
|
||||||
|
asyncTest('clip-change event', function() {
|
||||||
|
expect(0);
|
||||||
|
var clip = new OpenSeadragon.Rect(100, 100, 800, 800);
|
||||||
|
|
||||||
|
viewer.addHandler('open', function() {
|
||||||
|
var image = viewer.world.getItemAt(0);
|
||||||
|
image.addOnceHandler('clip-change', function() {
|
||||||
|
image.addOnceHandler('clip-change', function() {
|
||||||
|
start();
|
||||||
|
});
|
||||||
|
image.setClip(clip);
|
||||||
|
});
|
||||||
|
image.setClip(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
viewer.open({
|
||||||
|
tileSource: '/test/data/testpattern.dzi'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// ----------
|
// ----------
|
||||||
asyncTest('getClipBounds', function() {
|
asyncTest('getClipBounds', function() {
|
||||||
var clip = new OpenSeadragon.Rect(100, 200, 800, 500);
|
var clip = new OpenSeadragon.Rect(100, 200, 800, 500);
|
||||||
@ -294,6 +315,34 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// ----------
|
// ----------
|
||||||
|
asyncTest('rotation', function() {
|
||||||
|
|
||||||
|
function testDefaultRotation() {
|
||||||
|
var image = viewer.world.getItemAt(0);
|
||||||
|
strictEqual(image.getRotation(), 0, 'image has default rotation');
|
||||||
|
|
||||||
|
image.setRotation(400);
|
||||||
|
strictEqual(image.getRotation(), 40, 'rotation is set correctly');
|
||||||
|
|
||||||
|
viewer.addOnceHandler('open', testTileSourceRotation);
|
||||||
|
viewer.open({
|
||||||
|
tileSource: '/test/data/testpattern.dzi',
|
||||||
|
degrees: -60
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function testTileSourceRotation() {
|
||||||
|
var image = viewer.world.getItemAt(0);
|
||||||
|
strictEqual(image.getRotation(), 300, 'image has correct rotation');
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
viewer.addOnceHandler('open', testDefaultRotation);
|
||||||
|
viewer.open({
|
||||||
|
tileSource: '/test/data/testpattern.dzi',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
asyncTest('fitBounds', function() {
|
asyncTest('fitBounds', function() {
|
||||||
|
|
||||||
function assertRectEquals(actual, expected, message) {
|
function assertRectEquals(actual, expected, message) {
|
||||||
|
@ -105,7 +105,8 @@
|
|||||||
orig = config.getOrig(config.testArray[i], viewport);
|
orig = config.getOrig(config.testArray[i], viewport);
|
||||||
expected = config.getExpected(orig, viewport);
|
expected = config.getExpected(orig, viewport);
|
||||||
actual = viewport[config.method](orig);
|
actual = viewport[config.method](orig);
|
||||||
propEqual(
|
var assert = config.assert || propEqual;
|
||||||
|
assert(
|
||||||
actual,
|
actual,
|
||||||
expected,
|
expected,
|
||||||
"Correctly converted coordinates " + orig
|
"Correctly converted coordinates " + orig
|
||||||
@ -118,6 +119,10 @@
|
|||||||
viewer.open(DZI_PATH);
|
viewer.open(DZI_PATH);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function assertPointsEquals(actual, expected, message) {
|
||||||
|
Util.assertPointsEquals(actual, expected, 1e-15, message);
|
||||||
|
}
|
||||||
|
|
||||||
// Tests start here.
|
// Tests start here.
|
||||||
|
|
||||||
asyncTest('getContainerSize', function() {
|
asyncTest('getContainerSize', function() {
|
||||||
@ -872,7 +877,8 @@
|
|||||||
getExpected: function(orig, viewport) {
|
getExpected: function(orig, viewport) {
|
||||||
return orig.divide(viewer.source.dimensions.x);
|
return orig.divide(viewer.source.dimensions.x);
|
||||||
},
|
},
|
||||||
method: 'imageToViewportCoordinates'
|
method: 'imageToViewportCoordinates',
|
||||||
|
assert: assertPointsEquals
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -885,7 +891,8 @@
|
|||||||
getExpected: function(orig, viewport) {
|
getExpected: function(orig, viewport) {
|
||||||
return orig.divide(ZOOM_FACTOR * viewport.getContainerSize().x);
|
return orig.divide(ZOOM_FACTOR * viewport.getContainerSize().x);
|
||||||
},
|
},
|
||||||
method: 'imageToViewportCoordinates'
|
method: 'imageToViewportCoordinates',
|
||||||
|
assert: assertPointsEquals
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
asyncTest('imageToViewportRectangle', function() {
|
asyncTest('imageToViewportRectangle', function() {
|
||||||
@ -902,7 +909,8 @@
|
|||||||
orig.height / viewer.source.dimensions.x
|
orig.height / viewer.source.dimensions.x
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
method: 'imageToViewportRectangle'
|
method: 'imageToViewportRectangle',
|
||||||
|
assert: assertPointsEquals
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user