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 TEMP_FILE_EXTENSION = ".tmp";
namespace po = boost::program_options;
void print_version()
{
/// @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;
}
void print_options(const boost::program_options::options_description& options)
void print_options(const po::options_description& options)
{
std::cerr << "\n" << options;
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;
void write_log();
int main(int argc, char** argv) {
boost::program_options::options_description generic_options("Command line options");
/// @todo Make this a feature of IfcGeom::Iterator instead.
/// 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()
("help,h", "display usage information")
("version", "display version information")
("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()
("input-file", boost::program_options::value<std::string>(), "input IFC file")
("output-file", boost::program_options::value<std::string>(), "output geometry file");
("input-file", po::value<std::string>(), "input IFC file")
("output-file", po::value<std::string>(), "output geometry file");
std::vector<std::string> entity_vector, names;
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()
("plan",
"Specifies whether to include curves in the output result. Typically "
@@ -174,47 +193,44 @@ int main(int argc, char** argv) {
("enable-layerset-slicing",
"Specifies whether to enable the slicing of products according "
"to their associated IfcMaterialLayerSet.")
("include",
"Specifies that the entities and/or names listed after --entities and/or --names are to be included")
("exclude",
"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(),
"A list of entities that should be included in or excluded from the "
"geometrical output, depending on whether --exclude or --include is specified. "
"Defaults to IfcOpeningElement and IfcSpace to be excluded. SVG output defaults "
"to IfcSpace to be included."
"The names are handled case-insensitively. Cannot be placed right before input file argument.")
("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.")
("include", po::value<inclusion_filter>(&include_filter)->multitoken(),
"Specifies that the entities or names provided are to be included. "
"If --include=entities specified, the following list of types should be included in the geometrical output. "
"SVG output defaults to IfcSpace to be included. The entity names are handled case-insensitively. "
"If --include=names specified, the following list of names or wildcard patterns should be included in the "
"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<exclusion_filter>(&exclude_filter)->multitoken(),
"Specifies that the 'entities' or 'names' 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 "
"in instances where you're going to recompute normals for the exported "
"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.")
("generate-uvs",
"Generates UVs (texture coordinates) by using simple box projection. Requires normals. "
"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.");
"HasOpenings, FillsVoid, ContainedInStructure) of the filtered entity, e.g. --include=names \"Level 1\" "
"--traverse includes entity with name \"Level 1\" and all of its children.");
std::string bounds, offset_str;
#ifdef HAVE_ICU
std::string unicode_mode;
#endif
short precision;
boost::program_options::options_description serializer_options("Serialization options");
po::options_description serializer_options("Serialization options");
serializer_options.add_options()
#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. "
"Accepted values 'utf8' (the default) and 'escape'.")
#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 "
"output will be scaled. Only used when converting to SVG.")
("use-element-names",
@@ -229,39 +245,42 @@ 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", 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.")
("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. "
"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, "
" 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);
boost::program_options::positional_options_description positional_options;
po::positional_options_description positional_options;
positional_options.add("input-file", 1);
positional_options.add("output-file", 1);
boost::program_options::variables_map vmap;
try {
boost::program_options::store(boost::program_options::command_line_parser(argc, argv).
options(cmdline_options).positional(positional_options).run(), vmap);
} catch (const boost::program_options::unknown_option& e) {
std::cerr << "[Error] Unknown option '" << e.get_option_name() << "'" << std::endl << std::endl;
po::variables_map vmap;
try {
po::store(po::command_line_parser(argc, argv).
options(cmdline_options).positional(positional_options).run(), vmap);
} catch (const po::unknown_option& e) {
std::cerr << "[Error] Unknown option '" << e.get_option_name() << "'" << "\n\n";
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) {
std::cerr << "[Error] " << e.what() << "\n\n";
print_usage();
return 1;
} catch (...) {
// Catch other errors such as invalid command line syntax
return EXIT_FAILURE;
} catch (...) { // other errors such as invalid command line syntax
print_usage();
return 1;
}
boost::program_options::notify(vmap);
po::notify(vmap);
print_version();
@@ -275,12 +294,7 @@ int main(int argc, char** argv) {
std::cerr << "[Error] Input file not specified" << std::endl;
print_usage();
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 weld_vertices = vmap.count("weld-vertices") != 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;
#endif
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_model = vmap.count("model") != 0 || (!include_plan);
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>();
if (!file_exists(input_filename)) {
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);
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::Verbosity(verbose ? Logger::LOG_NOTICE : Logger::LOG_ERROR);
if (output_extension == ".xml") {
int exit_code = 1;
try {
@@ -416,7 +413,6 @@ int main(int argc, char** argv) {
settings.set(IfcGeom::IteratorSettings::NO_NORMALS, no_normals);
settings.set(IfcGeom::IteratorSettings::GENERATE_UVS, generate_uvs);
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_GUIDS, use_element_guids);
@@ -489,7 +485,6 @@ int main(int argc, char** argv) {
}
if (!serializer->ready()) {
Logger::Message(Logger::LOG_ERROR, "Unable to open output '" + output_filename + "' file for writing");
write_log();
return 1;
}
@@ -606,3 +601,45 @@ void write_log() {
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.
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();
foreach(const std::string &name, names)
@@ -340,7 +340,7 @@ namespace IfcGeom {
}
/// @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();
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) {
IfcSchema::IfcProduct* parent, * current = prod;
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) {
IfcSchema::IfcProduct* parent, *current = prod;
while ((parent = static_cast<IfcSchema::IfcProduct*>(kernel.get_decomposing_entity(current))) != 0) {