我需要找到一个允许我从不规则的2d数据中获取插值的库。想象一下这样的事情:
var data = [{{x: 0, y: 0, value: 0},
{x: 0.5, y: 1, value: 1},
{x: 1, y: 0, value: 2}}, .... Many more elements]
var value = interpolate(data, 0.24, 0.3); // 0.24 = x, 0.3 = y
插值方法的作用是找到坐标在里面的元素,在本例中是一个三角形。然后它在包含它的元素的角之间插值。
我确实意识到其中有很多方面可以优化性能,例如构建一个允许通过预处理边界框快速缩小元素的树。所有这一切都会很棒,但我只是想开始。
那里必须有一些我可以使用的库,而不是自己写的。
答案 0 :(得分:1)
由于javascript 中重心插值的搜索结果尚无定论,因此这里有一些代码可能会帮助您入门。
此代码将2D点的数据集作为输入,每个点具有“值”,以及具有未知值的“新点”。它首先在数据集中找到包含“新点”的最小三角形,然后使用该三角形执行barycentric interpolation以找到“新点”的值。
这种方法运行得相当快,数据集为几百个点。测试,错误检查和优化有很多机会 - 例如,不要查看数据集中的每个可能的三角形。 N选择3以N的立方增长,因此优化以查看仅使用“接近”“新点”的点制作的三角形可以显示出显着的性能提升。
// Calculate the area of a triangle
function triangle_area(vertexA, vertexB, vertexC) {
return Math.abs(((vertexA.x - vertexC.x) * (vertexB.y - vertexA.y) - (
vertexA.x - vertexB.x) * (vertexC.y - vertexA.y)) * 0.5)
}
// Given a number N, return a list of all possible triples from the list [1..N]
// credit: http://stackoverflow.com/a/5752056/1612562
function list_triples(N) {
var fn = function(n, src, got, all) {
if (n == 0) {
if (got.length > 0) {
all[all.length] = got;
}
return;
}
for (var j = 0; j < src.length; j++) {
fn(n - 1, src.slice(j + 1), got.concat([ src[j] ]), all);
}
return;
}
var triples = [];
// Generates the list [0, ..., N]
// credit: http://stackoverflow.com/a/20066663/1612562
var indices =
Array.apply(null, {length: N}).map(Number.call, Number);
fn(3, indices, [], triples);
return triples;
}
// Given three vertices of a triangle and a point, determine if
// the point falls in the triangle
// credit: https://koozdra.wordpress.com/2012/06/27/javascript-is-point-in-triangle/
// credit: http://www.blackpawn.com/texts/pointinpoly/default.html
function is_in_triangle(newPoint, vertexA, vertexB, vertexC) {
var v0 = [vertexC.x - vertexA.x, vertexC.y - vertexA.y];
var v1 = [vertexB.x - vertexA.x, vertexB.y - vertexA.y];
var v2 = [newPoint.x - vertexA.x, newPoint.y - vertexA.y];
var dot00 = (v0[0] * v0[0]) + (v0[1] * v0[1]);
var dot01 = (v0[0] * v1[0]) + (v0[1] * v1[1]);
var dot02 = (v0[0] * v2[0]) + (v0[1] * v2[1]);
var dot11 = (v1[0] * v1[0]) + (v1[1] * v1[1]);
var dot12 = (v1[0] * v2[0]) + (v1[1] * v2[1]);
var invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
return ((u >= 0) && (v >= 0) && (u + v < 1));
}
// Perform barycentric interpolation on a point in a triangle
function barycentric_interpolate(newPoint, vertexA, vertexB, vertexC) {
var area = triangle_area(vertexA, vertexB, vertexC);
var sub_area_1 = triangle_area(newPoint, vertexB, vertexC);
var sub_area_2 = triangle_area(vertexA, newPoint, vertexC);
var sub_area_3 = triangle_area(vertexA, vertexB, newPoint);
return ((sub_area_1 * vertexA.v) + (sub_area_2 * vertexB.v) + (sub_area_3 *
vertexC.v)) / area;
}
// Find the smallest triangle in the data set containing the new
// point, and perform barycentric interpolation using that triangle
function interpolate(newPoint, data) {
var triangles = list_triples(data.length);
var smallest_triangle_area = Number.MAX_VALUE;
var smallest_triangle;
for (t in triangles) {
var vertexA = data[triangles[t][0]];
var vertexB = data[triangles[t][1]];
var vertexC = data[triangles[t][2]];
var in_triangle = is_in_triangle(newPoint, vertexA, vertexB, vertexC);
if (in_triangle) {
if (triangle_area(vertexA, vertexB, vertexC) < smallest_triangle_area) {
smallest_triangle = [vertexA, vertexB, vertexC];
}
}
}
return smallest_triangle
? barycentric_interpolate(newPoint, smallest_triangle[0], smallest_triangle[1], smallest_triangle[2])
: "Interpolation failed: newPoint isn't in a triangle";
}
var newPoint = {'x': 0.24, 'y': 0.3};
var data = [
{'x': 0, 'y': 0, 'v': 0},
{'x': 0.5, 'y': 1, 'v': 1},
{'x': 1, 'y': 0, 'v': 2},
{'x': 1.5, 'y': 2.5, 'v': 1.5},
{'x': 2, 'y': 1, 'v': 0.5}
];
console.log(interpolate(newPoint, data));
还有其他种类的空间插值,例如kriging,至少有一个ready-made .js library。