/* * Copyright (c) 1991-2020 by STEP Tools Inc. * All Rights Reserved. * * Permission to use, copy, modify, and distribute this software and * its documentation is hereby granted, provided that this copyright * notice and license appear on all copies of the software. * * STEP TOOLS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE * SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. STEP TOOLS * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A * RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. * * Author: David Loffredo (loffredo@steptools.com) */ // stp2stl -- write a single STL file for a STEP model. This facets // everything in one pass, and then work on the cached data. It then // recursively walks down through any assemblies, applying transforms // to the facet data and writing either ASCII STL or binary STL. // #include #include #include extern void print_ascii_stl (FILE *, RoseDesign *); extern void print_binary_stl (FILE *, RoseDesign *); int main (int argc, char ** argv) { ROSE.quiet(1); stplib_init(); // initialize step aim library stixmesh_init(); const char * srcname = "as1-ac-214.stp"; const char * dstname = "as1-ac-214.stl"; RoseDesign * d = ROSE.findDesign(srcname); if (!d) { printf ("Could not open STEP file %s\n", srcname); exit (1); } // prepare for working with assemblies rose_compute_backptrs(d); stix_tag_asms(d); stix_tag_units (d); // start the worker threads rendering in parallel, then wait for // them to finish. RoseMeshWorkerContext context; RoseMeshOptions facet_opts; context.setFacetOptions(&facet_opts); stixmesh_worker_render_design(d, &context); rose_mesh_worker_wait_all(); // Follow the shape tree of the assembly and write facet data as // STL with the appropriate assembly transforms. You can write as // either binary or text STL by changing the following flag. // int do_binary = 0; // 0 = text, nonzero = binary #ifdef _WIN32 // binary files need the wb mode on windows FILE * dstfile = fopen (dstname, do_binary? "wb": "w"); #else FILE * dstfile = fopen (dstname, "w"); #endif if (!dstfile) { printf ("Could not open output file\n"); return 2; } if (do_binary) print_binary_stl(dstfile, d); else print_ascii_stl(dstfile, d); fclose (dstfile); return 0; } //------------------------------------------------------------ //------------------------------------------------------------ // COUNT FACETS -- Binary STL needs an upfront count, which we // need to compute ahead of time. //------------------------------------------------------------ //------------------------------------------------------------ unsigned count_mesh_for_shape ( stp_representation * rep ) { unsigned i, sz; unsigned count = 0; if (!rep) return count; // Count any local meshes SetOfstp_representation_item * items = rep->items(); for (i=0, sz=items->size(); iget(i); StixMeshBuilder * mb = stixmesh_worker_find_builder (rep, it); if (!mb) continue; count += mb-> getStpMesh()->getFacetCount(); } // Count all of the child shapes StixMgrAsmShapeRep * rep_mgr = StixMgrAsmShapeRep::find(rep); if (!rep_mgr) return count; for (i=0, sz=rep_mgr->child_rels.size(); ichild_rels[i]; stp_representation * child = stix_get_shape_usage_child_rep (rel); count += count_mesh_for_shape (child); } for (i=0, sz=rep_mgr->child_mapped_items.size(); ichild_mapped_items[i]; stp_representation * child = stix_get_shape_usage_child_rep (rel); count += count_mesh_for_shape (child); } return count; } unsigned count_mesh_for_product ( stp_product_definition * pd ) { unsigned i, sz; unsigned count = 0; StixMgrAsmProduct * pm = StixMgrAsmProduct::find(pd); if (!pm) return count; for (i=0, sz=pm->shapes.size(); ishapes[i]; count += count_mesh_for_shape (rep); } return count; } //------------------------------------------------------------ //------------------------------------------------------------ // Binary utilities -- Binary STL uses little endian 32bit float, and // little endan 32bit unsigned integer. This matches the common intel // usage for windows, mac and linux. We swap big endian if working on // aix, sparc, hpux or ppc macs. //------------------------------------------------------------ //------------------------------------------------------------ #if defined(_AIX) || defined(__sparc) || defined(__hpux) #define BIG_ENDIAN #endif #ifdef __APPLE__ #if defined (__ppc__) || defined(__ppc64__) #define BIG_ENDIAN #endif #endif static void write_stlbin_float (FILE * file, double val) { union { float float_elem; /* assume 32bit float */ unsigned char words[4]; } olbuf; olbuf.float_elem = (float) val; #ifdef BIG_ENDIAN putc (olbuf.words[3], file); putc (olbuf.words[2], file); putc (olbuf.words[1], file); putc (olbuf.words[0], file); #else putc (olbuf.words[0], file); putc (olbuf.words[1], file); putc (olbuf.words[2], file); putc (olbuf.words[3], file); #endif } static void write_stlbin_unsigned (FILE * file, unsigned val) { // shifts work properly regardless of endian-ness putc (val & 0xff, file); putc ((val >> 8) & 0xff, file); putc ((val >> 16) & 0xff, file); putc ((val >> 24) & 0xff, file); } //------------------------------------------------------------ //------------------------------------------------------------ // PRINT THE FACET INFORMATION -- This follows the shape information // attached to a single product or assembly and prints it to the STL // file. This is adapted from the stixmesh facet assembly sample. // // The traversal code is the same for both binary and ascii stl. We // just call a different function at the end to print the actual // triangle data. //------------------------------------------------------------ //------------------------------------------------------------ static void print_ascii_triangle ( FILE * stlfile, const RoseMesh * fs, RoseXform &xform, unsigned facet_num ) { double v[3]; double n[3]; const RoseMeshFacet * f = fs-> getFacet(facet_num); const char * vertexfmt = " vertex %.15g %.15g %.15g\n"; if (!f) return; // The components of the triangle verticies and vertex normals are // given by an index into internal tables. Apply the transform so // that the facet is placed correctly in the part space. // fs->getFacetNormal(n, f); rose_xform_apply_dir (n, xform, n); fprintf(stlfile, "facet normal %.15g %.15g %.15g\n", n[0], n[1], n[2]); fputs(" outer loop\n", stlfile); rose_xform_apply (v, xform, fs-> getVertex(f-> verts[0])); fprintf(stlfile, vertexfmt, v[0], v[1], v[2]); rose_xform_apply (v, xform, fs-> getVertex(f-> verts[1])); fprintf(stlfile, vertexfmt, v[0], v[1], v[2]); rose_xform_apply (v, xform, fs-> getVertex(f-> verts[2])); fprintf(stlfile, vertexfmt, v[0], v[1], v[2]); fputs(" endloop\n", stlfile); fputs("endfacet\n", stlfile); } static void print_binary_triangle ( FILE * stlfile, const RoseMesh * fs, RoseXform &xform, unsigned facet_num ) { double v[3]; double n[3]; const RoseMeshFacet * f = fs-> getFacet(facet_num); if (!f) return; // The components of the triangle verticies and vertex normals are // given by an index into internal tables. Apply the transform so // that the facet is placed correctly in the part space. // fs->getFacetNormal(n, f); rose_xform_apply_dir (n, xform, n); write_stlbin_float(stlfile, n[0]); write_stlbin_float(stlfile, n[1]); write_stlbin_float(stlfile, n[2]); rose_xform_apply (v, xform, fs-> getVertex(f-> verts[0])); write_stlbin_float(stlfile, v[0]); write_stlbin_float(stlfile, v[1]); write_stlbin_float(stlfile, v[2]); rose_xform_apply (v, xform, fs-> getVertex(f-> verts[1])); write_stlbin_float(stlfile, v[0]); write_stlbin_float(stlfile, v[1]); write_stlbin_float(stlfile, v[2]); rose_xform_apply (v, xform, fs-> getVertex(f-> verts[2])); write_stlbin_float(stlfile, v[0]); write_stlbin_float(stlfile, v[1]); write_stlbin_float(stlfile, v[2]); putc(0, stlfile); // 16bit zero putc(0, stlfile); } static void print_mesh_for_shape ( FILE * stlfile, stp_representation * rep, RoseXform &rep_xform, int as_bin // true for binary stl, ascii otherwise ) { unsigned i, sz; unsigned j, szz; if (!rep) return; // Does the rep have any meshed items? In an assembly, some reps // just contain placements for transforming components. If there // are solids, we should have previously generated meshes. // SetOfstp_representation_item * items = rep->items(); for (i=0, sz=items->size(); iget(i); StixMeshBuilder * mb = stixmesh_worker_find_builder (rep, it); if (!mb) continue; const RoseMesh * mesh = mb-> getStpMesh(); for (j=0, szz=mesh->getFacetCount(); j< szz; j++) { if (as_bin) print_binary_triangle (stlfile, mesh, rep_xform, j); else print_ascii_triangle (stlfile, mesh, rep_xform, j); } } // Go through all of the child shapes which can be attached by a // shape_reprepresentation_relationship or a mapped_item. If the // relation has a NAUO associated with it, then it is the start of // a different product, otherwise it is still part of the shape of // this one. // StixMgrAsmShapeRep * rep_mgr = StixMgrAsmShapeRep::find(rep); if (!rep_mgr) return; for (i=0, sz=rep_mgr->child_rels.size(); ichild_rels[i]; stp_representation * child = stix_get_shape_usage_child_rep (rel); // Move to location in enclosing asm RoseXform child_xform = stix_get_shape_usage_xform (rel); rose_xform_compose(child_xform, rep_xform, child_xform); print_mesh_for_shape (stlfile, child, child_xform, as_bin); } for (i=0, sz=rep_mgr->child_mapped_items.size(); ichild_mapped_items[i]; stp_representation * child = stix_get_shape_usage_child_rep (rel); // Move to location in enclosing asm RoseXform child_xform = stix_get_shape_usage_xform (rel); rose_xform_compose(child_xform, rep_xform, child_xform); print_mesh_for_shape (stlfile, child, child_xform, as_bin); } } static void print_mesh_for_product ( FILE * stlfile, stp_product_definition * pd, RoseXform &starting_placement, int as_bin // true for binary stl, ascii otherwise ) { // Print the shape tree for each shape associated with a product, // and then follow the shape tree downward. At each level we // check the shape relationship for a link to product relations // because shape side because there can be relationships there // that are not linked to products. // unsigned i, sz; StixMgrAsmProduct * pm = StixMgrAsmProduct::find(pd); if (!pm) return; for (i=0, sz=pm->shapes.size(); ishapes[i]; print_mesh_for_shape (stlfile, rep, starting_placement, as_bin); } } //------------------------------------------------------------ //------------------------------------------------------------ // PRINT COMPLETE STL FILE -- The ASCII STL uses begin end text blocks // with numeric triples for the normal direction and vertex points. // The binary version has the same general form, but the data is // written as lists of 32bit little endian IEEE floats. // ------------------------------------------------------------ // ------------------------------------------------------------ void print_ascii_stl ( FILE * stlfile, RoseDesign * d ) { unsigned i,sz; StpAsmProductDefVec roots; stix_find_root_products (&roots, d); fputs ("solid ", stlfile); if (d->name()) fputs (d->name(), stlfile); fputs ("\n", stlfile); for (i=0, sz=roots.size(); iname()) fputs (d->name(), stlfile); fputs ("\n", stlfile); } void print_binary_stl ( FILE * stlfile, RoseDesign * d ) { unsigned i,sz; unsigned count = 0; StpAsmProductDefVec roots; stix_find_root_products (&roots, d); // Binary STL places a facet count at the start of the file, so do // a traversal to compute that. for (i=0, sz=roots.size(); i