• Which the release of FS2020 we see an explosition of activity on the forun and of course we are very happy to see this. But having all questions about FS2020 in one forum becomes a bit messy. So therefore we would like to ask you all to use the following guidelines when posting your questions:

    • Tag FS2020 specific questions with the MSFS2020 tag.
    • Questions about making 3D assets can be posted in the 3D asset design forum. Either post them in the subforum of the modelling tool you use or in the general forum if they are general.
    • Questions about aircraft design can be posted in the Aircraft design forum
    • Questions about airport design can be posted in the FS2020 airport design forum. Once airport development tools have been updated for FS2020 you can post tool speciifc questions in the subforums of those tools as well of course.
    • Questions about terrain design can be posted in the FS2020 terrain design forum.
    • Questions about SimConnect can be posted in the SimConnect forum.

    Any other question that is not specific to an aspect of development or tool can be posted in the General chat forum.

    By following these guidelines we make sure that the forums remain easy to read for everybody and also that the right people can find your post to answer it.

Tutorial in Rotating Number Drums

Messages
131
Country
unitedstates
We have all seen them, whether it be a Hobbs gauge, a "digital drum" on a PFD, or the odometer in your old car. Most of time these show up in FS, they are not very smooth. While an instant movement from one number to the next might be a desired effect, I wanted a smooth rolling drum, like the mechanical one in your old car.

There are some Hobbs gauges out there that achieve this effect, however, I found there logic extremely complex. I am not a highly skilled computer programmer, but I can usually grasp the logic behind something fairly easily. So I decided to give my own logic a try.

For those of you out there like me, that can't always look at code and immediately figure it out, I am going to take you through my logic step by step. A tutorial of sorts.

Here we go:

Lets make a 3 digit drum, that displays your current airspeed. To keep the code easier to read, we will use a macro named IAS that simply refers to the indicated airspeed (A:Airspeed indicated, knots).

Code:
<Macro id="Indicated Airspeed" Name="IAS">
        <MacroValue>(A:Airspeed indicated, knots)</MacroValue>
</Macro>

Setting up the drum:

Not going to go into detail here, because every drum will be different. Basically, what you need to do is use a image that has a strip of numbers. The strip must start with 0 and end with 0 for the motion to be fluid ( 0 1 2 3 4 5 6 7 8 9 0 ). If you want you drum to rotate downward, you need the 1 towards the bottom and 9 towards the top, on a vertical strip. Other configurations would be possible, but that is how this one is set up. Apply a mask so only one digit shows, then use a shift to move the numbers. A non-linear table makes setup easier, but isn't necessary if you use the scale argument. Either way works. Make 3 identical elements, shifting the float position over for each one.

Code:
<Element id="Airspeed Digit 1">
        <FloatPosition>68.000,246.000</FloatPosition>
        <Image id="drum_digits.bmp" Name="drum_digits.bmp">
            <Transparent>True</Transparent>
            <Axis>0.000,249.000</Axis>
            <Bright>True</Bright>
        </Image>
        <MaskImage id="drum_digits_mask.bmp" Name="drum_digits_mask.bmp">
        </MaskImage>
        <Shift id="Shift">
            <Scale>0.000,24.000</Scale>
            <Expression id="Expression">
                <Minimum>0.000</Minimum>
                <Maximum>9.999</Maximum>
                <Script>@IAS 10 %</Script>
            </Expression>
        </Shift>
</Element>

From here on, lets assume @IAS is 139.932, unless otherwise noted.

Digit 1:
**NOTE: I'll refer to the rightmost digit as digit 1. ie, in the number 321, digit 1 is the 1, digit 2 is the 2, and digit 3 is the 3

This is the easy one, since it is always rotating if the airspeed is changing. All we have to do here is feed the single digit and it's decimal to the shift expression. Even though the decimal isn't shown on the drum, it is needed so that the motion is fluid. It is also important in the other 2 digits, as you will see later. Anyways, chopping off the hundreds and tens is easy, all you have to do is divide by 10, and keep only the remainder. This is called taking the modulo, and the SDK provides that function with % operator. Note that the SDK has a misprint on how this operator works, so don't get confused. Taking the modulo requires 2 numbers to be on the stack, it divides them, and puts the remainder on the stack.

