Skip to content

Commit f0ac4cb

Browse files
Fix BSplineApprox :Average knot distribution issue
Co-authored-by: SouthEndMusic <[email protected]>
1 parent 069bb0f commit f0ac4cb

File tree

2 files changed

+57
-7
lines changed

2 files changed

+57
-7
lines changed

src/interpolation_caches.jl

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,15 +1098,33 @@ function BSplineApprox(
10981098
k[i] = k[1] + (i - d - 1) // (h - d) * (k[end] - k[1])
10991099
end
11001100
elseif knotVecType == :Average
1101-
# NOTE: verify that average method can be applied when size of k is less than size of p
11021101
# average spaced knot vector
1103-
idx = 1
1104-
if d + 2 <= h
1102+
# We need h - d - 1 internal knots distributed across the parameter domain
1103+
num_internal_knots = h - d - 1
1104+
if num_internal_knots > 0
1105+
# First internal knot uses the same formula as original
11051106
k[d + 2] = 1 // d * ps[d]
1106-
end
1107-
for i in (d + 3):h
1108-
k[i] = 1 // d * (ps[idx + d] - ps[idx])
1109-
idx += 1
1107+
1108+
# For remaining knots, distribute the sampling across the ps array
1109+
if num_internal_knots > 1
1110+
for i in 2:num_internal_knots
1111+
knot_idx = d + 1 + i
1112+
# In the original algorithm, idx goes from 1 to n-d-2
1113+
# We want to distribute our sampling across this same range
1114+
min_idx = 1
1115+
max_idx = n - d - 2 # This gives us the same range as the original
1116+
1117+
if max_idx >= min_idx
1118+
# Linear interpolation to distribute sampling points
1119+
sample_idx = min_idx + round(Int, (i - 2) * (max_idx - min_idx) / (num_internal_knots - 2))
1120+
sample_idx = min(sample_idx, max_idx)
1121+
1122+
# Use the difference formula: (ps[sample_idx + d] - ps[sample_idx]) / d
1123+
ps_high_idx = sample_idx + d
1124+
k[knot_idx] = 1 // d * (ps[ps_high_idx] - ps[sample_idx])
1125+
end
1126+
end
1127+
end
11101128
end
11111129
end
11121130
# control points

test/interpolation_tests.jl

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -914,6 +914,38 @@ end
914914
u_test = reduce(hcat, A.(t_test))
915915
@test isapprox(u_test, f_test, atol = 1e-2)
916916
end
917+
918+
@testset ":Average knot distribution" begin
919+
# Test for proper knot distribution across parameter domain (Issue #439)
920+
x_test = 0:0.1:10
921+
y_test = randn(101)
922+
sp = BSplineApprox(y_test, x_test, 3, 20, :ArcLen, :Average)
923+
924+
# Extract internal knots (skip the repeated boundary knots)
925+
d = sp.d
926+
h = sp.h
927+
internal_knots = sp.k[(d+2):h]
928+
929+
# Check that knots span a reasonable portion of the parameter domain
930+
param_range = maximum(sp.p) - minimum(sp.p)
931+
knot_coverage = maximum(internal_knots) - minimum(internal_knots)
932+
coverage_ratio = knot_coverage / param_range
933+
934+
# The coverage ratio should be at least 0.8 (80% of parameter domain)
935+
# Before the fix, this was only ~0.136 (13.6%)
936+
@test coverage_ratio >= 0.8
937+
938+
# Test that the interpolation works correctly
939+
@test sp(x_test[1]) y_test[1]
940+
@test sp(x_test[end]) y_test[end]
941+
942+
# Test interpolation at some intermediate points
943+
test_points = [2.5, 5.0, 7.5]
944+
for t in test_points
945+
@test !isnan(sp(t))
946+
@test isfinite(sp(t))
947+
end
948+
end
917949
end
918950
end
919951

0 commit comments

Comments
 (0)