Danny Bravo
Danny Bravo

Reputation: 4658

How to map a progress between values to a S-Curve?

I've created a utility for mapping progress to a linear function using the following function:

public static func map<T: FloatingPoint>(progress: T, min: T, max: T) -> T {
    assert(progress >= 0)
    assert(progress <= 1)
    return min + ((max - min) * progress)
}

This returns a set of linear values where, if you have a minimum of 0 and a maximum of 100, the following values are returned for each progress:

0.1 -> 10
0.2 -> 20
0.5 -> 50
0.8 -> 80
0.9 -> 90

I would like to create a similar function that maps the return values to an S-Curve, where the closer you are to the start point and end point of the progress the results become less affected by the outcome. This would be very useful for smoothing out animations using a CADisplayLink, for example. The expected outcomes for the examples above would look something like:

0.1 -> 01
0.2 -> 10
0.5 -> 50
0.8 -> 90
0.9 -> 99

I'm sure there's a fairly basic mathematical formula for this so any pointers would be greatly appreciated!

Upvotes: 2

Views: 343

Answers (1)

halileohalilei
halileohalilei

Reputation: 2280

Think of you progress as a single parameter mathematical function. In your current case it is a linear function and looks something like y = mx + n, where y is the returned value, n is (max - min), m is 1 and x is your progress value.

To achieve what you want, you need to use a displaced version of the sigmoid function instead of a linear one. You need the middle value at x = 0.5 and are only interested in values between 0 and 1. Also, as the Wikipedia article suggests, the y values before and after x = -6 and 6, respectively are pretty close to each other, so you only need to scale the x value from range [0, 1] to [-6, 6]. The following should give you an idea

public static func map<T: FloatingPoint>(progress: T, min: T, max: T) -> T {
  assert(progress >= 0)
  assert(progress <= 1)
  return min + ((max - min) * sigmoid(progress))
}

private static func sigmoid(_ input: FloatingPoint) -> FloatingPoint {
  let x = (input - 0.5) * 12.0 // scale the input value to be between -6 and 6
  let ex = pow(M_E, x) // M_E is the Euler number and is a Double constant
  return ex / (ex + 1) // return the result of the sigmoid function
}

I've never used FloatingPoint before, so I'm not sure if this will work, there may be some type mismatches. But I think the logic should be fine.

Upvotes: 1

Related Questions