views:

257

answers:

4

I'm moving through the atmosphere with Microsoft Virtual Earth 3D and I can descend smoothly, but I don't know the math to ascend smoothly.

I'm descending like this:

for(int curAlt = startAlt; curAlt < endAlt; curAlt--){
    //do something
    curAlt -= curAlt/150
}

This works by decreasing the size of the jump the closer I get to the earth (lower altitude). I need a solution that would do similar, just in reverse, while still keeping the smaller jumps at the lower altitude.

How can I do this? Or is what I am doing unacceptable and should be done differently (say with logarithms)?


Note: I'm going to tag this as homework because it is a very homeworky question, however it is not homework for me (or anyone else I know).

A: 

It depends on what you want to achive by smoothly ascending. You could limit the altitude to some maximum value maxAlt and approach that value smoothly in the same way as you do with the ground.

curAlt += (maxAlt - curAlt) / 150

But if the maximum altitude is unbounded, you have to clearify what you exactly want to be smooth.

Also note that your code works only by some side effects. You are close to an infinite loop. I would suggest the folowing.

epsilon = 0.1; // A small value that fits your needs

curAlt = startAlt;

while (curAlt > endAlt + epsilon)
{
   curAlt -= (curAlt - endAlt) / 150;
}

The iteration of curAlt -= (curAlt - endAlt) / 150 will never reach endAlt in theory and in real at most by rouding errors. Your code only works because you subtract one additional height unit per iteration. I am not sure if this is by design or a error that prevents a bug. Adding the epsilon threshold breaks the loop in a more logical way.

Daniel Brückner
The maximum is specified by the user, for this case, let's say 500k
Malfist
that's not exactly the same. Using a test case, for my descend I get 1338 steps, for the ascend with this I get 1557, but I guess that's close enough
Malfist
This might be releated to your additional substraction as mentioned above.
Daniel Brückner
Or may be rounding errors appear because you have many steps with small altitudes while descending and many step with large number while ascending.
Daniel Brückner
a light problem, this causes larger jumps at lower altitudes which is exactly what I don't want. I want smaller jumps at lower altitude, and larger jumps at higher altitudes
Malfist
added to the question for clarification
Malfist
+1  A: 

By the way, you should really make the ascension time dependent (framerate aware). All the answers here depend on the code being called at a specific interval; which it is not. If some process kicks in, Virtual Earth comes under stress somehow, if you minimize Virtual Earth, or if something that affects the performance of Virtual Earth happens the movement will be anything but smooth. Even if 'nothing' happens to Virtual Earth sometimes your 3D card will stall which means potentially you will get a jump every once in a while.

In particular if the user has VSync turned off you will get some really nasty things cropping up:

  • On slow machine the ascension will take forever (even with VSync on).
  • On fast machines it will be so fast you might not even notice it.

In your class:

private int lastTime;

In your loop/event:

if(lastTime == 0)
{
    lastTime = Environment.TickCount;
    return;
}

int curTime = Environment.TickCount; // store this baby.
int timeDiff = lastTime - curTime;

if(timeDiff == 0)
   return;

curAlt += (maxAlt - curAlt) * timeDiff / (150000); // TickCount reports
                                                   // time in Ticks
                                                   // (1000 ticks per second)

lastTime = curTime;

If you want to get fancy you could plug the code from the DX SDK. Environment.TickCount has a resolution of 15ms (reason I check for the timeDiff being zero, because it could easily be). The managed DX SDK sample framework has a class called DxTimer (or sorts) that has a better resolution.

There is an article that uses the same API.

Jonathan C Dickinson
reason for downvote please?
Jonathan C Dickinson
+3  A: 

An even better solution might be to use a function like the Logistic function.

Double minAlt = 0.0;
Double maxAlt = 500000.0;

Int32 numberSteps = 1000;

Double boundary = +6.0;

for (Int32 step = 0; step < numberSteps; step++)
{
   Double t = -boundary + 2.0 * boundary * step / (numberSteps - 1);
   Double correction = 1.0 / (1.0 + Math.Exp(Math.Abs(boundary)));
   Double value = 1.0 / (1.0 + Math.Exp(-t));
   Double correctedValue = (value - correction) / (1.0 - 2.0 * correction);
   Double curAlt = correctedValue * (maxAlt - minAlt) + minAlt;
}

