Blend Suppression#

Topological suppression of recognized blends with Euler operators.

Note

This document is intended for developers working on the shape_editing.euler module. Read Euler Operators first for the topological primitives it relies on.

Blend Suppression#

This document describes the topological blend-suppression pipeline in volmdlr_tools.shape_editing.euler. It removes recognized blends (fillets and rounds) from a solid by applying Euler operators locally around each blend and then rebuilding only the affected geometry, rather than deleting faces and asking the kernel to heal the result globally.

Status: proof-of-mechanism. The pipeline correctly suppresses several canonical configurations (single edge blends and edge/vertex-blend chains on planar and simple curved supports) but does not yet match the robustness claimed in the source papers. See Current status and known limitations below.

Background and academic basis#

The approach follows three papers:

  • Venkataraman & Sohoni (2001), “Blend Recognition Algorithm and Applications.” — how to recognize a blend from its boundary topology: a blend face is a constant- or variable-radius patch bounded by spring edges (the smooth tangency curves where it joins its support faces) and terminated at its ends by cross edges (shared with neighbouring blends in a chain) or terminating edges (shared with the ordinary faces that cap the chain).

  • Venkataraman, Sohoni & Elber (2002), “Removal of Blends from Boundary Representation Models.” — how to suppress a recognized blend: reconstruct the geometry the blend replaced by extending and intersecting its support faces, after a topological edit that re-glues those supports.

  • Slyadnev & Turlapov (2020), “Simplification of CAD Models by Automatic Recognition and Suppression of Blend Chains.” — how to drive the suppression with Euler operators so the model stays a valid manifold at every step, and how to treat a whole blend chain as one suppression unit.

Why Euler operators instead of direct face removal?#

volmdlr_tools already has a defeaturing path built on the kernel’s own tools — BRepTools_ReShape for soft (isolated) features and BRepAlgoAPI_Defeaturing for hard ones (see Shape Editing Architecture). Those work well for many features. So why a second, Euler-operator-based path specifically for blends?

1. A blend removal is fundamentally a local re-gluing, and global healers solve a harder problem than necessary. Deleting a fillet face and asking BRepAlgoAPI_Defeaturing to close the gap makes the kernel re-extend the surrounding surfaces, recompute all their mutual intersections, and rebuild the affected region of the solid as a global boolean-style operation. For a fillet whose effect is purely local — two known support faces that simply need to meet again at a sharp edge — that machinery is both heavier than the problem and harder to predict. The Euler-operator recipe states the answer directly: kill these boundary edges, kill the blend face, weld the two supports along the surviving spring edge.

2. Validity is guaranteed by construction, not checked after the fact. Every Euler operator preserves the Euler–Poincaré invariant (V - E + F - (L - F) - 2(S - G) = 0), so the intermediate model is always a valid manifold. A face-removal-plus-heal step, by contrast, can silently produce a non-manifold or self-intersecting result that only a posterior BRepCheck_Analyzer pass will catch — at which point the operation must be discarded wholesale. With Euler operators the topology is never in doubt; only the geometry reconstruction can fail, and it fails on a single well-posed intersection that can be rolled back cleanly.

3. Topology and geometry are decoupled, which makes failures legible. The topological recipe is exact and identical regardless of the blend’s radius or whether its supports are planar or curved. The geometry is reconstructed separately as a set of surface/curve intersections. When a blend cannot be suppressed, the failure is localized to a specific edge reconstruction with a specific reason (“carrier surfaces do not intersect”, “restored corner is degenerate”), instead of an opaque “the boolean operation failed”.

4. Chains can be suppressed coherently. A run of fillets meeting at vertex blends shares cross edges and corner vertices. Euler operators let the whole chain be edited on one shared engine — every cross edge and shared corner consumed before any geometry is rebuilt — so a corner can be resolved against the real faces that survive, not against a neighbouring blend that is itself about to disappear. A face-by-face global heal has no natural place to express that ordering.

The trade-off is that the geometry-reconstruction half must be written by hand per carrier type (planar, cylindrical, …), whereas the kernel healer covers many surface types out of the box. This is the central reason the Euler path is currently a proof-of-mechanism rather than a drop-in replacement.

The pipeline#

            ┌─────────────────────────┐
   solid ──►│  1. Blend recognition   │  BlendExtractor over the AAG
            │     (spring/cross/term   │  → blend faces + chains, each face
            │      edge classification)│    tagged with a BlendAttribute
            └────────────┬────────────┘
                         ▼
            ┌─────────────────────────┐
            │  2. Condition dispatch   │  recognize_condition()
            │     (local topology →    │  → FFIsolated / FFOrdinaryEBF /
            │      suppression recipe) │    FFOrdinaryVBF
            └────────────┬────────────┘
                         ▼
            ┌─────────────────────────┐
            │  3. Euler-operator edit  │  EulerEngine: KEV / KEF / KFMV,
            │     (topology only)      │  staged, history-tracked
            └────────────┬────────────┘
                         ▼
            ┌─────────────────────────┐
            │  4. Geometry             │  normalize_edges /
            │     normalization        │  normalize_chain_edges
            │     (rebuild affected    │  → restored sharp edges + corners
            │      edges only)         │
            └────────────┬────────────┘
                         ▼
                   suppressed solid

