/******************************************************************************** * * * 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 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 . * * * ********************************************************************************/ /** @file IfcGeomFilter.h @brief A set of predefined product filters for IfcGeom::Iterator */ #ifndef IFCGEOMFILTER_H #define IFCGEOMFILTER_H #include "IfcGeom.h" #include #include #include #include #include #include namespace IfcGeom { /// The filter function (free or member function) or function object (use boost::ref() to reference to it) /// should return true if the geometry for the product is wanted to be included in the output. /// http://www.boost.org/doc/libs/1_62_0/doc/html/function/tutorial.html typedef boost::function filter_t; struct filter { filter() : include(false), traverse(false) {} filter(bool incl, bool trav) : include(incl), traverse(trav) {} /// Should the product be included (true) or excluded (false). bool include; /// If traversal requested, traverse to the parents to see if they satisfy the criteria. E.g. we might be looking for /// children of a storey named "Level 20", or children of entities that have no representation, e.g. IfcCurtainWall. bool traverse; /// Optional description for the filtering criteria of this filter. std::string description; bool match(IfcSchema::IfcProduct* prod, const filter_t& pred) const { bool is_match = pred(prod); if (!is_match && traverse) { is_match = traverse_match(prod, pred); } return is_match == include; } static bool traverse_match(IfcSchema::IfcProduct* prod, const filter_t& pred) { IfcSchema::IfcProduct* parent, *current = prod; while ((parent = dynamic_cast(IfcGeom::Kernel::get_decomposing_entity(current))) != 0) { if (pred(parent)) { return true; } current = parent; } return false; } }; struct wildcard_filter : public filter { wildcard_filter() : filter(false, false) {} wildcard_filter(bool include, bool traverse, const std::set& patterns) : filter(include, traverse) { populate(patterns); } std::set values; void populate(const std::set& patterns) { values.clear(); BOOST_FOREACH(const std::string &pattern, patterns) { values.insert(wildcard_string_to_regex(pattern)); } } bool match(const std::string &str) const { return match_values(values, str); } static bool match_values(const std::set& values, const std::string &str) { BOOST_FOREACH(const boost::regex& r, values) { if (boost::regex_match(str, r)) { return true; } } return false; } static boost::regex wildcard_string_to_regex(std::string str) { // Escape all non-"*?" regex special chars static const std::string special_chars = "\\^.$|()[]+/"; BOOST_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); } }; /// @note supports only string arguments for now struct string_arg_filter : public wildcard_filter { // Using this for now in order to overcome the fact that different classes have the argument at different indices. typedef std::map arg_map_t; arg_map_t args; /// @todo Take only attribute name when IfcBaseClass and IfcLateBoundEntity are merged. string_arg_filter(arg_map_t args) : args(args) { assert_arguments(); } string_arg_filter(IfcSchema::Type::Enum type, unsigned short index) { args[type] = index; assert_arguments(); } string_arg_filter( IfcSchema::Type::Enum type1, unsigned short index1, IfcSchema::Type::Enum type2, unsigned short index2) { args[type1] = index1; args[type2] = index2; assert_arguments(); } /// @todo this won't be needed when we have the generic argument name access void assert_arguments() { #ifndef NDEBUG for (arg_map_t::const_iterator it = args.begin(); it != args.end(); ++it) { IfcEntityInstanceData dummy(it->first); IfcUtil::IfcBaseClass* base = IfcSchema::SchemaEntity(&dummy); assert(it->second < base->getArgumentCount() && "Argument index out of bounds"); assert(base->getArgumentType(it->second) == IfcUtil::Argument_STRING && "Argument type not string"); delete base; } #endif } std::string value(IfcSchema::IfcProduct* prod) const { for (arg_map_t::const_iterator it = args.begin(); it != args.end(); ++it) { if (prod->is(it->first) && it->second < prod->entity->getArgumentCount() && prod->getArgumentType(it->second) == IfcUtil::Argument_STRING) { Argument *arg = prod->entity->getArgument(it->second); if (!arg->isNull()) { return *arg; } } } return ""; } bool match(IfcSchema::IfcProduct* prod) const { return wildcard_filter::match(value(prod)); } bool operator()(IfcSchema::IfcProduct* prod) const { // @note bind1st() and mem_fun() deprecated in C++11, use bind() and mem_fn() when migrating to C++11. return filter::match(prod, std::bind1st(std::mem_fun(&string_arg_filter::match), this)); } void update_description() { std::stringstream ss; ss << (traverse ? "traverse " : "") << (include ? "include" : "exclude"); std::vector patterns; BOOST_FOREACH(const boost::regex& r, values) { patterns.push_back("\"" + r.str() + "\""); } for (arg_map_t::const_iterator it = args.begin(); it != args.end(); ++it) { IfcEntityInstanceData dummy(it->first); IfcUtil::IfcBaseClass* base = IfcSchema::SchemaEntity(&dummy); try { ss << " " << IfcSchema::Type::ToString(it->first) << "." << base->getArgumentName(it->second); } catch (const std::exception& e) { Logger::Error(e); } delete base; } ss << " values " << boost::algorithm::join(patterns, " "); description = ss.str(); } }; struct layer_filter : public wildcard_filter { typedef std::map layer_map_t; layer_filter() {} layer_filter(bool include, bool traverse, const std::set& patterns) : wildcard_filter(include, traverse, patterns) { } bool match(IfcSchema::IfcProduct* prod) const { layer_map_t layers = IfcGeom::Kernel::get_layers(prod); return std::find_if(layers.begin(), layers.end(), wildcards_match(values)) != layers.end(); } bool operator()(IfcSchema::IfcProduct* prod) const { return filter::match(prod, std::bind1st(std::mem_fun(&layer_filter::match), this)); } struct wildcards_match { wildcards_match(const std::set& patterns) : patterns(patterns) {} bool operator()(const layer_map_t::value_type& layer_map_value) const { return wildcard_filter::match_values(patterns, layer_map_value.first); } std::set patterns; }; void update_description() { std::stringstream ss; ss << (traverse ? "traverse " : "") << (include ? "include" : "exclude") << " layers"; std::vector str_values; BOOST_FOREACH(const boost::regex& r, values) { str_values.push_back(" \"" + r.str() + "\""); } ss << boost::algorithm::join(str_values, " "); description = ss.str(); } }; struct entity_filter : public filter { entity_filter() {} entity_filter(bool include, bool traverse/*, const std::set& types*/) : filter(include, traverse) { //populate(types); } std::set values; void populate(const std::set& types) { values.clear(); BOOST_FOREACH(const std::string& type, types) { IfcSchema::Type::Enum ty; try { ty = IfcSchema::Type::FromString(boost::to_upper_copy(type)); } catch (const IfcParse::IfcException&) { throw IfcParse::IfcException("'" + type + "' does not name a valid IFC entity"); } values.insert(ty); /// @todo Add child classes so that containment in set can be in O(log n) } } bool match(IfcSchema::IfcProduct* prod) const { // The set is iterated over to able to filter on subtypes. BOOST_FOREACH(IfcSchema::Type::Enum type, values) { if (prod->is(type)) { return true; } } return false; } bool operator()(IfcSchema::IfcProduct* prod) const { return filter::match(prod, std::bind1st(std::mem_fun(&entity_filter::match), this)); } void update_description() { std::stringstream ss; ss << (traverse ? "traverse " : "") << (include ? "include" : "exclude") << " entities"; BOOST_FOREACH(IfcSchema::Type::Enum type, values) { ss << " " << IfcSchema::Type::ToString(type); } description = ss.str(); } }; } #endif