tags:

views:

173

answers:

5

I have a function that will take a range of values, which need to be normalized to the range [-1, 1]. MathHelper.Clamp() just takes values that are outside the range and sets them to be whichever bound it exceeds. Does XNA or C# have anything that does what I'm looking for? (I'm looking through the documentation but can't find it.)

This is the get mouse movement. The values are the difference in mouse coordinates between one tick and the next. I'm not sure what the max value for that is.

This function will take values one at a time - so there is no iterating over all values to scale them all or see what the min and max is.

This is what I currently have:

// max - min cannot == 0
private float normalizeToRange(float value, float min, float max) {
      return (2.0f * (value - min) / (max - min)) -1.0f;
}
A: 

I believe this extension method will do what you are asking for.

public static class Extension
{
    public static IEnumerable<double> Normalize(this IEnumerable<double> source)
    {
        var min = source.Min();
        var max = source.Max();
        var range = max - min;
        foreach (var d in source)
        {
            yield return ((d - min) / range) * 2 - 1;
        }
    }
}
Jake Pearson
A: 

I don't believe XNA has a function that will do that normalization for you. It's a fairly simple operation though...

  1. Loop through all of the values and find the lowest and highest value - this lets you find the range of possible values: range = highestValue - lowestValue
  2. Loop through the values again and set each value to (minValue + value) / range * 2 - 1 to map the values from -1 to 1.

That's off the top of my head, so test thorgoughly :) But that's the basic idea.

Crappy Coding Guy
+1  A: 

Applying the formula 2*(x-min)/(max-min) + -1 to each value should normalize it for you.

(x-min)/(max-min) gives you a value between 0 and 1

multiplying by 2 gives you a value between 0 and 2

subtracting 1 gives you a value between -1 and 1

ESRogs
+1  A: 

If I'm understanding the question correctly, this isn't super difficult to do, but you need to know the possible range of your input values before you can determine the normalized output values.

For instance if your input values can range from -500 to +500, then a simple function like input/500.0 would do the trick.

I'm going to go out on a limb and assume you're wanting whatever range of values you have to be dropped into this range, from -1 to 1, with the scaling set so that the most extreme input values will be set to -1 and 1 exactly.

In that case, you want to iterate over all your values (O(n), bleh!...but you're going to have to iterate over all your values to scale them no matter what, so O(n) isn't avoidable), and find both the lowest and highest values. Determine the difference between these two values, divide that by your output range, and use that as your scaling factor. For each number you want to scale, add the inverse of the minimum input value, scale by the scaling factor, and then subtract the output range offset.

That sounds like a lot of gibberish, so let me provide an example. For example after iterating, you discover your minimum input value is -300, and your maximum input value is 700. The difference between these two (abs(max-min)) is 1000. Since your output range is of size 2 (-1 to 1) divide your scale factor by 2...this gives you your final scaling factor of 1000/2 = 500.

You then take the first point in your input data...we'll say it's 334. You subtract the minimum value from it, giving you 334+300 = 634. You divide that number by your scaling factor, so you get 634/500 = 1.268. Take that number, and subtract 1 (otherwise we're going to scale to (0 to 2), instead of (-1 to 1). That gives .268. That's your answer. Do this for all the points in your set, and you're done.

A quick sanity check shows that if we try 700 (the max value in our set) we get ((700 + 300) / 500) - 1 = 1. Likewise, if we try -300 we get ((-300 + 300) / 500) - 1 = -1.

So drop that into a function, and you're good.

If your input range doesn't vary based on your input, but is a known constant, you can, of course, avoid the iteration through the data at the beginning, which would certainly be a good thing.

So hopefully that helps...I won't bother writing up the actual function...I'm guessing that will be be fairly straightforward. But if have any questions, or if I've misunderstood the concept, let me know.

Edit: Responding to your statement:

This is the get mouse movement. The values are the difference in mouse coordinates between one tick and the next. I'm not sure what the max value for that is.

Presumably there is some kind of driver clamping on this system...if a normal mouse movement would be, say (-5, 3), presumably the mouse simply can't register arbitarily large values of (1000000, 1000000) for movement. You need to find out what the operating range for the mouse is, and what values can come out of it, and that will give you your min and max values.

Beska
Actually it looks like I was a bit unclear in my explanation, but this is helpful regardless.
Rosarch
A: 

There's two ways I can interpret your question.

In both cases, I interpret the input as an array of values, which will scale together.

In my first interpretation, you would like to calculate the distance from "origo" to the "vector" in N-space (N being the number of values) and then scale the vector so that the distance is exactly 1.

This means that the values (1, 1) would scale down to roughly (0.707, 0.707) which has a distance of approximately 1 from (0, 0).

In this case, the way to do it is as follows:

dist = sqrt(vector[0]^2 + vector[1]^2 + ... + vector[N-1]^2)
vector[x] = vector[x] / dist, for x=0..N-1

An example vector with the values (2, 1, 3) would yield:

dist = sqrt(2^2 + 1^2 + 3^2) = sqrt(24) ~ 4.9
vector[0] = vector[0] / 4.9 ~ 0.41
vector[1] = vector[1] / 4.9 ~ 0.2
vector[2] = vector[2] / 4.9 ~ 0.61
vector = (0.41, 0.2, 0.61)

In the second interpretation, you want the maximum value to be exactly 1 away from 0, and the rest scale to fit.

In this case, simply find the maximum value, and divide everything by it (make sure you make it positive first.)

maxValue = abs(max(vector[0], vector[1], ..., vector[N-1]))
vector[x] = vector[x] / maxValue, for x=0..N-1

The same vector as above would yield:

maxValue = abs(max(2, 1, 3)) = abs(3) = 3
vector[0] = vector[0] / 3 ~ 0.67
vector[1] = vector[1] / 3 ~ 0.33
vector[2] = vector[2] / 3 ~ 1
vector = (0.67, 0.33, 1)
Lasse V. Karlsen