freertos_f407/User/system/sgl/widgets/polygon/sgl_polygon.c

380 lines
12 KiB
C

/* source/widgets/polygon/sgl_polygon.c
*
* MIT License
*
* Copyright(c) 2023-present All contributors of SGL
* Document reference link: https://sgl-docs.readthedocs.io
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "sgl_polygon.h"
// Polygon construction callback function
static void sgl_polygon_construct_cb(sgl_surf_t *surf, sgl_obj_t* obj, sgl_event_t *evt)
{
sgl_polygon_t *polygon = (sgl_polygon_t*)obj;
if (evt->type != SGL_EVENT_DRAW_MAIN) {
return;
}
if (polygon->vertex_count < 3 || polygon->vertices == NULL) {
return; // At least 3 vertices are required to form a polygon
}
// Draw fill
if (polygon->fill_color.full != 0) {
// Calculate bounding box of polygon (using actual coordinates)
int16_t min_x = polygon->vertices[0].x, max_x = polygon->vertices[0].x;
int16_t min_y = polygon->vertices[0].y, max_y = polygon->vertices[0].y;
for (uint16_t i = 1; i < polygon->vertex_count; i++) {
sgl_pos_t vertex = polygon->vertices[i];
min_x = sgl_min(min_x, vertex.x);
max_x = sgl_max(max_x, vertex.x);
min_y = sgl_min(min_y, vertex.y);
max_y = sgl_max(max_y, vertex.y);
}
// Adjust coordinates relative to parent object
sgl_area_t polygon_area = {
.x1 = min_x + obj->parent->coords.x1, // Adjust to parent coordinates
.x2 = max_x + obj->parent->coords.x1,
.y1 = min_y + obj->parent->coords.y1,
.y2 = max_y + obj->parent->coords.y1
};
sgl_area_t clip;
if (sgl_surf_clip(surf, &polygon_area, &clip) && sgl_area_selfclip(&clip, &obj->area)) {
// Calculate intersections of scan lines with polygon
int intersections[64]; // Assume max 64 intersections per scan line
for (int y = clip.y1; y <= clip.y2; y++) {
uint8_t count = 0;
// Calculate intersections of scan line with polygon
for (uint16_t i = 0; i < polygon->vertex_count; i++) {
// Adjust vertex coordinates relative to parent
sgl_pos_t p1 = {polygon->vertices[i].x + obj->parent->coords.x1,
polygon->vertices[i].y + obj->parent->coords.y1};
sgl_pos_t p2 = {polygon->vertices[(i + 1) % polygon->vertex_count].x + obj->parent->coords.x1,
polygon->vertices[(i + 1) % polygon->vertex_count].y + obj->parent->coords.y1};
// Calculate intersection of scan line with edge
if ((p1.y > y) != (p2.y > y)) {
intersections[count++] = p1.x + (y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y);
}
}
// Sort intersections (using optimized bubble sort)
for (uint8_t i = 0; i < count - 1; i++) {
bool swapped = false;
for (uint8_t j = 0; j < count - i - 1; j++) {
if (intersections[j] > intersections[j + 1]) {
int temp = intersections[j];
intersections[j] = intersections[j + 1];
intersections[j + 1] = temp;
swapped = true;
}
}
if (!swapped) break; // If no swaps occurred, sorting is complete
}
// Fill scan line segments
sgl_color_t *buf = sgl_surf_get_buf(surf, clip.x1 - surf->x1, y - surf->y1);
for (uint8_t i = 0; i < count; i += 2) {
int start = sgl_max(intersections[i], clip.x1);
int end = sgl_min(intersections[i + 1], clip.x2);
if (start <= end) {
if (polygon->alpha == SGL_ALPHA_MAX) {
for (int x = start; x <= end; x++) {
buf[x - clip.x1] = polygon->fill_color;
}
} else {
for (int x = start; x <= end; x++) {
int buf_index = x - clip.x1;
buf[buf_index] = sgl_color_mixer(polygon->fill_color, buf[buf_index], polygon->alpha);
}
}
}
}
}
}
}
// Draw border
if (polygon->border_width > 0 && polygon->border_color.full != 0) {
for (uint16_t i = 0; i < polygon->vertex_count; i++) {
// Adjust vertex coordinates relative to parent
sgl_pos_t start = {polygon->vertices[i].x + obj->parent->coords.x1,
polygon->vertices[i].y + obj->parent->coords.y1};
sgl_pos_t end = {polygon->vertices[(i + 1) % polygon->vertex_count].x + obj->parent->coords.x1,
polygon->vertices[(i + 1) % polygon->vertex_count].y + obj->parent->coords.y1};
draw_line_fill_slanted(surf, &obj->area, start.x, start.y, end.x, end.y, polygon->border_width, polygon->border_color, polygon->alpha);
}
}
// Draw text
if (polygon->text && polygon->font) {
// Calculate center point of polygon
int32_t center_x = 0, center_y = 0;
for (uint16_t i = 0; i < polygon->vertex_count; i++) {
center_x += polygon->vertices[i].x + obj->parent->coords.x1; // Adjust to parent coordinates
center_y += polygon->vertices[i].y + obj->parent->coords.y1;
}
center_x /= polygon->vertex_count;
center_y /= polygon->vertex_count;
// Calculate text dimensions
int16_t text_width = sgl_font_get_string_width(polygon->text, polygon->font);
int16_t text_height = sgl_font_get_height(polygon->font);
// Text drawing position (centered)
int16_t text_x = center_x - text_width / 2;
int16_t text_y = center_y - text_height / 2;
sgl_draw_string(surf, &obj->area, text_x, text_y, polygon->text, polygon->text_color, polygon->alpha, polygon->font);
}
}
// Create polygon object
sgl_obj_t* sgl_polygon_create(sgl_obj_t* parent)
{
sgl_polygon_t *polygon = (sgl_polygon_t*)sgl_malloc(sizeof(sgl_polygon_t));
if (polygon == NULL) {
return NULL;
}
memset(polygon, 0, sizeof(sgl_polygon_t));
sgl_obj_t *obj = &polygon->obj;
sgl_obj_init(obj, parent);
obj->construct_fn = sgl_polygon_construct_cb;
// Set default values
polygon->vertex_count = 0;
polygon->vertices = NULL;
polygon->fill_color = sgl_rgb(127, 127, 127);
polygon->border_color = sgl_rgb(0, 0, 0);
polygon->border_width = 1;
polygon->alpha = SGL_ALPHA_MAX;
polygon->pixmap = NULL;
polygon->text = NULL;
polygon->font = NULL;
polygon->text_color = sgl_rgb(0, 0, 0);
return obj;
}
// Set polygon vertices
void sgl_polygon_set_vertices(sgl_obj_t* obj, sgl_pos_t* vertices, uint16_t count)
{
sgl_polygon_t *polygon = (sgl_polygon_t *)obj;
if (polygon == NULL || vertices == NULL || count < 3) {
return;
}
// Free old vertex data (if any)
if (polygon->vertices != NULL) {
sgl_free(polygon->vertices);
}
// Allocate new vertex data
polygon->vertices = (sgl_pos_t*)sgl_malloc(sizeof(sgl_pos_t) * count);
if (polygon->vertices == NULL) {
polygon->vertex_count = 0;
return;
}
// Copy vertex data
memcpy(polygon->vertices, vertices, sizeof(sgl_pos_t) * count);
polygon->vertex_count = count;
// Mark object as needing redraw
sgl_obj_set_dirty(obj);
}
// Set polygon vertices by coordinate arrays
void sgl_polygon_set_vertex_coords(sgl_obj_t* obj, int16_t* x_coords, int16_t* y_coords, uint16_t count)
{
sgl_polygon_t *polygon = (sgl_polygon_t *)obj;
if (polygon == NULL || x_coords == NULL || y_coords == NULL || count < 3) {
return;
}
// Free old vertex data (if any)
if (polygon->vertices != NULL) {
sgl_free(polygon->vertices);
}
// Allocate new vertex data
polygon->vertices = (sgl_pos_t*)sgl_malloc(sizeof(sgl_pos_t) * count);
if (polygon->vertices == NULL) {
polygon->vertex_count = 0;
return;
}
// Build vertices from coordinate arrays
for (uint16_t i = 0; i < count; i++) {
polygon->vertices[i].x = x_coords[i];
polygon->vertices[i].y = y_coords[i];
}
polygon->vertex_count = count;
// Mark object as needing redraw
sgl_obj_set_dirty(obj);
}
// Set polygon vertices by 2D coordinate array
void sgl_polygon_set_vertex_array(sgl_obj_t* obj, int16_t (*coords)[2], uint16_t count)
{
sgl_polygon_t *polygon = (sgl_polygon_t *)obj;
if (polygon == NULL || coords == NULL || count < 3) {
return;
}
// Free old vertex data (if any)
if (polygon->vertices != NULL) {
sgl_free(polygon->vertices);
}
// Allocate new vertex data
polygon->vertices = (sgl_pos_t*)sgl_malloc(sizeof(sgl_pos_t) * count);
if (polygon->vertices == NULL) {
polygon->vertex_count = 0;
return;
}
// Build vertices from 2D coordinate array
for (uint16_t i = 0; i < count; i++) {
polygon->vertices[i].x = coords[i][0];
polygon->vertices[i].y = coords[i][1];
}
polygon->vertex_count = count;
// Mark object as needing redraw
sgl_obj_set_dirty(obj);
}
// Set fill color
void sgl_polygon_set_fill_color(sgl_obj_t* obj, sgl_color_t color)
{
sgl_polygon_t *polygon = (sgl_polygon_t *)obj;
if (polygon == NULL) {
return;
}
polygon->fill_color = color;
sgl_obj_set_dirty(obj);
}
// Set border color
void sgl_polygon_set_border_color(sgl_obj_t* obj, sgl_color_t color)
{
sgl_polygon_t *polygon = (sgl_polygon_t *)obj;
if (polygon == NULL) {
return;
}
polygon->border_color = color;
sgl_obj_set_dirty(obj);
}
// Set border width
void sgl_polygon_set_border_width(sgl_obj_t* obj, uint8_t width)
{
sgl_polygon_t *polygon = (sgl_polygon_t *)obj;
if (polygon == NULL) {
return;
}
polygon->border_width = width;
sgl_obj_set_dirty(obj);
}
// Set alpha value
void sgl_polygon_set_alpha(sgl_obj_t* obj, uint8_t alpha)
{
sgl_polygon_t *polygon = (sgl_polygon_t *)obj;
if (polygon == NULL) {
return;
}
polygon->alpha = alpha;
sgl_obj_set_dirty(obj);
}
// Set background image
void sgl_polygon_set_pixmap(sgl_obj_t* obj, const sgl_pixmap_t* pixmap)
{
sgl_polygon_t *polygon = (sgl_polygon_t *)obj;
if (polygon == NULL) {
return;
}
polygon->pixmap = pixmap;
sgl_obj_set_dirty(obj);
}
// Set text
void sgl_polygon_set_text(sgl_obj_t* obj, const char* text)
{
sgl_polygon_t *polygon = (sgl_polygon_t *)obj;
if (polygon == NULL) {
return;
}
polygon->text = text;
sgl_obj_set_dirty(obj);
}
// Set font
void sgl_polygon_set_font(sgl_obj_t* obj, const sgl_font_t* font)
{
sgl_polygon_t *polygon = (sgl_polygon_t *)obj;
if (polygon == NULL) {
return;
}
polygon->font = font;
sgl_obj_set_dirty(obj);
}
// Set text color
void sgl_polygon_set_text_color(sgl_obj_t* obj, sgl_color_t color)
{
sgl_polygon_t *polygon = (sgl_polygon_t *)obj;
if (polygon == NULL) {
return;
}
polygon->text_color = color;
sgl_obj_set_dirty(obj);
}