mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 06:58:56 +00:00
IfcConvert/IfcGeom --traverse option (#159)
geometry filtering option for including/excluding decomposition and containment of filtered entities. Usage example: IfcConvert Duplex_A_20110907.ifc Level1.dae --include --traverse --names "Level 1"
This commit is contained in:
committed by
Thomas Krijnen
parent
e8ae491dc3
commit
714a365942
@@ -175,9 +175,9 @@ int main(int argc, char** argv) {
|
|||||||
"Specifies whether to enable the slicing of products according "
|
"Specifies whether to enable the slicing of products according "
|
||||||
"to their associated IfcMaterialLayerSet.")
|
"to their associated IfcMaterialLayerSet.")
|
||||||
("include",
|
("include",
|
||||||
"Specifies that the entities listed after --entities or --names are to be included")
|
"Specifies that the entities and/or names listed after --entities and/or --names are to be included")
|
||||||
("exclude",
|
("exclude",
|
||||||
"Specifies that the entities listed after --entities or --names are to be excluded")
|
"Specifies that the entities and/or names listed after --entities and/or --names are to be excluded")
|
||||||
("entities", boost::program_options::value< std::vector<std::string> >(&entity_vector)->multitoken(),
|
("entities", boost::program_options::value< std::vector<std::string> >(&entity_vector)->multitoken(),
|
||||||
"A list of entities that should be included in or excluded from the "
|
"A list of entities that should be included in or excluded from the "
|
||||||
"geometrical output, depending on whether --exclude or --include is specified. "
|
"geometrical output, depending on whether --exclude or --include is specified. "
|
||||||
@@ -196,7 +196,11 @@ int main(int argc, char** argv) {
|
|||||||
"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",
|
("generate-uvs",
|
||||||
"Generates UVs (texture coordinates) by using simple box projection. Requires normals. "
|
"Generates UVs (texture coordinates) by using simple box projection. Requires normals. "
|
||||||
"Not guaranteed to work properly if used with --weld-vertices.");
|
"Not guaranteed to work properly if used with --weld-vertices.")
|
||||||
|
("traverse",
|
||||||
|
"Applies --include or --exclude also to the decomposition and/or containment (IsDecomposedBy, "
|
||||||
|
"HasOpenings, FillsVoid, ContainedInStructure) of the filtered entity, e.g. "
|
||||||
|
"--include --traverse --names \"Level 1\" includes entity with name \"Level 1\" and all of its children.");
|
||||||
|
|
||||||
std::string bounds;
|
std::string bounds;
|
||||||
boost::program_options::options_description serializer_options("Serialization options");
|
boost::program_options::options_description serializer_options("Serialization options");
|
||||||
@@ -266,7 +270,8 @@ int main(int argc, char** argv) {
|
|||||||
const bool merge_boolean_operands = vmap.count("merge-boolean-operands") != 0;
|
const bool merge_boolean_operands = vmap.count("merge-boolean-operands") != 0;
|
||||||
#endif
|
#endif
|
||||||
const bool disable_opening_subtractions = vmap.count("disable-opening-subtractions") != 0;
|
const bool disable_opening_subtractions = vmap.count("disable-opening-subtractions") != 0;
|
||||||
bool include_entities = vmap.count("include") != 0;
|
bool include_entities = vmap.count("include") != 0 && !entity_vector.empty();
|
||||||
|
const bool include_names = vmap.count("include") != 0 && !names.empty();
|
||||||
const bool include_plan = vmap.count("plan") != 0;
|
const bool include_plan = vmap.count("plan") != 0;
|
||||||
const bool include_model = vmap.count("model") != 0 || (!include_plan);
|
const bool include_model = vmap.count("model") != 0 || (!include_plan);
|
||||||
const bool enable_layerset_slicing = vmap.count("enable-layerset-slicing") != 0;
|
const bool enable_layerset_slicing = vmap.count("enable-layerset-slicing") != 0;
|
||||||
@@ -276,6 +281,7 @@ int main(int argc, char** argv) {
|
|||||||
const bool no_normals = vmap.count("no-normals") != 0 ;
|
const bool no_normals = vmap.count("no-normals") != 0 ;
|
||||||
bool center_model = vmap.count("center-model") != 0 ;
|
bool center_model = vmap.count("center-model") != 0 ;
|
||||||
const bool generate_uvs = vmap.count("generate-uvs") != 0 ;
|
const bool generate_uvs = vmap.count("generate-uvs") != 0 ;
|
||||||
|
const bool traverse = vmap.count("traverse") != 0;
|
||||||
const bool deflection_tolerance_specified = vmap.count("deflection-tolerance") != 0 ;
|
const bool deflection_tolerance_specified = vmap.count("deflection-tolerance") != 0 ;
|
||||||
|
|
||||||
int bounding_width = -1, bounding_height = -1;
|
int bounding_width = -1, bounding_height = -1;
|
||||||
@@ -377,6 +383,7 @@ int main(int argc, char** argv) {
|
|||||||
settings.set(IfcGeom::IteratorSettings::NO_NORMALS, no_normals);
|
settings.set(IfcGeom::IteratorSettings::NO_NORMALS, no_normals);
|
||||||
settings.set(IfcGeom::IteratorSettings::CENTER_MODEL, center_model);
|
settings.set(IfcGeom::IteratorSettings::CENTER_MODEL, center_model);
|
||||||
settings.set(IfcGeom::IteratorSettings::GENERATE_UVS, generate_uvs);
|
settings.set(IfcGeom::IteratorSettings::GENERATE_UVS, generate_uvs);
|
||||||
|
settings.set(IfcGeom::IteratorSettings::TRAVERSE, traverse);
|
||||||
if (deflection_tolerance_specified) {
|
if (deflection_tolerance_specified) {
|
||||||
settings.set_deflection_tolerance(deflection_tolerance);
|
settings.set_deflection_tolerance(deflection_tolerance);
|
||||||
}
|
}
|
||||||
@@ -432,16 +439,20 @@ int main(int argc, char** argv) {
|
|||||||
try {
|
try {
|
||||||
if (include_entities) {
|
if (include_entities) {
|
||||||
context_iterator.includeEntities(entities);
|
context_iterator.includeEntities(entities);
|
||||||
context_iterator.include_entity_names(names);
|
|
||||||
} else {
|
} else {
|
||||||
context_iterator.excludeEntities(entities);
|
context_iterator.excludeEntities(entities);
|
||||||
context_iterator.exclude_entity_names(names);
|
|
||||||
}
|
}
|
||||||
} catch (const IfcParse::IfcException& e) {
|
} catch (const IfcParse::IfcException& e) {
|
||||||
std::cout << "[Error] " << e.what() << std::endl;
|
std::cout << "[Error] " << e.what() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (include_names) {
|
||||||
|
context_iterator.include_entity_names(names);
|
||||||
|
} else {
|
||||||
|
context_iterator.exclude_entity_names(names);
|
||||||
|
}
|
||||||
|
|
||||||
if (!serializer->ready()) {
|
if (!serializer->ready()) {
|
||||||
Logger::Message(Logger::LOG_ERROR, "Unable to open output '" + output_filename + "' file for writing");
|
Logger::Message(Logger::LOG_ERROR, "Unable to open output '" + output_filename + "' file for writing");
|
||||||
write_log();
|
write_log();
|
||||||
@@ -452,6 +463,8 @@ int main(int argc, char** argv) {
|
|||||||
time(&start);
|
time(&start);
|
||||||
|
|
||||||
if (!context_iterator.initialize()) {
|
if (!context_iterator.initialize()) {
|
||||||
|
/// @todo It would be nice to know and print separate error prints for a case where we failed to parse
|
||||||
|
/// the file and for a case where we found no entities that satisfy our filtering criteria.
|
||||||
Logger::Message(Logger::LOG_ERROR, "Unable to parse input file '" + input_filename + "' or no geometrical entities found");
|
Logger::Message(Logger::LOG_ERROR, "Unable to parse input file '" + input_filename + "' or no geometrical entities found");
|
||||||
write_log();
|
write_log();
|
||||||
return 1;
|
return 1;
|
||||||
@@ -516,7 +529,7 @@ int main(int argc, char** argv) {
|
|||||||
// Do not remove the temp file as user can salvage the conversion result from it.
|
// Do not remove the temp file as user can salvage the conversion result from it.
|
||||||
bool successful = rename_file(output_temp_filename, output_filename);
|
bool successful = rename_file(output_temp_filename, output_filename);
|
||||||
if (!successful) {
|
if (!successful) {
|
||||||
Logger::Message(Logger::LOG_ERROR, "Unable to write output file '" + output_filename + "");
|
Logger::Message(Logger::LOG_ERROR, "Unable to write output file '" + output_filename + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
write_log();
|
write_log();
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ namespace IfcGeom {
|
|||||||
std::set<boost::regex> names_to_include_or_exclude; // regex containing a name or a wildcard expression
|
std::set<boost::regex> names_to_include_or_exclude; // regex containing a name or a wildcard expression
|
||||||
std::set<IfcSchema::Type::Enum> entities_to_include_or_exclude;
|
std::set<IfcSchema::Type::Enum> entities_to_include_or_exclude;
|
||||||
bool include_entities_in_processing;
|
bool include_entities_in_processing;
|
||||||
|
bool include_names_in_processing_;
|
||||||
|
|
||||||
void populate_set(const std::set<std::string>& include_or_ignore) {
|
void populate_set(const std::set<std::string>& include_or_ignore) {
|
||||||
entities_to_include_or_exclude.clear();
|
entities_to_include_or_exclude.clear();
|
||||||
@@ -335,7 +336,7 @@ namespace IfcGeom {
|
|||||||
names_to_include_or_exclude.clear();
|
names_to_include_or_exclude.clear();
|
||||||
foreach(const std::string &name, names)
|
foreach(const std::string &name, names)
|
||||||
names_to_include_or_exclude.insert(wildcard_string_to_regex(name));
|
names_to_include_or_exclude.insert(wildcard_string_to_regex(name));
|
||||||
include_entities_in_processing = true;
|
include_names_in_processing_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @note Arbitrary names or wildcard expressions are handled case-sensitively.
|
/// @note Arbitrary names or wildcard expressions are handled case-sensitively.
|
||||||
@@ -344,7 +345,7 @@ namespace IfcGeom {
|
|||||||
names_to_include_or_exclude.clear();
|
names_to_include_or_exclude.clear();
|
||||||
foreach(const std::string &name, names)
|
foreach(const std::string &name, names)
|
||||||
names_to_include_or_exclude.insert(wildcard_string_to_regex(name));
|
names_to_include_or_exclude.insert(wildcard_string_to_regex(name));
|
||||||
include_entities_in_processing = false;
|
include_names_in_processing_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static boost::regex wildcard_string_to_regex(std::string str)
|
static boost::regex wildcard_string_to_regex(std::string str)
|
||||||
@@ -530,27 +531,64 @@ namespace IfcGeom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter the products based on the set of entities being included or excluded for
|
// Filter the products based on the set of entities and/or names being included or excluded for processing.
|
||||||
// processing. The set is iterated over to able to filter on subtypes.
|
// If traversal requested, traverse to the parents to see if they satisfy the criteria. E.g. we might be looking for
|
||||||
for ( IfcSchema::IfcProduct::list::it jt = unfiltered_products->begin(); jt != unfiltered_products->end(); ++jt ) {
|
// children of a storey named "Level 20", or children of entities that have no representation, e.g. IfcCurtainWall.
|
||||||
bool found = false;
|
const bool traverse = settings.get(IteratorSettings::TRAVERSE);
|
||||||
for (std::set<IfcSchema::Type::Enum>::const_iterator kt = entities_to_include_or_exclude.begin(); kt != entities_to_include_or_exclude.end(); ++kt) {
|
for (IfcSchema::IfcProduct::list::it jt = unfiltered_products->begin(); jt != unfiltered_products->end(); ++jt) {
|
||||||
if ((*jt)->is(*kt)) {
|
IfcSchema::IfcProduct* prod = *jt;
|
||||||
found = true;
|
bool type_found = false;
|
||||||
break;
|
// The set is iterated over to able to filter on subtypes.
|
||||||
}
|
foreach(IfcSchema::Type::Enum type, entities_to_include_or_exclude) {
|
||||||
}
|
if (prod->is(type)) {
|
||||||
|
type_found = true;
|
||||||
foreach(const boost::regex& r, names_to_include_or_exclude) {
|
|
||||||
if (boost::regex_match((*jt)->Name(), r)) {
|
|
||||||
found = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (found == include_entities_in_processing) {
|
if (!type_found && traverse) {
|
||||||
ifcproducts->push(*jt);
|
foreach(IfcSchema::Type::Enum type, entities_to_include_or_exclude) {
|
||||||
}
|
IfcSchema::IfcProduct* parent, * current = prod;
|
||||||
|
while ((parent = static_cast<IfcSchema::IfcProduct*>(kernel.get_decomposing_entity(current))) != 0) {
|
||||||
|
if (parent->is(type)) {
|
||||||
|
type_found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
current = parent;
|
||||||
|
}
|
||||||
|
if (type_found) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool name_found = false;
|
||||||
|
foreach(const boost::regex& r, names_to_include_or_exclude) {
|
||||||
|
if (prod->hasName() && boost::regex_match(prod->Name(), r)) {
|
||||||
|
name_found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!name_found && traverse) {
|
||||||
|
foreach(const boost::regex& r, names_to_include_or_exclude) {
|
||||||
|
IfcSchema::IfcProduct* parent, *current = prod;
|
||||||
|
while ((parent = static_cast<IfcSchema::IfcProduct*>(kernel.get_decomposing_entity(current))) != 0) {
|
||||||
|
if (parent->hasName() && boost::regex_match(parent->Name(), r)) {
|
||||||
|
name_found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
current = parent;
|
||||||
|
}
|
||||||
|
if (name_found) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_found == include_entities_in_processing && name_found == include_names_in_processing_) {
|
||||||
|
ifcproducts->push(prod);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ifcproduct_iterator = ifcproducts->begin();
|
ifcproduct_iterator = ifcproducts->begin();
|
||||||
@@ -704,7 +742,8 @@ namespace IfcGeom {
|
|||||||
// Upon initialisation, the (empty) set of entity names,
|
// Upon initialisation, the (empty) set of entity names,
|
||||||
// should be excluded, or no products would be processed.
|
// should be excluded, or no products would be processed.
|
||||||
include_entities_in_processing = false;
|
include_entities_in_processing = false;
|
||||||
|
include_names_in_processing_ = false;
|
||||||
|
|
||||||
unit_name = "METER";
|
unit_name = "METER";
|
||||||
unit_magnitude = 1.f;
|
unit_magnitude = 1.f;
|
||||||
|
|
||||||
|
|||||||
@@ -83,15 +83,19 @@ namespace IfcGeom
|
|||||||
/// Applicable for OBJ and DAE output.
|
/// Applicable for OBJ and DAE output.
|
||||||
USE_MATERIAL_NAMES = 1 << 14,
|
USE_MATERIAL_NAMES = 1 << 14,
|
||||||
/// Centers the models upon serialization by the applying the center point of
|
/// Centers the models upon serialization by the applying the center point of
|
||||||
/// the scene bounds as an offset. Applicable only for DAE output currently.
|
/// the scene bounds as an offset. Applicable for OBJ and DAE output currently.
|
||||||
CENTER_MODEL = 1 << 15,
|
CENTER_MODEL = 1 << 15,
|
||||||
/// Generates UVs by using simple box projection. Requires normals.
|
/// Generates UVs by using simple box projection. Requires normals.
|
||||||
/// Applicable only for DAE output currently.
|
/// Applicable for OBJ and DAE output.
|
||||||
GENERATE_UVS = 1 << 16,
|
GENERATE_UVS = 1 << 16,
|
||||||
/// Specifies whether to slice representations according to associated IfcLayerSets.
|
/// Specifies whether to slice representations according to associated IfcLayerSets.
|
||||||
APPLY_LAYERSETS = 1 << 17,
|
APPLY_LAYERSETS = 1 << 17,
|
||||||
|
/// Marks that include/exclude filtering should be applied also to the decomposition
|
||||||
|
/// and/or containment (IsDecomposedBy, HasOpenings, FillsVoid, ContainedInStructure)
|
||||||
|
/// of the filtered entity.
|
||||||
|
TRAVERSE = 1 << 18,
|
||||||
/// Number of different setting flags.
|
/// Number of different setting flags.
|
||||||
NUM_SETTINGS = 17
|
NUM_SETTINGS = 18
|
||||||
};
|
};
|
||||||
/// Used to store logical OR combination of setting flags.
|
/// Used to store logical OR combination of setting flags.
|
||||||
typedef unsigned SettingField;
|
typedef unsigned SettingField;
|
||||||
|
|||||||
Reference in New Issue
Block a user