Search Unity

Mapping or scaling values to a new range

Discussion in 'Scripting' started by seejayjames, Apr 28, 2013.

  1. seejayjames

    seejayjames

    Joined:
    Jan 28, 2013
    Posts:
    691
    Hi all, searched around a bit and didn't find anything, but certainly could have missed it.

    Wondering if there's a built-in function which can take a value and scale/map it to a different range, where you can set the input and output ranges. For example:

    inputLow = 1, inputHigh = 5
    outputLow = 10, outputHigh = 100

    if you send in 1, you'll get 10; if you send in 3, you'll get 60; if you send in 5, you'll get 100.

    So it's an offset-plus-scale, all in one. I suppose you can just roll your own each time, but wondering if it exists already as a function (and maybe with exponential scaling?)

    I know other languages have this, just wondering if it was something obvious I missed...maybe some trick with Lerp?

    Thanks!
     
  2. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    You can use a Lerp/InverseLerp combo:

    Code (csharp):
    1. var result = Mathf.Lerp (10, 100, Mathf.InverseLerp (1, 5, 3));
    Or a function I wrote to do this a while ago, which has the advantage of a little better performance in addition to somewhat simpler syntax:

    Code (csharp):
    1. var result = SuperLerp (10, 100, 1, 5, 3);
    2.  
    3. function SuperLerp (from : float, to : float, from2 : float, to2 : float, value : float) {
    4.     if (value <= from2)
    5.         return from;
    6.     else if (value >= to2)
    7.         return to;
    8.     return (to - from) * ((value - from2) / (to2 - from2)) + from;
    9. }
    --Eric
     
  3. seejayjames

    seejayjames

    Joined:
    Jan 28, 2013
    Posts:
    691
    Ah, that's it! I had that formula around somewhere (from Max/MSP "scale" object) but couldn't remember it exactly. Same as processing's "map" function. Might be nice to have it as a built-in function in Unity like "Mathf.Map" or something.

    Interesting about the Lerp/InverseLerp combo idea too, I'll have to experiment with that.

    Thanks for the info!
     
    nachoman81 likes this.
  4. yourulenl

    yourulenl

    Joined:
    Jun 20, 2014
    Posts:
    1
    These solutions didn't work for me after translating them to C#
    I ported a solution I found on stackoverflow. Maybe this is useful for somebody else
    Code (csharp):
    1. public float scale(float OldMin, float OldMax, float NewMin, float NewMax, float OldValue){
    2.  
    3.     float OldRange = (OldMax - OldMin);
    4.     float NewRange = (NewMax - NewMin);
    5.     float NewValue = (((OldValue - OldMin) * NewRange) / OldRange) + NewMin;
    6.  
    7.     return(NewValue);
    8. }
    Usage:
    Code (csharp):
    1. float scaleMe = 0.5F;
    2. float scaled = scale(0F, 1F, 10F, 20F, scaleMe);
    Returns: 15
     
    Trellace, suIly, Batuhan13 and 4 others like this.
  5. PeterWilkinson

    PeterWilkinson

    Joined:
    Dec 4, 2012
    Posts:
    5
    Thanks Eric5h5, I needed that as well. I rewrote the function in C# and called it Map for simplicity (hopefully not affecting any reserved names, though it seems to work fine).

    Code (CSharp):
    1. public float Map(float from, float to, float from2, float to2, float value){
    2.         if(value <= from2){
    3.             return from;
    4.         }else if(value >= to2){
    5.             return to;
    6.         }else{
    7.             return (to - from) * ((value - from2) / (to2 - from2)) + from;
    8.         }
    9.     }
    Code (CSharp):
    1. Debug.Log(Map(0,10,0,1024,500));
     
    Last edited: Mar 26, 2015
  6. moshangmusic

    moshangmusic

    Joined:
    Jul 16, 2015
    Posts:
    9
    Hey @EID,
    Somehow the code above didn't work for me, but the actual Arduino map function works well with minor modification (changing long datatype to double) - at least in my application.
    Code (csharp):
    1.  
    2.     public double Map(double x, double in_min, double in_max, double out_min, double out_max)
    3.     {
    4.         return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
    5.     }
    6.  
     
  7. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Since its back up top, animation curves are another way of mapping values between ranges with a bit more flexibility.
     
  8. CaseyHofland

    CaseyHofland

    Joined:
    Mar 18, 2016
    Posts:
    613
    'twas the same for me, and Arduino / C++ is probably the function most people will recognize
    But if I may add to it:

    Code (csharp):
    1.  
    2.     using System;
    3.  
    4.     public double Map(double x, double in_min, double in_max, double out_min, double out_max, bool clamp = false)
    5.     {
    6.         if (clamp) x = Math.Max(in_min, Math.Min(x, in_max));
    7.         return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
    8.     }
    9.  
    Set clamp to true and you'll be ensured your value is clamped in between in_min and in_max. A neat little parameter for a neat little function.
     
  9. Batuhan13

    Batuhan13

    Joined:
    Apr 9, 2017
    Posts:
    117
    Thanks it is working great =)
     
  10. toonlets

    toonlets

    Joined:
    Apr 3, 2021
    Posts:
    11
    I'm surprised there isn't a fit function as part of Mathf. Both "fit" and "fit01."
     
  11. CaseyHofland

    CaseyHofland

    Joined:
    Mar 18, 2016
    Posts:
    613
    fit01 would be Mathf.InverseLerp
     
  12. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    No, this topic is specifically not about InverseLerp. We all already know about that.

    --Eric
     
  13. CaseyHofland

    CaseyHofland

    Joined:
    Mar 18, 2016
    Posts:
    613
    But then what is fit01? I would expect "fit 20 to 0 and 1, based on 10 and 30". That would be
    Mathf.InverseLerp(10, 30, 20); // = 0.5f