Skip to content

Don't trim loft at profiles by default - #1331

Draft
joergbrech wants to merge 65 commits into
mainfrom
untrimmed-loft
Draft

Don't trim loft at profiles by default#1331
joergbrech wants to merge 65 commits into
mainfrom
untrimmed-loft

Conversation

@joergbrech

@joergbrech joergbrech commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Don't trim loft at profiles by default

Fixes #1262.

By default, GetLoft() now returns an untrimmed loft (a single continuous surface) for wings, fuselages, ducts, vessels and engine pylons. The previous trimmed behavior (UV seams / one face per profile segment) is still available via the new GetTrimmedLoft().

Motivation

TiGL 3 lofts a wing/fuselage as a single surface across all sections. To preserve the historical per-segment face count we used to trim the result at every profile. That trimming hurts downstream post-processing: meshers are unnecessarily constrained by the extra face boundaries, and the seams reduce continuity for boolean operations. After discussion with users we accept the breaking change and skip profile trimming by default.

@sdeinert, this removes trimming as kinks as well.

⚠️ A word of warning

This is a breaking change: GetLoft() output (face count, UV parameterization) changes for wings and fuselages. Use GetTrimmedLoft() to restore the old result.

There should be no geometric difference, because we just trimmed at isocurves previously, but some algorithms have slightly different behavior when confronted with trimmed vs. untrimmed faces. For instance, intersection algorithms or volume calculations in OCCT use approximations for intersection/boundary curves, and these approximations might cut off kinks and corners in the untrimmed loft, which were not present in the trimmed loft faces. This is especially apparent for wings, which are lofted linearly.

While all tests succeed, there is a real danger that due to this difference we introduce unnoticed and untested regressions. In the worst case, wing cutouts or other operations suddenly fail for CPACS configurations which worked before with the trimmed lofts.

One concrete regression was discovered and fixed during this development: leading edge devices (control surfaces on the leading edge) rely on boolean intersection between the device flap geometry and the wing loft. When the wing loft became untrimmed, the boolean returned an empty shape, silently dropping the device geometry. The fix builds leading edge device geometry against the trimmed clean shape via the new GetTrimmedWingCleanShape().

Architecture

Each affected component builds both a trimmed and an untrimmed loft lazily and caches them separately.

  • CTiglMakeLoft — new setEnableProfileCutting(bool) (default false).
    When false, the sewn faces are returned directly (no CutShellAtUVParameters
    / CutShellAtKinks). When true, the original cutting behavior is applied.

  • ITiglGeometricComponent / CTiglAbstractGeometricComponent — add
    GetTrimmedLoft(); the base implementation falls back to GetLoft().

  • CCPACSWing — split wingCleanShape cache into
    wingCleanShapeUntrimmed / wingCleanShapeTrimmed; GetLoft(),
    BuildLoft() and GetWingCleanShape() now return untrimmed. Adds
    GetUntrimmedLoft(), GetTrimmedLoft(), GetTrimmedWingCleanShape().
    CTiglWingBuilder takes an enableProfileCutting flag.

  • CCPACSFuselage — split cleanLoft into
    cleanLoftUntrimmed / cleanLoftTrimmed; adds GetUntrimmedLoft() /
    GetTrimmedLoft() (duct cutouts still applied to both).

  • CCPACSDuct, CCPACSVessel, CCPACSEnginePylon — gain trimmed/untrimmed
    caches and GetTrimmedLoft() / GetUntrimmedLoft(). Vessels built from design
    parameters have no trimmed loft (GetTrimmedLoft() returns null / falls back).

  • CCPACSFuselageSegment / CCPACSWingSegment — per-segment lofts are now
    extracted from the parent's trimmed loft, so per-profile face indexing is
    preserved.

  • CCPACSComponent — adds GetTrimmedLoft() / GetUntrimmedLoft() for API
    consistency; both return GetLoft() (generic multi-segment shapes are not
    trimmable).

  • CTiglPatchShell — hardened against empty / single-face / compound inputs
    (throws or handles gracefully instead of failing on degenerate shells).

  • Control surface devices (leading edge devices) — contained a small fiddle factor that was previously introduced as a workaround. With untrimmed lofts, the situation is reversed. Adding it breaks the geometry of simpletest-flaps.cpacs.xml, without it it looks good. The boolean operations around control devices are fragile in general, regardless of this PR.

