Demo touching the perimeter of a circle

Dependencies:   RA8875

main.cpp

Committer:
WiredHome
Date:
6 months ago
Revision:
2:3077639f94fa
Parent:
1:e4f92c21e329
Child:
3:d259be26795b

File content as of revision 2:3077639f94fa:

///
/// RA8875 display library example where a user touch on the perimeter
/// of a circle should be detected.
///
///
///                           0 deg
///                          -+-
///                    -             -
///        
///              /                         \
///        
///        
///            |                             |
/// -90 deg    +              +              + +90 deg
///            |                             |
///         
///         
///              \                         /
///         
///                   
///                    -             -
///                          -+-
///                        +180 deg
///
///
/// NOTE NOTE NOTE NOTE NOTE NOTE
///
/// The code in this example would greatly benefit by refactoring to 
/// use radians, rather than degrees. Further, the coordinate system
/// desired here is more like a clock-face, with 0 degrees is straight 
/// up and increasing angle in the clockwise direction.
///
/// There are a few transformations (direction of rotation and angle
/// offset) that didn't "feel right", even with the right result.
///
/// NOTE NOTE NOTE NOTE NOTE NOTE
///
/// I didn't pay too much attention to managing int, float, and double
/// to avoid unnecessary promotion or math complexity.
///
#include "mbed.h"
#include "RA8875.h"

// Display with Capacitive Touch panel on I2C
RA8875 lcd(p5,p6,p7,p12,NC, p9,p10,p13, "tft"); // MOSI,MISO,SCK,/ChipSelect,/reset, SDA,SCL,/IRQ, name
//RA8875 lcd(p5,p6,p7,p8,NC, p28,p27,p30, "tft");

// debug serial for printf. 
// Some versions of the OS don't support the baud initializer here, then use device.baud(460800) in main.
Serial device(USBTX,USBRX, 460800);    // Initialize to a fast baud
//Serial device(p26,p25);

// Screen size and color depth
#define LCD_W 800
#define LCD_H 480
#define LCD_C 8             // color - bits per pixel

#define BL_NORM 100         // Backlight Normal setting (0 to 255)

//
// The Circle definition around which touches are relevant
//
const point_t center = {200,250};
const dim_t radius = 125;
const dim_t touch_tolerance = 30;


// 
// Checkbox to turn on/off the touch-point
//
const rect_t check_tonoff = {400, 150, LCD_W-1, 182};     // Height based on double-size font
const char * check_tonoff_msg = "[%c] Visible touchpoints";
bool touchVisible = false;   // default hidden


//
// Radio buttons to select between pie pieces
// and a dial-indent
//
const rect_t radio_pie = {400, 250, LCD_W-1, 282};
const char * radio_pie_msg = "(%c) Pie-slice rendering";
const rect_t radio_dial = {400, 300, LCD_W-1, 332};
const char * radio_dial_msg = "(%c) Dial rendering";
int renderOption = 0;   // pie-slice

color_t edgeColor = BrightBlue;
color_t fillColor = Black;

void DrawCircle() {
    // Draw a thickened circle
    lcd.fillellipse(center.x, center.y, radius, radius, fillColor);
    for (int r = 0; r <= 3; r++) {
        lcd.ellipse(center.x, center.y, radius + r, radius + r, edgeColor);
    }
}

void ShowUserControls() {
    lcd.SetTextFontSize(2); // Bigger font; easier to touch
    //
    //      [X] Enable visible touch-points
    //
    lcd.foreground(White);
    lcd.SetTextCursor(check_tonoff.p1);
    lcd.printf(check_tonoff_msg, (touchVisible) ? 'X' : ' ');
    lcd.rect(check_tonoff, Gray);
    //
    //      (*) Pie-slice rendering
    //      ( ) Dial rendering
    //
    lcd.foreground(White);
    lcd.SetTextCursor(radio_pie.p1);
    lcd.printf(radio_pie_msg, (renderOption == 0) ? '*' : ' ');
    lcd.rect(radio_pie, Gray);
    lcd.foreground(White);
    lcd.SetTextCursor(radio_dial.p1);
    lcd.printf(radio_dial_msg, (renderOption == 1) ? '*' : ' ');
    lcd.rect(radio_dial, Gray);
    lcd.SetTextFontSize();
}

void DrawInitialScreen() {
    lcd.cls();
    lcd.foreground(White);          // Change to white
    lcd.printf("RA8875 Touch Dial Example - Build " __DATE__ " " __TIME__ "\r\n");
    lcd.printf("MBED v%d.%d.%d\r\n", MBED_MAJOR_VERSION, MBED_MINOR_VERSION, MBED_PATCH_VERSION);
    ShowUserControls();
    DrawCircle();
}

void TestToggleHit(point_t t) {
    if (lcd.Intersect(check_tonoff, t)) {
        touchVisible = !touchVisible;
        DrawInitialScreen();
    }
}

