AMCAX Kernel 1.0.0.0
Loading...
Searching...
No Matches
Helicopter Model Repair and Enhancement Example

Overview

This tutorial provides a series of repair methods for addressing geometric issues in a helicopter model. By utilizing GeomE's API, we perform geometric editing operations to fix the model.

Namespaces

For code clarity, we use the following namespaces:

using namespace std;
using namespace AMCAX;
using namespace GeomE;
Namespace of all interface in the AMCAX GeomE module.
Definition misc.docu:21
Namespace of all interface in the AMCAX kernel.
Definition misc.docu:8
STL namespace.

helicopter model Issues

The current helicopter model exhibits several problems including fragmented surfaces and gap holes as shown below:

Fragmented faces

Two fragmented faces exist beneath the wings (highlighted areas).

Gap Holes

Several gap holes appear at the nose and tail sections (marked in red).

Repair Methods

Helper Functions

To maintain clean code structure and focus on core logic, we've encapsulated common operations:

// 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 STEPTool; if it's .brep, call OCCTTool
if (suffix == "stp" || suffix == "step") {
return STEP::STEPTool::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 STEPTool; if it's .brep, call OCCTTool
if (suffix == "stp" || suffix == "step")
return STEP::STEPTool::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)
Read a shape from a stream.
static AMCAX_API bool Write(const TopoShape &s, std::ostream &os, int format=3)
Write a shape to a stream.
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.
Base class of shape, containing an underlying shape with a location and an orientation.
Definition TopoShape.hpp:15

Face Merging

The fuselage contains fragmented faces on both left and right sides, which we merge using:

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;
}
Template class of indexed set.
Definition IndexSet.hpp:20
int insert(T &&key)
Insert a new key.
Definition IndexSet.hpp:117
static AMCAX_API TopoShape UpgradeUnifySameDomain(const TopoShape &shape, bool isUnifyEdges=true, bool isUnifyFaces=true, bool isConcatBsplines=false)
Try to unify faces and edges of the shape which lie on the same geometry.
static AMCAX_API const TopoFace & Face(const TopoShape &s)
Cast shape to face.
static AMCAX_API void MapShapes(const TopoShape &s, ShapeType t, IndexSet< TopoShape > &shapeSet)
Construct a set of sub-shapes of given type.

Results showing merged faces:

In the above code, index references like faces[17 - 1] are obtained through FreeCAD software, representing topological object indices. The inspection method is as follows:

  1. Install FreeCAD software
  2. Import the model file
  3. Click on topological objects
  4. View the indices in the left panel as shown:

Important Note: After model modification, original object indices will change. To obtain target object indices again, you must re-query based on the latest modified model - never reuse indices from the initial model.

Gap Stitching

After face merging, we stitch the gaps:

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)
Find all free edges.
static AMCAX_API const TopoEdge & Edge(const TopoShape &s)
Cast shape to edge.
Class of edge.
Definition TopoEdge.hpp:12

The result is shown in the following figure, demonstrating that the gap formed by the two edges has been successfully repaired.

Hole Patching

Constructs new extended surfaces to fill slender triangular voids at the cockpit window area.

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;
}

The results shown in the figures below demonstrate successful elimination of the slender triangular void.

Click here example04 to get the full source code of the helicopter model repair and enhancement example. Everyone can download it as needed.

Appendix

#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 STEPTool; if it's .brep, call OCCTTool
if (suffix == "stp" || suffix == "step") {
return STEP::STEPTool::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 STEPTool; if it's .brep, call OCCTTool
if (suffix == "stp" || suffix == "step")
return STEP::STEPTool::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;
}
return 0;
}
The class of removing feature faces from the shape.
Class of Detect Tool.
Class of Edge Editor.
Class of Face Editor.
The class of fillet.
Class of tools for making shapes.
Class of making a solid.
Class of read and write shapes from OCCT BRep files.
Utility class for operations on STEP shape data structures.
Some useful tool for fixing shapes.
Class of casting tool.
Class of tool for explorer.
Class of shell.
Class of solid.
Class of tools for B-Rep structure.
constexpr double e
Base of natural logarithm (also known as Napier's constant / Euler's number)
Definition Constants.hpp:18