Drawing a Simple Mouse Cursor without "Ghost" Images

Hi,

So I'm very, very new to programming generally (including with the Arduino). Right now I'm trying to write a program that displays a city map on an Adafruit ST7735 LCD screen connected to my breadboard. I want to draw a simple shape to represent a mouse cursor (I'm going to call this the "cursor" from now on) and control it using a joystick (this one looks also exactly like the sticks on a PS3 controller, I think they also called them thumbsticks). I just have a little map at the moment that fits onto the LCD screen (so no scrolling is required). I also have code for the cursor that correctly draws the rectangle and moves the rectangle around the screen. The problem is that it doesn't erase the previous image of the cursor so instead of just one cursor that floats around the screen, it leaves a trail of old positions still drawn onto the map. It starts looking like that old game "Snake" after a while (except the tails don't move, they just stay where they were drawn the first time). As far as I can think of there are really only 2 possible ways to fix this:

  1. Refresh the entire map after each draw.
    This is incredibly cumbersome and would happen all the time because my cursor is currently being drawn in loop(). I can't figure out a way to write a separate function elsewhere and then call it in loop. I tried some other options like writing a function "void myCursor() {}" and then calling it in loop() just as "myCursor();" but it said that this function was not declared in this scope so I sort of gave up on that route. Having a seperate function would actually be pretty nice since it would clean up the code a bit and allow for clearer conditions to be put in, like for example if the joystick is not pressed in any direction I don't want it just continually drawing a rectangle at the current position (although if this is a trivial concern then I'm not married to it. I just think that if I start refreshing pixels or the map as a whole this can become an expensive operation to continually be drawing a rectangle, refreshing those pixels, drawing the rectangle again, refreshing again, when the joystick isn't being pressed). Anyway, that's really off-topic. In short, I don't really want to do the entire map refresh thing if I can help it.

  2. What I call the "Store-Draw-Refresh" strategy
    This is where I could somehow save the contents of the pixels before the cursor moves there into some temporary holding variable, then move and draw the cursor at the new position, then when the cursor moves away again I could refresh the pixels on the map from the value in my temporary holding variable, and repeat the process. As I think about it, the map (or anything displayed on the screen) is like an MxN matrix where each entry, a*[j], in the matrix is an RGB code (ie. 3 uint_8's). So pixel (10, 15) near the top-left corner of the screen would have some kind an RGB entry: a[10][15] = (58, 7, 124) or whatever. This is all fine in theory but I have absolutely NO IDEA how to work with this in C++. I don't know how to read the RGB codes from the pixel, how to store them, and then how to refresh them once the cursor has moved away. Unfortunately, this is also the best strategy (at least as far as I can see) since redrawing the whole map would just destroy the whole operation of the program in terms of processing time.*
    *These are the ideas that I have currently. I am by no means an expert so if there are better ideas I would absolutely LOVE to hear them. Otherwise, does someone out there have any idea about how to implement these ideas? I am just really stuck at the moment. *
    Thank you so much! A snippet of my existing code is pasted below for your review.
    ```
    *//Horizontal joystick control attached to analog pin 0.
    //Vertical joystick control attached to analog pin 1.
    int horizJoy = A0;
    int vertJoy = A1;

//Cursor variables.  Start cursor x-y at screen position (64, 64).
int16_t xCoor = 64;
int16_t yCoor = 64;

void loop() {
 
  //Draw a rectangular mouse cursor to move over the map of Edmonton.
  //Define this cursor to have an x-y screen position, a 3x3 pixel size, with
  //the colour black.
  tft.drawRect(xCoor, yCoor, 3, 3, ST7735_BLACK);

//Define variables to hold the current voltage read from the Joystick
  //horizontal and vertical pins.  Constrain this reading to prevent
  //overflow in the instance we get a really weird reading.  This also
  //assists when performing the calculations of screen position to have a
  //known, predictable range of possible voltage values.
  int16_t currentVoltageX = constrain(analogRead(horizJoy), 0, 1023);
  int16_t currentVoltageY = constrain(analogRead(vertJoy), 0, 1023);

//Define the current voltage given off by the joystick to the the CHANGE
  //in the x-y coordinates of the cursor on the screen.  This allows for
  //a change in relative position, instead of an absolute reference to
  //screen location.  The voltage given off by the joystick (from prior
  //testing) is reliably between 15 and 1023 for both horizontal and vertical
  //movements.  Define the max change in cursor position for given read to be
  //+/- 3 pixels at a time.
  int16_t changeinX = map(currentVoltageX, 15, 1023, -3, 3);
  int16_t changeinY = map(currentVoltageY, 15, 1023, -3, 3);

//Update the current x-y coordinates of the cursor on the screen using
  //the mapped change in x-y from the above.  Constrain these coordinates to
  //prevent the mouse from moving off into crazy, nowhere land (ie. off the
  //edge of the map).  Delay a short time to get a clear read from the
  //joystick.
  xCoor = constrain((xCoor + changeinX), 0, 128);
  yCoor = constrain((yCoor + changeinY), 0, 160);
  delay(100);

}*
```

There is a third possibility, and that it to draw the cursor in X-OR mode. Draw it twice - once to show it and once to erase it. Does your LCD support this mode?

Why do you have to refresh the entire map in option (1)? Why can't you just redraw the bit where the cursor was?

Why can't you just redraw the bit where the cursor was?

Likely because the code isn't drawing pixel by pixel. Instead it's likely drawing lines and arcs. And, figuring which stuff to redraw (and erase) would likely take longer than drawing the whole screen.

The XOR mode PaulS suggested is the easiest option, if it's available to you.

Make sure that you redraw the cursor after each screen refresh, and if you are doing any incremental updates to the screen you should remove the cursor (by XOR), make the update and then redraw the cursor (by XOR).

I thought XOR redrawing was patented by Apple or some such?

That can't be right but given today's laws anything is possible.


Rob