Repository: pygame/pygame (language: python)

Issue:
pygame.draw.polygon() not working properly with large coordinates
**Environment:**

- **Operating Windows (11)**
- **Python 3.11.5** 
- **SDL 2.28.2**
- **PyGame 2.5.1**

**Current behavior:**

You will see two triangles: a red one only consisting of the wireframe and one partially filled in green.

**Expected behavior:**

The green triangle should fill the red one out, as they are the same triangle with the only diffrence being, that the first one should get filled.

**Screenshots**

![image](https://github.com/pygame/pygame/assets/84497752/65d88ded-6bbe-4cfe-990d-166adc13241a)

**Steps to reproduce:**

1. Open code editor
2. Paste in code
3. Run

**Test code**

```python
import pygame

pygame.init()

screen_width = 2560
screen_height = 1440

screen = pygame.display.set_mode((screen_width, screen_height))

white = (255, 255, 255)
black = (0, 255, 0)
red = (255,0,0)

while True:

    screen.fill(white)

    # Draw the triangle twice once with lines once without
    pygame.draw.polygon(screen, black, ((713+ screen_width/2-1000,screen_height/2 - 471+1000),(440+ screen_width/2-1000,screen_height/2 - 934+1000),(58356+ screen_width/2-1000,screen_height/2 - 38726+1000)))
    pygame.draw.polygon(screen, red, ((713+ screen_width/2-1000,screen_height/2 - 471+1000),(440+ screen_width/2-1000,screen_height/2 - 934+1000),(58356+ screen_width/2-1000,screen_height/2 - 38726+1000)),2)

    pygame.display.flip()
```



Discussion / hints:
Hello. Thank you for reporting this!

Here's some next steps for anyone interested in working on this:
- Add a unit test to tests/draw_test.py that fails showing the property that it should hold
- Fix the polygon function in src_c/draw.c

Hi there, I am an ANU student and I would love to work on and be assigned to this issue.
I have the ability to replicate the environment with the operating system and believe I can complete this issue.
I'm trying to make a push with test files onto github, but I do not have permission. Is it possible I could gain access so I can continue work on this issue and have my pushes saved on the github for a possible peer review
@u7156704 you need to fork pygame and push to your fork
Hi i would like to investigate this issue as well. Can you please assign it to me?
Hi i would like to work this issue as well. Can you please assign it to me?

Fix patch:
diff --git a/src_c/draw.c b/src_c/draw.c
index de0502d7f1..23215df903 100644
--- a/src_c/draw.c
+++ b/src_c/draw.c
@@ -2327,9 +2327,9 @@ draw_fillpoly(SDL_Surface *surf, int *point_x, int *point_y,
      * num_points : the number of points
      */
     Py_ssize_t i, i_previous;  // i_previous is the index of the point before i
-    int y, miny, maxy;
-    int x1, y1;
-    int x2, y2;
+    double y, miny, maxy;
+    double x1, y1;
+    double x2, y2;
     /* x_intersect are the x-coordinates of intersections of the polygon
      * with some horizontal line */
     int *x_intersect = PyMem_New(int, num_points);
@@ -2356,7 +2356,8 @@ draw_fillpoly(SDL_Surface *surf, int *point_x, int *point_y,
             minx = MIN(minx, point_x[i]);
             maxx = MAX(maxx, point_x[i]);
         }
-        drawhorzlineclipbounding(surf, color, minx, miny, maxx, drawn_area);
+        drawhorzlineclipbounding(surf, color, minx, (int)miny, maxx,
+                                 drawn_area);
         PyMem_Free(x_intersect);
         return;
     }
@@ -2396,13 +2397,13 @@ draw_fillpoly(SDL_Surface *surf, int *point_x, int *point_y,
                 // add intersection if y crosses the edge (excluding the lower
                 // end), or when we are on the lowest line (maxy)
                 x_intersect[n_intersections++] =
-                    (y - y1) * (x2 - x1) / (y2 - y1) + x1;
+                    (int)((y - y1) * (x2 - x1) / (y2 - y1) + x1);
             }
         }
         qsort(x_intersect, n_intersections, sizeof(int), compare_int);
 
         for (i = 0; (i < n_intersections); i += 2) {
-            drawhorzlineclipbounding(surf, color, x_intersect[i], y,
+            drawhorzlineclipbounding(surf, color, x_intersect[i], (int)y,
                                      x_intersect[i + 1], drawn_area);
         }
     }
