Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/core/StarSectorArray2D.hpp
blob: 0fea5b040a9dcf9b6fe123296eee59064e4c3481 (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
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
#pragma once

#include "StarMultiArray.hpp"
#include "StarSet.hpp"
#include "StarVector.hpp"

namespace Star {

// Holds a sparse 2d array of data based on sector size.  Meant to be used as a
// fast-as-possible sparse array.  Memory requiremenets are equal to the size
// of all loaded sectors PLUS pointer size * sectors wide * sectors high
template <typename ElementT, size_t SectorSize>
class SectorArray2D {
public:
  typedef ElementT Element;
  typedef Vec2S Sector;

  struct SectorRange {
    // Lower left sector
    Vec2S min;
    // Upper right sector *non-inclusive*
    Vec2S max;
  };

  struct Array {
    Array();
    Array(Element const& def);

    Element const& operator()(size_t x, size_t y) const;
    Element& operator()(size_t x, size_t y);

    Element elements[SectorSize * SectorSize];
  };
  typedef unique_ptr<Array> ArrayPtr;

  typedef MultiArray<Element, 2> DynamicArray;

  SectorArray2D();
  SectorArray2D(size_t numSectorsWide, size_t numSectorsHigh);

  void init(size_t numSectorsWide, size_t numSectorsHigh);

  // Total size of array elements
  size_t width() const;
  size_t height() const;

  // Is sector within width() and heigh()
  bool sectorValid(Sector const& sector) const;

  // Returns the sector that contains the given point
  Sector sectorFor(size_t x, size_t y) const;
  // Returns the sector range that contains the given rectangle
  SectorRange sectorRange(size_t minX, size_t minY, size_t width, size_t height) const;

  Vec2S sectorCorner(Sector const& id) const;
  bool hasSector(Sector const& id) const;

  List<Sector> loadedSectors() const;
  size_t loadedSectorCount() const;
  bool sectorLoaded(Sector const& id) const;

  // Will return nullptr if sector is not loaded.
  Array* sector(Sector const& id);
  Array const* sector(Sector const& id) const;

  void loadSector(Sector const& id, ArrayPtr array);
  ArrayPtr copySector(Sector const& id);
  ArrayPtr takeSector(Sector const& id);
  void discardSector(Sector const& id);

  // Will return nullptr if sector is not loaded.
  Element const* get(size_t x, size_t y) const;
  Element* get(size_t x, size_t y);

  // Fast evaluate of elements in the given range.  If evalEmpty is true, then
  // function will be called even for unloaded sectors (with null pointer).
  // Function is called as function(size_t x, size_t y, Element* element).
  // Given function should return true to continue, false to stop.  Returns
  // false if any evaled functions return false.
  template <typename Function>
  bool eval(size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty = false) const;
  template <typename Function>
  bool eval(size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty = false);

  // Individual sectors are stored column-major, so for speed, use this method
  // to get whole columns at a time.  If eval empty is true, function will be
  // called with for each empty column with the correct size information, but
  // the pointer will be null.  Function will be called as
  // function(size_t x, size_t y, Element* columnElements, size_t columnSize)
  // columnSize is guaranteed never to be greater than SectorSize.  Given
  // function should return true to continue, false to stop.  Returns false if
  // any evaled columns return false.
  template <typename Function>
  bool evalColumns(
      size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty = false) const;
  template <typename Function>
  bool evalColumns(size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty = false);

private:
  typedef MultiArray<ArrayPtr, 2> SectorArray;

  template <typename Function>
  bool evalPriv(size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty);
  template <typename Function>
  bool evalColumnsPriv(size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty);

  SectorArray m_sectors;
  HashSet<Sector> m_loadedSectors;
};

template <typename ElementT, size_t SectorSize>
SectorArray2D<ElementT, SectorSize>::Array::Array()
  : elements() {}

template <typename ElementT, size_t SectorSize>
SectorArray2D<ElementT, SectorSize>::Array::Array(Element const& def) {
  for (size_t i = 0; i < SectorSize * SectorSize; ++i)
    elements[i] = def;
}

template <typename ElementT, size_t SectorSize>
ElementT const& SectorArray2D<ElementT, SectorSize>::Array::operator()(size_t x, size_t y) const {
  starAssert(x < SectorSize && y < SectorSize);
  return elements[x * SectorSize + y];
}

template <typename ElementT, size_t SectorSize>
ElementT& SectorArray2D<ElementT, SectorSize>::Array::operator()(size_t x, size_t y) {
  starAssert(x < SectorSize && y < SectorSize);
  return elements[x * SectorSize + y];
}

template <typename ElementT, size_t SectorSize>
SectorArray2D<ElementT, SectorSize>::SectorArray2D() {}

template <typename ElementT, size_t SectorSize>
SectorArray2D<ElementT, SectorSize>::SectorArray2D(size_t numSectorsWide, size_t numSectorsHigh) {
  init(numSectorsWide, numSectorsHigh);
}

template <typename ElementT, size_t SectorSize>
void SectorArray2D<ElementT, SectorSize>::init(size_t numSectorsWide, size_t numSectorsHigh) {
  m_sectors.clear();
  m_sectors.setSize(numSectorsWide, numSectorsHigh);
  m_loadedSectors.clear();
}

template <typename ElementT, size_t SectorSize>
size_t SectorArray2D<ElementT, SectorSize>::width() const {
  return m_sectors.size(0) * SectorSize;
}

template <typename ElementT, size_t SectorSize>
size_t SectorArray2D<ElementT, SectorSize>::height() const {
  return m_sectors.size(1) * SectorSize;
}

template <typename ElementT, size_t SectorSize>
bool SectorArray2D<ElementT, SectorSize>::sectorValid(Sector const& sector) const {
  return sector[0] < m_sectors.size(0) && sector[1] < m_sectors.size(1);
}

template <typename ElementT, size_t SectorSize>
auto SectorArray2D<ElementT, SectorSize>::sectorFor(size_t x, size_t y) const -> Sector {
  return {x / SectorSize, y / SectorSize};
}

template <typename ElementT, size_t SectorSize>
auto SectorArray2D<ElementT, SectorSize>::sectorRange(size_t minX, size_t minY, size_t width, size_t height) const -> SectorRange {
  return {
    {minX / SectorSize, minY / SectorSize},
    {(minX + width + SectorSize - 1) / SectorSize, (minY + height + SectorSize - 1) / SectorSize}
  };
}

template <typename ElementT, size_t SectorSize>
Vec2S SectorArray2D<ElementT, SectorSize>::sectorCorner(Sector const& id) const {
  return Vec2S(id[0] * SectorSize, id[1] * SectorSize);
}

template <typename ElementT, size_t SectorSize>
bool SectorArray2D<ElementT, SectorSize>::hasSector(Sector const& id) const {
  starAssert(id[0] < m_sectors.size(0) && id[1] < m_sectors.size(1));
  return (bool)m_sectors(id[0], id[1]);
}

template <typename ElementT, size_t SectorSize>
auto SectorArray2D<ElementT, SectorSize>::loadedSectors() const -> List<Sector> {
  return m_loadedSectors.values();
}

template <typename ElementT, size_t SectorSize>
size_t SectorArray2D<ElementT, SectorSize>::loadedSectorCount() const {
  return m_loadedSectors.size();
}

template <typename ElementT, size_t SectorSize>
bool SectorArray2D<ElementT, SectorSize>::sectorLoaded(Sector const& id) const {
  return m_loadedSectors.contains(id);
}

template <typename ElementT, size_t SectorSize>
auto SectorArray2D<ElementT, SectorSize>::sector(Sector const& id) -> Array * {
  return m_sectors(id[0], id[1]).get();
}

template <typename ElementT, size_t SectorSize>
auto SectorArray2D<ElementT, SectorSize>::sector(Sector const& id) const -> Array const * {
  return m_sectors(id[0], id[1]).get();
}

template <typename ElementT, size_t SectorSize>
void SectorArray2D<ElementT, SectorSize>::loadSector(Sector const& id, ArrayPtr array) {
  auto& data = m_sectors(id[0], id[1]);
  data = std::move(array);
  if (data)
    m_loadedSectors.add(id);
  else
    m_loadedSectors.remove(id);
}

template <typename ElementT, size_t SectorSize>
typename SectorArray2D<ElementT, SectorSize>::ArrayPtr SectorArray2D<ElementT, SectorSize>::copySector(
    Sector const& id) {
  if (auto const& array = m_sectors(id))
    return std::make_unique<Array>(*array);
  else
    return {};
}

template <typename ElementT, size_t SectorSize>
typename SectorArray2D<ElementT, SectorSize>::ArrayPtr SectorArray2D<ElementT, SectorSize>::takeSector(
    Sector const& id) {
  ArrayPtr ret;
  m_loadedSectors.remove(id);
  std::swap(m_sectors(id[0], id[1]), ret);
  return ret;
}

template <typename ElementT, size_t SectorSize>
void SectorArray2D<ElementT, SectorSize>::discardSector(Sector const& id) {
  m_loadedSectors.remove(id);
  m_sectors(id[0], id[1]).reset();
}

template <typename ElementT, size_t SectorSize>
typename SectorArray2D<ElementT, SectorSize>::Element const* SectorArray2D<ElementT, SectorSize>::get(
    size_t x, size_t y) const {
  Array* array = m_sectors(x / SectorSize, y / SectorSize).get();
  if (array) {
    return &(*array)(x % SectorSize, y % SectorSize);
  } else {
    return nullptr;
  }
}

template <typename ElementT, size_t SectorSize>
typename SectorArray2D<ElementT, SectorSize>::Element* SectorArray2D<ElementT, SectorSize>::get(size_t x, size_t y) {
  Array* array = m_sectors(x / SectorSize, y / SectorSize).get();
  if (array)
    return &(*array)(x % SectorSize, y % SectorSize);
  else
    return nullptr;
}

template <typename ElementT, size_t SectorSize>
template <typename Function>
bool SectorArray2D<ElementT, SectorSize>::eval(
    size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty) const {
  return const_cast<SectorArray2D*>(this)->evalPriv(minX, minY, width, height, std::forward<Function>(function), evalEmpty);
}

template <typename ElementT, size_t SectorSize>
template <typename Function>
bool SectorArray2D<ElementT, SectorSize>::eval(
    size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty) {
  return evalPriv(minX, minY, width, height, std::forward<Function>(function), evalEmpty);
}

template <typename ElementT, size_t SectorSize>
template <typename Function>
bool SectorArray2D<ElementT, SectorSize>::evalColumns(
    size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty) const {
  return const_cast<SectorArray2D*>(this)->evalColumnsPriv(
      minX, minY, width, height, std::forward<Function>(function), evalEmpty);
}

template <typename ElementT, size_t SectorSize>
template <typename Function>
bool SectorArray2D<ElementT, SectorSize>::evalColumns(
    size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty) {
  return evalColumnsPriv(minX, minY, width, height, std::forward<Function>(function), evalEmpty);
}

template <typename ElementT, size_t SectorSize>
template <typename Function>
bool SectorArray2D<ElementT, SectorSize>::evalPriv(
    size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty) {
  return evalColumnsPriv(minX,
      minY,
      width,
      height,
      [&function](size_t x, size_t y, Element* column, size_t columnSize) {
        for (size_t i = 0; i < columnSize; ++i) {
          if (column) {
            if (!function(x, y + i, column + i))
              return false;
          } else {
            if (!function(x, y + i, nullptr))
              return false;
          }
        }
        return true;
      },
      evalEmpty);
}

template <typename ElementT, size_t SectorSize>
template <typename Function>
bool SectorArray2D<ElementT, SectorSize>::evalColumnsPriv(
    size_t minX, size_t minY, size_t width, size_t height, Function&& function, bool evalEmpty) {
  if (width == 0 || height == 0)
    return true;

  size_t maxX = minX + width;
  size_t maxY = minY + height;
  size_t minXSector = minX / SectorSize;
  size_t maxXSector = (maxX - 1) / SectorSize;

  size_t minYSector = minY / SectorSize;
  size_t maxYSector = (maxY - 1) / SectorSize;

  for (size_t xSector = minXSector; xSector <= maxXSector; ++xSector) {
    size_t minXi = 0;
    if (xSector == minXSector)
      minXi = minX % SectorSize;

    size_t maxXi = SectorSize - 1;
    if (xSector == maxXSector)
      maxXi = (maxX - 1) % SectorSize;

    for (size_t ySector = minYSector; ySector <= maxYSector; ++ySector) {
      Array* array = m_sectors(xSector, ySector).get();

      if (!array && !evalEmpty)
        continue;

      size_t minYi = 0;
      if (ySector == minYSector)
        minYi = minY % SectorSize;

      size_t maxYi = SectorSize - 1;
      if (ySector == maxYSector)
        maxYi = (maxY - 1) % SectorSize;

      size_t y_ = ySector * SectorSize;
      size_t x_ = xSector * SectorSize;
      if (!array) {
        for (size_t xi = minXi; xi <= maxXi; ++xi) {
          if (!function(xi + x_, minYi + y_, nullptr, maxYi - minYi + 1))
            return false;
        }
      } else {
        for (size_t xi = minXi; xi <= maxXi; ++xi) {
          if (!function(xi + x_, minYi + y_, &array->elements[xi * SectorSize + minYi], maxYi - minYi + 1))
            return false;
        }
      }
    }
  }

  return true;
}

}