From 3abee03b9b43e269f9f72d54e793b16e1a9da217 Mon Sep 17 00:00:00 2001 From: Stinkfist0 Date: Thu, 2 Feb 2017 14:58:58 +0200 Subject: [PATCH] Filtering by entity GUID and layer name w.i.p. --- src/ifcconvert/IfcConvert.cpp | 54 ++++++++-- src/ifcgeom/IfcGeomIterator.h | 188 +++++++++++++++++++++++----------- 2 files changed, 175 insertions(+), 67 deletions(-) diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index 068e4a5896..37599498fd 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -117,11 +117,10 @@ static std::stringstream log_stream; void write_log(); /// @todo Make this a feature of IfcGeom::Iterator instead. -/// Also add GUID filtering. struct geom_filter { bool include; - enum filter_type { ENTITIES, NAMES }; + enum filter_type { ENTITY_TYPE, ENTITY_NAME, ENTITY_GUID, LAYER_NAME }; filter_type type; std::set values; }; @@ -201,7 +200,7 @@ int main(int argc, char** argv) "geometrical output. The name patterns are handled case-sensitively. Cannot be placed right before input file argument. " "Only single option supported for now.") ("exclude", po::value(&exclude_filter)->multitoken(), - "Specifies that the 'entities' or 'names' provided are to be excluded. Defaults to IfcOpeningElement and IfcSpace " + "Specifies that the 'entities', 'names', or 'guids' provided are to be excluded. Defaults to IfcOpeningElement and IfcSpace " "to be excluded. See --include for syntax and more details.") ("no-normals", "Disables computation of normals. Saves time and file size and is useful " @@ -245,7 +244,7 @@ int main(int argc, char** argv) ("center-model", "Centers the elements upon serialization by applying the center point of " "all placements as an offset. Applicable for OBJ and DAE output.") - ("model-offset", PO::value(&offset_str), + ("model-offset", po::value(&offset_str), "Applies an arbitrary offset of form 'x;y;z' to all placements. Applicable for OBJ and DAE output.") ("precision", po::value(&precision)->default_value(SerializerSettings::DEFAULT_PRECISION), "Sets the precision to be used to format floating-point values, 15 by default. " @@ -272,13 +271,14 @@ int main(int argc, char** argv) std::cerr << "[Error] Invalid usage of '" << e.get_option_name() << "': " << e.what() << "\n\n"; print_usage(); return EXIT_FAILURE; + return EXIT_FAILURE; } catch (const std::exception& e) { std::cerr << "[Error] " << e.what() << "\n\n"; print_usage(); return EXIT_FAILURE; } catch (...) { // other errors such as invalid command line syntax print_usage(); - return 1; + return EXIT_FAILURE; po::notify(vmap); @@ -295,6 +295,18 @@ int main(int argc, char** argv) print_usage(); return 1; } + if (include_filter.type == geom_filter::ENTITY_GUID) { + guids = include_filter.values; + } else if (exclude_filter.type == geom_filter::ENTITY_GUID) { + guids = exclude_filter.values; + } + + if (include_filter.type == geom_filter::LAYER_NAME) { + layers = include_filter.values; + } else if (exclude_filter.type == geom_filter::LAYER_NAME) { + layers = exclude_filter.values; + } + const bool verbose = vmap.count("verbose") != 0; const bool weld_vertices = vmap.count("weld-vertices") != 0; const bool use_world_coords = vmap.count("use-world-coords") != 0; @@ -304,6 +316,10 @@ int main(int argc, char** argv) const bool merge_boolean_operands = vmap.count("merge-boolean-operands") != 0; #endif const bool disable_opening_subtractions = vmap.count("disable-opening-subtractions") != 0; + bool include_entities = include_filter.type == geom_filter::ENTITY_TYPE && !include_filter.values.empty(); + const bool include_names = include_filter.type == geom_filter::ENTITY_NAME && !include_filter.values.empty(); + const bool include_guids = include_filter.type == geom_filter::ENTITY_GUID && !include_filter.values.empty(); + const bool include_layers = include_filter.type == geom_filter::LAYER_NAME && !include_filter.values.empty(); 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; @@ -484,6 +500,18 @@ int main(int argc, char** argv) context_iterator.exclude_entity_names(names); } + if (include_guids) { + context_iterator.include_entity_guids(guids); + } else { + context_iterator.exclude_entity_guids(guids); + } + + if (include_layers) { + context_iterator.include_layer_names(layers); + } else { + context_iterator.exclude_layer_names(layers); + } + if (!serializer->ready()) { write_log(); return 1; @@ -614,9 +642,13 @@ void validate(boost::any& v, const std::vector& values, inclusion_f } std::string type = *values.begin(); if (type == "entities") { - filter.type = geom_filter::ENTITIES; + filter.type = geom_filter::ENTITY_TYPE; } else if (type == "names") { - filter.type = geom_filter::NAMES; + filter.type = geom_filter::ENTITY_NAME; + } else if (type == "guids") { + filter.type = geom_filter::ENTITY_GUID; + //} else if (type == "layers") { + // filter.type = geom_filter::LAYER_NAME; } else { throw po::validation_error(po::validation_error::invalid_option_value); } @@ -634,9 +666,13 @@ void validate(boost::any& v, const std::vector& values, exclusion_f } std::string type = *values.begin(); if (type == "entities") { - filter.type = geom_filter::ENTITIES; + filter.type = geom_filter::ENTITY_TYPE; } else if (type == "names") { - filter.type = geom_filter::NAMES; + filter.type = geom_filter::ENTITY_NAME; + } else if (type == "guids") { + filter.type = geom_filter::ENTITY_GUID; + //} else if (type == "layers") { + // filter.type = geom_filter::LAYER_NAME; } else { throw po::validation_error(po::validation_error::invalid_option_value); } diff --git a/src/ifcgeom/IfcGeomIterator.h b/src/ifcgeom/IfcGeomIterator.h index c1cb00497a..77963095ba 100644 --- a/src/ifcgeom/IfcGeomIterator.h +++ b/src/ifcgeom/IfcGeomIterator.h @@ -135,27 +135,61 @@ namespace IfcGeom { } } - std::set names_to_include_or_exclude; // regex containing a name or a wildcard expression - std::set entities_to_include_or_exclude; - bool include_entities_in_processing; - bool include_names_in_processing_; + struct wildcard_filter + { + bool include; + std::set values; - 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) { - const std::string uppercase_type = boost::to_upper_copy(*it); - 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) - } - } + void populate(const std::set& patterns) + { + values.clear(); + foreach(const std::string &pattern, patterns) { + values.insert(wildcard_string_to_regex(pattern)); + } + } + + static boost::regex wildcard_string_to_regex(std::string str) + { + // Escape all non-"*?" regex special chars + std::string special_chars = "\\^.$|()[]+/"; + foreach(char c, special_chars) { + std::string char_str(1, c); + boost::replace_all(str, char_str, "\\" + char_str); + } + // Convert "*?" to their regex equivalents + boost::replace_all(str, "?", "."); + boost::replace_all(str, "*", ".*"); + return boost::regex(str); + } + }; + + wildcard_filter name_filter_; + wildcard_filter guid_filter_; + wildcard_filter layer_filter_; + + struct entity_filter + { + bool include; + std::set values; + + void populate(const std::set& types) + { + values.clear(); + foreach(const std::string& type, types) { + IfcSchema::Type::Enum ty; + try { + ty = IfcSchema::Type::FromString(boost::to_upper_copy(type)); + } catch (const IfcParse::IfcException&) { + std::stringstream ss; + ss << "'" << type << "' does not name a valid IFC entity"; + throw IfcParse::IfcException(ss.str()); + } + values.insert(ty); + // TODO: Add child classes so that containment in set can be in O(log n) + } + } + }; + entity_filter entity_filter_; public: bool initialize() { @@ -319,47 +353,59 @@ namespace IfcGeom { IfcParse::IfcFile* getFile() const { return ifc_file; } /// @note Entity names are handled case-insensitively. - void includeEntities(const std::set& entities) { - populate_set(entities); - include_entities_in_processing = true; - } + void includeEntities(const std::set& entities) + { + entity_filter_.populate(entities); + entity_filter_.include = true; + } - /// @note Entity names are handled case-insensitively. - void excludeEntities(const std::set& entities) { - populate_set(entities); - include_entities_in_processing = false; - } + /// @copydoc includeEntities() + void excludeEntities(const std::set& entities) + { + entity_filter_.populate(entities); + entity_filter_.include = false; + } /// @note Arbitrary names or wildcard expressions are handled case-sensitively. void include_entity_names(const std::set& names) { - names_to_include_or_exclude.clear(); - foreach(const std::string &name, names) - names_to_include_or_exclude.insert(wildcard_string_to_regex(name)); - include_names_in_processing_ = true; + name_filter_.populate(names); + name_filter_.include = true; + } + + /// @copydoc include_entity_names() + void exclude_entity_names(const std::set& names) + { + name_filter_.populate(names); + name_filter_.include = false; + } + + /// @note GUIDs (wildcard expressions allowed) are handled case-sensitively. + void include_entity_guids(const std::set& guids) + { + guid_filter_.populate(guids); + guid_filter_.include = true; + } + + /// @copydoc include_entity_guids() + void exclude_entity_guids(const std::set& guids) + { + guid_filter_.populate(guids); + guid_filter_.include = false; } /// @note Arbitrary names or wildcard expressions are handled case-sensitively. - void exclude_entity_names(const std::set& names) + void include_layer_names(const std::set& names) { - names_to_include_or_exclude.clear(); - foreach(const std::string &name, names) - names_to_include_or_exclude.insert(wildcard_string_to_regex(name)); - include_names_in_processing_ = false; + layer_filter_.populate(names); + layer_filter_.include = true; } - static boost::regex wildcard_string_to_regex(std::string str) + /// @copydoc include_layer_names() + void exclude_layer_names(const std::set& names) { - // Escape all non-"*?" regex special chars - std::string special_chars = "\\^.$|()[]+/"; - foreach(char c, special_chars) { - std::string char_str(1, c); - boost::replace_all(str, char_str, "\\" + char_str); - } - // Convert "*?" to their regex equivalents - boost::replace_all(str, "?", "."); - boost::replace_all(str, "*", ".*"); - return boost::regex(str); + layer_filter_.populate(names); + layer_filter_.include = false; } const gp_XYZ& bounds_min() const { return bounds_min_; } @@ -539,15 +585,15 @@ namespace IfcGeom { IfcSchema::IfcProduct* prod = *jt; bool type_found = false; // The set is iterated over to able to filter on subtypes. - foreach(IfcSchema::Type::Enum type, entities_to_include_or_exclude) { + foreach(IfcSchema::Type::Enum type, entity_filter_.values) { if (prod->is(type)) { type_found = true; break; } } - if (type_found != include_entities_in_processing && traverse) { - foreach(IfcSchema::Type::Enum type, entities_to_include_or_exclude) { + if (type_found != entity_filter_.include && traverse) { + foreach(IfcSchema::Type::Enum type, entity_filter_.values) { IfcSchema::IfcProduct* parent, * current = prod; while ((parent = static_cast(kernel.get_decomposing_entity(current))) != 0) { if (parent->is(type)) { @@ -563,15 +609,15 @@ namespace IfcGeom { } bool name_found = false; - foreach(const boost::regex& r, names_to_include_or_exclude) { + foreach(const boost::regex& r, name_filter_.values) { if (prod->hasName() && boost::regex_match(prod->Name(), r)) { name_found = true; break; } } - if (name_found != include_names_in_processing_ && traverse) { - foreach(const boost::regex& r, names_to_include_or_exclude) { + if (name_found != name_filter_.include && traverse) { + foreach(const boost::regex& r, name_filter_.values) { IfcSchema::IfcProduct* parent, *current = prod; while ((parent = static_cast(kernel.get_decomposing_entity(current))) != 0) { if (parent->hasName() && boost::regex_match(parent->Name(), r)) { @@ -586,7 +632,31 @@ namespace IfcGeom { } } - if (type_found == include_entities_in_processing && name_found == include_names_in_processing_) { + bool guid_found = false; + foreach(const boost::regex& r, guid_filter_.values) { + if (boost::regex_match(prod->GlobalId(), r)) { + guid_found = true; + break; + } + } + + if (guid_found != guid_filter_.include && traverse) { + foreach(const boost::regex& r, guid_filter_.values) { + IfcSchema::IfcProduct* parent, *current = prod; + while ((parent = static_cast(kernel.get_decomposing_entity(current))) != 0) { + if (boost::regex_match(parent->GlobalId(), r)) { + guid_found = true; + break; + } + current = parent; + } + if (guid_found) { + break; + } + } + } + + if (type_found == entity_filter_.include && name_found == name_filter_.include && guid_found == guid_filter_.include) { ifcproducts->push(prod); } } @@ -748,8 +818,10 @@ namespace IfcGeom { // Upon initialisation, the (empty) set of entity names, // should be excluded, or no products would be processed. - include_entities_in_processing = false; - include_names_in_processing_ = false; + entity_filter_.include = false; + name_filter_.include = false; + guid_filter_.include = false; + layer_filter_.include = false; unit_name = "METER"; unit_magnitude = 1.f;