Calculate the bounding box's X, Y, Height and Width of a rotated element via JavaScript

20,002

Solution 1

I know this is a bit late, but I've written a fiddle for exactly this problem, on an HTML5 canvas:

http://jsfiddle.net/oscarpalacious/ZdQKg/

I hope somebody finds it useful!

I'm actually not calculating your x,y for the upper left corner of the container. It's calculated as a result of the offset (code from the fiddle example):

this.w = Math.sin(this.angulo) * rotador.h + Math.cos(this.angulo) * rotador.w;
this.h = Math.sin(this.angulo) * rotador.w + Math.cos(this.angulo) * rotador.h;
// The offset on a canvas for the upper left corner (x, y) is
// given by the first two parameters for the rect() method:
contexto.rect(-(this.w/2), -(this.h/2), this.w, this.h);

Cheers

Solution 2

Have you tried using getBoundingClientRect() ?

This method returns an object with current values of "bottom, height, left, right, top, width" considering rotations

Solution 3

Turn the four corners into vectors from the center, rotate them, and get the new min/max width/height from them.

EDIT:

I see where you're having problems now. You're doing the calculations using the entire side when you need to be doing them with the offsets from the center of rotation. Yes, this results in four rotated points (which, strangely enough, is exactly as many points as you started with). Between them there will be one minimum X, one maximum X, one minimum Y, and one maximum Y. Those are your bounds.

Share:
20,002

Related videos on Youtube

Campbeln
Author by

Campbeln

Updated on May 19, 2020

