diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index db984857eb..41231ad6f0 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -49,6 +49,10 @@ #include #include +#include +#include +#include + #if USE_VLD #include #endif @@ -75,14 +79,37 @@ const std::string TEMP_FILE_EXTENSION = ".tmp"; namespace po = boost::program_options; + + + +struct IfcproductRepresentation +{ + int index; + IfcSchema::IfcRepresentation *representation; + IfcSchema::IfcProduct *product; + IfcGeom::Element *geom_object; + IfcGeom::BRepElement *element; +}; + +struct Bounds +{ + gp_XYZ min; + gp_XYZ max; +}; + +bool reuse_ok_(SerializerSettings settings, const IfcSchema::IfcProduct::list::ptr &products, IfcGeom::Kernel kernel); +void create_element(SerializerSettings &settings,IfcproductRepresentation &rep); +Bounds compute_bounds(IfcParse::IfcFile* ifc_file, IfcGeom::Kernel kernel); + + void print_version() { - cout_ << "IfcOpenShell " << IfcSchema::Identifier << " IfcConvert " << IFCOPENSHELL_VERSION << " (OCC " << OCC_VERSION_STRING_EXT << ")\n"; + cout_ << "IfcOpenShell " << IfcSchema::Identifier << " IfcConvert " << IFCOPENSHELL_VERSION << " (OCC " << OCC_VERSION_STRING_EXT << ")\n"; } void print_usage(bool suggest_help = true) { - cout_ << "Usage: IfcConvert [options] []\n" + cout_ << "Usage: IfcConvert [options] []\n" << "\n" << "Converts the geometry in an IFC file into one of the following formats:\n" << " .obj WaveFront OBJ (a .mtl file is also created)\n" @@ -95,39 +122,40 @@ void print_usage(bool suggest_help = true) << " .svg SVG Scalable Vector Graphics (2D floor plan)\n" << "\n" << "If no output filename given, ." << IfcUtil::path::from_utf8(DEFAULT_EXTENSION) << " will be used as the output file.\n"; - if (suggest_help) { - cout_ << "\nRun 'IfcConvert --help' for more information."; - } - cout_ << std::endl; + if (suggest_help) { + cout_ << "\nRun 'IfcConvert --help' for more information."; + } + cout_ << std::endl; } /// @todo Add help for single option void print_options(const po::options_description& options) { #if defined(_MSC_VER) && defined(_UNICODE) - // See issue https://svn.boost.org/trac10/ticket/10952 - std::ostringstream temp; - temp << options; - cout_ << "\n" << temp.str().c_str(); + // See issue https://svn.boost.org/trac10/ticket/10952 + std::ostringstream temp; + temp << options; + cout_ << "\n" << temp.str().c_str(); #else - cout_ << "\n" << options; + cout_ << "\n" << options; #endif - cout_ << std::endl; + cout_ << std::endl; } + template T change_extension(const T& fn, const T& ext) { - typename T::size_type dot = fn.find_last_of('.'); - if (dot != T::npos) { - return fn.substr(0, dot) + ext; - } else { - return fn + ext; - } + typename T::size_type dot = fn.find_last_of('.'); + if (dot != T::npos) { + return fn.substr(0, dot) + ext; + } else { + return fn + ext; + } } bool file_exists(const std::string& filename) { - std::ifstream file(IfcUtil::path::from_utf8(filename).c_str()); - return file.good(); + std::ifstream file(IfcUtil::path::from_utf8(filename).c_str()); + return file.good(); } static std::basic_stringstream log_stream; @@ -147,14 +175,14 @@ IfcGeom::string_arg_filter tag_filter(IfcSchema::Type::IfcProxy, 8, IfcSchema::T struct geom_filter { - geom_filter(bool include, bool traverse) : type(UNUSED), include(include), traverse(traverse) {} - geom_filter() : type(UNUSED), include(false), traverse(false) {} - enum filter_type { UNUSED, ENTITY_TYPE, LAYER_NAME, ENTITY_ARG }; - filter_type type; - bool include; - bool traverse; - std::string arg; - std::set values; + geom_filter(bool include, bool traverse) : type(UNUSED), include(include), traverse(traverse) {} + geom_filter() : type(UNUSED), include(false), traverse(false) {} + enum filter_type { UNUSED, ENTITY_TYPE, LAYER_NAME, ENTITY_ARG }; + filter_type type; + bool include; + bool traverse; + std::string arg; + std::set values; }; // Specialized classes for knowing which type of filter we are validating within validate(). // Could not figure out easily how else to know it if using single type for both. @@ -171,128 +199,128 @@ bool init_input_file(const std::string& filename, IfcParse::IfcFile& ifc_file, b #if defined(_MSC_VER) && defined(_UNICODE) int wmain(int argc, wchar_t** argv) { - typedef po::wcommand_line_parser command_line_parser; - typedef wchar_t char_t; + typedef po::wcommand_line_parser command_line_parser; + typedef wchar_t char_t; - _setmode(_fileno(stdout), _O_U16TEXT); - _setmode(_fileno(stderr), _O_U16TEXT); + _setmode(_fileno(stdout), _O_U16TEXT); + _setmode(_fileno(stderr), _O_U16TEXT); #else -int main(int argc, char** argv) { - typedef po::command_line_parser command_line_parser; - typedef char char_t; + int main(int argc, char** argv) { + typedef po::command_line_parser command_line_parser; + typedef char char_t; #endif - double deflection_tolerance; - inclusion_filter include_filter; - inclusion_traverse_filter include_traverse_filter; - exclusion_filter exclude_filter; - exclusion_traverse_filter exclude_traverse_filter; - path_t filter_filename; - path_t default_material_filename; - std::string log_format; + double deflection_tolerance; + inclusion_filter include_filter; + inclusion_traverse_filter include_traverse_filter; + exclusion_filter exclude_filter; + exclusion_traverse_filter exclude_traverse_filter; + path_t filter_filename; + path_t default_material_filename; + std::string log_format; po::options_description generic_options("Command line options"); - generic_options.add_options() - ("help,h", "display usage information") - ("version", "display version information") - ("verbose,v", "more verbose log messages") - ("quiet,q", "less status and progress output") - ("stderr-progress", "output progress to stderr stream") - ("yes,y", "answer 'yes' automatically to possible confirmation queries (e.g. overwriting an existing output file)") - ("no-progress", "suppress possible progress bar type of prints that use carriage return") - ("log-format", po::value(&log_format), "log format: plain or json"); + generic_options.add_options() + ("help,h", "display usage information") + ("version", "display version information") + ("verbose,v", "more verbose log messages") + ("quiet,q", "less status and progress output") + ("stderr-progress", "output progress to stderr stream") + ("yes,y", "answer 'yes' automatically to possible confirmation queries (e.g. overwriting an existing output file)") + ("no-progress", "suppress possible progress bar type of prints that use carriage return") + ("log-format", po::value(&log_format), "log format: plain or json"); po::options_description fileio_options; - fileio_options.add_options() + fileio_options.add_options() #ifdef USE_MMAP - ("mmap", "use memory-mapped file for input") + ("mmap", "use memory-mapped file for input") #endif - ("input-file", new po::typed_value(0), "input IFC file") - ("output-file", new po::typed_value(0), "output geometry file"); + ("input-file", new po::typed_value(0), "input IFC file") + ("output-file", new po::typed_value(0), "output geometry file"); po::options_description geom_options("Geometry 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.") + 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.") + // 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.") - ("enable-layerset-slicing", - "Specifies whether to enable the slicing of products according " - "to their associated IfcMaterialLayerSet.") - ("include", po::value(&include_filter)->multitoken(), - "Specifies that the entities that match a specific filtering criteria are to be included in the geometrical output:\n" - "1) 'entities': the following list of types should be included. SVG output defaults " - "to IfcSpace to be included. The entity names are handled case-insensitively.\n" - "2) 'layers': the entities that are assigned to presentation layers of which names " - "match the given values should be included.\n" - "3) 'arg ': the following list of values for that specific argument should be included. " - "Currently supported arguments are GlobalId, Name, Description, and Tag.\n\n" - "The values for 'layers' and 'arg' are handled case-sensitively (wildcards supported)." - "--include and --exclude cannot be placed right before input file argument and " - "only single of each argument supported for now. See also --exclude.") + ("disable-opening-subtractions", + "Specifies whether to disable the boolean subtraction of " + "IfcOpeningElement Representations from their RelatingElements.") + ("enable-layerset-slicing", + "Specifies whether to enable the slicing of products according " + "to their associated IfcMaterialLayerSet.") + ("include", po::value(&include_filter)->multitoken(), + "Specifies that the entities that match a specific filtering criteria are to be included in the geometrical output:\n" + "1) 'entities': the following list of types should be included. SVG output defaults " + "to IfcSpace to be included. The entity names are handled case-insensitively.\n" + "2) 'layers': the entities that are assigned to presentation layers of which names " + "match the given values should be included.\n" + "3) 'arg ': the following list of values for that specific argument should be included. " + "Currently supported arguments are GlobalId, Name, Description, and Tag.\n\n" + "The values for 'layers' and 'arg' are handled case-sensitively (wildcards supported)." + "--include and --exclude cannot be placed right before input file argument and " + "only single of each argument supported for now. See also --exclude.") ("include+", po::value(&include_traverse_filter)->multitoken(), - "Same as --include but applies filtering also to the decomposition and/or containment (IsDecomposedBy, " - "HasOpenings, FillsVoid, ContainedInStructure) of the filtered entity, e.g. --include+=arg Name \"Level 1\" " - "includes entity with name \"Level 1\" and all of its children. See --include for more information. ") + "Same as --include but applies filtering also to the decomposition and/or containment (IsDecomposedBy, " + "HasOpenings, FillsVoid, ContainedInStructure) of the filtered entity, e.g. --include+=arg Name \"Level 1\" " + "includes entity with name \"Level 1\" and all of its children. See --include for more information. ") ("exclude", po::value(&exclude_filter)->multitoken(), - "Specifies that the entities that match a specific filtering criteria are to be excluded in the geometrical output." - "See --include for syntax and more details. The default value is '--exclude=entities IfcOpeningElement IfcSpace'.") + "Specifies that the entities that match a specific filtering criteria are to be excluded in the geometrical output." + "See --include for syntax and more details. The default value is '--exclude=entities IfcOpeningElement IfcSpace'.") ("exclude+", po::value(&exclude_traverse_filter)->multitoken(), - "Same as --exclude but applies filtering also to the decomposition and/or containment " - "of the filtered entity. See --include+ for more details.") + "Same as --exclude but applies filtering also to the decomposition and/or containment " + "of the filtered entity. See --include+ for more details.") ("no-normals", - "Disables computation of normals. Saves time and file size and is useful " - "in instances where you're going to recompute normals for the exported " - "model in other modelling application in any case.") + "Disables computation of normals. Saves time and file size and is useful " + "in instances where you're going to recompute normals for the exported " + "model in other modelling application in any case.") ("deflection-tolerance", po::value(&deflection_tolerance)->default_value(1e-3), - "Sets the deflection tolerance of the mesher, 1e-3 by default if not specified.") + "Sets the deflection tolerance of the mesher, 1e-3 by default if not specified.") ("generate-uvs", - "Generates UVs (texture coordinates) by using simple box projection. Requires normals. " - "Not guaranteed to work properly if used with --weld-vertices.") + "Generates UVs (texture coordinates) by using simple box projection. Requires normals. " + "Not guaranteed to work properly if used with --weld-vertices.") ("filter-file", new po::typed_value(&filter_filename), - "Specifies a filter file that describes the used filtering criteria. Supported formats " - "are '--include=arg GlobalId ...' and 'include arg GlobalId ...'. Spaces and tabs can be used as delimiters." - "Multiple filters of same type with different values can be inserted on their own lines. " - "See --include, --include+, --exclude, and --exclude+ for more details.") + "Specifies a filter file that describes the used filtering criteria. Supported formats " + "are '--include=arg GlobalId ...' and 'include arg GlobalId ...'. Spaces and tabs can be used as delimiters." + "Multiple filters of same type with different values can be inserted on their own lines. " + "See --include, --include+, --exclude, and --exclude+ for more details.") ("default-material-file", new po::typed_value(&default_material_filename), - "Specifies a material file that describes the material object types will have" - "if an object does not have any specified material in the IFC file."); + "Specifies a material file that describes the material object types will have" + "if an object does not have any specified material in the IFC file."); std::string bounds, offset_str; @@ -300,251 +328,251 @@ int main(int argc, char** argv) { std::string unicode_mode; #endif short precision; - double section_height; + double section_height; po::options_description serializer_options("Serialization options"); serializer_options.add_options() #ifdef HAVE_ICU ("unicode", po::value(&unicode_mode), - "Specifies the Unicode handling behavior when parsing the IFC file. " - "Accepted values 'utf8' (the default) and 'escape'.") + "Specifies the Unicode handling behavior when parsing the IFC file. " + "Accepted values 'utf8' (the default) and 'escape'.") #endif ("bounds", po::value(&bounds), - "Specifies the bounding rectangle, for example 512x512, to which the " - "output will be scaled. Only used when converting to SVG.") - ("section-height", po::value(§ion_height), - "Specifies the cut section height for SVG 2D geometry.") + "Specifies the bounding rectangle, for example 512x512, to which the " + "output will be scaled. Only used when converting to SVG.") + ("section-height", po::value(§ion_height), + "Specifies the cut section height for SVG 2D geometry.") ("use-element-names", - "Use entity names instead of unique IDs for naming elements upon serialization. " - "Applicable for OBJ, DAE, and SVG output.") + "Use entity names instead of unique IDs for naming elements upon serialization. " + "Applicable for OBJ, DAE, and SVG output.") ("use-element-guids", - "Use entity GUIDs instead of unique IDs for naming elements upon serialization. " - "Applicable for OBJ, DAE, and SVG output.") + "Use entity GUIDs instead of unique IDs for naming elements upon serialization. " + "Applicable for OBJ, DAE, and SVG output.") ("use-material-names", - "Use material names instead of unique IDs for naming materials upon serialization. " - "Applicable for OBJ and DAE output.") - ("use-element-types", - "Use element types instead of unique IDs for naming elements upon serialization. " - "Applicable for DAE output.") - ("use-element-hierarchy", - "Order the elements using their IfcBuildingStorey parent. " - "Applicable for DAE output.") + "Use material names instead of unique IDs for naming materials upon serialization. " + "Applicable for OBJ and DAE output.") + ("use-element-types", + "Use element types instead of unique IDs for naming elements upon serialization. " + "Applicable for DAE output.") + ("use-element-hierarchy", + "Order the elements using their IfcBuildingStorey parent. " + "Applicable for DAE output.") ("center-model", - "Centers the elements upon serialization by applying the center point of " - "all placements as an offset. Applicable for OBJ and DAE output. Can take several minutes on large models.") + "Centers the elements upon serialization by applying the center point of " + "all placements as an offset. Applicable for OBJ and DAE output. Can take several minutes on large models.") ("model-offset", po::value(&offset_str), - "Applies an arbitrary offset of form 'x;y;z' to all placements. Applicable for OBJ and DAE output.") - ("site-local-placement", - "Place elements locally in the IfcSite coordinate system, instead of placing " - "them in the IFC global coords. Applicable for OBJ and DAE output.") - ("building-local-placement", - "Similar to --site-local-placement, but placing elements in locally in the parent IfcBuilding coord system") + "Applies an arbitrary offset of form 'x;y;z' to all placements. Applicable for OBJ and DAE output.") + ("site-local-placement", + "Place elements locally in the IfcSite coordinate system, instead of placing " + "them in the IFC global coords. Applicable for OBJ and DAE output.") + ("building-local-placement", + "Similar to --site-local-placement, but placing elements in locally in the parent IfcBuilding coord system") ("precision", po::value(&precision)->default_value(SerializerSettings::DEFAULT_PRECISION), - "Sets the precision to be used to format floating-point values, 15 by default. " - "Use a negative value to use the system's default precision (should be 6 typically). " - "Applicable for OBJ and DAE output. For DAE output, value >= 15 means that up to 16 decimals are used, " - " and any other value means that 6 or 7 decimals are used."); + "Sets the precision to be used to format floating-point values, 15 by default. " + "Use a negative value to use the system's default precision (should be 6 typically). " + "Applicable for OBJ and DAE output. For DAE output, value >= 15 means that up to 16 decimals are used, " + " and any other value means that 6 or 7 decimals are used."); po::options_description cmdline_options; - cmdline_options.add(generic_options).add(fileio_options).add(geom_options).add(serializer_options); + cmdline_options.add(generic_options).add(fileio_options).add(geom_options).add(serializer_options); po::positional_options_description positional_options; - positional_options.add("input-file", 1); - positional_options.add("output-file", 1); + positional_options.add("input-file", 1); + positional_options.add("output-file", 1); po::variables_map vmap; try { - po::store(command_line_parser(argc, argv). - options(cmdline_options).positional(positional_options).run(), vmap); + po::store(command_line_parser(argc, argv). + options(cmdline_options).positional(positional_options).run(), vmap); } catch (const po::unknown_option& e) { - cerr_ << "[Error] Unknown option '" << e.get_option_name().c_str() << "'\n\n"; - print_usage(); - return EXIT_FAILURE; + cerr_ << "[Error] Unknown option '" << e.get_option_name().c_str() << "'\n\n"; + print_usage(); + return EXIT_FAILURE; } catch (const po::error_with_option_name& e) { - cerr_ << "[Error] Invalid usage of '" << e.get_option_name().c_str() << "': " << e.what() << "\n\n"; - return EXIT_FAILURE; + cerr_ << "[Error] Invalid usage of '" << e.get_option_name().c_str() << "': " << e.what() << "\n\n"; + return EXIT_FAILURE; } catch (const std::exception& e) { - cerr_ << "[Error] " << e.what() << "\n\n"; - print_usage(); - return EXIT_FAILURE; + cerr_ << "[Error] " << e.what() << "\n\n"; + print_usage(); + return EXIT_FAILURE; } catch (...) { - cerr_ << "[Error] Unknown error parsing command line options\n\n"; - print_usage(); - return EXIT_FAILURE; + cerr_ << "[Error] Unknown error parsing command line options\n\n"; + print_usage(); + return EXIT_FAILURE; } po::notify(vmap); - const bool mmap = vmap.count("mmap") != 0; - const bool verbose = vmap.count("verbose") != 0; - const bool no_progress = vmap.count("no-progress") != 0; - const bool quiet = vmap.count("quiet") != 0; - const bool stderr_progress = vmap.count("stderr-progress") != 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; + const bool mmap = vmap.count("mmap") != 0; + const bool verbose = vmap.count("verbose") != 0; + const bool no_progress = vmap.count("no-progress") != 0; + const bool quiet = vmap.count("quiet") != 0; + const bool stderr_progress = vmap.count("stderr-progress") != 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; + const bool merge_boolean_operands = vmap.count("merge-boolean-operands") != 0; #endif - const bool disable_opening_subtractions = vmap.count("disable-opening-subtractions") != 0; - const bool include_plan = vmap.count("plan") != 0; - const bool include_model = vmap.count("model") != 0 || (!include_plan); - const bool enable_layerset_slicing = vmap.count("enable-layerset-slicing") != 0; - const bool use_element_names = vmap.count("use-element-names") != 0; - const bool use_element_guids = vmap.count("use-element-guids") != 0; - const bool use_material_names = vmap.count("use-material-names") != 0; - const bool use_element_types = vmap.count("use-element-types") != 0; - const bool use_element_hierarchy = vmap.count("use-element-hierarchy") != 0; - const bool no_normals = vmap.count("no-normals") != 0; - const bool center_model = vmap.count("center-model") != 0; - const bool model_offset = vmap.count("model-offset") != 0; - const bool site_local_placement = vmap.count("site-local-placement") != 0; - const bool building_local_placement = vmap.count("building-local-placement") != 0; - const bool generate_uvs = vmap.count("generate-uvs") != 0; + const bool disable_opening_subtractions = vmap.count("disable-opening-subtractions") != 0; + const bool include_plan = vmap.count("plan") != 0; + const bool include_model = vmap.count("model") != 0 || (!include_plan); + const bool enable_layerset_slicing = vmap.count("enable-layerset-slicing") != 0; + const bool use_element_names = vmap.count("use-element-names") != 0; + const bool use_element_guids = vmap.count("use-element-guids") != 0; + const bool use_material_names = vmap.count("use-material-names") != 0; + const bool use_element_types = vmap.count("use-element-types") != 0; + const bool use_element_hierarchy = vmap.count("use-element-hierarchy") != 0; + const bool no_normals = vmap.count("no-normals") != 0; + const bool center_model = vmap.count("center-model") != 0; + const bool model_offset = vmap.count("model-offset") != 0; + const bool site_local_placement = vmap.count("site-local-placement") != 0; + const bool building_local_placement = vmap.count("building-local-placement") != 0; + const bool generate_uvs = vmap.count("generate-uvs") != 0; if (!quiet || vmap.count("version")) { - print_version(); - } + print_version(); + } - if (vmap.count("version")) { - return EXIT_SUCCESS; + if (vmap.count("version")) { + return EXIT_SUCCESS; } else if (vmap.count("help")) { - print_usage(false); - print_options(generic_options.add(geom_options).add(serializer_options)); - return EXIT_SUCCESS; + print_usage(false); + print_options(generic_options.add(geom_options).add(serializer_options)); + return EXIT_SUCCESS; } else if (!vmap.count("input-file")) { - std::cerr << "[Error] Input file not specified" << std::endl; - print_usage(); - return EXIT_FAILURE; + std::cerr << "[Error] Input file not specified" << std::endl; + print_usage(); + return EXIT_FAILURE; } - if (vmap.count("log-format") == 1) { - boost::to_lower(log_format); - if (log_format == "plain") { - Logger::OutputFormat(Logger::FMT_PLAIN); - } else if (log_format == "json") { - Logger::OutputFormat(Logger::FMT_JSON); - } else { - std::cerr << "[Error] --log-format should be either plain or json" << std::endl; - print_usage(); - return EXIT_FAILURE; - } - } + if (vmap.count("log-format") == 1) { + boost::to_lower(log_format); + if (log_format == "plain") { + Logger::OutputFormat(Logger::FMT_PLAIN); + } else if (log_format == "json") { + Logger::OutputFormat(Logger::FMT_JSON); + } else { + std::cerr << "[Error] --log-format should be either plain or json" << std::endl; + print_usage(); + return EXIT_FAILURE; + } + } if (!filter_filename.empty()) { - size_t num_filters = read_filters_from_file(IfcUtil::path::to_utf8(filter_filename), include_filter, include_traverse_filter, exclude_filter, exclude_traverse_filter); - if (num_filters) { - Logger::Notice(boost::lexical_cast(num_filters) + " filters read from specifified file."); - } else { - std::cerr << "[Error] No filters read from specifified file.\n"; - return EXIT_FAILURE; - } + size_t num_filters = read_filters_from_file(IfcUtil::path::to_utf8(filter_filename), include_filter, include_traverse_filter, exclude_filter, exclude_traverse_filter); + if (num_filters) { + Logger::Notice(boost::lexical_cast(num_filters) + " filters read from specifified file."); + } else { + std::cerr << "[Error] No filters read from specifified file.\n"; + return EXIT_FAILURE; + } } #ifdef HAVE_ICU if (!unicode_mode.empty()) { - if (unicode_mode == "utf8") { - IfcParse::IfcCharacterDecoder::mode = IfcParse::IfcCharacterDecoder::UTF8; - } else if (unicode_mode == "escape") { - IfcParse::IfcCharacterDecoder::mode = IfcParse::IfcCharacterDecoder::JSON; - } else { - cerr_ << "[Error] Invalid value for --unicode" << std::endl; - print_options(serializer_options); - return 1; - } + if (unicode_mode == "utf8") { + IfcParse::IfcCharacterDecoder::mode = IfcParse::IfcCharacterDecoder::UTF8; + } else if (unicode_mode == "escape") { + IfcParse::IfcCharacterDecoder::mode = IfcParse::IfcCharacterDecoder::JSON; + } else { + cerr_ << "[Error] Invalid value for --unicode" << std::endl; + print_options(serializer_options); + return 1; + } } #endif if (!default_material_filename.empty()) { - try { - IfcGeom::set_default_style_file(IfcUtil::path::to_utf8(default_material_filename)); - } catch (const std::exception& e) { - std::cerr << "[Error] Could not read default material file:" << std::endl; - std::cerr << e.what() << std::endl; - return EXIT_FAILURE; - } - } - - boost::optional bounding_width; - boost::optional bounding_height; - if (vmap.count("bounds") == 1) { - int w, h; - if (sscanf(bounds.c_str(), "%ux%u", &w, &h) == 2 && w > 0 && h > 0) { - bounding_width = w; - bounding_height = h; - } else { - cerr_ << "[Error] Invalid use of --bounds" << std::endl; - print_options(serializer_options); - return EXIT_FAILURE; - } - } - - const path_t input_filename = vmap["input-file"].as(); - if (!file_exists(IfcUtil::path::to_utf8(input_filename))) { - cerr_ << "[Error] Input file '" << input_filename << "' does not exist" << std::endl; + try { + IfcGeom::set_default_style_file(IfcUtil::path::to_utf8(default_material_filename)); + } catch (const std::exception& e) { + std::cerr << "[Error] Could not read default material file:" << std::endl; + std::cerr << e.what() << std::endl; return EXIT_FAILURE; + } } - // If no output filename is specified a Wavefront OBJ file will be output - // to maintain backwards compatibility with the obsolete IfcObj executable. - const path_t output_filename = vmap.count("output-file") == 1 - ? vmap["output-file"].as() - : change_extension(input_filename, IfcUtil::path::from_utf8(DEFAULT_EXTENSION)); - - if (output_filename.size() < 5) { - cerr_ << "[Error] Invalid or unsupported output file '" << output_filename << "' given" << std::endl; - print_usage(); - return EXIT_FAILURE; - } + boost::optional bounding_width; + boost::optional bounding_height; + if (vmap.count("bounds") == 1) { + int w, h; + if (sscanf(bounds.c_str(), "%ux%u", &w, &h) == 2 && w > 0 && h > 0) { + bounding_width = w; + bounding_height = h; + } else { + cerr_ << "[Error] Invalid use of --bounds" << std::endl; + print_options(serializer_options); + return EXIT_FAILURE; + } + } + + const path_t input_filename = vmap["input-file"].as(); + if (!file_exists(IfcUtil::path::to_utf8(input_filename))) { + cerr_ << "[Error] Input file '" << input_filename << "' does not exist" << std::endl; + return EXIT_FAILURE; + } + + // If no output filename is specified a Wavefront OBJ file will be output + // to maintain backwards compatibility with the obsolete IfcObj executable. + const path_t output_filename = vmap.count("output-file") == 1 + ? vmap["output-file"].as() + : change_extension(input_filename, IfcUtil::path::from_utf8(DEFAULT_EXTENSION)); + + if (output_filename.size() < 5) { + cerr_ << "[Error] Invalid or unsupported output file '" << output_filename << "' given" << std::endl; + print_usage(); + return EXIT_FAILURE; + } if (file_exists(IfcUtil::path::to_utf8(output_filename)) && !vmap.count("yes")) { - std::string answer; - cout_ << "A file '" << output_filename << "' already exists. Overwrite the existing file?" << std::endl; - std::cin >> answer; - if (!boost::iequals(answer, "yes") && !boost::iequals(answer, "y")) { - return EXIT_SUCCESS; - } + std::string answer; + cout_ << "A file '" << output_filename << "' already exists. Overwrite the existing file?" << std::endl; + std::cin >> answer; + if (!boost::iequals(answer, "yes") && !boost::iequals(answer, "y")) { + return EXIT_SUCCESS; + } } - Logger::SetOutput(&cout_, &log_stream); - Logger::Verbosity(verbose ? Logger::LOG_NOTICE : Logger::LOG_ERROR); + Logger::SetOutput(&cout_, &log_stream); + Logger::Verbosity(verbose ? Logger::LOG_NOTICE : Logger::LOG_ERROR); path_t output_temp_filename = output_filename + IfcUtil::path::from_utf8(TEMP_FILE_EXTENSION); - path_t output_extension = output_filename.substr(output_filename.size()-4); - boost::to_lower(output_extension); + path_t output_extension = output_filename.substr(output_filename.size()-4); + boost::to_lower(output_extension); IfcParse::IfcFile ifc_file; - const path_t OBJ = IfcUtil::path::from_utf8(".obj"), - MTL = IfcUtil::path::from_utf8(".mtl"), - DAE = IfcUtil::path::from_utf8(".dae"), - STP = IfcUtil::path::from_utf8(".stp"), - IGS = IfcUtil::path::from_utf8(".igs"), - SVG = IfcUtil::path::from_utf8(".svg"), - XML = IfcUtil::path::from_utf8(".xml"); + const path_t OBJ = IfcUtil::path::from_utf8(".obj"), + MTL = IfcUtil::path::from_utf8(".mtl"), + DAE = IfcUtil::path::from_utf8(".dae"), + STP = IfcUtil::path::from_utf8(".stp"), + IGS = IfcUtil::path::from_utf8(".igs"), + SVG = IfcUtil::path::from_utf8(".svg"), + XML = IfcUtil::path::from_utf8(".xml"); if (output_extension == XML) { - int exit_code = EXIT_FAILURE; - try { - if (init_input_file(IfcUtil::path::to_utf8(input_filename), ifc_file, no_progress || quiet, mmap)) { - time_t start, end; - time(&start); - XmlSerializer s(IfcUtil::path::to_utf8(output_temp_filename)); - s.setFile(&ifc_file); - Logger::Status("Writing XML output..."); - s.finalize(); - time(&end); - Logger::Status("Done! Conversion took " + format_duration(start, end)); + int exit_code = EXIT_FAILURE; + try { + if (init_input_file(IfcUtil::path::to_utf8(input_filename), ifc_file, no_progress || quiet, mmap)) { + time_t start, end; + time(&start); + XmlSerializer s(IfcUtil::path::to_utf8(output_temp_filename)); + s.setFile(&ifc_file); + Logger::Status("Writing XML output..."); + s.finalize(); + time(&end); + Logger::Status("Done! Conversion took " + format_duration(start, end)); - IfcUtil::path::rename_file(IfcUtil::path::to_utf8(output_temp_filename), IfcUtil::path::to_utf8(output_filename)); - exit_code = EXIT_SUCCESS; - } - } catch (const std::exception& e) { - Logger::Error(e); - } - write_log(!quiet); - return exit_code; + IfcUtil::path::rename_file(IfcUtil::path::to_utf8(output_temp_filename), IfcUtil::path::to_utf8(output_filename)); + exit_code = EXIT_SUCCESS; + } + } catch (const std::exception& e) { + Logger::Error(e); + } + write_log(!quiet); + return exit_code; } /// @todo Clean up this filter code further. @@ -556,247 +584,603 @@ int main(int argc, char** argv) { std::vector filter_funcs = setup_filters(used_filters, IfcUtil::path::to_utf8(output_extension)); if (filter_funcs.empty()) { - cerr_ << "[Error] Failed to set up geometry filters\n"; - return EXIT_FAILURE; + cerr_ << "[Error] Failed to set up geometry filters\n"; + return EXIT_FAILURE; } - if (!entity_filter.values.empty()) { entity_filter.update_description(); Logger::Notice(entity_filter.description); } - if (!layer_filter.values.empty()) { layer_filter.update_description(); Logger::Notice(layer_filter.description); } - if (!guid_filter.values.empty()) { guid_filter.update_description(); Logger::Notice(guid_filter.description); } - if (!name_filter.values.empty()) { name_filter.update_description(); Logger::Notice(name_filter.description); } - if (!desc_filter.values.empty()) { desc_filter.update_description(); Logger::Notice(desc_filter.description); } - if (!tag_filter.values.empty()) { tag_filter.update_description(); Logger::Notice(tag_filter.description); } + if (!entity_filter.values.empty()) { + entity_filter.update_description(); Logger::Notice(entity_filter.description); } + if (!layer_filter.values.empty()) { + layer_filter.update_description(); Logger::Notice(layer_filter.description); } + if (!guid_filter.values.empty()) { + guid_filter.update_description(); Logger::Notice(guid_filter.description); } + if (!name_filter.values.empty()) { + name_filter.update_description(); Logger::Notice(name_filter.description); } + if (!desc_filter.values.empty()) { + desc_filter.update_description(); Logger::Notice(desc_filter.description); } + if (!tag_filter.values.empty()) { + tag_filter.update_description(); Logger::Notice(tag_filter.description); } #ifdef _MSC_VER - if (output_extension == DAE || output_extension == STP || output_extension == IGS) { - // These serializers do not support opening unicode paths on Windows. Therefore - // a random temp file is generated using only ASCII characters instead. - std::random_device rng; - std::uniform_int_distribution index_dist(L'A', L'Z'); - output_temp_filename = L".ifcopenshell."; - for (int i = 0; i < 8; ++i) { - output_temp_filename.push_back(static_cast(index_dist(rng))); - } - output_temp_filename += L".tmp"; - } + if (output_extension == DAE || output_extension == STP || output_extension == IGS) { + // These serializers do not support opening unicode paths on Windows. Therefore + // a random temp file is generated using only ASCII characters instead. + std::random_device rng; + std::uniform_int_distribution index_dist(L'A', L'Z'); + output_temp_filename = L".ifcopenshell."; + for (int i = 0; i < 8; ++i) { + output_temp_filename.push_back(static_cast(index_dist(rng))); + } + output_temp_filename += L".tmp"; + } #endif - SerializerSettings settings; - /// @todo Make APPLY_DEFAULT_MATERIALS configurable? Quickly tested setting this to false and using obj exporter caused the program to crash and burn. - 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); + SerializerSettings settings; + /// @todo Make APPLY_DEFAULT_MATERIALS configurable? Quickly tested setting this to false and using obj exporter caused the program to crash and burn. + 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); + 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); - settings.set(IfcGeom::IteratorSettings::APPLY_LAYERSETS, enable_layerset_slicing); + 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); + settings.set(IfcGeom::IteratorSettings::APPLY_LAYERSETS, enable_layerset_slicing); settings.set(IfcGeom::IteratorSettings::NO_NORMALS, no_normals); settings.set(IfcGeom::IteratorSettings::GENERATE_UVS, generate_uvs); - settings.set(IfcGeom::IteratorSettings::SEARCH_FLOOR, use_element_hierarchy); - settings.set(IfcGeom::IteratorSettings::SITE_LOCAL_PLACEMENT, site_local_placement); - settings.set(IfcGeom::IteratorSettings::BUILDING_LOCAL_PLACEMENT, building_local_placement); - - + settings.set(IfcGeom::IteratorSettings::SEARCH_FLOOR, use_element_hierarchy); + settings.set(IfcGeom::IteratorSettings::SITE_LOCAL_PLACEMENT, site_local_placement); + settings.set(IfcGeom::IteratorSettings::BUILDING_LOCAL_PLACEMENT, building_local_placement); settings.set(SerializerSettings::USE_ELEMENT_NAMES, use_element_names); settings.set(SerializerSettings::USE_ELEMENT_GUIDS, use_element_guids); settings.set(SerializerSettings::USE_MATERIAL_NAMES, use_material_names); - settings.set(SerializerSettings::USE_ELEMENT_TYPES, use_element_types); - settings.set(SerializerSettings::USE_ELEMENT_HIERARCHY, use_element_hierarchy); + settings.set(SerializerSettings::USE_ELEMENT_TYPES, use_element_types); + settings.set(SerializerSettings::USE_ELEMENT_HIERARCHY, use_element_hierarchy); settings.set_deflection_tolerance(deflection_tolerance); settings.precision = precision; - boost::shared_ptr serializer; /**< @todo use std::unique_ptr when possible */ - if (output_extension == OBJ) { - // Do not use temp file for MTL as it's such a small file. - const path_t mtl_filename = change_extension(output_filename, MTL); - if (!use_world_coords) { - Logger::Notice("Using world coords when writing WaveFront OBJ files"); - settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, true); - } - serializer = boost::make_shared(IfcUtil::path::to_utf8(output_temp_filename), IfcUtil::path::to_utf8(mtl_filename), settings); + //////////////////////////////////////////////////////////// + // Set up serializer + //////////////////////////////////////////////////////////// + + boost::shared_ptr serializer; /**< @todo use std::unique_ptr when possible */ + + if (output_extension == OBJ) + { + // Do not use temp file for MTL as it's such a small file. + const path_t mtl_filename = change_extension(output_filename, MTL); + if (!use_world_coords) + { + Logger::Notice("Using world coords when writing WaveFront OBJ files"); + settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, true); + } + serializer = boost::make_shared(IfcUtil::path::to_utf8(output_temp_filename), IfcUtil::path::to_utf8(mtl_filename), settings); #ifdef WITH_OPENCOLLADA - } else if (output_extension == DAE) { - serializer = boost::make_shared(IfcUtil::path::to_utf8(output_temp_filename), settings); + } else if (output_extension == DAE) + { + serializer = boost::make_shared(IfcUtil::path::to_utf8(output_temp_filename), settings); #endif - } else if (output_extension == STP) { - serializer = boost::make_shared(IfcUtil::path::to_utf8(output_temp_filename), settings); - } else if (output_extension == IGS) { - IGESControl_Controller::Init(); // work around Open Cascade bug - serializer = boost::make_shared(IfcUtil::path::to_utf8(output_temp_filename), settings); - } else if (output_extension == SVG) { - settings.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, true); - serializer = boost::make_shared(IfcUtil::path::to_utf8(output_temp_filename), settings); - if (vmap.count("section-height") != 0) { - Logger::Notice("Overriding section height"); - static_cast(serializer.get())->setSectionHeight(section_height); - } - if (bounding_width.is_initialized() && bounding_height.is_initialized()) { - static_cast(serializer.get())->setBoundingRectangle(bounding_width.get(), bounding_height.get()); - } - } else { - cerr_ << "[Error] Unknown output filename extension '" << output_extension << "'\n"; - write_log(!quiet); - print_usage(); - return EXIT_FAILURE; - } + } else if (output_extension == STP) { + serializer = boost::make_shared(IfcUtil::path::to_utf8(output_temp_filename), settings); + } else if (output_extension == IGS) { + IGESControl_Controller::Init(); // work around Open Cascade bug + serializer = boost::make_shared(IfcUtil::path::to_utf8(output_temp_filename), settings); + } else if (output_extension == SVG) { + settings.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, true); + serializer = boost::make_shared(IfcUtil::path::to_utf8(output_temp_filename), settings); + if (vmap.count("section-height") != 0) { + Logger::Notice("Overriding section height"); + static_cast(serializer.get())->setSectionHeight(section_height); + } + if (bounding_width.is_initialized() && bounding_height.is_initialized()) { + static_cast(serializer.get())->setBoundingRectangle(bounding_width.get(), bounding_height.get()); + } + } else { + cerr_ << "[Error] Unknown output filename extension '" << output_extension << "'\n"; + write_log(!quiet); + print_usage(); + return EXIT_FAILURE; + } if (use_element_hierarchy && output_extension != DAE) { - cerr_ << "[Error] --use-element-hierarchy can be used only with .dae output.\n"; - /// @todo Lots of duplicate error-and-exit code. - write_log(!quiet); - print_usage(); - IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); - return EXIT_FAILURE; - } + cerr_ << "[Error] --use-element-hierarchy can be used only with .dae output.\n"; + /// @todo Lots of duplicate error-and-exit code. + write_log(!quiet); + print_usage(); + IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); + return EXIT_FAILURE; + } const bool is_tesselated = serializer->isTesselated(); // isTesselated() doesn't change at run-time - if (!is_tesselated) { - if (weld_vertices) { - Logger::Notice("Weld vertices setting ignored when writing non-tesselated output"); - } - if (generate_uvs) { - Logger::Notice("Generate UVs setting ignored when writing non-tesselated output"); - } - if (center_model || model_offset) { - Logger::Notice("Centering/offsetting model setting ignored when writing non-tesselated output"); - } + if (!is_tesselated) { + if (weld_vertices) { + Logger::Notice("Weld vertices setting ignored when writing non-tesselated output"); + } + if (generate_uvs) { + Logger::Notice("Generate UVs setting ignored when writing non-tesselated output"); + } + if (center_model || model_offset) { + Logger::Notice("Centering/offsetting model setting ignored when writing non-tesselated output"); + } - settings.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, true); - } + settings.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, true); + } - if (!serializer->ready()) { - IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); - write_log(!quiet); - return EXIT_FAILURE; - } + if (!serializer->ready()) { + IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); + write_log(!quiet); + return EXIT_FAILURE; + } - time_t start,end; - time(&start); - + time_t start,end; + time(&start); + if (!init_input_file(IfcUtil::path::to_utf8(input_filename), ifc_file, no_progress || quiet, mmap)) { - write_log(!quiet); - IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); /**< @todo Windows Unicode support */ - return EXIT_FAILURE; + write_log(!quiet); + IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); /**< @todo Windows Unicode support */ + return EXIT_FAILURE; } + + //////////////////////////////////////////////////////////// + // initialize geometry + //////////////////////////////////////////////////////////// + + serializer->setFile(&ifc_file); + IfcGeom::Iterator context_iterator(settings, &ifc_file, filter_funcs); - if (!context_iterator.initialize()) { - /// @todo It would be nice to know and print separate error prints for a case where we found no entities - /// and for a case we found no entities that satisfy our filtering criteria. - Logger::Error("No geometrical entities found"); - IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); - write_log(!quiet); - return EXIT_FAILURE; - } + IfcGeom::Kernel kernel; + std::string unit_name = "METER" ; + double unit_magnitude = 1.f; + IfcSchema::IfcRepresentation::list::ptr ok_mapped_representations; + IfcSchema::IfcRepresentation::list::ptr representations = + IfcSchema::IfcRepresentation::list::ptr(new IfcSchema::IfcRepresentation::list); + IfcSchema::IfcRepresentation::list::it representation_iterator; - serializer->setFile(context_iterator.getFile()); - if (convert_back_units) { - serializer->setUnitNameAndMagnitude(context_iterator.getUnitName(), static_cast(context_iterator.getUnitMagnitude())); - } else { - serializer->setUnitNameAndMagnitude("METER", 1.0f); - } + try { - serializer->writeHeader(); + // constructor + // TriangulationElement

