Skip to content

Commit

Permalink
Add Json.NET serialization support
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobdufault committed Feb 11, 2014
1 parent 39a5333 commit d30270e
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 8 deletions.
3 changes: 3 additions & 0 deletions Forge.Collections/Forge.Collections.csproj
Expand Up @@ -62,6 +62,9 @@
<Reference Include="System.Threading" Condition=" '$(TargetFrameworkVersionNumber)' &gt;= '4.0' " />
</ItemGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json">
<HintPath>$(ExternalLibs)\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
Expand Down
27 changes: 26 additions & 1 deletion Forge.Collections/QuadTree.cs
Expand Up @@ -18,6 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using Forge.Utilities;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -26,6 +27,7 @@ namespace Forge.Collections {
/// <summary>
/// Interface for objects which are monitoring a specific region within a QuadTree.
/// </summary>
[JsonObject(MemberSerialization.OptIn, IsReference = true)]
public interface IQuadTreeMonitor<T> {
/// <summary>
/// Called when the given item has entered the region.
Expand All @@ -38,8 +40,16 @@ public interface IQuadTreeMonitor<T> {
void OnExit(T item);
}

[JsonObject(MemberSerialization.OptIn)]
internal struct Rect {
public int X0, Z0, X1, Z1;
[JsonProperty("X0")]
public int X0;
[JsonProperty("Z0")]
public int Z0;
[JsonProperty("X1")]
public int X1;
[JsonProperty("Z1")]
public int Z1;

public Rect(int x0, int z0, int x1, int z1) {
X0 = x0;
Expand Down Expand Up @@ -88,9 +98,14 @@ internal struct Rect {
}

// TODO: the linear searches in this class can be removed by using metadata
[JsonObject(MemberSerialization.OptIn)]
internal class Node<T> {
[JsonObject(MemberSerialization.OptIn)]
private class StoredMonitor {
[JsonProperty("Monitor")]
public IQuadTreeMonitor<T> Monitor;

[JsonProperty("Region")]
public Bound Region;

public StoredMonitor(IQuadTreeMonitor<T> monitor, Bound region) {
Expand All @@ -99,8 +114,12 @@ private class StoredMonitor {
}
}

[JsonObject(MemberSerialization.OptIn)]
private class StoredItem {
[JsonProperty("Item")]
public T Item;

[JsonProperty("Position")]
public Vector2r Position;

public StoredItem(T item, Vector2r position) {
Expand All @@ -112,11 +131,13 @@ private class StoredItem {
/// <summary>
/// The items that the node contains
/// </summary>
[JsonProperty("Items")]
private Bag<StoredItem> _items;

/// <summary>
/// The monitors that watch for adds/removes in this node
/// </summary>
[JsonProperty("Monitors")]
private Bag<StoredMonitor> _monitors;

/// <summary>
Expand All @@ -142,6 +163,7 @@ private class StoredItem {
/// <summary>
/// The area that this node is monitoring
/// </summary>
[JsonProperty("MonitoredRegion")]
public Rect MonitoredRegion {
get;
private set;
Expand Down Expand Up @@ -296,15 +318,18 @@ private class StoredItem {
/// chunks can be controlled by the constructor parameter.
/// </remarks>
/// <typeparam name="T">The type of object stored in the QuadTree</typeparam>
[JsonObject(MemberSerialization.OptIn)]
public class QuadTree<TItem> {
/// <summary>
/// The width/height of each chunk
/// </summary>
[JsonProperty("WorldScale")]
private int _worldScale;

/// <summary>
/// All of the chunks
/// </summary>
[JsonProperty("Chunks")]
private Node<TItem>[,] _chunks;

/// <summary>
Expand Down
3 changes: 3 additions & 0 deletions Forge.CollectionsTests/Forge.CollectionsTests.csproj
Expand Up @@ -64,6 +64,9 @@
<Reference Include="xunit.extensions">
<HintPath>..\External\xunit\xunit.extensions.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>$(ExternalLibs)\Newtonsoft.Json.dll</HintPath>
</Reference>
</ItemGroup>
<Choose>
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
Expand Down
50 changes: 43 additions & 7 deletions Forge.CollectionsTests/QuadTreeTests.cs
Expand Up @@ -19,23 +19,37 @@

using Forge.Collections;
using Forge.Utilities;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using Xunit;
using Xunit.Extensions;

namespace CollectionsTests {
public class QuadTreeTests {
private class TestMonitor : IQuadTreeMonitor<object> {
public HashSet<object> Contained = new HashSet<object>();
[JsonObject(MemberSerialization.OptIn, IsReference = true)]
private class TestMonitor<TItem> : IQuadTreeMonitor<TItem> {
[JsonProperty]
public HashSet<TItem> Contained = new HashSet<TItem>();

public void OnEnter(object item) {
public void OnEnter(TItem item) {
Assert.True(Contained.Add(item), "Duplicate item added to monitor");
}

public void OnExit(object item) {
public void OnExit(TItem item) {
Assert.True(Contained.Remove(item),
"Item removed from monitor that was not entered into the monitor");
}

public override bool Equals(object obj) {
var monitor = obj as TestMonitor<TItem>;
return monitor != null && Contained.Equals(monitor.Contained);
}

public override int GetHashCode() {
return Contained.GetHashCode();
}
}

[Theory, PropertyData("WorldScales")]
Expand All @@ -48,7 +62,7 @@ private class TestMonitor : IQuadTreeMonitor<object> {
// the item again, ensure the monitor contains the item, update the monitor so that it
// still contains the item & verify, then remove the monitor and ensure it is empty
{
var monitor = new TestMonitor();
var monitor = new TestMonitor<object>();

// inserting a monitor into an empty tree should result in an empty monitor
tree.AddMonitor(monitor, new Bound(0, 0, 25));
Expand Down Expand Up @@ -78,7 +92,7 @@ private class TestMonitor : IQuadTreeMonitor<object> {

// test a monitor on a non-empty tree
{
var monitor = new TestMonitor();
var monitor = new TestMonitor<object>();

// the monitor should have the object already in the tree added to it
tree.AddMonitor(monitor, new Bound(0, 0, 25));
Expand All @@ -97,7 +111,7 @@ private class TestMonitor : IQuadTreeMonitor<object> {
// test a monitor on a non-empty tree that doesn't contain the objects already in the
// tree
{
var monitor = new TestMonitor();
var monitor = new TestMonitor<object>();

// the monitor should remain empty when being added to the tree
tree.AddMonitor(monitor, new Bound(100, 100, 25));
Expand Down Expand Up @@ -126,6 +140,28 @@ private class TestMonitor : IQuadTreeMonitor<object> {
}
}

[Fact]
public void SerializeQuadTree() {
var tree = new QuadTree<string>();

tree.AddItem("(0, 0)", new Vector2r());
tree.AddItem("(1, -1)", new Vector2r(1, -1));
tree.AddMonitor(new TestMonitor<string>(), new Bound(0, 10, 5));

Console.WriteLine(SerializationHelpers.Serialize(tree));
var cloned = SerializationHelpers.DeepClone(tree);

Assert.Equal(tree.Items.Count(), cloned.Items.Count());
foreach (var item in tree.Items) {
Assert.Contains(item, cloned.Items);
}

Assert.Equal(tree.Monitors.Count(), cloned.Monitors.Count());
foreach (var monitor in tree.Monitors) {
Assert.True(tree.Monitors.Contains(monitor));
}
}

[Theory, PropertyData("WorldScales")]
public void CollectFromQuadTree(int worldScale) {
var tree = new QuadTree<string>(worldScale);
Expand Down

0 comments on commit d30270e

Please sign in to comment.