From 2e9304c65adac21f38d8d5759d2e4cc9db7e5e33 Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Sun, 6 Oct 2024 03:12:21 +0200 Subject: [PATCH 01/15] Prerequisites All stuff needed before implementing width Docs --- buildconfig/stubs/pygame/draw.pyi | 1 + docs/reST/ref/draw.rst | 15 +- src_c/doc/draw_doc.h | 2 +- src_c/draw.c | 235 +++++++++++++++++------------- 4 files changed, 151 insertions(+), 102 deletions(-) diff --git a/buildconfig/stubs/pygame/draw.pyi b/buildconfig/stubs/pygame/draw.pyi index 68ad3ace5b..5906e28e76 100644 --- a/buildconfig/stubs/pygame/draw.pyi +++ b/buildconfig/stubs/pygame/draw.pyi @@ -88,5 +88,6 @@ def aalines( surface: Surface, color: ColorLike, closed: bool, + width: int = 1, points: SequenceLike[Point], ) -> Rect: ... diff --git a/docs/reST/ref/draw.rst b/docs/reST/ref/draw.rst index dfe4b669d3..7558ce7918 100644 --- a/docs/reST/ref/draw.rst +++ b/docs/reST/ref/draw.rst @@ -506,9 +506,10 @@ object around the draw calls (see :func:`pygame.Surface.lock` and | :sl:`draw multiple contiguous straight antialiased line segments` | :sg:`aalines(surface, color, closed, points) -> Rect` + | :sg:`lines(surface, color, closed, points, width=1) -> Rect` - Draws a sequence of contiguous straight antialiased lines on the given - surface. + Draws a sequence of contiguous straight lines on the given surface. + For thick lines, the edges have miter joints and the ends are squared off. :param Surface surface: surface to draw on :param color: color to draw with, the alpha value is optional if using a @@ -525,6 +526,15 @@ object around the draw calls (see :func:`pygame.Surface.lock` and additionally if the ``closed`` parameter is ``True`` another line segment will be drawn from ``(x3, y3)`` to ``(x1, y1)`` :type points: tuple(point) or list(point) + :param int width: (optional) used for line thickness + + | if width >= 1, used for line thickness (default is 1) + | if width < 1, nothing will be drawn + | + + .. note:: + When using ``width`` values ``> 1`` refer to the ``width`` notes + of :func:`line` for details on how thick lines grow. :returns: a rect bounding the changed pixels, if nothing is drawn the bounding rect's position will be the position of the first point in the @@ -539,6 +549,7 @@ object around the draw calls (see :func:`pygame.Surface.lock` and .. versionchangedold:: 2.0.0 Added support for keyword arguments. .. versionchanged:: 2.4.0 Removed deprecated ``blend`` argument .. versionchanged:: 2.5.0 ``blend`` argument readded for backcompat, but will always raise a deprecation exception when used + .. versionchanged:: 2.5.2 Added line width .. ## pygame.draw.aalines ## diff --git a/src_c/doc/draw_doc.h b/src_c/doc/draw_doc.h index 384936e90c..6fa0b74a04 100644 --- a/src_c/doc/draw_doc.h +++ b/src_c/doc/draw_doc.h @@ -9,4 +9,4 @@ #define DOC_DRAW_LINE "line(surface, color, start_pos, end_pos) -> Rect\nline(surface, color, start_pos, end_pos, width=1) -> Rect\ndraw a straight line" #define DOC_DRAW_LINES "lines(surface, color, closed, points) -> Rect\nlines(surface, color, closed, points, width=1) -> Rect\ndraw multiple contiguous straight line segments" #define DOC_DRAW_AALINE "aaline(surface, color, start_pos, end_pos) -> Rect\naaline(surface, color, start_pos, end_pos, width=1) -> Rect\ndraw a straight antialiased line" -#define DOC_DRAW_AALINES "aalines(surface, color, closed, points) -> Rect\ndraw multiple contiguous straight antialiased line segments" +#define DOC_DRAW_AALINES "aalines(surface, color, closed, points) -> Rect\nlines(surface, color, closed, points, width=1) -> Rect\ndraw multiple contiguous straight antialiased line segments" diff --git a/src_c/draw.c b/src_c/draw.c index 84138bc077..40371fe193 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -44,7 +44,7 @@ draw_line_width(SDL_Surface *surf, Uint32 color, int x1, int y1, int x2, static void draw_line(SDL_Surface *surf, int x1, int y1, int x2, int y2, Uint32 color, int *drawn_area); -void +static void line_width_corners(float from_x, float from_y, float to_x, float to_y, int width, float *x1, float *y1, float *x2, float *y2, float *x3, float *y3, float *x4, float *y4); @@ -54,6 +54,9 @@ draw_aaline(SDL_Surface *surf, Uint32 color, float startx, float starty, int disable_first_endpoint, int disable_second_endpoint, int extra_pixel_for_aalines); static void +draw_aalines(SDL_Surface *surf, Uint32 color, float *xlist, float *ylist, + int closed, Py_ssize_t length, int *drawn_area); +static void draw_arc(SDL_Surface *surf, int x_center, int y_center, int radius1, int radius2, int width, double angle_start, double angle_stop, Uint32 color, int *drawn_area); @@ -277,26 +280,21 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) PyObject *points, *item = NULL; SDL_Surface *surf = NULL; Uint32 color; - float pts[4]; - float pts_prev[4]; float *xlist, *ylist; float x, y; int l, t; - int extra_px; - int disable_endpoints; - int steep_prev; - int steep_curr; + int width = 1; /* Default width. */ PyObject *blend = NULL; int drawn_area[4] = {INT_MAX, INT_MAX, INT_MIN, INT_MIN}; /* Used to store bounding box values */ int result, closed; Py_ssize_t loop, length; - static char *keywords[] = {"surface", "color", "closed", - "points", "blend", NULL}; + static char *keywords[] = {"surface", "color", "closed", "points", + "width", "blend", NULL}; - if (!PyArg_ParseTupleAndKeywords(arg, kwargs, "O!OpO|O", keywords, + if (!PyArg_ParseTupleAndKeywords(arg, kwargs, "O!OpO|iO", keywords, &pgSurface_Type, &surfobj, &colorobj, - &closed, &points, &blend)) { + &closed, &points, &width, &blend)) { return NULL; /* Exception already set. */ } @@ -366,99 +364,19 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) ylist[loop] = y; } - if (!pgSurface_Lock(surfobj)) { + if (width < 1) { PyMem_Free(xlist); PyMem_Free(ylist); - return RAISE(PyExc_RuntimeError, "error locking surface"); - } - - /* first line - if open, add endpoint pixels.*/ - pts[0] = xlist[0]; - pts[1] = ylist[0]; - pts[2] = xlist[1]; - pts[3] = ylist[1]; - - /* Previous points. - * Used to compare previous and current line.*/ - pts_prev[0] = pts[0]; - pts_prev[1] = pts[1]; - pts_prev[2] = pts[2]; - pts_prev[3] = pts[3]; - steep_prev = - fabs(pts_prev[2] - pts_prev[0]) < fabs(pts_prev[3] - pts_prev[1]); - steep_curr = fabs(xlist[2] - pts[2]) < fabs(ylist[2] - pts[1]); - extra_px = steep_prev > steep_curr; - disable_endpoints = - !((roundf(pts[2]) == pts[2]) && (roundf(pts[3]) == pts[3])); - if (closed) { - draw_aaline(surf, color, pts[0], pts[1], pts[2], pts[3], drawn_area, - disable_endpoints, disable_endpoints, extra_px); - } - else { - draw_aaline(surf, color, pts[0], pts[1], pts[2], pts[3], drawn_area, 0, - disable_endpoints, extra_px); + return pgRect_New4(x, y, 0, 0); } - for (loop = 2; loop < length - 1; ++loop) { - pts[0] = xlist[loop - 1]; - pts[1] = ylist[loop - 1]; - pts[2] = xlist[loop]; - pts[3] = ylist[loop]; - - /* Comparing previous and current line. - * If one is steep and other is not, extra pixel must be drawn.*/ - steep_prev = - fabs(pts_prev[2] - pts_prev[0]) < fabs(pts_prev[3] - pts_prev[1]); - steep_curr = fabs(pts[2] - pts[0]) < fabs(pts[3] - pts[1]); - extra_px = steep_prev != steep_curr; - disable_endpoints = - !((roundf(pts[2]) == pts[2]) && (roundf(pts[3]) == pts[3])); - pts_prev[0] = pts[0]; - pts_prev[1] = pts[1]; - pts_prev[2] = pts[2]; - pts_prev[3] = pts[3]; - draw_aaline(surf, color, pts[0], pts[1], pts[2], pts[3], drawn_area, - disable_endpoints, disable_endpoints, extra_px); + if (!pgSurface_Lock(surfobj)) { + PyMem_Free(xlist); + PyMem_Free(ylist); + return RAISE(PyExc_RuntimeError, "error locking surface"); } - /* Last line - if open, add endpoint pixels. */ - pts[0] = xlist[length - 2]; - pts[1] = ylist[length - 2]; - pts[2] = xlist[length - 1]; - pts[3] = ylist[length - 1]; - steep_prev = - fabs(pts_prev[2] - pts_prev[0]) < fabs(pts_prev[3] - pts_prev[1]); - steep_curr = fabs(pts[2] - pts[0]) < fabs(pts[3] - pts[1]); - extra_px = steep_prev != steep_curr; - disable_endpoints = - !((roundf(pts[2]) == pts[2]) && (roundf(pts[3]) == pts[3])); - pts_prev[0] = pts[0]; - pts_prev[1] = pts[1]; - pts_prev[2] = pts[2]; - pts_prev[3] = pts[3]; - if (closed) { - draw_aaline(surf, color, pts[0], pts[1], pts[2], pts[3], drawn_area, - disable_endpoints, disable_endpoints, extra_px); - } - else { - draw_aaline(surf, color, pts[0], pts[1], pts[2], pts[3], drawn_area, - disable_endpoints, 0, extra_px); - } - - if (closed && length > 2) { - pts[0] = xlist[length - 1]; - pts[1] = ylist[length - 1]; - pts[2] = xlist[0]; - pts[3] = ylist[0]; - steep_prev = - fabs(pts_prev[2] - pts_prev[0]) < fabs(pts_prev[3] - pts_prev[1]); - steep_curr = fabs(pts[2] - pts[0]) < fabs(pts[3] - pts[1]); - extra_px = steep_prev != steep_curr; - disable_endpoints = - !((roundf(pts[2]) == pts[2]) && (roundf(pts[3]) == pts[3])); - draw_aaline(surf, color, pts[0], pts[1], pts[2], pts[3], drawn_area, - disable_endpoints, disable_endpoints, extra_px); - } + draw_aalines(surf, color, xlist, ylist, closed, length, drawn_area); PyMem_Free(xlist); PyMem_Free(ylist); @@ -1589,6 +1507,107 @@ draw_aaline(SDL_Surface *surf, Uint32 color, float from_x, float from_y, } } +static void +draw_aalines(SDL_Surface *surf, Uint32 color, float *xlist, float *ylist, + int closed, Py_ssize_t length, int *drawn_area) +{ + float pts[4]; + float pts_prev[4]; + int extra_px; + int disable_endpoints; + int steep_prev; + int steep_curr; + Py_ssize_t loop; + + /* first line - if open, add endpoint pixels.*/ + pts[0] = xlist[0]; + pts[1] = ylist[0]; + pts[2] = xlist[1]; + pts[3] = ylist[1]; + + /* Previous points. + * Used to compare previous and current line.*/ + pts_prev[0] = pts[0]; + pts_prev[1] = pts[1]; + pts_prev[2] = pts[2]; + pts_prev[3] = pts[3]; + steep_prev = + fabs(pts_prev[2] - pts_prev[0]) < fabs(pts_prev[3] - pts_prev[1]); + steep_curr = fabs(xlist[2] - pts[2]) < fabs(ylist[2] - pts[1]); + extra_px = steep_prev > steep_curr; + disable_endpoints = + !((roundf(pts[2]) == pts[2]) && (roundf(pts[3]) == pts[3])); + if (closed) { + draw_aaline(surf, color, pts[0], pts[1], pts[2], pts[3], drawn_area, + disable_endpoints, disable_endpoints, extra_px); + } + else { + draw_aaline(surf, color, pts[0], pts[1], pts[2], pts[3], drawn_area, 0, + disable_endpoints, extra_px); + } + + for (loop = 2; loop < length - 1; ++loop) { + pts[0] = xlist[loop - 1]; + pts[1] = ylist[loop - 1]; + pts[2] = xlist[loop]; + pts[3] = ylist[loop]; + + /* Comparing previous and current line. + * If one is steep and other is not, extra pixel must be drawn.*/ + steep_prev = + fabs(pts_prev[2] - pts_prev[0]) < fabs(pts_prev[3] - pts_prev[1]); + steep_curr = fabs(pts[2] - pts[0]) < fabs(pts[3] - pts[1]); + extra_px = steep_prev != steep_curr; + disable_endpoints = + !((roundf(pts[2]) == pts[2]) && (roundf(pts[3]) == pts[3])); + pts_prev[0] = pts[0]; + pts_prev[1] = pts[1]; + pts_prev[2] = pts[2]; + pts_prev[3] = pts[3]; + draw_aaline(surf, color, pts[0], pts[1], pts[2], pts[3], drawn_area, + disable_endpoints, disable_endpoints, extra_px); + } + + /* Last line - if open, add endpoint pixels. */ + pts[0] = xlist[length - 2]; + pts[1] = ylist[length - 2]; + pts[2] = xlist[length - 1]; + pts[3] = ylist[length - 1]; + steep_prev = + fabs(pts_prev[2] - pts_prev[0]) < fabs(pts_prev[3] - pts_prev[1]); + steep_curr = fabs(pts[2] - pts[0]) < fabs(pts[3] - pts[1]); + extra_px = steep_prev != steep_curr; + disable_endpoints = + !((roundf(pts[2]) == pts[2]) && (roundf(pts[3]) == pts[3])); + pts_prev[0] = pts[0]; + pts_prev[1] = pts[1]; + pts_prev[2] = pts[2]; + pts_prev[3] = pts[3]; + if (closed) { + draw_aaline(surf, color, pts[0], pts[1], pts[2], pts[3], drawn_area, + disable_endpoints, disable_endpoints, extra_px); + } + else { + draw_aaline(surf, color, pts[0], pts[1], pts[2], pts[3], drawn_area, + disable_endpoints, 0, extra_px); + } + + if (closed && length > 2) { + pts[0] = xlist[length - 1]; + pts[1] = ylist[length - 1]; + pts[2] = xlist[0]; + pts[3] = ylist[0]; + steep_prev = + fabs(pts_prev[2] - pts_prev[0]) < fabs(pts_prev[3] - pts_prev[1]); + steep_curr = fabs(pts[2] - pts[0]) < fabs(pts[3] - pts[1]); + extra_px = steep_prev != steep_curr; + disable_endpoints = + !((roundf(pts[2]) == pts[2]) && (roundf(pts[3]) == pts[3])); + draw_aaline(surf, color, pts[0], pts[1], pts[2], pts[3], drawn_area, + disable_endpoints, disable_endpoints, extra_px); + } +} + static void drawhorzline(SDL_Surface *surf, Uint32 color, int x1, int y1, int x2) { @@ -1868,7 +1887,7 @@ draw_line_width(SDL_Surface *surf, Uint32 color, int x1, int y1, int x2, // Calculates 4 points, representing corners of draw_line_width() // first two points assemble left line and second two - right line -void +static void line_width_corners(float from_x, float from_y, float to_x, float to_y, int width, float *x1, float *y1, float *x2, float *y2, float *x3, float *y3, float *x4, float *y4) @@ -1897,6 +1916,24 @@ line_width_corners(float from_x, float from_y, float to_x, float to_y, *x4 = to_x; *y4 = to_y + extra_width - aa_width; } + + // sort and right points, so (x1, y1), (x2, y2) are always left + if ((to_x - from_x) * (*y3 - from_y) - (to_y - from_y) * (*x3 - from_x) > + 0) { + float temp; + temp = *x3; + *x3 = *x1; + *y1 = temp; + temp = *y3; + *y3 = *y1; + *y1 = temp; + temp = *x4; + *x4 = *x2; + *x2 = temp; + temp = *y4; + *y4 = *y2; + *y2 = temp; + } } /* Algorithm modified from From a988a776a44e95ca57fb2fe5e7d758f8283ffc1d Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Sun, 6 Oct 2024 03:18:22 +0200 Subject: [PATCH 02/15] Helper functions --- src_c/draw.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src_c/draw.c b/src_c/draw.c index 40371fe193..2363674e2d 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -44,6 +44,12 @@ draw_line_width(SDL_Surface *surf, Uint32 color, int x1, int y1, int x2, static void draw_line(SDL_Surface *surf, int x1, int y1, int x2, int y2, Uint32 color, int *drawn_area); +static int +is_intersect(float x1, float y1, float x2, float y2, float x3, float y3, + float x4, float y4); +static void +intersect_point(float x1, float y1, float x2, float y2, float x3, float y3, + float x4, float y4, float *x, float *y); static void line_width_corners(float from_x, float from_y, float to_x, float to_y, int width, float *x1, float *y1, float *x2, float *y2, @@ -376,7 +382,12 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) return RAISE(PyExc_RuntimeError, "error locking surface"); } - draw_aalines(surf, color, xlist, ylist, closed, length, drawn_area); + if (width == 1) { + draw_aalines(surf, color, xlist, ylist, closed, length, drawn_area); + } + else { + // TODO + } PyMem_Free(xlist); PyMem_Free(ylist); @@ -1140,6 +1151,47 @@ compare_int(const void *a, const void *b) return (*(const int *)a) - (*(const int *)b); } +static int +ccw(float x1, float y1, float x2, float y2, float x3, float y3) +{ + return (y3 - y1) * (x2 - x1) > (y2 - y1) * (x3 - x1); +} + +/* Returns True if 2 line segments are intersecting. + * (x1, y1) and (x2, y2) are points of first line, + * (x3, y3) and (x4, y4) are points of second line. + */ +static int +is_intersect(float x1, float y1, float x2, float y2, float x3, float y3, + float x4, float y4) +{ + return ccw(x1, y1, x3, y3, x4, y4) != ccw(x2, y2, x3, y3, x4, y4) && + ccw(x1, y1, x2, y2, x3, y3) != ccw(x1, y1, x2, y2, x4, y4); +} + +/* Finds intersection coordinates of 2 lines. + * (x1, y1) and (x2, y2) are points of first line, + * (x3, y3) and (x4, y4) are points of second line. + */ +static void +intersect_point(float x1, float y1, float x2, float y2, float x3, float y3, + float x4, float y4, float *x, float *y) +{ + float d = (y2 - y1) * (x3 - x4) - (y4 - y3) * (x1 - x2); // determinant + + if (d == 0) { + *x = x1; + *y = y1; + return; + } + + // Cramer's rule + *x = ((x1 * y2 - x2 * y1) * (x3 - x4) - (x3 * y4 - x4 * y3) * (x1 - x2)) / + d; + *y = ((y2 - y1) * (x3 * y4 - x4 * y3) - (y4 - y3) * (x1 * y2 - x2 * y1)) / + d; +} + static Uint32 get_antialiased_color(SDL_Surface *surf, int x, int y, Uint32 original_color, float brightness) From 6233f80f4a11a215ceb4f2c4b710f70a737df690 Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Mon, 7 Oct 2024 00:59:49 +0200 Subject: [PATCH 03/15] Main function body And initial stuff --- src_c/draw.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/src_c/draw.c b/src_c/draw.c index 2363674e2d..e98f52c8d9 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -386,7 +386,97 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) draw_aalines(surf, color, xlist, ylist, closed, length, drawn_area); } else { - // TODO + /* Variables naming: + * prev_- - Variable is for previous point + * orig_- - Original value, not changed by intersecting + * -i- - This is "inner" point, from lines with slightly lower width + * -l/r/- - This coordinate is on left or right side of draw.lines + * -f/t- - from/to, this is either startig or ending point + * -x/y - X and Y coordinates of one same point + */ + float *left_xlist, *left_ylist, *right_xlist, *right_ylist; + float point_x, point_y, prev_x, prev_y, last_x, last_y; + float prev_lfx, prev_lfy, prev_ltx, prev_lty, prev_rfx, prev_rfy, + prev_rtx, prev_rty; + float prev_ilfx, prev_ilfy, prev_iltx, prev_ilty, prev_irfx, prev_irfy, + prev_irtx, prev_irty; + float lfx, lfy, ltx, lty, rfx, rfy, rtx, rty; + float ilfx, ilfy, iltx, ilty, irfx, irfy, irtx, irty; + float orig_ilfx, orig_ilfy, orig_irfx, orig_irfy; + left_xlist = PyMem_New(float, length); + left_ylist = PyMem_New(float, length); + right_xlist = PyMem_New(float, length); + right_ylist = PyMem_New(float, length); + + // Data for initial points + prev_x = xlist[0]; + prev_y = ylist[0]; + last_x = xlist[length - 1]; + last_y = ylist[length - 1]; + line_width_corners(last_x, last_y, prev_x, prev_x, width, &prev_lfx, + &prev_lfy, &prev_ltx, &prev_lty, &prev_rfx, + &prev_rfy, &prev_rtx, &prev_rty); + line_width_corners(last_x, last_y, prev_x, prev_x, width - 1.5, + &prev_ilfx, &prev_ilfy, &prev_iltx, &prev_ilty, + &prev_irfx, &prev_irfy, &prev_irtx, &prev_irty); + if (closed) { + // move first point to end of xlist and ylist // PROBLEM + } + + // Loop over all points, skipping first one + for (loop = 1; loop < length; ++loop) { + point_x = xlist[loop]; + point_y = ylist[loop]; + prev_x = xlist[loop - 1]; + prev_y = ylist[loop - 1]; + line_width_corners(prev_x, prev_x, point_x, point_y, width, &lfx, + &lfy, <x, <y, &rfx, &rfy, &rtx, &rty); + line_width_corners(prev_x, prev_x, point_x, point_y, width, &ilfx, + &ilfy, &iltx, &ilty, &irfx, &irfy, &irtx, + &irty); + orig_ilfx = ilfx; + orig_ilfy = ilfy; + orig_irfx = irfx; + orig_irfy = irfy; + + // Find and change corners + // TODO + + // For aalines + left_xlist[loop - 1] = lfx; + left_ylist[loop - 1] = lfy; + right_xlist[loop - 1] = rfx; + right_ylist[loop - 1] = rfy; + + // Fill gaps in corners + if (closed || (!closed && (loop != 1))) { + // This line + // TODO + // if (TODO) { + // // Previous line + // TODO + // } + } + + // Data for the next iteration + // prev_x = point_x; + // prev_y = point_y; + // TODO + } + + // Last point for aalines + // left_points.append(draw_to_1) + // right_points.append(draw_to_2) + + // Drawing + // lines + // aalines + // aalines + + PyMem_Free(left_xlist); + PyMem_Free(left_ylist); + PyMem_Free(right_xlist); + PyMem_Free(right_ylist); } PyMem_Free(xlist); From d8ce0d1b2ff54d917793a32e7ad48329d5275215 Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Mon, 7 Oct 2024 01:33:39 +0200 Subject: [PATCH 04/15] Find and change corners --- src_c/draw.c | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src_c/draw.c b/src_c/draw.c index e98f52c8d9..908e43d405 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -440,7 +440,46 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) orig_irfy = irfy; // Find and change corners - // TODO + if (loop != 1 || (loop == 1 && closed)) { + // Left + if (lfx != prev_ltx && lfy != prev_lty) { + intersect_point(rfx, rfy, rtx, rty, prev_rfx, prev_rfy, + prev_rtx, prev_rty, &rfx, &rfy); + intersect_point(irfx, irfy, irtx, irty, prev_irfx, + prev_irfy, prev_irtx, prev_irty, &irfx, + &irfy); + } + else { + if (is_intersect(lfx, lfy, ltx, lty, prev_rfx, prev_rfy, + prev_rtx, prev_rty)) { + // special case where both points are mismatched + intersect_point(rfx, rfy, rtx, rty, prev_lfx, prev_lfy, + prev_ltx, prev_lty, &rfx, &rfy); + intersect_point(irfx, irfy, irtx, irty, prev_ilfx, + prev_ilfy, prev_iltx, prev_ilty, &irfx, + &irfy); + } + } + // Right + if (rfx != prev_rtx && rfy != prev_rty) { + intersect_point(lfx, lfy, ltx, lty, prev_rfx, prev_rfy, + prev_ltx, prev_lty, &lfx, &lfy); + intersect_point(ilfx, ilfy, iltx, ilty, prev_irfx, + prev_irfy, prev_iltx, prev_ilty, &ilfx, + &ilfy); + } + else { + if (is_intersect(rfx, rfy, rtx, rty, prev_lfx, prev_lfy, + prev_ltx, prev_lty)) { + // special case where both points are mismatched + intersect_point(lfx, lfy, ltx, lty, prev_rfx, prev_rfy, + prev_rtx, prev_rty, &lfx, &lfy); + intersect_point(ilfx, ilfy, iltx, ilty, prev_irfx, + prev_irfy, prev_irtx, prev_irty, &ilfx, + &ilfy); + } + } + } // For aalines left_xlist[loop - 1] = lfx; @@ -449,7 +488,7 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) right_ylist[loop - 1] = rfy; // Fill gaps in corners - if (closed || (!closed && (loop != 1))) { + if (closed || (!closed && loop != 1)) { // This line // TODO // if (TODO) { From b68bc66a118f12507f147782a5eab5fad0371691 Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Mon, 7 Oct 2024 02:40:43 +0200 Subject: [PATCH 05/15] Fill gaps in corners --- src_c/draw.c | 86 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 16 deletions(-) diff --git a/src_c/draw.c b/src_c/draw.c index 908e43d405..e4bff05b85 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -394,7 +394,9 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) * -f/t- - from/to, this is either startig or ending point * -x/y - X and Y coordinates of one same point */ - float *left_xlist, *left_ylist, *right_xlist, *right_ylist; + float *left_xlist, *left_ylist, *right_xlist, *right_ylist, + *corner_xlist, *corner_ylist; + int *int_corner_xlist, *int_corner_ylist; float point_x, point_y, prev_x, prev_y, last_x, last_y; float prev_lfx, prev_lfy, prev_ltx, prev_lty, prev_rfx, prev_rfy, prev_rtx, prev_rty; @@ -407,6 +409,11 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) left_ylist = PyMem_New(float, length); right_xlist = PyMem_New(float, length); right_ylist = PyMem_New(float, length); + corner_xlist = PyMem_New(float, 4); + corner_ylist = PyMem_New(float, 4); + int_corner_xlist = PyMem_New(int, 4); + int_corner_ylist = PyMem_New(int, 4); + Py_ssize_t corner_loop; // Data for initial points prev_x = xlist[0]; @@ -427,8 +434,6 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) for (loop = 1; loop < length; ++loop) { point_x = xlist[loop]; point_y = ylist[loop]; - prev_x = xlist[loop - 1]; - prev_y = ylist[loop - 1]; line_width_corners(prev_x, prev_x, point_x, point_y, width, &lfx, &lfy, <x, <y, &rfx, &rfy, &rtx, &rty); line_width_corners(prev_x, prev_x, point_x, point_y, width, &ilfx, @@ -490,32 +495,81 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) // Fill gaps in corners if (closed || (!closed && loop != 1)) { // This line - // TODO - // if (TODO) { - // // Previous line - // TODO - // } + corner_xlist[0] = ilfx; + corner_xlist[1] = orig_ilfx; + corner_xlist[2] = irfx; + corner_xlist[3] = orig_irfx; + corner_ylist[0] = ilfy; + corner_ylist[1] = orig_ilfy; + corner_ylist[2] = irfy; + corner_ylist[3] = orig_irfy; + for (corner_loop = 0; corner_loop < length; ++corner_loop) { + int_corner_xlist[corner_loop] = + roundf(corner_xlist[corner_loop]); + int_corner_ylist[corner_loop] = + roundf(corner_ylist[corner_loop]); + } + // polygon(int_corner_points) + // aalines(corner_points) + if (orig_ilfx != prev_iltx && orig_ilfy != prev_ilty && + orig_irfx != prev_irtx && orig_irfy != prev_irty) { + // Previous line + corner_xlist[1] = prev_iltx; + corner_xlist[3] = prev_irtx; + corner_ylist[1] = prev_ilty; + corner_ylist[3] = prev_irty; + for (corner_loop = 0; corner_loop < length; + ++corner_loop) { + int_corner_xlist[corner_loop] = + roundf(corner_xlist[corner_loop]); + int_corner_ylist[corner_loop] = + roundf(corner_ylist[corner_loop]); + } + // polygon(int_corner_points) + // aalines(corner_points) + } } // Data for the next iteration - // prev_x = point_x; - // prev_y = point_y; - // TODO + prev_x = point_x; + prev_y = point_y; + prev_lfx = lfx; + prev_lfy = lfy; + prev_ltx = ltx; + prev_lty = lty; + prev_rfx = rfx; + prev_rfy = rfy; + prev_rtx = rtx; + prev_rty = rty; + prev_ilfx = ilfx; + prev_ilfy = ilfy; + prev_iltx = iltx; + prev_ilty = ilty; + prev_irfx = irfx; + prev_irfy = irfy; + prev_irtx = irtx; + prev_irty = irty; } // Last point for aalines - // left_points.append(draw_to_1) - // right_points.append(draw_to_2) + left_xlist[length - 1] = ltx; + left_ylist[length - 1] = lty; + right_xlist[length - 1] = rtx; + right_ylist[length - 1] = rty; // Drawing - // lines - // aalines - // aalines + // lines(points) + // aalines(left_xlist, left_ylist) + // aalines(right_xlist, right_ylist) PyMem_Free(left_xlist); PyMem_Free(left_ylist); PyMem_Free(right_xlist); PyMem_Free(right_ylist); + PyMem_Free(corner_xlist); + PyMem_Free(corner_ylist); + PyMem_Free(int_corner_xlist); + PyMem_Free(int_corner_ylist); } PyMem_Free(xlist); From 2213ab2e779e73730037df40424342ad6ef717da Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Mon, 7 Oct 2024 16:51:04 +0200 Subject: [PATCH 06/15] Drawing lines --- src_c/draw.c | 82 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/src_c/draw.c b/src_c/draw.c index e4bff05b85..01f37d35a6 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -420,24 +420,24 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) prev_y = ylist[0]; last_x = xlist[length - 1]; last_y = ylist[length - 1]; - line_width_corners(last_x, last_y, prev_x, prev_x, width, &prev_lfx, + line_width_corners(last_x, last_y, prev_x, prev_y, width, &prev_lfx, &prev_lfy, &prev_ltx, &prev_lty, &prev_rfx, &prev_rfy, &prev_rtx, &prev_rty); - line_width_corners(last_x, last_y, prev_x, prev_x, width - 1.5, + line_width_corners(last_x, last_y, prev_x, prev_y, width - 1.5, &prev_ilfx, &prev_ilfy, &prev_iltx, &prev_ilty, &prev_irfx, &prev_irfy, &prev_irtx, &prev_irty); if (closed) { - // move first point to end of xlist and ylist // PROBLEM + // copy/move first point to end of xlist and ylist // PROBLEM } // Loop over all points, skipping first one for (loop = 1; loop < length; ++loop) { point_x = xlist[loop]; point_y = ylist[loop]; - line_width_corners(prev_x, prev_x, point_x, point_y, width, &lfx, + line_width_corners(prev_x, prev_y, point_x, point_y, width, &lfx, &lfy, <x, <y, &rfx, &rfy, &rtx, &rty); - line_width_corners(prev_x, prev_x, point_x, point_y, width, &ilfx, - &ilfy, &iltx, &ilty, &irfx, &irfy, &irtx, + line_width_corners(prev_x, prev_y, point_x, point_y, width - 1.5, + &ilfx, &ilfy, &iltx, &ilty, &irfx, &irfy, &irtx, &irty); orig_ilfx = ilfx; orig_ilfy = ilfy; @@ -447,12 +447,12 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) // Find and change corners if (loop != 1 || (loop == 1 && closed)) { // Left - if (lfx != prev_ltx && lfy != prev_lty) { + if (lfx != prev_ltx || lfy != prev_lty) { intersect_point(rfx, rfy, rtx, rty, prev_rfx, prev_rfy, prev_rtx, prev_rty, &rfx, &rfy); - intersect_point(irfx, irfy, irtx, irty, prev_irfx, - prev_irfy, prev_irtx, prev_irty, &irfx, - &irfy); + // intersect_point(irfx, irfy, irtx, irty, prev_irfx, + // prev_irfy, prev_irtx, prev_irty, &irfx, + // &irfy); } else { if (is_intersect(lfx, lfy, ltx, lty, prev_rfx, prev_rfy, @@ -460,18 +460,18 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) // special case where both points are mismatched intersect_point(rfx, rfy, rtx, rty, prev_lfx, prev_lfy, prev_ltx, prev_lty, &rfx, &rfy); - intersect_point(irfx, irfy, irtx, irty, prev_ilfx, - prev_ilfy, prev_iltx, prev_ilty, &irfx, - &irfy); + // intersect_point(irfx, irfy, irtx, irty, prev_ilfx, + // prev_ilfy, prev_iltx, prev_ilty, + // &irfx, &irfy); } } // Right - if (rfx != prev_rtx && rfy != prev_rty) { - intersect_point(lfx, lfy, ltx, lty, prev_rfx, prev_rfy, + if (rfx != prev_rtx || rfy != prev_rty) { + intersect_point(lfx, lfy, ltx, lty, prev_lfx, prev_lfy, prev_ltx, prev_lty, &lfx, &lfy); - intersect_point(ilfx, ilfy, iltx, ilty, prev_irfx, - prev_irfy, prev_iltx, prev_ilty, &ilfx, - &ilfy); + // intersect_point(ilfx, ilfy, iltx, ilty, prev_ilfx, + // prev_ilfy, prev_iltx, prev_ilty, &ilfx, + // &ilfy); } else { if (is_intersect(rfx, rfy, rtx, rty, prev_lfx, prev_lfy, @@ -479,9 +479,9 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) // special case where both points are mismatched intersect_point(lfx, lfy, ltx, lty, prev_rfx, prev_rfy, prev_rtx, prev_rty, &lfx, &lfy); - intersect_point(ilfx, ilfy, iltx, ilty, prev_irfx, - prev_irfy, prev_irtx, prev_irty, &ilfx, - &ilfy); + // intersect_point(ilfx, ilfy, iltx, ilty, prev_irfx, + // prev_irfy, prev_irtx, prev_irty, + // &ilfx, &ilfy); } } } @@ -509,8 +509,9 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) int_corner_ylist[corner_loop] = roundf(corner_ylist[corner_loop]); } - // polygon(int_corner_points) - // aalines(corner_points) + // polygon(int_corner_xlist, int_corner_ylist) + // draw_aalines(surf, color, corner_xlist, corner_ylist, + // closed, length, drawn_area); if (orig_ilfx != prev_iltx && orig_ilfy != prev_ilty && orig_irfx != prev_irtx && orig_irfy != prev_irty) { // Previous line @@ -525,8 +526,9 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) int_corner_ylist[corner_loop] = roundf(corner_ylist[corner_loop]); } - // polygon(int_corner_points) - // aalines(corner_points) + // polygon(int_corner_xlist, int_corner_ylist) + // draw_aalines(surf, color, corner_xlist, corner_ylist, + // closed, length, drawn_area); } } @@ -557,10 +559,21 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) right_xlist[length - 1] = rtx; right_ylist[length - 1] = rty; - // Drawing - // lines(points) - // aalines(left_xlist, left_ylist) - // aalines(right_xlist, right_ylist) + // Drawing lines + for (loop = 1; loop < length; ++loop) { + draw_line_width(surf, color, xlist[loop - 1], ylist[loop - 1], + xlist[loop], ylist[loop], width, drawn_area); + } + if (closed && length > 2) { + draw_line_width(surf, color, xlist[length - 1], ylist[length - 1], + xlist[0], ylist[0], width, drawn_area); + } + + // Drawing aalines + draw_aalines(surf, color, left_xlist, left_ylist, closed, length, + drawn_area); + draw_aalines(surf, color, right_xlist, right_ylist, closed, length, + drawn_area); PyMem_Free(left_xlist); PyMem_Free(left_ylist); @@ -1360,9 +1373,8 @@ static void intersect_point(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float *x, float *y) { - float d = (y2 - y1) * (x3 - x4) - (y4 - y3) * (x1 - x2); // determinant - - if (d == 0) { + float det = (y2 - y1) * (x3 - x4) - (y4 - y3) * (x1 - x2); // determinant + if (det == 0) { *x = x1; *y = y1; return; @@ -1370,9 +1382,9 @@ intersect_point(float x1, float y1, float x2, float y2, float x3, float y3, // Cramer's rule *x = ((x1 * y2 - x2 * y1) * (x3 - x4) - (x3 * y4 - x4 * y3) * (x1 - x2)) / - d; + det; *y = ((y2 - y1) * (x3 * y4 - x4 * y3) - (y4 - y3) * (x1 * y2 - x2 * y1)) / - d; + det; } static Uint32 @@ -2158,7 +2170,7 @@ line_width_corners(float from_x, float from_y, float to_x, float to_y, float temp; temp = *x3; *x3 = *x1; - *y1 = temp; + *x1 = temp; temp = *y3; *y3 = *y1; *y1 = temp; From 8f9581536a3f581b3c8bceb0c985a41bc9ea02c1 Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Mon, 7 Oct 2024 18:11:44 +0200 Subject: [PATCH 07/15] Drawig polygons in gaps --- src_c/draw.c | 57 ++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/src_c/draw.c b/src_c/draw.c index 01f37d35a6..476b775fb1 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -52,7 +52,7 @@ intersect_point(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float *x, float *y); static void line_width_corners(float from_x, float from_y, float to_x, float to_y, - int width, float *x1, float *y1, float *x2, float *y2, + float width, float *x1, float *y1, float *x2, float *y2, float *x3, float *y3, float *x4, float *y4); static void draw_aaline(SDL_Surface *surf, Uint32 color, float startx, float starty, @@ -426,9 +426,6 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) line_width_corners(last_x, last_y, prev_x, prev_y, width - 1.5, &prev_ilfx, &prev_ilfy, &prev_iltx, &prev_ilty, &prev_irfx, &prev_irfy, &prev_irtx, &prev_irty); - if (closed) { - // copy/move first point to end of xlist and ylist // PROBLEM - } // Loop over all points, skipping first one for (loop = 1; loop < length; ++loop) { @@ -439,6 +436,7 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) line_width_corners(prev_x, prev_y, point_x, point_y, width - 1.5, &ilfx, &ilfy, &iltx, &ilty, &irfx, &irfy, &irtx, &irty); + orig_ilfx = ilfx; orig_ilfy = ilfy; orig_irfx = irfx; @@ -450,9 +448,9 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) if (lfx != prev_ltx || lfy != prev_lty) { intersect_point(rfx, rfy, rtx, rty, prev_rfx, prev_rfy, prev_rtx, prev_rty, &rfx, &rfy); - // intersect_point(irfx, irfy, irtx, irty, prev_irfx, - // prev_irfy, prev_irtx, prev_irty, &irfx, - // &irfy); + intersect_point(irfx, irfy, irtx, irty, prev_irfx, + prev_irfy, prev_irtx, prev_irty, &irfx, + &irfy); } else { if (is_intersect(lfx, lfy, ltx, lty, prev_rfx, prev_rfy, @@ -460,18 +458,18 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) // special case where both points are mismatched intersect_point(rfx, rfy, rtx, rty, prev_lfx, prev_lfy, prev_ltx, prev_lty, &rfx, &rfy); - // intersect_point(irfx, irfy, irtx, irty, prev_ilfx, - // prev_ilfy, prev_iltx, prev_ilty, - // &irfx, &irfy); + intersect_point(irfx, irfy, irtx, irty, prev_ilfx, + prev_ilfy, prev_iltx, prev_ilty, &irfx, + &irfy); } } // Right if (rfx != prev_rtx || rfy != prev_rty) { intersect_point(lfx, lfy, ltx, lty, prev_lfx, prev_lfy, prev_ltx, prev_lty, &lfx, &lfy); - // intersect_point(ilfx, ilfy, iltx, ilty, prev_ilfx, - // prev_ilfy, prev_iltx, prev_ilty, &ilfx, - // &ilfy); + intersect_point(ilfx, ilfy, iltx, ilty, prev_ilfx, + prev_ilfy, prev_iltx, prev_ilty, &ilfx, + &ilfy); } else { if (is_intersect(rfx, rfy, rtx, rty, prev_lfx, prev_lfy, @@ -479,9 +477,9 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) // special case where both points are mismatched intersect_point(lfx, lfy, ltx, lty, prev_rfx, prev_rfy, prev_rtx, prev_rty, &lfx, &lfy); - // intersect_point(ilfx, ilfy, iltx, ilty, prev_irfx, - // prev_irfy, prev_irtx, prev_irty, - // &ilfx, &ilfy); + intersect_point(ilfx, ilfy, iltx, ilty, prev_irfx, + prev_irfy, prev_irtx, prev_irty, &ilfx, + &ilfy); } } } @@ -493,7 +491,7 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) right_ylist[loop - 1] = rfy; // Fill gaps in corners - if (closed || (!closed && loop != 1)) { + if (closed || loop != 1) { // This line corner_xlist[0] = ilfx; corner_xlist[1] = orig_ilfx; @@ -503,15 +501,16 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) corner_ylist[1] = orig_ilfy; corner_ylist[2] = irfy; corner_ylist[3] = orig_irfy; - for (corner_loop = 0; corner_loop < length; ++corner_loop) { + for (corner_loop = 0; corner_loop < 4; ++corner_loop) { int_corner_xlist[corner_loop] = roundf(corner_xlist[corner_loop]); int_corner_ylist[corner_loop] = roundf(corner_ylist[corner_loop]); } - // polygon(int_corner_xlist, int_corner_ylist) - // draw_aalines(surf, color, corner_xlist, corner_ylist, - // closed, length, drawn_area); + draw_fillpoly(surf, int_corner_xlist, int_corner_ylist, 4, + color, drawn_area); + draw_aalines(surf, color, corner_xlist, corner_ylist, 1, 4, + drawn_area); if (orig_ilfx != prev_iltx && orig_ilfy != prev_ilty && orig_irfx != prev_irtx && orig_irfy != prev_irty) { // Previous line @@ -519,16 +518,16 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) corner_xlist[3] = prev_irtx; corner_ylist[1] = prev_ilty; corner_ylist[3] = prev_irty; - for (corner_loop = 0; corner_loop < length; - ++corner_loop) { + for (corner_loop = 0; corner_loop < 4; ++corner_loop) { int_corner_xlist[corner_loop] = roundf(corner_xlist[corner_loop]); int_corner_ylist[corner_loop] = roundf(corner_ylist[corner_loop]); } - // polygon(int_corner_xlist, int_corner_ylist) - // draw_aalines(surf, color, corner_xlist, corner_ylist, - // closed, length, drawn_area); + draw_fillpoly(surf, int_corner_xlist, int_corner_ylist, 4, + color, drawn_area); + draw_aalines(surf, color, corner_xlist, corner_ylist, 1, 4, + drawn_area); } } @@ -2136,11 +2135,11 @@ draw_line_width(SDL_Surface *surf, Uint32 color, int x1, int y1, int x2, // first two points assemble left line and second two - right line static void line_width_corners(float from_x, float from_y, float to_x, float to_y, - int width, float *x1, float *y1, float *x2, float *y2, + float width, float *x1, float *y1, float *x2, float *y2, float *x3, float *y3, float *x4, float *y4) { - float aa_width = (float)width / 2; - float extra_width = (1.0f - (width % 2)) / 2; + float aa_width = width / 2; + float extra_width = (1.0f - ((int)width % 2)) / 2; int steep = fabs(to_x - from_x) <= fabs(to_y - from_y); if (steep) { From 9f3d84bd6dc1db7581ecc605815d3bf1507f333f Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Mon, 7 Oct 2024 18:43:58 +0200 Subject: [PATCH 08/15] Handle colsed aalines --- src_c/draw.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src_c/draw.c b/src_c/draw.c index 476b775fb1..41467502d8 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -428,9 +428,17 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) &prev_irfx, &prev_irfy, &prev_irtx, &prev_irty); // Loop over all points, skipping first one - for (loop = 1; loop < length; ++loop) { - point_x = xlist[loop]; - point_y = ylist[loop]; + for (loop = 1; loop < length + closed; ++loop) { + if (closed && (loop == length)) { + // extra iteration to allow filling gaps on closed aaline + point_x = xlist[0]; + point_y = ylist[0]; + } + else { + point_x = xlist[loop]; + point_y = ylist[loop]; + } + line_width_corners(prev_x, prev_y, point_x, point_y, width, &lfx, &lfy, <x, <y, &rfx, &rfy, &rtx, &rty); line_width_corners(prev_x, prev_y, point_x, point_y, width - 1.5, @@ -552,11 +560,13 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) prev_irty = irty; } - // Last point for aalines - left_xlist[length - 1] = ltx; - left_ylist[length - 1] = lty; - right_xlist[length - 1] = rtx; - right_ylist[length - 1] = rty; + // Last point for open aalines + if (!closed) { + left_xlist[length - 1] = ltx; + left_ylist[length - 1] = lty; + right_xlist[length - 1] = rtx; + right_ylist[length - 1] = rty; + } // Drawing lines for (loop = 1; loop < length; ++loop) { From f1c4af4436cbe6c7943e55d1988b6e8d8922d23e Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Mon, 7 Oct 2024 18:59:49 +0200 Subject: [PATCH 09/15] Code examples Screenshot --- .../ref/code_examples/draw_module_example.png | Bin 13220 -> 13755 bytes .../ref/code_examples/draw_module_example.py | 6 ++++++ 2 files changed, 6 insertions(+) diff --git a/docs/reST/ref/code_examples/draw_module_example.png b/docs/reST/ref/code_examples/draw_module_example.png index 6e3e6af13cc0b21b83d1f9f54e94843e8dfe3634..178e0f1dfbb32c690be4909baa0864ca8a780670 100644 GIT binary patch delta 9898 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%0D_X7(Y5V9V(0&>Nok60FQ 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 delta 9550 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 diff --git a/docs/reST/ref/code_examples/draw_module_example.py b/docs/reST/ref/code_examples/draw_module_example.py index 1e155247b2..4879b114f3 100644 --- a/docs/reST/ref/code_examples/draw_module_example.py +++ b/docs/reST/ref/code_examples/draw_module_example.py @@ -45,6 +45,12 @@ screen, "black", False, [[0, 80], [50, 90], [200, 80], [220, 30]], 5 ) + # Draw on the screen 3 black antialiased lines, each 5 pixels wide. + # The 'False' means the first and last points are not connected. + pygame.draw.aalines( + screen, "black", False, [[70, 40], [160, 50], [130, 65], [70, 55]], 5 + ) + # Draw a rectangle outline pygame.draw.rect(screen, "black", [75, 10, 50, 20], 2) From b3486b4bd0f65320f3e5cabd8518e2009cf4b3be Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Mon, 7 Oct 2024 19:47:17 +0200 Subject: [PATCH 10/15] Inline function --- src_c/draw.c | 423 ++++++++++++++++++++++++++------------------------- 1 file changed, 216 insertions(+), 207 deletions(-) diff --git a/src_c/draw.c b/src_c/draw.c index 41467502d8..6710f9281d 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -62,6 +62,9 @@ draw_aaline(SDL_Surface *surf, Uint32 color, float startx, float starty, static void draw_aalines(SDL_Surface *surf, Uint32 color, float *xlist, float *ylist, int closed, Py_ssize_t length, int *drawn_area); +static PG_FORCEINLINE void +draw_aalines_width(SDL_Surface *surf, Uint32 color, float *xlist, float *ylist, + int closed, Py_ssize_t length, int width, int *drawn_area); static void draw_arc(SDL_Surface *surf, int x_center, int y_center, int radius1, int radius2, int width, double angle_start, double angle_stop, @@ -386,212 +389,8 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) draw_aalines(surf, color, xlist, ylist, closed, length, drawn_area); } else { - /* Variables naming: - * prev_- - Variable is for previous point - * orig_- - Original value, not changed by intersecting - * -i- - This is "inner" point, from lines with slightly lower width - * -l/r/- - This coordinate is on left or right side of draw.lines - * -f/t- - from/to, this is either startig or ending point - * -x/y - X and Y coordinates of one same point - */ - float *left_xlist, *left_ylist, *right_xlist, *right_ylist, - *corner_xlist, *corner_ylist; - int *int_corner_xlist, *int_corner_ylist; - float point_x, point_y, prev_x, prev_y, last_x, last_y; - float prev_lfx, prev_lfy, prev_ltx, prev_lty, prev_rfx, prev_rfy, - prev_rtx, prev_rty; - float prev_ilfx, prev_ilfy, prev_iltx, prev_ilty, prev_irfx, prev_irfy, - prev_irtx, prev_irty; - float lfx, lfy, ltx, lty, rfx, rfy, rtx, rty; - float ilfx, ilfy, iltx, ilty, irfx, irfy, irtx, irty; - float orig_ilfx, orig_ilfy, orig_irfx, orig_irfy; - left_xlist = PyMem_New(float, length); - left_ylist = PyMem_New(float, length); - right_xlist = PyMem_New(float, length); - right_ylist = PyMem_New(float, length); - corner_xlist = PyMem_New(float, 4); - corner_ylist = PyMem_New(float, 4); - int_corner_xlist = PyMem_New(int, 4); - int_corner_ylist = PyMem_New(int, 4); - Py_ssize_t corner_loop; - - // Data for initial points - prev_x = xlist[0]; - prev_y = ylist[0]; - last_x = xlist[length - 1]; - last_y = ylist[length - 1]; - line_width_corners(last_x, last_y, prev_x, prev_y, width, &prev_lfx, - &prev_lfy, &prev_ltx, &prev_lty, &prev_rfx, - &prev_rfy, &prev_rtx, &prev_rty); - line_width_corners(last_x, last_y, prev_x, prev_y, width - 1.5, - &prev_ilfx, &prev_ilfy, &prev_iltx, &prev_ilty, - &prev_irfx, &prev_irfy, &prev_irtx, &prev_irty); - - // Loop over all points, skipping first one - for (loop = 1; loop < length + closed; ++loop) { - if (closed && (loop == length)) { - // extra iteration to allow filling gaps on closed aaline - point_x = xlist[0]; - point_y = ylist[0]; - } - else { - point_x = xlist[loop]; - point_y = ylist[loop]; - } - - line_width_corners(prev_x, prev_y, point_x, point_y, width, &lfx, - &lfy, <x, <y, &rfx, &rfy, &rtx, &rty); - line_width_corners(prev_x, prev_y, point_x, point_y, width - 1.5, - &ilfx, &ilfy, &iltx, &ilty, &irfx, &irfy, &irtx, - &irty); - - orig_ilfx = ilfx; - orig_ilfy = ilfy; - orig_irfx = irfx; - orig_irfy = irfy; - - // Find and change corners - if (loop != 1 || (loop == 1 && closed)) { - // Left - if (lfx != prev_ltx || lfy != prev_lty) { - intersect_point(rfx, rfy, rtx, rty, prev_rfx, prev_rfy, - prev_rtx, prev_rty, &rfx, &rfy); - intersect_point(irfx, irfy, irtx, irty, prev_irfx, - prev_irfy, prev_irtx, prev_irty, &irfx, - &irfy); - } - else { - if (is_intersect(lfx, lfy, ltx, lty, prev_rfx, prev_rfy, - prev_rtx, prev_rty)) { - // special case where both points are mismatched - intersect_point(rfx, rfy, rtx, rty, prev_lfx, prev_lfy, - prev_ltx, prev_lty, &rfx, &rfy); - intersect_point(irfx, irfy, irtx, irty, prev_ilfx, - prev_ilfy, prev_iltx, prev_ilty, &irfx, - &irfy); - } - } - // Right - if (rfx != prev_rtx || rfy != prev_rty) { - intersect_point(lfx, lfy, ltx, lty, prev_lfx, prev_lfy, - prev_ltx, prev_lty, &lfx, &lfy); - intersect_point(ilfx, ilfy, iltx, ilty, prev_ilfx, - prev_ilfy, prev_iltx, prev_ilty, &ilfx, - &ilfy); - } - else { - if (is_intersect(rfx, rfy, rtx, rty, prev_lfx, prev_lfy, - prev_ltx, prev_lty)) { - // special case where both points are mismatched - intersect_point(lfx, lfy, ltx, lty, prev_rfx, prev_rfy, - prev_rtx, prev_rty, &lfx, &lfy); - intersect_point(ilfx, ilfy, iltx, ilty, prev_irfx, - prev_irfy, prev_irtx, prev_irty, &ilfx, - &ilfy); - } - } - } - - // For aalines - left_xlist[loop - 1] = lfx; - left_ylist[loop - 1] = lfy; - right_xlist[loop - 1] = rfx; - right_ylist[loop - 1] = rfy; - - // Fill gaps in corners - if (closed || loop != 1) { - // This line - corner_xlist[0] = ilfx; - corner_xlist[1] = orig_ilfx; - corner_xlist[2] = irfx; - corner_xlist[3] = orig_irfx; - corner_ylist[0] = ilfy; - corner_ylist[1] = orig_ilfy; - corner_ylist[2] = irfy; - corner_ylist[3] = orig_irfy; - for (corner_loop = 0; corner_loop < 4; ++corner_loop) { - int_corner_xlist[corner_loop] = - roundf(corner_xlist[corner_loop]); - int_corner_ylist[corner_loop] = - roundf(corner_ylist[corner_loop]); - } - draw_fillpoly(surf, int_corner_xlist, int_corner_ylist, 4, - color, drawn_area); - draw_aalines(surf, color, corner_xlist, corner_ylist, 1, 4, - drawn_area); - if (orig_ilfx != prev_iltx && orig_ilfy != prev_ilty && - orig_irfx != prev_irtx && orig_irfy != prev_irty) { - // Previous line - corner_xlist[1] = prev_iltx; - corner_xlist[3] = prev_irtx; - corner_ylist[1] = prev_ilty; - corner_ylist[3] = prev_irty; - for (corner_loop = 0; corner_loop < 4; ++corner_loop) { - int_corner_xlist[corner_loop] = - roundf(corner_xlist[corner_loop]); - int_corner_ylist[corner_loop] = - roundf(corner_ylist[corner_loop]); - } - draw_fillpoly(surf, int_corner_xlist, int_corner_ylist, 4, - color, drawn_area); - draw_aalines(surf, color, corner_xlist, corner_ylist, 1, 4, - drawn_area); - } - } - - // Data for the next iteration - prev_x = point_x; - prev_y = point_y; - prev_lfx = lfx; - prev_lfy = lfy; - prev_ltx = ltx; - prev_lty = lty; - prev_rfx = rfx; - prev_rfy = rfy; - prev_rtx = rtx; - prev_rty = rty; - prev_ilfx = ilfx; - prev_ilfy = ilfy; - prev_iltx = iltx; - prev_ilty = ilty; - prev_irfx = irfx; - prev_irfy = irfy; - prev_irtx = irtx; - prev_irty = irty; - } - - // Last point for open aalines - if (!closed) { - left_xlist[length - 1] = ltx; - left_ylist[length - 1] = lty; - right_xlist[length - 1] = rtx; - right_ylist[length - 1] = rty; - } - - // Drawing lines - for (loop = 1; loop < length; ++loop) { - draw_line_width(surf, color, xlist[loop - 1], ylist[loop - 1], - xlist[loop], ylist[loop], width, drawn_area); - } - if (closed && length > 2) { - draw_line_width(surf, color, xlist[length - 1], ylist[length - 1], - xlist[0], ylist[0], width, drawn_area); - } - - // Drawing aalines - draw_aalines(surf, color, left_xlist, left_ylist, closed, length, - drawn_area); - draw_aalines(surf, color, right_xlist, right_ylist, closed, length, - drawn_area); - - PyMem_Free(left_xlist); - PyMem_Free(left_ylist); - PyMem_Free(right_xlist); - PyMem_Free(right_ylist); - PyMem_Free(corner_xlist); - PyMem_Free(corner_ylist); - PyMem_Free(int_corner_xlist); - PyMem_Free(int_corner_ylist); + draw_aalines_width(surf, color, xlist, ylist, closed, length, width, + drawn_area); } PyMem_Free(xlist); @@ -1356,7 +1155,7 @@ compare_int(const void *a, const void *b) return (*(const int *)a) - (*(const int *)b); } -static int +static PG_FORCEINLINE int ccw(float x1, float y1, float x2, float y2, float x3, float y3) { return (y3 - y1) * (x2 - x1) > (y2 - y1) * (x3 - x1); @@ -1864,6 +1663,216 @@ draw_aalines(SDL_Surface *surf, Uint32 color, float *xlist, float *ylist, } } +static PG_FORCEINLINE void +draw_aalines_width(SDL_Surface *surf, Uint32 color, float *xlist, float *ylist, + int closed, Py_ssize_t length, int width, int *drawn_area) +{ + /* Variables naming: + * prev_- - Variable is for previous point + * orig_- - Original value, not changed by intersecting + * -i- - This is "inner" point, from lines with slightly lower width + * -l/r/- - This coordinate is on left or right side of draw.lines + * -f/t- - from/to, this is either startig or ending point + * -x/y - X and Y coordinates of one same point + */ + float *left_xlist, *left_ylist, *right_xlist, *right_ylist, *corner_xlist, + *corner_ylist; + int *int_corner_xlist, *int_corner_ylist; + float point_x, point_y, prev_x, prev_y, last_x, last_y; + float prev_lfx, prev_lfy, prev_ltx, prev_lty, prev_rfx, prev_rfy, prev_rtx, + prev_rty; + float prev_ilfx, prev_ilfy, prev_iltx, prev_ilty, prev_irfx, prev_irfy, + prev_irtx, prev_irty; + float lfx, lfy, ltx, lty, rfx, rfy, rtx, rty; + float ilfx, ilfy, iltx, ilty, irfx, irfy, irtx, irty; + float orig_ilfx, orig_ilfy, orig_irfx, orig_irfy; + left_xlist = PyMem_New(float, length); + left_ylist = PyMem_New(float, length); + right_xlist = PyMem_New(float, length); + right_ylist = PyMem_New(float, length); + corner_xlist = PyMem_New(float, 4); + corner_ylist = PyMem_New(float, 4); + int_corner_xlist = PyMem_New(int, 4); + int_corner_ylist = PyMem_New(int, 4); + Py_ssize_t loop, corner_loop; + + // Data for initial points + prev_x = xlist[0]; + prev_y = ylist[0]; + last_x = xlist[length - 1]; + last_y = ylist[length - 1]; + line_width_corners(last_x, last_y, prev_x, prev_y, width, &prev_lfx, + &prev_lfy, &prev_ltx, &prev_lty, &prev_rfx, &prev_rfy, + &prev_rtx, &prev_rty); + line_width_corners(last_x, last_y, prev_x, prev_y, width - 1.5, &prev_ilfx, + &prev_ilfy, &prev_iltx, &prev_ilty, &prev_irfx, + &prev_irfy, &prev_irtx, &prev_irty); + + // Loop over all points, skipping first one + for (loop = 1; loop < length + closed; ++loop) { + if (closed && (loop == length)) { + // extra iteration to allow filling gaps on closed aaline + point_x = xlist[0]; + point_y = ylist[0]; + } + else { + point_x = xlist[loop]; + point_y = ylist[loop]; + } + + line_width_corners(prev_x, prev_y, point_x, point_y, width, &lfx, &lfy, + <x, <y, &rfx, &rfy, &rtx, &rty); + line_width_corners(prev_x, prev_y, point_x, point_y, width - 1.5, + &ilfx, &ilfy, &iltx, &ilty, &irfx, &irfy, &irtx, + &irty); + + orig_ilfx = ilfx; + orig_ilfy = ilfy; + orig_irfx = irfx; + orig_irfy = irfy; + + // Find and change corners + if (loop != 1 || (loop == 1 && closed)) { + // Left + if (lfx != prev_ltx || lfy != prev_lty) { + intersect_point(rfx, rfy, rtx, rty, prev_rfx, prev_rfy, + prev_rtx, prev_rty, &rfx, &rfy); + intersect_point(irfx, irfy, irtx, irty, prev_irfx, prev_irfy, + prev_irtx, prev_irty, &irfx, &irfy); + } + else { + if (is_intersect(lfx, lfy, ltx, lty, prev_rfx, prev_rfy, + prev_rtx, prev_rty)) { + // special case where both points are mismatched + intersect_point(rfx, rfy, rtx, rty, prev_lfx, prev_lfy, + prev_ltx, prev_lty, &rfx, &rfy); + intersect_point(irfx, irfy, irtx, irty, prev_ilfx, + prev_ilfy, prev_iltx, prev_ilty, &irfx, + &irfy); + } + } + // Right + if (rfx != prev_rtx || rfy != prev_rty) { + intersect_point(lfx, lfy, ltx, lty, prev_lfx, prev_lfy, + prev_ltx, prev_lty, &lfx, &lfy); + intersect_point(ilfx, ilfy, iltx, ilty, prev_ilfx, prev_ilfy, + prev_iltx, prev_ilty, &ilfx, &ilfy); + } + else { + if (is_intersect(rfx, rfy, rtx, rty, prev_lfx, prev_lfy, + prev_ltx, prev_lty)) { + // special case where both points are mismatched + intersect_point(lfx, lfy, ltx, lty, prev_rfx, prev_rfy, + prev_rtx, prev_rty, &lfx, &lfy); + intersect_point(ilfx, ilfy, iltx, ilty, prev_irfx, + prev_irfy, prev_irtx, prev_irty, &ilfx, + &ilfy); + } + } + } + + // For aalines + left_xlist[loop - 1] = lfx; + left_ylist[loop - 1] = lfy; + right_xlist[loop - 1] = rfx; + right_ylist[loop - 1] = rfy; + + // Fill gaps in corners + if (closed || loop != 1) { + // This line + corner_xlist[0] = ilfx; + corner_xlist[1] = orig_ilfx; + corner_xlist[2] = irfx; + corner_xlist[3] = orig_irfx; + corner_ylist[0] = ilfy; + corner_ylist[1] = orig_ilfy; + corner_ylist[2] = irfy; + corner_ylist[3] = orig_irfy; + for (corner_loop = 0; corner_loop < 4; ++corner_loop) { + int_corner_xlist[corner_loop] = + roundf(corner_xlist[corner_loop]); + int_corner_ylist[corner_loop] = + roundf(corner_ylist[corner_loop]); + } + draw_fillpoly(surf, int_corner_xlist, int_corner_ylist, 4, color, + drawn_area); + draw_aalines(surf, color, corner_xlist, corner_ylist, 1, 4, + drawn_area); + if (orig_ilfx != prev_iltx && orig_ilfy != prev_ilty && + orig_irfx != prev_irtx && orig_irfy != prev_irty) { + // Previous line + corner_xlist[1] = prev_iltx; + corner_xlist[3] = prev_irtx; + corner_ylist[1] = prev_ilty; + corner_ylist[3] = prev_irty; + for (corner_loop = 0; corner_loop < 4; ++corner_loop) { + int_corner_xlist[corner_loop] = + roundf(corner_xlist[corner_loop]); + int_corner_ylist[corner_loop] = + roundf(corner_ylist[corner_loop]); + } + draw_fillpoly(surf, int_corner_xlist, int_corner_ylist, 4, + color, drawn_area); + draw_aalines(surf, color, corner_xlist, corner_ylist, 1, 4, + drawn_area); + } + } + + // Data for the next iteration + prev_x = point_x; + prev_y = point_y; + prev_lfx = lfx; + prev_lfy = lfy; + prev_ltx = ltx; + prev_lty = lty; + prev_rfx = rfx; + prev_rfy = rfy; + prev_rtx = rtx; + prev_rty = rty; + prev_ilfx = ilfx; + prev_ilfy = ilfy; + prev_iltx = iltx; + prev_ilty = ilty; + prev_irfx = irfx; + prev_irfy = irfy; + prev_irtx = irtx; + prev_irty = irty; + } + + // Last point for open aalines + if (!closed) { + left_xlist[length - 1] = ltx; + left_ylist[length - 1] = lty; + right_xlist[length - 1] = rtx; + right_ylist[length - 1] = rty; + } + + // Drawing lines + for (loop = 1; loop < length; ++loop) { + draw_line_width(surf, color, xlist[loop - 1], ylist[loop - 1], + xlist[loop], ylist[loop], width, drawn_area); + } + if (closed && length > 2) { + draw_line_width(surf, color, xlist[length - 1], ylist[length - 1], + xlist[0], ylist[0], width, drawn_area); + } + + // Drawing aalines + draw_aalines(surf, color, left_xlist, left_ylist, closed, length, + drawn_area); + draw_aalines(surf, color, right_xlist, right_ylist, closed, length, + drawn_area); + + PyMem_Free(left_xlist); + PyMem_Free(left_ylist); + PyMem_Free(right_xlist); + PyMem_Free(right_ylist); + PyMem_Free(corner_xlist); + PyMem_Free(corner_ylist); + PyMem_Free(int_corner_xlist); + PyMem_Free(int_corner_ylist); +} + static void drawhorzline(SDL_Surface *surf, Uint32 color, int x1, int y1, int x2) { From 26358788c597c4ea7fc8c3384d6aff3888c7b82e Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Tue, 8 Oct 2024 00:27:11 +0200 Subject: [PATCH 11/15] Tests --- src_c/draw.c | 3 + test/draw_test.py | 158 +++++++++++++++++++++++++++++++++++----------- 2 files changed, 125 insertions(+), 36 deletions(-) diff --git a/src_c/draw.c b/src_c/draw.c index 6710f9281d..66d9afbafd 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -373,6 +373,9 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) ylist[loop] = y; } + x = xlist[0]; + y = ylist[0]; + if (width < 1) { PyMem_Free(xlist); PyMem_Free(ylist); diff --git a/test/draw_test.py b/test/draw_test.py index 872e72ee58..b722051bab 100644 --- a/test/draw_test.py +++ b/test/draw_test.py @@ -3289,7 +3289,7 @@ class AALinesMixin(BaseLineMixin): def test_aalines__args(self): """Ensures draw aalines accepts the correct args.""" bounds_rect = self.draw_aalines( - pygame.Surface((3, 3)), (0, 10, 0, 50), False, ((0, 0), (1, 1)) + pygame.Surface((3, 3)), (0, 10, 0, 50), False, ((0, 0), (1, 1)), 1 ) self.assertIsInstance(bounds_rect, pygame.Rect) @@ -3307,6 +3307,7 @@ def test_aalines__blend_warning(self): (0, 0, 0, 50), False, ((0, 0), (1, 1)), + 1, blend, ) # Check if there is only one warning and is a DeprecationWarning. @@ -3318,8 +3319,15 @@ def test_aalines__kwargs(self): surface = pygame.Surface((4, 4)) color = pygame.Color("yellow") points = ((0, 0), (1, 1), (2, 2)) + width = 2 kwargs_list = [ - {"surface": surface, "color": color, "closed": False, "points": points}, + { + "surface": surface, + "color": color, + "closed": False, + "points": points, + "width": width, + }, ] for kwargs in kwargs_list: @@ -3332,6 +3340,7 @@ def test_aalines__kwargs_order_independent(self): bounds_rect = self.draw_aalines( closed=1, points=((0, 0), (1, 1), (2, 2)), + width=2, color=(10, 20, 30), surface=pygame.Surface((3, 2)), ) @@ -3362,6 +3371,7 @@ def test_aalines__kwargs_missing(self): "color": pygame.Color("red"), "closed": 1, "points": ((2, 2), (1, 1)), + "width": 2, } for name in ("points", "closed", "color", "surface"): @@ -3401,6 +3411,7 @@ def test_aalines__kwarg_invalid_types(self): "color": pygame.Color("green"), "closed": False, "points": ((1, 2), (2, 1)), + "width": 1, } invalid_kwargs = { @@ -3450,11 +3461,13 @@ def test_aalines__args_and_kwargs(self): color = (255, 255, 0, 0) closed = 0 points = ((1, 2), (2, 1)) + width = 1 kwargs = { "surface": surface, "color": color, "closed": closed, "points": points, + "width": width, } for name in ("surface", "color", "closed", "points"): @@ -3487,6 +3500,7 @@ def test_aalines__valid_points_format(self): "color": expected_color, "closed": False, "points": None, + "width": 1, } # The point type can be a tuple/list/Vector2. @@ -3527,6 +3541,7 @@ def test_aalines__invalid_points_formats(self): "color": pygame.Color("red"), "closed": False, "points": None, + "width": 1, } points_fmts = ( @@ -3572,6 +3587,7 @@ def test_aalines__valid_closed_values(self): "color": line_color, "closed": None, "points": ((1, 1), (4, 1), (4, 4), (1, 4)), + "width": 1, } true_values = (-7, 1, 10, "2", 3.1, (4,), [5], True) @@ -3598,6 +3614,7 @@ def test_aalines__valid_color_formats(self): "color": None, "closed": False, "points": (pos, (2, 1)), + "width": 1, } greens = ( (0, 255, 0), @@ -3627,6 +3644,7 @@ def test_aalines__invalid_color_formats(self): "color": None, "closed": False, "points": ((1, 1), (1, 2)), + "width": 1, } for expected_color in (2.3, self): @@ -3648,6 +3666,39 @@ def test_aalines__color(self): for pos, color in border_pos_and_color(surface): self.assertEqual(color, expected_color, f"pos={pos}") + def test_aalines__color_with_thickness(self): + """Ensures thick aalines are drawn using the correct color.""" + x_left = y_top = 5 + for surface in self._create_surfaces(): + x_right = surface.get_width() - 5 + y_bottom = surface.get_height() - 5 + endpoints = ( + (x_left, y_top), + (x_right, y_top), + (x_right, y_bottom), + (x_left, y_bottom), + ) + for expected_color in self.COLORS: + self.draw_aalines(surface, expected_color, True, endpoints, 4) + + for t in (-1, 0, 1): + for x in range(x_left, x_right + 1): + for y in (y_top, y_bottom): + pos = (x, y + t) + self.assertEqual( + surface.get_at(pos), + expected_color, + f"pos={pos}", + ) + for y in range(y_top, y_bottom + 1): + for x in (x_left, x_right): + pos = (x + t, y) + self.assertEqual( + surface.get_at(pos), + expected_color, + f"pos={pos}", + ) + def test_aalines__gaps(self): """Tests if the aalines drawn contain any gaps. @@ -3663,11 +3714,34 @@ def test_aalines__gaps(self): for pos, color in border_pos_and_color(surface): self.assertEqual(color, expected_color, f"pos={pos}") + def test_aalines__gaps_with_thickness(self): + """Ensures thick aalines are drawn without any gaps.""" + expected_color = (255, 255, 255) + x_left = y_top = 5 + for surface in self._create_surfaces(): + h = (surface.get_width() - 11) // 5 + w = h * 5 + x_right = x_left + w + y_bottom = y_top + h + endpoints = ((x_left, y_top), (x_right, y_top), (x_right, y_bottom)) + self.draw_aalines(surface, expected_color, True, endpoints, 4) + + for x in range(x_left, x_right + 1): + for t in (-1, 0, 1): + pos = (x, y_top + t) + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + pos = (x, y_top + t + ((x - 3) // 5)) + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + for y in range(y_top, y_bottom + 1): + for t in (-1, 0, 1): + pos = (x_right + t, y) + self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}") + def test_aalines__bounding_rect(self): """Ensures draw aalines returns the correct bounding rect. - Test lines with endpoints on and off the surface and blending - enabled and disabled. + Test lines with endpoints on and off the surface and a range of + width/thickness values. """ line_color = pygame.Color("red") surf_color = pygame.Color("blue") @@ -3685,20 +3759,31 @@ def test_aalines__bounding_rect(self): # and off the surface. for pos in rect_corners_mids_and_center(surf_rect): pos_rect.center = pos - # Shape: Triangle (if closed), ^ caret (if not closed). - pts = (pos_rect.midleft, pos_rect.midtop, pos_rect.midright) - pos = pts[0] # Rect position if nothing drawn. - for closed in (True, False): - surface.fill(surf_color) # Clear for each test. + # Draw using different thicknesses. + for thickness in range(-1, 5): + # Shape: Triangle (if closed), ^ caret (if not closed). + pts = (pos_rect.midleft, pos_rect.midtop, pos_rect.midright) + pos = pts[0] # Rect position if nothing drawn. - bounding_rect = self.draw_aalines(surface, line_color, closed, pts) + for closed in (True, False): + surface.fill(surf_color) # Clear for each test. - # Calculating the expected_rect after the lines are - # drawn (it uses what is actually drawn). - expected_rect = create_bounding_rect(surface, surf_color, pos) + bounding_rect = self.draw_aalines( + surface, line_color, closed, pts, thickness + ) - self.assertEqual(bounding_rect, expected_rect) + if 0 < thickness: + # Calculating the expected_rect after the lines are + # drawn (it uses what is actually drawn). + expected_rect = create_bounding_rect( + surface, surf_color, pos + ) + else: + # Nothing drawn. + expected_rect = pygame.Rect(pos, (0, 0)) + + self.assertEqual(bounding_rect, expected_rect) def test_aalines__surface_clip(self): """Ensures draw aalines respects a surface's clip area.""" @@ -3718,34 +3803,35 @@ def test_aalines__surface_clip(self): pos_rect.center = center pts = (pos_rect.midtop, pos_rect.center, pos_rect.midbottom) for closed in (True, False): # Test closed and not closed. - # Get the expected points by drawing the aalines without - # the clip area set. - surface.set_clip(None) - surface.fill(surface_color) - self.draw_aalines(surface, aaline_color, closed, pts) + for thickness in (1, 3): # Test different line widths. + # Get the expected points by drawing the aalines without + # the clip area set. + surface.set_clip(None) + surface.fill(surface_color) + self.draw_aalines(surface, aaline_color, closed, pts, thickness) - expected_pts = get_color_points( - surface, surface_color, clip_rect, False - ) + expected_pts = get_color_points( + surface, surface_color, clip_rect, False + ) - # Clear the surface and set the clip area. Redraw the - # aalines and check that only the clip area is modified. - surface.fill(surface_color) - surface.set_clip(clip_rect) + # Clear the surface and set the clip area. Redraw the + # aalines and check that only the clip area is modified. + surface.fill(surface_color) + surface.set_clip(clip_rect) - self.draw_aalines(surface, aaline_color, closed, pts) + self.draw_aalines(surface, aaline_color, closed, pts, thickness) - surface.lock() # For possible speed up. + surface.lock() # For possible speed up. - # Check all the surface points to ensure the expected_pts - # are not surface_color. - for pt in ((x, y) for x in range(surfw) for y in range(surfh)): - if pt in expected_pts: - self.assertNotEqual(surface.get_at(pt), surface_color, pt) - else: - self.assertEqual(surface.get_at(pt), surface_color, pt) + # Check all the surface points to ensure the expected_pts + # are not surface_color. + for pt in ((x, y) for x in range(surfw) for y in range(surfh)): + if pt in expected_pts: + self.assertNotEqual(surface.get_at(pt), surface_color, pt) + else: + self.assertEqual(surface.get_at(pt), surface_color, pt) - surface.unlock() + surface.unlock() class DrawAALinesTest(AALinesMixin, DrawTestCase): From 3412b369fda0b720cce1d6a5beade82486764134 Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Tue, 8 Oct 2024 02:16:40 +0200 Subject: [PATCH 12/15] Float-int conversions --- src_c/draw.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src_c/draw.c b/src_c/draw.c index 66d9afbafd..7407615f65 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -184,8 +184,8 @@ aaline(PyObject *self, PyObject *arg, PyObject *kwargs) if (width > 1) { float x1, y1, x2, y2, x3, y3, x4, y4; - line_width_corners(startx, starty, endx, endy, width, &x1, &y1, &x2, - &y2, &x3, &y3, &x4, &y4); + line_width_corners(startx, starty, endx, endy, (float)width, &x1, &y1, + &x2, &y2, &x3, &y3, &x4, &y4); draw_line_width(surf, color, (int)startx, (int)starty, (int)endx, (int)endy, width, drawn_area); draw_aaline(surf, color, x1, y1, x2, y2, drawn_area, 0, 0, 0); @@ -379,7 +379,7 @@ aalines(PyObject *self, PyObject *arg, PyObject *kwargs) if (width < 1) { PyMem_Free(xlist); PyMem_Free(ylist); - return pgRect_New4(x, y, 0, 0); + return pgRect_New4((int)x, (int)y, 0, 0); } if (!pgSurface_Lock(surfobj)) { @@ -1704,12 +1704,12 @@ draw_aalines_width(SDL_Surface *surf, Uint32 color, float *xlist, float *ylist, prev_y = ylist[0]; last_x = xlist[length - 1]; last_y = ylist[length - 1]; - line_width_corners(last_x, last_y, prev_x, prev_y, width, &prev_lfx, + line_width_corners(last_x, last_y, prev_x, prev_y, (float)width, &prev_lfx, &prev_lfy, &prev_ltx, &prev_lty, &prev_rfx, &prev_rfy, &prev_rtx, &prev_rty); - line_width_corners(last_x, last_y, prev_x, prev_y, width - 1.5, &prev_ilfx, - &prev_ilfy, &prev_iltx, &prev_ilty, &prev_irfx, - &prev_irfy, &prev_irtx, &prev_irty); + line_width_corners(last_x, last_y, prev_x, prev_y, (float)width - 1.5f, + &prev_ilfx, &prev_ilfy, &prev_iltx, &prev_ilty, + &prev_irfx, &prev_irfy, &prev_irtx, &prev_irty); // Loop over all points, skipping first one for (loop = 1; loop < length + closed; ++loop) { @@ -1723,11 +1723,11 @@ draw_aalines_width(SDL_Surface *surf, Uint32 color, float *xlist, float *ylist, point_y = ylist[loop]; } - line_width_corners(prev_x, prev_y, point_x, point_y, width, &lfx, &lfy, - <x, <y, &rfx, &rfy, &rtx, &rty); - line_width_corners(prev_x, prev_y, point_x, point_y, width - 1.5, - &ilfx, &ilfy, &iltx, &ilty, &irfx, &irfy, &irtx, - &irty); + line_width_corners(prev_x, prev_y, point_x, point_y, (float)width, + &lfx, &lfy, <x, <y, &rfx, &rfy, &rtx, &rty); + line_width_corners(prev_x, prev_y, point_x, point_y, + (float)width - 1.5f, &ilfx, &ilfy, &iltx, &ilty, + &irfx, &irfy, &irtx, &irty); orig_ilfx = ilfx; orig_ilfy = ilfy; @@ -1793,9 +1793,9 @@ draw_aalines_width(SDL_Surface *surf, Uint32 color, float *xlist, float *ylist, corner_ylist[3] = orig_irfy; for (corner_loop = 0; corner_loop < 4; ++corner_loop) { int_corner_xlist[corner_loop] = - roundf(corner_xlist[corner_loop]); + (int)roundf(corner_xlist[corner_loop]); int_corner_ylist[corner_loop] = - roundf(corner_ylist[corner_loop]); + (int)roundf(corner_ylist[corner_loop]); } draw_fillpoly(surf, int_corner_xlist, int_corner_ylist, 4, color, drawn_area); @@ -1810,9 +1810,9 @@ draw_aalines_width(SDL_Surface *surf, Uint32 color, float *xlist, float *ylist, corner_ylist[3] = prev_irty; for (corner_loop = 0; corner_loop < 4; ++corner_loop) { int_corner_xlist[corner_loop] = - roundf(corner_xlist[corner_loop]); + (int)roundf(corner_xlist[corner_loop]); int_corner_ylist[corner_loop] = - roundf(corner_ylist[corner_loop]); + (int)roundf(corner_ylist[corner_loop]); } draw_fillpoly(surf, int_corner_xlist, int_corner_ylist, 4, color, drawn_area); @@ -1852,12 +1852,14 @@ draw_aalines_width(SDL_Surface *surf, Uint32 color, float *xlist, float *ylist, // Drawing lines for (loop = 1; loop < length; ++loop) { - draw_line_width(surf, color, xlist[loop - 1], ylist[loop - 1], - xlist[loop], ylist[loop], width, drawn_area); + draw_line_width(surf, color, (int)xlist[loop - 1], + (int)ylist[loop - 1], (int)xlist[loop], + (int)ylist[loop], width, drawn_area); } if (closed && length > 2) { - draw_line_width(surf, color, xlist[length - 1], ylist[length - 1], - xlist[0], ylist[0], width, drawn_area); + draw_line_width(surf, color, (int)xlist[length - 1], + (int)ylist[length - 1], (int)xlist[0], (int)ylist[0], + width, drawn_area); } // Drawing aalines From bbf7e8f6f2de45eea1cadd401265d954e8c29604 Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Tue, 8 Oct 2024 02:22:28 +0200 Subject: [PATCH 13/15] Fixed buildconfig --- buildconfig/stubs/pygame/draw.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildconfig/stubs/pygame/draw.pyi b/buildconfig/stubs/pygame/draw.pyi index 5906e28e76..ccd413560a 100644 --- a/buildconfig/stubs/pygame/draw.pyi +++ b/buildconfig/stubs/pygame/draw.pyi @@ -88,6 +88,6 @@ def aalines( surface: Surface, color: ColorLike, closed: bool, - width: int = 1, points: SequenceLike[Point], + width: int = 1, ) -> Rect: ... From e566377a16c5e438938268569c49cf3ef114f01a Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Sun, 13 Oct 2024 15:08:23 +0200 Subject: [PATCH 14/15] Using ints for corner coordinates Change some functions to be inline --- docs/reST/ref/draw.rst | 2 +- src_c/draw.c | 57 +++++++++++++++++++++--------------------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/docs/reST/ref/draw.rst b/docs/reST/ref/draw.rst index 7558ce7918..bf5bb1194d 100644 --- a/docs/reST/ref/draw.rst +++ b/docs/reST/ref/draw.rst @@ -549,7 +549,7 @@ object around the draw calls (see :func:`pygame.Surface.lock` and .. versionchangedold:: 2.0.0 Added support for keyword arguments. .. versionchanged:: 2.4.0 Removed deprecated ``blend`` argument .. versionchanged:: 2.5.0 ``blend`` argument readded for backcompat, but will always raise a deprecation exception when used - .. versionchanged:: 2.5.2 Added line width + .. versionchanged:: 2.5.3 Added line width .. ## pygame.draw.aalines ## diff --git a/src_c/draw.c b/src_c/draw.c index 7407615f65..3ed0abcd0a 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -47,19 +47,19 @@ draw_line(SDL_Surface *surf, int x1, int y1, int x2, int y2, Uint32 color, static int is_intersect(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4); -static void +static PG_FORCEINLINE void intersect_point(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float *x, float *y); static void -line_width_corners(float from_x, float from_y, float to_x, float to_y, - float width, float *x1, float *y1, float *x2, float *y2, - float *x3, float *y3, float *x4, float *y4); +line_width_corners(int from_x, int from_y, int to_x, int to_y, float width, + float *x1, float *y1, float *x2, float *y2, float *x3, + float *y3, float *x4, float *y4); static void draw_aaline(SDL_Surface *surf, Uint32 color, float startx, float starty, float endx, float endy, int *drawn_area, int disable_first_endpoint, int disable_second_endpoint, int extra_pixel_for_aalines); -static void +static PG_INLINE void draw_aalines(SDL_Surface *surf, Uint32 color, float *xlist, float *ylist, int closed, Py_ssize_t length, int *drawn_area); static PG_FORCEINLINE void @@ -1180,7 +1180,7 @@ is_intersect(float x1, float y1, float x2, float y2, float x3, float y3, * (x1, y1) and (x2, y2) are points of first line, * (x3, y3) and (x4, y4) are points of second line. */ -static void +static PG_FORCEINLINE void intersect_point(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float *x, float *y) { @@ -1565,7 +1565,7 @@ draw_aaline(SDL_Surface *surf, Uint32 color, float from_x, float from_y, } } -static void +static PG_INLINE void draw_aalines(SDL_Surface *surf, Uint32 color, float *xlist, float *ylist, int closed, Py_ssize_t length, int *drawn_area) { @@ -1681,7 +1681,7 @@ draw_aalines_width(SDL_Surface *surf, Uint32 color, float *xlist, float *ylist, float *left_xlist, *left_ylist, *right_xlist, *right_ylist, *corner_xlist, *corner_ylist; int *int_corner_xlist, *int_corner_ylist; - float point_x, point_y, prev_x, prev_y, last_x, last_y; + int point_x, point_y, prev_x, prev_y, last_x, last_y; float prev_lfx, prev_lfy, prev_ltx, prev_lty, prev_rfx, prev_rfy, prev_rtx, prev_rty; float prev_ilfx, prev_ilfy, prev_iltx, prev_ilty, prev_irfx, prev_irfy, @@ -1700,10 +1700,10 @@ draw_aalines_width(SDL_Surface *surf, Uint32 color, float *xlist, float *ylist, Py_ssize_t loop, corner_loop; // Data for initial points - prev_x = xlist[0]; - prev_y = ylist[0]; - last_x = xlist[length - 1]; - last_y = ylist[length - 1]; + prev_x = (int)xlist[0]; + prev_y = (int)ylist[0]; + last_x = (int)xlist[length - 1]; + last_y = (int)ylist[length - 1]; line_width_corners(last_x, last_y, prev_x, prev_y, (float)width, &prev_lfx, &prev_lfy, &prev_ltx, &prev_lty, &prev_rfx, &prev_rfy, &prev_rtx, &prev_rty); @@ -1715,12 +1715,12 @@ draw_aalines_width(SDL_Surface *surf, Uint32 color, float *xlist, float *ylist, for (loop = 1; loop < length + closed; ++loop) { if (closed && (loop == length)) { // extra iteration to allow filling gaps on closed aaline - point_x = xlist[0]; - point_y = ylist[0]; + point_x = (int)xlist[0]; + point_y = (int)ylist[0]; } else { - point_x = xlist[loop]; - point_y = ylist[loop]; + point_x = (int)xlist[loop]; + point_y = (int)ylist[loop]; } line_width_corners(prev_x, prev_y, point_x, point_y, (float)width, @@ -2158,32 +2158,33 @@ draw_line_width(SDL_Surface *surf, Uint32 color, int x1, int y1, int x2, // Calculates 4 points, representing corners of draw_line_width() // first two points assemble left line and second two - right line static void -line_width_corners(float from_x, float from_y, float to_x, float to_y, - float width, float *x1, float *y1, float *x2, float *y2, - float *x3, float *y3, float *x4, float *y4) +line_width_corners(int from_x, int from_y, int to_x, int to_y, float width, + float *x1, float *y1, float *x2, float *y2, float *x3, + float *y3, float *x4, float *y4) { float aa_width = width / 2; float extra_width = (1.0f - ((int)width % 2)) / 2; - int steep = fabs(to_x - from_x) <= fabs(to_y - from_y); + // "steep" is same as "xinc" in draw_line_width + int steep = abs(to_x - from_x) <= abs(to_y - from_y); if (steep) { *x1 = from_x + extra_width + aa_width; - *y1 = from_y; + *y1 = (float)from_y; *x2 = to_x + extra_width + aa_width; - *y2 = to_y; + *y2 = (float)to_y; *x3 = from_x + extra_width - aa_width; - *y3 = from_y; + *y3 = (float)from_y; *x4 = to_x + extra_width - aa_width; - *y4 = to_y; + *y4 = (float)to_y; } else { - *x1 = from_x; + *x1 = (float)from_x; *y1 = from_y + extra_width + aa_width; - *x2 = to_x; + *x2 = (float)to_x; *y2 = to_y + extra_width + aa_width; - *x3 = from_x; + *x3 = (float)from_x; *y3 = from_y + extra_width - aa_width; - *x4 = to_x; + *x4 = (float)to_x; *y4 = to_y + extra_width - aa_width; } From 42a7fdf9b2b477b9af41412e8de5b089277d7228 Mon Sep 17 00:00:00 2001 From: Marko Zivic Date: Sun, 13 Oct 2024 15:21:26 +0200 Subject: [PATCH 15/15] Float-int again D'oh! --- src_c/draw.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src_c/draw.c b/src_c/draw.c index 3ed0abcd0a..8da3121109 100644 --- a/src_c/draw.c +++ b/src_c/draw.c @@ -184,8 +184,9 @@ aaline(PyObject *self, PyObject *arg, PyObject *kwargs) if (width > 1) { float x1, y1, x2, y2, x3, y3, x4, y4; - line_width_corners(startx, starty, endx, endy, (float)width, &x1, &y1, - &x2, &y2, &x3, &y3, &x4, &y4); + line_width_corners((int)startx, (int)starty, (int)endx, (int)endy, + (float)width, &x1, &y1, &x2, &y2, &x3, &y3, &x4, + &y4); draw_line_width(surf, color, (int)startx, (int)starty, (int)endx, (int)endy, width, drawn_area); draw_aaline(surf, color, x1, y1, x2, y2, drawn_area, 0, 0, 0);