Calculate the Bounding Box'S X, Y, Height and Width of a Rotated Element Via JavaScript

Calculate correct width and height when resizing a rotated element

Finally I've found a working solution. The key was to calculate the new center point by getting the current mouse position and the opposite corner. Now I rotate both points back to its unrotated state and get the bounds to use.
The relevant code looks like this:

// the current rotation angle in degrees
angle = me.angle;
// the current mouse point (dragging a selection control)
var mousePoint = new Point(e.clientX, e.clientY);
// Get opposite point. Rotate it to get the visual position
var topLeft = new Point(left, top).rotate(center, angle);
// calculate the new center
var centerPoint = mousePoint.getMiddle(topLeft);
// rotate the opposite point back around the new center
var topLeftRotated = topLeft.rotate(centerPoint, -angle);
// rotate the mouse point around the new center.
var mousePointRotated = mousePoint.rotate(centerPoint, -angle);

// now we have the top left and lower right points and
// can calculate the dimensions
var x1 = topLeftRotated.x;
var x2 = mousePointRotated.x;
var w = x2-x1;
var y1 = topLeftRotated.y;
var y2 = mousePointRotated.y;
var h = y2-y1;

me.selection.style.width = w + "px";
me.selection.style.height = h + "px";
me.selection.style.left = x1 + "px";
me.selection.style.top = y1 + "px";

See the updated fiddle
(Note. This is more a proof of concept and no production ready solution.)

I'm open to more elegant solutions.

Calculate bounding box width and height from given coordinates(x,y)

Using the map() function transform the input array into array of x or y values. You can then feed these transformed arrays to Math.min() and Math.max() to get left, right, bottom and top to compute the bounding box. When you have the bounding box, the width and height calculation is straight forward (subtract min from max). See code snippet below.

const coords = [{x:10, y:20}, {x:5, y:6}, {x:1, y:25}, {x:11, y:2}];
const xArr = coords.map(c => c.x);
const yArr = coords.map(c => c.y);

const l = Math.min(...xArr);
const r = Math.max(...xArr);
const b = Math.min(...yArr);
const t = Math.max(...yArr);

const width = r - l;
const height = t - b;
console.log(width, height);

How to calculate a bounding box for a rectangle rotated around its corner?

After rotation about corner x0, y0 by angle Fi rectangle center has coordinates

cx = x0 + w/2*Cos(Fi) - h/2*Sin(Fi)
cy = y0 + w/2*Sin(Fi) + h/2*Cos(Fi)

Same coordinates are for bounding box center. So bounding box base corner is

bbx = cx - bbW/2
bby = cy - bbH/2

let canvas = document.querySelector("canvas");
let ctx = canvas.getContext("2d");

function drawRectangle(rX, rY, rW, rH) {
ctx.beginPath();
ctx.rect(rX, rY, rW, rH);
ctx.stroke();
}

function degreesToRadians(degrees) { return degrees * (Math.PI / 180); }

function rotateCanvas(radians, centerX, centerY) {
ctx.translate(centerX, centerY);
ctx.rotate(radians);
ctx.translate(-centerX, -centerY);
}

function drawRotatedRectangle(rX, rY, rW, rH, rAngle, rOX, rOY) {
rotateCanvas(rAngle, rOX, rOY);
drawRectangle(rX, rY, rW, rH);
rotateCanvas(-rAngle, rOX, rOY);
}

function computeBB(x, y, w, h, a) {
let sinA = Math.abs(Math.sin(a));
let cosA = Math.abs(Math.cos(a));

let bbH = w * sinA + h * cosA;
let bbW = w * cosA + h * sinA;

let cx = x + w/2*Math.cos(a) - h/2*Math.sin(a)
let cy = y + w/2*Math.sin(a) + h/2*Math.cos(a)

let bbX = cx - bbW / 2;
let bbY = cy - bbH / 2;

return { x: bbX, y: bbY, w: bbW, h: bbH };
}

let rX = 100;
let rY = 100;
let rW = 100;
let rH = 50;
let rA = degreesToRadians(45);

let rOX = rX;
let rOY = rY;

drawRotatedRectangle(rX, rY, rW, rH, rA, rOX, rOY);

let bb = computeBB(rX, rY, rW, rH, rA);

ctx.strokeStyle = "#ff0000";
drawRectangle(bb.x, bb.y, bb.w, bb.h);
<canvas width="600" height="600"></canvas>

How to calculate a bounding box for a rectangle rotated around its center?

Size of bounding box (description here)

H = w * Abs(Sin(Fi)) + h * Abs(Cos(Fi))
W = w * Abs(Cos(Fi)) + h * Abs(Sin(Fi))

Having rXCenter, rYCenter, you can find get bounds coordinates

x0  = rXCenter - W/2
y0 = rYCenter - H/2

Compute bounding box of multiple rectangles (with and/or without rotation)

Here is my version of utils functions that I was using in one app for calculating the bouding box of shapes.

export function degToRad(angle) {
return angle / 180 * Math.PI;
}

export function getShapeRect(shape) {
const angleRad = degToRad(shape.rotation);
const x1 = shape.x;
const y1 = shape.y;
const x2 = x1 + shape.width * Math.cos(angleRad);
const y2 = y1 + shape.width * Math.sin(angleRad);
const x3 =
shape.x +
shape.width * Math.cos(angleRad) +
shape.height * Math.sin(-angleRad);
const y3 =
shape.y +
shape.height * Math.cos(angleRad) +
shape.width * Math.sin(angleRad);
const x4 = shape.x + shape.height * Math.sin(-angleRad);
const y4 = shape.y + shape.height * Math.cos(angleRad);

const leftX = Math.min(x1, x2, x3, x4);
const rightX = Math.max(x1, x2, x3, x4);
const topY = Math.min(y1, y2, y3, y4);
const bottomY = Math.max(y1, y2, y3, y4);
return {
x: leftX,
y: topY,
width: rightX - leftX,
height: bottomY - topY
};
}

export function getBoundingBox(shapes) {
let x1 = 9999999999;
let y1 = 9999999999;
let x2 = -999999999;
let y2 = -999999999;
shapes.forEach(shape => {
const rect = getShapeRect(shape);
x1 = Math.min(x1, rect.x);
y1 = Math.min(y1, rect.y);
x2 = Math.max(x2, rect.x + rect.width);
y2 = Math.max(y2, rect.y + rect.height);
});
return {
x: x1,
y: y1,
width: x2 - x1,
height: y2 - y1,
rotation: 0
};
}

Demo: https://codesandbox.io/s/j28nz494r3



Related Topics



Leave a reply



Submit