AMCAX Kernel
Geometry kernel for CAD/CAE/CAM
九韶内核 1.0.0.0
载入中...
搜索中...
未找到
直升机模型修复与完善示例

概述

本教程基于直升机模型存在的诸多问题提供了一系列修复方法。通过调用 GeomE 的 API 对模型进行几何编辑操作以实现对模型的修复。

命名空间

为了示例代码清晰,使用命名空间。

using namespace std;
using namespace AMCAX;
using namespace GeomE;
AMCAX GeomE 模块提供的所有接口所在的命名空间。
定义 misc.docu:21
AMCAX 内核提供的所有接口所在的命名空间。
定义 misc.docu:8
STL namespace

直升机模型中存在的问题

当前直升机模型存在碎面、缝隙孔洞等问题。如下图所示:

碎面

在机翼下方存在两个碎面(高亮位置)。

缝隙孔洞

在机头和机尾存在一些缝隙孔洞(红色位置)。

修复方法

辅助函数

为使示例代码结构更清晰、逻辑更聚焦,我们对以下辅助功能进行了封装。

// Read
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);
});
// Check the file extension. If it's .stp or .step, call StepDataTool; if it's .brep, call OCCTTool
if (suffix == "stp" || suffix == "step") {
return STEP::StepDataTool::Read(s, mfile);
}
else if (suffix == "brep") {
return OCCTIO::OCCTTool::Read(s, mfile);
}
else {
std::cout << "Unsupported file format!" << endl;
return false;
}
}
// Write
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);
});
// Check the file extension. If it's .stp or .step, call StepDataTool; if it's .brep, call OCCTTool
if (suffix == "stp" || suffix == "step")
return STEP::StepDataTool::Write(s, outfile);
else if (suffix == "brep")
return OCCTIO::OCCTTool::Write(s, outfile);
else {
cout << "Unsupported output file format: " << suffix << endl;
return false;
}
}
static AMCAX_API bool Read(TopoShape &s, std::istream &is)
读取输入流中的形状
static AMCAX_API bool Write(const TopoShape &s, std::ostream &os, int format=3)
将形状写入输出流
static AMCAX_API bool Write(const AMCAX::TopoShape &s, std::ostream &os)
Write a TopoShape to a stream in STEP format.
static AMCAX_API bool Read(AMCAX::TopoShape &s, std::istream &is)
Read a TopoShape from a stream in STEP format.
形状的基类,包含具有位置和方向信息的基础形状
定义 TopoShape.hpp:15

碎面融合

在机身的左侧和右侧均存在碎面,将通过以下代码将碎面进行融合。

TopoShape combineFaces(TopoShape& shape)
{
IndexSet<TopoShape> edges, faces;
TopoExplorerTool::MapShapes(shape, ShapeType::Face, faces);
TopoExplorerTool::MapShapes(shape, ShapeType::Edge, edges);
FaceEditor faceEditor;
// Right side of the fuselage
{
// Delete two faces
faceEditor.DeleteFace(shape, TopoCast::Face(faces[17 - 1]));
faceEditor.DeleteFace(shape, TopoCast::Face(faces[19 - 1]));
// Construct a new face based on the given set of edges and an existing face
// Top
IndexSet<TopoShape> set;// All edges to be fused
set.insert(edges[110 - 1]);
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]));
// Bottom
IndexSet<TopoShape> setdown;// All edges to be fused
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]));
}
// Left side of the fuselage
{
// Delete three faces
faceEditor.DeleteFace(shape, TopoCast::Face(faces[52 - 1]));
faceEditor.DeleteFace(shape, TopoCast::Face(faces[26 - 1]));
faceEditor.DeleteFace(shape, TopoCast::Face(faces[24 - 1]));
// Construct a new face based on the given set of edges and an existing face
// Top
IndexSet<TopoShape> set;// All edges to be fused
set.insert(edges[233 - 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]));
// Bottom
IndexSet<TopoShape> setdown;// All edges to be fused
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]));
}
// Unify faces and edges of the shape
writetopo(result, "combineFaces.brep");
return result;
}
索引集的模板类
定义 IndexSet.hpp:20
int insert(T &&key)
插入一个新键
定义 IndexSet.hpp:102
static AMCAX_API TopoShape UpgradeUnifySameDomain(const TopoShape &shape, bool isUnifyEdges=true, bool isUnifyFaces=true, bool isConcatBsplines=false)
将使用同一几何的面或边进行合并。需要注意的是,这里的同一几何是指同一指针。
static AMCAX_API const TopoFace & Face(const TopoShape &s)
将形状转换为面
static AMCAX_API void MapShapes(const TopoShape &s, ShapeType t, IndexSet< TopoShape > &shapeSet)
构造给定类型的子形状集合

