2017-02-07 17:34:23 +02:00
|
|
|
/********************************************************************************
|
|
|
|
|
* *
|
|
|
|
|
* 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 <http://www.gnu.org/licenses/>. *
|
|
|
|
|
* *
|
|
|
|
|
********************************************************************************/
|
|
|
|
|
|
|
|
|
|
/** @file IfcGeomFilter.h
|
|
|
|
|
@brief A set of predefined product filters for IfcGeom::Iterator */
|
|
|
|
|
|
|
|
|
|
#ifndef IFCGEOMFILTER_H
|
|
|
|
|
#define IFCGEOMFILTER_H
|
|
|
|
|
|
|
|
|
|
#include "IfcGeom.h"
|
2017-02-16 19:49:46 +02:00
|
|
|
|
|
|
|
|
#include <boost/function.hpp>
|
2017-02-07 17:34:23 +02:00
|
|
|
#include <boost/regex.hpp>
|
2017-02-16 19:49:46 +02:00
|
|
|
#include <boost/algorithm/string/replace.hpp>
|
|
|
|
|
#include <boost/algorithm/string/case_conv.hpp>
|
2017-02-07 17:34:23 +02:00
|
|
|
|
|
|
|
|
namespace IfcGeom
|
|
|
|
|
{
|
|
|
|
|
typedef boost::function<bool(IfcSchema::IfcProduct*)> 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;
|
|
|
|
|
|
|
|
|
|
//static bool traverse_match(IfcSchema::IfcProduct* prod, filter_t pred)
|
|
|
|
|
//{
|
|
|
|
|
// bool is_match = false;
|
|
|
|
|
// IfcSchema::IfcProduct* parent, *current = prod;
|
|
|
|
|
// while ((parent = static_cast<IfcSchema::IfcProduct*>(IfcGeom::Kernel::get_decomposing_entity(current))) != 0) {
|
|
|
|
|
// if (pred(parent)) {
|
|
|
|
|
// is_match = true;
|
|
|
|
|
// break;
|
|
|
|
|
// }
|
|
|
|
|
// current = parent;
|
|
|
|
|
// }
|
|
|
|
|
//}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct wildcard_filter : public filter
|
|
|
|
|
{
|
|
|
|
|
wildcard_filter() : filter(false, false) {}
|
|
|
|
|
wildcard_filter(bool include, bool traverse, const std::set<std::string>& patterns)
|
|
|
|
|
: filter(include, traverse)
|
|
|
|
|
{
|
|
|
|
|
populate(patterns);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::set<boost::regex> values;
|
|
|
|
|
|
|
|
|
|
void populate(const std::set<std::string>& patterns)
|
|
|
|
|
{
|
|
|
|
|
values.clear();
|
|
|
|
|
foreach(const std::string &pattern, patterns) {
|
|
|
|
|
values.insert(wildcard_string_to_regex(pattern));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool match(const std::string &str) const
|
|
|
|
|
{
|
|
|
|
|
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
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2017-02-17 09:27:37 +02:00
|
|
|
struct string_arg_filter : public wildcard_filter
|
2017-02-07 17:34:23 +02:00
|
|
|
{
|
2017-02-17 09:27:37 +02:00
|
|
|
// Using this for now in order to overcome the fact that different classes have the argument at different indices.
|
|
|
|
|
typedef std::map<IfcSchema::Type::Enum, unsigned short> 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) {}
|
|
|
|
|
string_arg_filter(IfcSchema::Type::Enum type, unsigned short index) { args[type] = index; }
|
2017-02-07 17:34:23 +02:00
|
|
|
|
2017-02-17 09:27:37 +02:00
|
|
|
std::string value(IfcSchema::IfcProduct* prod) const
|
2017-02-07 17:34:23 +02:00
|
|
|
{
|
2017-02-17 09:27:37 +02:00
|
|
|
for (arg_map_t::const_iterator it = args.begin(); it != args.end(); ++it) {
|
|
|
|
|
if (prod->is(it->first)) {
|
|
|
|
|
Argument *arg = (it->second <= prod->entity->getArgumentCount() ? prod->entity->getArgument(it->second) : 0);
|
|
|
|
|
if (arg && !arg->isNull()) {
|
|
|
|
|
return *arg;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return "";
|
2017-02-07 17:34:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool operator()(IfcSchema::IfcProduct* prod) const
|
|
|
|
|
{
|
|
|
|
|
bool is_match = match(value(prod));
|
|
|
|
|
if (is_match != include && traverse) {
|
|
|
|
|
IfcSchema::IfcProduct* parent, *current = prod;
|
|
|
|
|
while ((parent = static_cast<IfcSchema::IfcProduct*>(IfcGeom::Kernel::get_decomposing_entity(current))) != 0) {
|
|
|
|
|
if (match(value(parent))) {
|
|
|
|
|
is_match = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
current = parent;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return is_match == include;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct layer_filter : public wildcard_filter
|
|
|
|
|
{
|
|
|
|
|
layer_filter() {}
|
|
|
|
|
layer_filter(bool include, bool traverse, const std::set<std::string>& patterns)
|
|
|
|
|
: wildcard_filter(include, traverse, patterns)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool operator()(IfcSchema::IfcProduct* prod) const
|
|
|
|
|
{
|
|
|
|
|
bool is_match = false;
|
|
|
|
|
std::map<std::string, IfcSchema::IfcPresentationLayerAssignment*> layers = IfcGeom::Kernel::get_layers(prod);
|
|
|
|
|
std::map<std::string, IfcSchema::IfcPresentationLayerAssignment*>::const_iterator lit;
|
|
|
|
|
for (lit = layers.begin(); lit != layers.end(); ++lit) {
|
|
|
|
|
if (match(lit->first)) {
|
|
|
|
|
is_match = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_match != include && traverse) {
|
|
|
|
|
for (lit = layers.begin(); lit != layers.end(); ++lit) {
|
|
|
|
|
IfcSchema::IfcProduct* parent, *current = prod;
|
|
|
|
|
while ((parent = static_cast<IfcSchema::IfcProduct*>(IfcGeom::Kernel::get_decomposing_entity(current))) != 0) {
|
|
|
|
|
if (match(lit->first)) {
|
|
|
|
|
is_match = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
current = parent;
|
|
|
|
|
}
|
|
|
|
|
if (is_match) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return is_match == include;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct entity_filter : public filter
|
|
|
|
|
{
|
|
|
|
|
entity_filter() {}
|
|
|
|
|
entity_filter(bool include, bool traverse/*, const std::set<std::string>& types*/)
|
|
|
|
|
: filter(include, traverse)
|
|
|
|
|
{
|
|
|
|
|
//populate(types);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::set<IfcSchema::Type::Enum> values;
|
|
|
|
|
|
|
|
|
|
void populate(const std::set<std::string>& 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&) {
|
|
|
|
|
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.
|
|
|
|
|
foreach(IfcSchema::Type::Enum type, values) {
|
|
|
|
|
if (prod->is(type)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool operator()(IfcSchema::IfcProduct* prod) const
|
|
|
|
|
{
|
|
|
|
|
bool is_match = match(prod);
|
|
|
|
|
if (is_match != include && traverse) {
|
|
|
|
|
IfcSchema::IfcProduct* parent, *current = prod;
|
|
|
|
|
while ((parent = static_cast<IfcSchema::IfcProduct*>(IfcGeom::Kernel::get_decomposing_entity(current))) != 0) {
|
|
|
|
|
if (match(parent)) {
|
|
|
|
|
is_match = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
current = parent;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return is_match == include;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|