import { spline } from 'cubic-hermite-spline'
import { zeros, NdArray } from 'ndarray'
/**
* 双三次样条插值类
*/
class BicubicSpline {
private x: number[]
private y: number[]
private z: NdArray<number>
constructor(x: number[], y: number[], z: number[][]) {
this.x = x
this.y = y
const nx = x.length
const ny = y.length
if (nx < 2 || ny < 2) {
throw new Error("x and y must have at least two points.")
}
if (z.length !== ny || z.some(row => row.length !== nx)) {
throw new Error("z must be a matrix of size [ny][nx].")
}
// 将 z 转换为 NdArray 格式
this.z = zeros([ny, nx])
for (let i = 0
for (let j = 0
this.z.set(i, j, z[i][j])
}
}
}
/**
* 插值函数
*/
public interpolate(xVal: number, yVal: number): number {
const { x, y, z } = this
// 找到 xVal 和 yVal 所在的区间索引
const xi = this.findInterval(x, xVal)
const yi = this.findInterval(y, yVal)
// 提取局部网格点
const xLocal = [x[xi], x[xi + 1]]
const yLocal = [y[yi], y[yi + 1]]
const zLocal: number[][] = [
[z.get(yi, xi), z.get(yi, xi + 1)],
[z.get(yi + 1, xi), z.get(yi + 1, xi + 1)]
]
// 使用 cubic-hermite-spline 进行插值
return spline(
[xVal, yVal],
[xLocal, yLocal],
zLocal
)
}
/**
* 找到值所在的区间索引
*/
private findInterval(arr: number[], val: number): number {
let i = 0
while (i < arr.length - 1 && val > arr[i + 1]) {
i++
}
return i
}
}
// 示例用法
const x = [0, 1, 2]
const y = [0, 1, 2]
const z = [
[0, 1, 4],
[1, 2, 5],
[4, 5, 8]
]
const bicubicSpline = new BicubicSpline(x, y, z)
console.log(bicubicSpline.interpolate(0.5, 0.5))
console.log(bicubicSpline.interpolate(1.5, 1.5))
console.log(bicubicSpline.interpolate(1.2, 0.8))
import CubicSpline from 'cubic-spline'
/**
* 双三次样条插值类
*/
class BicubicSpline {
private x: number[]
private y: number[]
private z: number[][]
private rowSplines: CubicSpline[]
constructor(x: number[], y: number[], z: number[][]) {
this.x = x
this.y = y
this.z = z
const nx = x.length
const ny = y.length
if (nx < 2 || ny < 2) {
throw new Error("x and y must have at least two points.")
}
if (z.length !== ny || z.some(row => row.length !== nx)) {
throw new Error("z must be a matrix of size [ny][nx].")
}
// 对每一行 y 进行 x 方向的一维插值
this.rowSplines = z.map((row) => new CubicSpline(x, row))
}
/**
* 插值函数
*/
public interpolate(xVal: number, yVal: number): number {
// 在 x 方向上插值
const rowValues = this.rowSplines.map(spline => spline.at(xVal))
// 在 y 方向上插值
const columnSpline = new CubicSpline(this.y, rowValues)
return columnSpline.at(yVal)
}
}
// 示例用法
const x = [0, 1, 2]
const y = [0, 1, 2]
const z = [
[0, 1, 4],
[1, 2, 5],
[4, 5, 8]
]
const bicubicSpline = new BicubicSpline(x, y, z)
console.log(bicubicSpline.interpolate(0.5, 0.5))
console.log(bicubicSpline.interpolate(1.5, 1.5))
console.log(bicubicSpline.interpolate(1.2, 0.8))