diff --git a/src/DtronixCommon/Collections/Lists/DoubleList2.cs b/src/DtronixCommon/Collections/Lists/DoubleList2.cs
index 2d58d7f..bb011e5 100644
--- a/src/DtronixCommon/Collections/Lists/DoubleList2.cs
+++ b/src/DtronixCommon/Collections/Lists/DoubleList2.cs
@@ -16,17 +16,23 @@ namespace DtronixCommon.Collections.Lists;
/// List of double with varying size with a backing array. Items erased are returned to be reused.
///
/// https://stackoverflow.com/a/48354356
-internal class DoubleList2 : IDisposable
+public class DoubleList2 : IDisposable
{
[StructLayout(LayoutKind.Explicit)]
public struct ValType
{
[FieldOffset(0)] // 1 byte
- internal double Value;
+ public double Value;
[FieldOffset(0)] // 4 bytes
- internal int IntValue;
+ public int IntValue;
public static implicit operator ValType(double d) => new ValType() { Value = d };
+ public static implicit operator ValType(int d) => new ValType() { IntValue = d };
+
+ public override string ToString()
+ {
+ return $"Value: {Value}; Int: {IntValue}";
+ }
}
@@ -164,7 +170,7 @@ public ref ValType Get2(int index, int fieldStart)
/// Interger of the specified element field.
public int GetInt(int index, int field)
{
- return (int)Get(index, field);
+ return _data![index * _numFields + field].IntValue;
}
///
@@ -219,7 +225,6 @@ public int PushBack()
public int PushBack(ReadOnlySpan values)
{
int newPos = (InternalCount + 1) * _numFields;
-
// If the list is full, we need to reallocate the buffer to make room
// for the new element.
if (newPos > _data!.Length)
@@ -238,6 +243,30 @@ public int PushBack(ReadOnlySpan values)
return InternalCount++;
}
+ ///
+ /// Inserts an element to the back of the list and adds the passed values to the data.
+ ///
+ ///
+ public int PushBack(scoped ref ValType values, int count)
+ {
+ int newPos = (InternalCount + 1) * _numFields;
+ // If the list is full, we need to reallocate the buffer to make room
+ // for the new element.
+ if (newPos > _data!.Length)
+ {
+ // Use double the size for the new capacity.
+ int newCap = newPos * 2;
+
+ // Allocate new array and copy former contents.
+ var newArray = new ValType[newCap];
+ Array.Copy(_data, newArray, _data.Length);
+ _data = newArray;
+ }
+ MemoryMarshal.CreateSpan(ref values, count).CopyTo(_data.AsSpan(InternalCount * _numFields));
+
+ return InternalCount++;
+ }
+
///
/// Removes the element at the back of the list.
///
diff --git a/src/DtronixCommon/Collections/Lists/IntList2.cs b/src/DtronixCommon/Collections/Lists/IntList2.cs
new file mode 100644
index 0000000..5a288b8
--- /dev/null
+++ b/src/DtronixCommon/Collections/Lists/IntList2.cs
@@ -0,0 +1,333 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace DtronixCommon.Collections.Lists;
+///
+/// List of int with varying size with a backing array. Items erased are returned to be reused.
+///
+/// https://stackoverflow.com/a/48354356
+public class IntList2 : IDisposable
+{
+ public class Cache
+ {
+ private ConcurrentQueue- _cachedLists = new ConcurrentQueue
- ();
+ private readonly int _fieldCount;
+
+ public class Item
+ {
+ public readonly long ExpireTime;
+ public readonly IntList2 List;
+ private readonly ConcurrentQueue
- _returnQueue;
+
+ public Item(IntList2 list, ConcurrentQueue
- queue)
+ {
+ List = list;
+ _returnQueue = queue;
+ }
+
+ public void Return()
+ {
+ List.InternalCount = 0;
+ List._freeElement = -1;
+ _returnQueue.Enqueue(this);
+ }
+ }
+
+ public Cache(int fieldCount)
+ {
+ _fieldCount = fieldCount;
+ }
+
+ public Item Get()
+ {
+ if (!_cachedLists.TryDequeue(out var list))
+ {
+ return new Item(new IntList2(_fieldCount), _cachedLists);
+ }
+
+ return list;
+ }
+ }
+
+ ///
+ /// Contains the data.
+ ///
+ private int[]? _data;
+
+ ///
+ /// Number of fields which are used in the list. This number is multuplied
+ ///
+ private int _numFields = 0;
+
+ ///
+ /// Current number of elements the list contains.
+ ///
+ internal int InternalCount = 0;
+
+ ///
+ /// Index of the last free element in the array. -1 if there are no free elements.
+ ///
+ private int _freeElement = -1;
+
+ ///
+ /// Number of elements the list contains.
+ ///
+ public int Count => InternalCount;
+
+ ///
+ /// Creates a new list of elements which each consist of integer fields.
+ /// 'fieldCount' specifies the number of integer fields each element has.
+ /// Capacity starts starts at 128.
+ ///
+ /// Number of fields
+ public IntList2(int fieldCount)
+ : this(fieldCount, 128)
+ {
+ }
+
+ ///
+ /// Creates a new list of elements which each consist of integer fields with the specified number of elements.
+ /// 'fieldCount' specifies the number of integer fields each element has.
+ ///
+ ///
+ /// Number of total elements this collection supports. This ignores fieldCount.
+ public IntList2(int fieldCount, int capacity)
+ {
+ _numFields = fieldCount;
+ _data = new int[capacity];
+ }
+
+ ///
+ /// Returns the value of the specified field for the nth element.
+ ///
+ ///
+ ///
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int Get(int index, int field)
+ {
+ Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
+ return _data![index * _numFields + field];
+ }
+
+ ///
+ /// Returns the range of values for the specified element.
+ /// WARNING: Does not perform bounds checks.
+ ///
+ ///
+ /// Starting position of the field.
+ ///
+ /// Nubmer of fields to return. Make sure to not let this run outside
+ /// of the max and min ranges for fields.
+ /// Span of data for the range
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlySpan Get(int index, int fieldStart, int fieldCount)
+ {
+ return new ReadOnlySpan(_data, index * _numFields + fieldStart, fieldCount);
+ }
+
+ ///
+ /// Returns an integer from the currently passed field.
+ ///
+ /// index of the element to retrieve
+ /// Field of the element to retrieve.
+ /// Interger of the specified element field.
+ public int GetInt(int index, int field)
+ {
+ return (int)Get(index, field);
+ }
+
+ ///
+ /// Sets the value of the specified field for the nth element.
+ ///
+ ///
+ ///
+ ///
+ public void Set(int index, int field, int value)
+ {
+ Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
+ _data![index * _numFields + field] = value;
+ }
+
+ ///
+ /// Clears the list, making it empty.
+ ///
+ public void Clear()
+ {
+ InternalCount = 0;
+ _freeElement = -1;
+ }
+
+ ///
+ /// Inserts an element to the back of the list and returns an index to it.
+ ///
+ ///
+ public int PushBack()
+ {
+ int newPos = (InternalCount + 1) * _numFields;
+
+ // If the list is full, we need to reallocate the buffer to make room
+ // for the new element.
+ if (newPos > _data!.Length)
+ {
+ // Use double the size for the new capacity.
+ int newCap = newPos * 2;
+
+ // Allocate new array and copy former contents.
+ var newArray = new int[newCap];
+ Array.Copy(_data, newArray, _data.Length);
+ _data = newArray;
+ }
+
+ return InternalCount++;
+ }
+
+ ///
+ /// Inserts an element to the back of the list and adds the passed values to the data.
+ ///
+ ///
+ public int PushBack(ReadOnlySpan values)
+ {
+ int newPos = (InternalCount + 1) * _numFields;
+
+ // If the list is full, we need to reallocate the buffer to make room
+ // for the new element.
+ if (newPos > _data!.Length)
+ {
+ // Use double the size for the new capacity.
+ int newCap = newPos * 2;
+
+ // Allocate new array and copy former contents.
+ var newArray = new int[newCap];
+ Array.Copy(_data, newArray, _data.Length);
+ _data = newArray;
+ }
+
+ values.CopyTo(_data.AsSpan(InternalCount * _numFields));
+
+ return InternalCount++;
+ }
+
+ ///
+ /// Inserts an element to the back of the list and adds the passed values to the data.
+ ///
+ ///
+ public int PushBackCount(ReadOnlySpan values, int count)
+ {
+ int newPos = (InternalCount + count) * _numFields;
+
+ // If the list is full, we need to reallocate the buffer to make room
+ // for the new element.
+ if (newPos > _data!.Length)
+ {
+ // Use double the size for the new capacity.
+ int newCap = newPos * 2;
+
+ // Allocate new array and copy former contents.
+ var newArray = new int[newCap];
+ Array.Copy(_data, newArray, _data.Length);
+ _data = newArray;
+ }
+
+ values.CopyTo(_data.AsSpan(InternalCount * _numFields));
+
+ var id = InternalCount;
+ InternalCount += count;
+ return id;
+ }
+
+ ///
+ /// Removes the element at the back of the list.
+ ///
+ public void PopBack()
+ {
+ // Just decrement the list size.
+ Debug.Assert(InternalCount > 0);
+ --InternalCount;
+ }
+
+ public void Increment(int index, int field)
+ {
+ Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
+ _data![index * _numFields + field]++;
+ }
+
+ public void Decrement(int index, int field)
+ {
+ Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
+ _data![index * _numFields + field]--;
+ }
+
+ ///
+ /// Inserts an element to a vacant position in the list and returns an index to it.
+ ///
+ ///
+ public int Insert()
+ {
+ // If there's a free index in the free list, pop that and use it.
+ if (_freeElement != -1)
+ {
+ int index = _freeElement;
+ int pos = index * _numFields;
+
+ // Set the free index to the next free index.
+ _freeElement = (int)_data![pos];
+
+ // Return the free index.
+ return index;
+ }
+
+ // Otherwise insert to the back of the array.
+ return PushBack();
+ }
+
+ ///
+ /// Inserts an element to a vacant position in the list and returns an index to it.
+ ///
+ ///
+ public int Insert(ReadOnlySpan values)
+ {
+ // If there's a free index in the free list, pop that and use it.
+ if (_freeElement != -1)
+ {
+ int index = _freeElement;
+ int pos = index * _numFields;
+
+ // Set the free index to the next free index.
+ _freeElement = (int)_data![pos];
+
+ // Return the free index.
+ values.CopyTo(_data.AsSpan(index * _numFields));
+ return index;
+ }
+
+ // Otherwise insert to the back of the array.
+ return PushBack(values);
+ }
+
+ ///
+ /// Removes the nth element in the list.
+ ///
+ ///
+ public void Erase(int index)
+ {
+ // Push the element to the free list.
+ int pos = index * _numFields;
+ _data![pos] = _freeElement;
+ _freeElement = index;
+ }
+
+ ///
+ /// Disposes of the list.
+ ///
+ public void Dispose()
+ {
+ _data = null;
+ }
+}
diff --git a/src/DtronixCommon/Collections/Lists/Lists.g.cs b/src/DtronixCommon/Collections/Lists/Lists.g.cs
index 7d54f62..410a7d2 100644
--- a/src/DtronixCommon/Collections/Lists/Lists.g.cs
+++ b/src/DtronixCommon/Collections/Lists/Lists.g.cs
@@ -1,4 +1,4 @@
-#nullable enable
+#nullable enable
// ----------------------------
// This file is auto generated.
// Any modifications to this file will be overridden
@@ -60,7 +60,7 @@ public Item Get()
///
/// Contains the data.
///
- private float[]? _data;
+ public float[]? Data;
///
/// Number of fields which are used in the list. This number is multuplied
@@ -102,7 +102,7 @@ public FloatList(int fieldCount)
public FloatList(int fieldCount, int capacity)
{
_numFields = fieldCount;
- _data = new float[capacity];
+ Data = new float[capacity];
}
///
@@ -115,7 +115,7 @@ public FloatList(int fieldCount, int capacity)
public float Get(int index, int field)
{
Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
- return _data![index * _numFields + field];
+ return Data![index * _numFields + field];
}
///
@@ -131,7 +131,7 @@ public float Get(int index, int field)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan Get(int index, int fieldStart, int fieldCount)
{
- return new ReadOnlySpan(_data, index * _numFields + fieldStart, fieldCount);
+ return new ReadOnlySpan(Data, index * _numFields + fieldStart, fieldCount);
}
///
@@ -154,7 +154,7 @@ public int GetInt(int index, int field)
public void Set(int index, int field, float value)
{
Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
- _data![index * _numFields + field] = value;
+ Data![index * _numFields + field] = value;
}
///
@@ -176,15 +176,15 @@ public int PushBack()
// If the list is full, we need to reallocate the buffer to make room
// for the new element.
- if (newPos > _data!.Length)
+ if (newPos > Data!.Length)
{
// Use double the size for the new capacity.
int newCap = newPos * 2;
// Allocate new array and copy former contents.
var newArray = new float[newCap];
- Array.Copy(_data, newArray, _data.Length);
- _data = newArray;
+ Array.Copy(Data, newArray, Data.Length);
+ Data = newArray;
}
return InternalCount++;
@@ -200,18 +200,18 @@ public int PushBack(ReadOnlySpan values)
// If the list is full, we need to reallocate the buffer to make room
// for the new element.
- if (newPos > _data!.Length)
+ if (newPos > Data!.Length)
{
// Use double the size for the new capacity.
int newCap = newPos * 2;
// Allocate new array and copy former contents.
var newArray = new float[newCap];
- Array.Copy(_data, newArray, _data.Length);
- _data = newArray;
+ Array.Copy(Data, newArray, Data.Length);
+ Data = newArray;
}
- values.CopyTo(_data.AsSpan(InternalCount * _numFields));
+ values.CopyTo(Data.AsSpan(InternalCount * _numFields));
return InternalCount++;
}
@@ -229,13 +229,13 @@ public void PopBack()
public void Increment(int index, int field)
{
Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
- _data![index * _numFields + field]++;
+ Data![index * _numFields + field]++;
}
public void Decrement(int index, int field)
{
Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
- _data![index * _numFields + field]--;
+ Data![index * _numFields + field]--;
}
///
@@ -251,7 +251,7 @@ public int Insert()
int pos = index * _numFields;
// Set the free index to the next free index.
- _freeElement = (int)_data![pos];
+ _freeElement = (int)Data![pos];
// Return the free index.
return index;
@@ -274,10 +274,10 @@ public int Insert(ReadOnlySpan values)
int pos = index * _numFields;
// Set the free index to the next free index.
- _freeElement = (int)_data![pos];
+ _freeElement = (int)Data![pos];
// Return the free index.
- values.CopyTo(_data.AsSpan(index * _numFields));
+ values.CopyTo(Data.AsSpan(index * _numFields));
return index;
}
@@ -293,7 +293,7 @@ public void Erase(int index)
{
// Push the element to the free list.
int pos = index * _numFields;
- _data![pos] = _freeElement;
+ Data![pos] = _freeElement;
_freeElement = index;
}
@@ -302,7 +302,7 @@ public void Erase(int index)
///
public void Dispose()
{
- _data = null;
+ Data = null;
}
}
@@ -356,7 +356,7 @@ public Item Get()
///
/// Contains the data.
///
- private double[]? _data;
+ public double[]? Data;
///
/// Number of fields which are used in the list. This number is multuplied
@@ -398,7 +398,7 @@ public DoubleList(int fieldCount)
public DoubleList(int fieldCount, int capacity)
{
_numFields = fieldCount;
- _data = new double[capacity];
+ Data = new double[capacity];
}
///
@@ -411,7 +411,7 @@ public DoubleList(int fieldCount, int capacity)
public double Get(int index, int field)
{
Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
- return _data![index * _numFields + field];
+ return Data![index * _numFields + field];
}
///
@@ -427,7 +427,7 @@ public double Get(int index, int field)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan Get(int index, int fieldStart, int fieldCount)
{
- return new ReadOnlySpan(_data, index * _numFields + fieldStart, fieldCount);
+ return new ReadOnlySpan(Data, index * _numFields + fieldStart, fieldCount);
}
///
@@ -450,7 +450,7 @@ public int GetInt(int index, int field)
public void Set(int index, int field, double value)
{
Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
- _data![index * _numFields + field] = value;
+ Data![index * _numFields + field] = value;
}
///
@@ -472,15 +472,15 @@ public int PushBack()
// If the list is full, we need to reallocate the buffer to make room
// for the new element.
- if (newPos > _data!.Length)
+ if (newPos > Data!.Length)
{
// Use double the size for the new capacity.
int newCap = newPos * 2;
// Allocate new array and copy former contents.
var newArray = new double[newCap];
- Array.Copy(_data, newArray, _data.Length);
- _data = newArray;
+ Array.Copy(Data, newArray, Data.Length);
+ Data = newArray;
}
return InternalCount++;
@@ -496,22 +496,23 @@ public int PushBack(ReadOnlySpan values)
// If the list is full, we need to reallocate the buffer to make room
// for the new element.
- if (newPos > _data!.Length)
+ if (newPos > Data!.Length)
{
// Use double the size for the new capacity.
int newCap = newPos * 2;
// Allocate new array and copy former contents.
var newArray = new double[newCap];
- Array.Copy(_data, newArray, _data.Length);
- _data = newArray;
+ Array.Copy(Data, newArray, Data.Length);
+ Data = newArray;
}
- values.CopyTo(_data.AsSpan(InternalCount * _numFields));
+ values.CopyTo(Data.AsSpan(InternalCount * _numFields));
return InternalCount++;
}
+
///
/// Removes the element at the back of the list.
///
@@ -525,13 +526,13 @@ public void PopBack()
public void Increment(int index, int field)
{
Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
- _data![index * _numFields + field]++;
+ Data![index * _numFields + field]++;
}
public void Decrement(int index, int field)
{
Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
- _data![index * _numFields + field]--;
+ Data![index * _numFields + field]--;
}
///
@@ -547,7 +548,7 @@ public int Insert()
int pos = index * _numFields;
// Set the free index to the next free index.
- _freeElement = (int)_data![pos];
+ _freeElement = (int)Data![pos];
// Return the free index.
return index;
@@ -570,10 +571,10 @@ public int Insert(ReadOnlySpan values)
int pos = index * _numFields;
// Set the free index to the next free index.
- _freeElement = (int)_data![pos];
+ _freeElement = (int)Data![pos];
// Return the free index.
- values.CopyTo(_data.AsSpan(index * _numFields));
+ values.CopyTo(Data.AsSpan(index * _numFields));
return index;
}
@@ -589,7 +590,7 @@ public void Erase(int index)
{
// Push the element to the free list.
int pos = index * _numFields;
- _data![pos] = _freeElement;
+ Data![pos] = _freeElement;
_freeElement = index;
}
@@ -598,7 +599,7 @@ public void Erase(int index)
///
public void Dispose()
{
- _data = null;
+ Data = null;
}
}
@@ -652,7 +653,7 @@ public Item Get()
///
/// Contains the data.
///
- private int[]? _data;
+ public int[]? Data;
///
/// Number of fields which are used in the list. This number is multuplied
@@ -694,7 +695,7 @@ public IntList(int fieldCount)
public IntList(int fieldCount, int capacity)
{
_numFields = fieldCount;
- _data = new int[capacity];
+ Data = new int[capacity];
}
///
@@ -707,7 +708,7 @@ public IntList(int fieldCount, int capacity)
public int Get(int index, int field)
{
Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
- return _data![index * _numFields + field];
+ return Data![index * _numFields + field];
}
///
@@ -723,7 +724,7 @@ public int Get(int index, int field)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan Get(int index, int fieldStart, int fieldCount)
{
- return new ReadOnlySpan(_data, index * _numFields + fieldStart, fieldCount);
+ return new ReadOnlySpan(Data, index * _numFields + fieldStart, fieldCount);
}
///
@@ -746,7 +747,7 @@ public int GetInt(int index, int field)
public void Set(int index, int field, int value)
{
Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
- _data![index * _numFields + field] = value;
+ Data![index * _numFields + field] = value;
}
///
@@ -768,15 +769,15 @@ public int PushBack()
// If the list is full, we need to reallocate the buffer to make room
// for the new element.
- if (newPos > _data!.Length)
+ if (newPos > Data!.Length)
{
// Use double the size for the new capacity.
int newCap = newPos * 2;
// Allocate new array and copy former contents.
var newArray = new int[newCap];
- Array.Copy(_data, newArray, _data.Length);
- _data = newArray;
+ Array.Copy(Data, newArray, Data.Length);
+ Data = newArray;
}
return InternalCount++;
@@ -792,18 +793,18 @@ public int PushBack(ReadOnlySpan values)
// If the list is full, we need to reallocate the buffer to make room
// for the new element.
- if (newPos > _data!.Length)
+ if (newPos > Data!.Length)
{
// Use double the size for the new capacity.
int newCap = newPos * 2;
// Allocate new array and copy former contents.
var newArray = new int[newCap];
- Array.Copy(_data, newArray, _data.Length);
- _data = newArray;
+ Array.Copy(Data, newArray, Data.Length);
+ Data = newArray;
}
- values.CopyTo(_data.AsSpan(InternalCount * _numFields));
+ values.CopyTo(Data.AsSpan(InternalCount * _numFields));
return InternalCount++;
}
@@ -821,13 +822,13 @@ public void PopBack()
public void Increment(int index, int field)
{
Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
- _data![index * _numFields + field]++;
+ Data![index * _numFields + field]++;
}
public void Decrement(int index, int field)
{
Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
- _data![index * _numFields + field]--;
+ Data![index * _numFields + field]--;
}
///
@@ -843,7 +844,7 @@ public int Insert()
int pos = index * _numFields;
// Set the free index to the next free index.
- _freeElement = (int)_data![pos];
+ _freeElement = (int)Data![pos];
// Return the free index.
return index;
@@ -866,10 +867,10 @@ public int Insert(ReadOnlySpan values)
int pos = index * _numFields;
// Set the free index to the next free index.
- _freeElement = (int)_data![pos];
+ _freeElement = (int)Data![pos];
// Return the free index.
- values.CopyTo(_data.AsSpan(index * _numFields));
+ values.CopyTo(Data.AsSpan(index * _numFields));
return index;
}
@@ -885,7 +886,7 @@ public void Erase(int index)
{
// Push the element to the free list.
int pos = index * _numFields;
- _data![pos] = _freeElement;
+ Data![pos] = _freeElement;
_freeElement = index;
}
@@ -894,7 +895,7 @@ public void Erase(int index)
///
public void Dispose()
{
- _data = null;
+ Data = null;
}
}
@@ -948,7 +949,7 @@ public Item Get()
///
/// Contains the data.
///
- private long[]? _data;
+ public long[]? Data;
///
/// Number of fields which are used in the list. This number is multuplied
@@ -990,7 +991,7 @@ public LongList(int fieldCount)
public LongList(int fieldCount, int capacity)
{
_numFields = fieldCount;
- _data = new long[capacity];
+ Data = new long[capacity];
}
///
@@ -1003,7 +1004,7 @@ public LongList(int fieldCount, int capacity)
public long Get(int index, int field)
{
Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
- return _data![index * _numFields + field];
+ return Data![index * _numFields + field];
}
///
@@ -1019,7 +1020,7 @@ public long Get(int index, int field)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan Get(int index, int fieldStart, int fieldCount)
{
- return new ReadOnlySpan(_data, index * _numFields + fieldStart, fieldCount);
+ return new ReadOnlySpan(Data, index * _numFields + fieldStart, fieldCount);
}
///
@@ -1042,7 +1043,7 @@ public int GetInt(int index, int field)
public void Set(int index, int field, long value)
{
Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
- _data![index * _numFields + field] = value;
+ Data![index * _numFields + field] = value;
}
///
@@ -1064,15 +1065,15 @@ public int PushBack()
// If the list is full, we need to reallocate the buffer to make room
// for the new element.
- if (newPos > _data!.Length)
+ if (newPos > Data!.Length)
{
// Use double the size for the new capacity.
int newCap = newPos * 2;
// Allocate new array and copy former contents.
var newArray = new long[newCap];
- Array.Copy(_data, newArray, _data.Length);
- _data = newArray;
+ Array.Copy(Data, newArray, Data.Length);
+ Data = newArray;
}
return InternalCount++;
@@ -1088,18 +1089,18 @@ public int PushBack(ReadOnlySpan values)
// If the list is full, we need to reallocate the buffer to make room
// for the new element.
- if (newPos > _data!.Length)
+ if (newPos > Data!.Length)
{
// Use double the size for the new capacity.
int newCap = newPos * 2;
// Allocate new array and copy former contents.
var newArray = new long[newCap];
- Array.Copy(_data, newArray, _data.Length);
- _data = newArray;
+ Array.Copy(Data, newArray, Data.Length);
+ Data = newArray;
}
- values.CopyTo(_data.AsSpan(InternalCount * _numFields));
+ values.CopyTo(Data.AsSpan(InternalCount * _numFields));
return InternalCount++;
}
@@ -1117,13 +1118,13 @@ public void PopBack()
public void Increment(int index, int field)
{
Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
- _data![index * _numFields + field]++;
+ Data![index * _numFields + field]++;
}
public void Decrement(int index, int field)
{
Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
- _data![index * _numFields + field]--;
+ Data![index * _numFields + field]--;
}
///
@@ -1139,7 +1140,7 @@ public int Insert()
int pos = index * _numFields;
// Set the free index to the next free index.
- _freeElement = (int)_data![pos];
+ _freeElement = (int)Data![pos];
// Return the free index.
return index;
@@ -1162,10 +1163,10 @@ public int Insert(ReadOnlySpan values)
int pos = index * _numFields;
// Set the free index to the next free index.
- _freeElement = (int)_data![pos];
+ _freeElement = (int)Data![pos];
// Return the free index.
- values.CopyTo(_data.AsSpan(index * _numFields));
+ values.CopyTo(Data.AsSpan(index * _numFields));
return index;
}
@@ -1181,7 +1182,7 @@ public void Erase(int index)
{
// Push the element to the free list.
int pos = index * _numFields;
- _data![pos] = _freeElement;
+ Data![pos] = _freeElement;
_freeElement = index;
}
@@ -1190,6 +1191,6 @@ public void Erase(int index)
///
public void Dispose()
{
- _data = null;
+ Data = null;
}
}
diff --git a/src/DtronixCommon/Collections/Lists/Lists.tt b/src/DtronixCommon/Collections/Lists/Lists.tt
index dec851a..a329c04 100644
--- a/src/DtronixCommon/Collections/Lists/Lists.tt
+++ b/src/DtronixCommon/Collections/Lists/Lists.tt
@@ -96,7 +96,7 @@ namespace DtronixCommon.Collections.Lists;
///
/// Contains the data.
///
- private <#=config.NumberType#>[]? _data;
+ public <#=config.NumberType#>[]? Data;
///
/// Number of fields which are used in the list. This number is multuplied
@@ -138,7 +138,7 @@ namespace DtronixCommon.Collections.Lists;
public <#=config.ClassName#>(int fieldCount, int capacity)
{
_numFields = fieldCount;
- _data = new <#=config.NumberType#>[capacity];
+ Data = new <#=config.NumberType#>[capacity];
}
///
@@ -151,7 +151,7 @@ namespace DtronixCommon.Collections.Lists;
public <#=config.NumberType#> Get(int index, int field)
{
Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
- return _data![index * _numFields + field];
+ return Data![index * _numFields + field];
}
///
@@ -167,7 +167,7 @@ namespace DtronixCommon.Collections.Lists;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<<#=config.NumberType#>> Get(int index, int fieldStart, int fieldCount)
{
- return new ReadOnlySpan<<#=config.NumberType#>>(_data, index * _numFields + fieldStart, fieldCount);
+ return new ReadOnlySpan<<#=config.NumberType#>>(Data, index * _numFields + fieldStart, fieldCount);
}
///
@@ -190,7 +190,7 @@ namespace DtronixCommon.Collections.Lists;
public void Set(int index, int field, <#=config.NumberType#> value)
{
Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
- _data![index * _numFields + field] = value;
+ Data![index * _numFields + field] = value;
}
///
@@ -212,15 +212,15 @@ namespace DtronixCommon.Collections.Lists;
// If the list is full, we need to reallocate the buffer to make room
// for the new element.
- if (newPos > _data!.Length)
+ if (newPos > Data!.Length)
{
// Use double the size for the new capacity.
int newCap = newPos * 2;
// Allocate new array and copy former contents.
var newArray = new <#=config.NumberType#>[newCap];
- Array.Copy(_data, newArray, _data.Length);
- _data = newArray;
+ Array.Copy(Data, newArray, Data.Length);
+ Data = newArray;
}
return InternalCount++;
@@ -236,18 +236,18 @@ namespace DtronixCommon.Collections.Lists;
// If the list is full, we need to reallocate the buffer to make room
// for the new element.
- if (newPos > _data!.Length)
+ if (newPos > Data!.Length)
{
// Use double the size for the new capacity.
int newCap = newPos * 2;
// Allocate new array and copy former contents.
var newArray = new <#=config.NumberType#>[newCap];
- Array.Copy(_data, newArray, _data.Length);
- _data = newArray;
+ Array.Copy(Data, newArray, Data.Length);
+ Data = newArray;
}
- values.CopyTo(_data.AsSpan(InternalCount * _numFields));
+ values.CopyTo(Data.AsSpan(InternalCount * _numFields));
return InternalCount++;
}
@@ -265,13 +265,13 @@ namespace DtronixCommon.Collections.Lists;
public void Increment(int index, int field)
{
Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
- _data![index * _numFields + field]++;
+ Data![index * _numFields + field]++;
}
public void Decrement(int index, int field)
{
Debug.Assert(index >= 0 && index < InternalCount && field >= 0 && field < _numFields);
- _data![index * _numFields + field]--;
+ Data![index * _numFields + field]--;
}
///
@@ -287,7 +287,7 @@ namespace DtronixCommon.Collections.Lists;
int pos = index * _numFields;
// Set the free index to the next free index.
- _freeElement = (int)_data![pos];
+ _freeElement = (int)Data![pos];
// Return the free index.
return index;
@@ -310,10 +310,10 @@ namespace DtronixCommon.Collections.Lists;
int pos = index * _numFields;
// Set the free index to the next free index.
- _freeElement = (int)_data![pos];
+ _freeElement = (int)Data![pos];
// Return the free index.
- values.CopyTo(_data.AsSpan(index * _numFields));
+ values.CopyTo(Data.AsSpan(index * _numFields));
return index;
}
@@ -329,7 +329,7 @@ namespace DtronixCommon.Collections.Lists;
{
// Push the element to the free list.
int pos = index * _numFields;
- _data![pos] = _freeElement;
+ Data![pos] = _freeElement;
_freeElement = index;
}
@@ -338,7 +338,7 @@ namespace DtronixCommon.Collections.Lists;
///
public void Dispose()
{
- _data = null;
+ Data = null;
}
}
<#
diff --git a/src/DtronixCommon/Collections/Trees/QuadTree2.cs b/src/DtronixCommon/Collections/Trees/QuadTree2.cs
index 0b6f310..34bafaf 100644
--- a/src/DtronixCommon/Collections/Trees/QuadTree2.cs
+++ b/src/DtronixCommon/Collections/Trees/QuadTree2.cs
@@ -517,39 +517,42 @@ private DoubleList2.Cache.Item find_leaves(
while (toProcess.List.InternalCount > 0)
{
int backIdx = toProcess.List.InternalCount - 1;
- var ndData = toProcess.List.Get(backIdx, 0, 6);
-
- var ndIndex = ndData[_ndIdxIndex].IntValue;
- var ndDepth = ndData[_ndIdxDepth].IntValue;
+ ref var ndData = ref toProcess.List.Get2(backIdx, 0);
+ ;
+ var ndIndex = Unsafe.AddByteOffset(ref ndData, (nint)8 * _ndIdxIndex).IntValue; //ndData[_ndIdxIndex].IntValue;
+ var ndDepth = Unsafe.AddByteOffset(ref ndData, (nint)8 * _ndIdxDepth).IntValue; //ndData[_ndIdxDepth].IntValue;
toProcess.List.PopBack();
// If this node is a leaf, insert it to the list.
if (_nodes.Get(ndIndex, _nodeIdxNum) != -1)
- leaves.List.PushBack(ndData);
+ leaves.List.PushBack(ref ndData, 7);
else
{
// Otherwise push the children that intersect the rectangle.
int fc = _nodes.Get(ndIndex, _nodeIdxFc);
- var hx = ndData[_ndIdxSx].Value / 2;
- var hy = ndData[_ndIdxSy].Value / 2;
- var l = ndData[_ndIdxMx].Value - hx;
- var t = ndData[_ndIdxMy].Value - hx;
- var r = ndData[_ndIdxMx].Value + hx;
- var b = ndData[_ndIdxMy].Value + hy;
-
- if (bounds[_eltIdxTop].Value <= ndData[_ndIdxMy].Value)
+ var yVal = Unsafe.AddByteOffset(ref ndData, (nint)8 * _ndIdxMy).Value;
+ var xVal = Unsafe.AddByteOffset(ref ndData, (nint)8 * _ndIdxMx).Value;
+
+ var hx = Unsafe.AddByteOffset(ref ndData, (nint)8 * _ndIdxSx).Value / 2; //ndData[_ndIdxSx].Value / 2;
+ var hy = Unsafe.AddByteOffset(ref ndData, (nint)8 * _ndIdxSy).Value / 2; //ndData[_ndIdxSy].Value / 2;
+ var l = xVal - hx; //ndData[_ndIdxMx].Value - hx;
+ var t = yVal - hx; //ndData[_ndIdxMy].Value - hx;
+ var r = xVal + hx; //ndData[_ndIdxMx].Value + hx;
+ var b = yVal + hy; //ndData[_ndIdxMy].Value + hy;
+
+ if (bounds[_eltIdxTop].Value <= yVal)
{
- if (bounds[_eltIdxLft].Value <= ndData[_ndIdxMx].Value)
+ if (bounds[_eltIdxLft].Value <= xVal)
PushNode(toProcess.List, fc + 0, ndDepth + 1, l, t, hx, hy);
- if (bounds[_eltIdxRgt].Value > ndData[_ndIdxMx].Value)
+ if (bounds[_eltIdxRgt].Value > xVal)
PushNode(toProcess.List, fc + 1, ndDepth + 1, r, t, hx, hy);
}
- if (bounds[_eltIdxBtm].Value > ndData[_ndIdxMy].Value)
+ if (bounds[_eltIdxBtm].Value > yVal)
{
- if (bounds[_eltIdxLft].Value <= ndData[_ndIdxMx].Value)
+ if (bounds[_eltIdxLft].Value <= xVal)
PushNode(toProcess.List, fc + 2, ndDepth + 1, l, b, hx, hy);
- if (bounds[_eltIdxRgt].Value > ndData[_ndIdxMx].Value)
+ if (bounds[_eltIdxRgt].Value > xVal)
PushNode(toProcess.List, fc + 3, ndDepth + 1, r, b, hx, hy);
}
}
diff --git a/src/DtronixCommon/Collections/Trees/QuadTree3.cs b/src/DtronixCommon/Collections/Trees/QuadTree3.cs
new file mode 100644
index 0000000..791e391
--- /dev/null
+++ b/src/DtronixCommon/Collections/Trees/QuadTree3.cs
@@ -0,0 +1,756 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+using DtronixCommon.Collections.Lists;
+using DtronixCommon.Reflection;
+
+namespace DtronixCommon.Collections.Trees;
+///
+/// Quadtree with
+///
+public class DoubleQuadTree3 : IDisposable
+ where T : IQuadTreeItem
+{
+ // ----------------------------------------------------------------------------------------
+ // Element node fields:
+ // ----------------------------------------------------------------------------------------
+ // Points to the next element in the leaf node. A value of -1
+ // indicates the end of the list.
+ const int _enodeIdxNext = 0;
+
+ // Stores the element index.
+ const int _enodeIdxElt = 1;
+
+ // Stores all the element nodes in the quadtree.
+ private readonly IntList2 _eleNodes;
+
+ // ----------------------------------------------------------------------------------------
+ // Element fields:
+ // ----------------------------------------------------------------------------------------
+ // Stores the rectangle encompassing the element.
+ const int _eltIdxLft = 0;
+ const int _eltIdxTop = 1;
+ const int _eltIdxRgt = 2;
+ const int _eltIdxBtm = 3;
+
+ // Stores the ID of the element.
+ const int _eleBoundsItems = 4;
+
+ // Stores all the elements in the quadtree.
+ private readonly DoubleList _eleBounds;
+
+ // ----------------------------------------------------------------------------------------
+ // Node fields:
+ // ----------------------------------------------------------------------------------------
+ // Points to the first child if this node is a branch or the first element
+ // if this node is a leaf.
+ const int _nodeIdxFc = 0;
+
+ // Stores the number of elements in the node or -1 if it is not a leaf.
+ const int _nodeIdxNum = 1;
+
+ // Stores the number of elements in the node or -1 if it is not a leaf.
+ private static readonly int[] _defaultNode4Values = new[] { -1, 0, -1, 0, -1, 0, -1, 0, };
+
+ // Stores all the nodes in the quadtree. The first node in this
+ // sequence is always the root.
+ private readonly IntList2 _nodes;
+
+ // ----------------------------------------------------------------------------------------
+ // Node data fields:
+ // ----------------------------------------------------------------------------------------
+ const int _ndNum = 6;
+
+ // Stores the extents of the node using a centered rectangle and half-size.
+ const int _ndIdxMx = 0;
+ const int _ndIdxMy = 1;
+ const int _ndIdxSx = 2;
+ const int _ndIdxSy = 3;
+
+ // Stores the index of the node.
+ const int _ndIdxIndex = 4;
+
+ // Stores the depth of the node.
+ const int _ndIdxDepth = 5;
+
+ // ----------------------------------------------------------------------------------------
+ // Data Members
+ // ----------------------------------------------------------------------------------------
+ // Temporary buffer used for queries.
+ private bool[]? _temp;
+
+ // Stores the size of the temporary buffer.
+ private int _tempSize = 0;
+
+ // Maximum allowed elements in a leaf before the leaf is subdivided/split unless
+ // the leaf is at the maximum allowed tree depth.
+ private int _maxElements;
+
+ // Stores the maximum depth allowed for the quadtree.
+ private int _maxDepth;
+
+ private T[]? items;
+
+ private readonly double[] _rootNode;
+
+ private readonly DoubleList.Cache _listCache = new DoubleList.Cache(_ndNum);
+
+ private static readonly Action _quadTreeIdSetter;
+ ///
+ /// Items contained in the quad tree. The index of the items matches their QuadTreeId.
+ ///
+ public ReadOnlySpan Items => new ReadOnlySpan(items);
+
+ ///
+ /// Creates a quadtree with the requested extents, maximum elements per leaf,
+ /// and maximum tree depth and maximum capacity.
+ ///
+ /// Width extents of the root node.
+ /// Height extents of the root node.
+ ///
+ /// Maximum allowed elements in a leaf before the leaf is subdivided/split unless
+ /// the leaf is at the maximum allowed tree depth.
+ ///
+ /// Maximum depth allowed for the quadtree.
+ /// Initial element capacity for the tree.
+ public DoubleQuadTree3(double width, double height, int startMaxElements, int startMaxDepth, int initialCapacity = 128)
+ {
+ _maxElements = startMaxElements;
+ _maxDepth = startMaxDepth;
+
+ _eleNodes = new IntList2(2, 2 * initialCapacity);
+ _nodes = new IntList2(2, 2 * initialCapacity);
+ _eleBounds = new DoubleList(_eleBoundsItems, _eleBoundsItems * initialCapacity);
+ items = new T[initialCapacity];
+
+ // Insert the root node to the qt.
+ _nodes.Insert();
+ _nodes.Set(0, _nodeIdxFc, -1);
+ _nodes.Set(0, _nodeIdxNum, 0);
+
+ // Set the extents of the root node.
+ _rootNode = new[]
+ {
+ width / 2, // _ndIdxMx
+ height / 2, // _ndIdxMy
+ width / 2, // _ndIdxSx
+ height / 2, // _ndIdxSy
+ 0, // _ndIdxIndex
+ 0 // _ndIdxDepth
+ };
+ }
+
+ static DoubleQuadTree3()
+ {
+ // Implemented interface.
+ var property = typeof(T).GetProperty("QuadTreeId",
+ BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
+
+ if (property == null)
+ {
+ // Explicit interface implementation
+ property = typeof(T).GetProperty("DtronixCommon.Collections.Trees.IQuadTreeItem.QuadTreeId",
+ BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
+ }
+
+ if (property == null)
+ throw new Exception(
+ $"Type {typeof(T).FullName} does not contain a interger property named QuadTreeId as required.");
+
+ _quadTreeIdSetter = property.GetBackingField().CreateSetter();
+ }
+
+ ///
+ /// Inserts an element into the quad tree at with the specified bounds.
+ ///
+ /// Min X
+ /// Min Y
+ /// Max X
+ /// Max Y
+ /// Item to insert into the quad tree.
+ /// Index of the new element. -1 if the element exists in the quad tree.
+ public int Insert(double x1, double y1, double x2, double y2, T element)
+ {
+ if (element.QuadTreeId != -1)
+ return -1;
+
+ ReadOnlySpan bounds = stackalloc[] { x1, y1, x2, y2 };
+ // Insert a new element.
+ var newElement = _eleBounds.Insert(bounds);
+
+ if (newElement == items!.Length)
+ Array.Resize(ref items, items.Length * 2);
+
+ items[newElement] = element;
+
+ // Insert the element to the appropriate leaf node(s).
+ node_insert(new ReadOnlySpan(_rootNode), bounds, newElement);
+ _quadTreeIdSetter(element, newElement);
+ return newElement;
+ }
+
+ ///
+ /// Removes the specified element from the tree.
+ ///
+ /// Element to remove.
+ public void Remove(T element)
+ {
+ var id = element.QuadTreeId;
+ // Find the leaves.
+ var leaves = find_leaves(
+ new ReadOnlySpan(_rootNode),
+ _eleBounds.Get(id, 0, 4));
+
+ int nodeIndex;
+ int ndIndex;
+
+ // For each leaf node, remove the element node.
+ for (int j = 0; j < leaves.List.InternalCount; ++j)
+ {
+ ndIndex = leaves.List.GetInt(j, _ndIdxIndex);
+
+ // Walk the list until we find the element node.
+ nodeIndex = _nodes.Get(ndIndex, _nodeIdxFc);
+ int prevIndex = -1;
+ while (nodeIndex != -1 && _eleNodes.Get(nodeIndex, _enodeIdxElt) != id)
+ {
+ prevIndex = nodeIndex;
+ nodeIndex = _eleNodes.Get(nodeIndex, _enodeIdxNext);
+ }
+
+ if (nodeIndex != -1)
+ {
+ // Remove the element node.
+ var nextIndex = _eleNodes.Get(nodeIndex, _enodeIdxNext);
+ if (prevIndex == -1)
+ _nodes.Set(ndIndex, _nodeIdxFc, nextIndex);
+ else
+ _eleNodes.Set(prevIndex, _enodeIdxNext, nextIndex);
+ _eleNodes.Erase(nodeIndex);
+
+ // Decrement the leaf element count.
+ _nodes.Decrement(ndIndex, _nodeIdxNum);
+ }
+ }
+ leaves.Return();
+ // Remove the element.
+ _eleBounds.Erase(id);
+ items![id] = default!;
+ _quadTreeIdSetter(element, -1);
+ }
+
+ ///
+ /// Cleans up the tree, removing empty leaves.
+ ///
+ public void Cleanup()
+ {
+ IntList toProcess = new IntList(1);
+
+ // Only process the root if it's not a leaf.
+ if (_nodes.Get(0, _nodeIdxNum) == -1)
+ {
+ // Push the root index to the stack.
+ toProcess.Set(toProcess.PushBack(), 0, 0);
+ }
+
+ while (toProcess.InternalCount > 0)
+ {
+ // Pop a node from the stack.
+ int node = (int)toProcess.Get(toProcess.InternalCount - 1, 0);
+ int fc = _nodes.Get(node, _nodeIdxFc);
+ int numEmptyLeaves = 0;
+ toProcess.PopBack();
+
+ // Loop through the children.
+ for (int j = 0; j < 4; ++j)
+ {
+ int child = fc + j;
+
+ // Increment empty leaf count if the child is an empty
+ // leaf. Otherwise if the child is a branch, add it to
+ // the stack to be processed in the next iteration.
+ if (_nodes.Get(child, _nodeIdxNum) == 0)
+ ++numEmptyLeaves;
+ else if (_nodes.Get(child, _nodeIdxNum) == -1)
+ {
+ // Push the child index to the stack.
+ toProcess.Set(toProcess.PushBack(), 0, child);
+ }
+ }
+
+ // If all the children were empty leaves, remove them and
+ // make this node the new empty leaf.
+ if (numEmptyLeaves == 4)
+ {
+ // Remove all 4 children in reverse order so that they
+ // can be reclaimed on subsequent insertions in proper
+ // order.
+ _nodes.Erase(fc + 3);
+ _nodes.Erase(fc + 2);
+ _nodes.Erase(fc + 1);
+ _nodes.Erase(fc + 0);
+
+ // Make this node the new empty leaf.
+ _nodes.Set(node, _nodeIdxFc, -1);
+ _nodes.Set(node, _nodeIdxNum, 0);
+ }
+ }
+ }
+
+ ///
+ /// Queries the QuadTree and returns a list of the items which intersect the passed bounds.
+ ///
+ /// Min X
+ /// Min Y
+ /// Max X
+ /// Max Y
+ /// List of items which intersect the bounds.
+ public List Query(
+ double x1,
+ double y1,
+ double x2,
+ double y2)
+ {
+ var listOut = new List();
+ ReadOnlySpan bounds = stackalloc[] { x1, y1, x2, y2 };
+
+ // Find the leaves that intersect the specified query rectangle.
+ var leaves = find_leaves(new ReadOnlySpan(_rootNode), bounds);
+
+ if (_tempSize < _eleBounds.InternalCount)
+ {
+ _tempSize = _eleBounds.InternalCount;
+ _temp = new bool[_tempSize];
+ }
+
+ int ndIndex;
+
+ // For each leaf node, look for elements that intersect.
+ for (int j = 0; j < leaves.List.InternalCount; ++j)
+ {
+ ndIndex = leaves.List.GetInt(j, _ndIdxIndex);
+ // Walk the list and add elements that intersect.
+ int eltNodeIndex = _nodes.Get(ndIndex, _nodeIdxFc);
+ while (eltNodeIndex != -1)
+ {
+ int element = _eleNodes.Get(eltNodeIndex, _enodeIdxElt);
+ if (!_temp![element] && Intersect(bounds, _eleBounds.Get(element, 0, 4)))
+ {
+ listOut.Add(items![element]);
+ _temp[element] = true;
+ }
+ eltNodeIndex = _eleNodes.Get(eltNodeIndex, _enodeIdxNext);
+ }
+ }
+
+
+ leaves.Return();
+ // Unmark the elements that were inserted.
+ for (int j = 0; j < listOut.Count; j++)
+ _temp![listOut[j].QuadTreeId] = false;
+
+ return listOut;
+ }
+
+ ///
+ /// Queries the QuadTree and returns a list of the items which intersect the passed bounds.
+ ///
+ /// Min X
+ /// Min Y
+ /// Max X
+ /// Max Y
+ ///
+ /// Callback which is invoked on each found element.
+ /// Return true to continue searching, false to stop.
+ ///
+ /// List of items which intersect the bounds.
+ public IntList Query(
+ double x1,
+ double y1,
+ double x2,
+ double y2,
+ Func callback)
+ {
+ var intListOut = new IntList(1);
+ ReadOnlySpan bounds = stackalloc[] { x1, y1, x2, y2 };
+
+ // Find the leaves that intersect the specified query rectangle.
+ var leaves = find_leaves(new ReadOnlySpan(_rootNode), bounds);
+
+ if (_tempSize < _eleBounds.InternalCount)
+ {
+ _tempSize = _eleBounds.InternalCount;
+ _temp = new bool[_tempSize];
+ }
+
+ bool cancel = false;
+ int ndIndex;
+ // For each leaf node, look for elements that intersect.
+ for (int j = 0; j < leaves.List.InternalCount; ++j)
+ {
+ ndIndex = leaves.List.GetInt(j, _ndIdxIndex);
+
+ // Walk the list and add elements that intersect.
+ int eltNodeIndex = _nodes.Get(ndIndex, _nodeIdxFc);
+ while (eltNodeIndex != -1)
+ {
+ int element = _eleNodes.Get(eltNodeIndex, _enodeIdxElt);
+ if (Intersect(bounds, _eleBounds.Get(element, 0, 4)))
+ {
+ cancel = !callback.Invoke(items![element]);
+ if (cancel)
+ break;
+ intListOut.Set(intListOut.PushBack(), 0, element);
+ _temp![element] = true;
+ }
+ eltNodeIndex = _eleNodes.Get(eltNodeIndex, _enodeIdxNext);
+ }
+
+ if (cancel)
+ break;
+ }
+
+ leaves.Return();
+
+ // Unmark the elements that were inserted.
+ for (int j = 0; j < intListOut.InternalCount; ++j)
+ _temp![intListOut.Get(j, 0)] = false;
+
+ return intListOut;
+ }
+
+ ///
+ /// Walks the specified bounds of the QuadTree and invokes the callback on each found element.
+ ///
+ /// Min X
+ /// Min Y
+ /// Max X
+ /// Max Y
+ ///
+ /// Callback which is invoked on each found element.
+ /// Return true to continue searching, false to stop.
+ ///
+ public unsafe void Walk(
+ double x1,
+ double y1,
+ double x2,
+ double y2,
+ Func callback)
+ {
+ ReadOnlySpan bounds = stackalloc[] { x1, y1, x2, y2 };
+ // Find the leaves that intersect the specified query rectangle.
+ var leaves = find_leaves(new ReadOnlySpan(_rootNode), bounds);
+
+ bool cancel = false;
+ int ndIndex;
+ // For each leaf node, look for elements that intersect.
+ for (int j = 0; j < leaves.List.InternalCount; ++j)
+ {
+ ndIndex = leaves.List.GetInt(j, _ndIdxIndex);
+
+ // Walk the list and add elements that intersect.
+ int eltNodeIndex = _nodes.Get(ndIndex, _nodeIdxFc);
+ int element;
+ while (eltNodeIndex != -1)
+ {
+ element = _eleNodes.Get(eltNodeIndex, _enodeIdxElt);
+ if (Intersect(bounds, _eleBounds.Get(element, 0, 4)))
+ {
+ cancel = !callback.Invoke(items![element]);
+ if (cancel)
+ break;
+ }
+ eltNodeIndex = _eleNodes.Get(eltNodeIndex, _enodeIdxNext);
+ }
+
+ if (cancel)
+ break;
+ }
+
+ leaves.Return();
+ }
+
+ ///
+ /// Clears the quad tree for use.
+ ///
+ public void Clear()
+ {
+ _eleNodes.Clear();
+ _nodes.Clear();
+ _eleBounds.Clear();
+
+#if NET6_0_OR_GREATER
+ Array.Clear(items!);
+#else
+ Array.Clear(items!, 0, items.Length);
+#endif
+ _nodes.Insert();
+ _nodes.Set(0, _nodeIdxFc, -1);
+ _nodes.Set(0, _nodeIdxNum, 0);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool Intersect(
+ ReadOnlySpan b1,
+ ReadOnlySpan b2)
+ {
+ return b2[_eltIdxLft] <= b1[_eltIdxRgt]
+ && b2[_eltIdxRgt] >= b1[_eltIdxLft]
+ && b2[_eltIdxTop] <= b1[_eltIdxBtm]
+ && b2[_eltIdxBtm] >= b1[_eltIdxTop];
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void PushNode(DoubleList nodes, int ndIndex, int ndDepth, double ndMx, double ndMy, double ndSx, double ndSy)
+ {
+ nodes.PushBack(stackalloc[] { ndMx, ndMy, ndSx, ndSy, ndIndex, ndDepth });
+ }
+ private DoubleList.Cache.Item find_leaves(
+ ReadOnlySpan data,
+ ReadOnlySpan bounds)
+ {
+ var leaves = _listCache.Get();
+ var toProcess = _listCache.Get();
+ toProcess.List.PushBack(data);
+
+ while (toProcess.List.InternalCount > 0)
+ {
+ int backIdx = toProcess.List.InternalCount - 1;
+ var ndData = toProcess.List.Get(backIdx, 0, 6);
+
+ var ndIndex = (int)ndData[_ndIdxIndex];
+ var ndDepth = (int)ndData[_ndIdxDepth];
+ toProcess.List.PopBack();
+
+ // If this node is a leaf, insert it to the list.
+ if (_nodes.Get(ndIndex, _nodeIdxNum) != -1)
+ leaves.List.PushBack(ndData);
+ else
+ {
+ var mx = ndData[_ndIdxMx];
+ var my = ndData[_ndIdxMy];
+ // Otherwise push the children that intersect the rectangle.
+ int fc = _nodes.Get(ndIndex, _nodeIdxFc);
+ var hx = ndData[_ndIdxSx] / 2;
+ var hy = ndData[_ndIdxSy] / 2;
+ var l = mx - hx;
+ var t = my - hx;
+ var r = mx + hx;
+ var b = my + hy;
+
+ if (bounds[_eltIdxTop] <= my)
+ {
+ if (bounds[_eltIdxLft] <= mx)
+ PushNode(toProcess.List, fc + 0, ndDepth + 1, l, t, hx, hy);
+ if (bounds[_eltIdxRgt] > mx)
+ PushNode(toProcess.List, fc + 1, ndDepth + 1, r, t, hx, hy);
+ }
+
+ if (bounds[_eltIdxBtm] > my)
+ {
+ if (bounds[_eltIdxLft] <= mx)
+ PushNode(toProcess.List, fc + 2, ndDepth + 1, l, b, hx, hy);
+ if (bounds[_eltIdxRgt] > mx)
+ PushNode(toProcess.List, fc + 3, ndDepth + 1, r, b, hx, hy);
+ }
+ }
+ }
+
+ toProcess.Return();
+
+ return leaves;
+ }
+
+
+ private DoubleList.Cache.Item find_leaves2(
+ ReadOnlySpan data,
+ ReadOnlySpan bounds)
+ {
+ var leaves = _listCache.Get();
+ var toProcess = _listCache.Get();
+ Span processItem = stackalloc double[6];
+ toProcess.List.PushBack(data);
+
+ while (toProcess.List.InternalCount > 0)
+ {
+ int backIdx = toProcess.List.InternalCount - 1;
+ var ndData = toProcess.List.Get(backIdx, 0, 6);
+
+ var ndIndex = (int)ndData[_ndIdxIndex];
+ var ndDepth = (int)ndData[_ndIdxDepth];
+ toProcess.List.PopBack();
+
+ // If this node is a leaf, insert it to the list.
+ if (_nodes.Get(ndIndex, _nodeIdxNum) != -1)
+ leaves.List.PushBack(ndData);
+ else
+ {
+ var mx = ndData[_ndIdxMx];
+ var my = ndData[_ndIdxMy];
+ // Otherwise push the children that intersect the rectangle.
+ int fc = _nodes.Get(ndIndex, _nodeIdxFc);
+ var hx = ndData[_ndIdxSx] / 2;
+ var hy = ndData[_ndIdxSy] / 2;
+ var l = mx - hx;
+ var r = mx + hx;
+
+ processItem[2] = hx; // ndSx
+ processItem[3] = hy; // ndSy
+ processItem[5] = ndDepth + 1; // ndDepth
+
+ if (bounds[_eltIdxTop] <= my)
+ {
+ var t = my - hx;
+ if (bounds[_eltIdxLft] <= mx)
+ {
+ processItem[0] = l; // ndMx
+ processItem[1] = t; // ndMy
+ //processItem[2] = hx; // ndSx
+ //processItem[3] = hy; // ndSy
+ processItem[4] = fc + 0; // ndIndex
+ //processItem[5] = ndDepth + 1; // ndDepth
+
+ toProcess.List.PushBack(processItem);
+ // toProcess.List.PushBack(stackalloc[] { l, t, hx, hy, fc + 0, ndDepth + 1 });
+ //PushNode(toProcess.List, fc + 0, ndDepth + 1, l, t, hx, hy);
+ }
+
+ if (bounds[_eltIdxRgt] > mx)
+ {
+ processItem[0] = r; // ndMx
+ processItem[1] = t; // ndMy
+ //processItem[2] = hx; // ndSx
+ //processItem[3] = hy; // ndSy
+ processItem[4] = fc + 1; // ndIndex
+ //processItem[5] = ndDepth + 1; // ndDepth
+
+ toProcess.List.PushBack(processItem);
+ //toProcess.List.PushBack(stackalloc[] { r, t, hx, hy, fc + 1, ndDepth + 1 });
+ //PushNode(toProcess.List, fc + 1, ndDepth + 1, r, t, hx, hy);
+ }
+ }
+
+ if (bounds[_eltIdxBtm] > my)
+ {
+ var b = my + hy;
+ if (bounds[_eltIdxLft] <= mx)
+ {
+ processItem[0] = l; // ndMx
+ processItem[1] = b; // ndMy
+ //processItem[2] = hx; // ndSx
+ //processItem[3] = hy; // ndSy
+ processItem[4] = fc + 2; // ndIndex
+ //processItem[5] = ndDepth + 1; // ndDepth
+
+ toProcess.List.PushBack(processItem);
+ //toProcess.List.PushBack(stackalloc[] { l, b, hx, hy, fc + 2, ndDepth + 1 });
+ //PushNode(toProcess.List, fc + 2, ndDepth + 1, l, b, hx, hy);
+ }
+
+ if (bounds[_eltIdxRgt] > mx)
+ {
+ processItem[0] = r; // ndMx
+ processItem[1] = b; // ndMy
+ //processItem[2] = hx; // ndSx
+ //processItem[3] = hy; // ndSy
+ processItem[4] = fc + 3; // ndIndex
+ //processItem[5] = ndDepth + 1; // ndDepth
+
+ toProcess.List.PushBack(processItem);
+ //toProcess.List.PushBack(stackalloc[] { r, b, hx, hy, fc + 3, ndDepth + 1 });
+ //PushNode(toProcess.List, fc + 3, ndDepth + 1, r, b, hx, hy);
+ }
+ }
+ }
+ }
+
+ toProcess.Return();
+
+ return leaves;
+ }
+
+ private void node_insert(ReadOnlySpan data, ReadOnlySpan elementBounds, int elementId)
+ {
+ var leaves = find_leaves(data, elementBounds);
+
+ for (int j = 0; j < leaves.List.InternalCount; ++j)
+ leaf_insert(elementId, leaves.List.Get(j, 0, 6));
+
+ leaves.Return();
+ }
+
+ private void leaf_insert(int element, ReadOnlySpan data)
+ {
+ var node = (int)data[_ndIdxIndex];
+ var depth = (int)data[_ndIdxDepth];
+
+ // Insert the element node to the leaf.
+ int ndFc = _nodes.Get(node, _nodeIdxFc);
+
+ _nodes.Set(node, _nodeIdxFc, _eleNodes.Insert());
+ _eleNodes.Set(_nodes.Get(node, _nodeIdxFc), _enodeIdxNext, ndFc);
+ _eleNodes.Set(_nodes.Get(node, _nodeIdxFc), _enodeIdxElt, element);
+
+ // If the leaf is full, split it.
+ if (_nodes.Get(node, _nodeIdxNum) == _maxElements && depth < _maxDepth)
+ {
+ // Transfer elements from the leaf node to a list of elements.
+ IntList elts = new IntList(1);
+ while (_nodes.Get(node, _nodeIdxFc) != -1)
+ {
+ int index = _nodes.Get(node, _nodeIdxFc);
+ int nextIndex = _eleNodes.Get(index, _enodeIdxNext);
+ int elt = _eleNodes.Get(index, _enodeIdxElt);
+
+ // Pop off the element node from the leaf and remove it from the qt.
+ _nodes.Set(node, _nodeIdxFc, nextIndex);
+ _eleNodes.Erase(index);
+
+ // Insert element to the list.
+ elts.Set(elts.PushBack(), 0, elt);
+ }
+
+ // Start by allocating 4 child nodes.
+ int fc = _nodes.PushBackCount(_defaultNode4Values, 4);
+ _nodes.Set(node, _nodeIdxFc, fc);
+
+ // Transfer the elements in the former leaf node to its new children.
+ _nodes.Set(node, _nodeIdxNum, -1);
+ for (int j = 0; j < elts.InternalCount; ++j)
+ {
+ var id = elts.GetInt(j, 0);
+ node_insert(data, _eleBounds.Get(id, 0, 4), id);
+ }
+ }
+ else
+ {
+ // Increment the leaf element count.
+ _nodes.Increment(node, _nodeIdxNum);
+ }
+ }
+
+ ///
+ /// Disposes the quad tree.
+ ///
+ public void Dispose()
+ {
+ if (items == null)
+ return;
+
+ _eleNodes?.Dispose();
+ _eleBounds?.Dispose();
+ _nodes?.Dispose();
+#if NET6_0_OR_GREATER
+ Array.Clear(items!);
+#else
+ Array.Clear(items!, 0, items.Length);
+#endif
+ items = null!;
+ }
+}
diff --git a/src/DtronixCommon/DtronixCommon.csproj b/src/DtronixCommon/DtronixCommon.csproj
index 9c20764..6cce166 100644
--- a/src/DtronixCommon/DtronixCommon.csproj
+++ b/src/DtronixCommon/DtronixCommon.csproj
@@ -4,7 +4,7 @@
enable
0.8.0.0
enable
- 10
+ latest
Dtronix
Dtronix Common
Copyright © Dtronix 2023
@@ -25,18 +25,6 @@
true
-
-
-
-
-
-
-
-
-
-
-
-
True
diff --git a/src/DtronixCommonBenchmarks/Collections/Trees/QuadTreeBenchmarks.cs b/src/DtronixCommonBenchmarks/Collections/Trees/QuadTreeBenchmarks.cs
index e0c9597..d257636 100644
--- a/src/DtronixCommonBenchmarks/Collections/Trees/QuadTreeBenchmarks.cs
+++ b/src/DtronixCommonBenchmarks/Collections/Trees/QuadTreeBenchmarks.cs
@@ -12,10 +12,10 @@ public class QuadTreeBenchmarks
private FloatQuadTree
- _quadTreeF;
private DoubleQuadTree
- _quadTreeD;
- private DoubleQuadTree2
- _quadTreeD2;
+ private DoubleQuadTree3
- _quadTreeD2;
private FloatQuadTree
- _quadTreeFFull;
private DoubleQuadTree
- _quadTreeDFull;
- private DoubleQuadTree2
- _quadTreeD2Full;
+ private DoubleQuadTree3
- _quadTreeD2Full;
private class Item : IQuadTreeItem
{
@@ -28,7 +28,7 @@ public void GlobalSetup()
var offsetX = 5;
var offsetY = 5;
_quadTreeD = new DoubleQuadTree
- (10000, 10000, 8, 8, 200);
- _quadTreeD2 = new DoubleQuadTree2
- (10000, 10000, 8, 8, 200);
+ _quadTreeD2 = new DoubleQuadTree3
- (10000, 10000, 8, 8, 200);
_quadTreeF = new FloatQuadTree
- (10000, 10000, 8, 8, 200);
_quadTreeFFull = new FloatQuadTree
- (10000, 10000, 8, 8, 1024);
for (int x = 0; x < 50; x++)
@@ -56,7 +56,7 @@ public void GlobalSetup()
}
}
- _quadTreeD2Full = new DoubleQuadTree2
- (10000, 10000, 8, 8, 1024);
+ _quadTreeD2Full = new DoubleQuadTree3
- (10000, 10000, 8, 8, 1024);
for (int x = 0; x < 50; x++)
{
for (int y = 0; y < 50; y++)
@@ -71,13 +71,13 @@ public void GlobalSetup()
}
- [Benchmark]
+ //[Benchmark]
public void WalkFloat()
{
_quadTreeFFull.Walk(-5000, -5000, 5000, 5000, item => true);
}
- [Benchmark]
+ //[Benchmark]
public void InsertFloat()
{
@@ -120,7 +120,7 @@ public void InsertDouble()
}
[Benchmark]
- public void InsertDouble2()
+ public void InsertDouble3()
{
var offsetX = 5;
@@ -148,7 +148,7 @@ public void WalkDouble()
}
[Benchmark]
- public void WalkDouble2()
+ public void WalkDouble3()
{
_quadTreeD2Full.Walk(-5000, -5000, 5000, 5000, item => true);
}
diff --git a/src/DtronixCommonSamples/DtronixCommonSamples.csproj b/src/DtronixCommonSamples/DtronixCommonSamples.csproj
index bc2fa13..daf21b4 100644
--- a/src/DtronixCommonSamples/DtronixCommonSamples.csproj
+++ b/src/DtronixCommonSamples/DtronixCommonSamples.csproj
@@ -2,7 +2,7 @@
Exe
- net6.0
+ net7.0
diff --git a/src/DtronixCommonSamples/Program.cs b/src/DtronixCommonSamples/Program.cs
index 636e54d..3b4bdf5 100644
--- a/src/DtronixCommonSamples/Program.cs
+++ b/src/DtronixCommonSamples/Program.cs
@@ -4,59 +4,32 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
+using DtronixCommon.Collections.Lists;
using DtronixCommon.Collections.Trees;
+using DtronixCommonBenchmarks.Collections.Trees;
namespace DtronixCommonSamples
{
- public class DesignViewVisual : IQuadTreeItem
+ class Item : IQuadTreeItem
{
- internal int InternalQuadTreeId = -1;
-
- int IQuadTreeItem.QuadTreeId
- {
- get => InternalQuadTreeId;
- set => InternalQuadTreeId = value;
- }
+ public int QuadTreeId { get; set; } = -1;
}
-
+
+
internal class Program
{
+ private static DoubleQuadTree2
- _quadTreeD2Full;
+
private class MyClass : IQuadTreeItem
{
int IQuadTreeItem.QuadTreeId { get; set; } = -1;
}
static void Main(string[] args)
{
- var qtf = new FloatQuadTree(float.MaxValue, float.MaxValue, 8, 8, 510 * 510);
-
- var visual = new DesignViewVisual();
- qtf.Insert(-1, -1, 1, 1, visual);
- qtf.Insert(-1, -1, 1, 1, visual);
-
-
- var offsetX = 2;
- var offsetY = 2;
-
-
-
- while (true)
- {
- for (int x = 0; x < 500; x++)
- {
- for (int y = 0; y < 500; y++)
- {
- qtf.Insert(
- x - offsetX + offsetX * x,
- y - offsetY + offsetY * y,
- x + offsetX + offsetX * x,
- y + offsetY + offsetY * y, new DesignViewVisual());
- }
- }
-
- qtf.Clear();
- //qtf.Walk(float.MinValue, float.MinValue, float.MaxValue, float.MaxValue, ele => true);
- }
+ var b = new QuadTreeBenchmarks();
+ b.GlobalSetup();
+ b.InsertDouble3();
Console.ReadLine();
}
diff --git a/src/DtronixCommonSamples/QuadTreeBenchmarks.cs b/src/DtronixCommonSamples/QuadTreeBenchmarks.cs
new file mode 100644
index 0000000..a4e1027
--- /dev/null
+++ b/src/DtronixCommonSamples/QuadTreeBenchmarks.cs
@@ -0,0 +1,148 @@
+using System;
+using DtronixCommon.Collections.Lists;
+using DtronixCommon.Collections.Trees;
+
+namespace DtronixCommonBenchmarks.Collections.Trees;
+public class QuadTreeBenchmarks
+{
+
+ //private FloatQuadTree
- _quadTreeF;
+ //private DoubleQuadTree
- _quadTreeD;
+ private DoubleQuadTree3
- _quadTreeD2;
+ //private FloatQuadTree
- _quadTreeFFull;
+ //private DoubleQuadTree
- _quadTreeDFull;
+ private DoubleQuadTree3
- _quadTreeD2Full;
+
+ private class Item : IQuadTreeItem
+ {
+ public int QuadTreeId { get; set; } = -1;
+ }
+
+ public void GlobalSetup()
+ {
+ var offsetX = 5;
+ var offsetY = 5;
+ //_quadTreeD = new DoubleQuadTree
- (10000, 10000, 8, 8, 200);
+ _quadTreeD2 = new DoubleQuadTree3
- (10000, 10000, 8, 8, 200);
+ /*_quadTreeF = new FloatQuadTree
- (10000, 10000, 8, 8, 200);
+ _quadTreeFFull = new FloatQuadTree
- (10000, 10000, 8, 8, 1024);
+ for (int x = 0; x < 50; x++)
+ {
+ for (int y = 0; y < 50; y++)
+ {
+ _quadTreeFFull.Insert(
+ x - offsetX + offsetX * x,
+ y - offsetY + offsetY * y,
+ x + offsetX + offsetX * x,
+ y + offsetY + offsetY * y, new Item());
+ }
+ }
+
+ _quadTreeDFull = new DoubleQuadTree
- (10000, 10000, 8, 8, 1024);
+ for (int x = 0; x < 50; x++)
+ {
+ for (int y = 0; y < 50; y++)
+ {
+ _quadTreeDFull.Insert(
+ x - offsetX + offsetX * x,
+ y - offsetY + offsetY * y,
+ x + offsetX + offsetX * x,
+ y + offsetY + offsetY * y, new Item());
+ }
+ }*/
+
+ _quadTreeD2Full = new DoubleQuadTree3
- (10000, 10000, 8, 8, 1024);
+ for (int x = 0; x < 50; x++)
+ {
+ for (int y = 0; y < 50; y++)
+ {
+ _quadTreeD2Full.Insert(
+ x - offsetX + offsetX * x,
+ y - offsetY + offsetY * y,
+ x + offsetX + offsetX * x,
+ y + offsetY + offsetY * y, new Item());
+ }
+ }
+
+ }
+ /*
+ //[Benchmark]
+ public void WalkFloat()
+ {
+ _quadTreeFFull.Walk(-5000, -5000, 5000, 5000, item => true);
+ }
+
+ //[Benchmark]
+ public void InsertFloat()
+ {
+
+ var offsetX = 5;
+ var offsetY = 5;
+
+ for (int x = 0; x < 10; x++)
+ {
+ for (int y = 0; y < 10; y++)
+ {
+ _quadTreeF.Insert(
+ x - offsetX + offsetX * x,
+ y - offsetY + offsetY * y,
+ x + offsetX + offsetX * x,
+ y + offsetY + offsetY * y, new Item());
+ }
+ }
+ _quadTreeF.Clear();
+ }
+
+ public void InsertDouble()
+ {
+
+ var offsetX = 5;
+ var offsetY = 5;
+
+ for (int x = 0; x < 10; x++)
+ {
+ for (int y = 0; y < 10; y++)
+ {
+ _quadTreeD.Insert(
+ x - offsetX + offsetX * x,
+ y - offsetY + offsetY * y,
+ x + offsetX + offsetX * x,
+ y + offsetY + offsetY * y, new Item());
+ }
+ }
+ _quadTreeD.Clear();
+ }
+ */
+ public void InsertDouble3()
+ {
+
+ var offsetX = 5;
+ var offsetY = 5;
+
+ for (int x = 0; x < 10; x++)
+ {
+ for (int y = 0; y < 10; y++)
+ {
+ _quadTreeD2.Insert(
+ x - offsetX + offsetX * x,
+ y - offsetY + offsetY * y,
+ x + offsetX + offsetX * x,
+ y + offsetY + offsetY * y, new Item());
+ }
+ }
+ //_quadTreeD2.Clear();
+ }
+ /*
+
+ public void WalkDouble()
+ {
+ _quadTreeDFull.Walk(-5000, -5000, 5000, 5000, item => true);
+ }*/
+
+ public void WalkDouble3()
+ {
+ _quadTreeD2Full.Walk(-5000, -5000, 5000, 5000, item => true);
+ }
+
+}
+