如何将(lerp)范围输入线性插值到范围输出?

时间:2016-12-27 03:04:06

标签: javascript algorithm linear-interpolation

我想在一系列输入值之间进行一般插值(例如,在A和B之间)并获得一系列输出值(例如,在C和D之间)。 有时我想要钳制值(因此B + 10000仍然输出D),有时我不会。我该怎么做?

例如,如果输入速度介于20英里/小时和80英里/小时之间,我想在17到15之间调整地图的缩放级别:

没有夹紧

     | \ 
     |  \
 17  |   \
     |    \
     |     \
     |      \
     |       \
 15  |        \
     |         \
     +----------\-
        20  80   \

使用Clamping

     |
 17  |----
     |    \
     |     \
     |      \
     |       \
 15  |        ----
     |
     +------------
        20  80    

我发现this utility function,但是(a)它不支持自己进行钳位,需要第二次函数调用,(b)它只支持0到1之间的输入。

1 个答案:

答案 0 :(得分:2)

您想要的一般(未夹紧)等式是:

var unclamped = (x-minX) * (maxY-minY)/(maxX-minX) + minY;

对于钳位,您可以在计算结果后钳制输出:

var clamped = Math.max( minY, Math.min( maxY, unclamped ) );

或者您可以在使用之前钳制输入:

x = Math.max( minX, Math.min( maxX, x ) )
var clamped = (x-minX) * (maxY-minY)/(maxX-minX) + minY;

如果线的斜率没有变化,并且您的夹紧要求不会改变,您可以通过预先计算一次并根据您的输入和需求生成一个功能来提高性能:

// Generate a lerp function for a specific set of input/output,
// with or without clamping to the output range.
function lerp(minX, maxX, minY, maxY, clampFlag) {
  var slope = (maxY-minY)/(maxX-minX);
  return clampFlag ?
    function(x){ return ((x<minX?minX:x>maxX?maxX:x) - minX) * slope + minY }
    :
    function(x){ return (x-minX)*slope + minY }
}

行动中:

prepPlotter(); // Get ready to draw a pretty graph of the results

// Make a simple input/output function
var zoomForSpeed = lerp(20, 80, 17, 15, true);

for (var speed=0; speed<=99; speed++) {
  var zoom = zoomForSpeed(speed); // Bam! Lerp'd!
  plot(speed,zoom);               // Proof of the goodness
}

// Show the min/max input and output
ctx.fillStyle = 'red';
plot(20,17,2);
plot(80,15,2);

function plot(speed,zoom,scale) {
  ctx.fillRect(speed,zoom,0.5*(scale||1),0.03*(scale||1));
}

function prepPlotter() {
  ctx = document.querySelector('canvas').getContext('2d');
  ctx.translate(0,875);
  ctx.scale(3,-50);
}

function lerp(minX, maxX, minY, maxY, clampFlag) {
  var slope = (maxY-minY)/(maxX-minX);
  return clampFlag ? function(x){ return ((x<minX?minX:x>maxX?maxX:x) - minX) * slope + minY } : function(x){ return (x-minX)*slope + minY }
}
<canvas>Press "Run code snippet" for a graph of zoomForSpeed</canvas>