summaryrefslogtreecommitdiff
path: root/Plugins/MonoGame.Extended/source/MonoGame.Extended.Collisions/SpatialHash.cs
blob: 2b0920e7de6b043c89ba19637763338f54b3f18d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

namespace MonoGame.Extended.Collisions;

public class SpatialHash: ISpaceAlgorithm
{
    private readonly Dictionary<int, List<ICollisionActor>> _dictionary = new();
    private readonly List<ICollisionActor> _actors = new();

    private readonly Size2 _size;

    public SpatialHash(Size2 size)
    {
        _size = size;
    }

    public void Insert(ICollisionActor actor)
    {
        InsertToHash(actor);
        _actors.Add(actor);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private void InsertToHash(ICollisionActor actor)
    {
        var rect = actor.Bounds.BoundingRectangle;
        for (var x = rect.Left; x < rect.Right; x+=_size.Width)
        for (var y = rect.Top; y < rect.Bottom; y+=_size.Height)
            AddToCell(x, y, actor);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private void AddToCell(float x, float y, ICollisionActor actor)
    {
        var index = GetIndex(x, y);
        if (_dictionary.TryGetValue(index, out var actors))
            actors.Add(actor);
        else
            _dictionary[index] = new() { actor };
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private int GetIndex(float x, float y)
    {
        return (int)(x / _size.Width) << 16 + (int)(y / _size.Height);
    }

    public bool Remove(ICollisionActor actor)
    {
        foreach (var actors in _dictionary.Values)
            actors.Remove(actor);
        return _actors.Remove(actor);
    }

    public IEnumerable<ICollisionActor> Query(RectangleF boundsBoundingRectangle)
    {
        var results = new HashSet<ICollisionActor>();
        var bounds = boundsBoundingRectangle.BoundingRectangle;

        for (var x = boundsBoundingRectangle.Left; x < boundsBoundingRectangle.Right; x+=_size.Width)
        for (var y = boundsBoundingRectangle.Top; y < boundsBoundingRectangle.Bottom; y+=_size.Height)
            if (_dictionary.TryGetValue(GetIndex(x, y), out var actors))
                foreach (var actor in actors)
                    if (bounds.Intersects(actor.Bounds))
                        results.Add(actor);
        return results;
    }

    public List<ICollisionActor>.Enumerator GetEnumerator() => _actors.GetEnumerator();

    public void Reset()
    {
        _dictionary.Clear();
        foreach (var actor in _actors)
            InsertToHash(actor);
    }
}