AMCAX Kernel
Geometry kernel for CAD/CAE/CAM
九韶内核 1.0.0.0
载入中...
搜索中...
未找到
AMCAXMeshing网格算法示例

1. 概述

本教程提供了使用AMCAXMeshing三角网格算法的基本用法。

1.1 TriangleSoup

AMCAXMeshing使用TriangleSoup对网格数据进行存储。TriSoup 主要包含网格点的坐标与面的索引信息,是网格算法依赖的模板参数。

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 AMCAX;
using namespace Meshing;
AMCAX Meshing 模块提供的所有接口所在的命名空间。
定义 misc.docu:35
AMCAX 内核提供的所有接口所在的命名空间。
定义 misc.docu:8

1.3 网格输入/输出

AMCAXMeshing目前提供了obj, stl常用网格文件格式的读写。

网格读写头文件

Obj file reader.
Obj file writer.

编写"example_utils.h"设置输入与输出路径

#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. 算法示例

2.1 网格简化算法

#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;
[&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;
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;
obj_reader.m_triangles);
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.
static AMCAXMeshing_API void elapse_reset()
reset elapsed time.
AMCAXMeshing_NODISCARD static AMCAXMeshing_API std::chrono::duration< double > elapsed()
return the elapsed time since the last reset.
Options about pre-described properties in mesh IO.
定义 IOOptions.hpp:19
Read triangle soup from an OBJ file.
定义 OBJReader.hpp:38
Points m_points
point position
定义 OBJReader.hpp:125
Triangles m_triangles
triangle faces
定义 OBJReader.hpp:130
AMCAXMeshing_API bool read(const std::string &filename, IOOptions &opt)
Read triangle soup from the file. results will be stored in member variables of reader,...
Write triangle soup to an OBJ file.
定义 OBJWriter.hpp:38
AMCAXMeshing_API void clear()
Clear data stored in writer
Triangles m_triangles
triangle faces
定义 OBJWriter.hpp:131
AMCAXMeshing_API bool write(const std::string &filename, IOOptions &opt, std::streamsize precision)
Write triangle soup to file with given options and precison.
Points m_points
point position
定义 OBJWriter.hpp:126
Implement a fast QEM simplification algorithm based on triangle soup.
定义 TriSoup_FastQEM.hpp:32
std::function< void(int stage, int current, int total, bool &cancel)> ProFn
function that reports process
定义 TriSoup_FastQEM.hpp:87

点击这里example1可获得以上示例完整源码,大家根据学习需要自行下载。

2.2 网格布尔算法

#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.
定义 MeshBoolean.hpp:48
Read triangle soup from an STL file.
定义 STLReader.hpp:33
AMCAXMeshing_API bool read(const std::string &filename, IOOptions &opt)
Read triangle soup from the file.
Points m_points
vertex position
定义 STLReader.hpp:67
Triangles m_triangles
triangles
定义 STLReader.hpp:68
Write mesh to an STL file.
定义 STLWriter.hpp:33

点击这里example2可获得以上示例完整源码,大家根据学习需要自行下载。

2.3 网格修复算法

#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 << "/"
<< AMCAX::Meshing::MeshTools::TriMeshRepair<TriSoupTraits_Coord>::Total;
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;
std::string modelname = "CamelBox";
TriSoupTraits_Coord::Points result_points;
TriSoupTraits_Coord::Triangles result_triangles;
stl_reader.read("./data/repair_data/" + modelname + ".stl", io_options);
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.
Triangles m_triangles
triangles
定义 STLWriter.hpp:90
AMCAXMeshing_API bool write(const std::string &filename, IOOptions &opt, std::streamsize precision=6)
Write mesh to file with given options and precison.
Points m_points
vertex position
定义 STLWriter.hpp:89
定义 TriMeshRepair.hpp:36
std::function< void(int stage, bool &cancel)> ProFn
function that reports process
定义 TriMeshRepair.hpp:95

点击这里example3可获得以上示例完整源码,大家根据学习需要自行下载。

2.4 网格细分算法

2.4.1 ButterFly细分

#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;
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.})
{
obj_reader.m_points, obj_reader.m_triangles);
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.
Modified butterfly subdivision for triangle mesh. Interpolating subdivision for meshes with arbitrary...
定义 TriSoup_ButterFlySubdivision.hpp:35

点击这里example4可获得以上示例完整源码,大家根据学习需要自行下载。

2.4.2 Loop细分

#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;
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.})
{
obj_reader.m_points, obj_reader.m_triangles);
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.
Loop subdivision for triangle mesh.
定义 TriSoup_LoopSubdivision.hpp:78

点击这里example5可获得以上示例完整源码,大家根据学习需要自行下载。

2.5 重网格化

2.5.1 增量式重网格化

#include "example_utils.h"
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::Remeshing;
using TriSoupTraits = AMCAX::Meshing::Mesh::TriSoupTraits_Coord;
void incremental_remesh_test()
{
std::string inputfilename = "./data/remesh_data/filename_1.obj";
IOOptions io_options;
io_options.vertex_has_point = true;
TriSoupTraits::Points input_points, output_points;
TriSoupTraits::Triangles input_triangles, output_triangles;
reader.read(inputfilename, io_options);
input_points = std::move(reader.m_points);
input_triangles = std::move(reader.m_triangles);
output_points = input_points;
output_triangles = input_triangles;
params.featurePreserved = true;
params.isAdaptive = true;
params.iterNum = 10;
params.Max_error = 0.0005;
params.targetEdgeLength = 0.1;
params.tolAngle = 30. / 180. * M_PI;
remesher.setReferenceMesh(input_points, input_triangles);
remesher.setVariableMesh(output_points, output_triangles);
remesher.remesh(params);
std::string outputfilename =
"./data/" + std::to_string(params.iterNum) + ".obj";
writer.m_points = std::move(output_points);
writer.m_triangles = std::move(output_triangles);
writer.write(outputfilename, io_options, 15);
}
Mesh IO options.
Interfaces of triMesh incremental remeshing.
定义 TriMesh_IncrementalRemeshing.hpp:24
AMCAXMeshing_API void setReferenceMesh(const iPoints &points, const iTriangles &triangles)
add a triangle mesh (triangle soup) as one input.
AMCAXMeshing_API void setVariableMesh(iPoints &points, iTriangles &triangles)
Set the triangle mesh (triangle soup) as output destination.
AMCAXMeshing_API void remesh(Params params)
Adaptive/Isotropic remeshing controled by the control parameters
the parameters that control the remeshing
定义 TriMesh_IncrementalRemeshing.hpp:40

点击这里example6可获得以上示例完整源码,大家根据学习需要自行下载。

2.6 网格参数化算法

// mesh IO header
// mesh parameterization header
// some utils functions
#include "example_utils.h"
using namespace AMCAX::Meshing;
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::Parameterization;
using TriSoupTraits = AMCAX::Meshing::Mesh::TriSoupTraits_Coord;
void Parameterization_Bijective()
{
// make directory for output file path
OUTPUT_DIRECTORY(Parameterization, Bijective);
IOOptions io_options;
io_options.vertex_has_point = true;
// 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);
}
Bijective parameterization algorithm.
定义 MeshParameterization.hpp:23

点击这里example7可获得以上示例完整源码,大家根据学习需要自行下载。

2.7 网格切割算法

#include "example_utils.h"
using namespace AMCAX::Meshing;
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::Parameterization;
using TriSoupTraits = AMCAX::Meshing::Mesh::TriSoupTraits_Coord;
// 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;
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.

点击这里example8可获得以上示例完整源码,大家根据学习需要自行下载。