mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-17 02:49:12 +00:00
Option to validate created geometries based on explicit quantities
This commit is contained in:
@@ -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<IfcGeom::Representation::BRep> previous_geometry_pointer;
|
||||
|
||||
do {
|
||||
IfcGeom::BRepElement<double>* geom_object = context_iterator.get_native();
|
||||
for (;; ++num_created) {
|
||||
bool has_more = true;
|
||||
if (num_created) {
|
||||
has_more = context_iterator.next();
|
||||
}
|
||||
IfcGeom::BRepElement<double>* 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<std::string>(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<std::string>(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) {
|
||||
|
||||
@@ -1591,17 +1591,83 @@ IfcGeom::BRepElement<P, PP>* IfcGeom::Kernel::create_brep_for_representation_and
|
||||
context_string = representation->ContextOfItems()->ContextType();
|
||||
}
|
||||
|
||||
return new BRepElement<P, PP>(
|
||||
auto elem = new BRepElement<P, PP>(
|
||||
product->data().id(),
|
||||
parent_id,
|
||||
name,
|
||||
name,
|
||||
product_type,
|
||||
guid,
|
||||
context_string,
|
||||
trsf,
|
||||
boost::shared_ptr<IfcGeom::Representation::BRep>(shape),
|
||||
product
|
||||
product
|
||||
);
|
||||
|
||||
if (settings.get(IteratorSettings::VALIDATE_QUANTITIES)) {
|
||||
auto rels = product->IsDefinedBy();
|
||||
for (auto& rel : *rels) {
|
||||
if (rel->as<IfcSchema::IfcRelDefinesByProperties>()) {
|
||||
auto pdef = rel->as<IfcSchema::IfcRelDefinesByProperties>()->RelatingPropertyDefinition();
|
||||
if (pdef->as<IfcSchema::IfcElementQuantity>()) {
|
||||
if (pdef->as<IfcSchema::IfcElementQuantity>()->OwnerHistory()->OwningApplication()->ApplicationDeveloper()->Name() == "IfcOpenShell") {
|
||||
auto qs = pdef->as<IfcSchema::IfcElementQuantity>()->Quantities();
|
||||
for (auto& q : *qs) {
|
||||
if (q->as<IfcSchema::IfcQuantityArea>() && q->Name() == "Total Surface Area") {
|
||||
double a_calc;
|
||||
double a_file = q->as<IfcSchema::IfcQuantityArea>()->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<IfcSchema::IfcQuantityVolume>() && q->Name() == "Volume") {
|
||||
double v_calc;
|
||||
double v_file = q->as<IfcSchema::IfcQuantityVolume>()->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<IfcSchema::IfcPhysicalComplexQuantity>() && q->Name() == "Shape Validation Properties") {
|
||||
auto qs2 = q->as<IfcSchema::IfcPhysicalComplexQuantity>()->HasQuantities();
|
||||
bool all_succeeded = qs2->size() > 0;
|
||||
for (auto& q2 : *qs2) {
|
||||
if (q2->as<IfcSchema::IfcQuantityCount>() && q2->Name() == "Surface Genus" && q2->hasDescription()) {
|
||||
int item_id = boost::lexical_cast<int>(q2->Description().substr(1));
|
||||
int genus = q2->as<IfcSchema::IfcQuantityCount>()->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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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*);
|
||||
|
||||
@@ -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<IfcUtil::IfcBaseClass*> Logger::current_product;
|
||||
|
||||
@@ -44,6 +44,7 @@ private:
|
||||
static Severity verbosity;
|
||||
static Format format;
|
||||
static boost::optional<IfcUtil::IfcBaseClass*> current_product;
|
||||
static Severity max_severity;
|
||||
public:
|
||||
|
||||
static void SetProduct(boost::optional<IfcUtil::IfcBaseClass*> 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);
|
||||
|
||||
Reference in New Issue
Block a user