text: /*
  * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
  * its licensors.
  *
  * For complete copyright and license terms please see the LICENSE at the root of this
  * distribution (the "License"). All use of this software is governed by the License,
  * or, if provided, by the license below or the license accompanying this file. Do not
  * remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  *
  */
  
  #pragma once
  
  // include the required headers
  #include "StandardHeaders.h"
  #include "Vector.h"
  #include "Algorithms.h"
  
  
  namespace MCore
  {
      /**
       * An axis aligned bounding box (AABB).
       * This box is constructed out of two 3D points, a minimum and a maximum.
       * This means the box does not rotate, but always keeps aligned each axis.
       * Usage for this template could be for building a bounding volume around any given 3D object and
       * use it to accelerate ray tracing or visibility tests.
       */
      class AABB
      {
      public:
          /**
           * Default constructor. Initializes the min and max point to the extremes.
           * This means it's basically inside out at infinite size. So you are ready to start encapsulating points.
           */
          MCORE_INLINE AABB()                                                                     { Init(); }
  
          /**
           * Constructor which inits the minimum and maximum point of the box.
           * @param minPnt The minimum point.
           * @param maxPnt The maximum point.
           */
          MCORE_INLINE AABB(const AZ::Vector3& minPnt, const AZ::Vector3& maxPnt)
              : mMin(minPnt)
              , mMax(maxPnt) {}
  
          /**
           * Initialize the box minimum and maximum points.
           * Sets the points to floating point maximum and minimum. After calling this method you are ready to encapsulate points in it.
           * Note, that the default constructor already calls this method. So you should only call it when you want to 'reset' the minimum and
           * maximum points of the box.
           */
          MCORE_INLINE void Init()                                                                { mMin.Set(FLT_MAX, FLT_MAX, FLT_MAX); mMax.Set(-FLT_MAX, -FLT_MAX, -FLT_MAX); }
  
          /**
           * Check if this is a valid AABB or not.
           * The box is only valid if the minimum values are all smaller or equal than the maximum values.
           * @result True when the AABB is valid, otherwise false is returned.
           */
          MCORE_INLINE bool CheckIfIsValid() const
          {
              if (mMin.GetX() > mMax.GetX())
              {
                  return false;
              }
              if (mMin.GetY() > mMax.GetY())
              {
                  return false;
              }
              if (mMin.GetZ() > mMax.GetZ())
              {
                  return false;
              }
              return true;
          }
  
          /**
           * Encapsulate a point in the box.
           * This means that the box will automatically calculate the new minimum and maximum points when needed.
           * @param v The vector (3D point) to 'add' to the box.
           */
          MCORE_INLINE void Encapsulate(const AZ::Vector3& v);
  
          /**
           * Encapsulate another AABB with this box.
           * This method automatically adjusts the minimum and maximum point of the box after 'adding' the given AABB to this box.
           * @param box The AABB to 'add' to this box.
           */
          MCORE_INLINE void Encapsulate(const AABB& box)                                          { Encapsulate(box.mMin); Encapsulate(box.mMax); }
  
          /**
           * Widen the box in all dimensions with a given number of units.
           * @param delta The delta size in units to enlarge the box with. The delta value will be added to the maximum point as well as subtracted from the minimum point.
           */
          MCORE_INLINE void Widen(float delta)
          {
              mMin.SetX(mMin.GetX() - delta);
              mMin.SetY(mMin.GetY() - delta);
              mMin.SetZ(mMin.GetZ() - delta);
              mMax.SetX(mMax.GetX() + delta);
              mMax.SetY(mMax.GetY() + delta);
              mMax.SetZ(mMax.GetZ() + delta);
          }
  
          /**
           * Translates the box with a given offset vector.
           * This means the middle of the box will be moved by the given vector.
           * @param offset The offset vector to translate (move) the box with.
           */
          MCORE_INLINE void Translate(const AZ::Vector3& offset)                                   { mMin += offset; mMax += offset; }
  
          /**
           * Checks if a given point is inside this box or not.
           * Note that the edges/planes of the box are also counted as 'inside'.
           * @param v The vector (3D point) to perform the test with.
           * @result Returns true when the given point is inside the box, otherwise false is returned.
           */
          MCORE_INLINE bool Contains(const AZ::Vector3& v) const                                   { return (InRange(v.GetX(), mMin.GetX(), mMax.GetX()) && InRange(v.GetZ(), mMin.GetZ(), mMax.GetZ()) && InRange(v.GetY(), mMin.GetY(), mMax.GetY())); }
  
          /**
           * Checks if a given AABB is COMPLETELY inside this box or not.
           * Note that the edges/planes of this box are counted as 'inside'.
           * @param box The AABB to perform the test with.
           * @result Returns true when the AABB 'b' is COMPLETELY inside this box. If it's completely or partially outside, false will be returned.
           */
          MCORE_INLINE bool Contains(const AABB& box) const                                       { return (Contains(box.mMin) && Contains(box.mMax)); }
  
          /**
           * Checks if a given AABB partially or completely contains, so intersects, this box or not.
           * Please note that the edges of this box are counted as 'inside'.
           * @param box The AABB to perform the test with.
           * @result Returns true when the given AABB 'b' is completely or partially inside this box. Only false will be returned when the given AABB 'b' is COMPLETELY outside this box.
           */
          MCORE_INLINE bool Intersects(const AABB& box) const                                     { return !(mMin.GetX() > box.mMax.GetX() || mMax.GetX() < box.mMin.GetX() || mMin.GetY() > box.mMax.GetY() || mMax.GetY() < box.mMin.GetY() || mMin.GetZ() > box.mMax.GetZ() || mMax.GetZ() < box.mMin.GetZ()); }
  
          /**
           * Calculates and returns the width of the box.
           * The width is the distance between the minimum and maximum point, along the X-axis.
           * @result The width of the box.
           */
          MCORE_INLINE float CalcWidth() const                                                    { return mMax.GetX() - mMin.GetX(); }
  
          /**
           * Calculates and returns the height of the box.
           * The height is the distance between the minimum and maximum point, along the Z-axis.
           * @result The height of the box.
           */
          MCORE_INLINE float CalcHeight() const                                                   { return mMax.GetZ() - mMin.GetZ(); }
  
          /**
           * Calculates and returns the depth of the box.
           * The depth is the distance between the minimum and maximum point, along the Y-axis.
           * @result The depth of the box.
           */
          MCORE_INLINE float CalcDepth() const                                                    { return mMax.GetY() - mMin.GetY(); }
  
          /**
           * Calculate the volume of the box.
           * This equals width x height x depth.
           * @result The volume of the box.
           */
          MCORE_INLINE float CalcVolume() const                                                   { return (mMax.GetX() - mMin.GetX()) * (mMax.GetY() - mMin.GetY()) * (mMax.GetZ() - mMin.GetZ()); }
  
          /**
           * Calculate the surface area of the box.
           * @result The surface area.
           */
          MCORE_INLINE float CalcSurfaceArea() const
          {
              const float width = mMax.GetX() - mMin.GetX();
              const float height = mMax.GetY() - mMin.GetY();
              const float depth = mMax.GetZ() - mMin.GetZ();
              return (2.0f * height * width) + (2.0f * height * depth) + (2.0f * width * depth);
          }
  
          /**
           * Calculates the center/middle of the box.
           * This is simply done by taking the average of the minimum and maximum point along each axis.
           * @result The center (or middle) point of this box.
           */
          MCORE_INLINE AZ::Vector3 CalcMiddle() const                                             { return (mMin + mMax) * 0.5f; }
  
          /**
           * Calculates the extents of the box.
           * This is the vector from the center to a corner of the box.
           * @result The vector containing the extents.
           */
          MCORE_INLINE AZ::Vector3 CalcExtents() const                                            { return (mMax - mMin) * 0.5f; }
  
          /**
           * Calculates the radius of this box.
           * With radius we mean the length of the vector from the center of the box to the minimum or maximum point.
           * So if you would construct a sphere with as center the middle of this box and with a radius returned by this method, you will
           * get the minimum sphere which exactly contains this box.
           * @result The length of the center of the box to one of the extreme points. So the minimum radius of the bounding sphere containing this box.
           */
          MCORE_INLINE float CalcRadius() const                                                   { return SafeLength(mMax - mMin) * 0.5f; }
  
          /**
           * Get the minimum point of the box.
           * @result The minimum point of the box.
           */
          MCORE_INLINE const AZ::Vector3& GetMin() const                                              { return mMin; }
  
          /**
           * Get the maximum point of the box.
           * @result The maximum point of the box.
           */
          MCORE_INLINE const AZ::Vector3& GetMax() const                                              { return mMax; }
  
          /**
           * Set the minimum point of the box.
           * @param minVec The vector representing the minimum point of the box.
           */
          MCORE_INLINE void SetMin(const AZ::Vector3& minVec)                                         { mMin = minVec; }
  
          /**
           * Set the maximum point of the box.
           * @param maxVec The vector representing the maximum point of the box.
           */
          MCORE_INLINE void SetMax(const AZ::Vector3& maxVec)                                         { mMax = maxVec; }
  
          void CalcCornerPoints(AZ::Vector3* outPoints) const
          {
              outPoints[0].Set(mMin.GetX(), mMin.GetY(), mMax.GetZ());       //   4-------------5
              outPoints[1].Set(mMax.GetX(), mMin.GetY(), mMax.GetZ());       //  /|           / |
              outPoints[2].Set(mMax.GetX(), mMin.GetY(), mMin.GetZ());       // 0------------1  |
              outPoints[3].Set(mMin.GetX(), mMin.GetY(), mMin.GetZ());       // | |          |  |
              outPoints[4].Set(mMin.GetX(), mMax.GetY(), mMax.GetZ());       // | 7----------|--6
              outPoints[5].Set(mMax.GetX(), mMax.GetY(), mMax.GetZ());       // | /          | /
              outPoints[6].Set(mMax.GetX(), mMax.GetY(), mMin.GetZ());       // |/           |/
              outPoints[7].Set(mMin.GetX(), mMax.GetY(), mMin.GetZ());       // 3------------2
          }
  
  
      private:
          AZ::Vector3     mMin;   /**< The minimum point. */
          AZ::Vector3     mMax;   /**< The maximum point. */
      };
  
  
      // encapsulate a point in the box
      MCORE_INLINE void AABB::Encapsulate(const AZ::Vector3& v)
      {
          mMin.SetX(Min(mMin.GetX(), v.GetX()));
          mMin.SetY(Min(mMin.GetY(), v.GetY()));
          mMin.SetZ(Min(mMin.GetZ(), v.GetZ()));
  
          mMax.SetX(Max(mMax.GetX(), v.GetX()));
          mMax.SetY(Max(mMax.GetY(), v.GetY()));
          mMax.SetZ(Max(mMax.GetZ(), v.GetZ()));
      }
  }   // namespace MCore
  