SDDS ToolKit Programs and Libraries for C and Python
All Classes Files Functions Variables Macros Pages
stl2sdds.c File Reference

Detailed Description

Converts STL (STereo-Lithography) files to SDDS format.

This program reads an STL (STereo-Lithography) file and converts it into the Self Describing Data Sets (SDDS) format. It supports both ASCII and binary STL files and can output in ASCII or binary SDDS formats. Additionally, it supports piping options for input and output.

Usage

stl2sdds [<inputFile>] [<outputFile>]
[-pipe[=in][,out]]
[-ascii | -binary]

Options

Optional Description
-pipe Enables piping for input and/or output using the SDDS toolkit pipe option.
-ascii Requests SDDS ASCII output. Default is binary.
-binary Requests SDDS binary output.

Incompatibilities

  • Only one of the following may be specified:
    • -ascii
    • -binary
License
This file is distributed under the terms of the Software License Agreement found in the file LICENSE included with this distribution.
Author
R. Soliday

Definition in file stl2sdds.c.

#include "mdb.h"
#include "SDDS.h"
#include "scan.h"

Go to the source code of this file.

Functions

float float_reverse_bytes (float x)
 
int leqi (char *string1, char *string2)
 
int main (int argc, char **argv)
 

Function Documentation

◆ float_reverse_bytes()

float float_reverse_bytes ( float x)

Definition at line 434 of file stl2sdds.c.

434 {
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}

◆ leqi()

int leqi ( char * string1,
char * string2 )

Definition at line 456 of file stl2sdds.c.

456 {
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}

◆ main()

int main ( int argc,
char ** argv )

Definition at line 77 of file stl2sdds.c.

77 {
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}
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