Comments

  • Campbeln
    Campbeln almost 4 years

    Basically I'm asking this question for JavaScript: Calculate Bounding box coordinates from a rotated rectangle

    Diagram

    In this case:

    • iX = Width of rotated (blue) HTML element
    • iY = Height of rotated (blue) HTML element
    • bx = Width of Bounding Box (red)
    • by = Height of Bounding Box (red)
    • x = X coord of Bounding Box (red)
    • y = Y coord of Bounding Box (red)
    • iAngle/t = Angle of rotation of HTML element (blue; not shown but used in code below), FYI: It's 37 degrees in this example (not that it matters for the example)

    How does one calculate the X, Y, Height and Width of a bounding box (all the red numbers) surrounding a rotated HTML element (given its width, height, and Angle of rotation) via JavaScript? A sticky bit to this will be getting the rotated HTML element (blue box)'s original X/Y coords to use as an offset somehow (this is not represented in the code below). This may well have to look at CSS3's transform-origin to determine the center point.

    I've got a partial solution, but the calculation of the X/Y coords is not functioning properly...

    var boundingBox = function (iX, iY, iAngle) {
        var x, y, bx, by, t;
    
        //# Allow for negetive iAngle's that rotate counter clockwise while always ensuring iAngle's < 360
        t = ((iAngle < 0 ? 360 - iAngle : iAngle) % 360);
    
        //# Calculate the width (bx) and height (by) of the .boundingBox
        //#     NOTE: See https://stackoverflow.com/questions/3231176/how-to-get-size-of-a-rotated-rectangle
        bx = (iX * Math.sin(iAngle) + iY * Math.cos(iAngle));
        by = (iX * Math.cos(iAngle) + iY * Math.sin(iAngle));
    
        //# This part is wrong, as it's re-calculating the iX/iY of the rotated element (blue)
        //# we want the x/y of the bounding box (red)
        //#     NOTE: See https://stackoverflow.com/questions/9971230/calculate-rotated-rectangle-size-from-known-bounding-box-coordinates
        x = (1 / (Math.pow(Math.cos(t), 2) - Math.pow(Math.sin(t), 2))) * (bx * Math.cos(t) - by * Math.sin(t));
        y = (1 / (Math.pow(Math.cos(t), 2) - Math.pow(Math.sin(t), 2))) * (-bx * Math.sin(t) + by * Math.cos(t));
    
        //# Return an object to the caller representing the x/y and width/height of the calculated .boundingBox
        return {
            x: parseInt(x), width: parseInt(bx),
            y: parseInt(y), height: parseInt(by)
        }
    };
    

    I feel like I am so close, and yet so far...

    Many thanks for any help you can provide!

    TO HELP THE NON-JAVASCRIPTERS...

    Once the HTML element is rotated, the browser returns a "matrix transform" or "rotation matrix" which seems to be this: rotate(Xdeg) = matrix(cos(X), sin(X), -sin(X), cos(X), 0, 0); See this page for more info.

    I have a feeling this will enlighten us on how to get the X,Y of the bounding box (red) based solely on the Width, Height and Angle of the rotated element (blue).

    New Info

    Humm... interesting...

    enter image description here

    Each browser seems to handle the rotation differently from an X/Y perspective! FF ignores it completely, IE & Opera draw the bounding box (but its properties are not exposed, ie: bx & by) and Chrome & Safari rotate the rectangle! All are properly reporting the X/Y except FF. So... the X/Y issue seems to exist for FF only! How very odd!

    Also of note, it seems that $(document).ready(function () {...}); fires too early for the rotated X/Y to be recognized (which was part of my original problem!). I am rotating the elements directly before the X/Y interrogation calls in $(document).ready(function () {...}); but they don't seem to update until some time after(!?).

    When I get a little more time, I will toss up a jFiddle with the example, but I'm using a modified form of "jquery-css-transform.js" so I have a tiny bit of tinkering before the jFiddle...

    So... what's up, FireFox? That ain't cool, man!

    The Plot Thickens...

    Well, FF12 seems to fix the issue with FF11, and now acts like IE and Opera. But now I am back to square one with the X/Y, but at least I think I know why now...

    It seems that even though the X/Y is being reported correctly by the browsers for the rotated object, a "ghost" X/Y still exists on the un-rotated version. It seems as though this is the order of operations:

    • Starting with an un-rotated element at an X,Y of 20,20
    • Rotate said element, resulting in the reporting of X,Y as 15,35
    • Move said element via JavaScript/CSS to X,Y 10,10
    • Browser logically un-rotates element back to 20,20, moves to 10,10 then re-rotates, resulting in an X,Y of 5,25

    So... I want the element to end up at 10,10 post rotation, but thanks to the fact that the element is (seemingly) re-rotated post move, the resulting X,Y differs from the set X,Y.

    This is my problem! So what I really need is a function to take the desired destination coords (10,10), and work backwards from there to get the starting X,Y coords that will result in the element being rotated into 10,10. At least I know what my problem is now, as thanks to the inner workings of the browsers, it seems with a rotated element 10=5!

    • Li-aung Yip
      Li-aung Yip about 12 years
      Can you post a diagram of what you want? Pictures speak a thousand words and all.
    • Ignacio Vazquez-Abrams
      Ignacio Vazquez-Abrams about 12 years
      I second a picture, especially so that you can see what you're doing.
    • Campbeln
      Campbeln about 12 years
      Thanks! The diagram really was necessary (rather then relying on the referenced Q's picture).
    • Ignacio Vazquez-Abrams
      Ignacio Vazquez-Abrams about 12 years
      You should post a "before" picture for your benefit.
    • Campbeln
      Campbeln about 12 years
      My goal is to start with the "after". The "before" would be the blue box un-rotated. And thanks to CSS3's transform-origin property, the blue box could be rotated from any center point. See: w3schools.com/cssref/css3_pr_transform-origin.asp ... of course if this makes the question unpossible, then I'll re-evaluate =)
    • abernier
      abernier over 8 years
  • Campbeln
    Campbeln about 12 years
    Thank you, but I am very math-stupid (it never liked me and I rarely liked it =) I'm sure you are correct, but I require hand-holding.
  • Li-aung Yip
    Li-aung Yip about 12 years
    You can't just go rotating things without running into some trig. ;) SOH CAH TOA! SOH CAH TOA! SOH... It's much easier to learn once you need it to solve a real problem you care about, as opposed to classroom problems of no importance. ("Farmer Jones has a flagpole of height 5 and the sun is at an angle of 36 degrees... how long is the shadow?")
  • ARF
    ARF over 11 years
    That function is not transform aware in most browsers. See developer.mozilla.org/en-US/docs/DOM/…
  • docodemore
    docodemore over 8 years
    How awesome is this! Just what I was looking for :D
  • winthers
    winthers over 7 years
    hi, pretty cool, how would u adapt it to handle a shape that is not rotated around its center ?
  • rixo
    rixo over 6 years
    For the sake of completeness, this calculation works if you have an angle between 0 and 2*PI, AND applying this correction (from the fiddle): this.angulo = ((rotador.angulo > Math.PI * 0.5 && rotador.angulo < Math.PI * 1) || (rotador.angulo > Math.PI * 1.5 && rotador.angulo < Math.PI * 2))? Math.PI - rotador.angulo : rotador.angulo;
  • Admin
    Admin over 4 years
    2019 - Transform awareness is no more an issue. See caniuse.com/#search=getBoundingClientRect()