Skip to content

Commit

Permalink
Merge pull request #821 from wildmeshing/mtao/map_invariant
Browse files Browse the repository at this point in the history
simplifying multimesh map invariant
  • Loading branch information
mtao authored Oct 22, 2024
2 parents 40576b4 + c16d84d commit 54b90a3
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 79 deletions.
149 changes: 74 additions & 75 deletions src/wmtk/invariants/MultiMeshMapValidInvariant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,101 +7,100 @@
#include <wmtk/TetMesh.hpp>
#include <wmtk/TriMesh.hpp>
#include <wmtk/multimesh/MultiMeshSimplexVisitor.hpp>
#include <wmtk/simplex/cofaces_single_dimension_iterable.hpp>
#include <wmtk/simplex/top_dimension_cofaces.hpp>
#include "wmtk/simplex/cofaces_single_dimension.hpp"

namespace wmtk {
namespace {

bool are_all_ears_in_child(const TriMesh& parent, const EdgeMesh& child, const Tuple& t)
// checks if two simplices both are mappable
bool both_map_to_child(
const Mesh& parent,
const Mesh& child,
const simplex::Simplex& left,
const simplex::Simplex& right)
{
const Tuple parent_ear_0 = parent.switch_edge(t);
const Tuple paretn_ear_1 = parent.switch_edge(parent.switch_vertex(t));
bool find_ear_0 =
!parent.map_to_child(child, simplex::Simplex::edge(parent, parent_ear_0)).empty();
bool find_ear_1 =
!parent.map_to_child(child, simplex::Simplex::edge(parent, paretn_ear_1)).empty();
return find_ear_0 && find_ear_1;
return parent.can_map(child, left) && parent.can_map(child, right);
}

bool are_all_ears_in_child(const TetMesh& parent, const TriMesh& child, const Tuple& t)

// computes teh two ears in a K+1 simplex over the input edge to see if their facets will be mapped
// into one another
bool both_map_to_child(const Mesh& parent, const Mesh& child, const Tuple& input)
{
const Tuple parent_ear_0 = parent.switch_face(parent.switch_edge(t));
const Tuple parent_ear_1 = parent.switch_face(parent.switch_edge(parent.switch_vertex(t)));
bool find_ear_0 =
!parent.map_to_child(child, simplex::Simplex::face(parent, parent_ear_0)).empty();
bool find_ear_1 =
!parent.map_to_child(child, simplex::Simplex::face(parent, parent_ear_1)).empty();
return find_ear_0 && find_ear_1;
const PrimitiveType child_type = child.top_simplex_type();
const PrimitiveType parent_type = parent.top_simplex_type();
assert(parent_type > child_type);
const PrimitiveType collapsed_simplex_type = std::min(child_type + 1, parent_type);
auto opposite = [&parent, collapsed_simplex_type](Tuple t) {
//switch(collapsed_simplex_type) {
// case PrimitiveType::Tetrahedron:
// t = parent.switch_tuples(t, {PrimitiveType::Vertex, PrimitiveType::Edge, PrimitiveType::Triangle});
// case PrimitiveType::Triangle:
// t = parent.switch_tuples(t, {PrimitiveType::Vertex, PrimitiveType::Edge});
// case PrimitiveType::Edge:
// t = parent.switch_tuple(t, PrimitiveType::Vertex);

// default:
// case PrimitiveType::Vertex:
// break;
//}
for (PrimitiveType pt = PrimitiveType::Vertex; pt < collapsed_simplex_type; pt = pt + 1) {
assert(pt < parent.top_simplex_type());
t = parent.switch_tuple(t, pt);
}
return t;
};
const simplex::Simplex left(child_type, opposite(input));
const simplex::Simplex right(
child_type,
opposite(parent.switch_tuple(input, PrimitiveType::Vertex)));
return both_map_to_child(parent, child, left, right);
}

bool are_all_ears_in_child(const TetMesh& parent, const EdgeMesh& child, const Tuple& t)

// two child K-facets will merge into one another if they are the ears of a K+1 simplex whose
// "input edge" is the input edge. This function iterates through those K+1 simplices and lets
// both_map_to_child check for if both ears are mapped
bool any_pairs_both_map_to_child(
const Mesh& parent,
const Mesh& child,
const simplex::Simplex& edge)
{
const Tuple parent_ear_0 = parent.switch_edge(t);
const Tuple parent_ear_1 = parent.switch_edge(parent.switch_vertex(t));
bool find_ear_0 =
!parent.map_to_child(child, simplex::Simplex::edge(parent, parent_ear_0)).empty();
bool find_ear_1 =
!parent.map_to_child(child, simplex::Simplex::edge(parent, parent_ear_1)).empty();
assert(edge.primitive_type() == PrimitiveType::Edge);
const PrimitiveType parent_type = parent.top_simplex_type();
const PrimitiveType child_type = child.top_simplex_type();
assert(parent_type > child_type);
if (parent_type == child_type) {
// if the meshes are the same dimension then there isn't a pair, so this function returns
// false
return false;
} else if (parent_type == child_type + 1) {
return both_map_to_child(parent, child, edge.tuple());
}
for (const Tuple& tuple :
simplex::cofaces_single_dimension_iterable(parent, edge, child.top_simplex_type() + 1)) {
if (both_map_to_child(parent, child, tuple)) {
return true;
}
}
return false;
}

const Tuple t_switch_face = parent.switch_face(t);
const Tuple parent_ear_2 = parent.switch_edge(t_switch_face);
const Tuple parent_ear_3 = parent.switch_edge(parent.switch_vertex(t_switch_face));
bool find_ear_2 =
!parent.map_to_child(child, simplex::Simplex::edge(parent, parent_ear_2)).empty();
bool find_ear_3 =
!parent.map_to_child(child, simplex::Simplex::edge(parent, parent_ear_3)).empty();

return (find_ear_0 && find_ear_1) || (find_ear_2 && find_ear_3);
}
struct MultiMeshMapValidFunctor
{
template <typename T>
bool operator()(const T& m, const simplex::Simplex& s, int64_t) {
return this->operator()(m,s);
}
bool operator()(const Mesh& m, const simplex::Simplex& s) const { return false; }
bool operator()(const PointMesh& m, const simplex::Simplex& s) const { return false; }

bool operator()(const EdgeMesh& m, const simplex::Simplex& s) const { return false; }
bool operator()(const TriMesh& m, const simplex::Simplex& s) const
bool operator()(const T& m, const simplex::Simplex& s, int64_t)
{
const std::vector<Tuple> equivalent_tuples = simplex::top_dimension_cofaces_tuples(m, s);

for (auto child_ptr : m.get_child_meshes()) {
if (child_ptr->top_cell_dimension() != 1) {
continue;
}

for (const Tuple& t : equivalent_tuples) {
const EdgeMesh& child = dynamic_cast<const EdgeMesh&>(*child_ptr);
if (m.map_to_child(child, s).empty() && are_all_ears_in_child(m, child, t)) {
return false;
}
}
}
return true;
return this->operator()(m, s);
}
bool operator()(const TetMesh& m, const simplex::Simplex& s) const
bool operator()(const Mesh& m, const simplex::Simplex& s) const
{
const std::vector<Tuple> equivalent_tuples = simplex::top_dimension_cofaces_tuples(m, s);

for (auto child_ptr : m.get_child_meshes()) {
if (child_ptr->top_cell_dimension() != 2 && child_ptr->top_cell_dimension() != 1) {
continue;
}

for (const Tuple& t : equivalent_tuples) {
if (child_ptr->top_cell_dimension() == 2) {
const TriMesh& child = dynamic_cast<const TriMesh&>(*child_ptr);
if (m.map_to_child(child, s).empty() && are_all_ears_in_child(m, child, t)) {
return false;
}
} else {
const EdgeMesh& child = dynamic_cast<const EdgeMesh&>(*child_ptr);
if (m.map_to_child(child, s).empty() && are_all_ears_in_child(m, child, t)) {
return false;
}
}
if (any_pairs_both_map_to_child(m, *child_ptr, s)) {
return false;
}
}
return true;
Expand All @@ -119,7 +118,7 @@ bool MultiMeshMapValidInvariant::before(const simplex::Simplex& t) const
std::integral_constant<int64_t, 1>{}, // specify that this runs on edges
MultiMeshMapValidFunctor{});
// TODO: fix visitor to work for const data
visitor.execute_from_root(const_cast<Mesh&>(mesh()), simplex::NavigatableSimplex(mesh(),t));
visitor.execute_from_root(const_cast<Mesh&>(mesh()), simplex::NavigatableSimplex(mesh(), t));
const auto& data = visitor.cache();

for (const auto& [key, value_var] : data) {
Expand Down
17 changes: 13 additions & 4 deletions src/wmtk/simplex/Simplex.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace wmtk {
class Mesh;

namespace multimesh {
class MultiMeshManager;
class MultiMeshManager;
}
template <typename Derived>
class MeshCRTP;
Expand All @@ -30,17 +30,17 @@ class Simplex
friend class NavigatableSimplex;
PrimitiveType m_primitive_type;
Tuple m_tuple;

public:
// the mesh class can use this index value to cache/accelerate operations
protected:
// private constructor mesh might want to use if it knows the ids beforehand
Simplex(const PrimitiveType& ptype, const Tuple& t)
: m_primitive_type{ptype}
, m_tuple{t}
{}


public:
Simplex() = default;
// TODO: deprecate
Simplex(const Mesh& m, const PrimitiveType& ptype, const Tuple& t);

Simplex(const Simplex&) = default;
Expand All @@ -52,20 +52,29 @@ class Simplex
int64_t dimension() const { return get_primitive_type_id(m_primitive_type); }
const Tuple& tuple() const { return m_tuple; }

// TODO: deprecate
static Simplex vertex(const Mesh& m, const Tuple& t)
{
return Simplex(PrimitiveType::Vertex, t);
}
// TODO: deprecate
static Simplex edge(const Mesh& m, const Tuple& t) { return Simplex(PrimitiveType::Edge, t); }
// TODO: deprecate
static Simplex face(const Mesh& m, const Tuple& t)
{
return Simplex(PrimitiveType::Triangle, t);
}
// TODO: deprecate
static Simplex tetrahedron(const Mesh& m, const Tuple& t)
{
return Simplex(PrimitiveType::Tetrahedron, t);
}

static Simplex vertex(const Tuple& t) { return Simplex(PrimitiveType::Vertex, t); }
static Simplex edge(const Tuple& t) { return Simplex(PrimitiveType::Edge, t); }
static Simplex face(const Tuple& t) { return Simplex(PrimitiveType::Triangle, t); }
static Simplex tetrahedron(const Tuple& t) { return Simplex(PrimitiveType::Tetrahedron, t); }

// these operations are only internally defined if caching is enabled to make sure there's a
// consistent semantic when simplex id caching is enabled vs not
#if defined(WMTK_ENABLE_SIMPLEX_ID_CACHING)
Expand Down

0 comments on commit 54b90a3

Please sign in to comment.