Как линейно интерполировать (lerp) ранжированные входные данные в ранжированные выходные данные?

Я хочу в общем случае интерполировать между диапазоном входных значений (скажем, между A и B) и получить диапазон выходных значений (скажем, между C и D). Иногда я хочу зафиксировать значение (чтобы B+10000 по-прежнему выдавало D), а иногда нет. Как это сделать?

Например, учитывая скорость ввода от 20 до 80 миль в час, я хочу настроить уровень масштабирования моей карты от 17 до 15:

Без зажима

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

С зажимом

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

Я нашел эту вспомогательную функцию, но (а) она не поддерживает фиксация сама по себе, требующая второго вызова функции, и (б) он поддерживает ввод только между 0 и 1.


person Phrogz    schedule 27.12.2016    source источник


Ответы (1)


Общее (незажимное) уравнение, которое вы хотите:

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>

person Phrogz    schedule 27.12.2016