blue_noise.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2020 Alec Jacobson <[email protected]>
  4. //
  5. // This Source Code Form is subject to the terms of the Mozilla Public License
  6. // v. 2.0. If a copy of the MPL was not distributed with this file, You can
  7. // obtain one at http://mozilla.org/MPL/2.0/.
  8. #include "blue_noise.h"
  9. #include "doublearea.h"
  10. #include "random_points_on_mesh.h"
  11. #include "slice.h"
  12. #include "sortrows.h"
  13. #include "PI.h"
  14. #include "get_seconds.h"
  15. #include <unordered_map>
  16. #include <algorithm>
  17. #include <vector>
  18. #include <random>
  19. namespace igl
  20. {
  21. // It is very important that we use 64bit keys to avoid out of bounds (easy to
  22. // get to happen with dense samplings (e.g., r = 0.0005*bbd)
  23. typedef int64_t BlueNoiseKeyType;
  24. }
  25. // Helper functions
  26. namespace igl
  27. {
  28. // Should probably find and replace with less generic name
  29. //
  30. // Map 3D subscripts (x,y,z) to unique index (return value)
  31. //
  32. // Inputs:
  33. // w side length of w×w×w integer cube lattice
  34. // x subscript along x direction
  35. // y subscript along y direction
  36. // z subscript along z direction
  37. // Returns index value
  38. //
  39. inline BlueNoiseKeyType blue_noise_key(
  40. const BlueNoiseKeyType w, // pass by copy --> int64_t so that multiplication is OK
  41. const BlueNoiseKeyType x, // pass by copy --> int64_t so that multiplication is OK
  42. const BlueNoiseKeyType y, // pass by copy --> int64_t so that multiplication is OK
  43. const BlueNoiseKeyType z) // pass by copy --> int64_t so that multiplication is OK
  44. {
  45. return x+w*(y+w*z);
  46. }
  47. // Determine if a query candidate at position X.row(i) is too close to already
  48. // selected sites (stored in S).
  49. //
  50. // Inputs:
  51. // X #X by 3 list of raw candidate positions
  52. // Xs #Xs by 3 list of corresponding integer cell subscripts
  53. // i index of candidate in question
  54. // S map from cell index to index into X of selected candidate (or -1 if
  55. // cell is currently empty)
  56. // rr Poisson disk radius squared
  57. // w side length of w×w×w integer cube lattice (into which Xs subscripts)
  58. template <
  59. typename DerivedX,
  60. typename DerivedXs>
  61. inline bool blue_noise_far_enough(
  62. const Eigen::MatrixBase<DerivedX> & X,
  63. const Eigen::MatrixBase<DerivedXs> & Xs,
  64. const std::unordered_map<BlueNoiseKeyType,int> & S,
  65. const double & rr,
  66. const int & w,
  67. const int i)
  68. {
  69. const int xi = Xs(i,0);
  70. const int yi = Xs(i,1);
  71. const int zi = Xs(i,2);
  72. BlueNoiseKeyType k = blue_noise_key(w,xi,yi,zi);
  73. int g = 2; // ceil(r/s)
  74. for(int x = std::max(xi-g,0);x<=std::min(xi+g,w-1);x++)
  75. for(int y = std::max(yi-g,0);y<=std::min(yi+g,w-1);y++)
  76. for(int z = std::max(zi-g,0);z<=std::min(zi+g,w-1);z++)
  77. {
  78. if(x!=xi || y!=yi || z!=zi)
  79. {
  80. const BlueNoiseKeyType nk = blue_noise_key(w,x,y,z);
  81. // have already selected from this cell
  82. const auto Siter = S.find(nk);
  83. if(Siter !=S.end() && Siter->second >= 0)
  84. {
  85. const int ni = Siter->second;
  86. // too close
  87. if( (X.row(i)-X.row(ni)).squaredNorm() < rr)
  88. {
  89. return false;
  90. }
  91. }
  92. }
  93. }
  94. return true;
  95. }
  96. // Try to activate a candidate in a given cell
  97. //
  98. // Inputs:
  99. // X #X by 3 list of raw candidate positions
  100. // Xs #Xs by 3 list of corresponding integer cell subscripts
  101. // rr Poisson disk radius squared
  102. // w side length of w×w×w integer cube lattice (into which Xs subscripts)
  103. // nk index of cell in which we'd like to activate a candidate
  104. // M map from cell index to list of candidates
  105. // S map from cell index to index into X of selected candidate (or -1 if
  106. // cell is currently empty)
  107. // active list of indices into X of active candidates
  108. // Outputs:
  109. // M visited candidates deemed too close to already selected points are
  110. // removed
  111. // S updated to reflect activated point (if successful)
  112. // active updated to reflect activated point (if successful)
  113. // Returns true iff activation was successful
  114. template <
  115. typename DerivedX,
  116. typename DerivedXs>
  117. inline bool activate(
  118. const Eigen::MatrixBase<DerivedX> & X,
  119. const Eigen::MatrixBase<DerivedXs> & Xs,
  120. const double & rr,
  121. const int & i,
  122. const int & w,
  123. const BlueNoiseKeyType & nk,
  124. std::unordered_map<BlueNoiseKeyType,std::vector<int> > & M,
  125. std::unordered_map<BlueNoiseKeyType,int> & S,
  126. std::vector<int> & active)
  127. {
  128. assert(M.count(nk));
  129. auto & Mvec = M.find(nk)->second;
  130. auto miter = Mvec.begin();
  131. while(miter != Mvec.end())
  132. {
  133. const int mi = *miter;
  134. // mi is our candidate sample. Is it far enough from all existing
  135. // samples?
  136. if(i>=0 && (X.row(i)-X.row(mi)).squaredNorm() > 4.*rr)
  137. {
  138. // too far skip (reject)
  139. miter++;
  140. } else if(blue_noise_far_enough(X,Xs,S,rr,w,mi))
  141. {
  142. active.push_back(mi);
  143. S.find(nk)->second = mi;
  144. //printf(" found %d\n",mi);
  145. return true;
  146. }else
  147. {
  148. // remove forever (instead of incrementing we swap and eat from the
  149. // back)
  150. //std::swap(*miter,Mvec.back());
  151. *miter = Mvec.back();
  152. bool was_last = (std::next(miter) == Mvec.end());
  153. Mvec.pop_back();
  154. if (was_last) {
  155. // popping from the vector can invalidate the iterator, if it was
  156. // pointing to the last element that was popped. Alternatively,
  157. // one could use indices directly...
  158. miter = Mvec.end();
  159. }
  160. }
  161. }
  162. return false;
  163. }
  164. template <
  165. typename DerivedX,
  166. typename DerivedXs,
  167. typename URBG>
  168. inline bool step(
  169. const Eigen::MatrixBase<DerivedX> & X,
  170. const Eigen::MatrixBase<DerivedXs> & Xs,
  171. const double & rr,
  172. const int & w,
  173. URBG && urbg,
  174. std::unordered_map<BlueNoiseKeyType,std::vector<int> > & M,
  175. std::unordered_map<BlueNoiseKeyType,int> & S,
  176. std::vector<int> & active,
  177. std::vector<int> & collected
  178. )
  179. {
  180. //considered.clear();
  181. if(active.size() == 0) return false;
  182. // random entry
  183. std::uniform_int_distribution<> dis(0, active.size()-1);
  184. const int e = dis(urbg);
  185. const int i = active[e];
  186. //printf("%d\n",i);
  187. const int xi = Xs(i,0);
  188. const int yi = Xs(i,1);
  189. const int zi = Xs(i,2);
  190. //printf("%d %d %d - %g %g %g\n",xi,yi,zi,X(i,0),X(i,1),X(i,2));
  191. // cell indices of neighbors
  192. int g = 4;
  193. std::vector<BlueNoiseKeyType> N;N.reserve((1+g*1)^3-1);
  194. for(int x = std::max(xi-g,0);x<=std::min(xi+g,w-1);x++)
  195. for(int y = std::max(yi-g,0);y<=std::min(yi+g,w-1);y++)
  196. for(int z = std::max(zi-g,0);z<=std::min(zi+g,w-1);z++)
  197. {
  198. if(x!=xi || y!=yi || z!=zi)
  199. {
  200. //printf(" %d %d %d\n",x,y,z);
  201. const BlueNoiseKeyType nk = blue_noise_key(w,x,y,z);
  202. // haven't yet selected from this cell?
  203. const auto Siter = S.find(nk);
  204. if(Siter !=S.end() && Siter->second < 0)
  205. {
  206. assert(M.find(nk) != M.end());
  207. N.emplace_back(nk);
  208. }
  209. }
  210. }
  211. //printf(" --------\n");
  212. // randomize order: this might be a little paranoid...
  213. std::shuffle(std::begin(N), std::end(N), urbg);
  214. bool found = false;
  215. for(const BlueNoiseKeyType & nk : N)
  216. {
  217. assert(M.find(nk) != M.end());
  218. if(activate(X,Xs,rr,i,w,nk,M,S,active))
  219. {
  220. found = true;
  221. break;
  222. }
  223. }
  224. if(!found)
  225. {
  226. // remove i from active list
  227. // https://stackoverflow.com/a/60765833/148668
  228. collected.push_back(i);
  229. //printf(" before: "); for(const int j : active) { printf("%d ",j); } printf("\n");
  230. std::swap(active[e], active.back());
  231. //printf(" after : "); for(const int j : active) { printf("%d ",j); } printf("\n");
  232. active.pop_back();
  233. //printf(" removed %d\n",i);
  234. }
  235. //printf(" active: "); for(const int j : active) { printf("%d ",j); } printf("\n");
  236. return true;
  237. }
  238. }
  239. template <
  240. typename DerivedV,
  241. typename DerivedF,
  242. typename DerivedB,
  243. typename DerivedFI,
  244. typename DerivedP,
  245. typename URBG>
  246. IGL_INLINE void igl::blue_noise(
  247. const Eigen::MatrixBase<DerivedV> & V,
  248. const Eigen::MatrixBase<DerivedF> & F,
  249. const typename DerivedV::Scalar r,
  250. Eigen::PlainObjectBase<DerivedB> & B,
  251. Eigen::PlainObjectBase<DerivedFI> & FI,
  252. Eigen::PlainObjectBase<DerivedP> & P,
  253. URBG && urbg)
  254. {
  255. typedef typename DerivedV::Scalar Scalar;
  256. typedef Eigen::Matrix<Scalar,Eigen::Dynamic,1> VectorXS;
  257. // float+RowMajor is faster...
  258. typedef Eigen::Matrix<Scalar,Eigen::Dynamic,3,Eigen::RowMajor> MatrixX3S;
  259. assert(V.cols() == 3 && "Only 3D embeddings allowed");
  260. // minimum radius
  261. const Scalar min_r = r;
  262. // cell size based on 3D distance
  263. // It works reasonably well (but is probably biased to use s=2*r/√3 here and
  264. // g=1 in the outer loop below.
  265. //
  266. // One thing to try would be to store a list in S (rather than a single point)
  267. // or equivalently a mask over M and just use M as a generic spatial hash
  268. // (with arbitrary size) and then tune its size (being careful to make g a
  269. // function of r and s; and removing the `if S=-1 checks`)
  270. const Scalar s = r/sqrt(3.0);
  271. const double area =
  272. [&](){Eigen::VectorXd A;igl::doublearea(V,F,A);return A.array().sum()/2;}();
  273. // Circle packing in the plane has igl::PI*sqrt(3)/6 efficiency
  274. const double expected_number_of_points =
  275. area * (igl::PI * sqrt(3.0) / 6.0) / (igl::PI * min_r * min_r / 4.0);
  276. // Make a uniform random sampling with 30*expected_number_of_points.
  277. const int nx = 30.0*expected_number_of_points;
  278. MatrixX3S X,XB;
  279. Eigen::VectorXi XFI;
  280. igl::random_points_on_mesh(nx,V,F,XB,XFI,X,urbg);
  281. // Rescale so that s = 1
  282. Eigen::Matrix<int,Eigen::Dynamic,3,Eigen::RowMajor> Xs =
  283. ((X.rowwise()-X.colwise().minCoeff())/s).template cast<int>();
  284. const int w = Xs.maxCoeff()+1;
  285. {
  286. Eigen::VectorXi I;
  287. igl::sortrows(decltype(Xs)(Xs),true,Xs,I);
  288. igl::slice(decltype(X)(X),I,1,X);
  289. // These two could be spun off in their own thread.
  290. igl::slice(decltype(XB)(XB),I,1,XB);
  291. igl::slice(decltype(XFI)(XFI),I,1,XFI);
  292. }
  293. // Initialization
  294. std::unordered_map<BlueNoiseKeyType,std::vector<int> > M;
  295. std::unordered_map<BlueNoiseKeyType, int > S;
  296. // attempted to seed
  297. std::unordered_map<BlueNoiseKeyType, int > A;
  298. // Q: Too many?
  299. // A: Seems to help though.
  300. M.reserve(Xs.rows());
  301. S.reserve(Xs.rows());
  302. for(int i = 0;i<Xs.rows();i++)
  303. {
  304. BlueNoiseKeyType k = blue_noise_key(w,Xs(i,0),Xs(i,1),Xs(i,2));
  305. const auto Miter = M.find(k);
  306. if(Miter == M.end())
  307. {
  308. M.insert({k,{i}});
  309. }else
  310. {
  311. Miter->second.push_back(i);
  312. }
  313. S.emplace(k,-1);
  314. A.emplace(k,false);
  315. }
  316. std::vector<int> active;
  317. // precompute r²
  318. // Q: is this necessary?
  319. const double rr = r*r;
  320. std::vector<int> collected;
  321. collected.reserve(2.0*expected_number_of_points);
  322. auto Mouter = M.begin();
  323. // Just take the first point as the initial seed
  324. const auto initialize = [&]()->bool
  325. {
  326. while(true)
  327. {
  328. if(Mouter == M.end())
  329. {
  330. return false;
  331. }
  332. const BlueNoiseKeyType k = Mouter->first;
  333. // Haven't placed in this cell yet
  334. if(S[k]<0)
  335. {
  336. if(activate(X,Xs,rr,-1,w,k,M,S,active)) return true;
  337. }
  338. Mouter++;
  339. }
  340. assert(false && "should not be reachable.");
  341. };
  342. // important if mesh contains many connected components
  343. while(initialize())
  344. {
  345. while(active.size()>0)
  346. {
  347. step(X,Xs,rr,w,urbg,M,S,active,collected);
  348. }
  349. }
  350. {
  351. const int n = collected.size();
  352. P.resize(n,3);
  353. B.resize(n,3);
  354. FI.resize(n);
  355. for(int i = 0;i<n;i++)
  356. {
  357. const int c = collected[i];
  358. P.row(i) = X.row(c).template cast<typename DerivedP::Scalar>();
  359. B.row(i) = XB.row(c).template cast<typename DerivedB::Scalar>();
  360. FI(i) = XFI(c);
  361. }
  362. }
  363. }
  364. #ifdef IGL_STATIC_LIBRARY
  365. // Explicit template instantiation
  366. template void igl::blue_noise<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, std::mt19937_64 >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::Matrix<double, -1, -1, 0, -1, -1>::Scalar, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, std::mt19937_64&&);
  367. template void igl::blue_noise<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, std::mt19937 >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::Matrix<double, -1, -1, 0, -1, -1>::Scalar, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, std::mt19937&&);
  368. #endif