* current_triangulation =0 ; + // BRepElement

* current_shape_model = 0; + // SerializedElement

* current_serialization = 0; + kernel.setValue(IfcGeom::Kernel::GV_MAX_FACES_TO_SEW, settings.get(IfcGeom::IteratorSettings::SEW_SHELLS) ? 1000 : -1); + kernel.setValue(IfcGeom::Kernel::GV_DIMENSIONALITY, (settings.get(IfcGeom::IteratorSettings::INCLUDE_CURVES) + ? (settings.get(IfcGeom::IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES) ? -1. : 0.) : +1.)); + if (settings.get(IfcGeom::IteratorSettings::BUILDING_LOCAL_PLACEMENT)) { + if (settings.get(IfcGeom::IteratorSettings::SITE_LOCAL_PLACEMENT)) { + Logger::Message(Logger::LOG_WARNING, "building-local-placement takes precedence over site-local-placement"); + } + kernel.set_conversion_placement_rel_to(IfcSchema::Type::IfcBuilding); + } else if (settings.get(IfcGeom::IteratorSettings::SITE_LOCAL_PLACEMENT)) { + kernel.set_conversion_placement_rel_to(IfcSchema::Type::IfcSite); + } - int old_progress = quiet ? 0 : -1; + // initialize() + // initunits() + IfcSchema::IfcProject::list::ptr projects = ifc_file.entitiesByType(); + std::set allowed_context_types; + std::set context_types; + double lowest_precision_encountered = std::numeric_limits::infinity(); + bool any_precision_encountered = false; + + if (projects->size() == 1) { + IfcSchema::IfcProject* project = *projects->begin(); + std::pair length_unit = kernel.initializeUnits(project->UnitsInContext()); + unit_name = length_unit.first; + unit_magnitude = length_unit.second; + } else { + Logger::Error("A single IfcProject is expected (encountered " + boost::lexical_cast(projects->size()) + "); unable to read unit information."); + } + + allowed_context_types.insert("model"); + allowed_context_types.insert("plan"); + allowed_context_types.insert("notdefined"); + if (!settings.get(IfcGeom::IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES)) { + // Really this should only be 'Model', as per + // the standard 'Design' is deprecated. So, + // just for backwards compatibility: + context_types.insert("model"); + context_types.insert("design"); + // Some earlier (?) versions DDS-CAD output their own ContextTypes + context_types.insert("model view"); + context_types.insert("detail view"); + } + if (settings.get(IfcGeom::IteratorSettings::INCLUDE_CURVES)) { + context_types.insert("plan"); + } - if (is_tesselated && (center_model || model_offset)) { - double* offset = serializer->settings().offset; - if (center_model) { - if (site_local_placement || building_local_placement) { - Logger::Error("Cannot use --center-model together with --{site,building}-local-placement"); - return EXIT_FAILURE; - } + representations = IfcSchema::IfcRepresentation::list::ptr(new IfcSchema::IfcRepresentation::list); + ok_mapped_representations = IfcSchema::IfcRepresentation::list::ptr(new IfcSchema::IfcRepresentation::list); - if (!quiet) Logger::Status("Computing bounds..."); - context_iterator.compute_bounds(); - if (!quiet) Logger::Status("Done!"); + IfcSchema::IfcGeometricRepresentationContext::list::it it; + IfcSchema::IfcGeometricRepresentationSubContext::list::it jt; + IfcSchema::IfcGeometricRepresentationContext::list::ptr contexts = + ifc_file.entitiesByType(); - gp_XYZ center = (context_iterator.bounds_min() + context_iterator.bounds_max()) * 0.5; - offset[0] = -center.X(); - offset[1] = -center.Y(); - offset[2] = -center.Z(); + IfcSchema::IfcGeometricRepresentationContext::list::ptr filtered_contexts (new IfcSchema::IfcGeometricRepresentationContext::list); + + for (it = contexts->begin(); it != contexts->end(); ++it) { + IfcSchema::IfcGeometricRepresentationContext* context = *it; + if (context->is(IfcSchema::Type::IfcGeometricRepresentationSubContext)) { + // Continue, as the list of subcontexts will be considered + // by the parent's context inverse attributes. + continue; + } + try { + if (context->hasContextType()) { + std::string context_type = context->ContextType(); + boost::to_lower(context_type); + if (allowed_context_types.find(context_type) == allowed_context_types.end()) { + Logger::Message(Logger::LOG_ERROR, std::string("ContextType '") + context->ContextType() + "' not allowed:", context->entity); + } // == allowed_context_types.end() + if (context_types.find(context_type) != context_types.end()) { + filtered_contexts->push(context); + } // != context_types.end() + } // hasContextType() + } catch (const std::exception& e) { + Logger::Error(e); + } + } // end iterating contexts + + // In case no contexts are identified based on their ContextType, all contexts are + // considered. Note that sub contexts are excluded as they are considered later on. + if (filtered_contexts->size() == 0) { + for (it = contexts->begin(); it != contexts->end(); ++it) { + IfcSchema::IfcGeometricRepresentationContext* context = *it; + if (!context->is(IfcSchema::Type::IfcGeometricRepresentationSubContext)) { + filtered_contexts->push(context); + } + } + } + + for (it = filtered_contexts->begin(); it != filtered_contexts->end(); ++it) { + IfcSchema::IfcGeometricRepresentationContext* context = *it; + + representations->push(context->RepresentationsInContext()); + try { + if (context->hasPrecision() && context->Precision() < lowest_precision_encountered) { + lowest_precision_encountered = context->Precision(); + any_precision_encountered = true; + } + } catch (const std::exception& e) { + Logger::Error(e); + } + + IfcSchema::IfcGeometricRepresentationSubContext::list::ptr sub_contexts = context->HasSubContexts(); + for (jt = sub_contexts->begin(); jt != sub_contexts->end(); ++jt) { + representations->push((*jt)->RepresentationsInContext()); + } + // There is no need for full recursion as the following is governed by the schema: + // WR31: The parent context shall not be another geometric representation sub context. + } // end iterating filtered_contexts + + if (any_precision_encountered) { + // Some arbitrary factor that has proven to work better for the models in the set of test files. + lowest_precision_encountered *= 10.; + lowest_precision_encountered *= unit_magnitude; + if (lowest_precision_encountered < 1.e-7) { + Logger::Message(Logger::LOG_WARNING, "Precision lower than 0.0000001 meter not enforced"); + kernel.setValue(IfcGeom::Kernel::GV_PRECISION, 1.e-7); } else { - if (sscanf(offset_str.c_str(), "%lf;%lf;%lf", &offset[0], &offset[1], &offset[2]) != 3) { - cerr_ << "[Error] Invalid use of --model-offset\n"; - IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); - print_options(serializer_options); - return EXIT_FAILURE; - } + kernel.setValue(IfcGeom::Kernel::GV_PRECISION, lowest_precision_encountered); } + } else { + kernel.setValue(IfcGeom::Kernel::GV_PRECISION, 1.e-5); + } - std::stringstream msg; - msg << "Using model offset (" << offset[0] << "," << offset[1] << "," << offset[2] << ")"; - Logger::Notice(msg.str()); + if (representations->size() == 0) { + Logger::Message(Logger::LOG_ERROR, "No representations encountered in relevant contexts, using all"); + representations = ifc_file.entitiesByType(); + } + + if (representations->size() == 0) { + Logger::Message(Logger::LOG_ERROR, "No representations encountered, aborting"); + return 0; + } + + // representation_iterator = representations->begin(); + // ifcproducts.reset(); + + // if (!create()) { + // return false; + // } + + // done = 0; + // total = representations->size(); + + // return true; + } catch (const std::exception& e) { + Logger::Error(e); + Logger::Error("No geometrical entities found"); + IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); + write_log(!quiet); + return EXIT_FAILURE; + } + + //replaces: + // if (!context_iterator.initialize()) { + // /// @todo It would be nice to know and print separate error prints for a case where we found no entities + // /// and for a case we found no entities that satisfy our filtering criteria. + // Logger::Error("No geometrical entities found"); + // IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); + // write_log(!quiet); + // return EXIT_FAILURE; + // } + + if (convert_back_units) { + // serializer->setUnitNameAndMagnitude(context_iterator.getUnitName(), static_cast(context_iterator.getUnitMagnitude())); + serializer->setUnitNameAndMagnitude(unit_name, static_cast(unit_magnitude)); + } else { + serializer->setUnitNameAndMagnitude("METER", 1.0f); } - if (!quiet) { - Logger::Status("Creating geometry..."); - } + serializer->writeHeader(); - // 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 - // non-null return value guarantees that a successfully processed product is - // available. - size_t num_created = 0; - - do { - IfcGeom::Element *geom_object = context_iterator.get(); - - if (is_tesselated) - { - serializer->write(static_cast*>(geom_object)); - } - else - { - serializer->write(static_cast*>(geom_object)); - } - - if (!no_progress) { - if (quiet) { - const int progress = context_iterator.progress(); - for (; old_progress < progress; ++old_progress) { - std::cout << "."; - if (stderr_progress) - std::cerr << "."; - } - std::cout << std::flush; - if (stderr_progress) - std::cerr << std::flush; - } else { - const int progress = context_iterator.progress() / 2; - if (old_progress != progress) Logger::ProgressBar(progress); - old_progress = progress; - } + int old_progress = quiet ? 0 : -1; + Bounds model_bounds; + if (is_tesselated && (center_model || model_offset)) { + double* offset = serializer->settings().offset; + if (center_model) { + if (site_local_placement || building_local_placement) { + Logger::Error("Cannot use --center-model together with --{site,building}-local-placement"); + return EXIT_FAILURE; } - } while (++num_created, context_iterator.next()); - if (!no_progress && quiet) { - for (; old_progress < 100; ++old_progress) { - std::cout << "."; - if (stderr_progress) - std::cerr << "."; - } - std::cout << std::flush; - if (stderr_progress) - std::cerr << std::flush; - } else { - Logger::Status("\rDone creating geometry (" + boost::lexical_cast(num_created) + - " objects) "); - } + if (!quiet) Logger::Status("Computing bounds..."); + model_bounds = compute_bounds( &ifc_file, kernel ); + if (!quiet) Logger::Status("Done!"); + gp_XYZ center = (model_bounds.min + model_bounds.max) * 0.5; + offset[0] = -center.X(); + offset[1] = -center.Y(); + offset[2] = -center.Z(); + } else { + if (sscanf(offset_str.c_str(), "%lf;%lf;%lf", &offset[0], &offset[1], &offset[2]) != 3) { + cerr_ << "[Error] Invalid use of --model-offset\n"; + IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); + print_options(serializer_options); + return EXIT_FAILURE; + } + } + + std::stringstream msg; + msg << "Using model offset (" << offset[0] << "," << offset[1] << "," << offset[2] << ")"; + Logger::Notice(msg.str()); + } + + if (!quiet) { + 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 + // non-null return value guarantees that a successfully processed product is + // available. + size_t num_created = 0; + + + //////////////////////////////////////////////////////////// + // start initializing elements for threading + //////////////////////////////////////////////////////////// + + std::vector IfcproductRepresentations; + + // do { + // IfcGeom::Element *geom_object = context_iterator.get(); + + // if (is_tesselated) + // { + // serializer->write(static_cast*>(geom_object)); + // } + // else + // { + // serializer->write(static_cast*>(geom_object)); + // } + + // if (!no_progress) { + // if (quiet) { + // const int progress = context_iterator.progress(); + // for (; old_progress < progress; ++old_progress) { + // std::cout << "."; + // if (stderr_progress) + // std::cerr << "."; + // } + // std::cout << std::flush; + // if (stderr_progress) + // std::cerr << std::flush; + // } else { + // const int progress = context_iterator.progress() / 2; + // if (old_progress != progress) Logger::ProgressBar(progress); + // old_progress = progress; + // } + // } + // } while (++num_created, context_iterator.next()); + + // if (!no_progress && quiet) { + // for (; old_progress < 100; ++old_progress) { + // std::cout << "."; + // if (stderr_progress) + // std::cerr << "."; + // } + // std::cout << std::flush; + // if (stderr_progress) + // std::cerr << std::flush; + // } else { + // Logger::Status("\rDone creating geometry (" + boost::lexical_cast(num_created) + + // " objects) "); + // } + + + //////////////////////////////////////////////////////////// + // Copy/Paste from IfcInfo, fine-tooth comb over vars + //////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// + /////// find products associated with representations (... ?) + //////////////////////////////////////////////////////////// + IfcSchema::IfcProduct::list::ptr ifcproducts; + IfcSchema::IfcProduct::list::it ifcproduct_iterator; + std::vector filters_; + + IfcGeom::layer_filter layer_filter; + IfcGeom::entity_filter entity_filter; + IfcGeom::string_arg_filter guid_filter(IfcSchema::Type::IfcRoot, 0); + IfcGeom::string_arg_filter name_filter(IfcSchema::Type::IfcRoot, 2); + IfcGeom::string_arg_filter desc_filter(IfcSchema::Type::IfcRoot, 3); + IfcGeom::string_arg_filter tag_filter(IfcSchema::Type::IfcProxy, 8, + IfcSchema::Type::IfcElement, + 7); + filters_.emplace_back(boost::ref(layer_filter)); + filters_.emplace_back(boost::ref(entity_filter)); + filters_.emplace_back(boost::ref(guid_filter)); + filters_.emplace_back(boost::ref(name_filter)); + filters_.emplace_back(boost::ref(desc_filter)); + filters_.emplace_back(boost::ref(tag_filter)); + bool geometry_reuse_ok_for_current_representation_; + + // functor + struct filter_match + { + filter_match(IfcSchema::IfcProduct *prod) : product(prod) {} + bool operator()(const IfcGeom::filter_t &filter) const { return filter(product); } + IfcSchema::IfcProduct *product; + }; + + Logger::Status("starting to iterate over representations "); + + start = std::chrono::system_clock::now(); + int index_count = 0; + for (representation_iterator = representations->begin(); + representation_iterator != representations->end(); representation_iterator++) + { + IfcSchema::IfcRepresentation *representation = *representation_iterator; + ifcproducts.reset(); + ifcproducts = IfcSchema::IfcProduct::list::ptr(new IfcSchema::IfcProduct::list); + IfcSchema::IfcProduct::list::ptr unfiltered_products = + kernel.products_represented_by(representation); + geometry_reuse_ok_for_current_representation_ = reuse_ok_(settings, unfiltered_products); + IfcSchema::IfcRepresentationMap::list::ptr maps = representation->RepresentationMap(); + if (!geometry_reuse_ok_for_current_representation_ && maps->size() == 1) + { + // unfiltered_products contains products represented by this representation by means of + // mapped items. For example because of openings applied to products, reuse might not be + // acceptable and then the products will be processed by means of their immediate + // representation and not the mapped representation. + + // IfcRepresentationMaps are also used for IfcTypeProducts, so an additional check is + // performed whether the map is indeed used by IfcMappedItems. + IfcSchema::IfcRepresentationMap *map = *maps->begin(); + if (map->MapUsage()->size() > 0) + { + // _nextShape(); + // continue; + // NOTE(sander): is this equivalent to _nextShape() ? + continue; + } + } + + bool representation_processed_as_mapped_item = false; + IfcSchema::IfcRepresentation *representation_mapped_to = + kernel.representation_mapped_to(representation); + if (representation_mapped_to) + { + // Check if this representation has (or will be) processed as part its mapped + // representation + bool contains = ok_mapped_representations->contains(representation_mapped_to); + bool reuse = reuse_ok_(settings, kernel.products_represented_by(representation_mapped_to)); + representation_processed_as_mapped_item = contains || reuse; + } + if (representation_processed_as_mapped_item) + { + ok_mapped_representations->push(representation_mapped_to); + // _nextShape(); + // continue; + continue; + } + // Filter the products based on the set of entities and/or names being included or excluded + // for processing. + for (IfcSchema::IfcProduct::list::it jt = unfiltered_products->begin(); + jt != unfiltered_products->end(); ++jt) + { + IfcSchema::IfcProduct *prod = *jt; + if (boost::all(filters_, filter_match(prod))) + { + ifcproducts->push(prod); + } + } // end for unfiltered_products + + for (ifcproduct_iterator = ifcproducts->begin(); ifcproduct_iterator != ifcproducts->end(); + ifcproduct_iterator++) + { + IfcproductRepresentation ir; + ir.index = index_count; + ir.product = *ifcproduct_iterator; + ir.representation = representation; + IfcproductRepresentations.push_back(ir); + index_count++; + } // end for ifcproducts + + } // for representation in representations + end = std::chrono::system_clock::now(); + elapsed_seconds = end - start; + Logger::Status("iterated over representations: " + std::to_string(elapsed_seconds.count())); + Logger::Status("count: " + std::to_string(index_count)); + + const unsigned int conc_threads = std::thread::hardware_concurrency(); + std::cout << "amount of threads available for use on this machine: " << conc_threads << std::endl; + std::vector> threadpool; + count = 0; + for (int j = 0; j < (int)IfcproductRepresentations.size();) + { + IfcproductRepresentation &r = IfcproductRepresentations[j]; + if (threadpool.size() < conc_threads) + { + std::future fu = std::async(std::launch::async, create_element, std::ref(settings), std::ref(r)); + threadpool.emplace_back(std::move(fu)); + j++; + } + else + { + bool waiting = true; + while (waiting) + { + for (int i = 0; i < (int)threadpool.size(); i++) + { + std::future &fu = threadpool[i]; + std::future_status status; + status = fu.wait_for(std::chrono::seconds(0)); + if (status == std::future_status::ready) + { + fu.get(); + threadpool.erase(threadpool.begin() + i); + waiting = false; + } // if + } // for + } // while + } // else + } + + for (std::future &fu : threadpool) + { + fu.get(); + } + + + + //////////////////////////////////////////////////////////// + // + //////////////////////////////////////////////////////////// + serializer->finalize(); // Make sure the dtor is explicitly run here (e.g. output files are closed before renaming them). serializer.reset(); @@ -805,53 +1189,53 @@ int main(int argc, char** argv) { // Do not remove the temp file as user can salvage the conversion result from it. bool successful = IfcUtil::path::rename_file(IfcUtil::path::to_utf8(output_temp_filename), IfcUtil::path::to_utf8(output_filename)); if (!successful) { - cerr_ << "Unable to write output file '" << output_filename << "', see '" << - output_temp_filename << "' for the conversion result."; + cerr_ << "Unable to write output file '" << output_filename << "', see '" << + output_temp_filename << "' for the conversion result."; } - write_log(!quiet); + write_log(!quiet); - time(&end); + time(&end); if (!quiet) { - Logger::Status("\nConversion took " + format_duration(start, end)); + Logger::Status("\nConversion took " + format_duration(start, end)); } return successful ? EXIT_SUCCESS : EXIT_FAILURE; -} + } -std::string format_duration(time_t start, time_t end) -{ + std::string format_duration(time_t start, time_t end) + { int seconds = (int)difftime(end, start); std::stringstream ss; int minutes = seconds / 60; seconds = seconds % 60; if (minutes > 0) { - ss << minutes << " minute"; - if (minutes == 0 || minutes > 1) { - ss << "s"; - } - ss << " "; + ss << minutes << " minute"; + if (minutes == 0 || minutes > 1) { + ss << "s"; + } + ss << " "; } ss << seconds << " second"; if (seconds == 0 || seconds > 1) { - ss << "s"; + ss << "s"; } return ss.str(); -} + } -void write_log(bool header) { - path_t log = log_stream.str(); - if (!log.empty()) { - if (header) { - cout_ << "\nLog:\n"; - } - cout_ << log << std::endl; - } -} + void write_log(bool header) { + path_t log = log_stream.str(); + if (!log.empty()) { + if (header) { + cout_ << "\nLog:\n"; + } + cout_ << log << std::endl; + } + } -bool init_input_file(const std::string &filename, IfcParse::IfcFile &ifc_file, bool no_progress, bool mmap) -{ + bool init_input_file(const std::string &filename, IfcParse::IfcFile &ifc_file, bool no_progress, bool mmap) + { time_t start, end; // Prevent IfcFile::Init() prints by setting output to null temporarily @@ -859,62 +1243,62 @@ bool init_input_file(const std::string &filename, IfcParse::IfcFile &ifc_file, b time(&start); #ifdef USE_MMAP - if (!ifc_file.Init(filename, mmap)) { + if (!ifc_file.Init(filename, mmap)) { #else - (void)mmap; - if (!ifc_file.Init(filename)) { + (void)mmap; + if (!ifc_file.Init(filename)) { #endif Logger::Error("Unable to parse input file '" + filename + "'"); return false; + } + time(&end); + + if (no_progress) { Logger::SetOutput(&cout_, &log_stream); } + else { Logger::Status("Parsing input file took " + format_duration(start, end)); } + + return true; } - time(&end); - if (no_progress) { Logger::SetOutput(&cout_, &log_stream); } - else { Logger::Status("Parsing input file took " + format_duration(start, end)); } - - return true; -} - -bool append_filter(const std::string& type, const std::vector& values, geom_filter& filter) -{ - geom_filter temp; - parse_filter(temp, values); - // Merge values only if type and arg match. - if ((filter.type != geom_filter::UNUSED && filter.type != temp.type) || (!filter.arg.empty() && filter.arg != temp.arg)) { + bool append_filter(const std::string& type, const std::vector& values, geom_filter& filter) + { + geom_filter temp; + parse_filter(temp, values); + // Merge values only if type and arg match. + if ((filter.type != geom_filter::UNUSED && filter.type != temp.type) || (!filter.arg.empty() && filter.arg != temp.arg)) { cerr_ << "[Error] Multiple '" << type.c_str() << "' filters specified with different criteria\n"; return false; + } + filter.type = temp.type; + filter.values.insert(temp.values.begin(), temp.values.end()); + filter.arg = temp.arg; + return true; } - filter.type = temp.type; - filter.values.insert(temp.values.begin(), temp.values.end()); - filter.arg = temp.arg; - return true; -} -size_t read_filters_from_file( - const std::string& filename, - inclusion_filter& include_filter, - inclusion_traverse_filter& include_traverse_filter, - exclusion_filter& exclude_filter, - exclusion_traverse_filter& exclude_traverse_filter) -{ - std::ifstream filter_file(IfcUtil::path::from_utf8(filename).c_str()); + size_t read_filters_from_file( + const std::string& filename, + inclusion_filter& include_filter, + inclusion_traverse_filter& include_traverse_filter, + exclusion_filter& exclude_filter, + exclusion_traverse_filter& exclude_traverse_filter) + { + std::ifstream filter_file(IfcUtil::path::from_utf8(filename).c_str()); - if (!filter_file.is_open()) { + if (!filter_file.is_open()) { cerr_ << "[Error] Unable to open filter file '" << IfcUtil::path::from_utf8(filename) << "' or the file does not exist.\n"; return 0; - } + } - size_t line_number = 1, num_filters = 0; - for (std::string line; std::getline(filter_file, line); ++line_number) { + size_t line_number = 1, num_filters = 0; + for (std::string line; std::getline(filter_file, line); ++line_number) { boost::trim(line); if (line.empty()) { - continue; + continue; } std::vector values; boost::split(values, line, boost::is_any_of("\t "), boost::token_compress_on); if (values.empty()) { - continue; + continue; } std::string type = values.front(); @@ -924,147 +1308,262 @@ size_t read_filters_from_file( boost::trim_left_if(type, boost::is_any_of("-")); size_t equal_pos = type.find('='); if (equal_pos != std::string::npos) { - std::string value = type.substr(equal_pos + 1); - type = type.substr(0, equal_pos); - values.insert(values.begin(), value); + std::string value = type.substr(equal_pos + 1); + type = type.substr(0, equal_pos); + values.insert(values.begin(), value); } try { - if (type == "include") { if (append_filter("include", values, include_filter)) { ++num_filters; } } - else if (type == "include+") { if (append_filter("include+", values, include_traverse_filter)) { ++num_filters; } } - else if (type == "exclude") { if (append_filter("exclude", values, exclude_filter)) { ++num_filters; } } - else if (type == "exclude+") { if (append_filter("exclude+", values, exclude_traverse_filter)) { ++num_filters; } } - else { - cerr_ << "[Error] Invalid filtering type at line " << boost::lexical_cast(line_number) << "\n"; - return 0; - } - } catch(...) { - cerr_ << "[Error] Unable to parse filter at line " << boost::lexical_cast(line_number) << ".\n"; + if (type == "include") { if (append_filter("include", values, include_filter)) { ++num_filters; } } + else if (type == "include+") { if (append_filter("include+", values, include_traverse_filter)) { ++num_filters; } } + else if (type == "exclude") { if (append_filter("exclude", values, exclude_filter)) { ++num_filters; } } + else if (type == "exclude+") { if (append_filter("exclude+", values, exclude_traverse_filter)) { ++num_filters; } } + else { + cerr_ << "[Error] Invalid filtering type at line " << boost::lexical_cast(line_number) << "\n"; return 0; + } + } catch(...) { + cerr_ << "[Error] Unable to parse filter at line " << boost::lexical_cast(line_number) << ".\n"; + return 0; } + } + return num_filters; } - return num_filters; -} -void parse_filter(geom_filter &filter, const std::vector& values) -{ - if (values.size() == 0) { + void parse_filter(geom_filter &filter, const std::vector& values) + { + if (values.size() == 0) { throw po::validation_error(po::validation_error::at_least_one_value_required); - } - std::string type = *values.begin(); - if (type == "entities") { + } + std::string type = *values.begin(); + if (type == "entities") { filter.type = geom_filter::ENTITY_TYPE; - } else if (type == "layers") { + } else if (type == "layers") { filter.type = geom_filter::LAYER_NAME; - } else if (type == "arg") { + } else if (type == "arg") { filter.type = geom_filter::ENTITY_ARG; filter.arg = *(values.begin() + 1); if (std::find(supported_args.begin(), supported_args.end(), filter.arg) == supported_args.end()) { - throw po::validation_error(po::validation_error::invalid_option_value); + throw po::validation_error(po::validation_error::invalid_option_value); } - } else { + } else { throw po::validation_error(po::validation_error::invalid_option_value); + } + filter.values.insert(values.begin() + (filter.type == geom_filter::ENTITY_ARG ? 2 : 1), values.end()); } - filter.values.insert(values.begin() + (filter.type == geom_filter::ENTITY_ARG ? 2 : 1), values.end()); -} -void validate(boost::any& v, const std::vector& values, inclusion_filter*, int) -{ - /// @todo For now only single --include, --include+, --exclude, or --exclude+ supported. Support having multiple. - po::validators::check_first_occurrence(v); - inclusion_filter filter; - parse_filter(filter, values); - v = filter; -} + void validate(boost::any& v, const std::vector& values, inclusion_filter*, int) + { + /// @todo For now only single --include, --include+, --exclude, or --exclude+ supported. Support having multiple. + po::validators::check_first_occurrence(v); + inclusion_filter filter; + parse_filter(filter, values); + v = filter; + } -void validate(boost::any& v, const std::vector& values, inclusion_traverse_filter*, int) -{ - po::validators::check_first_occurrence(v); - inclusion_traverse_filter filter; - parse_filter(filter, values); - v = filter; -} + void validate(boost::any& v, const std::vector& values, inclusion_traverse_filter*, int) + { + po::validators::check_first_occurrence(v); + inclusion_traverse_filter filter; + parse_filter(filter, values); + v = filter; + } -void validate(boost::any& v, const std::vector& values, exclusion_filter*, int) -{ - po::validators::check_first_occurrence(v); - exclusion_filter filter; - parse_filter(filter, values); - v = filter; -} + void validate(boost::any& v, const std::vector& values, exclusion_filter*, int) + { + po::validators::check_first_occurrence(v); + exclusion_filter filter; + parse_filter(filter, values); + v = filter; + } -void validate(boost::any& v, const std::vector& values, exclusion_traverse_filter*, int) -{ - po::validators::check_first_occurrence(v); - exclusion_traverse_filter filter; - parse_filter(filter, values); - v = filter; -} + void validate(boost::any& v, const std::vector& values, exclusion_traverse_filter*, int) + { + po::validators::check_first_occurrence(v); + exclusion_traverse_filter filter; + parse_filter(filter, values); + v = filter; + } -/// @todo Clean up this filter initialization code further. -/// @return References to the used filter functors, if none an error occurred. -std::vector setup_filters(const std::vector& filters, const std::string& output_extension) -{ - std::vector filter_funcs; - BOOST_FOREACH(const geom_filter& f, filters) { + /// @todo Clean up this filter initialization code further. + /// @return References to the used filter functors, if none an error occurred. + std::vector setup_filters(const std::vector& filters, const std::string& output_extension) + { + std::vector filter_funcs; + BOOST_FOREACH(const geom_filter& f, filters) { if (f.type == geom_filter::ENTITY_TYPE) { - entity_filter.include = f.include; - entity_filter.traverse = f.traverse; - try { - entity_filter.populate(f.values); - } catch (const IfcParse::IfcException& e) { - cerr_ << "[Error] " << e.what() << std::endl; - return std::vector(); - } - } else if (f.type == geom_filter::LAYER_NAME) { - layer_filter.include = f.include; - layer_filter.traverse = f.traverse; - layer_filter.populate(f.values); - } else if (f.type == geom_filter::ENTITY_ARG) { - if (f.arg == GUID_ARG) { - guid_filter.include = f.include; - guid_filter.traverse = f.traverse; - guid_filter.populate(f.values); - } else if (f.arg == NAME_ARG) { - name_filter.include = f.include; - name_filter.traverse = f.traverse; - name_filter.populate(f.values); - } else if (f.arg == DESC_ARG) { - desc_filter.include = f.include; - desc_filter.traverse = f.traverse; - desc_filter.populate(f.values); - } else if (f.arg == TAG_ARG) { - tag_filter.include = f.include; - tag_filter.traverse = f.traverse; - tag_filter.populate(f.values); - } - } - } - - // If no entity names are specified these are the defaults to skip from output - if (entity_filter.values.empty()) { - try { - std::set entities; - entities.insert("IfcSpace"); - if (output_extension == ".svg") { - entity_filter.include = true; - } else { - entities.insert("IfcOpeningElement"); - } - entity_filter.populate(entities); - } catch (const IfcParse::IfcException& e) { + entity_filter.include = f.include; + entity_filter.traverse = f.traverse; + try { + entity_filter.populate(f.values); + } catch (const IfcParse::IfcException& e) { cerr_ << "[Error] " << e.what() << std::endl; return std::vector(); + } + } else if (f.type == geom_filter::LAYER_NAME) { + layer_filter.include = f.include; + layer_filter.traverse = f.traverse; + layer_filter.populate(f.values); + } else if (f.type == geom_filter::ENTITY_ARG) { + if (f.arg == GUID_ARG) { + guid_filter.include = f.include; + guid_filter.traverse = f.traverse; + guid_filter.populate(f.values); + } else if (f.arg == NAME_ARG) { + name_filter.include = f.include; + name_filter.traverse = f.traverse; + name_filter.populate(f.values); + } else if (f.arg == DESC_ARG) { + desc_filter.include = f.include; + desc_filter.traverse = f.traverse; + desc_filter.populate(f.values); + } else if (f.arg == TAG_ARG) { + tag_filter.include = f.include; + tag_filter.traverse = f.traverse; + tag_filter.populate(f.values); + } } + } + + // If no entity names are specified these are the defaults to skip from output + if (entity_filter.values.empty()) { + try { + std::set entities; + entities.insert("IfcSpace"); + if (output_extension == ".svg") { + entity_filter.include = true; + } else { + entities.insert("IfcOpeningElement"); + } + entity_filter.populate(entities); + } catch (const IfcParse::IfcException& e) { + cerr_ << "[Error] " << e.what() << std::endl; + return std::vector(); + } + } + + if (!layer_filter.values.empty()) { filter_funcs.push_back(boost::ref(layer_filter)); } + if (!entity_filter.values.empty()) { filter_funcs.push_back(boost::ref(entity_filter)); } + if (!guid_filter.values.empty()) { filter_funcs.push_back(boost::ref(guid_filter)); } + if (!name_filter.values.empty()) { filter_funcs.push_back(boost::ref(name_filter)); } + if (!desc_filter.values.empty()) { filter_funcs.push_back(boost::ref(desc_filter)); } + if (!tag_filter.values.empty()) { filter_funcs.push_back(boost::ref(tag_filter)); } + + return filter_funcs; + } +bool reuse_ok_(SerializerSettings settings, const IfcSchema::IfcProduct::list::ptr &products, IfcGeom::Kernel kernel) +{ + // IfcGeom::Kernel kernel; + + // With world coords enabled, object transformations are directly applied to + // the BRep. There is no way to re-use the geometry for multiple products. + if (settings.get(IfcGeom::IteratorSettings::USE_WORLD_COORDS)) + { + return false; + } + + std::set associated_single_materials; + + for (IfcSchema::IfcProduct::list::it it = products->begin(); it != products->end(); ++it) + { + IfcSchema::IfcProduct *product = *it; + if (!settings.get(IfcGeom::IteratorSettings::DISABLE_OPENING_SUBTRACTIONS) && + kernel.find_openings(product)->size()) + { + return false; + } + if (settings.get(IfcGeom::IteratorSettings::APPLY_LAYERSETS)) + { + IfcSchema::IfcRelAssociates::list::ptr associations = product->HasAssociations(); + for (IfcSchema::IfcRelAssociates::list::it jt = associations->begin(); + jt != associations->end(); ++jt) + { + IfcSchema::IfcRelAssociatesMaterial *assoc = + (*jt)->as(); + if (assoc) + { + if (assoc->RelatingMaterial()->is(IfcSchema::Type::IfcMaterialLayerSetUsage)) + { + // TODO: Check whether single layer? + return false; + } + } + } + } + // Note that this can be a nullptr (!), but the fact that set size should be one still holds + associated_single_materials.insert(kernel.get_single_material_association(product)); + if (associated_single_materials.size() > 1) + return false; + } + return associated_single_materials.size() == 1; +} + +void create_element(SerializerSettings &settings, IfcproductRepresentation &rep) +{ + Logger::Status("processing item #: " + std::to_string(rep.index)); + IfcGeom::Kernel kernel; + IfcSchema::IfcRepresentation *representation= rep.representation; + IfcSchema::IfcProduct *product = rep.product; + // IfcGeom::BRepElement *element; + rep.element = + kernel.create_brep_for_representation_and_product(settings, representation, product); + //if(geometry_reuse_ok_for_current_representation_) + // { + // // element = kernel.create_brep_for_processed_representation(settings, representation, + // // product, + // // current_shape_model); + // } + + + return; +} + +//@todo MOVE this include +#include + +Bounds compute_bounds(IfcParse::IfcFile* ifc_file, IfcGeom::Kernel kernel) + { + gp_XYZ bounds_min_; + gp_XYZ bounds_max_; + Bounds bounds; + + for (int i = 1; i < 4; ++i) { + bounds_min_.SetCoord(i, std::numeric_limits::infinity()); + bounds_max_.SetCoord(i, -std::numeric_limits::infinity()); } - if (!layer_filter.values.empty()) { filter_funcs.push_back(boost::ref(layer_filter)); } - if (!entity_filter.values.empty()) { filter_funcs.push_back(boost::ref(entity_filter)); } - if (!guid_filter.values.empty()) { filter_funcs.push_back(boost::ref(guid_filter)); } - if (!name_filter.values.empty()) { filter_funcs.push_back(boost::ref(name_filter)); } - if (!desc_filter.values.empty()) { filter_funcs.push_back(boost::ref(desc_filter)); } - if (!tag_filter.values.empty()) { filter_funcs.push_back(boost::ref(tag_filter)); } + IfcSchema::IfcProduct::list::ptr products = ifc_file->entitiesByType(); + for (IfcSchema::IfcProduct::list::it iter = products->begin(); iter != products->end(); ++iter) { + IfcSchema::IfcProduct* product = *iter; + if (product->hasObjectPlacement()) { + // Use a fresh trsf every time in order to prevent the result to be concatenated + gp_Trsf trsf; + bool success = false; + + try { + success = kernel.convert(product->ObjectPlacement(), trsf); + } catch (const std::exception& e) { + Logger::Error(e); + } catch (...) { + Logger::Error("Failed to construct placement"); + } + + if (!success) { + continue; + } + + const gp_XYZ& pos = trsf.TranslationPart(); + bounds_min_.SetX(std::min(bounds_min_.X(), pos.X())); + bounds_min_.SetY(std::min(bounds_min_.Y(), pos.Y())); + bounds_min_.SetZ(std::min(bounds_min_.Z(), pos.Z())); + bounds_max_.SetX(std::max(bounds_max_.X(), pos.X())); + bounds_max_.SetY(std::max(bounds_max_.Y(), pos.Y())); + bounds_max_.SetZ(std::max(bounds_max_.Z(), pos.Z())); + } + } + bounds.min = bounds_min_; + bounds.max = bounds_max_; + return bounds; + } - return filter_funcs; -} diff --git a/src/ifcgeom/IfcGeomIterator.h b/src/ifcgeom/IfcGeomIterator.h index f452845b8c..ddc29ab013 100644 --- a/src/ifcgeom/IfcGeomIterator.h +++ b/src/ifcgeom/IfcGeomIterator.h @@ -91,679 +91,679 @@ #endif namespace IfcGeom { - - template - class Iterator { - private: - Iterator(const Iterator&); // N/I - Iterator& operator=(const Iterator&); // N/I + +template +class Iterator { + private: + Iterator(const Iterator&); // N/I + Iterator& operator=(const Iterator&); // N/I - Kernel kernel; - IteratorSettings settings; + Kernel kernel; + IteratorSettings settings; - IfcParse::IfcFile* ifc_file; + IfcParse::IfcFile* ifc_file; - // A container and iterator for IfcRepresentations - IfcSchema::IfcRepresentation::list::ptr representations; - IfcSchema::IfcRepresentation::list::it representation_iterator; + // A container and iterator for IfcRepresentations + IfcSchema::IfcRepresentation::list::ptr representations; + IfcSchema::IfcRepresentation::list::it representation_iterator; - // The object is fetched beforehand to be sure that get() returns a valid element - TriangulationElement

