Merge pull request #896 from avandecreme/overlays

Overlays improvements
This commit is contained in:
Ian Gilman 2016-04-08 10:42:05 -07:00
commit eec0d4c9ff
8 changed files with 1289 additions and 413 deletions

View File

@ -5,6 +5,12 @@ OPENSEADRAGON CHANGELOG
* BREAKING CHANGE: Viewport.homeBounds, Viewport.contentSize, Viewport.contentAspectX and * BREAKING CHANGE: Viewport.homeBounds, Viewport.contentSize, Viewport.contentAspectX and
Viewport.contentAspectY have been removed. (#846) Viewport.contentAspectY have been removed. (#846)
* BREAKING CHANGE: The Overlay.getBounds method now takes the viewport as parameter. (#896)
* DEPRECATION: Overlay.scales, Overlay.bounds and Overlay.position have been deprecated. (#896)
* Overlay.width !== null should be used to test whether the overlay scales horizontally
* Overlay.height !== null should be used to test whether the overlay scales vertically
* The Overlay.getBounds method should be used to get the bounds of the overlay in viewport coordinates
* Overlay.location replaces Overlay.position
* DEPRECATION: Viewport.setHomeBounds has been deprecated (#846) * DEPRECATION: Viewport.setHomeBounds has been deprecated (#846)
* DEPRECATION: the Viewport constructor is now ignoring the contentSize option (#846) * DEPRECATION: the Viewport constructor is now ignoring the contentSize option (#846)
* Tile edge smoothing at high zoom (#764) * Tile edge smoothing at high zoom (#764)
@ -30,6 +36,8 @@ OPENSEADRAGON CHANGELOG
* Fixed issue causing HTML pages to jump unwantedly to the reference strip upon loading (#872) * Fixed issue causing HTML pages to jump unwantedly to the reference strip upon loading (#872)
* Added addOnceHandler method to EventSource (#887) * Added addOnceHandler method to EventSource (#887)
* Added TiledImage.fitBounds method (#888) * Added TiledImage.fitBounds method (#888)
* Overlays can now be scaled in a single dimension by providing a point location and either width or height (#896)
* Added full rotation support to overlays (#729, #193)
2.1.0: 2.1.0:

View File

@ -1337,6 +1337,49 @@ if (typeof define === 'function' && define.amd) {
return window.getComputedStyle( element, "" ); return window.getComputedStyle( element, "" );
}, },
/**
* Returns the property with the correct vendor prefix appended.
* @param {String} property the property name
* @returns {String} the property with the correct prefix or null if not
* supported.
*/
getCssPropertyWithVendorPrefix: function(property) {
var memo = {};
$.getCssPropertyWithVendorPrefix = function(property) {
if (memo[property] !== undefined) {
return memo[property];
}
var style = document.createElement('div').style;
var result = null;
if (style[property] !== undefined) {
result = property;
} else {
var prefixes = ['Webkit', 'Moz', 'MS', 'O',
'webkit', 'moz', 'ms', 'o'];
var suffix = $.capitalizeFirstLetter(property);
for (var i = 0; i < prefixes.length; i++) {
var prop = prefixes[i] + suffix;
if (style[prop] !== undefined) {
result = prop;
break;
}
}
}
memo[property] = result;
return result;
};
return $.getCssPropertyWithVendorPrefix(property);
},
/**
* Capitalizes the first letter of a string
* @param {String} string
* @returns {String} The string with the first letter capitalized
*/
capitalizeFirstLetter: function(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
},
/** /**
* 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).

View File

@ -55,6 +55,23 @@
*/ */
$.OverlayPlacement = $.Placement; $.OverlayPlacement = $.Placement;
/**
* An enumeration of possible ways to handle overlays rotation
* @memberOf OpenSeadragon
* @static
* @property {Number} NO_ROTATION The overlay ignore the viewport rotation.
* @property {Number} EXACT The overlay use CSS 3 transforms to rotate with
* the viewport. If the overlay contains text, it will get rotated as well.
* @property {Number} BOUNDING_BOX The overlay adjusts for rotation by
* taking the size of the bounding box of the rotated bounds.
* Only valid for overlays with Rect location and scalable in both directions.
*/
$.OverlayRotationMode = {
NO_ROTATION: 1,
EXACT: 2,
BOUNDING_BOX: 3
};
/** /**
* @class Overlay * @class Overlay
* @classdesc Provides a way to float an HTML element on top of the viewer element. * @classdesc Provides a way to float an HTML element on top of the viewer element.
@ -64,17 +81,25 @@
* @param {Element} options.element * @param {Element} options.element
* @param {OpenSeadragon.Point|OpenSeadragon.Rect} options.location - The * @param {OpenSeadragon.Point|OpenSeadragon.Rect} options.location - The
* location of the overlay on the image. If a {@link OpenSeadragon.Point} * location of the overlay on the image. If a {@link OpenSeadragon.Point}
* is specified, the overlay will keep a constant size independently of the * is specified, the overlay will be located at this location with respect
* zoom. If a {@link OpenSeadragon.Rect} is specified, the overlay size will * to the placement option. If a {@link OpenSeadragon.Rect} is specified,
* be adjusted when the zoom changes. * the overlay will be placed at this location with the corresponding width
* and height and placement TOP_LEFT.
* @param {OpenSeadragon.Placement} [options.placement=OpenSeadragon.Placement.TOP_LEFT] * @param {OpenSeadragon.Placement} [options.placement=OpenSeadragon.Placement.TOP_LEFT]
* Relative position to the viewport. * Defines what part of the overlay should be at the specified options.location
* Only used if location is a {@link OpenSeadragon.Point}.
* @param {OpenSeadragon.Overlay.OnDrawCallback} [options.onDraw] * @param {OpenSeadragon.Overlay.OnDrawCallback} [options.onDraw]
* @param {Boolean} [options.checkResize=true] Set to false to avoid to * @param {Boolean} [options.checkResize=true] Set to false to avoid to
* check the size of the overlay everytime it is drawn when using a * check the size of the overlay everytime it is drawn in the directions
* {@link OpenSeadragon.Point} as options.location. It will improve * which are not scaled. It will improve performances but will cause a
* performances but will cause a misalignment if the overlay size changes. * misalignment if the overlay size changes.
* @param {Number} [options.width] The width of the overlay in viewport
* coordinates. If specified, the width of the overlay will be adjusted when
* the zoom changes.
* @param {Number} [options.height] The height of the overlay in viewport
* coordinates. If specified, the height of the overlay will be adjusted when
* the zoom changes.
* @param {Boolean} [options.rotationMode=OpenSeadragon.OverlayRotationMode.EXACT]
* How to handle the rotation of the viewport.
*/ */
$.Overlay = function(element, location, placement) { $.Overlay = function(element, location, placement) {
@ -100,34 +125,48 @@
} }
this.element = options.element; this.element = options.element;
this.scales = options.location instanceof $.Rect;
this.bounds = new $.Rect(
options.location.x,
options.location.y,
options.location.width,
options.location.height
);
this.position = new $.Point(
options.location.x,
options.location.y
);
this.size = new $.Point(
options.location.width,
options.location.height
);
this.style = options.element.style; this.style = options.element.style;
// rects are always top-left this._init(options);
this.placement = options.location instanceof $.Point ?
options.placement : $.Placement.TOP_LEFT;
this.onDraw = options.onDraw;
this.checkResize = options.checkResize === undefined ?
true : options.checkResize;
}; };
/** @lends OpenSeadragon.Overlay.prototype */ /** @lends OpenSeadragon.Overlay.prototype */
$.Overlay.prototype = { $.Overlay.prototype = {
// private
_init: function(options) {
this.location = options.location;
this.placement = options.placement === undefined ?
$.Placement.TOP_LEFT : options.placement;
this.onDraw = options.onDraw;
this.checkResize = options.checkResize === undefined ?
true : options.checkResize;
// When this.width is not null, the overlay get scaled horizontally
this.width = options.width === undefined ? null : options.width;
// When this.height is not null, the overlay get scaled vertically
this.height = options.height === undefined ? null : options.height;
this.rotationMode = options.rotationMode || $.OverlayRotationMode.EXACT;
// Having a rect as location is a syntactic sugar
if (this.location instanceof $.Rect) {
this.width = this.location.width;
this.height = this.location.height;
this.location = this.location.getTopLeft();
this.placement = $.Placement.TOP_LEFT;
}
// Deprecated properties kept for backward compatibility.
this.scales = this.width !== null && this.height !== null;
this.bounds = new $.Rect(
this.location.x, this.location.y, this.width, this.height);
this.position = this.location;
},
/** /**
* Internal function to adjust the position of an overlay
* depending on it size and placement.
* @function * @function
* @param {OpenSeadragon.Point} position * @param {OpenSeadragon.Point} position
* @param {OpenSeadragon.Point} size * @param {OpenSeadragon.Point} size
@ -153,8 +192,8 @@
* @function * @function
*/ */
destroy: function() { destroy: function() {
var element = this.element, var element = this.element;
style = this.style; var style = this.style;
if (element.parentNode) { if (element.parentNode) {
element.parentNode.removeChild(element); element.parentNode.removeChild(element);
@ -177,10 +216,20 @@
style.left = ""; style.left = "";
style.position = ""; style.position = "";
if ( this.scales ) { if (this.width !== null) {
style.width = ""; style.width = "";
}
if (this.height !== null) {
style.height = ""; style.height = "";
} }
var transformOriginProp = $.getCssPropertyWithVendorPrefix(
'transformOrigin');
var transformProp = $.getCssPropertyWithVendorPrefix(
'transform');
if (transformOriginProp && transformProp) {
style[transformOriginProp] = "";
style[transformProp] = "";
}
}, },
/** /**
@ -188,109 +237,236 @@
* @param {Element} container * @param {Element} container
*/ */
drawHTML: function(container, viewport) { drawHTML: function(container, viewport) {
var element = this.element, var element = this.element;
style = this.style, if (element.parentNode !== container) {
scales = this.scales,
degrees = viewport.degrees,
position = viewport.pixelFromPoint(
this.bounds.getTopLeft(),
true
),
size,
overlayCenter;
if ( element.parentNode != container ) {
//save the source parent for later if we need it //save the source parent for later if we need it
element.prevElementParent = element.parentNode; element.prevElementParent = element.parentNode;
element.prevNextSibling = element.nextSibling; element.prevNextSibling = element.nextSibling;
container.appendChild(element); container.appendChild(element);
// this.size is used by overlays which don't get scaled in at
// least one direction when this.checkResize is set to false.
this.size = $.getElementSize(element); this.size = $.getElementSize(element);
} }
if ( scales ) { var positionAndSize = this._getOverlayPositionAndSize(viewport);
size = viewport.deltaPixelsFromPoints(
this.bounds.getSize(),
true
);
} else if ( this.checkResize ) {
size = $.getElementSize( element );
} else {
size = this.size;
}
this.position = position; var position = positionAndSize.position;
this.size = size; var size = this.size = positionAndSize.size;
var rotate = positionAndSize.rotate;
this.adjust( position, size );
position = position.apply( Math.round );
size = size.apply( Math.round );
// 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 );
var drawerCenter = new $.Point(
viewport.viewer.drawer.canvas.width / 2,
viewport.viewer.drawer.canvas.height / 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 it exists to allow one to overwrite // call the onDraw callback if it exists to allow one to overwrite
// the drawing/positioning/sizing of the overlay // the drawing/positioning/sizing of the overlay
if (this.onDraw) { if (this.onDraw) {
this.onDraw( position, size, element ); this.onDraw(position, size, this.element);
} else { } else {
var style = this.style;
style.left = position.x + "px"; style.left = position.x + "px";
style.top = position.y + "px"; style.top = position.y + "px";
if (this.width !== null) {
style.width = size.x + "px";
}
if (this.height !== null) {
style.height = size.y + "px";
}
var transformOriginProp = $.getCssPropertyWithVendorPrefix(
'transformOrigin');
var transformProp = $.getCssPropertyWithVendorPrefix(
'transform');
if (transformOriginProp && transformProp) {
if (rotate) {
style[transformOriginProp] = this._getTransformOrigin();
style[transformProp] = "rotate(" + rotate + "deg)";
} else {
style[transformOriginProp] = "";
style[transformProp] = "";
}
}
style.position = "absolute"; style.position = "absolute";
if (style.display != 'none') { if (style.display !== 'none') {
style.display = 'block'; style.display = 'block';
} }
if ( scales ) {
style.width = size.x + "px";
style.height = size.y + "px";
}
} }
}, },
// private
_getOverlayPositionAndSize: function(viewport) {
var position = viewport.pixelFromPoint(this.location, true);
var size = this._getSizeInPixels(viewport);
this.adjust(position, size);
var rotate = 0;
if (viewport.degrees &&
this.rotationMode !== $.OverlayRotationMode.NO_ROTATION) {
// BOUNDING_BOX is only valid if both directions get scaled.
// Get replaced by EXACT otherwise.
if (this.rotationMode === $.OverlayRotationMode.BOUNDING_BOX &&
this.width !== null && this.height !== null) {
var rect = new $.Rect(position.x, position.y, size.x, size.y);
var boundingBox = this._getBoundingBox(rect, viewport.degrees);
position = boundingBox.getTopLeft();
size = boundingBox.getSize();
} else {
rotate = viewport.degrees;
}
}
return {
position: position,
size: size,
rotate: rotate
};
},
// private
_getSizeInPixels: function(viewport) {
var width = this.size.x;
var height = this.size.y;
if (this.width !== null || this.height !== null) {
var scaledSize = viewport.deltaPixelsFromPointsNoRotate(
new $.Point(this.width || 0, this.height || 0), true);
if (this.width !== null) {
width = scaledSize.x;
}
if (this.height !== null) {
height = scaledSize.y;
}
}
if (this.checkResize &&
(this.width === null || this.height === null)) {
var eltSize = this.size = $.getElementSize(this.element);
if (this.width === null) {
width = eltSize.x;
}
if (this.height === null) {
height = eltSize.y;
}
}
return new $.Point(width, height);
},
// private
_getBoundingBox: function(rect, degrees) {
var refPoint = this._getPlacementPoint(rect);
return rect.rotate(degrees, refPoint).getBoundingBox();
},
// private
_getPlacementPoint: function(rect) {
var result = new $.Point(rect.x, rect.y);
var properties = $.Placement.properties[this.placement];
if (properties) {
if (properties.isHorizontallyCentered) {
result.x += rect.width / 2;
} else if (properties.isRight) {
result.x += rect.width;
}
if (properties.isVerticallyCentered) {
result.y += rect.height / 2;
} else if (properties.isBottom) {
result.y += rect.height;
}
}
return result;
},
// private
_getTransformOrigin: function() {
var result = "";
var properties = $.Placement.properties[this.placement];
if (!properties) {
return result;
}
if (properties.isLeft) {
result = "left";
} else if (properties.isRight) {
result = "right";
}
if (properties.isTop) {
result += " top";
} else if (properties.isBottom) {
result += " bottom";
}
return result;
},
/** /**
* Changes the overlay settings.
* @function * @function
* @param {OpenSeadragon.Point|OpenSeadragon.Rect} location * @param {OpenSeadragon.Point|OpenSeadragon.Rect|Object} location
* If an object is specified, the options are the same than the constructor
* except for the element which can not be changed.
* @param {OpenSeadragon.Placement} position * @param {OpenSeadragon.Placement} position
*/ */
update: function(location, placement) { update: function(location, placement) {
this.scales = location instanceof $.Rect; var options = $.isPlainObject(location) ? location : {
this.bounds = new $.Rect( location: location,
location.x, placement: placement
location.y, };
location.width, this._init({
location.height location: options.location || this.location,
); placement: options.placement !== undefined ?
// rects are always top-left options.placement : this.placement,
this.placement = location instanceof $.Point ? onDraw: options.onDraw || this.onDraw,
placement : $.Placement.TOP_LEFT; checkResize: options.checkResize || this.checkResize,
width: options.width !== undefined ? options.width : this.width,
height: options.height !== undefined ? options.height : this.height,
rotationMode: options.rotationMode || this.rotationMode
});
}, },
/** /**
* Returns the current bounds of the overlay in viewport coordinates
* @function * @function
* @param {OpenSeadragon.Viewport} viewport the viewport
* @returns {OpenSeadragon.Rect} overlay bounds * @returns {OpenSeadragon.Rect} overlay bounds
*/ */
getBounds: function() { getBounds: function(viewport) {
return this.bounds.clone(); $.console.assert(viewport,
'A viewport must now be passed to Overlay.getBounds.');
var width = this.width;
var height = this.height;
if (width === null || height === null) {
var size = viewport.deltaPointsFromPixelsNoRotate(this.size, true);
if (width === null) {
width = size.x;
}
if (height === null) {
height = size.y;
}
}
var location = this.location.clone();
this.adjust(location, new $.Point(width, height));
return this._adjustBoundsForRotation(
viewport, new $.Rect(location.x, location.y, width, height));
},
// private
_adjustBoundsForRotation: function(viewport, bounds) {
if (!viewport ||
viewport.degrees === 0 ||
this.rotationMode === $.OverlayRotationMode.EXACT) {
return bounds;
}
if (this.rotationMode === $.OverlayRotationMode.BOUNDING_BOX) {
// If overlay not fully scalable, BOUNDING_BOX falls back to EXACT
if (this.width === null || this.height === null) {
return bounds;
}
// It is easier to just compute the position and size and
// convert to viewport coordinates.
var positionAndSize = this._getOverlayPositionAndSize(viewport);
return viewport.viewerElementToViewportRectangle(new $.Rect(
positionAndSize.position.x,
positionAndSize.position.y,
positionAndSize.size.x,
positionAndSize.size.y));
}
// NO_ROTATION case
return bounds.rotate(-viewport.degrees,
this._getPlacementPoint(bounds));
} }
}; };

View File

@ -110,6 +110,33 @@ $.Rect = function(x, y, width, height, degrees) {
} }
}; };
/**
* Builds a rectangle having the 3 specified points as summits.
* @static
* @memberof OpenSeadragon.Rect
* @param {OpenSeadragon.Point} topLeft
* @param {OpenSeadragon.Point} topRight
* @param {OpenSeadragon.Point} bottomLeft
* @returns {OpenSeadragon.Rect}
*/
$.Rect.fromSummits = function(topLeft, topRight, bottomLeft) {
var width = topLeft.distanceTo(topRight);
var height = topLeft.distanceTo(bottomLeft);
var diff = topRight.minus(topLeft);
var radians = Math.atan(diff.y / diff.x);
if (diff.x < 0) {
radians += Math.PI;
} else if (diff.y < 0) {
radians += 2 * Math.PI;
}
return new $.Rect(
topLeft.x,
topLeft.y,
width,
height,
radians / Math.PI * 180);
};
/** @lends OpenSeadragon.Rect.prototype */ /** @lends OpenSeadragon.Rect.prototype */
$.Rect.prototype = { $.Rect.prototype = {
/** /**
@ -284,7 +311,7 @@ $.Rect.prototype = {
* Rotates a rectangle around a point. * Rotates a rectangle around a point.
* @function * @function
* @param {Number} degrees The angle in degrees to rotate. * @param {Number} degrees The angle in degrees to rotate.
* @param {OpenSeadragon.Point} pivot The point about which to rotate. * @param {OpenSeadragon.Point} [pivot] The point about which to rotate.
* Defaults to the center of the rectangle. * Defaults to the center of the rectangle.
* @return {OpenSeadragon.Rect} * @return {OpenSeadragon.Rect}
*/ */

View File

@ -1788,7 +1788,9 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
* is closed which include when changing page. * is closed which include when changing page.
* @method * @method
* @param {Element|String|Object} element - A reference to an element or an id for * @param {Element|String|Object} element - A reference to an element or an id for
* the element which will be overlayed. Or an Object specifying the configuration for the overlay * the element which will be overlayed. Or an Object specifying the configuration for the overlay.
* If using an object, see {@link OpenSeadragon.Overlay} for a list of
* all available options.
* @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - The point or * @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - The point or
* rectangle which will be overlayed. This is a viewport relative location. * rectangle which will be overlayed. This is a viewport relative location.
* @param {OpenSeadragon.Placement} placement - The position of the * @param {OpenSeadragon.Placement} placement - The position of the
@ -2199,32 +2201,23 @@ function getOverlayObject( viewer, overlay ) {
} }
var location = overlay.location; var location = overlay.location;
var width = overlay.width;
var height = overlay.height;
if (!location) { if (!location) {
if ( overlay.width && overlay.height ) { var x = overlay.x;
location = overlay.px !== undefined ? var y = overlay.y;
viewer.viewport.imageToViewportRectangle( new $.Rect( if (overlay.px !== undefined) {
var rect = viewer.viewport.imageToViewportRectangle(new $.Rect(
overlay.px, overlay.px,
overlay.py, overlay.py,
overlay.width, width || 0,
overlay.height height || 0));
) ) : x = rect.x;
new $.Rect( y = rect.y;
overlay.x, width = width !== undefined ? rect.width : undefined;
overlay.y, height = height !== undefined ? rect.height : undefined;
overlay.width,
overlay.height
);
} else {
location = overlay.px !== undefined ?
viewer.viewport.imageToViewportCoordinates( new $.Point(
overlay.px,
overlay.py
) ) :
new $.Point(
overlay.x,
overlay.y
);
} }
location = new $.Point(x, y);
} }
var placement = overlay.placement; var placement = overlay.placement;
@ -2237,7 +2230,10 @@ function getOverlayObject( viewer, overlay ) {
location: location, location: location,
placement: placement, placement: placement,
onDraw: overlay.onDraw, onDraw: overlay.onDraw,
checkResize: overlay.checkResize checkResize: overlay.checkResize,
width: width,
height: height,
rotationMode: overlay.rotationMode
}); });
} }

View File

@ -1262,6 +1262,34 @@ $.Viewport.prototype = {
return this.pixelFromPoint( point, true ); return this.pixelFromPoint( point, true );
}, },
/**
* Convert a rectangle in pixel coordinates relative to the viewer element
* to viewport coordinates.
* @param {OpenSeadragon.Rect} rectangle the rectangle to convert
* @returns {OpenSeadragon.Rect} the converted rectangle
*/
viewerElementToViewportRectangle: function(rectangle) {
return $.Rect.fromSummits(
this.pointFromPixel(rectangle.getTopLeft(), true),
this.pointFromPixel(rectangle.getTopRight(), true),
this.pointFromPixel(rectangle.getBottomLeft(), true)
);
},
/**
* Convert a rectangle in viewport coordinates to pixel coordinates relative
* to the viewer element.
* @param {OpenSeadragon.Rect} rectangle the rectangle to convert
* @returns {OpenSeadragon.Rect} the converted rectangle
*/
viewportToViewerElementRectangle: function(rectangle) {
return $.Rect.fromSummits(
this.pixelFromPoint(rectangle.getTopLeft(), true),
this.pixelFromPoint(rectangle.getTopRight(), true),
this.pixelFromPoint(rectangle.getBottomLeft(), true)
);
},
/** /**
* Convert pixel coordinates relative to the window to viewport coordinates. * Convert pixel coordinates relative to the window to viewport coordinates.
* @param {OpenSeadragon.Point} pixel * @param {OpenSeadragon.Point} pixel

View File

@ -14,7 +14,9 @@
<body> <body>
<div id="contentDiv" class="openseadragon1"></div> <div id="contentDiv" class="openseadragon1"></div>
<div id="annotation-div"> <div id="annotation-div">
<input type="button" value="Hide Overlay" id="hideOverlay"> <input type="button" value="Hide Overlays" id="hideOverlays">
<input type="button" value="Rotate" id="rotate">
<span id="degrees">0deg</span>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
@ -22,19 +24,74 @@
var viewer = OpenSeadragon({ var viewer = OpenSeadragon({
id: "contentDiv", id: "contentDiv",
prefixUrl: "../../build/openseadragon/images/", prefixUrl: "../../build/openseadragon/images/",
tileSources: "../data/testpattern.dzi" tileSources: "../data/testpattern.dzi",
minZoomImageRatio: 0,
maxZoomPixelRatio: 10
}); });
viewer.addHandler("open", function(event) { viewer.addHandler("open", function(event) {
var elt = document.createElement("div"); var elt = document.createElement("div");
elt.className = "runtime-overlay";
elt.style.background = "green"; elt.style.background = "green";
elt.id = "runtime-overlay"; elt.style.outline = "3px solid red";
elt.style.border = "1px solid red"; elt.style.opacity = "0.7";
viewer.addOverlay( elt, new OpenSeadragon.Rect(0.2, 0.2, 0.75, 0.75) ); elt.textContent = "Scaled overlay";
viewer.addOverlay({
element: elt,
location: new OpenSeadragon.Rect(0.21, 0.21, 0.099, 0.099),
rotationMode: OpenSeadragon.OverlayRotationMode.BOUNDING_BOX
}); });
$("#hideOverlay").click(function(){ elt = document.createElement("div");
$("#runtime-overlay").toggle(); elt.className = "runtime-overlay";
elt.style.background = "white";
elt.style.outline = "3px solid red";
elt.style.width = "100px";
elt.textContent = "Scaled vertically";
viewer.addOverlay({
element: elt,
location: new OpenSeadragon.Point(0.6, 0.6),
height: 0.1,
placement: OpenSeadragon.Placement.TOP_LEFT,
rotationMode: OpenSeadragon.OverlayRotationMode.NO_ROTATION
});
elt = document.createElement("div");
elt.className = "runtime-overlay";
elt.style.background = "white";
elt.style.opacity = "0.5";
elt.style.outline = "1px solid blue";
elt.style.height = "100px";
elt.textContent = "Scaled horizontally";
viewer.addOverlay({
element: elt,
location: new OpenSeadragon.Point(0.1, 0.5),
width: 0.1
});
elt = document.createElement("div");
elt.className = "runtime-overlay";
elt.style.background = "white";
elt.style.opacity = "0.5";
elt.style.outline = "5px solid pink";
elt.style.width = "100px";
elt.style.height = "100px";
elt.textContent = "Not scaled, centered in the middle";
viewer.addOverlay({
element: elt,
location: new OpenSeadragon.Point(0.5, 0.5),
placement: OpenSeadragon.Placement.CENTER,
checkResize: false,
rotationMode: OpenSeadragon.OverlayRotationMode.EXACT
});
});
$("#hideOverlays").click(function(){
$(".runtime-overlay").toggle();
});
$("#rotate").click(function() {
viewer.viewport.setRotation(viewer.viewport.getRotation() + 22.5);
$("#degrees").text(viewer.viewport.getRotation() + "deg");
}); });
</script> </script>

View File

@ -2,6 +2,8 @@
(function() { (function() {
var viewer; var viewer;
// jQuery.position can give results quite different than what set in style.left
var epsilon = 1;
module("Overlays", { module("Overlays", {
setup: function() { setup: function() {
@ -241,26 +243,34 @@
var viewport = viewer.viewport; var viewport = viewer.viewport;
var expPosition = viewport.imageToViewerElementCoordinates( var expPosition = viewport.imageToViewerElementCoordinates(
new OpenSeadragon.Point( 13, 120 ) ).apply( Math.round ); new OpenSeadragon.Point(13, 120));
var actPosition = $("#overlay").position(); var actPosition = $("#overlay").position();
equal( actPosition.left, expPosition.x, "X position mismatch " + contextMessage ); Util.assessNumericValue(actPosition.left, expPosition.x, epsilon,
equal( actPosition.top, expPosition.y, "Y position mismatch " + contextMessage ); "X position mismatch " + contextMessage);
Util.assessNumericValue(actPosition.top, expPosition.y, epsilon,
"Y position mismatch " + contextMessage);
var zoom = viewport.viewportToImageZoom(viewport.getZoom(true)); var zoom = viewport.viewportToImageZoom(viewport.getZoom(true));
var expectedWidth = Math.round( 124 * zoom ); var expectedWidth = 124 * zoom;
var expectedHeight = Math.round( 132 * zoom ); var expectedHeight = 132 * zoom;
equal( $( "#overlay" ).width(), expectedWidth, "Width mismatch " + contextMessage ); Util.assessNumericValue($("#overlay").width(), expectedWidth, epsilon,
equal( $( "#overlay" ).height( ), expectedHeight, "Height mismatch " + contextMessage ); "Width mismatch " + contextMessage);
Util.assessNumericValue($("#overlay").height(), expectedHeight, epsilon,
"Height mismatch " + contextMessage);
expPosition = viewport.imageToViewerElementCoordinates( expPosition = viewport.imageToViewerElementCoordinates(
new OpenSeadragon.Point( 400, 500 ) ).apply( Math.round ); new OpenSeadragon.Point(400, 500));
actPosition = $("#fixed-overlay").position(); actPosition = $("#fixed-overlay").position();
equal( actPosition.left, expPosition.x, "Fixed overlay X position mismatch " + contextMessage ); Util.assessNumericValue(actPosition.left, expPosition.x, epsilon,
equal( actPosition.top, expPosition.y, "Fixed overlay Y position mismatch " + contextMessage ); "Fixed overlay X position mismatch " + contextMessage);
Util.assessNumericValue(actPosition.top, expPosition.y, epsilon,
"Fixed overlay Y position mismatch " + contextMessage);
equal( $( "#fixed-overlay" ).width(), 70, "Fixed overlay width mismatch " + contextMessage ); Util.assessNumericValue($("#fixed-overlay").width(), 70, epsilon,
equal( $( "#fixed-overlay" ).height( ), 60, "Fixed overlay height mismatch " + contextMessage ); "Fixed overlay width mismatch " + contextMessage);
Util.assessNumericValue($("#fixed-overlay").height(), 60, epsilon,
"Fixed overlay height mismatch " + contextMessage);
} }
waitForViewer(function() { waitForViewer(function() {
@ -305,25 +315,33 @@
var viewport = viewer.viewport; var viewport = viewer.viewport;
var expPosition = viewport.viewportToViewerElementCoordinates( var expPosition = viewport.viewportToViewerElementCoordinates(
new OpenSeadragon.Point( 0.2, 0.1 ) ).apply( Math.round ); new OpenSeadragon.Point(0.2, 0.1));
var actPosition = $("#overlay").position(); var actPosition = $("#overlay").position();
equal( actPosition.left, expPosition.x, "X position mismatch " + contextMessage ); Util.assessNumericValue(actPosition.left, expPosition.x, epsilon,
equal( actPosition.top, expPosition.y, "Y position mismatch " + contextMessage ); "X position mismatch " + contextMessage);
Util.assessNumericValue(actPosition.top, expPosition.y, epsilon,
"Y position mismatch " + contextMessage);
var expectedSize = viewport.deltaPixelsFromPoints( var expectedSize = viewport.deltaPixelsFromPoints(
new OpenSeadragon.Point(0.5, 0.1)); new OpenSeadragon.Point(0.5, 0.1));
equal( $( "#overlay" ).width(), expectedSize.x, "Width mismatch " + contextMessage ); Util.assessNumericValue($("#overlay").width(), expectedSize.x, epsilon,
equal( $( "#overlay" ).height(), expectedSize.y, "Height mismatch " + contextMessage ); "Width mismatch " + contextMessage);
Util.assessNumericValue($("#overlay").height(), expectedSize.y, epsilon,
"Height mismatch " + contextMessage);
expPosition = viewport.viewportToViewerElementCoordinates( expPosition = viewport.viewportToViewerElementCoordinates(
new OpenSeadragon.Point( 0.5, 0.6 ) ).apply( Math.round ); new OpenSeadragon.Point(0.5, 0.6));
actPosition = $("#fixed-overlay").position(); actPosition = $("#fixed-overlay").position();
equal( actPosition.left, expPosition.x, "Fixed overlay X position mismatch " + contextMessage ); Util.assessNumericValue(actPosition.left, expPosition.x, epsilon,
equal( actPosition.top, expPosition.y, "Fixed overlay Y position mismatch " + contextMessage ); "Fixed overlay X position mismatch " + contextMessage);
Util.assessNumericValue(actPosition.top, expPosition.y, epsilon,
"Fixed overlay Y position mismatch " + contextMessage);
equal( $( "#fixed-overlay" ).width(), 70, "Fixed overlay width mismatch " + contextMessage ); Util.assessNumericValue($("#fixed-overlay").width(), 70, epsilon,
equal( $( "#fixed-overlay" ).height( ), 60, "Fixed overlay height mismatch " + contextMessage ); "Fixed overlay width mismatch " + contextMessage);
Util.assessNumericValue($("#fixed-overlay").height(), 60, epsilon,
"Fixed overlay height mismatch " + contextMessage);
} }
waitForViewer(function() { waitForViewer(function() {
@ -373,10 +391,12 @@
var viewport = viewer.viewport; var viewport = viewer.viewport;
var expPosition = viewport.viewportToViewerElementCoordinates( var expPosition = viewport.viewportToViewerElementCoordinates(
new OpenSeadragon.Point( 0.2, 0.1 ) ).apply( Math.round ); new OpenSeadragon.Point(0.2, 0.1));
var actPosition = $("#overlay").position(); var actPosition = $("#overlay").position();
equal( actPosition.left, expPosition.x, "X position mismatch " + contextMessage ); Util.assessNumericValue(actPosition.left, expPosition.x, epsilon,
equal( actPosition.top, expPosition.y, "Y position mismatch " + contextMessage ); "X position mismatch " + contextMessage);
Util.assessNumericValue(actPosition.top, expPosition.y, epsilon,
"Y position mismatch " + contextMessage);
} }
function checkFixedOverlayPosition(expectedOffset, contextMessage) { function checkFixedOverlayPosition(expectedOffset, contextMessage) {
@ -384,11 +404,12 @@
var expPosition = viewport.viewportToViewerElementCoordinates( var expPosition = viewport.viewportToViewerElementCoordinates(
new OpenSeadragon.Point(0.5, 0.6)) new OpenSeadragon.Point(0.5, 0.6))
.apply( Math.round )
.plus(expectedOffset); .plus(expectedOffset);
var actPosition = $("#fixed-overlay").position(); var actPosition = $("#fixed-overlay").position();
equal( actPosition.left, expPosition.x, "Fixed overlay X position mismatch " + contextMessage ); Util.assessNumericValue(actPosition.left, expPosition.x, epsilon,
equal( actPosition.top, expPosition.y, "Fixed overlay Y position mismatch " + contextMessage ); "Fixed overlay X position mismatch " + contextMessage);
Util.assessNumericValue(actPosition.top, expPosition.y, epsilon,
"Fixed overlay Y position mismatch " + contextMessage);
} }
waitForViewer(function() { waitForViewer(function() {
@ -449,11 +470,12 @@
var expPosition = viewport.viewportToViewerElementCoordinates( var expPosition = viewport.viewportToViewerElementCoordinates(
new OpenSeadragon.Point(0.5, 0.6)) new OpenSeadragon.Point(0.5, 0.6))
.apply( Math.round )
.plus(expectedOffset); .plus(expectedOffset);
var actPosition = $("#fixed-overlay").position(); var actPosition = $("#fixed-overlay").position();
equal( actPosition.left, expPosition.x, "Fixed overlay X position mismatch " + contextMessage ); Util.assessNumericValue(actPosition.left, expPosition.x, epsilon,
equal( actPosition.top, expPosition.y, "Fixed overlay Y position mismatch " + contextMessage ); "Fixed overlay X position mismatch " + contextMessage);
Util.assessNumericValue(actPosition.top, expPosition.y, epsilon,
"Fixed overlay Y position mismatch " + contextMessage);
} }
waitForViewer(function() { waitForViewer(function() {
@ -503,11 +525,12 @@
var expPosition = viewport.viewportToViewerElementCoordinates( var expPosition = viewport.viewportToViewerElementCoordinates(
new OpenSeadragon.Point(0.5, 0.6)) new OpenSeadragon.Point(0.5, 0.6))
.apply( Math.round )
.plus(expectedOffset); .plus(expectedOffset);
var actPosition = $("#fixed-overlay").position(); var actPosition = $("#fixed-overlay").position();
equal( actPosition.left, expPosition.x, "Fixed overlay X position mismatch " + contextMessage ); Util.assessNumericValue(actPosition.left, expPosition.x, epsilon,
equal( actPosition.top, expPosition.y, "Fixed overlay Y position mismatch " + contextMessage ); "Fixed overlay X position mismatch " + contextMessage);
Util.assessNumericValue(actPosition.top, expPosition.y, epsilon,
"Fixed overlay Y position mismatch " + contextMessage);
} }
waitForViewer(function() { waitForViewer(function() {
@ -565,4 +588,522 @@
}); });
}); });
// ----------
asyncTest('Overlay scaled horizontally only', function() {
viewer = OpenSeadragon({
id: 'example-overlays',
prefixUrl: '/build/openseadragon/images/',
tileSources: '/test/data/testpattern.dzi',
springStiffness: 100 // Faster animation = faster tests
});
viewer.addHandler('open', function() {
viewer.addOverlay({
id: "horizontally-scaled-overlay",
x: 0,
y: 0,
width: 1
});
var width = $("#horizontally-scaled-overlay").width();
var height = 100;
var zoom = 1.1;
$("#horizontally-scaled-overlay").get(0).style.height = height + "px";
viewer.viewport.zoomBy(zoom);
waitForViewer(function() {
var newWidth = $("#horizontally-scaled-overlay").width();
var newHeight = $("#horizontally-scaled-overlay").height();
equal(newWidth, width * zoom, "Width should be scaled.");
equal(newHeight, height, "Height should not be scaled.");
start();
});
});
});
// ----------
asyncTest('Overlay scaled vertically only', function() {
viewer = OpenSeadragon({
id: 'example-overlays',
prefixUrl: '/build/openseadragon/images/',
tileSources: '/test/data/testpattern.dzi',
springStiffness: 100 // Faster animation = faster tests
});
viewer.addHandler('open', function() {
viewer.addOverlay({
id: "vertically-scaled-overlay",
x: 0,
y: 0,
height: 1
});
var width = 100;
var height = $("#vertically-scaled-overlay").height();
var zoom = 1.1;
$("#vertically-scaled-overlay").get(0).style.width = width + "px";
viewer.viewport.zoomBy(zoom);
waitForViewer(function() {
var newWidth = $("#vertically-scaled-overlay").width();
var newHeight = $("#vertically-scaled-overlay").height();
equal(newWidth, width, "Width should not be scaled.");
equal(newHeight, height * zoom, "Height should be scaled.");
start();
});
});
});
// ----------
asyncTest('Overlay.getBounds', function() {
viewer = OpenSeadragon({
id: 'example-overlays',
prefixUrl: '/build/openseadragon/images/',
tileSources: '/test/data/testpattern.dzi',
springStiffness: 100 // Faster animation = faster tests
});
viewer.addHandler('open', function() {
viewer.addOverlay({
id: "fully-scaled-overlay",
x: 1,
y: 1,
width: 1,
height: 1,
placement: OpenSeadragon.Placement.BOTTOM_RIGHT
});
viewer.addOverlay({
id: "horizontally-scaled-overlay",
x: 0.5,
y: 0.5,
width: 1,
placement: OpenSeadragon.Placement.CENTER
});
viewer.addOverlay({
id: "vertically-scaled-overlay",
x: 0,
y: 0.5,
height: 1,
placement: OpenSeadragon.Placement.LEFT
});
viewer.addOverlay({
id: "not-scaled-overlay",
x: 1,
y: 0,
placement: OpenSeadragon.Placement.TOP_RIGHT
});
var notScaledWidth = 100;
var notScaledHeight = 100;
$("#horizontally-scaled-overlay").get(0).style.height = notScaledHeight + "px";
$("#vertically-scaled-overlay").get(0).style.width = notScaledWidth + "px";
$("#not-scaled-overlay").get(0).style.width = notScaledWidth + "px";
$("#not-scaled-overlay").get(0).style.height = notScaledHeight + "px";
var notScaledSize = viewer.viewport.deltaPointsFromPixelsNoRotate(
new OpenSeadragon.Point(notScaledWidth, notScaledHeight));
// Force refresh to takes new dimensions into account.
viewer._drawOverlays();
var actualBounds = viewer.getOverlayById("fully-scaled-overlay")
.getBounds(viewer.viewport);
var expectedBounds = new OpenSeadragon.Rect(0, 0, 1, 1);
ok(expectedBounds.equals(actualBounds),
"The fully scaled overlay should have bounds " +
expectedBounds + " but found " + actualBounds);
actualBounds = viewer.getOverlayById("horizontally-scaled-overlay")
.getBounds(viewer.viewport);
expectedBounds = new OpenSeadragon.Rect(
0, 0.5 - notScaledSize.y / 2, 1, notScaledSize.y);
ok(expectedBounds.equals(actualBounds),
"The horizontally scaled overlay should have bounds " +
expectedBounds + " but found " + actualBounds);
actualBounds = viewer.getOverlayById("vertically-scaled-overlay")
.getBounds(viewer.viewport);
expectedBounds = new OpenSeadragon.Rect(
0, 0, notScaledSize.x, 1);
ok(expectedBounds.equals(actualBounds),
"The vertically scaled overlay should have bounds " +
expectedBounds + " but found " + actualBounds);
actualBounds = viewer.getOverlayById("not-scaled-overlay")
.getBounds(viewer.viewport);
expectedBounds = new OpenSeadragon.Rect(
1 - notScaledSize.x, 0, notScaledSize.x, notScaledSize.y);
ok(expectedBounds.equals(actualBounds),
"The not scaled overlay should have bounds " +
expectedBounds + " but found " + actualBounds);
start();
});
});
// ----------
asyncTest('Fully scaled overlay rotation mode NO_ROTATION', function() {
viewer = OpenSeadragon({
id: 'example-overlays',
prefixUrl: '/build/openseadragon/images/',
tileSources: '/test/data/testpattern.dzi',
springStiffness: 100, // Faster animation = faster tests
degrees: 45,
overlays: [{
id: "fully-scaled-overlay",
x: 1,
y: 1,
width: 1,
height: 1,
placement: OpenSeadragon.Placement.BOTTOM_RIGHT,
rotationMode: OpenSeadragon.OverlayRotationMode.NO_ROTATION
}]
});
viewer.addOnceHandler('open', function() {
var viewport = viewer.viewport;
var $overlay = $("#fully-scaled-overlay");
var expectedSize = viewport.deltaPixelsFromPointsNoRotate(
new OpenSeadragon.Point(1, 1));
var expectedPosition = viewport.viewportToViewerElementCoordinates(
new OpenSeadragon.Point(1, 1))
.minus(expectedSize);
var actualPosition = $overlay.position();
Util.assessNumericValue(actualPosition.left, expectedPosition.x, epsilon,
"Scaled overlay position.x should adjust to rotation.");
Util.assessNumericValue(actualPosition.top, expectedPosition.y, epsilon,
"Scaled overlay position.y should adjust to rotation.");
var actualWidth = $overlay.width();
var actualHeight = $overlay.height();
Util.assessNumericValue(actualWidth, expectedSize.x, epsilon,
"Scaled overlay width should not adjust to rotation.");
Util.assessNumericValue(actualHeight, expectedSize.y, epsilon,
"Scaled overlay height should not adjust to rotation.");
var actualBounds = viewer.getOverlayById("fully-scaled-overlay")
.getBounds(viewport);
var expectedBounds = new OpenSeadragon.Rect(0, 0, 1, 1)
.rotate(-45, new OpenSeadragon.Point(1, 1));
ok(expectedBounds.equals(actualBounds),
"The fully scaled overlay should have bounds " +
expectedBounds + " but found " + actualBounds);
start();
});
});
// ----------
asyncTest('Horizontally scaled overlay rotation mode NO_ROTATION', function() {
viewer = OpenSeadragon({
id: 'example-overlays',
prefixUrl: '/build/openseadragon/images/',
tileSources: '/test/data/testpattern.dzi',
springStiffness: 100, // Faster animation = faster tests
degrees: 45,
overlays: [{
id: "horizontally-scaled-overlay",
x: 0.5,
y: 0.5,
width: 1,
placement: OpenSeadragon.Placement.CENTER,
rotationMode: OpenSeadragon.OverlayRotationMode.NO_ROTATION
}]
});
viewer.addOnceHandler('open', function() {
var $overlay = $("#horizontally-scaled-overlay");
var notScaledWidth = 100;
var notScaledHeight = 100;
$overlay.get(0).style.height = notScaledHeight + "px";
var viewport = viewer.viewport;
var notScaledSize = viewport.deltaPointsFromPixelsNoRotate(
new OpenSeadragon.Point(notScaledWidth, notScaledHeight));
// Force refresh to takes new dimensions into account.
viewer._drawOverlays();
var expectedWidth = viewport.deltaPixelsFromPointsNoRotate(
new OpenSeadragon.Point(1, 1)).x;
var expectedPosition = viewport.viewportToViewerElementCoordinates(
new OpenSeadragon.Point(0.5, 0.5))
.minus(new OpenSeadragon.Point(expectedWidth / 2, notScaledHeight / 2));
var actualPosition = $overlay.position();
Util.assessNumericValue(actualPosition.left, expectedPosition.x, epsilon,
"Horizontally scaled overlay position.x should adjust to rotation.");
Util.assessNumericValue(actualPosition.top, expectedPosition.y, epsilon,
"Horizontally scaled overlay position.y should adjust to rotation.");
var actualWidth = $overlay.width();
var actualHeight = $overlay.height();
Util.assessNumericValue(actualWidth, expectedWidth, epsilon,
"Horizontally scaled overlay width should not adjust to rotation.");
Util.assessNumericValue(actualHeight, notScaledHeight, epsilon,
"Horizontally scaled overlay height should not adjust to rotation.");
var actualBounds = viewer.getOverlayById("horizontally-scaled-overlay")
.getBounds(viewport);
var expectedBounds = new OpenSeadragon.Rect(
0, 0.5 - notScaledSize.y / 2, 1, notScaledSize.y)
.rotate(-45, new OpenSeadragon.Point(0.5, 0.5));
ok(expectedBounds.equals(actualBounds),
"The horizontally scaled overlay should have bounds " +
expectedBounds + " but found " + actualBounds);
start();
});
});
// ----------
asyncTest('Vertically scaled overlay rotation mode NO_ROTATION', function() {
viewer = OpenSeadragon({
id: 'example-overlays',
prefixUrl: '/build/openseadragon/images/',
tileSources: '/test/data/testpattern.dzi',
springStiffness: 100, // Faster animation = faster tests
degrees: 45,
overlays: [{
id: "vertically-scaled-overlay",
x: 0,
y: 0.5,
height: 1,
placement: OpenSeadragon.Placement.LEFT,
rotationMode: OpenSeadragon.OverlayRotationMode.NO_ROTATION
}]
});
viewer.addOnceHandler('open', function() {
var $overlay = $("#vertically-scaled-overlay");
var notScaledWidth = 100;
var notScaledHeight = 100;
$overlay.get(0).style.width = notScaledWidth + "px";
var viewport = viewer.viewport;
var notScaledSize = viewport.deltaPointsFromPixelsNoRotate(
new OpenSeadragon.Point(notScaledWidth, notScaledHeight));
// Force refresh to takes new dimensions into account.
viewer._drawOverlays();
var expectedHeight = viewport.deltaPixelsFromPointsNoRotate(
new OpenSeadragon.Point(1, 1)).y;
var expectedPosition = viewport.viewportToViewerElementCoordinates(
new OpenSeadragon.Point(0, 0.5))
.minus(new OpenSeadragon.Point(0, expectedHeight / 2));
var actualPosition = $overlay.position();
Util.assessNumericValue(actualPosition.left, expectedPosition.x, epsilon,
"Vertically scaled overlay position.x should adjust to rotation.");
Util.assessNumericValue(actualPosition.top, expectedPosition.y, epsilon,
"Vertically scaled overlay position.y should adjust to rotation.");
var actualWidth = $overlay.width();
var actualHeight = $overlay.height();
Util.assessNumericValue(actualWidth, notScaledWidth, epsilon,
"Vertically scaled overlay width should not adjust to rotation.");
Util.assessNumericValue(actualHeight, expectedHeight, epsilon,
"Vertically scaled overlay height should not adjust to rotation.");
var actualBounds = viewer.getOverlayById("vertically-scaled-overlay")
.getBounds(viewport);
var expectedBounds = new OpenSeadragon.Rect(
0, 0, notScaledSize.x, 1)
.rotate(-45, new OpenSeadragon.Point(0, 0.5));
ok(expectedBounds.equals(actualBounds),
"The vertically scaled overlay should have bounds " +
expectedBounds + " but found " + actualBounds);
start();
});
});
// ----------
asyncTest('Not scaled overlay rotation mode NO_ROTATION', function() {
viewer = OpenSeadragon({
id: 'example-overlays',
prefixUrl: '/build/openseadragon/images/',
tileSources: '/test/data/testpattern.dzi',
springStiffness: 100, // Faster animation = faster tests
degrees: 45,
overlays: [{
id: "not-scaled-overlay",
x: 1,
y: 0,
placement: OpenSeadragon.Placement.TOP_RIGHT,
rotationMode: OpenSeadragon.OverlayRotationMode.NO_ROTATION
}]
});
viewer.addOnceHandler('open', function() {
var $overlay = $("#not-scaled-overlay");
var notScaledWidth = 100;
var notScaledHeight = 100;
$overlay.get(0).style.width = notScaledWidth + "px";
$overlay.get(0).style.height = notScaledHeight + "px";
var viewport = viewer.viewport;
var notScaledSize = viewport.deltaPointsFromPixelsNoRotate(
new OpenSeadragon.Point(notScaledWidth, notScaledHeight));
// Force refresh to takes new dimensions into account.
viewer._drawOverlays();
var expectedPosition = viewport.viewportToViewerElementCoordinates(
new OpenSeadragon.Point(1, 0))
.minus(new OpenSeadragon.Point(notScaledWidth, 0));
var actualPosition = $overlay.position();
Util.assessNumericValue(actualPosition.left, expectedPosition.x, epsilon,
"Not scaled overlay position.x should adjust to rotation.");
Util.assessNumericValue(actualPosition.top, expectedPosition.y, epsilon,
"Not scaled overlay position.y should adjust to rotation.");
var actualWidth = $overlay.width();
var actualHeight = $overlay.height();
Util.assessNumericValue(actualWidth, notScaledWidth, epsilon,
"Not scaled overlay width should not adjust to rotation.");
Util.assessNumericValue(actualHeight, notScaledHeight, epsilon,
"Not scaled overlay height should not adjust to rotation.");
var actualBounds = viewer.getOverlayById("not-scaled-overlay")
.getBounds(viewport);
var expectedBounds = new OpenSeadragon.Rect(
1 - notScaledSize.x, 0, notScaledSize.x, notScaledSize.y)
.rotate(-45, new OpenSeadragon.Point(1, 0));
ok(expectedBounds.equals(actualBounds),
"Not scaled overlay should have bounds " +
expectedBounds + " but found " + actualBounds);
start();
});
});
// ----------
asyncTest('Fully scaled overlay rotation mode BOUNDING_BOX', function() {
viewer = OpenSeadragon({
id: 'example-overlays',
prefixUrl: '/build/openseadragon/images/',
tileSources: '/test/data/testpattern.dzi',
springStiffness: 100, // Faster animation = faster tests
degrees: 45,
overlays: [{
id: "fully-scaled-overlay",
x: 1,
y: 1,
width: 1,
height: 1,
placement: OpenSeadragon.Placement.BOTTOM_RIGHT,
rotationMode: OpenSeadragon.OverlayRotationMode.BOUNDING_BOX
}]
});
viewer.addOnceHandler('open', function() {
var viewport = viewer.viewport;
var $overlay = $("#fully-scaled-overlay");
var expectedRect = viewport.viewportToViewerElementRectangle(
new OpenSeadragon.Rect(0, 0, 1, 1)).getBoundingBox();
var actualPosition = $overlay.position();
Util.assessNumericValue(actualPosition.left, expectedRect.x, epsilon,
"Scaled overlay position.x should adjust to rotation.");
Util.assessNumericValue(actualPosition.top, expectedRect.y, epsilon,
"Scaled overlay position.y should adjust to rotation.");
var actualWidth = $overlay.width();
var actualHeight = $overlay.height();
Util.assessNumericValue(actualWidth, expectedRect.width, epsilon,
"Scaled overlay width should not adjust to rotation.");
Util.assessNumericValue(actualHeight, expectedRect.height, epsilon,
"Scaled overlay height should not adjust to rotation.");
var actualBounds = viewer.getOverlayById("fully-scaled-overlay")
.getBounds(viewport);
var expectedBounds = new OpenSeadragon.Rect(
0.5, -0.5, Math.sqrt(2), Math.sqrt(2), 45);
var boundsEpsilon = 0.000001;
Util.assessNumericValue(actualBounds.x, expectedBounds.x, boundsEpsilon,
"The fully scaled overlay should have adjusted bounds.x");
Util.assessNumericValue(actualBounds.y, expectedBounds.y, boundsEpsilon,
"The fully scaled overlay should have adjusted bounds.y");
Util.assessNumericValue(actualBounds.width, expectedBounds.width, boundsEpsilon,
"The fully scaled overlay should have adjusted bounds.width");
Util.assessNumericValue(actualBounds.height, expectedBounds.height, boundsEpsilon,
"The fully scaled overlay should have adjusted bounds.height");
Util.assessNumericValue(actualBounds.degrees, expectedBounds.degrees, boundsEpsilon,
"The fully scaled overlay should have adjusted bounds.degrees");
start();
});
});
// ----------
asyncTest('Fully scaled overlay rotation mode EXACT', function() {
viewer = OpenSeadragon({
id: 'example-overlays',
prefixUrl: '/build/openseadragon/images/',
tileSources: '/test/data/testpattern.dzi',
springStiffness: 100, // Faster animation = faster tests
degrees: 45,
overlays: [{
id: "fully-scaled-overlay",
x: 1,
y: 1,
width: 1,
height: 1,
placement: OpenSeadragon.Placement.BOTTOM_RIGHT,
rotationMode: OpenSeadragon.OverlayRotationMode.EXACT
}]
});
viewer.addOnceHandler('open', function() {
var viewport = viewer.viewport;
var $overlay = $("#fully-scaled-overlay");
var expectedSize = viewport.deltaPixelsFromPointsNoRotate(
new OpenSeadragon.Point(1, 1));
var expectedPosition = viewport.pixelFromPoint(
new OpenSeadragon.Point(1, 1))
.minus(expectedSize);
// We can't rely on jQuery.position with transforms.
var actualStyle = $overlay.get(0).style;
var left = Number(actualStyle.left.replace("px", ""));
var top = Number(actualStyle.top.replace("px", ""));
Util.assessNumericValue(left, expectedPosition.x, epsilon,
"Scaled overlay position.x should adjust to rotation.");
Util.assessNumericValue(top, expectedPosition.y, epsilon,
"Scaled overlay position.y should adjust to rotation.");
var actualWidth = $overlay.width();
var actualHeight = $overlay.height();
Util.assessNumericValue(actualWidth, expectedSize.x, epsilon,
"Scaled overlay width should not adjust to rotation.");
Util.assessNumericValue(actualHeight, expectedSize.y, epsilon,
"Scaled overlay height should not adjust to rotation.");
var transformOriginProp = OpenSeadragon.getCssPropertyWithVendorPrefix(
'transformOrigin');
var transformProp = OpenSeadragon.getCssPropertyWithVendorPrefix(
'transform');
var transformOrigin = actualStyle[transformOriginProp];
// Some browsers replace "right bottom" by "100% 100%"
ok(transformOrigin.match(/(100% 100%)|(right bottom)/),
"Transform origin should be right bottom. Got: " + transformOrigin);
equal(actualStyle[transformProp], "rotate(45deg)",
"Transform should be rotate(45deg).");
var actualBounds = viewer.getOverlayById("fully-scaled-overlay")
.getBounds(viewport);
var expectedBounds = new OpenSeadragon.Rect(0, 0, 1, 1);
ok(expectedBounds.equals(actualBounds),
"The fully scaled overlay should have bounds " +
expectedBounds + " but found " + actualBounds);
start();
});
});
})(); })();