From de416eca491621b289269f7319855cf435cf291a Mon Sep 17 00:00:00 2001 From: Sander Boer Date: Tue, 18 Dec 2018 12:49:11 +0100 Subject: [PATCH 01/14] Too lazy to comment --- src/ifcparse/IfcParse.h | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/ifcparse/IfcParse.h b/src/ifcparse/IfcParse.h index ff892ee5cf..ee9d0fc438 100644 --- a/src/ifcparse/IfcParse.h +++ b/src/ifcparse/IfcParse.h @@ -142,17 +142,30 @@ namespace IfcParse { Token OperatorTokenPtr(IfcSpfLexer* tokens, unsigned start, unsigned end); Token GeneralTokenPtr(IfcSpfLexer* tokens, unsigned start, unsigned end); Token NoneTokenPtr(); - +/* gcc doesn't know _Thread_local from C11 yet */ +#ifdef __GNUC__ +# define thread_local __thread +#elif __STDC_VERSION__ >= 201112L +# define thread_local _Thread_local +#elif defined(_MSC_VER) +# define thread_local __declspec(thread) +#else +# error Cannot define thread_local +#endif /// A stream of tokens to be read from a IfcSpfStream. class IFC_PARSE_API IfcSpfLexer { private: IfcCharacterDecoder* decoder; //storage for temporary string without allocation - mutable std::string _tempString; + // mutable std::string _tempString; unsigned int skipWhitespace(); unsigned int skipComment(); public: - std::string &GetTempString() const { return _tempString; } + // std::string &GetTempString() const { return _tempString; } + std::string &GetTempString() const { + static thread_local std::string s; + return s; + } IfcSpfStream* stream; IfcFile* file; IfcSpfLexer(IfcSpfStream* s, IfcFile* f); From 6468329f01d40f3605eb1647f7fdfe9cbf6497cb Mon Sep 17 00:00:00 2001 From: Sander Boer Date: Tue, 30 Apr 2019 14:26:27 +0200 Subject: [PATCH 02/14] merge with ifcopenshell --- test/input | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/input b/test/input index a75c92451c..2abd02c2a3 160000 --- a/test/input +++ b/test/input @@ -1 +1 @@ -Subproject commit a75c92451c8e5b63641b57ed8216849b0121d33d +Subproject commit 2abd02c2a3078fa52601dd8d5c9630c982a0e25c From 8ea77bdeca3a6d3e748bf070d1bf2de019a93d77 Mon Sep 17 00:00:00 2001 From: Sander Boer Date: Wed, 1 May 2019 18:34:01 +0200 Subject: [PATCH 03/14] Linearizing the use of IfcGeomIterator by integrating it into IfcConvert in full, atm it does not build. --- src/ifcconvert/IfcConvert.cpp | 1865 +++++++++++++++++++++------------ src/ifcgeom/IfcGeomIterator.h | 1234 +++++++++++----------- 2 files changed, 1799 insertions(+), 1300 deletions(-) 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 From 66508876a51e91fb40f69d9e7054bb21afaff01f Mon Sep 17 00:00:00 2001 From: Sander Boer Date: Fri, 3 May 2019 17:28:08 +0200 Subject: [PATCH 04/14] builds nicely, but chokes immediately on serializer- first element, needs debugging --- src/ifcconvert/IfcConvert.cpp | 2883 ++++++++++++++++++++------------- test/input | 2 +- 2 files changed, 1733 insertions(+), 1152 deletions(-) diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index 41231ad6f0..f419643ae8 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -1,37 +1,37 @@ /******************************************************************************** * * - * This file is part of IfcOpenShell. * + * This file is part of IfcOpenShell. * * * - * IfcOpenShell is free software: you can redistribute it and/or modify * - * it under the terms of the Lesser GNU General Public License as published by * - * the Free Software Foundation, either version 3.0 of the License, or * - * (at your option) any later version. * + * IfcOpenShell is free software: you can redistribute it and/or modify * it + *under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * (at + *your option) any later version. * * * - * IfcOpenShell is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * Lesser GNU General Public License for more details. * + * IfcOpenShell is distributed in the hope that it will be useful, * but WITHOUT + *ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Lesser GNU + *General Public License for more details. * * * - * You should have received a copy of the Lesser GNU General Public License * - * along with this program. If not, see . * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * * * ********************************************************************************/ /******************************************************************************** * * - * This started as a brief example of how IfcOpenShell can be interfaced from * - * within a C++ context, it has since then evolved into a fullfledged command * - * line application that is able to convert geometry in an IFC files into * - * several tesselated and topological output formats. * + * This started as a brief example of how IfcOpenShell can be interfaced from * + * within a C++ context, it has since then evolved into a fullfledged command * + * line application that is able to convert geometry in an IFC files into * + * several tesselated and topological output formats. * * * ********************************************************************************/ #include "../ifcconvert/ColladaSerializer.h" #include "../ifcconvert/IgesSerializer.h" #include "../ifcconvert/StepSerializer.h" +#include "../ifcconvert/SvgSerializer.h" #include "../ifcconvert/WavefrontObjSerializer.h" #include "../ifcconvert/XmlSerializer.h" -#include "../ifcconvert/SvgSerializer.h" #include "../ifcgeom/IfcGeomIterator.h" #include "../ifcgeom/IfcGeomRenderStyles.h" @@ -41,37 +41,37 @@ #include #include -#include #include +#include #include -#include #include +#include #include +#include #include #include -#include #if USE_VLD #include #endif #ifdef _MSC_VER -#include #include +#include // C++11 header: #include #endif #if defined(_MSC_VER) && defined(_UNICODE) typedef std::wstring path_t; -static std::wostream& cout_ = std::wcout; -static std::wostream& cerr_ = std::wcerr; +static std::wostream &cout_ = std::wcout; +static std::wostream &cerr_ = std::wcerr; #else typedef std::string path_t; -static std::ostream& cout_ = std::cout; -static std::ostream& cerr_ = std::cerr; +static std::ostream &cout_ = std::cout; +static std::ostream &cerr_ = std::cerr; #endif const std::string DEFAULT_EXTENSION = "obj"; @@ -79,9 +79,6 @@ const std::string TEMP_FILE_EXTENSION = ".tmp"; namespace po = boost::program_options; - - - struct IfcproductRepresentation { int index; @@ -97,39 +94,60 @@ struct Bounds 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); + +//////////////////////////////////////////////////////////// +// from iterator: +//////////////////////////////////////////////////////////// + +bool reuse_ok_(SerializerSettings settings, + const IfcSchema::IfcProduct::list::ptr &products, + IfcGeom::Kernel kernel); +void create_element(SerializerSettings &settings, + IfcproductRepresentation &rep, + IfcGeom::Kernel); +Bounds compute_bounds(IfcParse::IfcFile *, IfcGeom::Kernel); +void write_element(boost::shared_ptr, + IfcproductRepresentation *, bool); + + +//////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////// 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" << "\n" - << "Converts the geometry in an IFC file into one of the following formats:\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" #ifdef WITH_OPENCOLLADA << " .dae Collada Digital Assets Exchange\n" #endif << " .stp STEP Standard for the Exchange of Product Data\n" << " .igs IGES Initial Graphics Exchange Specification\n" - << " .xml XML Property definitions and decomposition tree\n" + << " .xml XML Property definitions and decomposition " + "tree\n" << " .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) { + << "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; } /// @todo Add help for single option -void print_options(const po::options_description& options) +void print_options(const po::options_description &options) { #if defined(_MSC_VER) && defined(_UNICODE) // See issue https://svn.boost.org/trac10/ticket/10952 @@ -142,18 +160,21 @@ void print_options(const po::options_description& options) cout_ << std::endl; } - -template -T change_extension(const T& fn, const T& ext) { +template T change_extension(const T &fn, const T &ext) +{ typename T::size_type dot = fn.find_last_of('.'); - if (dot != T::npos) { + if (dot != T::npos) + { return fn.substr(0, dot) + ext; - } else { + } + else + { return fn + ext; } } -bool file_exists(const std::string& filename) { +bool file_exists(const std::string &filename) +{ std::ifstream file(IfcUtil::path::from_utf8(filename).c_str()); return file.good(); } @@ -163,883 +184,1232 @@ void write_log(bool); std::string format_duration(time_t start, time_t end); /// @todo make the filters non-global -IfcGeom::entity_filter entity_filter; // Entity filter is used always by default. +IfcGeom::entity_filter + entity_filter; // Entity filter is used always by default. IfcGeom::layer_filter layer_filter; -const std::string NAME_ARG = "Name", GUID_ARG = "GlobalId", DESC_ARG = "Description", TAG_ARG = "Tag"; -boost::array supported_args = { { NAME_ARG, GUID_ARG, DESC_ARG, TAG_ARG } }; -IfcGeom::string_arg_filter guid_filter(IfcSchema::Type::IfcRoot, 0); // IfcRoot.GlobalId +const std::string NAME_ARG = "Name", GUID_ARG = "GlobalId", + DESC_ARG = "Description", TAG_ARG = "Tag"; +boost::array supported_args = { + {NAME_ARG, GUID_ARG, DESC_ARG, TAG_ARG}}; +IfcGeom::string_arg_filter guid_filter(IfcSchema::Type::IfcRoot, + 0); // IfcRoot.GlobalId // Note: skipping IfcRoot OwnerHistory, argument index 1 -IfcGeom::string_arg_filter name_filter(IfcSchema::Type::IfcRoot, 2); // IfcRoot.Name -IfcGeom::string_arg_filter desc_filter(IfcSchema::Type::IfcRoot, 3); // IfcRoot.Description -IfcGeom::string_arg_filter tag_filter(IfcSchema::Type::IfcProxy, 8, IfcSchema::Type::IfcElement, 7); // IfcProxy.Tag & IfcElement.Tag +IfcGeom::string_arg_filter name_filter(IfcSchema::Type::IfcRoot, + 2); // IfcRoot.Name +IfcGeom::string_arg_filter desc_filter(IfcSchema::Type::IfcRoot, + 3); // IfcRoot.Description +IfcGeom::string_arg_filter tag_filter(IfcSchema::Type::IfcProxy, 8, + IfcSchema::Type::IfcElement, + 7); // IfcProxy.Tag & IfcElement.Tag struct geom_filter { - geom_filter(bool include, bool traverse) : type(UNUSED), include(include), traverse(traverse) {} + 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 }; + 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. -struct inclusion_filter : public geom_filter { inclusion_filter() : geom_filter(true, false) {} }; -struct inclusion_traverse_filter : public geom_filter { inclusion_traverse_filter() : geom_filter(true, true) {} }; -struct exclusion_filter : public geom_filter { exclusion_filter() : geom_filter(false, false) {} }; -struct exclusion_traverse_filter : public geom_filter { exclusion_traverse_filter() : geom_filter(false, true) {} }; +// 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. +struct inclusion_filter : public geom_filter +{ + inclusion_filter() : geom_filter(true, false) {} +}; +struct inclusion_traverse_filter : public geom_filter +{ + inclusion_traverse_filter() : geom_filter(true, true) {} +}; +struct exclusion_filter : public geom_filter +{ + exclusion_filter() : geom_filter(false, false) {} +}; +struct exclusion_traverse_filter : public geom_filter +{ + exclusion_traverse_filter() : geom_filter(false, true) {} +}; -size_t read_filters_from_file(const std::string&, inclusion_filter&, inclusion_traverse_filter&, exclusion_filter&, exclusion_traverse_filter&); -void parse_filter(geom_filter &, const std::vector&); -std::vector setup_filters(const std::vector&, const std::string&); +size_t read_filters_from_file(const std::string &, inclusion_filter &, + inclusion_traverse_filter &, exclusion_filter &, + exclusion_traverse_filter &); +void parse_filter(geom_filter &, const std::vector &); +std::vector setup_filters(const std::vector &, + const std::string &); -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); #if defined(_MSC_VER) && defined(_UNICODE) -int wmain(int argc, wchar_t** argv) { +int wmain(int argc, wchar_t **argv) +{ typedef po::wcommand_line_parser command_line_parser; typedef wchar_t char_t; _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"); + 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"); - po::options_description fileio_options; - fileio_options.add_options() + po::options_description fileio_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.") + 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.") #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.") - ("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. ") - ("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'.") - ("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.") - ("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.") - ("deflection-tolerance", po::value(&deflection_tolerance)->default_value(1e-3), - "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.") - ("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.") - ("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."); + ("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. ")( + "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'.")( + "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.")( + "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.")( + "deflection-tolerance", + po::value(&deflection_tolerance)->default_value(1e-3), + "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.")( + "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.")( + "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."); - - std::string bounds, offset_str; + std::string bounds, offset_str; #ifdef HAVE_ICU - std::string unicode_mode; + std::string unicode_mode; #endif - short precision; - double section_height; - po::options_description serializer_options("Serialization options"); - serializer_options.add_options() + short precision; + 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'.") + ("unicode", po::value(&unicode_mode), + "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.") - ("use-element-names", - "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-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.") - ("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.") - ("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") - ("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."); + ("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.")( + "use-element-names", "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-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.")( + "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.")( + "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")( + "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."); - po::options_description cmdline_options; - cmdline_options.add(generic_options).add(fileio_options).add(geom_options).add(serializer_options); + po::options_description cmdline_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); + po::positional_options_description positional_options; + 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); - } catch (const po::unknown_option& e) { - 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; - } catch (const std::exception& e) { - 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; - } + po::variables_map vmap; + try + { + 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; + } + 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; + } + catch (const std::exception &e) + { + 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; + } - po::notify(vmap); + 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(); + if (!quiet || vmap.count("version")) + { + print_version(); + } + + 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; + } + else if (!vmap.count("input-file")) + { + 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); } - - 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; - } else if (!vmap.count("input-file")) { - std::cerr << "[Error] Input file not specified" << std::endl; + 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."); } - - 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; - } + 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.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; + } + } #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; - } + if (!default_material_filename.empty()) + { + try + { + IfcGeom::set_default_style_file( + IfcUtil::path::to_utf8(default_material_filename)); } - - 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; + 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(); + 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; } + } - 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; + 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; + } + } + + 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); + + 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"); + + 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)); + + IfcUtil::path::rename_file(IfcUtil::path::to_utf8(output_temp_filename), + IfcUtil::path::to_utf8(output_filename)); + exit_code = EXIT_SUCCESS; } } - - 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); - - 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"); - - 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)); - - 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; + catch (const std::exception &e) + { + Logger::Error(e); } + write_log(!quiet); + return exit_code; + } - /// @todo Clean up this filter code further. - std::vector used_filters; - if (include_filter.type != geom_filter::UNUSED) { used_filters.push_back(include_filter); } - if (include_traverse_filter.type != geom_filter::UNUSED) { used_filters.push_back(include_traverse_filter); } - if (exclude_filter.type != geom_filter::UNUSED) { used_filters.push_back(exclude_filter); } - if (exclude_traverse_filter.type != geom_filter::UNUSED) { used_filters.push_back(exclude_traverse_filter); } + /// @todo Clean up this filter code further. + std::vector used_filters; + if (include_filter.type != geom_filter::UNUSED) + { + used_filters.push_back(include_filter); + } + if (include_traverse_filter.type != geom_filter::UNUSED) + { + used_filters.push_back(include_traverse_filter); + } + if (exclude_filter.type != geom_filter::UNUSED) + { + used_filters.push_back(exclude_filter); + } + if (exclude_traverse_filter.type != geom_filter::UNUSED) + { + used_filters.push_back(exclude_traverse_filter); + } - 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; - } + 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; + } - 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::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(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_deflection_tolerance(deflection_tolerance); - settings.precision = precision; + 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(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_deflection_tolerance(deflection_tolerance); + settings.precision = precision; - //////////////////////////////////////////////////////////// - // Set up serializer - //////////////////////////////////////////////////////////// - - boost::shared_ptr serializer; /**< @todo use std::unique_ptr when possible */ + //////////////////////////////////////////////////////////// + // Set up serializer + //////////////////////////////////////////////////////////// - if (output_extension == OBJ) + 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) { - // 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); + 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; + } + + 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 (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; + 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; + } + + 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; + } + + //////////////////////////////////////////////////////////// + // initialize geometry + //////////////////////////////////////////////////////////// + + serializer->setFile(&ifc_file); + + // IfcGeom::Iterator context_iterator(settings, &ifc_file, + // filter_funcs); + + 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; + + try + { + + // 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); } - 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"); - } + // 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; - settings.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, true); + 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."); } - if (!serializer->ready()) { - IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); - write_log(!quiet); - return EXIT_FAILURE; + 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"); } - 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; - } + 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(); - //////////////////////////////////////////////////////////// - // initialize geometry - //////////////////////////////////////////////////////////// - - serializer->setFile(&ifc_file); - - IfcGeom::Iterator context_iterator(settings, &ifc_file, filter_funcs); - 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; + IfcSchema::IfcGeometricRepresentationContext::list::ptr filtered_contexts( + new IfcSchema::IfcGeometricRepresentationContext::list); - - try { - - // 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); + 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; } - - // 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"); - } - - 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); - } // == 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)) { + 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 { - 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 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; + + 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 + { + 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 0; + } + + // representation_iterator = representations->begin(); + // ifcproducts.reset(); + + // if (!create()) { + // return false; // } - - 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); - } - serializer->writeHeader(); + // done = 0; + // total = representations->size(); - 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; - } + // 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; + } - if (!quiet) Logger::Status("Computing bounds..."); - model_bounds = compute_bounds( &ifc_file, kernel ); - if (!quiet) Logger::Status("Done!"); + // above try block 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; + // } - 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; - } + 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); + } + + serializer->writeHeader(); + + //////////////////////////////////////////////////////////// + // with serializer ready to receive data, + // let's get to the geometry: + //////////////////////////////////////////////////////////// + + // 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; } - std::stringstream msg; - msg << "Using model offset (" << offset[0] << "," << offset[1] << "," << offset[2] << ")"; - Logger::Notice(msg.str()); + 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; + } } - if (!quiet) { - Logger::Status("Creating geometry..."); - } + std::stringstream msg; + msg << "Using model offset (" << offset[0] << "," << offset[1] << "," + << offset[2] << ")"; + Logger::Notice(msg.str()); + } - // 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; + 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. - //////////////////////////////////////////////////////////// - // start initializing elements for threading - //////////////////////////////////////////////////////////// - - std::vector IfcproductRepresentations; - - // do { - // IfcGeom::Element *geom_object = context_iterator.get(); + //////////////////////////////////////////////////////////// + // start initializing elements for threading + //////////////////////////////////////////////////////////// - // if (is_tesselated) - // { - // serializer->write(static_cast*>(geom_object)); - // } - // else - // { - // serializer->write(static_cast*>(geom_object)); - // } + // size_t num_created = 0; - // 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()); + // do { + // IfcGeom::Element *geom_object = context_iterator.get(); - // 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 (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()); - //////////////////////////////////////////////////////////// - // Copy/Paste from IfcInfo, fine-tooth comb over vars - //////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////// + // 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 (... ?) //////////////////////////////////////////////////////////// + std::vector IfcproductRepresentations; 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); + 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)); @@ -1052,33 +1422,41 @@ int wmain(int argc, wchar_t** argv) { struct filter_match { filter_match(IfcSchema::IfcProduct *prod) : product(prod) {} - bool operator()(const IfcGeom::filter_t &filter) const { return filter(product); } + 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++) + representation_iterator != representations->end(); + representation_iterator++) { IfcSchema::IfcRepresentation *representation = *representation_iterator; ifcproducts.reset(); - ifcproducts = IfcSchema::IfcProduct::list::ptr(new IfcSchema::IfcProduct::list); + 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(); + geometry_reuse_ok_for_current_representation_ = + reuse_ok_(settings, unfiltered_products, kernel); + 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 + // 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. + // 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) { @@ -1094,10 +1472,13 @@ int wmain(int argc, wchar_t** argv) { 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)); + // 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), + kernel); representation_processed_as_mapped_item = contains || reuse; } if (representation_processed_as_mapped_item) @@ -1107,8 +1488,8 @@ int wmain(int argc, wchar_t** argv) { // continue; continue; } - // Filter the products based on the set of entities and/or names being included or excluded - // for processing. + // 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) { @@ -1119,8 +1500,8 @@ int wmain(int argc, wchar_t** argv) { } } // end for unfiltered_products - for (ifcproduct_iterator = ifcproducts->begin(); ifcproduct_iterator != ifcproducts->end(); - ifcproduct_iterator++) + for (ifcproduct_iterator = ifcproducts->begin(); + ifcproduct_iterator != ifcproducts->end(); ifcproduct_iterator++) { IfcproductRepresentation ir; ir.index = index_count; @@ -1131,328 +1512,484 @@ int wmain(int argc, wchar_t** argv) { } // 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; + // int count = 0; + unsigned int conc_threads = std::thread::hardware_concurrency(); + if (conc_threads > (unsigned int)IfcproductRepresentations.size()) + { + conc_threads = (unsigned int)IfcproductRepresentations.size(); + } std::vector> threadpool; - count = 0; - for (int j = 0; j < (int)IfcproductRepresentations.size();) + + cout_ << "threads available: " << conc_threads << "\n"; + + cout_ << "ready to process " << IfcproductRepresentations.size() + << " items.\n"; + + for (int j = 0; j < (int)IfcproductRepresentations.size(); j++) { 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++; - } + create_element(settings, r, kernel); + // 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(); + // } + + + for (int j = 0; j < (int)IfcproductRepresentations.size(); j++) + { + IfcproductRepresentation *rep = &IfcproductRepresentations[j]; + //write_element(serializer, rep, is_tesselated); + cout_ << "writing to file, element #: " << rep->index << "\n"; + IfcGeom::Element *geom_object = rep->element; + if (is_tesselated) + { + serializer->write( + static_cast *>( + geom_object)); + } 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 + serializer->write( + static_cast *>(geom_object)); + } + } - - for (std::future &fu : threadpool) + //////////////////////////////////////////////////////////// + // Elements are generated and in memory (...) + // Time to write it out. + //////////////////////////////////////////////////////////// + + serializer->finalize(); + // Make sure the dtor is explicitly run here (e.g. output files are closed + // before renaming them). + serializer.reset(); + + // Renaming might fail (e.g. maybe the existing file was open in a viewer + // application) 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) { - fu.get(); + cerr_ << "Unable to write output file '" << output_filename << "', see '" + << output_temp_filename << "' for the conversion result."; } + write_log(!quiet); + time(&end); - //////////////////////////////////////////////////////////// - // - //////////////////////////////////////////////////////////// - - serializer->finalize(); - // Make sure the dtor is explicitly run here (e.g. output files are closed before renaming them). - serializer.reset(); - - // Renaming might fail (e.g. maybe the existing file was open in a viewer application) - // 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."; - } - - write_log(!quiet); - - time(&end); - - if (!quiet) { - Logger::Status("\nConversion took " + format_duration(start, end)); - } - - return successful ? EXIT_SUCCESS : EXIT_FAILURE; - } - - std::string format_duration(time_t start, time_t end) + if (!quiet) { - 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 << seconds << " second"; - if (seconds == 0 || seconds > 1) { + Logger::Status("\nConversion took " + format_duration(start, end)); + } + + return successful ? EXIT_SUCCESS : EXIT_FAILURE; +} + +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"; } - return ss.str(); + ss << " "; } - - 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) + ss << seconds << " second"; + if (seconds == 0 || seconds > 1) { - time_t start, end; + ss << "s"; + } + return ss.str(); +} - // Prevent IfcFile::Init() prints by setting output to null temporarily - if (no_progress) { Logger::SetOutput(NULL, &log_stream); } +void write_log(bool header) +{ + path_t log = log_stream.str(); + if (!log.empty()) + { + if (header) + { + cout_ << "\nLog:\n"; + } + cout_ << log << std::endl; + } +} - time(&start); +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 + if (no_progress) + { + Logger::SetOutput(NULL, &log_stream); + } + + 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); + 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)); } + if (no_progress) + { + Logger::SetOutput(&cout_, &log_stream); + } + else + { + Logger::Status("Parsing input file took " + format_duration(start, end)); + } - return true; + 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)) + { + 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; +} + +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()) + { + 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) + { + boost::trim(line); + if (line.empty()) + { + continue; } - bool append_filter(const std::string& type, const std::vector& values, geom_filter& filter) + std::vector values; + boost::split(values, line, boost::is_any_of("\t "), + boost::token_compress_on); + if (values.empty()) { - 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; + continue; } - 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::string type = values.front(); + values.erase(values.begin()); + // Support both "--include=arg GlobalId 1VQ5n5$RrEbPk8le4ZCI81" and + // "include arg GlobalId 1VQ5n5$RrEbPk8le4ZCI81" and tolerate extraneous + // whitespace. + boost::trim_left_if(type, boost::is_any_of("-")); + size_t equal_pos = type.find('='); + if (equal_pos != std::string::npos) { - std::ifstream filter_file(IfcUtil::path::from_utf8(filename).c_str()); + std::string value = type.substr(equal_pos + 1); + type = type.substr(0, equal_pos); + values.insert(values.begin(), value); + } - if (!filter_file.is_open()) { - cerr_ << "[Error] Unable to open filter file '" << IfcUtil::path::from_utf8(filename) << "' or the file does not exist.\n"; + 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"; + return 0; + } + } + return num_filters; +} - 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; - } +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") + { + filter.type = geom_filter::ENTITY_TYPE; + } + else if (type == "layers") + { + filter.type = geom_filter::LAYER_NAME; + } + 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); + } + } + 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()); +} - std::vector values; - boost::split(values, line, boost::is_any_of("\t "), boost::token_compress_on); - if (values.empty()) { - continue; - } +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; +} - std::string type = values.front(); - values.erase(values.begin()); - // Support both "--include=arg GlobalId 1VQ5n5$RrEbPk8le4ZCI81" and "include arg GlobalId 1VQ5n5$RrEbPk8le4ZCI81" - // and tolerate extraneous whitespace. - 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); - } +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; +} - 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"; - return 0; - } +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; +} + +/// @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); } - return num_filters; - } - - 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); + catch (const IfcParse::IfcException &e) + { + cerr_ << "[Error] " << e.what() << std::endl; + return std::vector(); } - std::string type = *values.begin(); - if (type == "entities") { - filter.type = geom_filter::ENTITY_TYPE; - } else if (type == "layers") { - filter.type = geom_filter::LAYER_NAME; - } 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); - } - } else { - throw po::validation_error(po::validation_error::invalid_option_value); + } + 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); } - 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_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_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) { - 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); - } - } + else if (f.arg == NAME_ARG) + { + name_filter.include = f.include; + name_filter.traverse = f.traverse; + name_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(); - } + 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 (!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) + } + + // 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; @@ -1465,17 +2002,20 @@ bool reuse_ok_(SerializerSettings settings, const IfcSchema::IfcProduct::list::p std::set associated_single_materials; - for (IfcSchema::IfcProduct::list::it it = products->begin(); it != products->end(); ++it) + for (IfcSchema::IfcProduct::list::it it = products->begin(); + it != products->end(); ++it) { IfcSchema::IfcProduct *product = *it; - if (!settings.get(IfcGeom::IteratorSettings::DISABLE_OPENING_SUBTRACTIONS) && + 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(); + IfcSchema::IfcRelAssociates::list::ptr associations = + product->HasAssociations(); for (IfcSchema::IfcRelAssociates::list::it jt = associations->begin(); jt != associations->end(); ++jt) { @@ -1483,7 +2023,8 @@ bool reuse_ok_(SerializerSettings settings, const IfcSchema::IfcProduct::list::p (*jt)->as(); if (assoc) { - if (assoc->RelatingMaterial()->is(IfcSchema::Type::IfcMaterialLayerSetUsage)) + if (assoc->RelatingMaterial()->is( + IfcSchema::Type::IfcMaterialLayerSetUsage)) { // TODO: Check whether single layer? return false; @@ -1491,30 +2032,59 @@ bool reuse_ok_(SerializerSettings settings, const IfcSchema::IfcProduct::list::p } } } - // 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)); + // 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) +void write_element(boost::shared_ptr serializer, + IfcproductRepresentation *rep, bool is_tesselated) { + // IfcGeom::Element *geom_object = context_iterator.get(); + + IfcGeom::Element *geom_object = rep->element; + if (is_tesselated) + { + serializer->write( + static_cast *>( + geom_object)); + } + else + { + serializer->write( + static_cast *>(geom_object)); + } +} + +void create_element(SerializerSettings &settings, IfcproductRepresentation &rep, + IfcGeom::Kernel kernel) +{ + + + //////////////////////////////////////////////////////////// + // is kernel thread-safe (-ish) ? + //////////////////////////////////////////////////////////// + Logger::Status("processing item #: " + std::to_string(rep.index)); - IfcGeom::Kernel kernel; - IfcSchema::IfcRepresentation *representation= rep.representation; + // 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_) + 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); - // } - + // // element = + // kernel.create_brep_for_processed_representation(settings, + // representation, + // // product, + // // current_shape_model); + // } return; } @@ -1522,48 +2092,59 @@ void create_element(SerializerSettings &settings, IfcproductRepresentation &rep) //@todo MOVE this include #include -Bounds compute_bounds(IfcParse::IfcFile* ifc_file, IfcGeom::Kernel kernel) +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) { - 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()); - } - - 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; + 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())); + } + } + bounds.min = bounds_min_; + bounds.max = bounds_max_; + return bounds; +} diff --git a/test/input b/test/input index 2abd02c2a3..e3d6488df2 160000 --- a/test/input +++ b/test/input @@ -1 +1 @@ -Subproject commit 2abd02c2a3078fa52601dd8d5c9630c982a0e25c +Subproject commit e3d6488df2dcc55308dc8810b1f9d911117dcbee From 0b02f7fb2b285dae89443582302e5b287c81f5df Mon Sep 17 00:00:00 2001 From: Sander Boer Date: Fri, 3 May 2019 17:30:48 +0200 Subject: [PATCH 05/14] .clang-format settings indent=2 for me, temporarily. style=allman width=80 --- .clang-format | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000..c4f8f88ee8 --- /dev/null +++ b/.clang-format @@ -0,0 +1,14 @@ +# We'll use defaults from the LLVM style +# https://clangformat.com/ + +BasedOnStyle: llvm +IndentWidth: 2 +ColumnLimit: 80 +BreakBeforeBraces: Allman +AlignTrailingComments: true + + +Language: Cpp +## Force pointers to the type for C++. +# DerivePointerAlignment: false +# PointerAlignment: Right From a286598e55f6a0dc6854af45f265e9712bd059d4 Mon Sep 17 00:00:00 2001 From: You Yue Date: Tue, 2 Jul 2019 12:10:08 +0200 Subject: [PATCH 06/14] Sander boer's change for multithreading 1. Added multithread for create element 2. Added support function in IfcConvert.cpp for multithreading at bottom --- src/ifcconvert/IfcConvert.cpp | 184 +++++++++++++++++++++++++--------- 1 file changed, 136 insertions(+), 48 deletions(-) diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index 49f707a93c..1142b685e5 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -787,61 +787,61 @@ int main(int argc, char** argv) { 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; + /// =============== Sanders approach for multiple threading ============================ + for (int j = 0; j < (int)IfcproductRepresentations.size(); j++) + { + IfcproductRepresentation &r = IfcproductRepresentations[j]; + create_element(settings, r, kernel2x3); - do { - IfcGeom::Element *geom_object = context_iterator.get(); - - if (is_tesselated) + if (threadpool.size() < concurrency) { - serializer->write(static_cast*>(geom_object)); + std::future fu = std::async(std::launch::async, create_element, std::ref(settings), std::ref(r), std::ref(kernel2x3)); + threadpool.emplace_back(std::move(fu)); + j++; } else { - serializer->write(static_cast*>(geom_object)); - } + bool waiting = true; + while (waiting) + { + for (int i = 0; i < (int)threadpool.size(); i++) + { + cout << "Thread pool size: " << threadpool.size(); + 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 + } - 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 << "."; + // Serializer + for (int j = 0; j < (int)IfcproductRepresentations.size(); j++) + { + IfcproductRepresentation *rep = &IfcproductRepresentations[j]; + //write_element(serializer, rep, is_tesselated); + cout_ << "writing to file, element #: " << rep->index << "\n"; + IfcGeom::Element *geom_object = rep->element; + if (geom_object == nullptr) + { + cout_ << "skipped" << std::endl; + continue; } - std::cout << std::flush; - if (stderr_progress) - std::cerr << std::flush; - } else { - Logger::Status("\rDone creating geometry (" + boost::lexical_cast(num_created) + - " objects) "); + if (is_tesselated) + { + serializer->write(static_cast *>(geom_object)); + } + else + { + serializer->write(static_cast *>(geom_object)); + } + } serializer->finalize(); @@ -1348,3 +1348,91 @@ void fix_quantities(IfcParse::IfcFile& f, bool no_progress, bool quiet, bool std } } + + +////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////// ***** Added for multithreading ****** ///////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool reuse_ok_(SerializerSettings settings, const IfcSchema::IfcProduct::list::ptr &products, IfcGeom::Kernel kernel) +{ + IfcGeom::KernelIfc2x3 kernel2x3; + + 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) && + kernel2x3.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::IfcMaterialLayerSetUsage)) + if (assoc->RelatingMaterial()->declaration().is(IfcSchema::IfcMaterialLayerSetUsage::Class())) + { + return false; + } + } + } + } + associated_single_materials.insert(kernel2x3.get_single_material_association(product)); + if (associated_single_materials.size() > 1) + { + return false; + } + } + return associated_single_materials.size() == 1; +} + +void write_element(boost::shared_ptr serializer, IfcproductRepresentation *rep, bool is_tesselated) +{ + IfcGeom::Element *geom_object = rep->element; + if (is_tesselated) + { + serializer->write(static_cast *>(geom_object)); + } + else + { + serializer->write(static_cast *>(geom_object)); + } +} + +void create_element(SerializerSettings &settings, IfcproductRepresentation &rep, IfcGeom::KernelIfc2x3& kernel2x3) +{ + //////////////////////////////////////////////////////////// + // is kernel thread-safe ? + // @tfk: no, it's not, my advise would be to boot one + // kernel instance per thread. + //////////////////////////////////////////////////////////// + Logger::Status("processing item #: " + std::to_string(rep.index)); + IfcSchema::IfcRepresentation *representation = rep.representation; + IfcSchema::IfcProduct *product = rep.product; + // IfcGeom::BRepElement *element; + //rep.element = kernel2x3.create_brep_for_representation_and_product(settings, representation, product); + rep.brep = kernel2x3.create_brep_for_representation_and_product(settings, representation, product); + rep.element = rep.brep ? new IfcGeom::TriangulationElement(*rep.brep) : nullptr; + // if(geometry_reuse_ok_for_current_representation_) + // { + // // element = + // kernel.create_brep_for_processed_representation(settings, + // representation, + // // product, + // // current_shape_model); + // } + + return; +} \ No newline at end of file From 0dec407dbe0ebb946c42d726e024d57a10e3da71 Mon Sep 17 00:00:00 2001 From: You Yue Date: Tue, 2 Jul 2019 12:30:40 +0200 Subject: [PATCH 07/14] Last commit for multithreading, Airsquire implementation --- src/ifcconvert/IfcConvert.cpp | 307 ++++++++++++++++-- .../ThreadedIteratorImplementation.h | 0 test/input | 2 +- 3 files changed, 276 insertions(+), 33 deletions(-) create mode 100644 src/ifcconvert/ThreadedIteratorImplementation.h diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index 1142b685e5..9bd6244366 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -54,6 +54,25 @@ #include #include +/////////////// Multithreading part ////////////// +#include +#include +#include +#include +#include "../ifcparse/Ifc2x3.h" +#include "../ifcparse/Ifc4.h" +#ifdef USE_IFC4 +#include "../ifcparse/Ifc4.h" +#define IfcSchema Ifc4 +#else +#include "../ifcparse/Ifc2x3.h" +#define IfcSchema Ifc2x3 +#endif +#include "../ifcgeom/IfcGeom.h" +#include "../serializers/GeometrySerializer.h" +#include "../ifcgeom/IfcGeomIteratorImplementation.h" +#include "ThreadedIteratorImplementation.h" + #if USE_VLD #include #endif @@ -80,6 +99,29 @@ const std::string TEMP_FILE_EXTENSION = ".tmp"; namespace po = boost::program_options; +using namespace multithreading; + +struct IfcproductRepresentation +{ + int index; + IfcSchema::IfcRepresentation *representation; + IfcSchema::IfcProduct *product; + IfcGeom::Element *geom_object; + IfcGeom::BRepElement *brep; + IfcGeom::TriangulationElement *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, IfcGeom::KernelIfc2x3&); +Bounds compute_bounds(IfcParse::IfcFile *, IfcGeom::Kernel); +void write_element(boost::shared_ptr, IfcproductRepresentation *, bool); + void print_version() { cout_ << "IfcOpenShell IfcConvert " << IFCOPENSHELL_VERSION << " (OCC " << OCC_VERSION_STRING_EXT << ")\n"; @@ -672,7 +714,7 @@ int main(int argc, char** argv) { // According to https://tracker.dev.opencascade.org/view.php?id=25689 something has been fixed in 6.9.0 IGESControl_Controller::Init(); // work around Open Cascade bug #endif - serializer = boost::make_shared(IfcUtil::path::to_utf8(output_temp_filename), settings); + 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); @@ -752,7 +794,8 @@ int main(int argc, char** argv) { serializer->writeHeader(); int old_progress = quiet ? 0 : -1; - + + Bounds model_bounds; if (is_tesselated && (center_model || model_offset)) { double* offset = serializer->settings().offset; if (center_model) { @@ -787,42 +830,185 @@ int main(int argc, char** argv) { Logger::Status("Creating geometry..."); } + + /// =============== Sanders approach for multiple threading ============================ - for (int j = 0; j < (int)IfcproductRepresentations.size(); j++) - { - IfcproductRepresentation &r = IfcproductRepresentations[j]; - create_element(settings, r, kernel2x3); + // for (int j = 0; j < (int)IfcproductRepresentations.size(); j++) + // { + // IfcproductRepresentation &r = IfcproductRepresentations[j]; + // create_element(settings, r, kernel2x3); - if (threadpool.size() < concurrency) + // if (threadpool.size() < concurrency) + // { + // std::future fu = std::async(std::launch::async, create_element, std::ref(settings), std::ref(r), std::ref(kernel2x3)); + // threadpool.emplace_back(std::move(fu)); + // j++; + // } + // else + // { + // bool waiting = true; + // while (waiting) + // { + // for (int i = 0; i < (int)threadpool.size(); i++) + // { + // cout << "Thread pool size: " << threadpool.size(); + // 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 + // } + + // // Serializer + // for (int j = 0; j < (int)IfcproductRepresentations.size(); j++) + // { + // IfcproductRepresentation *rep = &IfcproductRepresentations[j]; + // //write_element(serializer, rep, is_tesselated); + // cout_ << "writing to file, element #: " << rep->index << "\n"; + // IfcGeom::Element *geom_object = rep->element; + // if (geom_object == nullptr) + // { + // cout_ << "skipped" << std::endl; + // continue; + // } + // if (is_tesselated) + // { + // serializer->write(static_cast *>(geom_object)); + // } + // else + // { + // serializer->write(static_cast *>(geom_object)); + // } + + // } + + //================= Airsquire approach =================== + + IfcGeom::Kernel kernel; + IfcGeom::KernelIfc2x3 kernel2x3; + size_t num_created = 0; + int currentElementIndex = 0; + 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; + + IfcSchema::IfcMaterialLayerSetUsage::Class(); + vector> threadpool; + unsigned int concurrency = std::thread::hardware_concurrency(); + cout << "Threads available: " << concurrency << endl; + + // From Sander's version/work (v0.5) + std::vector filters_; + std::vector IfcproductRepresentations; + IfcSchema::IfcProduct::list::ptr ifcproducts; + IfcSchema::IfcProduct::list::it ifcproduct_iterator; + IfcGeom::entity_filter entity_filter; // Entity filter is used always by default. + IfcGeom::layer_filter layer_filter; + IfcGeom::attribute_filter attribute_filter; + + // Version v0.6 + filters_.emplace_back(boost::ref(layer_filter)); + filters_.emplace_back(boost::ref(entity_filter)); + filters_.emplace_back(boost::ref(attribute_filter)); + bool geometry_reuse_ok_for_current_representation_; + + // fucntor + struct filter_match + { + filter_match(IfcSchema::IfcProduct *prod) : product(prod) {} + bool operator()(const IfcGeom::filter_t &filter) const { - std::future fu = std::async(std::launch::async, create_element, std::ref(settings), std::ref(r), std::ref(kernel2x3)); - threadpool.emplace_back(std::move(fu)); - j++; + return filter(product); } - else + IfcSchema::IfcProduct *product; + }; + + int index_count = 0; + for (representation_iterator = representations->begin(); + representation_iterator != representations->end(); + representation_iterator++) + { + IfcSchema::IfcRepresentation *representation = *representation_iterator; + //ifcproducts.reset(); + ifcproducts.reset(new IfcSchema::IfcProduct::list); + ifcproducts = IfcSchema::IfcProduct::list::ptr(new IfcSchema::IfcProduct::list); + IfcSchema::IfcProduct::list::ptr unfiltered_products = kernel2x3.products_represented_by(representation); + + geometry_reuse_ok_for_current_representation_ = reuse_ok_(settings, unfiltered_products, kernel2x3); + IfcSchema::IfcRepresentationMap::list::ptr maps = representation->RepresentationMap(); + + if(!geometry_reuse_ok_for_current_representation_ && maps->size() == 1) { - bool waiting = true; - while (waiting) - { - for (int i = 0; i < (int)threadpool.size(); i++) - { - cout << "Thread pool size: " << threadpool.size(); - 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 + IfcSchema::IfcRepresentationMap *map = *maps->begin(); + if(map->MapUsage()->size() > 0) + { + continue; + } + } + + bool representation_processed_as_mapped_item = false; + IfcSchema::IfcRepresentation *representation_mapped_to = kernel2x3.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, kernel2x3.products_represented_by(representation_mapped_to), kernel); + representation_processed_as_mapped_item = contains || reuse; + } + if (representation_processed_as_mapped_item) + { + ok_mapped_representations->push(representation_mapped_to); + // _nextShape(); + 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); + } + } + 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++; + } } - // Serializer - for (int j = 0; j < (int)IfcproductRepresentations.size(); j++) + vector> futureVector; + for (int i = 0; i < (int)IfcproductRepresentations.size(); i++) + { + IfcGeom::KernelIfc2x3 kernel2x3; + IfcproductRepresentation &r = IfcproductRepresentations[i]; + futureVector.emplace_back( + multithreading::ThreadPool::enqueue( + &create_element, + settings, + r, + kernel2x3) + ); + } + + + for (int j = 0; j < (int)IfcproductRepresentations.size(); j++) { IfcproductRepresentation *rep = &IfcproductRepresentations[j]; //write_element(serializer, rep, is_tesselated); @@ -839,11 +1025,12 @@ int main(int argc, char** argv) { } else { - serializer->write(static_cast *>(geom_object)); + serializer->write(static_cast *>(geom_object)); } } + serializer->finalize(); // Make sure the dtor is explicitly run here (e.g. output files are closed before renaming them). serializer.reset(); @@ -1435,4 +1622,60 @@ void create_element(SerializerSettings &settings, IfcproductRepresentation &rep, // } return; +} + +Bounds compute_bounds(IfcParse::IfcFile *ifc_file, IfcGeom::Kernel kernel) +{ + IfcGeom::KernelIfc2x3 kernel2x3; + 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()); + } + + IfcSchema::IfcProduct::list::ptr products = ifc_file->instances_by_type(); + 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 = kernel2x3.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; } \ No newline at end of file diff --git a/src/ifcconvert/ThreadedIteratorImplementation.h b/src/ifcconvert/ThreadedIteratorImplementation.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/input b/test/input index 8fb6b6610d..a75c92451c 160000 --- a/test/input +++ b/test/input @@ -1 +1 @@ -Subproject commit 8fb6b6610dd1ec4d2a2426dd6c7bab1549ad76cf +Subproject commit a75c92451c8e5b63641b57ed8216849b0121d33d From 3ebca0516e8484b7369e03737da23dcba31ce45f Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Wed, 3 Jul 2019 12:10:27 +0200 Subject: [PATCH 08/14] Implement multi_threaded implementation in Iterator --- src/ifcconvert/IfcConvert.cpp | 6 +- src/ifcgeom/IfcGeomIteratorImplementation.cpp | 4 +- src/ifcgeom/IfcGeomIteratorImplementation.h | 218 +++++++++++++--- src/ifcgeom_schema_agnostic/IfcGeomIterator.h | 8 +- .../IteratorImplementation.cpp | 4 +- .../IteratorImplementation.h | 8 +- src/ifcparse/IfcCharacterDecoder.cpp | 242 +++++++++++------- src/ifcparse/IfcCharacterDecoder.h | 10 +- src/ifcparse/IfcFile.h | 3 +- src/ifcparse/IfcLogger.cpp | 4 + src/ifcparse/IfcParse.cpp | 82 ++++-- src/ifcparse/IfcParse.h | 18 +- src/ifcparse/IfcSpfStream.h | 4 + 13 files changed, 434 insertions(+), 177 deletions(-) diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index 49f707a93c..bca7396422 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -218,9 +218,13 @@ int main(int argc, char** argv) { ifc_options.add_options() ("calculate-quantities", "Calculate or fix the physical quantity definitions " "based on an interpretation of the geometry when exporting IFC"); + + size_t num_threads; po::options_description geom_options("Geometry options"); geom_options.add_options() + ("threads,j", po::value(&num_threads)->default_value(1), + "Number of parallel processing threads for geometry interpretation.") ("plan", "Specifies whether to include curves in the output result. Typically " "these are representations of type Plan or Axis. Excluded by default.") @@ -730,7 +734,7 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } - IfcGeom::Iterator context_iterator(settings, ifc_file, filter_funcs); + IfcGeom::Iterator context_iterator(settings, ifc_file, filter_funcs, num_threads); 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. diff --git a/src/ifcgeom/IfcGeomIteratorImplementation.cpp b/src/ifcgeom/IfcGeomIteratorImplementation.cpp index d693dbfa68..3d3616bf21 100644 --- a/src/ifcgeom/IfcGeomIteratorImplementation.cpp +++ b/src/ifcgeom/IfcGeomIteratorImplementation.cpp @@ -14,8 +14,8 @@ namespace IfcGeom { namespace { template struct MAKE_TYPE_NAME(factory_t) { - IfcGeom::IteratorImplementation* operator()(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters) const { - return new IfcGeom::MAKE_TYPE_NAME(IteratorImplementation_)(settings, file, filters); + IfcGeom::IteratorImplementation* operator()(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters, size_t num_threads) const { + return new IfcGeom::MAKE_TYPE_NAME(IteratorImplementation_)(settings, file, filters, num_threads); } }; } diff --git a/src/ifcgeom/IfcGeomIteratorImplementation.h b/src/ifcgeom/IfcGeomIteratorImplementation.h index 84e1ffdcec..6c79da4bcc 100644 --- a/src/ifcgeom/IfcGeomIteratorImplementation.h +++ b/src/ifcgeom/IfcGeomIteratorImplementation.h @@ -64,6 +64,10 @@ #include #include +#include +#include +#include + #include #include @@ -92,12 +96,46 @@ #undef max #endif +namespace { + template + struct geometry_conversion_task { + int index; + IfcSchema::IfcRepresentation *representation; + IfcSchema::IfcProduct::list::ptr products; + std::vector*> breps; + std::vector*> elements; + }; + + template + void create_element( + IfcGeom::MAKE_TYPE_NAME(Kernel)* kernel, + const IfcGeom::IteratorSettings& settings, + geometry_conversion_task* rep) + { + IfcSchema::IfcRepresentation *representation = rep->representation; + IfcSchema::IfcProduct *product = *rep->products->begin(); + rep->breps = { kernel->create_brep_for_representation_and_product(settings, representation, product) }; + // @todo based on settings + rep->elements = { rep->breps[0] ? new IfcGeom::TriangulationElement(*rep->breps[0]) : nullptr }; + + for (auto it = rep->products->begin() + 1; it != rep->products->end(); ++it) { + rep->breps.push_back(kernel->create_brep_for_processed_representation(settings, representation, *it, rep->breps[0])); + rep->elements.push_back(rep->breps.back() ? new IfcGeom::TriangulationElement(*rep->breps.back()) : nullptr); + } + } +} + namespace IfcGeom { template class MAKE_TYPE_NAME(IteratorImplementation_) : public IteratorImplementation { private: + size_t num_threads_; + std::vector> tasks_; + std::vector*> all_processed_elements_; + typename std::vector*>::const_iterator task_result_iterator_; + MAKE_TYPE_NAME(IteratorImplementation_)(const MAKE_TYPE_NAME(IteratorImplementation_)&); // N/I MAKE_TYPE_NAME(IteratorImplementation_)& operator=(const MAKE_TYPE_NAME(IteratorImplementation_)&); // N/I @@ -282,16 +320,90 @@ namespace IfcGeom { representation_iterator = representations->begin(); ifcproducts.reset(); - if (!create()) { - return false; - } - done = 0; total = representations->size(); + if (num_threads_ != 1) { + collect(); + process_concurrently(); + } else { + if (!create()) { + return false; + } + } + return true; } + void collect() { + int i = 0; + IfcSchema::IfcProduct::list* previous = nullptr; + while (auto rp = get_next_task()) { + // Note that get_next_task() mutates the state of the iterator + // we use that capture all products that can be processed as + // part of this representation and then keep iterating until + // the underlying list of products changes. + if (ifcproducts.get() != previous) { + previous = ifcproducts.get(); + geometry_conversion_task t; + t.index = i++; + t.representation = *representation_iterator; + t.products = ifcproducts; + tasks_.emplace_back(t); + } + + _nextShape(); + } + } + + void process_concurrently() { + unsigned int conc_threads = std::thread::hardware_concurrency(); + if (conc_threads > (unsigned int)tasks_.size()) { + conc_threads = (unsigned int)tasks_.size(); + } + + std::vector kernel_pool; + kernel_pool.reserve(conc_threads); + for (unsigned i = 0; i < conc_threads; ++i) { + kernel_pool.push_back(new MAKE_TYPE_NAME(Kernel)(kernel)); + } + + std::vector> threadpool; + + for (auto& rep : tasks_) { + auto K = kernel_pool[threadpool.size()]; + + while (threadpool.size() == conc_threads) { + 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(); + std::swap(threadpool[i], threadpool.back()); + threadpool.pop_back(); + std::swap(kernel_pool[i], kernel_pool.back()); + K = kernel_pool.back(); + break; + } // if + } // for + } // while + + std::future fu = std::async(std::launch::async, create_element, K, std::ref(settings), &rep); + threadpool.emplace_back(std::move(fu)); + } + + for (std::future &fu : threadpool) { + fu.get(); + } + + for (auto& rep : tasks_) { + all_processed_elements_.insert(all_processed_elements_.end(), rep.elements.begin(), rep.elements.end()); + } + + task_result_iterator_ = all_processed_elements_.begin(); + } + /// Computes model's bounding box (bounds_min and bounds_max). /// @note Can take several minutes for large files. void compute_bounds() @@ -403,13 +515,13 @@ namespace IfcGeom { return associated_single_materials.size() == 1; } - BRepElement* create_shape_model_for_next_entity() { + boost::optional> get_next_task() { for (;;) { IfcSchema::IfcRepresentation* representation; - if ( representation_iterator == representations->end() ) { + if (representation_iterator == representations->end()) { representations.reset(); - return 0; // reached the end of our list of representations + return boost::none; // reached the end of our list of representations } representation = *representation_iterator; @@ -417,20 +529,20 @@ namespace IfcGeom { // 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); - } - } + // 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 (ifcproducts->size() == 0) { - _nextShape(); - continue; - } + if (ifcproducts->size() == 0) { + _nextShape(); + continue; + } - geometry_reuse_ok_for_current_representation_ = reuse_ok_(ifcproducts); + geometry_reuse_ok_for_current_representation_ = reuse_ok_(ifcproducts); IfcSchema::IfcRepresentationMap::list::ptr maps = representation->RepresentationMap(); @@ -450,14 +562,14 @@ namespace IfcGeom { // 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); + 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) || reuse_ok_(kernel.products_represented_by(representation_mapped_to))); + representation_processed_as_mapped_item = geometry_reuse_ok_for_current_representation_ && ( + ok_mapped_representations->contains(representation_mapped_to) || reuse_ok_(kernel.products_represented_by(representation_mapped_to))); } if (representation_processed_as_mapped_item) { - ok_mapped_representations->push(representation_mapped_to); + ok_mapped_representations->push(representation_mapped_to); _nextShape(); continue; } @@ -466,13 +578,28 @@ namespace IfcGeom { } // Have we reached the end of our list of IfcProducts? - if ( ifcproduct_iterator == ifcproducts->end() ) { + if (ifcproduct_iterator == ifcproducts->end()) { _nextShape(); continue; } IfcSchema::IfcProduct* product = *ifcproduct_iterator; - Logger::SetProduct(product); + + + return std::make_pair(representation, product); + } + } + + BRepElement* create_shape_model_for_next_entity() { + for (;;) { + auto rp = get_next_task(); + if (!rp) { + return nullptr; + } + auto representation = rp->first; + auto product = rp->second; + + Logger::SetProduct(product); BRepElement* element; if (ifcproduct_iterator == ifcproducts->begin() || !geometry_reuse_ok_for_current_representation_) { @@ -520,13 +647,24 @@ namespace IfcGeom { /// Moves to the next shape representation, create its geometry, and returns the associated product. /// Use get() to retrieve the created geometry. IfcUtil::IfcBaseClass* next() { - // Increment the iterator over the list of products using the current - // shape representation - if (ifcproducts) { - ++ifcproduct_iterator; - } + if (num_threads_ != 1) { + do { + task_result_iterator_++; + } while (task_result_iterator_ != all_processed_elements_.end() && *task_result_iterator_ == nullptr); + if (task_result_iterator_ == all_processed_elements_.end()) { + return nullptr; + } else { + return (*task_result_iterator_)->product(); + } + } else { + // Increment the iterator over the list of products using the current + // shape representation + if (ifcproducts) { + ++ifcproduct_iterator; + } - return create(); + return create(); + } } /// Gets the representation of the current geometrical entity. @@ -534,9 +672,18 @@ namespace IfcGeom { { // 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 (num_threads_ != 1) { + ret = *task_result_iterator_; + } else { + 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)) @@ -721,11 +868,12 @@ namespace IfcGeom { bool owns_ifc_file; public: - MAKE_TYPE_NAME(IteratorImplementation_)(const IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters) + MAKE_TYPE_NAME(IteratorImplementation_)(const IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters, size_t num_threads) : settings(settings) , ifc_file(file) , filters_(filters) , owns_ifc_file(false) + , num_threads_(num_threads) { _initialize(); } diff --git a/src/ifcgeom_schema_agnostic/IfcGeomIterator.h b/src/ifcgeom_schema_agnostic/IfcGeomIterator.h index 4658d37780..9d46b39ac3 100644 --- a/src/ifcgeom_schema_agnostic/IfcGeomIterator.h +++ b/src/ifcgeom_schema_agnostic/IfcGeomIterator.h @@ -83,19 +83,19 @@ namespace IfcGeom { IteratorImplementation* implementation_; public: - Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file) + Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, size_t num_threads = 1) : file_(file) , settings_(settings) { - implementation_ = iterator_implementations().construct(file_->schema()->name(), settings, file, filters_); + implementation_ = iterator_implementations().construct(file_->schema()->name(), settings, file, filters_, num_threads); } - Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters) + Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters, size_t num_threads = 1) : file_(file) , settings_(settings) , filters_(filters) { - implementation_ = iterator_implementations().construct(file_->schema()->name(), settings, file, filters_); + implementation_ = iterator_implementations().construct(file_->schema()->name(), settings, file, filters_, num_threads); } bool initialize() { diff --git a/src/ifcgeom_schema_agnostic/IteratorImplementation.cpp b/src/ifcgeom_schema_agnostic/IteratorImplementation.cpp index 10608d8cb7..cf2f89f2df 100644 --- a/src/ifcgeom_schema_agnostic/IteratorImplementation.cpp +++ b/src/ifcgeom_schema_agnostic/IteratorImplementation.cpp @@ -31,14 +31,14 @@ void IteratorFactoryImplementation::bind(const std::string& schema_name, } template -IfcGeom::IteratorImplementation* IteratorFactoryImplementation::construct(const std::string& schema_name, const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters) { +IfcGeom::IteratorImplementation* IteratorFactoryImplementation::construct(const std::string& schema_name, const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters, size_t num_threads) { const std::string schema_name_lower = boost::to_lower_copy(schema_name); typename std::map::type>::const_iterator it; it = this->find(schema_name_lower); if (it == this->end()) { throw IfcParse::IfcException("No geometry iterator registered for " + schema_name); } - return it->second(settings, file, filters); + return it->second(settings, file, filters, num_threads); } diff --git a/src/ifcgeom_schema_agnostic/IteratorImplementation.h b/src/ifcgeom_schema_agnostic/IteratorImplementation.h index 69ddbb618f..5622c0eec8 100644 --- a/src/ifcgeom_schema_agnostic/IteratorImplementation.h +++ b/src/ifcgeom_schema_agnostic/IteratorImplementation.h @@ -23,9 +23,9 @@ namespace IfcGeom { class BRepElement; } -typedef boost::function3*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&> iterator_float_float_fn; -typedef boost::function3*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&> iterator_float_double_fn; -typedef boost::function3*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&> iterator_double_double_fn; +typedef boost::function4*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, size_t> iterator_float_float_fn; +typedef boost::function4*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, size_t> iterator_float_double_fn; +typedef boost::function4*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, size_t> iterator_double_double_fn; template struct get_factory_type {}; @@ -50,7 +50,7 @@ class IteratorFactoryImplementation : public std::map::type fn); - IfcGeom::IteratorImplementation* construct(const std::string& schema_name, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&); + IfcGeom::IteratorImplementation* construct(const std::string& schema_name, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, size_t); }; template diff --git a/src/ifcparse/IfcCharacterDecoder.cpp b/src/ifcparse/IfcCharacterDecoder.cpp index c0957f7684..3aef95d15f 100644 --- a/src/ifcparse/IfcCharacterDecoder.cpp +++ b/src/ifcparse/IfcCharacterDecoder.cpp @@ -85,112 +85,162 @@ IfcCharacterDecoder::IfcCharacterDecoder(IfcParse::IfcSpfStream* f) { IfcCharacterDecoder::~IfcCharacterDecoder() { } -IfcCharacterDecoder::operator std::string() { - unsigned int parse_state = 0; - builder_.clear(); - builder_.push_back('\''); - char current_char; - int codepage = 1; - unsigned int hex = 0; - unsigned int hex_count = 0; +namespace { + static unsigned int reference_helper = 0; - while ( (current_char = file->Peek()) != 0 ) { - if ( EXPECTS_CHARACTER(parse_state) ) { - builder_.push_back(IfcUtil::convert_codepage(codepage, current_char + 0x80)); - parse_state = 0; - } else if ( current_char == '\'' && ! parse_state ) { - parse_state = APOSTROPHE; - } else if ( current_char == '\\' && ! parse_state ) { - parse_state = FIRST_SOLIDUS; - } else if ( current_char == '\\' && EXPECTS_SOLIDUS(parse_state) ) { - if ( parse_state & ALPHABET_DEFINITION || - parse_state & IGNORED_DIRECTIVE || - parse_state & ENDEXTENDED_0 ) parse_state = hex = hex_count = 0; - else if ( parse_state & ENCOUNTERED_HEX ) { - parse_state += THIRD_SOLIDUS; - parse_state -= ENCOUNTERED_HEX; + class pure_impure_helper { + private: + bool pure_; + IfcParse::IfcSpfStream* stream_; + unsigned int& pointer_; + std::wstring builder_; + + char peek() { + if (pure_) { + return stream_->peek_at(pointer_); + } else { + return stream_->Peek(); } - else parse_state += SECOND_SOLIDUS; - } else if ( current_char == 'X' && EXPECTS_ENDEXTENDED_X(parse_state) ) { - parse_state += ENDEXTENDED_X; - } else if ( current_char == '0' && EXPECTS_ENDEXTENDED_0(parse_state) ) { - parse_state += ENDEXTENDED_0; - } else if ( current_char == 'X' && EXPECTS_ARBITRARY(parse_state) ) { - parse_state += ARBITRARY; - } else if ( current_char == '2' && EXPECTS_ARBITRARY2(parse_state) ) { - parse_state += EXTENDED2; - } else if ( current_char == '4' && EXPECTS_ARBITRARY2(parse_state) ) { - parse_state += EXTENDED2 + EXTENDED4; - } else if ( current_char == 'P' && EXPECTS_ALPHABET(parse_state) ) { - parse_state += ALPHABET; - } else if ( (current_char == 'N' || current_char == 'F') && EXPECTS_N_OR_F(parse_state) ) { - parse_state += IGNORED_DIRECTIVE; - } else if ( IS_VALID_ALPHABET_DEFINITION(current_char) && EXPECTS_ALPHABET_DEFINITION(parse_state) ) { - codepage = current_char - 0x40; - parse_state += ALPHABET_DEFINITION; - } else if ( current_char == 'S' && EXPECTS_PAGE(parse_state) ) { - parse_state += PAGE; - } else if ( IS_HEXADECIMAL(current_char) && EXPECTS_HEX(parse_state) ) { - hex <<= 4; - parse_state += HEX((++hex_count)); - hex += HEX_TO_INT(current_char); - if ( (hex_count == 2 && !(parse_state & EXTENDED2)) || - (hex_count == 4 && !(parse_state & EXTENDED4)) || - (hex_count == 8) ) - { - builder_.push_back(hex); - if ( hex_count == 2 ) parse_state = 0; - else { - CLEAR_HEX(parse_state); - parse_state |= ENCOUNTERED_HEX; - } - hex = hex_count = 0; - } - } else if ( parse_state && !( - (current_char == '\\' && parse_state == FIRST_SOLIDUS) || - (current_char == '\'' && parse_state == APOSTROPHE) - ) ) { - if ( parse_state == APOSTROPHE && current_char != '\'' ) break; - throw IfcInvalidTokenException(file->Tell(), current_char); - } else { - parse_state = hex = hex_count = 0; - builder_.push_back(current_char); } - file->Inc(); - } - builder_.push_back('\''); - if (mode == UTF8) { - return IfcUtil::convert_utf8(builder_); - } else if (mode == SUBSTITUTE) { - std::string r; - r.reserve(builder_.size()); - const char& sub = substitution_character; - std::transform(builder_.begin(), builder_.end(), std::back_inserter(r), [&sub](wchar_t c) { - if (c >= 0x20 && c <= 0x7e) { - return (char)c; + unsigned int tell() { + if (pure_) { + return pointer_; } else { - return sub; + return stream_->Tell(); } - }); - return r; - } else if (mode == ESCAPE) { - std::stringstream str; - str << std::hex << std::setw(4) << std::setfill('0'); - std::for_each(builder_.begin(), builder_.end(), [&str](wchar_t c) { - if (c >= 0x20 && c <= 0x7e) { - str.put((char)c); + } + + void increment() { + if (pure_) { + stream_->increment_at(pointer_); } else { - str << "\\u" << c; + stream_->Inc(); } - }); - return str.str(); - } else { - throw IfcParse::IfcException("Invalid conversion mode"); - } + } + + public: + pure_impure_helper(IfcParse::IfcSpfStream* stream) + : pure_(false), stream_(stream), pointer_(reference_helper) + {} + + pure_impure_helper(IfcParse::IfcSpfStream* stream, unsigned int& pointer) + : pure_(true), stream_(stream), pointer_(pointer) + {} + + std::string get(IfcParse::IfcCharacterDecoder::ConversionMode mode, char substitution_character) { + unsigned int parse_state = 0; + builder_.clear(); + builder_.push_back('\''); + char current_char; + int codepage = 1; + unsigned int hex = 0; + unsigned int hex_count = 0; + + while ((current_char = peek()) != 0) { + if (EXPECTS_CHARACTER(parse_state)) { + builder_.push_back(IfcUtil::convert_codepage(codepage, current_char + 0x80)); + parse_state = 0; + } else if (current_char == '\'' && !parse_state) { + parse_state = APOSTROPHE; + } else if (current_char == '\\' && !parse_state) { + parse_state = FIRST_SOLIDUS; + } else if (current_char == '\\' && EXPECTS_SOLIDUS(parse_state)) { + if (parse_state & ALPHABET_DEFINITION || + parse_state & IGNORED_DIRECTIVE || + parse_state & ENDEXTENDED_0) parse_state = hex = hex_count = 0; + else if (parse_state & ENCOUNTERED_HEX) { + parse_state += THIRD_SOLIDUS; + parse_state -= ENCOUNTERED_HEX; + } else parse_state += SECOND_SOLIDUS; + } else if (current_char == 'X' && EXPECTS_ENDEXTENDED_X(parse_state)) { + parse_state += ENDEXTENDED_X; + } else if (current_char == '0' && EXPECTS_ENDEXTENDED_0(parse_state)) { + parse_state += ENDEXTENDED_0; + } else if (current_char == 'X' && EXPECTS_ARBITRARY(parse_state)) { + parse_state += ARBITRARY; + } else if (current_char == '2' && EXPECTS_ARBITRARY2(parse_state)) { + parse_state += EXTENDED2; + } else if (current_char == '4' && EXPECTS_ARBITRARY2(parse_state)) { + parse_state += EXTENDED2 + EXTENDED4; + } else if (current_char == 'P' && EXPECTS_ALPHABET(parse_state)) { + parse_state += ALPHABET; + } else if ((current_char == 'N' || current_char == 'F') && EXPECTS_N_OR_F(parse_state)) { + parse_state += IGNORED_DIRECTIVE; + } else if (IS_VALID_ALPHABET_DEFINITION(current_char) && EXPECTS_ALPHABET_DEFINITION(parse_state)) { + codepage = current_char - 0x40; + parse_state += ALPHABET_DEFINITION; + } else if (current_char == 'S' && EXPECTS_PAGE(parse_state)) { + parse_state += PAGE; + } else if (IS_HEXADECIMAL(current_char) && EXPECTS_HEX(parse_state)) { + hex <<= 4; + parse_state += HEX((++hex_count)); + hex += HEX_TO_INT(current_char); + if ((hex_count == 2 && !(parse_state & EXTENDED2)) || + (hex_count == 4 && !(parse_state & EXTENDED4)) || + (hex_count == 8)) { + builder_.push_back(hex); + if (hex_count == 2) parse_state = 0; + else { + CLEAR_HEX(parse_state); + parse_state |= ENCOUNTERED_HEX; + } + hex = hex_count = 0; + } + } else if (parse_state && !( + (current_char == '\\' && parse_state == FIRST_SOLIDUS) || + (current_char == '\'' && parse_state == APOSTROPHE) + )) { + if (parse_state == APOSTROPHE && current_char != '\'') break; + throw IfcInvalidTokenException(tell(), current_char); + } else { + parse_state = hex = hex_count = 0; + builder_.push_back(current_char); + } + increment(); + } + builder_.push_back('\''); + + if (mode == IfcParse::IfcCharacterDecoder::UTF8) { + return IfcUtil::convert_utf8(builder_); + } else if (mode == IfcParse::IfcCharacterDecoder::SUBSTITUTE) { + std::string r; + r.reserve(builder_.size()); + std::transform(builder_.begin(), builder_.end(), std::back_inserter(r), [&substitution_character](wchar_t c) { + if (c >= 0x20 && c <= 0x7e) { + return (char)c; + } else { + return substitution_character; + } + }); + return r; + } else if (mode == IfcParse::IfcCharacterDecoder::ESCAPE) { + std::stringstream str; + str << std::hex << std::setw(4) << std::setfill('0'); + std::for_each(builder_.begin(), builder_.end(), [&str](wchar_t c) { + if (c >= 0x20 && c <= 0x7e) { + str.put((char)c); + } else { + str << "\\u" << c; + } + }); + return str.str(); + } else { + throw IfcParse::IfcException("Invalid conversion mode"); + } + } + }; } -void IfcCharacterDecoder::dryRun() { +IfcCharacterDecoder::operator std::string() { + return pure_impure_helper(file).get(mode, substitution_character); +} + +std::string IfcCharacterDecoder::get(unsigned int& ptr) { + return pure_impure_helper(file, ptr).get(mode, substitution_character); +} + +void IfcCharacterDecoder::skip() { unsigned int parse_state = 0; char current_char; unsigned int hex_count = 0; diff --git a/src/ifcparse/IfcCharacterDecoder.h b/src/ifcparse/IfcCharacterDecoder.h index d178bae71d..9aaff758f3 100644 --- a/src/ifcparse/IfcCharacterDecoder.h +++ b/src/ifcparse/IfcCharacterDecoder.h @@ -43,7 +43,6 @@ namespace IfcParse { class IFC_PARSE_API IfcCharacterDecoder { private: IfcParse::IfcSpfStream* file; - std::wstring builder_; int codepage_; public: enum ConversionMode {SUBSTITUTE, UTF8, ESCAPE}; @@ -51,8 +50,15 @@ namespace IfcParse { static char substitution_character; IfcCharacterDecoder(IfcParse::IfcSpfStream* file); ~IfcCharacterDecoder(); - void dryRun(); + // Only advances the underlying token stream read pointer + // to the next token. + void skip(); + // Gets a decoded string representation at the token stream + // read pointer and advances the underlying token stream. operator std::string(); + // Gets a decoded string representation at the offset provided, + // does not mutate the underlying token stream read pointer. + std::string get(unsigned int&); }; } diff --git a/src/ifcparse/IfcFile.h b/src/ifcparse/IfcFile.h index f613c938c9..9021850341 100644 --- a/src/ifcparse/IfcFile.h +++ b/src/ifcparse/IfcFile.h @@ -199,8 +199,9 @@ public: std::string createTimestamp() const; - void load(const IfcEntityInstanceData&); size_t load(unsigned entity_instance_name, Argument**& attributes, size_t num_attributes); + void seek_to(const IfcEntityInstanceData& data); + void try_read_semicolon(); void register_inverse(unsigned, Token); void register_inverse(unsigned, IfcUtil::IfcBaseClass*); diff --git a/src/ifcparse/IfcLogger.cpp b/src/ifcparse/IfcLogger.cpp index 8320c0eb04..9ab29e74a4 100644 --- a/src/ifcparse/IfcLogger.cpp +++ b/src/ifcparse/IfcLogger.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -114,6 +115,9 @@ void Logger::SetOutput(std::wostream* l1, std::wostream* l2) { } void Logger::Message(Logger::Severity type, const std::string& message, const IfcUtil::IfcBaseClass* instance) { + static std::mutex m; + std::lock_guard lk(m); + if (type > max_severity) { max_severity = type; } diff --git a/src/ifcparse/IfcParse.cpp b/src/ifcparse/IfcParse.cpp index c97e4095c1..cb6cadf604 100644 --- a/src/ifcparse/IfcParse.cpp +++ b/src/ifcparse/IfcParse.cpp @@ -17,17 +17,6 @@ * * ********************************************************************************/ -#include -#include -#include -#include -#include -#include -#include - -#include -#include - #include "../ifcparse/IfcCharacterDecoder.h" #include "../ifcparse/IfcParse.h" #include "../ifcparse/IfcException.h" @@ -42,6 +31,18 @@ #include #endif +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + #define PERMISSIVE_FLOAT using namespace IfcParse; @@ -251,9 +252,11 @@ void IfcSpfStream::Inc() { eof = true; return; } - /// @todo: Shouldn't this be a loop of some kind const char current = IfcSpfStream::Peek(); - if ( current == '\n' || current == '\r' ) IfcSpfStream::Inc(); + if (current == '\n' || current == '\r') { + // NB this is recursive. It might as well be a loop. + IfcSpfStream::Inc(); + } } IfcSpfLexer::IfcSpfLexer(IfcParse::IfcSpfStream *s, IfcParse::IfcFile* f) { @@ -331,34 +334,46 @@ Token IfcSpfLexer::Next() { len ++; // If a string is encountered defer processing to the IfcCharacterDecoder - if ( c == '\'' ) decoder->dryRun(); + if ( c == '\'' ) decoder->skip(); } if ( len ) return GeneralTokenPtr(this, pos, stream->Tell()); else return NoneTokenPtr(); } +bool IfcSpfStream::is_eof_at(unsigned int local_ptr) { + return local_ptr >= len; +} + +void IfcSpfStream::increment_at(unsigned int& local_ptr) { + if (++local_ptr == len) { + return; + } + const char current = IfcSpfStream::peek_at(local_ptr); + if (current == '\n' || current == '\r') IfcSpfStream::increment_at(local_ptr); +} + +char IfcSpfStream::peek_at(unsigned int local_ptr) { + return buffer[local_ptr]; +} + // // Reads a std::string from the file at specified offset // Omits whitespace and comments // void IfcSpfLexer::TokenString(unsigned int offset, std::string &buffer) { - const bool was_eof = stream->eof; - unsigned int old_offset = stream->Tell(); - stream->Seek(offset); buffer.clear(); - while ( ! stream->eof ) { - char c = stream->Peek(); + while (!stream->is_eof_at(offset)) { + char c = stream->peek_at(offset); if ( buffer.size() && (c == '(' || c == ')' || c == '=' || c == ',' || c == ';' || c == '/') ) break; - stream->Inc(); + stream->increment_at(offset); if ( c == ' ' || c == '\r' || c == '\n' || c == '\t' ) continue; else if ( c == '\'' ) { - buffer = *decoder; + // todo, make decoder use local offset ptr + buffer = decoder->get(offset); break; } else buffer.push_back(c); } - if ( was_eof ) stream->eof = true; - else stream->Seek(old_offset); } //Note: according to STEP standard, there may be newlines in tokens @@ -887,14 +902,16 @@ IfcEntityInstanceData* IfcParse::read(unsigned int i, IfcFile* f, boost::optiona return e; } -void IfcParse::IfcFile::load(const IfcEntityInstanceData& data) { +void IfcParse::IfcFile::seek_to(const IfcEntityInstanceData& data) { if (tokens->stream->Tell() != data.offset_in_file()) { tokens->stream->Seek(data.offset_in_file()); Token datatype = tokens->Next(); if (!TokenFunc::isKeyword(datatype)) throw IfcException("Unexpected token while parsing entity instance"); } tokens->Next(); - load(data.id(), data.attributes(), data.getArgumentCount()); +} + +void IfcParse::IfcFile::try_read_semicolon() { unsigned int old_offset = tokens->stream->Tell(); Token semilocon = tokens->Next(); if (!TokenFunc::isOperator(semilocon, ';')) { @@ -984,15 +1001,26 @@ unsigned IfcEntityInstanceData::set_id(boost::optional i) { // Returns the entities of Entity type that have this entity in their ArgumentList // IfcEntityList::ptr IfcEntityInstanceData::getInverse(const IfcParse::declaration* type, int attribute_index) const { + static std::mutex m; + std::lock_guard lk(m); + return file->getInverse(id_, type, attribute_index); } void IfcEntityInstanceData::load() const { + static std::recursive_mutex m; + std::lock_guard lk(m); + // type_ is 0 for header entities which have their size predetermined in code + Argument** tmp_data = nullptr; if (type_ != 0) { - attributes_ = new Argument*[getArgumentCount()]; + tmp_data = new Argument*[getArgumentCount()]; } - file->load(*this); + file->seek_to(*this); + file->load(id(), tmp_data, getArgumentCount()); + file->try_read_semicolon(); + // @todo does this need to be atomic somehow? + attributes_ = tmp_data; } IfcEntityInstanceData::IfcEntityInstanceData(const IfcEntityInstanceData& e) { diff --git a/src/ifcparse/IfcParse.h b/src/ifcparse/IfcParse.h index b05474990e..61803d23cd 100644 --- a/src/ifcparse/IfcParse.h +++ b/src/ifcparse/IfcParse.h @@ -49,6 +49,17 @@ #include "../ifcparse/IfcSpfStream.h" + /* gcc doesn't know _Thread_local from C11 yet */ +#ifdef __GNUC__ +# define my_thread_local __thread +#elif __STDC_VERSION__ >= 201112L +# define my_thread_local _Thread_local +#elif defined(_MSC_VER) +# define my_thread_local __declspec(thread) +#else +# error Cannot define thread_local +#endif + namespace IfcParse { class IfcFile; @@ -141,12 +152,13 @@ namespace IfcParse { class IFC_PARSE_API IfcSpfLexer { private: IfcCharacterDecoder* decoder; - //storage for temporary string without allocation - mutable std::string _tempString; unsigned int skipWhitespace(); unsigned int skipComment(); public: - std::string &GetTempString() const { return _tempString; } + std::string &GetTempString() const { + static my_thread_local std::string s; + return s; + } IfcSpfStream* stream; IfcFile* file; IfcSpfLexer(IfcSpfStream* s, IfcFile* f); diff --git a/src/ifcparse/IfcSpfStream.h b/src/ifcparse/IfcSpfStream.h index 820b69d275..2172842a9f 100644 --- a/src/ifcparse/IfcSpfStream.h +++ b/src/ifcparse/IfcSpfStream.h @@ -72,6 +72,10 @@ namespace IfcParse { void Seek(unsigned int offset); /// Returns the cursor position unsigned int Tell(); + + bool is_eof_at(unsigned int); + void increment_at(unsigned int&); + char peek_at(unsigned int); }; } From 3bcced7baa5ac8b120d435873167efb841231ee1 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Tue, 9 Jul 2019 16:00:29 +0200 Subject: [PATCH 09/14] Some fixes to threading work --- src/ifcgeom/IfcGeomIteratorImplementation.h | 76 +++++++++++++++++---- 1 file changed, 64 insertions(+), 12 deletions(-) diff --git a/src/ifcgeom/IfcGeomIteratorImplementation.h b/src/ifcgeom/IfcGeomIteratorImplementation.h index 6c79da4bcc..5583dd0a14 100644 --- a/src/ifcgeom/IfcGeomIteratorImplementation.h +++ b/src/ifcgeom/IfcGeomIteratorImplementation.h @@ -63,6 +63,7 @@ #include #include #include +#include #include #include @@ -106,6 +107,35 @@ namespace { std::vector*> elements; }; + template + IfcGeom::Element* process_based_on_settings( + const IfcGeom::IteratorSettings& settings, + IfcGeom::BRepElement* elem, + IfcGeom::TriangulationElement* previous=nullptr) + { + if (settings.get(IfcGeom::IteratorSettings::USE_BREP_DATA)) { + try { + return new IfcGeom::SerializedElement(*elem); + } catch (...) { + Logger::Message(Logger::LOG_ERROR, "Getting a serialized element from model failed."); + return nullptr; + } + } else if (!settings.get(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION)) { + try { + if (!previous) { + return new IfcGeom::TriangulationElement(*elem); + } else { + return new IfcGeom::TriangulationElement(*elem, previous->geometry_pointer()); + } + } catch (...) { + Logger::Message(Logger::LOG_ERROR, "Getting a triangulation element from model failed."); + return nullptr; + } + } else { + return elem; + } + } + template void create_element( IfcGeom::MAKE_TYPE_NAME(Kernel)* kernel, @@ -114,13 +144,28 @@ namespace { { IfcSchema::IfcRepresentation *representation = rep->representation; IfcSchema::IfcProduct *product = *rep->products->begin(); - rep->breps = { kernel->create_brep_for_representation_and_product(settings, representation, product) }; - // @todo based on settings - rep->elements = { rep->breps[0] ? new IfcGeom::TriangulationElement(*rep->breps[0]) : nullptr }; + auto brep = kernel->create_brep_for_representation_and_product(settings, representation, product); + if (!brep) { + return; + } + + auto elem = process_based_on_settings(settings, brep); + if (!elem) { + return; + } + + rep->breps = { brep }; + rep->elements = { elem }; for (auto it = rep->products->begin() + 1; it != rep->products->end(); ++it) { - rep->breps.push_back(kernel->create_brep_for_processed_representation(settings, representation, *it, rep->breps[0])); - rep->elements.push_back(rep->breps.back() ? new IfcGeom::TriangulationElement(*rep->breps.back()) : nullptr); + auto brep2 = kernel->create_brep_for_processed_representation(settings, representation, *it, brep); + if (brep2) { + auto elem2 = process_based_on_settings(settings, brep, dynamic_cast*>(elem)); + if (elem2) { + rep->breps.push_back(brep2); + rep->elements.push_back(elem2); + } + } } } } @@ -134,7 +179,9 @@ namespace IfcGeom { size_t num_threads_; std::vector> tasks_; std::vector*> all_processed_elements_; + std::vector*> all_processed_native_elements_; typename std::vector*>::const_iterator task_result_iterator_; + typename std::vector*>::const_iterator native_task_result_iterator_; MAKE_TYPE_NAME(IteratorImplementation_)(const MAKE_TYPE_NAME(IteratorImplementation_)&); // N/I MAKE_TYPE_NAME(IteratorImplementation_)& operator=(const MAKE_TYPE_NAME(IteratorImplementation_)&); // N/I @@ -357,9 +404,9 @@ namespace IfcGeom { } void process_concurrently() { - unsigned int conc_threads = std::thread::hardware_concurrency(); - if (conc_threads > (unsigned int)tasks_.size()) { - conc_threads = (unsigned int)tasks_.size(); + size_t conc_threads = num_threads_; + if (conc_threads > tasks_.size()) { + conc_threads = tasks_.size(); } std::vector kernel_pool; @@ -399,9 +446,11 @@ namespace IfcGeom { for (auto& rep : tasks_) { all_processed_elements_.insert(all_processed_elements_.end(), rep.elements.begin(), rep.elements.end()); + all_processed_native_elements_.insert(all_processed_native_elements_.end(), rep.breps.begin(), rep.breps.end()); } task_result_iterator_ = all_processed_elements_.begin(); + native_task_result_iterator_ = all_processed_native_elements_.begin(); } /// Computes model's bounding box (bounds_min and bounds_max). @@ -648,9 +697,8 @@ namespace IfcGeom { /// Use get() to retrieve the created geometry. IfcUtil::IfcBaseClass* next() { if (num_threads_ != 1) { - do { - task_result_iterator_++; - } while (task_result_iterator_ != all_processed_elements_.end() && *task_result_iterator_ == nullptr); + task_result_iterator_++; + native_task_result_iterator_++; if (task_result_iterator_ == all_processed_elements_.end()) { return nullptr; } else { @@ -738,7 +786,11 @@ namespace IfcGeom { BRepElement* get_native() { // TODO: Test settings and throw - return current_shape_model; + if (num_threads_ != 1) { + return *native_task_result_iterator_; + } else { + return current_shape_model; + } } const Element* get_object(int id) { From 40937b3c72a00d9600ed33e3024fa4418db42a50 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Tue, 9 Jul 2019 16:00:58 +0200 Subject: [PATCH 10/14] Write progress for threaded conversion --- src/ifcconvert/IfcConvert.cpp | 46 ++++++++++++++------- src/ifcgeom/IfcGeomIteratorImplementation.h | 28 ++++++++++++- 2 files changed, 59 insertions(+), 15 deletions(-) diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index bca7396422..9141e31f41 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -62,8 +62,9 @@ #include #include #endif -// C++11 header: + #include +#include #if defined(_MSC_VER) && defined(_UNICODE) typedef std::wstring path_t; @@ -520,7 +521,7 @@ int main(int argc, char** argv) { } } - Logger::SetOutput(&cout_, &log_stream); + Logger::SetOutput(quiet ? nullptr : &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); @@ -734,7 +735,18 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } - IfcGeom::Iterator context_iterator(settings, ifc_file, filter_funcs, num_threads); + if (num_threads == 0) { + num_threads = std::thread::hardware_concurrency(); + Logger::Notice("Using " + std::to_string(num_threads) + " threads"); + } + + if (!quiet && num_threads > 1) { + Logger::Status("Creating geometry..."); + } + + Logger::SetOutput(quiet ? nullptr : &cout_, &log_stream); + + IfcGeom::Iterator context_iterator(settings, ifc_file, filter_funcs, num_threads); 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. @@ -788,7 +800,11 @@ int main(int argc, char** argv) { } if (!quiet) { - Logger::Status("Creating geometry..."); + if (num_threads == 1) { + Logger::Status("Creating geometry..."); + } else { + Logger::Status("Writing geometry..."); + } } // The functions IfcGeom::Iterator::get() and IfcGeom::Iterator::next() @@ -819,13 +835,13 @@ int main(int argc, char** argv) { if (quiet) { const int progress = context_iterator.progress(); for (; old_progress < progress; ++old_progress) { - std::cout << "."; + cout_ << "."; if (stderr_progress) - std::cerr << "."; + cerr_ << "."; } - std::cout << std::flush; + cout_ << std::flush; if (stderr_progress) - std::cerr << std::flush; + cerr_ << std::flush; } else { const int progress = context_iterator.progress() / 2; if (old_progress != progress) Logger::ProgressBar(progress); @@ -836,15 +852,17 @@ int main(int argc, char** argv) { if (!no_progress && quiet) { for (; old_progress < 100; ++old_progress) { - std::cout << "."; + cout_ << "."; if (stderr_progress) - std::cerr << "."; + cerr_ << "."; + } + cout_ << std::flush; + if (stderr_progress) { + cerr_ << std::flush; } - std::cout << std::flush; - if (stderr_progress) - std::cerr << std::flush; } else { - Logger::Status("\rDone creating geometry (" + boost::lexical_cast(num_created) + + const std::string task = ((num_threads == 1) ? "creating" : "writing"); + Logger::Status("\rDone " + task + " geometry (" + boost::lexical_cast(num_created) + " objects) "); } diff --git a/src/ifcgeom/IfcGeomIteratorImplementation.h b/src/ifcgeom/IfcGeomIteratorImplementation.h index 5583dd0a14..b36ec75c9a 100644 --- a/src/ifcgeom/IfcGeomIteratorImplementation.h +++ b/src/ifcgeom/IfcGeomIteratorImplementation.h @@ -417,8 +417,16 @@ namespace IfcGeom { std::vector> threadpool; + int old_progress = -1; + int processed = 0; + + Logger::ProgressBar(0); + for (auto& rep : tasks_) { - auto K = kernel_pool[threadpool.size()]; + MAKE_TYPE_NAME(Kernel)* K = nullptr; + if (threadpool.size() < kernel_pool.size()) { + K = kernel_pool[threadpool.size()]; + } while (threadpool.size() == conc_threads) { for (int i = 0; i < (int)threadpool.size(); i++) { @@ -427,6 +435,14 @@ namespace IfcGeom { status = fu.wait_for(std::chrono::seconds(0)); if (status == std::future_status::ready) { fu.get(); + + processed += 1; + const int progress = processed * 50 / tasks_.size(); + if (progress != old_progress) { + Logger::ProgressBar(progress); + old_progress = progress; + } + std::swap(threadpool[i], threadpool.back()); threadpool.pop_back(); std::swap(kernel_pool[i], kernel_pool.back()); @@ -442,6 +458,13 @@ namespace IfcGeom { for (std::future &fu : threadpool) { fu.get(); + + processed += 1; + const int progress = processed * 50 / tasks_.size(); + if (progress != old_progress) { + Logger::ProgressBar(progress); + old_progress = progress; + } } for (auto& rep : tasks_) { @@ -451,6 +474,9 @@ namespace IfcGeom { task_result_iterator_ = all_processed_elements_.begin(); native_task_result_iterator_ = all_processed_native_elements_.begin(); + + Logger::Status("\rDone creating geometry (" + boost::lexical_cast(all_processed_elements_.size()) + + " objects) "); } /// Computes model's bounding box (bounds_min and bounds_max). From 1cd2287d86df514056a0ab24e63174572efd78bb Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Tue, 9 Jul 2019 16:34:35 +0200 Subject: [PATCH 11/14] Cleanup concurrently created breps and triangulations --- src/ifcgeom/IfcGeomIteratorImplementation.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ifcgeom/IfcGeomIteratorImplementation.h b/src/ifcgeom/IfcGeomIteratorImplementation.h index b36ec75c9a..c1d342f3c5 100644 --- a/src/ifcgeom/IfcGeomIteratorImplementation.h +++ b/src/ifcgeom/IfcGeomIteratorImplementation.h @@ -961,6 +961,16 @@ namespace IfcGeom { delete ifc_file; } + if (settings.get(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION)) { + for (auto& p : all_processed_native_elements_) { + delete p; + } + } + + for (auto& p : all_processed_elements_) { + delete p; + } + free_shapes(); } }; From 6e4a692de33d7cf754489e6c47de7586fd9dc0be Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Fri, 19 Jul 2019 15:55:22 +0200 Subject: [PATCH 12/14] Threads in python app --- src/ifcconvert/IfcConvert.cpp | 4 +- src/ifcgeom/IfcGeomIteratorImplementation.cpp | 2 +- src/ifcgeom/IfcGeomIteratorImplementation.h | 31 ++++-- src/ifcgeom_schema_agnostic/IfcGeomIterator.h | 2 +- .../IteratorImplementation.cpp | 2 +- .../IteratorImplementation.h | 8 +- .../ifcopenshell/geom/app.py | 103 ++++++++++++------ .../ifcopenshell/geom/main.py | 12 +- 8 files changed, 105 insertions(+), 59 deletions(-) diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index 9141e31f41..01324129c8 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -220,7 +220,7 @@ int main(int argc, char** argv) { ("calculate-quantities", "Calculate or fix the physical quantity definitions " "based on an interpretation of the geometry when exporting IFC"); - size_t num_threads; + int num_threads; po::options_description geom_options("Geometry options"); geom_options.add_options() @@ -735,7 +735,7 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } - if (num_threads == 0) { + if (num_threads <= 0) { num_threads = std::thread::hardware_concurrency(); Logger::Notice("Using " + std::to_string(num_threads) + " threads"); } diff --git a/src/ifcgeom/IfcGeomIteratorImplementation.cpp b/src/ifcgeom/IfcGeomIteratorImplementation.cpp index 3d3616bf21..1206f0fd5f 100644 --- a/src/ifcgeom/IfcGeomIteratorImplementation.cpp +++ b/src/ifcgeom/IfcGeomIteratorImplementation.cpp @@ -14,7 +14,7 @@ namespace IfcGeom { namespace { template struct MAKE_TYPE_NAME(factory_t) { - IfcGeom::IteratorImplementation* operator()(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters, size_t num_threads) const { + IfcGeom::IteratorImplementation* operator()(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters, int num_threads) const { return new IfcGeom::MAKE_TYPE_NAME(IteratorImplementation_)(settings, file, filters, num_threads); } }; diff --git a/src/ifcgeom/IfcGeomIteratorImplementation.h b/src/ifcgeom/IfcGeomIteratorImplementation.h index c1d342f3c5..aff570af0b 100644 --- a/src/ifcgeom/IfcGeomIteratorImplementation.h +++ b/src/ifcgeom/IfcGeomIteratorImplementation.h @@ -89,6 +89,8 @@ #include "../ifcgeom_schema_agnostic/IfcGeomFilter.h" #include "../ifcgeom_schema_agnostic/IteratorImplementation.h" +#include + // The infamous min & max Win32 #defines can leak here from OCE depending on the build configuration #ifdef min #undef min @@ -176,7 +178,8 @@ namespace IfcGeom { class MAKE_TYPE_NAME(IteratorImplementation_) : public IteratorImplementation { private: - size_t num_threads_; + int num_threads_; + std::atomic progress_; std::vector> tasks_; std::vector*> all_processed_elements_; std::vector*> all_processed_native_elements_; @@ -437,10 +440,10 @@ namespace IfcGeom { fu.get(); processed += 1; - const int progress = processed * 50 / tasks_.size(); - if (progress != old_progress) { - Logger::ProgressBar(progress); - old_progress = progress; + progress_ = processed * 50 / tasks_.size(); + if (progress_ != old_progress) { + Logger::ProgressBar(progress_); + old_progress = progress_; } std::swap(threadpool[i], threadpool.back()); @@ -460,10 +463,10 @@ namespace IfcGeom { fu.get(); processed += 1; - const int progress = processed * 50 / tasks_.size(); - if (progress != old_progress) { - Logger::ProgressBar(progress); - old_progress = progress; + progress_ = processed * 50 / tasks_.size(); + if (progress_ != old_progress) { + Logger::ProgressBar(progress_); + old_progress = progress_; } } @@ -519,7 +522,13 @@ namespace IfcGeom { } } - int progress() const { return 100 * done / total; } + int progress() const { + if (num_threads_ == 1) { + return 100 * done / total; + } else { + return progress_; + } + } const std::string& getUnitName() const { return unit_name; } @@ -946,7 +955,7 @@ namespace IfcGeom { bool owns_ifc_file; public: - MAKE_TYPE_NAME(IteratorImplementation_)(const IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters, size_t num_threads) + MAKE_TYPE_NAME(IteratorImplementation_)(const IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters, int num_threads) : settings(settings) , ifc_file(file) , filters_(filters) diff --git a/src/ifcgeom_schema_agnostic/IfcGeomIterator.h b/src/ifcgeom_schema_agnostic/IfcGeomIterator.h index 9d46b39ac3..50d15c1917 100644 --- a/src/ifcgeom_schema_agnostic/IfcGeomIterator.h +++ b/src/ifcgeom_schema_agnostic/IfcGeomIterator.h @@ -83,7 +83,7 @@ namespace IfcGeom { IteratorImplementation* implementation_; public: - Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, size_t num_threads = 1) + Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, int num_threads = 1) : file_(file) , settings_(settings) { diff --git a/src/ifcgeom_schema_agnostic/IteratorImplementation.cpp b/src/ifcgeom_schema_agnostic/IteratorImplementation.cpp index cf2f89f2df..7cc968f6bc 100644 --- a/src/ifcgeom_schema_agnostic/IteratorImplementation.cpp +++ b/src/ifcgeom_schema_agnostic/IteratorImplementation.cpp @@ -31,7 +31,7 @@ void IteratorFactoryImplementation::bind(const std::string& schema_name, } template -IfcGeom::IteratorImplementation* IteratorFactoryImplementation::construct(const std::string& schema_name, const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters, size_t num_threads) { +IfcGeom::IteratorImplementation* IteratorFactoryImplementation::construct(const std::string& schema_name, const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector& filters, int num_threads) { const std::string schema_name_lower = boost::to_lower_copy(schema_name); typename std::map::type>::const_iterator it; it = this->find(schema_name_lower); diff --git a/src/ifcgeom_schema_agnostic/IteratorImplementation.h b/src/ifcgeom_schema_agnostic/IteratorImplementation.h index 5622c0eec8..0d6ec96c4c 100644 --- a/src/ifcgeom_schema_agnostic/IteratorImplementation.h +++ b/src/ifcgeom_schema_agnostic/IteratorImplementation.h @@ -23,9 +23,9 @@ namespace IfcGeom { class BRepElement; } -typedef boost::function4*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, size_t> iterator_float_float_fn; -typedef boost::function4*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, size_t> iterator_float_double_fn; -typedef boost::function4*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, size_t> iterator_double_double_fn; +typedef boost::function4*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, int> iterator_float_float_fn; +typedef boost::function4*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, int> iterator_float_double_fn; +typedef boost::function4*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, int> iterator_double_double_fn; template struct get_factory_type {}; @@ -50,7 +50,7 @@ class IteratorFactoryImplementation : public std::map::type fn); - IfcGeom::IteratorImplementation* construct(const std::string& schema_name, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, size_t); + IfcGeom::IteratorImplementation* construct(const std::string& schema_name, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector&, int); }; template diff --git a/src/ifcopenshell-python/ifcopenshell/geom/app.py b/src/ifcopenshell-python/ifcopenshell/geom/app.py index 9fcd3191a0..2005416ed9 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/app.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/app.py @@ -7,6 +7,7 @@ import sys import time import operator import functools +import multiprocessing import OCC.AIS @@ -54,6 +55,43 @@ from .. import version as ifcopenshell_version if ifcopenshell_version < "0.6": # not yet ported from .. import get_supertype + +class geometry_creation_signals(QtCore.QObject): + completed = QtCore.pyqtSignal('PyQt_PyObject') + progress = QtCore.pyqtSignal('PyQt_PyObject') + +class geometry_creation_thread(QtCore.QThread): + def __init__(self, signals, settings, f): + QtCore.QThread.__init__(self) + self.signals = signals + self.settings = settings + self.f = f + + def run(self): + t0 = time.time() + + # detect concurrency from hardware, we need to have + # at least two threads because otherwise the interface + # is different + # is different + it = iterator(self.settings, self.f, max(2, multiprocessing.cpu_count())) + if not it.initialize(): + self.signals.completed.emit([]) + return + + def _(): + + old_progress = -1 + while True: + shape = it.get() + + if shape: + yield shape + + if not it.next(): + break + + self.signals.completed.emit((it, self.f, list(_()))) class configuration(object): def __init__(self): @@ -393,62 +431,59 @@ class application(QtWidgets.QApplication): self.product_to_ais = {} self.counter = 0 self.window = widget + self.thread = None def initialize(self): self.InitDriver() self._display.Select = self.HandleSelection - def load_file(self, f, setting=None): - - if setting is None: - setting = settings() - setting.set(setting.USE_PYTHON_OPENCASCADE, True) - + def finished(self, file_shapes): + it, f, shapes = file_shapes v = self._display - + t = {0: time.time()} def update(dt=None): t1 = time.time() - if t1 - t[0] > (dt or -1): + if dt is None or t1 - t[0] > dt: v.FitAll() v.Repaint() t[0] = t1 - - terminate = [False] - self.window.window_closed.connect(lambda *args: operator.setitem(terminate, 0, True)) - - t0 = time.time() - - it = iterator(setting, f) - if not it.initialize(): - return - - old_progress = -1 - while True: - if terminate[0]: - break - shape = it.get() - product = f[shape.data.id] + + for shape in shapes: ais = display_shape(shape, viewer_handle=v) + product = f[shape.data.id] + ais.GetObject().SetSelectionPriority(self.counter) self.ais_to_product[self.counter] = product self.product_to_ais[product] = ais self.counter += 1 + QtWidgets.QApplication.processEvents() + if product.is_a() in {'IfcSpace', 'IfcOpeningElement'}: v.Context.Erase(ais, True) - progress = it.progress() // 2 - if progress > old_progress: - print("\r[" + "#" * progress + " " * (50 - progress) + "]", end="") - old_progress = progress - if not it.next(): - break - update(0.2) - - print("\rOpened file in %.2f seconds%s" % (time.time() - t0, " " * 25)) - + + update(1.) + update() + + self.thread = None + + def load_file(self, f, setting=None): + + if self.thread is not None: + return + + if setting is None: + setting = settings() + setting.set(setting.USE_PYTHON_OPENCASCADE, True) + + self.signals = geometry_creation_signals() + thread = self.thread = geometry_creation_thread(self.signals, setting, f) + self.window.window_closed.connect(lambda *args: thread.terminate()) + self.signals.completed.connect(self.finished) + self.thread.start() def select(self, product): ais = self.product_to_ais.get(product) diff --git a/src/ifcopenshell-python/ifcopenshell/geom/main.py b/src/ifcopenshell-python/ifcopenshell/geom/main.py index a73800b915..55ff561164 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/main.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/main.py @@ -47,9 +47,11 @@ def wrap_shape_creation(settings, shape): if has_occ: from . import occ_utils as utils - def wrap_shape_creation(settings, shape): return utils.create_shape_from_serialization(shape) if getattr(settings, - 'use_python_opencascade', - False) else shape + def wrap_shape_creation(settings, shape): + if getattr(settings, 'use_python_opencascade', False): + return utils.create_shape_from_serialization(shape) + else: + return shape # Subclass the settings module to provide an additional @@ -77,13 +79,13 @@ _iterator = ifcopenshell_wrapper.iterator_double_precision # Make sure people are able to use python's platform agnostic paths class iterator(_iterator): - def __init__(self, settings, file_or_filename): + def __init__(self, settings, file_or_filename, num_threads = 1): self.settings = settings if isinstance(file_or_filename, file): file_or_filename = file_or_filename.wrapped_data else: file_or_filename = os.path.abspath(file_or_filename) - _iterator.__init__(self, settings, file_or_filename) + _iterator.__init__(self, settings, file_or_filename, num_threads) if has_occ: def get(self): From 88a5dc30535be27dc3b2c937a1e64ad9ec07b9ca Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Fri, 2 Aug 2019 13:27:33 +0200 Subject: [PATCH 13/14] fix po::value --- src/ifcconvert/IfcConvert.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index 01324129c8..ad08685262 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -224,7 +224,7 @@ int main(int argc, char** argv) { po::options_description geom_options("Geometry options"); geom_options.add_options() - ("threads,j", po::value(&num_threads)->default_value(1), + ("threads,j", po::value(&num_threads)->default_value(1), "Number of parallel processing threads for geometry interpretation.") ("plan", "Specifies whether to include curves in the output result. Typically " From fd3469029f5679b7cd3cc235aa4781288ee7f0f8 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Fri, 2 Aug 2019 14:55:55 +0200 Subject: [PATCH 14/14] -pthread on unix --- cmake/CMakeLists.txt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 070f345e57..67a8c2c061 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -266,6 +266,10 @@ endif() # resolved. Also add thread and rt libraries. get_filename_component(libTKernelExt ${libTKernel} EXT) if("${libTKernelExt}" STREQUAL ".a") + set(OCCT_STATIC ON) +endif() + +if(OCCT_STATIC) find_package(Threads) # OPENCASCADE_LIBRARIES repeated three times below in order to fix cyclic dependencies - use --start-group ... --end-group instead? set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) @@ -561,7 +565,12 @@ set(SCHEMA_AGNOSTIC_FILES ${SCHEMA_AGNOSTIC_H_FILES} ${SCHEMA_AGNOSTIC_CPP_FILES add_library(IfcGeom ${SCHEMA_AGNOSTIC_FILES}) set_target_properties(IfcGeom PROPERTIES COMPILE_FLAGS -DIFC_GEOM_EXPORTS) -TARGET_LINK_LIBRARIES(IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES}) + +if (UNIX) +find_package(Threads) +endif() + +TARGET_LINK_LIBRARIES(IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) endif(BUILD_IFCGEOM)