Repository: python-pillow/Pillow
Pull request: Added ImageDraw circle()
Resolves #8083

Implements the ImageDraw.circle feature as described and desired in the issue


Problem statement:
Missing ImageDraw.Draw.circle function
### What did you do?

I find it frustrating that Pillow's ImageDraw does still not have a function to draw a circle. Every time I come around to do it, I find that I must use ImageDraw.Draw.ellipse instead, which costs time. It must be even more frustrating for beginners who don't know more complex shapes like ellipses.

### What did you expect to happen?

An ImageDraw.Draw.circle to be available

### What actually happened?

It does not exist

### What are your OS, Python and Pillow versions?

* OS: Every
* Python: Every
* Pillow: Every

Hints from issue discussion:
Sounds like a useful shortcut, would you like to propose a PR?
Aye, https://github.com/python-pillow/Pillow/pull/8085

Commit message: Update docs/reference/ImageDraw.rst - move circle method up to indicate it is new

Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>

Patch to review:
diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst
index 4ccfacae75e..ed762c8d69c 100644
--- a/docs/reference/ImageDraw.rst
+++ b/docs/reference/ImageDraw.rst
@@ -240,6 +240,21 @@ Methods
 
         .. versionadded:: 5.3.0
 
+.. py:method:: ImageDraw.circle(xy, radius, fill=None, outline=None, width=1)
+
+    Draws a circle given the center coordinates and a radius.
+    
+    .. versionadded:: 10.4.0
+
+    :param xy: One point to define the circle center. Sequence:
+        ``[x, y]``
+    :param radius: Radius of the circle
+    :param outline: Color to use for the outline.
+    :param fill: Color to use for the fill.
+    :param width: The line width, in pixels.
+
+        .. versionadded:: 10.4.0
+
 .. py:method:: ImageDraw.line(xy, fill=None, width=0, joint=None)
 
     Draws a line between the coordinates in the ``xy`` list.
diff --git a/docs/releasenotes/10.4.0.rst b/docs/releasenotes/10.4.0.rst
index 41f33102fdd..3c42588014c 100644
--- a/docs/releasenotes/10.4.0.rst
+++ b/docs/releasenotes/10.4.0.rst
@@ -45,6 +45,10 @@ TODO
 API Additions
 =============
 
+Added PIL.ImageDraw.circle()
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Given the circle center coordinate pair and a radius, plots a circle using PIL.ImageDraw.ellipse()
+
 TODO
 ^^^^
 
diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py
index 42f2ee8c799..b270cc6baf8 100644
--- a/src/PIL/ImageDraw.py
+++ b/src/PIL/ImageDraw.py
@@ -181,6 +181,11 @@ def ellipse(self, xy: Coords, fill=None, outline=None, width=1) -> None:
         if ink is not None and ink != fill and width != 0:
             self.draw.draw_ellipse(xy, ink, 0, width)
 
+    def circle(self, xy: Coords, radius, fill=None, outline=None, width=1) -> None:
+        """Draw a circle given center coordinates and a radius."""
+        ellipse_xy = (xy[0] - radius, xy[1] - radius, xy[0] + radius, xy[1] + radius)
+        self.ellipse(ellipse_xy, fill, outline, width)
+
     def line(self, xy: Coords, fill=None, width=0, joint=None) -> None:
         """Draw a line, or a connected sequence of line segments."""
         ink = self._getink(fill)[0]


Code review comments:
diff_hunk: @@ -181,6 +181,11 @@ def ellipse(self, xy: Coords, fill=None, outline=None, width=1) -> None:
         if ink is not None and ink != fill and width != 0:
             self.draw.draw_ellipse(xy, ink, 0, width)
 
+    def circle(self, xy: Coords, radius, fill=None, outline=None, width=1) -> None:
path: src/PIL/ImageDraw.py
text: @user1:
```suggestion
    def circle(
        self, xy: Sequence[float], radius: float, fill=None, outline=None, width=1
    ) -> None:
```
`Coords` is either a sequence or a sequence of a sequence. Here, you just want a sequence.
https://github.com/python-pillow/Pillow/blob/bbe1effd63149eb3d43d59efb5fe2659f93700d0/src/PIL/_typing.py#L20