AMCAX Kernel
Geometry kernel for CAD/CAE/CAM
九韶内核
载入中...
搜索中...
未找到
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.

2. 算法示例

2.1 网格简化算法

#include <cfloat>
#include <thread>
using namespace AMCAX::Meshing;
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::Remeshing;
void 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;
obj_reader.read("./data/32770_sf.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);
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 = "FastQEM_" + std::to_string(ratio) + ".obj";
obj_writer.write(out_filename, io_options, 10);
}
}
int main()
{
Remeshing_FastQEM();
}
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 网格布尔算法

// 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()
{
// 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/bunny25k.obj", points1, triangles1, io_options);
read_mesh("./data/cow.obj", points2, triangles2, io_options);
MeshBoolean boolean(true);
// set input meshes
boolean.addTriMeshAsInput(points1, triangles1);
boolean.addTriMeshAsInput(points2, triangles2);
// set output mesh
std::vector<std::pair<MeshBoolean::iPoints, MeshBoolean::iTriangles>> result_meshes;
result_meshes.emplace_back(result_points, result_triangles);
boolean.setTriMeshAsOutput(result_meshes);
// 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();
std::cout << "Union results: " << result_meshes.size() << " meshes" << std::endl;
for (size_t i = 0; i < result_meshes.size(); ++i) {
std::string filename = "AMCAXMeshing_Union_" + std::to_string(i) + ".obj";
write_mesh(filename.c_str(), result_meshes[i].first, result_meshes[i].second, io_options);
}
}
{
boolean.Intersection();
std::cout << "Intersection results: " << result_meshes.size() << " meshes" << std::endl;
for (size_t i = 0; i < result_meshes.size(); ++i) {
std::string filename = "AMCAXMeshing_Intersection_" + std::to_string(i) + ".obj";
write_mesh(filename.c_str(), result_meshes[i].first, result_meshes[i].second, io_options);
}
}
{
boolean.Xor();
std::cout << "Xor results: " << result_meshes.size() << " meshes" << std::endl;
for (size_t i = 0; i < result_meshes.size(); ++i) {
std::string filename = "AMCAXMeshing_Xor" + std::to_string(i) + ".obj";
write_mesh(filename.c_str(), result_meshes[i].first, result_meshes[i].second, io_options);
}
}
{
boolean.Subtraction();
std::cout << "Subtraction results: " << result_meshes.size() << " meshes" << std::endl;
for (size_t i = 0; i < result_meshes.size(); ++i) {
std::string filename = "AMCAXMeshing_Subtraction_" + std::to_string(i) + ".obj";
write_mesh(filename.c_str(), result_meshes[i].first, result_meshes[i].second, io_options);
}
}
}
int main() {
MeshBoolean_MeshBoolean();
}
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 网格修复算法

using namespace AMCAX::Meshing;
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::MeshTools;
void 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;
TriSoupTraits_Coord::Points result_points;
TriSoupTraits_Coord::Triangles result_triangles;
stl_reader.read("./data/CamelBox.stl", io_options);
repair.repair(true, pro_fn);
stl_writer.m_points = repair.m_points;
stl_writer.m_triangles = repair.m_triangles;
io_options.stl_binary = true;
stl_writer.write("repaired.stl", io_options, 10);
obj_writer.m_points = repair.m_points;
obj_writer.m_triangles = repair.m_triangles;
obj_writer.write("repaired.obj", io_options, 10);
}
int main() {
MeshRepair_MeshRepair();
}
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细分

using namespace AMCAX::Meshing;
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::Subdivision;
using namespace AMCAX::Meshing::Remeshing;
void Subdivision_TriSoup_ButterFly()
{
IOOptions io_options;
io_options.vertex_has_point = true;
obj_reader.read("./data/kelan.obj", io_options);
for (double ratio : {2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.})
{
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 = "TriSoupModifiedButterFly_" + std::to_string(ratio) + ".obj";
obj_writer.write(out_filename, io_options, 10);
}
}
int main()
{
Subdivision_TriSoup_ButterFly();
}
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细分

using namespace AMCAX::Meshing;
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::Remeshing;
using namespace AMCAX::Meshing::Subdivision;
void Subdivision_TriSoup_LoopSubdivision()
{
IOOptions io_options;
io_options.vertex_has_point = true;
obj_reader.read("./data/kelan.obj", io_options);
for (double ratio : {2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.})
{
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 = "TriSoupLoopSub_" + std::to_string(ratio) + ".obj";
obj_writer.write(out_filename, io_options, 10);
}
}
int main()
{
Subdivision_TriSoup_LoopSubdivision();
}
Implement loop subdivision algorithm.
Loop subdivision for triangle mesh.
定义 TriSoup_LoopSubdivision.hpp:80

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

2.5 重网格化

2.5.1 增量式重网格化

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/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. * AMCAX::Constants::pi;
remesher.setReferenceMesh(input_points, input_triangles);
remesher.setVariableMesh(output_points, output_triangles);
remesher.remesh(params);
std::string outputfilename = 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);
}
int main()
{
incremental_remesh_test();
}
常用数学常数
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
constexpr double pi
数学常数 Pi,圆的周长与直径之比
定义 Constants.hpp:42
the parameters that control the remeshing
定义 TriMesh_IncrementalRemeshing.hpp:40

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

2.6 网格参数化算法

// mesh IO header
// mesh parameterization header
using namespace AMCAX::Meshing;
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::Parameterization;
void Parameterization_Bijective()
{
IOOptions io_options;
io_options.vertex_has_point = true;
// read mesh
obj_reader.read("./data/cow.obj", io_options);
// define parameterization object
// execute algorithm
para.parameterization();
// write mesh
obj_writer.m_triangles = std::move(obj_reader.m_triangles);
std::string out_filename = "res_parameterization.obj";
obj_writer.write(out_filename, io_options, 10);
}
int main()
{
Parameterization_Bijective();
}
Bijective parameterization algorithm.
定义 MeshParameterization.hpp:23

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

2.7 网格切割算法

using namespace AMCAX::Meshing;
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::Parameterization;
// Define points and triangles from traits
using Points = typename TriSoupTraits_Coord::Points;
using Triangles = typename TriSoupTraits_Coord::Triangles;
// Function for parameterization with mesh cut
void Parameterization_With_MeshCut()
{
IOOptions io_options;
io_options.vertex_has_point = true;
std::string in_filename = "./data/bunny25k.obj";
std::string out_cut_filename = "bunny_cut.obj";
std::string out_para_filename ="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
AMCAX::Meshing::TriMeshCut::GreedyCut<TriSoupTraits_Coord> 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
AMCAX::Meshing::Parameterization::BijectivePara<TriSoupTraits_Coord> para(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);
}
int main()
{
Parameterization_With_MeshCut();
}
Greedy cut algorithm for triangle mesh.

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