Skip to content

Limited Loft surface repair #1353

Description

@gumyr

Loft between two planar sections can create BSpline side faces that fail offset

Problem

A user reported (#1351) that a part created with loft() in build123d v0.11.0 fails during a later offset() operation, while an equivalent part created with extrude() + draft() offsets successfully.

A simplified reproducer is:

skt2 = RectangleRounded(length, WIDTH, 7) - Pos(Y=-WIDTH / 2) * Rectangle(40, 20)
skt2 = fillet(skt2.vertices(), 2.5)

bottom_face = offset(skt2, -1).face()
top_face = (Pos(Z=40) * skt2).face()

loft_part = loft([bottom_face, top_face])
open_loft_part = offset(
    loft_part, -1, openings=loft_part.faces().filter_by(Axis.Z)
)  # RuntimeError

draft_part = extrude(skt2, 40)
draft_part = draft(
    draft_part.faces().filter_by(Axis.Z, reverse=True),
    Plane.XY.offset(40),
    -degrees(atan2(1, 40)),
)
open_draft_part = offset(draft_part, -1, openings=draft_part.faces().filter_by(Axis.Z))

Inspection shows that the lofted part contains faces with geometry types:

PLANE, CONE, BSPLINE

while the drafted part contains only:

PLANE, CONE

The BSPLINE side faces appear to be geometrically simple ruled faces. In some cases their boundary edges are:

LINE, BSPLINE, LINE, BSPLINE

or:

CIRCLE, BSPLINE, CIRCLE, BSPLINE

The BSpline edges appear to be longitudinal connector edges generated by OCCT’s BRepOffsetAPI_ThruSections. These surfaces are usually planar or conical, but they are not represented analytically, and BRepOffsetAPI_MakeThickSolid can fail when offsetting the resulting solid.

This may be related to OCCT/OCP behavior changes between the v0.10.0 and v0.11.0 dependency sets. build123d moved from cadquery-ocp >= 7.8, < 7.9 to cadquery-ocp-novtk >= 7.9, < 8.0.

Proposed Limited Fix

Keep the existing BRepOffsetAPI_ThruSections loft as the primary loft builder. After the loft is created, inspect the generated faces. For any face whose geom_type is BSPLINE, attempt a targeted analytic reconstruction.

This reconstruction would only be attempted for lofts with exactly two parallel planar input sections, where the BSpline face appears to be a simple ruled patch between corresponding section edges.

The cleanup would examine the BSpline face boundary. In the observed failure cases, these faces have boundary patterns such as:

LINE, BSPLINE, LINE, BSPLINE

or:

CIRCLE, BSPLINE, CIRCLE, BSPLINE

The non-BSpline edges are the original section/profile edges. The BSpline edges are longitudinal connector edges generated by OCCT. The proposed cleanup would keep the original section/profile edges, replace the longitudinal BSpline connector edges with straight connector edges, and then attempt to rebuild the face analytically.

Initial reconstruction cases:

  • LINE / LINE: attempt to rebuild the face as a planar face from the two line section edges and two straight longitudinal connector edges. This is not mathematically guaranteed to be planar in all cases, but should cover common user cases where the loft is equivalent to a drafted extrusion.
  • CIRCLE / CIRCLE: attempt to detect whether the two circular section edges define a cylindrical or conical patch from their centers, axes, and radii. If detected, rebuild the face as an analytic cylindrical or conical face.

If analytic reconstruction succeeds, replace the original BSpline face in the loft result. If reconstruction fails, leave the original BSpline face unchanged.

The fallback is therefore per-face, not all-or-nothing: unsupported or ambiguous BSpline faces remain as generated by BRepOffsetAPI_ThruSections.

Notes

This should not attempt to become a general BSpline-surface simplifier. The goal is a targeted fix for a common “draft-like loft between two planar profiles” case where the expected result is ruled and analytic.

This may also improve downstream operations such as offset() by avoiding unnecessary BSpline surfaces in simple lofts.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions