From 1304499b664ec633e1647bcd5f1c25cc450fd2dc Mon Sep 17 00:00:00 2001 From: Tiberiu Chibici Date: Fri, 29 May 2015 10:48:20 +0300 Subject: [PATCH] Implemented quad tree, added test project. --- Game/Assets/Scripts/Model/IPositionable.cs | 12 + .../Scripts/Model/IPositionable.cs.meta | 12 + Game/Assets/Scripts/Model/Point.cs | 23 -- Game/Assets/Scripts/Utils/QuadTree.cs | 290 ++++++++++++++++++ Game/Assets/Scripts/Utils/QuadTree.cs.meta | 12 + Tools/Test/Test.sln | 28 ++ Tools/Test/Test/App.config | 6 + Tools/Test/Test/Program.cs | 18 ++ Tools/Test/Test/Properties/AssemblyInfo.cs | 36 +++ Tools/Test/Test/Test.csproj | 64 ++++ 10 files changed, 478 insertions(+), 23 deletions(-) create mode 100644 Game/Assets/Scripts/Model/IPositionable.cs create mode 100644 Game/Assets/Scripts/Model/IPositionable.cs.meta delete mode 100644 Game/Assets/Scripts/Model/Point.cs create mode 100644 Game/Assets/Scripts/Utils/QuadTree.cs create mode 100644 Game/Assets/Scripts/Utils/QuadTree.cs.meta create mode 100644 Tools/Test/Test.sln create mode 100644 Tools/Test/Test/App.config create mode 100644 Tools/Test/Test/Program.cs create mode 100644 Tools/Test/Test/Properties/AssemblyInfo.cs create mode 100644 Tools/Test/Test/Test.csproj diff --git a/Game/Assets/Scripts/Model/IPositionable.cs b/Game/Assets/Scripts/Model/IPositionable.cs new file mode 100644 index 0000000..18b684c --- /dev/null +++ b/Game/Assets/Scripts/Model/IPositionable.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TransportGame.Model +{ + public interface IPositionable + { + Vector2 Position { get; } + } +} diff --git a/Game/Assets/Scripts/Model/IPositionable.cs.meta b/Game/Assets/Scripts/Model/IPositionable.cs.meta new file mode 100644 index 0000000..a28da09 --- /dev/null +++ b/Game/Assets/Scripts/Model/IPositionable.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: cdecfb69b7ca68446910d0a607e934e6 +timeCreated: 1432824088 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Game/Assets/Scripts/Model/Point.cs b/Game/Assets/Scripts/Model/Point.cs deleted file mode 100644 index 082326f..0000000 --- a/Game/Assets/Scripts/Model/Point.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace TransportGame.Model -{ - public class Point - { - public int X { get; set; } - public int Y { get; set; } - - public Point() - { - } - - public Point(int x, int y) - { - X = x; - Y = y; - } - } -} diff --git a/Game/Assets/Scripts/Utils/QuadTree.cs b/Game/Assets/Scripts/Utils/QuadTree.cs new file mode 100644 index 0000000..642ee67 --- /dev/null +++ b/Game/Assets/Scripts/Utils/QuadTree.cs @@ -0,0 +1,290 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using TransportGame.Model; + +namespace TransportGame.Utils +{ + public class QuadTree: ICollection where T : IPositionable + { + private const int Capacity = 8; + + /// + /// Gets the subtree in northwest position + /// + public QuadTree NorthWest { get; private set; } + + /// + /// Gets the subtree in northeast position + /// + public QuadTree NorthEast { get; private set; } + + /// + /// Gets the subtree in southeast position + /// + public QuadTree SouthEast { get; private set; } + + /// + /// Gets the subtree in southwest position + /// + public QuadTree SouthWest { get; private set; } + + /// + /// Gets the boundary of this quad tree + /// + public Rectangle Boundary { get; private set; } + + private List points = new List(Capacity); + + /// + /// Initializes a quad tree using specified boundaries + /// + /// Left + /// Top + /// Right + /// Bottom + public QuadTree(float left, float top, float right, float bottom) + { + Boundary = new Rectangle(left, top, right, bottom); + } + + /// + /// Initializes a quad tree using specified boundaries + /// + /// Boundaries + public QuadTree(Rectangle boundary) + { + Boundary = boundary; + } + + private void Subdivide() + { + float midx = Boundary.Left + Boundary.Width / 2f; + float midy = Boundary.Top + Boundary.Height / 2f; + + NorthWest = new QuadTree(Boundary.Left, Boundary.Top, midx, midy); + NorthEast = new QuadTree(midx, Boundary.Top, Boundary.Right, midy); + SouthEast = new QuadTree(midx, midy, Boundary.Right, Boundary.Bottom); + SouthWest = new QuadTree(Boundary.Left, midy, midx, Boundary.Bottom); + + foreach (var point in points) + Add(point); + + points.Clear(); + } + + private void Merge() + { + foreach (var point in NorthWest) + points.Add(point); + + foreach (var point in NorthEast) + points.Add(point); + + foreach (var point in SouthEast) + points.Add(point); + + foreach (var point in SouthWest) + points.Add(point); + + NorthWest = null; + NorthEast = null; + SouthEast = null; + SouthWest = null; + } + + /// + /// Adds a point in this quad tree + /// + /// Point + public void Add(T item) + { + // Precondition - point must be inside boundaries + if (!Boundary.Contains(item.Position)) + throw new ArgumentException("Point must be inside boundaries."); + + // Reached capacity, subdivide + if (NorthWest == null && points.Count >= Capacity) + Subdivide(); + + // Not divided in subtrees + if (NorthWest == null) + { + if (!points.Any(p => p.Position.Equals(item.Position))) + points.Add(item); + } + + // Add in the right subtree + else + { + if (NorthWest.Boundary.Contains(item.Position)) + NorthWest.Add(item); + + else if (NorthEast.Boundary.Contains(item.Position)) + NorthEast.Add(item); + + else if (SouthEast.Boundary.Contains(item.Position)) + SouthEast.Add(item); + + else SouthWest.Add(item); + } + } + + /// + /// Empties the entire tree + /// + public void Clear() + { + NorthWest = null; + NorthEast = null; + SouthEast = null; + SouthWest = null; + points.Clear(); + } + + /// + /// Tests if specified point is contained in this tree + /// + /// Point + /// True if point is contained + public bool Contains(T item) + { + if (NorthWest == null) + return points.Contains(item); + else + return NorthWest.Contains(item) || NorthEast.Contains(item) || SouthEast.Contains(item) || SouthWest.Contains(item); + } + + /// + /// Copies all points to specified array + /// + /// Array + /// Index where to start copying + public void CopyTo(T[] array, int arrayIndex) + { + foreach (var point in this) + array[arrayIndex++] = point; + } + + /// + /// Gets the number of points in this quad tree + /// + public int Count + { + get + { + return (NorthWest == null) ? points.Count : NorthWest.Count + NorthEast.Count + SouthEast.Count + SouthWest.Count; + } + } + + /// + /// Returns true if this container is read-only. + /// + public bool IsReadOnly + { + get + { + return false; + } + } + + /// + /// Removes an item from the tree + /// + /// Item to remove + /// True if item was removed + public bool Remove(T item) + { + if (NorthWest == null) + return points.Remove(item); + + else + { + bool result = NorthWest.Remove(item) || NorthEast.Remove(item) || SouthEast.Remove(item) || SouthWest.Remove(item); + + // We can merge subdivisions + if (Count < (Capacity - Capacity / 3)) + Merge(); + + return result; + } + } + + /// + /// Gets an enumerator for this collection + /// + /// + System.Collections.IEnumerator IEnumerable.GetEnumerator() + { + foreach (var point in this) + yield return point; + } + + /// + /// Gets enumerator for this collection + /// + /// + public IEnumerator GetEnumerator() + { + if (NorthWest == null) + { + foreach (var point in points) + yield return point; + } + + else + { + foreach (var point in NorthWest) + yield return point; + + foreach (var point in NorthEast) + yield return point; + + foreach (var point in SouthEast) + yield return point; + + foreach (var point in SouthWest) + yield return point; + } + } + + /// + /// Gets all the points in the specified region + /// + /// Region + /// Points + public IEnumerable Query(Rectangle rect) + { + // No intersection + if (!Rectangle.Intersects(rect, Boundary)) + return Enumerable.Empty(); + + if (NorthWest == null) + { + return points.Where(p => rect.Contains(p.Position)); + } + else + { + return NorthWest.Query(rect) + .Concat(NorthEast.Query(rect)) + .Concat(SouthEast.Query(rect)) + .Concat(SouthWest.Query(rect)); + } + } + + /// + /// Gets all the points in the specified region + /// + /// Left + /// Top + /// Right + /// Bottom + /// Points + public IEnumerable Query(float left, float top, float right, float bottom) + { + return Query(new Rectangle(left, top, right, bottom)); + } + } +} diff --git a/Game/Assets/Scripts/Utils/QuadTree.cs.meta b/Game/Assets/Scripts/Utils/QuadTree.cs.meta new file mode 100644 index 0000000..1d6cf98 --- /dev/null +++ b/Game/Assets/Scripts/Utils/QuadTree.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: cb5a7be6addc5fe41bfda46c611f4d6b +timeCreated: 1432824088 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tools/Test/Test.sln b/Tools/Test/Test.sln new file mode 100644 index 0000000..b698151 --- /dev/null +++ b/Tools/Test/Test.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{1A040CAD-758D-4574-A234-E0229D3C9AF9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityVS.Game.CSharp", "..\..\Game\UnityVS.Game.CSharp.csproj", "{02576F1D-BE9C-CFA7-763D-1EBF63B36977}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1A040CAD-758D-4574-A234-E0229D3C9AF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1A040CAD-758D-4574-A234-E0229D3C9AF9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1A040CAD-758D-4574-A234-E0229D3C9AF9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1A040CAD-758D-4574-A234-E0229D3C9AF9}.Release|Any CPU.Build.0 = Release|Any CPU + {02576F1D-BE9C-CFA7-763D-1EBF63B36977}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {02576F1D-BE9C-CFA7-763D-1EBF63B36977}.Debug|Any CPU.Build.0 = Debug|Any CPU + {02576F1D-BE9C-CFA7-763D-1EBF63B36977}.Release|Any CPU.ActiveCfg = Release|Any CPU + {02576F1D-BE9C-CFA7-763D-1EBF63B36977}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Tools/Test/Test/App.config b/Tools/Test/Test/App.config new file mode 100644 index 0000000..8e15646 --- /dev/null +++ b/Tools/Test/Test/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Tools/Test/Test/Program.cs b/Tools/Test/Test/Program.cs new file mode 100644 index 0000000..3c1ab54 --- /dev/null +++ b/Tools/Test/Test/Program.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TransportGame.Model; +using TransportGame.Utils; + +namespace Test +{ + class Program + { + static void Main(string[] args) + { + // TODO: Tests go here + } + } +} diff --git a/Tools/Test/Test/Properties/AssemblyInfo.cs b/Tools/Test/Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8fe9c0b --- /dev/null +++ b/Tools/Test/Test/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Test")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("044c2cd3-18db-41ca-ac95-a2df6028b50d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Tools/Test/Test/Test.csproj b/Tools/Test/Test/Test.csproj new file mode 100644 index 0000000..c6532c8 --- /dev/null +++ b/Tools/Test/Test/Test.csproj @@ -0,0 +1,64 @@ + + + + + Debug + AnyCPU + {1A040CAD-758D-4574-A234-E0229D3C9AF9} + Exe + Properties + Test + Test + v4.5 + 512 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + {02576f1d-be9c-cfa7-763d-1ebf63b36977} + UnityVS.Game.CSharp + + + + + \ No newline at end of file