MeshLib
 
Loading...
Searching...
No Matches
Examples

This page contains some examples of using MeshLib

Basing your application on MeshLib

First of all you should make main function like this

#include "MRMesh/MRLog.h"
int main( int argc, char** argv )
{
// Init the viewer
MR::Viewer::LaunchParams launchParams{ .argc = argc, .argv = argv };
launchParams.name = "Your app name";
MR::ViewerSetup viewerSetup;
viewerSetup.setupCommonModifiers( MR::Viewer::instance() );
viewerSetup.setupCommonPlugins( MR::Viewer::instance() );
viewerSetup.setupSettingsManager( MR::Viewer::instance(), launchParams.name );
viewerSetup.setupConfiguration( MR::Viewer::instance() );
return MR::launchDefaultViewer( launchParams, viewerSetup );
}
Definition MRSetupViewer.h:10
virtual MRVIEWER_API void setupBasePlugins(Viewer *) const
static MRVIEWER_API void parseLaunchParams(LaunchParams &params)
static Viewer * instance()
Definition MRViewer.h:102
MRMESH_API void setupLoggerByDefault()
MRVIEWER_API int launchDefaultViewer(const Viewer::LaunchParams &params, const ViewerSetup &setup)
Definition MRViewer.h:47
int argc
Definition MRViewer.h:67

Then you should make your plugins, to find how have a look at State Plugins overview page

C++ Basic Examples

You can find example project in MeshLib/source/meshconv

Following code presents example of loading and saving mesh file

#include <iostream>
int main()
{
std::filesystem::path inFilePath = "mesh.stl";
auto loadRes = MR::MeshLoad::fromAnySupportedFormat( inFilePath );
if ( loadRes.has_value() )
{
std::filesystem::path outFilePath = "mesh.ply";
auto saveRes = MR::MeshSave::toAnySupportedFormat( loadRes.value(), outFilePath );
if ( !saveRes.has_value() )
std::cerr << saveRes.error() << "\n";
}
else
std::cerr << loadRes.error() << "\n";
return 0;
}
MRMESH_API Expected< Mesh, std::string > fromAnySupportedFormat(const std::filesystem::path &file, const MeshLoadSettings &settings={})
detects the format from file extension and loads mesh from it
MRMESH_API VoidOrErrStr toAnySupportedFormat(const Mesh &mesh, const std::filesystem::path &file, const SaveSettings &settings={})
detects the format from file extension and save mesh to it

Further examples won't check return values for sake of clarity

See also
MR::MeshLoad
MR::MeshSave

Some examples of mesh modification are presented here

#include "MRMesh/MRMesh.h"
int main()
{
// Load mesh
MR::Mesh mesh = MR::MeshLoad::fromAnySupportedFormat( "mesh.stl" ).value();
// Relax mesh (5 iterations)
MR::relax( mesh, {{5}} );
// Subdivide mesh
props.maxDeviationAfterFlip = 0.5f;
MR::subdivideMesh( mesh, props );
// Rotate mesh
mesh.transform( rotationXf );
return 0;
}
MRMESH_API bool relax(Mesh &mesh, const MeshRelaxParams &params={}, ProgressCallback cb={})
MRMESH_API int subdivideMesh(Mesh &mesh, const SubdivideSettings &settings={})
Definition MRAffineXf.h:10
static constexpr AffineXf linear(const M &A) noexcept
creates linear-only transformation (without translation)
Definition MRAffineXf.h:24
static constexpr Matrix3 rotation(const Vector3< float > &axis, float angle) noexcept
creates matrix representing rotation around given axis on given angle
Definition MRMesh.h:23
MRMESH_API void transform(const AffineXf3f &xf, const VertBitSet *region=nullptr)
Definition MRMeshSubdivide.h:17
float maxDeviationAfterFlip
Improves local mesh triangulation by doing edge flips if it does not make too big surface deviation.
Definition MRMeshSubdivide.h:23
static constexpr Vector3 plusZ() noexcept
Definition MRVector3.h:31

Export example of points and triangles from mesh (e.g. for rendering)

