diff --git a/.clang-tidy b/.clang-tidy index c5d55af02..20868d455 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -8,6 +8,7 @@ Checks: > -fuchsia*, -llvmlibc*, -llvm-header-guard, + -llvm-prefer-static-over-anonymous-namespace, -misc-no-recursion, -modernize-use-trailing-return-type, -readability-redundant-access-specifiers, @@ -15,19 +16,19 @@ Checks: > -cppcoreguidelines-avoid-const-or-ref-data-members CheckOptions: - - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic - value: '1' + - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: "1" - key: readability-identifier-length.MinimumLoopCounterNameLength value: 1 - key: readability-identifier-length.IgnoredVariableNames - value: '^[defijkptuvw]$' + value: "^[defijkptuvw]$" # More options here: https://clang.llvm.org/extra/clang-tidy/checks/readability/identifier-naming.html - key: readability-identifier-naming.NamespaceCase value: lower_case - key: readability-identifier-naming.ClassCase - value: CamelCase + value: CamelCase - key: readability-identifier-naming.StructCase - value: CamelCase + value: CamelCase - key: readability-identifier-naming.FunctionCase value: lower_case - key: readability-identifier-naming.VariableCase @@ -51,14 +52,12 @@ CheckOptions: - key: readability-identifier-naming.GlobalFunctionCase value: lower_case - key: readability-identifier-naming.MemberConstantCase - value: CamelCase + value: CamelCase - key: readability-identifier-naming.StaticConstantCase - value: lower_case + value: lower_case - key: readability-function-cognitive-complexity.Threshold value: 10 - key: readability-function-size.ParameterThreshold value: 4 - key: misc-include-cleaner.IgnoreHeaders - value: utility;cstddef;geode/.*_export\.h;geode/.*/common\.h;geode/basic/types\.h;geode/basic/assert\.h; - - + value: utility;cstddef;geode/.*_export\.h;geode/.*/common\.h;geode/basic/types\.h;geode/basic/assert\.h; diff --git a/include/geode/model/helpers/compute_unique_vertices.hpp b/include/geode/model/helpers/compute_unique_vertices.hpp new file mode 100644 index 000000000..98bc18dda --- /dev/null +++ b/include/geode/model/helpers/compute_unique_vertices.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019 - 2026 Geode-solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#pragma once + +#include + +namespace geode +{ +} // namespace geode + +namespace geode +{ + template < typename Model > + void compute_model_unique_vertices( + const Model& model, typename Model::Builder& builder ); +} // namespace geode \ No newline at end of file diff --git a/src/geode/model/CMakeLists.txt b/src/geode/model/CMakeLists.txt index 78f7ca248..c616b1d64 100644 --- a/src/geode/model/CMakeLists.txt +++ b/src/geode/model/CMakeLists.txt @@ -29,6 +29,7 @@ add_geode_library( "helpers/component_mesh_polyhedra.cpp" "helpers/component_mesh_vertices.cpp" "helpers/component_mensurations.cpp" + "helpers/compute_unique_vertices.cpp" "helpers/convert_brep_section.cpp" "helpers/convert_model_meshes.cpp" "helpers/convert_to_mesh.cpp" @@ -108,6 +109,7 @@ add_geode_library( "helpers/component_mesh_polyhedra.hpp" "helpers/component_mesh_vertices.hpp" "helpers/component_mensurations.hpp" + "helpers/compute_unique_vertices.hpp" "helpers/convert_brep_section.hpp" "helpers/convert_model_meshes.hpp" "helpers/convert_to_mesh.hpp" diff --git a/src/geode/model/helpers/compute_unique_vertices.cpp b/src/geode/model/helpers/compute_unique_vertices.cpp new file mode 100644 index 000000000..ca28dab9e --- /dev/null +++ b/src/geode/model/helpers/compute_unique_vertices.cpp @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2019 - 2026 Geode-solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + template < typename Model > + std::vector< geode::Point< Model::dim > > get_all_points_base( + const Model& model ) + { + std::vector< geode::Point< Model::dim > > points; + for( const auto& corner : model.corners() ) + { + for( const auto v : geode::Range{ corner.mesh().nb_vertices() } ) + { + points.emplace_back( corner.mesh().point( v ) ); + } + } + for( const auto& line : model.lines() ) + { + for( const auto v : geode::Range{ line.mesh().nb_vertices() } ) + { + points.emplace_back( line.mesh().point( v ) ); + } + } + for( const auto& surface : model.surfaces() ) + { + for( const auto v : geode::Range{ surface.mesh().nb_vertices() } ) + { + points.emplace_back( surface.mesh().point( v ) ); + } + } + return points; + } + + std::vector< geode::Point2D > get_all_points( const geode::Section& model ) + { + return get_all_points_base< geode::Section >( model ); + } + + std::vector< geode::Point3D > get_all_points( const geode::BRep& model ) + { + auto points = get_all_points_base< geode::BRep >( model ); + for( const auto& block : model.blocks() ) + { + for( const auto v : geode::Range{ block.mesh().nb_vertices() } ) + { + points.emplace_back( block.mesh().point( v ) ); + } + } + return points; + } + + template < typename Model > + const geode::Point< Model::dim >& get_point_base( + const Model& model, const geode::ComponentMeshVertex& cmv ) + { + if( cmv.component_id.type() + == geode::Surface< Model::dim >::component_type_static() ) + { + const auto& surface = model.surface( cmv.component_id.id() ); + return surface.mesh().point( cmv.vertex ); + } + if( cmv.component_id.type() + == geode::Line< Model::dim >::component_type_static() ) + { + const auto& line = model.line( cmv.component_id.id() ); + return line.mesh().point( cmv.vertex ); + } + if( cmv.component_id.type() + == geode::Corner< Model::dim >::component_type_static() ) + { + const auto& corner = model.corner( cmv.component_id.id() ); + return corner.mesh().point( cmv.vertex ); + } + throw geode::OpenGeodeException{ + "[compute_unique_vertices::get_point] Unknown component type: ", + cmv.component_id.string() + }; + const auto& corner = model.corner( cmv.component_id.id() ); + return corner.mesh().point( cmv.vertex ); + } + + const geode::Point2D& get_point( + const geode::Section& model, const geode::ComponentMeshVertex& cmv ) + { + return get_point_base< geode::Section >( model, cmv ); + } + + const geode::Point3D& get_point( + const geode::BRep& model, const geode::ComponentMeshVertex& cmv ) + { + if( cmv.component_id.type() + == geode::Block< 3 >::component_type_static() ) + { + const auto& block = model.block( cmv.component_id.id() ); + return block.mesh().point( cmv.vertex ); + } + return get_point_base< geode::BRep >( model, cmv ); + } + + template < typename Model > + void set_unique_vertices_base( const Model& model, + typename Model::Builder& builder, + absl::Span< const geode::index_t > initial_uv_correspondance, + const geode::NNSearch< Model::dim >& unique_nns ) + { + for( const auto& corner : model.corners() ) + { + for( const auto v : geode::Range{ corner.mesh().nb_vertices() } ) + { + geode::ComponentMeshVertex cmv{ corner.component_id(), v }; + if( model.unique_vertex( { corner.component_id(), v } ) + != geode::NO_ID ) + { + continue; + } + const auto& point = corner.mesh().point( v ); + const auto unique_vertex_id = + unique_nns.closest_neighbor( point ); + builder.set_unique_vertex( + cmv, initial_uv_correspondance[unique_vertex_id] ); + } + } + for( const auto& line : model.lines() ) + { + for( const auto v : geode::Range{ line.mesh().nb_vertices() } ) + { + geode::ComponentMeshVertex cmv{ line.component_id(), v }; + if( model.unique_vertex( cmv ) != geode::NO_ID ) + { + continue; + } + const auto& point = line.mesh().point( v ); + const auto unique_vertex_id = + unique_nns.closest_neighbor( point ); + builder.set_unique_vertex( + cmv, initial_uv_correspondance[unique_vertex_id] ); + } + } + for( const auto& surface : model.surfaces() ) + { + for( const auto v : geode::Range{ surface.mesh().nb_vertices() } ) + { + geode::ComponentMeshVertex cmv{ surface.component_id(), v }; + if( model.unique_vertex( cmv ) != geode::NO_ID ) + { + continue; + } + const auto& point = surface.mesh().point( v ); + const auto unique_vertex_id = + unique_nns.closest_neighbor( point ); + builder.set_unique_vertex( + cmv, initial_uv_correspondance[unique_vertex_id] ); + } + } + } + + void set_unique_vertices( const geode::Section& model, + geode::SectionBuilder& builder, + absl::Span< const geode::index_t > initial_uv_correspondance, + const geode::NNSearch2D& unique_nns ) + { + set_unique_vertices_base( + model, builder, initial_uv_correspondance, unique_nns ); + } + + void set_unique_vertices( const geode::BRep& model, + geode::BRepBuilder& builder, + absl::Span< const geode::index_t > initial_uv_correspondance, + const geode::NNSearch3D& unique_nns ) + { + set_unique_vertices_base( + model, builder, initial_uv_correspondance, unique_nns ); + for( const auto& block : model.blocks() ) + { + for( const auto v : geode::Range{ block.mesh().nb_vertices() } ) + { + geode::ComponentMeshVertex cmv{ block.component_id(), v }; + if( model.unique_vertex( cmv ) != geode::NO_ID ) + { + continue; + } + const auto& point = block.mesh().point( v ); + const auto unique_vertex_id = + unique_nns.closest_neighbor( point ); + builder.set_unique_vertex( + cmv, initial_uv_correspondance[unique_vertex_id] ); + } + } + } + + template < typename Model > + std::vector< geode::index_t > compute_initial_uv_correspondance( + const Model& model, const geode::NNSearch< Model::dim >& unique_nns ) + { + std::vector< geode::index_t > initial_uv_correspondance( + unique_nns.nb_points(), geode::NO_ID ); + for( const auto uv : geode::Range{ model.nb_unique_vertices() } ) + { + const auto& cmv = model.component_mesh_vertices( uv ).front(); + const auto& point = get_point( model, cmv ); + initial_uv_correspondance[unique_nns.closest_neighbor( point )] = + uv; + } + geode::index_t new_uv = model.nb_unique_vertices(); + for( auto& uv : initial_uv_correspondance ) + { + if( uv == geode::NO_ID ) + { + uv = new_uv++; + } + } + return initial_uv_correspondance; + } + +} // namespace + +namespace geode +{ + template < typename Model > + void compute_model_unique_vertices( + const Model& model, typename Model::Builder& builder ) + { + auto all_vertices = get_all_points( model ); + NNSearch< Model::dim > nns{ all_vertices }; + const auto colocated_info = + nns.colocated_index_mapping( GLOBAL_EPSILON ); + const auto nb_initial_unique_vertices = model.nb_unique_vertices(); + NNSearch< Model::dim > unique_nns{ colocated_info.unique_points }; + const auto initial_uv_correspondance = + compute_initial_uv_correspondance( model, unique_nns ); + builder.create_unique_vertices( + colocated_info.nb_unique_points() - nb_initial_unique_vertices ); + set_unique_vertices( + model, builder, initial_uv_correspondance, unique_nns ); + } + + template void opengeode_model_api compute_model_unique_vertices( + const BRep&, BRepBuilder& ); + template void opengeode_model_api compute_model_unique_vertices( + const Section&, SectionBuilder& ); +} // namespace geode diff --git a/src/geode/model/helpers/detail/surface_mesh_validity_fix.cpp b/src/geode/model/helpers/detail/surface_mesh_validity_fix.cpp index cbc0c021b..b5c3a7758 100644 --- a/src/geode/model/helpers/detail/surface_mesh_validity_fix.cpp +++ b/src/geode/model/helpers/detail/surface_mesh_validity_fix.cpp @@ -29,6 +29,8 @@ #include +#include +#include #include #include diff --git a/tests/data/rectangular_cuboid_without_uv.og_brep b/tests/data/rectangular_cuboid_without_uv.og_brep new file mode 100644 index 000000000..ad7212ae9 Binary files /dev/null and b/tests/data/rectangular_cuboid_without_uv.og_brep differ diff --git a/tests/model/CMakeLists.txt b/tests/model/CMakeLists.txt index 7cd864a1a..9209debae 100644 --- a/tests/model/CMakeLists.txt +++ b/tests/model/CMakeLists.txt @@ -44,6 +44,13 @@ add_geode_test( ${PROJECT_NAME}::basic ${PROJECT_NAME}::model ) +add_geode_test( + SOURCE "test-compute-unique-vertices.cpp" + DEPENDENCIES + ${PROJECT_NAME}::basic + ${PROJECT_NAME}::geometry + ${PROJECT_NAME}::model +) add_geode_test( SOURCE "test-convert-brep.cpp" DEPENDENCIES diff --git a/tests/model/test-compute-unique-vertices.cpp b/tests/model/test-compute-unique-vertices.cpp new file mode 100644 index 000000000..c8b43d0cd --- /dev/null +++ b/tests/model/test-compute-unique-vertices.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019 - 2026 Geode-solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include + +#include +#include + +#include +#include +#include +#include + +void test_brep() +{ + auto brep = geode::load_brep( absl::StrCat( + geode::DATA_PATH, "rectangular_cuboid_without_uv.og_brep" ) ); + geode::BRepBuilder brep_builder{ brep }; + geode::compute_model_unique_vertices( brep, brep_builder ); + OPENGEODE_EXCEPTION( brep.nb_unique_vertices() == 8, + "[Test] Wrong number of unique vertices" ); +} + +void test() +{ + geode::OpenGeodeModelLibrary::initialize(); + test_brep(); +} + +OPENGEODE_TEST( "convert-brep" ) \ No newline at end of file