using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Xna.Framework; using ABTest.SpacialHash; using System.Diagnostics; namespace ABTest.IntegerOct { public class BoundingBox { public Point3 Min; public Point3 Max; public int Width { get { return Max.X - Min.X; } } public int Height { get { return Max.Y - Min.Y; } } public int Depth { get { return Max.Z - Min.Z; } } public BoundingBox(Point3 Min, Point3 Max) { this.Min = Min; this.Max = Max; } public BoundingBox(Microsoft.Xna.Framework.BoundingBox Box) { this.Min = new Point3((int)Math.Floor(Box.Min.X), (int)Math.Floor(Box.Min.Y), (int)Math.Floor(Box.Min.Z)); this.Max = new Point3((int)Math.Ceiling(Box.Max.X), (int)Math.Ceiling(Box.Max.Y), (int)Math.Ceiling(Box.Max.Z)); } /// /// Inclusive at both boundaries /// /// /// public bool Contains(Point3 P) { return P.X >= Min.X && P.X <= Max.X && P.Y >= Min.Y && P.Y <= Max.Y && P.Z >= Min.Z && P.Z <= Max.Z; } public bool Intersects(BoundingBox Other) { if (Min.X > Other.Max.X || Max.X < Other.Min.X) return false; if (Min.Y > Other.Max.Y || Max.Y < Other.Min.Y) return false; if (Min.Z > Other.Max.Z || Max.Z < Other.Min.Z) return false; return true; } } public class IntegerOctTreeNode { public BoundingBox Bounds; public IntegerOctTreeNode[] Children; public List> Items = new List>(); private Point3 Mid; public IntegerOctTreeNode(Point3 Min, Point3 Max) { Bounds = new BoundingBox(Min, Max); Bounds.Max.X = Bounds.Min.X + NextPowerOfTwo(Bounds.Width); Bounds.Max.Y = Bounds.Min.Y + NextPowerOfTwo(Bounds.Height); Bounds.Max.Z = Bounds.Min.Z + NextPowerOfTwo(Bounds.Depth); Mid = new Point3(Min.X + Bounds.Width / 2, Min.Y + Bounds.Height / 2, Min.Z + Bounds.Depth / 2); Debug.Assert(NextPowerOfTwo(Mid.X - Min.X) == (Mid.X - Min.X)); } private int NextPowerOfTwo(int N) { var r = 1; while (r < N) r <<= 1; return r; } private void Subdivide() { var Min = Bounds.Min; var Max = Bounds.Max; Children = new IntegerOctTreeNode[8] { /*000*/ new IntegerOctTreeNode(new Point3(Min.X, Min.Y, Min.Z), new Point3(Mid.X, Mid.Y, Mid.Z)), /*001*/ new IntegerOctTreeNode(new Point3(Mid.X, Min.Y, Min.Z), new Point3(Max.X, Mid.Y, Mid.Z)), /*010*/ new IntegerOctTreeNode(new Point3(Min.X, Mid.Y, Min.Z), new Point3(Mid.X, Max.Y, Mid.Z)), /*011*/ new IntegerOctTreeNode(new Point3(Mid.X, Mid.Y, Min.Z), new Point3(Max.X, Max.Y, Mid.Z)), /*100*/ new IntegerOctTreeNode(new Point3(Min.X, Min.Y, Mid.Z), new Point3(Mid.X, Mid.Y, Max.Z)), /*101*/ new IntegerOctTreeNode(new Point3(Mid.X, Min.Y, Mid.Z), new Point3(Max.X, Mid.Y, Max.Z)), /*110*/ new IntegerOctTreeNode(new Point3(Min.X, Mid.Y, Mid.Z), new Point3(Mid.X, Max.Y, Max.Z)), /*111*/ new IntegerOctTreeNode(new Point3(Mid.X, Mid.Y, Mid.Z), new Point3(Max.X, Max.Y, Max.Z)) }; } private int Bin(Point3 P) { var x = (P.X < Mid.X) ? 0 : 1; var y = (P.Y < Mid.Y) ? 0 : 2; var z = (P.Z < Mid.Z) ? 0 : 4; return x + y + z; } public void AddItem(T Item, Point3 Point) { AddToTree(Tuple.Create(Item, Point), 8); } private void AddToTree(Tuple Item, int SubdivideThreshold) { if (!Bounds.Contains(Item.Item2)) return; if (Children != null) { Children[Bin(Item.Item2)].AddToTree(Item, SubdivideThreshold); } else if (Items.Count == SubdivideThreshold && Bounds.Width > 8) { Subdivide(); for (var i = 0; i < Items.Count; ++i) Children[Bin(Items[i].Item2)].AddToTree(Items[i], SubdivideThreshold); Children[Bin(Item.Item2)].AddToTree(Item, SubdivideThreshold); Items.Clear(); } else { Items.Add(Item); } } public void FindItemsInBox(BoundingBox SearchBounds, HashSet results) { if (SearchBounds.Intersects(Bounds)) { if (Children == null) { for (var i = 0; i < Items.Count; ++i) { var P = Items[i].Item2; if (P.X >= SearchBounds.Min.X && P.X <= SearchBounds.Max.X && P.Y >= SearchBounds.Min.Y && P.Y <= SearchBounds.Max.Y && P.Z >= SearchBounds.Min.Z && P.Z <= SearchBounds.Max.Z) results.Add(Items[i].Item1); } } else { for (var i = 0; i < 8; ++i) Children[i].FindItemsInBox(SearchBounds, results); } } } } }