void TestRenderHit(point_t t) {
    bool update = false;
    if (lcd.Intersect(radio_pie, t)) {
        renderOption = 0;
        edgeColor = BrightBlue;
        fillColor = Black;
        update = true;
    } else if (lcd.Intersect(radio_dial, t)) {
        renderOption = 1;
        edgeColor = BrightBlue;
        fillColor = Gray;
        update = true;
    }
    if (update) {
        DrawInitialScreen();
    }
}


#define PI 3.14159265359f

//              ---------------
// radius = \  / x ^ 2 + y ^ 2
//           \/
int GetRadius(point_t touch, point_t center)
{
    return sqrt(pow(touch.x - center.x, 2) + pow(touch.y - center.y, 2));
}

//           (radians * 180)
// degrees = ---------------
//               pi
float Degrees(float radians)
{
    return radians * 180 / PI;
}

//           (pi * degrees)
// radians = --------------
//                180
float Radians(float degrees)
{
    return (degrees * PI)/180;
}

int GetAngle(point_t touch, point_t center)
{
    int angle = 180 - Degrees(atan2(touch.x - center.x, touch.y - center.y));
    if (angle < 0)
        angle += 360;
    return angle;
}

// From a starting point, and at a given angle (where 0 is straight up),
// and clockwise is increasing angle,
// project a given distance at that angle and return those coordinates.
//
point_t ProjectPoint(point_t start, int angle, dim_t distance)
{
    point_t newPoint;
    float radians = Radians(angle);     // radians are rooted in 0 to the right, and increasing counterclockwise
    radians = -radians + Radians(90);   // reverse direction and 0 at the top
    //device.printf("adj Radians %4.3f\r\n", radians);
    newPoint.x = start.x + distance * sin(radians);
    newPoint.y = start.y - distance * cos(radians);
    return newPoint;
}


int main()
{
    //device.baud(460800);
    printf("\r\n   RA8875 Touch Dial Example - Build " __DATE__ " " __TIME__ "\r\n");
    printf("MBED v%d.%d.%d\r\n", MBED_MAJOR_VERSION, MBED_MINOR_VERSION, MBED_PATCH_VERSION);

    lcd.init(LCD_W,LCD_H,LCD_C,BL_NORM);
    lcd.TouchPanelInit();

    DrawInitialScreen();
    
    while (1) {
        TouchCode_t touched;
        static point_t lastTouch = {LCD_W, LCD_H};     // Init off-screen for easy detection
        touched = lcd.TouchPanelReadable();
        if (touched) {
            point_t Touch = lcd.TouchCoordinates();

            if (touched == touch) {     // Only "on-touch"
                TestToggleHit(Touch);
                TestRenderHit(Touch);
            }

            lcd.foreground(White);
            lcd.SetTextCursor(LCD_W - 8 * 9, 0);
            lcd.printf("(%3d,%3d)", Touch.x,Touch.y);

            // If the touch is near the drawn circle (+/- 30 pixels),
            // compute the angle to the touch from the center of the circle
            if (abs(GetRadius(Touch, center) - radius) <= touch_tolerance) {
                int angle = GetAngle(Touch, center);
                printf("Touch at (%4d,%4d) is %3d degrees from (%4d,%4d)\r\n", 
                    Touch.x, Touch.y, angle, center.x, center.y);
                
                point_t lastP;
                switch (renderOption) {
                    default:
                    case 0:
                        // Fill the circle using 6° pie slices up to the angle of the touch
                        lastP = ProjectPoint(center, 90, radius);  // seed it at angle 0
                        for (int a=6; a<=360; a+=6) {
                            point_t p = ProjectPoint(center, 90 - a, radius);
                            color_t fillColor = (a <= angle) ? Blue : Black;
                            //if ((a <= angle)) {     // show the triangle coordinates (only the fill)
                            //    printf("    (%3d,%3d), (%3d,%3d), (%3d,%3d)\r\n",
                            //        lastP.x,lastP.y, p.x,p.y, center.x,center.y);
                            //}
                            lcd.filltriangle(center, p, lastP, fillColor);
                            lastP = p;
                        }
                        lastTouch.x = LCD_W;    // reset to untouched for a change to the dial
                        break;
                    case 1:
                        lastP = ProjectPoint(center, 90 - angle, 0.75 * radius);
                        if (lastTouch.x != LCD_W) {
                            lcd.fillellipse(lastTouch, radius/5,radius/5, fillColor);
                        }
                        lcd.fillellipse(lastP, radius/5,radius/5, Black);
                        lastTouch = lastP;
                        break;
                }
                // Show the touch point (note we're not erasing the old one)
                if (touchVisible) {
                    lcd.fillellipse(Touch.x, Touch.y, 5, 5, BrightRed);
                }
            }
        }
    }
}