TiGLCreator

Untrimmed lofts need finer tessellation to render cleanly. Immediately after the shape especially fuselages looked like someone had taken a bite from them. The reason for this is that the tesselation accuracy is a relative value with respect to the bounding box diaogonal of a shape. Before, this meant that tesselation accuracy was coupled to segment bounds, now it is coupled to the fuselage bounds.

The default tessellation accuracy value and slider endpoints were re-tuned, an angular deviation (tesselationDeviationAngle()) is now applied to displayed shapes, and slider values are clamped to valid ranges. With the application of the angular deviation, we an now actually increase the default tesselation by a factor of 10 and still get reasonable looking results. This might speed up loading times in TiGLCreator.

API summary

Class GetLoft() GetUntrimmedLoft() GetTrimmedLoft()
CCPACSWing Untrimmed (new default) Untrimmed Trimmed (legacy)
CCPACSFuselage Untrimmed (new default) Untrimmed Trimmed (legacy)
CCPACSDuct Untrimmed Untrimmed Trimmed
CCPACSVessel Untrimmed Untrimmed Trimmed (null if built from design parameters)
CCPACSEnginePylon Untrimmed Untrimmed Trimmed
CCPACSComponent Untrimmed == GetLoft() == GetLoft()

CCPACSWing additionally exposes GetTrimmedWingCleanShape() alongside the
existing GetWingCleanShape() (untrimmed).

How Has This Been Tested?

  • Full unit + integration test suites pass.
  • Updated face-count / face-trait expectations for untrimmed geometry
    (tiglExports.cpp 24→16, tiglSystems.cpp 4→3 and 10→3, tiglTanks.cpp).
  • Tests that must assert the legacy layout now call GetTrimmedLoft()
    (testDuct.cpp, testFuselageStandardProfileSuperellipse.cpp).
  • tiglGetCrossSectionArea tolerance relaxed to accommodate OCCT's b-spline
    approximation of intersection boundaries on the untrimmed surface.
  • New regression test leadingEdgeDeviceFlapShapeNotEmpty guards the control
    surface device boolean intersection fix.

Screenshots

Before After
Before After

Disclaimer:

This PR has been co-authored with AI, but all AI parts have been thoroughly checked by me.

Checklist:

  • At least one test for the new functionality was added. (see tiglLoftTrimming.cpp, tiglControlSurfaceDevice.cpp)
  • New classes have been added to the Python interface. (does not apply - no new public classes)
  • The code is properly documented with doxygen docstrings
  • Changes are documented at the top of ChangeLog.md

@codecov

codecov Bot commented Jun 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.91667% with 53 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.14%. Comparing base (2f0d279) to head (f6cac5d).
⚠️ Report is 51 commits behind head on main.

Files with missing lines Patch % Lines
src/geometry/CTiglPatchShell.cpp 55.17% 13 Missing ⚠️
src/fuselage/CCPACSFuselageSegment.cpp 57.14% 9 Missing ⚠️
src/engine_pylon/CCPACSEnginePylon.cpp 62.50% 6 Missing ⚠️
src/wing/CCPACSWingSegment.cpp 50.00% 6 Missing ⚠️
src/fuselage/CCPACSFuselage.cpp 83.87% 5 Missing ⚠️
src/fuelTanks/CCPACSVessel.cpp 91.30% 4 Missing ⚠️
src/systems/CCPACSComponent.cpp 0.00% 4 Missing ⚠️
src/wing/CTiglWingBuilder.cpp 70.00% 3 Missing ⚠️
src/engine_pylon/CTiglEnginePylonBuilder.cpp 0.00% 1 Missing ⚠️
src/geometry/ITiglGeometricComponent.h 0.00% 1 Missing ⚠️
... and 1 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1331      +/-   ##
==========================================
- Coverage   73.20%   73.14%   -0.07%     
==========================================
  Files         324      324              
  Lines       28546    28683     +137     
