From 72c96905b9eacc51b05f2918079632a7c454f0d3 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Wed, 12 Dec 2018 12:33:09 +0100 Subject: [PATCH] Option to validate created geometries based on explicit quantities --- src/ifcconvert/IfcConvert.cpp | 55 ++++++++++++-------- src/ifcgeom/IfcGeomFunctions.cpp | 72 ++++++++++++++++++++++++-- src/ifcgeom/IfcGeomIteratorSettings.h | 4 +- src/ifcgeom/IfcGeomShapes.cpp | 2 +- src/ifcgeom_schema_agnostic/Kernel.cpp | 12 +++++ src/ifcgeom_schema_agnostic/Kernel.h | 1 + src/ifcparse/IfcLogger.cpp | 6 +++ src/ifcparse/IfcLogger.h | 2 + 8 files changed, 128 insertions(+), 26 deletions(-) diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index e2ed533f7b..292a0a4859 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -379,6 +379,7 @@ int main(int argc, char** argv) const bool site_local_placement = vmap.count("site-local-placement") != 0; const bool building_local_placement = vmap.count("building-local-placement") != 0; const bool generate_uvs = vmap.count("generate-uvs") != 0; + const bool validate = vmap.count("validate") != 0; if (!quiet || vmap.count("version")) { print_version(); @@ -571,7 +572,7 @@ int main(int argc, char** argv) settings.set(IfcGeom::IteratorSettings::SEARCH_FLOOR, use_element_hierarchy); settings.set(IfcGeom::IteratorSettings::SITE_LOCAL_PLACEMENT, site_local_placement); settings.set(IfcGeom::IteratorSettings::BUILDING_LOCAL_PLACEMENT, building_local_placement); - + settings.set(IfcGeom::IteratorSettings::VALIDATE_QUANTITIES, validate); settings.set(SerializerSettings::USE_ELEMENT_NAMES, use_element_names); settings.set(SerializerSettings::USE_ELEMENT_GUIDS, use_element_guids); @@ -785,6 +786,11 @@ int main(int argc, char** argv) output_temp_filename + "' for the conversion result."); } + if (validate && Logger::MaxSeverity() >= Logger::LOG_ERROR) { + Logger::Error("Errors encountered during proccessing."); + successful = false; + } + write_log(!quiet); time(&end); @@ -1151,10 +1157,17 @@ void fix_quantities(IfcParse::IfcFile& f, bool no_progress, bool quiet, bool std IfcEntityList::ptr objects; boost::shared_ptr previous_geometry_pointer; - do { - IfcGeom::BRepElement* geom_object = context_iterator.get_native(); + for (;; ++num_created) { + bool has_more = true; + if (num_created) { + has_more = context_iterator.next(); + } + IfcGeom::BRepElement* geom_object = nullptr; + if (has_more) { + geom_object = context_iterator.get_native(); + } - if (geom_object->geometry_pointer() == previous_geometry_pointer) { + if (geom_object && geom_object->geometry_pointer() == previous_geometry_pointer) { objects->push(geom_object->product()); } else { if (quantity) { @@ -1164,6 +1177,10 @@ void fix_quantities(IfcParse::IfcFile& f, bool no_progress, bool quiet, bool std latebound_access::set(rel, "RelatingPropertyDefinition", quantity); } + if (!geom_object) { + break; + } + IfcEntityList::ptr quantities(new IfcEntityList); double a, b, c; @@ -1188,27 +1205,23 @@ void fix_quantities(IfcParse::IfcFile& f, bool no_progress, bool quiet, bool std quantities->push(quantity_area); } - for (auto& part : geom_object->geometry()) { - auto quantity_complex = latebound_access::create(f, "IfcPhysicalComplexQuantity"); - latebound_access::set(quantity_complex, "Name", std::string("Shape validation properties")); - latebound_access::set(quantity_complex, "Discrimination", '#' + boost::lexical_cast(part.ItemId())); + auto quantity_complex = latebound_access::create(f, "IfcPhysicalComplexQuantity"); + latebound_access::set(quantity_complex, "Name", std::string("Shape Validation Properties")); + quantities->push(quantity_complex); - IfcEntityList::ptr quantities_2(new IfcEntityList); - - int nv = IfcGeom::Kernel::count(part.Shape(), TopAbs_VERTEX, true); - int ne = IfcGeom::Kernel::count(part.Shape(), TopAbs_EDGE, true); - int nf = IfcGeom::Kernel::count(part.Shape(), TopAbs_FACE, true); + IfcEntityList::ptr quantities_2(new IfcEntityList); - const int euler = nv - ne + nf; - const int genus = (2 - euler) / 2; + for (auto& part : geom_object->geometry()) { + auto quantity_count = latebound_access::create(f, "IfcQuantityCount"); + latebound_access::set(quantity_count, "Name", std::string("Surface Genus")); + latebound_access::set(quantity_count, "Description", '#' + boost::lexical_cast(part.ItemId())); + latebound_access::set(quantity_count, "CountValue", IfcGeom::Kernel::surface_genus(part.Shape())); - auto quantity_area = latebound_access::create(f, "IfcQuantityCount"); - latebound_access::set(quantity_area, "Name", std::string("Surface genus")); - latebound_access::set(quantity_area, "CountValue", genus); - - latebound_access::set(quantity_complex, "HasQuantities", quantities_2); + quantities_2->push(quantity_count); } + latebound_access::set(quantity_complex, "HasQuantities", quantities_2); + if (quantities->size()) { quantity = latebound_access::create(f, "IfcElementQuantity"); latebound_access::set(quantity, "OwnerHistory", ownerhist); @@ -1238,7 +1251,7 @@ void fix_quantities(IfcParse::IfcFile& f, bool no_progress, bool quiet, bool std old_progress = progress; } } - } while (++num_created, context_iterator.next()); + } if (!no_progress && quiet) { for (; old_progress < 100; ++old_progress) { diff --git a/src/ifcgeom/IfcGeomFunctions.cpp b/src/ifcgeom/IfcGeomFunctions.cpp index e430db93e8..4b07162ebd 100644 --- a/src/ifcgeom/IfcGeomFunctions.cpp +++ b/src/ifcgeom/IfcGeomFunctions.cpp @@ -1591,17 +1591,83 @@ IfcGeom::BRepElement* IfcGeom::Kernel::create_brep_for_representation_and context_string = representation->ContextOfItems()->ContextType(); } - return new BRepElement( + auto elem = new BRepElement( product->data().id(), parent_id, - name, + name, product_type, guid, context_string, trsf, boost::shared_ptr(shape), - product + product ); + + if (settings.get(IteratorSettings::VALIDATE_QUANTITIES)) { + auto rels = product->IsDefinedBy(); + for (auto& rel : *rels) { + if (rel->as()) { + auto pdef = rel->as()->RelatingPropertyDefinition(); + if (pdef->as()) { + if (pdef->as()->OwnerHistory()->OwningApplication()->ApplicationDeveloper()->Name() == "IfcOpenShell") { + auto qs = pdef->as()->Quantities(); + for (auto& q : *qs) { + if (q->as() && q->Name() == "Total Surface Area") { + double a_calc; + double a_file = q->as()->AreaValue(); + if (elem->geometry().calculate_surface_area(a_calc)) { + double diff = std::abs(a_calc - a_file); + if (diff / std::sqrt(a_file) > getValue(GV_PRECISION)) { + Logger::Error("Validation of surface area failed for:", product); + } else { + Logger::Notice("Validation of surface area succeeded for:", product); + } + } else { + Logger::Error("Validation of surface area failed for:", product); + } + } else if (q->as() && q->Name() == "Volume") { + double v_calc; + double v_file = q->as()->VolumeValue(); + if (elem->geometry().calculate_volume(v_calc)) { + double diff = std::abs(v_calc - v_file); + if (diff / std::sqrt(v_file) > getValue(GV_PRECISION)) { + Logger::Error("Validation of volume failed for:", product); + } else { + Logger::Notice("Validation of volume succeeded for:", product); + } + } else { + Logger::Error("Validation of volume failed for:", product); + } + } else if (q->as() && q->Name() == "Shape Validation Properties") { + auto qs2 = q->as()->HasQuantities(); + bool all_succeeded = qs2->size() > 0; + for (auto& q2 : *qs2) { + if (q2->as() && q2->Name() == "Surface Genus" && q2->hasDescription()) { + int item_id = boost::lexical_cast(q2->Description().substr(1)); + int genus = q2->as()->CountValue(); + for (auto& part : elem->geometry()) { + if (part.ItemId() == item_id) { + if (surface_genus(part.Shape()) != genus) { + all_succeeded = false; + } + } + } + } + } + if (!all_succeeded) { + Logger::Error("Validation of surface genus failed for:", product); + } else { + Logger::Notice("Validation of surface genus succeeded for:", product); + } + } + } + } + } + } + } + } + + return elem; } IfcSchema::IfcRepresentation* IfcGeom::Kernel::representation_mapped_to(const IfcSchema::IfcRepresentation* representation) { diff --git a/src/ifcgeom/IfcGeomIteratorSettings.h b/src/ifcgeom/IfcGeomIteratorSettings.h index 8a601c4e55..7928d296ec 100644 --- a/src/ifcgeom/IfcGeomIteratorSettings.h +++ b/src/ifcgeom/IfcGeomIteratorSettings.h @@ -84,8 +84,10 @@ namespace IfcGeom SITE_LOCAL_PLACEMENT = 1 << 15, /// BUILDING_LOCAL_PLACEMENT = 1 << 16, + /// + VALIDATE_QUANTITIES = 1 << 17, /// Number of different setting flags. - NUM_SETTINGS = 16 + NUM_SETTINGS = 17 }; /// Used to store logical OR combination of setting flags. typedef unsigned SettingField; diff --git a/src/ifcgeom/IfcGeomShapes.cpp b/src/ifcgeom/IfcGeomShapes.cpp index ea26084fef..86cfd21418 100644 --- a/src/ifcgeom/IfcGeomShapes.cpp +++ b/src/ifcgeom/IfcGeomShapes.cpp @@ -725,7 +725,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcRepresentation* l, IfcRepresen } else { TopoDS_Shape s; if (convert_shape(representation_item,s)) { - shapes.push_back(IfcRepresentationShapeItem(l->data().id(), s, get_style(representation_item))); + shapes.push_back(IfcRepresentationShapeItem(representation_item->data().id(), s, get_style(representation_item))); part_succes |= true; } } diff --git a/src/ifcgeom_schema_agnostic/Kernel.cpp b/src/ifcgeom_schema_agnostic/Kernel.cpp index b09e34e6ca..f80bcbef2f 100644 --- a/src/ifcgeom_schema_agnostic/Kernel.cpp +++ b/src/ifcgeom_schema_agnostic/Kernel.cpp @@ -31,6 +31,18 @@ int IfcGeom::Kernel::count(const TopoDS_Shape& s, TopAbs_ShapeEnum t, bool uniqu } } + +int IfcGeom::Kernel::surface_genus(const TopoDS_Shape& s) { + int nv = count(s, TopAbs_VERTEX, true); + int ne = count(s, TopAbs_EDGE, true); + int nf = count(s, TopAbs_FACE, true); + + const int euler = nv - ne + nf; + const int genus = (2 - euler) / 2; + + return genus; +} + IfcGeom::impl::KernelFactoryImplementation& IfcGeom::impl::kernel_implementations() { static KernelFactoryImplementation impl; return impl; diff --git a/src/ifcgeom_schema_agnostic/Kernel.h b/src/ifcgeom_schema_agnostic/Kernel.h index edea2e62a5..338995d9d7 100644 --- a/src/ifcgeom_schema_agnostic/Kernel.h +++ b/src/ifcgeom_schema_agnostic/Kernel.h @@ -84,6 +84,7 @@ namespace IfcGeom { } static int count(const TopoDS_Shape&, TopAbs_ShapeEnum, bool unique=false); + static int surface_genus(const TopoDS_Shape&); static bool is_manifold(const TopoDS_Shape& a); static IfcUtil::IfcBaseEntity* get_decomposing_entity(IfcUtil::IfcBaseEntity*); diff --git a/src/ifcparse/IfcLogger.cpp b/src/ifcparse/IfcLogger.cpp index fbab840cfa..dd834d4241 100644 --- a/src/ifcparse/IfcLogger.cpp +++ b/src/ifcparse/IfcLogger.cpp @@ -76,6 +76,9 @@ void Logger::SetOutput(std::ostream* l1, std::ostream* l2) { } void Logger::Message(Logger::Severity type, const std::string& message, const IfcUtil::IfcBaseClass* instance) { + if (type > max_severity) { + max_severity = type; + } if (log2 && type >= verbosity) { if (format == FMT_PLAIN) { plain_text_message(*log2, current_product, type, message, instance); @@ -110,6 +113,8 @@ std::string Logger::GetLog() { void Logger::Verbosity(Logger::Severity v) { verbosity = v; } Logger::Severity Logger::Verbosity() { return verbosity; } +Logger::Severity Logger::MaxSeverity() { return max_severity; } + void Logger::OutputFormat(Format f) { format = f; } Logger::Format Logger::OutputFormat() { return format; } @@ -117,5 +122,6 @@ std::ostream* Logger::log1 = 0; std::ostream* Logger::log2 = 0; std::stringstream Logger::log_stream; Logger::Severity Logger::verbosity = Logger::LOG_NOTICE; +Logger::Severity Logger::max_severity = Logger::LOG_NOTICE; Logger::Format Logger::format = Logger::FMT_PLAIN; boost::optional Logger::current_product; diff --git a/src/ifcparse/IfcLogger.h b/src/ifcparse/IfcLogger.h index 30a9171374..783dcbdcfd 100644 --- a/src/ifcparse/IfcLogger.h +++ b/src/ifcparse/IfcLogger.h @@ -44,6 +44,7 @@ private: static Severity verbosity; static Format format; static boost::optional current_product; + static Severity max_severity; public: static void SetProduct(boost::optional product) { @@ -56,6 +57,7 @@ public: /// Determines the types of log messages to get logged static void Verbosity(Severity v); static Severity Verbosity(); + static Severity MaxSeverity(); /// Determines output format: plain text or sequence of JSON objects static void OutputFormat(Format f);