Wildmeshing Toolkit
TriMesh.cpp
Go to the documentation of this file.
1 #include "TriMesh.hpp"
2 
4 #include <numeric>
7 #include <wmtk/utils/Logger.hpp>
9 
10 namespace wmtk {
11 
12 TriMesh::~TriMesh() = default;
14  : MeshCRTP<TriMesh>(2)
15  , m_vf_handle(register_attribute_typed<int64_t>("m_vf", PrimitiveType::Vertex, 1, false, -1))
16  , m_ef_handle(register_attribute_typed<int64_t>("m_ef", PrimitiveType::Edge, 1, false, -1))
17  , m_fv_handle(register_attribute_typed<int64_t>("m_fv", PrimitiveType::Triangle, 3, false, -1))
18  , m_fe_handle(register_attribute_typed<int64_t>("m_fe", PrimitiveType::Triangle, 3, false, -1))
19  , m_ff_handle(register_attribute_typed<int64_t>("m_ff", PrimitiveType::Triangle, 3, false, -1))
20 {
22 }
23 
25 {
26  m_vf_accessor = std::make_unique<attribute::Accessor<int64_t, TriMesh>>(*this, m_vf_handle);
27  m_ef_accessor = std::make_unique<attribute::Accessor<int64_t, TriMesh>>(*this, m_ef_handle);
28  m_fv_accessor = std::make_unique<attribute::Accessor<int64_t, TriMesh>>(*this, m_fv_handle);
29  m_fe_accessor = std::make_unique<attribute::Accessor<int64_t, TriMesh>>(*this, m_fe_handle);
30  m_ff_accessor = std::make_unique<attribute::Accessor<int64_t, TriMesh>>(*this, m_ff_handle);
31 }
32 
34  : MeshCRTP<TriMesh>(std::move(o))
35 {
36  m_vf_handle = o.m_vf_handle;
37  m_ef_handle = o.m_ef_handle;
38  m_fv_handle = o.m_fv_handle;
39  m_fe_handle = o.m_fe_handle;
40  m_ff_handle = o.m_ff_handle;
41 
43 }
45 {
46  Mesh::operator=(std::move(o));
47  m_vf_handle = o.m_vf_handle;
48  m_ef_handle = o.m_ef_handle;
49  m_fv_handle = o.m_fv_handle;
50  m_fe_handle = o.m_fe_handle;
51  m_ff_handle = o.m_ff_handle;
52 
54  return *this;
55 }
56 
57 
58 bool TriMesh::is_boundary(PrimitiveType pt, const Tuple& tuple) const
59 {
60  switch (pt) {
61  case PrimitiveType::Vertex: return is_boundary_vertex(tuple);
62  case PrimitiveType::Edge: return is_boundary_edge(tuple);
65  default: break;
66  }
67  assert(
68  false); // "tried to compute the boundary of an tri mesh for an invalid simplex dimension"
69  return false;
70 }
71 
72 bool TriMesh::is_boundary_edge(const Tuple& tuple) const
73 {
74  assert(is_valid(tuple));
75  return m_ff_accessor->const_vector_attribute<3>(tuple)(tuple.m_local_eid) < 0;
76 }
77 
78 bool TriMesh::is_boundary_vertex(const Tuple& vertex) const
79 {
80  // go through all edges and check if they are boundary
81  // const simplex::SimplexCollection neigh = simplex::open_star(*this, Simplex::vertex(vertex));
82  // for (const Simplex& s : neigh.get_edges()) {
83  // if (is_boundary(s.tuple())) {
84  // return true;
85  // }
86  //}
87 
88  Tuple t = vertex;
89  do {
90  if (is_boundary_edge(t)) {
91  return true;
92  }
93  t = switch_edge(switch_face(t));
94  } while (t != vertex);
95 
96  return false;
97 }
98 
100 {
101  assert(is_valid(tuple));
102  bool ccw = is_ccw(tuple);
103 
104  switch (type) {
106  const int64_t gvid = id(tuple, PrimitiveType::Vertex);
107  const int64_t geid = id(tuple, PrimitiveType::Edge);
108  const int64_t gfid = id(tuple, PrimitiveType::Triangle);
109 
110  auto ff = m_ff_accessor->const_vector_attribute<3>(tuple);
111 
112  int64_t gcid_new = ff(tuple.m_local_eid);
113  int64_t lvid_new = -1, leid_new = -1;
114 
115  auto fv = m_fv_accessor->index_access().const_vector_attribute<3>(gcid_new);
116 
117  auto fe = m_fe_accessor->index_access().const_vector_attribute<3>(gcid_new);
118 
119  if (gfid == gcid_new) {
120  // this supports 0,1,0 triangles not 0,0,0 triangles
121  int64_t oleid = tuple.m_local_eid;
122  int64_t olvid = tuple.m_local_vid;
123  for (int64_t i = 0; i < 3; ++i) {
124  if (i != oleid && fe(i) == geid) {
125  leid_new = i;
126  }
127  }
128  // if the old vertex is no "opposite of the old or new edges
129  // then they share the vertex
130  // a
131  // 0/ \1 <-- 0 and c share local ids, 1 and b share local ids
132  // /____\.
133  // b c
134  if (oleid != olvid && leid_new != olvid) {
135  lvid_new = olvid;
136  } else {
137  for (int64_t i = 0; i < 3; ++i) {
138  if (i != olvid && fv(i) == gvid) {
139  lvid_new = i;
140  }
141  }
142  }
143  } else {
144  for (int64_t i = 0; i < 3; ++i) {
145  if (fe(i) == geid) {
146  leid_new = i;
147  }
148  if (fv(i) == gvid) {
149  lvid_new = i;
150  }
151  }
152  }
153  assert(lvid_new != -1);
154  assert(leid_new != -1);
155 
156  const Tuple res(lvid_new, leid_new, tuple.m_local_fid, gcid_new);
157  assert(is_valid(res));
158  return res;
159  }
163  default: {
164  assert(false);
165  return autogen::tri_mesh::local_switch_tuple(tuple, type);
166  }
167  }
168 }
169 
170 bool TriMesh::is_ccw(const Tuple& tuple) const
171 {
172  assert(is_valid(tuple));
173  return autogen::tri_mesh::is_ccw(tuple);
174 }
175 
177  Eigen::Ref<const RowVectors3l> FV,
178  Eigen::Ref<const RowVectors3l> FE,
179  Eigen::Ref<const RowVectors3l> FF,
180  Eigen::Ref<const VectorXl> VF,
181  Eigen::Ref<const VectorXl> EF)
182 {
183  // reserve memory for attributes
184 
185 
186  std::vector<int64_t> cap{
187  static_cast<int64_t>(VF.rows()),
188  static_cast<int64_t>(EF.rows()),
189  static_cast<int64_t>(FF.rows())};
190 
191  set_capacities(cap);
192 
193 
194  // get Accessors for topology
195  attribute::Accessor<int64_t> fv_accessor = create_accessor<int64_t>(m_fv_handle);
196  attribute::Accessor<int64_t> fe_accessor = create_accessor<int64_t>(m_fe_handle);
197  attribute::Accessor<int64_t> ff_accessor = create_accessor<int64_t>(m_ff_handle);
198  attribute::Accessor<int64_t> vf_accessor = create_accessor<int64_t>(m_vf_handle);
199  attribute::Accessor<int64_t> ef_accessor = create_accessor<int64_t>(m_ef_handle);
200 
204 
205  // iterate over the matrices and fill attributes
206  for (int64_t i = 0; i < capacity(PrimitiveType::Triangle); ++i) {
207  fv_accessor.index_access().vector_attribute<3>(i) = FV.row(i).transpose();
208  fe_accessor.index_access().vector_attribute<3>(i) = FE.row(i).transpose();
209  ff_accessor.index_access().vector_attribute<3>(i) = FF.row(i).transpose();
210 
211  f_flag_accessor.index_access().activate(i);
212  }
213  // m_vf
214  for (int64_t i = 0; i < capacity(PrimitiveType::Vertex); ++i) {
215  auto& vf = vf_accessor.index_access().scalar_attribute(i);
216  vf = VF(i);
217  v_flag_accessor.index_access().activate(i);
218  }
219  // m_ef
220  for (int64_t i = 0; i < capacity(PrimitiveType::Edge); ++i) {
221  auto& ef = ef_accessor.index_access().scalar_attribute(i);
222  ef = EF(i);
223  e_flag_accessor.index_access().activate(i);
224  }
225 }
226 
227 void TriMesh::initialize(Eigen::Ref<const RowVectors3l> F, bool is_free)
228 {
229  this->m_is_free = is_free;
230  auto [FE, FF, VF, EF] = trimesh_topology_initialization(F);
231  if (is_free) {
232  FF.setConstant(-1);
233  }
234  initialize(F, FE, FF, VF, EF);
235 }
236 void TriMesh::initialize_free(int64_t count)
237 {
238  // 0 1 2
239  // 3 4 5
240  RowVectors3l S(count, 3);
241  std::iota(S.data(), S.data() + S.size(), int64_t(0));
242  initialize(S, true);
243 }
244 
245 Tuple TriMesh::tuple_from_global_ids(int64_t fid, int64_t eid, int64_t vid) const
246 {
247  auto fv = m_fv_accessor->index_access().const_vector_attribute<3>(fid);
248  auto fe = m_fe_accessor->index_access().const_vector_attribute<3>(fid);
249 
250 
251  int64_t lvid = -1;
252  int64_t leid = -1;
253 
254  for (int j = 0; j < 3; ++j) {
255  if (fv(j) == vid) {
256  lvid = j;
257  }
258  if (fe(j) == eid) {
259  leid = j;
260  }
261  }
262  assert(lvid != -1);
263  assert(leid != -1);
264 
265  return Tuple(lvid, leid, -1, fid);
266 }
267 
268 Tuple TriMesh::tuple_from_id(const PrimitiveType type, const int64_t gid) const
269 {
270  switch (type) {
271  case PrimitiveType::Vertex: {
272  return vertex_tuple_from_id(gid);
273  }
274  case PrimitiveType::Edge: {
275  return edge_tuple_from_id(gid);
276  }
278  return face_tuple_from_id(gid);
279  }
281  throw std::runtime_error("no tet tuple supported for trimesh");
282  break;
283  }
284  default: assert(false); // "Invalid primitive type"
285  }
286  return Tuple();
287 }
288 
290 {
291  auto f = m_vf_accessor->index_access().const_scalar_attribute(id);
292  auto fv = m_fv_accessor->index_access().const_vector_attribute<3>(f);
293  for (int64_t i = 0; i < 3; ++i) {
294  if (fv(i) == id) {
296  }
297  }
298  assert(false); // "vertex_tuple_from_id failed"
299 
300  return Tuple();
301 }
302 
304 {
305  auto f = m_ef_accessor->index_access().const_scalar_attribute(id);
306  auto fe = m_fe_accessor->index_access().const_vector_attribute<3>(f);
307  for (int64_t i = 0; i < 3; ++i) {
308  if (fe(i) == id) {
310  }
311  }
312  assert(false); // "edge_tuple_from_id failed"
313 
314  return Tuple();
315 }
316 
318 {
319  Tuple f_tuple = Tuple(
322  -1,
323  id);
324  assert(is_ccw(f_tuple));
325  assert(is_valid(f_tuple));
326  return f_tuple;
327 }
328 
329 bool TriMesh::is_valid(const Tuple& tuple) const
330 {
331  if (!Mesh::is_valid(tuple)) {
332  logger().trace("Tuple was null and therefore not valid");
333  return false;
334  }
335  const bool is_connectivity_valid = tuple.m_local_vid >= 0 && tuple.m_local_eid >= 0 &&
336  tuple.m_global_cid >= 0 &&
338 
339  if (!is_connectivity_valid) {
340 #if !defined(NDEBUG)
341  logger().trace(
342  "tuple.m_local_vid={} >= 0 && tuple.m_local_eid={} >= 0 &&"
343  " tuple.m_global_cid={} >= 0 &&"
344  " autogen::tri_mesh::tuple_is_valid_for_ccw(tuple)={}",
345  tuple.m_local_vid,
346  tuple.m_local_eid,
347  tuple.m_global_cid,
349  assert(tuple.m_local_vid >= 0);
350  assert(tuple.m_local_eid >= 0);
351  assert(tuple.m_global_cid >= 0);
353 #endif
354  return false;
355  }
356  return true;
357 }
358 
360 {
361  // get Accessors for topology
362  const attribute::Accessor<int64_t> fv_accessor = create_const_accessor<int64_t>(m_fv_handle);
363  const attribute::Accessor<int64_t> fe_accessor = create_const_accessor<int64_t>(m_fe_handle);
364  const attribute::Accessor<int64_t> ff_accessor = create_const_accessor<int64_t>(m_ff_handle);
365  const attribute::Accessor<int64_t> vf_accessor = create_const_accessor<int64_t>(m_vf_handle);
366  const attribute::Accessor<int64_t> ef_accessor = create_const_accessor<int64_t>(m_ef_handle);
367  const attribute::FlagAccessor<TriMesh> v_flag_accessor =
370  const attribute::FlagAccessor<TriMesh> f_flag_accessor =
372 
373 
374  for (int64_t i = 0; i < capacity(PrimitiveType::Triangle); ++i) {
375  if (!f_flag_accessor.index_access().is_active(i)) {
376  continue;
377  }
378  auto fe = fe_accessor.index_access().const_vector_attribute<3>(i);
379  auto fv = fv_accessor.index_access().const_vector_attribute<3>(i);
380 
381  bool bad_face = false;
382 
383  for (int64_t j = 0; j < 3; ++j) {
384  int64_t ei = fe(j);
385  int64_t vi = fv(j);
386  if (!e_flag_accessor.index_access().is_active(ei)) {
387  wmtk::logger().error(
388  "Face {} refers to edge {} at local index {} which was deleted",
389  i,
390  ei,
391  j);
392  bad_face = true;
393  }
394  if (!v_flag_accessor.index_access().is_active(vi)) {
395  wmtk::logger().error(
396  "Face {} refers to vertex{} at local index {} which was deleted",
397  i,
398  vi,
399  j);
400  bad_face = true;
401  }
402  }
403  if (bad_face) {
404  return false;
405  }
406  }
407  // EF and FE
408  for (int64_t i = 0; i < capacity(PrimitiveType::Edge); ++i) {
409  if (!e_flag_accessor.index_access().is_active(i)) {
410  continue;
411  }
412  int cnt = 0;
413  long ef_val = ef_accessor.index_access().const_scalar_attribute(i);
414 
415  auto fe_val = fe_accessor.index_access().const_vector_attribute<3>(ef_val);
416  for (int64_t j = 0; j < 3; ++j) {
417  if (fe_val(j) == i) {
418  cnt++;
419  }
420  }
421  if (cnt == 0) {
422  wmtk::logger().error(
423  "EF[{0}] {1} and FE:[EF[{0}]] = {2} are not "
424  "compatible ",
425  i,
426  ef_val,
427  fmt::join(fe_val, ","));
428 
429  // std::cout << "EF and FE not compatible" << std::endl;
430  return false;
431  }
432  }
433 
434  // VF and FV
435  for (int64_t i = 0; i < capacity(PrimitiveType::Vertex); ++i) {
436  const int64_t vf = vf_accessor.index_access().const_scalar_attribute(i);
437  if (!v_flag_accessor.index_access().is_active(i)) {
438  continue;
439  }
440  int cnt = 0;
441 
442  auto fv = fv_accessor.index_access().const_vector_attribute<3>(vf);
443  for (int64_t j = 0; j < 3; ++j) {
444  if (fv(j) == i) {
445  cnt++;
446  }
447  }
448  if (cnt == 0) {
449  wmtk::logger().error(
450  "VF and FV not compatible, could not find VF[{}] = {} "
451  "in FV[{}] = [{}]",
452  i,
453  vf,
454  vf,
455  fmt::join(fv, ","));
456  return false;
457  }
458  }
459 
460  // FE and EF
461  for (int64_t i = 0; i < capacity(PrimitiveType::Triangle); ++i) {
462  if (!f_flag_accessor.index_access().is_active(i)) {
463  continue;
464  }
465  auto fe = fe_accessor.index_access().const_vector_attribute<3>(i);
466  auto ff = ff_accessor.index_access().const_vector_attribute<3>(i);
467 
468  for (int64_t j = 0; j < 3; ++j) {
469  int neighbor_fid = ff(j);
470  const bool is_boundary = neighbor_fid == -1;
471  if (is_boundary) {
472  auto ef = ef_accessor.index_access().const_scalar_attribute(fe(j));
473  if (ef != i) {
474  wmtk::logger().error(
475  "Even though local edge {} of face {} is "
476  "boundary (global eid is {}), "
477  "ef[{}] = {} != {}",
478  j,
479  i,
480  fe(j),
481  fe(j),
482  ef,
483  i);
484  return false;
485  }
486  } else {
487  if (neighbor_fid == i) {
488  logger().error(
489  "Connectivity check cannot work when mapping a "
490  "face to itself (face {})",
491  i);
492  assert(false);
493  continue;
494  }
495  auto neighbor_ff =
496  ff_accessor.index_access().const_vector_attribute<3>(neighbor_fid);
497 
498  if ((neighbor_ff.array() == i).any()) {
499  auto neighbor_fe =
500  fe_accessor.index_access().const_vector_attribute<3>(neighbor_fid);
501 
502  int edge_shared_count = 0;
503  for (int local_neighbor_eid = 0; local_neighbor_eid < 3; ++local_neighbor_eid) {
504  // find some edge which is shared
505  if (neighbor_ff(local_neighbor_eid) == i) {
506  if (fe(j) == neighbor_fe(local_neighbor_eid)) {
507  edge_shared_count++;
508  }
509  }
510  }
511  if (edge_shared_count != 1) {
512  wmtk::logger().error(
513  "face {} with fe={} neighbor fe[{}] = {} "
514  "was unable to find itself "
515  "uniquely (found {})",
516  i,
517  fmt::join(fe, ","),
518  neighbor_fid,
519  fmt::join(neighbor_fe, ","),
520  edge_shared_count);
521  return false;
522  }
523  } else {
524  wmtk::logger().error(
525  "face {} with ff={} neighbor ff[{}] = {} was "
526  "unable to find itself",
527  i,
528  fmt::join(ff, ","),
529  neighbor_fid,
530  fmt::join(neighbor_ff, ","));
531  return false;
532  }
533  }
534  }
535  }
536 
537  return true;
538 }
539 
541 {
542  Tuple r = t;
543  r.m_global_cid = cid;
544  return r;
545 }
546 
547 std::vector<std::vector<TypedAttributeHandle<int64_t>>> TriMesh::connectivity_attributes() const
548 {
549  std::vector<std::vector<TypedAttributeHandle<int64_t>>> handles(3);
550 
551  handles[2].push_back(m_vf_handle);
552  handles[2].push_back(m_ef_handle);
553  handles[2].push_back(m_ff_handle);
554 
555  handles[1].push_back(m_fe_handle);
556 
557  handles[0].push_back(m_fv_handle);
558 
559  return handles;
560 }
561 
562 std::vector<Tuple> TriMesh::orient_vertices(const Tuple& tuple) const
563 {
564  int64_t cid = tuple.m_global_cid;
565  return {Tuple(0, 2, -1, cid), Tuple(1, 0, -1, cid), Tuple(2, 1, -1, cid)};
566 }
567 
568 
569 } // namespace wmtk
A Curiously Recurring Template Pattern shim to enable generic specialization of functions.
Definition: MeshCRTP.hpp:24
int64_t id(const Tuple &tuple, PrimitiveType type) const
return the global id of the Tuple of the given dimension
Definition: Mesh.hpp:1021
bool m_is_free
Definition: Mesh.hpp:847
void set_capacities(std::vector< int64_t > capacities)
int64_t capacity(PrimitiveType type) const
read in the m_capacities return the upper bound for the number of entities of the given dimension
bool is_free() const
Definition: Mesh.hpp:988
virtual bool is_valid(const Tuple &tuple) const
check validity of tuple including its hash
Definition: Mesh.cpp:113
Mesh & operator=(const Mesh &other)=delete
const attribute::FlagAccessor< Mesh > get_flag_accessor(PrimitiveType type) const
Definition: Mesh.cpp:159
std::vector< std::vector< TypedAttributeHandle< int64_t > > > connectivity_attributes() const override
Returns a vector of vectors of attribute handles.
Definition: TriMesh.cpp:547
Tuple switch_face(const Tuple &tuple) const
Definition: TriMesh.hpp:121
Tuple face_tuple_from_id(int64_t id) const
Definition: TriMesh.cpp:317
Tuple switch_tuple(const Tuple &tuple, PrimitiveType type) const final override
switch the orientation of the Tuple of the given dimension
Definition: TriMesh.cpp:99
bool is_boundary_vertex(const Tuple &tuple) const
Definition: TriMesh.cpp:78
attribute::TypedAttributeHandle< int64_t > m_vf_handle
Definition: TriMesh.hpp:90
Tuple tuple_from_id(const PrimitiveType type, const int64_t gid) const final override
internal function that returns the tuple of requested type, and has the global index cid
Definition: TriMesh.cpp:268
TriMesh & operator=(const TriMesh &o)=delete
std::unique_ptr< attribute::Accessor< int64_t, TriMesh > > m_ef_accessor
Definition: TriMesh.hpp:98
bool is_boundary(const simplex::Simplex &tuple) const
check if a simplex lies on a boundary or not
Definition: Mesh.cpp:107
bool is_valid(const Tuple &tuple) const final override
check validity of tuple including its hash
Definition: TriMesh.cpp:329
Tuple vertex_tuple_from_id(int64_t id) const
Definition: TriMesh.cpp:289
bool is_connectivity_valid() const final override
Definition: TriMesh.cpp:359
Tuple tuple_from_global_ids(int64_t fid, int64_t eid, int64_t vid) const
Definition: TriMesh.cpp:245
void make_cached_accessors()
Definition: TriMesh.cpp:24
std::unique_ptr< attribute::Accessor< int64_t, TriMesh > > m_fe_accessor
Definition: TriMesh.hpp:100
std::unique_ptr< attribute::Accessor< int64_t, TriMesh > > m_fv_accessor
Definition: TriMesh.hpp:99
void initialize_free(int64_t count)
Definition: TriMesh.cpp:236
Tuple switch_edge(const Tuple &tuple) const
Definition: TriMesh.hpp:117
static Tuple with_different_cid(const Tuple &t, int64_t cid)
Definition: TriMesh.cpp:540
void initialize(Eigen::Ref< const RowVectors3l > FV, Eigen::Ref< const RowVectors3l > FE, Eigen::Ref< const RowVectors3l > FF, Eigen::Ref< const VectorXl > VF, Eigen::Ref< const VectorXl > EF)
Definition: TriMesh.cpp:176
attribute::TypedAttributeHandle< int64_t > m_ff_handle
Definition: TriMesh.hpp:95
attribute::TypedAttributeHandle< int64_t > m_ef_handle
Definition: TriMesh.hpp:91
std::unique_ptr< attribute::Accessor< int64_t, TriMesh > > m_ff_accessor
Definition: TriMesh.hpp:101
bool is_boundary_edge(const Tuple &tuple) const
Definition: TriMesh.cpp:72
bool is_ccw(const Tuple &tuple) const final override
TODO this needs dimension?
Definition: TriMesh.cpp:170
attribute::TypedAttributeHandle< int64_t > m_fv_handle
Definition: TriMesh.hpp:93
Tuple edge_tuple_from_id(int64_t id) const
Definition: TriMesh.cpp:303
std::vector< Tuple > orient_vertices(const Tuple &t) const override
Definition: TriMesh.cpp:562
std::unique_ptr< attribute::Accessor< int64_t, TriMesh > > m_vf_accessor
Definition: TriMesh.hpp:97
attribute::TypedAttributeHandle< int64_t > m_fe_handle
Definition: TriMesh.hpp:94
~TriMesh() override
int8_t m_local_vid
Definition: Tuple.hpp:47
int8_t m_local_eid
Definition: Tuple.hpp:48
int64_t m_global_cid
Definition: Tuple.hpp:46
int8_t m_local_fid
Definition: Tuple.hpp:49
CachingBaseType & index_access()
Definition: Accessor.hpp:95
T const_scalar_attribute(const int64_t index) const
MapResult< D > vector_attribute(const int64_t index)
T & scalar_attribute(const int64_t index)
ConstMapResult< D > const_vector_attribute(const int64_t index) const
const IndexBaseType & index_access() const
bool is_active(int64_t t) const
Definition: autodiff.h:995
const int64_t auto_2d_table_complete_vertex[3][2]
Tuple get_tuple_from_simplex_local_vertex_id(int8_t local_id, int64_t global_id)
Tuple get_tuple_from_simplex_local_edge_id(int8_t local_id, int64_t global_id)
bool tuple_is_valid_for_ccw(const Tuple &t)
Definition: is_ccw.hxx:17
Tuple local_switch_tuple(const Tuple &t, PrimitiveType pt)
bool is_ccw(const Tuple &t)
Definition: is_ccw.hxx:10
Definition: Accessor.hpp:6
RowVectors< int64_t, 3 > RowVectors3l
Definition: Types.hpp:47
spdlog::logger & logger()
Retrieves the current logger.
Definition: Logger.cpp:58
std::tuple< RowVectors3l, RowVectors3l, VectorXl, VectorXl > trimesh_topology_initialization(Eigen::Ref< const RowVectors3l > F)