From de416eca491621b289269f7319855cf435cf291a Mon Sep 17 00:00:00 2001 From: Sander Boer Date: Tue, 18 Dec 2018 12:49:11 +0100 Subject: [PATCH 1/5] 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 2/5] 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 3/5] 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 4/5] 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 5/5] .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