==========================================
+ Hits        20898    20980      +82     
- Misses       7648     7703      +55     
Flag Coverage Δ
unittests 73.14% <77.91%> (-0.07%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/common/tiglcommonfunctions.cpp 78.65% <100.00%> (+0.13%) ⬆️
src/common/tiglcommonfunctions.h 100.00% <ø> (ø)
...ices/CCPACSControlSurfaceOuterShapeLeadingEdge.cpp 87.09% <100.00%> (-3.15%) ⬇️
src/ducts/CCPACSDuct.cpp 91.46% <100.00%> (+1.60%) ⬆️
src/engine_pylon/CCPACSEnginePylon.h 50.00% <ø> (ø)
src/engine_pylon/CTiglEnginePylonBuilder.h 100.00% <100.00%> (ø)
src/fuselage/CCPACSFuselage.h 100.00% <ø> (ø)
src/fuselage/CCPACSFuselageProfile.cpp 87.50% <100.00%> (ø)
src/geometry/CTiglMakeLoft.cpp 78.84% <100.00%> (+1.51%) ⬆️
src/wing/CCPACSWing.h 100.00% <ø> (ø)
... and 11 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@joergbrech joergbrech changed the title breaking change: don't trim loft at profiles Don't trim loft at profiles by default Jun 26, 2026
@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

PR Reminders

This PR has changes in src/, TIGLCreator/, and tests/unittests/ but not in tests/integrationtests/, tests/python/, or bindings/python_internal/. ChangeLog.md has changes.

  • 💡 Tip: Please check if new classes need to be added to the Python bindings

This is an automated reminder from CI

@joergbrech

joergbrech commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Because flap geometries are somewhat fragile in TiGL and I am worried, that this PR introduces a regression, I decided to do a manual visual analysis of the flaps of a bunch of CPACS files we have. I only checked geometry, no other issues like weird deflection paths or intersecting geometries during deflection etc.

TL;DR: Performance is consistently better on this branch, but flap and slat geometry regresses in a majority of configs — the wing cutouts near trailing edge devices and side caps on leading edge devices are frequently broken. Overall, main is still the better base to ship.


Legend

Symbol Explanation
Everything looks good
⚠️ Minor geometric issues, e.g. missing side caps, not watertight for at least one moveable
Major geometric issues for at least one moveable
Non-geometric failure (e.g. due to bad/outdated CPACS definition)

Test Suite

Config On main On this branch Remark
CPACS_30_aircraft_flaps.xml ❌ D150_VAMP_w1_CompSeg1_innerLED is missing PR wins
CPACS_30_D150.xml Tie
CPACS_30_D250_10.xml CPACS error in LE definition
D150_v30.xml ❌ wing with cutouts bad near D150_OuterFlap ❌ wing with cutouts is missing upper surface, cutout shape generally bad. D150_InnerFlap geometry is missing. Both fail, this PR slightly worse
D150WithGuides.xml Error: Unsupported type for iso line transformation referenced by UID XXX. Where XXX is the UID of a control device. Fails for wing, HTP and VTP
simpletest-flaps.cpacs.xml ⚠️ InnerLED geometry has a missing outer side cap. The adjacent face in the wing cutout is is missing as well. main wins

Digital Hangar

Config On main On this branch Remark
D250-TF-2040 ❌ Wing cut out near TE is broken ⚠️ InnerSlat1UID geometry has a missing outer side cap. PR wins
D250-TFLH2-MHEP-2040 ❌ InnerFlap1UID Boolean operation failed. The geometry contains the big box-shaped cutout shape. Wing Cutout: Upper Wing surface is not properly trimmed for the TEDs (Cutouts for LEDs look fine). InnerSlat1UID is missing the outer sidecap. main wins
DLH25 ⚠️ OuterSlat1UID has weird geometric artifacts, that look like four lines sticking up from its TE, normal to the upper wing surface ⚠️ InnerSlat1UID has missing outer side cap. OuterSlat1UID has weird geometric artifacts, that look like four lines sticking up from its TE, normal to the upper wing surface (Note: stabilizer has a nested elevator that is not cutout. Nested flaps are currently not supported). Slightly better on main
GA9-TP Tie
D150-BWB-2035 ❌ OuterFlap1UID geometry missing, wing cutout of upper wing surface not good near TEDs. main wins, but flap loading is much faster on PR
T25 ❌ Wing Cutout near TE is not good. InnerFlap1UID Boolean operation failed. The geometry contains the big box-shaped cutout shape. OuterSlat3UID is missing. (Note: stabilizer has a nested elevator that is not cutout. Nested flaps are currently not supported). Clear win for main.

Other (local/confidential)

Config On main On this branch Remark
DLR-F25_v1 ⚠️ geometric artifacts: Lines sticking out in normal direction of wing surface near LEDs ⚠️ InnerSlat1UID is missing outer sidecap Tie, both versions have slightly different minor issues
DLR-F25-v0.6-Oct2025 ⚠️ Geometric artifacts: Lines sticking out in upper wing normal direction of LEDs' TE ❌ InnerSlat1UID is missing both side caps. OuterSlat7UID is missing. (Note: stabilizer has a nested elevator that is not cutout. Nested flaps are currently not supported)*.
D239L_wingStructureAdapted _fuelSystem_0623 ❌ Stabilizer is cut from entire HTP, but after the Boolean, the outer side cap of the HTP is still there. InnerFlap1UID and outerFlap1UID are missing. Wing Cutout near TEDs failed. InnerSlat1 is missing outer side cap. OuterSlat3 is missing outer sidecap. Just visualization: Default tesselation accuracy insufficent at winglets. (Note: stabilizer has a nested elevator that is not cutout. Nested flaps are currently not supported)*. Clear win for main
codex_example_aircraft Performance better on PR branch
ATLAS_new Performance better on PR branch
ARB_ATLAS_17_12_2020 _ExactFluegelKalibrierung _delisOut_LOADzero _external_simple ⚠️ Lines sticking out normally from LEDs' TE ❌ Wing Cutout near TEDs not good. outerFlap1UID still contains the big box-shaped cutout shape. main wins
D239+_Virenfrei_cpacsResult_v1 ❌ Wing with cutouts broken, only few faces remain. Visual lines sticking out of LEDs' TEs, normal to wing surface ⚠️ InnerSlat1UID is missing outer side cap PR wins
EXReference_v2 ❌ Wing with cutouts completely broken. Only a few surfaces remain. Geometric artifact: Many flaps have lines sticking out normally to the surface at their TEs. InnerFlap1UID is missing. InnerSlat1UID has no outer side cap (Note: stabilizer has a nested elevator that is not cutout. Nested flaps are currently not supported)*. Performance better on PR branch, but main wins
EXBaseline_v2 (Note: stabilizer has a nested elevator that is not cutout. Nested flaps are currently not supported)*. Tie for geometry. Performance better on PR branch
EX450 ⚠️ Geometric artifacts: Lines sticking out of LEDs' TEs ❌ innerSlat1UID is missing (Note: stabilizer has a nested elevator that is not cutout. Nested flaps are currently not supported)*. main wins
D369 ⚠️ InnerSlat1UID is missing its outer side cap. (Note: stabilizer has a nested elevator that is not cutout. Nested flaps are currently not supported)*. Performance better on PR branch. main wins
D250M-TP ❌ wing cutouts failed near TEDs, InnerFlap1has thin artifact faces that span the while wing along its lower surface back to front. Slats are good ❌ Wing cutouts failed near TEDs. AileronUID and innerFlap1UID are missing. Slats are good (Note: stabilizer has a nested elevator that is not cutout. Nested flaps are currently not supported)*. Tie: main and PR branch bad
ATR72 ❌Wing cutouts failed near TEDs. ❌Wing cutouts failed near TEDs. (Note: stabilizer has a nested elevator that is not cutout. Nested flaps are currently not supported)*. Tie

Remark: A config with ❌ might still have good geometries for almost all flaps and I am reporting only failures here. So the situation is not quite as dire as it looks. I commented on performance only if I noticed that it was extremely slow.

Conclusion

Performance is consistently better on this branch (sometimes dramatically so, e.g. on D150-BWB-2035 or the EX* configs).

The boolean operations around control surface devices are clearly sensitive to trimmed vs. untrimmed loft input. In total, 10 out of 17 configs are better on main, 3 are better on this branch, and 4 are roughly equal. The PR consistently introduces missing outer side caps on InnerSlat1UID.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Return fuselage/wing lofts as one surface, not cut at the sections

1 participant