From bffb19a92f7ecf224f36f29c51b17de6c522ca90 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Sun, 24 Nov 2013 15:44:27 +0000 Subject: [PATCH] Use the precision from the IfcGeometricRepresentationContext to dictate OCC modeler tolerances --- src/ifcgeom/IfcGeom.h | 5 + src/ifcgeom/IfcGeomFunctions.cpp | 12 +++ src/ifcgeom/IfcGeomObjects.cpp | 27 ++++++ src/ifcgeom/IfcGeomObjects.h | 1 + src/ifcgeom/IfcGeomShapes.cpp | 128 +++----------------------- src/ifcgeom/IfcRegister.cpp | 12 ++- src/ifcgeom/IfcRegisterConvertShape.h | 12 ++- 7 files changed, 74 insertions(+), 123 deletions(-) diff --git a/src/ifcgeom/IfcGeom.h b/src/ifcgeom/IfcGeom.h index 754fbd9cf0..0bfb4be70a 100644 --- a/src/ifcgeom/IfcGeom.h +++ b/src/ifcgeom/IfcGeom.h @@ -73,6 +73,10 @@ namespace IfcGeom { // the interpretation of IfcParamaterValues of IfcTrimmedCurves // Default: -1.0 (= not set, fist try degrees, then radians) GV_PLANEANGLE_UNIT, + // The precision used in boolean operations, setting this value too low results + // in artefacts and potentially modelling failures + // Default: 0.00001 (obtained from IfcGeometricRepresentationContext if available) + GV_PRECISION }; bool convert_wire_to_face(const TopoDS_Wire& wire, TopoDS_Face& face); @@ -95,6 +99,7 @@ namespace IfcGeom { bool profile_helper(int numVerts, double* verts, int numFillets, int* filletIndices, double* filletRadii, gp_Trsf2d trsf, TopoDS_Face& face); double shape_volume(const TopoDS_Shape& s); double face_area(const TopoDS_Face& f); + void apply_tolerance(TopoDS_Shape& s, double t); void SetValue(GeomValue var, double value); double GetValue(GeomValue var); Ifc2x3::IfcProductDefinitionShape* tesselate(TopoDS_Shape& shape, double deflection, IfcEntities es); diff --git a/src/ifcgeom/IfcGeomFunctions.cpp b/src/ifcgeom/IfcGeomFunctions.cpp index 6f478b4954..b4868cab72 100644 --- a/src/ifcgeom/IfcGeomFunctions.cpp +++ b/src/ifcgeom/IfcGeomFunctions.cpp @@ -423,6 +423,11 @@ gp_Pnt IfcGeom::point_above_plane(const gp_Pln& pln, bool agree) { } } +void IfcGeom::apply_tolerance(TopoDS_Shape& s, double t) { + ShapeFix_ShapeTolerance tol; + tol.SetTolerance(s, t); +} + static double deflection_tolerance = 0.001; static double wire_creation_tolerance = 0.0001; static double minimal_face_area = 0.000001; @@ -431,6 +436,7 @@ static double max_faces_to_sew = -1.0; static double ifc_length_unit = 1.0; static double ifc_planeangle_unit = -1.0; static double force_ccw_face_orientation = -1.0; +static double modelling_precision = 0.00001; void IfcGeom::SetValue(GeomValue var, double value) { switch (var) { @@ -458,6 +464,9 @@ void IfcGeom::SetValue(GeomValue var, double value) { case GV_FORCE_CCW_FACE_ORIENTATION: force_ccw_face_orientation = value; break; + case GV_PRECISION: + modelling_precision = value; + break; default: assert(!"never reach here"); } @@ -484,6 +493,9 @@ double IfcGeom::GetValue(GeomValue var) { case GV_FORCE_CCW_FACE_ORIENTATION: return force_ccw_face_orientation; break; + case GV_PRECISION: + return modelling_precision; + break; } assert(!"never reach here"); return 0; diff --git a/src/ifcgeom/IfcGeomObjects.cpp b/src/ifcgeom/IfcGeomObjects.cpp index 82987f3438..1762919207 100644 --- a/src/ifcgeom/IfcGeomObjects.cpp +++ b/src/ifcgeom/IfcGeomObjects.cpp @@ -573,6 +573,32 @@ double UnitPrefixToValue( Ifc2x3::IfcSIPrefix::IfcSIPrefix v ) { else return 1.0f; } +void IfcGeomObjects::InitPrecision() { + Ifc2x3::IfcGeometricRepresentationContext::list rep_contexts = ifc_file->EntitiesByType(); + // Currently, IfcGeometricRepresentationContext aren't used as much as they should be + // in the evaluation of shape representations, hence, we try to find the one with the + // lowest precision. Typically, a value of 1e-5 is encountered. This value is applied + // to all TopoDS_Shapes generated by one of the IfcGeom::convert() functions. + // TODO: Many of the empirically found tolerances should probably be substituted by + // one that is defined in the model file. + double lowest_precision_encountered = std::numeric_limits::infinity(); + bool any_precision_encountered = false; + for (Ifc2x3::IfcGeometricRepresentationContext::it it = rep_contexts->begin(); it != rep_contexts->end(); ++it) { + Ifc2x3::IfcGeometricRepresentationContext* rep_context = *it; + if (rep_context->is(Ifc2x3::Type::IfcGeometricRepresentationSubContext)) continue; + if (rep_context->hasPrecision()) { + const double precision = rep_context->Precision(); + if (precision < lowest_precision_encountered) { + any_precision_encountered = true; + lowest_precision_encountered = precision; + } + } + } + if (any_precision_encountered) { + IfcGeom::SetValue(IfcGeom::GV_PRECISION, lowest_precision_encountered); + } +} + void IfcGeomObjects::InitUnits() { // Set default units, set length to meters, angles to undefined IfcGeom::SetValue(IfcGeom::GV_LENGTH_UNIT,1.0); @@ -638,6 +664,7 @@ bool IfcGeomObjects::Init(const std::string fn) { } bool _Init() { IfcGeomObjects::InitUnits(); + IfcGeomObjects::InitPrecision(); shapereps = ifc_file->EntitiesByType(); if ( ! shapereps ) return false; diff --git a/src/ifcgeom/IfcGeomObjects.h b/src/ifcgeom/IfcGeomObjects.h index 11d473084c..050ae2993d 100644 --- a/src/ifcgeom/IfcGeomObjects.h +++ b/src/ifcgeom/IfcGeomObjects.h @@ -265,6 +265,7 @@ namespace IfcGeomObjects { void Settings(int setting, bool value); void InitUnits(); + void InitPrecision(); const IfcGeomObject* Get(); const IfcObject* GetObject(int id); diff --git a/src/ifcgeom/IfcGeomShapes.cpp b/src/ifcgeom/IfcGeomShapes.cpp index 40b9117dba..8e71433b70 100644 --- a/src/ifcgeom/IfcGeomShapes.cpp +++ b/src/ifcgeom/IfcGeomShapes.cpp @@ -158,19 +158,6 @@ bool IfcGeom::convert(const Ifc2x3::IfcBooleanClippingResult::ptr l, TopoDS_Shap Ifc2x3::IfcBooleanOperand operand1 = l->FirstOperand(); Ifc2x3::IfcBooleanOperand operand2 = l->SecondOperand(); bool is_halfspace = operand2->is(Ifc2x3::Type::IfcHalfSpaceSolid); - bool is_bounded = operand2->is(Ifc2x3::Type::IfcPolygonalBoundedHalfSpace); - - // The rationale of this elaborate processing of bounded halfspace subtractions - // is that occasionally we have encountered bounded halfspaces of which the - // boundary coincides roughly with the footprint of the first operand solid and - // subtracting this naively resulted in precision artefacts. However, when the - // final subtraction result is the outcome of multiple successive bounded - // halfspace subtractions the added complexity of this scheme can potentially - // produce wrong results too. Hence this check. - bool parent_is_bounded_halfspace = operand1->is(Ifc2x3::Type::IfcBooleanClippingResult) && - ((Ifc2x3::IfcBooleanClippingResult*) operand1)->SecondOperand()->is(Ifc2x3::Type::IfcPolygonalBoundedHalfSpace); - - bool is_convex_bound = false; if ( ! IfcGeom::convert_shape(operand1,s1) ) return false; @@ -185,13 +172,6 @@ bool IfcGeom::convert(const Ifc2x3::IfcBooleanClippingResult::ptr l, TopoDS_Shap return true; } - if ( is_bounded ) { - Ifc2x3::IfcPolygonalBoundedHalfSpace::ptr ifc_bounded_halfspace = - (Ifc2x3::IfcPolygonalBoundedHalfSpace::ptr) operand2; - IfcGeom::convert_wire(ifc_bounded_halfspace->PolygonalBoundary(),boundary_wire); - is_convex_bound = is_convex(boundary_wire); - } - if ( ! is_halfspace ) { const double second_operand_volume = shape_volume(s2); if ( second_operand_volume <= ALMOST_ZERO ) @@ -199,102 +179,19 @@ bool IfcGeom::convert(const Ifc2x3::IfcBooleanClippingResult::ptr l, TopoDS_Shap } bool valid_cut = false; - if ( !is_bounded || !is_convex_bound || parent_is_bounded_halfspace) { - BRepAlgoAPI_Cut brep_cut(s1,s2); - if ( brep_cut.IsDone() ) { - TopoDS_Shape result = brep_cut; - bool is_valid = BRepCheck_Analyzer(result).IsValid() != 0; - if ( is_valid ) { - shape = result; - valid_cut = true; - } - } - if ( !valid_cut && !is_bounded ) { - Ifc2x3::IfcHalfSpaceSolid::ptr ifc_halfspace = (Ifc2x3::IfcHalfSpaceSolid::ptr) operand2; - Ifc2x3::IfcSurface::ptr surface = ifc_halfspace->BaseSurface(); - if ( surface->is(Ifc2x3::Type::IfcPlane) ) { - gp_Pln pln; - IfcGeom::convert(reinterpret_pointer_cast(surface),pln); - gp_Pnt pnt = pln.Location(); - bool reverse = ifc_halfspace->AgreementFlag(); - gp_Vec direction = pln.Axis().Direction(); - if ( reverse ) direction *= -1; - pnt.Translate(direction); - pln.SetLocation(pln.Location().Translated(direction * -0.0001)); - TopoDS_Shape halfspace = BRepPrimAPI_MakeHalfSpace(BRepBuilderAPI_MakeFace(pln),pnt).Solid(); + BRepAlgoAPI_Cut brep_cut(s1,s2); + if ( brep_cut.IsDone() ) { + TopoDS_Shape result = brep_cut; - BRepAlgoAPI_Cut brep_cut(s1,halfspace); - if ( brep_cut.IsDone() ) { - TopoDS_Shape result = brep_cut; - bool is_valid = BRepCheck_Analyzer(result).IsValid() != 0; - if ( is_valid ) { - shape = result; - valid_cut = true; - Logger::Message(Logger::LOG_WARNING,"Slightly nudged the SecondOperand of:",l->entity); - } - } - } - } - } else { - Ifc2x3::IfcPolygonalBoundedHalfSpace::ptr ifc_bounded_halfspace = - (Ifc2x3::IfcPolygonalBoundedHalfSpace::ptr) operand2; - gp_Trsf trsf; - convert(ifc_bounded_halfspace->Position(),trsf); - TopoDS_Shape face = BRepBuilderAPI_MakeFace(boundary_wire).Face(); - TopoDS_Shape prism = BRepPrimAPI_MakePrism (boundary_wire,gp_Vec(0,0,1),1); - prism.Move(trsf); - face.Move(trsf); - - gp_Pln pln = plane_from_face(TopoDS::Face(face)); - gp_Pnt pnt = point_above_plane(pln,ifc_bounded_halfspace->AgreementFlag()); - - TopoDS_Shape halfspace; - Ifc2x3::IfcHalfSpaceSolid::ptr ifc_halfspace = (Ifc2x3::IfcHalfSpaceSolid::ptr) operand2; - if ( ! IfcGeom::convert(ifc_halfspace,halfspace) ) return false; - - TopoDS_Shape subtraction_volume = s1; - double subtraction_volume_volume = shape_volume(subtraction_volume); - - const double minimal_substraction_difference = subtraction_volume_volume * 0.0001; - - BRepAlgoAPI_Common brep_common(subtraction_volume,halfspace); - if ( brep_common.IsDone() ) { - TopoDS_Shape brep_common_shape = brep_common; - bool is_valid = BRepCheck_Analyzer(brep_common_shape).IsValid() != 0; - double new_subtraction_volume_volume = shape_volume(brep_common_shape); - double subtraction_volume_difference = subtraction_volume_volume - new_subtraction_volume_volume; - if ( is_valid && subtraction_volume_difference > minimal_substraction_difference ) { - subtraction_volume = brep_common_shape; - subtraction_volume_volume = new_subtraction_volume_volume; - } - } - - TopExp_Explorer exp(prism,TopAbs_FACE); - while ( exp.More() ) { - TopoDS_Shape halfspace = halfspace_from_plane(plane_from_face(TopoDS::Face(exp.Current())),pnt); - BRepAlgoAPI_Common brep_common(subtraction_volume,halfspace); - if ( brep_common.IsDone() ) { - TopoDS_Shape brep_common_shape = brep_common; - bool is_valid = BRepCheck_Analyzer(brep_common_shape).IsValid() != 0; - double new_subtraction_volume_volume = shape_volume(brep_common_shape); - double subtraction_volume_difference = subtraction_volume_volume - new_subtraction_volume_volume; - if ( is_valid && subtraction_volume_difference > minimal_substraction_difference ) { - subtraction_volume = brep_common_shape; - subtraction_volume_volume = new_subtraction_volume_volume; - } - } - exp.Next(); - } - - BRepAlgoAPI_Cut brep_cut(s1,subtraction_volume); - if ( brep_cut.IsDone() ) { - TopoDS_Shape result = brep_cut; - bool is_valid = BRepCheck_Analyzer(result).IsValid() != 0; - if ( is_valid ) { - shape = result; - valid_cut = true; - } - } + ShapeFix_Shape fix(result); + fix.Perform(); + result = fix.Shape(); + + bool is_valid = BRepCheck_Analyzer(result).IsValid() != 0; + if ( is_valid ) { + shape = result; + valid_cut = true; + } } if ( valid_cut ) { @@ -307,7 +204,6 @@ bool IfcGeom::convert(const Ifc2x3::IfcBooleanClippingResult::ptr l, TopoDS_Shap } return true; - } bool IfcGeom::convert(const Ifc2x3::IfcConnectedFaceSet::ptr l, TopoDS_Shape& shape) { Ifc2x3::IfcFace::list faces = l->CfsFaces(); diff --git a/src/ifcgeom/IfcRegister.cpp b/src/ifcgeom/IfcRegister.cpp index 52b433c510..262a5f8a3f 100644 --- a/src/ifcgeom/IfcRegister.cpp +++ b/src/ifcgeom/IfcRegister.cpp @@ -42,11 +42,19 @@ bool IfcGeom::is_shape_collection(const IfcBaseClass* l) { } bool IfcGeom::convert_shape(const IfcBaseClass* l, TopoDS_Shape& r) { const unsigned int id = l->entity->id(); + bool success = false; + bool processed = false; std::map::const_iterator it = Cache::Shape.find(id); if ( it != Cache::Shape.end() ) { r = it->second; return true; } #include "IfcRegisterConvertShape.h" - Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l->entity); - return 0; + if ( processed ) { + const double precision = IfcGeom::GetValue(GV_PRECISION); + IfcGeom::apply_tolerance(r, precision); + Cache::Shape[id] = r; + } else { + Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l->entity); + } + return success; } bool IfcGeom::convert_wire(const IfcBaseClass* l, TopoDS_Wire& r) { #include "IfcRegisterConvertWire.h" diff --git a/src/ifcgeom/IfcRegisterConvertShape.h b/src/ifcgeom/IfcRegisterConvertShape.h index 8cb8e35496..192776555d 100644 --- a/src/ifcgeom/IfcRegisterConvertShape.h +++ b/src/ifcgeom/IfcRegisterConvertShape.h @@ -1,14 +1,16 @@ #include "IfcRegisterUndef.h" #define SHAPE(T) \ - if ( l->is(T::Class()) ) { \ + if ( !processed && l->is(T::Class()) ) { \ + processed = true; \ try { \ if ( convert((T*)l,r) ) { \ - Cache::Shape[id] = r; \ - return true; \ + success = true; \ } \ } catch(...) { } \ - Logger::Message(Logger::LOG_ERROR,"Failed to convert:",l->entity); \ - return 0; \ + if ( !success) { \ + Logger::Message(Logger::LOG_ERROR,"Failed to convert:",l->entity); \ + return false; \ + } \ } #include "IfcRegisterDef.h"