Stack: 10 3 %
Stack: 1

But we will only be modulo'ing:confused:(don't think that is a word) by 10 and 100, which will strip the nubmer to only single digit, or double digits respectivly, also leaving the decimal in both cases.

So our script for the digit 1
Code:
@IAS 10 %
gives us:

Stack: 139.932 10 %
Stack: 9.932

The stack is done, so our image is shifted 9.932, or just about to the top 0.

BTW, the shift function must be capped 9.999 max value, so that it will start over at 0. This "start over" is surprisingly smooth.

continued...
 
Digit 2:

OK, now it will get complicated. One would think you could just modulo by 100, then divide by 10, giving you:

Stack: 139.932 100 % 10 /
Stack: 39.932 10 /
Stack: 3.9932

The stack is done, so our digit 2 image is shifted 3.9932, or just below the 4.

Isn't that what we want, it gives us a drum that is about to turn to 40, and 39.932, is almost 40. So what is the problem? Think about it...what happens if IAS was 134.932? Our digit 2 is now shifted 3.4932, or almost half way between 3 and 4, making it not easy to read. Imagine the odometer on a car, a digit only spins if the digit to the right of it is spinning from 9 to 0, other than the rightmost digit of course. That is where it gets complicated. To accomplish this, we need to determine if digit 1 shift is between 9 and 9.999, then shift digit 2.

Here is the expression for digit 2 shift.

Code:
@IAS 100 % 10 / s1 d flr - s2 0.9 <=
if{ l1 flr } 
els{ l1 flr l2 10 * 9 - + }

This time I'll break down the stack, stopping each time an operator is put on the stack. Lets start with an instance where we don't want the number to shift, IAS is 134.932

Stack: 134.932 100 %
Stack: 34.932 10 /
Stack: 3.4932 s1 <-------Store 3.4932 in register 1 for future use, leaving it on the stack.
Stack: 3.4932 d <-------Duplicate value on top of stack.
Stack: 3.4932 3.4932 flr <-------Floor removes decimal
Stack: 3.4932 3 -
Stack: 0.4932 s2 <-------Store 0.4932
Stack: 0.4932 0.9 <= <-------Yes, 0.4932 is less than .9.
Stack: 1 if......

Now we would move on the the conditions, but first, lets review. Basically that mess of mathematics give us the same number digit 1 did, with the decimal point moved over one place. But, we also stored 2 numbers that we will use in the conditions, making the more complicated process worth it. The if statement then checks to see if digit 1 if shifting between 9 and 9.999. It is not, so,

Stack: l1 <-------l1 recalls register 1, which had 3.4932 in it.
Stack: 3.4932 flr <-------Floor removes the decimal
Stack: 3

The stack is done, so our digit 2 image is shifted 3.0, in other words, it stays on 3.

Now if IAS had been 139.500, following the previous logic you would have register 1 = 3.9500, register 2 0.9500 and the else condition would run.

Stack: l1 <-------l1 recalls register 1, which had 3.9500 in it.
Stack: 3.9500 flr <-------Floor removes the decimal
Stack: 3 l2 <-------l2 recalls register
Stack: 3 0.9500 10 *
Stack: 3 9.500 9 -
Stack: 3 .500 +
Stack: 3.5

The stack is done, so our digit 2 image is shifted 3.5, or half way between 3 and 4. I you take the IAS of 139.500 back to the digit 1 logic, you will see digit 1 is halfway between 9 and the top 0. Exactly what we want, digit 2 shifts at the same pace as digit 1 when digit 1 is between 9 and the top 0.

Digit 3:

The hard work is done now. We just have to tweak the digit 2 expression for use with digit 3. Now we only want digit 3 to shift when digit 2 and 1 are collectively shifting between 99 and 99.999. Here is the expression.

Code:
@IAS 1000 % 100 / s1 l1 flr - s2 0.99 <=
if{ l1 flr } 
els{ l1 flr l2 100 * 99 - + }

