# Add gravity to an animated graphic

Right, I see. OK. Let me give that a try. Makes sense now. Maybe I needed to sleep on it.Thank you Scott and Pharap.

2 Likes

If thatâs the case then thatâs easy enoughâŚ

``````#include <Arduboy2.h>

Arduboy2 arduboy;

// Start at the top of the screen
float positionX = (WIDTH / 2.0f);
float positionY = -8.0f;

float velocityX = 0.0f;
float velocityY = 0.0f;

uint8_t iteration = 60;

void updateCube()
{
// Increase velocity
velocityY += 0.01f;

// Update position
positionX += velocityX;
positionY += velocityY;

// Cycle the rotation
if(iteration > 0)
--iteration;
else
iteration = 60;

}

void drawCube()
{
constexpr int halfRad = (38 / 2);

float SpinAng = ((iteration * 6) / 57.296f);
float SpinAng2 = (SpinAng + (90.0f / 57.296f));

float sin1 = sin(SpinAng);
float cos1 = cos(SpinAng);
float sin2 = sin(SpinAng2);
float cos2 = cos(SpinAng2);

float positionY2 = positionY - 40;

int a = (positionX + (sin1 * halfRad));
int b = (positionY + (-cos1 * halfRad)) / 2;
int c = (positionX + (-sin1 * halfRad));
int d = (positionY + (cos1 * halfRad)) / 2;

int a1 = (positionX + (sin2 * halfRad));
int b1 = (positionY + (-cos2 * halfRad)) / 2;
int c1 = (positionX + (-sin2 * halfRad));
int d1 = (positionY + (cos2 * halfRad)) / 2;

// Draw first rectangle
arduboy.drawLine(a, b, a1, b1, WHITE);
arduboy.drawLine(a1, b1, c, d, WHITE);
arduboy.drawLine(c, d, c1, d1, WHITE);
arduboy.drawLine(c1, d1, a, b, WHITE);

int e = (positionX + (sin1 * halfRad));
int f = (positionY2 + (-cos1 * halfRad)) / 2;
int g = (positionX + (-sin1 * halfRad));
int h = (positionY2 + (cos1 * halfRad)) / 2;

int e1 = (positionX + (sin2 * halfRad));
int f1 = (positionY2 + (-cos2 * halfRad)) / 2;
int g1 = (positionX + (-sin2 * halfRad));
int h1 = (positionY2 + (cos2 * halfRad)) / 2;

// Draw second rectangle
arduboy.drawLine(e, f, e1, f1, WHITE);
arduboy.drawLine(e1, f1, g, h, WHITE);
arduboy.drawLine(g, h, g1, h1, WHITE);
arduboy.drawLine(g1, h1, e, f, WHITE);

// Join the rectangles
arduboy.drawLine(a, b, e, f, WHITE);
arduboy.drawLine(g, h, c, d, WHITE);
arduboy.drawLine(a1, b1, e1, f1, WHITE);
arduboy.drawLine(g1, h1, c1, d1, WHITE);
}

void setup()
{
arduboy.begin();
}

void loop()
{
if(!arduboy.nextFrame())
return;

arduboy.pollButtons();

arduboy.clear();

updateCube();
drawCube();

arduboy.display();
}
``````

Accelerate.ino.hex (26.6 KB)

@Maxm, it may require some tweaking to behave more like how you want it to, but the basics are there.

Personally Iâd suggest storing the cube model as an array of coordinates and then offsetting those coordinates with `positionX` and `positionY` and then doing the rotation (or doing the rotation and then offsetting, whicheverâs easier).
That ought to work out cheaper than other options.

Iâm still not quite sure how your spin angles are actually working,
but offering up a better solution would mean figuring out the matrices involved and Iâm not really in a hurry to do that if I can avoid it.
Also Iâd have to figure out which corners of the cube correspond to your letter labellings.
I.e. which corner is `a`, `b`; which is `a1`, `b1`.

(From what I gather youâre actually effectively drawing two 2D rectangles and then linking them up.)

Edit:
Using the realisation that the top and base rectangles are identical,
I found a way to cut the memory down significantly:

``````#include <Arduboy2.h>

Arduboy2 arduboy;

// Start at the top of the screen
float positionX = (WIDTH / 2.0f);
float positionY = -8.0f;

float velocityX = 0.0f;
float velocityY = 0.0f;

uint8_t iteration = 60;

void updateCube()
{
// Increase velocity
velocityY += 0.01f;

// Update position
positionX += velocityX;
positionY += velocityY;

// Cycle the rotation
if(iteration > 0)
--iteration;
else
iteration = 60;

}

