From 79817341e06306d130510212cf461ac269ed31e5 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Thu, 5 Feb 2015 11:55:25 +0000 Subject: [PATCH] Ignore entity types earlier in IfcConvert so that representations are not processed in vain --- src/ifcconvert/IfcConvert.cpp | 109 ++++++++++++++++++++-------------- src/ifcgeom/IfcGeomIterator.h | 74 +++++++++++++++++++---- 2 files changed, 127 insertions(+), 56 deletions(-) diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index c4d520d094..c2ff5b1872 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -84,35 +84,48 @@ int main(int argc, char** argv) { ("input-file", boost::program_options::value(), "input IFC file") ("output-file", boost::program_options::value(), "output geometry file"); - std::vector ignore_types_vector; + std::vector entity_vector; boost::program_options::options_description geom_options; geom_options.add_options() - ("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 artifacts 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.") - ("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.") - ("force-ccw-face-orientation", "Recompute topological face normals using Newell's Method to " - "guarantee that face vertices are defined in a Counter Clock " - "Wise order, even if the faces are not part of a closed shell.") - ("disable-opening-subtractions", "Specifies whether to disable the boolean subtraction of " - "IfcOpeningElement Representations from their RelatingElements.") - ("ignore-types", boost::program_options::value< std::vector >(&ignore_types_vector)->multitoken(), - "A list of IFC datatype keywords that should not be included in the geometrical output. " - "Defaults to IfcOpeningElement and IfcSpace"); + ("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.") + ("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.") + ("force-ccw-face-orientation", + "Recompute topological face normals using Newell's Method to " + "guarantee that face vertices are defined in a Counter Clock " + "Wise order, even if the faces are not part of a closed shell.") + ("disable-opening-subtractions", + "Specifies whether to disable the boolean subtraction of " + "IfcOpeningElement Representations from their RelatingElements.") + ("include", + "Specifies that the entities listed after --entities are to be included") + ("exclude", + "Specifies that the entities listed after --entities are to be excluded") + ("entities", boost::program_options::value< std::vector >(&entity_vector)->multitoken(), + "A list of entities that should be included in or excluded from the " + "geometrical output, depending on whether --ignore or --include is " + "specified. Defaults to IfcOpeningElement and IfcSpace to be excluded."); boost::program_options::options_description cmdline_options; cmdline_options.add(generic_options).add(fileio_options).add(geom_options); @@ -139,6 +152,10 @@ int main(int argc, char** argv) { } else if (vmap.count("help") || !vmap.count("input-file")) { printUsage(generic_options, geom_options); return vmap.count("help") ? 0 : 1; + } else if (vmap.count("include") && vmap.count("exclude")) { + std::cerr << "[Error] --include and --ignore can not be specified together" << std::endl; + printUsage(generic_options, geom_options); + return 1; } const bool verbose = vmap.count("verbose") != 0; @@ -149,20 +166,22 @@ int main(int argc, char** argv) { const bool merge_boolean_operands = vmap.count("merge-boolean-operands") != 0; const bool force_ccw_face_orientation = vmap.count("force-ccw-face-orientation") != 0; const bool disable_opening_subtractions = vmap.count("disable-opening-subtractions") != 0; + const bool include_entities = vmap.count("include") != 0; // Gets the set ifc types to be ignored from the command line. - std::set ignore_types; - for (std::vector::const_iterator it = ignore_types_vector.begin(); it != ignore_types_vector.end(); ++it) { + std::set entities; + for (std::vector::const_iterator it = entity_vector.begin(); it != entity_vector.end(); ++it) { std::string lowercase_type = *it; for (std::string::iterator c = lowercase_type.begin(); c != lowercase_type.end(); ++c) { *c = tolower(*c); } - ignore_types.insert(lowercase_type); + entities.insert(lowercase_type); } - // If none are specified these are the defaults to skip from output - if (ignore_types_vector.empty()) { - ignore_types.insert("ifcopeningelement"); - ignore_types.insert("ifcspace"); + + // If no entities are specified these are the defaults to skip from output + if (entity_vector.empty()) { + entities.insert("ifcopeningelement"); + entities.insert("ifcspace"); } const std::string input_filename = vmap["input-file"].as(); @@ -224,6 +243,17 @@ int main(int argc, char** argv) { IfcGeom::Iterator context_iterator(settings, input_filename); + try { + if (include_entities) { + context_iterator.includeEntities(entities); + } else { + context_iterator.excludeEntities(entities); + } + } catch (const IfcParse::IfcException& e) { + std::cout << "[Error] " << e.what() << std::endl; + return 1; + } + if (!serializer->ready()) { Logger::Message(Logger::LOG_ERROR, "Unable to open output file for writing"); write_log(); @@ -231,7 +261,6 @@ int main(int argc, char** argv) { } if (!serializer->isTesselated()) { - if (weld_vertices) { Logger::Message(Logger::LOG_NOTICE, "Weld vertices setting ignored when writing STEP or IGES files"); } @@ -239,7 +268,7 @@ int main(int argc, char** argv) { time_t start,end; time(&start); - // Parse the file supplied in argv[1]. Returns true on succes. + if (!context_iterator.findContext()) { Logger::Message(Logger::LOG_ERROR, "Unable to parse .ifc file or no geometrical entities found"); write_log(); @@ -264,13 +293,7 @@ int main(int argc, char** argv) { // IfcGeomObjects::Next() is used to poll whether more geometrical entities are available do { const IfcGeom::Element* geom_object = context_iterator.get(); - - std::string lowercase_type = geom_object->type(); - for (std::string::iterator c = lowercase_type.begin(); c != lowercase_type.end(); ++c) { - *c = tolower(*c); - } - if (ignore_types.find(lowercase_type) != ignore_types.end()) continue; - + if (serializer->isTesselated()) { serializer->write(static_cast*>(geom_object)); } else { diff --git a/src/ifcgeom/IfcGeomIterator.h b/src/ifcgeom/IfcGeomIterator.h index 1dcc36ab52..a2fbbf64b5 100644 --- a/src/ifcgeom/IfcGeomIterator.h +++ b/src/ifcgeom/IfcGeomIterator.h @@ -99,7 +99,7 @@ namespace IfcGeom { SerializedElement

* current_serialization; // A container and iterator for IfcBuildingElements for the current IfcRepresentation referenced by *representation_iterator - IfcSchema::IfcProduct::list::ptr entities; + IfcSchema::IfcProduct::list::ptr ifcproducts; IfcSchema::IfcProduct::list::it ifcproduct_iterator; int done; @@ -119,6 +119,29 @@ namespace IfcGeom { } } + std::set entities_to_include_or_exclude; + bool include_entities_in_processing; + + void populate_set(const std::set& include_or_ignore) { + entities_to_include_or_exclude.clear(); + for (std::set::const_iterator it = include_or_ignore.begin(); it != include_or_ignore.end(); ++it) { + std::string uppercase_type = *it; + for (std::string::iterator c = uppercase_type.begin(); c != uppercase_type.end(); ++c) { + *c = toupper(*c); + } + IfcSchema::Type::Enum ty; + try { + ty = IfcSchema::Type::FromString(uppercase_type); + } catch (const IfcParse::IfcException&) { + std::stringstream ss; + ss << "'" << *it << "' does not name a valid IFC entity"; + throw IfcParse::IfcException(ss.str()); + } + entities_to_include_or_exclude.insert(ty); + // TODO: Add child classes so that containment in set can be in O(log n) + } + } + public: bool findContext() { try { @@ -202,7 +225,7 @@ namespace IfcGeom { if (representations->size() == 0) return false; representation_iterator = representations->begin(); - entities.reset(); + ifcproducts.reset(); if (!create()) { return false; @@ -234,10 +257,20 @@ namespace IfcGeom { return ifc_file; } + void includeEntities(const std::set& entities) { + populate_set(entities); + include_entities_in_processing = true; + } + + void excludeEntities(const std::set& entities) { + populate_set(entities); + include_entities_in_processing = false; + } + private: // Move to the next IfcRepresentation void _nextShape() { - entities.reset(); + ifcproducts.reset(); ++ representation_iterator; ++ done; } @@ -254,14 +287,16 @@ namespace IfcGeom { representation = *representation_iterator; // Has the list of IfcProducts for this representation been initialized? - if ( ! entities ) { + if (!ifcproducts) { IfcSchema::IfcProductRepresentation::list::ptr prodreps = representation->OfProductRepresentation(); - entities = IfcSchema::IfcProduct::list::ptr(new IfcSchema::IfcProduct::list); + ifcproducts = IfcSchema::IfcProduct::list::ptr(new IfcSchema::IfcProduct::list); + IfcSchema::IfcProduct::list::ptr unfiltered_products(new IfcSchema::IfcProduct::list); + for ( IfcSchema::IfcProductRepresentation::list::it it = prodreps->begin(); it != prodreps->end(); ++it ) { if ( (*it)->is(IfcSchema::Type::IfcProductDefinitionShape) ) { IfcSchema::IfcProductDefinitionShape* pds = (IfcSchema::IfcProductDefinitionShape*)*it; - entities->push(pds->ShapeOfProduct()); + unfiltered_products->push(pds->ShapeOfProduct()); } else { // http://buildingsmart-tech.org/ifc/IFC2x3/TC1/html/ifcrepresentationresource/lexical/ifcproductrepresentation.htm // IFC2x Edition 3 NOTE Users should not instantiate the entity IfcProductRepresentation from IFC2x Edition 3 onwards. @@ -269,21 +304,34 @@ namespace IfcGeom { // IfcProductRepresentation also lacks the INVERSE relation to IfcProduct // Let's find the IfcProducts that reference the IfcProductRepresentation anyway - IfcEntityList::ptr products = (*it)->entity->getInverse(IfcSchema::Type::IfcProduct, -1); - for ( IfcEntityList::it it = products->begin(); it != products->end(); ++ it ) { - entities->push((IfcSchema::IfcProduct*)*it); + unfiltered_products->push((*it)->entity->getInverse(IfcSchema::Type::IfcProduct, -1)->as()); + } + + // Filter the products based on the set of entities being included or excluded for + // processing. The set is iterated over te able to filter on subtypes. + for ( IfcSchema::IfcProduct::list::it it = unfiltered_products->begin(); it != unfiltered_products->end(); ++it ) { + bool found = false; + for (std::set::const_iterator jt = entities_to_include_or_exclude.begin(); jt != entities_to_include_or_exclude.end(); ++jt) { + if ((*it)->is(*jt)) { + found = true; + break; + } + } + if (found == include_entities_in_processing) { + ifcproducts->push(*it); } } + } // Does this representation have any IfcProducts? - if ( ! entities->size() ) { + if (!ifcproducts->size()) { _nextShape(); continue; } - ifcproduct_iterator = entities->begin(); + ifcproduct_iterator = ifcproducts->begin(); } // Have we reached the end of our list of IfcProducts? - if ( ifcproduct_iterator == entities->end() ) { + if ( ifcproduct_iterator == ifcproducts->end() ) { _nextShape(); continue; } @@ -314,7 +362,7 @@ namespace IfcGeom { // Increment the iterator over the list of products using the current // shape representation - if (entities) { + if (ifcproducts) { ++ifcproduct_iterator; }