* current_triangulation; - BRepElement

* current_shape_model; - SerializedElement

* current_serialization; - - // A container and iterator for IfcBuildingElements for the current IfcRepresentation referenced by *representation_iterator - IfcSchema::IfcProduct::list::ptr ifcproducts; - IfcSchema::IfcProduct::list::it ifcproduct_iterator; + // The object is fetched beforehand to be sure that get() returns a valid element + TriangulationElement

* current_triangulation; + BRepElement

* current_shape_model; + SerializedElement

* current_serialization; + + // A container and iterator for IfcBuildingElements for the current IfcRepresentation referenced by *representation_iterator + IfcSchema::IfcProduct::list::ptr ifcproducts; + IfcSchema::IfcProduct::list::it ifcproduct_iterator; - IfcSchema::IfcRepresentation::list::ptr ok_mapped_representations; + IfcSchema::IfcRepresentation::list::ptr ok_mapped_representations; - int done; - int total; + int done; + int total; - std::string unit_name; - double unit_magnitude; + std::string unit_name; + double unit_magnitude; - gp_XYZ bounds_min_; - gp_XYZ bounds_max_; + gp_XYZ bounds_min_; + gp_XYZ bounds_max_; - std::vector filters_; + std::vector filters_; - struct filter_match - { - filter_match(IfcSchema::IfcProduct *prod) : product(prod) {} - bool operator()(const filter_t& filter) const { return filter(product); } + struct filter_match + { + filter_match(IfcSchema::IfcProduct *prod) : product(prod) {} + bool operator()(const filter_t& filter) const { return filter(product); } - IfcSchema::IfcProduct* product; - }; + IfcSchema::IfcProduct* product; + }; - void initUnits() { - IfcSchema::IfcProject::list::ptr projects = ifc_file->entitiesByType(); - if (projects->size() == 1) { - IfcSchema::IfcProject* project = *projects->begin(); - std::pair length_unit = kernel.initializeUnits(project->UnitsInContext()); - unit_name = length_unit.first; - unit_magnitude = length_unit.second; - } else { - Logger::Error("A single IfcProject is expected (encountered " + boost::lexical_cast(projects->size()) + "); unable to read unit information."); - } - } + void initUnits() { + IfcSchema::IfcProject::list::ptr projects = ifc_file->entitiesByType(); + if (projects->size() == 1) { + IfcSchema::IfcProject* project = *projects->begin(); + std::pair length_unit = kernel.initializeUnits(project->UnitsInContext()); + unit_name = length_unit.first; + unit_magnitude = length_unit.second; + } else { + Logger::Error("A single IfcProject is expected (encountered " + boost::lexical_cast(projects->size()) + "); unable to read unit information."); + } + } - /// @todo public/private sections all over the place: move all public to the beginning of the class - public: - Iterator(const IteratorSettings& settings, IfcParse::IfcFile* file, std::vector& filters) - : settings(settings) - , ifc_file(file) - , filters_(filters) - , owns_ifc_file(false) - { - _initialize(); + /// @todo public/private sections all over the place: move all public to the beginning of the class + public: + Iterator(const IteratorSettings& settings, IfcParse::IfcFile* file, std::vector& filters) + : settings(settings) + , ifc_file(file) + , filters_(filters) + , owns_ifc_file(false) + { + _initialize(); + } + + bool initialize() { + try { + initUnits(); + } catch (const std::exception& e) { + Logger::Error(e); + } + + std::set allowed_context_types; + allowed_context_types.insert("model"); + allowed_context_types.insert("plan"); + allowed_context_types.insert("notdefined"); + + std::set context_types; + if (!settings.get(IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES)) { + // Really this should only be 'Model', as per + // the standard 'Design' is deprecated. So, + // just for backwards compatibility: + context_types.insert("model"); + context_types.insert("design"); + // Some earlier (?) versions DDS-CAD output their own ContextTypes + context_types.insert("model view"); + context_types.insert("detail view"); + } + if (settings.get(IteratorSettings::INCLUDE_CURVES)) { + context_types.insert("plan"); + } + + double lowest_precision_encountered = std::numeric_limits::infinity(); + bool any_precision_encountered = false; + + representations = IfcSchema::IfcRepresentation::list::ptr(new IfcSchema::IfcRepresentation::list); + ok_mapped_representations = IfcSchema::IfcRepresentation::list::ptr(new IfcSchema::IfcRepresentation::list); + + IfcSchema::IfcGeometricRepresentationContext::list::it it; + IfcSchema::IfcGeometricRepresentationSubContext::list::it jt; + IfcSchema::IfcGeometricRepresentationContext::list::ptr contexts = + ifc_file->entitiesByType(); + + IfcSchema::IfcGeometricRepresentationContext::list::ptr filtered_contexts (new IfcSchema::IfcGeometricRepresentationContext::list); + + for (it = contexts->begin(); it != contexts->end(); ++it) { + IfcSchema::IfcGeometricRepresentationContext* context = *it; + if (context->is(IfcSchema::Type::IfcGeometricRepresentationSubContext)) { + // Continue, as the list of subcontexts will be considered + // by the parent's context inverse attributes. + continue; + } + try { + if (context->hasContextType()) { + std::string context_type = context->ContextType(); + boost::to_lower(context_type); + + if (allowed_context_types.find(context_type) == allowed_context_types.end()) { + Logger::Message(Logger::LOG_ERROR, std::string("ContextType '") + context->ContextType() + "' not allowed:", context->entity); + } + if (context_types.find(context_type) != context_types.end()) { + filtered_contexts->push(context); + } + } + } catch (const std::exception& e) { + Logger::Error(e); + } + } + + // In case no contexts are identified based on their ContextType, all contexts are + // considered. Note that sub contexts are excluded as they are considered later on. + if (filtered_contexts->size() == 0) { + for (it = contexts->begin(); it != contexts->end(); ++it) { + IfcSchema::IfcGeometricRepresentationContext* context = *it; + if (!context->is(IfcSchema::Type::IfcGeometricRepresentationSubContext)) { + filtered_contexts->push(context); + } + } + } + + for (it = filtered_contexts->begin(); it != filtered_contexts->end(); ++it) { + IfcSchema::IfcGeometricRepresentationContext* context = *it; + + representations->push(context->RepresentationsInContext()); + try { + if (context->hasPrecision() && context->Precision() < lowest_precision_encountered) { + lowest_precision_encountered = context->Precision(); + any_precision_encountered = true; + } + } catch (const std::exception& e) { + Logger::Error(e); + } + + IfcSchema::IfcGeometricRepresentationSubContext::list::ptr sub_contexts = context->HasSubContexts(); + for (jt = sub_contexts->begin(); jt != sub_contexts->end(); ++jt) { + representations->push((*jt)->RepresentationsInContext()); + } + // There is no need for full recursion as the following is governed by the schema: + // WR31: The parent context shall not be another geometric representation sub context. + } + + if (any_precision_encountered) { + // Some arbitrary factor that has proven to work better for the models in the set of test files. + lowest_precision_encountered *= 10.; + + lowest_precision_encountered *= unit_magnitude; + if (lowest_precision_encountered < 1.e-7) { + Logger::Message(Logger::LOG_WARNING, "Precision lower than 0.0000001 meter not enforced"); + kernel.setValue(IfcGeom::Kernel::GV_PRECISION, 1.e-7); + } else { + kernel.setValue(IfcGeom::Kernel::GV_PRECISION, lowest_precision_encountered); + } + } else { + kernel.setValue(IfcGeom::Kernel::GV_PRECISION, 1.e-5); + } + + if (representations->size() == 0) { + Logger::Message(Logger::LOG_ERROR, "No representations encountered in relevant contexts, using all"); + representations = ifc_file->entitiesByType(); + } + + if (representations->size() == 0) { + Logger::Message(Logger::LOG_ERROR, "No representations encountered, aborting"); + return false; + } + + representation_iterator = representations->begin(); + ifcproducts.reset(); + + if (!create()) { + return false; + } + + done = 0; + total = representations->size(); + + return true; + } + + /// Computes model's bounding box (bounds_min and bounds_max). + /// @note Can take several minutes for large files. + void compute_bounds() + { + for (int i = 1; i < 4; ++i) { + bounds_min_.SetCoord(i, std::numeric_limits::infinity()); + bounds_max_.SetCoord(i, -std::numeric_limits::infinity()); + } + + IfcSchema::IfcProduct::list::ptr products = ifc_file->entitiesByType(); + for (IfcSchema::IfcProduct::list::it iter = products->begin(); iter != products->end(); ++iter) { + IfcSchema::IfcProduct* product = *iter; + if (product->hasObjectPlacement()) { + // Use a fresh trsf every time in order to prevent the result to be concatenated + gp_Trsf trsf; + bool success = false; + + try { + success = kernel.convert(product->ObjectPlacement(), trsf); + } catch (const std::exception& e) { + Logger::Error(e); + } catch (...) { + Logger::Error("Failed to construct placement"); } - bool initialize() { - try { - initUnits(); - } catch (const std::exception& e) { - Logger::Error(e); - } - - std::set allowed_context_types; - allowed_context_types.insert("model"); - allowed_context_types.insert("plan"); - allowed_context_types.insert("notdefined"); - - std::set context_types; - if (!settings.get(IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES)) { - // Really this should only be 'Model', as per - // the standard 'Design' is deprecated. So, - // just for backwards compatibility: - context_types.insert("model"); - context_types.insert("design"); - // Some earlier (?) versions DDS-CAD output their own ContextTypes - context_types.insert("model view"); - context_types.insert("detail view"); - } - if (settings.get(IteratorSettings::INCLUDE_CURVES)) { - context_types.insert("plan"); - } - - double lowest_precision_encountered = std::numeric_limits::infinity(); - bool any_precision_encountered = false; - - representations = IfcSchema::IfcRepresentation::list::ptr(new IfcSchema::IfcRepresentation::list); - ok_mapped_representations = IfcSchema::IfcRepresentation::list::ptr(new IfcSchema::IfcRepresentation::list); - - IfcSchema::IfcGeometricRepresentationContext::list::it it; - IfcSchema::IfcGeometricRepresentationSubContext::list::it jt; - IfcSchema::IfcGeometricRepresentationContext::list::ptr contexts = - ifc_file->entitiesByType(); - - IfcSchema::IfcGeometricRepresentationContext::list::ptr filtered_contexts (new IfcSchema::IfcGeometricRepresentationContext::list); - - for (it = contexts->begin(); it != contexts->end(); ++it) { - IfcSchema::IfcGeometricRepresentationContext* context = *it; - if (context->is(IfcSchema::Type::IfcGeometricRepresentationSubContext)) { - // Continue, as the list of subcontexts will be considered - // by the parent's context inverse attributes. - continue; - } - try { - if (context->hasContextType()) { - std::string context_type = context->ContextType(); - boost::to_lower(context_type); - - if (allowed_context_types.find(context_type) == allowed_context_types.end()) { - Logger::Message(Logger::LOG_ERROR, std::string("ContextType '") + context->ContextType() + "' not allowed:", context->entity); - } - if (context_types.find(context_type) != context_types.end()) { - filtered_contexts->push(context); - } - } - } catch (const std::exception& e) { - Logger::Error(e); - } - } - - // In case no contexts are identified based on their ContextType, all contexts are - // considered. Note that sub contexts are excluded as they are considered later on. - if (filtered_contexts->size() == 0) { - for (it = contexts->begin(); it != contexts->end(); ++it) { - IfcSchema::IfcGeometricRepresentationContext* context = *it; - if (!context->is(IfcSchema::Type::IfcGeometricRepresentationSubContext)) { - filtered_contexts->push(context); - } - } - } - - for (it = filtered_contexts->begin(); it != filtered_contexts->end(); ++it) { - IfcSchema::IfcGeometricRepresentationContext* context = *it; - - representations->push(context->RepresentationsInContext()); - try { - if (context->hasPrecision() && context->Precision() < lowest_precision_encountered) { - lowest_precision_encountered = context->Precision(); - any_precision_encountered = true; - } - } catch (const std::exception& e) { - Logger::Error(e); - } - - IfcSchema::IfcGeometricRepresentationSubContext::list::ptr sub_contexts = context->HasSubContexts(); - for (jt = sub_contexts->begin(); jt != sub_contexts->end(); ++jt) { - representations->push((*jt)->RepresentationsInContext()); - } - // There is no need for full recursion as the following is governed by the schema: - // WR31: The parent context shall not be another geometric representation sub context. - } - - if (any_precision_encountered) { - // Some arbitrary factor that has proven to work better for the models in the set of test files. - lowest_precision_encountered *= 10.; - - lowest_precision_encountered *= unit_magnitude; - if (lowest_precision_encountered < 1.e-7) { - Logger::Message(Logger::LOG_WARNING, "Precision lower than 0.0000001 meter not enforced"); - kernel.setValue(IfcGeom::Kernel::GV_PRECISION, 1.e-7); - } else { - kernel.setValue(IfcGeom::Kernel::GV_PRECISION, lowest_precision_encountered); - } - } else { - kernel.setValue(IfcGeom::Kernel::GV_PRECISION, 1.e-5); - } - - if (representations->size() == 0) { - Logger::Message(Logger::LOG_ERROR, "No representations encountered in relevant contexts, using all"); - representations = ifc_file->entitiesByType(); - } - - if (representations->size() == 0) { - Logger::Message(Logger::LOG_ERROR, "No representations encountered, aborting"); - return false; - } - - representation_iterator = representations->begin(); - ifcproducts.reset(); - - if (!create()) { - return false; - } - - done = 0; - total = representations->size(); - - return true; - } - - /// Computes model's bounding box (bounds_min and bounds_max). - /// @note Can take several minutes for large files. - void compute_bounds() - { - for (int i = 1; i < 4; ++i) { - bounds_min_.SetCoord(i, std::numeric_limits::infinity()); - bounds_max_.SetCoord(i, -std::numeric_limits::infinity()); - } - - IfcSchema::IfcProduct::list::ptr products = ifc_file->entitiesByType(); - for (IfcSchema::IfcProduct::list::it iter = products->begin(); iter != products->end(); ++iter) { - IfcSchema::IfcProduct* product = *iter; - if (product->hasObjectPlacement()) { - // Use a fresh trsf every time in order to prevent the result to be concatenated - gp_Trsf trsf; - bool success = false; - - try { - success = kernel.convert(product->ObjectPlacement(), trsf); - } catch (const std::exception& e) { - Logger::Error(e); - } catch (...) { - Logger::Error("Failed to construct placement"); - } - - if (!success) { - continue; - } - - const gp_XYZ& pos = trsf.TranslationPart(); - bounds_min_.SetX(std::min(bounds_min_.X(), pos.X())); - bounds_min_.SetY(std::min(bounds_min_.Y(), pos.Y())); - bounds_min_.SetZ(std::min(bounds_min_.Z(), pos.Z())); - bounds_max_.SetX(std::max(bounds_max_.X(), pos.X())); - bounds_max_.SetY(std::max(bounds_max_.Y(), pos.Y())); - bounds_max_.SetZ(std::max(bounds_max_.Z(), pos.Z())); - } - } + if (!success) { + continue; } - int progress() const { return 100 * done / total; } + const gp_XYZ& pos = trsf.TranslationPart(); + bounds_min_.SetX(std::min(bounds_min_.X(), pos.X())); + bounds_min_.SetY(std::min(bounds_min_.Y(), pos.Y())); + bounds_min_.SetZ(std::min(bounds_min_.Z(), pos.Z())); + bounds_max_.SetX(std::max(bounds_max_.X(), pos.X())); + bounds_max_.SetY(std::max(bounds_max_.Y(), pos.Y())); + bounds_max_.SetZ(std::max(bounds_max_.Z(), pos.Z())); + } + } + } - const std::string& getUnitName() const { return unit_name; } + int progress() const { return 100 * done / total; } - /// @note Double always as per IFC specification. - double getUnitMagnitude() const { return unit_magnitude; } - - std::string getLog() const { return Logger::GetLog(); } + const std::string& getUnitName() const { return unit_name; } - IfcParse::IfcFile* getFile() const { return ifc_file; } + /// @note Double always as per IFC specification. + double getUnitMagnitude() const { return unit_magnitude; } + + std::string getLog() const { return Logger::GetLog(); } - const std::vector& filters() const { return filters_; } - std::vector& filters() { return filters_; } + IfcParse::IfcFile* getFile() const { return ifc_file; } - const gp_XYZ& bounds_min() const { return bounds_min_; } - const gp_XYZ& bounds_max() const { return bounds_max_; } + const std::vector& filters() const { return filters_; } + std::vector& filters() { return filters_; } - private: - // Move to the next IfcRepresentation - void _nextShape() { - // In order to conserve memory and reduce cache insertion times, the cache is - // cleared after an arbitrary number of processed representations. This has been - // benchmarked extensively: https://github.com/IfcOpenShell/IfcOpenShell/pull/47 - static const int clear_interval = 64; - if (done % clear_interval == clear_interval - 1) { - kernel.purge_cache(); - } - ifcproducts.reset(); - ++ representation_iterator; - ++ done; - } + const gp_XYZ& bounds_min() const { return bounds_min_; } + const gp_XYZ& bounds_max() const { return bounds_max_; } - bool geometry_reuse_ok_for_current_representation_; + private: + // Move to the next IfcRepresentation + void _nextShape() { + // In order to conserve memory and reduce cache insertion times, the cache is + // cleared after an arbitrary number of processed representations. This has been + // benchmarked extensively: https://github.com/IfcOpenShell/IfcOpenShell/pull/47 + static const int clear_interval = 64; + if (done % clear_interval == clear_interval - 1) { + kernel.purge_cache(); + } + ifcproducts.reset(); + ++ representation_iterator; + ++ done; + } - bool reuse_ok_(const IfcSchema::IfcProduct::list::ptr& products) { - // With world coords enabled, object transformations are directly applied to - // the BRep. There is no way to re-use the geometry for multiple products. - if (settings.get(IteratorSettings::USE_WORLD_COORDS)) { - return false; - } + bool geometry_reuse_ok_for_current_representation_; - std::set associated_single_materials; + bool reuse_ok_(const IfcSchema::IfcProduct::list::ptr& products) { + // With world coords enabled, object transformations are directly applied to + // the BRep. There is no way to re-use the geometry for multiple products. + if (settings.get(IteratorSettings::USE_WORLD_COORDS)) { + return false; + } - for (IfcSchema::IfcProduct::list::it it = products->begin(); it != products->end(); ++it) { - IfcSchema::IfcProduct* product = *it; + std::set associated_single_materials; - if (!settings.get(IteratorSettings::DISABLE_OPENING_SUBTRACTIONS) && kernel.find_openings(product)->size()) { - return false; - } + for (IfcSchema::IfcProduct::list::it it = products->begin(); it != products->end(); ++it) { + IfcSchema::IfcProduct* product = *it; - if (settings.get(IteratorSettings::APPLY_LAYERSETS)) { - IfcSchema::IfcRelAssociates::list::ptr associations = product->HasAssociations(); - for (IfcSchema::IfcRelAssociates::list::it jt = associations->begin(); jt != associations->end(); ++jt) { - IfcSchema::IfcRelAssociatesMaterial* assoc = (*jt)->as(); - if (assoc) { - if (assoc->RelatingMaterial()->is(IfcSchema::Type::IfcMaterialLayerSetUsage)) { - // TODO: Check whether single layer? - return false; - } - } - } - } + if (!settings.get(IteratorSettings::DISABLE_OPENING_SUBTRACTIONS) && kernel.find_openings(product)->size()) { + return false; + } - // Note that this can be a nullptr (!), but the fact that set size should be one still holds - associated_single_materials.insert(kernel.get_single_material_association(product)); - if (associated_single_materials.size() > 1) return false; - } + if (settings.get(IteratorSettings::APPLY_LAYERSETS)) { + IfcSchema::IfcRelAssociates::list::ptr associations = product->HasAssociations(); + for (IfcSchema::IfcRelAssociates::list::it jt = associations->begin(); jt != associations->end(); ++jt) { + IfcSchema::IfcRelAssociatesMaterial* assoc = (*jt)->as(); + if (assoc) { + if (assoc->RelatingMaterial()->is(IfcSchema::Type::IfcMaterialLayerSetUsage)) { + // TODO: Check whether single layer? + return false; + } + } + } + } - return associated_single_materials.size() == 1; - } + // Note that this can be a nullptr (!), but the fact that set size should be one still holds + associated_single_materials.insert(kernel.get_single_material_association(product)); + if (associated_single_materials.size() > 1) return false; + } - BRepElement

