diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 93000cfb242f8bdc675e572c2918a1cd8492e3e7..7b59f21d18c3fb38666edb5a5e6147f2b2168972 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,8 @@ 4.10.2 (Work-in-progress): fixed regression introduced in 4.9 for recombined -meshes with boundary layers; new HealShapes command in .geo files; simplified -calculation of OCC STL bounding boxes; generalized Crack plugin; small bug fixes. +meshes with boundary layers; new Geometry.OCCSafeUnbind option to disable +boolean optimization introduced in 4.10.0 (for backward compatibility); new +HealShapes command in .geo files; simplified calculation of OCC STL bounding +boxes; generalized Crack plugin; small bug fixes. 4.10.1 (May 1, 2022): small bug fixes. diff --git a/examples/api/crack.py b/examples/api/crack.py index 9082ea28b36df05c36d6a36d0a20bb89db0f982e..a391a11a6acaa74bd239ce654a7620360af6bdcd 100644 --- a/examples/api/crack.py +++ b/examples/api/crack.py @@ -31,7 +31,7 @@ gmsh.model.addPhysicalGroup(1, new_lines, 101) gmsh.model.mesh.generate(2) gmsh.plugin.setNumber("Crack", "PhysicalGroup", 101) -# gmsh.plugin.setNumber("Crack", "DebugView", 1) +gmsh.plugin.setNumber("Crack", "DebugView", 1) gmsh.plugin.run("Crack") # save all the elements in the mesh (even those that do not belong to any diff --git a/examples/api/crack3d.py b/examples/api/crack3d.py index f98166287c62ee3b610fb983685f2f107bb62d83..620319ca346dc3906722fe34e8057f66a4ca5ce4 100644 --- a/examples/api/crack3d.py +++ b/examples/api/crack3d.py @@ -28,7 +28,7 @@ gmsh.model.mesh.generate(3) # "crack" the mesh by duplicating the elements and nodes on the small surface gmsh.plugin.setNumber("Crack", "Dimension", 2) gmsh.plugin.setNumber("Crack", "PhysicalGroup", phys) -#gmsh.plugin.setNumber("Crack", "DebugView", 1) +gmsh.plugin.setNumber("Crack", "DebugView", 1) gmsh.plugin.run("Crack") # save all the elements in the mesh (even those that do not belong to any diff --git a/src/common/Context.h b/src/common/Context.h index 3cf6f789d2cdbf8436dda966e8ce1f6a9137c213..50e73b066d58cf04f9d86904b1b24598315194d3 100644 --- a/src/common/Context.h +++ b/src/common/Context.h @@ -97,7 +97,7 @@ struct contextGeometryOptions { int autoCoherence; int autoExtrude; // FIXME: temporary for auto-extrude testing double tolerance, toleranceBoolean, snap[3], transform[3][3], offset[3]; - int occAutoFix, occAutoEmbed; + int occAutoFix, occAutoEmbed, occSafeUnbind; int occFixDegenerated, occFixSmallEdges, occFixSmallFaces; int occSewFaces, occMakeSolids, occParallel, occBooleanPreserveNumbering; int occBoundsUseSTL, occDisableSTL, occImportLabels, occUnionUnify; diff --git a/src/common/DefaultOptions.h b/src/common/DefaultOptions.h index 539a6e7adb9484a8c20d31e30798780a4d5cdc24..14bf8b01e912475c151adab8f7a038af768bb4b4 100644 --- a/src/common/DefaultOptions.h +++ b/src/common/DefaultOptions.h @@ -954,6 +954,9 @@ StringXNumber GeometryOptions_Number[] = { "OpenCASCADE kernel" }, { F|O, "OCCParallel" , opt_geometry_occ_parallel , 0. , "Use multi-threaded OpenCASCADE boolean operators" }, + { F|O, "OCCSafeUnbind" , opt_geometry_occ_safe_unbind , 0. , + "Revert to safe (i.e. with recursive checks on boundaries) unbinding of entities " + "in boolean operations and geometrical transformations" }, { F|O, "OCCScaling" , opt_geometry_occ_scaling , 1. , "Scale STEP, IGES and BRep models by the given factor when importing them with the " "OpenCASCADE kernel" }, diff --git a/src/common/Options.cpp b/src/common/Options.cpp index 674e31f2d45ec75bd2827753690fdcbafc549ba4..b592925f01605bcc03ac63b1b4a9ad35c67c2c88 100644 --- a/src/common/Options.cpp +++ b/src/common/Options.cpp @@ -4571,6 +4571,12 @@ double opt_geometry_occ_auto_embed(OPT_ARGS_NUM) return CTX::instance()->geom.occAutoEmbed; } +double opt_geometry_occ_safe_unbind(OPT_ARGS_NUM) +{ + if(action & GMSH_SET) CTX::instance()->geom.occSafeUnbind = val ? 1 : 0; + return CTX::instance()->geom.occSafeUnbind; +} + double opt_geometry_occ_auto_fix(OPT_ARGS_NUM) { if(action & GMSH_SET) CTX::instance()->geom.occAutoFix = val ? 1 : 0; diff --git a/src/common/Options.h b/src/common/Options.h index 96cc661f6adfb5ed47878f6adf8183abef569548..de3fb5e0b8712b5a4f1b30e244883a71d5501d00 100644 --- a/src/common/Options.h +++ b/src/common/Options.h @@ -399,6 +399,7 @@ double opt_geometry_surface_type(OPT_ARGS_NUM); double opt_geometry_light(OPT_ARGS_NUM); double opt_geometry_light_two_side(OPT_ARGS_NUM); double opt_geometry_occ_auto_embed(OPT_ARGS_NUM); +double opt_geometry_occ_safe_unbind(OPT_ARGS_NUM); double opt_geometry_occ_auto_fix(OPT_ARGS_NUM); double opt_geometry_occ_bounds_use_stl(OPT_ARGS_NUM); double opt_geometry_occ_disable_stl(OPT_ARGS_NUM); diff --git a/src/geo/GModelIO_OCC.cpp b/src/geo/GModelIO_OCC.cpp index d99b082d20086a7b29491703e587959da4718ffb..ddff7e90f78fbd2617033fb8fc3af1fbd3080511 100644 --- a/src/geo/GModelIO_OCC.cpp +++ b/src/geo/GModelIO_OCC.cpp @@ -142,9 +142,6 @@ #include <XCAFDoc_ShapeTool.hxx> #endif -// define this to deactive the optimizations introduced in #1240: -// #define SAFE_UNBIND - // for debugging: template <class T> void writeBrep(const T &shapes, const std::string &fileName = "debug.brep") @@ -3672,11 +3669,12 @@ bool OCC_Internals::booleanOperator( if(remove) { int d = inDimTags[i].first; int t = inDimTags[i].second; -#ifdef SAFE_UNBIND - if(_isBound(d, t)) _unbind(_find(d, t), d, t, true); -#else - if(_isBound(d, t)) _unbindWithoutChecks(_find(d, t)); -#endif + if(_isBound(d, t)) { + if(CTX::instance()->geom.occSafeUnbind) + _unbind(_find(d, t), d, t, true); + else + _unbindWithoutChecks(_find(d, t)); + } } } _multiBind(result, tag, outDimTags, (tag >= 0) ? true : false, true, @@ -3694,11 +3692,12 @@ bool OCC_Internals::booleanOperator( int tag = inDimTags[i].second; bool remove = (i < numObjects) ? removeObject : removeTool; if(mapDeleted[i]) { // deleted -#ifdef SAFE_UNBIND - if(remove) _unbind(mapOriginal[i], dim, tag, true); -#else - if(remove) _unbindWithoutChecks(mapOriginal[i]); -#endif + if(remove) { + if(CTX::instance()->geom.occSafeUnbind) + _unbind(mapOriginal[i], dim, tag, true); + else + _unbindWithoutChecks(mapOriginal[i]); + } Msg::Debug("BOOL (%d,%d) deleted", dim, tag); } else if(mapModified[i].Extent() == 0) { // not modified @@ -3960,20 +3959,21 @@ bool OCC_Internals::_transform( for(std::size_t i = 0; i < inDimTags.size(); i++) { int dim = inDimTags[i].first; int tag = inDimTags[i].second; -#ifdef SAFE_UNBIND - // safe, but slow: _unbind() has linear complexity with respect to the number - // of entities in the model (due to the dependency checking of upward - // adjencencies and the maximum tag update). Using this in a for loop to - // translate copies of entities leads to quadratic complexity. - _unbind(inShapes[i], dim, tag, true); -#else - // bypass it by unbinding the shape and all its subshapes without checking - // dependencies: this is a bit dangerous, as one could translate e.g. the - // face of a cube (this is not allowed!) - which will unbind the face of the - // cube. But the original face will actually be re-bound (with a warning) at - // the next syncronization point, so it's not too bad... - _unbindWithoutChecks(inShapes[i]); -#endif + if(CTX::instance()->geom.occSafeUnbind) { + // safe, but slow: _unbind() has linear complexity with respect to the number + // of entities in the model (due to the dependency checking of upward + // adjencencies and the maximum tag update). Using this in a for loop to + // translate copies of entities leads to quadratic complexity. + _unbind(inShapes[i], dim, tag, true); + } + else { + // bypass it by unbinding the shape and all its subshapes without checking + // dependencies: this is a bit dangerous, as one could translate e.g. the + // face of a cube (this is not allowed!) - which will unbind the face of + // the cube. But the original face will actually be re-bound (with a + // warning) at the next syncronization point, so it's not too bad... + _unbindWithoutChecks(inShapes[i]); + } // TODO: it would be even better to code a rebind() function to reuse the // tags not only of the shape, but of all the sub-shapes as well _bind(outShapes[i], dim, tag, true);