mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-10-02 04:59:52 +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 site_local_placement = vmap.count("site-local-placement") != 0;
|
||||||
const bool building_local_placement = vmap.count("building-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 generate_uvs = vmap.count("generate-uvs") != 0;
|
||||||
|
const bool validate = vmap.count("validate") != 0;
|
||||||
|
|
||||||
if (!quiet || vmap.count("version")) {
|
if (!quiet || vmap.count("version")) {
|
||||||
print_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::SEARCH_FLOOR, use_element_hierarchy);
|
||||||
settings.set(IfcGeom::IteratorSettings::SITE_LOCAL_PLACEMENT, site_local_placement);
|
settings.set(IfcGeom::IteratorSettings::SITE_LOCAL_PLACEMENT, site_local_placement);
|
||||||
settings.set(IfcGeom::IteratorSettings::BUILDING_LOCAL_PLACEMENT, building_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_NAMES, use_element_names);
|
||||||
settings.set(SerializerSettings::USE_ELEMENT_GUIDS, use_element_guids);
|
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.");
|
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);
|
write_log(!quiet);
|
||||||
|
|
||||||
time(&end);
|
time(&end);
|
||||||
@@ -1151,10 +1157,17 @@ void fix_quantities(IfcParse::IfcFile& f, bool no_progress, bool quiet, bool std
|
|||||||
IfcEntityList::ptr objects;
|
IfcEntityList::ptr objects;
|
||||||
boost::shared_ptr<IfcGeom::Representation::BRep> previous_geometry_pointer;
|
boost::shared_ptr<IfcGeom::Representation::BRep> previous_geometry_pointer;
|
||||||
|
|
||||||
do {
|
for (;; ++num_created) {
|
||||||
IfcGeom::BRepElement<double>* geom_object = context_iterator.get_native();
|
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());
|
objects->push(geom_object->product());
|
||||||
} else {
|
} else {
|
||||||
if (quantity) {
|
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);
|
latebound_access::set(rel, "RelatingPropertyDefinition", quantity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!geom_object) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
IfcEntityList::ptr quantities(new IfcEntityList);
|
IfcEntityList::ptr quantities(new IfcEntityList);
|
||||||
|
|
||||||
double a, b, c;
|
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);
|
quantities->push(quantity_area);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
for (auto& part : geom_object->geometry()) {
|
for (auto& part : geom_object->geometry()) {
|
||||||
auto quantity_complex = latebound_access::create(f, "IfcPhysicalComplexQuantity");
|
auto quantity_count = latebound_access::create(f, "IfcQuantityCount");
|
||||||
latebound_access::set(quantity_complex, "Name", std::string("Shape validation properties"));
|
latebound_access::set(quantity_count, "Name", std::string("Surface Genus"));
|
||||||
latebound_access::set(quantity_complex, "Discrimination", '#' + boost::lexical_cast<std::string>(part.ItemId()));
|
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()));
|
||||||
|
|
||||||
IfcEntityList::ptr quantities_2(new IfcEntityList);
|
quantities_2->push(quantity_count);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
const int euler = nv - ne + nf;
|
|
||||||
const int genus = (2 - euler) / 2;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
latebound_access::set(quantity_complex, "HasQuantities", quantities_2);
|
||||||
|
|
||||||
if (quantities->size()) {
|
if (quantities->size()) {
|
||||||
quantity = latebound_access::create(f, "IfcElementQuantity");
|
quantity = latebound_access::create(f, "IfcElementQuantity");
|
||||||
latebound_access::set(quantity, "OwnerHistory", ownerhist);
|
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;
|
old_progress = progress;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (++num_created, context_iterator.next());
|
}
|
||||||
|
|
||||||
if (!no_progress && quiet) {
|
if (!no_progress && quiet) {
|
||||||
for (; old_progress < 100; ++old_progress) {
|
for (; old_progress < 100; ++old_progress) {
|
||||||
|
|||||||
@@ -1591,7 +1591,7 @@ IfcGeom::BRepElement<P, PP>* IfcGeom::Kernel::create_brep_for_representation_and
|
|||||||
context_string = representation->ContextOfItems()->ContextType();
|
context_string = representation->ContextOfItems()->ContextType();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new BRepElement<P, PP>(
|
auto elem = new BRepElement<P, PP>(
|
||||||
product->data().id(),
|
product->data().id(),
|
||||||
parent_id,
|
parent_id,
|
||||||
name,
|
name,
|
||||||
@@ -1600,8 +1600,74 @@ IfcGeom::BRepElement<P, PP>* IfcGeom::Kernel::create_brep_for_representation_and
|
|||||||
context_string,
|
context_string,
|
||||||
trsf,
|
trsf,
|
||||||
boost::shared_ptr<IfcGeom::Representation::BRep>(shape),
|
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) {
|
IfcSchema::IfcRepresentation* IfcGeom::Kernel::representation_mapped_to(const IfcSchema::IfcRepresentation* representation) {
|
||||||
|
|||||||
@@ -84,8 +84,10 @@ namespace IfcGeom
|
|||||||
SITE_LOCAL_PLACEMENT = 1 << 15,
|
SITE_LOCAL_PLACEMENT = 1 << 15,
|
||||||
///
|
///
|
||||||
BUILDING_LOCAL_PLACEMENT = 1 << 16,
|
BUILDING_LOCAL_PLACEMENT = 1 << 16,
|
||||||
|
///
|
||||||
|
VALIDATE_QUANTITIES = 1 << 17,
|
||||||
/// Number of different setting flags.
|
/// Number of different setting flags.
|
||||||
NUM_SETTINGS = 16
|
NUM_SETTINGS = 17
|
||||||
};
|
};
|
||||||
/// Used to store logical OR combination of setting flags.
|
/// Used to store logical OR combination of setting flags.
|
||||||
typedef unsigned SettingField;
|
typedef unsigned SettingField;
|
||||||
|
|||||||
@@ -725,7 +725,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcRepresentation* l, IfcRepresen
|
|||||||
} else {
|
} else {
|
||||||
TopoDS_Shape s;
|
TopoDS_Shape s;
|
||||||
if (convert_shape(representation_item,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;
|
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() {
|
IfcGeom::impl::KernelFactoryImplementation& IfcGeom::impl::kernel_implementations() {
|
||||||
static KernelFactoryImplementation impl;
|
static KernelFactoryImplementation impl;
|
||||||
return impl;
|
return impl;
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ namespace IfcGeom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int count(const TopoDS_Shape&, TopAbs_ShapeEnum, bool unique=false);
|
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 bool is_manifold(const TopoDS_Shape& a);
|
||||||
static IfcUtil::IfcBaseEntity* get_decomposing_entity(IfcUtil::IfcBaseEntity*);
|
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) {
|
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 (log2 && type >= verbosity) {
|
||||||
if (format == FMT_PLAIN) {
|
if (format == FMT_PLAIN) {
|
||||||
plain_text_message(*log2, current_product, type, message, instance);
|
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; }
|
void Logger::Verbosity(Logger::Severity v) { verbosity = v; }
|
||||||
Logger::Severity Logger::Verbosity() { return verbosity; }
|
Logger::Severity Logger::Verbosity() { return verbosity; }
|
||||||
|
|
||||||
|
Logger::Severity Logger::MaxSeverity() { return max_severity; }
|
||||||
|
|
||||||
void Logger::OutputFormat(Format f) { format = f; }
|
void Logger::OutputFormat(Format f) { format = f; }
|
||||||
Logger::Format Logger::OutputFormat() { return format; }
|
Logger::Format Logger::OutputFormat() { return format; }
|
||||||
|
|
||||||
@@ -117,5 +122,6 @@ std::ostream* Logger::log1 = 0;
|
|||||||
std::ostream* Logger::log2 = 0;
|
std::ostream* Logger::log2 = 0;
|
||||||
std::stringstream Logger::log_stream;
|
std::stringstream Logger::log_stream;
|
||||||
Logger::Severity Logger::verbosity = Logger::LOG_NOTICE;
|
Logger::Severity Logger::verbosity = Logger::LOG_NOTICE;
|
||||||
|
Logger::Severity Logger::max_severity = Logger::LOG_NOTICE;
|
||||||
Logger::Format Logger::format = Logger::FMT_PLAIN;
|
Logger::Format Logger::format = Logger::FMT_PLAIN;
|
||||||
boost::optional<IfcUtil::IfcBaseClass*> Logger::current_product;
|
boost::optional<IfcUtil::IfcBaseClass*> Logger::current_product;
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ private:
|
|||||||
static Severity verbosity;
|
static Severity verbosity;
|
||||||
static Format format;
|
static Format format;
|
||||||
static boost::optional<IfcUtil::IfcBaseClass*> current_product;
|
static boost::optional<IfcUtil::IfcBaseClass*> current_product;
|
||||||
|
static Severity max_severity;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
static void SetProduct(boost::optional<IfcUtil::IfcBaseClass*> product) {
|
static void SetProduct(boost::optional<IfcUtil::IfcBaseClass*> product) {
|
||||||
@@ -56,6 +57,7 @@ public:
|
|||||||
/// Determines the types of log messages to get logged
|
/// Determines the types of log messages to get logged
|
||||||
static void Verbosity(Severity v);
|
static void Verbosity(Severity v);
|
||||||
static Severity Verbosity();
|
static Severity Verbosity();
|
||||||
|
static Severity MaxSeverity();
|
||||||
|
|
||||||
/// Determines output format: plain text or sequence of JSON objects
|
/// Determines output format: plain text or sequence of JSON objects
|
||||||
static void OutputFormat(Format f);
|
static void OutputFormat(Format f);
|
||||||
|
|||||||
Reference in New Issue
Block a user