2 ** Support functions for writing a combined (grid and results) file
3 ** in the binary FIELDVIEW unstructured format.
6 /* Include system stuff for I/O and string and exit functions. */
11 /* Include the defines for the FV_* codes and wall_info values. */
12 #include "fv_reader_tags.h"
15 /* Don't change these - used by fv_encode_elem_header ! */
16 #define MAX_NUM_ELEM_FACES 6
17 #define BITS_PER_WALL 3
18 #define ELEM_TYPE_BIT_SHIFT (MAX_NUM_ELEM_FACES*BITS_PER_WALL)
22 ** fv_encode_elem_header: return an encoded binary element header
25 ** elem_type: integer element type as shown in fv_reader_tags.h
26 ** wall_info: array of integer "wall" flags, one for each face of
27 ** the element. The wall flags are used during streamline
28 ** calculation. Currently, the only meaningful values are
29 ** A_WALL and NOT_A_WALL as shown in fv_reader_tags.h.
30 ** Streamlines are forced away from faces marked as
31 ** "A_WALL", by limiting velocity and position very near
34 ** Function return value is the encoded binary element header.
38 unsigned int fv_encode_elem_header (int elem_type
, int wall_info
[])
40 unsigned int fv_encode_elem_header (elem_type
, wall_info
)
51 header
= (1 << ELEM_TYPE_BIT_SHIFT
);
55 header
= (4 << ELEM_TYPE_BIT_SHIFT
);
58 case FV_PRISM_ELEM_ID
:
59 header
= (3 << ELEM_TYPE_BIT_SHIFT
);
63 header
= (2 << ELEM_TYPE_BIT_SHIFT
);
67 fprintf(stderr
, "ERROR: Unknown element type\n");
71 for (i
= 0; i
< nfaces
; i
++)
73 unsigned int u
= wall_info
[i
];
76 fprintf(stderr
, "ERROR: Bad wall value\n");
79 header
|= (u
<< (i
*BITS_PER_WALL
));
85 ** fwrite_str80: write out a string padded to 80 characters.
87 ** Like fwrite, this returns the number of items written, which
88 ** should be 80 if successful, and less than 80 if it failed.
91 size_t fwrite_str80 (char *str
, FILE *fp
)
93 int fwrite_str80 (str
, fp
)
102 /* Most of this just to avoid garbage after the name. */
104 strncpy(cbuf
, str
, len
< 80 ? len
: 80);
106 for (i
= len
; i
< 80; i
++)
107 cbuf
[i
] = '\0'; /* pad with zeros */
109 return fwrite(cbuf
, sizeof(char), 80, fp
);
114 ** Sample program which writes out a single unstructured grid containing
115 ** four hex elements, with pressure and velocity data at the nodes, and
116 ** surface data for temperature and velocity on some of the boundaries.
118 ** The data is written as a combined (grid and results) file in the
119 ** binary FIELDVIEW unstructured format.
121 ** For simplicity, no error checking is done on the calls to fwrite
124 #if 0 /***** CHANGE THIS TO "#if 1" TO RUN THE SAMPLE PROGRAM. *****/
127 char *file_name
= "quad_hex.uns";
130 int num_face_types
= 5;
132 ** Note that one of the boundary type names is "wall."
133 ** The boundary name "wall" has no special meaning in FIELDVIEW.
134 ** Boundary types and element walls are independent pieces of
135 ** information. The only way to mark an element face as a wall
136 ** (for streamline calculation) is with the wall_info array passed
137 ** to fv_encode_elem_header.
139 static char *face_type_names
[5] = { "bottom", "top", "wall",
140 "trimmed cell", "hanging node cell"};
142 ** Each boundary type is flagged with 1 or 0 depending on
143 ** whether surface results are present or absent (see below).
145 static int results_flag
[5] = { 1, 1, 0, 1, 1 };
147 ** Each boundary type is flagged with 1 or 0 depending on
148 ** whether surface normals can be calculated from a "right
149 ** hand rule" (see below).
151 static int normals_flag
[5] = { 1, 1, 0, 1, 1 };
154 ** Note that vector variables are specified by a ';' and vector name
155 ** following the first scalar name of 3 scalar components of the
156 ** vector. If writing 2-D results, the third component must still
157 ** be provided here, and its values must be written in the variables
158 ** section below (typically padded with zeros.)
161 static char *var_names
[4] = { "pressure", "uvel; velocity", "vvel", "wvel" };
163 static char *bvar_names
[4] = { "temperature", "uvel; velocity", "vvel", "wvel" };
165 unsigned int elem_header
;
169 int nnodes
= 31; /* Number of nodes in the grid. */
170 const int num_faces_trim_cell
= 7;
171 const int num_faces_hang_cell
= 6;
174 static float consts
[4] = { 1., 0., 0., 0. };
176 /* XYZ coordinates of the nodes. */
177 static float x
[31] = {-1., -1., 1., 1., -1., -1., 1., 1., -1., -1., 1.,1., 2., 2., 3., 3., 2.5, 3., 2., 3., 3., 2., 2.5,
178 3., 3., 3., 2.5, 2., 2., 2.0, 2.5};
180 static float y
[31] = {-1., -1., -1., -1., 1., 1., 1., 1., 3., 3., 3., 3.,
181 0., 0., 0., 0., 0., .5, 1., 1., 1., 1., .5,
182 2., 2., 1.5, 2., 2., 2., 1.45, 1.5};
184 static float z
[31] = {-1., 1., -1., 1., -1., 1., -1., 1., -1., 1., -1.,1., 1., 0., 0., .5, 1., 1., 1., 1., 0., 0., .5,
185 0., 1., 1., 1., 1., 0., 1., 1.};
187 /* hex1 and hex2 are hex elements, defined as an array of node numbers. */
188 static int hex1
[8] = {1,2,3,4,5,6,7,8};
189 static int hex2
[8] = {5,6,7,8,9,10,11,12};
192 ** Face definitions for boundary faces.
193 ** All faces have 4 vertices. If the face is triangular,
194 ** the last vertex should be zero.
196 static int bot_faces
[4] = { 1,2,4,3 };
197 static int top_faces
[4] = { 9,10,12,11 };
198 static int wall_faces
[8][4] =
199 { {1,2,6,5}, {5,6,10,9}, {3,4,8,7}, {7,8,12,11},
200 {1,3,7,5}, {5,7,11,9}, {2,4,8,6}, {6,8,12,10} };
202 /* Arbitrary Polyhedron faces: */
203 static int trim_cell_face
[num_faces_trim_cell
][6] =
204 { {5,13,14,15,16,17}, {3,16,18,17},
205 {5,15,21,20,18,16}, {5,13,17,18,20,19},
206 {4,13,19,22,14}, {4,14,22,21,15},
209 static int hang_cell_face
[num_faces_hang_cell
][8] =
210 { {5,20,21,24,25,26},
212 {7,20,26,25,27,28,30,19},
215 {5,22,19,30,28,29} };
217 /* Wall values for element faces. */
218 static int hex1_walls
[6] = { A_WALL
, A_WALL
, NOT_A_WALL
,
219 NOT_A_WALL
, A_WALL
, A_WALL
};
220 static int hex2_walls
[6] = { A_WALL
, A_WALL
, NOT_A_WALL
,
221 NOT_A_WALL
, A_WALL
, A_WALL
};
223 /* 4 variables (pressure and velocity values) at the 31 grid nodes. */
224 static float vars
[4][31] =
225 { {1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,1.10,1.11,
226 1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,1.10,1.11,
227 1.12,1.13,1.14,1.15,1.16,1.17,1.18},
228 {0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.2,
229 1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,1.10,1.11,
230 1.12,1.13,1.14,1.15,1.16,1.17,1.18},
231 {1.2,1.1,1.0,0.9,0.8,0.7,0.6,0.5,0.4,0.3,0.2,0.1,
232 1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,1.10,1.11,
233 1.12,1.13,1.14,1.15,1.16,1.17,1.18},
234 {0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.2,
235 1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,1.10,1.11,
236 1.12,1.13,1.14,1.15,1.16,1.17,1.18} };
239 ** 4 boundary variables (temperature and velocity values) defined on
240 ** the single top face, and the the single bottom face.
242 static float top_bvars
[4] = { 1.0, 2.0,4.0,2.5 };
243 static float bot_bvars
[4] = { 1.0, 4.5,3.0,3.0 };
245 /* Arbitrary Polyhedron boundary face variables: */
246 static float trim_cell_bvars
[4][num_faces_trim_cell
] =
247 { {1.0,1.1,1.2,1.3,1.4,1.5,1.6},
248 {1.7,1.8,1.9,1.1,1.11,1.12,1.13},
249 {1.14,1.15,1.16,1.17,1.18,1.19,1.2},
250 {1.21,1.22,1.23,1.24,1.25,1.26,1.27} };
252 static float hang_cell_bvars
[4][num_faces_hang_cell
] =
253 { {1.1,1.11,1.12,1.13,1.14,1.15},
254 {1.16,1.17,1.18,1.19,1.2,1.21},
255 {1.22,1.23,1.24,1.25,1.26,1.27},
256 {1.28,1.29,1.30,1.31,1.32,1.33} };
258 /* Open the file for binary write access. */
259 if ((outfp
= fopen(file_name
, "wb")) == NULL
)
261 perror ("Cannot open output file");
265 /* Output the magic number. */
267 fwrite(ibuf
, sizeof(int), 1, outfp
);
269 /* Output file header and version number. */
270 fwrite_str80("FIELDVIEW", outfp
);
273 ** This version of the FIELDVIEW unstructured file is "3.0".
274 ** This is written as two integers.
278 fwrite(ibuf
, sizeof(int), 2, outfp
);
280 /* File type code - new in version 2.7 */
281 ibuf
[0] = FV_COMBINED_FILE
;
282 fwrite(ibuf
, sizeof(int), 1, outfp
);
284 /* Reserved field, always write a zero - new in version 2.6 */
286 fwrite(ibuf
, sizeof(int), 1, outfp
);
288 /* Output constants for time, fsmach, alpha and re. */
289 fwrite(consts
, sizeof(float), 4, outfp
);
291 /* Output the number of grids. */
293 fwrite(ibuf
, sizeof(int), 1, outfp
);
296 ** Output the table of boundary types.
297 ** Each boundary type is preceded by 2 integer flags.
298 ** The first flag is an "surface results flag".
299 ** A value of 1 means surface results will be present for this
300 ** boundary type (if any boundary variables are specified in the
301 ** boundary variable names table below).
302 ** A value of 0 means no surface results will be present.
303 ** The second flag indicates whether boundary faces of this type have
304 ** consistent "clockness" for the purpose of calculating a surface
305 ** normal. A value of 1 means that all faces of this type are
306 ** written following a "right hand rule" for clockness. In other
307 ** words, if the vertices are written on counter-clockwise:
311 ** then the normal to the face is pointing towards you (not away
312 ** from you). A value of 0 means that the faces do not have any
313 ** consistent clockness. The "clockness" of surface normals is
314 ** only used for calculating certain special surface integrals
315 ** that involve surface normals. If the surface normals flag
316 ** is 0, these special integrals will not be available.
318 ibuf
[0] = num_face_types
;
319 fwrite(ibuf
, sizeof(int), 1, outfp
);
320 for (i
= 0; i
< num_face_types
; i
++) {
321 ibuf
[0] = results_flag
[i
];
322 ibuf
[1] = normals_flag
[i
];
323 fwrite(ibuf
, sizeof(int), 2, outfp
);
324 fwrite_str80(face_type_names
[i
], outfp
);
327 /* Output the table of variable names. */
328 /* The number of variables can be zero. */
330 fwrite(ibuf
, sizeof(int), 1, outfp
);
331 for (i
= 0; i
< num_vars
; i
++)
332 fwrite_str80(var_names
[i
], outfp
);
335 ** Output the table of boundary variable names.
336 ** Boundary variables are associated with boundary faces, rather than
338 ** FIELDVIEW will automatically append "[BNDRY]" to each name
339 ** so boundary variables can be easily distinguished from ordinary
340 ** (grid node) variables.
341 ** The number of boundary variables can be different from the number
342 ** of ordinary variables. The number of boundary variables can be
346 fwrite(ibuf
, sizeof(int), 1, outfp
);
347 for (i
= 0; i
< num_bvars
; i
++)
348 fwrite_str80(bvar_names
[i
], outfp
);
350 /* Output grid data. */
351 for (grid
= 0; grid
< num_grids
; grid
++)
353 /* Output the node definition section for this grid. */
356 fwrite(ibuf
, sizeof(int), 2, outfp
);
359 ** Output the X, then Y, then Z node coordinates.
360 ** Note that all of the X coordinates are output before any of
361 ** the Y coordinates.
363 fwrite(x
, sizeof(float), nnodes
, outfp
);
364 fwrite(y
, sizeof(float), nnodes
, outfp
);
365 fwrite(z
, sizeof(float), nnodes
, outfp
);
368 ** Output boundary faces of the 3 different types.
369 ** All faces have 4 vertices. If the face is triangular,
370 ** the last vertex should be zero.
371 ** TIP: A single boundary type can be broken into several sections
372 ** if you prefer. Also, boundary face sections do not have to
373 ** be in order. You may have a section of 10 faces of type 3,
374 ** followed by a section of 20 faces of type 2, followed by a
375 ** section of 15 more faces of type 3. Breaking a boundary
376 ** type into very many short sections is less efficient. The
377 ** boundaries will require more memory and be somewhat
378 ** slower to calculate in FIELDVIEW.
382 ibuf
[1] = 1; /* first boundary type */
383 ibuf
[2] = 1; /* number of faces of this type */
384 fwrite(ibuf
, sizeof(int), 3, outfp
);
385 fwrite(bot_faces
, sizeof(int), 4, outfp
);
388 ibuf
[1] = 2; /* second boundary type */
389 ibuf
[2] = 1; /* number of faces of this type */
390 fwrite(ibuf
, sizeof(int), 3, outfp
);
391 fwrite(top_faces
, sizeof(int), 4, outfp
);
394 ibuf
[1] = 3; /* third boundary type */
395 ibuf
[2] = 8; /* number of faces of this type */
396 fwrite(ibuf
, sizeof(int), 3, outfp
);
397 fwrite(wall_faces
, sizeof(int), 8*4, outfp
);
399 /* Arbitrary Polygon boundary faces:
400 ** The format (in psuedocode) is as follows:
401 ** >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
402 ** FV_ARB_POLY_FACES (section header)
403 ** BndryFaceType NumBndryFaces
405 ** [for N = 1, NumBndryFaces]
406 ** NumVertsFaceN Vert1 Vert2 ... Vert{NumVertsFaceN}
408 ** <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
409 ** * The above block should be repeated for different boundary face
410 ** types, as is the case for standard boundary faces.
411 ** * These blocks should be after the blocks for standard faces,
412 ** within the FIELDVIEW-Uns file.
413 ** * The node ordering for specifying faces should follow a
414 ** right-handed rule with the normal pointing away from the
415 ** cell center. So nodes should be given by traversing the face
416 ** in a counter-clockwise manner.
417 ** * Hanging nodes are not permitted on boundary faces.
420 ibuf
[0] = FV_ARB_POLY_FACES
;
421 ibuf
[1] = 4; /* boundary face type */
422 ibuf
[2] = 7; /* num faces for the trimmed cell */
424 fwrite(ibuf
, sizeof(int), 3, outfp
);
426 for(i
= 0; i
< num_faces_trim_cell
; ++i
) /* loop over the faces */
427 fwrite(trim_cell_face
[i
], sizeof(int), trim_cell_face
[i
][0] + 1,
430 ibuf
[0] = FV_ARB_POLY_FACES
;
431 ibuf
[1] = 5; /* boundary face type */
432 ibuf
[2] = 6; /* num faces for the hanging node cell */
434 fwrite(ibuf
, sizeof(int), 3, outfp
);
436 for(i
= 0; i
< num_faces_hang_cell
; ++i
) /* loop over the faces */
437 fwrite(hang_cell_face
[i
], sizeof(int), hang_cell_face
[i
][0] + 1,
441 ** Start an elements section.
442 ** There may be as many elements sections as needed.
443 ** Each section may contain a single element type or a
444 ** mixture of element types.
445 ** For maximum efficiency, each section should contain
446 ** a significant percentage of the elements in the grid.
447 ** The most efficient case is a single section containing
448 ** all elements in the grid.
450 ibuf
[0] = FV_ELEMENTS
;
451 ibuf
[1] = 0; /* tet count */
452 ibuf
[2] = 2; /* hex count */
453 ibuf
[3] = 0; /* prism count */
454 ibuf
[4] = 0; /* pyramid count */
455 fwrite(ibuf
, sizeof(int), 5, outfp
);
457 /* Write header for first element. */
458 elem_header
= fv_encode_elem_header(FV_HEX_ELEM_ID
, hex1_walls
);
459 if (elem_header
== 0)
461 fprintf (stderr
, "fv_encode_elem_header failed for first hex.\n");
464 fwrite (&elem_header
, sizeof(elem_header
), 1, outfp
);
466 /* Write node definition for first element. */
467 fwrite(hex1
, sizeof(int), 8, outfp
);
469 /* Write header for second element. */
470 elem_header
= fv_encode_elem_header(FV_HEX_ELEM_ID
, hex2_walls
);
471 if (elem_header
== 0)
473 fprintf (stderr
, "fv_encode_elem_header failed for second hex.\n");
476 fwrite (&elem_header
, sizeof(elem_header
), 1, outfp
);
478 /* Write node definition for second element. */
479 fwrite(hex2
, sizeof(int), 8, outfp
);
481 /* Arbitrary Polyhedron elements:
482 ** The format (in psuedocode) is as follows:
483 ** >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
484 ** FV_ARB_POLY_ELEMENTS (section header)
485 ** NumArbPolyElements
487 ** [for elem = 1, NumArbPolyElements]
489 ** NumFaces NumNodesElement CenterNode
491 ** [for face = 1, NumFaces]
492 ** WallFlag NumNodesFace FaceNode1 ... FaceNode{NumNodesFace}
493 ** NumHangNodes HangNode1 ... HangNode{NumHangNodes}
495 ** <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
496 ** * These blocks can be after or before the standard element blocks.
497 ** There can be any number of these for any one grid.
498 ** * The WallFlag has the same meaning as for standard elements,
499 ** i.e., A_WALL or NOT_A_WALL.
500 ** * The node ordering for specifying faces should follow a
501 ** right-handed rule with the normal pointing away from the
502 ** cell center. So nodes should be given by traversing the face
503 ** in a counter-clockwise manner.
504 ** * Hanging nodes are associated with a face interior and should
505 ** not be on an edge. Hanging nodes on an edge should be
506 ** interpretted as a regular face node.
508 ibuf
[0] = FV_ARB_POLY_ELEMENTS
;
509 ibuf
[1] = 2; /* have 2 elements here */
511 fwrite(ibuf
, sizeof(int), 2, outfp
);
513 /* trimmed face element */
514 ibuf
[0] = 7; /* num faces for the trimmed cell */
515 ibuf
[1] = 11; /* number of nodes including a center node */
516 ibuf
[2] = 23; /* the center node */
517 fwrite(ibuf
, sizeof(int), 3, outfp
);
519 ibuf
[0] = A_WALL
; /* wall value */
520 ibuf
[1] = 0; /* number of hanging nodes */
522 for(i
= 0; i
< num_faces_trim_cell
; ++i
) /* write out face info */
524 fwrite(ibuf
, sizeof(int), 1, outfp
); /* write wall value */
525 fwrite(trim_cell_face
[i
], sizeof(int), trim_cell_face
[i
][0] + 1,
526 outfp
); /* write num verts and verts */
527 fwrite(&ibuf
[1], sizeof(int), 1, outfp
); /* write num hang nodes */
530 /* hanging node element */
531 ibuf
[0] = 6; /* num faces for the hanging node cell */
532 ibuf
[1] = 12; /* number of nodes excluding a center node */
533 ibuf
[2] = -1; /* the center node, this indicates that FIELDVIEW
534 ** should calculate the center node and associated
535 ** centernode variable values
537 fwrite(ibuf
, sizeof(int), 3, outfp
);
539 ibuf
[0] = A_WALL
; /* wall value */
540 ibuf
[1] = 0; /* number of hanging nodes */
541 ibuf
[2] = 1; /* number of hanging nodes for face 3 */
542 ibuf
[3] = 31; /* the node number for the hanging node on face 3*/
544 for(i
= 0; i
< 2; ++i
) /* write out face info for first 2 faces */
546 fwrite(ibuf
, sizeof(int), 1, outfp
); /* write wall value */
547 fwrite(hang_cell_face
[i
], sizeof(int), hang_cell_face
[i
][0] + 1,
548 outfp
); /* write num verts and verts */
549 fwrite(&ibuf
[1], sizeof(int), 1, outfp
); /* write num hang nodes */
552 /* this face has a hanging node */
553 fwrite(ibuf
, sizeof(int), 1, outfp
);
554 fwrite(hang_cell_face
[2], sizeof(int), hang_cell_face
[2][0] + 1, outfp
);
555 fwrite(&ibuf
[2], sizeof(int), 2, outfp
);
557 /* write out face info for last 3 faces */
558 for(i
= 3; i
< num_faces_hang_cell
; ++i
)
560 fwrite(ibuf
, sizeof(int), 1, outfp
); /* write wall value */
561 fwrite(hang_cell_face
[i
], sizeof(int), hang_cell_face
[i
][0] + 1,
562 outfp
); /* write num verts and verts */
563 fwrite(&ibuf
[1], sizeof(int), 1, outfp
); /* write num hang nodes */
567 ** Output the variables data.
568 ** You must write the section header even if the number
569 ** of variables is zero.
571 ibuf
[0] = FV_VARIABLES
;
572 fwrite(ibuf
, sizeof(int), 1, outfp
);
575 ** Note that all of the data for the first variable is output
576 ** before any of the data for the second variable.
578 for (i
= 0; i
< num_vars
; i
++)
579 fwrite(vars
[i
], sizeof(float), nnodes
, outfp
);
582 ** Output the boundary variables data.
583 ** Remember that the Boundary Table above has a "surface results
584 ** flag" indicating which boundary types have surface results.
585 ** The data should be written in the same order as the faces in
586 ** the Boundary Faces section, skipping over faces whose boundary
587 ** type has a surface results flag of zero (false).
588 ** For each variable, you should write one number per boundary face.
589 ** You must write the section header even if the number of boundary
590 ** variables is zero.
592 ibuf
[0] = FV_BNDRY_VARS
;
593 fwrite(ibuf
, sizeof(int), 1, outfp
);
596 ** Note that all of the data for the first variable is output
597 ** before any of the data for the second variable.
599 for (i
= 0; i
< num_bvars
; i
++) {
602 ** The data for the bottom face is written first for each
603 ** variable, because the bottom face was written first in the
604 ** "Boundary Faces" section.
605 ** The "wall" faces are skipped, because the surface results
606 ** flag for the wall boundary type was 0 (false) in the
607 ** Boundary Table section.
609 num_faces
= 1; /* number of bottom faces */
610 fwrite(&bot_bvars
[i
], sizeof(float), num_faces
, outfp
);
611 num_faces
= 1; /* number of top faces */
612 fwrite(&top_bvars
[i
], sizeof(float), num_faces
, outfp
);
615 /* Arbitrary Polyhedron boundary face results:
616 ** The format is the same as for standard boundary face results.
618 ibuf
[0] = FV_ARB_POLY_BNDRY_VARS
;
619 fwrite(ibuf
, sizeof(int), 1, outfp
);
621 for (i
= 0; i
< num_bvars
; ++i
)
625 num_faces
= 7; /* num faces for the trimmed cell */
626 fwrite(trim_cell_bvars
[i
], sizeof(float), num_faces
, outfp
);
628 num_faces
= 6; /* num faces for the hanging node cell */
629 fwrite(hang_cell_bvars
[i
], sizeof(float), num_faces
, outfp
);
633 if (fclose(outfp
) != 0)
635 perror ("Cannot close output file");
641 #endif /* end commenting out the sample program */