概述
本教程基于直升机模型存在的诸多问题提供了一系列修复方法。通过调用 GeomE 的 API 对模型进行几何编辑操作以实现对模型的修复。
命名空间
为了示例代码清晰,使用命名空间。
AMCAX GeomE 模块提供的所有接口所在的命名空间。
定义 misc.docu:21
AMCAX 内核提供的所有接口所在的命名空间。
定义 misc.docu:8
直升机模型中存在的问题
当前直升机模型存在碎面、缝隙孔洞等问题。如下图所示:
碎面
在机翼下方存在两个碎面(高亮位置)。
缝隙孔洞
在机头和机尾存在一些缝隙孔洞(红色位置)。
修复方法
辅助函数
为使示例代码结构更清晰、逻辑更聚焦,我们对以下辅助功能进行了封装。
bool readtopo(
TopoShape& s,
const string& mfile)
{
if (!std::filesystem::exists(mfile)) {
cout << "File not found!" << endl;
return false;
}
string suffix = mfile.substr(mfile.find_last_of('.') + 1);
std::transform(suffix.begin(), suffix.end(), suffix.begin(), [](unsigned char c) {
return std::tolower(c);
});
if (suffix == "stp" || suffix == "step") {
}
else if (suffix == "brep") {
}
else {
std::cout << "Unsupported file format!" << endl;
return false;
}
}
bool writetopo(
const TopoShape& s,
const string& outfile)
{
string suffix = outfile.substr(outfile.find_last_of('.') + 1);
transform(suffix.begin(), suffix.end(), suffix.begin(), [](unsigned char c) {
return tolower(c);
});
if (suffix == "stp" || suffix == "step")
else if (suffix == "brep")
else {
cout << "Unsupported output file format: " << suffix << endl;
return false;
}
}
形状的基类,包含具有位置和方向信息的基础形状
定义 TopoShape.hpp:15
碎面融合
在机身的左侧和右侧均存在碎面,将通过以下代码将碎面进行融合。
{
FaceEditor faceEditor;
{
set.insert(edges[111 - 1]);
set.insert(edges[109 - 1]);
set.insert(edges[28 - 1]);
set.insert(edges[27 - 1]);
faceEditor.BuildFaceFromSurface(shape, set,
TopoCast::Face(faces[18 - 1]));
setdown.
insert(edges[114 - 1]);
setdown.
insert(edges[115 - 1]);
setdown.
insert(edges[116 - 1]);
setdown.
insert(edges[35 - 1]);
faceEditor.BuildFaceFromSurface(shape, setdown,
TopoCast::Face(faces[18 - 1]));
}
{
set.insert(edges[138 - 1]);
set.insert(edges[61 - 1]);
set.insert(edges[60 - 1]);
set.insert(edges[135 - 1]);
set.insert(edges[190 - 1]);
set.insert(edges[137 - 1]);
faceEditor.BuildFaceFromSurface(shape, set,
TopoCast::Face(faces[25 - 1]));
setdown.
insert(edges[130 - 1]);
setdown.
insert(edges[131 - 1]);
setdown.
insert(edges[132 - 1]);
setdown.
insert(edges[53 - 1]);
faceEditor.BuildFaceFromSurface(shape, setdown,
TopoCast::Face(faces[25 - 1]));
}
writetopo(result, "combineFaces.brep");
return result;
}
索引集的模板类
定义 IndexSet.hpp:20
int insert(T &&key)
插入一个新键
定义 IndexSet.hpp:102
static AMCAX_API const TopoFace & Face(const TopoShape &s)
将形状转换为面
结果如下图所示,碎面已融合。
在上述代码中,出现的 faces[17 - 1] 等索引信息是通过 FreeCAD 软件查看到的,它是拓扑对象的索引序号。查看方法如下:
- 安装 FreeCAD 软件
- 导入模型文件
- 点击拓扑对象
- 查看软件左侧便能看到序号,如下图所示
注意:模型修改后,原有对象索引会发生变化。如需再次获取目标对象索引,应基于最新修改后的模型重新查询,不可沿用初始模型的索引值。
缝隙缝合
在融合碎面后,继续对融合后的模型进行缝隙缝合。
void sewEdges(
TopoShape& shape,
const string& filePath,
int e1,
int e2,
double tolerance)
{
cout << "Applying auto fix (tolerance = " << tolerance << ") to file: " << filePath << endl;
try {
EdgeEditor editor;
editor.SewEdges(shape, edge1, edge2, tolerance);
}
catch (const exception& e) {
cout << "Auto fix failed for file " << filePath << ". Exception: " << e.what() << endl;
return;
}
filesystem::path p(filePath);
string stem = p.stem().string();
string extension = p.extension().string();
extension = ".step";
string outFile = (p.parent_path() / (stem + "_autosew" + extension)).string();
if (writetopo(shape, outFile))
cout << "Auto fix succeeded, output saved to: " << outFile << endl;
else
cout << "Failed to write output file: " << outFile << endl;
cout << "success auto fix" << endl;
}
sewEdges(shape, "./combineFaces.step", 187 - 1, 247 - 1, 0.007);
static AMCAX_API const TopoEdge & Edge(const TopoShape &s)
将形状转换为边
结果如下图所示,由两条边形成的缝隙已被修复。
填补孔洞
通过构造新的延长面以填补机头窗户处细长三角形空洞。
{
set.insert(edges[16 - 1]);
set.insert(edges[88 - 1]);
FaceEditor faceEditor;
faceEditor.BuildFaceFromSurface(shape, set,
TopoCast::Face(faces[3 - 1]));
writetopo(result, "fillhole_with_newface.step");
return result;
}
结果如下图所示,细长三角形空洞已消除。
点击这里example04可获得直升机模型修复与完善示例的完整源码,大家根据学习需要自行下载。
附录
#include <iostream>
#include <vector>
#include <string>
#include <filesystem>
bool readtopo(
TopoShape& s,
const string& mfile)
{
if (!std::filesystem::exists(mfile)) {
cout << "File not found!" << endl;
return false;
}
string suffix = mfile.substr(mfile.find_last_of('.') + 1);
std::transform(suffix.begin(), suffix.end(), suffix.begin(), [](unsigned char c) {
return std::tolower(c);
});
if (suffix == "stp" || suffix == "step") {
}
else if (suffix == "brep") {
}
else {
std::cout << "Unsupported file format!" << endl;
return false;
}
}
bool writetopo(
const TopoShape& s,
const string& outfile)
{
string suffix = outfile.substr(outfile.find_last_of('.') + 1);
transform(suffix.begin(), suffix.end(), suffix.begin(), [](unsigned char c) {
return tolower(c);
});
if (suffix == "stp" || suffix == "step")
else if (suffix == "brep")
else {
cout << "Unsupported output file format: " << suffix << endl;
return false;
}
}
{
FaceEditor faceEditor;
{
set.insert(edges[111 - 1]);
set.insert(edges[109 - 1]);
set.insert(edges[28 - 1]);
set.insert(edges[27 - 1]);
faceEditor.BuildFaceFromSurface(shape, set,
TopoCast::Face(faces[18 - 1]));
setdown.
insert(edges[114 - 1]);
setdown.
insert(edges[115 - 1]);
setdown.
insert(edges[116 - 1]);
setdown.
insert(edges[35 - 1]);
faceEditor.BuildFaceFromSurface(shape, setdown,
TopoCast::Face(faces[18 - 1]));
}
{
set.insert(edges[138 - 1]);
set.insert(edges[61 - 1]);
set.insert(edges[60 - 1]);
set.insert(edges[135 - 1]);
set.insert(edges[190 - 1]);
set.insert(edges[137 - 1]);
faceEditor.BuildFaceFromSurface(shape, set,
TopoCast::Face(faces[25 - 1]));
setdown.
insert(edges[130 - 1]);
setdown.
insert(edges[131 - 1]);
setdown.
insert(edges[132 - 1]);
setdown.
insert(edges[53 - 1]);
faceEditor.BuildFaceFromSurface(shape, setdown,
TopoCast::Face(faces[25 - 1]));
}
writetopo(result, "combineFaces.brep");
return result;
}
void sewEdges(
TopoShape& shape,
const string& filePath,
int e1,
int e2,
double tolerance)
{
cout << "Applying auto fix (tolerance = " << tolerance << ") to file: " << filePath << endl;
try {
EdgeEditor editor;
editor.SewEdges(shape, edge1, edge2, tolerance);
}
catch (const exception& e) {
cout <<
"Auto fix failed for file " << filePath <<
". Exception: " <<
e.what() << endl;
return;
}
filesystem::path p(filePath);
string stem = p.stem().string();
string extension = p.extension().string();
extension = ".step";
string outFile = (p.parent_path() / (stem + "_autosew" + extension)).string();
if (writetopo(shape, outFile))
cout << "Auto fix succeeded, output saved to: " << outFile << endl;
else
cout << "Failed to write output file: " << outFile << endl;
cout << "success auto fix" << endl;
}
{
set.insert(edges[16 - 1]);
set.insert(edges[88 - 1]);
FaceEditor faceEditor;
faceEditor.BuildFaceFromSurface(shape, set,
TopoCast::Face(faces[3 - 1]));
writetopo(result, "fillhole_with_newface.step");
return result;
}
int main(int argc, char* argv[])
{
try {
std::cout << "start work" << endl;
readtopo(shape, "./data/helicopter-2025.stp");
shape = combineFaces(shape);
sewEdges(shape, "./combineFaces.step", 187 - 1, 247 - 1, 0.007);
shape = fillhole_with_newface(shape);
writetopo(shape, "./outshape.brep");
}
catch (const std::exception& e) {
std::cout <<
"Sorry, there was an exception: " <<
e.what() << endl;
}
catch (...) {
std::cout << "Unknown exception" << endl;
}
system("pause");
return 0;
}
constexpr double e
自然对数的底数(也称为纳皮尔常数/欧拉数)
定义 Constants.hpp:18