Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@
- `read_node_table` parses into a preallocated matrix instead of `reduce(vcat, …)`
over a generator, which was quadratic in the row count: ~21× faster on a 16 MB
surface table (2.49 s → 0.12 s), benefiting every existing dataset.
- `shrink_wrap` traces the rolling ball exactly — pivoting it around the cloud and
emitting the arcs its contact side sweeps (`pivot_contour`) — instead of thresholding
and marching-squares-tracing a distance field, so there is no grid resolution left to
set. `ShrinkWrap`'s `cell_size` is accordingly named `min_clearance`, still accepted
under the old name, and only floors `clearance`. The wrap sits at exactly `clearance`
from the cloud instead of a cell over it, so a V3 canopy's aft strip comes out
`2 * clearance` thick where the grid gave `3.5 * cell_size`. `min_concave_radius` also
stops costing anything, having padded the grid in both directions before: one V3 slice
at radius 0.4 drops from 173 ms to 10 ms, and at the default radius from 15 ms to 3 ms.

## VortexStepMethod v4.0.0 2026-08-03

Expand Down
10 changes: 6 additions & 4 deletions docs/src/airfoil_pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,12 @@ interior rib, spar and seam the cutting plane happened to cross.
## 2. Shrink-wrap

[`shrink_wrap`](@ref), configured by [`ShrinkWrap`](@ref), turns that noisy cloud into
a single clean closed airfoil. It builds a distance field on a grid, thresholds it at a
rolling-ball radius (bridging gaps between points and ignoring interior structure),
flood-fills the outside, erodes the boundary back to a small `clearance`, and traces the
resulting level set with marching squares. The contour is parameterised by arclength, so
a single clean closed airfoil. It pivots a ball of the `min_concave_radius` around the
outside of the cloud (bridging gaps between points and ignoring interior structure) and
takes what the ball's contact side sweeps: an arc of radius `clearance` about each point
it touches, joined by an arc of the ball radius across each gap it cannot enter. Those
arcs are the wrap boundary exactly, so there is no grid resolution to choose. The
contour is parameterised by arclength, so
the leading edge comes out genuinely round and the blunt trailing edge is capped by an
arc — and the output points are cosine-clustered toward both edges. The same wrapped
airfoil is what *both* backends analyse, so the geometry the polar is generated for
Expand Down
15 changes: 8 additions & 7 deletions docs/src/private_functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,17 @@ get_lower_upper
turn_trailing_edge!
```

### Shrink-wrap distance field
### Shrink-wrap rolling ball
```@docs
distance_parabolas!
squared_distance_transform!
grid_sampler
flood_outside
trace_level_set
point_buckets
turn_measure
pivot_step
push_arc!
pivot_contour
largest_linking_gap
enforce_min_spacing!
resample_arc
smooth_turning!
smoothed_curvature
```

### NeuralFoil network
Expand Down
4 changes: 2 additions & 2 deletions examples/V3_neuralfoil.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ ROTATION = I
# Marched leading-edge stations across the span (finer => smoother edge trace).
N_BINS = 100

# Shrink-wrap each raw slice into a clean closed airfoil: the distance-field wrap
# hugs the cloud at `clearance` (floored at one grid `cell_size`); a single-skin
# Shrink-wrap each raw slice into a clean closed airfoil: the rolling-ball wrap
# hugs the cloud at `clearance` (floored at `min_clearance`); a single-skin
# canopy becomes a thin capsule. `min_concave_radius` sets the fillet bridging the
# concave tube-canopy junction: 0.2 smooths the neck over entirely; the default
# (0.02) traces it, which NeuralFoil also handles fine.
Expand Down
4 changes: 2 additions & 2 deletions examples/ram_air_kite.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ N_SECTIONS = 10
# wingtips, which slice to degenerate airfoils that XFoil cannot analyse.
WINGTIP_DISTANCE = 0.1

# Shrink-wrap each raw slice into a clean closed airfoil: the distance-field wrap
# hugs the cloud at `clearance` (floored at one grid `cell_size`) with a round nose.
# Shrink-wrap each raw slice into a clean closed airfoil: the rolling-ball wrap
# hugs the cloud at `clearance` (floored at `min_clearance`) with a round nose.
WRAP = ShrinkWrap(clearance=0.0)

# 3D slice diagnostic (live preview): mesh + LE/TE curves + section contours, their
Expand Down
2 changes: 1 addition & 1 deletion src/airfoil_aero/airfoil_solvers/common.jl
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ into clean cosine panels and fit [`LeastSquaresFit`](@ref) Kulfan parameters to
XFoil consumes the coordinates directly, NeuralFoil the Kulfan parameters.

`flip_thickness_neg` folds a soft membrane about its lower surface for negative `delta`.
The re-wrap uses zero clearance (it hugs the deflected shape at grid resolution);
The re-wrap uses zero clearance (it hugs the deflected shape at `min_clearance`);
the rolling-ball wrap bridges the crease with a `min_concave_radius` fillet instead
of the overlapping panels that XFoil's own repaneling can hit there. The wrap runs
for every `delta` including `0`, so all deflections share the same node count
Expand Down
Loading
Loading