// // Distributed under the BSD Licence (see LICENCE file). // // Copyright (c) 2014, Nition, http://www.momentstudio.co.nz/ // Copyright (c) 2017, Máté Cserép, http://codenet.hu // All rights reserved. // namespace Octree { using System; using System.Runtime.Serialization; /// /// Represents an axis aligned bounding box (AABB). /// /// /// This class was inspired by the Bounds type of the Unity Engine and /// designed with the exact same interface to provide maximum compatibility. /// public struct BoundingBox { Point _min; Point _max; Point _center; Point _extents; /// /// Gets or sets the center of the bounding box. /// public Point Center { get { return _center; } set { _center = value; UpdateMinMaxFromCenterOrExtents(); } } /// /// Gets or sets the extents of the bounding box. This is always half of the . /// public Point Extents { get { return _extents; } set { _extents = value; UpdateMinMaxFromCenterOrExtents(); } } /// /// Gets or sets the size of the bounding box. This is always twice as large as the . /// public Point Size { get { return Extents * 2; } set { Extents = value * 0.5f; } } /// /// Gets or sets the minimal point of the box. /// /// /// This is always equal to center-extents. /// public Point Min { get { return _min; } set { SetMinMax(value, _max); } } /// /// Gets or sets the maximal point of the box. /// /// /// This is always equal to center+extents. /// public Point Max { get { return _max; } set { SetMinMax(_min, value); } } private void UpdateMinMaxFromCenterOrExtents() { SetMinMax(Center - Extents, Center + Extents); } /// /// Creates a new bounding box. /// /// The center of the box. /// The size of the box. public BoundingBox(Point center, Point size) { _center = center; _extents = size * 0.5f; _min = _max = new Point(); UpdateMinMaxFromCenterOrExtents(); } /// /// Sets the bounds to the min and max value of the box. /// /// The minimal point. /// The maximal point. public void SetMinMax(Point min, Point max) { _min = min; _max = max; _extents = (_max - _min) * 0.5f; _center = _min + _extents; } /// /// Grows the bounding box include the point. /// /// The specified point to include. public void Encapsulate(Point point) { SetMinMax(Point.Min(Min, point), Point.Max(Max, point)); } /// /// Grows the bounding box include the other box. /// /// The specified box to include. public void Encapsulate(BoundingBox box) { Encapsulate(box.Center - box.Extents); Encapsulate(box.Center + box.Extents); } /// /// Expands the bounds by increasing its by along each side. /// /// The expansions for each dimension. public void Expand(double amount) { amount *= 0.5f; Extents += new Point(amount, amount, amount); } /// /// Expands the bounds by increasing its by along each side. /// /// The expansions for each dimension in order. public void Expand(Point amount) { Extents += amount * 0.5f; } /// /// Determines whether the box contains the point. /// /// The point to test. /// true if the box contains the point; otherwise, false. public bool Contains(Point point) { return Min.X <= point.X && Max.X >= point.X && Min.Y <= point.Y && Max.Y >= point.Y && Min.Z <= point.Z && Max.Z >= point.Z; } /// /// Determines whether the bounding box intersects with another box. /// /// The box to test. /// true if the bounding box intersects with another box, false otherwise. public bool Intersects(BoundingBox box) { return Min.X <= box.Max.X && Max.X >= box.Min.X && Min.Y <= box.Max.Y && Max.Y >= box.Min.Y && Min.Z <= box.Max.Z && Max.Z >= box.Min.Z; } /// /// Determines whether the bounding box intersects with a ray. /// /// The ray to test. /// true if the box intersects with the ray, false otherwise. public bool IntersectRay(Ray ray) { double distance; return IntersectRay(ray, out distance); } /// /// Determines whether the bounding box intersects with a ray. /// /// The ray to test. /// The calculated distance from the origin of the ray to the box along the ray. /// true if the box intersects with the ray, false otherwise. public bool IntersectRay(Ray ray, out double distance) { Point dirFrac = new Point( 1f / ray.Direction.X, 1f / ray.Direction.Y, 1f / ray.Direction.Z ); double t1 = (Min.X - ray.Origin.X) * dirFrac.X; double t2 = (Max.X - ray.Origin.X) * dirFrac.X; double t3 = (Min.Y - ray.Origin.Y) * dirFrac.Y; double t4 = (Max.Y - ray.Origin.Y) * dirFrac.Y; double t5 = (Min.Z - ray.Origin.Z) * dirFrac.Z; double t6 = (Max.Z - ray.Origin.Z) * dirFrac.Z; double tmin = Math.Max(Math.Max(Math.Min(t1, t2), Math.Min(t3, t4)), Math.Min(t5, t6)); double tmax = Math.Min(Math.Min(Math.Max(t1, t2), Math.Max(t3, t4)), Math.Max(t5, t6)); // if tmax < 0, ray (line) is intersecting AABB, but the whole AABB is behind us if (tmax < 0) { distance = tmax; return false; } // if tmin > tmax, ray doesn't intersect AABB if (tmin > tmax) { distance = tmax; return false; } distance = tmin; return true; } /// /// Determines whether the bounding box intersects with a plane. /// /// The plane to test. /// The minimum distance from the bounding box to the plane . /// true if the box intersects with the plane, false otherwise. public bool IntersectPlane(Plane plane, out double distance) { // https://gdbooks.gitbooks.io/3dcollisions/content/Chapter2/static_aabb_plane.html // Compute the projection interval radius of b onto L(t) = b.c + t * p.n double r = Extents.X * Math.Abs(plane.Normal.X) + Extents.Y * Math.Abs(plane.Normal.Y) + Extents.Z * Math.Abs(plane.Normal.Z); // Compute distance of box center from plane double s = Point.Dot(plane.Normal, Center) + plane.D; // Intersection occurs when distance s falls within [-r,+r] interval if (Math.Abs(s) <= r) { distance = 0; return true; } else { if (s > 0) distance = s - r; else distance = s + r; return false; } } /// /// Returns a hash code for this instance. /// /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. public override int GetHashCode() { return Center.GetHashCode() ^ Extents.GetHashCode() << 2; } /// /// Determines whether the specified object as a is equal to this instance. /// /// The object to compare with this instance. /// true if the specified box is equal to this instance; otherwise, false. public override bool Equals(object other) { bool result; if (!(other is BoundingBox)) { result = false; } else { BoundingBox box = (BoundingBox)other; result = (Center.Equals(box.Center) && Extents.Equals(box.Extents)); } return result; } /// /// Returns a nicely formatted string for this bounding box. /// public override string ToString() { return String.Format("Center: {0}, Extents: {1}", Center, Extents ); } /// /// Returns a nicely formatted string for this bounding box. /// /// The format for the center and the extent. public string ToString(string format) { return String.Format("Center: {0}, Extents: {1}", Center.ToString(format), Extents.ToString(format) ); } /// /// Determines whether two bounding boxes are equal. /// /// The first box. /// The second box. public static bool operator ==(BoundingBox lhs, BoundingBox rhs) { return lhs.Center == rhs.Center && lhs.Extents == rhs.Extents; } /// /// Determines whether two bounding boxes are different. /// /// The first box. /// The second box. public static bool operator !=(BoundingBox lhs, BoundingBox rhs) { return !(lhs == rhs); } } }