Added Base Project
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Firebird2D.Datatypes
|
||||
{
|
||||
|
||||
|
||||
public class AVL_Tree<T> : BinarySearchTree<T>
|
||||
{
|
||||
public AVL_Tree(IComparer<T> comparer) : base(comparer) { }
|
||||
|
||||
internal AVL_Tree(BinaryNode<T> node, IComparer<T> comparer) : base (node, comparer) {}
|
||||
|
||||
public new void InsertItem(T item)
|
||||
{
|
||||
InsertItem(item, ref root);
|
||||
}
|
||||
|
||||
private void InsertItem(T item, ref BinaryNode<T>? node)
|
||||
{
|
||||
if(node == null)
|
||||
{
|
||||
node = new BinaryNode<T>(item);
|
||||
}
|
||||
else if (Comparer.Compare(item,node.content) < 0)
|
||||
{
|
||||
InsertItem(item, ref node.left);
|
||||
}
|
||||
else if (Comparer.Compare(item, node.content) > 0)
|
||||
{
|
||||
InsertItem(item, ref node.right);
|
||||
}
|
||||
else
|
||||
{
|
||||
InsertItem(item, ref node.left);
|
||||
}
|
||||
balanceTree(ref node);
|
||||
}
|
||||
|
||||
public new void RemoveItem(T item)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void RemoveItem(T item, ref BinaryNode<T>? node)
|
||||
{
|
||||
if (node != null)
|
||||
{
|
||||
if(Comparer.Compare(item, node.content) < 0)
|
||||
{
|
||||
RemoveItem(item, ref node.left);
|
||||
}
|
||||
else if (Comparer.Compare(item, node.content) > 0)
|
||||
{
|
||||
RemoveItem(item, ref node.right);
|
||||
}
|
||||
else if (Comparer.Compare(item, node.content) == 0)
|
||||
{
|
||||
if (node.left == null)
|
||||
{
|
||||
node = node.right;
|
||||
}
|
||||
else if (node.right == null)
|
||||
{
|
||||
node = node.left;
|
||||
}
|
||||
else
|
||||
{
|
||||
T newRoot = leastItem(node.right);
|
||||
node.content = newRoot;
|
||||
RemoveItem(newRoot, ref node.right);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
balanceTree(ref node);
|
||||
}
|
||||
|
||||
private void balanceTree(ref BinaryNode<T> node)
|
||||
{
|
||||
if (node != null)
|
||||
{
|
||||
node.balanceFactor = height(node.left) - height(node.right);
|
||||
if(node.balanceFactor <= -2)
|
||||
{
|
||||
rotateLeft(ref node);
|
||||
}
|
||||
if(node.balanceFactor >= 2)
|
||||
{
|
||||
rotateRight(ref node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void rotateLeft(ref BinaryNode<T> node)
|
||||
{
|
||||
if ( node.left.balanceFactor > 0)
|
||||
{
|
||||
rotateRight(ref node);
|
||||
}
|
||||
|
||||
BinaryNode<T> oldRoot = node;
|
||||
BinaryNode<T> newRoot = node.right;
|
||||
|
||||
oldRoot.right = newRoot.left;
|
||||
newRoot.left = oldRoot;
|
||||
|
||||
node = newRoot;
|
||||
}
|
||||
|
||||
private void rotateRight(ref BinaryNode<T> node)
|
||||
{
|
||||
if (node.left.balanceFactor < 0)
|
||||
{
|
||||
rotateLeft(ref node.left);
|
||||
}
|
||||
|
||||
BinaryNode<T> oldRoot = node;
|
||||
BinaryNode <T> newRoot = node.left;
|
||||
|
||||
oldRoot.left = newRoot.right;
|
||||
newRoot.right = oldRoot;
|
||||
|
||||
node = newRoot;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Firebird2D.Datatypes
|
||||
{
|
||||
internal class BinaryNode<T>
|
||||
{
|
||||
public T content;
|
||||
public int balanceFactor;
|
||||
public BinaryNode<T>? left;
|
||||
public BinaryNode<T>? right;
|
||||
|
||||
public BinaryNode(T item)
|
||||
{
|
||||
content = item;
|
||||
left = null;
|
||||
right = null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Firebird2D.Datatypes
|
||||
{
|
||||
public class BinarySearchTree<T> : BinaryTree<T>
|
||||
{
|
||||
public int Height { get { return height(root); } }
|
||||
|
||||
protected readonly IComparer<T> Comparer;
|
||||
|
||||
public BinarySearchTree(IComparer<T> comparer)
|
||||
{
|
||||
root = null;
|
||||
Comparer = comparer;
|
||||
}
|
||||
|
||||
internal BinarySearchTree(BinaryNode<T> node, IComparer<T> comparer)
|
||||
{
|
||||
root = node;
|
||||
Comparer = comparer;
|
||||
}
|
||||
|
||||
public void InsertItem(T item)
|
||||
{
|
||||
InsertItem(item, ref root);
|
||||
}
|
||||
|
||||
private void InsertItem(T item, ref BinaryNode<T>? node)
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
node = new BinaryNode<T>(item);
|
||||
}
|
||||
else if(Comparer.Compare(item, node.content) < 0)
|
||||
{
|
||||
InsertItem(item, ref node.left);
|
||||
}
|
||||
else if(Comparer.Compare(item, node.content) > 0)
|
||||
{
|
||||
InsertItem(item, ref node.right);
|
||||
}
|
||||
}
|
||||
|
||||
internal int height(BinaryNode<T>? tree)
|
||||
{
|
||||
if(tree == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1 + Math.Max(height(tree.left), height(tree.right));
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(T item)
|
||||
{
|
||||
return Contains(item, root);
|
||||
}
|
||||
|
||||
private bool Contains(T item, BinaryNode<T>? node)
|
||||
{
|
||||
if(node != null)
|
||||
{
|
||||
if(item.Equals(node.content))
|
||||
return true;
|
||||
if(Comparer.Compare(item, node.content) < 0)
|
||||
{
|
||||
return Contains(item, node.left);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Contains(item, node.right);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveItem(T item)
|
||||
{
|
||||
RemoveItem(item, ref root);
|
||||
}
|
||||
|
||||
private void RemoveItem(T item, ref BinaryNode<T>? node)
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (Comparer.Compare(item, node.content) < 0)
|
||||
{
|
||||
RemoveItem(item, ref node.left);
|
||||
}
|
||||
else if (Comparer.Compare(item, node.content) > 0)
|
||||
{
|
||||
RemoveItem(item, ref node.right);
|
||||
}
|
||||
|
||||
if (item.Equals(node.content))
|
||||
{
|
||||
if(node.left == null)
|
||||
{
|
||||
node = node.right;
|
||||
}
|
||||
else if (node.right == null)
|
||||
{
|
||||
node = node.left;
|
||||
}
|
||||
else
|
||||
{
|
||||
T newRoot = leastItem(node.right);
|
||||
node.content = newRoot;
|
||||
RemoveItem(newRoot, ref node.right );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal T leastItem(BinaryNode<T> node)
|
||||
{
|
||||
if(node.left == null)
|
||||
{
|
||||
return node.content;
|
||||
}
|
||||
else
|
||||
{
|
||||
return leastItem(node.left);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Metrics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Firebird2D.Datatypes
|
||||
{
|
||||
public abstract class BinaryTree<T>
|
||||
{
|
||||
internal BinaryNode<T> root = null;
|
||||
|
||||
public int Count { get {
|
||||
int counter = 0;
|
||||
count(root, ref counter);
|
||||
return counter;
|
||||
} }
|
||||
|
||||
public T[]? InOrder()
|
||||
{
|
||||
if (root == null)
|
||||
return Array.Empty<T>();
|
||||
|
||||
List<T> result = new();
|
||||
Stack<BinaryNode<T>> stack = new();
|
||||
BinaryNode<T>? current = root;
|
||||
|
||||
while (current != null || stack.Count > 0)
|
||||
{
|
||||
// Gehe nach links, so weit wie möglich
|
||||
while (current != null)
|
||||
{
|
||||
stack.Push(current);
|
||||
current = current.left;
|
||||
}
|
||||
|
||||
// Knoten aus dem Stack holen
|
||||
current = stack.Pop();
|
||||
result.Add(current.content);
|
||||
|
||||
// Gehe nach rechts
|
||||
current = current.right;
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
public T[]? PreOrder()
|
||||
{
|
||||
if (root == null)
|
||||
return Array.Empty<T>();
|
||||
|
||||
List<T> result = new();
|
||||
Stack<BinaryNode<T>> stack = new();
|
||||
stack.Push(root);
|
||||
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
// Knoten aus dem Stack holen
|
||||
var current = stack.Pop();
|
||||
result.Add(current.content);
|
||||
|
||||
// Zuerst den rechten Knoten hinzufügen (wird später bearbeitet)
|
||||
if (current.right != null)
|
||||
stack.Push(current.right);
|
||||
|
||||
// Dann den linken Knoten hinzufügen
|
||||
if (current.left != null)
|
||||
stack.Push(current.left);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
public T[]? PostOrder()
|
||||
{
|
||||
if (root == null)
|
||||
return Array.Empty<T>();
|
||||
|
||||
List<T> result = new();
|
||||
Stack<BinaryNode<T>> stack1 = new();
|
||||
Stack<BinaryNode<T>> stack2 = new();
|
||||
stack1.Push(root);
|
||||
|
||||
while (stack1.Count > 0)
|
||||
{
|
||||
// Knoten aus dem ersten Stack holen
|
||||
var current = stack1.Pop();
|
||||
stack2.Push(current);
|
||||
|
||||
// Linken und rechten Knoten in den ersten Stack schieben
|
||||
if (current.left != null)
|
||||
stack1.Push(current.left);
|
||||
if (current.right != null)
|
||||
stack1.Push(current.right);
|
||||
}
|
||||
|
||||
// Ergebnis aus dem zweiten Stack lesen (umgekehrte Reihenfolge)
|
||||
while (stack2.Count > 0)
|
||||
{
|
||||
result.Add(stack2.Pop().content);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
public void Copy(BinaryTree<T> tree2)
|
||||
{
|
||||
Copy(ref root, tree2.root);
|
||||
}
|
||||
|
||||
private void Copy(ref BinaryNode<T>? tree, BinaryNode<T>? tree2)
|
||||
{
|
||||
if(tree == null && tree2 != null)
|
||||
{
|
||||
tree = new BinaryNode<T>(tree2.content);
|
||||
}
|
||||
if(tree != null && tree2 != null)
|
||||
{
|
||||
tree.content = tree2.content;
|
||||
Copy(ref tree.left, tree2.left);
|
||||
Copy(ref tree.right, tree2.right);
|
||||
}
|
||||
}
|
||||
|
||||
private void count(BinaryNode<T>? tree, ref int counter)
|
||||
{
|
||||
if (root != null)
|
||||
{
|
||||
counter++;
|
||||
count(tree.left, ref counter);
|
||||
count(tree.right, ref counter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using SFML.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Firebird2D.Datatypes
|
||||
{
|
||||
public interface ISpatial
|
||||
{
|
||||
public FloatRect Bounds { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
using Firebird2D.Requirement;
|
||||
using SFML.Graphics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Firebird2D.SFMLExtensions;
|
||||
using System.Drawing;
|
||||
using SFML.System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Firebird2D.Datatypes
|
||||
{
|
||||
public class Quadtree<T> where T : ISpatial
|
||||
{
|
||||
private readonly List<T> _elements = [];
|
||||
|
||||
private readonly int _bucketCapacity;
|
||||
|
||||
private readonly int _maxDepth;
|
||||
|
||||
private Quadtree<T>? _topLeft, _topRight, _bottomLeft, _bottomRight;
|
||||
|
||||
public FloatRect Bounds { get; }
|
||||
|
||||
public int Level { get; init; }
|
||||
|
||||
[MemberNotNullWhen(false, nameof(_topLeft), nameof(_topRight), nameof(_bottomLeft), nameof(_bottomRight))]
|
||||
public bool IsLeaf
|
||||
=> _topLeft == null || _topRight == null || _bottomLeft == null || _bottomRight == null;
|
||||
|
||||
public bool AllowOutOfBounds { get; set; }
|
||||
|
||||
public Quadtree(FloatRect bounds, int bucketCapacity, int maxDepth)
|
||||
{
|
||||
Bounds = bounds;
|
||||
_bucketCapacity = bucketCapacity;
|
||||
_maxDepth = maxDepth;
|
||||
}
|
||||
|
||||
public Quadtree(FloatRect bounds): this(bounds,32,5) { }
|
||||
|
||||
public void Insert(T element)
|
||||
{
|
||||
Require.NotNull(element, nameof(element));
|
||||
|
||||
if (!(Bounds.Contains(element.Bounds) || AllowOutOfBounds))
|
||||
throw new ArgumentException(string.Format("{0} is out of Quadtreebounds", nameof(element)), nameof(element));
|
||||
|
||||
if (_elements.Count >= _bucketCapacity)
|
||||
Split();
|
||||
|
||||
Quadtree<T>? containingChild = GetContainingChild(element.Bounds);
|
||||
|
||||
if (containingChild != null)
|
||||
{
|
||||
containingChild.Insert(element);
|
||||
}
|
||||
else
|
||||
{
|
||||
_elements.Add(element);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(T element)
|
||||
{
|
||||
Require.NotNull(element, nameof(element));
|
||||
|
||||
Quadtree<T>? containingChild = GetContainingChild(element.Bounds);
|
||||
|
||||
bool removed = containingChild?.Remove(element) ?? _elements.Remove(element);
|
||||
|
||||
if (removed && CountElements() <= _bucketCapacity)
|
||||
Merge();
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
public int CountElements()
|
||||
{
|
||||
int count = _elements.Count;
|
||||
if(!IsLeaf)
|
||||
{
|
||||
count += _topLeft.CountElements();
|
||||
count += _topRight.CountElements();
|
||||
count += _bottomLeft.CountElements();
|
||||
count += _bottomRight.CountElements();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetElements()
|
||||
{
|
||||
List<T> children = new();
|
||||
Queue<Quadtree<T>> nodes = new Queue<Quadtree<T>>();
|
||||
|
||||
nodes.Enqueue(this);
|
||||
|
||||
while (nodes.Count > 0)
|
||||
{
|
||||
Quadtree<T> node = nodes.Dequeue();
|
||||
|
||||
if (!node.IsLeaf)
|
||||
{
|
||||
nodes.Enqueue(node._topLeft);
|
||||
nodes.Enqueue(node._topRight);
|
||||
nodes.Enqueue(node._bottomLeft);
|
||||
nodes.Enqueue(node._bottomRight);
|
||||
}
|
||||
|
||||
children.AddRange(node._elements);
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
private void Split()
|
||||
{
|
||||
if (!IsLeaf)
|
||||
return;
|
||||
|
||||
if (Level + 1 > _maxDepth)
|
||||
return;
|
||||
|
||||
_topLeft = CreateChild(Bounds.Location());
|
||||
_topRight = CreateChild(new Vector2f(Bounds.Center().X, Bounds.Location().Y));
|
||||
_bottomLeft = CreateChild(new Vector2f(Bounds.Location().X, Bounds.Center().Y));
|
||||
_bottomRight = CreateChild(Bounds.Center());
|
||||
|
||||
List<T> elements = _elements.ToList();
|
||||
|
||||
foreach (T element in elements)
|
||||
{
|
||||
Quadtree<T>? containingChild = GetContainingChild(element.Bounds);
|
||||
|
||||
if (containingChild != null)
|
||||
{
|
||||
_elements.Remove(element);
|
||||
|
||||
containingChild.Insert(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Quadtree<T> CreateChild(Vector2f location)
|
||||
=> new(new FloatRect(location, Bounds.Size / 2), _bucketCapacity, _maxDepth) { Level = Level + 1 };
|
||||
|
||||
private void Merge()
|
||||
{
|
||||
if (IsLeaf)
|
||||
return;
|
||||
|
||||
_elements.AddRange(_topLeft._elements);
|
||||
_elements.AddRange(_topRight._elements);
|
||||
_elements.AddRange(_bottomLeft._elements);
|
||||
_elements.AddRange(_bottomRight._elements);
|
||||
|
||||
_topLeft = _topRight = _bottomLeft = _bottomRight = null;
|
||||
}
|
||||
|
||||
private Quadtree<T>? GetContainingChild(FloatRect bounds)
|
||||
{
|
||||
if (IsLeaf)
|
||||
return null;
|
||||
|
||||
if(_topLeft.Bounds.Contains(bounds))
|
||||
return _topLeft;
|
||||
|
||||
if (_topRight.Bounds.Contains(bounds))
|
||||
return _topRight;
|
||||
|
||||
if (_bottomLeft.Bounds.Contains(bounds))
|
||||
return _bottomLeft;
|
||||
|
||||
if (_bottomRight.Bounds.Contains(bounds))
|
||||
return _bottomRight;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public IEnumerable<T> FindCollisions(T element)
|
||||
{
|
||||
Require.NotNull(element, nameof(element));
|
||||
|
||||
var nodes = new Queue<Quadtree<T>>();
|
||||
var collisions = new List<T>();
|
||||
|
||||
nodes.Enqueue(this);
|
||||
|
||||
while (nodes.Count > 0)
|
||||
{
|
||||
var node = nodes.Dequeue();
|
||||
|
||||
if (!element.Bounds.Intersects(node.Bounds))
|
||||
continue;
|
||||
|
||||
collisions.AddRange(node._elements.Where(e => e.Bounds.Intersects(element.Bounds)));
|
||||
|
||||
if (!node.IsLeaf)
|
||||
{
|
||||
if (element.Bounds.Intersects(node._topLeft.Bounds))
|
||||
nodes.Enqueue(node._topLeft);
|
||||
|
||||
if (element.Bounds.Intersects(node._topRight.Bounds))
|
||||
nodes.Enqueue(node._topRight);
|
||||
|
||||
if (element.Bounds.Intersects(node._bottomLeft.Bounds))
|
||||
nodes.Enqueue(node._bottomLeft);
|
||||
|
||||
if (element.Bounds.Intersects(node._bottomRight.Bounds))
|
||||
nodes.Enqueue(node._bottomRight);
|
||||
}
|
||||
}
|
||||
|
||||
return collisions;
|
||||
}
|
||||
|
||||
public IEnumerable<T> FindInRegion(FloatRect region)
|
||||
{
|
||||
var results = new List<T>();
|
||||
var stack = new Stack<Quadtree<T>>();
|
||||
stack.Push(this);
|
||||
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var node = stack.Pop();
|
||||
|
||||
if (!node.Bounds.Intersects(region))
|
||||
continue;
|
||||
|
||||
results.AddRange(node._elements.Where(e => region.Intersects(e.Bounds)));
|
||||
|
||||
if (!node.IsLeaf)
|
||||
{
|
||||
stack.Push(node._topLeft);
|
||||
stack.Push(node._topRight);
|
||||
stack.Push(node._bottomLeft);
|
||||
stack.Push(node._bottomRight);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public AVL_Tree<T> FindInRegionAVL(FloatRect region, IComparer<T> comparer)
|
||||
{
|
||||
List<T> values = (List<T>)FindInRegion(region);
|
||||
AVL_Tree<T> returnTree = new(comparer);
|
||||
foreach (T elment in values)
|
||||
{
|
||||
returnTree.InsertItem(elment);
|
||||
}
|
||||
|
||||
return returnTree;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user