6cfd186780
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.
549 lines
18 KiB
C++
549 lines
18 KiB
C++
#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
|