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.
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;
// Start with no velocity
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;
// Start with no velocity
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.)
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 byprintln
) 
static_cast<int16_t>(positionY)
=318

velocityY
=287.33
(rounded byprintln
) 
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
HA! Playing around, how about this?
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.)
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 constantrate 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.
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.
@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?