AMCAX Kernel 1.0.0.0
AMCAXMeshing Algorithm Samples

1. Overview

This tutorial provides the basic usage of using AMCAXMeshing for triangle mesh algorithm.

1.1 Trianlge Soup

AMCAXMeshing uses triangle soup to store mesh data. Triangle soup mainly contains the coordinates of mesh points and the index information of faces, and is a template parameter that mesh algorithms rely on.

class TriSoupTraits_Coord
{
public:
using PointT = VecProxy<3, double>;
using VecT = VecProxy<3, double>;
using NormalT = VecProxy<3, double>;
using Tex3D = VecProxy<3, double>;
using Color = VecProxy<3, float>;
using Triangle = VecProxy<3, int>;
using Points = std::vector<PointT>;
using Normals = std::vector<NormalT>;
using Tex3Ds = std::vector<Tex3D>;
using Colors = std::vector<Color>;
using Triangles = std::vector<Triangle>;
};

1.2 Using namespace

For clarity in the example code, using namespace

using namespace AMCAX;
using namespace Meshing;
Namespace of all interface in the AMCAX kernel.
Definition: misc.docu:8

1.3 Mesh IO

AMCAXMeshing currently provides reading and writing of commonly used mesh file formats such as obj and stl.

mesh IO header file

Obj file reader.
Obj file writer.

write "example_utils.h" to set input and output paths

#pragma once
#include <filesystem>
#include <fstream>
#include <stack>
#include <string>
#include <vector>
inline bool make_file_writable(const std::string &filename)
{
std::filesystem::path filepath(filename);
if (std::filesystem::exists(filepath) && std::filesystem::is_regular_file(filepath))
return true;
std::filesystem::path parent_path = filepath.parent_path();
if (std::filesystem::exists(parent_path) && std::filesystem::is_directory(parent_path))
return true;
std::filesystem::create_directories(parent_path);
return false;
}
inline bool make_dir_writable(const std::string &dirname)
{
std::filesystem::path dirpath(dirname);
if (std::filesystem::exists(dirpath) && std::filesystem::is_directory(dirpath))
return true;
std::filesystem::create_directories(dirpath);
return false;
}
#define OUTPUT_DIRECTORY(TEST_SUIT_NAME, TEST_NAME) \
std::string outdir = \
"./data/test_output/" #TEST_SUIT_NAME "/" #TEST_NAME "/"; \
make_dir_writable(outdir)

2. Algorithm Examples

2.1 Mesh Simplify

#include "example_utils.h"
#include <cfloat>
#include <thread>
using namespace AMCAX::Meshing;
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::Remeshing;
void Remeshing_FastQEM()
{
OUTPUT_DIRECTORY(Remeshing, FastQEM);
bool first = true;
int stage, current, total;
FastQEM<TriSoupTraits_Coord>::ProFn pro_fn =
[&first, &stage, &current, &total](int _stage, int _current, int _total,
AMCAXMeshing_UNUSED bool &cancel) -> void
{
stage = _stage;
current = _current;
total = _total;
if (!first)
std::cout << "\r\033[K";
std::cout << "stage: " << _stage << "/"
<< FastQEM<TriSoupTraits_Coord>::Total << ", process: " << current
<< "/" << total;
first = false;
// set cancel to true if you want to terminate the algorithm :)
if (stage == FastQEM<TriSoupTraits_Coord>::Stage::Total)
std::cout << std::endl;
};
IOOptions io_options;
io_options.vertex_has_point = true;
OBJReader<TriSoupTraits_Coord> obj_reader;
OBJWriter<TriSoupTraits_Coord> obj_writer;
std::string filename = "32770_sf";
obj_reader.read("./data/" + filename + ".obj", io_options);
for (double ratio : {0.01, 0.1, 0.3, 0.6})
{
first = true;
FastQEM<TriSoupTraits_Coord> fast_qem(obj_reader.m_points,
obj_reader.m_triangles);
Logger::elapse_reset();
std::thread simplify_thread(
[&]()
{
fast_qem.simplify(size_t(obj_reader.m_triangles.size() * ratio),
0.1f, 20, pro_fn);
});
simplify_thread.join();
auto elapsed = Logger::elapsed();
Logger::info("FastQEM: ratio " + std::to_string(ratio) + "; time " +
std::to_string(elapsed.count()) + " s");
obj_writer.clear();
obj_writer.m_points = std::move(fast_qem.m_points);
obj_writer.m_triangles = std::move(fast_qem.m_triangles);
std::string out_filename =
outdir + filename + "_FastQEM_" + std::to_string(ratio) + ".obj";
obj_writer.write(out_filename, io_options, 10);
}
}
Fast QEM simplification algorithm.
Namespace of all interface in the AMCAX Meshing module.
Definition: misc.docu:28

