Files
ViewDesignEngine/tests/brep/test_iges_import.cpp
T
茂之钳 6cfd186780
CI / Build & Test (push) Failing after 33s
CI / Release Build (push) Failing after 32s
feat(brep): S13-A — IGES import support
Implement IGES (Initial Graphics Exchange Specification) file reader that
converts IGES entities to B-Rep models.

Key features:
- 80-character fixed-width card image parser (S/G/D/P/T sections)
- Directory Entry parsing with all 18 DE fields per entity
- Parameter Data parsing with DE pointer tracking
- D exponent notation support (e.g., 1.5D+2 = 150)

Supported IGES entity types:
  Curve entities (→ NurbsCurve):
    Type 100 - Circular Arc (rational degree-2)
    Type 102 - Composite Curve
    Type 104 - Conic Arc (ellipse/hyperbola/parabola)
    Type 106 - Copious Data (polyline)
    Type 110 - Line (degree-1)
    Type 126 - Rational B-Spline Curve

  Surface entities (→ NurbsSurface):
    Type 108 - Plane
    Type 114 - Parametric Spline Surface
    Type 118 - Ruled Surface
    Type 120 - Surface of Revolution
    Type 122 - Tabulated Cylinder (extrude)
    Type 128 - Rational B-Spline Surface
    Type 140 - Offset Surface
    Type 192 - Right Circular Cylindrical Surface
    Type 194 - Right Circular Conical Surface
    Type 196 - Spherical Surface
    Type 198 - Toroidal Surface

  Topology entities (→ BrepModel):
    Type 186 - Manifold Solid B-Rep Object
    Type 502 - Vertex
    Type 504 - Edge
    Type 508 - Loop
    Type 510 - Face
    Type 514 - Shell

Tests cover: line, arc, cylinder, sphere, cone, torus, plane,
B-spline curve/surface, ruled surface, revolution surface,
tabulated cylinder, copious data polyline, manifold solid B-Rep,
D exponent notation, empty/truncated file handling, and file errors.
2026-07-24 07:37:03 +00:00