#include "MRMesh/MRMesh.h"
#include "MRMesh/MRCube.h"
int main()
{
// create some mesh
// all vertices of valid triangles
const std::vector<std::array<MR::VertId, 3>> triangles = mesh.topology.getAllTriVerts();
// all point coordinates
const std::vector<MR::Vector3f> & points = mesh.points.vec_;
// triangle vertices as tripples of ints (pointing to elements in points vector)
const int * vertexTripples = reinterpret_cast<const int*>( triangles.data() );
return 0;
}
MRMESH_API std::vector< ThreeVertIds > getAllTriVerts() const
returns three vertex ids for valid triangles, invalid triangles are skipped
std::vector< T > vec_
the user can directly manipulate the vector, anyway she cannot break anything
Definition MRVector.h:135
MRMESH_API Mesh makeCube(const Vector3f &size=Vector3f::diagonal(1.0f), const Vector3f &base=Vector3f::diagonal(-0.5f))
MeshTopology topology
Definition MRMesh.h:24
VertCoords points
Definition MRMesh.h:25

Example of mesh decimate

#include "MRMesh/MRMesh.h"
int main()
{
// Load mesh
// Setup decimate parameters
settings.maxError = 0.05f;
// Decimate mesh
MR::decimateMesh( mesh, settings );
// Save result
MR::MeshSave::toAnySupportedFormat( mesh, "decimatedMesh.stl" );
}
MRMESH_API DecimateResult decimateMesh(Mesh &mesh, const DecimateSettings &settings={})
Collapse edges in mesh region according to the settings.
Parameters structure for MR::decimateMesh.
Definition MRMeshDecimate.h:32
float maxError
Definition MRMeshDecimate.h:38

Example of Boolean operation

#include <iostream>
#include "MRMesh/MRMesh.h"
int main()
{
// create first sphere with radius of 1 unit
MR::Mesh sphere1 = MR::makeUVSphere( 1.0f, 64, 64 );
// create second sphere by cloning the first sphere and moving it in X direction
MR::Mesh sphere2 = sphere1;
sphere2.transform( xf );
// perform boolean operation
MR::Mesh resultMesh = *result;
if ( !result.valid() )
std::cerr << result.errorString << "\n";
// save result to STL file
MR::MeshSave::toAnySupportedFormat( resultMesh, "out_boolean.stl" );
return 0;
}
MRMESH_API BooleanResult boolean(const Mesh &meshA, const Mesh &meshB, BooleanOperation operation, const AffineXf3f *rigidB2A, BooleanResultMapper *mapper=nullptr, ProgressCallback cb={})
Performs CSG operation on two meshes.
MRMESH_API Mesh makeUVSphere(float radius=1.0, int horisontalResolution=16, int verticalResolution=16)
creates a mesh of sphere with regular triangulation (parallels and meridians)
static constexpr AffineXf translation(const V &b) noexcept
creates translation-only transformation (with identity linear component)
Definition MRAffineXf.h:22
Structure contain boolean result.
Definition MRMeshBoolean.h:27

Example of mesh offset

#include "MRMesh/MRMesh.h"
#include "MRMesh/MRBox.h"
int main()
{
// Load mesh
// Setup parameters
params.voxelSize = mesh.computeBoundingBox().diagonal() * 5e-3f; // offset grid precision (algorithm is voxel based)
if ( !MR::findRightBoundary( mesh.topology ).empty() )
params.signDetectionMode = MR::SignDetectionMode::HoleWindingRule; // use if you have holes in mesh
// Make offset mesh
float offset = mesh.computeBoundingBox().diagonal() * 0.05f;
auto meshRes = MR::generalOffsetMesh( mesh, offset, params );
if ( !meshRes.has_value() )
{
// log meshRes.error()
return 1;
}
// Save result
MR::MeshSave::toAnySupportedFormat( *meshRes, "mesh_offset.stl" );
return 0;
}
@ HoleWindingRule
computes winding number generalization with support of holes in mesh, slower than WindingRule
MRMESH_API std::vector< EdgeLoop > findRightBoundary(const MeshTopology &topology, const FaceBitSet *region=nullptr)
MRMESH_API Expected< Mesh > generalOffsetMesh(const MeshPart &mp, float offset, const GeneralOffsetParameters &params)
Offsets mesh by converting it to voxels and back using one of three modes specified in the parameters...
float voxelSize
Definition MROffset.h:17
allows the user to select in the parameters which offset algorithm to call
Definition MROffset.h:87
MRMESH_API Box3f computeBoundingBox(const AffineXf3f *toWorld=nullptr) const
SignDetectionMode signDetectionMode
determines the method to compute distance sign
Definition MROffset.h:29

