- Initial commit of base firmware - which is still very raw
This commit is contained in:
760
Firmware/Display_Shapes.c
Normal file
760
Firmware/Display_Shapes.c
Normal file
@@ -0,0 +1,760 @@
|
||||
/*
|
||||
* Display_Shapes.c
|
||||
*
|
||||
* Created: Mon Jul 19 2021 17:57:03
|
||||
* Author Chris
|
||||
*/
|
||||
#include "Display_Shapes.h"
|
||||
#include "Display_Color.h"
|
||||
#include "Display_Objects.h"
|
||||
|
||||
#include "hardware/dma.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
// ============================================================================================
|
||||
// Defines
|
||||
#define CORNER_TOP_LEFT 0x01
|
||||
#define CORNER_TOP_RIGHT 0x02
|
||||
#define CORNER_BOTTOM_RIGHT 0x04 // Fixed: was incorrectly 0x08
|
||||
#define CORNER_BOTTOM_LEFT 0x08 // Fixed: was incorrectly 0x04
|
||||
|
||||
#define DEG2RAD (float)(M_PI / 180)
|
||||
|
||||
#define RECT_WIDTH_DMA_THRESHOLD 16
|
||||
|
||||
|
||||
// ============================================================================================
|
||||
// Variables
|
||||
static Display_Image_Buffer** _Current_Buffer;
|
||||
|
||||
static int _DMA_Channel_Fill_Screen;
|
||||
static dma_channel_config _DMA_Config_Fill_Screen;
|
||||
|
||||
static int _DMA_Channel_Drawing;
|
||||
static dma_channel_config _DMA_Config_Drawing;
|
||||
|
||||
|
||||
// ============================================================================================
|
||||
// Function Declarations
|
||||
void Display_Shapes_Draw_Circle_Helper(int16_t x0, int16_t y0, uint16_t radius, uint16_t thickness, uint8_t cornername, Display_Color color);
|
||||
void Display_Shapes_Draw_Circle_Helper_Improved(int16_t x0, int16_t y0, uint16_t radius, uint16_t thickness, uint8_t cornername, Display_Color color);
|
||||
void Display_Shapes_Draw_Circle_Helper_Single_Pixel(int16_t x0, int16_t y0, uint16_t radius, uint8_t cornername, Display_Color color);
|
||||
void Display_Shapes_Draw_Circle_Helper_Filled(int16_t x0, int16_t y0, uint16_t radius, uint8_t corners, int16_t delta, Display_Color color);
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
Functions
|
||||
*******************************************************************/
|
||||
void Display_Shapes_Init(Display_Image_Buffer** current_buffer)
|
||||
{
|
||||
_Current_Buffer = current_buffer;
|
||||
|
||||
_DMA_Channel_Fill_Screen = dma_claim_unused_channel(true);
|
||||
_DMA_Config_Fill_Screen = dma_channel_get_default_config(_DMA_Channel_Fill_Screen);
|
||||
channel_config_set_transfer_data_size(&_DMA_Config_Fill_Screen, DMA_SIZE_32);
|
||||
channel_config_set_read_increment(&_DMA_Config_Fill_Screen, false);
|
||||
channel_config_set_write_increment(&_DMA_Config_Fill_Screen, true);
|
||||
|
||||
|
||||
_DMA_Channel_Drawing = dma_claim_unused_channel(true);
|
||||
_DMA_Config_Drawing = dma_channel_get_default_config(_DMA_Channel_Drawing);
|
||||
channel_config_set_transfer_data_size(&_DMA_Config_Drawing, DMA_SIZE_32);
|
||||
channel_config_set_read_increment(&_DMA_Config_Drawing, false);
|
||||
channel_config_set_write_increment(&_DMA_Config_Drawing, true);
|
||||
}
|
||||
|
||||
void Display_Shapes_Fill_Screen(Display_Color color)
|
||||
{
|
||||
static uint32_t Clear_Value;
|
||||
Clear_Value = (color << 16) | color; // Duplicate 16-bit value
|
||||
|
||||
dma_channel_configure(_DMA_Channel_Fill_Screen, &_DMA_Config_Fill_Screen, (*_Current_Buffer)->Dim_1, &Clear_Value, DISPLAY_IMAGE_BUFFER_PIXEL_SIZE/2, false); // Half the transfers
|
||||
dma_channel_start(_DMA_Channel_Fill_Screen);
|
||||
dma_channel_wait_for_finish_blocking(_DMA_Channel_Fill_Screen);
|
||||
}
|
||||
|
||||
void Display_Shapes_Draw_Pixel_Safe(int16_t x, int16_t y, Display_Color color)
|
||||
{
|
||||
if(x >= 0 && x < DISPLAY_WIDTH && y >= 0 && y < DISPLAY_HEIGHT)
|
||||
{
|
||||
(*_Current_Buffer)->Dim_2[y][x] = color;
|
||||
}
|
||||
}
|
||||
|
||||
void Display_Shapes_Draw_HLine(int16_t x, int16_t y, uint16_t width, uint16_t thickness, Display_Color color)
|
||||
{
|
||||
Display_Shapes_Draw_Rect_Filled(x, y, width, thickness, color);
|
||||
}
|
||||
|
||||
void Display_Shapes_Draw_VLine(int16_t x, int16_t y, uint16_t height, uint16_t thickness, Display_Color color)
|
||||
{
|
||||
Display_Shapes_Draw_Rect_Filled(x, y, thickness, height, color);
|
||||
}
|
||||
|
||||
void Display_Shapes_Draw_Line_XY(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t thickness, Display_Color color)
|
||||
{
|
||||
int16_t Temp;
|
||||
|
||||
if(y0 == y1)
|
||||
{
|
||||
if(x1 > x0)
|
||||
{
|
||||
Display_Shapes_Draw_HLine(x0, y0, x1 - x0 + 1, thickness, color);
|
||||
}
|
||||
else if (x1 < x0)
|
||||
{
|
||||
Display_Shapes_Draw_HLine(x1, y0, x0 - x1 + 1, thickness, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
Display_Shapes_Draw_Circle_Filled(x0, y0, thickness, color);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if(x0 == x1)
|
||||
{
|
||||
if(y1 > y0)
|
||||
{
|
||||
Display_Shapes_Draw_VLine(x0, y0, y1 - y0 + 1, thickness, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
Display_Shapes_Draw_VLine(x0, y1, y0 - y1 + 1, thickness, color);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bool Steep = abs(y1 - y0) > abs(x1 - x0);
|
||||
if(Steep == true)
|
||||
{
|
||||
Temp = x0; x0 = y0; y0 = Temp;
|
||||
Temp = x1; x1 = y1; y1 = Temp;
|
||||
}
|
||||
if(x0 > x1)
|
||||
{
|
||||
Temp = x0; x0 = x1; x1 = Temp;
|
||||
Temp = y0; y0 = y1; y1 = Temp;
|
||||
}
|
||||
|
||||
int16_t dx, dy;
|
||||
dx = x1 - x0;
|
||||
dy = abs(y1 - y0);
|
||||
|
||||
int16_t err = dx / 2;
|
||||
int16_t ystep;
|
||||
|
||||
if (y0 < y1)
|
||||
{
|
||||
ystep = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ystep = -1;
|
||||
}
|
||||
|
||||
int16_t xbegin = x0;
|
||||
if(Steep == true)
|
||||
{
|
||||
for (; x0<=x1; x0++)
|
||||
{
|
||||
err -= dy;
|
||||
if (err < 0)
|
||||
{
|
||||
int16_t len = x0 - xbegin;
|
||||
if (len>1)
|
||||
{
|
||||
Display_Shapes_Draw_VLine(y0, xbegin, len + 1, thickness, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(thickness == 1)
|
||||
Display_Shapes_Draw_Pixel_Safe(y0, x0, color);
|
||||
else
|
||||
Display_Shapes_Draw_Circle_Filled(y0, x0, thickness >> 1, color);
|
||||
}
|
||||
xbegin = x0 + 1;
|
||||
y0 += ystep;
|
||||
err += dx;
|
||||
}
|
||||
}
|
||||
if (x0 > xbegin + 1)
|
||||
{
|
||||
Display_Shapes_Draw_VLine(y0, xbegin, x0 - xbegin, thickness, color);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
for (; x0<=x1; x0++)
|
||||
{
|
||||
err -= dy;
|
||||
if (err < 0)
|
||||
{
|
||||
int16_t len = x0 - xbegin;
|
||||
if (len>1)
|
||||
{
|
||||
Display_Shapes_Draw_HLine(xbegin, y0, len + 1, thickness, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(thickness == 1)
|
||||
Display_Shapes_Draw_Pixel_Safe(x0, y0, color);
|
||||
else
|
||||
Display_Shapes_Draw_Circle_Filled(x0, y0, thickness >> 1, color);
|
||||
}
|
||||
xbegin = x0 + 1;
|
||||
y0 += ystep;
|
||||
err += dx;
|
||||
}
|
||||
}
|
||||
if (x0 > xbegin + 1)
|
||||
{
|
||||
Display_Shapes_Draw_HLine(xbegin, y0, x0 - xbegin, thickness, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Display_Shapes_Draw_Line_Rad(int16_t x, int16_t y, float angle, uint16_t radius_start, uint16_t radius_end, uint16_t thickness, Display_Color color)
|
||||
{
|
||||
int16_t X0 = x + cos(angle * DEG2RAD) * radius_start;
|
||||
int16_t Y0 = y + sin(angle * DEG2RAD) * radius_start;
|
||||
|
||||
int16_t X1 = x + cos(angle * DEG2RAD) * radius_end;
|
||||
int16_t Y1 = y + sin(angle * DEG2RAD) * radius_end;
|
||||
|
||||
Display_Shapes_Draw_Line_XY(X0, Y0, X1, Y1, thickness, color);
|
||||
}
|
||||
|
||||
void Display_Shapes_Draw_Rect_Frame(int16_t x, int16_t y, uint16_t width, uint16_t height, uint16_t thickness, Display_Color color)
|
||||
{
|
||||
Display_Shapes_Draw_HLine(x , y , width , thickness, color);
|
||||
Display_Shapes_Draw_HLine(x , y + height, width + thickness , thickness, color);
|
||||
|
||||
Display_Shapes_Draw_VLine(x , y , height , thickness, color);
|
||||
Display_Shapes_Draw_VLine(x + width , y , height + thickness, thickness, color);
|
||||
}
|
||||
|
||||
void Display_Shapes_Draw_Rect_Filled(int16_t x, int16_t y, uint16_t width, uint16_t height, Display_Color color)
|
||||
{
|
||||
// Early bounds checking
|
||||
if (x >= DISPLAY_WIDTH || y >= DISPLAY_HEIGHT || x + width <= 0 || y + height <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clip to screen bounds
|
||||
int16_t Start_X = (x < 0) ? 0 : x;
|
||||
int16_t Start_Y = (y < 0) ? 0 : y;
|
||||
int16_t End_X = (x + width > DISPLAY_WIDTH) ? DISPLAY_WIDTH : x + width;
|
||||
int16_t End_Y = (y + height > DISPLAY_HEIGHT) ? DISPLAY_HEIGHT : y + height;
|
||||
|
||||
uint16_t Clipped_Width = End_X - Start_X;
|
||||
uint16_t Clipped_Height = End_Y - Start_Y;
|
||||
|
||||
if (Clipped_Width == 0 || Clipped_Height == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For wide rectangles, use DMA for each horizontal row (memory-contiguous)
|
||||
if (Clipped_Width >= RECT_WIDTH_DMA_THRESHOLD) // DMA threshold for width
|
||||
{
|
||||
static uint32_t Fill_Value;
|
||||
Fill_Value = (color << 16) | color; // Pack two 16-bit values
|
||||
|
||||
for (int16_t row = Start_Y; row < End_Y; row++) {
|
||||
// Calculate destination address for this row
|
||||
Display_Color* Dst = &(*_Current_Buffer)->Dim_2[row][Start_X];
|
||||
|
||||
// Use DMA for horizontal row fill (contiguous memory)
|
||||
dma_channel_configure(_DMA_Channel_Drawing, &_DMA_Config_Drawing, Dst, &Fill_Value, Clipped_Width / 2, false);
|
||||
dma_channel_start(_DMA_Channel_Drawing);
|
||||
dma_channel_wait_for_finish_blocking(_DMA_Channel_Drawing);
|
||||
|
||||
// Handle odd width
|
||||
if (Clipped_Width & 1) {
|
||||
(*_Current_Buffer)->Dim_2[row][End_X - 1] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For narrow rectangles, use optimized nested loop
|
||||
for (int16_t row = Start_Y; row < End_Y; row++)
|
||||
{
|
||||
// Each row is contiguous in memory - cache friendly
|
||||
Display_Color* Row_Ptr = &(*_Current_Buffer)->Dim_2[row][Start_X];
|
||||
|
||||
for (int16_t col = 0; col < Clipped_Width; col++)
|
||||
{
|
||||
Row_Ptr[col] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Display_Shapes_Draw_Circle_Frame(int16_t center_x, int16_t center_y, uint16_t radius, uint16_t thickness, Display_Color color)
|
||||
{
|
||||
if(thickness == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int16_t f = 1 - radius;
|
||||
int16_t ddF_x = 1;
|
||||
int16_t ddF_y = -2 * radius;
|
||||
int16_t X = 0;
|
||||
int16_t Y = radius;
|
||||
|
||||
if(thickness == 1)
|
||||
{
|
||||
Display_Shapes_Draw_Pixel_Safe(center_x , center_y+radius , color);
|
||||
Display_Shapes_Draw_Pixel_Safe(center_x , center_y-radius , color);
|
||||
Display_Shapes_Draw_Pixel_Safe(center_x+radius , center_y , color);
|
||||
Display_Shapes_Draw_Pixel_Safe(center_x-radius , center_y , color);
|
||||
}
|
||||
else
|
||||
{
|
||||
Display_Shapes_Draw_Circle_Filled(center_x , center_y+radius , thickness >> 1, color);
|
||||
Display_Shapes_Draw_Circle_Filled(center_x , center_y-radius , thickness >> 1, color);
|
||||
Display_Shapes_Draw_Circle_Filled(center_x+radius , center_y , thickness >> 1, color);
|
||||
Display_Shapes_Draw_Circle_Filled(center_x-radius , center_y , thickness >> 1, color);
|
||||
}
|
||||
|
||||
while (X<Y)
|
||||
{
|
||||
if (f >= 0)
|
||||
{
|
||||
Y--;
|
||||
ddF_y += 2;
|
||||
f += ddF_y;
|
||||
}
|
||||
X++;
|
||||
ddF_x += 2;
|
||||
f += ddF_x;
|
||||
|
||||
if(thickness == 1)
|
||||
{
|
||||
Display_Shapes_Draw_Pixel_Safe(center_x + X, center_y + Y, color);
|
||||
Display_Shapes_Draw_Pixel_Safe(center_x - X, center_y + Y, color);
|
||||
Display_Shapes_Draw_Pixel_Safe(center_x + X, center_y - Y, color);
|
||||
Display_Shapes_Draw_Pixel_Safe(center_x - X, center_y - Y, color);
|
||||
Display_Shapes_Draw_Pixel_Safe(center_x + Y, center_y + X, color);
|
||||
Display_Shapes_Draw_Pixel_Safe(center_x - Y, center_y + X, color);
|
||||
Display_Shapes_Draw_Pixel_Safe(center_x + Y, center_y - X, color);
|
||||
Display_Shapes_Draw_Pixel_Safe(center_x - Y, center_y - X, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
Display_Shapes_Draw_Circle_Filled(center_x + X, center_y + Y, thickness >> 1, color);
|
||||
Display_Shapes_Draw_Circle_Filled(center_x - X, center_y + Y, thickness >> 1, color);
|
||||
Display_Shapes_Draw_Circle_Filled(center_x + X, center_y - Y, thickness >> 1, color);
|
||||
Display_Shapes_Draw_Circle_Filled(center_x - X, center_y - Y, thickness >> 1, color);
|
||||
Display_Shapes_Draw_Circle_Filled(center_x + Y, center_y + X, thickness >> 1, color);
|
||||
Display_Shapes_Draw_Circle_Filled(center_x - Y, center_y + X, thickness >> 1, color);
|
||||
Display_Shapes_Draw_Circle_Filled(center_x + Y, center_y - X, thickness >> 1, color);
|
||||
Display_Shapes_Draw_Circle_Filled(center_x - Y, center_y - X, thickness >> 1, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Display_Shapes_Draw_Circle_Filled(int16_t center_x, int16_t center_y, uint16_t radius, Display_Color color)
|
||||
{
|
||||
Display_Shapes_Draw_VLine(center_x, center_y-radius, 2*radius+1, 1, color);
|
||||
|
||||
uint8_t Corner_Name = 3;
|
||||
int16_t Delta = 0;
|
||||
|
||||
int16_t f = 1 - radius;
|
||||
int16_t ddF_x = 1;
|
||||
int16_t ddF_y = -2 * radius;
|
||||
int16_t X = 0;
|
||||
int16_t Y = radius;
|
||||
|
||||
while(X<Y)
|
||||
{
|
||||
if(f >= 0)
|
||||
{
|
||||
Y--;
|
||||
ddF_y += 2;
|
||||
f += ddF_y;
|
||||
}
|
||||
X++;
|
||||
ddF_x += 2;
|
||||
f += ddF_x;
|
||||
|
||||
if ((Corner_Name & 0x1) > 0)
|
||||
{
|
||||
Display_Shapes_Draw_VLine(center_x+X, center_y-Y, 2*Y+1+Delta, 1, color);
|
||||
Display_Shapes_Draw_VLine(center_x+Y, center_y-X, 2*X+1+Delta, 1, color);
|
||||
}
|
||||
if ((Corner_Name & 0x2)>0)
|
||||
{
|
||||
Display_Shapes_Draw_VLine(center_x-X, center_y-Y, 2*Y+1+Delta, 1, color);
|
||||
Display_Shapes_Draw_VLine(center_x-Y, center_y-X, 2*X+1+Delta, 1, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Display_Shapes_Draw_Round_Rect_Frame(int16_t x, int16_t y, uint16_t width, uint16_t height, uint16_t radius, uint16_t thickness, Display_Color color)
|
||||
{
|
||||
// Validate input parameters
|
||||
if (width < 2 || height < 2 || thickness == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clamp radius to maximum possible value
|
||||
uint16_t max_radius = ((width < height) ? width : height) / 2;
|
||||
if (radius > max_radius) {
|
||||
radius = max_radius;
|
||||
}
|
||||
|
||||
// For very small rectangles, just draw a filled rectangle
|
||||
if (radius == 0 || width <= 2*radius || height <= 2*radius) {
|
||||
Display_Shapes_Draw_Rect_Frame(x, y, width, height, thickness, color);
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw the straight edges (avoiding corners)
|
||||
// Top edge
|
||||
if (width > 2 * radius) {
|
||||
Display_Shapes_Draw_HLine(x + radius, y, width - 2 * radius, thickness, color);
|
||||
}
|
||||
|
||||
// Bottom edge
|
||||
if (width > 2 * radius) {
|
||||
Display_Shapes_Draw_HLine(x + radius, y + height - thickness, width - 2 * radius, thickness, color);
|
||||
}
|
||||
|
||||
// Left edge
|
||||
if (height > 2 * radius) {
|
||||
Display_Shapes_Draw_VLine(x, y + radius, height - 2 * radius, thickness, color);
|
||||
}
|
||||
|
||||
// Right edge
|
||||
if (height > 2 * radius) {
|
||||
Display_Shapes_Draw_VLine(x + width - thickness, y + radius, height - 2 * radius, thickness, color);
|
||||
}
|
||||
|
||||
// Draw the four corner arcs with corrected positioning
|
||||
Display_Shapes_Draw_Circle_Helper_Improved(x + radius, y + radius, radius, thickness, CORNER_TOP_LEFT, color); // Top-left corner
|
||||
Display_Shapes_Draw_Circle_Helper_Improved(x + width - radius - 1, y + radius, radius, thickness, CORNER_TOP_RIGHT, color); // Top-right corner
|
||||
Display_Shapes_Draw_Circle_Helper_Improved(x + width - radius - 1, y + height - radius - 1, radius, thickness, CORNER_BOTTOM_RIGHT, color); // Bottom-right corner
|
||||
Display_Shapes_Draw_Circle_Helper_Improved(x + radius, y + height - radius - 1, radius, thickness, CORNER_BOTTOM_LEFT, color); // Bottom-left corner
|
||||
}
|
||||
|
||||
void Display_Shapes_Draw_Round_Rect_Filled(int16_t x, int16_t y, uint16_t width, uint16_t height, uint16_t radius, Display_Color color)
|
||||
{
|
||||
uint16_t Max_Radius = ((width < height) ? width : height) / 2; // 1/2 minor axis
|
||||
|
||||
if (radius > Max_Radius)
|
||||
{
|
||||
radius = Max_Radius;
|
||||
}
|
||||
|
||||
// Smarter Version
|
||||
Display_Shapes_Draw_Rect_Filled(x + radius, y, width - 2 * radius, height, color);
|
||||
|
||||
// Draw Four Corners
|
||||
Display_Shapes_Draw_Circle_Helper_Filled(x + width - radius - 1 , y + radius, radius, 1, height - 2 * radius - 1, color);
|
||||
Display_Shapes_Draw_Circle_Helper_Filled(x + radius , y + radius, radius, 2, height - 2 * radius - 1, color);
|
||||
}
|
||||
|
||||
void Display_Shapes_Draw_Arc_Frame(int16_t center_x, int16_t center_y, int16_t radius, uint16_t thickness, float angle_start, float angle_end, uint16_t steps, Display_Color color)
|
||||
{
|
||||
if (thickness == 0 || radius == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalize angles to 0-360 range
|
||||
while (angle_start < 0.0f) angle_start += 360.0f;
|
||||
while (angle_start >= 360.0f) angle_start -= 360.0f;
|
||||
while (angle_end < 0.0f) angle_end += 360.0f;
|
||||
while (angle_end >= 360.0f) angle_end -= 360.0f;
|
||||
|
||||
// Handle case where arc crosses 0° boundary
|
||||
while(angle_end < angle_start) {
|
||||
angle_end += 360;
|
||||
}
|
||||
|
||||
if(steps == ARC_FRAME_AUTO_STEPS)
|
||||
{
|
||||
float Arc_Length = angle_end - angle_start;
|
||||
steps = (uint16_t)(Arc_Length * 2.0f); // 2 Steps per Degree
|
||||
|
||||
// Minimum steps for small arcs
|
||||
if (steps < 8) {
|
||||
steps = 8;
|
||||
}
|
||||
}
|
||||
|
||||
float Angle_Step = (float)(angle_end - angle_start) / (float)steps;
|
||||
|
||||
for (float i = angle_start; i <= angle_end; i = i + Angle_Step)
|
||||
{
|
||||
|
||||
if(thickness == 1) {
|
||||
Display_Shapes_Draw_Pixel_Safe(center_x + cos(i*DEG2RAD) * radius, center_y + sin(i*DEG2RAD) * radius, color);
|
||||
}
|
||||
else {
|
||||
Display_Shapes_Draw_Circle_Filled(center_x + cos(i*DEG2RAD) * radius, center_y + sin(i*DEG2RAD) * radius, thickness >> 1, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Display_Shapes_Draw_Glow_Circle(int16_t center_x, int16_t center_y, uint16_t radius, Display_Color core_color, Display_Color glow_color)
|
||||
{
|
||||
// Draw a circle with a subtle glow effect
|
||||
// Core circle with gradually fading outer ring
|
||||
|
||||
if (radius == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Define glow parameters
|
||||
uint16_t Glow_Radius = radius + 4; // Glow extends 4 pixels beyond core
|
||||
uint16_t Core_Radius = radius;
|
||||
|
||||
// Draw glow layers from outside to inside for proper alpha blending effect
|
||||
for (uint16_t Layer = Glow_Radius; Layer > Core_Radius; Layer--) {
|
||||
// Calculate glow intensity based on distance from core
|
||||
float Distance_From_Core = (float)(Layer - Core_Radius);
|
||||
float Max_Glow_Distance = (float)(Glow_Radius - Core_Radius);
|
||||
float Glow_Intensity = 1.0f - (Distance_From_Core / Max_Glow_Distance);
|
||||
|
||||
// Apply easing curve for more natural glow falloff
|
||||
Glow_Intensity = Glow_Intensity * Glow_Intensity; // Quadratic falloff
|
||||
|
||||
// Blend glow color with background (assuming dark background)
|
||||
Display_Color Layer_Color = Display_Color_Interpolate_Float(
|
||||
Display_Objects_Background_Color_Get(),
|
||||
glow_color,
|
||||
Glow_Intensity * 0.3f // Max 30% glow intensity
|
||||
);
|
||||
|
||||
// Draw this glow layer as a thin circle frame
|
||||
Display_Shapes_Draw_Circle_Frame(center_x, center_y, Layer, 1, Layer_Color);
|
||||
}
|
||||
|
||||
// Draw the solid core circle
|
||||
Display_Shapes_Draw_Circle_Filled(center_x, center_y, Core_Radius, core_color);
|
||||
|
||||
// Add a subtle highlight for 3D effect (optional)
|
||||
if (Core_Radius > 3) {
|
||||
// Small highlight offset towards top-left
|
||||
int16_t Highlight_X = center_x - (Core_Radius / 3);
|
||||
int16_t Highlight_Y = center_y - (Core_Radius / 3);
|
||||
uint16_t Highlight_Radius = Core_Radius / 3;
|
||||
|
||||
Display_Color Highlight_Color = Display_Color_Interpolate_Float(
|
||||
core_color,
|
||||
DISPLAY_COLOR_WHITE,
|
||||
0.4f // 40% white blend for highlight
|
||||
);
|
||||
|
||||
Display_Shapes_Draw_Circle_Filled(Highlight_X, Highlight_Y, Highlight_Radius, Highlight_Color);
|
||||
}
|
||||
}
|
||||
|
||||
Coordinates Display_Shapes_Polar_To_XY(int16_t origin_x, int16_t origin_y, float angle, uint16_t radius)
|
||||
{
|
||||
Coordinates Return_Value = { .X = origin_x, .Y = origin_y };
|
||||
|
||||
Return_Value.X += cos(angle * DEG2RAD) * radius;
|
||||
Return_Value.Y += sin(angle * DEG2RAD) * radius;
|
||||
|
||||
return Return_Value;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
Internal Functions
|
||||
*******************************************************************/
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief Quarter-circle drawer, used to do circles and roundrects
|
||||
@param x0 Center-point x coordinate
|
||||
@param y0 Center-point y coordinate
|
||||
@param r Radius of circle
|
||||
@param cornername Mask bit #1 or bit #2 to indicate which quarters of the circle we're doing
|
||||
@param color 16-bit 5-6-5 Color to draw with
|
||||
*/
|
||||
/**************************************************************************/
|
||||
void Display_Shapes_Draw_Circle_Helper(int16_t x0, int16_t y0, uint16_t radius, uint16_t thickness, uint8_t cornername, Display_Color color)
|
||||
{
|
||||
int16_t f = 1 - radius;
|
||||
int16_t ddF_x = 1;
|
||||
int16_t ddF_y = -2 * radius;
|
||||
int16_t x = 0;
|
||||
int16_t y = radius;
|
||||
|
||||
while (x < y)
|
||||
{
|
||||
if (f >= 0)
|
||||
{
|
||||
y--;
|
||||
ddF_y += 2;
|
||||
f += ddF_y;
|
||||
}
|
||||
x++;
|
||||
ddF_x += 2;
|
||||
f += ddF_x;
|
||||
|
||||
if (cornername & CORNER_BOTTOM_RIGHT)
|
||||
{
|
||||
Display_Shapes_Draw_Circle_Filled(x0 + x, y0 + y, thickness >> 1, color);
|
||||
Display_Shapes_Draw_Circle_Filled(x0 + y, y0 + x, thickness >> 1, color);
|
||||
}
|
||||
|
||||
if (cornername & CORNER_TOP_RIGHT)
|
||||
{
|
||||
Display_Shapes_Draw_Circle_Filled(x0 + x, y0 - y, thickness >> 1, color);
|
||||
Display_Shapes_Draw_Circle_Filled(x0 + y, y0 - x, thickness >> 1, color);
|
||||
}
|
||||
|
||||
if (cornername & CORNER_BOTTOM_LEFT)
|
||||
{
|
||||
Display_Shapes_Draw_Circle_Filled(x0 - x, y0 + y, thickness >> 1, color);
|
||||
Display_Shapes_Draw_Circle_Filled(x0 - y, y0 + x, thickness >> 1, color);
|
||||
}
|
||||
|
||||
if (cornername & CORNER_TOP_LEFT)
|
||||
{
|
||||
Display_Shapes_Draw_Circle_Filled(x0 - x, y0 - y, thickness >> 1, color);
|
||||
Display_Shapes_Draw_Circle_Filled(x0 - y, y0 - x, thickness >> 1, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Display_Shapes_Draw_Circle_Helper_Improved(int16_t x0, int16_t y0, uint16_t radius, uint16_t thickness, uint8_t cornername, Display_Color color)
|
||||
{
|
||||
if (radius == 0) return;
|
||||
|
||||
// For thickness of 1, use single pixel drawing
|
||||
if (thickness == 1) {
|
||||
Display_Shapes_Draw_Circle_Helper_Single_Pixel(x0, y0, radius, cornername, color);
|
||||
return;
|
||||
}
|
||||
|
||||
// For thicker lines, draw multiple concentric quarter-circles
|
||||
// This ensures consistent thickness with the straight edges
|
||||
for (uint16_t t = 0; t < thickness; t++) {
|
||||
uint16_t current_radius = radius - t;
|
||||
if (current_radius == 0) break;
|
||||
|
||||
Display_Shapes_Draw_Circle_Helper_Single_Pixel(x0, y0, current_radius, cornername, color);
|
||||
}
|
||||
}
|
||||
|
||||
void Display_Shapes_Draw_Circle_Helper_Single_Pixel(int16_t x0, int16_t y0, uint16_t radius, uint8_t cornername, Display_Color color)
|
||||
{
|
||||
if (radius == 0) return;
|
||||
|
||||
// Use Bresenham's circle algorithm for single pixel drawing
|
||||
int16_t f = 1 - radius;
|
||||
int16_t ddF_x = 1;
|
||||
int16_t ddF_y = -2 * radius;
|
||||
int16_t x = 0;
|
||||
int16_t y = radius;
|
||||
|
||||
while (x < y) {
|
||||
if (f >= 0) {
|
||||
y--;
|
||||
ddF_y += 2;
|
||||
f += ddF_y;
|
||||
}
|
||||
x++;
|
||||
ddF_x += 2;
|
||||
f += ddF_x;
|
||||
|
||||
// Draw quarter-circle segments based on corner mask - single pixels only
|
||||
if (cornername & CORNER_TOP_LEFT) {
|
||||
Display_Shapes_Draw_Pixel_Safe(x0 - x, y0 - y, color);
|
||||
Display_Shapes_Draw_Pixel_Safe(x0 - y, y0 - x, color);
|
||||
}
|
||||
|
||||
if (cornername & CORNER_TOP_RIGHT) {
|
||||
Display_Shapes_Draw_Pixel_Safe(0 + x, y0 - y, color);
|
||||
Display_Shapes_Draw_Pixel_Safe(x0 + y, y0 - x, color);
|
||||
}
|
||||
|
||||
if (cornername & CORNER_BOTTOM_RIGHT) {
|
||||
Display_Shapes_Draw_Pixel_Safe(x0 + x, y0 + y, color);
|
||||
Display_Shapes_Draw_Pixel_Safe(x0 + y, y0 + x, color);
|
||||
}
|
||||
|
||||
if (cornername & CORNER_BOTTOM_LEFT) {
|
||||
Display_Shapes_Draw_Pixel_Safe(x0 - x, y0 + y, color);
|
||||
Display_Shapes_Draw_Pixel_Safe(x0 - y, y0 + x, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief Quarter-circle drawer with fill, used for circles and roundrects
|
||||
@param x0 Center-point x coordinate
|
||||
@param y0 Center-point y coordinate
|
||||
@param r Radius of circle
|
||||
@param corners Mask bits indicating which quarters we're doing
|
||||
@param delta Offset from center-point, used for round-rects
|
||||
@param color 16-bit 5-6-5 Color to fill with
|
||||
*/
|
||||
/**************************************************************************/
|
||||
void Display_Shapes_Draw_Circle_Helper_Filled(int16_t x0, int16_t y0, uint16_t radius, uint8_t corners, int16_t delta, Display_Color color)
|
||||
{
|
||||
int16_t f = 1 - radius;
|
||||
int16_t ddF_x = 1;
|
||||
int16_t ddF_y = -2 * radius;
|
||||
int16_t x = 0;
|
||||
int16_t y = radius;
|
||||
int16_t px = x;
|
||||
int16_t py = y;
|
||||
|
||||
delta++; // Avoid some +1's in the loop
|
||||
|
||||
while (x < y)
|
||||
{
|
||||
if (f >= 0)
|
||||
{
|
||||
y--;
|
||||
ddF_y += 2;
|
||||
f += ddF_y;
|
||||
}
|
||||
x++;
|
||||
ddF_x += 2;
|
||||
f += ddF_x;
|
||||
|
||||
// These checks avoid double-drawing certain lines, important
|
||||
// for the SSD1306 library which has an INVERT drawing mode.
|
||||
if (x < (y + 1))
|
||||
{
|
||||
if (corners & CORNER_TOP_LEFT) {
|
||||
Display_Shapes_Draw_VLine(x0 + x, y0 - y, 2 * y + delta, 1, color);
|
||||
// writeFastVLine(x0 + x, y0 - y, 2 * y + delta, color);
|
||||
}
|
||||
if (corners & CORNER_TOP_RIGHT) {
|
||||
Display_Shapes_Draw_VLine(x0 - x, y0 - y, 2 * y + delta, 1, color);
|
||||
// writeFastVLine(x0 - x, y0 - y, 2 * y + delta, color);
|
||||
}
|
||||
}
|
||||
|
||||
if (y != py)
|
||||
{
|
||||
if (corners & CORNER_TOP_LEFT) {
|
||||
Display_Shapes_Draw_VLine(x0 + py, y0 - px, 2 * px + delta, 1, color);
|
||||
// writeFastVLine(x0 + py, y0 - px, 2 * px + delta, color);
|
||||
}
|
||||
if (corners & CORNER_TOP_RIGHT) {
|
||||
Display_Shapes_Draw_VLine(x0 - py, y0 - px, 2 * px + delta, 1, color);
|
||||
// writeFastVLine(x0 - py, y0 - px, 2 * px + delta, color);
|
||||
}
|
||||
py = y;
|
||||
}
|
||||
px = x;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user