From 14aeafe3eda0af6654f7263a8ed5a132e0b84185 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Mon, 20 Aug 2018 11:46:26 +0200 Subject: [PATCH] Work on #426 --- src/ifcgeom/IfcGeom.h | 2 + src/ifcgeom/IfcGeomFunctions.cpp | 181 ++++++++++++++++++++++++++++++- src/ifcgeom/IfcGeomShapes.cpp | 15 +++ 3 files changed, 192 insertions(+), 6 deletions(-) diff --git a/src/ifcgeom/IfcGeom.h b/src/ifcgeom/IfcGeom.h index 88b05f0cb6..5ce6a8801d 100644 --- a/src/ifcgeom/IfcGeom.h +++ b/src/ifcgeom/IfcGeom.h @@ -195,6 +195,8 @@ public: bool boolean_operation(const TopoDS_Shape&, const TopoDS_Shape&, BOPAlgo_Operation, TopoDS_Shape&, double fuzziness = -1.); #endif + bool fit_halfspace(const TopoDS_Shape& a, const TopoDS_Shape& b, TopoDS_Shape& box, double& height); + const Handle_Geom_Curve intersect(const Handle_Geom_Surface&, const Handle_Geom_Surface&); const Handle_Geom_Curve intersect(const Handle_Geom_Surface&, const TopoDS_Face&); const Handle_Geom_Curve intersect(const TopoDS_Face&, const Handle_Geom_Surface&); diff --git a/src/ifcgeom/IfcGeomFunctions.cpp b/src/ifcgeom/IfcGeomFunctions.cpp index 64ab7e59fa..5630f0bb8d 100644 --- a/src/ifcgeom/IfcGeomFunctions.cpp +++ b/src/ifcgeom/IfcGeomFunctions.cpp @@ -135,6 +135,9 @@ #include +#include +#include + #include #include "../ifcparse/IfcSIPrefix.h" @@ -630,8 +633,27 @@ bool IfcGeom::Kernel::convert_wire_to_face(const TopoDS_Wire& w, TopoDS_Face& fa select_largest(results, wire); } - ShapeFix_ShapeTolerance FTol; - FTol.SetTolerance(wire, getValue(GV_PRECISION), TopAbs_WIRE); + bool is_2d = true; + TopExp_Explorer exp(wire, TopAbs_EDGE); + for (; exp.More(); exp.Next()) { + double a, b; + Handle(Geom_Curve) crv = BRep_Tool::Curve(TopoDS::Edge(exp.Current()), a, b); + if (crv->DynamicType() != STANDARD_TYPE(Geom_Line)) { + is_2d = false; + break; + } + Handle(Geom_Line) line = Handle(Geom_Line)::DownCast(crv); + if (line->Lin().Direction().Z() > ALMOST_ZERO) { + is_2d = false; + break; + } + } + + if (!is_2d) { + // For 2d wires (e.g. profiles) a higher tolerance for plane fitting is never required. + ShapeFix_ShapeTolerance FTol; + FTol.SetTolerance(wire, getValue(GV_PRECISION), TopAbs_WIRE); + } BRepBuilderAPI_MakeFace mf(wire, false); BRepBuilderAPI_FaceError er = mf.Error(); @@ -2870,7 +2892,7 @@ bool IfcGeom::Kernel::wire_intersections(const TopoDS_Wire& wire, TopTools_ListO if (i == n - 1 && j == 0) continue; bool unbounded_intersects; - const double eps = getValue(GV_PRECISION) * 2.; + const double eps = getValue(GV_PRECISION) * 10.; double u11, u12, u21, u22, U1, U2; GeomAPI_ExtremaCurveCurve ecc( @@ -3006,6 +3028,99 @@ void IfcGeom::Kernel::select_largest(const TopTools_ListOfShape& shapes, TopoDS_ } } +bool IfcGeom::Kernel::fit_halfspace(const TopoDS_Shape& a, const TopoDS_Shape& b, TopoDS_Shape& box, double& height) { + TopExp_Explorer exp(b, TopAbs_FACE); + if (!exp.More()) { + return false; + } + + TopoDS_Face face = TopoDS::Face(exp.Current()); + exp.Next(); + + if (exp.More()) { + return false; + } + + Handle(Geom_Surface) surf = BRep_Tool::Surface(face); + + // const gp_XYZ xyz = a.Location().Transformation().TranslationPart(); + // std::cout << "dz " << xyz.Z() << std::endl; + + if (surf->DynamicType() != STANDARD_TYPE(Geom_Plane)) { + return false; + } + + Bnd_Box bb; + BRepBndLib::Add(a, bb); + + double xs[2], ys[2], zs[2]; + bb.Get(xs[0], ys[0], zs[0], xs[1], ys[1], zs[1]); + + gp_Pln pln = Handle(Geom_Plane)::DownCast(surf)->Pln(); + + gp_Pnt P = pln.Position().Location(); + gp_Vec z = pln.Position().Direction(); + gp_Vec x = pln.Position().XDirection(); + gp_Vec y = pln.Position().YDirection(); + + if (face.Orientation() != TopAbs_REVERSED) { + z.Reverse(); + } + + double D, Umin, Umax, Vmin, Vmax; + D = 0.; + Umin = Vmin = +std::numeric_limits::infinity(); + Umax = Vmax = -std::numeric_limits::infinity(); + + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + for (int k = 0; k < 2; ++k) { + gp_Pnt p(xs[i], ys[j], zs[k]); + + gp_Vec d = p.XYZ() - P.XYZ(); + const double u = d.Dot(x); + const double v = d.Dot(y); + const double w = d.Dot(z); + + if (w > D) { + D = w; + } + if (u < Umin) { + Umin = u; + } + if (u > Umax) { + Umax = u; + } + if (v < Vmin) { + Vmin = v; + } + if (v > Vmax) { + Vmax = v; + } + } + } + } + + const double eps = getValue(GV_PRECISION) * 2.; + + BRepBuilderAPI_MakePolygon poly; + poly.Add(P.XYZ() + x.XYZ() * (Umin + eps) + y.XYZ() * (Vmin + eps)); + poly.Add(P.XYZ() + x.XYZ() * (Umax + eps) + y.XYZ() * (Vmin + eps)); + poly.Add(P.XYZ() + x.XYZ() * (Umax + eps) + y.XYZ() * (Vmax + eps)); + poly.Add(P.XYZ() + x.XYZ() * (Umin + eps) + y.XYZ() * (Vmax + eps)); + poly.Close(); + + BRepBuilderAPI_MakeFace mf(surf, poly.Wire(), true); + + gp_Vec vec = gp_Vec(z.XYZ() * (D + eps)); + + BRepPrimAPI_MakePrism mp(mf.Face(), vec); + box = mp.Shape(); + + height = D; + return true; +} + #if OCC_VERSION_HEX < 0x60900 bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopTools_ListOfShape& b, BOPAlgo_Operation op, TopoDS_Shape& result) { result = a; @@ -3103,6 +3218,48 @@ namespace { } return min_edge_len; } + + double min_vertex_edge_distance(const TopoDS_Shape& a, double t) { + TopExp_Explorer exp(a, TopAbs_VERTEX); + + double M = std::numeric_limits::infinity(); + + for (; exp.More(); exp.Next()) { + if (exp.Current().Orientation() != TopAbs_FORWARD) { + continue; + } + + const TopoDS_Vertex& v = TopoDS::Vertex(exp.Current()); + gp_Pnt p = BRep_Tool::Pnt(v); + + TopExp_Explorer exp2(a, TopAbs_EDGE); + for (; exp2.More(); exp2.Next()) { + const TopoDS_Edge& e = TopoDS::Edge(exp2.Current()); + TopoDS_Vertex v1, v2; + TopExp::Vertices(e, v1, v2); + + if (v.IsEqual(v1) || v.IsEqual(v2)) { + continue; + } + + BRepAdaptor_Curve crv(e); + Extrema_ExtPC ext(p, crv); + if (!ext.IsDone()) { + continue; + } + + for (int i = 1; i <= ext.NbExt(); ++i) { + const double m = sqrt(ext.SquareDistance(i)); + if (m < M && m > t) { + M = m; + } + } + } + } + + return M; + } + } bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopTools_ListOfShape& b, BOPAlgo_Operation op, TopoDS_Shape& result, double fuzziness) { @@ -3121,8 +3278,20 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopTools_Li fuzziness = getValue(GV_PRECISION); } - const double min_edge_len = min_edge_length(a); - const double fuzz = (std::min)(min_edge_len / 3., fuzziness); + double min_len = (std::min)(min_edge_length(a), min_vertex_edge_distance(a, getValue(GV_PRECISION))); + TopTools_ListIteratorOfListOfShape it(b); + for (; it.More(); it.Next()) { + double d = min_edge_length(it.Value()); + if (d < min_len) { + min_len = d; + } + d = min_vertex_edge_distance(it.Value(), getValue(GV_PRECISION)); + if (d < min_len) { + min_len = d; + } + } + + const double fuzz = (std::min)(min_len / 10., fuzziness); TopTools_ListOfShape s1s; s1s.Append(copy_operand(a)); @@ -3156,7 +3325,7 @@ bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopTools_Li delete builder; if (!success) { const double new_fuzziness = fuzziness * 10.; - if (new_fuzziness + 1e-15 <= getValue(GV_PRECISION) * 1000. && new_fuzziness < min_edge_len) { + if (new_fuzziness + 1e-15 <= getValue(GV_PRECISION) * 1000. && new_fuzziness < min_len) { return boolean_operation(a, b, op, result, new_fuzziness); } } diff --git a/src/ifcgeom/IfcGeomShapes.cpp b/src/ifcgeom/IfcGeomShapes.cpp index 579d886a1b..81a8af14ca 100644 --- a/src/ifcgeom/IfcGeomShapes.cpp +++ b/src/ifcgeom/IfcGeomShapes.cpp @@ -479,6 +479,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape IfcSchema::IfcBooleanOperand* operand1 = l->FirstOperand(); IfcSchema::IfcBooleanOperand* operand2 = l->SecondOperand(); bool is_halfspace = operand2->is(IfcSchema::Type::IfcHalfSpaceSolid); + bool is_unbounded_halfspace = is_halfspace && !operand2->is(IfcSchema::Type::IfcPolygonalBoundedHalfSpace); if ( shape_type(operand1) == ST_SHAPELIST ) { if (!(convert_shapes(operand1, items1) && flatten_shape_list(items1, s1, true))) { @@ -524,6 +525,20 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape Logger::Message(Logger::LOG_WARNING,"Empty solid for:",operand2->entity); } + if (is_unbounded_halfspace) { + TopoDS_Shape temp; + double d; + if (fit_halfspace(s1, s2, temp, d)) { + if (d < getValue(GV_PRECISION)) { + Logger::Message(Logger::LOG_WARNING, "Subtraction yields unchanged volume:", l->entity); + shape = s1; + return true; + } else { + s2 = temp; + } + } + } + const IfcSchema::IfcBooleanOperator::IfcBooleanOperator op = l->Operator(); /*