549 lines
18 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <gtest/gtest.h>
#include "vde/brep/iges_import.h"
#include "vde/brep/brep.h"
#include "vde/brep/modeling.h"
#include <cmath>
#include <sstream>
#include <iomanip>
#include <vector>
#include <string>
using namespace vde::brep;
using namespace vde::core;
// ─────────────────────────────────────────────────────────────
// Helper: Build IGES 80-character fixed-width records
// ─────────────────────────────────────────────────────────────
namespace {
/// Pad a string to exactly `width` characters (right-justified for integers, left for strings)
std::string pad_right(const std::string& s, int width) {
std::string r = s;
if (static_cast<int>(r.size()) > width) r = r.substr(0, width);
else r.append(width - r.size(), ' ');
return r;
}
std::string pad_left(int val, int width) {
std::string s = std::to_string(val);
if (static_cast<int>(s.size()) > width) s = s.substr(0, width);
else s.insert(0, width - s.size(), ' ');
return s;
}
/// Build one IGES Directory Entry record (72 chars of fields + section char + 7-char seq)
std::string de_line(const std::vector<int>& fields, char section, int seq) {
// fields: 9 values, each right-justified in 8 chars
std::string line;
for (size_t i = 0; i < fields.size() && i < 9; ++i) {
std::string val = std::to_string(fields[i]);
// Right-justify integer in 8-char field
if (val.size() < 8) val.insert(0, 8 - val.size(), ' ');
line += val;
}
// pad remaining to 72
while (line.size() < 72) line += ' ';
line = line.substr(0, 72);
line += section;
line += pad_left(seq, 7);
return line;
}
/// Build one IGES Parameter Data record
std::string pd_line(const std::string& data, int de_ptr, int seq) {
std::string line = data;
// Data occupies cols 1-64 max
while (line.size() < 65) line += ' ';
line = line.substr(0, 64);
// Pad to col 65 (char index 64)
// Col 65 is blank
line += ' ';
// DE pointer in cols 66-72 (right justified in 7 chars)
std::string deplabel = std::to_string(de_ptr);
if (deplabel.size() < 7) deplabel = std::string(7 - deplabel.size(), ' ') + deplabel;
line += deplabel;
line += 'P';
line += pad_left(seq, 7);
return line;
}
/// Build a full IGES file string
struct IgesBuilder {
std::vector<std::string> s_lines;
std::vector<std::string> g_lines;
std::vector<std::string> d_lines; // pairs: 2 per entity
std::vector<std::string> p_lines;
void add_s_line(const std::string& txt) {
std::string line = pad_right(txt, 72) + "S";
line += pad_left(static_cast<int>(s_lines.size() + 1), 7);
s_lines.push_back(line);
}
void add_g_line(const std::string& txt) {
std::string line = pad_right(txt, 72) + "G";
line += pad_left(static_cast<int>(g_lines.size() + 1), 7);
g_lines.push_back(line);
}
// Add a directory entry (two 80-char lines)
void add_de(int type_num, int pd_ptr, int struct_val, int line_font, int level,
int view, int transform, int label, int status,
int line_weight, int color, int param_count, int form_num) {
int seq = static_cast<int>(d_lines.size() + 1);
// Line 1
d_lines.push_back(de_line({type_num, pd_ptr, struct_val, line_font, level,
view, transform, label, status}, 'D', seq));
// Line 2: type, line_weight, color, param_count, form_num, 0, 0, label, subscript
d_lines.push_back(de_line({type_num, line_weight, color, param_count, form_num,
0, 0, label, 0}, 'D', seq + 1));
}
// Add parameter data
void add_pd(const std::string& data, int de_ptr) {
int seq = static_cast<int>(p_lines.size() + 1);
p_lines.push_back(pd_line(data, de_ptr, seq));
}
std::string build() {
std::string result;
for (const auto& l : s_lines) result += l + "\n";
for (const auto& l : g_lines) result += l + "\n";
for (const auto& l : d_lines) result += l + "\n";
for (const auto& l : p_lines) result += l + "\n";
// Terminate section
int s_cnt = static_cast<int>(s_lines.size());
int g_cnt = static_cast<int>(g_lines.size());
int d_cnt = static_cast<int>(d_lines.size());
int p_cnt = static_cast<int>(p_lines.size());
std::string t_line = pad_right(
pad_left(s_cnt, 8) + pad_left(g_cnt, 8) + pad_left(d_cnt, 8) + pad_left(p_cnt, 8),
72) + "T" + pad_left(1, 7);
result += t_line + "\n";
return result;
}
};
/// Standard global section for minimal IGES files
std::string standard_global() {
return "1H,,1H;,4HSLOT,1,,,13,0.016,0.1,,1.0,1.0,1.0,2,1,1,8,,1.0,1,1,0,0;";
}
} // anonymous namespace
// ─────────────────────────────────────────────────────────────
TEST(IgesImport, EmptyData) {
auto bodies = import_iges_from_string("");
EXPECT_TRUE(bodies.empty());
EXPECT_EQ(iges_last_error(), IgesError::ParseError);
}
TEST(IgesImport, TruncatedFile) {
// Missing sections — just a start section
IgesBuilder b;
b.add_s_line("Truncated test file");
auto bodies = import_iges_from_string(b.build());
EXPECT_TRUE(bodies.empty());
EXPECT_NE(iges_last_error(), IgesError::Ok);
}
TEST(IgesImport, MinimalLine) {
IgesBuilder b;
b.add_s_line("Test IGES file with a single line");
b.add_g_line(standard_global());
// Single line entity: type 110
// DE entry: PD pointer 1, param count 1
b.add_de(110, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
// Line: X1,Y1,Z1,X2,Y2,Z2 = (0,0,0) to (10,0,0)
b.add_pd("0.0,0.0,0.0,10.0,0.0,0.0;", 1);
auto bodies = import_iges_from_string(b.build());
EXPECT_EQ(iges_last_error(), IgesError::Ok);
// Should produce at least one body (fallback mode with a surface)
ASSERT_FALSE(bodies.empty());
EXPECT_GT(bodies[0].num_surfaces(), 0u);
}
TEST(IgesImport, SingleArc) {
IgesBuilder b;
b.add_s_line("Test IGES file with a circular arc");
b.add_g_line(standard_global());
// Type 100: Circular Arc — 7 params: ZT, Xc, Yc, Xs, Ys, Xe, Ye
// Full circle: center (0,0), radius 5, start (5,0), end (5,0) → full circle
b.add_de(100, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("0.0,0.0,0.0,5.0,0.0,5.0,0.0;", 1);
auto bodies = import_iges_from_string(b.build());
EXPECT_EQ(iges_last_error(), IgesError::Ok);
ASSERT_FALSE(bodies.empty());
}
TEST(IgesImport, SingleLine110) {
// Test line (type 110): basic endpoint geometry
IgesBuilder b;
b.add_s_line("Line test");
b.add_g_line(standard_global());
b.add_de(110, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("1.0,2.0,3.0,4.0,5.0,6.0;", 1);
auto bodies = import_iges_from_string(b.build());
EXPECT_EQ(iges_last_error(), IgesError::Ok);
}
TEST(IgesImport, CylinderSurface) {
IgesBuilder b;
b.add_s_line("Cylinder test");
b.add_g_line(standard_global());
// Type 192: Right Circular Cylindrical Surface
// location(0,0,0), axis(0,0,1), radius=5
b.add_de(192, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("0.0,0.0,0.0,0.0,0.0,1.0,5.0;", 1);
auto bodies = import_iges_from_string(b.build());
EXPECT_EQ(iges_last_error(), IgesError::Ok);
}
TEST(IgesImport, SphereSurface) {
IgesBuilder b;
b.add_s_line("Sphere test");
b.add_g_line(standard_global());
// Type 196: Spherical Surface — center(0,0,0), radius=10
b.add_de(196, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("0.0,0.0,0.0,10.0;", 1);
auto bodies = import_iges_from_string(b.build());
EXPECT_EQ(iges_last_error(), IgesError::Ok);
}
TEST(IgesImport, ConeSurface) {
IgesBuilder b;
b.add_s_line("Cone test");
b.add_g_line(standard_global());
// Type 194: Right Circular Conical Surface
// location(0,0,0), axis(0,0,1), radius=5, semi_angle=0.5 rad
b.add_de(194, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("0.0,0.0,0.0,0.0,0.0,1.0,5.0,0.5;", 1);
auto bodies = import_iges_from_string(b.build());
EXPECT_EQ(iges_last_error(), IgesError::Ok);
}
TEST(IgesImport, TorusSurface) {
IgesBuilder b;
b.add_s_line("Torus test");
b.add_g_line(standard_global());
// Type 198: Toroidal Surface
// center(0,0,0), axis(0,0,1), R=10, r=3
b.add_de(198, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("0.0,0.0,0.0,0.0,0.0,1.0,10.0,3.0;", 1);
auto bodies = import_iges_from_string(b.build());
EXPECT_EQ(iges_last_error(), IgesError::Ok);
}
TEST(IgesImport, PlaneSurface) {
IgesBuilder b;
b.add_s_line("Plane test");
b.add_g_line(standard_global());
// Type 108: Plane — equation Ax+By+Cz+D=0
// XY plane: 0x+0y+1z+0=0
b.add_de(108, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("0.0,0.0,1.0,0.0;", 1);
auto bodies = import_iges_from_string(b.build());
EXPECT_EQ(iges_last_error(), IgesError::Ok);
}
TEST(IgesImport, BSplineCurve) {
IgesBuilder b;
b.add_s_line("B-Spline curve test");
b.add_g_line(standard_global());
// Type 126: Rational B-Spline Curve
// K=5, M=2 (degree 2), PROP1=0, PROP2=0 (non-rational), PROP3=0, N=3 (4 CPs)
// Knot sequence: A(0..K) = K+1=6 values: 0,0,0,0.5,1,1
// CPs: (0,0,0), (2,3,0), (5,1,0), (7,4,0)
b.add_de(126, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("5,2,0,0,0,3,0.0,0.0,0.0,0.5,1.0,1.0,0.0,0.0,0.0,2.0,3.0,0.0,5.0,1.0,0.0,7.0,4.0,0.0;", 1);
auto bodies = import_iges_from_string(b.build());
EXPECT_EQ(iges_last_error(), IgesError::Ok);
}
TEST(IgesImport, BSplineSurface) {
IgesBuilder b;
b.add_s_line("B-Spline surface test");
b.add_g_line(standard_global());
// Type 128: Rational B-Spline Surface
// K1=1, K2=1, M1=1, M2=1, PROP1-4=0, PROP5=0, N1=0, N2=0 → 1×1 CP, deg 1×1
// U knots: 0.0, 1.0 (K1+1=2 values)
// V knots: 0.0, 1.0 (K2+1=2 values)
// CP: (0,0,0)
b.add_de(128, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("1,1,1,1,0,0,0,0,0,0,0,0.0,1.0,0.0,1.0,0.0,0.0,0.0;", 1);
auto bodies = import_iges_from_string(b.build());
EXPECT_EQ(iges_last_error(), IgesError::Ok);
}
TEST(IgesImport, RuledSurface) {
IgesBuilder b;
b.add_s_line("Ruled surface test");
b.add_g_line(standard_global());
// Need two curves as DE entries first, then a ruled surface referencing them
// Line 1: (0,0,0) → (10,0,0), DE index 1
b.add_de(110, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("0.0,0.0,0.0,10.0,0.0,0.0;", 1);
// Line 2: (0,5,0) → (10,5,0), DE index 2
b.add_de(110, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("0.0,5.0,0.0,10.0,5.0,0.0;", 2);
// Ruled surface 118 referencing lines 1 and 2
b.add_de(118, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("1,2,0,0;", 3);
auto bodies = import_iges_from_string(b.build());
EXPECT_EQ(iges_last_error(), IgesError::Ok);
}
TEST(IgesImport, RevolutionSurface) {
IgesBuilder b;
b.add_s_line("Revolution surface test");
b.add_g_line(standard_global());
// Profile line: (5,0,0) → (5,0,10), DE index 1
b.add_de(110, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("5.0,0.0,0.0,5.0,0.0,10.0;", 1);
// Surface of revolution 120: profile DE=1, axis origin=(0,0,0), axis dir=(0,0,1)
b.add_de(120, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("1,0.0,0.0,0.0,0.0,0.0,1.0,0.0,6.283185307;", 2);
auto bodies = import_iges_from_string(b.build());
EXPECT_EQ(iges_last_error(), IgesError::Ok);
}
TEST(IgesImport, TabulatedCylinder) {
IgesBuilder b;
b.add_s_line("Tabulated cylinder test");
b.add_g_line(standard_global());
// Profile line: (5,0,0) → (5,0,0)... actually a single point doesn't work
// Let's use a simple line: (0,0,0) → (5,0,0), DE 1
b.add_de(110, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("0.0,0.0,0.0,5.0,0.0,0.0;", 1);
// Tabulated cylinder 122: curve DE=1, direction (0,0,10)
b.add_de(122, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("1,0.0,0.0,10.0;", 2);
auto bodies = import_iges_from_string(b.build());
EXPECT_EQ(iges_last_error(), IgesError::Ok);
}
TEST(IgesImport, CopiousDataPolyline) {
IgesBuilder b;
b.add_s_line("Polyline test");
b.add_g_line(standard_global());
// Type 106: Copious Data, IP=1 (polyline), N=4 points
// Points: (0,0,0), (2,0,0), (2,2,0), (0,2,0)
b.add_de(106, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("1,4,0.0,0.0,0.0,2.0,0.0,0.0,2.0,2.0,0.0,0.0,2.0,0.0;", 1);
auto bodies = import_iges_from_string(b.build());
EXPECT_EQ(iges_last_error(), IgesError::Ok);
}
TEST(IgesImport, ManifoldSolidBox) {
// Build a minimal B-Rep box via IGES topology entities
IgesBuilder b;
b.add_s_line("Manifold solid box test");
b.add_g_line(standard_global());
// Entities:
// 1: Vertex (0,0,0) type 502
// 2: Vertex (10,0,0)
// 3: Vertex (10,10,0)
// 4: Vertex (0,10,0)
// 5: Vertex (0,0,10)
// 6: Vertex (10,0,10)
// 7: Vertex (10,10,10)
// 8: Vertex (0,10,10)
// 9: Line (0,0,0)-(10,0,0) type 110 → edge uses curve
// ... and so on. Let's do a simpler subset.
// Vertex 1: (0,0,0)
b.add_de(502, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("0.0,0.0,0.0;", 1);
// Vertex 2: (10,0,0)
b.add_de(502, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("10.0,0.0,0.0;", 2);
// Vertex 3: (10,10,0)
b.add_de(502, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("10.0,10.0,0.0;", 3);
// Vertex 4: (0,10,0)
b.add_de(502, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("0.0,10.0,0.0;", 4);
// Line for edge: (0,0,0)-(10,0,0)
b.add_de(110, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("0.0,0.0,0.0,10.0,0.0,0.0;", 5);
// Edge 1: curve type=1 (line), curve DE=5, start vx DE=1, sv_index=0, end vx DE=2, ev_index=1
b.add_de(504, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("1,5,1,0,2,1,0,0.0,1.0;", 6);
// Plane for bottom face: z=0 plane
b.add_de(108, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("0.0,0.0,1.0,0.0;", 7);
// Loop: N=1 edge, type=0, edge_DE=6, vx_count=2, start=1, end=2, cur_srf=0, sparams=0, eparams=0
b.add_de(508, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("1,0,6,2,1,2,0,0.0,1.0;", 8);
// Face: surface_DE=7, N_loops=1, loop_flag=0, loop_DE=8
b.add_de(510, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("7,1,0,8;", 9);
// Shell: N=1 face, face_DE=9
b.add_de(514, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("1,9;", 10);
// Manifold Solid B-Rep 186: shell DE=10
b.add_de(186, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("10;", 11);
auto bodies = import_iges_from_string(b.build());
EXPECT_EQ(iges_last_error(), IgesError::Ok);
ASSERT_FALSE(bodies.empty());
EXPECT_GT(bodies[0].num_faces(), 0u);
}
TEST(IgesImport, RoundtripBox) {
// Create a box via modeling, then verify IGES import infrastructure works
auto box = make_box(2.0, 1.0, 3.0);
EXPECT_GT(box.num_faces(), 0u);
// Verify we can build and parse IGES with a cylinder
IgesBuilder b;
b.add_s_line("Roundtrip test - cylinder");
b.add_g_line(standard_global());
b.add_de(192, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("0.0,0.0,0.0,0.0,0.0,1.0,5.0;", 1);
auto bodies = import_iges_from_string(b.build());
EXPECT_EQ(iges_last_error(), IgesError::Ok);
}
TEST(IgesImport, InvalidSectionOrder) {
// Put sections in wrong order — only S and G with no D
IgesBuilder b;
b.add_s_line("Bad file");
b.add_g_line(standard_global());
// No D or P sections — just T
std::string result;
for (const auto& l : b.s_lines) result += l + "\n";
for (const auto& l : b.g_lines) result += l + "\n";
// Manually add T with wrong section
std::string t_line = " T 1\n";
result += t_line;
auto bodies = import_iges_from_string(result);
EXPECT_TRUE(bodies.empty());
EXPECT_NE(iges_last_error(), IgesError::Ok);
}
TEST(IgesImport, DEExponentNotation) {
// IGES uses D exponent notation (e.g., 1.0D+2 = 100)
IgesBuilder b;
b.add_s_line("D exponent notation test");
b.add_g_line(standard_global());
b.add_de(110, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("1.0D+2,2.0D+1,3.5D+0,4.0D-1,5.0D-2,6.0D+3;", 1);
auto bodies = import_iges_from_string(b.build());
EXPECT_EQ(iges_last_error(), IgesError::Ok);
}
TEST(IgesImport, MultipleSurfaces) {
IgesBuilder b;
b.add_s_line("Multiple surface types test");
b.add_g_line(standard_global());
// Plane
b.add_de(108, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("0.0,0.0,1.0,0.0;", 1);
// Cylinder
b.add_de(192, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("0.0,0.0,0.0,0.0,0.0,1.0,5.0;", 2);
// Sphere
b.add_de(196, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("10.0,0.0,0.0,3.0;", 3);
auto bodies = import_iges_from_string(b.build());
EXPECT_EQ(iges_last_error(), IgesError::Ok);
}
TEST(IgesImport, FileNotFound) {
auto bodies = import_iges("/nonexistent/path/file.iges");
EXPECT_TRUE(bodies.empty());
EXPECT_EQ(iges_last_error(), IgesError::FileNotFound);
}
TEST(IgesImport, VerifyArcGeometry) {
// A 90-degree arc: center(0,0), start(5,0), end(0,5)
IgesBuilder b;
b.add_s_line("Arc geometry test");
b.add_g_line(standard_global());
b.add_de(100, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("0.0,0.0,0.0,5.0,0.0,0.0,5.0;", 1);
auto bodies = import_iges_from_string(b.build());
EXPECT_EQ(iges_last_error(), IgesError::Ok);
ASSERT_FALSE(bodies.empty());
}
TEST(IgesImport, FullCircleArc) {
// Full circle: center(0,0), start(10,0), end(10,0) → 360°
IgesBuilder b;
b.add_s_line("Full circle arc test");
b.add_g_line(standard_global());
b.add_de(100, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
b.add_pd("0.0,0.0,0.0,10.0,0.0,10.0,0.0;", 1);
auto bodies = import_iges_from_string(b.build());
EXPECT_EQ(iges_last_error(), IgesError::Ok);
ASSERT_FALSE(bodies.empty());
}
} // namespace vde::brep