Skip to content

Commit 308091c

Browse files
committed
ArrayMap performance improvements
1 parent b3da0b0 commit 308091c

File tree

1 file changed

+52
-78
lines changed

1 file changed

+52
-78
lines changed

spatial/hash.go

Lines changed: 52 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,26 @@ import (
88
)
99

1010
type arrayMap[T any] struct {
11-
topRight [][]*T
12-
topLeft [][]*T
13-
botRight [][]*T
14-
botLeft [][]*T
11+
// (+x, +y) => 0
12+
// (+x, -y) => 2
13+
// (-x, +y) => 1
14+
// (-x, -y) => 3
15+
quad [][][]*T
1516
}
1617

1718
func newArrayMap[T any](size int) *arrayMap[T] {
1819
size = size / 2 // Note: We cut in half b/c we use 4 quadrants
1920
m := &arrayMap[T]{
20-
topRight: make([][]*T, size),
21-
topLeft: make([][]*T, size),
22-
botRight: make([][]*T, size),
23-
botLeft: make([][]*T, size),
21+
quad: make([][][]*T, size),
2422
}
2523

26-
for i := range m.topRight {
27-
m.topRight[i] = make([]*T, size)
28-
}
29-
for i := range m.topLeft {
30-
m.topLeft[i] = make([]*T, size)
31-
}
32-
for i := range m.botRight {
33-
m.botRight[i] = make([]*T, size)
34-
}
35-
for i := range m.botLeft {
36-
m.botLeft[i] = make([]*T, size)
24+
// Create 4 quadrants
25+
for range 4 {
26+
slice := make([][]*T, size)
27+
for i := range slice {
28+
slice[i] = make([]*T, size)
29+
}
30+
m.quad = append(m.quad, slice)
3731
}
3832

3933
return m
@@ -61,20 +55,29 @@ func (m *arrayMap[T]) safePut(slice [][]*T, x, y int, t *T) [][]*T {
6155
return slice
6256
}
6357

64-
func (m *arrayMap[T]) Put(x, y int, t *T) {
65-
if x >= 0 {
66-
if y >= 0 {
67-
m.topRight = m.safePut(m.topRight, x, y, t)
68-
} else {
69-
m.botRight = m.safePut(m.botRight, x, -y, t)
70-
}
71-
} else {
72-
if y >= 0 {
73-
m.topLeft = m.safePut(m.topLeft, -x, y, t)
74-
} else {
75-
m.botLeft = m.safePut(m.botLeft, -x, -y, t)
76-
}
58+
// Returns the qudrant index, the X index, and the Y Index
59+
func (m *arrayMap[T]) getQuadIndexes(x, y int) (int, int, int) {
60+
// Calculates the following Quadrants:
61+
// (+x, +y) => 0
62+
// (+x, -y) => 2
63+
// (-x, +y) => 1
64+
// (-x, -y) => 3
65+
66+
idx := 0
67+
if x < 0 {
68+
idx += 1
69+
x = -x
70+
}
71+
if y < 0 {
72+
idx += 2
73+
y = -y
7774
}
75+
return idx, x, y
76+
}
77+
78+
func (m *arrayMap[T]) Put(x, y int, t *T) {
79+
idx, xIdx, yIdx := m.getQuadIndexes(x, y)
80+
m.quad[idx] = m.safePut(m.quad[idx], xIdx, yIdx, t)
7881
}
7982

8083
func (m *arrayMap[T]) safeGet(slice [][]*T, x, y int) (*T, bool) {
@@ -90,56 +93,20 @@ func (m *arrayMap[T]) safeGet(slice [][]*T, x, y int) (*T, bool) {
9093
}
9194

9295
func (m *arrayMap[T]) Get(x, y int) (*T, bool) {
93-
if x >= 0 {
94-
if y >= 0 {
95-
return m.safeGet(m.topRight, x, y)
96-
} else {
97-
return m.safeGet(m.botRight, x, -y)
98-
}
99-
} else {
100-
if y >= 0 {
101-
return m.safeGet(m.topLeft, -x, y)
102-
} else {
103-
return m.safeGet(m.botLeft, -x, -y)
104-
}
105-
}
96+
idx, xIdx, yIdx := m.getQuadIndexes(x, y)
97+
return m.safeGet(m.quad[idx], xIdx, yIdx)
10698
}
10799

108100
func (m *arrayMap[T]) ForEachValue(lambda func(t *T)) {
109-
for x := range m.topRight {
110-
for y := range m.topRight[x] {
111-
val := m.topRight[x][y]
112-
if val == nil {
113-
continue
114-
}
115-
lambda(val)
116-
}
117-
}
118-
for x := range m.topLeft {
119-
for y := range m.topLeft[x] {
120-
val := m.topLeft[x][y]
121-
if val == nil {
122-
continue
123-
}
124-
lambda(val)
125-
}
126-
}
127-
for x := range m.botLeft {
128-
for y := range m.botLeft[x] {
129-
val := m.botLeft[x][y]
130-
if val == nil {
131-
continue
132-
}
133-
lambda(val)
134-
}
135-
}
136-
for x := range m.botRight {
137-
for y := range m.botRight[x] {
138-
val := m.botRight[x][y]
139-
if val == nil {
140-
continue
101+
for idx := range m.quad {
102+
for x := range m.quad[idx] {
103+
for y := range m.quad[idx][x] {
104+
val := m.quad[idx][x][y]
105+
if val == nil {
106+
continue
107+
}
108+
lambda(val)
141109
}
142-
lambda(val)
143110
}
144111
}
145112
}
@@ -287,6 +254,13 @@ func (h *Hashmap[T]) Add(shape Shape, val T) {
287254
min := h.PositionToIndex(shape.Bounds.Min)
288255
max := h.PositionToIndex(shape.Bounds.Max)
289256

257+
// Early case if it's only one bucket
258+
if min == max {
259+
bucket := h.GetBucket(min)
260+
bucket.Add(shape, val)
261+
return
262+
}
263+
290264
for x := min.X; x <= max.X; x++ {
291265
for y := min.Y; y <= max.Y; y++ {
292266
bucket := h.GetBucket(Index{x, y})

0 commit comments

Comments
 (0)