Click here example1 to get the complete source code for the above example, which you can download according to your learning needs.

2.2 Mesh Boolean

#include "example_utils.h"
// geometry kernel, hidden for users, just use it. :D
using EIAC = AMCAX::Meshing::Geometry::EIAC;
// Triangle soup traits defined by Common
using TriSoupTraits = AMCAX::Meshing::Mesh::TriSoupTraits_Coord;
// points and triangles from traits
using Points = typename TriSoupTraits::Points;
using Triangles = typename TriSoupTraits::Triangles;
// Define reader and writer
// Define boolean
void read_mesh(const std::string &filename, Points &points,
Triangles &triangles, IOOptions &io_options
)
{
if (AMCAX::Meshing::ends_with(filename, ".obj"))
{
OBJReader reader;
reader.read(filename, io_options);
points = std::move(reader.m_points);
triangles = std::move(reader.m_triangles);
}
else if (AMCAX::Meshing::ends_with(filename, ".stl"))
{
STLReader reader;
reader.read(filename, io_options);
points = std::move(reader.m_points);
triangles = std::move(reader.m_triangles);
}
}
void write_mesh(const std::string &filename, const Points &points,
const Triangles &triangles, IOOptions &io_options)
{
OBJWriter writer;
writer.m_points = std::move(points);
writer.m_triangles = std::move(triangles);
writer.write(filename, io_options, 10);
};
void MeshBoolean_MeshBoolean()
{
OUTPUT_DIRECTORY(Boolean, Boolean);
// Define IO
IOOptions io_options;
io_options.vertex_has_point = true;
Points points1, points2, result_points;
Triangles triangles1, triangles2, result_triangles;
// read mesh
read_mesh("./data/boolean_data/bunny25k.obj", points1, triangles1, io_options);
read_mesh("./data/boolean_data/cow.obj", points2, triangles2, io_options);
MeshBoolean boolean(true);
// set input meshes
boolean.addTriMeshAsInput(points1, triangles1);
boolean.addTriMeshAsInput(points2, triangles2);
// set output mesh
boolean.setTriMeshAsOutput(result_points, result_triangles);
// run arrangements and labeling.
// the results will be temporarily cached, until be cleared or destroyed.
boolean.computeLabels();
// after arrangements and labeling done, we can
// apply differenct boolean operations faster.
// (NOTE: must call computeLabels before boolean operation)
{
boolean.Union();
make_file_writable(outdir + "AMCAXMeshing_Union.obj");
write_mesh(outdir + "AMCAXMeshing_Union.obj", result_points,
result_triangles, io_options);
}
{
boolean.Intersection();
make_file_writable(outdir + "AMCAXMeshing_Intersection.obj");
write_mesh(outdir + "AMCAXMeshing_Intersection.obj", result_points,
result_triangles, io_options);
}
{
boolean.Xor();
make_file_writable(outdir + "AMCAXMeshing_Xor.obj");
write_mesh(outdir + "AMCAXMeshing_Xor.obj", result_points, result_triangles,
io_options);
}
{
boolean.Subtraction();
make_file_writable(outdir + "AMCAXMeshing_Subtraction.obj");
write_mesh(outdir + "AMCAXMeshing_Subtraction.obj", result_points,
result_triangles, io_options);
}
}
Mesh boolean algorithm.
reader triangle soup from STL file.
This file implement writer for STL file.
Repair triangle mesh to clean mesh.
Definition: MeshBoolean.hpp:48
Options about pre-described properties in mesh IO.
Definition: IOOptions.hpp:19
Read triangle soup from an OBJ file.
Definition: OBJReader.hpp:35
Write triangle soup to an OBJ file.
Definition: OBJWriter.hpp:35
Read triangle soup from an STL file.
Definition: STLReader.hpp:33
Write mesh to an STL file.
Definition: STLWriter.hpp:33

