Skip to content

Commit

Permalink
added boolean operation in meshconv (union, subtract, Intersection) (#…
Browse files Browse the repository at this point in the history
…166)

* added boolean operation in meshconv (union, subtract, Intersection)

* reworked. fixed parsing
  • Loading branch information
ABSitf authored May 13, 2022
1 parent 8a45cc5 commit 803be5e
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 24 deletions.
19 changes: 11 additions & 8 deletions source/MREAlgorithms/MREBooleanOperation.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
namespace MRE
{

/** \ingroup BooleanGroup
/// \addtogroup BooleanGroup
/// \{

/**
* Enum class of available CSG operations
* \image html boolean/no_bool.png "Two separate meshes" width = 300cm
* \sa \ref MRE::boolean
Expand Down Expand Up @@ -42,10 +45,9 @@ enum class BooleanOperation
Count ///< not a valid operation
};

/// Structure to map old mesh BitSets to new
/** \struct MRE::BooleanResultMapper
* \ingroup BooleanGroup
* \brief Structure to easily map topology of MRE::boolean input meshes to result mesh
* \brief Structure to map old mesh BitSets to new
* \details Structure to easily map topology of MRE::boolean input meshes to result mesh
*
* This structure allows to map faces, vertices and edges of mesh `A` and mesh `B` input of MRE::boolean to result mesh topology primitives
* \sa \ref MRE::boolean
Expand Down Expand Up @@ -81,13 +83,14 @@ struct BooleanResultMapper
std::array<Maps, size_t( MapObject::Count )> maps;
};


// Perform boolean operation on cut meshes,
// returns mesh in space of meshA or error.
// note: actually this function is meant to be internal, use "boolean" instead
/// Perform boolean operation on cut meshes
/// \return mesh in space of meshA or error.
/// \note: actually this function is meant to be internal, use "boolean" instead
MREALGORITHMS_API tl::expected<MR::Mesh, std::string> doBooleanOperation( const MR::Mesh& meshACut, const MR::Mesh& meshBCut,
const std::vector<MR::EdgePath>& cutEdgesA, const std::vector<MR::EdgePath>& cutEdgesB,
BooleanOperation operation, const MR::AffineXf3f* rigidB2A = nullptr,
BooleanResultMapper* mapper = nullptr );

/// \}

}
110 changes: 94 additions & 16 deletions source/meshconv/meshconv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,120 @@
#include "MRMesh/MRMeshLoad.h"
#include "MRMesh/MRMeshSave.h"
#include "MREAlgorithms/MREMeshDecimate.h"
#include "MREAlgorithms/MREMeshBoolean.h"
#include <boost/program_options.hpp>
#include <boost/exception/diagnostic_information.hpp>
#include <iostream>

bool doCommand( const boost::program_options::option& option, MR::Mesh& mesh )
{
namespace po = boost::program_options;
if ( option.string_key == "remesh" )
{
float targetEdgeLen{ 0.f };
if ( !option.value.empty() )
targetEdgeLen = std::stof( option.value[0] );
if ( targetEdgeLen <= 0 )
targetEdgeLen = mesh.averageEdgeLength();

MRE::RemeshSettings rems;
rems.targetEdgeLen = targetEdgeLen;
rems.maxDeviation = targetEdgeLen / 100;
MRE::remesh( mesh, rems );

std::cout << "re-meshed successfully to target edge length " << targetEdgeLen << "\n";
}
else if ( option.string_key == "unite" || option.string_key == "subtract" || option.string_key == "intersect" )
{
std::filesystem::path meshPath = option.value[0];

auto loadRes = MR::MeshLoad::fromAnySupportedFormat( meshPath );
if ( !loadRes.has_value() )
{
std::cerr << "Mesh load error: " << loadRes.error() << "\n";
return false;
}
auto meshB = std::move( loadRes.value() );
std::cout << meshPath << " loaded successfully\n";

MRE::BooleanOperation bo{ MRE::BooleanOperation::Union };
if ( option.string_key == "subtract" )
bo = MRE::BooleanOperation::OutsideA;
if ( option.string_key == "intersect" )
bo = MRE::BooleanOperation::Intersection;
auto booleanRes = MRE::boolean( mesh, meshB, bo );

if ( !booleanRes )
{
std::cerr << booleanRes.errorString << "\n";
return false;
}
else
{
std::cout << option.string_key << " success!\n";
mesh = std::move( booleanRes.mesh );
}
}
return true;
}

// can throw
static int mainInternal( int argc, char **argv )
{
std::filesystem::path inFilePath;
std::filesystem::path outFilePath;
float targetEdgeLen = 0;

namespace po = boost::program_options;
po::options_description desc( "Available options" );
desc.add_options()
po::options_description generalOptions( "General options" );
generalOptions.add_options()
("help", "produce help message")
("input-file", po::value<std::filesystem::path>( &inFilePath ), "filename of input mesh")
("output-file", po::value<std::filesystem::path>( &outFilePath ), "filename of output mesh")
("remesh", po::value<float>( &targetEdgeLen )->implicit_value( targetEdgeLen ), "optional argument if positive is target edge length after remeshing")
;

po::options_description commands( "Commands" );
commands.add_options()
( "remesh", po::value<float>()->implicit_value( 0 ), "optional argument if positive is target edge length after remeshing" )
( "unite", po::value<std::filesystem::path>(), "unite mesh from input file and given mesh" )
( "subtract", po::value<std::filesystem::path>(), "subtract given mesh from input file mesh given mesh" )
( "intersect", po::value<std::filesystem::path>(), "intersect mesh from input file and given mesh" )
;

po::options_description allCommands( "Available options" );
allCommands.add( generalOptions ).add( commands );

po::positional_options_description p;
p.add("input-file", 1);
p.add("output-file", 1);

po::parsed_options parsedGeneral = po::command_line_parser( argc, argv )
.options( generalOptions )
.positional( p )
.allow_unregistered()
.run();

std::vector<std::string> unregisteredOptions;
for ( const auto& o : parsedGeneral.options )
{
if ( o.unregistered )
unregisteredOptions.insert( unregisteredOptions.end(), o.original_tokens.begin(), o.original_tokens.end() );
}

po::variables_map vm;
po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
po::store(parsedGeneral, vm);
po::notify(vm);

po::parsed_options parsedCommands = po::command_line_parser( unregisteredOptions )
.options( commands )
.allow_unregistered()
.run();

if ( vm.count("help") || !vm.count("input-file") || !vm.count("output-file") )
{
std::cerr <<
"meshconv is mesh file conversion utility based on MeshInspector/MeshLib\n"
"Usage: meshconv input-file output-file [options]\n"
<< desc << "\n";
<< allCommands << "\n";
return 1;
}

Expand All @@ -48,19 +128,17 @@ static int mainInternal( int argc, char **argv )
auto mesh = std::move( loadRes.value() );
std::cout << inFilePath << " loaded successfully\n";

if ( vm.count("remesh") )
std::vector<std::vector<std::string>> lists;
for ( const po::option& o : parsedCommands.options )
{
if ( targetEdgeLen <= 0 )
targetEdgeLen = mesh.averageEdgeLength();

MRE::RemeshSettings rems;
rems.targetEdgeLen = targetEdgeLen;
rems.maxDeviation = targetEdgeLen / 100;
MRE::remesh( mesh, rems );

std::cout << "re-meshed successfully to target edge length " << targetEdgeLen << "\n";
if ( !doCommand( o, mesh ) )
{
std::cerr << "Error in command : \""<< o.string_key << " " << o.value[0] << "\"\nBreak\n";
return 1;
}
}


auto saveRes = MR::MeshSave::toAnySupportedFormat( mesh, outFilePath );
if ( !saveRes.has_value() )
{
Expand Down

0 comments on commit 803be5e

Please sign in to comment.