Go Down

Topic: Adafruit_GFX fillArc (Read 5310 times) previous topic - next topic


bodmer

#1
Jul 17, 2016, 08:19 pm Last Edit: Jul 17, 2016, 08:19 pm by bodmer
Here is some example code to draw an arc:

Code: [Select]
float start_angle = 0.5, end_angle = 1.5;
  int x = 50, y = 100, r = 30;
  for (float i = start_angle; i < end_angle; i = i + 0.05)
  {
    display.drawPixel(x + cos(i) * r, y + sin(i) * r, WHITE); // center point is (50,100)
  }


You will have to adapt it for your needs. Bear in mind the angles are in radians.

david_prentice

You want to fillArc() rather than drawArc()
So you will want to identify the lhs and rhs of any Arc.
Then drawLine()  between these points.  It is going to be fairly efficient even with the trig functions.
You can use Bresenham's algorithm if you want.
.
David.

bodmer

This is an example that produces an ellipsoid arc of defined thickness similar to your needs. This could easily be adapted for your own TFT library and to create the graphic you are looking for.

Code: [Select]


#include <TFT_HX8357_Due.h> // Hardware-specific library

TFT_HX8357_Due tft = TFT_HX8357_Due();       // Invoke custom library

#define DEG2RAD 0.0174532925

void setup(void) {
  tft.begin();

  tft.setRotation(1);

  tft.fillScreen(HX8357_BLACK);

}


void loop() {

  int w = 8;
  int rx = 160;
  int ry = 100;
  
  for (int n = 0; n < 5; n++) {
    fillArc(160, 100, 300, 40, rx-n*w, ry-n*w, w, 31-n*6);
  }

  while(1);
}

// #########################################################################
// Draw an arc with a defined thickness
// #########################################################################

// x,y == coords of centre of arc
// start_angle = 0 - 359
// seg_count = number of 3 degree segments to draw (120 => 360 degree arc)
// rx = x axis radius
// yx = y axis radius
// w  = width (thickness) of arc in pixels
// colour = 16 bit colour value
// Note if rx and ry are the same an arc of a circle is drawn

int fillArc(int x, int y, int start_angle, int seg_count, int rx, int ry, int w, unsigned int colour)
{

  byte seg = 3; // Segments are 3 degrees wide = 120 segments for 360 degrees
  byte inc = 3; // Draw segments every 3 degrees, increase to 6 for segmented ring

  // Draw colour blocks every inc degrees
  for (int i = start_angle; i < start_angle + seg * seg_count; i += inc) {
    // Calculate pair of coordinates for segment start
    float sx = cos((i - 90) * DEG2RAD);
    float sy = sin((i - 90) * DEG2RAD);
    uint16_t x0 = sx * (rx - w) + x;
    uint16_t y0 = sy * (ry - w) + y;
    uint16_t x1 = sx * rx + x;
    uint16_t y1 = sy * ry + y;

    // Calculate pair of coordinates for segment end
    float sx2 = cos((i + seg - 90) * DEG2RAD);
    float sy2 = sin((i + seg - 90) * DEG2RAD);
    int x2 = sx2 * (rx - w) + x;
    int y2 = sy2 * (ry - w) + y;
    int x3 = sx2 * rx + x;
    int y3 = sy2 * ry + y;

    tft.fillTriangle(x0, y0, x1, y1, x2, y2, colour);
    tft.fillTriangle(x1, y1, x2, y2, x3, y3, colour);
  }
}



Using filled triangles to create trapezoidal segments means the arc will not having missing pixels (a common problem with other methods).

s-car


bodmer

You are welcome!

I have set the segment increment size to 3 degrees for you, but I usually use the function with 6 degree increments:

Code: [Select]

  byte seg = 6; // Segments are 6 degrees wide = 60 segments for 360 degrees
  byte inc = 6; // Draw segments every 6 degrees, increase to 12 for segmented ring


Then there are 60 segments to a full circle  and hence a 1 minute clock with a seconds indicator can easily be created.


bodmer

I have now updated this arcDraw() function to reduce the number of sin/cos trig. calculations in the loop.

This boosts performance noticeably on an UNO or Mega.

Code: [Select]
// #########################################################################
// Draw a circular or elliptical arc with a defined thickness
// #########################################################################

// x,y == coords of centre of arc
// start_angle = 0 - 359
// seg_count = number of 3 degree segments to draw (120 => 360 degree arc)
// rx = x axis radius
// yx = y axis radius
// w  = width (thickness) of arc in pixels
// colour = 16 bit colour value
// Note if rx and ry are the same then an arc of a circle is drawn

int fillArc2(int x, int y, int start_angle, int seg_count, int rx, int ry, int w, unsigned int colour)
{

  byte seg = 3; // Segments are 3 degrees wide = 120 segments for 360 degrees
  byte inc = 3; // Draw segments every 3 degrees, increase to 6 for segmented ring

    // Calculate first pair of coordinates for segment start
    float sx = cos((start_angle - 90) * DEG2RAD);
    float sy = sin((start_angle - 90) * DEG2RAD);
    uint16_t x0 = sx * (rx - w) + x;
    uint16_t y0 = sy * (ry - w) + y;
    uint16_t x1 = sx * rx + x;
    uint16_t y1 = sy * ry + y;

  // Draw colour blocks every inc degrees
  for (int i = start_angle; i < start_angle + seg * seg_count; i += inc) {

    // Calculate pair of coordinates for segment end
    float sx2 = cos((i + seg - 90) * DEG2RAD);
    float sy2 = sin((i + seg - 90) * DEG2RAD);
    int x2 = sx2 * (rx - w) + x;
    int y2 = sy2 * (ry - w) + y;
    int x3 = sx2 * rx + x;
    int y3 = sy2 * ry + y;

    tft.fillTriangle(x0, y0, x1, y1, x2, y2, colour);
    tft.fillTriangle(x1, y1, x2, y2, x3, y3, colour);

    // Copy segment end to sgement start for next segment
    x0 = x2;
    y0 = y2;
    x1 = x3;
    y1 = y3;
  }
}



As noted previously another performance boost is to increase the segment and increment size to 6 degrees instead of 3.

Alexandrelv

Hi Bodmer I'm sorry to bother your library and the best in everything I'm using with esp32 and it's amazing about the example TFT_ring_meter how I manage to make the bar just horizontal without being arched as in the video and photo below, I know you it must be very busy but i would be very grateful for the help i'm desperate i need for a college job and i'm still walking in programming i'm from brazil so i'm sorry for the bad english ok

https://www.youtube.com/watch?v=8HfOyRfHlAU


Go Up