fix: STEP AP203 import — SolidWorks compatibility
AP203 schema auto-detection from FILE_SCHEMA header FACE_OUTER_BOUND entity support (equivalent to FACE_BOUND) CLOSED_SHELL as top-level entity in AP203 mode SolidWorks-specific: PRODUCT_DEFINITION_FORMATION, STYLED_ITEM skip, PRESENTATION_STYLE_ASSIGNMENT skip, generic entity error recovery StepImportDiagnostic.ap203_entities field 26/27 tests pass (1 pre-existing skip_to_semicolon bug)
This commit is contained in:
@@ -145,12 +145,15 @@ struct StepImportReport {
|
||||
int skipped_unsupported = 0; ///< 跳过的非标实体数
|
||||
int approximated_surfaces = 0; ///< 近似处理的曲面数
|
||||
int recovered_faces = 0; ///< 从破损数据中恢复的面数
|
||||
bool ap203_specific = false; ///< 是否启用 AP203 兼容模式
|
||||
int skipped_solidworks_entities = 0; ///< 跳过的 SolidWorks 非几何实体数
|
||||
std::vector<std::string> warnings; ///< 警告消息列表
|
||||
|
||||
/// 是否有任何异常
|
||||
[[nodiscard]] bool has_issues() const {
|
||||
return skipped_damaged > 0 || skipped_unsupported > 0 ||
|
||||
approximated_surfaces > 0 || recovered_faces > 0 ||
|
||||
skipped_solidworks_entities > 0 ||
|
||||
!warnings.empty();
|
||||
}
|
||||
|
||||
|
||||
+233
-9
@@ -35,6 +35,8 @@ struct StepImportDiagnostic {
|
||||
int approximated_surfaces = 0; // non-standard surfaces approximated
|
||||
int recovered_faces = 0; // faces recovered from damaged data
|
||||
int entities_with_warnings = 0;
|
||||
bool is_ap203 = false; // AP203 compatibility mode enabled
|
||||
int skipped_solidworks_entities = 0; // SolidWorks non-geometric entities skipped
|
||||
std::vector<std::string> warnings;
|
||||
|
||||
void add_warning(const std::string& w) {
|
||||
@@ -238,7 +240,7 @@ struct StepEntity {
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
class StepParser {
|
||||
public:
|
||||
explicit StepParser(std::string data) : tokenizer_(std::move(data)) {}
|
||||
explicit StepParser(std::string data) : data_(std::move(data)), tokenizer_(data_) {}
|
||||
|
||||
bool parse_data_section() {
|
||||
// Find DATA; section
|
||||
@@ -274,9 +276,40 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Scan HEADER section for FILE_SCHEMA to detect AP203
|
||||
/// Must be called BEFORE parse_data_section()
|
||||
bool detect_ap203_header() {
|
||||
// We need to scan from the beginning, so create a fresh tokenizer
|
||||
// or scan inline — we scan the raw data for FILE_SCHEMA pattern
|
||||
const std::string& raw = data_; // We need access to the raw data
|
||||
|
||||
// Find "FILE_SCHEMA"
|
||||
size_t pos = raw.find("FILE_SCHEMA");
|
||||
if (pos == std::string::npos) return false;
|
||||
|
||||
// Find the opening parenthesis
|
||||
pos = raw.find('(', pos);
|
||||
if (pos == std::string::npos || pos + 1 >= raw.size()) return false;
|
||||
|
||||
// Collect everything until the matching closing parenthesis
|
||||
int depth = 1;
|
||||
size_t end = pos + 1;
|
||||
while (end < raw.size() && depth > 0) {
|
||||
if (raw[end] == '(') depth++;
|
||||
else if (raw[end] == ')') depth--;
|
||||
end++;
|
||||
}
|
||||
|
||||
std::string schema_str = raw.substr(pos, end - pos);
|
||||
// Check for "203" in the schema identifier
|
||||
// AP203 manifests as 'CONFIG_CONTROL_DESIGN' or contains '203' in the schema id
|
||||
return (schema_str.find("203") != std::string::npos);
|
||||
}
|
||||
|
||||
const std::unordered_map<int, StepEntity>& entities() const { return entities_; }
|
||||
|
||||
private:
|
||||
std::string data_; // raw step data for header scanning (MUST be before tokenizer_)
|
||||
StepTokenizer tokenizer_;
|
||||
std::unordered_map<int, StepEntity> entities_;
|
||||
|
||||
@@ -463,20 +496,67 @@ public:
|
||||
resolve_all_directions();
|
||||
resolve_all_placements();
|
||||
|
||||
// Pre-scan: count SolidWorks non-geometric entities for diagnostics
|
||||
for (const auto& [id, ent] : entities_) {
|
||||
if (ent.type == "APPLICATION_CONTEXT" ||
|
||||
ent.type == "APPLICATION_PROTOCOL_DEFINITION" ||
|
||||
ent.type == "MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION" ||
|
||||
ent.type == "STYLED_ITEM" ||
|
||||
ent.type == "PRESENTATION_STYLE_ASSIGNMENT" ||
|
||||
ent.type == "SHAPE_REPRESENTATION" ||
|
||||
ent.type == "REPRESENTATION_CONTEXT" ||
|
||||
ent.type == "PRODUCT_DEFINITION_CONTEXT" ||
|
||||
ent.type == "PRODUCT_RELATED_PRODUCT_CATEGORY" ||
|
||||
ent.type == "PRODUCT_CONTEXT" ||
|
||||
ent.type == "DESIGN_CONTEXT" ||
|
||||
ent.type == "CURVE_STYLE" ||
|
||||
ent.type == "SURFACE_STYLE_USAGE" ||
|
||||
ent.type == "SURFACE_SIDE_STYLE" ||
|
||||
ent.type == "SURFACE_STYLE_FILL_AREA" ||
|
||||
ent.type == "FILL_AREA_STYLE_COLOUR" ||
|
||||
ent.type == "COLOUR_RGB" ||
|
||||
ent.type == "DRAUGHTING_PRE_DEFINED_COLOUR" ||
|
||||
ent.type == "PRESENTATION_LAYER_ASSIGNMENT" ||
|
||||
ent.type == "MECHANICAL_CONTEXT" ||
|
||||
ent.type == "APPROVAL_ROLE" ||
|
||||
ent.type == "APPROVAL_PERSON_ORGANIZATION" ||
|
||||
ent.type == "APPROVAL_DATE_TIME" ||
|
||||
ent.type == "APPROVAL" ||
|
||||
ent.type == "CC_DESIGN_APPROVAL" ||
|
||||
ent.type == "CC_DESIGN_DATE_AND_TIME_ASSIGNMENT" ||
|
||||
ent.type == "CC_DESIGN_PERSON_AND_ORGANIZATION_ASSIGNMENT" ||
|
||||
ent.type == "CC_DESIGN_SECURITY_CLASSIFICATION" ||
|
||||
ent.type == "SECURITY_CLASSIFICATION_LEVEL" ||
|
||||
ent.type == "PERSON_AND_ORGANIZATION" ||
|
||||
ent.type == "PERSON" ||
|
||||
ent.type == "ORGANIZATION" ||
|
||||
ent.type == "DATE_AND_TIME" ||
|
||||
ent.type == "CALENDAR_DATE" ||
|
||||
ent.type == "LOCAL_TIME" ||
|
||||
ent.type == "COORDINATED_UNIVERSAL_TIME_OFFSET" ||
|
||||
ent.type == "UNCERTAINTY_MEASURE_WITH_UNIT" ||
|
||||
ent.type == "PROPERTY_DEFINITION" ||
|
||||
ent.type == "SUPPLIED_BREP_REPRESENTATION") {
|
||||
g_import_diag.skipped_solidworks_entities++;
|
||||
}
|
||||
}
|
||||
|
||||
// Find top-level products or directly find MANIFOLD_SOLID_BREP
|
||||
std::vector<int> root_entities;
|
||||
|
||||
// Try finding PRODUCT entities first
|
||||
// Try finding PRODUCT entities first (also handle SolidWorks variants)
|
||||
for (const auto& [id, ent] : entities_) {
|
||||
if (ent.type == "PRODUCT" || ent.type == "PRODUCT_DEFINITION") {
|
||||
if (ent.type == "PRODUCT" || ent.type == "PRODUCT_DEFINITION" ||
|
||||
ent.type == "PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE") {
|
||||
root_entities.push_back(id);
|
||||
}
|
||||
}
|
||||
|
||||
// If no PRODUCT, find SHAPE_DEFINITION_REPRESENTATION or directly MANIFOLD_SOLID_BREP
|
||||
// If no PRODUCT, find SHAPE_DEFINITION_REPRESENTATION or SHAPE_REPRESENTATION_RELATIONSHIP
|
||||
if (root_entities.empty()) {
|
||||
for (const auto& [id, ent] : entities_) {
|
||||
if (ent.type == "SHAPE_DEFINITION_REPRESENTATION") {
|
||||
if (ent.type == "SHAPE_DEFINITION_REPRESENTATION" ||
|
||||
ent.type == "SHAPE_REPRESENTATION_RELATIONSHIP") {
|
||||
root_entities.push_back(id);
|
||||
}
|
||||
}
|
||||
@@ -490,6 +570,41 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// AP203 mode: CLOSED_SHELL may directly be the top-level entity (no MANIFOLD_SOLID_BREP wrapper)
|
||||
std::vector<int> direct_shell_ids;
|
||||
if (solid_ids.empty() || g_import_diag.is_ap203) {
|
||||
for (const auto& [id, ent] : entities_) {
|
||||
if (ent.type == "CLOSED_SHELL") {
|
||||
// In AP203, CLOSED_SHELL directly contains faces — no MANIFOLD_SOLID_BREP wrapper
|
||||
// Check that this CLOSED_SHELL is NOT referenced by any MANIFOLD_SOLID_BREP (to avoid duplicates)
|
||||
bool is_referenced_by_solid = false;
|
||||
for (const auto& [sid, sent] : entities_) {
|
||||
if (sent.type == "MANIFOLD_SOLID_BREP" || sent.type == "SHELL_BASED_SURFACE_MODEL") {
|
||||
for (const auto& arg : sent.args) {
|
||||
if (arg.type == StepValue::Type::Ref && arg.ival == id) {
|
||||
is_referenced_by_solid = true;
|
||||
break;
|
||||
}
|
||||
if (arg.type == StepValue::Type::List) {
|
||||
for (const auto& sv : arg.list) {
|
||||
if (sv.type == StepValue::Type::Ref && sv.ival == id) {
|
||||
is_referenced_by_solid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_referenced_by_solid) break;
|
||||
}
|
||||
}
|
||||
if (is_referenced_by_solid) break;
|
||||
}
|
||||
if (!is_referenced_by_solid) {
|
||||
direct_shell_ids.push_back(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!solid_ids.empty()) {
|
||||
for (int sid : solid_ids) {
|
||||
BrepModel body = convert_solid(sid);
|
||||
@@ -499,6 +614,27 @@ public:
|
||||
result.push_back(std::move(body));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle direct CLOSED_SHELL bodies (AP203 mode)
|
||||
if (!direct_shell_ids.empty()) {
|
||||
for (int sh_id : direct_shell_ids) {
|
||||
BrepModel model;
|
||||
auto face_ids = convert_shell(sh_id, model);
|
||||
if (!face_ids.empty()) {
|
||||
int shell_id = model.add_shell(face_ids, true);
|
||||
std::string name = find_product_name(sh_id);
|
||||
model.add_body({shell_id}, name);
|
||||
if (model.num_faces() > 0) {
|
||||
result.push_back(std::move(model));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Walk product tree for any root entities not yet handled
|
||||
if (!solid_ids.empty() || !direct_shell_ids.empty()) {
|
||||
// Already handled above
|
||||
} else if (!root_entities.empty()) {
|
||||
// Walk PRODUCT → PRODUCT_DEFINITION_FORMATION → ...
|
||||
for (int rid : root_entities) {
|
||||
@@ -1172,6 +1308,14 @@ private:
|
||||
|
||||
bool is_outer = (ent.type == "FACE_OUTER_BOUND");
|
||||
|
||||
// AP203 compatibility: FACE_OUTER_BOUND is the standard bound type in AP203
|
||||
// Both FACE_BOUND and FACE_OUTER_BOUND have identical structure
|
||||
if (ent.type != "FACE_BOUND" && ent.type != "FACE_OUTER_BOUND") {
|
||||
// Unknown bound type — try to parse anyway
|
||||
g_import_diag.add_warning("Unknown face bound type #" + std::to_string(bound_id)
|
||||
+ ": " + ent.type + " — attempting to parse as FACE_BOUND");
|
||||
}
|
||||
|
||||
int loop_ref = -1;
|
||||
for (const auto& arg : ent.args) {
|
||||
int r = to_ref(arg);
|
||||
@@ -1411,11 +1555,61 @@ private:
|
||||
if (!entities_.count(root_id)) return result;
|
||||
|
||||
const auto& ent = entities_.at(root_id);
|
||||
// SHAPE_DEFINITION_REPRESENTATION → find MANIFOLD_SOLID_BREP refs
|
||||
|
||||
// Skip SolidWorks-specific non-geometric entities
|
||||
if (ent.type == "APPLICATION_CONTEXT" ||
|
||||
ent.type == "APPLICATION_PROTOCOL_DEFINITION" ||
|
||||
ent.type == "MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION" ||
|
||||
ent.type == "STYLED_ITEM" ||
|
||||
ent.type == "PRESENTATION_STYLE_ASSIGNMENT" ||
|
||||
ent.type == "SHAPE_REPRESENTATION" ||
|
||||
ent.type == "REPRESENTATION_CONTEXT" ||
|
||||
ent.type == "PRODUCT_DEFINITION_CONTEXT" ||
|
||||
ent.type == "PRODUCT_RELATED_PRODUCT_CATEGORY" ||
|
||||
ent.type == "PRODUCT_CONTEXT" ||
|
||||
ent.type == "DESIGN_CONTEXT" ||
|
||||
ent.type == "CURVE_STYLE" ||
|
||||
ent.type == "SURFACE_STYLE_USAGE" ||
|
||||
ent.type == "SURFACE_SIDE_STYLE" ||
|
||||
ent.type == "SURFACE_STYLE_FILL_AREA" ||
|
||||
ent.type == "FILL_AREA_STYLE_COLOUR" ||
|
||||
ent.type == "COLOUR_RGB" ||
|
||||
ent.type == "DRAUGHTING_PRE_DEFINED_COLOUR" ||
|
||||
ent.type == "PRESENTATION_LAYER_ASSIGNMENT" ||
|
||||
ent.type == "MECHANICAL_CONTEXT" ||
|
||||
ent.type == "APPROVAL_ROLE" ||
|
||||
ent.type == "APPROVAL_PERSON_ORGANIZATION" ||
|
||||
ent.type == "APPROVAL_DATE_TIME" ||
|
||||
ent.type == "APPROVAL" ||
|
||||
ent.type == "CC_DESIGN_APPROVAL" ||
|
||||
ent.type == "CC_DESIGN_DATE_AND_TIME_ASSIGNMENT" ||
|
||||
ent.type == "CC_DESIGN_PERSON_AND_ORGANIZATION_ASSIGNMENT" ||
|
||||
ent.type == "CC_DESIGN_SECURITY_CLASSIFICATION" ||
|
||||
ent.type == "SECURITY_CLASSIFICATION_LEVEL" ||
|
||||
ent.type == "PERSON_AND_ORGANIZATION" ||
|
||||
ent.type == "PERSON" ||
|
||||
ent.type == "ORGANIZATION" ||
|
||||
ent.type == "DATE_AND_TIME" ||
|
||||
ent.type == "CALENDAR_DATE" ||
|
||||
ent.type == "LOCAL_TIME" ||
|
||||
ent.type == "COORDINATED_UNIVERSAL_TIME_OFFSET" ||
|
||||
ent.type == "UNCERTAINTY_MEASURE_WITH_UNIT" ||
|
||||
ent.type == "PROPERTY_DEFINITION" ||
|
||||
ent.type == "SUPPLIED_BREP_REPRESENTATION") {
|
||||
g_import_diag.skipped_solidworks_entities++;
|
||||
return result;
|
||||
}
|
||||
|
||||
// SHAPE_DEFINITION_REPRESENTATION / SHAPE_REPRESENTATION_RELATIONSHIP → find MANIFOLD_SOLID_BREP refs
|
||||
std::vector<int> solid_refs;
|
||||
find_nested_refs(ent, "MANIFOLD_SOLID_BREP", solid_refs);
|
||||
find_nested_refs(ent, "SHELL_BASED_SURFACE_MODEL", solid_refs);
|
||||
|
||||
// AP203: also look for CLOSED_SHELL directly
|
||||
if (solid_refs.empty() || g_import_diag.is_ap203) {
|
||||
find_nested_refs(ent, "CLOSED_SHELL", solid_refs);
|
||||
}
|
||||
|
||||
if (solid_refs.empty()) {
|
||||
// Try just iterating all args
|
||||
collect_all_refs(ent, solid_refs);
|
||||
@@ -1424,16 +1618,32 @@ private:
|
||||
for (int r : solid_refs) {
|
||||
if (entities_.count(r)) {
|
||||
const auto& e = entities_.at(r);
|
||||
if (e.type == "MANIFOLD_SOLID_BREP" || e.type == "SHELL_BASED_SURFACE_MODEL")
|
||||
if (e.type == "MANIFOLD_SOLID_BREP" || e.type == "SHELL_BASED_SURFACE_MODEL" ||
|
||||
e.type == "CLOSED_SHELL") {
|
||||
filtered.push_back(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
solid_refs = filtered;
|
||||
}
|
||||
|
||||
for (int sid : solid_refs) {
|
||||
auto body = convert_solid(sid);
|
||||
if (body.num_faces() > 0) result.push_back(std::move(body));
|
||||
if (entities_.count(sid) && entities_.at(sid).type == "CLOSED_SHELL") {
|
||||
// Direct CLOSED_SHELL (AP203 mode)
|
||||
BrepModel model;
|
||||
auto face_ids = convert_shell(sid, model);
|
||||
if (!face_ids.empty()) {
|
||||
int shell_id = model.add_shell(face_ids, true);
|
||||
std::string name = find_product_name(sid);
|
||||
model.add_body({shell_id}, name);
|
||||
if (model.num_faces() > 0) {
|
||||
result.push_back(std::move(model));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto body = convert_solid(sid);
|
||||
if (body.num_faces() > 0) result.push_back(std::move(body));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1492,6 +1702,12 @@ std::vector<BrepModel> import_step_from_string(const std::string& step_data) {
|
||||
}
|
||||
|
||||
StepParser parser(step_data);
|
||||
|
||||
// Detect AP203 schema from HEADER before parsing DATA
|
||||
if (parser.detect_ap203_header()) {
|
||||
g_import_diag.is_ap203 = true;
|
||||
}
|
||||
|
||||
if (!parser.parse_data_section()) {
|
||||
// Even if parse failed entirely, return what we have if any entities parsed
|
||||
if (parser.entities().empty()) {
|
||||
@@ -1543,6 +1759,12 @@ std::string StepImportReport::report() const {
|
||||
oss << "Skipped (unsupported): " << skipped_unsupported << "\n";
|
||||
oss << "Approximated surfaces: " << approximated_surfaces << "\n";
|
||||
oss << "Recovered faces: " << recovered_faces << "\n";
|
||||
if (ap203_specific) {
|
||||
oss << "AP203 compatibility mode: enabled\n";
|
||||
}
|
||||
if (skipped_solidworks_entities > 0) {
|
||||
oss << "Skipped SolidWorks non-geometric entities: " << skipped_solidworks_entities << "\n";
|
||||
}
|
||||
if (!warnings.empty()) {
|
||||
oss << "Warnings (" << warnings.size() << "):\n";
|
||||
for (size_t i = 0; i < warnings.size() && i < 50; ++i) {
|
||||
@@ -1563,6 +1785,8 @@ StepImportReport step_import_diagnostic() {
|
||||
r.skipped_unsupported = g_import_diag.skipped_unsupported;
|
||||
r.approximated_surfaces = g_import_diag.approximated_surfaces;
|
||||
r.recovered_faces = g_import_diag.recovered_faces;
|
||||
r.ap203_specific = g_import_diag.is_ap203;
|
||||
r.skipped_solidworks_entities = g_import_diag.skipped_solidworks_entities;
|
||||
r.warnings = g_import_diag.warnings;
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -680,3 +680,200 @@ TEST(StepImport, MixedValidAndInvalidEntities) {
|
||||
ASSERT_GE(bodies.size(), 1u);
|
||||
EXPECT_TRUE(bodies[0].is_valid());
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Helper: build an AP203 header
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
|
||||
static std::string step_header_ap203() {
|
||||
return R"(ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION(('ViewDesignEngine AP203 test'),'2;1');
|
||||
FILE_NAME('test_ap203.stp','2025-01-01T00:00:00',('Author'),(''),'','','');
|
||||
FILE_SCHEMA(('CONFIG_CONTROL_DESIGN { 1 0 10303 203 2 1 4 }'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
)";
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Test: AP203 CLOSED_SHELL direct import (no MANIFOLD_SOLID_BREP)
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
|
||||
TEST(StepImport, AP203_ClosedShellDirect) {
|
||||
// AP203 may have CLOSED_SHELL directly as top-level without MANIFOLD_SOLID_BREP
|
||||
std::string step = step_header_ap203() + R"(
|
||||
#1=CARTESIAN_POINT('',(0.0,0.0,0.0));
|
||||
#2=DIRECTION('',(0.0,0.0,1.0));
|
||||
#3=AXIS2_PLACEMENT_3D('',#1,#2,$);
|
||||
#4=CARTESIAN_POINT('',(5.0,0.0,0.0));
|
||||
#5=VERTEX_POINT('',#4);
|
||||
#6=CIRCLE('',#3,5.0);
|
||||
#7=EDGE_CURVE('',#5,#5,#6,.T.);
|
||||
#8=ORIENTED_EDGE('',*,*,#7,.T.);
|
||||
#9=EDGE_LOOP('',(#8));
|
||||
#10=FACE_OUTER_BOUND('',#9,.T.);
|
||||
#11=PLANE('',#3);
|
||||
#12=ADVANCED_FACE('',(#10),#11,.T.);
|
||||
#13=CLOSED_SHELL('ap203_disc',(#12));
|
||||
)" + step_footer();
|
||||
|
||||
auto bodies = import_step_from_string(step);
|
||||
EXPECT_EQ(step_last_error(), StepError::Ok);
|
||||
|
||||
// AP203 mode should have been detected
|
||||
auto diag = step_import_diagnostic();
|
||||
EXPECT_TRUE(diag.ap203_specific);
|
||||
|
||||
ASSERT_GE(bodies.size(), 1u);
|
||||
EXPECT_TRUE(bodies[0].is_valid());
|
||||
EXPECT_GE(bodies[0].num_faces(), 1u);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Test: AP203 CLOSED_SHELL with ADVANCED_FACE using FACE_OUTER_BOUND
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
|
||||
TEST(StepImport, AP203_FaceOuterBound) {
|
||||
// AP203 uses FACE_OUTER_BOUND instead of FACE_BOUND
|
||||
std::string step = step_header_ap203() + R"(
|
||||
#1=CARTESIAN_POINT('',(0.0,0.0,0.0));
|
||||
#2=CARTESIAN_POINT('',(1.0,0.0,0.0));
|
||||
#3=DIRECTION('',(0.0,0.0,1.0));
|
||||
#4=DIRECTION('',(1.0,0.0,0.0));
|
||||
#5=AXIS2_PLACEMENT_3D('',#1,#3,#4);
|
||||
#6=VERTEX_POINT('',#2);
|
||||
#7=LINE('',#1,#4);
|
||||
#8=EDGE_CURVE('',#6,#6,#7,.T.);
|
||||
#9=ORIENTED_EDGE('',*,*,#8,.T.);
|
||||
#10=EDGE_LOOP('',(#9));
|
||||
#11=FACE_OUTER_BOUND('outer',#10,.T.);
|
||||
#12=PLANE('',#5);
|
||||
#13=ADVANCED_FACE('face_ap203',(#11),#12,.T.);
|
||||
#14=CLOSED_SHELL('shell_ap203',(#13));
|
||||
)" + step_footer();
|
||||
|
||||
auto bodies = import_step_from_string(step);
|
||||
EXPECT_EQ(step_last_error(), StepError::Ok);
|
||||
|
||||
auto diag = step_import_diagnostic();
|
||||
EXPECT_TRUE(diag.ap203_specific);
|
||||
|
||||
ASSERT_GE(bodies.size(), 1u);
|
||||
EXPECT_TRUE(bodies[0].is_valid());
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Test: AP203 with SolidWorks-style non-geometric entities
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
|
||||
TEST(StepImport, AP203_SolidWorksStyleEntities) {
|
||||
// Simulate SolidWorks AP203 export with non-geometric entities
|
||||
std::string step = step_header_ap203() + R"(
|
||||
#1=CARTESIAN_POINT('',(0.0,0.0,0.0));
|
||||
#2=DIRECTION('',(0.0,0.0,1.0));
|
||||
#3=AXIS2_PLACEMENT_3D('',#1,#2,$);
|
||||
#4=CARTESIAN_POINT('',(3.0,0.0,0.0));
|
||||
#5=VERTEX_POINT('',#4);
|
||||
#6=CIRCLE('',#3,3.0);
|
||||
#7=EDGE_CURVE('',#5,#5,#6,.T.);
|
||||
#8=ORIENTED_EDGE('',*,*,#7,.T.);
|
||||
#9=EDGE_LOOP('',(#8));
|
||||
#10=FACE_OUTER_BOUND('',#9,.T.);
|
||||
#11=PLANE('',#3);
|
||||
#12=ADVANCED_FACE('',(#10),#11,.T.);
|
||||
#13=CLOSED_SHELL('part',(#12));
|
||||
#20=APPLICATION_CONTEXT('configuration controlled 3d designs of mechanical parts and assemblies');
|
||||
#21=APPLICATION_PROTOCOL_DEFINITION('','config_control_design',2002,#20);
|
||||
#22=PRODUCT('Part1','Part1','',(#20));
|
||||
#23=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('','',#22,.NOT_KNOWN.);
|
||||
#24=PRODUCT_DEFINITION('design','',#23,#20);
|
||||
#25=PRODUCT_DEFINITION_SHAPE('',$,#24);
|
||||
#26=SHAPE_DEFINITION_REPRESENTATION(#25,#13);
|
||||
#30=STYLED_ITEM('color',(#40),#12);
|
||||
#31=PRESENTATION_STYLE_ASSIGNMENT((#41));
|
||||
#40=PRESENTATION_LAYER_ASSIGNMENT('layer1','visible',(#30));
|
||||
#41=SURFACE_STYLE_USAGE(.BOTH.,#42);
|
||||
#42=SURFACE_SIDE_STYLE('',(#43));
|
||||
#43=SURFACE_STYLE_FILL_AREA(#44);
|
||||
#44=FILL_AREA_STYLE_COLOUR('',#45);
|
||||
#45=COLOUR_RGB('red',1.0,0.0,0.0);
|
||||
)" + step_footer();
|
||||
|
||||
auto bodies = import_step_from_string(step);
|
||||
EXPECT_EQ(step_last_error(), StepError::Ok);
|
||||
|
||||
auto diag = step_import_diagnostic();
|
||||
EXPECT_TRUE(diag.ap203_specific);
|
||||
// SolidWorks non-geometric entities should be counted as skipped
|
||||
EXPECT_GT(diag.skipped_solidworks_entities, 0);
|
||||
|
||||
ASSERT_GE(bodies.size(), 1u);
|
||||
EXPECT_TRUE(bodies[0].is_valid());
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Test: AP203 diagnostic report includes ap203_specific
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
|
||||
TEST(StepImport, AP203_DiagnosticReport) {
|
||||
std::string step = step_header_ap203() + R"(
|
||||
#1=CARTESIAN_POINT('',(0.0,0.0,0.0));
|
||||
#2=DIRECTION('',(0.0,0.0,1.0));
|
||||
#3=AXIS2_PLACEMENT_3D('',#1,#2,$);
|
||||
#4=CARTESIAN_POINT('',(2.0,0.0,0.0));
|
||||
#5=VERTEX_POINT('',#4);
|
||||
#6=CIRCLE('',#3,2.0);
|
||||
#7=EDGE_CURVE('',#5,#5,#6,.T.);
|
||||
#8=ORIENTED_EDGE('',*,*,#7,.T.);
|
||||
#9=EDGE_LOOP('',(#8));
|
||||
#10=FACE_OUTER_BOUND('',#9,.T.);
|
||||
#11=PLANE('',#3);
|
||||
#12=ADVANCED_FACE('',(#10),#11,.T.);
|
||||
#13=CLOSED_SHELL('ap203_part',(#12));
|
||||
)" + step_footer();
|
||||
|
||||
auto bodies = import_step_from_string(step);
|
||||
|
||||
auto diag = step_import_diagnostic();
|
||||
EXPECT_TRUE(diag.ap203_specific);
|
||||
EXPECT_GT(diag.total_entities, 0);
|
||||
|
||||
std::string report = diag.report();
|
||||
EXPECT_NE(report.find("AP203 compatibility mode: enabled"), std::string::npos);
|
||||
EXPECT_NE(report.find("=== STEP Import Diagnostic Report ==="), std::string::npos);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Test: AP203 with MANIFOLD_SOLID_BREP (backward compatible)
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
|
||||
TEST(StepImport, AP203_WithManifoldSolidBrep) {
|
||||
// Some AP203 files still use MANIFOLD_SOLID_BREP — should work fine
|
||||
std::string step = step_header_ap203() + R"(
|
||||
#1=CARTESIAN_POINT('',(0.0,0.0,0.0));
|
||||
#2=DIRECTION('',(0.0,0.0,1.0));
|
||||
#3=AXIS2_PLACEMENT_3D('',#1,#2,$);
|
||||
#4=CARTESIAN_POINT('',(4.0,0.0,0.0));
|
||||
#5=VERTEX_POINT('',#4);
|
||||
#6=CIRCLE('',#3,4.0);
|
||||
#7=EDGE_CURVE('',#5,#5,#6,.T.);
|
||||
#8=ORIENTED_EDGE('',*,*,#7,.T.);
|
||||
#9=EDGE_LOOP('',(#8));
|
||||
#10=FACE_OUTER_BOUND('',#9,.T.);
|
||||
#11=PLANE('',#3);
|
||||
#12=ADVANCED_FACE('',(#10),#11,.T.);
|
||||
#13=CLOSED_SHELL('',(#12));
|
||||
#14=MANIFOLD_SOLID_BREP('box_with_msb',#13);
|
||||
)" + step_footer();
|
||||
|
||||
auto bodies = import_step_from_string(step);
|
||||
EXPECT_EQ(step_last_error(), StepError::Ok);
|
||||
|
||||
auto diag = step_import_diagnostic();
|
||||
EXPECT_TRUE(diag.ap203_specific);
|
||||
|
||||
ASSERT_GE(bodies.size(), 1u);
|
||||
EXPECT_TRUE(bodies[0].is_valid());
|
||||
EXPECT_GE(bodies[0].num_faces(), 1u);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user