1. Recognition#

BlendExtractor walks the Attributed Adjacency Graph and tags each blend face with a BlendAttribute recording its kind (Ordinary edge blend or Vertex blend) and the indices of its spring, cross and terminating edges. Tangentially-connected blend faces are grouped into chains. (Recognition is covered in depth by the feature-recognition documentation; this pipeline consumes its output.)

2. Condition dispatch — recognize_condition()#

A blend is suppressed by the Euler sequence implied by its local topology — the arrangement, on the AAG, of its bounding edges and the support/terminating faces around it. recognize_condition() tries an extensible registry of BlendTopoCondition subclasses and returns the first that matches:

Condition

Matches

Recipe

FFIsolatedCondition

edge blend with 2 spring + 2 terminating edges, no cross edges (a standalone fillet)

KEV each terminating edge, then KEF the blend face

FFOrdinaryEBFCondition

edge blend inside a chain: 2 spring edges + a boundary of 2 cross edges (or 1 cross + 1 terminating)

KEV each boundary edge, then KEF the blend face

FFOrdinaryVBFCondition

a vertex blend

KFMV — collapse the patch to its corner vertex

Each condition also declares exactly which edges to rebuild afterwards and which vertices stay frozen, so normalization never has to guess.

3. Topological edit#

The condition’s recipe runs on a shared EulerEngine (see Euler Operators). For an isolated fillet this is, in essence:

engine = EulerEngine(solid)
condition = recognize_condition(aag, blend_face_index)
condition.suppress(engine)   # KEV + KEV + KEF, staged with history actualize

After this step the blend face is gone and the two support faces are topologically welded along the surviving spring edge — but that edge still carries its old curve.

4. Geometry normalization#

normalize_edges (single blend) and normalize_chain_edges (whole chain) rebuild only the declared affected edges:

  • The survivor (sharp) edge becomes the intersection of its two support surfaces. For two planar supports this is their intersection line; for curved supports it is the intersection branch nearest the old edge.

  • A moved corner is recomputed as that curve met with the declared terminating face, or — in a chain — as the triple point where two restored edges’ carrier surfaces meet. A corner the suppression did not move keeps its position. If the recomputed point lands on a vertex already present in the model, that existing vertex is reused rather than a coincident-but-distinct one minted — keeping the restored edges welded to the surrounding topology.

  • A setback side edge that collapses is removed. When a blend abuts an existing model vertex, one of the support faces’ short side edges (its length was the blend radius) shrinks to zero as the sharp edge is restored — its moved corner snaps onto its other endpoint. Such an edge is dropped (an in-normalizer kill-edge) instead of rebuilt, welding the corner to that vertex, which is exactly what direct face removal produces.

  • Parametric curves are re-derived on each host face and a final same-parameter / shape-fix pass closes the model.

If any reconstruction is ill-posed (surfaces that do not intersect, a corner that collapses to zero length), normalization raises and the driver rolls the suppression back — keeping the blend is always preferable to emitting a broken solid (Golden Rule: no silent failures).

Public entry points#

from volmdlr_tools.shape_editing.euler.suppressor import suppress_blend, suppress_blend_chain

# Suppress a single recognized blend face (0-based index, AAG with recognition already run):
result = suppress_blend(solid, blend_face_index, aag)

# Suppress a whole chain at once (edge blends are consumed before vertex blends):
result = suppress_blend_chain(solid, chain_face_indices, aag)

For incremental, repeated suppression across a model, IncrementalBlendRemover drives this engine chain-by-chain and tracks history. The Euler path is the default (use_euler=True); pass use_euler=False to fall back to the legacy global face-removal path.

Current status and known limitations#

The Euler path is a proof-of-mechanism and does not yet reach the robustness reported in the papers. Known gaps:

  • Carrier coverage. Geometry normalization is implemented for planar carriers and simple curved carriers (single-branch surface intersections). Multi-branch and tangential curved cases are only partially handled.

  • Topological cases. Only the three conditions above are registered. Blends whose local topology does not match — overflow configurations, or blends whose supports share no direct edge — are not recognized and are left in place.

  • No variable-radius reconstruction. Variable-radius and spherical-corner reconstructions beyond the vertex-blend collapse are not yet implemented.

When a blend is declined, the surrounding model is never left in a partial state — the suppression is atomic and rolls back to the input solid.

See also#

See Also#