diff --git a/src/rectangle.js b/src/rectangle.js index 57bd48f1..35544cdf 100644 --- a/src/rectangle.js +++ b/src/rectangle.js @@ -43,6 +43,8 @@ * y increases when going to the bottom * degrees increases clockwise with 0 being the horizontal * + * The constructor normalizes the rectangle to always have 0 <= degrees < 90 + * * @memberof OpenSeadragon * @param {Number} [x=0] The vector component 'x'. * @param {Number} [y=0] The vector component 'y'. @@ -77,6 +79,35 @@ $.Rect = function(x, y, width, height, degrees) { this.height = typeof(height) === "number" ? height : 0; this.degrees = typeof(degrees) === "number" ? degrees : 0; + + // Normalizes the rectangle. + this.degrees = this.degrees % 360; + if (this.degrees < 0) { + this.degrees += 360; + } + var newTopLeft, newWidth; + if (this.degrees >= 270) { + newTopLeft = this.getTopRight(); + this.x = newTopLeft.x; + this.y = newTopLeft.y; + newWidth = this.height; + this.height = this.width; + this.width = newWidth; + this.degrees -= 270; + } else if (this.degrees >= 180) { + newTopLeft = this.getBottomRight(); + this.x = newTopLeft.x; + this.y = newTopLeft.y; + this.degrees -= 180; + } else if (this.degrees >= 90) { + newTopLeft = this.getBottomLeft(); + this.x = newTopLeft.x; + this.y = newTopLeft.y; + newWidth = this.height; + this.height = this.width; + this.width = newWidth; + this.degrees -= 90; + } }; $.Rect.prototype = /** @lends OpenSeadragon.Rect.prototype */{ @@ -257,10 +288,13 @@ $.Rect.prototype = /** @lends OpenSeadragon.Rect.prototype */{ * @return {OpenSeadragon.Rect} */ rotate: function(degrees, pivot) { - degrees = (degrees + 360) % 360; + degrees = degrees % 360; if (degrees === 0) { return this.clone(); } + if (degrees < 0) { + degrees += 360; + } pivot = pivot || this.getCenter(); var newTopLeft = this.getTopLeft().rotate(degrees, pivot); diff --git a/test/modules/rectangle.js b/test/modules/rectangle.js index 09f5e467..437f20b4 100644 --- a/test/modules/rectangle.js +++ b/test/modules/rectangle.js @@ -15,11 +15,11 @@ Util.assessNumericValue(rectA.x, rectB.x, precision, message + " x: "); Util.assessNumericValue(rectA.y, rectB.y, precision, message + " y: "); Util.assessNumericValue(rectA.width, rectB.width, precision, - message + " width: "); + message + " width: "); Util.assessNumericValue(rectA.height, rectB.height, precision, - message + " height: "); + message + " height: "); Util.assessNumericValue(rectA.degrees, rectB.degrees, precision, - message + " degrees: "); + message + " degrees: "); } test('Constructor', function() { @@ -36,6 +36,39 @@ strictEqual(rect.width, 0, 'rect.width should be 0'); strictEqual(rect.height, 0, 'rect.height should be 0'); strictEqual(rect.degrees, 0, 'rect.degrees should be 0'); + + rect = new OpenSeadragon.Rect(0, 0, 1, 2, -405); + Util.assessNumericValue(Math.sqrt(2) / 2, rect.x, precision, + 'rect.x should be sqrt(2)/2'); + Util.assessNumericValue(-Math.sqrt(2) / 2, rect.y, precision, + 'rect.y should be -sqrt(2)/2'); + Util.assessNumericValue(2, rect.width, precision, + 'rect.width should be 2'); + Util.assessNumericValue(1, rect.height, precision, + 'rect.height should be 1'); + strictEqual(45, rect.degrees, 'rect.degrees should be 45'); + + rect = new OpenSeadragon.Rect(0, 0, 1, 2, 135); + Util.assessNumericValue(-Math.sqrt(2), rect.x, precision, + 'rect.x should be -sqrt(2)'); + Util.assessNumericValue(-Math.sqrt(2), rect.y, precision, + 'rect.y should be -sqrt(2)'); + Util.assessNumericValue(2, rect.width, precision, + 'rect.width should be 2'); + Util.assessNumericValue(1, rect.height, precision, + 'rect.height should be 1'); + strictEqual(45, rect.degrees, 'rect.degrees should be 45'); + + rect = new OpenSeadragon.Rect(0, 0, 1, 1, 585); + Util.assessNumericValue(0, rect.x, precision, + 'rect.x should be 0'); + Util.assessNumericValue(-Math.sqrt(2), rect.y, precision, + 'rect.y should be -sqrt(2)'); + Util.assessNumericValue(1, rect.width, precision, + 'rect.width should be 1'); + Util.assessNumericValue(1, rect.height, precision, + 'rect.height should be 1'); + strictEqual(45, rect.degrees, 'rect.degrees should be 45'); }); test('getTopLeft', function() { @@ -52,7 +85,7 @@ rect.degrees = 45; expected = new OpenSeadragon.Point(1 / Math.sqrt(2), 1 / Math.sqrt(2)); assertPointsEquals(expected, rect.getTopRight(), - "Incorrect top right point with rotation."); + "Incorrect top right point with rotation."); }); test('getBottomLeft', function() { @@ -63,7 +96,7 @@ rect.degrees = 45; expected = new OpenSeadragon.Point(-1 / Math.sqrt(2), 1 / Math.sqrt(2)); assertPointsEquals(expected, rect.getBottomLeft(), - "Incorrect bottom left point with rotation."); + "Incorrect bottom left point with rotation."); }); test('getBottomRight', function() { @@ -74,17 +107,17 @@ rect.degrees = 45; expected = new OpenSeadragon.Point(0, Math.sqrt(2)); assertPointsEquals(expected, rect.getBottomRight(), - "Incorrect bottom right point with 45 rotation."); + "Incorrect bottom right point with 45 rotation."); rect.degrees = 90; expected = new OpenSeadragon.Point(-1, 1); assertPointsEquals(expected, rect.getBottomRight(), - "Incorrect bottom right point with 90 rotation."); + "Incorrect bottom right point with 90 rotation."); rect.degrees = 135; expected = new OpenSeadragon.Point(-Math.sqrt(2), 0); assertPointsEquals(expected, rect.getBottomRight(), - "Incorrect bottom right point with 135 rotation."); + "Incorrect bottom right point with 135 rotation."); }); test('getCenter', function() { @@ -95,17 +128,31 @@ rect.degrees = 45; expected = new OpenSeadragon.Point(0, 0.5 * Math.sqrt(2)); assertPointsEquals(expected, rect.getCenter(), - "Incorrect bottom right point with 45 rotation."); + "Incorrect bottom right point with 45 rotation."); rect.degrees = 90; expected = new OpenSeadragon.Point(-0.5, 0.5); assertPointsEquals(expected, rect.getCenter(), - "Incorrect bottom right point with 90 rotation."); + "Incorrect bottom right point with 90 rotation."); rect.degrees = 135; expected = new OpenSeadragon.Point(-0.5 * Math.sqrt(2), 0); assertPointsEquals(expected, rect.getCenter(), - "Incorrect bottom right point with 135 rotation."); + "Incorrect bottom right point with 135 rotation."); + }); + + test('times', function() { + var rect = new OpenSeadragon.Rect(1, 2, 3, 4, 45); + var expected = new OpenSeadragon.Rect(2, 4, 6, 8, 45); + var actual = rect.times(2); + assertRectangleEquals(expected, actual, "Incorrect x2 rectangles."); + }); + + test('translate', function() { + var rect = new OpenSeadragon.Rect(1, 2, 3, 4, 45); + var expected = new OpenSeadragon.Rect(2, 4, 3, 4, 45); + var actual = rect.translate(new OpenSeadragon.Point(1, 2)); + assertRectangleEquals(expected, actual, "Incorrect translation."); }); test('union', function() { @@ -114,7 +161,7 @@ var expected = new OpenSeadragon.Rect(0, 1, 4, 4); var actual = rect1.union(rect2); assertRectangleEquals(expected, actual, - "Incorrect union with horizontal rectangles."); + "Incorrect union with horizontal rectangles."); rect1 = new OpenSeadragon.Rect(0, -Math.sqrt(2), 2, 2, 45); rect2 = new OpenSeadragon.Rect(1, 0, 2, 2, 0); @@ -125,7 +172,7 @@ 2 + Math.sqrt(2)); actual = rect1.union(rect2); assertRectangleEquals(expected, actual, - "Incorrect union with non horizontal rectangles."); + "Incorrect union with non horizontal rectangles."); }); test('rotate', function() { @@ -137,29 +184,29 @@ 2, 1, 45); - var actual = rect.rotate(45); + var actual = rect.rotate(-675); assertRectangleEquals(expected, actual, - "Incorrect rectangle after rotation of 45deg around center."); + "Incorrect rectangle after rotation of -675deg around center."); expected = new OpenSeadragon.Rect(0, 0, 2, 1, 33); actual = rect.rotate(33, rect.getTopLeft()); assertRectangleEquals(expected, actual, - "Incorrect rectangle after rotation of 33deg around topLeft."); + "Incorrect rectangle after rotation of 33deg around topLeft."); expected = new OpenSeadragon.Rect(0, 0, 2, 1, 101); actual = rect.rotate(101, rect.getTopLeft()); assertRectangleEquals(expected, actual, - "Incorrect rectangle after rotation of 187deg around topLeft."); + "Incorrect rectangle after rotation of 187deg around topLeft."); expected = new OpenSeadragon.Rect(0, 0, 2, 1, 187); actual = rect.rotate(187, rect.getTopLeft()); assertRectangleEquals(expected, actual, - "Incorrect rectangle after rotation of 187deg around topLeft."); + "Incorrect rectangle after rotation of 187deg around topLeft."); expected = new OpenSeadragon.Rect(0, 0, 2, 1, 300); actual = rect.rotate(300, rect.getTopLeft()); assertRectangleEquals(expected, actual, - "Incorrect rectangle after rotation of 300deg around topLeft."); + "Incorrect rectangle after rotation of 300deg around topLeft."); }); test('getBoundingBox', function() { @@ -167,22 +214,22 @@ var bb = rect.getBoundingBox(); ok(rect.equals(bb), "Bounding box of horizontal rectangle should be " + - "identical to rectangle."); + "identical to rectangle."); rect.degrees = 90; var expected = new OpenSeadragon.Rect(-3, 0, 3, 2); assertRectangleEquals(expected, rect.getBoundingBox(), - "Bounding box of rect rotated 90deg."); + "Bounding box of rect rotated 90deg."); rect.degrees = 180; var expected = new OpenSeadragon.Rect(-2, -3, 2, 3); assertRectangleEquals(expected, rect.getBoundingBox(), - "Bounding box of rect rotated 180deg."); + "Bounding box of rect rotated 180deg."); rect.degrees = 270; var expected = new OpenSeadragon.Rect(0, -2, 3, 2); assertRectangleEquals(expected, rect.getBoundingBox(), - "Bounding box of rect rotated 270deg."); + "Bounding box of rect rotated 270deg."); }); })();