* create_shape_model_for_next_entity() { - for (;;) { - IfcSchema::IfcRepresentation* representation; + return associated_single_materials.size() == 1; + } - if ( representation_iterator == representations->end() ) { - representations.reset(); - return 0; // reached the end of our list of representations - } - representation = *representation_iterator; + BRepElement

* create_shape_model_for_next_entity() { + for (;;) { + IfcSchema::IfcRepresentation* representation; - if (!ifcproducts) { - // Init. the list of filtered IfcProducts for this representation - ifcproducts = IfcSchema::IfcProduct::list::ptr(new IfcSchema::IfcProduct::list); - IfcSchema::IfcProduct::list::ptr unfiltered_products = kernel.products_represented_by(representation); - // Include only the desired products for processing. - for (IfcSchema::IfcProduct::list::it jt = unfiltered_products->begin(); jt != unfiltered_products->end(); ++jt) { - IfcSchema::IfcProduct* prod = *jt; - if (boost::all(filters_, filter_match(prod))) { - ifcproducts->push(prod); - } - } + if ( representation_iterator == representations->end() ) { + representations.reset(); + return 0; // reached the end of our list of representations + } + representation = *representation_iterator; - if (ifcproducts->size() == 0) { - _nextShape(); - continue; - } - - geometry_reuse_ok_for_current_representation_ = reuse_ok_(ifcproducts); - - IfcSchema::IfcRepresentationMap::list::ptr maps = representation->RepresentationMap(); - - if (!geometry_reuse_ok_for_current_representation_ && maps->size() == 1) { - // unfiltered_products contains products represented by this representation by means of mapped items. - // For example because of openings applied to products, reuse might not be acceptable and then the - // products will be processed by means of their immediate representation and not the mapped representation. - - // IfcRepresentationMaps are also used for IfcTypeProducts, so an additional check is performed whether the map - // is indeed used by IfcMappedItems. - IfcSchema::IfcRepresentationMap* map = *maps->begin(); - if (map->MapUsage()->size() > 0) { - _nextShape(); - continue; - } - } - - // Check if this represenation has (or will be) processed as part its mapped representation - bool representation_processed_as_mapped_item = false; - IfcSchema::IfcRepresentation* representation_mapped_to = kernel.representation_mapped_to(representation); - if (representation_mapped_to) { - representation_processed_as_mapped_item = geometry_reuse_ok_for_current_representation_ || - ok_mapped_representations->contains(representation_mapped_to); - } - - if (representation_processed_as_mapped_item) { - ok_mapped_representations->push(representation_mapped_to); - _nextShape(); - continue; - } - - ifcproduct_iterator = ifcproducts->begin(); - } - - // Have we reached the end of our list of IfcProducts? - if ( ifcproduct_iterator == ifcproducts->end() ) { - _nextShape(); - continue; - } - - IfcSchema::IfcProduct* product = *ifcproduct_iterator; - Logger::SetProduct(product); - - BRepElement

* element; - if (ifcproduct_iterator == ifcproducts->begin() || !geometry_reuse_ok_for_current_representation_) { - element = kernel.create_brep_for_representation_and_product

(settings, representation, product); - } else { - element = kernel.create_brep_for_processed_representation(settings, representation, product, current_shape_model); - } - - Logger::SetProduct(boost::none); - - if (!element) { - _nextShape(); - continue; - } - - return element; - } - } - - void free_shapes() { - // Free all possible representations of the current geometrical entity - delete current_triangulation; - current_triangulation = 0; - delete current_serialization; - current_serialization = 0; - delete current_shape_model; - current_shape_model = 0; - } - - public: - /// Returns what would be the product for the next shape representation - /// @todo Double-check and test the impl. - //IfcSchema::IfcProduct* peek_next() const - //{ - // if (ifcproducts && ifcproduct_iterator + 1 != ifcproducts->end()){ - // return *(ifcproduct_iterator + 1); - // } else { - // return 0; - // } - //} - - /// @todo Would this be as simple as the following code? - //void skip_next() { if (ifcproducts) { ++ifcproduct_iterator; } } - - /// Moves to the next shape representation, create its geometry, and returns the associated product. - /// Use get() to retrieve the created geometry. - IfcSchema::IfcProduct* next() { - // Increment the iterator over the list of products using the current - // shape representation - if (ifcproducts) { - ++ifcproduct_iterator; - } - - return create(); - } - - /// Gets the representation of the current geometrical entity. - Element

* get() - { - // TODO: Test settings and throw - Element

* ret = 0; - if (current_triangulation) { ret = current_triangulation; } - else if (current_serialization) { ret = current_serialization; } - else if (current_shape_model) { ret = current_shape_model; } - - // If we want to organize the element considering their hierarchy - if (settings.get(IteratorSettings::SEARCH_FLOOR)) - { - // We are going to build a vector with the element parents. - // First, create the parent vector - std::vector*> parents; - - // if the element has a parent - if (ret->parent_id() != -1) - { - const IfcGeom::Element

* parent_object = NULL; - bool hasParent = true; - - // get the parent - try { - parent_object = getObject(ret->parent_id()); - } catch (const std::exception& e) { - Logger::Error(e); - hasParent = false; - } - - // Add the previously found parent to the vector - if (hasParent) parents.insert(parents.begin(), parent_object); - - // We need to find all the parents - while (parent_object != NULL && hasParent && parent_object->parent_id() != -1) - { - // Find the next parent - try { - parent_object = getObject(parent_object->parent_id()); - } catch (const std::exception& e) { - Logger::Error(e); - hasParent = false; - } - - // Add the previously found parent to the vector - if (hasParent) parents.insert(parents.begin(), parent_object); - - hasParent = hasParent && parent_object->parent_id() != -1; - } - - // when done push the parent list in the Element object - ret->SetParents(parents); - } - } - - return ret; + if (!ifcproducts) { + // Init. the list of filtered IfcProducts for this representation + ifcproducts = IfcSchema::IfcProduct::list::ptr(new IfcSchema::IfcProduct::list); + IfcSchema::IfcProduct::list::ptr unfiltered_products = kernel.products_represented_by(representation); + // Include only the desired products for processing. + for (IfcSchema::IfcProduct::list::it jt = unfiltered_products->begin(); jt != unfiltered_products->end(); ++jt) { + IfcSchema::IfcProduct* prod = *jt; + if (boost::all(filters_, filter_match(prod))) { + ifcproducts->push(prod); + } } - /// Gets the native (Open Cascade) representation of the current geometrical entity. - BRepElement

* get_native() - { - // TODO: Test settings and throw - return current_shape_model; - } + if (ifcproducts->size() == 0) { + _nextShape(); + continue; + } - const Element

* getObject(int id) { - gp_Trsf trsf; - int parent_id = -1; - std::string instance_type, product_name, product_guid; - IfcSchema::IfcProduct* ifc_product = 0; + geometry_reuse_ok_for_current_representation_ = reuse_ok_(ifcproducts); - try { - IfcUtil::IfcBaseClass* ifc_entity = ifc_file->entityById(id); - instance_type = IfcSchema::Type::ToString(ifc_entity->type()); + IfcSchema::IfcRepresentationMap::list::ptr maps = representation->RepresentationMap(); - if (ifc_entity->is(IfcSchema::Type::IfcRoot)) { - IfcSchema::IfcRoot* ifc_root = ifc_entity->as(); - product_guid = ifc_root->GlobalId(); - product_name = ifc_root->hasName() ? ifc_root->Name() : ""; - } + if (!geometry_reuse_ok_for_current_representation_ && maps->size() == 1) { + // unfiltered_products contains products represented by this representation by means of mapped items. + // For example because of openings applied to products, reuse might not be acceptable and then the + // products will be processed by means of their immediate representation and not the mapped representation. - if (ifc_entity->is(IfcSchema::Type::IfcProduct)) { - ifc_product = ifc_entity->as(); - parent_id = -1; - try { - IfcSchema::IfcObjectDefinition* parent_object = kernel.get_decomposing_entity(ifc_product); - if (parent_object) { - parent_id = parent_object->entity->id(); - } - } catch (const std::exception& e) { - Logger::Error(e); - } catch (...) { - Logger::Error("Failed to find decomposing entity"); - } + // IfcRepresentationMaps are also used for IfcTypeProducts, so an additional check is performed whether the map + // is indeed used by IfcMappedItems. + IfcSchema::IfcRepresentationMap* map = *maps->begin(); + if (map->MapUsage()->size() > 0) { + _nextShape(); + continue; + } + } - try { - kernel.convert(ifc_product->ObjectPlacement(), trsf); - } catch (const std::exception& e) { - Logger::Error(e); - } catch (...) { - Logger::Error("Failed to construct placement"); - } - } - } catch (const std::exception& e) { - Logger::Error(e); - } catch (const Standard_Failure& e) { - if (e.GetMessageString() && strlen(e.GetMessageString())) { - Logger::Error(e.GetMessageString()); - } else { - Logger::Error("Unknown error returning product"); - } - } catch (...) { - Logger::Error("Unknown error returning product"); - } + // Check if this represenation has (or will be) processed as part its mapped representation + bool representation_processed_as_mapped_item = false; + IfcSchema::IfcRepresentation* representation_mapped_to = kernel.representation_mapped_to(representation); + if (representation_mapped_to) { + representation_processed_as_mapped_item = geometry_reuse_ok_for_current_representation_ || + ok_mapped_representations->contains(representation_mapped_to); + } - ElementSettings element_settings(settings, unit_magnitude, instance_type); + if (representation_processed_as_mapped_item) { + ok_mapped_representations->push(representation_mapped_to); + _nextShape(); + continue; + } - Element

* ifc_object = new Element

(element_settings, id, parent_id, product_name, instance_type, product_guid, "", trsf, ifc_product); - return ifc_object; - } + ifcproduct_iterator = ifcproducts->begin(); + } - IfcSchema::IfcProduct* create() { - IfcGeom::BRepElement

* next_shape_model = 0; - IfcGeom::SerializedElement

* next_serialization = 0; - IfcGeom::TriangulationElement

* next_triangulation = 0; + // Have we reached the end of our list of IfcProducts? + if ( ifcproduct_iterator == ifcproducts->end() ) { + _nextShape(); + continue; + } - try { - next_shape_model = create_shape_model_for_next_entity(); - } catch (const std::exception& e) { - Logger::Error(e); - } catch (const Standard_Failure& e) { - if (e.GetMessageString() && strlen(e.GetMessageString())) { - Logger::Error(e.GetMessageString()); - } else { - Logger::Error("Unknown error creating geometry"); - } - } catch (...) { - Logger::Error("Unknown error creating geometry"); - } + IfcSchema::IfcProduct* product = *ifcproduct_iterator; + Logger::SetProduct(product); - if (next_shape_model) { - if (settings.get(IteratorSettings::USE_BREP_DATA)) { - try { - next_serialization = new SerializedElement

(*next_shape_model); - } catch (...) { - Logger::Message(Logger::LOG_ERROR, "Getting a serialized element from model failed."); - } - } else if (!settings.get(IteratorSettings::DISABLE_TRIANGULATION)) { - try { - if (ifcproduct_iterator == ifcproducts->begin() || !geometry_reuse_ok_for_current_representation_) { - next_triangulation = new TriangulationElement

(*next_shape_model); - } else { - next_triangulation = new TriangulationElement

(*next_shape_model, current_triangulation->geometry_pointer()); - } - } catch (...) { - Logger::Message(Logger::LOG_ERROR, "Getting a triangulation element from model failed."); - } - } - } + BRepElement

* element; + if (ifcproduct_iterator == ifcproducts->begin() || !geometry_reuse_ok_for_current_representation_) { + element = kernel.create_brep_for_representation_and_product

(settings, representation, product); + } else { + element = kernel.create_brep_for_processed_representation(settings, representation, product, current_shape_model); + } - free_shapes(); + Logger::SetProduct(boost::none); - current_shape_model = next_shape_model; - current_serialization = next_serialization; - current_triangulation = next_triangulation; + if (!element) { + _nextShape(); + continue; + } - return next_shape_model ? next_shape_model->product() : 0; - } - private: - void _initialize() { - current_triangulation = 0; - current_shape_model = 0; - current_serialization = 0; + return element; + } + } - unit_name = "METER"; - unit_magnitude = 1.f; + void free_shapes() { + // Free all possible representations of the current geometrical entity + delete current_triangulation; + current_triangulation = 0; + delete current_serialization; + current_serialization = 0; + delete current_shape_model; + current_shape_model = 0; + } - kernel.setValue(IfcGeom::Kernel::GV_MAX_FACES_TO_SEW, settings.get(IteratorSettings::SEW_SHELLS) ? 1000 : -1); - kernel.setValue(IfcGeom::Kernel::GV_DIMENSIONALITY, (settings.get(IteratorSettings::INCLUDE_CURVES) - ? (settings.get(IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES) ? -1. : 0.) : +1.)); - if (settings.get(IteratorSettings::BUILDING_LOCAL_PLACEMENT)) { - if (settings.get(IteratorSettings::SITE_LOCAL_PLACEMENT)) { - Logger::Message(Logger::LOG_WARNING, "building-local-placement takes precedence over site-local-placement"); - } - kernel.set_conversion_placement_rel_to(IfcSchema::Type::IfcBuilding); - } else if (settings.get(IteratorSettings::SITE_LOCAL_PLACEMENT)) { - kernel.set_conversion_placement_rel_to(IfcSchema::Type::IfcSite); - } - } + public: + /// Returns what would be the product for the next shape representation + /// @todo Double-check and test the impl. + //IfcSchema::IfcProduct* peek_next() const + //{ + // if (ifcproducts && ifcproduct_iterator + 1 != ifcproducts->end()){ + // return *(ifcproduct_iterator + 1); + // } else { + // return 0; + // } + //} - bool owns_ifc_file; - public: - Iterator(const IteratorSettings& settings, IfcParse::IfcFile* file) - : settings(settings) - , ifc_file(file) - , owns_ifc_file(false) - { - _initialize(); - } - Iterator(const IteratorSettings& settings, const std::string& filename) - : settings(settings) - , ifc_file(new IfcParse::IfcFile) - , owns_ifc_file(true) - { - ifc_file->Init(filename); - _initialize(); - } - Iterator(const IteratorSettings& settings, void* data, int length) - : settings(settings) - , ifc_file(new IfcParse::IfcFile) - , owns_ifc_file(true) - { - ifc_file->Init(data, length); - _initialize(); - } - Iterator(const IteratorSettings& settings, std::istream& filestream, int length) - : settings(settings) - , ifc_file(new IfcParse::IfcFile) - , owns_ifc_file(true) - { - ifc_file->Init(filestream, length); - _initialize(); - } + /// @todo Would this be as simple as the following code? + //void skip_next() { if (ifcproducts) { ++ifcproduct_iterator; } } - ~Iterator() { - if (owns_ifc_file) { - delete ifc_file; - } + /// Moves to the next shape representation, create its geometry, and returns the associated product. + /// Use get() to retrieve the created geometry. + IfcSchema::IfcProduct* next() { + // Increment the iterator over the list of products using the current + // shape representation + if (ifcproducts) { + ++ifcproduct_iterator; + } - free_shapes(); - } - }; + return create(); + } + + /// Gets the representation of the current geometrical entity. + Element

* get() + { + // TODO: Test settings and throw + Element

* ret = 0; + if (current_triangulation) { ret = current_triangulation; } + else if (current_serialization) { ret = current_serialization; } + else if (current_shape_model) { ret = current_shape_model; } + + // If we want to organize the element considering their hierarchy + if (settings.get(IteratorSettings::SEARCH_FLOOR)) + { + // We are going to build a vector with the element parents. + // First, create the parent vector + std::vector*> parents; + + // if the element has a parent + if (ret->parent_id() != -1) + { + const IfcGeom::Element

* parent_object = NULL; + bool hasParent = true; + + // get the parent + try { + parent_object = getObject(ret->parent_id()); + } catch (const std::exception& e) { + Logger::Error(e); + hasParent = false; + } + + // Add the previously found parent to the vector + if (hasParent) parents.insert(parents.begin(), parent_object); + + // We need to find all the parents + while (parent_object != NULL && hasParent && parent_object->parent_id() != -1) + { + // Find the next parent + try { + parent_object = getObject(parent_object->parent_id()); + } catch (const std::exception& e) { + Logger::Error(e); + hasParent = false; + } + + // Add the previously found parent to the vector + if (hasParent) parents.insert(parents.begin(), parent_object); + + hasParent = hasParent && parent_object->parent_id() != -1; + } + + // when done push the parent list in the Element object + ret->SetParents(parents); + } + } + + return ret; + } + + /// Gets the native (Open Cascade) representation of the current geometrical entity. + BRepElement

* get_native() + { + // TODO: Test settings and throw + return current_shape_model; + } + + const Element

* getObject(int id) { + gp_Trsf trsf; + int parent_id = -1; + std::string instance_type, product_name, product_guid; + IfcSchema::IfcProduct* ifc_product = 0; + + try { + IfcUtil::IfcBaseClass* ifc_entity = ifc_file->entityById(id); + instance_type = IfcSchema::Type::ToString(ifc_entity->type()); + + if (ifc_entity->is(IfcSchema::Type::IfcRoot)) { + IfcSchema::IfcRoot* ifc_root = ifc_entity->as(); + product_guid = ifc_root->GlobalId(); + product_name = ifc_root->hasName() ? ifc_root->Name() : ""; + } + + if (ifc_entity->is(IfcSchema::Type::IfcProduct)) { + ifc_product = ifc_entity->as(); + parent_id = -1; + try { + IfcSchema::IfcObjectDefinition* parent_object = kernel.get_decomposing_entity(ifc_product); + if (parent_object) { + parent_id = parent_object->entity->id(); + } + } catch (const std::exception& e) { + Logger::Error(e); + } catch (...) { + Logger::Error("Failed to find decomposing entity"); + } + + try { + kernel.convert(ifc_product->ObjectPlacement(), trsf); + } catch (const std::exception& e) { + Logger::Error(e); + } catch (...) { + Logger::Error("Failed to construct placement"); + } + } + } catch (const std::exception& e) { + Logger::Error(e); + } catch (const Standard_Failure& e) { + if (e.GetMessageString() && strlen(e.GetMessageString())) { + Logger::Error(e.GetMessageString()); + } else { + Logger::Error("Unknown error returning product"); + } + } catch (...) { + Logger::Error("Unknown error returning product"); + } + + ElementSettings element_settings(settings, unit_magnitude, instance_type); + + Element

* ifc_object = new Element

(element_settings, id, parent_id, product_name, instance_type, product_guid, "", trsf, ifc_product); + return ifc_object; + } + + IfcSchema::IfcProduct* create() { + IfcGeom::BRepElement

* next_shape_model = 0; + IfcGeom::SerializedElement

* next_serialization = 0; + IfcGeom::TriangulationElement

* next_triangulation = 0; + + try { + next_shape_model = create_shape_model_for_next_entity(); + } catch (const std::exception& e) { + Logger::Error(e); + } catch (const Standard_Failure& e) { + if (e.GetMessageString() && strlen(e.GetMessageString())) { + Logger::Error(e.GetMessageString()); + } else { + Logger::Error("Unknown error creating geometry"); + } + } catch (...) { + Logger::Error("Unknown error creating geometry"); + } + + if (next_shape_model) { + if (settings.get(IteratorSettings::USE_BREP_DATA)) { + try { + next_serialization = new SerializedElement

(*next_shape_model); + } catch (...) { + Logger::Message(Logger::LOG_ERROR, "Getting a serialized element from model failed."); + } + } else if (!settings.get(IteratorSettings::DISABLE_TRIANGULATION)) { + try { + if (ifcproduct_iterator == ifcproducts->begin() || !geometry_reuse_ok_for_current_representation_) { + next_triangulation = new TriangulationElement

(*next_shape_model); + } else { + next_triangulation = new TriangulationElement

(*next_shape_model, current_triangulation->geometry_pointer()); + } + } catch (...) { + Logger::Message(Logger::LOG_ERROR, "Getting a triangulation element from model failed."); + } + } + } + + free_shapes(); + + current_shape_model = next_shape_model; + current_serialization = next_serialization; + current_triangulation = next_triangulation; + + return next_shape_model ? next_shape_model->product() : 0; + } + private: + void _initialize() { + current_triangulation = 0; + current_shape_model = 0; + current_serialization = 0; + + unit_name = "METER"; + unit_magnitude = 1.f; + + kernel.setValue(IfcGeom::Kernel::GV_MAX_FACES_TO_SEW, settings.get(IteratorSettings::SEW_SHELLS) ? 1000 : -1); + kernel.setValue(IfcGeom::Kernel::GV_DIMENSIONALITY, (settings.get(IteratorSettings::INCLUDE_CURVES) + ? (settings.get(IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES) ? -1. : 0.) : +1.)); + if (settings.get(IteratorSettings::BUILDING_LOCAL_PLACEMENT)) { + if (settings.get(IteratorSettings::SITE_LOCAL_PLACEMENT)) { + Logger::Message(Logger::LOG_WARNING, "building-local-placement takes precedence over site-local-placement"); + } + kernel.set_conversion_placement_rel_to(IfcSchema::Type::IfcBuilding); + } else if (settings.get(IteratorSettings::SITE_LOCAL_PLACEMENT)) { + kernel.set_conversion_placement_rel_to(IfcSchema::Type::IfcSite); + } + } + + bool owns_ifc_file; + public: + Iterator(const IteratorSettings& settings, IfcParse::IfcFile* file) + : settings(settings) + , ifc_file(file) + , owns_ifc_file(false) + { + _initialize(); + } + Iterator(const IteratorSettings& settings, const std::string& filename) + : settings(settings) + , ifc_file(new IfcParse::IfcFile) + , owns_ifc_file(true) + { + ifc_file->Init(filename); + _initialize(); + } + Iterator(const IteratorSettings& settings, void* data, int length) + : settings(settings) + , ifc_file(new IfcParse::IfcFile) + , owns_ifc_file(true) + { + ifc_file->Init(data, length); + _initialize(); + } + Iterator(const IteratorSettings& settings, std::istream& filestream, int length) + : settings(settings) + , ifc_file(new IfcParse::IfcFile) + , owns_ifc_file(true) + { + ifc_file->Init(filestream, length); + _initialize(); + } + + ~Iterator() { + if (owns_ifc_file) { + delete ifc_file; + } + + free_shapes(); + } +}; } #endif