Because the current altitude is explicitly calculated you do not have to rely on a iterative calculation introducing all sorts of precision releated errors.

See the sample code for how to tune the function shape.


Here is a sample console application that displays the function. You can play a bit with the parameters to get a feeling for the behavior.

using System;

namespace LogisticFunction
{
    class Program
    {
        static void Main(string[] args)
        {
            Double minAlt = 5.0;
            Double maxAlt = 95.0;

            Int32 numberSteps = 60;

            // Keep maxAlt and numberSteps small if you don't want a giant console window.
            Console.SetWindowSize((Int32)maxAlt + 12, numberSteps + 1);

            // Positive values produce ascending functions.
            // Negative values produce descending functions.
            // Values with smaller magnitude produce more linear functions.
            // Values with larger magnitude produce more step like functions.
            // Zero causes an error.
            // Try for example +1.0, +6.0, +20.0 and -1.0, -6.0, -20.0
            Double boundary = +6.0;

            for (Int32 step = 0; step < numberSteps; step++)
            {
                Double t = -boundary + 2.0 * boundary * step / (numberSteps - 1);
                Double correction = 1.0 / (1.0 + Math.Exp(Math.Abs(boundary)));
                Double value = 1.0 / (1.0 + Math.Exp(-t));
                Double correctedValue = (value - correction) / (1.0 - 2.0 * correction);
                Double curAlt = correctedValue * (maxAlt - minAlt) + minAlt;

                Console.WriteLine(String.Format("{0, 10:N4} {1}", curAlt, new String('#', (Int32)Math.Round(curAlt))));
            }

            Console.ReadLine();
        }
    }
}
Daniel Brückner
that may be what I'm looking for! Let me test it
Malfist
I think I may be implementing it the wrong way. I get a huge huge jump of the first iteration, but everyone after that is as it should be. Here's the line of code: double curAlt = 1 / (1 + Math.Exp(-t)) * (properties.endingAltitude) - properties.startingAltitude;
Malfist
Also, say, I only want the first half or second half of the curve, how would I do that?
Malfist
for example, using what I just posted, the growth on the first iteration is 11861 feet, and the second is 149 feet
Malfist
I have at least one bug ... gona update the answer.
Daniel Brückner
this still gives an insatiably high growth for the first number. First iteration: 12510, second: 150, third 152, etc
Malfist
This is the nature of the logistic function - it will never reach minAlt or maxAlt because the function approaches asymptotically. It starts a little bit above minAlt stops a little bit below maxAlt. But I just found a way to correct this. Stay tuned.
Daniel Brückner
I think it's more than that. Using this: double curAlt = (properties.endingAltitude - properties.startingAltitude)/(1 + numSteps * Math.Exp(-step)); CurLat is almost 600000 (100,000 more than maxAlt), then the second alt is 1348771. I think something is wrong with the first step.
Malfist
Got it! Starts exactly at minAlt, ends exactly at maxAlt and you have control over the function shape.
Daniel Brückner
What happens if I only want the growth of the first half of the graph? (I want it to be going fastest at the end) Or if I want the second half of the graph (fastest when it starts?) Thanks! You've been a big help!
Malfist
Just go the half way from 0 to numberSteps / 2 or from numberStep / 2 to numberSteps - 1. Or adjust the calculation of t. While t is negative you are accelerating and then deaccelerating for positive t. May be you want to adjust the calculation of curAlt, too. The highest velocity is reached
Daniel Brückner
for correctedValue 0.5. At the same time t is zero and step is numberSteps / 2.
Daniel Brückner
A: 

Since you use curAlt -= curAlt/150 for decending, why not use curAlt += curAlt*150 for ascending?

As curAlt increases you are accelerating more and more while he wants to slow down.
Daniel Brückner
His description says "keeping the smaller jumps at the lower altitude", so understand it to mean the step size would get larger at higher altitudes.
He actually wants to keep the small steps at low altitudes while achieving small steps at high altitudes, too. http://en.wikipedia.org/wiki/Logistic_function shows what he wants. Start smoothly at the ground, accelerate, reach maximum climb rate, deaccelerate, and smoothly approach maximum altitude.
Daniel Brückner