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
How to Call the Same Method for Different Elements With Different Ids
How Does Group by Works in Sequelize
Bootstrap 4 Navbar-Toggler-Icon Does Not Appear
What Is the Opposite of Evt.Preventdefault();
How to Get the Value of Li on Dropdown Menu Click in Laravel Using Jquery
How to Check If a Background Image Is Loaded
Process a Dictionary and Return Each Entry in React
Resource Blocked Due to Mime Type Mismatch (X-Content-Type-Options: Nosniff)
How to Include Third Party Js Libraries in React App
Get the Value of Datepicker on Change in Jquery
Why the Value of Input Elements Are Not Changing
How to Tell If a Dom Element Is Visible in the Current Viewport
How to Know If Browser Tab Is Already Open Using JavaScript
Javascript Dashes in Phone Number
Matching a Forward Slash With a Regex
How to Make Any Hovered Color Darker/Lighter That the Original