IfcConvert/IfcGeom: Possibility to include and exclude simultaneously by type and name, w.i.p.

This commit is contained in:
Stinkfist0
2016-12-16 13:51:07 +02:00
committed by Thomas Krijnen
parent 6b2c8ad23e
commit f1709dc71a
2 changed files with 111 additions and 74 deletions
+107 -70
View File
@@ -53,6 +53,8 @@
const std::string DEFAULT_EXTENSION = "obj"; const std::string DEFAULT_EXTENSION = "obj";
const std::string TEMP_FILE_EXTENSION = ".tmp"; const std::string TEMP_FILE_EXTENSION = ".tmp";
namespace po = boost::program_options;
void print_version() void print_version()
{ {
/// @todo Why cerr used for info prints? Change to cout. /// @todo Why cerr used for info prints? Change to cout.
@@ -80,7 +82,7 @@ void print_usage(bool suggest_help = true)
std::cerr << std::endl; std::cerr << std::endl;
} }
void print_options(const boost::program_options::options_description& options) void print_options(const po::options_description& options)
{ {
std::cerr << "\n" << options; std::cerr << "\n" << options;
std::cerr << std::endl; std::cerr << std::endl;
@@ -114,22 +116,39 @@ bool rename_file(const std::string& old_filename, const std::string& new_filenam
static std::stringstream log_stream; static std::stringstream log_stream;
void write_log(); void write_log();
int main(int argc, char** argv) { /// @todo Make this a feature of IfcGeom::Iterator instead.
boost::program_options::options_description generic_options("Command line options"); /// Also add GUID filtering.
struct geom_filter
{
bool include;
enum filter_type { ENTITIES, NAMES };
filter_type type;
std::set<std::string> values;
};
// Specialized classes for knowing which type of filter we are validating within validate().
// Could not figure out easily how else to know it if using single type for both.
struct inclusion_filter : public geom_filter { inclusion_filter() { include = true; } };
struct exclusion_filter : public geom_filter { exclusion_filter() { include = false; } };
int main(int argc, char** argv)
{
po::options_description generic_options("Command line options");
generic_options.add_options() generic_options.add_options()
("help,h", "display usage information") ("help,h", "display usage information")
("version", "display version information") ("version", "display version information")
("verbose,v", "more verbose output") ("verbose,v", "more verbose output")
("yes,y", "answer 'yes' automatically to possible confirmation queries (e.g overwriting an existing output file)"); ("yes,y", "answer 'yes' automatically to possible confirmation queries (e.g. overwriting an existing output file)");
boost::program_options::options_description fileio_options; po::options_description fileio_options;
fileio_options.add_options() fileio_options.add_options()
("input-file", boost::program_options::value<std::string>(), "input IFC file") ("input-file", po::value<std::string>(), "input IFC file")
("output-file", boost::program_options::value<std::string>(), "output geometry file"); ("output-file", po::value<std::string>(), "output geometry file");
std::vector<std::string> entity_vector, names;
double deflection_tolerance; double deflection_tolerance;
boost::program_options::options_description geom_options("Geometry options"); inclusion_filter include_filter;
exclusion_filter exclude_filter;
po::options_description geom_options("Geometry options");
geom_options.add_options() geom_options.add_options()
("plan", ("plan",
"Specifies whether to include curves in the output result. Typically " "Specifies whether to include curves in the output result. Typically "
@@ -174,47 +193,44 @@ int main(int argc, char** argv) {
("enable-layerset-slicing", ("enable-layerset-slicing",
"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", po::value<inclusion_filter>(&include_filter)->multitoken(),
"Specifies that the entities and/or names listed after --entities and/or --names are to be included") "Specifies that the entities or names provided are to be included. "
("exclude", "If --include=entities specified, the following list of types should be included in the geometrical output. "
"Specifies that the entities and/or names listed after --entities and/or --names are to be excluded") "SVG output defaults to IfcSpace to be included. The entity names are handled case-insensitively. "
("entities", boost::program_options::value< std::vector<std::string> >(&entity_vector)->multitoken(), "If --include=names specified, the following list of names or wildcard patterns should be included in the "
"A list of entities that should be included in or excluded from the " "geometrical output. The name patterns are handled case-sensitively. Cannot be placed right before input file argument. "
"geometrical output, depending on whether --exclude or --include is specified. " "Only single option supported for now.")
"Defaults to IfcOpeningElement and IfcSpace to be excluded. SVG output defaults " ("exclude", po::value<exclusion_filter>(&exclude_filter)->multitoken(),
"to IfcSpace to be included." "Specifies that the 'entities' or 'names' provided are to be excluded. Defaults to IfcOpeningElement and IfcSpace "
"The names are handled case-insensitively. Cannot be placed right before input file argument.") "to be excluded. See --include for syntax and more details.")
("names", boost::program_options::value< std::vector<std::string> >(&names)->multitoken(),
"A list of names or wildcard patterns that should be included in or excluded from the "
"geometrical output, depending on whether --exclude or --include is specified. "
"The names are handled case-sensitively. Cannot be placed right before input file argument.")
("no-normals", ("no-normals",
"Disables computation of normals. Saves time and file size and is useful " "Disables computation of normals. Saves time and file size and is useful "
"in instances where you're going to recompute normals for the exported " "in instances where you're going to recompute normals for the exported "
"model in other modelling application in any case.") "model in other modelling application in any case.")
("deflection-tolerance", boost::program_options::value<double>(&deflection_tolerance)->default_value(1e-3), ("deflection-tolerance", po::value<double>(&deflection_tolerance)->default_value(1e-3),
"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", ("traverse",
"Applies --include or --exclude also to the decomposition and/or containment (IsDecomposedBy, " "Applies --include or --exclude also to the decomposition and/or containment (IsDecomposedBy, "
"HasOpenings, FillsVoid, ContainedInStructure) of the filtered entity, e.g. " "HasOpenings, FillsVoid, ContainedInStructure) of the filtered entity, e.g. --include=names \"Level 1\" "
"--include --traverse --names \"Level 1\" includes entity with name \"Level 1\" and all of its children."); "--traverse includes entity with name \"Level 1\" and all of its children.");
std::string bounds, offset_str; std::string bounds, offset_str;
#ifdef HAVE_ICU #ifdef HAVE_ICU
std::string unicode_mode; std::string unicode_mode;
#endif #endif
short precision; short precision;
boost::program_options::options_description serializer_options("Serialization options");
po::options_description serializer_options("Serialization options");
serializer_options.add_options() serializer_options.add_options()
#ifdef HAVE_ICU #ifdef HAVE_ICU
("unicode", boost::program_options::value<std::string>(&unicode_mode), ("unicode", po::value<std::string>(&unicode_mode),
"Specifies the Unicode handling behavior when parsing the IFC file. " "Specifies the Unicode handling behavior when parsing the IFC file. "
"Accepted values 'utf8' (the default) and 'escape'.") "Accepted values 'utf8' (the default) and 'escape'.")
#endif #endif
("bounds", boost::program_options::value<std::string>(&bounds), ("bounds", po::value<std::string>(&bounds),
"Specifies the bounding rectangle, for example 512x512, to which the " "Specifies the bounding rectangle, for example 512x512, to which the "
"output will be scaled. Only used when converting to SVG.") "output will be scaled. Only used when converting to SVG.")
("use-element-names", ("use-element-names",
@@ -229,39 +245,42 @@ int main(int argc, char** argv) {
("center-model", ("center-model",
"Centers the elements upon serialization by applying the center point of " "Centers the elements upon serialization by applying the center point of "
"all placements as an offset. Applicable for OBJ and DAE output.") "all placements as an offset. Applicable for OBJ and DAE output.")
("model-offset", boost::program_options::value<std::string>(&offset_str), ("model-offset", PO::value<std::string>(&offset_str),
"Applies an arbitrary offset of form 'x;y;z' to all placements. Applicable for OBJ and DAE output.") "Applies an arbitrary offset of form 'x;y;z' to all placements. Applicable for OBJ and DAE output.")
("precision", boost::program_options::value<short>(&precision)->default_value(SerializerSettings::DEFAULT_PRECISION), ("precision", po::value<short>(&precision)->default_value(SerializerSettings::DEFAULT_PRECISION),
"Sets the precision to be used to format floating-point values, 15 by default. " "Sets the precision to be used to format floating-point values, 15 by default. "
"Use a negative value to use the system's default precision (should be 6 typically). " "Use a negative value to use the system's default precision (should be 6 typically). "
"Applicable for OBJ and DAE output. For DAE output, value >= 15 means that up to 16 decimals are used, " "Applicable for OBJ and DAE output. For DAE output, value >= 15 means that up to 16 decimals are used, "
" and any other value means that 6 or 7 decimals are used."); " and any other value means that 6 or 7 decimals are used.");
boost::program_options::options_description cmdline_options; po::options_description cmdline_options;
cmdline_options.add(generic_options).add(fileio_options).add(geom_options).add(serializer_options); cmdline_options.add(generic_options).add(fileio_options).add(geom_options).add(serializer_options);
boost::program_options::positional_options_description positional_options; po::positional_options_description positional_options;
positional_options.add("input-file", 1); positional_options.add("input-file", 1);
positional_options.add("output-file", 1); positional_options.add("output-file", 1);
boost::program_options::variables_map vmap; po::variables_map vmap;
try { try {
boost::program_options::store(boost::program_options::command_line_parser(argc, argv). po::store(po::command_line_parser(argc, argv).
options(cmdline_options).positional(positional_options).run(), vmap); options(cmdline_options).positional(positional_options).run(), vmap);
} catch (const boost::program_options::unknown_option& e) { } catch (const po::unknown_option& e) {
std::cerr << "[Error] Unknown option '" << e.get_option_name() << "'" << std::endl << std::endl; std::cerr << "[Error] Unknown option '" << e.get_option_name() << "'" << "\n\n";
print_usage(); print_usage();
return 1; return EXIT_FAILURE;
} catch (const po::error_with_option_name& e) {
std::cerr << "[Error] Invalid usage of '" << e.get_option_name() << "': " << e.what() << "\n\n";
print_usage();
return EXIT_FAILURE;
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << "[Error] " << e.what() << "\n\n"; std::cerr << "[Error] " << e.what() << "\n\n";
print_usage(); print_usage();
return 1; return EXIT_FAILURE;
} catch (...) { } catch (...) { // other errors such as invalid command line syntax
// Catch other errors such as invalid command line syntax
print_usage(); print_usage();
return 1; return 1;
}
boost::program_options::notify(vmap); po::notify(vmap);
print_version(); print_version();
@@ -275,12 +294,7 @@ int main(int argc, char** argv) {
std::cerr << "[Error] Input file not specified" << std::endl; std::cerr << "[Error] Input file not specified" << std::endl;
print_usage(); print_usage();
return 1; return 1;
} else if (vmap.count("include") && vmap.count("exclude")) { }
std::cerr << "[Error] --include and --exclude can not be specified together" << std::endl;
print_options(geom_options);
return 1;
}
const bool verbose = vmap.count("verbose") != 0; const bool verbose = vmap.count("verbose") != 0;
const bool weld_vertices = vmap.count("weld-vertices") != 0; const bool weld_vertices = vmap.count("weld-vertices") != 0;
const bool use_world_coords = vmap.count("use-world-coords") != 0; const bool use_world_coords = vmap.count("use-world-coords") != 0;
@@ -290,8 +304,6 @@ 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 && !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;
@@ -331,9 +343,6 @@ int main(int argc, char** argv) {
} }
} }
// Gets the set ifc types to be ignored from the command line.
std::set<std::string> entities(entity_vector.begin(), entity_vector.end());
const std::string input_filename = vmap["input-file"].as<std::string>(); const std::string input_filename = vmap["input-file"].as<std::string>();
if (!file_exists(input_filename)) { if (!file_exists(input_filename)) {
std::cerr << "[Error] Input file '" << input_filename << "' does not exist" << std::endl; std::cerr << "[Error] Input file '" << input_filename << "' does not exist" << std::endl;
@@ -366,19 +375,7 @@ int main(int argc, char** argv) {
std::string output_extension = output_filename.substr(output_filename.size()-4); std::string output_extension = output_filename.substr(output_filename.size()-4);
boost::to_lower(output_extension); boost::to_lower(output_extension);
// If no entity or names filters are specified these are the defaults to skip from output
if (entities.empty() && names.empty()) {
entities.insert("IfcSpace");
if (output_extension == ".svg") {
include_entities = true;
} else {
entities.insert("IfcOpeningElement");
}
}
Logger::SetOutput(&std::cout, &log_stream); Logger::SetOutput(&std::cout, &log_stream);
Logger::Verbosity(verbose ? Logger::LOG_NOTICE : Logger::LOG_ERROR);
if (output_extension == ".xml") { if (output_extension == ".xml") {
int exit_code = 1; int exit_code = 1;
try { try {
@@ -416,7 +413,6 @@ 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::GENERATE_UVS, generate_uvs); settings.set(IfcGeom::IteratorSettings::GENERATE_UVS, generate_uvs);
settings.set(IfcGeom::IteratorSettings::TRAVERSE, traverse); settings.set(IfcGeom::IteratorSettings::TRAVERSE, traverse);
settings.set_deflection_tolerance(deflection_tolerance);
settings.set(SerializerSettings::USE_ELEMENT_NAMES, use_element_names); settings.set(SerializerSettings::USE_ELEMENT_NAMES, use_element_names);
settings.set(SerializerSettings::USE_ELEMENT_GUIDS, use_element_guids); settings.set(SerializerSettings::USE_ELEMENT_GUIDS, use_element_guids);
@@ -489,7 +485,6 @@ int main(int argc, char** argv) {
} }
if (!serializer->ready()) { if (!serializer->ready()) {
Logger::Message(Logger::LOG_ERROR, "Unable to open output '" + output_filename + "' file for writing");
write_log(); write_log();
return 1; return 1;
} }
@@ -606,3 +601,45 @@ void write_log() {
std::cerr << "\n" << "Log:\n" << log << std::endl; std::cerr << "\n" << "Log:\n" << log << std::endl;
} }
} }
/// @todo Duplicate code for inclusion_filter and exclusion_filter validation.
void validate(boost::any& v, const std::vector<std::string>& values, inclusion_filter*, int)
{
/// @todo For now only single --include or --exclude supported. Support having multiple.
po::validators::check_first_occurrence(v);
inclusion_filter filter;
if (values.size() == 0) {
throw po::validation_error(po::validation_error::at_least_one_value_required);
}
std::string type = *values.begin();
if (type == "entities") {
filter.type = geom_filter::ENTITIES;
} else if (type == "names") {
filter.type = geom_filter::NAMES;
} else {
throw po::validation_error(po::validation_error::invalid_option_value);
}
filter.values.insert(values.begin() + 1, values.end());
v = filter;
}
void validate(boost::any& v, const std::vector<std::string>& values, exclusion_filter*, int)
{
po::validators::check_first_occurrence(v);
exclusion_filter filter;
if (values.size() == 0) {
throw po::validation_error(po::validation_error::at_least_one_value_required);
}
std::string type = *values.begin();
if (type == "entities") {
filter.type = geom_filter::ENTITIES;
} else if (type == "names") {
filter.type = geom_filter::NAMES;
} else {
throw po::validation_error(po::validation_error::invalid_option_value);
}
filter.values.insert(values.begin() + 1, values.end());
v = filter;
}
+4 -4
View File
@@ -331,7 +331,7 @@ namespace IfcGeom {
} }
/// @note Arbitrary names or wildcard expressions are handled case-sensitively. /// @note Arbitrary names or wildcard expressions are handled case-sensitively.
void include_entity_names(const std::vector<std::string>& names) void include_entity_names(const std::set<std::string>& names)
{ {
names_to_include_or_exclude.clear(); names_to_include_or_exclude.clear();
foreach(const std::string &name, names) foreach(const std::string &name, names)
@@ -340,7 +340,7 @@ namespace IfcGeom {
} }
/// @note Arbitrary names or wildcard expressions are handled case-sensitively. /// @note Arbitrary names or wildcard expressions are handled case-sensitively.
void exclude_entity_names(const std::vector<std::string>& names) void exclude_entity_names(const std::set<std::string>& names)
{ {
names_to_include_or_exclude.clear(); names_to_include_or_exclude.clear();
foreach(const std::string &name, names) foreach(const std::string &name, names)
@@ -546,7 +546,7 @@ namespace IfcGeom {
} }
} }
if (!type_found && traverse) { if (type_found != include_entities_in_processing && traverse) {
foreach(IfcSchema::Type::Enum type, entities_to_include_or_exclude) { foreach(IfcSchema::Type::Enum type, entities_to_include_or_exclude) {
IfcSchema::IfcProduct* parent, * current = prod; IfcSchema::IfcProduct* parent, * current = prod;
while ((parent = static_cast<IfcSchema::IfcProduct*>(kernel.get_decomposing_entity(current))) != 0) { while ((parent = static_cast<IfcSchema::IfcProduct*>(kernel.get_decomposing_entity(current))) != 0) {
@@ -570,7 +570,7 @@ namespace IfcGeom {
} }
} }
if (!name_found && traverse) { if (name_found != include_names_in_processing_ && traverse) {
foreach(const boost::regex& r, names_to_include_or_exclude) { foreach(const boost::regex& r, names_to_include_or_exclude) {
IfcSchema::IfcProduct* parent, *current = prod; IfcSchema::IfcProduct* parent, *current = prod;
while ((parent = static_cast<IfcSchema::IfcProduct*>(kernel.get_decomposing_entity(current))) != 0) { while ((parent = static_cast<IfcSchema::IfcProduct*>(kernel.get_decomposing_entity(current))) != 0) {