snap_rounding.cpp 6.6 KB

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