Skip to content

Instantly share code, notes, and snippets.

@redgeoff
Created November 24, 2016 00:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save redgeoff/911cdeac0ffbb7325194a6642ea35619 to your computer and use it in GitHub Desktop.
Save redgeoff/911cdeac0ffbb7325194a6642ea35619 to your computer and use it in GitHub Desktop.
Rotated Rectangles
<body>
<script>
var Point = function (x, y) {
this.x = x;
this.y = y;
};
var rotatePoint = function (p, rads) {
return new Point(p.x*Math.cos(rads) - p.y*Math.sin(rads),
p.x*Math.sin(rads) + p.y*Math.cos(rads));
};
var rotatePointAround = function (p, rads, ctr) {
var p2 = rotatePoint(new Point(p.x - ctr.x, p.y - ctr.y), rads);
return new Point(p2.x + ctr.x, p2.y + ctr.y);
};
var rotatePointsAround = function (points, rads, ctr) {
var rotatedPoints = [];
points.forEach(function (point) {
rotatedPoints.push(rotatePointAround(point, rads, ctr));
});
return rotatedPoints;
};
var boundingRect = function (vertices) {
var left = vertices[0].x,
right = vertices[0].x,
top = vertices[0].y,
bottom = vertices[0].y;
vertices.forEach(function (vertex) {
if (vertex.x < left) {
left = vertex.x;
}
if (vertex.x > right) {
right = vertex.x;
}
if (vertex.y < top) {
top = vertex.y;
}
if (vertex.y > bottom) {
bottom = vertex.y;
}
});
return { left: left, right: right, top: top, bottom: bottom };
};
var toVertices = function (x, y, width, height) {
return [
new Point(x, y),
new Point(width, y),
new Point(width, height),
new Point(x, height)
];
};
var rotateAndGetBoundingRect = function (width, height, rads) {
var vertices = toVertices(0, 0, width, height);
var center = new Point(width/2, height/2);
var rotatedVertices = rotatePointsAround(vertices, rads, center);
return boundingRect(rotatedVertices);
};
// Allow for 1 extra pixel as the canvas's implementation of round may differ from ours by up to 1
// pixel and we don't want to clip any pixels
var EXTRA_PIXEL = 1;
var getOffsets = function (left, top) {
// Use floor so that we don't clip any pixels as we are rounding to the nearest pixel
var x = -Math.floor(left) + EXTRA_PIXEL;
var y = -Math.floor(top) + EXTRA_PIXEL;
return { x: x, y: y };
};
var drawRotatedRect = function (canvas, width, height, rads) {
// Get the bounding rect after rotation and use this to position our rectangle and size our canvas.
var rect = rotateAndGetBoundingRect(width, height, rads);
var offsets = getOffsets(rect.left, rect.top);
// Use ceil so that we don't clip any pixels
var canvasWidth = Math.ceil(rect.right) + offsets.x + EXTRA_PIXEL;
var canvasHeight = Math.ceil(rect.bottom) + offsets.y + EXTRA_PIXEL;
canvas.width = canvasWidth;
canvas.height = canvasWidth;
var ctx = canvas.getContext('2d');
ctx.translate(offsets.x + width/2, offsets.y + height/2);
ctx.rotate(rads);
ctx.rect(-width/2, -height/2, width, height);
ctx.stroke();
return offsets;
};
var createAndDrawRotatedRect = function (x, y, width, height, rads) {
var canvas = document.createElement('canvas');
var offsets = drawRotatedRect(canvas, width, height, rads);
canvas.style.position = 'absolute';
canvas.style.left = (x - offsets.x) + 'px';
canvas.style.top = (y - offsets.y) + 'px';
document.body.appendChild(canvas);
};
var degs2Rads = function (degs) {
return degs*Math.PI/180;
};
// -----
// Starting point for 1st rectangle
var x = 100, y = 100;
var width = 100, height = 100, rads = degs2Rads(45);
var rect = rotateAndGetBoundingRect(width, height, rads);
var center = new Point(x + width/2, y + height/2);
var rotatedVertices = rotatePointsAround(toVertices(x, y, width, height), rads, center);
var offsets = getOffsets(rect.left, rect.top);
createAndDrawRotatedRect(x, y, width, height, rads);
var dX = Math.cos(Math.PI/2 - rads)*height;
var dY = Math.sin(Math.PI/2 - rads)*height;
createAndDrawRotatedRect(x + dX, y - dY, width, height, rads);
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment