Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a slider with a non-linear scale?

Tags:

c#

.net

wpf

I have a slider with a minimum value of 0 and maximum of 500.

I want to when the slider goes to 100, the thumb be in the middle of the slider.

I know it seems wierd, but some programs do it with zoom slider, and I believe it's better.

like image 593
alansiqueira27 Avatar asked Aug 30 '11 16:08

alansiqueira27


3 Answers

A good formula for the displayed value is a monotonous function such as a power curve, in the following form:

DisplayValue = A + B * Math.Exp(C * SliderValue);

The internal slider value (from 0 to 1 for instance) is obtained by inverting the formula:

SliderValue = Math.Log((DisplayValue - A) / B) / C;

Now how to obtain A, B and C? By using the three constraints you gave:

f(0.0) = 0
f(0.5) = 100
f(1.0) = 500

Three equations, three unknowns, this is solved using basic maths:

A + B = 0
A + B exp(C * 0.5) = 100
A + B exp(C) = 500

B (exp(C * 0.5) - 1) = 100
B (exp(C) - 1) = 500

exp(C) - 5 exp(C * 0.5) + 4 = 0  // this is a quadratic equation

exp(C * 0.5) = 4

C = log(16)
B = 100/3
A = -100/3

Yielding the following code:

double B = 100.0 / 3;
double C = Math.Log(16.0);
DisplayValue = B * (Math.Exp(C * SliderValue) - 1.0);

You can see that the display value is at 100 when the internal value is in the middle:

final curve

Edit: since a generic formula was requested, here it is. Given:

f(0.0) = x
f(0.5) = y
f(1.0) = z

The values for A, B and C are:

A = (xz - y²) / (x - 2y + z)
B = (y - x)² / (x - 2y + z)
C = 2 * log((z-y) / (y-x))

Note that if x - 2y + z or y - x is zero, there is no solution and you’ll get a division by zero. That’s because in this case, the scale is actually linear. You need to take care of that special case.

like image 72
sam hocevar Avatar answered Nov 10 '22 20:11

sam hocevar


let the slider as it is and use a ValueConverter for your bindings. In the ValueConverter use the non-linear scaling to scale the value as you wish.

like image 42
Random Dev Avatar answered Nov 10 '22 18:11

Random Dev


Just as a further reference; if you are not interested on exact positions for your slider to correspond to specific values in your scale but still want a behavior where the slider is more sensitive to values on the beginning of the scale than on the end, then perhaps using a simple log scale may suffice.

public class LogScaleConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        double x = (int)value;
        return Math.Log(x);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        double x = (double)value;
        return (int)Math.Exp(x);
    }
}
like image 10
Cesar Avatar answered Nov 10 '22 18:11

Cesar