Merge pull request #185 from robhobbes/master

Adding partial support for rotation.
This commit is contained in:
iangilman 2013-08-20 11:08:58 -07:00
commit aefe7f3610
6 changed files with 228 additions and 12 deletions

View File

@ -407,6 +407,10 @@ $.Drawer.prototype = {
}
return loading;
},
canRotate: function() {
return USE_CANVAS;
}
};
@ -509,6 +513,7 @@ function updateViewport( drawer ) {
Math.log( 2 )
))
),
degrees = drawer.viewport.degrees,
renderPixelRatioC,
renderPixelRatioT,
zeroRatioT,
@ -533,7 +538,14 @@ function updateViewport( drawer ) {
drawer.context.clearRect( 0, 0, viewportSize.x, viewportSize.y );
}
//TODO
//Change bounds for rotation
if (degrees === 90 || degrees === 270) {
var rotatedBounds = viewportBounds.rotate( degrees );
viewportTL = rotatedBounds.getTopLeft();
viewportBR = rotatedBounds.getBottomRight();
}
//Don't draw if completely outside of the viewport
if ( !drawer.wrapHorizontal &&
( viewportBR.x < 0 || viewportTL.x > 1 ) ) {
return;
@ -575,6 +587,7 @@ function updateViewport( drawer ) {
continue;
}
//Perform calculations for draw if we haven't drawn this
renderPixelRatioT = drawer.viewport.deltaPixelsFromPoints(
drawer.source.getPixelRatio( level ),
false
@ -1117,7 +1130,7 @@ function drawOverlay( viewport, overlay, container ){
overlay.bounds.getSize(),
true
);
overlay.drawHTML( container );
overlay.drawHTML( container, viewport );
}
function drawTiles( drawer, lastDrawn ){
@ -1196,7 +1209,15 @@ function drawTiles( drawer, lastDrawn ){
} else {
if ( USE_CANVAS ) {
tile.drawCanvas( drawer.context );
// TODO do this in a more performant way
// specifically, don't save,rotate,restore every time we draw a tile
if( drawer.viewport.degrees !== 0 ) {
offsetForRotation( tile, drawer.canvas, drawer.context, drawer.viewport.degrees );
tile.drawCanvas( drawer.context );
restoreRotationChanges( tile, drawer.canvas, drawer.context );
} else {
tile.drawCanvas( drawer.context );
}
} else {
tile.drawHTML( drawer.canvas );
}
@ -1222,6 +1243,32 @@ function drawTiles( drawer, lastDrawn ){
}
}
function offsetForRotation( tile, canvas, context, degrees ){
var cx = canvas.width / 2,
cy = canvas.height / 2,
px = tile.position.x - cx,
py = tile.position.y - cy;
context.save();
context.translate(cx, cy);
context.rotate( Math.PI / 180 * degrees);
tile.position.x = px;
tile.position.y = py;
}
function restoreRotationChanges( tile, canvas, context ){
var cx = canvas.width / 2,
cy = canvas.height / 2,
px = tile.position.x + cx,
py = tile.position.y + cy;
tile.position.x = px;
tile.position.y = py;
context.restore();
}
function drawDebugInfo( drawer, tile, count, i ){

View File

@ -533,6 +533,9 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
navigatorPosition: null,
navigatorSizeRatio: 0.2,
// INITIAL ROTATION
degrees: 0,
//REFERENCE STRIP SETTINGS
showReferenceStrip: false,
referenceStripScroll: 'horizontal',

View File

@ -174,12 +174,18 @@
* @function
* @param {Element} container
*/
drawHTML: function( container ) {
drawHTML: function( container, viewport ) {
var element = this.element,
style = this.style,
scales = this.scales,
drawerCenter = new $.Point(
viewport.viewer.drawer.canvas.width / 2,
viewport.viewer.drawer.canvas.height / 2
),
degrees = viewport.degrees,
position,
size;
size,
overlayCenter;
if ( element.parentNode != container ) {
//save the source parent for later if we need it
@ -200,6 +206,23 @@
position = position.apply( Math.floor );
size = size.apply( Math.ceil );
// rotate the position of the overlay
// TODO only rotate overlays if in canvas mode
// TODO replace the size rotation with CSS3 transforms
// TODO add an option to overlays to not rotate with the image
// Currently only rotates position and size
if( degrees !== 0 && this.scales ) {
overlayCenter = new $.Point( size.x / 2, size.y / 2 );
position = position.plus( overlayCenter ).rotate(
degrees,
drawerCenter
).minus( overlayCenter );
size = size.rotate( degrees, new $.Point( 0, 0 ) );
size = new $.Point( Math.abs( size.x ), Math.abs( size.y ) );
}
// call the onDraw callback if there is one to allow, this allows someone to overwrite
// the drawing/positioning/sizing of the overlay
if (this.onDraw) {

View File

@ -160,6 +160,21 @@ $.Point.prototype = {
);
},
/**
* Rotates the point around the specified pivot
* From http://stackoverflow.com/questions/4465931/rotate-rectangle-around-a-point
* @function
* @param {Number} degress to rotate around the pivot.
* @param {OpenSeadragon.Point} pivot Point about which to rotate.
* @returns {OpenSeadragon.Point}. A new point representing the point rotated around the specified pivot
*/
rotate: function ( degrees, pivot ) {
var angle = degrees * Math.PI / 180.0,
x = Math.cos( angle ) * ( this.x - pivot.x ) - Math.sin( angle ) * ( this.y - pivot.y ) + pivot.x,
y = Math.sin( angle ) * ( this.x - pivot.x ) + Math.cos( angle ) * ( this.y - pivot.y ) + pivot.y;
return new $.Point( x, y );
},
/**
* Add another Point to this point and return a new Point.
* @function

View File

@ -69,14 +69,17 @@ $.Rect.prototype = {
},
/**
* Provides the coordinates of the upper-left corner of the rectanglea s a
* Provides the coordinates of the upper-left corner of the rectangle as a
* point.
* @function
* @returns {OpenSeadragon.Point} The coordinate of the upper-left corner of
* the rectangle.
*/
getTopLeft: function() {
return new $.Point( this.x, this.y );
return new $.Point(
this.x,
this.y
);
},
/**
@ -93,10 +96,38 @@ $.Rect.prototype = {
);
},
/**
* Provides the coordinates of the top-right corner of the rectangle as a
* point.
* @function
* @returns {OpenSeadragon.Point} The coordinate of the top-right corner of
* the rectangle.
*/
getTopRight: function() {
return new $.Point(
this.x + this.width,
this.y
);
},
/**
* Provides the coordinates of the bottom-left corner of the rectangle as a
* point.
* @function
* @returns {OpenSeadragon.Point} The coordinate of the bottom-left corner of
* the rectangle.
*/
getBottomLeft: function() {
return new $.Point(
this.x,
this.y + this.height
);
},
/**
* Computes the center of the rectangle.
* @function
* @returns {OpenSeadragon.Point} The center of the rectangle as represnted
* @returns {OpenSeadragon.Point} The center of the rectangle as represented
* as represented by a 2-dimensional vector (x,y)
*/
getCenter: function() {
@ -109,7 +140,7 @@ $.Rect.prototype = {
/**
* Returns the width and height component as a vector OpenSeadragon.Point
* @function
* @returns {OpenSeadragon.Point} The 2 dimensional vector represnting the
* @returns {OpenSeadragon.Point} The 2 dimensional vector representing the
* the width and height of the rectangle.
*/
getSize: function() {
@ -117,7 +148,7 @@ $.Rect.prototype = {
},
/**
* Determines if two Rectanlges have equivalent components.
* Determines if two Rectangles have equivalent components.
* @function
* @param {OpenSeadragon.Rect} rectangle The Rectangle to compare to.
* @return {Boolean} 'true' if all components are equal, otherwise 'false'.
@ -131,7 +162,62 @@ $.Rect.prototype = {
},
/**
* Provides a string representation of the retangle which is useful for
* Rotates a rectangle around a point. Currently only 90, 180, and 270
* degrees are supported.
* @function
* @param {Number} degrees The angle in degrees to rotate.
* @param {OpenSeadragon.Point} pivot The point about which to rotate.
* Defaults to the center of the rectangle.
* @return {OpenSeadragon.Rect}
*/
rotate: function( degrees, pivot ) {
// TODO support arbitrary rotation
var width = this.width,
height = this.height,
newTopLeft;
degrees = ( degrees + 360 ) % 360;
if( degrees % 90 !== 0 ) {
throw new Error('Currently only 0, 90, 180, and 270 degrees are supported.');
}
if( degrees === 0 ){
return new $.Rect(
this.x,
this.y,
this.width,
this.height
);
}
pivot = pivot || this.getCenter();
switch ( degrees ) {
case 90:
newTopLeft = this.getBottomLeft();
width = this.height;
height = this.width;
break;
case 180:
newTopLeft = this.getBottomRight();
break;
case 270:
newTopLeft = this.getTopRight();
width = this.height;
height = this.width;
break;
default:
newTopLeft = this.getTopLeft();
break;
}
newTopLeft = newTopLeft.rotate(degrees, pivot);
return new $.Rect(newTopLeft.x, newTopLeft.y, width, height);
},
/**
* Provides a string representation of the rectangle which is useful for
* debugging.
* @function
* @returns {String} A string representation of the rectangle.

View File

@ -79,7 +79,8 @@ $.Viewport = function( options ) {
wrapVertical: $.DEFAULT_SETTINGS.wrapVertical,
defaultZoomLevel: $.DEFAULT_SETTINGS.defaultZoomLevel,
minZoomLevel: $.DEFAULT_SETTINGS.minZoomLevel,
maxZoomLevel: $.DEFAULT_SETTINGS.maxZoomLevel
maxZoomLevel: $.DEFAULT_SETTINGS.maxZoomLevel,
degrees: $.DEFAULT_SETTINGS.degrees
}, options );
@ -500,6 +501,7 @@ $.Viewport.prototype = {
this.centerSpringX.target.value,
this.centerSpringY.target.value
);
delta = delta.rotate( -this.degrees, new $.Point( 0, 0 ) );
return this.panTo( center.plus( delta ), immediately );
},
@ -534,6 +536,12 @@ $.Viewport.prototype = {
* @return {OpenSeadragon.Viewport} Chainable.
*/
zoomBy: function( factor, refPoint, immediately ) {
if( refPoint ) {
refPoint = refPoint.rotate(
-this.degrees,
new $.Point( this.centerSpringX.target.value, this.centerSpringY.target.value )
);
}
return this.zoomTo( this.zoomSpring.target.value * factor, refPoint, immediately );
},
@ -565,6 +573,40 @@ $.Viewport.prototype = {
return this;
},
/**
* Currently only 90 degree rotation is supported and it only works
* with the canvas. Additionally, the navigator does not rotate yet,
* debug mode doesn't rotate yet, and overlay rotation is only
* partially supported.
* @function
* @name OpenSeadragon.Viewport.prototype.setRotation
* @return {OpenSeadragon.Viewport} Chainable.
*/
setRotation: function( degrees ) {
if( !( this.viewer && this.viewer.drawer.canRotate() ) ) {
return this;
}
degrees = ( degrees + 360 ) % 360;
if( degrees % 90 !== 0 ) {
throw new Error('Currently only 0, 90, 180, and 270 degrees are supported.');
}
this.degrees = degrees;
this.viewer.drawer.update();
return this;
},
/**
* Gets the current rotation in degrees.
* @function
* @name OpenSeadragon.Viewport.prototype.setRotation
* @return {Number} The current rotation in degrees.
*/
getRotation: function() {
return this.degrees;
},
/**
* @function
* @return {OpenSeadragon.Viewport} Chainable.