Skip to content

Commit 3ff619c

Browse files
committed
Quantities in convert and geomserver. Faceset helper for creating edge pairs.
1 parent ad41f04 commit 3ff619c

11 files changed

Lines changed: 703 additions & 196 deletions

File tree

src/ifcconvert/IfcConvert.cpp

Lines changed: 249 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ void print_usage(bool suggest_help = true)
6969
{
7070
std::cout << "Usage: IfcConvert [options] <input.ifc> [<output>]\n"
7171
<< "\n"
72-
<< "Converts the geometry in an IFC file into one of the following formats:\n"
72+
<< "Converts (the geometry in) an IFC file into one of the following formats:\n"
7373
<< " .obj WaveFront OBJ (a .mtl file is also created)\n"
7474
#ifdef WITH_OPENCOLLADA
7575
<< " .dae Collada Digital Assets Exchange\n"
@@ -78,7 +78,8 @@ void print_usage(bool suggest_help = true)
7878
<< " .igs IGES Initial Graphics Exchange Specification\n"
7979
<< " .xml XML Property definitions and decomposition tree\n"
8080
<< " .svg SVG Scalable Vector Graphics (2D floor plan)\n"
81-
<< "\n"
81+
<< " .ifc IFC-SPF Industry Foundation Classes\n"
82+
<< "\n"
8283
<< "If no output filename given, <input>." + DEFAULT_EXTENSION + " will be used as the output file.\n";
8384
if (suggest_help) {
8485
std::cout << "\nRun 'IfcConvert --help' for more information.";
@@ -120,6 +121,7 @@ bool rename_file(const std::string& old_filename, const std::string& new_filenam
120121

121122
static std::stringstream log_stream;
122123
void write_log(bool);
124+
void fix_quantities(IfcParse::IfcFile&, bool, bool, bool);
123125

124126
/// @todo make the filters non-global
125127
IfcGeom::entity_filter entity_filter; // Entity filter is used always by default.
@@ -180,8 +182,13 @@ int main(int argc, char** argv)
180182
exclusion_traverse_filter exclude_traverse_filter;
181183
std::string filter_filename;
182184
std::string default_material_filename;
183-
184-
po::options_description geom_options("Geometry options");
185+
186+
po::options_description ifc_options("IFC options");
187+
ifc_options.add_options()
188+
("calculate-quantities", "Calculate or fix the physical quantity definitions "
189+
"based on an interpretation of the geometry when exporting IFC");
190+
191+
po::options_description geom_options("Geometry options");
185192
geom_options.add_options()
186193
("plan",
187194
"Specifies whether to include curves in the output result. Typically "
@@ -466,23 +473,44 @@ int main(int argc, char** argv)
466473

467474
IfcParse::IfcFile* ifc_file = 0;
468475

469-
if (output_extension == ".xml") {
470-
int exit_code = EXIT_FAILURE;
471-
try {
472-
if (init_input_file(input_filename, ifc_file, no_progress || quiet, mmap)) {
473-
XmlSerializer s(ifc_file, output_temp_filename);
474-
Logger::Status("Writing XML output...");
475-
s.finalize();
476-
Logger::Status("Done!");
477-
rename_file(output_temp_filename, output_filename);
478-
exit_code = EXIT_SUCCESS;
479-
}
480-
} catch (const std::exception& e) {
476+
// @todo clean up serializer selection
477+
// @todo detect program options that conflict with the chosen serializer
478+
if (output_extension == ".xml") {
479+
int exit_code = EXIT_FAILURE;
480+
try {
481+
if (init_input_file(input_filename, ifc_file, no_progress || quiet, mmap)) {
482+
XmlSerializer s(ifc_file, output_temp_filename);
483+
Logger::Status("Writing XML output...");
484+
s.finalize();
485+
Logger::Status("Done!");
486+
rename_file(output_temp_filename, output_filename);
487+
exit_code = EXIT_SUCCESS;
488+
}
489+
} catch (const std::exception& e) {
481490
Logger::Error(e);
482491
}
483-
write_log(!quiet);
484-
return exit_code;
485-
}
492+
write_log(!quiet);
493+
return exit_code;
494+
} else if (output_extension == ".ifc") {
495+
int exit_code = EXIT_FAILURE;
496+
try {
497+
if (init_input_file(input_filename, ifc_file, no_progress || quiet, mmap)) {
498+
std::ofstream fs(output_filename.c_str());
499+
if (fs.is_open()) {
500+
if (vmap.count("calculate-quantities")) {
501+
fix_quantities(*ifc_file, no_progress, quiet, stderr_progress);
502+
}
503+
fs << *ifc_file;
504+
} else {
505+
Logger::Error("Unable to open output file for writing");
506+
}
507+
}
508+
} catch (const std::exception& e) {
509+
Logger::Error(e);
510+
}
511+
write_log(!quiet);
512+
return exit_code;
513+
}
486514

487515
if (!filter_filename.empty()) {
488516
size_t num_filters = read_filters_from_file(filter_filename, include_filter, include_traverse_filter, exclude_filter, exclude_traverse_filter);
@@ -984,3 +1012,205 @@ std::vector<IfcGeom::filter_t> setup_filters(const std::vector<geom_filter>& fil
9841012

9851013
return filter_funcs;
9861014
}
1015+
1016+
namespace latebound_access {
1017+
1018+
template <typename T>
1019+
void set(IfcUtil::IfcBaseClass* inst, const std::string& attr, T t);
1020+
1021+
template <typename T>
1022+
void set_enumeration(IfcUtil::IfcBaseClass*, const std::string&, const IfcParse::enumeration_type*, T) {}
1023+
1024+
template <>
1025+
void set_enumeration(IfcUtil::IfcBaseClass* inst, const std::string& attr, const IfcParse::enumeration_type* enum_type, std::string t) {
1026+
std::vector<std::string>::const_iterator it = std::find(
1027+
enum_type->enumeration_items().begin(),
1028+
enum_type->enumeration_items().end(),
1029+
t);
1030+
1031+
return set(inst, attr, IfcWrite::IfcWriteArgument::EnumerationReference(it - enum_type->enumeration_items().begin(), it->c_str()));
1032+
}
1033+
1034+
template <typename T>
1035+
void set(IfcUtil::IfcBaseClass* inst, const std::string& attr, T t) {
1036+
auto decl = inst->declaration().as_entity();
1037+
auto i = decl->attribute_index(attr);
1038+
1039+
auto attr_type = decl->attribute_by_index(i)->type_of_attribute();
1040+
if (attr_type->as_named_type() && attr_type->as_named_type()->declared_type()->as_enumeration_type()) {
1041+
set_enumeration(inst, attr, attr_type->as_named_type()->declared_type()->as_enumeration_type(), t);
1042+
}
1043+
1044+
IfcWrite::IfcWriteArgument* a = new IfcWrite::IfcWriteArgument;
1045+
a->set(t);
1046+
inst->data().attributes()[i] = a;
1047+
}
1048+
1049+
IfcUtil::IfcBaseClass* create(IfcParse::IfcFile& f, const std::string& entity) {
1050+
auto decl = f.schema()->declaration_by_name(entity);
1051+
auto data = new IfcEntityInstanceData(decl);
1052+
auto inst = f.schema()->instantiate(data);
1053+
if (decl->is("IfcRoot")) {
1054+
IfcParse::IfcGlobalId guid;
1055+
latebound_access::set(inst, "GlobalId", (std::string) guid);
1056+
}
1057+
return f.addEntity(inst);
1058+
}
1059+
}
1060+
1061+
void fix_quantities(IfcParse::IfcFile& f, bool no_progress, bool quiet, bool stderr_progress) {
1062+
{
1063+
auto delete_reversed = [&f](const IfcEntityList::ptr& insts) {
1064+
if (!insts) {
1065+
return;
1066+
}
1067+
// Lists are traversed back to front as the list may be mutated when
1068+
// instances are removed from the grouping by type.
1069+
for (auto it = insts->end() - 1; it >= insts->begin(); --it) {
1070+
IfcUtil::IfcBaseClass* const inst = *it;
1071+
f.removeEntity(inst);
1072+
}
1073+
};
1074+
1075+
// Delete quantities
1076+
auto quantities = f.instances_by_type("IfcPhysicalQuantity");
1077+
if (quantities) {
1078+
quantities = quantities->filtered({ f.schema()->declaration_by_name("IfcPhysicalComplexQuantity") });
1079+
delete_reversed(quantities);
1080+
}
1081+
1082+
// Delete complexes
1083+
delete_reversed(f.instances_by_type("IfcPhysicalComplexQuantity"));
1084+
1085+
auto element_quantities = f.instances_by_type("IfcElementQuantity");
1086+
1087+
// Capture relationship nodes
1088+
std::vector<IfcUtil::IfcBaseClass*> relationships;
1089+
auto IfcRelDefinesByProperties = f.schema()->declaration_by_name("IfcRelDefinesByProperties");
1090+
for (auto& eq : *element_quantities) {
1091+
auto rels = eq->data().getInverse(IfcRelDefinesByProperties, -1);
1092+
for (auto& rel : *rels) {
1093+
relationships.push_back(rel);
1094+
}
1095+
}
1096+
1097+
// Delete element quantities
1098+
delete_reversed(element_quantities);
1099+
1100+
// Delete relationship nodes
1101+
for (auto& rel : relationships) {
1102+
f.removeEntity(rel);
1103+
}
1104+
}
1105+
1106+
IfcGeom::IteratorSettings settings;
1107+
settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, false);
1108+
settings.set(IfcGeom::IteratorSettings::WELD_VERTICES, false);
1109+
settings.set(IfcGeom::IteratorSettings::SEW_SHELLS, true);
1110+
settings.set(IfcGeom::IteratorSettings::CONVERT_BACK_UNITS, true);
1111+
settings.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, true);
1112+
1113+
IfcGeom::Iterator<double> context_iterator(settings, &f);
1114+
1115+
if (!context_iterator.initialize()) {
1116+
return;
1117+
}
1118+
1119+
size_t num_created = 0;
1120+
int old_progress = quiet ? 0 : -1;
1121+
1122+
auto person = latebound_access::create(f, "IfcPerson");
1123+
latebound_access::set(person, "FamilyName", std::string("IfcOpenShell"));
1124+
latebound_access::set(person, "GivenName", std::string("IfcOpenShell"));
1125+
1126+
auto org = latebound_access::create(f, "IfcOrganization");
1127+
latebound_access::set(org, "Name", std::string("IfcOpenShell"));
1128+
1129+
auto pando = latebound_access::create(f, "IfcPersonAndOrganization");
1130+
latebound_access::set(pando, "ThePerson", person);
1131+
latebound_access::set(pando, "TheOrganization", org);
1132+
1133+
auto application = latebound_access::create(f, "IfcApplication");
1134+
latebound_access::set(application, "ApplicationDeveloper", org);
1135+
latebound_access::set(application, "Version", std::string(IFCOPENSHELL_VERSION));
1136+
latebound_access::set(application, "ApplicationFullName", std::string("IfcConvert"));
1137+
latebound_access::set(application, "ApplicationIdentifier", std::string("IfcConvert" IFCOPENSHELL_VERSION));
1138+
1139+
auto ownerhist = latebound_access::create(f, "IfcOwnerHistory");
1140+
latebound_access::set(ownerhist, "OwningUser", pando);
1141+
latebound_access::set(ownerhist, "OwningApplication", application);
1142+
latebound_access::set(ownerhist, "ChangeAction", std::string("MODIFIED"));
1143+
latebound_access::set(ownerhist, "CreationDate", (int)time(0));
1144+
1145+
IfcUtil::IfcBaseClass* quantity = nullptr;
1146+
IfcEntityList::ptr objects;
1147+
boost::shared_ptr<IfcGeom::Representation::BRep> previous_geometry_pointer;
1148+
1149+
do {
1150+
IfcGeom::BRepElement<double>* geom_object = context_iterator.get_native();
1151+
1152+
if (geom_object->geometry_pointer() == previous_geometry_pointer) {
1153+
objects->push(geom_object->product());
1154+
} else {
1155+
if (quantity) {
1156+
auto rel = latebound_access::create(f, "IfcRelDefinesByProperties");
1157+
latebound_access::set(rel, "OwnerHistory", ownerhist);
1158+
latebound_access::set(rel, "RelatedObjects", objects);
1159+
latebound_access::set(rel, "RelatingPropertyDefinition", quantity);
1160+
}
1161+
1162+
IfcEntityList::ptr quantities(new IfcEntityList);
1163+
1164+
double a, b, c;
1165+
if (geom_object->geometry().calculate_surface_area(a)) {
1166+
auto quantity_area = latebound_access::create(f, "IfcQuantityArea");
1167+
latebound_access::set(quantity_area, "Name", std::string("Total Surface Area"));
1168+
latebound_access::set(quantity_area, "AreaValue", a);
1169+
quantities->push(quantity_area);
1170+
}
1171+
1172+
if (geom_object->geometry().calculate_volume(a)) {
1173+
auto quantity_volume = latebound_access::create(f, "IfcQuantityVolume");
1174+
latebound_access::set(quantity_volume, "Name", std::string("Volume"));
1175+
latebound_access::set(quantity_volume, "VolumeValue", a);
1176+
quantities->push(quantity_volume);
1177+
}
1178+
1179+
if (geom_object->calculate_projected_surface_area(a, b, c)) {
1180+
auto quantity_area = latebound_access::create(f, "IfcQuantityArea");
1181+
latebound_access::set(quantity_area, "Name", std::string("Footprint Area"));
1182+
latebound_access::set(quantity_area, "AreaValue", c);
1183+
quantities->push(quantity_area);
1184+
}
1185+
1186+
if (quantities->size()) {
1187+
quantity = latebound_access::create(f, "IfcElementQuantity");
1188+
latebound_access::set(quantity, "OwnerHistory", ownerhist);
1189+
latebound_access::set(quantity, "Quantities", quantities);
1190+
}
1191+
1192+
objects.reset(new IfcEntityList);
1193+
objects->push(geom_object->product());
1194+
}
1195+
1196+
previous_geometry_pointer = geom_object->geometry_pointer();
1197+
1198+
if (!no_progress) {
1199+
if (quiet) {
1200+
const int progress = context_iterator.progress();
1201+
for (; old_progress < progress; ++old_progress) {
1202+
std::cout << ".";
1203+
if (stderr_progress)
1204+
std::cerr << ".";
1205+
}
1206+
std::cout << std::flush;
1207+
if (stderr_progress)
1208+
std::cerr << std::flush;
1209+
} else {
1210+
const int progress = context_iterator.progress() / 2;
1211+
if (old_progress != progress) Logger::ProgressBar(progress);
1212+
old_progress = progress;
1213+
}
1214+
}
1215+
} while (++num_created, context_iterator.next());
1216+
}

0 commit comments

Comments
 (0)