Sign in to follow this  

Please review the following bounding box rotation code

This topic is 4091 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

In an upcoming chapter of my learning XNA book, I am dealing with rotating sprites and using game tools. The tool creates sprite sheets and as part of its function, it will also create pixel perfect bounding boxes. ... This was a great idea, right up until I started rotating said sprites :( Suffice to say, there arent too many ellegant ways to rotate 2D bounding boxes... something I forgot before creating the tool ( otherwise, I would have used bounding spheres, and to hell with it! [smile] ). Anyways, the following code is what I used to create updated bounding boxes on an image rotated an arbitrary amount. Frankly, something about it is bugging me, although it seems to return the correct amounts every time. Can I please get a few eyes to look at it and tell me if I am making any glaring mistakes. Also, the code is aimed at newer developers, so unless it is brutal, I dont care too much about optimizations. Also, please keep an eye to constructs that might confuse (relatively) new developers. Thats also my goal here. In a nutshell, I take a bounding box, read from file, break it to its four cardinal points, translate those points about the origin, apply the rotate, then translate them back to real space. At this point, I traverse the point set ( as topLeft becomes relatively meaningless once rotated ), find the new boundaries, and update the bounding rect accordingly. Perhaps I am doing WAY too much work, it feels like I am to me. Anyways, here is the source ( C# and XNA, but should be readable to anyone with 3D experience ). Like I said earlier, the results seem correct at the least.
        public Rectangle currentBoundingBox
        {
            get
            {
                // Get the initial bounding box as saved to disk based on pixel data
                Rectangle rect = _sprites[_currentSprite].boundingBox;

                // Find the center point of the bounding box, this will be used to move the box centered to 0,0 for rotation
                float centerX = (float)((rect.Left + rect.Right) / 2);
                float centerY = (float)((rect.Top + rect.Bottom) / 2);

                // Now move each of the 4 bounding corners to the origin
                Vector2 topLeft = new Vector2(rect.Left - centerX, rect.Top - centerY);
                Vector2 topRight = new Vector2(rect.Right - centerX, rect.Top - centerY);
                Vector2 bottomRight = new Vector2(rect.Right - centerX, rect.Bottom - centerY);
                Vector2 bottomLeft = new Vector2(rect.Left - centerX, rect.Bottom - centerY);

                // Create a standard rotation around Z ( into the screen when dealing with 2d math )
                Matrix rotMatrix = Matrix.CreateRotationZ(_currentSpriteRotation);

                // Now rotate each point
                topLeft = Vector2.Transform(topLeft, rotMatrix);
                topRight = Vector2.Transform(topRight, rotMatrix);
                bottomLeft = Vector2.Transform(bottomLeft, rotMatrix);
                bottomRight = Vector2.Transform(bottomRight, rotMatrix);

                // Position means nothing now, bounding box is based off the biggest and smallest possible X,Y coords
                // Find the tightest box that fits
                Vector2[] verts = new Vector2[4];
                verts[0] = topLeft; verts[1] = topRight; verts[2] = bottomLeft; verts[3] = bottomRight;

                float smallestX, smallestY, biggestX, biggestY;
                
                // set the default values to insanely stupid defaults
                smallestX = smallestY = 10000;
                biggestX = biggestY = -10000;

                // loop throw all vertex info finding the outer limits ( oooohhwooowooo, do not attempt to adjust you're picture, we control t.....)
                foreach (Vector2 cur in verts)
                {
                    if (cur.X > biggestX)
                        biggestX = cur.X;
                    if (cur.Y > biggestY)
                        biggestY = cur.Y;
                    if (cur.X < smallestX)
                        smallestX = cur.X;
                    if (cur.Y < smallestY)
                        smallestY = cur.Y;
                }

                // now return the resulting bounding box translating X and Y back into realspace ( aka... not the origin )
                return new Rectangle((int)(smallestX + centerX), (int)(smallestY + centerY), (int)((biggestX - smallestX)), (int)((biggestY - smallestY)));
            }
        }

Share this post


Link to post
Share on other sites
I'm not a huge fan of the loop for a known (small) number of vertices, and the arbitrary initial values tickle me the wrong way as well. You might try something like this:

smallestX = Math.Min(topLeft.X, Math.Min(topRight.X, Math.Min(bottomLeft.X, bottomRight.X)));
smallestY = Math.Min(topLeft.Y, Math.Min(topRight.Y, Math.Min(bottomLeft.Y, bottomRight.Y)));
biggestX = Math.Max(topLeft.X, Math.Max(topRight.X, Math.Max(bottomLeft.X, bottomRight.X)));
biggestY = Math.Max(topLeft.Y, Math.Max(topRight.Y, Math.Max(bottomLeft.Y, bottomRight.Y)));

There might also be ways of determining the new boundaries without requiring any more math based on the rotation value. For instance, for rotations between 0° - 90°, topLeft.X will remain the minimum X, topRight.Y will remain the minimum Y, bottomRight.X will remain the maximum X, and bottomLeft.Y will remain the maximum Y. This relationship changes for the 90° - 180° rotation range and so on for each quadrant. However I don't think this is something you want to spend time explaining to your reader.

Share this post


Link to post
Share on other sites
If you need AABBs, use Zipster's solution. If you need OBBs, there has to be a rotation transformation, for eg. a matrix, for the sprites. I'd use that to transform the box's corners too. You can make this geneirc by storing a translation and a rotation matrix per sprite. If you need AABB you transform the box with the translation, and if you need an OBB you transform it with the translation than the rotation. Hope this helps.

Share this post


Link to post
Share on other sites
Quote:
Original post by Zipster
I'm not a huge fan of the loop for a known (small) number of vertices, and the arbitrary initial values tickle me the wrong way as well. You might try something like this:

smallestX = Math.Min(topLeft.X, Math.Min(topRight.X, Math.Min(bottomLeft.X, bottomRight.X)));
smallestY = Math.Min(topLeft.Y, Math.Min(topRight.Y, Math.Min(bottomLeft.Y, bottomRight.Y)));
biggestX = Math.Max(topLeft.X, Math.Max(topRight.X, Math.Max(bottomLeft.X, bottomRight.X)));
biggestY = Math.Max(topLeft.Y, Math.Max(topRight.Y, Math.Max(bottomLeft.Y, bottomRight.Y)));

There might also be ways of determining the new boundaries without requiring any more math based on the rotation value. For instance, for rotations between 0° - 90°, topLeft.X will remain the minimum X, topRight.Y will remain the minimum Y, bottomRight.X will remain the maximum X, and bottomLeft.Y will remain the maximum Y. This relationship changes for the 90° - 180° rotation range and so on for each quadrant. However I don't think this is something you want to spend time explaining to your reader.


Perfect, thanks for the suggestion. I really felt kind of perverse for using a loop and your suggestion is much better and probrably easier to read.

I considered going to 4 / 90 degree quadrant route, but when I started down that road it turned into a mess of almost unreadable code. I think im willing to sacrafice the performance hit for readability in this case.

Again, thanks alot.

Share this post


Link to post
Share on other sites

This topic is 4091 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this