Skip to content

Modeling Algorithms - Fix Defeaturing failing to remove a corner round - #1522

Open
GabrielJMS wants to merge 1 commit into
Open-Cascade-SAS:IRfrom
GabrielJMS:fix/defeaturing-extension-retry
Open

GabrielJMS wants to merge 1 commit into
Open-Cascade-SAS:IRfrom
GabrielJMS:fix/defeaturing-extension-retry

Conversation

@GabrielJMS

@GabrielJMS GabrielJMS commented Sep 2, 2026 •

Copy link
Copy Markdown

Pre-Submission Checks

  • I checked existing issues, pull requests, discussions, and forum topics for related work.
  • I followed the contribution guidance in .github/CONTRIBUTING.md.
  • I used a PR title in the Group - Summary format.

Fixes #1521.

Problem / Motivation

BRepAlgoAPI_Defeaturing cannot remove a convex cylindrical corner round, while the concave fillets of the very same part are removed without trouble. The operation reports BOPAlgo_AlertUnableToRemoveTheFeature and returns the shape unmodified.

FillGap::ExtendAdjacentFaces sizes the extension of the adjacent faces from the bounding box of the feature:

Bnd_Box aFeatureBox;
BRepBndLib::Add(myFeature, aFeatureBox);
const double anExtLength = sqrt(aFeatureBox.SquareExtent());

That length is unrelated to how far the adjacent faces actually have to travel to meet each other. For the part attached to #1521 the extension is 10.7278, and the extended neighbour plane reaches x = -58.44 where it needs x ~ -56.85 to intersect the extended neighbour cylinder - it misses by ~1.6 mm.

With no closing edge from that intersection, every split of the extended face still carries a free boundary edge of the extension, the ext-edge filter discards all of them, aLFTrimmed ends up empty and FillGap::TrimFace falls into its last-resort branch:

else if (aLFTrimmed.IsEmpty())
{
  // Use all splits, including those having the bounds of extended face

The untrimmed extensions then leak into the reconstruction handed to BOPAlgo_MakerVolume - one adjacent face keeps 2% of its area, another contributes pieces sticking 10.7 mm above and below the solid, a third contributes its whole extended plane. MakerVolume builds 0 solids (no errors, one BOPAlgo_AlertFaceBuilderUnusedEdges warning) and RemoveFeature takes the !anExpS.More() branch.

On that part, reaching the fallback branch correlates perfectly with failure: 20 of the 48 faces reach it and all 20 fail, while all 9 faces removable today reach it zero times.

Proposed Solution

Treat the degenerate trim as the retry signal it already is:

  • FillGap::TrimFace raises a new myTrimDegenerated flag when it takes the "use all splits" branch.
  • FillGap::Perform repeats extension + trim with a doubled length, up to three attempts, stopping at the first attempt that trims cleanly.
  • If no attempt trims cleanly, the result of the first one is restored, so every case that passes through the fallback today produces exactly the result it produces now.
  • ExtendAdjacentFaces takes the length as a parameter instead of recomputing it.

The retry only runs for features that currently reach the degenerate branch, so unaffected cases pay nothing.

Validation

Original

image

Defeatured

image

Built and tested on Linux x64, GCC 13.3, on this branch's base (IR @ c7ea5bf865, 8.1.0.dev1).

testgrid boolean removefeatures, same build with the patch toggled:

result
without the patch 1 FAILED, 27 OK, 31 SKIPPED
with the patch 28 OK, 31 SKIPPED

The single failing case is the new test, and the per-case diff of the two runs is exactly one line:

< CASE boolean removefeatures G5: FAILED (error)
---
> CASE boolean removefeatures G5: OK

None of the 27 existing cases changes state.

Checks performed:

  • Relevant tests were added or updated when applicable.
  • Relevant local tests were run.

CLA Confirmation

  • I confirm that I have read the contribution requirements, and that I have signed and submitted the CLA or I am covered by an approved company CLA.

CLA ID / submission reference: Pending

Review Notes

This PR needs a data file added to the test dataset. tests/boolean/removefeatures/G5 loads bug1521_corner_round.step, which is attached to #1521 (https://gist.github.com/GabrielJMS/6c722dc8e675c42211af777e342aeb25) and is not yet in opencascade-dataset. The test will report SKIPPED until it is added - happy to submit it wherever it should go.

31 of the 58 removefeatures cases were SKIPPED locally because their data files are absent from the public opencascade-dataset-7.9.0 archive (bug28840_*.brep, bug29481_L3.step, bug29481_window_slots.brep, bug30094.brep, bug26689_nist_ctc_01_asme1_ap242.stp), so I could not exercise those - CI covers them.

The number of attempts (3) and the doubling factor are the conservative values that fix the reported part; happy to change either if you would rather scale the extension differently, for instance from the bounding box of the adjacent faces instead of retrying.

@GabrielJMS

Copy link
Copy Markdown
Author

@dpasukhi Sorry, I deleted my previous comments because I think it’s actually a completely different issue, unrelated to this bug.

I’m now trying to defeature the fillet chain {113, 114, 115, 116, 117, 134, 137} on the attached shape, but I haven’t been able to figure out what the problem is with this one.

So, from my point of view, I think the fix is safe to go ahead with. Concerning this other case, I haven’t given up, I’ll keep investigating and propose a new PR if I figure it out.

piston.zip

@dpasukhi
dpasukhi self-requested a review September 3, 2026 07:34
@dpasukhi dpasukhi added this to the Release 8.0.2 milestone Sep 3, 2026
@dpasukhi dpasukhi added 1. Modeling Boolean operations, offsets, primitives, any conversion, brep builders and etc... 3. CLA approved User has the signed CLA and ready to review or integration processes labels Sep 3, 2026
@GabrielJMS
GabrielJMS marked this pull request as ready for review September 3, 2026 14:04
@GabrielJMS

Copy link
Copy Markdown
Author

@dpasukhi Sorry, I deleted my previous comments because I think it’s actually a completely different issue, unrelated to this bug.

I’m now trying to defeature the fillet chain {113, 114, 115, 116, 117, 134, 137} on the attached shape, but I haven’t been able to figure out what the problem is with this one.

So, from my point of view, I think the fix is safe to go ahead with. Concerning this other case, I haven’t given up, I’ll keep investigating and propose a new PR if I figure it out.

piston.zip

Found a solution inspired by Woo's cell-based decomposition: #1524

@dpasukhi

dpasukhi commented Sep 3, 2026

Copy link
Copy Markdown
Member

There is no delays from your side ;)

