diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 2e4eb6e910..89e096c9d0 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -58,6 +58,7 @@ add_component(${WMTK_COMPONENT_PREFIX} "procedural") add_component(${WMTK_COMPONENT_PREFIX} "fusion") add_component(${WMTK_COMPONENT_PREFIX} "triangle_insertion") add_component(${WMTK_COMPONENT_PREFIX} "to_points") +add_component(${WMTK_COMPONENT_PREFIX} "mesh_decimation") add_component(${WMTK_COMPONENT_PREFIX} "winding_number") string(LENGTH ${json_components} json_components_length) diff --git a/components/tests/CMakeLists.txt b/components/tests/CMakeLists.txt index 1de2a064a4..924f0607fc 100644 --- a/components/tests/CMakeLists.txt +++ b/components/tests/CMakeLists.txt @@ -17,7 +17,6 @@ wmtk::components) target_include_directories(${WMTK_COMPONENT_TEST_TARGET} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..) target_compile_definitions(${WMTK_COMPONENT_TEST_TARGET} PRIVATE WMTK_TEST_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\") - FetchContent_GetProperties(catch2) list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) include(Catch) diff --git a/components/tests/integration_test.cpp b/components/tests/integration_test.cpp index 196c689905..f6061e86bf 100644 --- a/components/tests/integration_test.cpp +++ b/components/tests/integration_test.cpp @@ -88,9 +88,10 @@ int authenticate_json(const std::string& json_file, const bool compute_validatio auto tetrahedra = in_args["tests"]["tetrahedra"]; if (meshes.size() != vertices.size() || meshes.size() != edges.size() || meshes.size() != faces.size() || meshes.size() != tetrahedra.size()) { - spdlog::error("JSON size missmatch between meshes and vertices edges faces or " - "tetrahedra or meshes key. Add a * " - "to the beginning of filename to allow appends."); + spdlog::error( + "JSON size missmatch between meshes and vertices edges faces or " + "tetrahedra or meshes key. Set true for the parameter \"compute_validation\"" + "to allow appends."); return 2; } @@ -197,6 +198,7 @@ WMTK_INTEGRATION("isotropic_remeshing", false); WMTK_INTEGRATION("isotropic_remeshing_mm", false); WMTK_INTEGRATION("disk_fan_mm", false); // WMTK_INTEGRATION("grid",false); +WMTK_INTEGRATION("mesh_decimation", false); // WMTK_INTEGRATION("wildmeshing_2d", false); // WMTK_INTEGRATION("wildmeshing_3d", false); WMTK_INTEGRATION("marching", false); diff --git a/components/tests/test_component_mesh_decimation.cpp b/components/tests/test_component_mesh_decimation.cpp new file mode 100644 index 0000000000..f315c5bfbe --- /dev/null +++ b/components/tests/test_component_mesh_decimation.cpp @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using json = nlohmann::json; +using namespace wmtk; + +const std::filesystem::path data_dir = WMTK_DATA_DIR; + +TEST_CASE("component_mesh_decimation_options", "[components][mesh_decimation]") +{ + using namespace components::internal; + + json o = { + {"input", "input_mesh"}, + {"output", "output_mesh"}, + {"target_len", 1.0}, + {"cell_constraint_tag_name", "tag"}, + {"attributes", {"vertices", "tag"}}, + {"pass_through", {"dummy"}}}; + + CHECK_NOTHROW(o.get()); +} + +TEST_CASE("component_mesh_decimation", "[components][2D][3D]") +{ + using namespace wmtk::components; + wmtk::io::Cache cache("wmtk_cache", "."); + + SECTION("3D") + { + auto mesh_in = wmtk::read_mesh(data_dir / "unit_test/meshes/sphere_regularized.hdf5"); + Mesh& mesh = *mesh_in; + + std::vector keep; + wmtk::attribute::MeshAttributeHandle constrait_cell_tag_handle = + mesh.get_attribute_handle("tag", mesh.top_simplex_type()); + wmtk::attribute::MeshAttributeHandle pos_handle = + mesh.get_attribute_handle("vertices", PrimitiveType::Vertex); + keep.emplace_back(constrait_cell_tag_handle); + keep.emplace_back(pos_handle); + mesh.clear_attributes(keep); + + std::vector pass_though; + internal::MeshDecimation md(mesh, constrait_cell_tag_handle, 5, pass_though); + md.process(); + + cache.write_mesh(mesh, "out3d"); + } + + SECTION("2D") + { + auto mesh_in = wmtk::read_mesh(data_dir / "2d/ellipse_layer/ellipse_01_substructure.hdf5"); + Mesh& mesh = *mesh_in; + + std::vector keep; + wmtk::attribute::MeshAttributeHandle constrait_cell_tag_handle = + mesh.get_attribute_handle("tag", mesh.top_simplex_type()); + wmtk::attribute::MeshAttributeHandle pos_handle = + mesh.get_attribute_handle("vertices", PrimitiveType::Vertex); + keep.emplace_back(constrait_cell_tag_handle); + keep.emplace_back(pos_handle); + mesh.clear_attributes(keep); + + std::vector pass_though; + internal::MeshDecimation md(mesh, constrait_cell_tag_handle, 5, pass_though); + md.process(); + + cache.write_mesh(mesh, "out2d"); + } +} \ No newline at end of file diff --git a/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/CMakeLists.txt b/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/CMakeLists.txt new file mode 100644 index 0000000000..36dd007eee --- /dev/null +++ b/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/CMakeLists.txt @@ -0,0 +1,13 @@ + +set(SRC_FILES + internal/MeshDecimationOptions.hpp + internal/MeshDecimationOptions.cpp + internal/MeshDecimation.hpp + internal/MeshDecimation.cpp + mesh_decimation.hpp + mesh_decimation.cpp + ) + + +#CURRENT_COMPONENT_LIB_NAME is set from the main cmake +target_sources(${CURRENT_COMPONENT_LIB_NAME} PRIVATE ${SRC_FILES}) diff --git a/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimation.cpp b/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimation.cpp new file mode 100644 index 0000000000..64aa3d3d5f --- /dev/null +++ b/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimation.cpp @@ -0,0 +1,188 @@ +#include "MeshDecimation.hpp" +#include "wmtk/Scheduler.hpp" +#include "wmtk/function/simplex/AMIPS.hpp" +#include "wmtk/invariants/MultiMeshLinkConditionInvariant.hpp" +#include "wmtk/invariants/SimplexInversionInvariant.hpp" +#include "wmtk/invariants/SmallerFunctionInvariant.hpp" +#include "wmtk/invariants/TodoInvariant.hpp" +#include "wmtk/operations/EdgeCollapse.hpp" +#include "wmtk/operations/attribute_new/CollapseNewAttributeStrategy.hpp" +#include "wmtk/operations/attribute_update/AttributeTransferStrategy.hpp" +#include "wmtk/utils/Logger.hpp" + +namespace wmtk::components::internal { + +MeshDecimation::MeshDecimation( + Mesh& mesh, + attribute::MeshAttributeHandle constrainted_cell_tag_handle, + double target_len, + const std::vector& pass_through_attributes) + : m_mesh(mesh) + , m_constrainted_cell_tag_handle(constrainted_cell_tag_handle) + , m_target_len(target_len) + , m_pass_through_attributes(pass_through_attributes) +{} + +void MeshDecimation::process() +{ + using namespace wmtk::attribute; + using namespace wmtk::invariants; + constexpr PrimitiveType PV = PrimitiveType::Vertex; + constexpr PrimitiveType PE = PrimitiveType::Edge; + constexpr PrimitiveType PF = PrimitiveType::Triangle; + constexpr PrimitiveType PT = PrimitiveType::Tetrahedron; + + MeshAttributeHandle& cell_tag_handle = m_constrainted_cell_tag_handle; + MeshAttributeHandle position = m_mesh.get_attribute_handle("vertices", PV); + MeshAttributeHandle todo_edge_handle = + m_mesh.register_attribute("mesh_decimation_todo_edge", PE, 1); + MeshAttributeHandle todo_vertex_handle = + m_mesh.register_attribute("mesh_decimation_todo_vertex", PV, 1); + MeshAttributeHandle edge_len_handle = + m_mesh.register_attribute("mesh_decimation_edge_len", PE, 1); + + Accessor acc_cell = m_mesh.create_accessor(cell_tag_handle); + Accessor acc_pos = m_mesh.create_accessor(position); + Accessor acc_edge = m_mesh.create_accessor(todo_edge_handle); + Accessor acc_vertex = m_mesh.create_accessor(todo_vertex_handle); + Accessor acc_len = m_mesh.create_accessor(edge_len_handle); + + // build edge tag to protect to topology of the tagged input + switch (m_mesh.top_simplex_type()) { + case PF: + for (const Tuple& edge : m_mesh.get_all(PE)) { + if (m_mesh.is_boundary(PE, edge)) { + acc_vertex.scalar_attribute(edge) = 1; + acc_vertex.scalar_attribute(m_mesh.switch_tuple(edge, PV)) = 1; + + acc_edge.scalar_attribute(edge) = 1; + } else if ( + acc_cell.scalar_attribute(edge) != + acc_cell.scalar_attribute(m_mesh.switch_tuple(edge, PF))) { + acc_vertex.scalar_attribute(edge) = 1; + acc_vertex.scalar_attribute(m_mesh.switch_tuple(edge, PV)) = 1; + + acc_edge.scalar_attribute(edge) = 1; + } + } + break; + case PT: { + for (const Tuple& face : m_mesh.get_all(PF)) { + if (m_mesh.is_boundary(PF, face)) { + acc_vertex.scalar_attribute(face) = 1; + acc_vertex.scalar_attribute(m_mesh.switch_tuple(face, PV)) = 1; + acc_vertex.scalar_attribute(m_mesh.switch_tuples(face, {PE, PV})) = 1; + + acc_edge.scalar_attribute(face) = 1; + acc_edge.scalar_attribute(m_mesh.switch_tuple(face, PE)) = 1; + acc_edge.scalar_attribute(m_mesh.switch_tuples(face, {PV, PE})) = 1; + } else if ( + acc_cell.scalar_attribute(face) != + acc_cell.scalar_attribute(m_mesh.switch_tuple(face, PT))) { + acc_vertex.scalar_attribute(face) = 1; + acc_vertex.scalar_attribute(m_mesh.switch_tuple(face, PV)) = 1; + acc_vertex.scalar_attribute(m_mesh.switch_tuples(face, {PE, PV})) = 1; + + acc_edge.scalar_attribute(face) = 1; + acc_edge.scalar_attribute(m_mesh.switch_tuple(face, PE)) = 1; + acc_edge.scalar_attribute(m_mesh.switch_tuples(face, {PV, PE})) = 1; + } + } + break; + } + case PE: + case PV: + default: + log_and_throw_error("MeshDecimation.cpp: mesh_decimation component only supports tetmesh " + "and trimesh for now!"); + } + + // stage 1: tag edges incident to the surface + for (const Tuple& edge : m_mesh.get_all(PE)) { + if (acc_vertex.scalar_attribute(edge) == 1 || + acc_vertex.scalar_attribute(m_mesh.switch_tuple(edge, PV)) == 1) { + acc_edge.scalar_attribute(edge) = 1; + } + } + // stage 2: tag vertices incident to the tagged edge + for (const Tuple& edge : m_mesh.get_all(PE)) { + if (acc_edge.scalar_attribute(edge) == 1) { + acc_vertex.scalar_attribute(edge) = 1; + acc_vertex.scalar_attribute(m_mesh.switch_tuple(edge, PV)) = 1; + } + } + // stage 3: tag edges incident to the tagged vertices + for (const Tuple& edge : m_mesh.get_all(PE)) { + if (acc_vertex.scalar_attribute(edge) == 1 || + acc_vertex.scalar_attribute(m_mesh.switch_tuple(edge, PV)) == 1) { + acc_edge.scalar_attribute(edge) = 1; + } + } + + // Storing edge lengths and Edge length update + auto compute_edge_length = [](const Eigen::MatrixXd& P) -> Eigen::VectorXd { + assert(P.cols() == 2); + assert(P.rows() == 2 || P.rows() == 3); + return Eigen::VectorXd::Constant(1, (P.col(0) - P.col(1)).norm()); + }; + auto edge_length_update = + std::make_shared>( + edge_len_handle, + position, + compute_edge_length); + edge_length_update->run_on_all(); + + auto m_prio_short_edges_first = [&](const simplex::Simplex& s) { + assert(s.primitive_type() == PrimitiveType::Edge); + auto acc = m_mesh.create_accessor(edge_len_handle); + return std::vector({acc.scalar_attribute(s.tuple())}); + }; + + auto op_collapse = std::make_shared(m_mesh); + + op_collapse->add_invariant( + std::make_shared(m_mesh, todo_edge_handle.as(), 0)); + op_collapse->add_invariant( + std::make_shared(m_mesh, edge_len_handle.as(), m_target_len)); + + auto m_amips = std::make_shared(m_mesh, position); + auto m_link_conditions = std::make_shared(m_mesh); + m_link_conditions->add(std::make_shared(m_mesh)); + auto m_function_invariant = + std::make_shared(m_mesh.top_simplex_type(), m_amips, 30); + auto m_inversion_invariant = + std::make_shared(m_mesh, position.as()); + + op_collapse->add_invariant(m_link_conditions); + op_collapse->add_invariant(m_inversion_invariant); + op_collapse->add_invariant(m_function_invariant); + + op_collapse->add_transfer_strategy(edge_length_update); + + op_collapse->set_priority(m_prio_short_edges_first); + + op_collapse->set_new_attribute_strategy( + position, + wmtk::operations::CollapseBasicStrategy::Mean); + op_collapse->set_new_attribute_strategy(todo_vertex_handle); + + op_collapse->set_new_attribute_strategy(todo_edge_handle); + op_collapse->set_new_attribute_strategy(edge_len_handle); + op_collapse->set_new_attribute_strategy(cell_tag_handle); + + // pass_through + for (const auto& attr : m_pass_through_attributes) { + op_collapse->set_new_attribute_strategy(attr); + } + + while (true) { + Scheduler scheduler; + SchedulerStats pass_stats = scheduler.run_operation_on_all(*op_collapse); + m_mesh.consolidate(); + if (pass_stats.number_of_successful_operations() == 0) { + break; + } + } +} + +} // namespace wmtk::components::internal diff --git a/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimation.hpp b/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimation.hpp new file mode 100644 index 0000000000..f569a456da --- /dev/null +++ b/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimation.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +namespace wmtk::components::internal { + +class MeshDecimation +{ +public: + MeshDecimation( + Mesh& mesh, + attribute::MeshAttributeHandle constrainted_cell_tag_handle, + double target_len, + const std::vector& pass_through_attributes); + + void process(); + +private: + Mesh& m_mesh; + + attribute::MeshAttributeHandle m_constrainted_cell_tag_handle; + double m_target_len; + + std::vector m_pass_through_attributes; +}; + +} // namespace wmtk::components::internal diff --git a/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimationOptions.cpp b/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimationOptions.cpp new file mode 100644 index 0000000000..8d4c596a32 --- /dev/null +++ b/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimationOptions.cpp @@ -0,0 +1,28 @@ +#include "MeshDecimationOptions.hpp" + +#include + +namespace wmtk::components::internal { + +void to_json(nlohmann::json& j, MeshDecimationOptions& o) +{ + j = { + {"input", o.input}, + {"output", o.output}, + {"target_len", o.target_len}, + {"cell_constraint_tag_name", o.cell_constraint_tag_name}, + {"attributes", o.attributes}, + {"pass_through", o.pass_through}}; +} + +void from_json(const nlohmann::json& j, MeshDecimationOptions& o) +{ + o.input = j.at("input"); + o.output = j.at("output"); + o.target_len = j.at("target_len"); + o.cell_constraint_tag_name = j.at("cell_constraint_tag_name"); + j.at("attributes").get_to(o.attributes); + j.at("pass_through").get_to(o.pass_through); +} + +} // namespace wmtk::components::internal \ No newline at end of file diff --git a/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimationOptions.hpp b/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimationOptions.hpp new file mode 100644 index 0000000000..e89f2fda9d --- /dev/null +++ b/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimationOptions.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include + +namespace wmtk { +namespace components { +namespace internal { + +struct MeshDecimationOptions +{ + std::string input; + std::string output; + double target_len; + std::string cell_constraint_tag_name; + std::vector attributes; + std::vector pass_through; +}; + +void to_json(nlohmann::json& j, MeshDecimationOptions& o); + +void from_json(const nlohmann::json& j, MeshDecimationOptions& o); + +} // namespace internal +} // namespace components +} // namespace wmtk \ No newline at end of file diff --git a/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/mesh_decimation.cpp b/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/mesh_decimation.cpp new file mode 100644 index 0000000000..4fc72bbd83 --- /dev/null +++ b/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/mesh_decimation.cpp @@ -0,0 +1,44 @@ +#include "mesh_decimation.hpp" +#include "internal/MeshDecimation.hpp" +#include "internal/MeshDecimationOptions.hpp" +#include "wmtk/components/base/get_attributes.hpp" + + +namespace wmtk::components { + +void mesh_decimation(const base::Paths& paths, const nlohmann::json& j, io::Cache& cache) +{ + using namespace internal; + + MeshDecimationOptions options = j.get(); + + // input + std::shared_ptr mesh_in = cache.read_mesh(options.input); + Mesh& mesh = *mesh_in; + auto pass_through_attributes = base::get_attributes(cache, mesh, options.pass_through); + auto original_attributes = base::get_attributes(cache, mesh, options.attributes); + auto cell_constrait_tag_handle = mesh.get_attribute_handle( + options.cell_constraint_tag_name, + mesh.top_simplex_type()); + + // clear attributes + { + std::vector keeps = pass_through_attributes; + keeps.insert(keeps.end(), original_attributes.begin(), original_attributes.end()); + mesh.clear_attributes(keeps); + } + + MeshDecimation md(mesh, cell_constrait_tag_handle, options.target_len, pass_through_attributes); + + md.process(); + + // clear attributes + { + std::vector keeps = pass_through_attributes; + keeps.insert(keeps.end(), original_attributes.begin(), original_attributes.end()); + mesh.clear_attributes(keeps); + } + + cache.write_mesh(*mesh_in, options.output); +} +} // namespace wmtk::components \ No newline at end of file diff --git a/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/mesh_decimation.hpp b/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/mesh_decimation.hpp new file mode 100644 index 0000000000..e9efa045bc --- /dev/null +++ b/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/mesh_decimation.hpp @@ -0,0 +1,19 @@ +#pragma once +#include +#include + +#include + + +namespace wmtk::components { + +/** + * @brief Perform decimation tetrahedra/triangles. + * If you given a mesh and the tag of the top simplex. then this component will + * preserve the tagged mesh's boundary edge and the edges incident to different + * tagged simplices. And decimate the mesh until the all edge need to collapse + * are large or equal the target length. + */ +void mesh_decimation(const base::Paths& paths, const nlohmann::json& j, io::Cache& cache); + +} // namespace wmtk::components diff --git a/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/mesh_decimation_example.json b/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/mesh_decimation_example.json new file mode 100644 index 0000000000..b1cc246eef --- /dev/null +++ b/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/mesh_decimation_example.json @@ -0,0 +1,17 @@ +{ + "components": [ + { + "mesh_decimation": { + "input": "input_mesh", + "output": "output_mesh", + "target_len": 2.0, + "cell_constraint_tag_name": "tag", + "attributes": [ + "tag", + "vertices" + ], + "pass_through": [] + } + } + ] +} \ No newline at end of file diff --git a/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/mesh_decimation_spec.json b/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/mesh_decimation_spec.json new file mode 100644 index 0000000000..1804914310 --- /dev/null +++ b/components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/mesh_decimation_spec.json @@ -0,0 +1,55 @@ +[ + { + "pointer": "/", + "type": "object", + "required": [ + "input", + "output", + "target_len", + "cell_constraint_tag_name", + "attributes" + ], + "optional": [ + "pass_through" + ] + }, + { + "pointer": "/input", + "type": "string", + "doc": "input mesh" + }, + { + "pointer": "/output", + "type": "string", + "doc": "output mesh" + }, + { + "pointer": "/cell_constraint_tag_name", + "type": "string", + "doc": "constraint_tag_handle's name" + }, + { + "pointer": "/target_len", + "type": "float", + "doc": "the decimation will make sure all edge will end with a length larger than target_len" + }, + { + "pointer": "/attributes", + "type": "list", + "doc": "the original attributes" + }, + { + "pointer": "/attributes/*", + "type": "string" + }, + { + "pointer": "/pass_through", + "type": "list", + "default": [], + "doc": "all attributes that are not deleted by the component but also not required" + }, + { + "pointer": "/pass_through/*", + "type": "string" + } +] \ No newline at end of file diff --git a/src/wmtk/invariants/CMakeLists.txt b/src/wmtk/invariants/CMakeLists.txt index 9279428240..59b83aed01 100644 --- a/src/wmtk/invariants/CMakeLists.txt +++ b/src/wmtk/invariants/CMakeLists.txt @@ -52,6 +52,8 @@ set(SRC_FILES FusionEdgeInvariant.hpp FusionEdgeInvariant.cpp + SmallerFunctionInvariant.hpp + SmallerFunctionInvariant.cpp uvEdgeInvariant.hpp uvEdgeInvariant.cpp diff --git a/src/wmtk/invariants/SmallerFunctionInvariant.cpp b/src/wmtk/invariants/SmallerFunctionInvariant.cpp new file mode 100644 index 0000000000..c7634953c6 --- /dev/null +++ b/src/wmtk/invariants/SmallerFunctionInvariant.cpp @@ -0,0 +1,39 @@ +#include "SmallerFunctionInvariant.hpp" + +#include +#include + +namespace wmtk::invariants { + +SmallerFunctionInvariant::SmallerFunctionInvariant( + const PrimitiveType type, + const std::shared_ptr& func, + const double max_value) + : Invariant(func->mesh()) + , m_func(func) + , m_max_value(max_value) + , m_type(type) +{} + +bool SmallerFunctionInvariant::after( + const std::vector& top_dimension_tuples_before, + const std::vector& top_dimension_tuples_after) const +{ + auto max = [&](const std::vector& tuples) { + double value = std::numeric_limits::lowest(); + for (const auto& t : tuples) + value = std::max(value, m_func->get_value(simplex::Simplex(m_type, t))); + + return value; + }; + const double after = max(top_dimension_tuples_after); + + if (after < m_max_value) { + return true; + } + + const double before = mesh().parent_scope(max, top_dimension_tuples_before); + + return after < before; +} +} // namespace wmtk::invariants \ No newline at end of file diff --git a/src/wmtk/invariants/SmallerFunctionInvariant.hpp b/src/wmtk/invariants/SmallerFunctionInvariant.hpp new file mode 100644 index 0000000000..9204ab4691 --- /dev/null +++ b/src/wmtk/invariants/SmallerFunctionInvariant.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include "Invariant.hpp" + +namespace wmtk { + +namespace function { +class PerSimplexFunction; +} + +namespace invariants { + +class SmallerFunctionInvariant : public Invariant +{ +public: + SmallerFunctionInvariant( + const PrimitiveType type, + const std::shared_ptr& func, + const double max_value); + + bool after( + const std::vector& top_dimension_tuples_before, + const std::vector& top_dimension_tuples_after) const override; + +private: + std::shared_ptr m_func; + const double m_max_value; + const PrimitiveType m_type; +}; +} // namespace invariants +} // namespace wmtk \ No newline at end of file