I posted this a while ago on the mailing list and can’t find it anymore. As it’s also one of those questions coming up again and again (and as help for my weak memory also) I’m reposting it here.
In contrast to the lincurve mapping which - with same conditions, but not necessarily positive range - solves
g(x) = k * (a ** (x - x0)) + b
with a given base ‘a’, with linexp mapping the base is determined by the conditions, moreover there’s no offset term b (thus no non-positive out values allowed)
As a solution for the linexp mapping we get
a = (y1 / y0) ** (1 / (x1 - x0))
k = y0 / (a ** x0)
and f, slightly transformed:
f(x) = y0 * ((y1 / y0) ** ((x - x0) / (x1 - x0)))
The solutions k, b of the lincurve mapping are
b = y1 - (y0 * (a ** (x1 - x0))) / (1 - (a ** (x1 - x0)))
k = y0 - b
The offset term b equals 0 for
a = (y1 / y0) ** (1 / (x1 - x0))
or
a = exp(ln(y1 / y0) / (x1 - x0))
And, by convention, not ‘a’ is passed as curvature in linexp, but its natural log, multiplied by (x1 - x0).
So to simulate linexp with lincurve, just take log(y1 / y0) as curvature.
(
~expMapping = { |x, inMin = 0, inMax = 1, outMin = 0, outMax = 1, a = 4|
var p = pow(a, inMax - inMin);
var b = outMax - (outMin * p) / (1 - p);
var k = outMin - b;
k * pow(a, (x - inMin)) + b
}
)
// all the same
{ |x| ~expMapping.(x, 0, 2, 0.01, 3, exp(log(300)/2)) }.plotGraph(from: 0, to: 2)
{ |x| x.lincurve(0, 2, 0.01, 3, log(300)) }.plotGraph(from: 0, to: 2)
{ |x| x.linexp(0, 2, 0.01, 3) }.plotGraph(from: 0, to: 2)
// difference is from floating point inaccuracy, not math
{ |x| x.linexp(0, 2, 0.01, 3) - x.lincurve(0, 2, 0.01, 3, log(300)) }.plotGraph(from: 0, to: 2)