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