Example of mesh ICP (finding transformation to match objects)

#include <iostream>
#include "MRMesh/MRMesh.h"
#include "MRMesh/MRICP.h"
#include "MRMesh/MRBox.h"
int main()
{
// Load meshes
MR::Mesh meshFloating = *MR::MeshLoad::fromAnySupportedFormat( "meshA.stl" );
MR::Mesh meshFixed = *MR::MeshLoad::fromAnySupportedFormat( "meshB.stl" );
// Prepare ICP parameters
float diagonal = meshFixed.getBoundingBox().diagonal();
float icpSamplingVoxelSize = diagonal * 0.01f; // To sample points from object
MR::ICPProperties icpParams;
icpParams.distThresholdSq = MR::sqr( diagonal * 0.1f ); // Use points pairs with maximum distance specified
icpParams.exitVal = diagonal * 0.003f; // Stop when distance reached
// Calculate transformation
MR::ICP icp(
MR::MeshOrPoints{ MR::MeshPart{ meshFloating } },
MR::MeshOrPoints{ MR::MeshPart{ meshFixed } },
icpSamplingVoxelSize );
icp.setParams( icpParams );
MR::AffineXf3f xf = icp.calculateTransformation();
// Transform floating mesh
meshFloating.transform( xf );
// Output information string
std::string info = icp.getLastICPInfo();
std::cerr << info << "\n";
// Save result
MR::MeshSave::toAnySupportedFormat( meshFloating, "meshA_icp.stl" );
}
Definition MRICP.h:172
void setParams(const ICPProperties &prop)
tune algorithm params before run calculateTransformation()
Definition MRICP.h:196
Definition MRMeshOrPoints.h:17
constexpr T sqr(T x) noexcept
Definition MRMeshFwd.h:519
AffineXf3< float > AffineXf3f
Definition MRMeshFwd.h:178
Definition MRICP.h:115
float distThresholdSq
Points pair will be counted only if squared distance between points is lower than.
Definition MRICP.h:129
Definition MRMeshPart.h:11

Example of filling holes

#include "MRMesh/MRMesh.h"
int main()
{
// Load mesh
// Find single edge for each hole in mesh
std::vector<MR::EdgeId> holeEdges = mesh.topology.findHoleRepresentiveEdges();
for ( MR::EdgeId e : holeEdges )
{
// Setup filling parameters
params.metric = MR::getUniversalMetric( mesh );
// Fill hole represented by `e`
MR::fillHole( mesh, e, params );
}
// Save result
auto saveRes = MR::MeshSave::toAnySupportedFormat( mesh, "filledMesh.stl" );
}
Definition MRId.h:52
MRMESH_API std::vector< EdgeId > findHoleRepresentiveEdges() const
returns one edge with no valid left face for every boundary in the mesh
MRMESH_API FillHoleMetric getUniversalMetric(const Mesh &mesh)
MRMESH_API void fillHole(Mesh &mesh, EdgeId a, const FillHoleParams &params={})
Fills hole in mesh .
Parameters structure for MR::fillHole Structure has some options to control MR::fillHole.
Definition MRMeshFillHole.h:25
FillHoleMetric metric
Definition MRMeshFillHole.h:30

Example of stitching holes (connect two holes with a cylinder)

