Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Spatial.h
Go to the documentation of this file.
1 /* Copyright (C) 2010 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #ifndef INCLUDED_SPATIAL
19 #define INCLUDED_SPATIAL
20 
22 #include "ps/CLogger.h"
23 
24 /**
25  * A simple fixed-size array that works similar to an std::vector
26  * but is obviously limited in its max items
27  */
29 {
30  enum { MAX_COUNT = 2048 };
31  int count;
33 
34  inline SpatialQueryArray() : count(0) {}
35  inline const uint32_t& operator[](int index) const { return items[index]; }
36  inline uint32_t& operator[](int index) { return items[index]; }
37  inline int size() const { return count; }
38  inline void clear() { count = 0; }
39  void make_unique() // removes any duplicate entries
40  {
41  if (count)
42  {
43  std::sort(items, items + count); // we need a sorted list for std::unique to work
44  count = int(std::unique(items, items + count) - items);
45  }
46  }
47 };
48 
49 /**
50  * A very basic subdivision scheme for finding items in ranges.
51  * Items are stored in lists in fixed-size divisions.
52  * Items have a size (min/max values of their axis-aligned bounding box)
53  * and are stored in all divisions overlapping that area.
54  *
55  * It is the caller's responsibility to ensure items are only added
56  * once, aren't removed unless they've been added, etc, and that
57  * Move/Remove are called with the same coordinates originally passed
58  * to Add (since this class doesn't remember which divisions an item
59  * occupies).
60  *
61  * (TODO: maybe an adaptive quadtree would be better than fixed sizes?)
62  */
64 {
66  {
67  std::vector<uint32_t> items;
68 
69  inline void push_back(uint32_t value)
70  {
71  items.push_back(value);
72  }
73 
74  inline void erase(int index)
75  {
76  // Delete by swapping with the last element then popping
77  if ((int)items.size() > 1) // but only if we have more than 1 elements
78  items[index] = items.back();
79  items.pop_back();
80  }
81 
83  {
84  if (items.empty())
85  return; // nothing to copy
86 
87  int dsti = out.count; // the index in [out] where to start copying
88  int count = (int)items.size();
89  if ((dsti + count) > SpatialQueryArray::MAX_COUNT)
90  {
91  LOGWARNING(L"SpatialSubdivision Query too large. Results truncated.");
92  count = SpatialQueryArray::MAX_COUNT - dsti; // don't copy overflowing items
93  }
94  uint32_t* dst = &out.items[dsti];
95  uint32_t* src = &items[0];
96  for (int i = 0; i < count; ++i) // copy all items
97  dst[i] = src[i];
98  out.count += count;
99  }
100  };
101 
102 
107 
110 
111 public:
113  {
114  }
116  {
117  delete[] m_Divisions;
118  }
120  {
124  size_t n = m_DivisionsW * m_DivisionsH;
125  m_Divisions = new SubDivisionGrid[n];
126  for (size_t i = 0; i < n; ++i)
127  m_Divisions[i] = rhs.m_Divisions[i]; // just fall back to piecemeal copy
128  }
130  {
131  if (this != &rhs)
132  {
134  size_t n = rhs.m_DivisionsW * rhs.m_DivisionsH;
135  if (m_DivisionsW != rhs.m_DivisionsW || m_DivisionsH != rhs.m_DivisionsH)
136  Create(n); // size changed, recreate
137 
140  for (size_t i = 0; i < n; ++i)
141  m_Divisions[i] = rhs.m_Divisions[i]; // just fall back to piecemeal copy
142  }
143  return *this;
144  }
145 
146  inline entity_pos_t GetDivisionSize() const { return m_DivisionSize; }
147  inline uint32_t GetWidth() const { return m_DivisionsW; }
148  inline uint32_t GetHeight() const { return m_DivisionsH; }
149 
150  void Create(size_t count)
151  {
152  if (m_Divisions) delete[] m_Divisions;
153  m_Divisions = new SubDivisionGrid[count];
154  }
155 
156  /**
157  * Equivalence test (ignoring order of items within each subdivision)
158  */
160  {
162  return false;
163 
165  for (uint32_t i = 0; i < n; ++i)
166  {
167  if (m_Divisions[i].items.size() != rhs.m_Divisions[i].items.size())
168  return false;
169 
170  // don't bother optimizing this, this is only used in the TESTING SUITE
171  std::vector<uint32_t> a = m_Divisions[i].items;
172  std::vector<uint32_t> b = rhs.m_Divisions[i].items;
173  std::sort(a.begin(), a.end());
174  std::sort(b.begin(), b.end());
175  if (a != b)
176  return false;
177  }
178  return true;
179  }
180 
181  inline bool operator!=(const SpatialSubdivision& rhs)
182  {
183  return !(*this == rhs);
184  }
185 
186  void Reset(entity_pos_t maxX, entity_pos_t maxZ, entity_pos_t divisionSize)
187  {
188  m_DivisionSize = divisionSize;
189  m_DivisionsW = (maxX / m_DivisionSize).ToInt_RoundToInfinity();
190  m_DivisionsH = (maxZ / m_DivisionSize).ToInt_RoundToInfinity();
191 
193  }
194 
195 
196  /**
197  * Add an item with the given 'to' size.
198  * The item must not already be present.
199  */
200  void Add(uint32_t item, CFixedVector2D toMin, CFixedVector2D toMax)
201  {
202  ENSURE(toMin.X <= toMax.X && toMin.Y <= toMax.Y);
203 
204  u32 i0 = GetI0(toMin.X);
205  u32 j0 = GetJ0(toMin.Y);
206  u32 i1 = GetI1(toMax.X);
207  u32 j1 = GetJ1(toMax.Y);
208  for (u32 j = j0; j <= j1; ++j)
209  {
210  for (u32 i = i0; i <= i1; ++i)
211  {
212  m_Divisions[i + j*m_DivisionsW].push_back(item);
213  }
214  }
215  }
216 
217  /**
218  * Remove an item with the given 'from' size.
219  * The item should already be present.
220  * The size must match the size that was last used when adding the item.
221  */
222  void Remove(uint32_t item, CFixedVector2D fromMin, CFixedVector2D fromMax)
223  {
224  ENSURE(fromMin.X <= fromMax.X && fromMin.Y <= fromMax.Y);
225 
226  u32 i0 = GetI0(fromMin.X);
227  u32 j0 = GetJ0(fromMin.Y);
228  u32 i1 = GetI1(fromMax.X);
229  u32 j1 = GetJ1(fromMax.Y);
230  for (u32 j = j0; j <= j1; ++j)
231  {
232  for (u32 i = i0; i <= i1; ++i)
233  {
235  int size = div.items.size();
236  for (int n = 0; n < size; ++n)
237  {
238  if (div.items[n] == item)
239  {
240  div.erase(n);
241  break;
242  }
243  }
244  }
245  }
246  }
247 
248  /**
249  * Equivalent to Remove() then Add(), but potentially faster.
250  */
251  void Move(uint32_t item, CFixedVector2D fromMin, CFixedVector2D fromMax, CFixedVector2D toMin, CFixedVector2D toMax)
252  {
253  // Skip the work if we're staying in the same divisions
254  if (GetIndex0(fromMin) == GetIndex0(toMin) && GetIndex1(fromMax) == GetIndex1(toMax))
255  return;
256 
257  Remove(item, fromMin, fromMax);
258  Add(item, toMin, toMax);
259  }
260 
261  /**
262  * Convenience function for Add() of individual points.
263  * (Note that points on a boundary may occupy multiple divisions.)
264  */
265  void Add(uint32_t item, CFixedVector2D to)
266  {
267  Add(item, to, to);
268  }
269 
270  /**
271  * Convenience function for Remove() of individual points.
272  */
273  void Remove(uint32_t item, CFixedVector2D from)
274  {
275  Remove(item, from, from);
276  }
277 
278  /**
279  * Convenience function for Move() of individual points.
280  */
282  {
283  Move(item, from, from, to, to);
284  }
285 
286  /**
287  * Returns a sorted list of unique items that includes all items
288  * within the given axis-aligned square range.
289  */
291  {
292  out.clear();
293  ENSURE(posMin.X <= posMax.X && posMin.Y <= posMax.Y);
294 
295  u32 i0 = GetI0(posMin.X);
296  u32 j0 = GetJ0(posMin.Y);
297  u32 i1 = GetI1(posMax.X);
298  u32 j1 = GetJ1(posMax.Y);
299  for (u32 j = j0; j <= j1; ++j)
300  {
301  for (u32 i = i0; i <= i1; ++i)
302  {
303  m_Divisions[i + j*m_DivisionsW].copy_items(out);
304  }
305  }
306  // some buildings span several tiles, so we need to make it unique
307  out.make_unique();
308  }
309 
310  /**
311  * Returns a sorted list of unique items that includes all items
312  * within the given circular distance of the given point.
313  */
315  {
316  // TODO: be cleverer and return a circular pattern of divisions,
317  // not this square over-approximation
318  CFixedVector2D r(range, range);
319  GetInRange(out, pos - r, pos + r);
320  }
321 
322 private:
323  // Helper functions for translating coordinates into division indexes
324  // (avoiding out-of-bounds accesses, and rounding correctly so that
325  // points precisely between divisions are counted in both):
326 
328  {
329  return Clamp((x / m_DivisionSize).ToInt_RoundToInfinity()-1, 0, (int)m_DivisionsW-1);
330  }
331 
333  {
334  return Clamp((z / m_DivisionSize).ToInt_RoundToInfinity()-1, 0, (int)m_DivisionsH-1);
335  }
336 
338  {
339  return Clamp((x / m_DivisionSize).ToInt_RoundToNegInfinity(), 0, (int)m_DivisionsW-1);
340  }
341 
343  {
344  return Clamp((z / m_DivisionSize).ToInt_RoundToNegInfinity(), 0, (int)m_DivisionsH-1);
345  }
346 
348  {
349  return GetI0(pos.X) + GetJ0(pos.Y)*m_DivisionsW;
350  }
351 
353  {
354  return GetI1(pos.X) + GetJ1(pos.Y)*m_DivisionsW;
355  }
356 };
357 
358 /**
359  * Serialization helper template for SpatialSubdivision
360  */
362 {
363  void operator()(ISerializer& serialize, const char* UNUSED(name), SpatialSubdivision& value)
364  {
365  serialize.NumberFixed_Unbounded("div size", value.m_DivisionSize);
366  serialize.NumberU32_Unbounded("divs w", value.m_DivisionsW);
367  serialize.NumberU32_Unbounded("divs h", value.m_DivisionsH);
368 
369  size_t count = value.m_DivisionsH * value.m_DivisionsW;
370  for (size_t i = 0; i < count; ++i)
371  SerializeVector<SerializeU32_Unbounded>()(serialize, "subdiv items", value.m_Divisions[i].items);
372  }
373 
374  void operator()(IDeserializer& serialize, const char* UNUSED(name), SpatialSubdivision& value)
375  {
376  serialize.NumberFixed_Unbounded("div size", value.m_DivisionSize);
377  serialize.NumberU32_Unbounded("divs w", value.m_DivisionsW);
378  serialize.NumberU32_Unbounded("divs h", value.m_DivisionsH);
379 
380  size_t count = value.m_DivisionsW * value.m_DivisionsH;
381  value.Create(count);
382  for (size_t i = 0; i < count; ++i)
383  SerializeVector<SerializeU32_Unbounded>()(serialize, "subdiv items", value.m_Divisions[i].items);
384  }
385 };
386 
387 #endif // INCLUDED_SPATIAL
A simple fixed-point number class.
Definition: Fixed.h:115
int size() const
Definition: Spatial.h:37
uint32_t GetWidth() const
Definition: Spatial.h:147
void clear()
Definition: Spatial.h:38
void GetInRange(SpatialQueryArray &out, CFixedVector2D posMin, CFixedVector2D posMax)
Returns a sorted list of unique items that includes all items within the given axis-aligned square ra...
Definition: Spatial.h:290
void NumberFixed_Unbounded(const char *name, fixed value)
Serialize a number.
Definition: ISerializer.h:191
Helper templates for serializing/deserializing common objects.
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
uint32_t GetI0(entity_pos_t x)
Definition: Spatial.h:327
void GetNear(SpatialQueryArray &out, CFixedVector2D pos, entity_pos_t range)
Returns a sorted list of unique items that includes all items within the given circular distance of t...
Definition: Spatial.h:314
void Remove(uint32_t item, CFixedVector2D fromMin, CFixedVector2D fromMax)
Remove an item with the given &#39;from&#39; size.
Definition: Spatial.h:222
void Reset(entity_pos_t maxX, entity_pos_t maxZ, entity_pos_t divisionSize)
Definition: Spatial.h:186
void copy_items(SpatialQueryArray &out)
Definition: Spatial.h:82
const uint32_t & operator[](int index) const
Definition: Spatial.h:35
uint32_t m_DivisionsH
Definition: Spatial.h:106
Serialization interface; see serialization overview.
Definition: ISerializer.h:120
SpatialSubdivision & operator=(const SpatialSubdivision &rhs)
Definition: Spatial.h:129
static void out(const wchar_t *fmt,...)
Definition: wdbg_sym.cpp:419
uint32_t GetHeight() const
Definition: Spatial.h:148
void Create(size_t count)
Definition: Spatial.h:150
virtual void NumberU32_Unbounded(const char *name, uint32_t &out)
uint32_t GetIndex0(CFixedVector2D pos)
Definition: Spatial.h:347
T Clamp(T val, T min, T max)
low-level aka &quot;lib&quot;
Definition: lib.h:68
virtual void NumberFixed_Unbounded(const char *name, fixed &out)
entity_pos_t m_DivisionSize
Definition: Spatial.h:103
void Remove(uint32_t item, CFixedVector2D from)
Convenience function for Remove() of individual points.
Definition: Spatial.h:273
friend struct SerializeSubDivisionGrid
Definition: Spatial.h:108
#define LOGWARNING
Definition: CLogger.h:34
uint32_t & operator[](int index)
Definition: Spatial.h:36
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
bool operator==(const SpatialSubdivision &rhs)
Equivalence test (ignoring order of items within each subdivision)
Definition: Spatial.h:159
uint32_t GetI1(entity_pos_t x)
Definition: Spatial.h:337
uint32_t GetJ0(entity_pos_t z)
Definition: Spatial.h:332
void operator()(IDeserializer &serialize, const char *name, SpatialSubdivision &value)
Definition: Spatial.h:374
void push_back(uint32_t value)
Definition: Spatial.h:69
void NumberU32_Unbounded(const char *name, uint32_t value)
Serialize a number.
Definition: ISerializer.h:171
void Add(uint32_t item, CFixedVector2D to)
Convenience function for Add() of individual points.
Definition: Spatial.h:265
A very basic subdivision scheme for finding items in ranges.
Definition: Spatial.h:63
uint32_t GetJ1(entity_pos_t z)
Definition: Spatial.h:342
SubDivisionGrid * m_Divisions
Definition: Spatial.h:104
#define u32
Definition: types.h:41
bool operator!=(const SpatialSubdivision &rhs)
Definition: Spatial.h:181
unsigned int uint32_t
Definition: wposix_types.h:53
void make_unique()
Definition: Spatial.h:39
void Move(uint32_t item, CFixedVector2D from, CFixedVector2D to)
Convenience function for Move() of individual points.
Definition: Spatial.h:281
SpatialSubdivision(const SpatialSubdivision &rhs)
Definition: Spatial.h:119
uint32_t items[MAX_COUNT]
Definition: Spatial.h:32
A simple fixed-size array that works similar to an std::vector but is obviously limited in its max it...
Definition: Spatial.h:28
uint32_t m_DivisionsW
Definition: Spatial.h:105
void Move(uint32_t item, CFixedVector2D fromMin, CFixedVector2D fromMax, CFixedVector2D toMin, CFixedVector2D toMax)
Equivalent to Remove() then Add(), but potentially faster.
Definition: Spatial.h:251
Serialization helper template for SpatialSubdivision.
Definition: Spatial.h:361
void Add(uint32_t item, CFixedVector2D toMin, CFixedVector2D toMax)
Add an item with the given &#39;to&#39; size.
Definition: Spatial.h:200
uint32_t GetIndex1(CFixedVector2D pos)
Definition: Spatial.h:352
void operator()(ISerializer &serialize, const char *name, SpatialSubdivision &value)
Definition: Spatial.h:363
std::vector< uint32_t > items
Definition: Spatial.h:67
entity_pos_t GetDivisionSize() const
Definition: Spatial.h:146
Deserialization interface; see serialization overview.
Definition: IDeserializer.h:34