straighten_seams.cpp 11 KB

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