void drawCube()
{
constexpr int halfRad = (38 / 2);

float SpinAng = ((iteration * 6) / 57.296f);
float SpinAng2 = (SpinAng + (90.0f / 57.296f));

float sin1 = sin(SpinAng);
float cos1 = cos(SpinAng);
float sin2 = sin(SpinAng2);
float cos2 = cos(SpinAng2);

int a = (positionX + (sin1 * halfRad));
int b = (positionY + (-cos1 * halfRad)) / 2;
int c = (positionX + (-sin1 * halfRad));
int d = (positionY + (cos1 * halfRad)) / 2;

int a1 = (positionX + (sin2 * halfRad));
int b1 = (positionY + (-cos2 * halfRad)) / 2;
int c1 = (positionX + (-sin2 * halfRad));
int d1 = (positionY + (cos2 * halfRad)) / 2;

// Draw first rectangle
arduboy.drawLine(a, b, a1, b1, WHITE);
arduboy.drawLine(a1, b1, c, d, WHITE);
arduboy.drawLine(c, d, c1, d1, WHITE);
arduboy.drawLine(c1, d1, a, b, WHITE);

int e = a;
int f = (b + 20);
int g = c;
int h = (d + 20);

int e1 = a1;
int f1 = (b1 + 20);
int g1 = c1;
int h1 = (d1 + 20);

// Draw second rectangle
arduboy.drawLine(e, f, e1, f1, WHITE);
arduboy.drawLine(e1, f1, g, h, WHITE);
arduboy.drawLine(g, h, g1, h1, WHITE);
arduboy.drawLine(g1, h1, e, f, WHITE);

// Join the rectangles
arduboy.drawLine(a, b, e, f, WHITE);
arduboy.drawLine(g, h, c, d, WHITE);
arduboy.drawLine(a1, b1, e1, f1, WHITE);
arduboy.drawLine(g1, h1, c1, d1, WHITE);
}

void setup()
{
arduboy.begin();
}

void loop()
{
if(!arduboy.nextFrame())
return;

arduboy.pollButtons();

arduboy.clear();

updateCube();
drawCube();

arduboy.display();
}
``````

(No sense recalculating data that you already have.)

@Maxm
A quick question: where did you actually get the value of 57.296 from?

I checked `90.0f / 57.296f` and it is actually very close to `Ď / 2`.
(The first 5 decimal places are the same.)

1 Like

Itâs interesting that without having termination logic in your example, the cube âvirtuallyâ continues to fall below the screen, within the large âoff screenâ space, still at constant acceleration. Eventually, the Y position wraps, so the cube is now falling from the top of âoff screenâ space, still accelerating. At some point the cube falls through the visible part of screen space and you see it zip by at great speed. This continues, with the cube appearing after shorter and shorter intervals but at faster and faster speeds.

I guess eventually the Y velocity would wrap (changing to maximum negative?)

Sorry for the sidetrack.
We now return you to your regularly scheduled programming.

That sounds reasonable for an integer type, but itâs a `float`, so potentially it might eventually reach positivie infinity, at which point addition should become equivalent to the identity operation, which means it should never wrap back around to the top and instead gets stuck at the theoretical positive infinity lineâŚ

But since the rendering operations accept integers rather than floats, that also raises the question of what value positive infinity generates when cast to an `int16_t`âŚ

Looking closer, youâre always only adding `0.01f` to the Y velocity, so I suspect the value would only increase to the point where adding 0.01 would have no ability to increase the value, and then it would be stuck there.

By âitâ I meant the Y position rather than the Y velocity.

Iâm not sure at what point adding `0.01f` might cease to have an effect (or even if there is such a point).

One thing I can confirm though is that the cube does indeed wrap back round, but itâs due to the implicit conversion to `int16_t` rather than the `float` itself wrapping around.

I ran the program whilst printing the values of `positionY` and `positionX`, as well as the results of casting them to `int16_t` to observe how larger `float`s are interpreted.
After a point the cast value effectively wraps around and starts producing negative numbers that eventually increase into the positive range before seemingly wrapping around again.

More interestingly though, there comes a point where the program appears to halt with:

• `positionY` = `4128450.75` (rounded by `println`)
• `static_cast<int16_t>(positionY)` = `-318`
• `velocityY` = `287.33` (rounded by `println`)
• `static_cast<int16_t>(velocityY)` = `287`

Theoretically if either condition in which addition became ineffective were reached, only one number should stop changing (unless they happened very close together, which seems unlikely), so I suspect this is something else.
I may run it again with an animation of some kind of animation in the corner to establish whether the program itself has ceased running.

Edit:
My test has confirmed that the Arduboy does actually stop at that point (i.e. the animation stops too), which most likely implies that it gets stuck in an infinite loop somehow.

