HilbertOrder3D.cpp
1 #include "HilbertOrder3D.hpp"
3 #include <vector>
4 #include <algorithm>
5 #include <ctime>
6 #include <iostream>
7 
8 #define NUMBER_OF_SHAPES 24
9 #define MAX_ROTATION_LENGTH 5
10 #define PI 3.14159
11 
14 {
15 public:
21  bool operator==(HilbertCurve3D_shape & shape);
23  vector<Vector3D> m_vShapePoints;
24 
25 };
26 // Constructor - uses the reference Hilbert curve shape:
28  m_vShapePoints(vector<Vector3D> ())
29 {
30  m_vShapePoints.resize(7);
31  m_vShapePoints[0] = Vector3D(0, 0, -1);
32  m_vShapePoints[1] = Vector3D(0, 1, 0);
33  m_vShapePoints[2] = Vector3D(0, 0, 1);
34  m_vShapePoints[3] = Vector3D(-1, 0, 0);
35  m_vShapePoints[4] = Vector3D(0, 0, -1);
36  m_vShapePoints[5] = Vector3D(0, -1, 0);
37  m_vShapePoints[6] = Vector3D(0, 0, 1);
38 }
39 
40 // Compare to a given Hilbert curve shape by comparing pairs of shape points:
42 {
43  bool b = true;
44  for (size_t ii = 0; ii < m_vShapePoints.size(); ++ii)
45  {
46  b = b && (m_vShapePoints[ii] == shape.m_vShapePoints[ii]);
47  }
48 
49  return b;
50 }
51 
54 {
55 public:
57  HilbertCurve3D(void);
63  size_t Hilbert3D_xyz2d(Vector3D const & rvPoint, int numOfIterations);
64 
65 private:
66  // Rotate a shape according to a given rotation scheme (in-place):
67  void RotateShape(int iShapeIndex, vector<int> vAxes);
68  // Rotate a shape according to rotation index, and return the rotated shape:
69  void RotateShape(HilbertCurve3D_shape const & roShape, HilbertCurve3D_shape & roShapeOut, int iRotationIndex);
76  int GetRotation(int * piRotation, int iRotationIndex);
77  // Find the index of a given shape object:
78  int FindShapeIndex(HilbertCurve3D_shape & roShape);
79  // Create the recursion rule:
80  void BuildRecursionRule();
81  // Create the shape order, for all shapes (the order of octants):
82  void BuildShapeOrder();
83 
84  // Stores all rotated shapes:
85  vector<HilbertCurve3D_shape> m_vRotatedShapes;
86  // Stores all rotation schemes:
87  vector < vector<int> > m_vRotations;
88 
89  // An array of the 8 integers defining the recursion rule of the shape:
90  vector< vector<int> > m_vShapeRecursion;
91 
92  // A 2x2x2 matrix indicating the 3 dimensional shape order
93  // array< array<int , 8 > , NUMBER_OF_SHAPES > m_mShapeOrder;
94  int m_mShapeOrder[NUMBER_OF_SHAPES][2][2][2];
95 };
96 
97 // Constructor - performs all required initiallizations and preprocessing:
99  m_vRotatedShapes(),
100  m_vRotations(),
101  m_vShapeRecursion()
102 {
103  m_vRotatedShapes.resize(NUMBER_OF_SHAPES);
104  m_vRotations.resize(NUMBER_OF_SHAPES);
105  m_vShapeRecursion.resize(NUMBER_OF_SHAPES);
106  for(size_t i=0;i<NUMBER_OF_SHAPES;++i)
107  m_vShapeRecursion[i].resize(8);
108  int rot[MAX_ROTATION_LENGTH];
109  for (int iRotIndex = 1; iRotIndex < NUMBER_OF_SHAPES; ++iRotIndex)
110  {
111  const int iRotLength = GetRotation(rot, iRotIndex);
112  m_vRotations[static_cast<size_t>(iRotIndex)].assign(rot, rot + iRotLength);
113  }
114 
115  for (int ii = 1; ii < NUMBER_OF_SHAPES; ++ii)
116  {
117  RotateShape(ii, m_vRotations[static_cast<size_t>(ii)]);
118  }
119 
120  BuildRecursionRule();
121  BuildShapeOrder();
122 }
123 
124 // FindShapeIndex - returns the index of a shape:
125 int HilbertCurve3D::FindShapeIndex(HilbertCurve3D_shape & roShape)
126 {
127  for (int ii = 0; ii < NUMBER_OF_SHAPES; ++ii)
128  {
129  if (roShape == m_vRotatedShapes[static_cast<size_t>(ii)])
130  {
131  return ii;
132  }
133  }
134  // TODO - manage this kind of return value (error)
135  return -1;
136 }
137 
138 void HilbertCurve3D::BuildRecursionRule()
139 {
140  // Reference recursion rule:
141  m_vShapeRecursion[0][0] = 12;
142  m_vShapeRecursion[0][1] = 16;
143  m_vShapeRecursion[0][2] = 16;
144  m_vShapeRecursion[0][3] = 2;
145  m_vShapeRecursion[0][4] = 2;
146  m_vShapeRecursion[0][5] = 14;
147  m_vShapeRecursion[0][6] = 14;
148  m_vShapeRecursion[0][7] = 10;
149 
150  HilbertCurve3D_shape oTempShape;
151  // What about ii=0? not necessary
152  for (int ii = 0; ii < NUMBER_OF_SHAPES; ++ii)
153  {
154  for (int jj = 0; jj < 8; ++jj)
155  {
156  // Rotate the appropriate block of the reference recursion rule, according to the ii rotation scheme:
157  RotateShape(m_vRotatedShapes[static_cast<size_t>(m_vShapeRecursion[0][static_cast<size_t>(jj)])], oTempShape, ii);
158  // Find the shape index of the rotated shape:
159  m_vShapeRecursion[static_cast<size_t>(ii)][static_cast<size_t>(jj)] = FindShapeIndex(oTempShape);
160  }
161  }
162 
163  return;
164 }
165 
166 // Return the rotation scheme of the ii rotation (manually precalculated):
167 int HilbertCurve3D::GetRotation(int * piRotation, int iRotationIndex)
168 {
169  switch (iRotationIndex)
170  {
171  case 1:
172  piRotation[0] = 1;
173  return 1;
174  case 2:
175  piRotation[0] = 1;
176  piRotation[1] = 1;
177  return 2;
178  case 3:
179  piRotation[0] = 1;
180  piRotation[1] = 1;
181  piRotation[2] = 1;
182  return 3;
183 
184  case 4:
185  piRotation[0] = 2;
186  return 1;
187  case 5:
188  piRotation[0] = 2;
189  piRotation[1] = 2;
190  return 2;
191  case 6:
192  piRotation[0] = 2;
193  piRotation[1] = 2;
194  piRotation[2] = 2;
195  return 3;
196 
197  case 7:
198  piRotation[0] = 3;
199  return 1;
200  case 8:
201  piRotation[0] = 3;
202  piRotation[1] = 3;
203  return 2;
204  case 9:
205  piRotation[0] = 3;
206  piRotation[1] = 3;
207  piRotation[2] = 3;
208  return 3;
209 
210  case 10:
211  piRotation[0] = 1;
212  piRotation[1] = 2;
213  return 2;
214  case 11:
215  piRotation[0] = 2;
216  piRotation[1] = 1;
217  return 2;
218  case 12:
219  piRotation[0] = 3;
220  piRotation[1] = 1;
221  return 2;
222  case 13:
223  piRotation[0] = 2;
224  piRotation[1] = 3;
225  return 2;
226 
227  case 14:
228  piRotation[0] = 1;
229  piRotation[1] = 1;
230  piRotation[2] = 1;
231  piRotation[3] = 3;
232  return 4;
233  case 15:
234  piRotation[0] = 2;
235  piRotation[1] = 2;
236  piRotation[2] = 2;
237  piRotation[3] = 1;
238  return 4;
239  case 16:
240  piRotation[0] = 3;
241  piRotation[1] = 3;
242  piRotation[2] = 3;
243  piRotation[3] = 2;
244  return 4;
245  case 17:
246  piRotation[0] = 3;
247  piRotation[1] = 2;
248  piRotation[2] = 3;
249  piRotation[3] = 2;
250  return 4;
251  case 18:
252  piRotation[0] = 3;
253  piRotation[1] = 2;
254  piRotation[2] = 2;
255  return 3;
256 
257  case 19:
258  piRotation[0] = 1;
259  piRotation[1] = 1;
260  piRotation[2] = 1;
261  piRotation[3] = 2;
262  piRotation[4] = 2;
263  return 5;
264  case 20:
265  piRotation[0] = 3;
266  piRotation[1] = 3;
267  piRotation[2] = 2;
268  return 3;
269  case 21:
270  piRotation[0] = 3;
271  piRotation[1] = 3;
272  piRotation[2] = 1;
273  return 3;
274  case 22:
275  piRotation[0] = 2;
276  piRotation[1] = 2;
277  piRotation[2] = 3;
278  return 3;
279  case 23:
280  piRotation[0] = 1;
281  piRotation[1] = 1;
282  piRotation[2] = 2;
283  return 3;
284 
285  default:
286  return 0;
287  // break;
288  }
289 }
290 
291 // Rotate a shape:
292 void HilbertCurve3D::RotateShape(int iShapeIndex, vector<int> vAxes)
293 {
294  int iSign = 0;
295 
296  for (size_t ii = 0; ii < 7; ++ii)
297  {
298  for (size_t iAx = 0; iAx < vAxes.size(); ++iAx)
299  {
300  // A trick to find the sign of vAxes[iAx]:
301  iSign = (vAxes[iAx] > 0) - (vAxes[iAx] < 0);
302 
303  switch (abs(vAxes[iAx]))
304  {
305  case 1:
306  m_vRotatedShapes[static_cast<size_t>(iShapeIndex)].m_vShapePoints[ii].RotateX( iSign * PI / 2 );
307  break;
308  case 2:
309  m_vRotatedShapes[static_cast<size_t>(iShapeIndex)].m_vShapePoints[ii].RotateY( iSign * PI / 2 );
310  break;
311  case 3:
312  m_vRotatedShapes[static_cast<size_t>(iShapeIndex)].m_vShapePoints[ii].RotateZ( iSign * PI / 2 );
313  break;
314  default:
315  break;
316  }
317  }
318  // Round off the results:
319  m_vRotatedShapes[static_cast<size_t>(iShapeIndex)].m_vShapePoints[ii].Round();
320  }
321 }
322 
323 void HilbertCurve3D::RotateShape(HilbertCurve3D_shape const & roShape, HilbertCurve3D_shape & roShapeOut , int iRotationIndex)
324 {
325  int iSign = 0;
326 
327  vector<int> vAxes = m_vRotations[static_cast<size_t>(iRotationIndex)];
328  roShapeOut = roShape;
329 
330  for (int ii = 0; ii < 7; ++ii)
331  {
332  for (size_t iAx = 0; iAx < vAxes.size(); ++iAx)
333  {
334  // A trick to find the sign of vAxes[iAx]:
335  iSign = (vAxes[iAx] > 0) - (vAxes[iAx] < 0);
336 
337  switch (abs(vAxes[iAx]))
338  {
339  case 1:
340  roShapeOut.m_vShapePoints[static_cast<size_t>(ii)].RotateX(iSign * PI / 2);
341  break;
342  case 2:
343  roShapeOut.m_vShapePoints[static_cast<size_t>(ii)].RotateY(iSign * PI / 2);
344  break;
345  case 3:
346  roShapeOut.m_vShapePoints[static_cast<size_t>(ii)].RotateZ(iSign * PI / 2);
347  break;
348  default:
349  break;
350  }
351  }
352  // Round off the results:
353  roShapeOut.m_vShapePoints[static_cast<size_t>(ii)].Round();
354  }
355 
356  return;
357 }
358 
359 void HilbertCurve3D::BuildShapeOrder()
360 {
361  vector<int> vShapeVerticesX(8);
362  vector<int> vShapeVerticesY(8);
363  vector<int> vShapeVerticesZ(8);
364 
365  vShapeVerticesX[0] = 0;
366  vShapeVerticesY[0] = 0;
367  vShapeVerticesZ[0] = 0;
368 
369  for (size_t iShapeInd = 0; iShapeInd < NUMBER_OF_SHAPES; ++iShapeInd)
370  {
371  for (size_t ii = 0; ii < m_vRotatedShapes[iShapeInd].m_vShapePoints.size(); ++ii)
372  {
373  vShapeVerticesX[ii + 1] = static_cast<int>(vShapeVerticesX[ii] + m_vRotatedShapes[iShapeInd].m_vShapePoints[ii].x);
374  vShapeVerticesY[ii + 1] = static_cast<int>(vShapeVerticesY[ii] + m_vRotatedShapes[iShapeInd].m_vShapePoints[ii].y);
375  vShapeVerticesZ[ii + 1] = static_cast<int>(vShapeVerticesZ[ii] + m_vRotatedShapes[iShapeInd].m_vShapePoints[ii].z);
376  }
377 
378  int iMinX = *min_element(vShapeVerticesX.begin(), vShapeVerticesX.end());
379  int iMinY = *min_element(vShapeVerticesY.begin(), vShapeVerticesY.end());
380  int iMinZ = *min_element(vShapeVerticesZ.begin(), vShapeVerticesZ.end());
381 
382  for (size_t jj = 0; jj < vShapeVerticesX.size(); ++jj)
383  {
384  vShapeVerticesX[jj] -= iMinX;
385  vShapeVerticesY[jj] -= iMinY;
386  vShapeVerticesZ[jj] -= iMinZ;
387  }
388 
389  for (size_t kk = 0; kk < vShapeVerticesX.size(); ++kk)
390  {
391  m_mShapeOrder[iShapeInd][vShapeVerticesX[kk]][vShapeVerticesY[kk]][vShapeVerticesZ[kk]] = static_cast<int>(kk);
392  }
393  }
394 
395  return;
396 }
397 
398 size_t HilbertCurve3D::Hilbert3D_xyz2d(Vector3D const & rvPoint, int numOfIterations)
399 {
400  // Extract the coordinates:
401  double x = rvPoint.x;
402  double y = rvPoint.y;
403  double z = rvPoint.z;
404 
405  // The output distance along the 3D-Hilbert Curve:
406  size_t d = 0;
407 
408  // The current shape index:
409  int iCurrentShape = 0;
410  for (int iN = 1; iN <= numOfIterations; ++iN)
411  {
412  // Calculate the current power of 0.5:
413  const double dbPow2 = (static_cast<double>(1)) / (1 << iN);
414  const bool bX = x > dbPow2;
415  const bool bY = y > dbPow2;
416  const bool bZ = z > dbPow2;
417 
418  x -= dbPow2*bX;
419  y -= dbPow2*bY;
420  z -= dbPow2*bZ;
421 
422  // Multiply the distance by 8 (for every recursion iteration):
423  d = d << 3;
424  const int iOctantNum = m_mShapeOrder[iCurrentShape][bX][bY][bZ];
425  d = d + static_cast<size_t>(iOctantNum);
426  iCurrentShape = m_vShapeRecursion[static_cast<size_t>(iCurrentShape)][static_cast<size_t>(iOctantNum)];
427  }
428 
429  // int a = 0;
430  return d;
431 }
432 
433 vector<size_t> HilbertOrder3D(vector<Vector3D> const& cor)
434 {
435  // If only 1 or 2 points are provided - do not reorder them
436  if ( 2 >= cor.size() )
437  {
438  vector<size_t> vIndSort( cor.size() );
439  for (size_t ii = 0; ii < cor.size(); ++ii)
440  {
441  vIndSort[ii] = ii;
442  }
443  return vIndSort;
444  }
445  // Create a 3D-Hilbert Curve Object:
446  HilbertCurve3D oHilbert;
447 
448  // Allocate an output vector:
449  int N = static_cast<int>(cor.size());
450  vector<size_t> vOut;
451  vOut.reserve(cor.size());
452 
453  // Estimate the number of required iterations:
454  int numOfIterations = EstimateHilbertIterationNum(cor);
455 
456  vector<Vector3D> vAdjustedPoints;
457 
458  // Adjust the points coordinates to the unit cube:
459  AdjustPoints(cor, vAdjustedPoints);
460 
461  // Run throught the points, and calculate the Hilbert distance of each:
462  for (int ii = 0; ii < N; ++ii)
463  {
464  vOut.push_back(oHilbert.Hilbert3D_xyz2d(vAdjustedPoints[static_cast<size_t>(ii)], numOfIterations+6));
465  //vOut.push_back(oHilbert.Hilbert3D_xyz2d(vAdjustedPoints[ii], 2));
466  }
467  // Get the sorting indices:
468  vector<size_t> vIndSort;
469  sort_index(vOut,vIndSort);
470  // Reorder the Hilbert distances vector (according to the sorting indices):
471  reorder( vOut, vIndSort );
472 
473  // Find indices with repeated Hilbert distance:
474  vector<vector<size_t> > vEqualIndices;
475  FindEqualIndices(vOut, vEqualIndices);
476 
477  // If all points have different Hilbert distances, return the sorting indices:
478  if (vEqualIndices.empty())
479  {
480  return vIndSort;
481  }
482  else
483  {
484  for (size_t ii = 0; ii < vEqualIndices.size(); ++ii)
485  {
486  vector<Vector3D> vPointsInner(vEqualIndices[ii].size() );
487  vector<size_t> vIndInner(vEqualIndices[ii].size());
488  vector<size_t> vIndSortInner(vEqualIndices[ii].size());
489  vector<size_t> vIndSortInner_cpy(vEqualIndices[ii].size());
490 
491  // Store the points with the equal indices
492  for (size_t jj = 0; jj < vEqualIndices[ii].size() ; ++jj)
493  {
494  vIndInner[jj] = vIndSort[vEqualIndices[ii][jj]];
495  vPointsInner[jj] = cor[vIndInner[jj]];
496  vIndSortInner_cpy[jj] = vIndSort[vIndInner[jj]];
497  }
498 
499  // Sort the repeated points:
500  vIndSortInner = HilbertOrder3D(vPointsInner);
501  // vector<size_t> vIndSortTemp = vIndSort;
502  for (size_t kk = 0; kk < vIndSortInner.size(); ++kk)
503  {
504  vIndSort[vIndInner[kk]] = vIndSortInner_cpy[vIndSortInner[kk]];
505  }
506  }
507 
508  // Return the sorting indices:
509  return vIndSort;
510  }
511 }
Vector3D RotateZ(Vector3D const &v, double a)
Rotates a 3D-vector around the Z axis.
Definition: Vector3D.cpp:35
void sort_index(const vector< T > &arr, vector< int > &res)
Returns the indeces of a sort.
Definition: utils.hpp:500
double z
Component in the z direction.
Definition: Vector3D.hpp:50
void FindEqualIndices(vector< size_t > const &vD_sorted, vector< vector< size_t > > &vOut)
Scale a vector of 3D points to the unit-cube.
Vector3D RotateX(Vector3D const &v, double a)
Rotates a 3D-vector around the X axis.
Definition: Vector3D.cpp:17
void AdjustPoints(vector< Vector3D > const &vPointsIn, vector< Vector3D > &vPointsOut)
Scale a vector of 3D points to the unit-cube.
Hilbert 3D-space filling curve.
Hilbert 3D Order - Utility functions.
Vector3D RotateY(Vector3D const &v, double a)
Rotates a 3D-vector around the Y axis.
Definition: Vector3D.cpp:26
3D Mathematical vector
Definition: Vector3D.hpp:15
size_t Hilbert3D_xyz2d(Vector3D const &rvPoint, int numOfIterations)
Calculate the Hilbert curve distance of a given point, given a required number of iterations...
vector< size_t > HilbertOrder3D(vector< Vector3D > const &cor)
Returns the 3D-Hilbert curve ordering.
vector< Vector3D > m_vShapePoints
An array of the 7 unit vector steps defining the shape.
HilbertCurve3D(void)
Constructor.
double y
Component in the y direction.
Definition: Vector3D.hpp:47
void reorder(vector< T > &v, vector< size_t > const &order)
Reorder a vector according to an index vector (obtained from the &#39;ordered&#39; function) ...
The elementary Hilbert Curve shape.
double abs(Vector3D const &v)
Norm of a vector.
Definition: Vector3D.cpp:44
int EstimateHilbertIterationNum(vector< Vector3D > const &cor)
Estimate the number of iterations required in the Hilbert Curve, according to the number of points...
bool operator==(HilbertCurve3D_shape &shape)
Comparison.
double x
Component in the x direction.
Definition: Vector3D.hpp:44
HilbertCurve3D_shape()
Class constructor.
Hilbert Curve.