At the moment, I have no ability to validate your PR :( probably it will be opened up to next weekends or maybe I will have some time on Sunday.

Thank you for your contribution

@dpasukhi

dpasukhi commented Sep 3, 2026 •

Copy link
Copy Markdown
Member

Also if possible, it Is better to create "synthetic" Google test (s) which can be unit or integration in some way. If you want screenshots, then draw still accepted, but better with no external files

We are no longer takes draw tests with external files (to simplify future life). If it is possible somehow to "isolate" the case to some syntactic geometry/topology generated by c++ as a GTest it would be amazing.

Your brep/step files will be used during review, but they will be not be merged :( im sorry

@GabrielJMS

Copy link
Copy Markdown
Author

Also if possible, it Is better to create "synthetic" Google test (s) which can be unit or integration in some way. If you want screenshots, then draw still accepted, but better with no external files

We are no longer takes draw tests with external files (to simplify future life). If it is possible somehow to "isolate" the case to some syntactic geometry/topology generated by c++ as a GTest it would be amazing.

Your brep/step files will be used during review, but they will be not be merged :( im sorry

Ok. Thank you! Fixed!

@GabrielJMS
GabrielJMS force-pushed the fix/defeaturing-extension-retry branch from 1db99e4 to 59e0d84 Compare September 3, 2026 22:09
FillGap::ExtendAdjacentFaces sized the extension of the adjacent faces from
the bounding box of the feature alone. That length is unrelated to the
distance those faces have to span to intersect each other, and for a small
feature between faces meeting at a shallow angle it is too short.

When the extended faces do not reach each other, no closing edge appears in
their intersection, every split of an extended face keeps a free boundary
edge of the extension, and TrimFace falls into its last-resort branch that
keeps all splits. The untrimmed extensions then leak into the reconstruction
handed to BOPAlgo_MakerVolume, which builds no solid at all, and the feature
is reported as BOPAlgo_AlertUnableToRemoveTheFeature.

Treat that degenerate trim as the retry signal it already is: TrimFace now
raises myTrimDegenerated, and FillGap::Perform repeats the extension and the
trim with a doubled length. If none of the attempts trims cleanly the result
of the first one is kept, so the cases that pass through the fallback today
are left unchanged.

The distance to reach is the one at which the adjacent surfaces meet, which
grows without bound as the angle between them closes and cannot be predicted
from the feature, so the doubling stops on the size of the solids the feature
belongs to: the last attempt is the first extension spanning the whole model,
and beyond that length the extended faces already cover it. The number of
attempts is never less than three, which keeps every case that succeeds today
on the attempt it succeeds on now. Only the cases that are failing pay for the
additional attempts, and only until one of them trims cleanly.

The regression test builds the case from primitives: a wedge of 15 degrees
with a short round of radius 5 on its sharp edge. The round measures about 12
across, while the tangency lines of its faces stand about 38 away from the
edge they replace, so the adjacent faces have to be extended by several times
the size of the feature before they meet. Removing the round must give back
exactly the sharp wedge it was built on.

Fixes Open-Cascade-SAS#1521

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@GabrielJMS
GabrielJMS force-pushed the fix/defeaturing-extension-retry branch from 59e0d84 to c1037f5 Compare September 4, 2026 12:09
@BarionLP

BarionLP commented Sep 21, 2026 •

Copy link
Copy Markdown

using this branch i was able to remove multiple features that I previously had to do by hand, thanks

@BarionLP

Copy link
Copy Markdown

i think there could be a bug:
If the intersection of extended faces fails outright on a retry (BOPAlgo_AlertBuilderFailed), TrimExtendedFaces returns early with myFaces empty and myTrimDegenerated == false. The loop takes that as success with zero reconstructed faces,
throwing away the (partial) result of attempt 0.

@GabrielJMS

Copy link
Copy Markdown
Author

@BarionLP Thank you for the feedback! I will investigate it!

Also, have you tried it with this branch? #1525

I’ve broken down the fixes into a series of chained PRs to address the different bugs I found and make the review easier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

1. Modeling Boolean operations, offsets, primitives, any conversion, brep builders and etc... 3. CLA approved User has the signed CLA and ready to review or integration processes

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

Modeling Algorithms - Defeaturing cannot remove a corner round: adjacent-face extension is sized by the feature, not by the distance the faces must span

3 participants