straighten_seams.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2017 Alec Jacobson <alecjacobson@gmail.com>
  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 "straighten_seams.h"
  9. #include "on_boundary.h"
  10. #include "sparse.h"
  11. #include "max.h"
  12. #include "count.h"
  13. #include "any.h"
  14. #include "slice_mask.h"
  15. #include "slice_into.h"
  16. #include "unique_simplices.h"
  17. #include "adjacency_matrix.h"
  18. #include "setxor.h"
  19. #include "edges_to_path.h"
  20. #include "ramer_douglas_peucker.h"
  21. #include "components.h"
  22. #include "list_to_matrix.h"
  23. #include "ears.h"
  24. #include "slice.h"
  25. #include "sum.h"
  26. #include "find.h"
  27. #include <iostream>
  28. template <
  29. typename DerivedV,
  30. typename DerivedF,
  31. typename DerivedVT,
  32. typename DerivedFT,
  33. typename Scalar,
  34. typename DerivedUE,
  35. typename DerivedUT,
  36. typename DerivedOT>
  37. IGL_INLINE void igl::straighten_seams(
  38. const Eigen::MatrixBase<DerivedV> & V,
  39. const Eigen::MatrixBase<DerivedF> & F,
  40. const Eigen::MatrixBase<DerivedVT> & VT,
  41. const Eigen::MatrixBase<DerivedFT> & FT,
  42. const Scalar tol,
  43. Eigen::PlainObjectBase<DerivedUE> & UE,
  44. Eigen::PlainObjectBase<DerivedUT> & UT,
  45. Eigen::PlainObjectBase<DerivedOT> & OT)
  46. {
  47. using namespace Eigen;
  48. // number of faces
  49. assert(FT.rows() == F.rows() && "#FT must == #F");
  50. assert(F.cols() == 3 && "F should contain triangles");
  51. assert(FT.cols() == 3 && "FT should contain triangles");
  52. const int m = F.rows();
  53. // Boundary edges of the texture map and 3d meshes
  54. Array<bool,Dynamic,1> _;
  55. Array<bool,Dynamic,3> BT,BF;
  56. on_boundary(FT,_,BT);
  57. on_boundary(F,_,BF);
  58. assert((!((BF && (BT!=true)).any())) &&
  59. "Not dealing with boundaries of mesh that get 'stitched' in texture mesh");
  60. typedef Matrix<typename DerivedF::Scalar,Dynamic,2> MatrixX2I;
  61. const MatrixX2I ET = (MatrixX2I(FT.rows()*3,2)
  62. <<FT.col(1),FT.col(2),FT.col(2),FT.col(0),FT.col(0),FT.col(1)).finished();
  63. // "half"-edges with indices into 3D-mesh
  64. const MatrixX2I EF = (MatrixX2I(F.rows()*3,2)
  65. <<F.col(1),F.col(2),F.col(2),F.col(0),F.col(0),F.col(1)).finished();
  66. // Find unique (undirected) edges in F
  67. VectorXi EFMAP;
  68. {
  69. MatrixX2I _1;
  70. VectorXi _2;
  71. unique_simplices(EF,_1,_2,EFMAP);
  72. }
  73. Array<bool,Dynamic,1>vBT = Map<Array<bool,Dynamic,1> >(BT.data(),BT.size(),1);
  74. Array<bool,Dynamic,1>vBF = Map<Array<bool,Dynamic,1> >(BF.data(),BF.size(),1);
  75. MatrixX2I OF;
  76. slice_mask(ET,vBT,1,OT);
  77. slice_mask(EF,vBT,1,OF);
  78. VectorXi OFMAP;
  79. slice_mask(EFMAP,vBT,1,OFMAP);
  80. // Two boundary edges on the texture-mapping are "equivalent" to each other on
  81. // the 3D-mesh if their 3D-mesh vertex indices match
  82. SparseMatrix<bool> OEQ;
  83. {
  84. SparseMatrix<bool> OEQR;
  85. sparse(
  86. VectorXi::LinSpaced(OT.rows(),0,OT.rows()-1),
  87. OFMAP,
  88. Array<bool,Dynamic,1>::Ones(OT.rows(),1),
  89. OT.rows(),
  90. m*3,
  91. OEQR);
  92. OEQ = OEQR * OEQR.transpose();
  93. // Remove diagonal
  94. OEQ.prune([](const int r, const int c, const bool)->bool{return r!=c;});
  95. }
  96. // For each edge in OT, for each endpoint, how many _other_ texture-vertices
  97. // are images of all the 3d-mesh vertices in F who map from "corners" in F/FT
  98. // mapping to this endpoint.
  99. //
  100. // Adjacency matrix between 3d-vertices and texture-vertices
  101. SparseMatrix<bool> V2VT;
  102. sparse(
  103. F,
  104. FT,
  105. Array<bool,Dynamic,3>::Ones(F.rows(),F.cols()),
  106. V.rows(),
  107. VT.rows(),
  108. V2VT);
  109. // For each 3d-vertex count how many different texture-coordinates its getting
  110. // from different incident corners
  111. VectorXi DV;
  112. count(V2VT,2,DV);
  113. VectorXi M,I;
  114. max(V2VT,1,M,I);
  115. assert( (M.array() == 1).all() );
  116. VectorXi DT;
  117. // Map counts onto texture-vertices
  118. slice(DV,I,1,DT);
  119. // Boundary in 3D && UV
  120. Array<bool,Dynamic,1> BTF;
  121. slice_mask(vBF, vBT, 1, BTF);
  122. // Texture-vertex is "sharp" if incident on "half-"edge that is not a
  123. // boundary in the 3D mesh but is a boundary in the texture-mesh AND is not
  124. // "cut cleanly" (the vertex is mapped to exactly 2 locations)
  125. Array<bool,Dynamic,1> SV = Array<bool,Dynamic,1>::Zero(VT.rows(),1);
  126. //std::cout<<"#SV: "<<SV.count()<<std::endl;
  127. assert(BTF.size() == OT.rows());
  128. for(int h = 0;h<BTF.size();h++)
  129. {
  130. if(!BTF(h))
  131. {
  132. SV(OT(h,0)) = true;
  133. SV(OT(h,1)) = true;
  134. }
  135. }
  136. //std::cout<<"#SV: "<<SV.count()<<std::endl;
  137. Array<bool,Dynamic,1> CL = DT.array()==2;
  138. SparseMatrix<bool> VTOT;
  139. {
  140. Eigen::MatrixXi I =
  141. VectorXi::LinSpaced(OT.rows(),0,OT.rows()-1).replicate(1,2);
  142. sparse(
  143. OT,
  144. I,
  145. Array<bool,Dynamic,2>::Ones(OT.rows(),OT.cols()),
  146. VT.rows(),
  147. OT.rows(),
  148. VTOT);
  149. Array<int,Dynamic,1> cuts;
  150. count( (VTOT*OEQ).eval(), 2, cuts);
  151. CL = (CL && (cuts.array() == 2)).eval();
  152. }
  153. //std::cout<<"#CL: "<<CL.count()<<std::endl;
  154. assert(CL.size() == SV.size());
  155. for(int c = 0;c<CL.size();c++) if(CL(c)) SV(c) = false;
  156. {}
  157. //std::cout<<"#SV: "<<SV.count()<<std::endl;
  158. {
  159. // vertices at the corner of ears are declared to be sharp. This is
  160. // conservative: for example, if the ear is strictly convex and stays
  161. // strictly convex then the ear won't be flipped.
  162. VectorXi ear,ear_opp;
  163. ears(FT,ear,ear_opp);
  164. //std::cout<<"#ear: "<<ear.size()<<std::endl;
  165. // There might be an ear on one copy, so mark vertices on other copies, too
  166. // ears as they live on the 3D mesh
  167. Array<bool,Dynamic,1> earT = Array<bool,Dynamic,1>::Zero(VT.rows(),1);
  168. for(int e = 0;e<ear.size();e++) earT(FT(ear(e),ear_opp(e))) = 1;
  169. //std::cout<<"#earT: "<<earT.count()<<std::endl;
  170. // Even if ear-vertices are marked as sharp if it changes, e.g., from
  171. // convex to concave then it will _force_ a flip of the ear triangle. So,
  172. // declare that neighbors of ears are also sharp.
  173. SparseMatrix<bool> A;
  174. adjacency_matrix(FT,A);
  175. earT = (earT || (A*earT.matrix()).array()).eval();
  176. //std::cout<<"#earT: "<<earT.count()<<std::endl;
  177. assert(earT.size() == SV.size());
  178. for(int e = 0;e<earT.size();e++) if(earT(e)) SV(e) = true;
  179. //std::cout<<"#SV: "<<SV.count()<<std::endl;
  180. }
  181. {
  182. SparseMatrix<bool> V2VTSV,V2VTC;
  183. slice_mask(V2VT,SV,2,V2VTSV);
  184. Array<bool,Dynamic,1> Cb;
  185. any(V2VTSV,2,Cb);
  186. slice_mask(V2VT,Cb,1,V2VTC);
  187. any(V2VTC,1,SV);
  188. }
  189. //std::cout<<"#SV: "<<SV.count()<<std::endl;
  190. SparseMatrix<bool> OTVT = VTOT.transpose();
  191. int nc;
  192. ArrayXi C;
  193. {
  194. // Doesn't Compile on older Eigen:
  195. //SparseMatrix<bool> A = OTVT * (!SV).matrix().asDiagonal() * VTOT;
  196. SparseMatrix<bool> A = OTVT * (SV!=true).matrix().asDiagonal() * VTOT;
  197. components(A,C);
  198. nc = C.maxCoeff()+1;
  199. }
  200. //std::cout<<"nc: "<<nc<<std::endl;
  201. // New texture-vertex locations
  202. UT = VT;
  203. // Indices into UT of coarse output polygon edges
  204. std::vector<std::vector<typename DerivedUE::Scalar> > vUE;
  205. // loop over each component
  206. std::vector<bool> done(nc,false);
  207. for(int c = 0;c<nc;c++)
  208. {
  209. if(done[c])
  210. {
  211. continue;
  212. }
  213. done[c] = true;
  214. // edges of this component
  215. Eigen::VectorXi Ic;
  216. find(C==c,Ic);
  217. if(Ic.size() == 0)
  218. {
  219. continue;
  220. }
  221. SparseMatrix<bool> OEQIc;
  222. slice(OEQ,Ic,1,OEQIc);
  223. Eigen::VectorXi N;
  224. sum(OEQIc,2,N);
  225. const int ncopies = N(0)+1;
  226. assert((N.array() == ncopies-1).all());
  227. assert((ncopies == 1 || ncopies == 2) &&
  228. "Not dealing with non-manifold meshes");
  229. Eigen::VectorXi vpath,epath,eend;
  230. typedef Eigen::Matrix<Scalar,Eigen::Dynamic,2> MatrixX2S;
  231. switch(ncopies)
  232. {
  233. case 1:
  234. {
  235. MatrixX2I OTIc;
  236. slice(OT,Ic,1,OTIc);
  237. edges_to_path(OTIc,vpath,epath,eend);
  238. Array<bool,Dynamic,1> SVvpath;
  239. slice(SV,vpath,1,SVvpath);
  240. assert(
  241. (vpath(0) != vpath(vpath.size()-1) || !SVvpath.any()) &&
  242. "Not dealing with 1-loops touching 'sharp' corners");
  243. // simple open boundary
  244. MatrixX2S PI;
  245. slice(VT,vpath,1,PI);
  246. const Scalar bbd =
  247. (PI.colwise().maxCoeff() - PI.colwise().minCoeff()).norm();
  248. // Do not collapse boundaries to fewer than 3 vertices
  249. const bool allow_boundary_collapse = false;
  250. Scalar eff_tol = std::min(tol,2.);
  251. VectorXi UIc;
  252. while(true)
  253. {
  254. MatrixX2S UPI,UTvpath;
  255. ramer_douglas_peucker(PI,eff_tol*bbd,UPI,UIc,UTvpath);
  256. slice_into(UTvpath,vpath,1,UT);
  257. if(allow_boundary_collapse)
  258. {
  259. break;
  260. }
  261. if(UPI.rows()>=4)
  262. {
  263. break;
  264. }
  265. eff_tol = eff_tol*0.5;
  266. }
  267. for(int i = 0;i<UIc.size()-1;i++)
  268. {
  269. vUE.push_back({vpath(UIc(i)),vpath(UIc(i+1))});
  270. }
  271. }
  272. break;
  273. case 2:
  274. {
  275. // Find copies
  276. VectorXi Icc;
  277. {
  278. VectorXi II;
  279. Array<bool,Dynamic,1> IV;
  280. SparseMatrix<bool> OEQIcT = OEQIc.transpose().eval();
  281. find(OEQIcT,Icc,II,IV);
  282. assert(II.size() == Ic.size() &&
  283. (II.array() ==
  284. VectorXi::LinSpaced(Ic.size(),0,Ic.size()-1).array()).all());
  285. assert(Icc.size() == Ic.size());
  286. const int cc = C(Icc(0));
  287. Eigen::VectorXi CIcc;
  288. slice(C,Icc,1,CIcc);
  289. assert((CIcc.array() == cc).all());
  290. assert(!done[cc]);
  291. done[cc] = true;
  292. }
  293. Array<bool,Dynamic,1> flipped;
  294. {
  295. MatrixX2I OFIc,OFIcc;
  296. slice(OF,Ic,1,OFIc);
  297. slice(OF,Icc,1,OFIcc);
  298. Eigen::VectorXi XOR,IA,IB;
  299. setxor(OFIc,OFIcc,XOR,IA,IB);
  300. assert(XOR.size() == 0);
  301. flipped = OFIc.array().col(0) != OFIcc.array().col(0);
  302. }
  303. if(Ic.size() == 1)
  304. {
  305. // No change to UT
  306. vUE.push_back({OT(Ic(0),0),OT(Ic(0),1)});
  307. assert(Icc.size() == 1);
  308. vUE.push_back({OT(Icc(0),flipped(0)?1:0),OT(Icc(0),flipped(0)?0:1)});
  309. }else
  310. {
  311. MatrixX2I OTIc;
  312. slice(OT,Ic,1,OTIc);
  313. edges_to_path(OTIc,vpath,epath,eend);
  314. // Flip endpoints if needed
  315. for(int e = 0;e<eend.size();e++)if(flipped(e))eend(e)=1-eend(e);
  316. VectorXi vpathc(epath.size()+1);
  317. for(int e = 0;e<epath.size();e++)
  318. {
  319. vpathc(e) = OT(Icc(epath(e)),eend(e));
  320. }
  321. vpathc(epath.size()) =
  322. OT(Icc(epath(epath.size()-1)),1-eend(eend.size()-1));
  323. assert(vpath.size() == vpathc.size());
  324. Matrix<Scalar,Dynamic,Dynamic> PI(vpath.size(),VT.cols()*2);
  325. for(int p = 0;p<PI.rows();p++)
  326. {
  327. for(int d = 0;d<VT.cols();d++)
  328. {
  329. PI(p, d) = VT( vpath(p),d);
  330. PI(p,VT.cols()+d) = VT(vpathc(p),d);
  331. }
  332. }
  333. const Scalar bbd =
  334. (PI.colwise().maxCoeff() - PI.colwise().minCoeff()).norm();
  335. Matrix<Scalar,Dynamic,Dynamic> UPI,SI;
  336. VectorXi UIc;
  337. ramer_douglas_peucker(PI,tol*bbd,UPI,UIc,SI);
  338. slice_into(SI.leftCols (VT.cols()), vpath,1,UT);
  339. slice_into(SI.rightCols(VT.cols()),vpathc,1,UT);
  340. for(int i = 0;i<UIc.size()-1;i++)
  341. {
  342. vUE.push_back({vpath(UIc(i)),vpath(UIc(i+1))});
  343. }
  344. for(int i = 0;i<UIc.size()-1;i++)
  345. {
  346. vUE.push_back({vpathc(UIc(i)),vpathc(UIc(i+1))});
  347. }
  348. }
  349. }
  350. break;
  351. default:
  352. assert(false && "Should never reach here");
  353. }
  354. }
  355. list_to_matrix(vUE,UE);
  356. }
  357. #ifdef IGL_STATIC_LIBRARY
  358. // Explicit template instantiation
  359. // generated by autoexplicit.sh
  360. template void igl::straighten_seams<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 1, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, double, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 1, -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::MatrixBase<Eigen::Matrix<double, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, double, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&);
  361. #endif