结果如下图所示,碎面已融合。

在上述代码中,出现的 faces[17 - 1] 等索引信息是通过 FreeCAD 软件查看到的,它是拓扑对象的索引序号。查看方法如下:

  1. 安装 FreeCAD 软件
  2. 导入模型文件
  3. 点击拓扑对象
  4. 查看软件左侧便能看到序号,如下图所示

注意:模型修改后,原有对象索引会发生变化。如需再次获取目标对象索引,应基于最新修改后的模型重新查询,不可沿用初始模型的索引值。

缝隙缝合

在融合碎面后,继续对融合后的模型进行缝隙缝合。

void sewEdges(TopoShape& shape, const string& filePath, int e1, int e2, double tolerance)
{
cout << "Applying auto fix (tolerance = " << tolerance << ") to file: " << filePath << endl;
try {
// Find all free edges
TopoExplorerTool::MapShapes(shape, ShapeType::Edge, edges);
// Sew two edges
TopoEdge edge1 = TopoCast::Edge(edges[e1]);
TopoEdge edge2 = TopoCast::Edge(edges[e2]);
EdgeEditor editor;
editor.SewEdges(shape, edge1, edge2, tolerance);
}
catch (const exception& e) {
cout << "Auto fix failed for file " << filePath << ". Exception: " << e.what() << endl;
return;
}
// Construct the output filename by appending "_autosew" suffix to the original filename
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();
// Write
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 IndexSet< AMCAX::TopoShape > DetectFreeEdges(const AMCAX::TopoShape &shape)
查找形状中的所有自由边
static AMCAX_API const TopoEdge & Edge(const TopoShape &s)
将形状转换为边
边的类
定义 TopoEdge.hpp:12

结果如下图所示,由两条边形成的缝隙已被修复。

填补孔洞

通过构造新的延长面以填补机头窗户处细长三角形空洞。

TopoShape fillhole_with_newface(TopoShape& shape)
{
IndexSet<TopoShape> edges, faces;
TopoExplorerTool::MapShapes(shape, ShapeType::Edge, edges);
TopoExplorerTool::MapShapes(shape, ShapeType::Face, faces);
set.insert(edges[14 - 1]);
set.insert(edges[16 - 1]);
set.insert(edges[88 - 1]);
FaceEditor faceEditor;
// Construct a new face extending from Face 2, using specified edge set. The new face shares the same surface geometry as Face 2.
faceEditor.BuildFaceFromSurface(shape, set, TopoCast::Face(faces[3 - 1]));
// Unify faces and edges of the shape
// Write
writetopo(result, "fillhole_with_newface.step");
return result;
}

结果如下图所示,细长三角形空洞已消除。

点击这里example04可获得直升机模型修复与完善示例的完整源码,大家根据学习需要自行下载。

附录

#include <iostream>
#include <vector>
#include <string>
#include <filesystem>
using namespace std;
using namespace AMCAX;
using namespace GeomE;
// Read
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);
});
// Check the file extension. If it's .stp or .step, call StepDataTool; if it's .brep, call OCCTTool
if (suffix == "stp" || suffix == "step") {
return STEP::StepDataTool::Read(s, mfile);
}
else if (suffix == "brep") {
return OCCTIO::OCCTTool::Read(s, mfile);
}
else {
std::cout << "Unsupported file format!" << endl;
return false;
}
}
// Write
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);
});
// Check the file extension. If it's .stp or .step, call StepDataTool; if it's .brep, call OCCTTool
if (suffix == "stp" || suffix == "step")
return STEP::StepDataTool::Write(s, outfile);
else if (suffix == "brep")
return OCCTIO::OCCTTool::Write(s, outfile);
else {
cout << "Unsupported output file format: " << suffix << endl;
return false;
}
}
TopoShape combineFaces(TopoShape& shape)
{
IndexSet<TopoShape> edges, faces;
TopoExplorerTool::MapShapes(shape, ShapeType::Face, faces);
TopoExplorerTool::MapShapes(shape, ShapeType::Edge, edges);
FaceEditor faceEditor;
// Right side of the fuselage
{
// Delete two faces
faceEditor.DeleteFace(shape, TopoCast::Face(faces[17 - 1]));
faceEditor.DeleteFace(shape, TopoCast::Face(faces[19 - 1]));
// Construct a new face based on the given set of edges and an existing face
// Top
IndexSet<TopoShape> set;// All edges to be fused
set.insert(edges[110 - 1]);
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]));
// Bottom
IndexSet<TopoShape> setdown;// All edges to be fused
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]));
}
// Left side of the fuselage
{
// Delete three faces
faceEditor.DeleteFace(shape, TopoCast::Face(faces[52 - 1]));
faceEditor.DeleteFace(shape, TopoCast::Face(faces[26 - 1]));
faceEditor.DeleteFace(shape, TopoCast::Face(faces[24 - 1]));
// Construct a new face based on the given set of edges and an existing face
// Top
IndexSet<TopoShape> set;// All edges to be fused
set.insert(edges[233 - 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]));
// Bottom
IndexSet<TopoShape> setdown;// All edges to be fused
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]));
}
// Unify faces and edges of the shape
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 {
// Find all free edges
TopoExplorerTool::MapShapes(shape, ShapeType::Edge, edges);
// Sew two edges
TopoEdge edge1 = TopoCast::Edge(edges[e1]);
TopoEdge edge2 = TopoCast::Edge(edges[e2]);
EdgeEditor editor;
editor.SewEdges(shape, edge1, edge2, tolerance);
}
catch (const exception& e) {
cout << "Auto fix failed for file " << filePath << ". Exception: " << e.what() << endl;
return;
}
// Construct the output filename by appending "_autosew" suffix to the original filename
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();
// Write
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;
}
TopoShape fillhole_with_newface(TopoShape& shape)
{
IndexSet<TopoShape> edges, faces;
TopoExplorerTool::MapShapes(shape, ShapeType::Edge, edges);
TopoExplorerTool::MapShapes(shape, ShapeType::Face, faces);
set.insert(edges[14 - 1]);
set.insert(edges[16 - 1]);
set.insert(edges[88 - 1]);
FaceEditor faceEditor;
// Construct a new face extending from Face 2, using specified edge set. The new face shares the same surface geometry as Face 2.
faceEditor.BuildFaceFromSurface(shape, set, TopoCast::Face(faces[3 - 1]));
// Unify faces and edges of the shape
// Write
writetopo(result, "fillhole_with_newface.step");
return result;
}
int main(int argc, char* argv[])
{
try {
std::cout << "start work" << endl;
TopoShape shape;
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;
}
从实体形状中移除特征面的类
检测工具类
编辑边的类
编辑面的类
创建形状的工具类
创建实体的类
用于读写 OCCT BRep 文件中形状的类
用于修复形状的实用工具类
Utility class for operations on STEP shape data structures.
类型转换的工具类
拓扑遍历的工具类
壳体类
实体类
B-Rep 结构的工具类
constexpr double e
自然对数的底数(也称为纳皮尔常数/欧拉数)
定义 Constants.hpp:18