Search code examples
javascriptmathcalculus

Algorithm to calculate a diminishing value, approaching a limit


I've been trying to find a Javascript version of a handy method I'd used (not written) a while ago in LPC, it was called dimval(), and it took this form:

NAME
     dimval() - returns values with a reduced increase in output
                as input values grow larger.

SYNOPSIS
     float dimval(float input, float max_input, float max_output,
                  float min_input, float min_output, float rate);

DESCRIPTION
     Returns (as a float) a value between min_output and max_output,
     with values increasing at a reduced rate as they move from
     min_input toward max_output.

     Input is the input value.

     Max_input is the maximum acceptable input. Any higher input
     value will be capped to this.

     Max_output is the maximum value returned.

     Min_input is the (optional) minimum input. Default is zero.

     Min_output is the (optional) minimum output. Default is zero.

     Rate determines how quickly the cost increases to achieve
     greater return values. Higher numbers are faster, lower numbers
     are slower.

I read this article, but it doesn't seem to capture what I want (it looks much simpler for a start). I also read this SO question and... well I think this could work... but the Math is beyond me, to be honest. I understand the description above and how the parameters work together to produce the kind of result I want.

I'd greatly appreciate it if someone could provide a method which has the above constraints, in Javascript.

cheers!

EDIT: Sample outputs from original method.

  • eval return dimval(5.0, 100.0, 100.0, 0.0, 0.0, 1.0) => 22.360680
  • eval return dimval(10.0, 100.0, 100.0, 0.0, 0.0, 1.0) => 31.622776
  • eval return dimval(50.0, 100.0, 100.0, 0.0, 0.0, 1.0) => 70.710678

  • eval return dimval(10.0, 100.0, 100.0, 0.0, 0.0, 2.0) => 15.811388

  • eval return dimval(10.0, 100.0, 100.0, 0.0, 0.0, 10.0) => 3.162278

  • eval return dimval(200.0, 100.0, 100.0, 0.0, 0.0, 10.0) => 10.000000

  • eval return dimval(200.0, 100.0, 100.0, 0.0, 0.0, 1.0) => 100.000000

  • eval return dimval(1.0, 100.0, 100.0, 10.0, 0.0, 10.0) => 0.000000


Let me know if you want me to run any more samples.


Solution

  • Maybe something like this?

    function dimval(n, min_in, max_in, min_out, max_out, exponent) {
      // unscale input
      n -= min_in
      n /= max_in - min_in
    
      n = Math.pow(n, exponent)
    
      // scale output
      n *= max_out - min_out
      n += min_out
      return n
    }
    

    0 < exponent < 1 for fast increase first, then smaller increase, exponent > 1 for the reverse.

    Example:

    > dimval(0, 0, 1, 0, 100, 2)
    0
    > dimval(0.1, 0, 1, 0, 100, 2)
    1.0000000000000002
    > dimval(0.2, 0, 1, 0, 100, 2)
    4.000000000000001
    
    
    > dimval(0, 0, 1, 0, 100, 0.5)
    0
    > dimval(0.1, 0, 1, 0, 100, 0.5)
    31.622776601683793
    > dimval(0.2, 0, 1, 0, 100, 0.5)
    44.721359549995796
    > dimval(0.3, 0, 1, 0, 100, 0.5)
    54.77225575051661
    > dimval(0.4, 0, 1, 0, 100, 0.5)
    63.245553203367585
    > dimval(0.5, 0, 1, 0, 100, 0.5)
    70.71067811865476
    > dimval(0.6, 0, 1, 0, 100, 0.5)
    77.45966692414834
    > dimval(0.7, 0, 1, 0, 100, 0.5)
    83.66600265340756
    > dimval(0.8, 0, 1, 0, 100, 0.5)
    89.44271909999159
    > dimval(0.9, 0, 1, 0, 100, 0.5)
    94.86832980505137
    > dimval(1, 0, 1, 0, 100, 0.5)
    100