ifc_objects_t<ColorType> extract_objects(std::string& root_guid, const fs::path& ifc_file, bool shared_vertices) { typedef typename cartan::openmesh_t<ColorType>::Point point_t; typedef typename cartan::openmesh_t<ColorType>::Normal normal_t; typedef typename cartan::openmesh_t<ColorType>::VertexHandle vertex_t; if (!fs::exists(ifc_file)) { throw std::runtime_error("Unable to open file \"" + ifc_file.string() + "\" for reading."); } IfcGeom::IteratorSettings settings; settings.set(IfcGeom::IteratorSettings::APPLY_DEFAULT_MATERIALS, true); settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, true); settings.set(IfcGeom::IteratorSettings::WELD_VERTICES, shared_vertices); settings.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, false); IfcGeom::Iterator<double> context_iterator(settings, ifc_file.string()); IfcParse::IfcFile* file_ptr = context_iterator.getFile(); auto root_entity = file_ptr->entitiesByType<IfcSchema::IfcRoot>(); root_guid = (*root_entity->begin())->GlobalId(); std::set<std::string> ignore_types; ignore_types.insert("ifcopeningelement"); ignore_types.insert("ifcspace"); context_iterator.excludeEntities(ignore_types); context_iterator.initialize(); ifc_objects_t<ColorType> objects; do { ifc_object_t<ColorType> object; std::get<0>(object).request_vertex_normals(); std::get<0>(object).request_face_normals(); const IfcGeom::Element<double>* geom_object = context_iterator.get(); auto triangulation = static_cast<const IfcGeom::TriangulationElement<double>*>(geom_object); const IfcGeom::Representation::Triangulation<double>& mesh = triangulation->geometry(); // get type std::get<2>(object) = geom_object->type(); for (std::string::iterator c = std::get<2>(object).begin(); c != std::get<2>(object).end(); ++c) { *c = tolower(*c); } std::get<1>(object) = geom_object->guid(); // get polygon data auto& fs = mesh.faces(); auto& vs = mesh.verts(); auto& ns = mesh.normals(); uint32_t v_count = vs.size() / 3; std::vector<vertex_t> vertex_handles; for (uint32_t v = 0; v < v_count; ++v) { auto vertex = std::get<0>(object).add_vertex(point_t(vs[3*v + 0], vs[3*v + 1], vs[3*v + 2])); if (!shared_vertices) { auto normal = normal_t(ns[3*v + 0], ns[3*v + 1], ns[3*v + 2]); float length = std::sqrt(normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]); normal[0] /= length; normal[1] /= length; normal[2] /= length; std::get<0>(object).set_normal(vertex, normal); } vertex_handles.push_back(vertex); } for (uint32_t f = 0; f < fs.size(); f += 3) { std::vector<vertex_t> vertices; for (uint32_t v = 0; v < 3; ++v) { int idx = fs[f+v]; vertices.push_back(vertex_handles[idx]); } std::get<0>(object).add_face(vertices); } // update mesh-internal normal state if (shared_vertices) { std::get<0>(object).update_normals(); } else { std::get<0>(object).update_face_normals(); } objects.push_back(object); } while (context_iterator.next()); return objects; }
int main () { // Redirect stdout to this stream, so that involuntary // writes to stdout do not interfere with our protocol. std::ostringstream oss; stdout_redir = oss.rdbuf(); stdout_orig = std::cout.rdbuf(); std::cout.rdbuf(stdout_redir); #ifdef SET_BINARY_STREAMS _setmode(_fileno(stdout), _O_BINARY); std::cout.setf(std::ios_base::binary); _setmode(_fileno(stdin), _O_BINARY); std::cin.setf(std::ios_base::binary); #endif bool has_more = false; IfcGeom::Iterator<float>* iterator = 0; Hello().write(std::cout); int exit_code = 0; for (;;) { const int32_t msg_type = sread<int32_t>(std::cin); switch (msg_type) { case IFC_MODEL: { IfcModel m; m.read(std::cin); std::string::size_type len = m.string().size(); char* data = new char[len]; memcpy(data, m.string().c_str(), len); IfcGeom::IteratorSettings settings; settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, false); settings.set(IfcGeom::IteratorSettings::WELD_VERTICES, false); settings.set(IfcGeom::IteratorSettings::CONVERT_BACK_UNITS, true); settings.set(IfcGeom::IteratorSettings::INCLUDE_CURVES, true); iterator = new IfcGeom::Iterator<float>(settings, data, (int)len); has_more = iterator->initialize(); More(has_more).write(std::cout); continue; } case GET: { Get g; g.read(std::cin); if (!has_more) { exit_code = 1; break; } const IfcGeom::TriangulationElement<float>* geom = static_cast<const IfcGeom::TriangulationElement<float>*>(iterator->get()); Entity(geom).write(std::cout); continue; } case NEXT: { Next n; n.read(std::cin); has_more = iterator->next(); if (!has_more) { delete iterator; iterator = 0; } More(has_more).write(std::cout); continue; } case GET_LOG: { GetLog gl; gl.read(std::cin); WriteLog(iterator->getLog()).write(std::cout); continue; } case BYE: { Bye().write(std::cout); exit_code = 0; break; } default: exit_code = 1; break; } break; } std::cout.rdbuf(stdout_orig); return exit_code; }
int main(int argc, char** argv) { boost::program_options::options_description generic_options; generic_options.add_options() ("help", "display usage information") ("version", "display version information") ("verbose,v", "more verbose output"); boost::program_options::options_description fileio_options; fileio_options.add_options() ("input-file", boost::program_options::value<std::string>(), "input IFC file") ("output-file", boost::program_options::value<std::string>(), "output geometry file"); std::string bounds; std::vector<std::string> entity_vector; boost::program_options::options_description geom_options; geom_options.add_options() ("plan", "Specifies whether to include curves in the output result. Typically " "these are representations of type Plan or Axis. Excluded by default.") ("model", "Specifies whether to include surfaces and solids in the output result. " "Typically these are representations of type Body or Facetation. " "Included by default.") ("weld-vertices", "Specifies whether vertices are welded, meaning that the coordinates " "vector will only contain unique xyz-triplets. This results in a " "manifold mesh which is useful for modelling applications, but might " "result in unwanted shading artefacts in rendering applications.") ("use-world-coords", "Specifies whether to apply the local placements of building elements " "directly to the coordinates of the representation mesh rather than " "to represent the local placement in the 4x3 matrix, which will in that " "case be the identity matrix.") ("convert-back-units", "Specifies whether to convert back geometrical output back to the " "unit of measure in which it is defined in the IFC file. Default is " "to use meters.") ("sew-shells", "Specifies whether to sew the faces of IfcConnectedFaceSets together. " "This is a potentially time consuming operation, but guarantees a " "consistent orientation of surface normals, even if the faces are not " "properly oriented in the IFC file.") #if OCC_VERSION_HEX < 0x60900 // In Open CASCADE version prior to 6.9.0 boolean operations with multiple // arguments where not introduced yet and a work-around was implemented to // subtract multiple openings as a single compound. This hack is obsolete // for newer versions of Open CASCADE. ("merge-boolean-operands", "Specifies whether to merge all IfcOpeningElement operands into a single " "operand before applying the subtraction operation. This may " "introduce a performance improvement at the risk of failing, in " "which case the subtraction is applied one-by-one.") #endif ("disable-opening-subtractions", "Specifies whether to disable the boolean subtraction of " "IfcOpeningElement Representations from their RelatingElements.") ("bounds", boost::program_options::value<std::string>(&bounds), "Specifies the bounding rectangle, for example 512x512, to which the " "output will be scaled. Only used when converting to SVG.") ("include", "Specifies that the entities listed after --entities are to be included") ("exclude", "Specifies that the entities listed after --entities are to be excluded") ("entities", boost::program_options::value< std::vector<std::string> >(&entity_vector)->multitoken(), "A list of entities that should be included in or excluded from the " "geometrical output, depending on whether --ignore or --include is " "specified. Defaults to IfcOpeningElement and IfcSpace to be excluded."); boost::program_options::options_description cmdline_options; cmdline_options.add(generic_options).add(fileio_options).add(geom_options); boost::program_options::positional_options_description positional_options; positional_options.add("input-file", 1); positional_options.add("output-file", 1); boost::program_options::variables_map vmap; try { boost::program_options::store(boost::program_options::command_line_parser(argc, argv). options(cmdline_options).positional(positional_options).run(), vmap); } catch (const boost::program_options::unknown_option& e) { std::cerr << "[Error] Unknown option '" << e.get_option_name() << "'" << std::endl << std::endl; // Usage information will be emitted below } catch (...) { // Catch other errors such as invalid command line syntax } boost::program_options::notify(vmap); if (vmap.count("version")) { printVersion(); return 0; } else if (vmap.count("help") || !vmap.count("input-file")) { printUsage(generic_options, geom_options); return vmap.count("help") ? 0 : 1; } else if (vmap.count("include") && vmap.count("exclude")) { std::cerr << "[Error] --include and --ignore can not be specified together" << std::endl; printUsage(generic_options, geom_options); return 1; } const bool verbose = vmap.count("verbose") != 0; const bool weld_vertices = vmap.count("weld-vertices") != 0; const bool use_world_coords = vmap.count("use-world-coords") != 0; const bool convert_back_units = vmap.count("convert-back-units") != 0; const bool sew_shells = vmap.count("sew-shells") != 0; #if OCC_VERSION_HEX < 0x60900 const bool merge_boolean_operands = vmap.count("merge-boolean-operands") != 0; #endif const bool disable_opening_subtractions = vmap.count("disable-opening-subtractions") != 0; bool include_entities = vmap.count("include") != 0; const bool include_plan = vmap.count("plan") != 0; const bool include_model = vmap.count("model") != 0 || (!include_plan); boost::optional<int> bounding_width, bounding_height; if (vmap.count("bounds") == 1) { int w, h; if (sscanf(bounds.c_str(), "%ux%u", &w, &h) == 2) { bounding_width = w; bounding_height = h; } else { std::cerr << "[Error] Invalid use of --bounds" << std::endl; printUsage(generic_options, geom_options); return 1; } } // Gets the set ifc types to be ignored from the command line. std::set<std::string> entities; for (std::vector<std::string>::const_iterator it = entity_vector.begin(); it != entity_vector.end(); ++it) { const std::string& mixed_case_type = *it; entities.insert(boost::to_lower_copy(mixed_case_type)); } const std::string input_filename = vmap["input-file"].as<std::string>(); // If no output filename is specified a Wavefront OBJ file will be output // to maintain backwards compatibility with the obsolete IfcObj executable. const std::string output_filename = vmap.count("output-file") == 1 ? vmap["output-file"].as<std::string>() : change_extension(input_filename, DEFAULT_EXTENSION); if (output_filename.size() < 5) { printUsage(generic_options, geom_options); return 1; } std::string output_extension = output_filename.substr(output_filename.size()-4); boost::to_lower(output_extension); // If no entities are specified these are the defaults to skip from output if (entity_vector.empty()) { if (output_extension == ".svg") { entities.insert("ifcspace"); include_entities = true; } else { entities.insert("ifcopeningelement"); entities.insert("ifcspace"); } } Logger::SetOutput(&std::cout, &log_stream); Logger::Verbosity(verbose ? Logger::LOG_NOTICE : Logger::LOG_ERROR); if (output_extension == ".xml") { int exit_code = 1; try { XmlSerializer s(output_filename); IfcParse::IfcFile f; if (!f.Init(input_filename)) { Logger::Message(Logger::LOG_ERROR, "Unable to parse .ifc file"); } else { s.setFile(&f); s.finalize(); exit_code = 0; } } catch (...) {} write_log(); return exit_code; } IfcGeom::IteratorSettings settings; settings.set(IfcGeom::IteratorSettings::APPLY_DEFAULT_MATERIALS, true); settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, use_world_coords); settings.set(IfcGeom::IteratorSettings::WELD_VERTICES, weld_vertices); settings.set(IfcGeom::IteratorSettings::SEW_SHELLS, sew_shells); settings.set(IfcGeom::IteratorSettings::CONVERT_BACK_UNITS, convert_back_units); #if OCC_VERSION_HEX < 0x60900 settings.set(IfcGeom::IteratorSettings::FASTER_BOOLEANS, merge_boolean_operands); #endif settings.set(IfcGeom::IteratorSettings::DISABLE_OPENING_SUBTRACTIONS, disable_opening_subtractions); settings.set(IfcGeom::IteratorSettings::INCLUDE_CURVES, include_plan); settings.set(IfcGeom::IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES, !include_model); GeometrySerializer* serializer; if (output_extension == ".obj") { const std::string mtl_filename = output_filename.substr(0,output_filename.size()-3) + "mtl"; if (!use_world_coords) { Logger::Message(Logger::LOG_NOTICE, "Using world coords when writing WaveFront OBJ files"); settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, true); } serializer = new WaveFrontOBJSerializer(output_filename, mtl_filename); #ifdef WITH_OPENCOLLADA } else if (output_extension == ".dae") { serializer = new ColladaSerializer(output_filename); #endif } else if (output_extension == ".stp") { serializer = new StepSerializer(output_filename); } else if (output_extension == ".igs") { // Not sure why this is needed, but it is. // See: http://tracker.dev.opencascade.org/view.php?id=23679 IGESControl_Controller::Init(); serializer = new IgesSerializer(output_filename); } else if (output_extension == ".svg") { settings.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, true); serializer = new SvgSerializer(output_filename); if (bounding_width && bounding_height) { ((SvgSerializer*) serializer)->setBoundingRectangle( static_cast<double>(*bounding_width), static_cast<double>(*bounding_height) ); } } else { Logger::Message(Logger::LOG_ERROR, "Unknown output filename extension"); write_log(); printUsage(generic_options, geom_options); return 1; } if (!serializer->isTesselated()) { if (weld_vertices) { Logger::Message(Logger::LOG_NOTICE, "Weld vertices setting ignored when writing STEP or IGES files"); } settings.disable_triangulation() = true; } IfcGeom::Iterator<double> context_iterator(settings, input_filename); IfcGeom::ProductFilter productFilter; try { if (include_entities) { productFilter.includeEntities(entities); } else { productFilter.excludeEntities(entities); } context_iterator.setProductFilter(&productFilter, &IfcGeom::ProductFilter::shouldConvertProduct); } catch (const IfcParse::IfcException& e) { std::cout << "[Error] " << e.what() << std::endl; return 1; } if (!serializer->ready()) { Logger::Message(Logger::LOG_ERROR, "Unable to open output file for writing"); write_log(); return 1; } time_t start,end; time(&start); if (!context_iterator.initialize()) { Logger::Message(Logger::LOG_ERROR, "Unable to parse .ifc file or no geometrical entities found"); write_log(); return 1; } serializer->setFile(context_iterator.getFile()); if (convert_back_units) { serializer->setUnitNameAndMagnitude(context_iterator.getUnitName(), static_cast<const float>(context_iterator.getUnitMagnitude())); } else { serializer->setUnitNameAndMagnitude("METER", 1.0f); } serializer->writeHeader(); std::set<std::string> materials; int old_progress = -1; Logger::Status("Creating geometry..."); // The functions IfcGeom::Iterator::get() and IfcGeom::Iterator::next() // wrap an iterator of all geometrical products in the Ifc file. // IfcGeom::Iterator::get() returns an IfcGeom::TriangulationElement or // -BRepElement pointer, based on current settings. (see IfcGeomIterator.h // for definition) IfcGeom::Iterator::next() is used to poll whether more // geometrical entities are available. None of these functions throw // exceptions, neither for parsing errors or geometrical errors. Upon // calling next() the entity to be returned has already been processed, a // true return value guarantees that a successfully processed product is // available. do { const IfcGeom::Element<double>* geom_object = context_iterator.get(); if (serializer->isTesselated()) { serializer->write(static_cast<const IfcGeom::TriangulationElement<double>*>(geom_object)); } else { serializer->write(static_cast<const IfcGeom::BRepElement<double>*>(geom_object)); } const int progress = context_iterator.progress() / 2; if (old_progress!= progress) Logger::ProgressBar(progress); old_progress = progress; } while (context_iterator.next()); serializer->finalize(); delete serializer; Logger::Status("\rDone creating geometry "); write_log(); time(&end); int dif = (int) difftime (end,start); printf ("\nConversion took %d seconds\n", dif ); return 0; }