First draft of tiled image rotation.

This commit is contained in:
Antoine Vandecreme 2016-08-17 15:43:08 +02:00
parent bbf354b6b6
commit 65b59c08d6
7 changed files with 118 additions and 47 deletions

View File

@ -464,7 +464,7 @@ $.Drawer.prototype = {
},
// private
drawDebugInfo: function( tile, count, i ){
drawDebugInfo: function(tile, count, i, tiledImage) {
if ( !this.useCanvas ) {
return;
}
@ -479,6 +479,12 @@ $.Drawer.prototype = {
if ( this.viewport.degrees !== 0 ) {
this._offsetForRotation(this.viewport.degrees);
}
if (tiledImage.degrees) {
this._offsetForRotation(
tiledImage.degrees,
tiledImage.viewport.pixelFromPointNoRotate(
tiledImage.getBounds(true).getTopLeft(), true));
}
context.strokeRect(
tile.position.x * $.pixelDensityRatio,
@ -541,6 +547,9 @@ $.Drawer.prototype = {
if ( this.viewport.degrees !== 0 ) {
this._restoreRotationChanges();
}
if (tiledImage.degrees) {
this._restoreRotationChanges();
}
context.restore();
},
@ -574,17 +583,19 @@ $.Drawer.prototype = {
return new $.Point(canvas.width, canvas.height);
},
// private
_offsetForRotation: function(degrees, useSketch) {
var cx = this.canvas.width / 2;
var cy = this.canvas.height / 2;
getCanvasCenter: function() {
return new $.Point(this.canvas.width / 2, this.canvas.height / 2);
},
// private
_offsetForRotation: function(degrees, point, useSketch) {
point = point || this.getCanvasCenter();
var context = this._getContext(useSketch);
context.save();
context.translate(cx, cy);
context.translate(point.x, point.y);
context.rotate(Math.PI / 180 * degrees);
context.translate(-cx, -cy);
context.translate(-point.x, -point.y);
},
// private

View File

@ -1375,6 +1375,21 @@ function OpenSeadragon( options ){
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).
* @function

View File

@ -190,10 +190,7 @@ $.Point.prototype = {
var sin;
// Avoid float computations when possible
if (degrees % 90 === 0) {
var d = degrees % 360;
if (d < 0) {
d += 360;
}
var d = $.positiveModulo(degrees, 360);
switch (d) {
case 0:
cos = 1;

View File

@ -81,10 +81,7 @@ $.Rect = function(x, y, width, height, degrees) {
this.degrees = typeof(degrees) === "number" ? degrees : 0;
// Normalizes the rectangle.
this.degrees = this.degrees % 360;
if (this.degrees < 0) {
this.degrees += 360;
}
this.degrees = $.positiveModulo(this.degrees, 360);
var newTopLeft, newWidth;
if (this.degrees >= 270) {
newTopLeft = this.getTopRight();
@ -442,13 +439,10 @@ $.Rect.prototype = {
* @return {OpenSeadragon.Rect}
*/
rotate: function(degrees, pivot) {
degrees = degrees % 360;
degrees = $.positiveModulo(degrees, 360);
if (degrees === 0) {
return this.clone();
}
if (degrees < 0) {
degrees += 360;
}
pivot = pivot || this.getCenter();
var newTopLeft = this.getTopLeft().rotate(degrees, pivot);

View File

@ -159,8 +159,8 @@ $.TiledImage = function( options ) {
crossOriginPolicy: $.DEFAULT_SETTINGS.crossOriginPolicy,
placeholderFillStyle: $.DEFAULT_SETTINGS.placeholderFillStyle,
opacity: $.DEFAULT_SETTINGS.opacity,
compositeOperation: $.DEFAULT_SETTINGS.compositeOperation
compositeOperation: $.DEFAULT_SETTINGS.compositeOperation,
degrees: 0
}, options );
this._xSpring = new $.Spring({
@ -274,13 +274,19 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
* @param {Boolean} [current=false] - Pass true for the current location; false for target location.
*/
getBounds: function(current) {
if (current) {
return new $.Rect( this._xSpring.current.value, this._ySpring.current.value,
this._worldWidthCurrent, this._worldHeightCurrent );
}
return new $.Rect( this._xSpring.target.value, this._ySpring.target.value,
this._worldWidthTarget, this._worldHeightTarget );
return current ?
new $.Rect(
this._xSpring.current.value,
this._ySpring.current.value,
this._worldWidthCurrent,
this._worldHeightCurrent,
this.degrees) :
new $.Rect(
this._xSpring.target.value,
this._ySpring.target.value,
this._worldWidthTarget,
this._worldHeightTarget,
this.degrees);
},
// deprecated
@ -304,7 +310,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
bounds.x + clip.x,
bounds.y + clip.y,
clip.width,
clip.height);
clip.height,
this.degrees);
}
return bounds;
},
@ -660,6 +667,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
$.console.assert(!newClip || newClip instanceof $.Rect,
"[TiledImage.setClip] newClip must be an OpenSeadragon.Rect or null");
//TODO: should this._raiseBoundsChange(); be called?
if (newClip instanceof $.Rect) {
this._clip = newClip.clone();
} else {
@ -684,6 +693,23 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
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} the rotation in degrees.
*/
setRotation: function(degrees) {
this.degrees = $.positiveModulo(degrees, 360);
this._needsDraw = true;
},
/**
* @returns {String} The TiledImage's current compositeOperation.
*/
@ -803,7 +829,8 @@ function updateViewport( tiledImage ) {
}
if (!tiledImage.wrapHorizontal && !tiledImage.wrapVertical) {
var tiledImageBounds = tiledImage.getClippedBounds(true);
var tiledImageBounds = tiledImage.getClippedBounds(true)
.getBoundingBox();
var intersection = viewportBounds.intersection(tiledImageBounds);
if (intersection === null) {
return;
@ -1464,10 +1491,20 @@ function drawTiles( tiledImage, lastDrawn ) {
tiledImage._drawer._clear(true, bounds);
}
// When scaling, we must rotate only when blending the sketch canvas to avoid
// interpolation
if (tiledImage.viewport.degrees !== 0 && !sketchScale) {
tiledImage._drawer._offsetForRotation(tiledImage.viewport.degrees, useSketch);
// When scaling, we must rotate only when blending the sketch canvas to
// avoid interpolation
if (!sketchScale) {
if (tiledImage.viewport.degrees !== 0) {
tiledImage._drawer._offsetForRotation(
tiledImage.viewport.degrees, useSketch);
}
if (tiledImage.degrees !== 0) {
tiledImage._drawer._offsetForRotation(
tiledImage.degrees,
tiledImage.viewport.pixelFromPointNoRotate(
tiledImage.getBounds(true).getTopLeft(), true),
useSketch);
}
}
var usedClip = false;
@ -1535,14 +1572,28 @@ function drawTiles( tiledImage, lastDrawn ) {
tiledImage._drawer.restoreContext( useSketch );
}
if (tiledImage.viewport.degrees !== 0 && !sketchScale) {
tiledImage._drawer._restoreRotationChanges(useSketch);
if (!sketchScale) {
if (tiledImage.degrees !== 0) {
tiledImage._drawer._restoreRotationChanges(useSketch);
}
if (tiledImage.viewport.degrees !== 0) {
tiledImage._drawer._restoreRotationChanges(useSketch);
}
}
if (useSketch) {
var offsetForRotation = tiledImage.viewport.degrees !== 0 && sketchScale;
if (offsetForRotation) {
tiledImage._drawer._offsetForRotation(tiledImage.viewport.degrees, false);
if (sketchScale) {
if (tiledImage.viewport.degrees !== 0) {
tiledImage._drawer._offsetForRotation(
tiledImage.viewport.degrees, false);
}
if (tiledImage.degrees !== 0) {
tiledImage._drawer._offsetForRotation(
tiledImage.degrees,
tiledImage.viewport.pixelFromPointNoRotate(
tiledImage.getBounds(true).getTopLeft(), true),
useSketch);
}
}
tiledImage._drawer.blendSketch({
opacity: tiledImage.opacity,
@ -1551,8 +1602,13 @@ function drawTiles( tiledImage, lastDrawn ) {
compositeOperation: tiledImage.compositeOperation,
bounds: bounds
});
if (offsetForRotation) {
tiledImage._drawer._restoreRotationChanges(false);
if (sketchScale) {
if (tiledImage.degrees !== 0) {
tiledImage._drawer._restoreRotationChanges(false);
}
if (tiledImage.viewport.degrees !== 0) {
tiledImage._drawer._restoreRotationChanges(false);
}
}
}
drawDebugInfo( tiledImage, lastDrawn );
@ -1563,7 +1619,8 @@ function drawDebugInfo( tiledImage, lastDrawn ) {
for ( var i = lastDrawn.length - 1; i >= 0; i-- ) {
var tile = lastDrawn[ i ];
try {
tiledImage._drawer.drawDebugInfo( tile, lastDrawn.length, i );
tiledImage._drawer.drawDebugInfo(
tile, lastDrawn.length, i, tiledImage);
} catch(e) {
$.console.error(e);
}

View File

@ -1369,6 +1369,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
clip: queueItem.options.clip,
placeholderFillStyle: queueItem.options.placeholderFillStyle,
opacity: queueItem.options.opacity,
degrees: queueItem.options.degrees,
compositeOperation: queueItem.options.compositeOperation,
springStiffness: _this.springStiffness,
animationTime: _this.animationTime,

View File

@ -852,11 +852,7 @@ $.Viewport.prototype = {
return this;
}
degrees = degrees % 360;
if (degrees < 0) {
degrees += 360;
}
this.degrees = degrees;
this.degrees = $.positiveModulo(degrees, 360);
this._setContentBounds(
this.viewer.world.getHomeBounds(),
this.viewer.world.getContentFactor());