diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index f320165e5a..1cc7a0aa81 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -946,14 +946,14 @@ void write_log(bool header) { } #include +#include bool init_input_file(const std::string& filename, IfcParse::IfcFile*& ifc_file, bool no_progress, bool mmap) { - time_t start, end; + std::clock_t c_start = std::clock(); // Prevent IfcFile::Init() prints by setting output to null temporarily if (no_progress) { Logger::SetOutput(NULL, &log_stream); } - time(&start); #ifdef USE_MMAP ifc_file = new IfcParse::IfcFile(filename, mmap); #else @@ -970,10 +970,15 @@ bool init_input_file(const std::string& filename, IfcParse::IfcFile*& ifc_file, Logger::Error("Unable to parse input file '" + filename + "'"); return false; } - time(&end); + + std::clock_t c_end = std::clock(); if (no_progress) { Logger::SetOutput(&cout_, &log_stream); } - else { Logger::Status("Parsing input file took " + format_duration(start, end)); } + else { + std::stringstream ss; + ss << std::setprecision(14) << (c_end - c_start) / (double)CLOCKS_PER_SEC; + Logger::Status("total_ifc_parse_time " + ss.str()); + } return true; diff --git a/src/ifcconvert/validate_space_boundaries.cpp b/src/ifcconvert/validate_space_boundaries.cpp index e0322badfd..b555eb19d6 100644 --- a/src/ifcconvert/validate_space_boundaries.cpp +++ b/src/ifcconvert/validate_space_boundaries.cpp @@ -1,25 +1,111 @@ #include "validation_utils.h" +using namespace ifcopenshell::geometry; + +#include +#include +#include +#include + +typedef Kernel_::FT FT; +typedef Kernel_::Point_3 Point; +typedef Kernel_::Segment_3 Segment; +typedef CGAL::Polyhedron_3 Polyhedron; +typedef CGAL::AABB_face_graph_triangle_primitive Primitive; +typedef CGAL::AABB_traits Traits; +typedef CGAL::AABB_tree Tree; +typedef Tree::Point_and_primitive_id Point_and_primitive_id; + void fix_spaceboundaries(IfcParse::IfcFile& f, bool no_progress, bool quiet, bool stderr_progress) { - intersection_validator v(f, { "IfcWall", "IfcSpace" }, no_progress, quiet, stderr_progress); + intersection_validator v(f, { "IfcWall", "IfcSpace" }, 1.e-5, no_progress, quiet, stderr_progress); + + auto rels = f.instances_by_type("IfcRelSpaceBoundary"); + + std::map, const IfcUtil::IfcBaseClass*> rel_by_space_elem; + + + if (rels) { + std::for_each(rels->begin(), rels->end(), [&rel_by_space_elem](const IfcUtil::IfcBaseClass* rel) { + auto x = ((IfcUtil::IfcBaseEntity*)rel)->get_value("RelatingSpace"); + try { + auto y = ((IfcUtil::IfcBaseEntity*)rel)->get_value("RelatedBuildingElement"); + rel_by_space_elem.insert({ { x,y }, rel }); + } catch (IfcParse::IfcException&) { + // RelatedBuildingElement can be NULL + } + }); + } + + std::set rels_encounted; + + IfcParse::IfcFile f2("boundaries-triangulated.ifc"); + ifcopenshell::geometry::settings settings; + + settings.set(ifcopenshell::geometry::settings::USE_WORLD_COORDS, false); + settings.set(ifcopenshell::geometry::settings::WELD_VERTICES, false); + settings.set(ifcopenshell::geometry::settings::SEW_SHELLS, true); + settings.set(ifcopenshell::geometry::settings::CONVERT_BACK_UNITS, true); + settings.set(ifcopenshell::geometry::settings::DISABLE_TRIANGULATION, true); + settings.set(ifcopenshell::geometry::settings::DISABLE_OPENING_SUBTRACTIONS, true); + + ifcopenshell::geometry::Converter c("cgal", &f2, settings); + + std::map, std::vector> elem_to_space_boundary_coords; + + for (auto& i : *f2.instances_by_type("IfcProduct")) { + auto n = ((IfcUtil::IfcBaseEntity*)i)->get_value("Name"); + auto g1 = n.substr(0, 22); + auto g2 = n.substr(23); + auto item = c.mapping()->map(i); + auto shell = (taxonomy::shell*) ((taxonomy::collection*)((taxonomy::collection*) item)->children[0])->children[0]; + for (auto& f : shell->children) { + auto face = (taxonomy::face*) f; + for (auto& w : face->children) { + auto wire = (taxonomy::loop*) w; + for (auto& e : wire->children) { + auto edge = (taxonomy::edge*) e; + auto p3 = boost::get(edge->start); + auto p4 = ((taxonomy::geom_item*)item)->matrix.components * p3.components.homogeneous(); + Kernel_::Point_3 P(p4(0), p4(1), p4(2)); + elem_to_space_boundary_coords[{g1, g2}].emplace_back(P); + } + } + } + } - v([](const intersection_validator::Box& a, const intersection_validator::Box& b) { + v([&rel_by_space_elem, &elem_to_space_boundary_coords](const intersection_validator::Box& a, const intersection_validator::Box& b) { std::ostringstream ss; // ss << id_map[a.id()]->first->data().toString() << "x" << id_map[b.id()]->first->data().toString() << std::endl; // auto x = id_map[a.id()]->second * id_map[b.id()]->second; - ss << a.handle()->first->data().toString() << "x" << a.handle()->first->data().toString() << std::endl; - auto x = a.handle()->second * b.handle()->second; - cgal_shape_t x_poly; - x.convert_to_polyhedron(x_poly); + auto A = a.handle()->first; + auto B = b.handle()->first; - CGAL::Polygon_mesh_processing::triangulate_faces(x_poly); + auto Aguid = A->get_value("GlobalId"); + auto Bguid = B->get_value("GlobalId"); - auto vs = vertices(x_poly); - if (std::distance(vs.begin(), vs.end()) == 0) { + int space_count = 0; + if (A->declaration().name() == "IfcSpace") { + space_count += 1; + } + if (B->declaration().name() == "IfcSpace") { + space_count += 1; + } + if (space_count != 1) { return; } + ss << a.handle()->first->data().toString() << "x" << a.handle()->first->data().toString() << std::endl; + auto x = a.handle()->second * b.handle()->second; + + if (x.is_empty()) { + // std::wcout << "empty" << std::endl; + return; + } + + cgal_shape_t x_poly; + x.convert_to_polyhedron(x_poly); + auto s0 = a.handle()->first->declaration().name(); auto s1 = b.handle()->first->declaration().name(); auto i0 = a.handle()->first->data().id(); @@ -30,6 +116,16 @@ void fix_spaceboundaries(IfcParse::IfcFile& f, bool no_progress, bool quiet, boo std::swap(i0, i1); } + Tree tree(faces(x_poly).first, faces(x_poly).second, x_poly); + tree.accelerate_distance_queries(); + + for (auto& p : elem_to_space_boundary_coords[{Aguid, Bguid}]) { + auto d = std::sqrt(CGAL::to_double(tree.squared_distance(p))); + std::wcout << d << std::endl; + } + + return; + { auto FN = s0 + "-" + s1 + "-" + std::to_string(i0) + "-" + std::to_string(i1) + "sb.off"; diff --git a/src/ifcconvert/validate_storey_containment.cpp b/src/ifcconvert/validate_storey_containment.cpp index 53bbe95914..c6c4564c16 100644 --- a/src/ifcconvert/validate_storey_containment.cpp +++ b/src/ifcconvert/validate_storey_containment.cpp @@ -46,12 +46,14 @@ void fix_storeycontainment(IfcParse::IfcFile& f, bool no_progress, bool quiet, b return get_elevation(a) < get_elevation(b); }); + /* std::wcout << "Storeys "; for (auto& s : storeys_sorted) { auto n = ((IfcUtil::IfcBaseEntity*)s)->get_value("Name"); std::wcout << n.c_str() << " "; } std::wcout << std::endl; + */ std::vector elevations; std::transform(storeys_sorted.begin(), storeys_sorted.end(), std::back_inserter(elevations), get_elevation); @@ -69,13 +71,14 @@ void fix_storeycontainment(IfcParse::IfcFile& f, bool no_progress, bool quiet, b std::vector> nefs; std::transform(elevation_slices.begin(), elevation_slices.end(), std::back_inserter(nefs), [&LARGE](const std::pair& p) { - std::wcout << p.first << " - " << p.second << std::endl; + // std::wcout << p.first << " - " << p.second << std::endl; Kernel_::Point_3 p1(-LARGE, -LARGE, p.first); Kernel_::Point_3 p2(+LARGE, +LARGE, p.second); auto poly = ifcopenshell::geometry::utils::create_cube(p1, p2); return ifcopenshell::geometry::utils::create_nef_polyhedron(poly); }); + /* for (auto& n : nefs) { auto poly = ifcopenshell::geometry::utils::create_polyhedron(n); auto bounds = CGAL::Polygon_mesh_processing::bbox_3(poly); @@ -87,6 +90,7 @@ void fix_storeycontainment(IfcParse::IfcFile& f, bool no_progress, bool quiet, b } std::wcout << "---" << std::endl; } + */ if (!context_iterator.initialize()) { return; @@ -108,13 +112,15 @@ void fix_storeycontainment(IfcParse::IfcFile& f, bool no_progress, bool quiet, b break; } + /* std::stringstream ss; ss << geom_object->product()->data().toString(); auto sss = ss.str(); std::wcout << sss.c_str() << std::endl; + */ if (elem_to_storey.find(geom_object->product()) == elem_to_storey.end()) { - std::wcout << "not associated to storey" << std::endl; + // std::wcout << "not associated to storey" << std::endl; continue; } @@ -140,6 +146,7 @@ void fix_storeycontainment(IfcParse::IfcFile& f, bool no_progress, bool quiet, b vertex->point() = vertex->point().transform(trsf).transform(trsf2); } + /* { auto bounds = CGAL::Polygon_mesh_processing::bbox_3(s); for (int i = 0; i < 3; ++i) { @@ -150,11 +157,12 @@ void fix_storeycontainment(IfcParse::IfcFile& f, bool no_progress, bool quiet, b } std::wcout << "---" << std::endl; } + */ CGAL::Nef_polyhedron_3 part_nef = ifcopenshell::geometry::utils::create_nef_polyhedron(s); if (!part_nef.is_simple()) { - std::wcout << "not simple" << std::endl; + // std::wcout << "not simple" << std::endl; continue; } @@ -166,18 +174,20 @@ void fix_storeycontainment(IfcParse::IfcFile& f, bool no_progress, bool quiet, b }); } + /* std::wcout << "volumes: "; for (auto& v : intersection_volumes) { std::wcout << v << " "; } std::wcout << std::endl; + */ auto idx = std::max_element(intersection_volumes.begin(), intersection_volumes.end()) - intersection_volumes.begin(); if (storeys_sorted[idx] != elem_to_storey[geom_object->product()]) { - auto s = geom_object->product()->data().toString(); - auto s1 = storeys_sorted[idx]->data().toString(); - auto s2 = elem_to_storey[geom_object->product()]->data().toString(); - std::wcout << "Mismatch on " << s.c_str() << ": " << s1.c_str() << " vs " << s2.c_str() << std::endl; + auto s = geom_object->product()->get_value("GlobalId"); + auto s1 = ((IfcUtil::IfcBaseEntity*)storeys_sorted[idx])->get_value("GlobalId"); + auto s2 = ((IfcUtil::IfcBaseEntity*)elem_to_storey[geom_object->product()])->get_value("GlobalId"); + Logger::Error("Element " + s + " contained in " + s2 + " located on " + s1); } if (!no_progress) { diff --git a/src/ifcconvert/validate_wall_connectivity.cpp b/src/ifcconvert/validate_wall_connectivity.cpp index df522845c2..ef7aedbfe4 100644 --- a/src/ifcconvert/validate_wall_connectivity.cpp +++ b/src/ifcconvert/validate_wall_connectivity.cpp @@ -1,11 +1,14 @@ #include "validation_utils.h" +#include +#include + #include using namespace ifcopenshell::geometry; void fix_wallconnectivity(IfcParse::IfcFile& f, bool no_progress, bool quiet, bool stderr_progress) { - intersection_validator v(f, { "IfcWall" }, no_progress, quiet, stderr_progress); + intersection_validator v(f, { "IfcWall" }, 1.e-3, no_progress, quiet, stderr_progress); ifcopenshell::geometry::settings settings; @@ -31,7 +34,10 @@ void fix_wallconnectivity(IfcParse::IfcFile& f, bool no_progress, bool quiet, bo std::set rels_encounted; - v([&c, &rel_by_elem, &rels_encounted](const intersection_validator::Box& a, const intersection_validator::Box& b) { + double total_nef_intersection_time = 0.; + double conversion_to_poly = 0.; + + v([&c, &rel_by_elem, &rels_encounted, &total_nef_intersection_time, &conversion_to_poly](const intersection_validator::Box& a, const intersection_validator::Box& b) { auto A = a.handle()->first; auto B = b.handle()->first; @@ -72,13 +78,33 @@ void fix_wallconnectivity(IfcParse::IfcFile& f, bool no_progress, bool quiet, bo std::ostringstream ss; ss << A->data().toString() << "x" << B->data().toString() << std::endl; + std::clock_t intersection_begin = std::clock(); auto x = a.handle()->second * b.handle()->second; + std::clock_t intersection_end = std::clock(); + + total_nef_intersection_time += (intersection_end - intersection_begin) / (double) CLOCKS_PER_SEC; + if (x.is_empty()) { return; } + std::clock_t poly_begin = std::clock(); cgal_shape_t x_poly; x.convert_to_polyhedron(x_poly); + std::clock_t poly_end = std::clock(); + conversion_to_poly += (poly_end - poly_begin) / (double)CLOCKS_PER_SEC; + + auto dza = a.bbox().zmax() - a.bbox().zmin(); + auto dzb = b.bbox().zmax() - b.bbox().zmin(); + auto bb = CGAL::Polygon_mesh_processing::bbox_3(x_poly); + if (bb.zmax() - bb.zmin() < std::min(dza, dzb) / 3.) { + return; + } + + CGAL::Polygon_mesh_processing::triangulate_faces(x_poly); + if (CGAL::Polygon_mesh_processing::area(x_poly) > 2.0) { + return; + } auto get_axis_parameter_min_max = [&c, &x_poly](IfcUtil::IfcBaseEntity* inst) { auto item = c.mapping()->map(inst); @@ -86,13 +112,13 @@ void fix_wallconnectivity(IfcParse::IfcFile& f, bool no_progress, bool quiet, bo auto loop = ((taxonomy::collection*) shaperep)->children[0]; if (loop->kind() != taxonomy::LOOP) { - std::wcout << "no suitable axis" << std::endl; + // std::wcout << "no suitable axis" << std::endl; } else { auto first_vertex = ((taxonomy::edge*) ((taxonomy::loop*) loop)->children.front())->start; auto last_vertex = ((taxonomy::edge*) ((taxonomy::loop*) loop)->children.back())->end; if (first_vertex.which() != 0 || last_vertex.which() != 0) { - std::wcout << "trims not supported" << std::endl; + // std::wcout << "trims not supported" << std::endl; } else { auto p0 = boost::get(first_vertex); auto p1 = boost::get(last_vertex); @@ -114,7 +140,7 @@ void fix_wallconnectivity(IfcParse::IfcFile& f, bool no_progress, bool quiet, bo }); auto pit = std::minmax_element(parameters.begin(), parameters.end()); - return std::make_pair(len, std::make_pair(CGAL::to_double(*pit.first), CGAL::to_double(*pit.first))); + return std::make_pair(len, std::make_pair(CGAL::to_double(*pit.first), CGAL::to_double(*pit.second))); } } const auto& nan = std::numeric_limits::quiet_NaN(); @@ -122,9 +148,9 @@ void fix_wallconnectivity(IfcParse::IfcFile& f, bool no_progress, bool quiet, bo }; auto qualify_connection_type = [](double l, const std::pair& p) { - if (p.first < 1.e-5) { + if (p.first < 1.e-3) { return "ATSTART"; - } else if (p.second > l - 1.e-5) { + } else if (p.second > l - 1.e-3) { return "ATEND"; } else { return "ATPATH"; @@ -141,21 +167,32 @@ void fix_wallconnectivity(IfcParse::IfcFile& f, bool no_progress, bool quiet, bo if (a_type != atype_computed || b_type != btype_computed) { if (rel) { - auto rel_str = rel->data().toString(); - std::wcout << "ERROR: " << rel_str.c_str() << " " << atype_computed << " " << btype_computed << std::endl; + Logger::Error(std::string("Connection type ") + atype_computed + " " + btype_computed + " for:", rel); } else { - auto A_str = A->data().toString(); - auto B_str = B->data().toString(); - std::wcout << "ERROR: no rel " << A_str.c_str() << " x " << B_str.c_str() << " " << atype_computed << " " << btype_computed << std::endl; + auto A_str = A->get_value("GlobalId"); + auto B_str = B->get_value("GlobalId"); + Logger::Error("No connection for adjacent " + A_str + " " + B_str); } } }); - std::for_each(rels->begin(), rels->end(), [&rels_encounted](const IfcUtil::IfcBaseClass* rel) { + std::for_each(rels->begin(), rels->end(), [&rels_encounted, &v](const IfcUtil::IfcBaseClass* rel) { if (rels_encounted.find(rel) == rels_encounted.end()) { - auto rel_str = rel->data().toString(); - std::wcout << "ERROR: " << rel_str.c_str() << " not found" << std::endl; + auto x = (IfcUtil::IfcBaseEntity*)((IfcUtil::IfcBaseEntity*)rel)->get_value("RelatingElement"); + auto y = (IfcUtil::IfcBaseEntity*)((IfcUtil::IfcBaseEntity*)rel)->get_value("RelatedElement"); + if (v.succesfully_processed.find(x) != v.succesfully_processed.end() && v.succesfully_processed.find(y) != v.succesfully_processed.end()) { + Logger::Error("Connection for non-adjacent walls", rel); + } } }); + + std::wcout << std::setprecision(14); + std::wcout << "total_map_time " << v.total_map_time << std::endl; + std::wcout << "total_geom_time " << v.total_geom_time << std::endl; + std::wcout << "total_nef_time " << v.total_nef_time << std::endl; + std::wcout << "total_minkowsky_time " << v.total_minkowsky_time << std::endl; + std::wcout << "total_box_time " << v.total_box_time << std::endl; + std::wcout << "total_nef_intersection_time " << total_nef_intersection_time << std::endl; + std::wcout << "total_conversion_to_poly_time " << conversion_to_poly << std::endl; } diff --git a/src/ifcconvert/validation_utils.h b/src/ifcconvert/validation_utils.h index e3c12e140f..42f367b520 100644 --- a/src/ifcconvert/validation_utils.h +++ b/src/ifcconvert/validation_utils.h @@ -421,7 +421,15 @@ struct intersection_validator { std::vector boxes; nefs_t nefs; - intersection_validator(IfcParse::IfcFile& f, std::initializer_list entities, bool no_progress, bool quiet, bool stderr_progress) { + double total_map_time = 0.; + double total_geom_time = 0.; + double total_nef_time = 0.; + double total_minkowsky_time = 0.; + double total_box_time = 0.; + + std::set succesfully_processed; + + intersection_validator(IfcParse::IfcFile& f, std::initializer_list entities, double eps, bool no_progress, bool quiet, bool stderr_progress) { ifcopenshell::geometry::settings settings; settings.set(ifcopenshell::geometry::settings::USE_WORLD_COORDS, false); @@ -441,8 +449,7 @@ struct intersection_validator { return; } - auto kernel = (ifcopenshell::geometry::kernels::CgalKernel*) context_iterator.converter().kernel(); - auto cube = kernel->precision_cube(); + auto cube = ifcopenshell::geometry::utils::create_nef_polyhedron(ifcopenshell::geometry::utils::create_cube(eps)); size_t num_created = 0; int old_progress = quiet ? 0 : -1; @@ -485,13 +492,21 @@ struct intersection_validator { vertex->point() = vertex->point().transform(trsf).transform(trsf2); } + std::clock_t nef_begin = std::clock(); CGAL::Nef_polyhedron_3 nef = ifcopenshell::geometry::utils::create_nef_polyhedron(s); + std::clock_t nef_end = std::clock(); + total_nef_time += (nef_end - nef_begin) / (double) CLOCKS_PER_SEC; if (nef.is_empty()) { std::wcout << "Failed to create nef" << std::endl; continue; } + succesfully_processed.insert(geom_object->product()); + nef = CGAL::minkowski_sum_3(nef, cube); + std::clock_t minkowski_end = std::clock(); + total_minkowsky_time += (minkowski_end - nef_end) / (double) CLOCKS_PER_SEC; + std::wcout << "product: " << geom_object->product() << std::endl; nefs.push_back({ geom_object->product(), nef }); @@ -550,10 +565,16 @@ struct intersection_validator { " objects "); } + total_geom_time = context_iterator.converter().total_geom_time; + total_map_time = context_iterator.converter().total_map_time; } template void operator()(Fn fn) { + std::clock_t box_overlap_begin = std::clock(); + CGAL::box_self_intersection_d(boxes.begin(), boxes.end(), [](auto& x, auto& y) {}); + std::clock_t box_overlap_end = std::clock(); + total_box_time += (box_overlap_end - box_overlap_begin) / (double) CLOCKS_PER_SEC; CGAL::box_self_intersection_d(boxes.begin(), boxes.end(), fn); } }; diff --git a/src/ifcgeom/schema_agnostic/Converter.cpp b/src/ifcgeom/schema_agnostic/Converter.cpp index 0a866e35a2..148aa6cdfe 100644 --- a/src/ifcgeom/schema_agnostic/Converter.cpp +++ b/src/ifcgeom/schema_agnostic/Converter.cpp @@ -44,11 +44,16 @@ ifcopenshell::geometry::NativeElement* ifcopenshell::geometry::Converter::create } */ + + std::clock_t map_start = std::clock(); + // @todo how to combine product_node and rep_item? auto product_node = (taxonomy::geom_item*) mapping_->map(product); if (product_node == nullptr) { return nullptr; } + + std::clock_t geom_start = std::clock(); auto place = taxonomy::matrix4(); std::swap(place, product_node->matrix); @@ -57,6 +62,11 @@ ifcopenshell::geometry::NativeElement* ifcopenshell::geometry::Converter::create shape = new ifcopenshell::geometry::Representation::BRep(s, representation_id_builder.str(), shapes); + std::clock_t geom_end = std::clock(); + + total_map_time += (geom_start - map_start) / (double) CLOCKS_PER_SEC; + total_geom_time += (geom_end - geom_start) / (double) CLOCKS_PER_SEC; + return new NativeElement( product->data().id(), parent_id, diff --git a/src/ifcgeom/schema_agnostic/Converter.h b/src/ifcgeom/schema_agnostic/Converter.h index 1f28e284fe..310bdc5785 100644 --- a/src/ifcgeom/schema_agnostic/Converter.h +++ b/src/ifcgeom/schema_agnostic/Converter.h @@ -76,10 +76,18 @@ namespace ifcopenshell { namespace geometry { } */ + double total_map_time = 0.; + double total_geom_time = 0.; + ifcopenshell::geometry::ConversionResults convert(IfcUtil::IfcBaseClass* item) { + std::clock_t map_start = std::clock(); auto geom_item = mapping_->map(item); + std::clock_t geom_start = std::clock(); ifcopenshell::geometry::ConversionResults results; kernel_->convert(geom_item, results); + std::clock_t geom_end = std::clock(); + total_map_time += (geom_start - map_start) / (double) CLOCKS_PER_SEC; + total_geom_time += (geom_end - geom_start) / (double) CLOCKS_PER_SEC; return results; }