I am going to spare the logic on this one, you should now be able to follow it, but I will point out the differences. We wouldn't technically have to modulo this, since IAS will always be less then 999.999 in my application, but in good practice, I left it in. I would get the same result with or without it. Anyways, we compare with 0.99 instead of 0.9, since we want both the other 9s to be shifting when digit 3 does. Then in the else statement we multiply by 100 instead of 10, and subtract 99 instead of 9. See a pattern here?

If we have a digit 4 it would look like this:

Code:
@IAS 10000 % 1000 / s1 l1 flr - s2 0.999 <=
if{ l1 flr } 
els{ l1 flr l2 1000 * 999 - + }

If we have a digit 5 it would look like this:

Code:
@IAS 100000 % 10000 / s1 l1 flr - s2 0.9999 <=
if{ l1 flr } 
els{ l1 flr l2 10000 * 9999 - + }

And so on...


Well, I hope I shed some light here. Post any questions, or comments, and please share your knowledge also.

DNLK
 
nice tutorial DNLK - I read it carefully even though I have absolutely no need for a barrel-rolling digit display whatsoever...

cheers - B21
 
...
Well, I hope I shed some light here. Post any questions, or comments, and please share your knowledge also.

DNLK

I've written a bunch of examples about rolling numbers in other forums that make the whole stuff very simple.

For the airspeed value in where you did a great analysis, it may be simplified by using one variant of the @Extdigit macro that I developed time ago and can be found explained here on Wiki examples:

Code:
<Macro Name="ExtDigit"> 
@1 sp0 
@3 0 >  
if{ l0 10 @3 pow * sp0 } 
l0 int 10 @2 pow % 10 @2 1 - 0 max pow / int  
</Macro>

Using a value of three integer digits plus decimals (not shown) - ie 135) ,

-for the first rightmost integer digit (5) = @IAS 1 % @ExtDigit(@IAS,1,0) +
-for the second rightmost digit (3) = @IAS 10 % s1 9 >= l1 9 - * @ExtDigit(@IAS,2,0) +
-for the third rightmost digit (1) = @IAS 100 % s1 99 >= l1 99 - * @ExtDigit(@IAS,3,0) +

It is very simple to adapt the position in where the preceding digit would start to rotate by changing the 9 and 99 values. For example, if you want to start at 80 % of cycle instead of 90 % just use
...8 >= l1 8 - 2 / * @ExtDigit(@IAS,2,0) ...
and
...98 >= l1 98 - 2 / * @ExtDigit(@IAS,3,0) ...
and so on. The same reasoning applies for other aircraft readouts like altimeters, barometrics, etc.

Again, a very nice tutorial :)


Tom

PS: It's not necessary to use a mask in rolling number bitmaps to hide extra data. <Clip> command will give the same result and consume less resources -a mask is still a bitmap.
 
Great tip on the <clip>...never thought of that. I can replace about 10 masks in my gauge by using that. I saw that it was available, but the SDK doesn't explain it at all. I am assuming it works similar the mask, ie, if I had a mask that was say 12x20, just replace that with the same size <clip>?

About the @Extdigit, I had looked at the wiki about that, but when I stared this project I thought I would need to keep the decimal on all the numbers...I know now that it wouldn't have been necessary. Again, thanks for the advice.

DNLK
 
Great tip on the <clip>...never thought of that. I can replace about 10 masks in my gauge by using that. I saw that it was available, but the SDK doesn't explain it at all. I am assuming it works similar the mask, ie, if I had a mask that was say 12x20, just replace that with the same size <clip>?


DNLK

I haven't found a working example of <Clip> in FSX new XML schema, but it should work like this:

Code:
<Element>
  Put the image, nonlinearity/shift/rotate/whatever values
  <ClipRect id="clip1">
    <FloatPosition>[B]top,left (or left,top)[/B]</Floatposition>
    <Size>[B]width,height[/B]</Size>
  </ClipRect>
</Element>

The element's image will show only within the bounds of top,left, width and height.

Tom

PS: You can get rid of "id" properties if you're not going to use ace.exe to buld your gauges.
 
Last edited:
Back
Top