Оптимизированы функции полттера добавлены функции для рисования стрелок

Плоттер
- функция для отрисовки интерфейса плоттера
- функция для отрисовки самого графика (кривой)
- функции для отрисовки float, int, uint8_t, uint16_t
- добавлены сдвиги осей

Стрелки
- функция для рисования ортогональных стрелок (вправо, влево, вверх, вниз)
- функция для рисования стрелок в любом направлении
This commit is contained in:
2025-02-21 17:22:04 +03:00
parent 9a4e3c3dad
commit 142b9faf5a
5 changed files with 442 additions and 256 deletions

View File

@@ -10,9 +10,9 @@
*/
#include "gfx_lib.h"
#include "font_tahoma_8_prop.h"
#include "math.h"
#define PI 3.1415926535
/* переменные */
uint8_t chSpacing = 0; //межсимвольный интервал в px
@@ -405,10 +405,14 @@ void GFX_Draw_Arrow(uint8_t *Buffer_Frame, GFX_ArrowHandleTypeDef *hArrow)
uint8_t xPos = hArrow->xPos;
uint8_t yPos = hArrow->yPos;
uint8_t size = hArrow->size;
uint16_t angle = hArrow->angle;
uint16_t angle = hArrow->angle % 360;
uint8_t pxColor = hArrow->pxColor;
__GFX_Draw_Arrow(Buffer_Frame, xPos, yPos, size, angle, pxColor);
if((angle == 0) || (angle == 90) || (angle == 180) || (angle == 270))
__GFX_Draw_Arrow_Ortho(Buffer_Frame, xPos, yPos, size, angle, pxColor);
else
__GFX_Draw_Arrow(Buffer_Frame, xPos, yPos, size, angle, pxColor);
}
/* Функция рисования дуги (четверти окружности) */
@@ -435,8 +439,9 @@ void GFX_Draw_Arc(uint8_t *Buffer_Frame, GFX_ArcHandleTypeDef *hArc)
}
/* Функция вывода графика */
void GFX_FloatPlotter(uint8_t *Buffer_Frame, GFX_PlotterFloatHandleTypeDef *hPlot, float *numb2plot)
/* Функция для отрисовки графика uint8_t массива */
void GFX_Plotter_uint8_t(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot, uint8_t *data, uint32_t data_size, float data_step, uint8_t data_max)
{
if((Buffer_Frame == NULL) || (hPlot == NULL))
return;
@@ -444,112 +449,157 @@ void GFX_FloatPlotter(uint8_t *Buffer_Frame, GFX_PlotterFloatHandleTypeDef *hPlo
return;
if((hPlot->yPos + hPlot->plotHeight == NULL) || (hPlot->xPos + hPlot->plotWidth == NULL))
return;
if((numb2plot == NULL) && (hPlot->pDataPtr == NULL))
if(data == NULL)
return;
if(hPlot->__initialized == 0)
{
hPlot->__initialized = 1;
hPlot->plotInd = 0;
GFX_Clean_Buffer_Frame(Buffer_Frame);
if(hPlot->plotXAxis)
{
__GFX_Draw_Line(Buffer_Frame, hPlot->xPos, hPlot->yPos+hPlot->plotHeight/2, hPlot->xPos+hPlot->plotWidth-1, hPlot->yPos+hPlot->plotHeight/2, 1);
__GFX_Draw_Arrow(Buffer_Frame, hPlot->xPos+hPlot->plotWidth-1, hPlot->yPos+hPlot->plotHeight/2, 2, 0, 1);
}
if(hPlot->plotYAxis)
{
__GFX_Draw_Line(Buffer_Frame, hPlot->xPos, hPlot->yPos, hPlot->xPos, hPlot->yPos+hPlot->plotHeight-1, 1);
__GFX_Draw_Arrow(Buffer_Frame, hPlot->xPos, hPlot->yPos, 2, 90, 1);
}
if(hPlot->plotFrame)
{
__GFX_Draw_Rectangle(Buffer_Frame, hPlot->xPos, hPlot->yPos, hPlot->plotWidth-1, hPlot->plotHeight-1, 1);
}
}
/* Расчет позиции пикселя */
uint8_t pix_y_uint8t;
if(data_size == 0)
pix_y_uint8t = *data;
else
pix_y_uint8t = data[(int)hPlot->dataInd];
// масштабирование под размеры графика
hPlot->dataY = (pix_y_uint8t*hPlot->plotHeight)/data_max;
/* Подготовка к выводу графика */
if((hPlot->pixX < hPlot->xPos) || (hPlot->pixX >= hPlot->plotWidth))
hPlot->f.dataSigned = 0;
/* Вывод пикселя */
__GFX_Draw_Plotter_Value(Buffer_Frame, hPlot);
/* Смещение графика далее */
hPlot->dataX++;
hPlot->dataPrevY = hPlot->dataY;
// Если используется массив плота
if(data == NULL)
{
hPlot->pixX = hPlot->xPos;
GFX_Clean_Area(Buffer_Frame, hPlot->xPos, hPlot->yPos, hPlot->plotWidth, hPlot->plotHeight);
hPlot->dataInd += data_step;
if(hPlot->dataInd >= data_size)
hPlot->dataInd -= data_size;
if(hPlot->plotXAxis)
{
__GFX_Draw_Line(Buffer_Frame, hPlot->xPos, hPlot->yPos+hPlot->plotHeight/2, hPlot->xPos+hPlot->plotWidth-1, hPlot->yPos+hPlot->plotHeight/2, 1);
__GFX_Draw_Arrow(Buffer_Frame, hPlot->xPos+hPlot->plotWidth-1, hPlot->yPos+hPlot->plotHeight/2, 2, 0, 1);
}
if(hPlot->plotYAxis)
{
__GFX_Draw_Line(Buffer_Frame, hPlot->xPos, hPlot->yPos, hPlot->xPos, hPlot->yPos+hPlot->plotHeight-1, 1);
__GFX_Draw_Arrow(Buffer_Frame, hPlot->xPos, hPlot->yPos, 2, 90, 1);
}
if(hPlot->plotFrame)
{
__GFX_Draw_Rectangle(Buffer_Frame, hPlot->xPos, hPlot->yPos, hPlot->plotWidth-1, hPlot->plotHeight-1, 1);
}
if(hPlot->dataInd < 0)
hPlot->dataInd += data_size;
}
}
/* Функция для отрисовки графика uint16_t массива */
void GFX_Plotter_uint16_t(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot, uint16_t *data, uint32_t data_size, float data_step, uint16_t data_max)
{
if((Buffer_Frame == NULL) || (hPlot == NULL))
return;
if((hPlot->plotHeight == NULL) || (hPlot->plotWidth == NULL))
return;
if((hPlot->yPos + hPlot->plotHeight == NULL) || (hPlot->xPos + hPlot->plotWidth == NULL))
return;
if(data == NULL)
return;
/* Расчет позиции пикселя */
uint16_t pix_y_uint16t;
if(data_size == 0)
pix_y_uint16t = *data;
else
pix_y_uint16t = data[(int)hPlot->dataInd];
// масштабирование под размеры графика
hPlot->dataY = (pix_y_uint16t*hPlot->plotHeight)/data_max;
hPlot->f.dataSigned = 0;
/* Вывод пикселя */
__GFX_Draw_Plotter_Value(Buffer_Frame, hPlot);
/* Смещение графика далее */
hPlot->dataX++;
hPlot->dataPrevY = hPlot->dataY;
// Если используется массив плота
if(data == NULL)
{
hPlot->dataInd += data_step;
if(hPlot->dataInd >= data_size)
hPlot->dataInd -= data_size;
if(hPlot->dataInd < 0)
hPlot->dataInd += data_size;
}
}
/* Функция для отрисовки графика int массива */
void GFX_Plotter_int(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot, int *data, uint32_t data_size, float data_step, int data_max)
{
if((Buffer_Frame == NULL) || (hPlot == NULL))
return;
if((hPlot->plotHeight == NULL) || (hPlot->plotWidth == NULL))
return;
if((hPlot->yPos + hPlot->plotHeight == NULL) || (hPlot->xPos + hPlot->plotWidth == NULL))
return;
if(data == NULL)
return;
/* Расчет позиции пикселя */
uint8_t pix_y_int;
if(data_size == 0)
pix_y_int = *data;
else
pix_y_int = data[(int)hPlot->dataInd];
// масштабирование под размеры графика
hPlot->dataY = (pix_y_int*hPlot->plotHeight)/data_max;
hPlot->f.dataSigned = 1;
/* Вывод пикселя */
__GFX_Draw_Plotter_Value(Buffer_Frame, hPlot);
/* Смещение графика далее */
hPlot->dataX++;
hPlot->dataPrevY = hPlot->dataY;
// Если используется массив плота
if(data == NULL)
{
hPlot->dataInd += data_step;
if(hPlot->dataInd >= data_size)
hPlot->dataInd -= data_size;
if(hPlot->dataInd < 0)
hPlot->dataInd += data_size;
}
}
/* Функция для отрисовки графика float массива */
void GFX_Plotter_float(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot, float *data, uint32_t data_size, float data_step, float data_max)
{
if((Buffer_Frame == NULL) || (hPlot == NULL))
return;
if((hPlot->plotHeight == NULL) || (hPlot->plotWidth == NULL))
return;
if((hPlot->yPos + hPlot->plotHeight == NULL) || (hPlot->xPos + hPlot->plotWidth == NULL))
return;
if((data == NULL) || (data_size == 0))
return;
/* Расчет позиции пикселя */
float pix_y_float;
if(numb2plot != NULL)
pix_y_float = *numb2plot;
if(data_size == 0)
pix_y_float = *data;
else
pix_y_float = (hPlot->pDataPtr[(int)hPlot->plotInd]);
pix_y_float = data[(int)hPlot->dataInd];
hPlot->f.dataSigned = 1;
// масштабирование под размеры графика
hPlot->pixY = (pix_y_float/hPlot->dataMax)*(hPlot->plotHeight);
// если график должен быть знаковым, то уменьшаем машстаб еще в два раза
if(hPlot->signedData)
hPlot->pixY = (hPlot->pixY + hPlot->plotHeight)/2;
// инвертирование y потому что он считается сверху вниз
uint8_t plot_y_down = hPlot->yPos + hPlot->plotHeight;
hPlot->pixY = plot_y_down - hPlot->pixY;
// Сдвиг графика
hPlot->pixY += hPlot->plotShift;
hPlot->dataY = (pix_y_float/data_max)*(hPlot->plotHeight);
/* Вывод пикселя позиции пикселя */
// Передний фронт
if(hPlot->pixY - hPlot->prevPixY > 0)
{
/* Цикл для заполнения пикселей по вертикали, когда фронт очень резкий */
for(int y = hPlot->prevPixY+1; y <= hPlot->pixY; y++)
{
if((y<=hPlot->yPos+hPlot->plotHeight) && (y>=hPlot->yPos))
GFX_Draw_Pixel(Buffer_Frame, hPlot->pixX, y, 1);
}
}// Задний фронт
else if (hPlot->pixY - hPlot->prevPixY < 0)
{
/* Цикл для заполнения пикселей по вертикали, когда фронт очень резкий */
for(int y = hPlot->prevPixY-1; y >= hPlot->pixY; y--)
{
if((y<=hPlot->yPos+hPlot->plotHeight) && (y>=hPlot->yPos))
GFX_Draw_Pixel(Buffer_Frame, hPlot->pixX, y, 1);
}
}// Плато
else
{
if((hPlot->pixY<=hPlot->yPos+hPlot->plotHeight) && (hPlot->pixY>=hPlot->yPos))
GFX_Draw_Pixel(Buffer_Frame, hPlot->pixX, hPlot->pixY, 1);
}
/* Вывод пикселя */
__GFX_Draw_Plotter_Value(Buffer_Frame, hPlot);
/* Смещение графика далее */
hPlot->pixX++;
hPlot->prevPixY = hPlot->pixY;
hPlot->dataX++;
hPlot->dataPrevY = hPlot->dataY;
// Если используется массив плота
if(numb2plot == NULL)
if(data_size != 0)
{
hPlot->plotInd += hPlot->plotSpeed;
if(hPlot->plotInd >= hPlot->dataSize)
hPlot->plotInd -= hPlot->dataSize;
hPlot->dataInd += data_step;
if(hPlot->dataInd >= data_size)
hPlot->dataInd -= data_size;
if(hPlot->plotInd < 0)
hPlot->plotInd += hPlot->dataSize;
if(hPlot->dataInd < 0)
hPlot->dataInd += data_size;
}
}
@@ -695,8 +745,41 @@ void __GFX_Draw_Triangle(uint8_t *Buffer_Frame, uint8_t xPos1, uint8_t yPos1, ui
__GFX_Draw_Line(Buffer_Frame, xPos3, yPos3, xPos1, yPos1, pxColor);
}
/* функция рисования стрелки */
float roundUp(float num) {
if (num > 0) {
return ceilf(num); // Для положительных чисел используем ceil
} else {
return floorf(num); // Для отрицательных чисел используем floor
}
}
void __GFX_Draw_Arrow(uint8_t *Buffer_Frame, uint8_t xPos, uint8_t yPos, uint8_t size, uint16_t angle, uint8_t pxColor)
{
float rad = (angle-90) * PI / 180.0; // Переводим угол в радианы
// Кончик стрелки (самая острая точка)
int16_t x1 = xPos;
int16_t y1 = yPos;
// Два боковых конца (формируют треугольник стрелки)
int16_t x2 = xPos + roundUp(size * sinf(rad + PI / 4));
int16_t y2 = yPos + roundUp(size * cosf(rad + PI / 4));
int16_t x3 = xPos + roundUp(size * sinf(rad - PI / 4));
int16_t y3 = yPos + roundUp(size * cosf(rad - PI / 4));
// Ограничение от выхода за границы (если нужно)
if (x2 < 0) x2 = 0;
if (x3 < 0) x3 = 0;
if (y2 < 0) y2 = 0;
if (y3 < 0) y3 = 0;
// Рисуем стрелку как две линии
__GFX_Draw_Line(Buffer_Frame, x1, y1, x2, y2, pxColor);
__GFX_Draw_Line(Buffer_Frame, x1, y1, x3, y3, pxColor);
}
/* функция рисования стрелки под прямым углом */
void __GFX_Draw_Arrow_Ortho(uint8_t *Buffer_Frame, uint8_t xPos, uint8_t yPos, uint8_t size, uint16_t angle, uint8_t pxColor)
{
int16_t x1, y1, x2, y2, x3, y3;
@@ -738,6 +821,8 @@ void __GFX_Draw_Arrow(uint8_t *Buffer_Frame, uint8_t xPos, uint8_t yPos, uint8_t
y3 = yPos + size;
}
else
return;
if(x1 < 0)
x1 = 0;
@@ -768,3 +853,101 @@ void __GFX_Draw_Arc(uint8_t *Buffer_Frame, uint8_t xPos, uint8_t yPos, uint8_t r
}
}
/* Функция для отрисовки осей и рамки графика */
HAL_StatusTypeDef __GFX_StartPlot(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot)
{
if((hPlot->dataX < hPlot->xPos) || (hPlot->dataX >= hPlot->plotWidth))
{
hPlot->dataX = hPlot->xPos;
GFX_Clean_Area(Buffer_Frame, hPlot->xPos, hPlot->yPos, hPlot->plotWidth, hPlot->plotHeight);
if(hPlot->f.plotXAxis)
{
uint8_t xaxis_x_start = hPlot->xPos;
uint8_t xaxis_x_end = hPlot->xPos+hPlot->plotWidth-1;
uint8_t xaxis_y = hPlot->yPos-hPlot->plotXShift;
if(hPlot->f.dataSigned)
xaxis_y += hPlot->plotHeight/2;
else
xaxis_y += hPlot->plotHeight-1;
__GFX_Draw_Line(Buffer_Frame, xaxis_x_start, xaxis_y, xaxis_x_end, xaxis_y, 1);
__GFX_Draw_Arrow_Ortho(Buffer_Frame, xaxis_x_end, xaxis_y, 2, 0, 1);
}
if(hPlot->f.plotYAxis)
{
uint8_t yaxis_y_start = hPlot->yPos;
uint8_t yaxis_y_end = hPlot->yPos+hPlot->plotHeight-1;
uint8_t yaxis_x = hPlot->xPos+hPlot->plotYShift;
__GFX_Draw_Line(Buffer_Frame, yaxis_x, yaxis_y_start, yaxis_x, yaxis_y_end, 1);
__GFX_Draw_Arrow_Ortho(Buffer_Frame, yaxis_x, yaxis_y_start, 2, 90, 1);
}
if(hPlot->f.plotFrame)
{
__GFX_Draw_Rectangle(Buffer_Frame, hPlot->xPos, hPlot->yPos, hPlot->plotWidth-1, hPlot->plotHeight-1, 1);
}
return HAL_OK;
}
else
{
return HAL_ERROR;
}
}
/* Функция для отрисовки выбранной точки графика*/
void __GFX_Draw_Plotter_Value(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot)
{
/* Первичная инициализация плоттера */
if(hPlot->f.initialized == 0)
{
hPlot->f.initialized = 1;
GFX_Clean_Buffer_Frame(Buffer_Frame);
hPlot->dataX = -1;
if(__GFX_StartPlot(Buffer_Frame, hPlot) != HAL_OK)
return;
}
/* Очищение графика после полного заполнения */
if((hPlot->dataX < hPlot->xPos) || (hPlot->dataX >= hPlot->plotWidth))
{
__GFX_StartPlot(Buffer_Frame, hPlot);
}
/* Расчет позиции на графике */
// если график должен быть знаковым, то уменьшаем машстаб еще в два раза
if(hPlot->f.dataSigned)
hPlot->dataY = (hPlot->dataY + hPlot->plotHeight)/2;
// инвертирование y потому что он считается сверху вниз
uint8_t plot_y_down = hPlot->yPos + hPlot->plotHeight-1;
hPlot->dataY = plot_y_down - hPlot->dataY;
// Сдвиг графика
hPlot->dataY += hPlot->plotXShift;
/* Вывод пикселя позиции пикселя */
// Передний фронт
if(hPlot->dataY - hPlot->dataPrevY > 0)
{
/* Цикл для заполнения пикселей по вертикали, когда фронт очень резкий */
for(int y = hPlot->dataPrevY+1; y <= hPlot->dataY; y++)
{
if((y<hPlot->yPos+hPlot->plotHeight) && (y>=hPlot->yPos))
GFX_Draw_Pixel(Buffer_Frame, hPlot->dataX, y, 1);
}
}// Задний фронт
else if (hPlot->dataY - hPlot->dataPrevY < 0)
{
/* Цикл для заполнения пикселей по вертикали, когда фронт очень резкий */
for(int y = hPlot->dataPrevY-1; y >= hPlot->dataY; y--)
{
if((y<hPlot->yPos+hPlot->plotHeight) && (y>=hPlot->yPos))
GFX_Draw_Pixel(Buffer_Frame, hPlot->dataX, y, 1);
}
}// Плато
else
{
if((hPlot->dataY<hPlot->yPos+hPlot->plotHeight) && (hPlot->dataY>=hPlot->yPos))
GFX_Draw_Pixel(Buffer_Frame, hPlot->dataX, hPlot->dataY, 1);
}
}

View File

@@ -10,7 +10,6 @@
#include "main.h"
#include "string.h"
#include "stdio.h"
#include "oled.h"
#define GFX_BufferWidth 128 //ширина дисплея в пикселях
#define GFX_BufferHeight 32 //высота дисплея в пикселях
@@ -89,23 +88,23 @@ typedef struct
uint8_t plotHeight;
uint8_t plotWidth;
float *pDataPtr;
uint32_t dataSize;
float dataMax;
int16_t dataX;
int32_t dataY;
float dataInd;
int32_t dataPrevY;
float plotSpeed;
float plotInd;
uint8_t plotShift;
uint8_t pixX;
int32_t pixY;
uint8_t prevPixY;
int16_t plotXShift;
int16_t plotYShift;
unsigned signedData:1;
unsigned plotFrame:1;
unsigned plotXAxis:1;
unsigned plotYAxis:1;
unsigned __initialized:1;
}GFX_PlotterFloatHandleTypeDef;
struct
{
unsigned dataSigned:1;
unsigned plotFrame:1;
unsigned plotXAxis:1;
unsigned plotYAxis:1;
unsigned initialized:1;
}f;
}GFX_PlotterHandleTypeDef;
@@ -126,7 +125,10 @@ void GFX_Draw_Circle(uint8_t *Buffer_Frame, GFX_CircleHandleTypeDef *hCircle);
void GFX_Draw_Triangle(uint8_t *Buffer_Frame, GFX_TriangleHandleTypeDef *hTriangle);
void GFX_Draw_Arrow(uint8_t *Buffer_Frame, GFX_ArrowHandleTypeDef *hArrow);
void GFX_Draw_Arc(uint8_t *Buffer_Frame, GFX_ArcHandleTypeDef *hArc);
void GFX_FloatPlotter(uint8_t *Buffer_Frame, GFX_PlotterFloatHandleTypeDef *hPlot, float *numb2plot);
void GFX_Plotter_uint8_t(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot, uint8_t *data, uint32_t data_size, float data_step, uint8_t data_max);
void GFX_Plotter_uint16_t(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot, uint16_t *data, uint32_t data_size, float data_step, uint16_t data_max);
void GFX_Plotter_int(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot, int *data, uint32_t data_size, float data_step, int data_max);
void GFX_Plotter_float(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot, float *data, uint32_t data_size, float data_step, float data_max);
@@ -141,5 +143,8 @@ void __GFX_Draw_Triangle(uint8_t *Buffer_Frame,
uint8_t xPos3, uint8_t yPos3,
uint8_t pxColor);
void __GFX_Draw_Arrow(uint8_t *Buffer_Frame, uint8_t xPos, uint8_t yPos, uint8_t size, uint16_t angle, uint8_t pxColor);
void __GFX_Draw_Arrow_Ortho(uint8_t *Buffer_Frame, uint8_t xPos, uint8_t yPos, uint8_t size, uint16_t angle, uint8_t pxColor);
void __GFX_Draw_Arc(uint8_t *Buffer_Frame, uint8_t xPos, uint8_t yPos, uint8_t radius, uint16_t startAngle, uint16_t endAngle, uint8_t pxColor);
HAL_StatusTypeDef __GFX_StartPlot(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot);
void __GFX_Draw_Plotter_Value(uint8_t *Buffer_Frame, GFX_PlotterHandleTypeDef *hPlot);
#endif /* INC_PIXEL_GRAPHICS_H_ */