Use the precision from the IfcGeometricRepresentationContext to dictate OCC modeler tolerances

This commit is contained in:
Thomas Krijnen
2013-11-24 15:44:27 +00:00
parent 0f53a877c7
commit bffb19a92f
7 changed files with 74 additions and 123 deletions
+5
View File
@@ -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);
+12
View File
@@ -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;
+27
View File
@@ -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<Ifc2x3::IfcGeometricRepresentationContext>();
// 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<double>::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<Ifc2x3::IfcShapeRepresentation>();
if ( ! shapereps ) return false;
+1
View File
@@ -265,6 +265,7 @@ namespace IfcGeomObjects {
void Settings(int setting, bool value);
void InitUnits();
void InitPrecision();
const IfcGeomObject* Get();
const IfcObject* GetObject(int id);
+12 -116
View File
@@ -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<Ifc2x3::IfcSurface,Ifc2x3::IfcPlane>(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();
+10 -2
View File
@@ -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<int,TopoDS_Shape>::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"
+7 -5
View File
@@ -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"