33 #include "stl_parser.h"
44 #define RUN_TEST(test_fn) \
45 std::cerr << "test " << #test_fn << "... "; \
49 std::cerr << "ok\n"; \
50 } catch (const std::runtime_error& err) { \
52 std::cerr << "FAILED: " << err.what() << "\n"; \
55 #define EXPECT_ERROR(stmt, err_msg) \
58 std::ostringstream oss; \
59 oss << "expected exception with message: \"" << err_msg << "\""; \
60 throw std::runtime_error(oss.str()); \
61 } catch (const std::exception& err) { \
62 if (err.what() != std::string(err_msg)) { \
63 std::ostringstream oss; \
64 oss << "got error message: \"" \
65 << err.what() << "\" but expected: \"" << err_msg << "\""; \
66 throw std::runtime_error(oss.str()); \
74 TempFile(
const std::string &filename,
const std::string &contents)
75 : filename_(filename) {
77 std::ofstream out(filename_);
83 std::remove(this->filename_.c_str());
86 std::string filename()
const {
90 std::string filename_;
94 return a.
x == b.
x && a.
y == b.
y && a.
z == b.
z;
97 static bool approx_eq(
double a,
double b,
double e = 1e-6) {
98 return (std::abs(a - b) <= e * (std::abs(a) + std::abs(b) + 1.0));
102 return approx_eq(a.
x, b.
x) && approx_eq(a.
y, b.
y) && approx_eq(a.
z, b.
z);
106 void egsInfoThrowing(
const char *msg, ...) {
110 vsprintf(buf, msg, ap);
112 throw std::runtime_error(buf);
115 namespace stl_parser {
117 static void missing_file() {
118 EXPECT_ERROR(stl_parser::parse_stl_file(
"temp_missing.stl"),
"STL file "
119 "`temp_missing.stl` does not exist or is not readable");
122 static void empty_file() {
123 TempFile empty(
"temp_empty.stl",
"");
124 EXPECT_ERROR(stl_parser::parse_stl_file(empty.filename()),
125 "failed to parse STL file `temp_empty.stl`");
128 static void parse_ascii_file() {
129 TempFile ascii(
"temp_ascii.stl", R
"(solid
130 facet normal 0.6229 0.35962 0.694744
132 vertex 43.062 20.491 -149.441
133 vertex 41.768 19.197 -147.611
134 vertex 43.946 19.607 -149.776
137 facet normal 0.730297 0.632387 0.258365
139 vertex 43.536 20.965 -151.941
140 vertex 43.062 20.491 -149.441
141 vertex 43.946 19.607 -149.776
144 facet normal 0.81508 0.547608 0.189131
146 vertex 43.536 20.965 -151.941
147 vertex 43.946 19.607 -149.776
148 vertex 44.593 18.96 -150.691
153 auto mesh = stl_parser::parse_stl_file(ascii.filename());
154 if (mesh.elements.size() != 3) {
155 throw std::runtime_error(
"expected 3 triangles, got "
156 + std::to_string(mesh.elements.size()));
158 if (!egsvec_approx_eq(mesh.elements[0].n,
EGS_Vector(0.6229f, 0.35962f, 0.694744f)) ||
159 !egsvec_approx_eq(mesh.elements[0].a,
EGS_Vector(43.062f, 20.491f, -149.441f)) ||
160 !egsvec_approx_eq(mesh.elements[0].b,
EGS_Vector(41.768f, 19.197f, -147.611f)) ||
161 !egsvec_approx_eq(mesh.elements[0].c,
EGS_Vector(43.946f, 19.607f, -149.776f))) {
162 throw std::runtime_error(
"element 0 parsing failed");
164 if (!egsvec_approx_eq(mesh.elements[1].n,
EGS_Vector(0.730297f, 0.632387f, 0.258365f)) ||
165 !egsvec_approx_eq(mesh.elements[1].a,
EGS_Vector(43.536f, 20.965f, -151.941f)) ||
166 !egsvec_approx_eq(mesh.elements[1].b,
EGS_Vector(43.062f, 20.491f, -149.441f)) ||
167 !egsvec_approx_eq(mesh.elements[1].c,
EGS_Vector(43.946f, 19.607f, -149.776f))) {
168 throw std::runtime_error(
"element 1 parsing failed");
170 if (!egsvec_approx_eq(mesh.elements[2].n,
EGS_Vector(0.81508f, 0.547608f, 0.189131f)) ||
171 !egsvec_approx_eq(mesh.elements[2].a,
EGS_Vector(43.536f, 20.965f, -151.941f)) ||
172 !egsvec_approx_eq(mesh.elements[2].b,
EGS_Vector(43.946f, 19.607f, -149.776f)) ||
173 !egsvec_approx_eq(mesh.elements[2].c,
EGS_Vector(44.593f, 18.96f, -150.691f))) {
174 throw std::runtime_error(
"element 2 parsing failed");
178 static void catch_zero_triangles() {
180 std::string header(84, 0);
181 TempFile binfile(
"temp_zero_tris.stl", header);
182 EXPECT_ERROR(stl_parser::parse_stl_file(binfile.filename()),
183 "STL file `temp_zero_tris.stl` has 0 triangles");
186 static void truncated_file() {
189 std::string header(80,
'A');
196 TempFile binfile(
"temp_truncated_file.stl", header);
197 std::ifstream input(binfile.filename(), std::ios::binary);
199 EXPECT_ERROR(stl_parser::parse_stl_file(binfile.filename()),
200 "failed to parse STL file `temp_truncated_file.stl`");
203 static void parse_binary_file() {
208 throw std::runtime_error(
"expected 3 triangles, got "
209 + std::to_string(mesh.
elements.size()));
212 if (!egsvec_approx_eq(mesh.
elements[0].n,
EGS_Vector(0.6229f, 0.35962f, 0.694744f)) ||
216 throw std::runtime_error(
"element 0 parsing failed");
218 if (!egsvec_approx_eq(mesh.
elements[1].n,
EGS_Vector(0.730297f, 0.632387f, 0.258365f)) ||
222 throw std::runtime_error(
"element 1 parsing failed");
224 if (!egsvec_approx_eq(mesh.
elements[2].n,
EGS_Vector(0.81508f, 0.547608f, 0.189131f)) ||
228 throw std::runtime_error(
"element 2 parsing failed");
234 namespace egs_triangle_mesh {
236 static void input_errors() {
243 std::string bad_stl_file(
244 ":start geometry definition:\n"
245 " :start geometry:\n"
247 " library = egs_triangle_mesh\n"
248 " #file = bad_input.stl\n"
250 " simulation geometry = my_mesh\n"
251 ":stop geometry definition:\n"
255 "createGeometry(EGS_TriangleMesh): no mesh file key `file` in input\n");
261 std::string bad_stl_file(
262 ":start geometry definition:\n"
263 " :start geometry:\n"
265 " library = egs_triangle_mesh\n"
266 " file = bad_input.stl\n"
268 " simulation geometry = my_mesh\n"
269 ":stop geometry definition:\n"
273 "\ncreateGeometry(EGS_TriangleMesh): STL file `bad_input.stl` does not exist or is not readable\n");
279 std::string bad_stl_scale(
280 ":start geometry definition:\n"
281 " :start geometry:\n"
283 " library = egs_triangle_mesh\n"
284 " file = sample_bin.stl\n"
287 " simulation geometry = my_mesh\n"
288 ":stop geometry definition:\n"
292 "createGeometry(EGS_TriangleMesh): invalid scale value (-1), expected a positive number\n");
299 static void zero_elements() {
301 EXPECT_ERROR(
EGS_TriangleMesh(std::move(spec)),
"empty triangles vector in EGS_TriangleMesh constructor");
305 static void check_scale() {
309 scaled_mesh.
scale(0.1);
311 for (
int i = 0; i < mesh.
elements.size(); i++) {
316 throw std::runtime_error(
"STL mesh scaling failed");
328 RUN_TEST(stl_parser::empty_file());
329 RUN_TEST(stl_parser::missing_file());
330 RUN_TEST(stl_parser::catch_zero_triangles());
331 RUN_TEST(stl_parser::truncated_file());
332 RUN_TEST(stl_parser::parse_binary_file());
333 RUN_TEST(stl_parser::parse_ascii_file());
335 RUN_TEST(egs_triangle_mesh::input_errors());
336 RUN_TEST(egs_triangle_mesh::zero_elements());
337 RUN_TEST(egs_triangle_mesh::check_scale());
339 std::cerr <<
"\ntest result: " << num_total - num_failed <<
" out of " <<
340 num_total <<
" tests passed\n";
static EGS_BaseGeometry * createGeometry(EGS_Input *)
Create a geometry (or geometries) from a given input.
A container for raw unstructured triangle surface mesh data.
void scale(EGS_Float factor)
Multiply all node coordinates by a constant factor.
std::vector< EGS_TriangleMeshSpec::Triangle > elements
Unique elements.
A triangular surface mesh geometry.
A class representing 3D vectors.
Triangle surface mesh geometry: header.
int main(int argc, char **argv)
A main program for egspp applications.
EGS_InfoFunction egsSetInfoFunction(EGS_InfoType t, EGS_InfoFunction func)
Set a function to be used for outputing information, warning messages or reporting fatal errors.
void egsSetDefaultIOFunctions()
Reset I/O functions to their defaults.