#include #include "vde/brep/step_import.h" #include "vde/brep/brep.h" #include "vde/brep/modeling.h" #include #include using namespace vde::brep; using namespace vde::core; // ───────────────────────────────────────────────────────────── // Helper: build a minimal STEP file as a string // ───────────────────────────────────────────────────────────── static std::string step_header() { return R"(ISO-10303-21; HEADER; FILE_DESCRIPTION(('ViewDesignEngine test'),'2;1'); FILE_NAME('test.stp','2025-01-01T00:00:00',('Author'),(''),'','',''); FILE_SCHEMA(('CONFIG_CONTROL_DESIGN','GEOMETRIC_MODEL_SCHEMA')); ENDSEC; DATA; )"; } static std::string step_footer() { return R"(ENDSEC; END-ISO-10303-21; )"; } // ───────────────────────────────────────────────────────────── // Test: Parse a minimal box STEP file // ───────────────────────────────────────────────────────────── TEST(StepImport, MinimalBox) { // A simple box as MANIFOLD_SOLID_BREP with 6 planar faces // Points are the 8 corners of a 2×2×2 box centered at origin std::string step = step_header() + R"( #1=CARTESIAN_POINT('',(-1.0,-1.0,-1.0)); #2=CARTESIAN_POINT('',(1.0,-1.0,-1.0)); #3=CARTESIAN_POINT('',(1.0,1.0,-1.0)); #4=CARTESIAN_POINT('',(-1.0,1.0,-1.0)); #5=CARTESIAN_POINT('',(-1.0,-1.0,1.0)); #6=CARTESIAN_POINT('',(1.0,-1.0,1.0)); #7=CARTESIAN_POINT('',(1.0,1.0,1.0)); #8=CARTESIAN_POINT('',(-1.0,1.0,1.0)); #10=DIRECTION('',(0.0,0.0,1.0)); #11=DIRECTION('',(1.0,0.0,0.0)); #12=DIRECTION('',(0.0,1.0,0.0)); #13=DIRECTION('',(0.0,0.0,-1.0)); #14=DIRECTION('',(-1.0,0.0,0.0)); #15=DIRECTION('',(0.0,-1.0,0.0)); #16=DIRECTION('',(1.0,1.0,0.0)); #17=DIRECTION('',(-1.0,1.0,0.0)); #18=DIRECTION('',(-1.0,-1.0,0.0)); #19=DIRECTION('',(1.0,-1.0,0.0)); #20=AXIS2_PLACEMENT_3D('',#1,#10,#11); #21=AXIS2_PLACEMENT_3D('',#5,#10,#11); #22=AXIS2_PLACEMENT_3D('',#1,#12,#11); #23=AXIS2_PLACEMENT_3D('',#4,#12,#11); #24=AXIS2_PLACEMENT_3D('',#2,#14,#12); #25=AXIS2_PLACEMENT_3D('',#1,#14,#12); #30=PLANE('',#20); #31=PLANE('',#21); #32=PLANE('',#22); #33=PLANE('',#23); #34=PLANE('',#24); #35=PLANE('',#25); #40=LINE('',#1,#16); #41=LINE('',#2,#17); #42=LINE('',#3,#12); #43=LINE('',#4,#18); #44=LINE('',#5,#19); #45=LINE('',#6,#17); #46=LINE('',#7,#11); #47=LINE('',#8,#18); #48=LINE('',#1,#10); #49=LINE('',#2,#10); #50=LINE('',#3,#10); #51=LINE('',#4,#10); #52=LINE('',#2,#11); #53=LINE('',#3,#12); #54=LINE('',#7,#12); #55=LINE('',#6,#15); #56=LINE('',#1,#15); #57=LINE('',#4,#14); #58=LINE('',#8,#14); #59=LINE('',#5,#15); #60=LINE('',#5,#14); #70=EDGE_CURVE('',#80,#81,#40,.T.); #71=EDGE_CURVE('',#81,#82,#41,.T.); #72=EDGE_CURVE('',#82,#83,#42,.T.); #73=EDGE_CURVE('',#83,#80,#43,.T.); #74=EDGE_CURVE('',#84,#85,#44,.T.); #75=EDGE_CURVE('',#85,#86,#45,.T.); #76=EDGE_CURVE('',#86,#87,#46,.T.); #77=EDGE_CURVE('',#87,#84,#47,.T.); #78=EDGE_CURVE('',#80,#84,#48,.T.); #79=EDGE_CURVE('',#81,#85,#49,.T.); #80=VERTEX_POINT('',#1); #81=VERTEX_POINT('',#2); #82=VERTEX_POINT('',#3); #83=VERTEX_POINT('',#4); #84=VERTEX_POINT('',#5); #85=VERTEX_POINT('',#6); #86=VERTEX_POINT('',#7); #87=VERTEX_POINT('',#8); #90=EDGE_CURVE('',#82,#86,#50,.T.); #91=EDGE_CURVE('',#85,#81,#52,.T.); #92=EDGE_CURVE('',#83,#87,#53,.T.); #93=EDGE_CURVE('',#86,#87,#54,.T.); #94=EDGE_CURVE('',#81,#80,#55,.T.); #95=EDGE_CURVE('',#84,#80,#56,.T.); #96=EDGE_CURVE('',#82,#83,#57,.T.); #97=EDGE_CURVE('',#87,#84,#58,.T.); #98=EDGE_CURVE('',#80,#83,#59,.T.); #99=EDGE_CURVE('',#84,#85,#60,.T.); #100=ORIENTED_EDGE('',*,*,#70,.T.); #101=ORIENTED_EDGE('',*,*,#71,.T.); #102=ORIENTED_EDGE('',*,*,#72,.T.); #103=ORIENTED_EDGE('',*,*,#73,.T.); #104=ORIENTED_EDGE('',*,*,#74,.F.); #105=ORIENTED_EDGE('',*,*,#75,.F.); #106=ORIENTED_EDGE('',*,*,#76,.F.); #107=ORIENTED_EDGE('',*,*,#77,.F.); #108=ORIENTED_EDGE('',*,*,#78,.T.); #109=ORIENTED_EDGE('',*,*,#94,.T.); #110=ORIENTED_EDGE('',*,*,#95,.F.); #111=ORIENTED_EDGE('',*,*,#98,.T.); #112=ORIENTED_EDGE('',*,*,#56,.T.); #113=ORIENTED_EDGE('',*,*,#79,.T.); #114=ORIENTED_EDGE('',*,*,#91,.T.); #115=ORIENTED_EDGE('',*,*,#90,.F.); #116=ORIENTED_EDGE('',*,*,#96,.F.); #117=ORIENTED_EDGE('',*,*,#92,.T.); #118=ORIENTED_EDGE('',*,*,#97,.F.); #119=ORIENTED_EDGE('',*,*,#54,.T.); #120=ORIENTED_EDGE('',*,*,#93,.F.); #121=ORIENTED_EDGE('',*,*,#46,.T.); #122=ORIENTED_EDGE('',*,*,#60,.T.); #123=ORIENTED_EDGE('',*,*,#99,.F.); #130=EDGE_LOOP('',(#100,#101,#102,#103)); #131=EDGE_LOOP('',(#104,#105,#106,#107)); #132=EDGE_LOOP('',(#108,#109,#110,#112)); #133=EDGE_LOOP('',(#113,#114,#115,#116)); #134=EDGE_LOOP('',(#117,#118,#119,#120)); #135=EDGE_LOOP('',(#121,#122,#123,#111)); #140=FACE_OUTER_BOUND('',#130,.T.); #141=FACE_OUTER_BOUND('',#131,.T.); #142=FACE_OUTER_BOUND('',#132,.T.); #143=FACE_OUTER_BOUND('',#133,.T.); #144=FACE_OUTER_BOUND('',#134,.T.); #145=FACE_OUTER_BOUND('',#135,.T.); #150=ADVANCED_FACE('',(#140),#30,.T.); #151=ADVANCED_FACE('',(#141),#31,.T.); #152=ADVANCED_FACE('',(#142),#32,.T.); #153=ADVANCED_FACE('',(#143),#33,.T.); #154=ADVANCED_FACE('',(#144),#34,.T.); #155=ADVANCED_FACE('',(#145),#35,.T.); #160=CLOSED_SHELL('',(#150,#151,#152,#153,#154,#155)); #170=MANIFOLD_SOLID_BREP('box',#160); )" + step_footer(); auto bodies = import_step_from_string(step); EXPECT_EQ(step_last_error(), StepError::Ok); ASSERT_GE(bodies.size(), 1u); const auto& body = bodies[0]; EXPECT_TRUE(body.is_valid()); EXPECT_GE(body.num_faces(), 6u); EXPECT_GE(body.num_edges(), 12u); EXPECT_GE(body.num_vertices(), 8u); EXPECT_EQ(body.num_bodies(), 1u); } // ───────────────────────────────────────────────────────────── // Test: Simple box with CIRCLE edge (cylinder top/bottom face) // ───────────────────────────────────────────────────────────── TEST(StepImport, SimpleCylinder) { // A simple cylinder: bottom circle + top circle + cylindrical side surface std::string step = step_header() + R"( #1=CARTESIAN_POINT('',(0.0,0.0,-2.0)); #2=CARTESIAN_POINT('',(0.0,0.0,2.0)); #3=CARTESIAN_POINT('',(1.0,0.0,-2.0)); #4=CARTESIAN_POINT('',(1.0,0.0,2.0)); #10=DIRECTION('',(0.0,0.0,1.0)); #11=DIRECTION('',(1.0,0.0,0.0)); #12=DIRECTION('',(0.0,1.0,0.0)); #13=DIRECTION('',(0.0,0.0,-1.0)); #20=AXIS2_PLACEMENT_3D('',#1,#10,#11); #21=AXIS2_PLACEMENT_3D('',#2,#10,#11); #30=PLANE('',#20); #31=PLANE('',#21); #40=CIRCLE('',#20,1.0); #41=CIRCLE('',#21,1.0); #50=EDGE_CURVE('',#80,#80,#40,.T.); #51=EDGE_CURVE('',#81,#81,#41,.T.); #60=LINE('',#3,#10); #70=EDGE_CURVE('',#80,#81,#60,.T.); #80=VERTEX_POINT('',#3); #81=VERTEX_POINT('',#4); #90=ORIENTED_EDGE('',*,*,#50,.T.); #91=ORIENTED_EDGE('',*,*,#51,.F.); #92=ORIENTED_EDGE('',*,*,#70,.T.); #100=EDGE_LOOP('',(#90)); #101=EDGE_LOOP('',(#91)); #110=FACE_OUTER_BOUND('',#100,.T.); #111=FACE_OUTER_BOUND('',#101,.T.); #120=ADVANCED_FACE('',(#110),#30,.T.); #121=ADVANCED_FACE('',(#111),#31,.T.); #130=CLOSED_SHELL('',(#120,#121)); #140=SHELL_BASED_SURFACE_MODEL('',(#130)); #142=CYLINDRICAL_SURFACE('',#20,1.0); #143=ADVANCED_FACE('',(#110),#142,.T.); #150=CLOSED_SHELL('',(#120,#121,#143)); #160=MANIFOLD_SOLID_BREP('cylinder',#150); )" + step_footer(); auto bodies = import_step_from_string(step); ASSERT_GE(bodies.size(), 1u); const auto& body = bodies[0]; EXPECT_TRUE(body.is_valid()); EXPECT_GE(body.num_faces(), 3u); } // ───────────────────────────────────────────────────────────── // Test: Parse STEP with multiple bodies // ───────────────────────────────────────────────────────────── TEST(StepImport, MultipleBodies) { std::string step = step_header() + R"( #1=CARTESIAN_POINT('',(-0.5,-0.5,-0.5)); #2=CARTESIAN_POINT('',(0.5,-0.5,-0.5)); #3=CARTESIAN_POINT('',(0.5,0.5,-0.5)); #4=CARTESIAN_POINT('',(-0.5,0.5,-0.5)); #5=CARTESIAN_POINT('',(-0.5,-0.5,0.5)); #6=CARTESIAN_POINT('',(0.5,-0.5,0.5)); #7=CARTESIAN_POINT('',(0.5,0.5,0.5)); #8=CARTESIAN_POINT('',(-0.5,0.5,0.5)); #10=DIRECTION('',(0.0,0.0,1.0)); #11=DIRECTION('',(1.0,0.0,0.0)); #12=DIRECTION('',(0.0,1.0,0.0)); #13=DIRECTION('',(0.0,0.0,-1.0)); #14=DIRECTION('',(-1.0,0.0,0.0)); #15=DIRECTION('',(0.0,-1.0,0.0)); #20=AXIS2_PLACEMENT_3D('',#1,#10,#11); #21=AXIS2_PLACEMENT_3D('',#5,#10,#11); #22=AXIS2_PLACEMENT_3D('',#1,#12,#11); #23=AXIS2_PLACEMENT_3D('',#4,#12,#11); #24=AXIS2_PLACEMENT_3D('',#2,#14,#12); #25=AXIS2_PLACEMENT_3D('',#1,#14,#12); #30=PLANE('',#20); #31=PLANE('',#21); #32=PLANE('',#22); #33=PLANE('',#23); #34=PLANE('',#24); #35=PLANE('',#25); #40=LINE('',#1,#2); #41=LINE('',#2,#3); #42=LINE('',#3,#4); #43=LINE('',#4,#1); #44=LINE('',#5,#6); #45=LINE('',#6,#7); #46=LINE('',#7,#8); #47=LINE('',#8,#5); #48=LINE('',#1,#5); #49=LINE('',#2,#6); #50=LINE('',#3,#7); #51=LINE('',#4,#8); #60=VERTEX_POINT('',#1); #61=VERTEX_POINT('',#2); #62=VERTEX_POINT('',#3); #63=VERTEX_POINT('',#4); #64=VERTEX_POINT('',#5); #65=VERTEX_POINT('',#6); #66=VERTEX_POINT('',#7); #67=VERTEX_POINT('',#8); #70=EDGE_CURVE('',#60,#61,#40,.T.); #71=EDGE_CURVE('',#61,#62,#41,.T.); #72=EDGE_CURVE('',#62,#63,#42,.T.); #73=EDGE_CURVE('',#63,#60,#43,.T.); #74=EDGE_CURVE('',#64,#65,#44,.T.); #75=EDGE_CURVE('',#65,#66,#45,.T.); #76=EDGE_CURVE('',#66,#67,#46,.T.); #77=EDGE_CURVE('',#67,#64,#47,.T.); #78=EDGE_CURVE('',#60,#64,#48,.T.); #79=EDGE_CURVE('',#61,#65,#49,.T.); #80=EDGE_CURVE('',#62,#66,#50,.T.); #81=EDGE_CURVE('',#63,#67,#51,.T.); #90=ORIENTED_EDGE('',*,*,#70,.T.); #91=ORIENTED_EDGE('',*,*,#71,.T.); #92=ORIENTED_EDGE('',*,*,#72,.T.); #93=ORIENTED_EDGE('',*,*,#73,.T.); #94=ORIENTED_EDGE('',*,*,#74,.T.); #95=ORIENTED_EDGE('',*,*,#75,.T.); #96=ORIENTED_EDGE('',*,*,#76,.T.); #97=ORIENTED_EDGE('',*,*,#77,.T.); #98=ORIENTED_EDGE('',*,*,#78,.T.); #99=ORIENTED_EDGE('',*,*,#79,.T.); #100=ORIENTED_EDGE('',*,*,#80,.T.); #101=ORIENTED_EDGE('',*,*,#81,.T.); #110=EDGE_LOOP('',(#90,#91,#92,#93)); #111=EDGE_LOOP('',(#94,#95,#96,#97)); #112=EDGE_LOOP('',(#98,#99,#100,#101)); #113=EDGE_LOOP('',(#73,#43,#42,#41)); #114=EDGE_LOOP('',(#77,#47,#46,#45)); #120=FACE_OUTER_BOUND('',#110,.T.); #121=FACE_OUTER_BOUND('',#111,.T.); #122=FACE_OUTER_BOUND('',#112,.T.); #130=ADVANCED_FACE('',(#120),#30,.T.); #131=ADVANCED_FACE('',(#121),#31,.T.); #132=ADVANCED_FACE('',(#122),#32,.T.); #140=CLOSED_SHELL('',(#130,#131,#132)); #150=MANIFOLD_SOLID_BREP('body1',#140); #200=CARTESIAN_POINT('',(2.0,-0.5,-0.5)); #201=CARTESIAN_POINT('',(3.0,-0.5,-0.5)); #202=CARTESIAN_POINT('',(3.0,0.5,-0.5)); #203=CARTESIAN_POINT('',(2.0,0.5,-0.5)); #204=CARTESIAN_POINT('',(2.0,-0.5,0.5)); #205=CARTESIAN_POINT('',(3.0,-0.5,0.5)); #206=CARTESIAN_POINT('',(3.0,0.5,0.5)); #207=CARTESIAN_POINT('',(2.0,0.5,0.5)); #220=AXIS2_PLACEMENT_3D('',#200,#10,#11); #230=PLANE('',#220); #240=LINE('',#200,#201); #241=LINE('',#201,#202); #242=LINE('',#202,#203); #243=LINE('',#203,#200); #244=LINE('',#204,#205); #245=LINE('',#205,#206); #246=LINE('',#206,#207); #247=LINE('',#207,#204); #248=LINE('',#200,#204); #249=LINE('',#201,#205); #250=LINE('',#202,#206); #251=LINE('',#203,#207); #260=VERTEX_POINT('',#200); #261=VERTEX_POINT('',#201); #262=VERTEX_POINT('',#202); #263=VERTEX_POINT('',#203); #264=VERTEX_POINT('',#204); #265=VERTEX_POINT('',#205); #266=VERTEX_POINT('',#206); #267=VERTEX_POINT('',#207); #270=EDGE_CURVE('',#260,#261,#240,.T.); #271=EDGE_CURVE('',#261,#262,#241,.T.); #272=EDGE_CURVE('',#262,#263,#242,.T.); #273=EDGE_CURVE('',#263,#260,#243,.T.); #274=EDGE_CURVE('',#264,#265,#244,.T.); #275=EDGE_CURVE('',#265,#266,#245,.T.); #276=EDGE_CURVE('',#266,#267,#246,.T.); #277=EDGE_CURVE('',#267,#264,#247,.T.); #278=EDGE_CURVE('',#260,#264,#248,.T.); #279=EDGE_CURVE('',#261,#265,#249,.T.); #280=EDGE_CURVE('',#262,#266,#250,.T.); #281=EDGE_CURVE('',#263,#267,#251,.T.); #290=ORIENTED_EDGE('',*,*,#270,.T.); #291=ORIENTED_EDGE('',*,*,#271,.T.); #292=ORIENTED_EDGE('',*,*,#272,.T.); #293=ORIENTED_EDGE('',*,*,#273,.T.); #294=ORIENTED_EDGE('',*,*,#274,.T.); #295=ORIENTED_EDGE('',*,*,#275,.T.); #296=ORIENTED_EDGE('',*,*,#276,.T.); #297=ORIENTED_EDGE('',*,*,#277,.T.); #298=ORIENTED_EDGE('',*,*,#278,.T.); #299=ORIENTED_EDGE('',*,*,#279,.T.); #300=ORIENTED_EDGE('',*,*,#280,.T.); #301=ORIENTED_EDGE('',*,*,#281,.T.); #310=EDGE_LOOP('',(#290,#291,#292,#293)); #311=EDGE_LOOP('',(#294,#295,#296,#297)); #312=EDGE_LOOP('',(#298,#299,#300,#301)); #320=FACE_OUTER_BOUND('',#310,.T.); #321=FACE_OUTER_BOUND('',#311,.T.); #322=FACE_OUTER_BOUND('',#312,.T.); #330=ADVANCED_FACE('',(#320),#30,.T.); #331=ADVANCED_FACE('',(#321),#31,.T.); #332=ADVANCED_FACE('',(#322),#32,.T.); #340=CLOSED_SHELL('',(#330,#331,#332)); #350=MANIFOLD_SOLID_BREP('body2',#340); )" + step_footer(); auto bodies = import_step_from_string(step); EXPECT_EQ(step_last_error(), StepError::Ok); ASSERT_GE(bodies.size(), 1u); // At least one body should parse int total_faces = 0; for (const auto& body : bodies) { EXPECT_TRUE(body.is_valid()); total_faces += static_cast(body.num_faces()); } // Two bodies, each with at least 3 faces (simplified test) EXPECT_GE(total_faces, 3); } // ───────────────────────────────────────────────────────────── // Test: Invalid input → graceful error // ───────────────────────────────────────────────────────────── TEST(StepImport, EmptyInput) { auto bodies = import_step_from_string(""); EXPECT_NE(step_last_error(), StepError::Ok); EXPECT_TRUE(bodies.empty()); } TEST(StepImport, GarbageInput) { auto bodies = import_step_from_string("this is not a step file at all"); EXPECT_NE(step_last_error(), StepError::Ok); EXPECT_TRUE(bodies.empty()); } TEST(StepImport, MissingEndsec) { std::string step = step_header() + "#1=CARTESIAN_POINT('',(0,0,0));"; // Missing ENDSEC and footer auto bodies = import_step_from_string(step); // May or may not produce bodies depending on where it fails // But should not crash EXPECT_NO_THROW(import_step_from_string(step)); } TEST(StepImport, OnlyHeader) { std::string step = step_header() + step_footer(); auto bodies = import_step_from_string(step); // Empty DATA section → no bodies EXPECT_EQ(bodies.size(), 0u); } TEST(StepImport, MissingEntityId) { std::string step = step_header() + "CARTESIAN_POINT('',(0,0,0));\n" + step_footer(); auto bodies = import_step_from_string(step); EXPECT_NE(step_last_error(), StepError::Ok); } // ───────────────────────────────────────────────────────────── // Test: Circle and ellipse curves // ───────────────────────────────────────────────────────────── TEST(StepImport, CircleCurve) { std::string step = step_header() + R"( #1=CARTESIAN_POINT('',(0.0,0.0,0.0)); #2=DIRECTION('',(0.0,0.0,1.0)); #3=DIRECTION('',(1.0,0.0,0.0)); #4=AXIS2_PLACEMENT_3D('',#1,#2,#3); #5=CARTESIAN_POINT('',(5.0,0.0,0.0)); #6=VERTEX_POINT('',#5); #7=CIRCLE('',#4,5.0); #8=EDGE_CURVE('',#6,#6,#7,.T.); #9=ORIENTED_EDGE('',*,*,#8,.T.); #10=EDGE_LOOP('',(#9)); #11=FACE_OUTER_BOUND('',#10,.T.); #12=PLANE('',#4); #13=ADVANCED_FACE('',(#11),#12,.T.); #14=CLOSED_SHELL('',(#13)); #15=MANIFOLD_SOLID_BREP('circle_test',#14); )" + step_footer(); auto bodies = import_step_from_string(step); ASSERT_GE(bodies.size(), 1u); EXPECT_TRUE(bodies[0].is_valid()); EXPECT_GE(bodies[0].num_vertices(), 1u); } TEST(StepImport, EllipseCurve) { std::string step = step_header() + R"( #1=CARTESIAN_POINT('',(0.0,0.0,0.0)); #2=DIRECTION('',(0.0,0.0,1.0)); #3=DIRECTION('',(1.0,0.0,0.0)); #4=AXIS2_PLACEMENT_3D('',#1,#2,#3); #5=CARTESIAN_POINT('',(3.0,0.0,0.0)); #6=VERTEX_POINT('',#5); #7=ELLIPSE('',#4,3.0,1.0); #8=EDGE_CURVE('',#6,#6,#7,.T.); #9=ORIENTED_EDGE('',*,*,#8,.T.); #10=EDGE_LOOP('',(#9)); #11=FACE_OUTER_BOUND('',#10,.T.); #12=PLANE('',#4); #13=ADVANCED_FACE('',(#11),#12,.T.); #14=CLOSED_SHELL('',(#13)); #15=MANIFOLD_SOLID_BREP('ellipse_test',#14); )" + step_footer(); auto bodies = import_step_from_string(step); ASSERT_GE(bodies.size(), 1u); EXPECT_TRUE(bodies[0].is_valid()); } // ───────────────────────────────────────────────────────────── // Test: STEP surfaces // ───────────────────────────────────────────────────────────── TEST(StepImport, SphericalSurface) { std::string step = step_header() + 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=SPHERICAL_SURFACE('',#5,5.0); #8=CIRCLE('',#5,5.0); #9=EDGE_CURVE('',#6,#6,#8,.T.); #10=ORIENTED_EDGE('',*,*,#9,.T.); #11=EDGE_LOOP('',(#10)); #12=FACE_OUTER_BOUND('',#11,.T.); #13=ADVANCED_FACE('',(#12),#7,.T.); #14=CLOSED_SHELL('',(#13)); #15=MANIFOLD_SOLID_BREP('sphere',#14); )" + step_footer(); auto bodies = import_step_from_string(step); ASSERT_GE(bodies.size(), 1u); EXPECT_TRUE(bodies[0].is_valid()); EXPECT_GE(bodies[0].num_faces(), 1u); } TEST(StepImport, ToroidalSurface) { std::string step = step_header() + R"( #1=CARTESIAN_POINT('',(0.0,0.0,0.0)); #2=DIRECTION('',(0.0,0.0,1.0)); #3=DIRECTION('',(1.0,0.0,0.0)); #4=AXIS2_PLACEMENT_3D('',#1,#2,#3); #5=CARTESIAN_POINT('',(8.0,0.0,0.0)); #6=VERTEX_POINT('',#5); #7=TOROIDAL_SURFACE('',#4,5.0,2.0); #8=CIRCLE('',#4,5.0); #9=EDGE_CURVE('',#6,#6,#8,.T.); #10=ORIENTED_EDGE('',*,*,#9,.T.); #11=EDGE_LOOP('',(#10)); #12=FACE_OUTER_BOUND('',#11,.T.); #13=ADVANCED_FACE('',(#12),#7,.T.); #14=CLOSED_SHELL('',(#13)); #15=MANIFOLD_SOLID_BREP('torus',#14); )" + step_footer(); auto bodies = import_step_from_string(step); ASSERT_GE(bodies.size(), 1u); EXPECT_TRUE(bodies[0].is_valid()); } // ───────────────────────────────────────────────────────────── // Test: B_SPLINE_CURVE and B_SPLINE_SURFACE // ───────────────────────────────────────────────────────────── TEST(StepImport, BSplineCurve) { std::string step = step_header() + R"( #1=CARTESIAN_POINT('',(0.0,0.0,0.0)); #2=CARTESIAN_POINT('',(1.0,2.0,0.0)); #3=CARTESIAN_POINT('',(3.0,1.0,0.0)); #4=CARTESIAN_POINT('',(4.0,0.0,0.0)); #10=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1,#2,#3,#4),.UNSPECIFIED.,.F.,.F.,(4,1,1,4),(0.0,0.25,0.75,1.0),.PIECEWISE_BEZIER_KNOTS.); #11=CARTESIAN_POINT('',(0.0,0.0,0.0)); #12=CARTESIAN_POINT('',(4.0,0.0,0.0)); #20=VERTEX_POINT('',#11); #21=VERTEX_POINT('',#12); #30=EDGE_CURVE('',#20,#21,#10,.T.); #40=LINE('',#11,#12); #41=EDGE_CURVE('',#21,#20,#40,.T.); #50=ORIENTED_EDGE('',*,*,#30,.T.); #51=ORIENTED_EDGE('',*,*,#41,.T.); #60=EDGE_LOOP('',(#50,#51)); #61=FACE_OUTER_BOUND('',#60,.T.); #70=DIRECTION('',(0.0,0.0,1.0)); #71=DIRECTION('',(1.0,0.0,0.0)); #72=AXIS2_PLACEMENT_3D('',#11,#70,#71); #73=PLANE('',#72); #80=ADVANCED_FACE('',(#61),#73,.T.); #90=CLOSED_SHELL('',(#80)); #100=MANIFOLD_SOLID_BREP('bspline_curve',#90); )" + step_footer(); auto bodies = import_step_from_string(step); ASSERT_GE(bodies.size(), 1u); EXPECT_TRUE(bodies[0].is_valid()); } // ───────────────────────────────────────────────────────────── // Test: Round-trip (create → export is not available, but we // can test creating a BrepModel programmatically and compare) // ───────────────────────────────────────────────────────────── TEST(StepImport, ProgrammaticBrepCanBeUsed) { auto box = make_box(2.0, 2.0, 2.0); EXPECT_TRUE(box.is_valid()); // Verify the box can be tessellated auto mesh = box.to_mesh(); EXPECT_GT(mesh.num_faces(), 0u); // Verify face and edge queries work after import auto edges = box.face_edges(0); EXPECT_FALSE(edges.empty()); auto efs = box.edge_faces(0); EXPECT_FALSE(efs.empty()); } // ───────────────────────────────────────────────────────────── // Test: String escaping in STEP // ───────────────────────────────────────────────────────────── TEST(StepImport, StringEscaping) { std::string step = step_header() + R"( #1=CARTESIAN_POINT('name with spaces',(1.0,2.0,3.0)); #2=CARTESIAN_POINT('it''s escaped',(4.0,5.0,6.0)); )" + step_footer(); auto bodies = import_step_from_string(step); // No bodies (no topology) but should not crash EXPECT_EQ(bodies.size(), 0u); } // ───────────────────────────────────────────────────────────── // Test: Entities with omitted parameters ($) // ───────────────────────────────────────────────────────────── TEST(StepImport, OmittedParameters) { std::string step = step_header() + 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('',(#12)); #14=MANIFOLD_SOLID_BREP('omitted_test',#13); )" + step_footer(); auto bodies = import_step_from_string(step); ASSERT_GE(bodies.size(), 1u); EXPECT_TRUE(bodies[0].is_valid()); } // ───────────────────────────────────────────────────────────── // Test: STEP file with comments // ───────────────────────────────────────────────────────────── TEST(StepImport, Comments) { std::string step = step_header() + R"( /* This is a comment block */ #1=CARTESIAN_POINT('',(0.0,0.0,0.0)); /* inline comment */ #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('',(#12)); #14=MANIFOLD_SOLID_BREP('commented',#13); )" + step_footer(); auto bodies = import_step_from_string(step); ASSERT_GE(bodies.size(), 1u); EXPECT_TRUE(bodies[0].is_valid()); } // ───────────────────────────────────────────────────────────── // Test: Multi-line entities // ───────────────────────────────────────────────────────────── TEST(StepImport, MultiLineEntities) { std::string step = step_header() + R"( #1=CARTESIAN_POINT('', (0.0,0.0,0.0)); #2=CARTESIAN_POINT('',( 5.0,0.0,0.0)); #3=DIRECTION('', (0.0,0.0,1.0)); #4=AXIS2_PLACEMENT_3D('point', #1,#3,$); #5=VERTEX_POINT('',#2); #6=CIRCLE('round',#4,5.0); #7=EDGE_CURVE('edge', #5,#5,#6,.T.); #8=ORIENTED_EDGE('',*,*,#7,.T.); #9=EDGE_LOOP('',(#8)); #10=FACE_OUTER_BOUND('',#9,.T.); #11=PLANE('flat',#4); #12=ADVANCED_FACE('face',(#10),#11,.T.); #13=CLOSED_SHELL('shell',(#12)); #14=MANIFOLD_SOLID_BREP('solid',#13); )" + step_footer(); auto bodies = import_step_from_string(step); ASSERT_GE(bodies.size(), 1u); EXPECT_TRUE(bodies[0].is_valid()); } // ───────────────────────────────────────────────────────────── // Test: SHELL_BASED_SURFACE_MODEL (alternative to MANIFOLD_SOLID_BREP) // ───────────────────────────────────────────────────────────── TEST(StepImport, ShellBasedSurfaceModel) { std::string step = step_header() + 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('',(#12)); #14=SHELL_BASED_SURFACE_MODEL('',(#13)); )" + step_footer(); auto bodies = import_step_from_string(step); ASSERT_GE(bodies.size(), 1u); EXPECT_TRUE(bodies[0].is_valid()); EXPECT_GE(bodies[0].num_faces(), 1u); }