/Utilities/WCell.Util/Graphics/BoundingBox.cs

https://github.com/ray2006/WCell · C# · 433 lines · 323 code · 34 blank · 76 comment · 75 complexity · 12c6adf1159ea028cc0f8a0584647098 MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Runtime.InteropServices;
  6. namespace WCell.Util.Graphics
  7. {
  8. /// <summary>
  9. /// Defines an axis-aligned bounding box.
  10. /// </summary>
  11. [StructLayout(LayoutKind.Explicit, Size = 24)]
  12. public struct BoundingBox : IEquatable<BoundingBox>
  13. {
  14. public static BoundingBox INVALID = new BoundingBox(new Vector3(float.MaxValue, float.MaxValue, float.MaxValue),
  15. new Vector3(float.MaxValue, float.MaxValue, float.MaxValue));
  16. /// <summary>
  17. /// The lower-left bound of the box.
  18. /// </summary>
  19. [FieldOffset(0)]
  20. public Vector3 Min;
  21. /// <summary>
  22. /// The upper-right bound of the box.
  23. /// </summary>
  24. [FieldOffset(12)]
  25. public Vector3 Max;
  26. /// <summary>
  27. /// Creates a new <see cref="BoundingBox" /> with the given coordinates.
  28. /// </summary>
  29. /// <param name="minX">lower-bound X</param>
  30. /// <param name="minY">lower-bound Y</param>
  31. /// <param name="minZ">lower-bound Z</param>
  32. /// <param name="maxX">upper-bound X</param>
  33. /// <param name="maxY">upper-bound Y</param>
  34. /// <param name="maxZ">upper-bound Z</param>
  35. public BoundingBox(float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
  36. {
  37. Min = new Vector3(minX, minY, minZ);
  38. Max = new Vector3(maxX, maxY, maxZ);
  39. }
  40. /// <summary>
  41. /// Creates a new <see cref="BoundingBox" /> with the given coordinates.
  42. /// </summary>
  43. /// <param name="minX">lower-bound X</param>
  44. /// <param name="minY">lower-bound Y</param>
  45. /// <param name="minZ">lower-bound Z</param>
  46. /// <param name="max">upper-bound vector</param>
  47. public BoundingBox(float minX, float minY, float minZ, Vector3 max)
  48. {
  49. Min = new Vector3(minX, minY, minZ);
  50. Max = max;
  51. }
  52. /// <summary>
  53. /// Creates a new <see cref="BoundingBox" /> with the given coordinates.
  54. /// </summary>
  55. /// <param name="min">lower-bound vector</param>
  56. /// <param name="maxX">upper-bound X</param>
  57. /// <param name="maxY">upper-bound Y</param>
  58. /// <param name="maxZ">upper-bound Z</param>
  59. public BoundingBox(Vector3 min, float maxX, float maxY, float maxZ)
  60. {
  61. Min = min;
  62. Max = new Vector3(maxX, maxY, maxZ);
  63. }
  64. /// <summary>
  65. /// Creates a new <see cref="BoundingBox" /> with the given coordinates.
  66. /// </summary>
  67. /// <param name="min">lower-bound vector</param>
  68. /// <param name="max">upper-bound vector</param>
  69. public BoundingBox(Vector3 min, Vector3 max)
  70. {
  71. Min = min;
  72. Max = max;
  73. }
  74. /// <summary>
  75. /// Creates a new <see cref="BoundingBox" /> containing the given vectors.
  76. /// </summary>
  77. /// <param name="vectors">The array of vectors to use.</param>
  78. public BoundingBox(Vector3[] vectors)
  79. {
  80. var minX = float.MaxValue;
  81. var minY = float.MaxValue;
  82. var minZ = float.MaxValue;
  83. var maxX = float.MinValue;
  84. var maxY = float.MinValue;
  85. var maxZ = float.MinValue;
  86. for (var i = 0; i < vectors.Length; i++)
  87. {
  88. var vector = vectors[i];
  89. minX = Math.Min(vector.X, minX);
  90. maxX = Math.Max(vector.X, maxX);
  91. minY = Math.Min(vector.Y, minY);
  92. maxY = Math.Max(vector.Y, maxY);
  93. minZ = Math.Min(vector.Z, minZ);
  94. maxZ = Math.Max(vector.Z, maxZ);
  95. }
  96. Min = new Vector3(minX, minY, minZ);
  97. Max = new Vector3(maxX, maxY, maxZ);
  98. }
  99. public float Width
  100. {
  101. get { return Max.X - Min.X; }
  102. }
  103. public float Height
  104. {
  105. get { return Max.Y - Min.Y; }
  106. }
  107. /// <summary>
  108. /// Checks whether the current <see cref="BoundingBox" /> intersects with the given <see cref="BoundingBox" />.
  109. /// </summary>
  110. /// <param name="box">the <see cref="BoundingBox" /> to check for intersection</param>
  111. /// <returns>an enumeration value describing the type of intersection between the two boxes</returns>
  112. public IntersectionType Intersects(ref BoundingBox box)
  113. {
  114. if ((Max.X < box.Min.X) || (Min.X > box.Max.X))
  115. {
  116. return IntersectionType.NoIntersection;
  117. }
  118. if ((Max.Y < box.Min.Y) || (Min.Y > box.Max.Y))
  119. {
  120. return IntersectionType.NoIntersection;
  121. }
  122. if ((Max.Z < box.Min.Z) || (Min.Z > box.Max.Z))
  123. {
  124. return IntersectionType.NoIntersection;
  125. }
  126. if ((((Min.X <= box.Min.X) && (box.Max.X <= Max.X))
  127. && ((Min.Y <= box.Min.Y) && (box.Max.Y <= Max.Y))
  128. && ((Min.Z <= box.Min.Z) && (box.Max.Z <= Max.Z))))
  129. {
  130. return IntersectionType.Contained;
  131. }
  132. return IntersectionType.Intersects;
  133. }
  134. /// <summary>
  135. /// Checks whether the current <see cref="BoundingBox" /> intersects with the given <see cref="BoundingSphere" />.
  136. /// </summary>
  137. /// <param name="sphere">the <see cref="BoundingSphere" /> to check for intersection</param>
  138. /// <returns>an enumeration value describing the type of intersection between the box and sphere</returns>
  139. public IntersectionType Intersects(ref BoundingSphere sphere)
  140. {
  141. Vector3 clampedCenter = sphere.Center.Clamp(ref Min, ref Max);
  142. float dist = sphere.Center.GetDistanceSquared(ref clampedCenter);
  143. float radius = sphere.Radius;
  144. if (dist > (radius * radius))
  145. {
  146. return IntersectionType.NoIntersection;
  147. }
  148. if (((((Min.X + radius) <= sphere.Center.X) &&
  149. (sphere.Center.X <= (Max.X - radius))) &&
  150. (((Max.X - Min.X) > radius) && ((Min.Y + radius) <= sphere.Center.Y))) &&
  151. (((sphere.Center.Y <= (Max.Y - radius)) && ((Max.Y - Min.Y) > radius)) &&
  152. ((((Min.Z + radius) <= sphere.Center.Z) && (sphere.Center.Z <= (Max.Z - radius))) &&
  153. (Max.X - Min.X) > radius)))
  154. {
  155. return IntersectionType.Contained;
  156. }
  157. return IntersectionType.Intersects;
  158. }
  159. /// <summary>
  160. /// Checks whether the <see cref="BoundingBox" /> contains the given <see cref="BoundingBox" />.
  161. /// </summary>
  162. /// <param name="box">the <see cref="BoundingBox" /> to check for containment.</param>
  163. /// <returns>true if the <see cref="BoundingBox" /> is contained; false otherwise</returns>
  164. public bool Contains(ref BoundingBox box)
  165. {
  166. return (box.Min.X > Min.X && box.Min.Y > Min.Y && box.Min.Z > Min.Z)
  167. && (box.Max.X < Max.X && box.Max.Y < Max.Y && box.Max.Z < Max.Z);
  168. }
  169. /// <summary>
  170. /// Checks whether the <see cref="BoundingBox" /> contains the given point.
  171. /// </summary>
  172. /// <param name="point">the point to check for containment.</param>
  173. /// <returns>true if the point is contained; false otherwise</returns>
  174. public bool Contains(ref Vector3 point)
  175. {
  176. return (Min.X <= point.X && point.X <= Max.X)
  177. && (Min.Y <= point.Y && point.Y <= Max.Y)
  178. && (Min.Z <= point.Z && point.Z <= Max.Z);
  179. }
  180. /// <summary>
  181. /// Checks whether the <see cref="BoundingBox" /> contains the given point.
  182. /// </summary>
  183. /// <param name="point">the point to check for containment.</param>
  184. /// <returns>true if the point is contained; false otherwise</returns>
  185. public bool Contains(ref Vector4 point)
  186. {
  187. return (Min.X <= point.X && point.X <= Max.X)
  188. && (Min.Y <= point.Y && point.Y <= Max.Y)
  189. && (Min.Z <= point.Z && point.Z <= Max.Z);
  190. }
  191. /// <summary>
  192. /// Checks equality of two boxes.
  193. /// </summary>
  194. /// <param name="other">the other box to compare with</param>
  195. /// <returns>true if both boxes are equal; false otherwise</returns>
  196. public bool Equals(BoundingBox other)
  197. {
  198. return ((Min == other.Min) && (Max == other.Max));
  199. }
  200. /// <summary>
  201. /// Checks equality with another object.
  202. /// </summary>
  203. /// <param name="obj">the object to compare</param>
  204. /// <returns>true if the object is <see cref="BoundingBox" /> and is equal; false otherwise</returns>
  205. public override bool Equals(object obj)
  206. {
  207. return obj is BoundingBox && Equals((BoundingBox) obj);
  208. }
  209. public override int GetHashCode()
  210. {
  211. return (Min.GetHashCode() + Max.GetHashCode());
  212. }
  213. public static bool operator ==(BoundingBox a, BoundingBox b)
  214. {
  215. return a.Equals(b);
  216. }
  217. public static bool operator !=(BoundingBox a, BoundingBox b)
  218. {
  219. if (!(a.Min != b.Min))
  220. {
  221. return (a.Max != b.Max);
  222. }
  223. return true;
  224. }
  225. public override string ToString()
  226. {
  227. return string.Format("(Min: {0}, Max: {1})", Min, Max);
  228. }
  229. public float? Intersects(Ray ray)
  230. {
  231. float num = 0f;
  232. float maxValue = float.MaxValue;
  233. if (Math.Abs(ray.Direction.X) < 1E-06f)
  234. {
  235. if ((ray.Position.X < this.Min.X) || (ray.Position.X > this.Max.X))
  236. {
  237. return null;
  238. }
  239. }
  240. else
  241. {
  242. float num11 = 1f / ray.Direction.X;
  243. float num8 = (this.Min.X - ray.Position.X) * num11;
  244. float num7 = (this.Max.X - ray.Position.X) * num11;
  245. if (num8 > num7)
  246. {
  247. float num14 = num8;
  248. num8 = num7;
  249. num7 = num14;
  250. }
  251. num = Math.Max(num8, num);
  252. maxValue = Math.Min(num7, maxValue);
  253. if (num > maxValue)
  254. {
  255. return null;
  256. }
  257. }
  258. if (Math.Abs(ray.Direction.Y) < 1E-06f)
  259. {
  260. if ((ray.Position.Y < this.Min.Y) || (ray.Position.Y > this.Max.Y))
  261. {
  262. return null;
  263. }
  264. }
  265. else
  266. {
  267. float num10 = 1f / ray.Direction.Y;
  268. float num6 = (this.Min.Y - ray.Position.Y) * num10;
  269. float num5 = (this.Max.Y - ray.Position.Y) * num10;
  270. if (num6 > num5)
  271. {
  272. float num13 = num6;
  273. num6 = num5;
  274. num5 = num13;
  275. }
  276. num = Math.Max(num6, num);
  277. maxValue = Math.Min(num5, maxValue);
  278. if (num > maxValue)
  279. {
  280. return null;
  281. }
  282. }
  283. if (Math.Abs(ray.Direction.Z) < 1E-06f)
  284. {
  285. if ((ray.Position.Z < this.Min.Z) || (ray.Position.Z > this.Max.Z))
  286. {
  287. return null;
  288. }
  289. }
  290. else
  291. {
  292. float num9 = 1f / ray.Direction.Z;
  293. float num4 = (this.Min.Z - ray.Position.Z) * num9;
  294. float num3 = (this.Max.Z - ray.Position.Z) * num9;
  295. if (num4 > num3)
  296. {
  297. float num12 = num4;
  298. num4 = num3;
  299. num3 = num12;
  300. }
  301. num = Math.Max(num4, num);
  302. maxValue = Math.Min(num3, maxValue);
  303. if (num > maxValue)
  304. {
  305. return null;
  306. }
  307. }
  308. return new float?(num);
  309. }
  310. public void Intersects(ref Ray ray, out float? result)
  311. {
  312. result = 0;
  313. float num = 0f;
  314. float maxValue = float.MaxValue;
  315. if (Math.Abs(ray.Direction.X) < 1E-06f)
  316. {
  317. if ((ray.Position.X < this.Min.X) || (ray.Position.X > this.Max.X))
  318. {
  319. return;
  320. }
  321. }
  322. else
  323. {
  324. float num11 = 1f / ray.Direction.X;
  325. float num8 = (this.Min.X - ray.Position.X) * num11;
  326. float num7 = (this.Max.X - ray.Position.X) * num11;
  327. if (num8 > num7)
  328. {
  329. float num14 = num8;
  330. num8 = num7;
  331. num7 = num14;
  332. }
  333. num = Math.Max(num8, num);
  334. maxValue = Math.Min(num7, maxValue);
  335. if (num > maxValue)
  336. {
  337. return;
  338. }
  339. }
  340. if (Math.Abs(ray.Direction.Y) < 1E-06f)
  341. {
  342. if ((ray.Position.Y < this.Min.Y) || (ray.Position.Y > this.Max.Y))
  343. {
  344. return;
  345. }
  346. }
  347. else
  348. {
  349. float num10 = 1f / ray.Direction.Y;
  350. float num6 = (this.Min.Y - ray.Position.Y) * num10;
  351. float num5 = (this.Max.Y - ray.Position.Y) * num10;
  352. if (num6 > num5)
  353. {
  354. float num13 = num6;
  355. num6 = num5;
  356. num5 = num13;
  357. }
  358. num = Math.Max(num6, num);
  359. maxValue = Math.Min(num5, maxValue);
  360. if (num > maxValue)
  361. {
  362. return;
  363. }
  364. }
  365. if (Math.Abs(ray.Direction.Z) < 1E-06f)
  366. {
  367. if ((ray.Position.Z < this.Min.Z) || (ray.Position.Z > this.Max.Z))
  368. {
  369. return;
  370. }
  371. }
  372. else
  373. {
  374. float num9 = 1f / ray.Direction.Z;
  375. float num4 = (this.Min.Z - ray.Position.Z) * num9;
  376. float num3 = (this.Max.Z - ray.Position.Z) * num9;
  377. if (num4 > num3)
  378. {
  379. float num12 = num4;
  380. num4 = num3;
  381. num3 = num12;
  382. }
  383. num = Math.Max(num4, num);
  384. maxValue = Math.Min(num3, maxValue);
  385. if (num > maxValue)
  386. {
  387. return;
  388. }
  389. }
  390. result = new float?(num);
  391. }
  392. public static BoundingBox Join(ref BoundingBox a, ref BoundingBox b)
  393. {
  394. var newMin = Vector3.Min(a.Min, b.Min);
  395. var newMax = Vector3.Max(a.Max, b.Max);
  396. return new BoundingBox(newMin, newMax);
  397. }
  398. }
  399. }