@@ -2421,7 +2422,7 @@ draw_fillpoly(SDL_Surface *surf, int *point_x, int *point_y,
         y = point_y[i];
 
         if ((miny < y) && (point_y[i_previous] == y) && (y < maxy)) {
-            drawhorzlineclipbounding(surf, color, point_x[i], y,
+            drawhorzlineclipbounding(surf, color, point_x[i], (int)y,
                                      point_x[i_previous], drawn_area);
         }
     }


Test patch:
diff --git a/test/draw_test.py b/test/draw_test.py
index 820b1fcbe8..a33f86494e 100644
--- a/test/draw_test.py
+++ b/test/draw_test.py
@@ -4279,72 +4279,74 @@ def test_polygon__surface_clip(self):
 
                 surface.unlock()
 
-    # Test cases for Issue #3989
+    # Test cases for Issue #3989 (fixed on #4191)
     # This tests the fill polygon bug to avoid
-    # Uncomment this code when working on this bug (commented to be built)
-    # def test_polygon_large_coords(self):
-    #     """
-    #     Ensures draw polygon works correctly with large points.
-    #     Testing the drawings of filled polygons
-    #     """
-    #     point_a = (600, 50)
-    #     point_b = (50, 600)
-    #     extreme_points_coords = (58000, 100000)
-    #     extreme_negative_coords = (-58000, -100000)
-    #     extreme_negative_x = (-58000, 100000)
-    #     extreme_negative_y = (58000, -100000)
-
-    #     surf_w = surf_h = 650
-    #     surface = pygame.Surface((surf_w, surf_h))
-
-    #     green = (0, 255, 0)
-    #     white = (0, 0, 0, 255)
-
-    #     # Extreme points case
-    #     def extreme_points(self):
-    #         self.assertEqual(surface.get_at((640, 50)), white)
-    #         self.assertEqual(surface.get_at((50, 640)), white)
-
-    #     # Extreme negative points case
-    #     def extreme_negative_pass(self):
-    #         self.assertEqual(surface.get_at((600, 25)), white)
-
-    #     @unittest.expectedFailure
-    #     def extreme_negative_fail(self):
-    #         self.assertEqual(surface.get_at((5, 5)), white)
-
-    #     # Extreme negative x case
-    #     def extreme_x_pass(self):
-    #         self.assertEqual(surface.get_at((600, 600)), white)
-
-    #     @unittest.expectedFailure
-    #     def extreme_x_fail(self):
-    #         self.assertEqual(surface.get_at((100, 640)), white)
-
-    #     # Extreme negative y case
-    #     def extreme_y_pass(self):
-    #         self.assertEqual(surface.get_at((600, 600)), white)
-
-    #     @unittest.expectedFailure
-    #     def extreme_y_fail(self):
-    #         self.assertEqual(surface.get_at((300, 300)), white)
-
-    #     # Checks the surface point to ensure the polygon has been drawn correctly.
-    #     # Uses multiple passing and failing test cases depending on the polygon
-    #     self.draw_polygon(surface, green, (point_a, point_b, extreme_points_coords))
-    #     extreme_points(self)
-
-    #     self.draw_polygon(surface, green, (point_a, point_b, extreme_negative_coords))
-    #     extreme_negative_pass(self)
-    #     extreme_negative_fail(self)
-
-    #     self.draw_polygon(surface, green, (point_a, point_b, extreme_negative_x))
-    #     extreme_x_pass(self)
-    #     extreme_x_fail(self)
-
-    #     self.draw_polygon(surface, green, (point_a, point_b, extreme_negative_y))
-    #     extreme_y_pass(self)
-    #     extreme_y_fail(self)
+    def test_polygon_large_coords_3989(self):
+        """
+        Ensures draw polygon works correctly with large points.
+        Testing the drawings of filled polygons
+        """
+        point_a = (600, 50)
+        point_b = (50, 600)
+        extreme_points_coords = (58000, 100000)
+        extreme_negative_coords = (-58000, -100000)
+        extreme_negative_x = (-58000, 100000)
+        extreme_negative_y = (58000, -100000)
+
+        surf_w = surf_h = 650
+        surface = pygame.Surface((surf_w, surf_h))
+
+        green = (0, 255, 0)
+        white = (0, 0, 0, 255)
+
+        # Draw white background
+        pygame.draw.rect(surface, white, (0, 0, surf_w, surf_h), 0)
+
+        # Extreme points case
+        def extreme_points(self):
+            self.assertEqual(surface.get_at((640, 50)), white)
+            self.assertEqual(surface.get_at((50, 640)), white)
+
+        # Extreme negative points case
+        def extreme_negative_pass(self):
+            self.assertEqual(surface.get_at((600, 25)), white)
+
+        def extreme_negative_fail(self):
+            self.assertNotEqual(surface.get_at((5, 5)), white)
+
+        # Extreme negative x case
+        def extreme_x_pass(self):
+            self.assertEqual(surface.get_at((600, 600)), white)
+
+        def extreme_x_fail(self):
+            self.assertNotEqual(surface.get_at((100, 640)), white)
+
+        # Extreme negative y case
+        def extreme_y_pass(self):
+            self.assertEqual(surface.get_at((600, 600)), white)
+
+        def extreme_y_fail(self):
+            self.assertNotEqual(surface.get_at((300, 300)), white)
+
+        # Checks the surface point to ensure the polygon has been drawn correctly.
+        # Uses multiple passing and failing test cases depending on the polygon
+        self.draw_polygon(surface, green, (point_a, point_b, extreme_points_coords))
+        extreme_points(self)
+
+        pygame.draw.rect(surface, white, (0, 0, surf_w, surf_h), 0)
+        self.draw_polygon(surface, green, (point_a, point_b, extreme_negative_coords))
+        extreme_negative_pass(self)
+        extreme_negative_fail(self)
+
+        pygame.draw.rect(surface, white, (0, 0, surf_w, surf_h), 0)
+        self.draw_polygon(surface, green, (point_a, point_b, extreme_negative_x))
+        extreme_x_pass(self)
+        extreme_x_fail(self)
+
+        pygame.draw.rect(surface, white, (0, 0, surf_w, surf_h), 0)
+        self.draw_polygon(surface, green, (point_a, point_b, extreme_negative_y))
+        extreme_y_pass(self)
+        extreme_y_fail(self)
 
 
 class DrawPolygonTest(DrawPolygonMixin, DrawTestCase):