split_nonmanifold.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2022 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 "split_nonmanifold.h"
  9. #include "unique_edge_map.h"
  10. #include "connected_components.h"
  11. #include "unique.h"
  12. #include "sort.h"
  13. #include "triangle_triangle_adjacency.h"
  14. #include "placeholders.h"
  15. #include "is_edge_manifold.h"
  16. #include <unordered_map>
  17. #include <cassert>
  18. #include <type_traits>
  19. #include "is_vertex_manifold.h"
  20. #include "matlab_format.h"
  21. #include <iostream>
  22. #include <unordered_set>
  23. #include <utility>
  24. template <
  25. typename DerivedF,
  26. typename DerivedSF,
  27. typename DerivedSVI
  28. >
  29. IGL_INLINE void igl::split_nonmanifold(
  30. const Eigen::MatrixBase<DerivedF> & F,
  31. Eigen::PlainObjectBase <DerivedSF> & SF,
  32. Eigen::PlainObjectBase <DerivedSVI> & SVI)
  33. {
  34. using Scalar = typename DerivedSF::Scalar;
  35. // Scalar must allow negative values
  36. static_assert(std::is_signed<Scalar>::value,"Scalar must be signed");
  37. using MatrixX2I = Eigen::Matrix<Scalar,Eigen::Dynamic,2>;
  38. using MatrixX3I = Eigen::Matrix<Scalar,Eigen::Dynamic,3>;
  39. using VectorXI = Eigen::Matrix< Scalar,Eigen::Dynamic,1>;
  40. MatrixX2I E,uE;
  41. VectorXI EMAP,uEC,uEE;
  42. igl::unique_edge_map(F,E,uE,EMAP,uEC,uEE);
  43. // Let's assume the most convenient connectivity data structure and worry
  44. // about performance later
  45. // There are always 3#F "corners".
  46. //
  47. // V[c] = v means that corner c is mapped to new-vertex v
  48. // Start with all corners mapped to singleton new-vertices
  49. Eigen::VectorXi V = Eigen::VectorXi::LinSpaced(F.size(),0,F.size()-1);
  50. // Convenience map so that CF(f,i) = V[c] = v where c is the ith corner of
  51. // face f.
  52. Eigen::Map<Eigen::MatrixXi> CF = Eigen::Map<Eigen::MatrixXi>(V.data(),F.rows(),F.cols());
  53. // C[v][j] = c means that c is the jth corner in the group of corners at
  54. // new-vertex v. As we merge these, we will clear "dead" new-vertices.
  55. std::vector<std::vector<int> > C(F.size());
  56. for(int i = 0;i<F.size();i++) { C[i] = {i}; }
  57. const int m = F.rows();
  58. // O(S) where S = |star(v)|
  59. // @param[in] v new-vertex index
  60. // @return list of face indices incident on new-vertex v
  61. const auto star = [&](const int v)->std::vector<int>
  62. {
  63. std::vector<int> faces(C[v].size());
  64. for(int i = 0;i<C[v].size();i++)
  65. {
  66. faces[i] = C[v][i]%m;
  67. }
  68. return faces;
  69. };
  70. // O(S) where S = |star(v)|
  71. // @param[in] v new-vertex index
  72. // @return list of half-edge indices incident on new-vertex v
  73. const auto nonmanifold_edge_star = [&](const int v)->std::vector<int>
  74. {
  75. std::vector<int> edges;
  76. // loop over edges opposite corners of v
  77. for(int e : C[v])
  78. {
  79. const int f = e%m;
  80. for(int j = 1;j<3;j++)
  81. {
  82. // next edge
  83. const int e1 = (e+j*m)%(3*m);
  84. const int u1 = EMAP(e1);
  85. if(uEC(u1+1)-uEC(u1) > 2)
  86. {
  87. edges.push_back(e1);
  88. }
  89. }
  90. }
  91. return edges;
  92. };
  93. // O(S) where S = |star(v)|
  94. const std::function<void(
  95. Eigen::VectorXi &,
  96. std::vector<std::vector<int> > &,
  97. const int, const int)> merge_vertex =
  98. [&merge_vertex](Eigen::VectorXi & V,
  99. std::vector<std::vector<int> > & C,
  100. const int u, const int v)
  101. {
  102. if(u == v) { return; }
  103. if(u > v) { merge_vertex(V,C,v,u); return; }
  104. assert(u < v);
  105. // Consider each corner in v
  106. for(const int c : C[v])
  107. {
  108. V[c] = u;
  109. }
  110. // Merge C[v] into C[u]
  111. C[u].insert(C[u].end(),C[v].begin(),C[v].end());
  112. C[v].clear();
  113. };
  114. // O(S) where S is the size of the star of e's first vertex.
  115. // This could probably be O(1) with careful bookkeeping
  116. const auto is_boundary = [&](const int e)->bool
  117. {
  118. // e----d
  119. // \ |
  120. // \f₁↑
  121. // \ |
  122. // s
  123. const int s = (e+1*m)%(3*m);
  124. const int d = (e+2*m)%(3*m);
  125. const int f = e%m;
  126. const int vs = V[s];
  127. const int vd = V[d];
  128. // Consider every face in the star of s
  129. for(const int g : star(vs))
  130. {
  131. if(g == f) { continue; }
  132. // Consider each edge in g
  133. for(int i = 0;i<3;i++)
  134. {
  135. const int a = (g+(i+1)*m)%(3*m);
  136. const int b = (g+(i+2)*m)%(3*m);
  137. // Is that edge the same as e?
  138. if(V[a] == vd && V[b] == vs) { return false; }
  139. if(V[a] == vs && V[b] == vd) { return false; }
  140. }
  141. }
  142. return true;
  143. };
  144. // Ω(m) and probably O(m log m) or worse.
  145. // This should take in the candidate merge edge pair, extract the submesh and
  146. // just check if that's manifold. Then it would be O(S) where S is the size of
  147. // biggest star of the edges' vertices.
  148. //
  149. // My guess is that is_edge_manifold is O(m) but is_vertex_manifold is
  150. // O(max(F))
  151. const auto is_manifold = [](Eigen::MatrixXi F)->bool
  152. {
  153. Eigen::Array<bool,Eigen::Dynamic,1> referenced =
  154. Eigen::Array<bool,Eigen::Dynamic,1>::Zero(F.maxCoeff()+1,1);
  155. for(int i = 0;i<F.size();i++)
  156. {
  157. referenced(F(i)) = true;
  158. }
  159. Eigen::Array<bool,Eigen::Dynamic,1> VM;
  160. igl::is_vertex_manifold(F,VM);
  161. for(int i = 0;i<VM.size();i++)
  162. {
  163. if(referenced(i) && !VM(i))
  164. {
  165. return false;
  166. }
  167. }
  168. return igl::is_edge_manifold(F);
  169. };
  170. // Ω(S) where S is the largest star of (vs1,vd2) or (vd1,vs2)
  171. // I think that is_vertex/edge_manifold(L) is O(|L| log |L|) so I think that
  172. // should make this O(|S| log |S|) with some gross constants because of all
  173. // the copying and sorting things into different data structures.
  174. //
  175. // merging edges (vs1,vd2) and (vd1,vs2) requires merging vertices (vs1→vd1) and
  176. // (vd2→vd2).
  177. //
  178. // Merging vertices (a→b) will change and only change the stars of a and b.
  179. // That is, some vertex c ≠ a,b will have the sam star before and after.
  180. //
  181. // Whether a vertex is singular depends entirely on its star.
  182. //
  183. // Therefore, the only vertices we need to check for non-manifoldness are
  184. // vs=(vs1,vd2) and vd=(vd1,vs2).
  185. const auto simulated_merge_is_manifold =
  186. [&](
  187. const int vs1, const int vd2,
  188. const int vd1, const int vs2)->bool
  189. {
  190. // all_faces[i] = f means that f is the ith face in the list of stars.
  191. std::vector<int> all_faces;
  192. for(int v : {vs1,vd2,vd1,vs2})
  193. {
  194. std::vector<int> star_v = star(v);
  195. all_faces.insert(all_faces.end(),star_v.begin(),star_v.end());
  196. }
  197. // unique_faces[l] = f means that f is the lth unique face in the list of
  198. // stars.
  199. std::vector<int> unique_faces;
  200. std::vector<size_t> _, local;
  201. igl::unique(all_faces,unique_faces,_,local);
  202. Eigen::MatrixXi L(unique_faces.size(),3);
  203. // collect local faces
  204. for(int l = 0;l<unique_faces.size();l++)
  205. {
  206. L.row(l) = CF.row(unique_faces[l]);
  207. }
  208. {
  209. int f = 0;
  210. const auto merge_local = [&](const int v1, const int v2)
  211. {
  212. const int u = std::min(v1,v2);
  213. for(const int v : {v1,v2})
  214. {
  215. for(const int c : C[v])
  216. {
  217. const int i = c/m;
  218. L(local[f++],i) = u;
  219. }
  220. }
  221. };
  222. // must match order {vs1,vd2,vd1,vs2} above
  223. merge_local(vs1,vd2);
  224. merge_local(vd1,vs2);
  225. }
  226. // remove unreferenced vertices by mapping each index in L to a unique
  227. // index between 0 and size(unique(L))
  228. std::unordered_map<int,int> M;
  229. for(int & i : L.reshaped())
  230. {
  231. if(M.find(i) == M.end())
  232. {
  233. M[i] = M.size();
  234. }
  235. i = M[i];
  236. }
  237. // Only need to check if the two vertices being merged are manifold
  238. Eigen::Array<bool,Eigen::Dynamic,1> VM;
  239. const int vs = std::min(vs1,vd2);
  240. const int vd = std::min(vd1,vs2);
  241. igl::is_vertex_manifold(L,VM);
  242. if(!VM(M[vs])) {
  243. return false;
  244. }
  245. if(!VM(M[vd])) {
  246. return false;
  247. }
  248. // Probably only need to check incident edges in star, but this also
  249. // checks link
  250. return igl::is_edge_manifold(L);
  251. };
  252. const auto merge_edge = [&](const int e1, const int e2)
  253. {
  254. // Ideally we would track whether an edge is a boundary so we can just
  255. // assert these. But because of "implied stitches" it's not necessarily just
  256. // e1 and e2 which become non-boundary when e1 and e2 are merged.
  257. //assert(is_boundary(e1));
  258. //assert(is_boundary(e2));
  259. if(!is_boundary(e1) || !is_boundary(e2)) { return false; }
  260. assert(e1 != e2);
  261. if(EMAP(e1) != EMAP(e2)) { return false; }
  262. assert(EMAP(e1) == EMAP(e2));
  263. const int u = EMAP(e1);
  264. const bool consistent = E(e1,0) == E(e2,1);
  265. // skip if inconsistently oriented
  266. if(!consistent) { return false; }
  267. // The code below is assuming merging consistently oriented edges
  268. if(E(e1,1) != E(e2,0))
  269. {
  270. }
  271. assert(E(e1,1) == E(e2,0));
  272. //
  273. // e1--d1 s2--e2
  274. // \ | | /
  275. // \f₁↑ ↓f₂/
  276. // \ | | /
  277. // s1 d2
  278. //
  279. //
  280. // "Cutting and Stitching: Converting Sets of Polygons to Manifold
  281. // Surfaces" [Guéziec et al. 2001]
  282. const int s1 = (e1+1*m)%(3*m);
  283. const int d1 = (e1+2*m)%(3*m);
  284. #ifndef NDEBUG
  285. {
  286. const int f1 = e1 % m;
  287. const int i1 = e1 / m;
  288. const int s1_test = f1 + ((i1+1)%3)*m;
  289. const int d1_test = f1 + ((i1+2)%3)*m;
  290. assert(s1 == s1_test);
  291. assert(d1 == d1_test);
  292. }
  293. #endif
  294. int s2 = (e2+1*m)%(3*m);
  295. int d2 = (e2+2*m)%(3*m);
  296. const int vs1 = V[s1];
  297. const int vd2 = V[d2];
  298. const int vd1 = V[d1];
  299. const int vs2 = V[s2];
  300. #ifdef IGL_SPLIT_NONMANIFOLD_DEBUG
  301. const auto simulated_merge_is_manifold_old = [&]()->bool
  302. {
  303. Eigen::VectorXi V_copy = V;
  304. std::vector<std::vector<int> > C_copy = C;
  305. merge_vertex(V_copy,C_copy,vs1,vd2);
  306. merge_vertex(V_copy,C_copy,vd1,vs2);
  307. Eigen::Map<Eigen::MatrixXi> CF_copy =
  308. Eigen::Map<Eigen::MatrixXi>(V_copy.data(),CF.rows(),CF.cols());
  309. if(!is_manifold(CF_copy)) { return false; }
  310. return true;
  311. };
  312. const bool ret_old = simulated_merge_is_manifold_old();
  313. const bool ret = simulated_merge_is_manifold(vs1,vd2,vd1,vs2);
  314. if(ret != ret_old)
  315. {
  316. assert(false);
  317. }
  318. #endif
  319. // I claim this is completely unnecessary if the unique edge was originally
  320. // manifold.
  321. //
  322. // I also hypothesize that this is unnecessary when conducting depth-first
  323. // traversals starting at a successful merge.
  324. //
  325. // That is, we never need to call this in the current algorithm.
  326. const int edge_valence = uEC(u+1)-uEC(u);
  327. assert(edge_valence >= 2);
  328. if(edge_valence>2 && !simulated_merge_is_manifold(vs1,vd2,vd1,vs2))
  329. {
  330. return false;
  331. }
  332. // Now we can merge
  333. merge_vertex(V,C,vs1,vd2);
  334. merge_vertex(V,C,vd1,vs2);
  335. return true;
  336. };
  337. // Consider each unique edge in the original mesh
  338. // number of faces incident on each unique edge
  339. VectorXI D = uEC.tail(uEC.rows()-1)-uEC.head(uEC.rows()-1);
  340. Eigen::VectorXi uI;
  341. {
  342. VectorXI sD;
  343. igl::sort(D,1,true,sD,uI);
  344. }
  345. const std::function<void(const int)> dfs = [&](const int e)
  346. {
  347. // we just successfully merged e, find all other non-manifold edges sharing
  348. // a current vertex with e and try to merge it too.
  349. const int s = (e+1*m)%(3*m);
  350. const int d = (e+2*m)%(3*m);
  351. for(const int c : {s,d})
  352. {
  353. const int v = V[c];
  354. std::vector<int> nme = nonmanifold_edge_star(v);
  355. // My thinking is that this must be size 0 or 2.
  356. //
  357. // But this seems very not true...
  358. for(int i = 0;i<nme.size();i++)
  359. {
  360. const int e1 = nme[i];
  361. for(int j = i+1;j<nme.size();j++)
  362. {
  363. const int e2 = nme[j];
  364. if(merge_edge(e1,e2))
  365. {
  366. dfs(e2);
  367. }
  368. }
  369. }
  370. }
  371. };
  372. // Every edge starts as a boundary
  373. for(auto u : uI)
  374. {
  375. // if boundary skip
  376. if(uEC(u+1)-uEC(u) == 1) { continue; }
  377. for(int j = uEC(u);j<uEC(u+1);j++)
  378. {
  379. const int e1 = uEE(j);
  380. for(int k = j+1;k<uEC(u+1);k++)
  381. {
  382. const int e2 = uEE(k);
  383. if(merge_edge(e1,e2))
  384. {
  385. // for non-manifold edges, launch search from e1 and e2
  386. if(uEC(u+1)-uEC(u) > 2)
  387. {
  388. dfs(e1);
  389. }
  390. break;
  391. }
  392. }
  393. }
  394. }
  395. // Ideally we'd do this so that all duplicated vertices end up at the end
  396. // rather than scrambling the whole mesh.
  397. {
  398. SVI.resize(F.size());
  399. std::vector<bool> marked(F.size());
  400. Eigen::VectorXi J = Eigen::VectorXi::Constant(F.size(),-1);
  401. SF.resize(F.rows(),F.cols());
  402. {
  403. int nv = 0;
  404. for(int f = 0;f<m;f++)
  405. {
  406. for(int i = 0;i<3;i++)
  407. {
  408. const int c = CF(f,i);
  409. if(J(c) == -1)
  410. {
  411. J(c) = nv;
  412. SVI(nv) = F(f,i);
  413. nv++;
  414. }
  415. SF(f,i) = J(c);
  416. }
  417. }
  418. SVI.conservativeResize(nv);
  419. }
  420. }
  421. }
  422. template <
  423. typename DerivedV,
  424. typename DerivedF,
  425. typename DerivedSV,
  426. typename DerivedSF,
  427. typename DerivedSVI
  428. >
  429. IGL_INLINE void igl::split_nonmanifold(
  430. const Eigen::MatrixBase<DerivedV> & V,
  431. const Eigen::MatrixBase<DerivedF> & F,
  432. Eigen::PlainObjectBase <DerivedSV> & SV,
  433. Eigen::PlainObjectBase <DerivedSF> & SF,
  434. Eigen::PlainObjectBase <DerivedSVI> & SVI)
  435. {
  436. igl::split_nonmanifold(F,SF,SVI);
  437. SV = V(SVI.derived(),igl::placeholders::all);
  438. }
  439. #ifdef IGL_STATIC_LIBRARY
  440. template void igl::split_nonmanifold<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<int, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
  441. #endif