test_sstl_writer.c (11770B)
1 /* Copyright (C) 2015, 2016, 2019, 2021, 2023, 2025 |Méso|Star> (contact@meso-star.com) 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation, either version 3 of the License, or 6 * (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU Lesser General Public License for more details. 12 * 13 * You should have received a copy of the GNU Lesser General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 15 16 #define _POSIX_C_SOURCE 200112L /* fork */ 17 18 #include "sstl.h" 19 20 #include <rsys/float3.h> 21 #include <rsys/logger.h> 22 #include <rsys/mem_allocator.h> 23 24 #include <string.h> 25 #include <unistd.h> /* fork, pipe */ 26 27 /******************************************************************************* 28 * Helper functions 29 ******************************************************************************/ 30 static void 31 log_stream(const char* msg, void* ctx) 32 { 33 ASSERT(msg); 34 (void)msg, (void)ctx; 35 printf("logger %s", msg); 36 } 37 38 static void 39 check_api(const enum sstl_type type) 40 { 41 struct logger logger; 42 struct mem_allocator allocator; 43 struct sstl_writer_create_args args = SSTL_WRITER_CREATE_ARGS_DEFAULT; 44 struct sstl_writer* writer = NULL; 45 const char* filename = "test.stl"; 46 47 CHK(sstl_writer_create(NULL, &writer) == RES_BAD_ARG); 48 CHK(sstl_writer_create(&args, NULL) == RES_BAD_ARG); 49 CHK(sstl_writer_create(&args, &writer) == RES_BAD_ARG); 50 51 args.filename = filename; 52 args.type = type; 53 args.verbose = 3; 54 CHK(sstl_writer_create(&args, &writer) == RES_OK); 55 56 CHK(sstl_write_facet(NULL, &SSTL_FACET_NULL) == RES_BAD_ARG); 57 CHK(sstl_write_facet(writer, NULL) == RES_BAD_ARG); 58 CHK(sstl_write_facet(writer, &SSTL_FACET_NULL) == RES_OK); 59 60 CHK(sstl_writer_ref_get(NULL) == RES_BAD_ARG); 61 CHK(sstl_writer_ref_get(writer) == RES_OK); 62 CHK(sstl_writer_ref_put(NULL) == RES_BAD_ARG); 63 CHK(sstl_writer_ref_put(writer) == RES_OK); 64 CHK(sstl_writer_ref_put(writer) == RES_OK); 65 66 args.triangles_count = 0; 67 CHK(sstl_writer_create(&args, &writer) == RES_OK); 68 CHK(sstl_write_facet(writer, &SSTL_FACET_NULL) == RES_BAD_OP); 69 CHK(sstl_writer_ref_put(writer) == RES_OK); 70 71 mem_init_proxy_allocator(&allocator, &mem_default_allocator); 72 CHK(logger_init(&allocator, &logger) == RES_OK); 73 logger_set_stream(&logger, LOG_OUTPUT, log_stream, NULL); 74 logger_set_stream(&logger, LOG_ERROR, log_stream, NULL); 75 logger_set_stream(&logger, LOG_WARNING, log_stream, NULL); 76 77 args.allocator = &allocator; 78 args.logger = &logger; 79 args.triangles_count = 1; 80 CHK(sstl_writer_create(&args, &writer) == RES_OK); 81 CHK(sstl_write_facet(writer, &SSTL_FACET_NULL) == RES_OK); 82 CHK(sstl_write_facet(writer, &SSTL_FACET_NULL) == RES_BAD_OP); 83 CHK(sstl_writer_ref_put(writer) == RES_OK); 84 85 logger_release(&logger); 86 CHK(MEM_ALLOCATED_SIZE(&allocator) == 0); 87 mem_shutdown_proxy_allocator(&allocator); 88 } 89 90 static void 91 check_1_triangle(struct sstl* sstl, const enum sstl_type type) 92 { 93 struct sstl_desc desc = SSTL_DESC_NULL; 94 struct sstl_facet facet = SSTL_FACET_NULL; 95 struct sstl_writer_create_args args = SSTL_WRITER_CREATE_ARGS_DEFAULT; 96 struct sstl_writer* writer = NULL; 97 98 float v[3] = {0,0,0}; 99 const char* filename = "1_triangle.stl"; 100 101 args.filename = filename; 102 args.type = type; 103 args.verbose = 3; 104 CHK(sstl_writer_create(&args, &writer) == RES_OK); 105 106 f3_splat(facet.normal, 0); /* Let sstl calculate it automatically */ 107 f3(facet.vertices[0], 0.f, 0.f, 0.f); 108 f3(facet.vertices[1], 1.f, 0.f, 0.f); 109 f3(facet.vertices[2], 0.f, 0.f, 1.f); 110 CHK(sstl_write_facet(writer, &facet) == RES_OK); 111 112 CHK(sstl_writer_ref_put(writer) == RES_OK); 113 114 CHK(sstl_load(sstl, filename) == RES_OK); 115 CHK(sstl_get_desc(sstl, &desc) == RES_OK); 116 117 CHK(desc.type == type); 118 CHK(!strcmp(desc.filename, filename)); 119 CHK(desc.solid_name == NULL); 120 CHK(desc.vertices_count == 3); 121 CHK(desc.triangles_count == 1); 122 CHK(desc.indices[0] == 0); 123 CHK(desc.indices[1] == 1); 124 CHK(desc.indices[2] == 2); 125 CHK(f3_eq(desc.vertices + 0*3, f3(v, 0.f, 0.f, 0.f)) == 1); 126 CHK(f3_eq(desc.vertices + 1*3, f3(v, 1.f, 0.f, 0.f)) == 1); 127 CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 1.f)) == 1); 128 CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1); 129 130 /* The triangle is missing. A warning is signalled but no error is returned: 131 * author finalization is assumed to always succeed. Partial finalization 132 * corrupts the output, and it would be too hasardous to try returning to the 133 * previous state. Finally, detecting a finalization error prevents the writer 134 * from being released, adding a memory leak to the finalization error, which 135 * could be impossible to resolve. */ 136 args.filename = filename; 137 args.type = SSTL_BINARY; 138 args.triangles_count = 1; 139 args.verbose = 3; 140 CHK(sstl_writer_create(&args, &writer) == RES_OK); 141 CHK(sstl_writer_ref_put(writer) == RES_OK); 142 } 143 144 static void 145 check_tetrahedron(struct sstl* sstl, const enum sstl_type type) 146 { 147 struct sstl_desc desc = SSTL_DESC_NULL; 148 struct sstl_facet facet = SSTL_FACET_NULL; 149 struct sstl_writer_create_args args = SSTL_WRITER_CREATE_ARGS_DEFAULT; 150 struct sstl_writer* writer = NULL; 151 152 float v[3] = {0,0,0}; 153 FILE* fp = NULL; 154 const char* filename = "Tetrahedron"; 155 156 CHK((fp = tmpfile()) != NULL); 157 args.filename = filename; 158 args.stream = fp; 159 args.type = type; 160 args.solid_name = "cube corner"; 161 args.verbose = 3; 162 CHK(sstl_writer_create(&args, &writer) == RES_OK); 163 164 f3(facet.normal, 0.f,-1.f, 0.f); 165 f3(facet.vertices[0], 0.0f, 0.0f, 0.0f); 166 f3(facet.vertices[1], 0.1f, 0.0f, 0.0f); 167 f3(facet.vertices[2], 0.0f, 0.0f, 0.1f); 168 CHK(sstl_write_facet(writer, &facet) == RES_OK); 169 170 f3(facet.normal, 0.f, 0.f, -1.f); 171 f3(facet.vertices[0], 0.0f, 0.0f, 0.0f); 172 f3(facet.vertices[1], 0.0f, 0.1f, 0.0f); 173 f3(facet.vertices[2], 0.1f, 0.0f, 0.0f); 174 CHK(sstl_write_facet(writer, &facet) == RES_OK); 175 176 f3(facet.normal,-1.f, 0.f, 0.f); 177 f3(facet.vertices[0], 0.0f, 0.0f, 0.0f); 178 f3(facet.vertices[1], 0.0f, 0.0f, 0.1f); 179 f3(facet.vertices[2], 0.0f, 0.1f, 0.0f); 180 CHK(sstl_write_facet(writer, &facet) == RES_OK); 181 182 f3(facet.normal, 0.577f, 0.577f, 0.577f); 183 f3(facet.vertices[0], 0.1f, 0.0f, 0.0f); 184 f3(facet.vertices[1], 0.0f, 0.1f, 0.0f); 185 f3(facet.vertices[2], 0.0f, 0.0f, 0.1f); 186 CHK(sstl_write_facet(writer, &facet) == RES_OK); 187 188 CHK(sstl_writer_ref_put(writer) == RES_OK); 189 190 rewind(fp); 191 CHK(sstl_load_stream(sstl, fp, filename) == RES_OK); 192 CHK(fclose(fp) == 0); 193 194 CHK(sstl_get_desc(sstl, &desc) == RES_OK); 195 CHK(!strcmp(desc.filename, filename)); 196 CHK(type == SSTL_BINARY || !strcmp(desc.solid_name, "cube corner")); 197 CHK(desc.type == type); 198 CHK(desc.vertices_count == 4); 199 CHK(desc.triangles_count == 4); 200 201 CHK(f3_eq(desc.vertices + desc.indices[0]*3, f3(v, 0.0f, 0.0f, 0.0f)) == 1); 202 CHK(f3_eq(desc.vertices + desc.indices[1]*3, f3(v, 0.1f, 0.0f, 0.0f)) == 1); 203 CHK(f3_eq(desc.vertices + desc.indices[2]*3, f3(v, 0.0f, 0.0f, 0.1f)) == 1); 204 CHK(f3_eq(desc.vertices + desc.indices[3]*3, f3(v, 0.0f, 0.0f, 0.0f)) == 1); 205 CHK(f3_eq(desc.vertices + desc.indices[4]*3, f3(v, 0.0f, 0.1f, 0.0f)) == 1); 206 CHK(f3_eq(desc.vertices + desc.indices[5]*3, f3(v, 0.1f, 0.0f, 0.0f)) == 1); 207 CHK(f3_eq(desc.vertices + desc.indices[6]*3, f3(v, 0.0f, 0.0f, 0.0f)) == 1); 208 CHK(f3_eq(desc.vertices + desc.indices[7]*3, f3(v, 0.0f, 0.0f, 0.1f)) == 1); 209 CHK(f3_eq(desc.vertices + desc.indices[8]*3, f3(v, 0.0f, 0.1f, 0.0f)) == 1); 210 CHK(f3_eq(desc.vertices + desc.indices[9]*3, f3(v, 0.1f, 0.0f, 0.0f)) == 1); 211 CHK(f3_eq(desc.vertices + desc.indices[10]*3, f3(v, 0.0f, 0.1f, 0.0f)) == 1); 212 CHK(f3_eq(desc.vertices + desc.indices[11]*3, f3(v, 0.0f, 0.0f, 0.1f)) == 1); 213 214 CHK(f3_eq(desc.normals + 0*3, f3(v, 0.f,-1.f, 0.f)) == 1); 215 CHK(f3_eq(desc.normals + 1*3, f3(v, 0.f, 0.f,-1.f)) == 1); 216 CHK(f3_eq(desc.normals + 2*3, f3(v,-1.f, 0.f, 0.f)) == 1); 217 f3_normalize(v, f3(v, 1.f, 1.f, 1.f)); 218 CHK(f3_eq_eps(desc.normals + 3*3, v, 1.e-6f) == 1); 219 } 220 221 static void 222 process_write(const enum sstl_type type, FILE* fp, const char* filename) 223 { 224 struct sstl_writer_create_args args = SSTL_WRITER_CREATE_ARGS_DEFAULT; 225 struct sstl_facet facet = SSTL_FACET_NULL; 226 struct sstl_writer* writer = NULL; 227 228 args.solid_name = "Triangle"; 229 args.filename = filename; 230 args.stream = fp; 231 args.type = type; 232 args.verbose = 3; 233 234 switch(type) { 235 case SSTL_ASCII: 236 CHK(sstl_writer_create(&args, &writer) == RES_OK); 237 break; 238 239 case SSTL_BINARY: 240 /* Binary StL can be written only if the number of triangles is known in 241 * advance */ 242 CHK(sstl_writer_create(&args, &writer) == RES_BAD_ARG); 243 args.triangles_count = 1; 244 CHK(sstl_writer_create(&args, &writer) == RES_OK); 245 break; 246 247 default: FATAL("Unreachable code\n"); break; 248 } 249 250 f3(facet.normal, 0.f,-1.f, 0.f); 251 f3(facet.vertices[0], 1.f, 0.f, 0.f); 252 f3(facet.vertices[1], 0.f, 0.f, 1.f); 253 f3(facet.vertices[2], 0.f, 0.f, 0.f); 254 CHK(sstl_write_facet(writer, &facet) == RES_OK); 255 256 CHK(sstl_writer_ref_put(writer) == RES_OK); 257 } 258 259 static void 260 process_read 261 (struct sstl* sstl, 262 const enum sstl_type type, 263 FILE* fp, 264 const char* filename) 265 { 266 struct sstl_desc desc = SSTL_DESC_NULL; 267 float v[3]; 268 269 switch(type) { 270 case SSTL_ASCII: 271 CHK(sstl_load_stream_ascii(sstl, fp, filename) == RES_OK); 272 break; 273 case SSTL_BINARY: 274 CHK(sstl_load_stream_binary(sstl, fp, filename) == RES_OK); 275 break; 276 default: FATAL("Unreachable code\n"); break; 277 } 278 279 CHK(sstl_get_desc(sstl, &desc) == RES_OK); 280 CHK(desc.type == type); 281 CHK(!strcmp(desc.filename, filename)); 282 CHK(type == SSTL_ASCII || desc.solid_name == NULL); 283 CHK(type == SSTL_BINARY || !strcmp(desc.solid_name, "Triangle")); 284 CHK(desc.vertices_count == 3); 285 CHK(desc.triangles_count == 1); 286 CHK(desc.indices[0] == 0); 287 CHK(desc.indices[1] == 1); 288 CHK(desc.indices[2] == 2); 289 CHK(f3_eq(desc.vertices + 0*3, f3(v, 1.f, 0.f, 0.f)) == 1); 290 CHK(f3_eq(desc.vertices + 1*3, f3(v, 0.f, 0.f, 1.f)) == 1); 291 CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 0.f)) == 1); 292 CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1); 293 } 294 295 static void 296 check_no_seekable_file(struct sstl* sstl, const enum sstl_type type) 297 { 298 int fd[2] = {0,0}; 299 FILE* fp = NULL; 300 const char* filename = "Pipe"; 301 pid_t pid = 0; 302 303 CHK(pipe(fd) == 0); 304 CHK((pid = fork()) != -1); 305 306 if(pid == 0) { /* Child process */ 307 CHK(close(fd[0]) == 0); /* Close the unused input stream */ 308 CHK(sstl_ref_put(sstl) == RES_OK); /* Release the unused sstl */ 309 CHK((fp = fdopen(fd[1], "w")) != NULL); 310 process_write(type, fp, filename); 311 CHK(fclose(fp) == 0); 312 exit(0); 313 314 } else { /* Parent process */ 315 CHK(close(fd[1]) == 0); /* Close the unused output stream */ 316 CHK(fp = fdopen(fd[0], "r")); 317 process_read(sstl, type, fp, filename); 318 CHK(fclose(fp) == 0); 319 } 320 } 321 322 /******************************************************************************* 323 * The test 324 ******************************************************************************/ 325 int 326 main(int argc, char** argv) 327 { 328 struct sstl* sstl = NULL; 329 (void)argc, (void)argv; 330 331 CHK(sstl_create(NULL, NULL, 3, &sstl) == RES_OK); 332 333 check_api(SSTL_ASCII); 334 check_api(SSTL_BINARY); 335 check_1_triangle(sstl, SSTL_ASCII); 336 check_1_triangle(sstl, SSTL_BINARY); 337 check_tetrahedron(sstl, SSTL_ASCII); 338 check_tetrahedron(sstl, SSTL_BINARY); 339 check_no_seekable_file(sstl, SSTL_ASCII); 340 check_no_seekable_file(sstl, SSTL_BINARY); 341 342 CHK(sstl_ref_put(sstl) == RES_OK); 343 CHK(mem_allocated_size() == 0); 344 return 0; 345 }