SDDS ToolKit Programs and Libraries for C and Python
All Classes Files Functions Variables Macros Pages
stl2sdds.c
Go to the documentation of this file.
1/**
2 * @file stl2sdds.c
3 * @brief Converts STL (STereo-Lithography) files to SDDS format.
4 *
5 * @details
6 * This program reads an STL (STereo-Lithography) file and converts it
7 * into the Self Describing Data Sets (SDDS) format. It supports both ASCII and binary
8 * STL files and can output in ASCII or binary SDDS formats. Additionally, it
9 * supports piping options for input and output.
10 *
11 * @section Usage
12 * ```
13 * stl2sdds [<inputFile>] [<outputFile>]
14 * [-pipe[=in][,out]]
15 * [-ascii | -binary]
16 * ```
17 *
18 * @section Options
19 * | Optional | Description |
20 * |---------------------------------------|---------------------------------------------------------------------------------------|
21 * | `-pipe` | Enables piping for input and/or output using the SDDS toolkit pipe option. |
22 * | `-ascii` | Requests SDDS ASCII output. Default is binary. |
23 * | `-binary` | Requests SDDS binary output. |
24 *
25 * @subsection Incompatibilities
26 * - Only one of the following may be specified:
27 * - `-ascii`
28 * - `-binary`
29 *
30 * @copyright
31 * - (c) 2002 The University of Chicago, as Operator of Argonne National Laboratory.
32 * - (c) 2002 The Regents of the University of California, as Operator of Los Alamos National Laboratory.
33 *
34 * @license
35 * This file is distributed under the terms of the Software License Agreement
36 * found in the file LICENSE included with this distribution.
37 *
38 * @author
39 * R. Soliday
40 */
41
42#include "mdb.h"
43#include "SDDS.h"
44#include "scan.h"
45
46#define LINE_MAX_LEN 256
47#define FALSE 0
48#define TRUE 1
49
50float float_reverse_bytes(float x);
51
52int leqi(char *string1, char *string2);
53
54/* Enumeration for option types */
55enum option_type {
56 SET_ASCII,
57 SET_BINARY,
58 SET_PIPE,
59 N_OPTIONS
60};
61
62char *option[N_OPTIONS] = {
63 "ascii", "binary", "pipe"};
64
65/* Improved and formatted usage message */
66char *USAGE =
67 "Usage: stl2sdds [<inputFile>] [<outputFile>] [-pipe[=in][,out]]\n"
68 " [-ascii | -binary]\n\n"
69 "Options:\n"
70 " -pipe[=in][,out] Enable piping for input and/or output using SDDS toolkit.\n"
71 " -ascii Output SDDS in ASCII format. Default is binary.\n"
72 " -binary Output SDDS in binary format.\n\n"
73 "Converts STL files to SDDS format.\n"
74 "Author: Robert Soliday.\n"
75 "Compiled on " __DATE__ " at " __TIME__ ", SVN revision: " SVN_VERSION "\n";
76
77int main(int argc, char **argv) {
78 char *inputFile = NULL, *outputFile = NULL;
79 SDDS_DATASET SDDSout;
80 SCANNED_ARG *scanned;
81 long iArg;
82 long ascii = 0;
83 unsigned long pipeFlags = 0;
84
85 int i, n = 0, solid = 0;
86 char init[7];
87 short attribute = 0;
88 int32_t face_num = 0, iface;
89 int32_t bigEndianMachine = 0;
90 int64_t bytes_num = 0, text_num = 0;
91 float *normalVector[3];
92 float *vertex1[3];
93 float *vertex2[3];
94 float *vertex3[3];
95 FILE *fd;
96 char input[LINE_MAX_LEN];
97 char *next;
98 char token[LINE_MAX_LEN];
99 int width;
100
102 argc = scanargs(&scanned, argc, argv);
103 if (argc < 2) {
104 fprintf(stderr, "%s", USAGE);
105 return EXIT_FAILURE;
106 }
107
108 for (iArg = 1; iArg < argc; iArg++) {
109 if (scanned[iArg].arg_type == OPTION) {
110 switch (match_string(scanned[iArg].list[0], option, N_OPTIONS, 0)) {
111 case SET_ASCII:
112 ascii = 1;
113 break;
114 case SET_BINARY:
115 ascii = 0;
116 break;
117 case SET_PIPE:
118 if (!processPipeOption(scanned[iArg].list + 1, scanned[iArg].n_items - 1, &pipeFlags)) {
119 fprintf(stderr, "Error: Invalid -pipe syntax.\n");
120 fprintf(stderr, "%s", USAGE);
121 return EXIT_FAILURE;
122 }
123 break;
124 default:
125 fprintf(stderr, "Error: Invalid option detected.\n%s", USAGE);
126 return EXIT_FAILURE;
127 }
128 } else {
129 if (!inputFile)
130 SDDS_CopyString(&inputFile, scanned[iArg].list[0]);
131 else if (!outputFile)
132 SDDS_CopyString(&outputFile, scanned[iArg].list[0]);
133 else {
134 fprintf(stderr, "Error: Too many filenames provided.\n%s", USAGE);
135 return EXIT_FAILURE;
136 }
137 }
138 }
139
140 processFilenames("stl2sdds", &inputFile, &outputFile, pipeFlags, 0, NULL);
141
142 if (inputFile) {
143 if (!fexists(inputFile)) {
144 fprintf(stderr, "Error: Input file '%s' not found.\n", inputFile);
145 return EXIT_FAILURE;
146 }
147 if (!(fd = fopen(inputFile, "rb"))) {
148 fprintf(stderr, "Error: Unable to open input file '%s'.\n", inputFile);
149 return EXIT_FAILURE;
150 }
151 } else {
152#if defined(_WIN32)
153 if (_setmode(_fileno(stdin), _O_BINARY) == -1) {
154 fprintf(stderr, "Error: Unable to set stdin to binary mode.\n");
155 return EXIT_FAILURE;
156 }
157#endif
158 fd = stdin;
159 }
160
161 bigEndianMachine = SDDS_IsBigEndianMachine();
162
163 /* Check for ASCII or BINARY STL type */
164 bytes_num += fread(&init, 1, 6, fd);
165 init[6] = '\0';
166 int stlascii = (strcasecmp("solid ", init) == 0) ? TRUE : FALSE;
167
168 if (!stlascii) {
169 /* 80 byte Header. (74 bytes remaining) */
170 for (i = 0; i < 74; i++) {
171 if (fgetc(fd) == EOF)
172 break;
173 bytes_num++;
174 }
175
176 /* Number of faces */
177 bytes_num += fread(&face_num, 1, sizeof(int32_t), fd);
178 if (bigEndianMachine) {
179 SDDS_SwapLong((int32_t *)&face_num);
180 }
181
182 for (i = 0; i < 3; i++) {
183 normalVector[i] = malloc(sizeof(*(normalVector[i])) * face_num);
184 vertex1[i] = malloc(sizeof(*(vertex1[i])) * face_num);
185 vertex2[i] = malloc(sizeof(*(vertex2[i])) * face_num);
186 vertex3[i] = malloc(sizeof(*(vertex3[i])) * face_num);
187 if (!normalVector[i] || !vertex1[i] || !vertex2[i] || !vertex3[i]) {
188 fprintf(stderr, "Error: Memory allocation failed.\n");
189 return EXIT_FAILURE;
190 }
191 }
192
193 /* Read each face's data */
194 for (iface = 0; iface < face_num; iface++) {
195 for (i = 0; i < 3; i++) {
196 if (fread(&(normalVector[i][iface]), 1, sizeof(float), fd) != sizeof(float)) {
197 fprintf(stderr, "Error: Unexpected end of file while reading normals.\n");
198 return EXIT_FAILURE;
199 }
200 bytes_num += sizeof(float);
201 if (bigEndianMachine) {
202 normalVector[i][iface] = float_reverse_bytes(normalVector[i][iface]);
203 }
204 }
205 for (i = 0; i < 3; i++) {
206 if (fread(&(vertex1[i][iface]), 1, sizeof(float), fd) != sizeof(float)) {
207 fprintf(stderr, "Error: Unexpected end of file while reading vertex1.\n");
208 return EXIT_FAILURE;
209 }
210 bytes_num += sizeof(float);
211 if (bigEndianMachine) {
212 vertex1[i][iface] = float_reverse_bytes(vertex1[i][iface]);
213 }
214 }
215 for (i = 0; i < 3; i++) {
216 if (fread(&(vertex2[i][iface]), 1, sizeof(float), fd) != sizeof(float)) {
217 fprintf(stderr, "Error: Unexpected end of file while reading vertex2.\n");
218 return EXIT_FAILURE;
219 }
220 bytes_num += sizeof(float);
221 if (bigEndianMachine) {
222 vertex2[i][iface] = float_reverse_bytes(vertex2[i][iface]);
223 }
224 }
225 for (i = 0; i < 3; i++) {
226 if (fread(&(vertex3[i][iface]), 1, sizeof(float), fd) != sizeof(float)) {
227 fprintf(stderr, "Error: Unexpected end of file while reading vertex3.\n");
228 return EXIT_FAILURE;
229 }
230 bytes_num += sizeof(float);
231 if (bigEndianMachine) {
232 vertex3[i][iface] = float_reverse_bytes(vertex3[i][iface]);
233 }
234 }
235 if (fread(&(attribute), 1, sizeof(short int), fd) != sizeof(short int)) {
236 fprintf(stderr, "Error: Unexpected end of file while reading attribute.\n");
237 return EXIT_FAILURE;
238 }
239 bytes_num += sizeof(short int);
240 }
241 } else {
242 for (i = 0; i < 3; i++) {
243 normalVector[i] = NULL;
244 vertex1[i] = NULL;
245 vertex2[i] = NULL;
246 vertex3[i] = NULL;
247 }
248 fseek(fd, 0, SEEK_SET);
249 while (fgets(input, LINE_MAX_LEN, fd) != NULL) {
250 text_num++;
251 for (next = input; *next != '\0' && isspace((unsigned char)*next); next++) {
252 }
253 if (*next == '\0' || *next == '#' || *next == '!' || *next == '$') {
254 continue;
255 }
256 sscanf(next, "%s%n", token, &width);
257 next += width;
258
259 if (leqi(token, "facet")) {
260 if (n == 0) {
261 n++;
262 for (i = 0; i < 3; i++) {
263 normalVector[i] = malloc(sizeof(*(normalVector[i])) * n);
264 vertex1[i] = malloc(sizeof(*(vertex1[i])) * n);
265 vertex2[i] = malloc(sizeof(*(vertex2[i])) * n);
266 vertex3[i] = malloc(sizeof(*(vertex3[i])) * n);
267 if (!normalVector[i] || !vertex1[i] || !vertex2[i] || !vertex3[i]) {
268 fprintf(stderr, "Error: Memory allocation failed.\n");
269 return EXIT_FAILURE;
270 }
271 }
272 } else {
273 n++;
274 for (i = 0; i < 3; i++) {
275 normalVector[i] = realloc(normalVector[i], sizeof(*(normalVector[i])) * n);
276 vertex1[i] = realloc(vertex1[i], sizeof(*(vertex1[i])) * n);
277 vertex2[i] = realloc(vertex2[i], sizeof(*(vertex2[i])) * n);
278 vertex3[i] = realloc(vertex3[i], sizeof(*(vertex3[i])) * n);
279 if (!normalVector[i] || !vertex1[i] || !vertex2[i] || !vertex3[i]) {
280 fprintf(stderr, "Error: Memory allocation failed.\n");
281 return EXIT_FAILURE;
282 }
283 }
284 }
285
286 /* Read normal vector */
287 sscanf(next, "%*s %e %e %e",
288 &(normalVector[0][n - 1]),
289 &(normalVector[1][n - 1]),
290 &(normalVector[2][n - 1]));
291
292 /* Skip "outer loop" */
293 if (!fgets(input, LINE_MAX_LEN, fd) || !fgets(input, LINE_MAX_LEN, fd)) {
294 fprintf(stderr, "Error: Unexpected end of file while reading outer loop.\n");
295 return EXIT_FAILURE;
296 }
297 text_num += 2;
298
299 /* Read vertex1 */
300 sscanf(next, "%*s %e %e %e",
301 &(vertex1[0][n - 1]),
302 &(vertex1[1][n - 1]),
303 &(vertex1[2][n - 1]));
304
305 /* Skip "vertex" */
306 if (!fgets(input, LINE_MAX_LEN, fd)) {
307 fprintf(stderr, "Error: Unexpected end of file while reading vertex2.\n");
308 return EXIT_FAILURE;
309 }
310 text_num++;
311
312 /* Read vertex2 */
313 sscanf(next, "%*s %e %e %e",
314 &(vertex2[0][n - 1]),
315 &(vertex2[1][n - 1]),
316 &(vertex2[2][n - 1]));
317
318 /* Skip "vertex" */
319 if (!fgets(input, LINE_MAX_LEN, fd)) {
320 fprintf(stderr, "Error: Unexpected end of file while reading vertex3.\n");
321 return EXIT_FAILURE;
322 }
323 text_num++;
324
325 /* Read vertex3 */
326 sscanf(next, "%*s %e %e %e",
327 &(vertex3[0][n - 1]),
328 &(vertex3[1][n - 1]),
329 &(vertex3[2][n - 1]));
330
331 /* Skip "endloop" and "endfacet" */
332 if (!fgets(input, LINE_MAX_LEN, fd) || !fgets(input, LINE_MAX_LEN, fd)) {
333 fprintf(stderr, "Error: Unexpected end of file while reading endloop/endfacet.\n");
334 return EXIT_FAILURE;
335 }
336 text_num += 2;
337 } else if (leqi(token, "color")) {
338 fprintf(stderr, "Warning: Color field seen in STL file ignored.\n");
339 } else if (leqi(token, "solid")) {
340 solid++;
341 if (solid > 1) {
342 fprintf(stderr, "Error: More than one solid field seen in STL file.\n");
343 return EXIT_FAILURE;
344 }
345 } else if (leqi(token, "endsolid")) {
346 /* Do nothing */
347 } else {
348 fprintf(stderr, "Error: Unrecognized keyword '%s' in STL file.\n", token);
349 return EXIT_FAILURE;
350 }
351 }
352 face_num = n;
353 }
354
355 /* Initialize SDDS output */
356 if (!SDDS_InitializeOutput(&SDDSout, ascii ? SDDS_ASCII : SDDS_BINARY, 1, NULL, NULL, outputFile)) {
357 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
358 return EXIT_FAILURE;
359 }
360
361 if (!ascii) {
362 SDDSout.layout.data_mode.column_major = 1;
363 }
364
365 /* Define SDDS columns */
366 const char *columns[] = {
367 "NormalVectorX", "NormalVectorY", "NormalVectorZ",
368 "Vertex1X", "Vertex1Y", "Vertex1Z",
369 "Vertex2X", "Vertex2Y", "Vertex2Z",
370 "Vertex3X", "Vertex3Y", "Vertex3Z"};
371 for (i = 0; i < 12; i++) {
372 if (!SDDS_DefineSimpleColumn(&SDDSout, columns[i], NULL, SDDS_FLOAT)) {
373 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
374 return EXIT_FAILURE;
375 }
376 }
377
378 /* Write SDDS layout */
379 if (!SDDS_WriteLayout(&SDDSout)) {
380 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
381 return EXIT_FAILURE;
382 }
383
384 /* Start SDDS table */
385 if (!SDDS_StartTable(&SDDSout, face_num)) {
386 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
387 return EXIT_FAILURE;
388 }
389
390 /* Set SDDS columns from data */
391 const char *column_names[] = {
392 "NormalVectorX", "NormalVectorY", "NormalVectorZ",
393 "Vertex1X", "Vertex1Y", "Vertex1Z",
394 "Vertex2X", "Vertex2Y", "Vertex2Z",
395 "Vertex3X", "Vertex3Y", "Vertex3Z"};
396 float *data_columns[12] = {
397 normalVector[0], normalVector[1], normalVector[2],
398 vertex1[0], vertex1[1], vertex1[2],
399 vertex2[0], vertex2[1], vertex2[2],
400 vertex3[0], vertex3[1], vertex3[2]};
401
402 for (i = 0; i < 12; i++) {
403 if (!SDDS_SetColumnFromFloats(&SDDSout, SDDS_SET_BY_NAME, data_columns[i], face_num, column_names[i])) {
404 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
405 return EXIT_FAILURE;
406 }
407 }
408
409 /* Write SDDS table */
410 if (!SDDS_WriteTable(&SDDSout)) {
411 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
412 free_scanargs(&scanned, argc);
413 return EXIT_FAILURE;
414 }
415
416 /* Terminate SDDS output */
417 if (!SDDS_Terminate(&SDDSout)) {
418 SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors);
419 return EXIT_FAILURE;
420 }
421
422 /* Clean up */
423 free_scanargs(&scanned, argc);
424 for (i = 0; i < 3; i++) {
425 free(normalVector[i]);
426 free(vertex1[i]);
427 free(vertex2[i]);
428 free(vertex3[i]);
429 }
430
431 return EXIT_SUCCESS;
432}
433
434float float_reverse_bytes(float x) {
435 char c;
436 union {
437 float yfloat;
438 char ychar[4];
439 } y;
440
441 y.yfloat = x;
442
443 /* Swap bytes 0 and 3 */
444 c = y.ychar[0];
445 y.ychar[0] = y.ychar[3];
446 y.ychar[3] = c;
447
448 /* Swap bytes 1 and 2 */
449 c = y.ychar[1];
450 y.ychar[1] = y.ychar[2];
451 y.ychar[2] = c;
452
453 return y.yfloat;
454}
455
456int leqi(char *string1, char *string2) {
457 int i;
458 int nchar;
459 int nchar1 = strlen(string1);
460 int nchar2 = strlen(string2);
461
462 nchar = (nchar1 < nchar2) ? nchar1 : nchar2;
463
464 /* Compare characters case-insensitively */
465 for (i = 0; i < nchar; i++) {
466 if (toupper((unsigned char)string1[i]) != toupper((unsigned char)string2[i])) {
467 return FALSE;
468 }
469 }
470
471 /* Check for trailing spaces in the longer string */
472 if (nchar1 > nchar) {
473 for (i = nchar; i < nchar1; i++) {
474 if (string1[i] != ' ') {
475 return FALSE;
476 }
477 }
478 } else if (nchar2 > nchar) {
479 for (i = nchar; i < nchar2; i++) {
480 if (string2[i] != ' ') {
481 return FALSE;
482 }
483 }
484 }
485
486 return TRUE;
487}
SDDS (Self Describing Data Set) Data Types Definitions and Function Prototypes.
void SDDS_SwapLong(int32_t *data)
Swaps the endianness of a 32-bit integer.
int32_t SDDS_SetColumnFromFloats(SDDS_DATASET *SDDS_dataset, int32_t mode, float *data, int64_t rows,...)
Sets the values for a single data column using single-precision floating-point numbers.
int32_t SDDS_Terminate(SDDS_DATASET *SDDS_dataset)
int32_t SDDS_InitializeOutput(SDDS_DATASET *SDDS_dataset, int32_t data_mode, int32_t lines_per_row, const char *description, const char *contents, const char *filename)
Initializes the SDDS output dataset.
int32_t SDDS_DefineSimpleColumn(SDDS_DATASET *SDDS_dataset, const char *name, const char *unit, int32_t type)
Defines a simple data column within the SDDS dataset.
int32_t SDDS_WriteLayout(SDDS_DATASET *SDDS_dataset)
Writes the SDDS layout header to the output file.
void SDDS_PrintErrors(FILE *fp, int32_t mode)
Prints recorded error messages to a specified file stream.
Definition SDDS_utils.c:432
void SDDS_RegisterProgramName(const char *name)
Registers the executable program name for use in error messages.
Definition SDDS_utils.c:288
int32_t SDDS_CopyString(char **target, const char *source)
Copies a source string to a target string with memory allocation.
Definition SDDS_utils.c:856
int32_t SDDS_IsBigEndianMachine()
Determines whether the current machine uses big-endian byte ordering.
#define SDDS_FLOAT
Identifier for the float data type.
Definition SDDStypes.h:43
long fexists(const char *filename)
Checks if a file exists.
Definition fexists.c:27
long match_string(char *string, char **option, long n_options, long mode)
Matches a given string against an array of option strings based on specified modes.
int scanargs(SCANNED_ARG **scanned, int argc, char **argv)
Definition scanargs.c:36
long processPipeOption(char **item, long items, unsigned long *flags)
Definition scanargs.c:356
void processFilenames(char *programName, char **input, char **output, unsigned long pipeFlags, long noWarnings, long *tmpOutputUsed)
Definition scanargs.c:390
void free_scanargs(SCANNED_ARG **scanned, int argc)
Definition scanargs.c:584