#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { void printFeature(const asiAlgo_Feature& feature) { std::cout << "["; bool first = true; for (TColStd_MapIteratorOfPackedMapOfInteger it(feature); it.More(); it.Next()) { if (!first) std::cout << ", "; first = false; std::cout << it.Key(); } std::cout << "]"; } void printPackedMap(const TColStd_PackedMapOfInteger& values) { std::cout << "["; bool first = true; for (TColStd_MapIteratorOfPackedMapOfInteger it(values); it.More(); it.Next()) { if (!first) std::cout << ", "; first = false; std::cout << it.Key(); } std::cout << "]"; } const char* surfaceTypeName(const GeomAbs_SurfaceType type) { switch (type) { case GeomAbs_Plane: return "plane"; case GeomAbs_Cylinder: return "cylinder"; case GeomAbs_Cone: return "cone"; case GeomAbs_Sphere: return "sphere"; case GeomAbs_Torus: return "torus"; case GeomAbs_BezierSurface: return "bezier"; case GeomAbs_BSplineSurface: return "bspline"; case GeomAbs_SurfaceOfRevolution: return "revolution"; case GeomAbs_SurfaceOfExtrusion: return "extrusion"; case GeomAbs_OffsetSurface: return "offset"; default: return "other"; } } void printStringIntMap(const std::map& values) { std::cout << "{"; bool first = true; for (const auto& item : values) { if (!first) std::cout << ", "; first = false; std::cout << "\"" << item.first << "\": " << item.second; } std::cout << "}"; } void printGeometricRelation( const int faceId, const int neighborId, const std::string& type, const double residual, const char* source, const bool first) { if (!first) std::cout << ",\n"; std::cout << " { \"faceIds\": [" << faceId << ", " << neighborId << "]" << ", \"type\": \"" << type << "\"" << ", \"residual\": " << residual << ", \"source\": \"" << source << "\" }"; } } int main(int argc, char** argv) { if (argc < 2) { std::cerr << "Usage: recognize_holes [max-radius]\n"; return 1; } const TCollection_AsciiString filename(argv[1]); const double radius = argc >= 3 ? std::atof(argv[2]) : Precision::Infinite(); TopoDS_Shape shape; if (!asiAlgo_STEP::Import(filename, shape)) { std::cerr << "Failed to import STEP: " << argv[1] << "\n"; return 2; } TopTools_IndexedMapOfShape faces; TopExp::MapShapes(shape, TopAbs_FACE, faces); BRepCheck_Analyzer analyzer(shape); Handle(asiAlgo_AAG) aag = new asiAlgo_AAG( shape, false, 1.0e-4, asiAlgo_AAG::CachedMap_All); asiAlgo_RecognizeDrillHoles recognizer(aag, true); if (!recognizer.Perform(radius)) { std::cerr << "Hole recognition failed.\n"; return 3; } const asiAlgo_Feature& holeFaceIds = recognizer.GetResultIndices(); std::vector holes; aag->GetConnectedComponents(holeFaceIds, holes); std::map surfaceSummary; std::map angleSummary; std::map geometricRelationSummary; for (int faceId = 1; faceId <= aag->GetNumberOfNodes(); ++faceId) { const TopoDS_Face& face = aag->GetFace(faceId); BRepAdaptor_Surface surface(face); surfaceSummary[surfaceTypeName(surface.GetType())]++; } std::cout << "{\n"; std::cout << " \"validBreP\": " << (analyzer.IsValid() ? "true" : "false") << ",\n"; std::cout << " \"faceCount\": " << faces.Extent() << ",\n"; std::cout << " \"aagNodeCount\": " << aag->GetNumberOfNodes() << ",\n"; std::cout << " \"holeFaceIds\": "; printFeature(holeFaceIds); std::cout << ",\n"; std::cout << " \"holeCount\": " << holes.size() << ",\n"; std::cout << " \"holes\": [\n"; for (std::size_t i = 0; i < holes.size(); ++i) { std::cout << " { \"index\": " << (i + 1) << ", \"faceIds\": "; printFeature(holes[i]); std::cout << " }"; if (i + 1 < holes.size()) std::cout << ","; std::cout << "\n"; } std::cout << " ],\n"; std::cout << " \"faces\": [\n"; for (int faceId = 1; faceId <= aag->GetNumberOfNodes(); ++faceId) { const TopoDS_Face& face = aag->GetFace(faceId); BRepAdaptor_Surface surface(face); std::cout << " { \"id\": " << faceId << ", \"surface\": \"" << surfaceTypeName(surface.GetType()) << "\"" << ", \"neighbors\": "; if (aag->HasNeighbors(faceId)) printFeature(aag->GetNeighbors(faceId)); else std::cout << "[]"; std::cout << " }"; if (faceId < aag->GetNumberOfNodes()) std::cout << ","; std::cout << "\n"; } std::cout << " ],\n"; std::cout << " \"adjacency\": [\n"; bool firstAdjacency = true; std::vector> tangentPairs; for (int faceId = 1; faceId <= aag->GetNumberOfNodes(); ++faceId) { if (!aag->HasNeighbors(faceId)) continue; const asiAlgo_Feature& neighbors = aag->GetNeighbors(faceId); for (TColStd_MapIteratorOfPackedMapOfInteger it(neighbors); it.More(); it.Next()) { const int neighborId = it.Key(); if (neighborId <= faceId) continue; const asiAlgo_AAG::t_arc arc(faceId, neighborId); Handle(asiAlgo_FeatureAttrAngle) angleAttr = Handle(asiAlgo_FeatureAttrAngle)::DownCast(aag->GetArcAttribute(arc)); Handle(asiAlgo_FeatureAttrAdjacency) adjacencyAttr = Handle(asiAlgo_FeatureAttrAdjacency)::DownCast(aag->GetArcAttribute(arc)); std::string angleType = "adjacent"; double angleRad = 0.0; if (!angleAttr.IsNull()) { angleType = asiAlgo_FeatureAngle::ToString(angleAttr->GetAngleType()); angleRad = angleAttr->GetAngleRad(); } angleSummary[angleType]++; if (angleType.find("smooth") != std::string::npos) tangentPairs.emplace_back(faceId, neighborId); if (!firstAdjacency) std::cout << ",\n"; firstAdjacency = false; std::cout << " { \"faceIds\": [" << faceId << ", " << neighborId << "]" << ", \"angleType\": \"" << angleType << "\"" << ", \"angleRad\": " << angleRad << ", \"edgeIds\": "; if (!adjacencyAttr.IsNull()) printPackedMap(adjacencyAttr->GetEdgeIndices()); else std::cout << "[]"; std::cout << " }"; } } std::cout << "\n ],\n"; std::cout << " \"geometricRelations\": [\n"; bool firstRelation = true; const double angleTol = 1.0e-7; const double linearTol = 1.0e-4; const int geometricPairFaceLimit = 800; const bool runFullGeometricPairScan = aag->GetNumberOfNodes() <= geometricPairFaceLimit; if (runFullGeometricPairScan) { for (int faceId = 1; faceId <= aag->GetNumberOfNodes(); ++faceId) { const TopoDS_Face& leftFace = aag->GetFace(faceId); BRepAdaptor_Surface leftSurface(leftFace); for (int neighborId = faceId + 1; neighborId <= aag->GetNumberOfNodes(); ++neighborId) { const TopoDS_Face& rightFace = aag->GetFace(neighborId); BRepAdaptor_Surface rightSurface(rightFace); std::string relationType; double residual = 0.0; if (leftSurface.GetType() == GeomAbs_Plane && rightSurface.GetType() == GeomAbs_Plane) { const gp_Pln leftPlane = leftSurface.Plane(); const gp_Pln rightPlane = rightSurface.Plane(); const gp_Dir leftDir = leftPlane.Axis().Direction(); const gp_Dir rightDir = rightPlane.Axis().Direction(); if (leftDir.IsParallel(rightDir, angleTol)) { residual = leftPlane.Distance(rightPlane.Location()); relationType = residual <= linearTol ? "coplanar" : "parallel"; } else if (leftDir.IsNormal(rightDir, angleTol)) { residual = std::abs(leftDir.Dot(rightDir)); relationType = "perpendicular"; } } else if (leftSurface.GetType() == GeomAbs_Cylinder && rightSurface.GetType() == GeomAbs_Cylinder) { const gp_Cylinder leftCylinder = leftSurface.Cylinder(); const gp_Cylinder rightCylinder = rightSurface.Cylinder(); const gp_Ax1 leftAxis = leftCylinder.Axis(); const gp_Ax1 rightAxis = rightCylinder.Axis(); if (leftAxis.IsCoaxial(rightAxis, angleTol, linearTol) || leftAxis.IsCoaxial(rightAxis.Reversed(), angleTol, linearTol)) { residual = std::abs(leftCylinder.Radius() - rightCylinder.Radius()); relationType = "coaxial"; } else if (leftAxis.Direction().IsParallel(rightAxis.Direction(), angleTol)) { residual = 0.0; relationType = "parallel_axis"; } } if (!relationType.empty()) { printGeometricRelation(faceId, neighborId, relationType, residual, "analysis-situs-probe", firstRelation); firstRelation = false; geometricRelationSummary[relationType]++; } } } } for (const auto& item : tangentPairs) { printGeometricRelation(item.first, item.second, "tangent", 0.0, "analysis-situs-aag-angle", firstRelation); firstRelation = false; geometricRelationSummary["tangent"]++; } std::cout << "\n ],\n"; std::cout << " \"surfaceSummary\": "; printStringIntMap(surfaceSummary); std::cout << ",\n"; std::cout << " \"angleSummary\": "; printStringIntMap(angleSummary); std::cout << ",\n"; std::cout << " \"geometricRelationMode\": \"" << (runFullGeometricPairScan ? "all-pairs" : "aag-smooth-only-large-model") << "\",\n"; std::cout << " \"geometricRelationSummary\": "; printStringIntMap(geometricRelationSummary); std::cout << "\n"; std::cout << "}\n"; return 0; }