63 lines
1.8 KiB
TypeScript
63 lines
1.8 KiB
TypeScript
/**
|
|
* Interpolação bilinear 2D conforme plano técnico (sec. 3.2).
|
|
*
|
|
* Dados quatro pontos Q₁₁(x₁,y₁), Q₁₂(x₁,y₂), Q₂₁(x₂,y₁), Q₂₂(x₂,y₂),
|
|
* estima f(x,y) por:
|
|
* f ≈ ((x₂-x)(y₂-y)·f₁₁ + (x-x₁)(y₂-y)·f₂₁ + (x₂-x)(y-y₁)·f₁₂ + (x-x₁)(y-y₁)·f₂₂) /
|
|
* ((x₂-x₁)(y₂-y₁))
|
|
*
|
|
* Aceita x fora do intervalo por extrapolação linear (clamp opcional).
|
|
*/
|
|
|
|
export type Grid2D = {
|
|
xs: readonly number[];
|
|
ys: readonly number[];
|
|
values: readonly (readonly number[])[];
|
|
};
|
|
|
|
function findBracket(xs: readonly number[], x: number): [number, number, boolean] {
|
|
const clamped = Math.max(xs[0], Math.min(x, xs[xs.length - 1]));
|
|
const extrapolated = clamped !== x;
|
|
if (xs.length === 1) return [0, 0, extrapolated];
|
|
if (clamped >= xs[xs.length - 1]) {
|
|
return [xs.length - 2, xs.length - 1, extrapolated];
|
|
}
|
|
for (let i = 0; i < xs.length - 1; i++) {
|
|
const a = xs[i];
|
|
const b = xs[i + 1];
|
|
if (clamped >= a && clamped <= b) {
|
|
return [i, i + 1, extrapolated];
|
|
}
|
|
}
|
|
return [0, xs.length - 1, extrapolated];
|
|
}
|
|
|
|
export function bilinearInterp(grid: Grid2D, x: number, y: number): number {
|
|
const { xs, ys, values } = grid;
|
|
|
|
const [ix0, ix1] = findBracket(xs, x);
|
|
const [iy0, iy1] = findBracket(ys, y);
|
|
|
|
const x1 = xs[ix0];
|
|
const x2 = xs[ix1];
|
|
const y1 = ys[iy0];
|
|
const y2 = ys[iy1];
|
|
|
|
const f11 = values[iy0][ix0];
|
|
const f21 = values[iy0][ix1];
|
|
const f12 = values[iy1][ix0];
|
|
const f22 = values[iy1][ix1];
|
|
|
|
const dx = x2 - x1;
|
|
const dy = y2 - y1;
|
|
if (dx === 0 || dy === 0) return f11;
|
|
|
|
const denom = dx * dy;
|
|
const num =
|
|
(x2 - x) * (y2 - y) * f11 +
|
|
(x - x1) * (y2 - y) * f21 +
|
|
(x2 - x) * (y - y1) * f12 +
|
|
(x - x1) * (y - y1) * f22;
|
|
|
|
return num / denom;
|
|
} |