@@ -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 << " \n Run 'IfcConvert --help' for more information." ;
@@ -120,6 +121,7 @@ bool rename_file(const std::string& old_filename, const std::string& new_filenam
120121
121122static std::stringstream log_stream;
122123void write_log (bool );
124+ void fix_quantities (IfcParse::IfcFile&, bool , bool , bool );
123125
124126// / @todo make the filters non-global
125127IfcGeom::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