snap_rounding.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. #include "snap_rounding.h"
  2. #include "resolve_intersections.h"
  3. #include "subdivide_segments.h"
  4. #include <igl/remove_unreferenced.h>
  5. #include <igl/unique.h>
  6. #include <CGAL/Segment_2.h>
  7. #include <CGAL/Point_2.h>
  8. #include <CGAL/Vector_2.h>
  9. #include <CGAL/Exact_predicates_exact_constructions_kernel.h>
  10. #include <algorithm>
  11. template <
  12. typename DerivedV,
  13. typename DerivedE,
  14. typename DerivedVI,
  15. typename DerivedEI,
  16. typename DerivedJ>
  17. IGL_INLINE void igl::copyleft::cgal::snap_rounding(
  18. const Eigen::PlainObjectBase<DerivedV> & V,
  19. const Eigen::PlainObjectBase<DerivedE> & E,
  20. Eigen::PlainObjectBase<DerivedVI> & VI,
  21. Eigen::PlainObjectBase<DerivedEI> & EI,
  22. Eigen::PlainObjectBase<DerivedJ> & J)
  23. {
  24. using namespace Eigen;
  25. using namespace igl;
  26. using namespace igl::copyleft::cgal;
  27. using namespace std;
  28. // Exact scalar type
  29. typedef CGAL::Epeck Kernel;
  30. typedef Kernel::FT EScalar;
  31. typedef CGAL::Segment_2<Kernel> Segment_2;
  32. typedef CGAL::Point_2<Kernel> Point_2;
  33. typedef CGAL::Vector_2<Kernel> Vector_2;
  34. typedef Matrix<EScalar,Dynamic,Dynamic> MatrixXE;
  35. // Convert vertex positions to exact kernel
  36. MatrixXE VE;
  37. {
  38. VectorXi IM;
  39. resolve_intersections(V,E,VE,EI,J,IM);
  40. for_each(
  41. EI.data(),
  42. EI.data()+EI.size(),
  43. [&IM](typename DerivedEI::Scalar& i){i=IM(i);});
  44. VectorXi _;
  45. remove_unreferenced( MatrixXE(VE), DerivedEI(EI), VE,EI,_);
  46. }
  47. // find all hot pixels
  48. //// southwest and north east corners
  49. //const RowVector2i SW(
  50. // round(VE.col(0).minCoeff()),
  51. // round(VE.col(1).minCoeff()));
  52. //const RowVector2i NE(
  53. // round(VE.col(0).maxCoeff()),
  54. // round(VE.col(1).maxCoeff()));
  55. // https://github.com/CGAL/cgal/issues/548
  56. // Round an exact scalar to the nearest integer. A priori can't just round
  57. // double. Suppose e=0.5+ε but double(e)<0.5
  58. //
  59. // Inputs:
  60. // e exact number
  61. // Outputs:
  62. // i closest integer
  63. const auto & round = [](const EScalar & e)->int
  64. {
  65. const double d = CGAL::to_double(e);
  66. // get an integer that's near the closest int
  67. int i = std::round(d);
  68. EScalar di_sqr = CGAL::square((e-EScalar(i)));
  69. const auto & search = [&i,&di_sqr,&e](const int dir)
  70. {
  71. while(true)
  72. {
  73. const int j = i+dir;
  74. const EScalar dj_sqr = CGAL::square((e-EScalar(j)));
  75. if(dj_sqr < di_sqr)
  76. {
  77. i = j;
  78. di_sqr = dj_sqr;
  79. }else
  80. {
  81. break;
  82. }
  83. }
  84. };
  85. // Try to increase/decrease int
  86. search(1);
  87. search(-1);
  88. return i;
  89. };
  90. vector<Point_2> hot;
  91. for(int i = 0;i<VE.rows();i++)
  92. {
  93. hot.emplace_back(round(VE(i,0)),round(VE(i,1)));
  94. }
  95. {
  96. std::vector<size_t> _1,_2;
  97. igl::unique(vector<Point_2>(hot),hot,_1,_2);
  98. }
  99. // find all segments intersecting hot pixels
  100. // split edge at closest point to hot pixel center
  101. vector<vector<Point_2>> steiner(EI.rows());
  102. // initialize each segment with endpoints
  103. for(int i = 0;i<EI.rows();i++)
  104. {
  105. steiner[i].emplace_back(VE(EI(i,0),0),VE(EI(i,0),1));
  106. steiner[i].emplace_back(VE(EI(i,1),0),VE(EI(i,1),1));
  107. }
  108. // silly O(n²) implementation
  109. for(const Point_2 & h : hot)
  110. {
  111. // North, East, South, West
  112. Segment_2 wall[4] =
  113. {
  114. {h+Vector_2(-0.5, 0.5),h+Vector_2( 0.5, 0.5)},
  115. {h+Vector_2( 0.5, 0.5),h+Vector_2( 0.5,-0.5)},
  116. {h+Vector_2( 0.5,-0.5),h+Vector_2(-0.5,-0.5)},
  117. {h+Vector_2(-0.5,-0.5),h+Vector_2(-0.5, 0.5)}
  118. };
  119. // consider all segments
  120. for(int i = 0;i<EI.rows();i++)
  121. {
  122. // endpoints
  123. const Point_2 s(VE(EI(i,0),0),VE(EI(i,0),1));
  124. const Point_2 d(VE(EI(i,1),0),VE(EI(i,1),1));
  125. // if either end-point is in h's pixel then ignore
  126. const Point_2 rs(round(s.x()),round(s.y()));
  127. const Point_2 rd(round(d.x()),round(d.y()));
  128. if(h == rs || h == rd)
  129. {
  130. continue;
  131. }
  132. // otherwise check for intersections with walls consider all walls
  133. const Segment_2 si(s,d);
  134. vector<Point_2> hits;
  135. for(int j = 0;j<4;j++)
  136. {
  137. const Segment_2 & sj = wall[j];
  138. if(CGAL::do_intersect(si,sj))
  139. {
  140. CGAL::Object result = CGAL::intersection(si,sj);
  141. if(const Point_2 * p = CGAL::object_cast<Point_2 >(&result))
  142. {
  143. hits.push_back(*p);
  144. }else if(const Segment_2 * s = CGAL::object_cast<Segment_2 >(&result))
  145. {
  146. // add both endpoints
  147. hits.push_back(s->vertex(0));
  148. hits.push_back(s->vertex(1));
  149. }
  150. }
  151. }
  152. if(hits.size() == 0)
  153. {
  154. continue;
  155. }
  156. // centroid of hits
  157. Vector_2 cen(0,0);
  158. for(const Point_2 & hit : hits)
  159. {
  160. cen = Vector_2(cen.x()+hit.x(), cen.y()+hit.y());
  161. }
  162. cen = Vector_2(cen.x()/EScalar(hits.size()),cen.y()/EScalar(hits.size()));
  163. const Point_2 rcen(round(cen.x()),round(cen.y()));
  164. // after all of that, don't add as a steiner unless it's going to round
  165. // to h
  166. if(rcen == h)
  167. {
  168. steiner[i].emplace_back(cen.x(),cen.y());
  169. }
  170. }
  171. }
  172. {
  173. DerivedJ prevJ = J;
  174. VectorXi IM;
  175. subdivide_segments(MatrixXE(VE),MatrixXi(EI),steiner,VE,EI,J,IM);
  176. for_each(J.data(),J.data()+J.size(),[&prevJ](typename DerivedJ::Scalar & j){j=prevJ(j);});
  177. for_each(
  178. EI.data(),
  179. EI.data()+EI.size(),
  180. [&IM](typename DerivedEI::Scalar& i){i=IM(i);});
  181. VectorXi _;
  182. remove_unreferenced( MatrixXE(VE), DerivedEI(EI), VE,EI,_);
  183. }
  184. VI.resize(VE.rows(),VE.cols());
  185. for(int i = 0;i<VE.rows();i++)
  186. {
  187. for(int j = 0;j<VE.cols();j++)
  188. {
  189. VI(i,j) = round(CGAL::to_double(VE(i,j)));
  190. }
  191. }
  192. }