VR-Forces 5.0.3 Developer's Guide
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
spatial.h
Go to the documentation of this file.
1 /******************************************************************************
2 ** Copyright (c) 2020 MAK Technologies, Inc.
3 ** All rights reserved.
4 ******************************************************************************/
5 
6 #pragma once
7 
9 
10 #include <unordered_map>
11 #include <queue>
12 
13 #include "makUtil/dataTypes.h"
14 #include "makUtil/array.h"
15 #include "makUtil/vector.h"
16 
17 #include <cmath>
18 #include <immintrin.h>
19 #include <tbb/spin_mutex.h>
20 
21 namespace MAK
22 {
23  template<typename DATA_ID>
25  {
26  static const int BITS_PER_COMPONENT = 30;
27  static const int BITS_PER_COMPONENT_AND_REGION = 31;
28 
30  public:
31  SpatialIndex();
32  ~SpatialIndex();
33 
34  typedef unsigned int NodeIndex;
35  typedef unsigned int PageIndex;
36 
38  Bool insert(const DATA_ID globalId, const Vector3d& pos);
40  Bool insert(const DATA_ID globalId, const Vector3d& pos, const UInt64 key);
42  Bool remove(const DATA_ID globalId);
44  Bool update(const DATA_ID globalId, const Vector3d& pos);
45 
48  std::vector<DATA_ID> nearestNeighbors(const Vector3d& loc, double distanceInMeter) const;
50  std::vector<DATA_ID> objectsWithin(const Vector3d& min, const Vector3d& max) const;
52  std::vector<DATA_ID> objectsWithin(const Vector2d& minLatLonDeg, const Vector2d& maxLatLonDeg) const;
54  std::vector<DATA_ID> objectsNearChord(const Vector3d& pointA, const Vector3d& pointB, const double delta) const;
55  std::vector<DATA_ID> spatialTreeWalk(UInt32 &totalDataCnt) const;
57  bool isDistanceToLineWithin(const Vector3d &pointC, double distance, const Vector3d &lineA, const Vector3d &lineB) const;
58 
59  bool isWholeGlobeRegion(const Vector3d &pos1, const Vector3d &pos2) const;
60 
62  void reset();
63 
64 
65  void endFrame();
66 
68  public:
69 
70  static Vector2d geocToSpherical(const Vector3d& pos)
71  {
72  double radius = pos.length();
73  Vector2d ret;
74  ret.x = atan2(pos.y, pos.x); //azimuth. atan2 returns -pi to pi
75  ret.y = radius ? acos(pos.z / radius) : 0; //elevation. returns between 0..pi
76 
78  ret.x = (ret.x + 3.1415926535897932);
80  ret.y = ret.y * 2;
81 
82  return ret;
83  }
84 
85  /*
86  bit scattering done so that we can interleave the two values so that we can take 2 bits at time and use them to determine which child to go into in the quadtree
87 
88  The earth is split into 4 regions. A region goes from the south to the north pole and contains a 90 degree section around the equator.
89  Positions are converted to polar coordinates and are quantized into 30 bit numbers.
90  Then the positions are converted into bit keys. The bit key is 64 bit longs. The top 2 significant bits are not used (maybe flags later)
91 
92  The next two bits (62,61) are the region that contains the quadtree.
93  The remaining 60 bits are a Morton code that determine the children to go into in the quadtree.
94 
95  As the azimuth values increase, the x bit increases, as the elevation increases the y bit increases.
96  More bits are required the deeper you move into the tree.
97 
98  With 30 bits, on the equator, we get about 3cm precision. This is close enough for determining if entities are close.
99 
100  Key layout:
101  |xx|rr|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|ea|
102 
103  x = not used
104  r = region (interpreted as 0..3)
105  e = elevation for that level
106  a = azimuth for that level
107  deepest level (30) in LSBs
108 
109 
110 
111  Bit encoding used the quadtree:
112  |-------------------------|
113  | 1010 | 1011| |
114  |-----10-----| 11 |
115  | 1000 | 1001| |
116  |-------------------------|
117  | | |
118  | 00 | 01 |
119  | | |
120  |-------------------------|
121  */
122  static UInt64 my_pdep_u64(UInt64 val, UInt64 mask) {
123  UInt64 res = 0;
124  for (UInt64 bb = 1; mask; bb += bb) {
125  if (val & bb)
126  res |= mask & -mask;
127  mask &= mask - 1;
128  }
129  return res;
130  }
131 
132  static UInt64 geocentricToSpatialKey(const Vector3d& pos)
133  {
134  //convert geocentric position to a spherical coordinate, normalize values to [0 - 2pi]
135  Vector2d spos = geocToSpherical(pos);
136 
137  //we're going to use 30 bits in our Morton code. Gives us about 3cm regions
138 
139  static const UInt64 quantization = 1ULL << BITS_PER_COMPONENT;
140  static const UInt64 bitmask = (1ULL << (BITS_PER_COMPONENT * 2)) - 1; //mask for bottom 60 bits
141 
142  //we're going to use 30 bits (about 3cm resolution at earth radius) in our Morton code, so quantize the spherical coordinates to that
143  static double qauntizationFactor = quantization / 6.283185307179586;
144 
145  //get quantized values
146  UInt64 azimuth = spos.x * qauntizationFactor;
147  UInt64 elevation = spos.y * qauntizationFactor;
148 
149  UInt64 region = azimuth / (quantization / 4); //which of the 4 world wide diamonds is it in, integer division should work
150 
151  //scatter the bits
152  azimuth = my_pdep_u64(azimuth, 0x5555555555555555); //scatter azimuth (x value) to the odd bits so the x value changes the least significant bit in a tuple
153  elevation = my_pdep_u64(elevation, 0xaaaaaaaaaaaaaaaa); //scatter elevation (y value) the even bits so the y value changes the most significant bit in a tuple
154 
155  //merge the values together
156  UInt64 ret = region << (BITS_PER_COMPONENT * 2);
157  ret |= azimuth;
158  ret |= elevation;
159 
160  return ret;
161  }
162 
164  protected:
165  struct Page;
166  struct Node;
167 
168  void growNodes(int count);
169  void growPages(int count);
170 
172  int findNodeToAdd(const UInt64 key);
173  Bool needSplit(const NodeIndex nodeIdx) const;
174  Bool canSplit(const NodeIndex nodeIdx) const;
176  int nextNode();
177  int nextPage();
178  NodeIndex findNodeContaining(NodeIndex, const UInt64 key) const;
179  NodeIndex findCommonRootNode(NodeIndex idx, const UInt64 key1, const UInt64 key2) const;
180 
181  bool generateMinMaxKeys(const Vector3d& pos1, const Vector3d& pos2, UInt64 &minKey, UInt64 &maxKey, Vector3d &minPos, Vector3d &maxPos) const;
182  bool generateMinMaxKeys(const Vector3d& pos1, const Vector3d& pos2, UInt64 &minKey, UInt64 &maxKey) const;
183 
184  UInt32 objectsWithinNode(const NodeIndex idx, const UInt64 minKey, const UInt64 maxKey,
185  const Vector3d& absMin, const Vector3d& absMax, std::vector<DATA_ID> &result) const;
186  UInt32 objectsWithinNode(const NodeIndex idx, const UInt64 minKey, const UInt64 maxKey,
187  const Vector3d& pos1, const Vector3d& pos2, const double distance,
188  std::vector<DATA_ID> &result) const;
189  UInt32 objectsWithinNode(const NodeIndex idx, const Vector2d& latLong1, const Vector2d& latLong2, std::vector<DATA_ID> &result) const;
190 
191  void eraseFromPage(Page& p, DATA_ID globalId);
192  void addToPage(Page& p, DATA_ID globalId, UInt64 key, const Vector3d& loc);
193 
194  bool contains(UInt64 key, NodeIndex n);
195 
196  bool isPositionWithin(const Vector3d &min, const Vector3d &max, const Vector3d &pos) const;
197  bool isPositionWithin(const Vector2d &latLong1, const Vector2d &latLong2, const Vector3d &pos) const;
198 
201  static const UInt64 MASK_AZIMUTH_ODD_BITS = (0x5555555555555555ULL);
202  static const UInt64 MASK_ELEVATION_EVEN_BITS = (0xAAAAAAAAAAAAAAAAULL);
203  static const UInt64 MASK_AZIMUTH_OBITS_REVERSE = ~(0x5555555555555555ULL);
204  static const UInt64 MASK_ELEVATION_EBITS_REVERSE = ~(0xAAAAAAAAAAAAAAAAULL);
205 
206  static const int SpatialMaxNodeDepth = 31;
207  struct Node
208  {
209  UInt64 key = 0;
210  UInt32 children[4] = {0, 0, 0, 0};
213  };
214 
215  struct Entry
216  {
217  UInt64 key = 0;
218  DATA_ID globalId = DATA_ID();
219  Vector3d location;
220  };
221 
222  static const int thePageSize = 100;
223 
224  struct Page
225  {
226  int count = 0;
227  std::vector<Entry> data;
228  std::shared_ptr<tbb::spin_mutex> dataMutex;
229  typedef tbb::spin_mutex::scoped_lock DataMutexLock;
230 
232  {
233  dataMutex = std::shared_ptr<tbb::spin_mutex>(new tbb::spin_mutex);
234  data.resize(thePageSize);
235  }
236 
237  Page(const Page& orig) : data(orig.data), count(orig.count), dataMutex(orig.dataMutex) {};
238  };
239 
242  std::queue<int> myFreePageQueue;
243  std::vector<Node> myNodes;
244  std::vector<Page> myPages;
245  std::mutex myPageMutex;
246  std::mutex myNodeMutex;
247  //we expect this to be a fairly large data structure of every entity (possibly millions)
248  //with an index to a node
249  std::unordered_map<DATA_ID, int> myReverseLookup;
250  };
251 }
252 
253 #include "spatial.inl"
std::vector< Node > myNodes
Definition: spatial.h:243
Definition: spatial.h:24
unsigned int UInt32
Definition: stateDataTypes.h:18
bool generateMinMaxKeys(const Vector3d &pos1, const Vector3d &pos2, UInt64 &minKey, UInt64 &maxKey, Vector3d &minPos, Vector3d &maxPos) const
std::vector< Page > myPages
Definition: spatial.h:244
Page(const Page &orig)
Definition: spatial.h:237
UInt32 depth
Definition: spatial.h:211
UInt32 myPageCount
Definition: spatial.h:241
std::vector< Entry > data
Definition: spatial.h:227
static const UInt64 MASK_ELEVATION_EBITS_REVERSE
Definition: spatial.h:204
Bool canSplit(const NodeIndex nodeIdx) const
NodeIndex findNodeContaining(NodeIndex, const UInt64 key) const
static const int SpatialMaxNodeDepth
Definition: spatial.h:206
std::mutex myPageMutex
Definition: spatial.h:245
static const int BITS_PER_COMPONENT
Definition: spatial.h:26
void addToPage(Page &p, DATA_ID globalId, UInt64 key, const Vector3d &loc)
Vector3d location
Definition: spatial.h:219
Bool update(const DATA_ID globalId, const Vector3d &pos)
Update entity&#39;s location.
bool isWholeGlobeRegion(const Vector3d &pos1, const Vector3d &pos2) const
SpatialIndex()
Public APIs.
std::unordered_map< DATA_ID, int > myReverseLookup
Definition: spatial.h:249
UInt8 Bool
Definition: stateDataTypes.h:20
std::mutex myNodeMutex
Definition: spatial.h:246
DT_DLL_VRVCORE double distance(const makVrv::DtCoordinateSystem &, const DtVector &from, const DtVector &to)
Returns the distance from the two points. Coordinates are in local database coordinates The coordinat...
bool isPositionWithin(const Vector3d &min, const Vector3d &max, const Vector3d &pos) const
unsigned int NodeIndex
Definition: spatial.h:34
int findNodeToAdd(const UInt64 key)
NodeIndex findNodeToAdd(const UInt64 key);.
Definition: spatial.h:215
Bool needSplit(const NodeIndex nodeIdx) const
UInt64 key
Definition: spatial.h:217
bool isDistanceToLineWithin(const Vector3d &pointC, double distance, const Vector3d &lineA, const Vector3d &lineB) const
Find the shortest distance from the point C to the line.
void reset()
Reset the entity entries, but maintains the node tree since that is likely useful the next frame...
unsigned long long UInt64
Definition: stateDataTypes.h:17
DATA_ID globalId
Definition: spatial.h:218
Definition: spatial.h:207
void growNodes(int count)
NodeIndex findCommonRootNode(NodeIndex idx, const UInt64 key1, const UInt64 key2) const
unsigned int PageIndex
Definition: spatial.h:35
std::vector< DATA_ID > objectsNearChord(const Vector3d &pointA, const Vector3d &pointB, const double delta) const
Find objects within the delta distance from the chord defined by npoint A &amp; B.
static const UInt64 MASK_ELEVATION_EVEN_BITS
Definition: spatial.h:202
tbb::spin_mutex::scoped_lock DataMutexLock
Definition: spatial.h:229
UInt32 pageIdx
Definition: spatial.h:212
UInt32 objectsWithinNode(const NodeIndex idx, const UInt64 minKey, const UInt64 maxKey, const Vector3d &absMin, const Vector3d &absMax, std::vector< DATA_ID > &result) const
UInt32 myNodeCount
Definition: spatial.h:240
void eraseFromPage(Page &p, DATA_ID globalId)
UInt32 children[4]
Definition: spatial.h:210
static Vector2d geocToSpherical(const Vector3d &pos)
spatial key generation specific static member functions
Definition: spatial.h:70
bool contains(UInt64 key, NodeIndex n)
static const int thePageSize
Definition: spatial.h:222
std::shared_ptr< tbb::spin_mutex > dataMutex
Definition: spatial.h:228
std::vector< DATA_ID > spatialTreeWalk(UInt32 &totalDataCnt) const
Definition: spatial.h:224
static const UInt64 MASK_AZIMUTH_OBITS_REVERSE
Definition: spatial.h:203
Bool insert(const DATA_ID globalId, const Vector3d &pos)
Generate spatial key and insert entity&#39;s global ID and location into spatial tree.
static UInt64 geocentricToSpatialKey(const Vector3d &pos)
Definition: spatial.h:132
int count
Definition: spatial.h:226
static const UInt64 MASK_AZIMUTH_ODD_BITS
60 bits masks used to encode or decode spatial keys (AZIMUTH, ELEVATION), Top 2 most significant bits...
Definition: spatial.h:201
static UInt64 my_pdep_u64(UInt64 val, UInt64 mask)
Definition: spatial.h:122
std::vector< DATA_ID > objectsWithin(const Vector3d &min, const Vector3d &max) const
Find objects within the area of min and max locations, including the min &amp; max locations.
std::vector< DATA_ID > nearestNeighbors(const Vector3d &loc, double distanceInMeter) const
Find nearest neighbors from the input location, neighbors x/y/z locations are &lt;= distanceInMeter. It returns an array of global IDs.
static const int BITS_PER_COMPONENT_AND_REGION
Definition: spatial.h:27
Page()
Definition: spatial.h:231
Bool splitNode(NodeIndex n)
void growPages(int count)
UInt64 key
Definition: spatial.h:209
std::queue< int > myFreePageQueue
Definition: spatial.h:242

Document ID: Generated on Thu Jun 1 17:58:13 EDT 2023 from SVN revision 255404
Copyright © 2005-2021 MAK Technologies. All Rights Reserved (www.mak.com)