Click here example2 to get the complete source code for the above example, which you can download according to your learning needs.

2.3 Mesh Repair

#include "example_utils.h"
using namespace AMCAX::Meshing;
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::MeshTools;
void MeshRepair_MeshRepair()
{
OUTPUT_DIRECTORY(MeshRepair, MeshRepair);
bool first = true;
int stage;
[&first, &stage](int _stage, AMCAXMeshing_UNUSED bool &cancel) -> void
{
stage = _stage;
if (!first)
std::cout << "\r\033[K";
std::cout
<< "stage: " << _stage << "/"
first = false;
// set cancel to true if you want to terminate the algorithm :)
TriSoupTraits_Coord>::Stage::Total)
std::cout << std::endl;
};
IOOptions io_options;
io_options.vertex_has_point = true;
io_options.face_has_normal = true;
OBJReader<TriSoupTraits_Coord> obj_reader;
STLReader<TriSoupTraits_Coord> stl_reader;
OBJWriter<TriSoupTraits_Coord> obj_writer;
STLWriter<TriSoupTraits_Coord> stl_writer;
std::string modelname = "CamelBox";
TriSoupTraits_Coord::Points result_points;
TriSoupTraits_Coord::Triangles result_triangles;
stl_reader.read("./data/repair_data/" + modelname + ".stl", io_options);
TriMeshRepair<TriSoupTraits_Coord> repair(stl_reader.m_points,
stl_reader.m_triangles);
repair.repair(true, pro_fn);
stl_writer.m_points = repair.m_points;
stl_writer.m_triangles = repair.m_triangles;
make_file_writable(outdir + modelname + "_repaired.stl");
io_options.stl_binary = true;
stl_writer.write(outdir + modelname + "_repaired.stl", io_options, 10);
obj_writer.m_points = repair.m_points;
obj_writer.m_triangles = repair.m_triangles;
make_file_writable(outdir + modelname + "_repaired.obj");
obj_writer.write(outdir + modelname + "_repaired.obj", io_options, 10);
}
Interfaces of mesh arrangements.
Definition: TriMeshRepair.hpp:36
std::function< void(int stage, bool &cancel)> ProFn
function that reports process
Definition: TriMeshRepair.hpp:95

Click here example3 to get the complete source code for the above example, which you can download according to your learning needs.

2.4 Mesh Subdivision

2.4.1 ButterFly Subdivision

