FreeCAD / FreeCAD/FreeCAD

Part: Boolean operations fail on compounds with coincident faces (Draft Array)

Open
#26,119 5 comments 0 reactions 0 assignees View on GitHub
Mod: Part Status: Confirmed Type: Bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.