snap_rounding.cpp 5.9 KB

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