#include "example_utils.h"
#include <cfloat>
using namespace AMCAX::Meshing;
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::Subdivision;
using namespace AMCAX::Meshing::Remeshing;
void Subdivision_TriSoup_ButterFly()
{
OUTPUT_DIRECTORY(Subdivision, TriSoup_ButterFly);
IOOptions io_options;
io_options.vertex_has_point = true;
OBJReader<TriSoupTraits_Coord> obj_reader;
OBJWriter<TriSoupTraits_Coord> obj_writer;
std::string filename = "kelan";
obj_reader.read("./data/subdivision_data/" + filename + ".obj", io_options);
for (double ratio : {2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.})
{
TriSoup_ButterFlySubdivision<TriSoupTraits_Coord, true> subdivider(
obj_reader.m_points, obj_reader.m_triangles);
Logger::elapse_reset();
size_t sub_times = size_t(std::ceil(ratio / 4.0) + 0.1);
for (size_t i = 0; i < sub_times; i++)
subdivider.subdivide();
FastQEM<TriSoupTraits_Coord> fast_qem(std::move(subdivider.m_points),
std::move(subdivider.m_triangles));
if (size_t(obj_reader.m_triangles.size() * ratio +
fast_qem.m_triangles.size() * 0.01) <
fast_qem.m_triangles.size())
fast_qem.simplify(size_t(obj_reader.m_triangles.size() * ratio), FLT_MAX,
20);
auto elapsed = Logger::elapsed();
Logger::info("ButterFly: ratio " + std::to_string(ratio) + "; time " +
std::to_string(elapsed.count()) + " s");
obj_writer.clear();
obj_writer.m_points = std::move(fast_qem.m_points);
obj_writer.m_triangles = std::move(fast_qem.m_triangles);
std::string out_filename = outdir + filename +
"_TriSoupModifiedButterFly_" +
std::to_string(ratio) + ".obj";
make_file_writable(out_filename);
obj_writer.write(out_filename, io_options, 10);
}
}
Implement ButterFly subdivision algorithm.

Click here example4 to get the complete source code for the above example, which you can download according to your learning needs.

2.4.2 Loop Subdivision

#include "example_utils.h"
#include <cfloat>
using namespace AMCAX::Meshing;
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::Remeshing;
using namespace AMCAX::Meshing::Subdivision;
void Subdivision_TriSoup_LoopSubdivision()
{
OUTPUT_DIRECTORY(Subdivision, TriSoup_LoopSubdivision);
IOOptions io_options;
io_options.vertex_has_point = true;
OBJReader<TriSoupTraits_Coord> obj_reader;
OBJWriter<TriSoupTraits_Coord> obj_writer;
std::string filename = "kelan";
obj_reader.read("./data/subdivision_data/" + filename + ".obj", io_options);
for (double ratio : {2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.})
{
TriSoup_LoopSubdivision<TriSoupTraits_Coord> subdivider(
obj_reader.m_points, obj_reader.m_triangles);
Logger::elapse_reset();
size_t sub_times = size_t(std::ceil(ratio / 4.0) + 0.1);
for (size_t i = 0; i < sub_times; i++)
subdivider.subdivide();
FastQEM<TriSoupTraits_Coord> fast_qem(std::move(subdivider.m_points),
std::move(subdivider.m_triangles));
if (size_t(obj_reader.m_triangles.size() * ratio +
fast_qem.m_triangles.size() * 0.01) <
fast_qem.m_triangles.size())
fast_qem.simplify(size_t(obj_reader.m_triangles.size() * ratio), FLT_MAX,
12);
auto elapsed = Logger::elapsed();
Logger::info("Loop: ratio " + std::to_string(ratio) + "; time " +
std::to_string(elapsed.count()) + " s");
obj_writer.clear();
obj_writer.m_points = std::move(fast_qem.m_points);
obj_writer.m_triangles = std::move(fast_qem.m_triangles);
std::string out_filename =
outdir + filename + "_TriSoupLoopSub_" + std::to_string(ratio) + ".obj";
make_file_writable(out_filename);
obj_writer.write(out_filename, io_options, 10);
}
}
Implement loop subdivision algorithm.

Click here example5 to get the complete source code for the above example, which you can download according to your learning needs.

2.5 Remeshing

2.5.1 Incremental Remeshing

