From 608564b00fb7b5944487ff359b7b0e542fe896be Mon Sep 17 00:00:00 2001 From: Bill Kendrick Date: Wed, 27 Mar 2024 23:41:41 -0700 Subject: [PATCH] WIP Filled Polygon Magic tool Currently able to add & adjust points, which forms a polygon. It is not filled yet. --- docs/CHANGES.txt | 5 +- magic/src/polyfill.c | 407 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 411 insertions(+), 1 deletion(-) create mode 100644 magic/src/polyfill.c diff --git a/docs/CHANGES.txt b/docs/CHANGES.txt index e3a918cf8..5469ed948 100644 --- a/docs/CHANGES.txt +++ b/docs/CHANGES.txt @@ -6,7 +6,7 @@ Copyright (c) 2002-2024 Various contributors (see below, and AUTHORS.txt) https://tuxpaint.org/ -2024.March.25 (0.9.33) +2024.March.27 (0.9.33) * New Magic Tools: ---------------- * WIP Specular Reflection: Draws a slightly blurred, wavy, and @@ -25,6 +25,9 @@ https://tuxpaint.org/ - Sound effects based on "Orion FIll.wav", Creative Commons 0 by KatHakaku + * WIP Filled Polygon - Draw points to form a polygon which is filled + Bill Kendrick + * Improvements to Eraser tool: ---------------------------- * Transparent erasers diff --git a/magic/src/polyfill.c b/magic/src/polyfill.c new file mode 100644 index 000000000..8e7e0fad3 --- /dev/null +++ b/magic/src/polyfill.c @@ -0,0 +1,407 @@ +/* polyfill.c + + A Magic tool for Tux Paint that creates a filled polygon. + by Bill Kendrick + + Last updated: March 27, 2024 +*/ + + +#include +#include +#include + +#include "tp_magic_api.h" +#include "SDL_image.h" +#include "SDL_mixer.h" + +enum { + TOOL_POLYFILL, + NUM_TOOLS +}; + +char * polyfill_names[NUM_TOOLS] = { + gettext_noop("Filled Polygon"), +}; + +char * polyfill_descr[NUM_TOOLS] = { + gettext_noop("Click multiple times in your picture to create a filled polygon. Click your starting position to complete the shape."), +}; + +char * polyfill_icon_filenames[NUM_TOOLS] = { + "dither.png", // FIXME +}; + +char * polyfill_snd_filenames[NUM_TOOLS] = { + "dither.ogg", // FIXME +}; + +#define SNAP_SIZE 16 + +#define MAX_PTS 17 + +SDL_Surface * polyfill_snapshot = NULL; +int polyfill_pt_x[MAX_PTS]; +int polyfill_pt_y[MAX_PTS]; +int polyfill_num_pts = 0; +int polyfill_editing = MAX_PTS; + + +Mix_Chunk *snd_effects[NUM_TOOLS]; + +Uint32 polyfill_color, polyfill_color_red, polyfill_color_green; + + +void polyfill_drag(magic_api * api, int which, SDL_Surface * canvas, + SDL_Surface * snapshot, int old_x, int old_y, int x, int y, + SDL_Rect * update_rect); + +void polyfill_line_callback(void *pointer, int which, SDL_Surface * canvas, + SDL_Surface * snapshot, int x, int y); + +void polyfill_draw_preview(magic_api * api, SDL_Surface * canvas, int show_handles); + + +Uint32 polyfill_api_version(void) +{ + return (TP_MAGIC_API_VERSION); +} + +int polyfill_init(magic_api * api, Uint8 disabled_features, Uint8 complexity_level) +{ + int i; + char filename[1024]; + + for (i = 0; i < NUM_TOOLS; i++) { + snprintf(filename, sizeof(filename), "%ssounds/magic/%s", api->data_directory, + polyfill_snd_filenames[i]); + snd_effects[i] = Mix_LoadWAV(filename); + } + + return (1); +} + + +int polyfill_get_tool_count(magic_api * api) +{ + return NUM_TOOLS; +} + +SDL_Surface *polyfill_get_icon(magic_api * api, int which) +{ + char filename[1024]; + + snprintf(filename, sizeof(filename), "%simages/magic/%s", + api->data_directory, polyfill_icon_filenames[which]); + + return (IMG_Load(filename)); +} + + +char *polyfill_get_name(magic_api * api, int which) +{ + return strdup(gettext(polyfill_names[which])); +} + + +int polyfill_get_group(magic_api * api, int which) +{ + return MAGIC_TYPE_ARTISTIC; +} + + +int polyfill_get_order(int which) +{ + return 610 + which; // FIXME +} + + +char *polyfill_get_description(magic_api * api, int which, int mode) +{ + return strdup(gettext(polyfill_descr[which])); +} + + +int polyfill_requires_colors(magic_api * api, int which) +{ + return 1; +} + + +int polyfill_modes(magic_api * api, int which) +{ + return MODE_PAINT; +} + + +Uint8 polyfill_accepted_sizes(magic_api * api, int which, int mode) +{ + return 1; +} + + +Uint8 polyfill_default_size(magic_api * api, int which, int mode) +{ + return 1; +} + + +void polyfill_shutdown(magic_api * api) +{ + int i; + + for (i = 0; i < NUM_TOOLS; i++) + { + if (snd_effects[i] != NULL) + { + Mix_FreeChunk(snd_effects[i]); + } + } + + if (polyfill_snapshot != NULL) { + SDL_FreeSurface(polyfill_snapshot); + polyfill_snapshot = NULL; + } +} + +void +polyfill_click(magic_api * api, int which, int mode, + SDL_Surface * canvas, SDL_Surface * snapshot, int x, int y, + SDL_Rect * update_rect) +{ + int i; + + printf("Click. num_pts = %d\n", polyfill_num_pts); + + /* See if we're clicking a pre-existing point, to edit it? */ + polyfill_editing = MAX_PTS; + for (i = 0; i < polyfill_num_pts && polyfill_editing == MAX_PTS; i++) { + if (abs(x - polyfill_pt_x[i]) <= SNAP_SIZE && + abs(y - polyfill_pt_y[i]) <= SNAP_SIZE) { + polyfill_editing = i; + } + } + + if (polyfill_editing != MAX_PTS) { + printf("Clicked %d to edit it\n", polyfill_editing); + polyfill_draw_preview(api, canvas, 1); + return; + } + + /* Trying to add a new point? */ + if (polyfill_num_pts < MAX_PTS) { + printf("Adding new point %d\n", polyfill_num_pts); + + polyfill_pt_x[polyfill_num_pts] = x; + polyfill_pt_y[polyfill_num_pts] = y; + polyfill_editing = polyfill_num_pts; + polyfill_num_pts++; + + /* Add the new point */ + polyfill_drag(api, which, canvas, snapshot, x, y, x, y, update_rect); + } else { + /* Out of points! */ + printf("Out of space for new points!\n"); + } +} + + +void +polyfill_drag(magic_api * api, int which, + SDL_Surface * canvas, SDL_Surface * snapshot, + int old_x, int old_y, int x, int y, + SDL_Rect * update_rect) +{ + if (polyfill_editing >= MAX_PTS) + return; + + polyfill_pt_x[polyfill_editing] = x; + polyfill_pt_y[polyfill_editing] = y; + + polyfill_draw_preview(api, canvas, 1); + + update_rect->x = 0; + update_rect->y = 0; + update_rect->w = canvas->w; + update_rect->h = canvas->h; +} + +void polyfill_draw_preview(magic_api * api, SDL_Surface * canvas, int show_handles) { + int i, xx, yy, max; + SDL_Rect dest; + + if (polyfill_snapshot == NULL) + return; + + SDL_BlitSurface(polyfill_snapshot, NULL, canvas, NULL); + for (i = 0; i < polyfill_num_pts - 1; i++) { + api->line((void *) api, 0 /* which */, canvas, NULL /* snapshot */, + polyfill_pt_x[i], polyfill_pt_y[i], + polyfill_pt_x[i + 1], polyfill_pt_y[i + 1], + 1, + polyfill_line_callback); + } + + if (show_handles) { + for (i = 1; i < polyfill_num_pts - 1; i++) { + for (yy = -4; yy <= 4; yy++) { + for (xx = -4; xx <= 4; xx++) { + api->xorpixel(canvas, polyfill_pt_x[i] + xx, polyfill_pt_y[i] + yy); + } + } + } + + dest.x = polyfill_pt_x[0] - SNAP_SIZE; + dest.y = polyfill_pt_y[0] - SNAP_SIZE; + dest.w = SNAP_SIZE * 2; + dest.h = SNAP_SIZE * 2; + SDL_FillRect(canvas, &dest, polyfill_color_green); + + if (polyfill_num_pts > 1) { + dest.x = polyfill_pt_x[polyfill_num_pts - 1] - SNAP_SIZE; + dest.y = polyfill_pt_y[polyfill_num_pts - 1] - SNAP_SIZE; + dest.w = SNAP_SIZE * 2; + dest.h = SNAP_SIZE * 2; + SDL_FillRect(canvas, &dest, polyfill_color_red); + } + } + + //SDL_UpdateRect(canvas, 0, 0, canvas->w, canvas->h); +} + +void +polyfill_release(magic_api * api, int which, + SDL_Surface * canvas, SDL_Surface * snapshot, int x, int y, + SDL_Rect * update_rect) +{ + int i; + + if (polyfill_snapshot == NULL) + return; + + if (polyfill_editing >= MAX_PTS) + return; + + printf("Release while editing %d\n", polyfill_editing); + +// if (polyfill_editing == polyfill_num_pts) { +// printf("Finalizing it...\n"); +// polyfill_num_pts++; +// } + + /* Moved (or placed) the final spot at the beginning? */ + if (polyfill_num_pts > 2 && + ( + (polyfill_editing == polyfill_num_pts - 1 && + abs(x - polyfill_pt_x[0]) <= SNAP_SIZE && + abs(y - polyfill_pt_y[0]) <= SNAP_SIZE) || + (polyfill_editing == 0 && + abs(x - polyfill_pt_x[polyfill_num_pts - 1]) <= SNAP_SIZE && + abs(y - polyfill_pt_y[polyfill_num_pts - 1]) <= SNAP_SIZE) + ) + ) { + printf("Ending the polygon!\n"); + + /* Snap the points */ + if (polyfill_editing == 0) { + polyfill_pt_x[0] = polyfill_pt_x[polyfill_num_pts - 1]; + polyfill_pt_y[0] = polyfill_pt_y[polyfill_num_pts - 1]; + } else { + polyfill_pt_x[polyfill_num_pts - 1] = polyfill_pt_x[0]; + polyfill_pt_y[polyfill_num_pts - 1] = polyfill_pt_y[0]; + } + + // FIXME + polyfill_draw_preview(api, canvas, 0); + + polyfill_num_pts = 0; + polyfill_editing = MAX_PTS; + + /* Update snapshot ahead of next polygon */ + SDL_BlitSurface(canvas, NULL, polyfill_snapshot, NULL); + } else { + /* Did not move (or place) the final spot at the beginning */ + + /* Did we stick to points together? We can merge them */ + if (polyfill_num_pts > 2) { + int to_merge = MAX_PTS; + + for (i = polyfill_editing - 1; i < polyfill_editing + 1; i++) { + if (i >= 0 && i < polyfill_num_pts - 1) { + if (abs(polyfill_pt_x[i] - polyfill_pt_x[i + 1]) <= SNAP_SIZE && + abs(polyfill_pt_y[i] - polyfill_pt_y[i + 1]) <= SNAP_SIZE) { + printf("%d & %d can be merged\n", i, i + 1); + to_merge = i; + } + } + } + + if (to_merge != MAX_PTS) { + printf("Merging %d with %d\n", to_merge, to_merge + 1); + for (i = to_merge; i < polyfill_num_pts - 1; i++) { + polyfill_pt_x[i] = polyfill_pt_x[i + 1]; + polyfill_pt_y[i] = polyfill_pt_y[i + 1]; + } + polyfill_num_pts--; + } + } + + polyfill_draw_preview(api, canvas, 1); + } + + update_rect->x = 0; + update_rect->y = 0; + update_rect->w = canvas->w; + update_rect->h = canvas->h; +} + +void polyfill_set_color(magic_api * api, int which, SDL_Surface * canvas, SDL_Surface * snapshot, Uint8 r, Uint8 g, Uint8 b, SDL_Rect * update_rect) +{ + polyfill_color = SDL_MapRGB(canvas->format, r, g, b); +} + +void polyfill_set_size(magic_api * api, int which, int mode, SDL_Surface * canvas, SDL_Surface * snapshot, Uint8 size, SDL_Rect * update_rect) +{ +} + + +void polyfill_line_callback(void *pointer, int which, SDL_Surface * canvas, + SDL_Surface * snapshot, int x, int y) +{ + SDL_Rect dest; + + magic_api *api = (magic_api *) pointer; + + dest.x = x - 1; + dest.y = y - 1; + dest.w = 3; + dest.h = 3; + + SDL_FillRect(canvas, &dest, polyfill_color); +} + + +void polyfill_switchin(magic_api * api, int which, int mode, + SDL_Surface * canvas) +{ + polyfill_color_red = SDL_MapRGB(canvas->format, 255, 0, 0); + polyfill_color_green = SDL_MapRGB(canvas->format, 0, 255, 0); + + if (polyfill_snapshot == NULL) { + polyfill_snapshot = SDL_CreateRGBSurface(SDL_SWSURFACE, canvas->w, canvas->h, + canvas->format->BitsPerPixel, canvas->format->Rmask, + canvas->format->Gmask, canvas->format->Bmask, canvas->format->Amask); + } + + if (polyfill_snapshot != NULL) { + SDL_BlitSurface(canvas, NULL, polyfill_snapshot, NULL); + } +} + +void polyfill_switchout(magic_api * api, int which, int mode, + SDL_Surface * canvas) +{ + /* FIXME: Do we want to do this? */ + // polyfill_num_pts = 0; + // polyfill_editing = MAX_PTS; +}