order_facets_around_edges.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2015 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 "order_facets_around_edges.h"
  9. #include "../sort_angles.h"
  10. #include <type_traits>
  11. #include <CGAL/Exact_predicates_exact_constructions_kernel.h>
  12. template<
  13. typename DerivedV,
  14. typename DerivedF,
  15. typename DerivedN,
  16. typename DerivedE,
  17. typename DeriveduE,
  18. typename DerivedEMAP,
  19. typename uE2EType,
  20. typename uE2oEType,
  21. typename uE2CType >
  22. IGL_INLINE
  23. typename std::enable_if<!std::is_same<typename DerivedV::Scalar,
  24. typename CGAL::Exact_predicates_exact_constructions_kernel::FT>::value, void>::type
  25. igl::cgal::order_facets_around_edges(
  26. const Eigen::PlainObjectBase<DerivedV>& V,
  27. const Eigen::PlainObjectBase<DerivedF>& F,
  28. const Eigen::PlainObjectBase<DerivedN>& N,
  29. const Eigen::PlainObjectBase<DerivedE>& E,
  30. const Eigen::PlainObjectBase<DeriveduE>& uE,
  31. const Eigen::PlainObjectBase<DerivedEMAP>& EMAP,
  32. const std::vector<std::vector<uE2EType> >& uE2E,
  33. std::vector<std::vector<uE2oEType> >& uE2oE,
  34. std::vector<std::vector<uE2CType > >& uE2C ) {
  35. typedef Eigen::Matrix<typename DerivedN::Scalar, 3, 1> Vector3F;
  36. const typename DerivedV::Scalar EPS = 1e-12;
  37. const size_t num_faces = F.rows();
  38. const size_t num_undirected_edges = uE.rows();
  39. auto edge_index_to_face_index = [&](size_t ei) { return ei % num_faces; };
  40. auto edge_index_to_corner_index = [&](size_t ei) { return ei / num_faces; };
  41. uE2oE.resize(num_undirected_edges);
  42. uE2C.resize(num_undirected_edges);
  43. for(size_t ui = 0;ui<num_undirected_edges;ui++)
  44. {
  45. const auto& adj_edges = uE2E[ui];
  46. const size_t edge_valance = adj_edges.size();
  47. assert(edge_valance > 0);
  48. const auto ref_edge = adj_edges[0];
  49. const auto ref_face = edge_index_to_face_index(ref_edge);
  50. Vector3F ref_normal = N.row(ref_face);
  51. const auto ref_corner_o = edge_index_to_corner_index(ref_edge);
  52. const auto ref_corner_s = (ref_corner_o+1)%3;
  53. const auto ref_corner_d = (ref_corner_o+2)%3;
  54. const typename DerivedF::Scalar o = F(ref_face, ref_corner_o);
  55. const typename DerivedF::Scalar s = F(ref_face, ref_corner_s);
  56. const typename DerivedF::Scalar d = F(ref_face, ref_corner_d);
  57. Vector3F edge = V.row(d) - V.row(s);
  58. auto edge_len = edge.norm();
  59. bool degenerated = edge_len < EPS;
  60. if (degenerated) {
  61. if (edge_valance <= 2) {
  62. // There is only one way to order 2 or less faces.
  63. edge.setZero();
  64. } else {
  65. edge.setZero();
  66. Eigen::Matrix<typename DerivedN::Scalar, Eigen::Dynamic, 3>
  67. normals(edge_valance, 3);
  68. for (size_t fei=0; fei<edge_valance; fei++) {
  69. const auto fe = adj_edges[fei];
  70. const auto f = edge_index_to_face_index(fe);
  71. normals.row(fei) = N.row(f);
  72. }
  73. for (size_t i=0; i<edge_valance; i++) {
  74. size_t j = (i+1) % edge_valance;
  75. Vector3F ni = normals.row(i);
  76. Vector3F nj = normals.row(j);
  77. edge = ni.cross(nj);
  78. edge_len = edge.norm();
  79. if (edge_len >= EPS) {
  80. edge.normalize();
  81. break;
  82. }
  83. }
  84. // Ensure edge direction are consistent with reference face.
  85. Vector3F in_face_vec = V.row(o) - V.row(s);
  86. if (edge.cross(in_face_vec).dot(ref_normal) < 0) {
  87. edge *= -1;
  88. }
  89. if (edge.norm() < EPS) {
  90. std::cerr << "=====================================" << std::endl;
  91. std::cerr << " ui: " << ui << std::endl;
  92. std::cerr << "edge: " << ref_edge << std::endl;
  93. std::cerr << "face: " << ref_face << std::endl;
  94. std::cerr << " vs: " << V.row(s) << std::endl;
  95. std::cerr << " vd: " << V.row(d) << std::endl;
  96. std::cerr << "adj face normals: " << std::endl;
  97. std::cerr << normals << std::endl;
  98. std::cerr << "Very degenerated case detected:" << std::endl;
  99. std::cerr << "Near zero edge surrounded by "
  100. << edge_valance << " neearly colinear faces" <<
  101. std::endl;
  102. std::cerr << "=====================================" << std::endl;
  103. }
  104. }
  105. } else {
  106. edge.normalize();
  107. }
  108. Eigen::MatrixXd angle_data(edge_valance, 3);
  109. std::vector<bool> cons(edge_valance);
  110. for (size_t fei=0; fei<edge_valance; fei++) {
  111. const auto fe = adj_edges[fei];
  112. const auto f = edge_index_to_face_index(fe);
  113. const auto c = edge_index_to_corner_index(fe);
  114. cons[fei] = (d == F(f, (c+1)%3));
  115. assert( cons[fei] || (d == F(f,(c+2)%3)));
  116. assert(!cons[fei] || (s == F(f,(c+2)%3)));
  117. assert(!cons[fei] || (d == F(f,(c+1)%3)));
  118. Vector3F n = N.row(f);
  119. angle_data(fei, 0) = ref_normal.cross(n).dot(edge);
  120. angle_data(fei, 1) = ref_normal.dot(n);
  121. if (cons[fei]) {
  122. angle_data(fei, 0) *= -1;
  123. angle_data(fei, 1) *= -1;
  124. }
  125. angle_data(fei, 0) *= -1; // Sort clockwise.
  126. angle_data(fei, 2) = (cons[fei]?1.:-1.)*(f+1);
  127. }
  128. std::cout << "angle_data:" << std::endl;
  129. std::cout << angle_data << std::endl;
  130. Eigen::VectorXi order;
  131. igl::sort_angles(angle_data, order);
  132. std::cout << "order: " << order.transpose() << std::endl;
  133. auto& ordered_edges = uE2oE[ui];
  134. auto& consistency = uE2C[ui];
  135. ordered_edges.resize(edge_valance);
  136. consistency.resize(edge_valance);
  137. for (size_t fei=0; fei<edge_valance; fei++) {
  138. ordered_edges[fei] = adj_edges[order[fei]];
  139. consistency[fei] = cons[order[fei]];
  140. }
  141. }
  142. }
  143. template<
  144. typename DerivedV,
  145. typename DerivedF,
  146. typename DerivedN,
  147. typename DerivedE,
  148. typename DeriveduE,
  149. typename DerivedEMAP,
  150. typename uE2EType,
  151. typename uE2oEType,
  152. typename uE2CType >
  153. IGL_INLINE
  154. typename std::enable_if<std::is_same<typename DerivedV::Scalar,
  155. typename CGAL::Exact_predicates_exact_constructions_kernel::FT>::value, void>::type
  156. igl::cgal::order_facets_around_edges(
  157. const Eigen::PlainObjectBase<DerivedV>& V,
  158. const Eigen::PlainObjectBase<DerivedF>& F,
  159. const Eigen::PlainObjectBase<DerivedN>& N,
  160. const Eigen::PlainObjectBase<DerivedE>& E,
  161. const Eigen::PlainObjectBase<DeriveduE>& uE,
  162. const Eigen::PlainObjectBase<DerivedEMAP>& EMAP,
  163. const std::vector<std::vector<uE2EType> >& uE2E,
  164. std::vector<std::vector<uE2oEType> >& uE2oE,
  165. std::vector<std::vector<uE2CType > >& uE2C ) {
  166. typedef Eigen::Matrix<typename DerivedN::Scalar, 3, 1> Vector3F;
  167. typedef Eigen::Matrix<typename DerivedV::Scalar, 3, 1> Vector3E;
  168. const typename DerivedV::Scalar EPS = 1e-12;
  169. const size_t num_faces = F.rows();
  170. const size_t num_undirected_edges = uE.rows();
  171. auto edge_index_to_face_index = [&](size_t ei) { return ei % num_faces; };
  172. auto edge_index_to_corner_index = [&](size_t ei) { return ei / num_faces; };
  173. uE2oE.resize(num_undirected_edges);
  174. uE2C.resize(num_undirected_edges);
  175. for(size_t ui = 0;ui<num_undirected_edges;ui++)
  176. {
  177. const auto& adj_edges = uE2E[ui];
  178. const size_t edge_valance = adj_edges.size();
  179. assert(edge_valance > 0);
  180. const auto ref_edge = adj_edges[0];
  181. const auto ref_face = edge_index_to_face_index(ref_edge);
  182. Vector3F ref_normal = N.row(ref_face);
  183. const auto ref_corner_o = edge_index_to_corner_index(ref_edge);
  184. const auto ref_corner_s = (ref_corner_o+1)%3;
  185. const auto ref_corner_d = (ref_corner_o+2)%3;
  186. const typename DerivedF::Scalar o = F(ref_face, ref_corner_o);
  187. const typename DerivedF::Scalar s = F(ref_face, ref_corner_s);
  188. const typename DerivedF::Scalar d = F(ref_face, ref_corner_d);
  189. Vector3E exact_edge = V.row(d) - V.row(s);
  190. exact_edge.array() /= exact_edge.squaredNorm();
  191. Vector3F edge(
  192. CGAL::to_double(exact_edge[0]),
  193. CGAL::to_double(exact_edge[1]),
  194. CGAL::to_double(exact_edge[2]));
  195. edge.normalize();
  196. Eigen::MatrixXd angle_data(edge_valance, 3);
  197. std::vector<bool> cons(edge_valance);
  198. for (size_t fei=0; fei<edge_valance; fei++) {
  199. const auto fe = adj_edges[fei];
  200. const auto f = edge_index_to_face_index(fe);
  201. const auto c = edge_index_to_corner_index(fe);
  202. cons[fei] = (d == F(f, (c+1)%3));
  203. assert( cons[fei] || (d == F(f,(c+2)%3)));
  204. assert(!cons[fei] || (s == F(f,(c+2)%3)));
  205. assert(!cons[fei] || (d == F(f,(c+1)%3)));
  206. Vector3F n = N.row(f);
  207. angle_data(fei, 0) = ref_normal.cross(n).dot(edge);
  208. angle_data(fei, 1) = ref_normal.dot(n);
  209. if (cons[fei]) {
  210. angle_data(fei, 0) *= -1;
  211. angle_data(fei, 1) *= -1;
  212. }
  213. angle_data(fei, 0) *= -1; // Sort clockwise.
  214. angle_data(fei, 2) = (cons[fei]?1.:-1.)*(f+1);
  215. }
  216. Eigen::VectorXi order;
  217. igl::sort_angles(angle_data, order);
  218. auto& ordered_edges = uE2oE[ui];
  219. auto& consistency = uE2C[ui];
  220. ordered_edges.resize(edge_valance);
  221. consistency.resize(edge_valance);
  222. for (size_t fei=0; fei<edge_valance; fei++) {
  223. ordered_edges[fei] = adj_edges[order[fei]];
  224. consistency[fei] = cons[order[fei]];
  225. }
  226. }
  227. }
  228. namespace igl {
  229. namespace cgal {
  230. namespace order_facets_around_edges_helper {
  231. template<typename T>
  232. std::vector<size_t> index_sort(const std::vector<T>& data) {
  233. const size_t len = data.size();
  234. std::vector<size_t> order(len);
  235. for (size_t i=0; i<len; i++) order[i] = i;
  236. auto comp = [&](size_t i, size_t j) {
  237. return data[i] < data[j];
  238. };
  239. std::sort(order.begin(), order.end(), comp);
  240. return order;
  241. }
  242. // adj_faces contains signed index starting from +- 1.
  243. template<
  244. typename DerivedV,
  245. typename DerivedF,
  246. typename DerivedI >
  247. void order_facets_around_edge(
  248. const Eigen::PlainObjectBase<DerivedV>& V,
  249. const Eigen::PlainObjectBase<DerivedF>& F,
  250. size_t s, size_t d,
  251. const std::vector<int>& adj_faces,
  252. Eigen::PlainObjectBase<DerivedI>& order) {
  253. // Although we only need exact predicates in the algorithm,
  254. // exact constructions are needed to avoid degeneracies due to
  255. // casting to double.
  256. typedef CGAL::Exact_predicates_exact_constructions_kernel K;
  257. typedef K::Point_3 Point_3;
  258. typedef K::Plane_3 Plane_3;
  259. auto get_face_index = [&](int adj_f)->size_t{
  260. return abs(adj_f) - 1;
  261. };
  262. auto get_opposite_vertex = [&](size_t fid)->size_t {
  263. if (F(fid, 0) != s && F(fid, 0) != d) return F(fid, 0);
  264. if (F(fid, 1) != s && F(fid, 1) != d) return F(fid, 1);
  265. if (F(fid, 2) != s && F(fid, 2) != d) return F(fid, 2);
  266. assert(false);
  267. return -1;
  268. };
  269. // Handle base cases
  270. if (adj_faces.size() == 0) {
  271. order.resize(0);
  272. return;
  273. } else if (adj_faces.size() == 1) {
  274. order.resize(1);
  275. order[0] = 0;
  276. return;
  277. } else if (adj_faces.size() == 2) {
  278. const size_t o1 =
  279. get_opposite_vertex(get_face_index(adj_faces[0]));
  280. const size_t o2 =
  281. get_opposite_vertex(get_face_index(adj_faces[1]));
  282. const Point_3 ps(V(s, 0), V(s, 1), V(s, 2));
  283. const Point_3 pd(V(d, 0), V(d, 1), V(d, 2));
  284. const Point_3 p1(V(o1, 0), V(o1, 1), V(o1, 2));
  285. const Point_3 p2(V(o2, 0), V(o2, 1), V(o2, 2));
  286. order.resize(2);
  287. switch (CGAL::orientation(ps, pd, p1, p2)) {
  288. case CGAL::POSITIVE:
  289. order[0] = 1;
  290. order[1] = 0;
  291. break;
  292. case CGAL::NEGATIVE:
  293. order[0] = 0;
  294. order[1] = 1;
  295. break;
  296. case CGAL::COPLANAR:
  297. order[0] = adj_faces[0] < adj_faces[1] ? 0:1;
  298. order[1] = adj_faces[0] < adj_faces[1] ? 1:0;
  299. break;
  300. default:
  301. assert(false);
  302. }
  303. return;
  304. }
  305. const size_t num_adj_faces = adj_faces.size();
  306. const size_t o = get_opposite_vertex(
  307. get_face_index(adj_faces[0]));
  308. const Point_3 p_s(V(s, 0), V(s, 1), V(s, 2));
  309. const Point_3 p_d(V(d, 0), V(d, 1), V(d, 2));
  310. const Point_3 p_o(V(o, 0), V(o, 1), V(o, 2));
  311. const Plane_3 separator(p_s, p_d, p_o);
  312. assert(!separator.is_degenerate());
  313. std::vector<Point_3> opposite_vertices;
  314. for (size_t i=0; i<num_adj_faces; i++) {
  315. const size_t o = get_opposite_vertex(
  316. get_face_index(adj_faces[i]));
  317. opposite_vertices.emplace_back(
  318. V(o, 0), V(o, 1), V(o, 2));
  319. }
  320. std::vector<int> positive_side;
  321. std::vector<int> negative_side;
  322. std::vector<int> tie_positive_oriented;
  323. std::vector<int> tie_negative_oriented;
  324. std::vector<size_t> positive_side_index;
  325. std::vector<size_t> negative_side_index;
  326. std::vector<size_t> tie_positive_oriented_index;
  327. std::vector<size_t> tie_negative_oriented_index;
  328. for (size_t i=0; i<num_adj_faces; i++) {
  329. const int f = adj_faces[i];
  330. const Point_3& p_a = opposite_vertices[i];
  331. auto orientation = separator.oriented_side(p_a);
  332. switch (orientation) {
  333. case CGAL::ON_POSITIVE_SIDE:
  334. positive_side.push_back(f);
  335. positive_side_index.push_back(i);
  336. break;
  337. case CGAL::ON_NEGATIVE_SIDE:
  338. negative_side.push_back(f);
  339. negative_side_index.push_back(i);
  340. break;
  341. case CGAL::ON_ORIENTED_BOUNDARY:
  342. {
  343. const Plane_3 other(p_s, p_d, p_a);
  344. const auto target_dir = separator.orthogonal_direction();
  345. const auto query_dir = other.orthogonal_direction();
  346. if (target_dir == query_dir) {
  347. tie_positive_oriented.push_back(f);
  348. tie_positive_oriented_index.push_back(i);
  349. } else if (target_dir == -query_dir) {
  350. tie_negative_oriented.push_back(f);
  351. tie_negative_oriented_index.push_back(i);
  352. } else {
  353. assert(false);
  354. }
  355. }
  356. break;
  357. default:
  358. // Should not be here.
  359. assert(false);
  360. }
  361. }
  362. Eigen::PlainObjectBase<DerivedI> positive_order, negative_order;
  363. igl::cgal::order_facets_around_edges_helper::order_facets_around_edge(
  364. V, F, s, d, positive_side, positive_order);
  365. igl::cgal::order_facets_around_edges_helper::order_facets_around_edge(
  366. V, F, s, d, negative_side, negative_order);
  367. std::vector<size_t> tie_positive_order =
  368. index_sort(tie_positive_oriented);
  369. std::vector<size_t> tie_negative_order =
  370. index_sort(tie_negative_oriented);
  371. // Copy results into order vector.
  372. const size_t tie_positive_size = tie_positive_oriented.size();
  373. const size_t tie_negative_size = tie_negative_oriented.size();
  374. const size_t positive_size = positive_order.size();
  375. const size_t negative_size = negative_order.size();
  376. order.resize(tie_positive_size + positive_size +
  377. tie_negative_size + negative_size);
  378. size_t count=0;
  379. for (size_t i=0; i<tie_positive_size; i++) {
  380. order[count+i] =
  381. tie_positive_oriented_index[tie_positive_order[i]];
  382. }
  383. count += tie_positive_size;
  384. for (size_t i=0; i<negative_size; i++) {
  385. order[count+i] = negative_side_index[negative_order[i]];
  386. }
  387. count += negative_size;
  388. for (size_t i=0; i<tie_negative_size; i++) {
  389. order[count+i] =
  390. tie_negative_oriented_index[tie_negative_order[i]];
  391. }
  392. count += tie_negative_size;
  393. for (size_t i=0; i<positive_size; i++) {
  394. order[count+i] = positive_side_index[positive_order[i]];
  395. }
  396. count += positive_size;
  397. assert(count == num_adj_faces);
  398. // Find the correct start point.
  399. size_t start_idx = 0;
  400. for (size_t i=0; i<num_adj_faces; i++) {
  401. const Point_3& p_a = opposite_vertices[order[i]];
  402. const Point_3& p_b =
  403. opposite_vertices[order[(i+1)%num_adj_faces]];
  404. if (CGAL::orientation(p_s, p_d, p_a, p_b) == CGAL::POSITIVE) {
  405. start_idx = (i+1)%num_adj_faces;
  406. break;
  407. }
  408. }
  409. DerivedI circular_order = order;
  410. for (size_t i=0; i<num_adj_faces; i++) {
  411. order[i] = circular_order[(start_idx + i)%num_adj_faces];
  412. }
  413. }
  414. }
  415. }
  416. }
  417. template<
  418. typename DerivedV,
  419. typename DerivedF,
  420. typename DerivedE,
  421. typename DeriveduE,
  422. typename DerivedEMAP,
  423. typename uE2EType,
  424. typename uE2oEType,
  425. typename uE2CType >
  426. IGL_INLINE void igl::cgal::order_facets_around_edges(
  427. const Eigen::PlainObjectBase<DerivedV>& V,
  428. const Eigen::PlainObjectBase<DerivedF>& F,
  429. const Eigen::PlainObjectBase<DerivedE>& E,
  430. const Eigen::PlainObjectBase<DeriveduE>& uE,
  431. const Eigen::PlainObjectBase<DerivedEMAP>& EMAP,
  432. const std::vector<std::vector<uE2EType> >& uE2E,
  433. std::vector<std::vector<uE2oEType> >& uE2oE,
  434. std::vector<std::vector<uE2CType > >& uE2C ) {
  435. typedef Eigen::Matrix<typename DerivedV::Scalar, 3, 1> Vector3E;
  436. const size_t num_faces = F.rows();
  437. const size_t num_undirected_edges = uE.rows();
  438. auto edge_index_to_face_index = [&](size_t ei) { return ei % num_faces; };
  439. auto edge_index_to_corner_index = [&](size_t ei) { return ei / num_faces; };
  440. uE2oE.resize(num_undirected_edges);
  441. uE2C.resize(num_undirected_edges);
  442. for(size_t ui = 0;ui<num_undirected_edges;ui++)
  443. {
  444. const auto& adj_edges = uE2E[ui];
  445. const size_t edge_valance = adj_edges.size();
  446. assert(edge_valance > 0);
  447. const auto ref_edge = adj_edges[0];
  448. const auto ref_face = edge_index_to_face_index(ref_edge);
  449. const auto ref_corner_o = edge_index_to_corner_index(ref_edge);
  450. const auto ref_corner_s = (ref_corner_o+1)%3;
  451. const auto ref_corner_d = (ref_corner_o+2)%3;
  452. const typename DerivedF::Scalar o = F(ref_face, ref_corner_o);
  453. const typename DerivedF::Scalar s = F(ref_face, ref_corner_s);
  454. const typename DerivedF::Scalar d = F(ref_face, ref_corner_d);
  455. std::vector<bool> cons(edge_valance);
  456. std::vector<int> adj_faces(edge_valance);
  457. for (size_t fei=0; fei<edge_valance; fei++) {
  458. const auto fe = adj_edges[fei];
  459. const auto f = edge_index_to_face_index(fe);
  460. const auto c = edge_index_to_corner_index(fe);
  461. cons[fei] = (d == F(f, (c+1)%3));
  462. adj_faces[fei] = (f+1) * (cons[fei] ? 1:-1);
  463. assert( cons[fei] || (d == F(f,(c+2)%3)));
  464. assert(!cons[fei] || (s == F(f,(c+2)%3)));
  465. assert(!cons[fei] || (d == F(f,(c+1)%3)));
  466. }
  467. Eigen::VectorXi order;
  468. igl::cgal::order_facets_around_edges_helper::order_facets_around_edge(
  469. V, F, s, d, adj_faces, order);
  470. assert(order.size() == edge_valance);
  471. auto& ordered_edges = uE2oE[ui];
  472. auto& consistency = uE2C[ui];
  473. ordered_edges.resize(edge_valance);
  474. consistency.resize(edge_valance);
  475. for (size_t fei=0; fei<edge_valance; fei++) {
  476. ordered_edges[fei] = adj_edges[order[fei]];
  477. consistency[fei] = cons[order[fei]];
  478. }
  479. }
  480. }