F.Y.I. Adding `0.01f` to a float will cease to be able to increase its value at 262144.0
(which is 2^16*4). I got the same result from both the Arduboy emulator and gcc on my 64bit Intel PC.

My code is (at this point in my journey) always a kind of hack that surprises me when it works. Because I donât know the rules I approach things in weird ways. Pharap, the new example nails what I am looking for. I get it now. Mr. Scott I also enjoyed the tangent about the indefinite continuation offscreen. Thank the maker you guys all have Arduboys and sink your teeth into n00b questions like mine.

Max

2 Likes

``````unsigned long elapsed = (millis()*.001); //seconds
if (arduboy.everyXFrames(60-(0.5*9.8*(pow(elapsed,2))))) //acceleration
``````

There is no doubt a better representation, more efficient.

Only updating your position after a given number of frames have elapsed may not appear as smooth as updating every frame, especially as the speed increases. If you only change position every 4 frames, there are potentially 3 other frames in between where the position could have changed.

Continually varying the number of frames you wait, using `everyXFrames()`, could be problematic as well. `everyXFrames()` works with integers. E.g. you canât have it return `true` every 2.75 frames.

You should also be aware of how `everyXFrames()` is implemented. The system keeps an overall running frame count and `everyXFrames()` just checks if this count can be be equally divided by the parameter you give it (i.e. if the remainder of the divide is 0). Therefore, itâs possible it will return `true` in less frames than expected after calling it with a value different than from the previous time it was called.

Overall, I think youâre better to work with time quantised by the frame rate, rather than trying to work in âreal timeâ using the system clock.

This is actually forcing the result of `millis()` to be promoted to `float`, only for it to be cast back to an `unsigned long`.
If you used `/ 1000` instead then there would be no conversion to `float`,
which would probably save memory and presumably also be faster.

Typically the obvious way is actually the more efficient.

Iâm not actually sure what youâre trying to do here.
`0.5 *` is obviously an alternative to `/ 2.0`.
Iâm presuming `9.8` is supposed to be Earthâs gravity?

I have no idea what `pow(elapsed, 2)` is supposed to be achieving though.
(For what itâs worth, that could just be `elapsed * elapsed`.
Itâs an integer anyway, so youâre not going to gain anything from feeding it to the `pow` function.)

2 Likes

OK ok, nuts I guess. Yes I was messing with an attempt at average acceleration. You guys sure know a lot. My (apparently flawed) thought with everyXFrames was that if I had some function that was constantly drawing, say a rotating cube, drawing at a consistent rate, then I applied a gravity based formula to trigger that constant drawing, that I could get a realistic âspooling upâ appearance in any constant-rate drawing routine. Like a turbine from a stop, accelerating to a set speed.

SoâŚYou wanted it to rotate?

What is the desired outcome for this? Like a prop spinning on a plane?

If you want to do that, you just apply a rotational acceleration formula for determining the next spin angle, at the same time that you do the linear acceleration calculation youâre using for making the cube fall.

However, note that at certain high spin speeds the cube may appear to stop or start spinning in reverse, due to the stroboscopic effect of a fixed frame rate. (Like wagon wheels and propellers in movies.)

Pharap, I meant to ask, where did adding an âfâ come from?
ie: float positionY = -8.0f;

The f (or F) designates the literal value as type float. If you left off the f it would be a double (which would be implicitly converted to a float because thatâs what positionY is). If you use the suffix l or L it designates the literal as having type long double.

https://en.cppreference.com/w/cpp/language/floating_literal

@Maxm,
When addressing a user using their username on this forum, itâs best to put an âatâ sign as a prefix to the username. Write @Pharap instead of just Pharap. This will usually cause the user to receive an alert (email or otherwise) informing them that they have been referenced, so they may respond sooner.

For the same reason, itâs best to use the personâs username (again, with a leading @) instead of their real name, so for me it would be @MLXXXp instead of Scott. Doing this is not considered disrespectful in any way.

1 Like

In addition to what @MLXXXp saidâŚ

Usually it isnât an issue if you omit the `f`, but if you were to use a double lieral in a calculation where all the other values were `float` youâd force them to be promoted to `double` which may not be what you intended and could potentially waste a bit of processing power or prevent certain optimisations (especially if the target is an x86 CPU).

On the Arduboy it doesnât really matter because `float` and `double` actually have the same representation, but I write code for more than just Arduboy, hence my habits are tuned to avoid potential bugs.

1 Like

@MLXXXp it strikes me that this may be what I need: âOverall, I think youâre better to work with time quantised by the frame rate, rather than trying to work in âreal timeâ using the system clock.â
Do you have an example that could show how this could be applied to acceleration?

Yes. @Pharap has already provided one for you.

1 Like