#include "MRMesh/MRMesh.h"
int main()
{
// Load meshes
auto meshARes = MR::MeshLoad::fromAnySupportedFormat( "meshAwithHole.stl" );
auto meshBRes = MR::MeshLoad::fromAnySupportedFormat( "meshBwithHole.stl" );
// Unite meshes
MR::Mesh mesh = std::move( meshARes.value() );
mesh.addPart( meshBRes.value() );
// Find holes (expect that there are exactly 2 holes)
std::vector<MR::EdgeId> edges = mesh.topology.findHoleRepresentiveEdges();
if ( edges.size() != 2 )
return 1;
// Connect two holes
params.metric = MR::getUniversalMetric( mesh );
MR::buildCylinderBetweenTwoHoles( mesh, edges.front(), edges.back(), params );
// Save result
auto saveRes = MR::MeshSave::toAnySupportedFormat( mesh, "stitchedMesh.stl" );
return 0;
}
MRMESH_API void buildCylinderBetweenTwoHoles(Mesh &mesh, EdgeId a, EdgeId b, const StitchHolesParams &params={})
Stitches two holes in Mesh .
MRMESH_API void addPart(const Mesh &from, FaceMap *outFmap=nullptr, VertMap *outVmap=nullptr, WholeEdgeMap *outEmap=nullptr, bool rearrangeTriangles=false)
appends mesh (from) in addition to this mesh: creates new edges, faces, verts and points
Parameters structure for MR::buildCylinderBetweenTwoHoles Structure has some options to control MR::b...
Definition MRMeshFillHole.h:74
FillHoleMetric metric
Definition MRMeshFillHole.h:79

Example of extrude faces on mesh

#include "MRMesh/MRMesh.h"
#include "MRMesh/MRId.h"
int main()
{
// Load mesh
// Select faces to extrude
MR::FaceBitSet facesToExtrude;
facesToExtrude.autoResizeSet( MR::FaceId( 1 ) );
facesToExtrude.autoResizeSet( MR::FaceId( 2 ) );
// Create duplicated verts on region boundary
MR::makeDegenerateBandAroundRegion( mesh, facesToExtrude );
// Find vertices that will be moved
auto vertsForMove = MR::getIncidentVerts( mesh.topology, facesToExtrude );
MR::BitSetParallelFor( vertsForMove, [&] ( MR::VertId v )
{
// Move each vertex
} );
// Invalidate internal caches after manual changing
// Save mesh
MR::MeshSave::toAnySupportedFormat( mesh, "extrudedMesh.stl" );
}
void autoResizeSet(IndexType pos, size_type len, bool val=true)
Definition MRBitSet.h:129
auto BitSetParallelFor(const BS &bs, F f, Cb &&... cb)
Definition MRBitSetParallelFor.h:202
MRMESH_API void makeDegenerateBandAroundRegion(Mesh &mesh, const FaceBitSet &region, const MakeDegenerateBandAroundRegionParams &params={})
Create a band of degenerate faces along the border of the specified region and the rest of the mesh.
MRMESH_API VertBitSet getIncidentVerts(const MeshTopology &topology, const FaceBitSet &faces)
MRMESH_API void invalidateCaches(bool pointsChanged=true)

Python Basic Examples

In this section we provide the same examples but with python code
Load and save example:

import meshlib.mrmeshpy as mrmeshpy
try:
mesh = mrmeshpy.loadMesh("mesh.stl")
except ValueError as e:
print(e)
mrmeshpy.saveMesh(mesh, "mesh.ply")

Using numpy to create mesh and get back its primitives

import meshlib.mrmeshpy as mrmeshpy
import meshlib.mrmeshnumpy as mrmeshnumpy
import numpy as np
faces = np.ndarray(shape=(2, 3), dtype=np.int32, buffer=np.array([[0, 1, 2], [2, 3, 0]], dtype=np.int32))
# mrmesh uses float32 for vertex coordinates
# however, you could also use float64
verts = np.ndarray(shape=(4, 3), dtype=np.float32, buffer=np.array([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0]], dtype=np.float32))
mesh = mrmeshnumpy.meshFromFacesVerts(faces, verts)
# some mesh manipulations
out_verts = mrmeshnumpy.getNumpyVerts(mesh)
out_faces = mrmeshnumpy.getNumpyFaces(mesh.topology)

Mesh modification examples

