Part: Boolean operations fail on compounds with coincident faces (Draft Array)
- Dominant language
- C++
- Stars
- 33.6k
- Forks
- 6k
- Avg merge
- 3d 17h
- Merged PRs (30d)
- 196
Description
## Problem
Boolean operations fail with "invalid shape" errors when applied to compounds containing shapes with coincident faces, such as Draft Arrays where step size equals object size.
```
OS: macOS 15.6.1
Architecture: arm64
Version: 1.2.0dev.20251209 (Git shallow)
Build date: 2025/12/09 13:53:17
Build type: Release
Branch: grafted,grafted
Hash: 5ad6a1ba58c665c23f1d217c23e74379d8c1a5a4
Python 3.11.14, Qt 6.8.3, Coin 4.0.3, Vtk 9.3.1, boost 1_86, Eigen3 3.4.0, PySide 6.8.3
shiboken 6.8.3, xerces-c 3.3.0, IfcOpenShell 0.8.2, OCC 7.8.1
Locale: English/United States (en_US)
Navigation Style/Orbit Style/Rotation Mode: TinkerCAD/Turntable/Drag at cursor
Stylesheet/Theme/QtStyle: FreeCAD Light.qss/FreeCAD Light/
Logical DPI/Physical DPI/Pixel Ratio: 72/135/2
Installed mods:
* PieMenu 1.12.1
* freecad.gears 1.3.0
* AICopilot
* lattice2 1.0.0
* CurvedShapes 1.0.13
* Curves 0.6.70
```
### Symptoms
- Draft Array with step = object size creates a compound with coincident faces
- `Part.CheckGeometry()` reports "Boolean operation: self-intersection" errors
- Subsequent boolean operations (cut, fuse, common) fail with "invalid shape"
- `shape.isValid()` returns `True`, making the error mystifying to users
### Root Cause
1. **Floating point precision**: Transformations (especially rotations) introduce ~1e-14 floating point errors, causing vertices that should be exactly coincident to be nearly-coincident instead.
2. **OCCT BOPAlgo limitation**: OCCT's boolean algorithm cannot handle nearly-coincident faces without a fuzzy tolerance.
3. **Missing default fuzzy tolerance**: `makeElementBoolean()` only applies fuzzy tolerance when explicitly requested (`tolerance > 0` or `tolerance < 0` for auto). When `tolerance == 0` (the default), no fuzzy tolerance is applied, causing booleans to fail on coincident geometry.
### Steps to Reproduce
1. Create a Link to an object in another document
2. Create a Draft Clone of the link
3. Rotate the clone (e.g., 270°)
4. Create a Draft Array with step size = object size
5. Run Part > Check Geometry - shows "self-intersection" errors
6. Attempt any boolean operation on the array - fails with "invalid shape"
### Workaround
Set `Fuse=True` on the Draft Array, which uses `multiFuse()` internally. However, this can be slow for large arrays and shouldn't be necessary for subsequent booleans on a compound.
## Proposed Solution
Change `makeElementBoolean()` to use auto-fuzzy tolerance by default instead of no fuzzy tolerance.
### Before
```cpp
if (tolerance > 0.0) {
mk->SetFuzzyValue(tolerance);
}
else if (tolerance < 0.0) {
FCBRepAlgoAPIHelper::setAutoFuzzy(mk.get());
}
// tolerance == 0 -> NO fuzzy tolerance
```
### After
```cpp
if (tolerance > 0.0) {
mk->SetFuzzyValue(tolerance);
}
else {
// Use auto-fuzzy by default to handle coincident faces from arrays,
// floating point errors from transformations, etc.
FCBRepAlgoAPIHelper::setAutoFuzzy(mk.get());
}
```
The auto-fuzzy calculation (`FuzzyHelper::getBooleanFuzzy() * sqrt(bounds.SquareExtent()) * Precision::Confusion()`) scales appropriately with object size and is already used by Part Design booleans.
## Testing
1. Create Draft Array scenario as described above
2. Verify boolean operations now succeed without needing `Fuse=True`
3. Run existing Part/PartDesign test suites to check for regressions
## Related Issues
- CheckGeometry still reports "self-intersection" warnings on compounds with coincident faces (separate issue - cosmetic, doesn't block workflow)
## Files Changed
- `src/Mod/Part/App/TopoShapeExpansion.cpp`: 4-line change in `makeElementBoolean()`
Contributor guide
Research direction
Start in src/Mod/Part/App/TopoShapeExpansion.cpp and read makeElementBoolean(), especially its tolerance handling and the existing fuzzy-tolerance helper calls. Reproduce the Draft Array case with coincident faces, then run the existing Part and PartDesign test suites to check for regressions. Done means boolean operations succeed for the described array without requiring Fuse=True.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- computer-graphics
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100