#include "example_utils.h"
using namespace AMCAX::Meshing;
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::Remeshing;
void Remeshing_Incremental()
{
IOOptions io_options;
io_options.vertex_has_point = true;
OBJReader<TriSoupTraits> obj_reader;
OBJWriter<TriSoupTraits> obj_writer;
TriMesh_IncrementalRemeshing<TriSoupTraits> inc_remesher;
std::string filename = "32770_sf";
obj_reader.read("./data/" + filename + ".obj", io_options);
obj_writer.clear();
inc_remesher.setReferenceMesh(obj_reader.m_points, obj_reader.m_triangles);
inc_remesher.setVariableMesh(obj_writer.m_points, obj_writer.m_triangles);
TriMesh_IncrementalRemeshing<TriSoupTraits>::Params param;
param.targetEdgeLength = 0.005;
param.isAdaptive = false;
inc_remesher.remesh(param);
std::string out_filename = filename + "_Incremental_.obj";
obj_writer.write(out_filename, io_options, 10);
}
Interfaces of triMesh incremental remeshing.

Click here example6 to get the complete source code for the above example, which you can download according to your learning needs.

2.6 Mesh parameterisation algorithm

// mesh IO header
// mesh parameterization header
#include "meshModeling/MeshParameterization.hpp"
// some utils functions
#include "example_utils.h"
using namespace AMCAX::Meshing;
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::Parameterization;
void Parameterization_Bijective()
{
// make directory for output file path
OUTPUT_DIRECTORY(Parameterization, Bijective);
IOOptions io_options;
io_options.vertex_has_point = true;
OBJReader<TriSoupTraits> obj_reader;
OBJWriter<TriSoupTraits> obj_writer;
// read mesh
obj_reader.read("./data/parameterization_data/cow.obj", io_options);
// define parameterization object
obj_reader.m_points, obj_reader.m_triangles, obj_writer.m_points);
// execute algorithm
para.parameterization();
// write mesh
obj_writer.m_triangles = std::move(obj_reader.m_triangles);
std::string out_filename = outdir + "res_parameterization.obj";
obj_writer.write(out_filename, io_options, 10);
}
Definition: MeshParameterization.hpp:23

2.7 Mesh Cutting Algorithm

#include "meshModeling/MeshParameterization.hpp"
#include "example_utils.h"
using namespace AMCAX::Meshing;
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::Parameterization;
// Define points and triangles from traits
using Points = typename TriSoupTraits::Points;
using Triangles = typename TriSoupTraits::Triangles;
// Function for parameterization with mesh cut
void Parameterization_With_MeshCut()
{
OUTPUT_DIRECTORY(Parameterization, Bijective_With_MeshCut);
IOOptions io_options;
io_options.vertex_has_point = true;
OBJReader<TriSoupTraits> obj_reader;
OBJWriter<TriSoupTraits> obj_writer;
std::string in_filename = "./data/boolean_data/bunny25k.obj";
std::string out_cut_filename = outdir + "bunny_cut.obj";
std::string out_para_filename = outdir + "bunny_parameterization.obj";
// Read the object file
obj_reader.read(in_filename, io_options);
Points i_points = std::move(obj_reader.m_points);
Triangles i_triangles = std::move(obj_reader.m_triangles);
// Perform mesh cut
i_points, i_triangles, /*verbose*/ true);
Points cut_points;
Triangles cut_triangles;
mesh_cut.cut(cut_points, cut_triangles);
// Write the cut mesh to a new file
obj_writer.m_points = cut_points;
obj_writer.m_triangles = cut_triangles;
obj_writer.write(out_cut_filename, io_options, 10);
obj_writer.clear();
Points para_points;
// Perform bijective parameterization on the cut mesh
cut_points, cut_triangles, para_points, /*verbose*/ true);
para.parameterization();
// Write the parameterized cut mesh to a new file
obj_writer.m_points = std::move(para_points);
obj_writer.m_triangles = std::move(cut_triangles);
obj_writer.write(out_para_filename, io_options, 10);
}
Greedy cut algorithm for triangle mesh.
Definition: TriMeshCut.hpp:24