-
Notifications
You must be signed in to change notification settings - Fork 2
/
SpatialHashGrid.cpp
166 lines (130 loc) · 5.45 KB
/
SpatialHashGrid.cpp
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#include <cstdlib>
#include <algorithm>
#include "SpatialHashGrid.h"
#include "VectorArithmetic.h"
SpatialHashGrid::SpatialHashGrid(const sf::Vector2u &winDim, int cellSize)
: m_windowDimensions(winDim)
, m_cellSize(cellSize)
{
m_gridDimensions.x = ceil(winDim.x / (double)cellSize);
m_gridDimensions.y = ceil(winDim.y / (double)cellSize);
// initialise distance matrix here
sf::Vector2u center = m_gridDimensions / (unsigned) 2; // considered as (0,0) for calculation of sq dist later
int centerKey = createKey(center.x, center.y);
// our m_grid representation is m_gridDimensions.x by m_gridDimensions.y
std::map<int, std::unordered_set<int>> offsetsByDistToCenter {};
int maxDistSquared = 0;
for(int rowIndex = 0; rowIndex < m_gridDimensions.y; rowIndex++) {
for(int colIndex = 0; colIndex < m_gridDimensions.x; colIndex++) {
if(rowIndex == center.y && colIndex == center.x) {
// this is the center cell
offsetsByDistToCenter[0].insert(0);
continue;
}
int key = createKey(colIndex, rowIndex);
int d_x = std::max((abs(int(colIndex - center.x))-1),0); // min.horizontal dist between this cell to center cell
int d_y = std::max((abs(int(rowIndex - center.y))-1),0); // min.vertical dist between this cell to center cell
int distSquared = d_x * d_x + d_y * d_y;
offsetsByDistToCenter[distSquared].insert(key - centerKey );
if(distSquared > maxDistSquared) maxDistSquared = distSquared; // record max squaredDist
}
}
m_nOffsetsWithinSqDist = new int[maxDistSquared + 1];
int numCells = m_gridDimensions.x * m_gridDimensions.y;
m_globalOffset = new int[numCells];
int i = 0;
// d2 stands for distance squared
for(int d2 = 0; d2 <= maxDistSquared; d2++) {
if(offsetsByDistToCenter[d2].empty()) {
// if there are no cells whose min. squared dist. to center is d2
m_nOffsetsWithinSqDist[d2] = m_nOffsetsWithinSqDist[d2-1];
} else {
for(int offset : offsetsByDistToCenter[d2]) {
m_globalOffset[i] = offset;
i++;
}
m_nOffsetsWithinSqDist[d2] = i;
}
}
}
void SpatialHashGrid::addBoid(std::shared_ptr<Boid> boid) {
// position of the base of boid arrow
auto i1 = getIndices(boid->getPosition());
int idx = createKey(i1.x, i1.y);
m_grid[idx].insert(boid);
boid->p_spatialIndex = idx;
}
int SpatialHashGrid::createKey(int x, int y) {
return x + y * m_gridDimensions.x;
}
sf::Vector2i SpatialHashGrid::getIndices(sf::Vector2f p) {
float x = p.x / m_windowDimensions.x;
float y = p.y / m_windowDimensions.y;
int xIndex = floor(x * (m_gridDimensions.x - 1));
int yIndex = floor(y * (m_gridDimensions.y - 1));
return {xIndex, yIndex};
}
void SpatialHashGrid::clear() {
m_grid.clear();
}
std::vector<std::shared_ptr<Boid>> SpatialHashGrid::radiusSearch(const std::shared_ptr<Boid> query, int radius) {
using namespace VectorArithmetic;
std::vector<std::shared_ptr<Boid>> res;
double d = radius / static_cast<double>(m_cellSize); // convert radius in pixel space to grid space
int n = std::floor(d * d);
int cellIndexOfQuery = query->p_spatialIndex;
for(int i = 0; i < m_nOffsetsWithinSqDist[n]; i++) {
int offset = m_globalOffset[i];
// since the offsets are taken relative to origin, these could be out of bounds, keep it within m_gridDimensions
int maxPossibleOffset = m_gridDimensions.x * m_gridDimensions.y - 1;
int offsetWithinBounds = std::min<int>(std::max(0, cellIndexOfQuery + offset), maxPossibleOffset);
for(auto boid : m_grid[offsetWithinBounds]) {
if(boid == query) continue; // ignore itself
if(getSquaredDistance(boid->getPosition(),query->getPosition()) <= radius * radius) {
res.push_back(boid);
}
}
}
return res;
}
void SpatialHashGrid::removeBoid(std::shared_ptr<Boid> boid) {
m_grid[boid->p_spatialIndex].erase(boid); // O(1)
}
void SpatialHashGrid::updateBoid(std::shared_ptr<Boid> boid) {
auto i1 = getIndices(boid->getPosition());
int key = createKey(i1.x, i1.y);
if(key == boid->p_spatialIndex) {
return; // same index occupied, don't update
}
removeBoid(boid);
addBoid(boid);
}
SpatialHashGrid::~SpatialHashGrid() {
delete[] m_nOffsetsWithinSqDist;
delete[] m_globalOffset;
}
SpatialHashGrid::SpatialHashGrid(SpatialHashGrid &&other) noexcept {
m_cellSize = other.m_cellSize;
m_gridDimensions = other.m_gridDimensions;
m_windowDimensions = other.m_windowDimensions;
m_grid = other.m_grid;
m_nOffsetsWithinSqDist = other.m_nOffsetsWithinSqDist;
m_globalOffset = other.m_globalOffset;
other.m_nOffsetsWithinSqDist = nullptr;
other.m_globalOffset = nullptr;
}
SpatialHashGrid &SpatialHashGrid::operator=(SpatialHashGrid &&other) noexcept {
if(this != &other) {
delete[] m_nOffsetsWithinSqDist;
delete[] m_globalOffset;
m_cellSize = other.m_cellSize;
m_gridDimensions = other.m_gridDimensions;
m_windowDimensions = other.m_windowDimensions;
m_grid = other.m_grid;
m_nOffsetsWithinSqDist = other.m_nOffsetsWithinSqDist;
m_globalOffset = other.m_globalOffset;
other.m_nOffsetsWithinSqDist = nullptr;
other.m_globalOffset = nullptr;
}
return *this;
}