diff --git a/src/ifcgeom/kernels/cgal/CgalKernel.cpp b/src/ifcgeom/kernels/cgal/CgalKernel.cpp index 45b6270ec7..514d26f469 100644 --- a/src/ifcgeom/kernels/cgal/CgalKernel.cpp +++ b/src/ifcgeom/kernels/cgal/CgalKernel.cpp @@ -577,27 +577,14 @@ bool CgalKernel::convert_impl(const taxonomy::extrusion* extrusion, ifcopenshell return true; } -bool CgalKernel::convert(const taxonomy::extrusion* extrusion, cgal_shape_t &shape) { - const double& height = extrusion->depth; - if (height < precision_) { - Logger::Message(Logger::LOG_ERROR, "Non-positive extrusion height encountered for:", extrusion->instance); - return false; - } - - // Outer - cgal_face_t bottom_face; - if (!convert(&extrusion->basis, bottom_face)) { - return false; - } - // std::cout << "Face vertices: " << face.outer.size() << std::endl; - - auto fs = *extrusion->direction.components; - cgal_direction_t dir(fs(0), fs(1), fs(2)); - // std::cout << "Direction: " << dir << std::endl; - +bool CgalKernel::process_extrusion(const cgal_face_t& bottom_face, const taxonomy::direction3& direction, double height, cgal_shape_t& shape) { + std::list face_list; face_list.push_back(bottom_face); + auto& fs = *direction.components; + cgal_direction_t dir(fs(0), fs(1), fs(2)); + for (std::vector::const_iterator current_vertex = bottom_face.outer.begin(); current_vertex != bottom_face.outer.end(); ++current_vertex) { @@ -664,7 +651,7 @@ bool CgalKernel::convert(const taxonomy::extrusion* extrusion, cgal_shape_t &sha try { nef_shape -= utils::create_nef_polyhedron(face_list); } catch (...) { - Logger::Message(Logger::LOG_ERROR, "IfcExtrudedAreaSolid: cannot subtract opening for:", extrusion->instance); + Logger::Message(Logger::LOG_ERROR, "IfcExtrudedAreaSolid: cannot subtract opening for:"); return false; } } @@ -679,10 +666,24 @@ bool CgalKernel::convert(const taxonomy::extrusion* extrusion, cgal_shape_t &sha nef_shape.convert_to_polyhedron(shape); return true; } catch (...) { - Logger::Message(Logger::LOG_ERROR, "IfcExtrudedAreaSolid: cannot convert Nef to polyhedron for:", extrusion->instance); + Logger::Message(Logger::LOG_ERROR, "IfcExtrudedAreaSolid: cannot convert Nef to polyhedron for:"); + return false; + } +} + +bool CgalKernel::convert(const taxonomy::extrusion* extrusion, cgal_shape_t &shape) { + const double& height = extrusion->depth; + if (height < precision_) { + Logger::Message(Logger::LOG_ERROR, "Non-positive extrusion height encountered for:", extrusion->instance); return false; } + cgal_face_t bottom_face; + if (!convert(&extrusion->basis, bottom_face)) { + return false; + } + + return process_extrusion(bottom_face, extrusion->direction, extrusion->depth, shape); } CGAL::Polyhedron_3 ifcopenshell::geometry::utils::create_cube(double d) { @@ -879,13 +880,222 @@ namespace { } } +#include +#define add_condition(x) for(auto& op : ops) { if (!(x)) return false; } + +namespace { + CGAL::Polygon_2 loop_to_polygon_2(taxonomy::loop* loop) { + CGAL::Polygon_2 polygon; + auto edges = loop->children_as(); + for (auto& e : edges) { + auto& p = boost::get(e->start); + CGAL::Point_2 pnt((*p.components)(0), (*p.components)(1)); + polygon.push_back(pnt); + } + return polygon; + } + + CGAL::Polygon_2 wire_to_polygon_2(cgal_wire_t& w) { + CGAL::Polygon_2 polygon; + for (auto& p : w) { + CGAL::Point_2 pnt(p.cartesian(0), p.cartesian(1)); + polygon.push_back(pnt); + } + return polygon; + } +} + +bool CgalKernel::process_as_2d_polygon(const taxonomy::boolean_result* br, std::list>& loops, double& z0, double& z1) { + // @todo can also be for other boolean operations, just depth/matrix operands are different + if (br->operation != taxonomy::boolean_result::SUBTRACTION) { + return false; + } + + auto& ops = br->children; + + std::vector extrusions; + std::transform(ops.begin(), ops.end(), std::back_inserter(extrusions), [](taxonomy::item* op) { + taxonomy::extrusion* nptr = nullptr; + if (op->kind() != taxonomy::COLLECTION) return nptr; + auto cl = (taxonomy::collection*) op; + if ((cl)->children.size() != 1) return nptr; + if (cl->children[0]->kind() == taxonomy::COLLECTION) { + cl = (taxonomy::collection*) cl->children[0]; + if ((cl)->children.size() != 1) return nptr; + } + if (cl->children[0]->kind() != taxonomy::EXTRUSION) return nptr; + auto ex = (taxonomy::extrusion*) cl->children[0]; + return ex; + }); + + if (std::find(extrusions.begin(), extrusions.end(), nullptr) != extrusions.end()) { + return false; + } + + // op[i].matrix[2,0:3] = <0 0 1> + Eigen::Vector3d Z(0., 0., 1.); + if (std::find_if(extrusions.begin(), extrusions.end(), [&Z](taxonomy::extrusion* ex) { + auto& m = *ex->matrix.components; + return std::abs(1. - std::abs(m.col(2).head<3>().dot(Z))) > 1.e-5; + }) != extrusions.end()) { + return false; + } + + // | op[i].matrix[2,0:3] . op[i].direction | = 1 + if (std::find_if(extrusions.begin(), extrusions.end(), [](taxonomy::extrusion* ex) { + auto& d = *ex->direction.components; + auto& m = *ex->matrix.components; + return std::abs(1. - std::abs(m.col(2).head<3>().dot(d))) > 1.e-5; + }) != extrusions.end()) { + return false; + } + + // op[0].depth <= op[i..n].depth + const auto& op_0_depth = extrusions[0]->depth; + if (std::find_if(extrusions.begin() + 1, extrusions.end(), [&op_0_depth](taxonomy::extrusion* ex) { + return op_0_depth > ex->depth; + }) != extrusions.end()) { + return false; + } + + const auto& op_0_matrix_2_3 = (*extrusions[0]->matrix.components)(2, 3); + if (std::find_if(extrusions.begin() + 1, extrusions.end(), [&op_0_matrix_2_3](taxonomy::extrusion* ex) { + return op_0_matrix_2_3 < (*ex->matrix.components)(2, 3); + }) != extrusions.end()) { + return false; + } + + std::vector wires; + try { + std::transform(extrusions.begin(), extrusions.end(), std::back_inserter(wires), [this](taxonomy::extrusion* ex) { + if (ex->basis.children.size() == 1 && ex->basis.children[0]->kind() == taxonomy::LOOP) { + auto l = (taxonomy::loop*) ex->basis.children[0]; + cgal_wire_t w; + cgal_placement_t trsf; + convert_placement(ex->matrix, trsf); + if (convert(l, w)) { + for (auto& p : w) { + p = p.transform(trsf); + } + return w; + } + } + throw std::runtime_error("failed to convert to polygon"); + }); + } catch (std::runtime_error&) { + return false; + } + + for (auto it = wires.begin(); it != wires.end(); ++it) { + auto& w = *it; + auto op = (taxonomy::geom_item*) (*(ops.begin() + std::distance(wires.begin(), it))); + cgal_placement_t trsf; + convert_placement(op->matrix, trsf); + for (auto& p : w) { + p = trsf.transform(p); + } + } + + loops.clear(); + std::transform(wires.begin(), wires.end(), std::back_inserter(loops), wire_to_polygon_2); + + z0 = op_0_matrix_2_3; + z1 = z0 + extrusions[0]->depth * (*extrusions[0]->direction.components)(2); + + if (z1 < z0) { + std::swap(z0, z1); + } + + return true; +} + +#include +#include +#include +#include +#include bool CgalKernel::convert_impl(const taxonomy::boolean_result* br, ifcopenshell::geometry::ConversionResults& results) { + double z0, z1; + std::list> loops; + if (process_as_2d_polygon(br, loops, z0, z1)) { + auto first_item_style = ((taxonomy::geom_item*)br->children[0])->surface_style; + + std::list> pwhs; + + auto it = loops.begin(); + const auto& p = *it; + + CGAL::Polygon_with_holes_2 pwh(p, ++it, loops.end()); + CGAL::Gps_segment_traits_2 traits; + if (false && !CGAL::are_holes_and_boundary_pairwise_disjoint(pwh, traits)) { + // this is very slow. + // the check is also slow... + CGAL::Polygon_set_2 result; + auto it = loops.begin(); + result.insert(*it++); + for (; it != loops.end(); ++it) { + result.difference(*it); + } + result.polygons_with_holes(std::back_inserter(pwhs)); + } else { + pwhs.push_back(pwh); + } + +#if 0 + CGAL::Polygon_vertical_decomposition_2 decompositor; +#else + CGAL::Polygon_triangulation_decomposition_2 decompositor; +#endif + + std::list> decom_polies; + for (auto& pwh : pwhs) { + decompositor(pwh, std::back_inserter(decom_polies)); + } + + std::transform(decom_polies.begin(), decom_polies.end(), std::back_inserter(results), [this, &br, &z0, &z1, &first_item_style](const CGAL::Polygon_2& p2) { + cgal_face_t f; + std::transform( + p2.vertices_begin(), + p2.vertices_end(), + std::back_inserter(f.outer), + [](const CGAL::Point_2& p) { + return CGAL::Point_3(p.cartesian(0), p.cartesian(1), 0); + } + ); + + cgal_shape_t shp; + taxonomy::direction3 d(0, 0, 1); + process_extrusion(f, d, z1 - z0, shp); + + for (auto it = shp.vertices_begin(); it != shp.vertices_end(); ++it) { + auto p = it->point(); + it->point() = cgal_point_t(p.cartesian(0), p.cartesian(1), p.cartesian(2) + z0); + } + + return ConversionResult( + br->instance->data().id(), + br->matrix, + new CgalShape(shp), + br->surface_style.diffuse ? br->surface_style : first_item_style + ); + }); + + Logger::Notice("Processed boolean operation as 2d arrangement"); + + return true; + + } + + bool first = true; CGAL::Nef_polyhedron_3 a; + CGAL::Nef_nary_union_3> second_operand_collector; + size_t second_operand_collector_size = 0; + taxonomy::style first_item_style; for (auto& c : br->children) { @@ -927,15 +1137,20 @@ bool CgalKernel::convert_impl(const taxonomy::boolean_result* br, ifcopenshell:: } CGAL::Nef_polyhedron_3 nef; - preprocess_boolean_operand(c->instance, entity_shape, nef, + if (!preprocess_boolean_operand(c->instance, entity_shape, nef, // Dilate boolean subtraction operands - (!first && br->operation == taxonomy::boolean_result::SUBTRACTION)); + (!first && br->operation == taxonomy::boolean_result::SUBTRACTION))) + { + continue; + } if (first) { a = nef; } else { if (br->operation == taxonomy::boolean_result::SUBTRACTION) { - a -= nef; + second_operand_collector.add_polyhedron(nef); + second_operand_collector_size++; + // a -= nef; } else if (br->operation == taxonomy::boolean_result::INTERSECTION) { a *= nef; } else if (br->operation == taxonomy::boolean_result::UNION) { @@ -947,6 +1162,10 @@ bool CgalKernel::convert_impl(const taxonomy::boolean_result* br, ifcopenshell:: first = false; } + if (br->operation == taxonomy::boolean_result::SUBTRACTION && second_operand_collector_size) { + a -= second_operand_collector.get_union(); + } + cgal_shape_t a_poly, b_poly; // CGAL::Nef_polyhedron_3 b; diff --git a/src/ifcgeom/kernels/cgal/CgalKernel.h b/src/ifcgeom/kernels/cgal/CgalKernel.h index 588fd5b3a8..39587319ac 100644 --- a/src/ifcgeom/kernels/cgal/CgalKernel.h +++ b/src/ifcgeom/kernels/cgal/CgalKernel.h @@ -127,6 +127,9 @@ namespace kernels { bool convert(const taxonomy::loop*, cgal_wire_t&); // bool convert(const taxonomy::matrix4*, cgal_placement_t&); bool convert(const taxonomy::shell*, cgal_shape_t&); + + bool process_extrusion(const cgal_face_t& bottom_face, const taxonomy::direction3& direction, double height, cgal_shape_t& shape); + bool process_as_2d_polygon(const taxonomy::boolean_result* br, std::list>& loops, double& z0, double& z1); // virtual bool convert_impl(const taxonomy::face*, ifcopenshell::geometry::ConversionResults&); virtual bool convert_impl(const taxonomy::shell*, ifcopenshell::geometry::ConversionResults&);