diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 9a3807d837..89b6cbbbe2 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -104,7 +104,6 @@ option(HDF5_SUPPORT "Enable HDF5 support (requires HDF5, zlib)" ON) option(WITH_PROJ "Enable output of Earth-Centered Earth-Fixed glTF output using the PROJ library" OFF) option(IFCXML_SUPPORT "Build IfcParse with ifcXML support (requires libxml2)." ON) option(USD_SUPPORT "Build IfcConvert with USD support (requires pixar's USD library)." OFF) -option(WITH_RELATIONSHIP_VALIDATION "Build IfcConvert with option to validate geometrical relationships." OFF) option(WITH_ROCKSDB "Support a RocksDB key-value store as a file backend in IfcOpenShell" OFF) option(WITH_ZSTD "Use Zstd compression in RocksDB writes" OFF) diff --git a/src/ifcconvert/CMakeLists.txt b/src/ifcconvert/CMakeLists.txt index af46bc7dfe..5e378ba2a1 100644 --- a/src/ifcconvert/CMakeLists.txt +++ b/src/ifcconvert/CMakeLists.txt @@ -1,13 +1,5 @@ # IfcConvert -if(WITH_RELATIONSHIP_VALIDATION) - file(GLOB IFCCONVERT_CPP_FILES *.cpp) - file(GLOB IFCCONVERT_H_FILES *.h) -else() - file(GLOB IFCCONVERT_CPP_FILES IfcConvert.cpp) - file(GLOB IFCCONVERT_H_FILES) -endif() -set(IFCCONVERT_FILES ${IFCCONVERT_CPP_FILES} ${IFCCONVERT_H_FILES}) -add_executable(IfcConvert ${IFCCONVERT_FILES}) +add_executable(IfcConvert IfcConvert.cpp) target_link_libraries( IfcConvert diff --git a/src/ifcconvert/validate_space_boundaries.cpp b/src/ifcconvert/validate_space_boundaries.cpp deleted file mode 100644 index 6572d1d659..0000000000 --- a/src/ifcconvert/validate_space_boundaries.cpp +++ /dev/null @@ -1,186 +0,0 @@ -#ifdef IFOPSH_WITH_CGAL - -#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, Logger& logger = Logger::Root()) { - intersection_validator v(f, { "IfcWall", "IfcSpace", "IfcSlab", "IfcCovering" }, 1.e-5, no_progress, quiet, stderr_progress, logger); - - 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"); - if (!f2.good()) { - return; - } - - ifcopenshell::geometry::Settings settings; - - settings.get().value = false; - settings.get().value = false; - settings.get().value = true; - settings.get().value = true; - settings.get().value = ifcopenshell::geometry::settings::NATIVE; - settings.get().value = true; - - ifcopenshell::geometry::Converter c(ifcopenshell::geometry::kernels::construct(&f2, "cgal", settings, logger), &f2, settings, logger); - - 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); - if (taxonomy::cast(item)->children[0] == nullptr) { - continue; - } - auto shell = taxonomy::cast(taxonomy::cast(taxonomy::cast(item)->children[0])->children[0]); - for (auto& face : shell->children) { - for (auto& wire : face->children) { - for (auto& edge : wire->children) { - auto p3 = boost::get(edge->start); - auto p4 = taxonomy::cast(item)->matrix->ccomponents() * p3->ccomponents().homogeneous(); - Kernel_::Point_3 P(p4(0), p4(1), p4(2)); - elem_to_space_boundary_coords[{g1, g2}].emplace_back(P); - } - } - } - } - - std::set< std::set > guid_pairs_visited; - - v([&logger, &rel_by_space_elem, &elem_to_space_boundary_coords, &guid_pairs_visited](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; - - auto A = a.handle()->first; - auto B = b.handle()->first; - - auto Aguid = A->get_value("GlobalId"); - auto Bguid = B->get_value("GlobalId"); - - 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()) { - return; - } - - guid_pairs_visited.insert({ Aguid, Bguid }); - - cgal_shape_t x_poly; - x.convert_to_polyhedron(x_poly); - - { - std::string fn = "computed_boundaries_" + Aguid + "_" + Bguid + ".off"; - std::ofstream computed_boundaries(fn.c_str()); - computed_boundaries.precision(17); - computed_boundaries << x_poly; - } - - Tree tree(faces(x_poly).first, faces(x_poly).second, x_poly); - tree.accelerate_distance_queries(); - - auto itelem = elem_to_space_boundary_coords.find({ Aguid, Bguid }); - - if (itelem == elem_to_space_boundary_coords.end()) { - logger.Error("VAL", 1, "Missing space boundary relationship " + Aguid + " " + Bguid); - return; - } - - const auto& coords = itelem->second; - std::vector distances; - std::transform(coords.begin(), coords.end(), std::back_inserter(distances), [&tree](const Kernel_::Point_3& p) { - return std::sqrt(CGAL::to_double(tree.squared_distance(p))); - }); - - bool valid = *std::max_element(distances.begin(), distances.end()) < 0.4; - - if (!valid) { - logger.Error("VAL", 2, "Wrong connection geometry " + Aguid + " " + Bguid); - } - - /*{ - remove_thickness r(x_poly); - std::string fn = "thin_computed_boundaries_" + Aguid + "_" + Bguid + ".off"; - std::ofstream computed_boundaries(fn.c_str()); - computed_boundaries.precision(17); - computed_boundaries << r.flattened; - }*/ - - /* - { - auto FN = s0 + "-" + s1 + "-" + std::to_string(i0) + "-" + std::to_string(i1) + "-sides-sb.off"; - std::ofstream os(FN.c_str()); - os.precision(17); - os << r.polyhedron2; - } - { - auto FN = s0 + "-" + s1 + "-" + std::to_string(i0) + "-" + std::to_string(i1) + "-flat-sb.off"; - std::ofstream os(FN.c_str()); - os.precision(17); - os << r.flattened; - } - */ - }); - - auto is_wall_space_or_slab = [&f](const std::string& g) { - auto decl = f.instance_by_guid(g)->declaration(); - return decl.is("IfcWall") || decl.is("IfcSpace") || decl.is("IfcSlab"); - }; - - 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); - if (is_wall_space_or_slab(g1) && is_wall_space_or_slab(g2) && guid_pairs_visited.find({ g1, g2 }) == guid_pairs_visited.end()) { - logger.Error("VAL", 3, "Space boundary for non-bounding geometry " + g1 + " " + g2); - } - } -} - -#endif diff --git a/src/ifcconvert/validate_storey_containment.cpp b/src/ifcconvert/validate_storey_containment.cpp deleted file mode 100644 index 7c40cce60a..0000000000 --- a/src/ifcconvert/validate_storey_containment.cpp +++ /dev/null @@ -1,238 +0,0 @@ -#ifdef IFOPSH_WITH_CGAL - -#include "../ifcgeom/kernels/cgal/CgalKernel.h" -#include "../ifcgeom/IfcGeomFilter.h" -#include "../ifcgeom/Iterator.h" - -#include -#include - -#include - -void fix_storeycontainment(IfcParse::IfcFile& f, bool no_progress, bool quiet, bool stderr_progress, Logger& logger = Logger::Root()) { - ifcopenshell::geometry::Settings settings; - - settings.get().value = false; - settings.get().value = false; - settings.get().value = true; - settings.get().value = true; - settings.get().value = ifcopenshell::geometry::settings::NATIVE; - settings.get().value = true; - - std::vector no_openings_and_spaces = { - IfcGeom::entity_filter(false, false, {"IfcOpeningElement", "IfcSpace"}) - }; - - IfcGeom::Iterator context_iterator(ifcopenshell::geometry::kernels::construct(&f, "cgal", settings, logger), settings, &f, no_openings_and_spaces, 1, logger); - - auto get_elevation = [](const IfcUtil::IfcBaseClass* a) { - return ((const IfcUtil::IfcBaseEntity*)a)->get_value("Elevation", 0.); - }; - - // latebound inverse attribute lookup not working - auto rels = f.instances_by_type("IfcRelContainedInSpatialStructure"); - std::map elem_to_storey; - std::for_each(rels->begin(), rels->end(), [&elem_to_storey](IfcUtil::IfcBaseClass* r) { - auto elems = ((IfcUtil::IfcBaseEntity*)r)->get_value("RelatedElements"); - auto storey = ((IfcUtil::IfcBaseEntity*)r)->get_value("RelatingStructure"); - - if (storey->declaration().name() == "IfcBuildingStorey") { - for (auto it = elems->begin(); it != elems->end(); ++it) { - elem_to_storey[*it] = storey; - } - } - }); - - auto storeys = f.instances_by_type("IfcBuildingStorey"); - std::vector storeys_sorted(storeys->begin(), storeys->end()); - std::sort(storeys_sorted.begin(), storeys_sorted.end(), [&get_elevation](const IfcUtil::IfcBaseClass* a, const IfcUtil::IfcBaseClass* 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); - - double LARGE = 1e4; - - std::vector> elevation_slices; - for (size_t i = 0; i < elevations.size(); ++i) { - elevation_slices.push_back({ - i == 0 ? -LARGE : elevations[i], - i + 1 == elevations.size() ? LARGE : elevations[i + 1] - }); - } - - std::for_each(elevation_slices.begin(), elevation_slices.end(), [](std::pair& p) { - p.first -= 0.3; - p.second += 0.3; - }); - - 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; - 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); - for (int i = 0; i < 3; ++i) { - std::wcout << bounds.min(i) << std::endl; - } - for (int i = 0; i < 3; ++i) { - std::wcout << bounds.max(i) << std::endl; - } - std::wcout << "---" << std::endl; - } - */ - - if (!context_iterator.initialize()) { - return; - } - - size_t num_created = 0; - int old_progress = quiet ? 0 : -1; - - 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) { - 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; - continue; - } - - std::vector intersection_volumes(nefs.size()); - - for (auto& g : geom_object->geometry()) { - auto s = std::static_pointer_cast(g.Shape())->poly(); - const auto& m = g.Placement()->ccomponents(); - const auto& n = geom_object->transformation().data()->ccomponents(); - - const cgal_placement_t trsf( - m(0, 0), m(0, 1), m(0, 2), m(0, 3), - m(1, 0), m(1, 1), m(1, 2), m(1, 3), - m(2, 0), m(2, 1), m(2, 2), m(2, 3)); - - const cgal_placement_t trsf2( - n(0, 0), n(0, 1), n(0, 2), n(0, 3), - n(1, 0), n(1, 1), n(1, 2), n(1, 3), - n(2, 0), n(2, 1), n(2, 2), n(2, 3)); - - // Apply transformation - for (auto &vertex : vertices(s)) { - vertex->point() = vertex->point().transform(trsf).transform(trsf2); - } - - /* - { - auto bounds = CGAL::Polygon_mesh_processing::bbox_3(s); - for (int i = 0; i < 3; ++i) { - std::wcout << bounds.min(i) << std::endl; - } - for (int i = 0; i < 3; ++i) { - std::wcout << bounds.max(i) << std::endl; - } - 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; - continue; - } - - std::vector::iterator accumulator = intersection_volumes.begin(); - std::for_each(nefs.begin(), nefs.end(), [&accumulator, &part_nef](const CGAL::Nef_polyhedron_3& storey_nef) { - auto poly = ifcopenshell::geometry::utils::create_polyhedron(part_nef * storey_nef); - CGAL::Polygon_mesh_processing::triangulate_faces(poly); - *accumulator += CGAL::to_double(CGAL::Polygon_mesh_processing::volume(poly)); - accumulator++; - }); - } - - /* - std::wcout << "volumes: "; - for (auto& v : intersection_volumes) { - std::wcout << v << " "; - } - std::wcout << std::endl; - */ - - auto calc_idx = std::max_element(intersection_volumes.begin(), intersection_volumes.end()) - intersection_volumes.begin(); - auto calc_overlap = intersection_volumes[calc_idx]; - auto assigned_idx = std::distance(storeys_sorted.begin(), std::find(storeys_sorted.begin(), storeys_sorted.end(), elem_to_storey[geom_object->product()])); - auto assigned_overlap = intersection_volumes[assigned_idx]; - if (calc_overlap > 0 && assigned_overlap < calc_overlap * 0.9) { - auto s = geom_object->product()->get_value("GlobalId"); - auto s1 = ((IfcUtil::IfcBaseEntity*)storeys_sorted[calc_idx])->get_value("GlobalId"); - auto s2 = ((IfcUtil::IfcBaseEntity*)elem_to_storey[geom_object->product()])->get_value("GlobalId"); - logger.Error("VAL", 4, "Element " + s + " contained in " + s2 + " located on " + s1); - } - - if (!no_progress) { - if (quiet) { - const int progress = context_iterator.progress(); - for (; old_progress < progress; ++old_progress) { - std::cout << "."; - if (stderr_progress) - std::cerr << "."; - } - std::cout << std::flush; - if (stderr_progress) - std::cerr << std::flush; - } else { - const int progress = context_iterator.progress() / 2; - if (old_progress != progress) logger.ProgressBar(progress); - old_progress = progress; - } - } - } - - if (!no_progress && quiet) { - for (; old_progress < 100; ++old_progress) { - std::cout << "."; - if (stderr_progress) - std::cerr << "."; - } - std::cout << std::flush; - if (stderr_progress) - std::cerr << std::flush; - } else { - logger.Status("\rDone fixing space boundaries for " + boost::lexical_cast(num_created) + - " objects "); - } -} - -#endif diff --git a/src/ifcconvert/validate_wall_connectivity.cpp b/src/ifcconvert/validate_wall_connectivity.cpp deleted file mode 100644 index 28e8849749..0000000000 --- a/src/ifcconvert/validate_wall_connectivity.cpp +++ /dev/null @@ -1,201 +0,0 @@ -#ifdef IFOPSH_WITH_CGAL - -#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, Logger& logger = Logger::Root()) { - intersection_validator v(f, { "IfcWall" }, 1.e-3, no_progress, quiet, stderr_progress, logger); - - ifcopenshell::geometry::Settings settings; - - settings.get().value = false; - settings.get().value = false; - settings.get().value = true; - settings.get().value = true; - settings.get().value = ifcopenshell::geometry::settings::NATIVE; - settings.get().value = true; - - settings.get().value = true; - settings.get().value = false; - - ifcopenshell::geometry::Converter c(ifcopenshell::geometry::kernels::construct(&f, "cgal", settings, logger), &f, settings, logger); - - auto rels = f.instances_by_type("IfcRelConnectsPathElements"); - std::map, const IfcUtil::IfcBaseClass*> rel_by_elem; - std::for_each(rels->begin(), rels->end(), [&rel_by_elem](const IfcUtil::IfcBaseClass* rel) { - auto x = ((IfcUtil::IfcBaseEntity*)rel)->get_value("RelatingElement"); - auto y = ((IfcUtil::IfcBaseEntity*)rel)->get_value("RelatedElement"); - rel_by_elem.insert({{ x,y }, rel}); - }); - - std::set rels_encounted; - - double total_nef_intersection_time = 0.; - double conversion_to_poly = 0.; - - v([&logger, &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; - - const IfcUtil::IfcBaseClass* rel = nullptr; - std::string a_type, b_type; - - auto rit = rel_by_elem.find({ A, B }); - if (rit != rel_by_elem.end()) { - rel = rit->second; - const bool a_is_relating = A == ((IfcUtil::IfcBaseEntity*)rel)->get_value("RelatingElement"); - a_type = ((IfcUtil::IfcBaseEntity*)rel)->get_value("RelatingConnectionType"); - b_type = ((IfcUtil::IfcBaseEntity*)rel)->get_value("RelatedConnectionType"); - if (!a_is_relating) { - std::swap(a_type, b_type); - } - } - -#if 0 - auto a_poly = ifcopenshell::geometry::utils::create_polyhedron(a.handle()->second); - auto b_poly = ifcopenshell::geometry::utils::create_polyhedron(b.handle()->second); - - std::wcout << "a" << std::endl; - for (auto& v : vertices(a_poly)) { - for (int i = 0; i < 3; ++i) { - std::wcout << CGAL::to_double(v->point().cartesian(i)) << " "; - } - std::wcout << std::endl; - } - - std::wcout << "b" << std::endl; - for (auto& v : vertices(b_poly)) { - for (int i = 0; i < 3; ++i) { - std::wcout << CGAL::to_double(v->point().cartesian(i)) << " "; - } - std::wcout << std::endl; - } -#endif - - 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(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) > 4.0) { - return; - } - - auto get_axis_parameter_min_max = [&c, &x_poly](const IfcUtil::IfcBaseEntity* inst) { - auto item = c.mapping()->map(inst); - auto shaperep = taxonomy::cast(item)->children[0]; - auto loop = taxonomy::dcast(taxonomy::cast(shaperep)->children[0]); - - if (!loop) { - // std::wcout << "no suitable axis" << std::endl; - } else { - auto first_vertex = loop->children.front()->start; - auto last_vertex = loop->children.back()->end; - - if (first_vertex.which() != 0 || last_vertex.which() != 0) { - // std::wcout << "trims not supported" << std::endl; - } else { - auto p0 = boost::get(first_vertex); - auto p1 = boost::get(last_vertex); - - auto v0 = taxonomy::cast(item)->matrix->ccomponents() * p0->ccomponents().homogeneous(); - auto v1 = taxonomy::cast(item)->matrix->ccomponents() * p1->ccomponents().homogeneous(); - - auto P0 = Kernel_::Point_3(v0(0), v0(1), v0(2)); - auto P1 = Kernel_::Point_3(v1(0), v1(1), v1(2)); - - auto D = P1 - P0; - auto len = std::sqrt(CGAL::to_double(D.squared_length())); - D /= len; - - std::vector parameters; - - std::transform(vertices(x_poly).begin(), vertices(x_poly).end(), std::back_inserter(parameters), [&P0, D](cgal_vertex_descriptor_t& v) { - return (v->point() - P0) * D; - }); - - 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.second))); - } - } - const auto& nan = std::numeric_limits::quiet_NaN(); - return std::make_pair(nan, std::make_pair(nan, nan)); - }; - - auto qualify_connection_type = [](double l, const std::pair& p) { - if (p.first < 1.e-3) { - return "ATSTART"; - } else if (p.second > l - 1.e-3) { - return "ATEND"; - } else { - return "ATPATH"; - } - }; - - auto alu0u1 = get_axis_parameter_min_max(A); - auto blu0u1 = get_axis_parameter_min_max(B); - - auto atype_computed = qualify_connection_type(alu0u1.first, alu0u1.second); - auto btype_computed = qualify_connection_type(blu0u1.first, blu0u1.second); - - rels_encounted.insert(rel); - - if (a_type != atype_computed || b_type != btype_computed) { - if (rel) { - logger.Error("VAL", 5, std::string("Connection type ") + atype_computed + " " + btype_computed + " for:", rel); - } else { - auto A_str = A->get_value("GlobalId"); - auto B_str = B->get_value("GlobalId"); - logger.Error("VAL", 6, "No connection for adjacent " + A_str + " " + B_str); - } - } - }); - - std::for_each(rels->begin(), rels->end(), [&logger, &rels_encounted, &v](const IfcUtil::IfcBaseClass* rel) { - if (rels_encounted.find(rel) == rels_encounted.end()) { - auto x = (IfcUtil::IfcBaseEntity*)((IfcUtil::IfcBaseEntity*)rel)->get_value("RelatingElement"); - auto y = (IfcUtil::IfcBaseEntity*)((IfcUtil::IfcBaseEntity*)rel)->get_value("RelatedElement"); - if (v.successfully_processed.find(x) != v.successfully_processed.end() && v.successfully_processed.find(y) != v.successfully_processed.end()) { - logger.Error("VAL", 7, "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; -} - -#endif diff --git a/src/ifcconvert/validation_utils.cpp b/src/ifcconvert/validation_utils.cpp deleted file mode 100644 index e4e591f853..0000000000 --- a/src/ifcconvert/validation_utils.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifdef IFOPSH_WITH_CGAL - -#include "validation_utils.h" - -double facet_area(const cgal_shape_t::Facet_handle& f) { - auto p0 = f->facet_begin()->vertex()->point(); - auto p1 = f->facet_begin()->next()->vertex()->point(); - auto p2 = f->facet_begin()->next()->next()->vertex()->point(); - return std::sqrt(CGAL::to_double(CGAL::cross_product(p0 - p1, p2 - p1).squared_length())); -} - -void dump_facet(const cgal_shape_t::Facet_handle& f) { - auto p0 = f->facet_begin()->vertex()->point(); - auto p1 = f->facet_begin()->next()->vertex()->point(); - auto p2 = f->facet_begin()->next()->next()->vertex()->point(); - auto V = CGAL::cross_product(p0 - p1, p2 - p1); - auto d = std::sqrt(CGAL::to_double(V.squared_length())); - if (d > 1.e-20) { - V /= d; - } - - std::ostringstream oss; - oss.precision(8); - oss << "Facet with area " << facet_area(f) << " and normal (" - << CGAL::to_double(V.cartesian(0)) << " " << CGAL::to_double(V.cartesian(1)) << " " - << CGAL::to_double(V.cartesian(2)) << ")"; - - auto osss = oss.str(); - std::wcout << osss.c_str() << std::endl; -} - -#endif diff --git a/src/ifcconvert/validation_utils.h b/src/ifcconvert/validation_utils.h deleted file mode 100644 index 5a98c07326..0000000000 --- a/src/ifcconvert/validation_utils.h +++ /dev/null @@ -1,603 +0,0 @@ -#ifdef IFOPSH_WITH_CGAL - -#include "../ifcgeom/kernels/cgal/CgalKernel.h" -#include "../ifcgeom/IfcGeomFilter.h" -#include "../ifcgeom/Iterator.h" -#include "../ifcgeom/hybrid_kernel.h" - -#include -#include - -#include -#if CGAL_VERSION_NR >= 1060000000 -#include -#else -#include -#endif -#include -#include - -#include -#include - -#if CGAL_VERSION_NR >= 1060000000 -#define variant_get std::get_if -#else -#define variant_get boost::get -#endif - -template -T enlarge(const T& t, double d = 1.e-5) { - typename T::NT min[3]; - typename T::NT max[3]; - for (int i = 0; i < t.dimension(); ++i) { - min[i] = t.min_coord(i) - d; - max[i] = t.max_coord(i) + d; - } - return T(min, max, t.handle()); -} - -template -struct Build_Offset : public CGAL::Modifier_base { - std::list input; - - void operator()(HDS& hds) { - // Postcondition: hds is a valid polyhedral surface. - CGAL::Polyhedron_incremental_builder_3 B(hds); - - int Nv = 0, Nf = 0; - for (auto& f : input) { - Nv += 3; - Nf += 1; - } - - B.begin_surface(Nv, Nf); - - for (auto& f : input) { - auto p0 = f->facet_begin()->vertex()->point(); - auto p1 = f->facet_begin()->next()->vertex()->point(); - auto p2 = f->facet_begin()->next()->next()->vertex()->point(); - - auto O = CGAL::centroid(p0, p1, p2); - - Kernel_::Point_3* p012[3] = { &p0, &p1, &p2 }; - for (int i = 0; i < 3; ++i) { - *p012[i] = CGAL::ORIGIN + (((*(p012[i])) - CGAL::ORIGIN) + ((*(p012[i])) - O)); - B.add_vertex(*p012[i]); - } - } - - Nv = 0; - for (int i = 0; i < Nf; ++i) { - B.begin_facet(); - B.add_vertex_to_facet(Nv++); - B.add_vertex_to_facet(Nv++); - B.add_vertex_to_facet(Nv++); - B.end_facet(); - } - - B.end_surface(); - } -}; - -template -std::list connected_faces(cgal_shape_t::Facet_handle f, const Ts& excluded) { - std::set fs = { f }; - - std::function process; - process = [&fs, &process, &excluded](cgal_shape_t::Facet_handle& f) { - cgal_shape_t::Halfedge_around_facet_circulator circ = f->facet_begin(), end(circ); - do { - auto ff = circ->opposite()->facet(); - if (excluded.find(ff) == excluded.end()) { - auto p = fs.insert(ff); - if (p.second) { - process(ff); - } - } - } while (++circ != end); - }; - - process(f); - return std::list(fs.begin(), fs.end()); -} - -template -struct Builder_With_Map : public CGAL::Modifier_base { - std::list input; - std::map mapping; - - void operator()(HDS& hds) { - // Postcondition: hds is a valid polyhedral surface. - CGAL::Polyhedron_incremental_builder_3 B(hds); - - std::set used_points; - - for (auto& f : input) { - cgal_shape_t::Halfedge_around_facet_circulator circ = f->facet_begin(), end(circ); - do { - auto P = circ->vertex()->point(); - auto it = mapping.find(P); - if (it == mapping.end()) { - std::wcout << "WARNING unprojected point :(" << std::endl; - } else { - P = it->second; - } - used_points.insert(P); - } while (++circ != end); - } - - B.begin_surface(used_points.size(), input.size()); - - for (auto& p : used_points) { - B.add_vertex(p); - } - - for (auto& f : input) { - B.begin_facet(); - cgal_shape_t::Halfedge_around_facet_circulator circ = f->facet_begin(), end(circ); - do { - auto P = circ->vertex()->point(); - auto it = mapping.find(P); - if (it == mapping.end()) { - std::wcout << "WARNING unprojected point :(" << std::endl; - } else { - P = it->second; - } - - auto jt = used_points.find(P); - if (jt == used_points.end()) { - throw std::runtime_error("Unable to map point"); - } - size_t idx = std::distance(used_points.begin(), jt); - std::wcout << "idx " << idx << std::endl; - B.add_vertex_to_facet(idx); - } while (++circ != end); - - B.end_facet(); - } - - B.end_surface(); - } -}; - -double facet_area(const cgal_shape_t::Facet_handle& f); - -void dump_facet(const cgal_shape_t::Facet_handle& f); - -struct remove_thickness { - typedef Kernel_::Point_3 Point; - typedef Kernel_::Plane_3 Plane; - typedef Kernel_::Vector_3 Vector; - typedef Kernel_::Segment_3 Segment; - typedef Kernel_::Ray_3 Ray; - - typedef CGAL::Polyhedron_3 Polyhedron; - typedef CGAL::AABB_face_graph_triangle_primitive Primitive; -#if CGAL_VERSION_NR >= 1060000000 - typedef CGAL::AABB_traits_3 AAbbTraits; -#else - typedef CGAL::AABB_traits AAbbTraits; -#endif - typedef CGAL::AABB_tree Tree; - typedef boost::optional::Type> Ray_intersection; - - cgal_shape_t polyhedron, polyhedron2, flattened; - - remove_thickness(const cgal_shape_t& p) - // edge_collapse(p) still does not work :( - : polyhedron(p) - , polyhedron2(p) { - CGAL::Polygon_mesh_processing::triangulate_faces(polyhedron); - CGAL::Polygon_mesh_processing::triangulate_faces(polyhedron2); - - std::list non_degenerate, degenerate, longitudinal; - std::set thin_sides; - - std::wcout << "ALL FACES:" << std::endl; - - for (auto& f : faces(polyhedron)) { - dump_facet(f); - if (facet_area(f) > 1.e-20) { - non_degenerate.push_back(f); - } else { - degenerate.push_front(f); - std::wcout << "Degenerate, area: " << facet_area(f) << std::endl; - } - } - - std::wcout << "NON DEGENERATE:" << std::endl; - for (auto& f : non_degenerate) { - dump_facet(f); - } - - cgal_shape_t enlarged_non_degenerate_triangles; - Build_Offset bo; - bo.input = non_degenerate; - enlarged_non_degenerate_triangles.delegate(bo); - - // @todo, first on non-enlarged faces, then on enlarged; to fix projection on concave surfaces where the enlarging operation shortens projection distances. - - Tree tree(faces(enlarged_non_degenerate_triangles).first, faces(enlarged_non_degenerate_triangles).second, enlarged_non_degenerate_triangles); - - std::map face_normals; - boost::associative_property_map> face_normals_map(face_normals); - CGAL::Polygon_mesh_processing::compute_face_normals(polyhedron, face_normals_map); - - for (auto& f : non_degenerate) { - auto O = CGAL::centroid( - f->facet_begin()->vertex()->point(), - f->facet_begin()->next()->vertex()->point(), - f->facet_begin()->next()->next()->vertex()->point() - ); - - Ray ray(O, -face_normals_map[f]); - - std::list intersections; - tree.all_intersections(ray, std::back_inserter(intersections)); - double N = std::numeric_limits::infinity(); - Point P; - for (auto& intersection : intersections) { - if (variant_get(&(intersection->first))) { - const Point* p = variant_get(&(intersection->first)); - const double d = std::sqrt(CGAL::to_double((*p - O).squared_length())); - if (d > 1.e-20 && d < N) { - N = d; - } - } - } - if (N != std::numeric_limits::infinity() && N > 1.e-4) { - thin_sides.insert(f); - } - } - - std::wcout << "THIN SIDES:" << std::endl; - for (auto& f : thin_sides) { - dump_facet(f); - } - - for (auto& f : non_degenerate) { - if (thin_sides.find(f) == thin_sides.end()) { - longitudinal.push_back(f); - } - } - - std::wcout << "LONGITUDONAL:" << std::endl; - for (auto& f : longitudinal) { - dump_facet(f); - } - - std::wcout << "faces " << faces(polyhedron).size() << "long " << longitudinal.size() << "thin " << thin_sides.size() << "non-degen " << non_degenerate.size() << std::endl; - - cgal_shape_t enlarged_indiv_triangles; - Build_Offset bo2; - bo2.input = longitudinal; - enlarged_indiv_triangles.delegate(bo2); - - { - std::ofstream ofs("enlarged.off"); - ofs.precision(17); - ofs << enlarged_indiv_triangles; - } - - Tree tree2(faces(enlarged_indiv_triangles).begin(), faces(enlarged_indiv_triangles).end(), enlarged_indiv_triangles); - - std::map new_points; - - for (Polyhedron::Facet_iterator fit = polyhedron.facets_begin(); - fit != polyhedron.facets_end(); - ++fit) { - if (CGAL::collinear( - fit->halfedge()->vertex()->point(), - fit->halfedge()->next()->vertex()->point(), - fit->halfedge()->opposite()->vertex()->point())) { - std::wcout << "degenerate triangle" << std::endl; - } - } - - for (auto& v : vertices(polyhedron)) { - auto O = v->point(); - - Kernel_::Vector_3 norm; - Kernel_::Vector_3 accum; - int count = 0; - CGAL::Face_around_target_circulator it(v->halfedge(), polyhedron), end(it); - do { - cgal_shape_t::Facet_handle fh = (*it)->halfedge()->facet(); - - auto jt = std::find(non_degenerate.begin(), non_degenerate.end(), fh); - std::wcout << "non degen: " << (jt != non_degenerate.end()) << std::endl; - auto kt = std::find(thin_sides.begin(), thin_sides.end(), fh); - std::wcout << "thin side: " << (kt != thin_sides.end()) << std::endl; - if (jt != non_degenerate.end() && kt == thin_sides.end()) { - // else degenerate, prevent div by zero, do not incorporate in vnorm. - // or else part of thin side - - auto p0 = (*it)->facet_begin()->vertex()->point(); - auto p1 = (*it)->facet_begin()->next()->vertex()->point(); - auto p2 = (*it)->facet_begin()->next()->next()->vertex()->point(); - - { - std::ostringstream oss; - oss.precision(8); - oss << "p0 " << p0.cartesian(0) << " " << p0.cartesian(1) << " " << p0.cartesian(2) << "\n"; - oss << "p1 " << p1.cartesian(0) << " " << p1.cartesian(1) << " " << p1.cartesian(2) << "\n"; - oss << "p2 " << p2.cartesian(0) << " " << p2.cartesian(1) << " " << p2.cartesian(2) << "\n"; - auto osss = oss.str(); - std::wcout << osss.c_str() << std::endl; - } - - auto fnorm = CGAL::cross_product(p0 - p1, p2 - p1); - fnorm /= std::sqrt(CGAL::to_double(fnorm.squared_length())); - - // const auto& fnorm = face_normals_map_2[*it]; - std::ostringstream oss; - oss.precision(8); - oss << fnorm.cartesian(0) << " " << fnorm.cartesian(1) << " " << fnorm.cartesian(2); - auto osss = oss.str(); - std::wcout << osss.c_str() << std::endl; - accum += fnorm; - - ++count; - } - - ++it; - } while (it != end); - - norm = accum / count; - std::wcout << "count " << count << std::endl; - - if (count == 0) { - // part of only degenerate or only thin sides - continue; - } - - // v->vertex_begin(); - Ray ray(O, norm); - std::ostringstream oss; - oss.precision(8); - oss << O << " -> " << norm; - auto osss = oss.str(); - std::wcout << osss.c_str() << std::endl; - - std::list intersections; - tree2.all_intersections(ray, std::back_inserter(intersections)); - double N = std::numeric_limits::infinity(); - Point P; - - bool used_intersection = false; - - if (intersections.size()) { - for (auto& intersection : intersections) { - if (variant_get(&(intersection->first))) { - const Point* p = variant_get(&(intersection->first)); - const double d = std::sqrt(CGAL::to_double((*p - O).squared_length())); - if (d < N && d > 1.e-20) { - N = d; - P = *p; - std::wcout << "intersection @ " << d << std::endl; - } - } - } - std::wcout << "-----------" << std::endl; - - // average the new point - new_points[O] = CGAL::ORIGIN + (((O - CGAL::ORIGIN) + (P - CGAL::ORIGIN))) / 2; - used_intersection = true; - } - - if (!used_intersection) { - std::wcout << "no intersection :(" << std::endl; - } - } - - auto thin_sides_degenerate = thin_sides; - thin_sides_degenerate.insert(degenerate.begin(), degenerate.end()); - - // @todo choose connected / connected_opposing based on largest combined area of facets? - - if (longitudinal.size() == 0) { - std::wcout << "no longitudinal faces detected :(" << std::endl; - return; - } - - auto connected = connected_faces(*longitudinal.begin(), thin_sides_degenerate); - decltype(connected) connected_opposing; - - for (auto& f : longitudinal) { - if (std::find(connected.begin(), connected.end(), f) == connected.end()) { - connected_opposing = connected_faces(f, thin_sides_degenerate); - - std::set longi(longitudinal.begin(), longitudinal.end()); - std::set both_sides(connected.begin(), connected.end()); - both_sides.insert(connected_opposing.begin(), connected_opposing.end()); - - if (longi == both_sides) { - std::wcout << "Facet connection functioning properly" << std::endl; - } else { - std::wcout << "Facet connection functioning incorrectly" << std::endl; - } - - break; - } - } - - Builder_With_Map b2; - b2.input = connected; - b2.mapping = new_points; - - flattened.delegate(b2); - } -}; - - -struct intersection_validator { - typedef std::list> > nefs_t; - typedef CGAL::Box_intersection_d::Box_with_handle_d Box; - - std::vector boxes; - nefs_t nefs; - - 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 successfully_processed; - - intersection_validator(IfcParse::IfcFile& f, std::initializer_list entities, double eps, bool no_progress, bool quiet, bool stderr_progress, Logger& logger = Logger::Root()) { - - ifcopenshell::geometry::Settings settings; - settings.get().value = false; - settings.get().value = false; - settings.get().value = true; - settings.get().value = true; - settings.get().value = ifcopenshell::geometry::settings::NATIVE; - settings.get().value = true; - - std::vector spaces_and_walls = { - IfcGeom::entity_filter(true, false, entities) - }; - - IfcGeom::Iterator context_iterator(ifcopenshell::geometry::kernels::construct(&f, "cgal", settings, logger), settings, &f, spaces_and_walls, 1, logger); - - if (!context_iterator.initialize()) { - return; - } - - auto polycube = ifcopenshell::geometry::utils::create_cube(eps); - auto cube = ifcopenshell::geometry::utils::create_nef_polyhedron(polycube); - - size_t num_created = 0; - int old_progress = quiet ? 0 : -1; - - 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) { - break; - } - - std::stringstream ss; - geom_object->product()->toString(ss); - auto sss = ss.str(); - std::wcout << sss.c_str() << std::endl; - - for (auto& g : geom_object->geometry()) { - cgal_shape_t s = *std::static_pointer_cast(g.Shape()); - const auto& m = g.Placement()->ccomponents(); - const auto& n = geom_object->transformation().data()->ccomponents(); - - const cgal_placement_t trsf( - m(0, 0), m(0, 1), m(0, 2), m(0, 3), - m(1, 0), m(1, 1), m(1, 2), m(1, 3), - m(2, 0), m(2, 1), m(2, 2), m(2, 3)); - - const cgal_placement_t trsf2( - n(0, 0), n(0, 1), n(0, 2), n(0, 3), - n(1, 0), n(1, 1), n(1, 2), n(1, 3), - n(2, 0), n(2, 1), n(2, 2), n(2, 3)); - - // Apply transformation - for (auto &vertex : vertices(s)) { - 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; - } - - successfully_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 }); - - Box b(&*(nefs.rbegin())); - // id_map[b.id()] = ; - - for (auto &vertex : vertices(s)) { - double p[3] = { - CGAL::to_double(vertex->point().cartesian(0)), - CGAL::to_double(vertex->point().cartesian(1)), - CGAL::to_double(vertex->point().cartesian(2)) - }; - b.extend(p); - } - - boxes.push_back(enlarge(b)); - - /* - std::ostringstream ss; - ss << geom_object->product()->data().toString() << std::endl << b.min_coord(0) << " - " << b.max_coord(0) << std::endl; - auto sss = ss.str(); - std::wcout << sss.c_str(); - */ - } - - if (!no_progress) { - if (quiet) { - const int progress = context_iterator.progress(); - for (; old_progress < progress; ++old_progress) { - std::cout << "."; - if (stderr_progress) - std::cerr << "."; - } - std::cout << std::flush; - if (stderr_progress) - std::cerr << std::flush; - } else { - const int progress = context_iterator.progress() / 2; - if (old_progress != progress) logger.ProgressBar(progress); - old_progress = progress; - } - } - } - - if (!no_progress && quiet) { - for (; old_progress < 100; ++old_progress) { - std::cout << "."; - if (stderr_progress) - std::cerr << "."; - } - std::cout << std::flush; - if (stderr_progress) - std::cerr << std::flush; - } else { - logger.Status("\rDone fixing space boundaries for " + boost::lexical_cast(num_created) + - " objects "); - } - - /* - // @todo - 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(), [](Box& x, Box& 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); - } -}; - -#endif