import meshlib.mrmeshpy as mrmeshpy
mesh = mrmeshpy.loadMesh("mesh.stl")
relax_params = mrmeshpy.MeshRelaxParams()
relax_params.iterations = 5
mrmeshpy.relax(mesh, relax_params)
props = mrmeshpy.SubdivideSettings()
props.maxDeviationAfterFlip = 0.5
mrmeshpy.subdivideMesh(mesh,props)
plus_z = mrmeshpy.Vector3f()
plus_z.z = 1.0
rotation_xf = mrmeshpy.AffineXf3f.linear(mrmeshpy.Matrix3f.rotation(plus_z, 3.1415 * 0.5))
mesh.transform(rotation_xf)

Simple triangulation

from meshlib import mrmeshpy as mm
from meshlib import mrmeshnumpy as mn
import numpy as np
u, v = np.mgrid[0:2 * np.pi:100j, 0:np.pi:100j]
x = np.cos(u) * np.sin(v)
y = np.sin(u) * np.sin(v)
z = np.cos(v)
# Prepare for MeshLib PointCloud
verts = np.stack((x.flatten(), y.flatten(), z.flatten()), axis=-1).reshape(-1, 3)
# Create MeshLib PointCloud from np ndarray
pc = mn.pointCloudFromPoints(verts)
# Remove duplicate points
pc.validPoints = mm.pointUniformSampling(pc, 1e-3)
pc.invalidateCaches()
# Triangulate it
triangulated_pc = mm.triangulatePointCloud(pc)
# Fix possible issues
triangulated_pc = mm.offsetMesh(triangulated_pc, 0.0)

Triangulation of regular numpy grid

from meshlib import mrmeshpy as mm
from meshlib import mrmeshnumpy as mn
import numpy as np
u, v = np.mgrid[0:2 * np.pi:50j, 0:np.pi:100j]
x = np.cos(u) * np.sin(v)
y = np.sin(u) * np.sin(v)
z = np.cos(v)
mesh = mn.meshFromUVPoints(x,y,z)

Using plotly to visualize mesh

from meshlib import mrmeshpy as mm
from meshlib import mrmeshnumpy as mn
import numpy as np
import plotly.graph_objects as go
# load mesh
mesh = mm.loadMesh("mesh.stl")
# extract numpy arrays
verts = mn.getNumpyVerts(mesh)
faces = mn.getNumpyFaces(mesh.topology)
# prepare data for plotly
verts_t = np.transpose(verts)
faces_t = np.transpose(faces)
# draw
fig = go.Figure(data=[
go.Mesh3d(
x=verts_t[0],
y=verts_t[1],
z=verts_t[2],
i=faces_t[0],
j=faces_t[1],
k=faces_t[2]
)
])
fig.show()
Plotly visualization

Example of mesh decimate

import meshlib.mrmeshpy as mrmeshpy
# Load mesh
mesh = mrmeshpy.loadMesh("mesh.stl")
# Setup decimate parameters
settings = mrmeshpy.DecimateSettings()
settings.maxError = 0.05
# Decimate mesh
mrmeshpy.decimateMesh(mesh, settings)
# Save result
mrmeshpy.saveMesh(mesh, "decimatedMesh.stl")

Example of Boolean operation

import meshlib.mrmeshpy as mrmeshpy
# create first sphere with radius of 1 unit
sphere1 = mrmeshpy.makeUVSphere(1.0, 64, 64)
# create second sphere by cloning the first sphere and moving it in X direction
sphere2 = mrmeshpy.copyMesh(sphere1)
xf = mrmeshpy.AffineXf3f.translation(mrmeshpy.Vector3f(0.7, 0.0, 0.0))
sphere2.transform(xf)
# perform boolean operation
result = mrmeshpy.boolean(sphere1, sphere2, mrmeshpy.BooleanOperation.Intersection)
result_mesh = result.mesh
if not result.valid():
print(result.errorString)
# save result to STL file
mrmeshpy.saveMesh(result_mesh, "out_boolean.stl")

Example of mesh offset

import meshlib.mrmeshpy as mrmeshpy
# Load mesh
mesh = mrmeshpy.loadMesh("mesh.stl")
# Setup parameters
params = mrmeshpy.OffsetParameters()
params.voxelSize = mesh.computeBoundingBox().diagonal() * 5e-3 # offset grid precision (algorithm is voxel based)
if mrmeshpy.findRightBoundary(mesh.topology).empty():
params.signDetectionMode = mrmeshpy.SignDetectionMode.HoleWindingRule # use if you have holes in mesh
# Make offset mesh
offset = mesh.computeBoundingBox().diagonal() * 0.05
result_mesh = mrmeshpy.offsetMesh(mesh, offset, params)
# Save result
mrmeshpy.saveMesh(result_mesh, "offsetMesh.stl")

