Problem Summary
I have a “square” rotating on my 3.5 Inch TFT screen (ILI9486 driver) with dots/circles at each of its 4 corners. It is connected to an Arduino MEGA 2560 There are a few issues with how it is currently implemented that I would like guidance on:
The image (square and dots) is “flashing” due to the constant clearing and redisplaying of the components (lines and dots);
The square is not fully closed (I can minimise this issue by reducing the angle of rotation applied in each step, but it becomes too slow);
Ideally, i'd like a smoothly rotating square that is fully closed and does not flash. A faster rotation speed than what is depicted below would be desirable.
I’m pretty sure I know why these issues are occurring, but I’m not sure how I should best go about fixing them.
More detail on the issues
This code/project is just to familiarise myself with the lower-level theory of rotating images on a screen. I am following 3Blue1Brown’s linear algebra series and became interested in using matrices as transformations. I will eventually work up to the 3D case, but I’d prefer to get the 2D technical issues out of the way first.
- Issue 1: The Flashing
As I stated in the problem summary, the square is “flashing”. See below for a video. The flashing is caused by how I clear and display the square. The process is essentially:
Foreach vertex:
Calculate new vertex positions
Clear the dot at the previous position of that vertex
Clear the line connecting that previous dot to its neighbouring vertex
Display the dot at the new vertex position
Display the line connecting that dot to its neighbouring vertex
Here is a video capture showing the flashing I am describing.
Is there anything I can do to improve this? Increasing the angle of rotation helps, but I run into the second issue, below.
- Issue 2: The unconnected line
My second major issue is that the square is not “closed”. It's not apparent in the images above because they had a smaller angle of rotation. This image shows what I’m talking about:
See this video here to see it in action.
I’ve labelled the vertices 1 – 4 in the picture. I know that the issue is caused by how I’m updating the square – the line connecting vertices 1 and 4 is only valid for a very short time interval – it is drawn just after vertex 4 is drawn, after which the position of 1 is immediately updated, so the line is pointing to the old position of vertex 1 for the majority of the time. I’ve tried altering the order in which lines are drawn but this only shifts the problem to different vertices.
Reducing the angle by which the square rotates minimises the issue, but then the square is rotating too slowly for what I want to use it for.
Code
#include <MCUFRIEND_kbv.h>
#include <Adafruit_GFX.h> // Core graphics library
#define LCD_RESET A4 // Can alternately just connect to Arduino's reset pin
#define LCD_CS A3 // Chip Select goes to Analog 3
#define LCD_CD A2 // Command/Data goes to Analog 2
#define LCD_WR A1 // LCD Write goes to Analog 1
#define LCD_RD A0 // LCD Read goes to Analog 0
// Assign human-readable names to some common 16-bit color values:
#define BLACK 0x0000
#define WHITE 0xFFFF
typedef struct {
float x;
float y;
bool draw;
} vertex_2d;
vertex_2d vertices[4]; //a square contains 4 vertices
vertex_2d old_vertices[4];
uint8_t point_size = 5;
float angle = 0.2;
MCUFRIEND_kbv tft;
char *msg[] = { "PORTRAIT", "LANDSCAPE", "PORTRAIT_REV", "LANDSCAPE_REV" };
uint8_t aspect = 1;
void rotate_2D(vertex_2d *vert, float angle){
float old_x = vert->x;
vert->x = old_x * cos(angle) - vert->y * sin(angle);
vert->y = old_x * sin(angle) + vert->y * cos(angle);
}
void translate_2d(vertex_2d *vert, int x_trans, int y_trans){
vert->x = vert->x + x_trans;
vert->y = vert->y + y_trans;
}
void setup() {
tft.reset();
uint16_t identifier = tft.readID();
tft.begin(identifier);
tft.fillScreen(BLACK);
tft.setRotation(1);
vertices[0] = {190, 110, true}; //vertex 1
vertices[1] = {290, 110, true}; //vertex 2
vertices[2] = {290, 210, true}; //vertex 3
vertices[3] = {190, 210, true}; //vertex 4
for (int i = 0; i < 4; i++){
old_vertices[i] = vertices[i];
}
}
void loop() {
for (int i = 0; i < 4; i++){
//translate back to origin, rotate and then translate back into position
translate_2d(&vertices[i], -240, -160);
rotate_2D(&vertices[i], angle);
translate_2d(&vertices[i], 240, 160);
}
for (int i = 0; i < 4; i++){
if (abs(old_vertices[i].x - vertices[i].x) > 1 || abs(old_vertices[i].y - vertices[i].y) > 1){ //if the dots have moved since the last loop
//clear the previous position
tft.fillCircle(old_vertices[i].x, old_vertices[i].y, point_size, BLACK);
tft.drawLine(old_vertices[i].x, old_vertices[i].y, old_vertices[(i+1)%4].x, old_vertices[(i+1)%4].y, BLACK);
}
tft.fillCircle(vertices[i].x, vertices[i].y, point_size, WHITE);
tft.drawLine(vertices[i].x, vertices[i].y, vertices[(i+1)%4].x, vertices[(i+1)%4].y, WHITE);
}
for (int i = 0; i < 4; i++) {
old_vertices[i] = vertices[i];
}
}
Screen
The screen is a 3.5” TFT Touch screen shield, connected directly to my Arduino MEGA 2560. It uses an ILI9486 driver. Bought from eBay.