Debug Rectangle Class

Code

    /// <summary>
    ///4------1
    ///| \    |\
    ///| 3----|-2
    ///| |    | |
    ///8-|----5 |
    /// \|     \|
    ///  7------6
    ///
    /// Note that the 1 is the maximum point on the rectangle and 7 is the minimum point on the rectangle. These will
    /// correspond to the minimum and maximum points from the bounds.
    /// </summary
 
    class Rectangle
    {
 
        protected static readonly Vector3 DEFAULT_SCALE = new Vector3 (1, 1, 1);
        protected Vector3 p1, p2, p3, p4, p5, p6, p7, p8;
 
        /// <summary>
        ///
        /// Calculates a Rectangle base on the parameters passed in. The rectangle will fully describe the bounds in world
        /// space coordinates.
        /// </summary>
        /// <returns>
        /// A Rectangle that represents the bounds.
        /// </returns>
        /// <param name='transform'>
        /// The transform of the thing being bounded.
        /// </param>
        /// <param name='bounds'>
        /// The bounds of the thing. Collider and renderer bounds are measured in world space. Mesh bounds are local space.
        /// </param>
        /// <param name='boundsAreWorld'>
        /// Bounds are measured in world space or not. There is no way to determine this otherwise.
        /// </param>>
        /// <returns>
        /// The locally oriented instance.
        /// </returns>
 
        public static Rectangle CreateBoundingRectangle (Transform transform, Bounds bounds, bool boundsAreWorld)
        {
            Rectangle rectangle = new Rectangle ();
            Quaternion orientation = Quaternion.identity;
            Vector3 scale = DEFAULT_SCALE;
            Vector3 center = transform.position;
            Vector3 localBoundsMax = Vector3.zero;
            Vector3 localBoundsMin = Vector3.zero;
            Vector3 up = Vector3.up;
 
            if (boundsAreWorld)
            {
                localBoundsMax = bounds.max - transform.position;
                localBoundsMin = bounds.min - transform.position;
 
                rectangle.p1 = bounds.max;
                rectangle.p7 = bounds.min;
            } else
            {
                scale = transform.lossyScale;
                up = transform.up;
                localBoundsMax = transform.rotation * Vector3.Scale (bounds.max, scale);
                localBoundsMin = transform.rotation * Vector3.Scale (bounds.min, scale);
                rectangle.p1 = transform.position + localBoundsMax;
                rectangle.p7 = transform.position + localBoundsMin;                
            }
 
            //The angle created between the center line and a line is drawn from the center to the max point
            //(Unity reports these bounds as unit so they must be scaled.)
            float xZTheta = Mathf.Atan2 (bounds.extents.z * scale.z, bounds.extents.x * scale.x) * Mathf.Rad2Deg;
            float doubleTheta = 2f * xZTheta;
 
//            print ("************");
//            print ("Center       :" + center);
//            print ("Euler rot.   :" + orientation.eulerAngles);
//            print ("Up           :" + up);
//            print ("World Bounds :" + (boundsAreWorld ? "yes" : "no"));
//            print ("Bounds       :" + bounds);
//            print ("Points       :" + bounds.min + " / " + bounds.max + " :: " + localBoundsMin + " / " + localBoundsMax);
//            print ("X-Z theta    :" + xZTheta);
//            print ("Orientation  :" + orientation.eulerAngles);
//            print ("Scale        : lossy: " + transform.lossyScale + " local: " + transform.localScale + " using: " + scale);
//            print ("************");
 
            orientation = Quaternion.AngleAxis (doubleTheta, up) * orientation;
            rectangle.p2 = center + (orientation * localBoundsMax);
            rectangle.p8 = center + (orientation * localBoundsMin);
            orientation = Quaternion.AngleAxis (180f - doubleTheta, up) * orientation;
            rectangle.p3 = center + (orientation * localBoundsMax);
            rectangle.p5 = center + (orientation * localBoundsMin);
            orientation = Quaternion.AngleAxis (doubleTheta, up) * orientation;
            rectangle.p4 = center + (orientation * localBoundsMax);
            rectangle.p6 = center + (orientation * localBoundsMin);
            return rectangle;
 
        }
 
        public static void DebugDraw (Rectangle rectangle, Color color)
        {
            UnityEngine.Debug.DrawLine (rectangle.p1, rectangle.p2, color);
            UnityEngine.Debug.DrawLine (rectangle.p2, rectangle.p3, color);
            UnityEngine.Debug.DrawLine (rectangle.p3, rectangle.p4, color);
            UnityEngine.Debug.DrawLine (rectangle.p4, rectangle.p1, color);
            UnityEngine.Debug.DrawLine (rectangle.p5, rectangle.p6, color);
            UnityEngine.Debug.DrawLine (rectangle.p6, rectangle.p7, color);
            UnityEngine.Debug.DrawLine (rectangle.p7, rectangle.p8, color);
            UnityEngine.Debug.DrawLine (rectangle.p8, rectangle.p5, color);         
            UnityEngine.Debug.DrawLine (rectangle.p1, rectangle.p5, color);
            UnityEngine.Debug.DrawLine (rectangle.p2, rectangle.p6, color);
            UnityEngine.Debug.DrawLine (rectangle.p3, rectangle.p7, color);
            UnityEngine.Debug.DrawLine (rectangle.p4, rectangle.p8, color);
        }
 
        public override string ToString ()
        {
            StringBuilder sb = new StringBuilder ();
 
            sb.Append ("P1 - ").Append (p1.ToString ()).Append ("\n");
            sb.Append ("P2 - ").Append (p2.ToString ()).Append ("\n");
            sb.Append ("P3 - ").Append (p3.ToString ()).Append ("\n");
            sb.Append ("P4 - ").Append (p4.ToString ()).Append ("\n");
            sb.Append ("P5 - ").Append (p5.ToString ()).Append ("\n");
            sb.Append ("P6 - ").Append (p6.ToString ()).Append ("\n");
            sb.Append ("P7 - ").Append (p7.ToString ()).Append ("\n");
            sb.Append ("P8 - ").Append (p8.ToString ()).Append ("\n");
 
            return sb.ToString ();
 
        }
 
    }

Commentary

This is a class that I wrote when exploring the ways in which colliders, renderers and bounds acted under certain circumstances. It creates a real rectangle represented by by 8 points. It works by mathematically spinning the box represented by bounds around its axis at orthogonal steps and taking note of where the corners are. It's just a few quaternion calculations so it's pretty fast.

The problem with Unity's bounds (other than the standard issues) is that they represent bounds with two points. So unless you know the source of the bounds there are always two parallelograms which the bounds could represent. This gives me a clear visual feedback with gizmos what the bounds are and how they are being placed.

Snippet Usage

Unless otherwise stated, the content of this page is licensed under GNU Free Documentation License.