Example of mesh ICP

import meshlib.mrmeshpy as mrmeshpy
# Load meshes
meshFloating = mrmeshpy.loadMesh("meshA.stl")
meshFixed = mrmeshpy.loadMesh("meshB.stl")
# Prepare ICP parameters
diagonal = meshFixed.getBoundingBox().diagonal()
icp_sampling_voxel_size = diagonal * 0.01 # To sample points from object
icp_params = mrmeshpy.ICPProperties()
icp_params.distThresholdSq = (diagonal * 0.1) ** 2 # Use points pairs with maximum distance specified
icp_params.exitVal = diagonal * 0.003 # Stop when this distance reached
# Calculate transformation
icp = mrmeshpy.ICP(meshFloating, meshFixed,
mrmeshpy.AffineXf3f(), mrmeshpy.AffineXf3f(),
icp_sampling_voxel_size)
icp.setParams(icp_params)
xf = icp.calculateTransformation()
# Transform floating mesh
meshFloating.transform(xf)
# Output information string
print(icp.getLastICPInfo())
# Save result
mrmeshpy.saveMesh(meshFloating, "meshA_icp.stl")
std::optional< T > distance(const Plane3< T > &plane1, const Plane3< T > &plane2, T errorLimit=std::numeric_limits< T >::epsilon() *T(20))
Definition MRIntersection.h:104

Example of filling holes

import meshlib.mrmeshpy as mrmeshpy
# Load mesh
mesh = mrmeshpy.loadMesh("mesh.stl")
# Find single edge for each hole in mesh
for e in hole_edges:
# Setup filling parameters
params = mrmeshpy.FillHoleParams()
params.metric = mrmeshpy.getUniversalMetric(mesh)
# Fill hole represented by `e`
mrmeshpy.fillHole(mesh, e, params)
# Save result
mrmeshpy.saveMesh(mesh, "filledMesh.stl")

Example of stitch holes

import meshlib.mrmeshpy as mrmeshpy
# Load meshes
mesh_a = mrmeshpy.loadMesh("meshAwithHole.stl")
mesh_b = mrmeshpy.loadMesh("meshBwithHole.stl")
# Unite meshes
mesh = mrmeshpy.mergeMeshes([mesh_a, mesh_b])
# Find holes
# Connect two holes
params = mrmeshpy.StitchHolesParams()
params.metric = mrmeshpy.getUniversalMetric(mesh)
mrmeshpy.buildCylinderBetweenTwoHoles(mesh, edges[0], edges[1], params)
# Save result
mrmeshpy.saveMesh(mesh, "stitchedMesh.stl")

Example of extrude faces on mesh

import meshlib.mrmeshpy as mrmeshpy
# Load mesh
mesh = mrmeshpy.loadMesh("mesh.stl")
# Prepare region to extrude
faces_to_extrude = mrmeshpy.FaceBitSet()
faces_to_extrude.resize(3, False)
faces_to_extrude.set(mrmeshpy.FaceId(1), True)
faces_to_extrude.set(mrmeshpy.FaceId(2), True)
# Create duplicated verts on region boundary
mrmeshpy.makeDegenerateBandAroundRegion(mesh, faces_to_extrude)
# Find vertices that will be moved
verts_for_move = mrmeshpy.getIncidentVerts(mesh.topology, faces_to_extrude)
# Move each vertex
for v in range(verts_for_move.size()):
if verts_for_move.test(mrmeshpy.VertId(v)):
mesh.points.vec[v] += mrmeshpy.Vector3f(0.0, 0.0, 1.0)
# Invalidate internal caches after manual changing
mesh.invalidateCaches()
# Save mesh
mrmeshpy.saveMesh(mesh, "extrudedMesh.stl")
Vector3< float > Vector3f
Definition MRMeshFwd.h:120
Id< VertTag > VertId
Definition MRMeshFwd.h:69
See also
Python overview