# splinefit: B-Spline Curve and Surface Fitting
A pure-Rust B-spline fitting library with an ergonomic high-level API, backed by a
line-by-line Rust translation of Paul Dierckx' classic Fortran fitting library.
No Fortran compiler, no C compiler, no build-time dependencies — just Rust.
The original Fortran library was written by Paul Dierckx in the mid-1980s and remains
one of the most complete and mathematically rigorous B-spline fitting libraries available.
It is described in detail in his book:
[Curve and Surface Fitting with Splines](https://www.google.com/books/edition/Curve_and_Surface_Fitting_with_Splines/-RIQ3SR0sZMC?hl=en),
Oxford University Press, 1993.
Numerical output is verified against [scipy](https://docs.scipy.org/doc/scipy/reference/interpolate.html),
which wraps the original Fortran library.
## Usage
Add this to your `Cargo.toml`:
```toml
[dependencies]
splinefit = "0.4.1"
```
## Example
Fit a smoothing cubic spline to noisy data, then evaluate it:
```rust
use splinefit::{CubicSplineFit, evaluate};
use std::f64::consts::PI;
let m = 20usize;
let spline = CubicSplineFit::new(x.clone(), y)
.smoothing_spline(0.05)
.expect("fit failed");
let y_fit = evaluate::evaluate(&spline, &x).expect("evaluate failed");
assert!((y_fit[0] - x[0].sin()).abs() < 0.1);
```
## Fit types
| `CubicSplineFit` | Smoothing / interpolating 1-D cubic spline |
| `LinearSplineFit` / `QuinticSplineFit` | Same, degree 1 or 5 |
| `CubicSplineFit2D` | Parametric cubic curve in 2-D |
| `CubicSplineFit3D` | Parametric cubic curve in 3-D |
| `ClosedCubicSplineFit2D` | Closed (periodic) parametric curve in 2-D |
| `ClosedCubicSplineFit3D` | Closed (periodic) parametric curve in 3-D |
Each fit type exposes three methods:
| `.interpolating_spline()` | Exact interpolation through all data points |
| `.smoothing_spline(rms)` | Fewest knots with RMS error ≤ `rms` |
| `.cardinal_spline(dt)` | Weighted least-squares on a fixed equidistant knot grid |
## Relationship to `dierckx-sys` and `splinify`
`dierckx-sys` wrapped the original Fortran sources via `gfortran`, requiring a Fortran
compiler at build time. `splinify` used `dierckx-sys` as its backend.
This crate (`splinefit`) combines the high-level API from `splinify` with a pure-Rust
translation of the Fortran engine, so it builds anywhere Rust builds with no external
toolchain dependencies.
## Performance
The pure-Rust engine matches or outperforms SciPy's Fortran-compiled FITPACK.
Benchmarks on Apple M1 (macOS), fitting and evaluating `sin(x)` on `[0, 2π]`:
**1-D smoothing fit**
| 50 | 17 µs | 21 µs |
| 200 | 57 µs | 62 µs |
| 1 000 | 279 µs | 282 µs |
| 5 000 | 1.35 ms | 1.4 ms |
**1-D evaluation (10× data points)**
| 50 | 12 µs | 18 µs |
| 200 | 50 µs | 61 µs |
| 1 000 | 254 µs | 307 µs |
| 5 000 | 1.27 ms | 1.5 ms |
**2-D parametric evaluation (10× data points)**
| 50 | 12 µs | 34 µs |
| 200 | 50 µs | 121 µs |
| 1 000 | 248 µs | 611 µs |
Reproduce with `cargo bench` (Rust) and `python3 benches/scipy_bench.py` (SciPy).
## WebAssembly / JavaScript
This crate compiles to WebAssembly via [`wasm-pack`](https://rustwasm.github.io/wasm-pack/).
Enable the `wasm` feature to expose a `CubicSpline` class to JavaScript.
```html
<script type="module">
import init, { CubicSpline } from "https://esm.sh/@harbik/splinefit";
await init();
const x = Float64Array.from({ length: 50 }, (_, i) => i * 2 * Math.PI / 49);
const y = x.map(Math.sin);
const spline = CubicSpline.smoothing(x, y, 0.05);
const xNew = Float64Array.from({ length: 200 }, (_, i) => i * 2 * Math.PI / 199);
const yFit = spline.evaluate(xNew);
const area = spline.integral(0, Math.PI); const zeros = spline.roots(); </script>
```
## License
The original Dierckx Fortran algorithms (on which this translation is based) were
downloaded from [netlib.org](http://www.netlib.org/dierckx/) and carry no license
restrictions. Please acknowledge Paul Dierckx' work in your projects.
All Rust source in this repository is © 2021–2026 Harbers Bik LLC, dual-licensed under:
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE))
* MIT license ([LICENSE-MIT](LICENSE-MIT))
at your option.
## Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.