Reparatur der venv.
This commit is contained in:
@@ -34,11 +34,25 @@ from __future__ import annotations
|
||||
import math
|
||||
import numbers
|
||||
import struct
|
||||
from typing import Sequence, cast
|
||||
from types import ModuleType
|
||||
from typing import TYPE_CHECKING, AnyStr, Callable, List, Sequence, Tuple, Union, cast
|
||||
|
||||
from . import Image, ImageColor
|
||||
from ._deprecate import deprecate
|
||||
from ._typing import Coords
|
||||
|
||||
# experimental access to the outline API
|
||||
Outline: Callable[[], Image.core._Outline] | None
|
||||
try:
|
||||
Outline = Image.core.outline
|
||||
except AttributeError:
|
||||
Outline = None
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import ImageDraw2, ImageFont
|
||||
|
||||
_Ink = Union[float, Tuple[int, ...], str]
|
||||
|
||||
"""
|
||||
A simple 2D drawing interface for PIL images.
|
||||
<p>
|
||||
@@ -48,7 +62,9 @@ directly.
|
||||
|
||||
|
||||
class ImageDraw:
|
||||
font = None
|
||||
font: (
|
||||
ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont | None
|
||||
) = None
|
||||
|
||||
def __init__(self, im: Image.Image, mode: str | None = None) -> None:
|
||||
"""
|
||||
@@ -92,7 +108,9 @@ class ImageDraw:
|
||||
self.fontmode = "L" # aliasing is okay for other modes
|
||||
self.fill = False
|
||||
|
||||
def getfont(self):
|
||||
def getfont(
|
||||
self,
|
||||
) -> ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont:
|
||||
"""
|
||||
Get the current default font.
|
||||
|
||||
@@ -117,43 +135,57 @@ class ImageDraw:
|
||||
self.font = ImageFont.load_default()
|
||||
return self.font
|
||||
|
||||
def _getfont(self, font_size: float | None):
|
||||
def _getfont(
|
||||
self, font_size: float | None
|
||||
) -> ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont:
|
||||
if font_size is not None:
|
||||
from . import ImageFont
|
||||
|
||||
font = ImageFont.load_default(font_size)
|
||||
return ImageFont.load_default(font_size)
|
||||
else:
|
||||
font = self.getfont()
|
||||
return font
|
||||
return self.getfont()
|
||||
|
||||
def _getink(self, ink, fill=None) -> tuple[int | None, int | None]:
|
||||
def _getink(
|
||||
self, ink: _Ink | None, fill: _Ink | None = None
|
||||
) -> tuple[int | None, int | None]:
|
||||
result_ink = None
|
||||
result_fill = None
|
||||
if ink is None and fill is None:
|
||||
if self.fill:
|
||||
fill = self.ink
|
||||
result_fill = self.ink
|
||||
else:
|
||||
ink = self.ink
|
||||
result_ink = self.ink
|
||||
else:
|
||||
if ink is not None:
|
||||
if isinstance(ink, str):
|
||||
ink = ImageColor.getcolor(ink, self.mode)
|
||||
if self.palette and not isinstance(ink, numbers.Number):
|
||||
ink = self.palette.getcolor(ink, self._image)
|
||||
ink = self.draw.draw_ink(ink)
|
||||
result_ink = self.draw.draw_ink(ink)
|
||||
if fill is not None:
|
||||
if isinstance(fill, str):
|
||||
fill = ImageColor.getcolor(fill, self.mode)
|
||||
if self.palette and not isinstance(fill, numbers.Number):
|
||||
fill = self.palette.getcolor(fill, self._image)
|
||||
fill = self.draw.draw_ink(fill)
|
||||
return ink, fill
|
||||
result_fill = self.draw.draw_ink(fill)
|
||||
return result_ink, result_fill
|
||||
|
||||
def arc(self, xy: Coords, start, end, fill=None, width=1) -> None:
|
||||
def arc(
|
||||
self,
|
||||
xy: Coords,
|
||||
start: float,
|
||||
end: float,
|
||||
fill: _Ink | None = None,
|
||||
width: int = 1,
|
||||
) -> None:
|
||||
"""Draw an arc."""
|
||||
ink, fill = self._getink(fill)
|
||||
if ink is not None:
|
||||
self.draw.draw_arc(xy, start, end, ink, width)
|
||||
|
||||
def bitmap(self, xy: Sequence[int], bitmap, fill=None) -> None:
|
||||
def bitmap(
|
||||
self, xy: Sequence[int], bitmap: Image.Image, fill: _Ink | None = None
|
||||
) -> None:
|
||||
"""Draw a bitmap."""
|
||||
bitmap.load()
|
||||
ink, fill = self._getink(fill)
|
||||
@@ -162,23 +194,55 @@ class ImageDraw:
|
||||
if ink is not None:
|
||||
self.draw.draw_bitmap(xy, bitmap.im, ink)
|
||||
|
||||
def chord(self, xy: Coords, start, end, fill=None, outline=None, width=1) -> None:
|
||||
def chord(
|
||||
self,
|
||||
xy: Coords,
|
||||
start: float,
|
||||
end: float,
|
||||
fill: _Ink | None = None,
|
||||
outline: _Ink | None = None,
|
||||
width: int = 1,
|
||||
) -> None:
|
||||
"""Draw a chord."""
|
||||
ink, fill = self._getink(outline, fill)
|
||||
if fill is not None:
|
||||
self.draw.draw_chord(xy, start, end, fill, 1)
|
||||
if ink is not None and ink != fill and width != 0:
|
||||
ink, fill_ink = self._getink(outline, fill)
|
||||
if fill_ink is not None:
|
||||
self.draw.draw_chord(xy, start, end, fill_ink, 1)
|
||||
if ink is not None and ink != fill_ink and width != 0:
|
||||
self.draw.draw_chord(xy, start, end, ink, 0, width)
|
||||
|
||||
def ellipse(self, xy: Coords, fill=None, outline=None, width=1) -> None:
|
||||
def ellipse(
|
||||
self,
|
||||
xy: Coords,
|
||||
fill: _Ink | None = None,
|
||||
outline: _Ink | None = None,
|
||||
width: int = 1,
|
||||
) -> None:
|
||||
"""Draw an ellipse."""
|
||||
ink, fill = self._getink(outline, fill)
|
||||
if fill is not None:
|
||||
self.draw.draw_ellipse(xy, fill, 1)
|
||||
if ink is not None and ink != fill and width != 0:
|
||||
ink, fill_ink = self._getink(outline, fill)
|
||||
if fill_ink is not None:
|
||||
self.draw.draw_ellipse(xy, fill_ink, 1)
|
||||
if ink is not None and ink != fill_ink and width != 0:
|
||||
self.draw.draw_ellipse(xy, ink, 0, width)
|
||||
|
||||
def line(self, xy: Coords, fill=None, width=0, joint=None) -> None:
|
||||
def circle(
|
||||
self,
|
||||
xy: Sequence[float],
|
||||
radius: float,
|
||||
fill: _Ink | None = None,
|
||||
outline: _Ink | None = None,
|
||||
width: int = 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: _Ink | None = None,
|
||||
width: int = 0,
|
||||
joint: str | None = None,
|
||||
) -> None:
|
||||
"""Draw a line, or a connected sequence of line segments."""
|
||||
ink = self._getink(fill)[0]
|
||||
if ink is not None:
|
||||
@@ -206,7 +270,9 @@ class ImageDraw:
|
||||
# This is a straight line, so no joint is required
|
||||
continue
|
||||
|
||||
def coord_at_angle(coord, angle):
|
||||
def coord_at_angle(
|
||||
coord: Sequence[float], angle: float
|
||||
) -> tuple[float, ...]:
|
||||
x, y = coord
|
||||
angle -= 90
|
||||
distance = width / 2 - 1
|
||||
@@ -247,37 +313,54 @@ class ImageDraw:
|
||||
]
|
||||
self.line(gap_coords, fill, width=3)
|
||||
|
||||
def shape(self, shape, fill=None, outline=None) -> None:
|
||||
def shape(
|
||||
self,
|
||||
shape: Image.core._Outline,
|
||||
fill: _Ink | None = None,
|
||||
outline: _Ink | None = None,
|
||||
) -> None:
|
||||
"""(Experimental) Draw a shape."""
|
||||
shape.close()
|
||||
ink, fill = self._getink(outline, fill)
|
||||
if fill is not None:
|
||||
self.draw.draw_outline(shape, fill, 1)
|
||||
if ink is not None and ink != fill:
|
||||
ink, fill_ink = self._getink(outline, fill)
|
||||
if fill_ink is not None:
|
||||
self.draw.draw_outline(shape, fill_ink, 1)
|
||||
if ink is not None and ink != fill_ink:
|
||||
self.draw.draw_outline(shape, ink, 0)
|
||||
|
||||
def pieslice(
|
||||
self, xy: Coords, start, end, fill=None, outline=None, width=1
|
||||
self,
|
||||
xy: Coords,
|
||||
start: float,
|
||||
end: float,
|
||||
fill: _Ink | None = None,
|
||||
outline: _Ink | None = None,
|
||||
width: int = 1,
|
||||
) -> None:
|
||||
"""Draw a pieslice."""
|
||||
ink, fill = self._getink(outline, fill)
|
||||
if fill is not None:
|
||||
self.draw.draw_pieslice(xy, start, end, fill, 1)
|
||||
if ink is not None and ink != fill and width != 0:
|
||||
ink, fill_ink = self._getink(outline, fill)
|
||||
if fill_ink is not None:
|
||||
self.draw.draw_pieslice(xy, start, end, fill_ink, 1)
|
||||
if ink is not None and ink != fill_ink and width != 0:
|
||||
self.draw.draw_pieslice(xy, start, end, ink, 0, width)
|
||||
|
||||
def point(self, xy: Coords, fill=None) -> None:
|
||||
def point(self, xy: Coords, fill: _Ink | None = None) -> None:
|
||||
"""Draw one or more individual pixels."""
|
||||
ink, fill = self._getink(fill)
|
||||
if ink is not None:
|
||||
self.draw.draw_points(xy, ink)
|
||||
|
||||
def polygon(self, xy: Coords, fill=None, outline=None, width=1) -> None:
|
||||
def polygon(
|
||||
self,
|
||||
xy: Coords,
|
||||
fill: _Ink | None = None,
|
||||
outline: _Ink | None = None,
|
||||
width: int = 1,
|
||||
) -> None:
|
||||
"""Draw a polygon."""
|
||||
ink, fill = self._getink(outline, fill)
|
||||
if fill is not None:
|
||||
self.draw.draw_polygon(xy, fill, 1)
|
||||
if ink is not None and ink != fill and width != 0:
|
||||
ink, fill_ink = self._getink(outline, fill)
|
||||
if fill_ink is not None:
|
||||
self.draw.draw_polygon(xy, fill_ink, 1)
|
||||
if ink is not None and ink != fill_ink and width != 0:
|
||||
if width == 1:
|
||||
self.draw.draw_polygon(xy, ink, 0, width)
|
||||
elif self.im is not None:
|
||||
@@ -303,22 +386,41 @@ class ImageDraw:
|
||||
self.im.paste(im.im, (0, 0) + im.size, mask.im)
|
||||
|
||||
def regular_polygon(
|
||||
self, bounding_circle, n_sides, rotation=0, fill=None, outline=None, width=1
|
||||
self,
|
||||
bounding_circle: Sequence[Sequence[float] | float],
|
||||
n_sides: int,
|
||||
rotation: float = 0,
|
||||
fill: _Ink | None = None,
|
||||
outline: _Ink | None = None,
|
||||
width: int = 1,
|
||||
) -> None:
|
||||
"""Draw a regular polygon."""
|
||||
xy = _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation)
|
||||
self.polygon(xy, fill, outline, width)
|
||||
|
||||
def rectangle(self, xy: Coords, fill=None, outline=None, width=1) -> None:
|
||||
def rectangle(
|
||||
self,
|
||||
xy: Coords,
|
||||
fill: _Ink | None = None,
|
||||
outline: _Ink | None = None,
|
||||
width: int = 1,
|
||||
) -> None:
|
||||
"""Draw a rectangle."""
|
||||
ink, fill = self._getink(outline, fill)
|
||||
if fill is not None:
|
||||
self.draw.draw_rectangle(xy, fill, 1)
|
||||
if ink is not None and ink != fill and width != 0:
|
||||
ink, fill_ink = self._getink(outline, fill)
|
||||
if fill_ink is not None:
|
||||
self.draw.draw_rectangle(xy, fill_ink, 1)
|
||||
if ink is not None and ink != fill_ink and width != 0:
|
||||
self.draw.draw_rectangle(xy, ink, 0, width)
|
||||
|
||||
def rounded_rectangle(
|
||||
self, xy: Coords, radius=0, fill=None, outline=None, width=1, *, corners=None
|
||||
self,
|
||||
xy: Coords,
|
||||
radius: float = 0,
|
||||
fill: _Ink | None = None,
|
||||
outline: _Ink | None = None,
|
||||
width: int = 1,
|
||||
*,
|
||||
corners: tuple[bool, bool, bool, bool] | None = None,
|
||||
) -> None:
|
||||
"""Draw a rounded rectangle."""
|
||||
if isinstance(xy[0], (list, tuple)):
|
||||
@@ -360,10 +462,10 @@ class ImageDraw:
|
||||
# that is a rectangle
|
||||
return self.rectangle(xy, fill, outline, width)
|
||||
|
||||
r = d // 2
|
||||
ink, fill = self._getink(outline, fill)
|
||||
r = int(d // 2)
|
||||
ink, fill_ink = self._getink(outline, fill)
|
||||
|
||||
def draw_corners(pieslice) -> None:
|
||||
def draw_corners(pieslice: bool) -> None:
|
||||
parts: tuple[tuple[tuple[float, float, float, float], int, int], ...]
|
||||
if full_x:
|
||||
# Draw top and bottom halves
|
||||
@@ -393,32 +495,32 @@ class ImageDraw:
|
||||
)
|
||||
for part in parts:
|
||||
if pieslice:
|
||||
self.draw.draw_pieslice(*(part + (fill, 1)))
|
||||
self.draw.draw_pieslice(*(part + (fill_ink, 1)))
|
||||
else:
|
||||
self.draw.draw_arc(*(part + (ink, width)))
|
||||
|
||||
if fill is not None:
|
||||
if fill_ink is not None:
|
||||
draw_corners(True)
|
||||
|
||||
if full_x:
|
||||
self.draw.draw_rectangle((x0, y0 + r + 1, x1, y1 - r - 1), fill, 1)
|
||||
self.draw.draw_rectangle((x0, y0 + r + 1, x1, y1 - r - 1), fill_ink, 1)
|
||||
else:
|
||||
self.draw.draw_rectangle((x0 + r + 1, y0, x1 - r - 1, y1), fill, 1)
|
||||
self.draw.draw_rectangle((x0 + r + 1, y0, x1 - r - 1, y1), fill_ink, 1)
|
||||
if not full_x and not full_y:
|
||||
left = [x0, y0, x0 + r, y1]
|
||||
if corners[0]:
|
||||
left[1] += r + 1
|
||||
if corners[3]:
|
||||
left[3] -= r + 1
|
||||
self.draw.draw_rectangle(left, fill, 1)
|
||||
self.draw.draw_rectangle(left, fill_ink, 1)
|
||||
|
||||
right = [x1 - r, y0, x1, y1]
|
||||
if corners[1]:
|
||||
right[1] += r + 1
|
||||
if corners[2]:
|
||||
right[3] -= r + 1
|
||||
self.draw.draw_rectangle(right, fill, 1)
|
||||
if ink is not None and ink != fill and width != 0:
|
||||
self.draw.draw_rectangle(right, fill_ink, 1)
|
||||
if ink is not None and ink != fill_ink and width != 0:
|
||||
draw_corners(False)
|
||||
|
||||
if not full_x:
|
||||
@@ -450,15 +552,13 @@ class ImageDraw:
|
||||
right[3] -= r + 1
|
||||
self.draw.draw_rectangle(right, ink, 1)
|
||||
|
||||
def _multiline_check(self, text) -> bool:
|
||||
def _multiline_check(self, text: AnyStr) -> bool:
|
||||
split_character = "\n" if isinstance(text, str) else b"\n"
|
||||
|
||||
return split_character in text
|
||||
|
||||
def _multiline_split(self, text) -> list[str | bytes]:
|
||||
split_character = "\n" if isinstance(text, str) else b"\n"
|
||||
|
||||
return text.split(split_character)
|
||||
def _multiline_split(self, text: AnyStr) -> list[AnyStr]:
|
||||
return text.split("\n" if isinstance(text, str) else b"\n")
|
||||
|
||||
def _multiline_spacing(self, font, spacing, stroke_width):
|
||||
return (
|
||||
@@ -469,10 +569,15 @@ class ImageDraw:
|
||||
|
||||
def text(
|
||||
self,
|
||||
xy,
|
||||
text,
|
||||
xy: tuple[float, float],
|
||||
text: str,
|
||||
fill=None,
|
||||
font=None,
|
||||
font: (
|
||||
ImageFont.ImageFont
|
||||
| ImageFont.FreeTypeFont
|
||||
| ImageFont.TransposedFont
|
||||
| None
|
||||
) = None,
|
||||
anchor=None,
|
||||
spacing=4,
|
||||
align="left",
|
||||
@@ -510,10 +615,11 @@ class ImageDraw:
|
||||
embedded_color,
|
||||
)
|
||||
|
||||
def getink(fill):
|
||||
ink, fill = self._getink(fill)
|
||||
def getink(fill: _Ink | None) -> int:
|
||||
ink, fill_ink = self._getink(fill)
|
||||
if ink is None:
|
||||
return fill
|
||||
assert fill_ink is not None
|
||||
return fill_ink
|
||||
return ink
|
||||
|
||||
def draw_text(ink, stroke_width=0, stroke_offset=None) -> None:
|
||||
@@ -526,7 +632,7 @@ class ImageDraw:
|
||||
coord.append(int(xy[i]))
|
||||
start.append(math.modf(xy[i])[0])
|
||||
try:
|
||||
mask, offset = font.getmask2(
|
||||
mask, offset = font.getmask2( # type: ignore[union-attr,misc]
|
||||
text,
|
||||
mode,
|
||||
direction=direction,
|
||||
@@ -542,7 +648,7 @@ class ImageDraw:
|
||||
coord = [coord[0] + offset[0], coord[1] + offset[1]]
|
||||
except AttributeError:
|
||||
try:
|
||||
mask = font.getmask(
|
||||
mask = font.getmask( # type: ignore[misc]
|
||||
text,
|
||||
mode,
|
||||
direction,
|
||||
@@ -591,10 +697,15 @@ class ImageDraw:
|
||||
|
||||
def multiline_text(
|
||||
self,
|
||||
xy,
|
||||
text,
|
||||
xy: tuple[float, float],
|
||||
text: str,
|
||||
fill=None,
|
||||
font=None,
|
||||
font: (
|
||||
ImageFont.ImageFont
|
||||
| ImageFont.FreeTypeFont
|
||||
| ImageFont.TransposedFont
|
||||
| None
|
||||
) = None,
|
||||
anchor=None,
|
||||
spacing=4,
|
||||
align="left",
|
||||
@@ -624,7 +735,7 @@ class ImageDraw:
|
||||
font = self._getfont(font_size)
|
||||
|
||||
widths = []
|
||||
max_width = 0
|
||||
max_width: float = 0
|
||||
lines = self._multiline_split(text)
|
||||
line_spacing = self._multiline_spacing(font, spacing, stroke_width)
|
||||
for line in lines:
|
||||
@@ -678,15 +789,20 @@ class ImageDraw:
|
||||
|
||||
def textlength(
|
||||
self,
|
||||
text,
|
||||
font=None,
|
||||
text: str,
|
||||
font: (
|
||||
ImageFont.ImageFont
|
||||
| ImageFont.FreeTypeFont
|
||||
| ImageFont.TransposedFont
|
||||
| None
|
||||
) = None,
|
||||
direction=None,
|
||||
features=None,
|
||||
language=None,
|
||||
embedded_color=False,
|
||||
*,
|
||||
font_size=None,
|
||||
):
|
||||
) -> float:
|
||||
"""Get the length of a given string, in pixels with 1/64 precision."""
|
||||
if self._multiline_check(text):
|
||||
msg = "can't measure length of multiline text"
|
||||
@@ -778,7 +894,7 @@ class ImageDraw:
|
||||
font = self._getfont(font_size)
|
||||
|
||||
widths = []
|
||||
max_width = 0
|
||||
max_width: float = 0
|
||||
lines = self._multiline_split(text)
|
||||
line_spacing = self._multiline_spacing(font, spacing, stroke_width)
|
||||
for line in lines:
|
||||
@@ -850,7 +966,7 @@ class ImageDraw:
|
||||
return bbox
|
||||
|
||||
|
||||
def Draw(im, mode: str | None = None) -> ImageDraw:
|
||||
def Draw(im: Image.Image, mode: str | None = None) -> ImageDraw:
|
||||
"""
|
||||
A simple 2D drawing interface for PIL images.
|
||||
|
||||
@@ -862,45 +978,38 @@ def Draw(im, mode: str | None = None) -> ImageDraw:
|
||||
defaults to the mode of the image.
|
||||
"""
|
||||
try:
|
||||
return im.getdraw(mode)
|
||||
return getattr(im, "getdraw")(mode)
|
||||
except AttributeError:
|
||||
return ImageDraw(im, mode)
|
||||
|
||||
|
||||
# experimental access to the outline API
|
||||
try:
|
||||
Outline = Image.core.outline
|
||||
except AttributeError:
|
||||
Outline = None
|
||||
|
||||
|
||||
def getdraw(im=None, hints=None):
|
||||
def getdraw(
|
||||
im: Image.Image | None = None, hints: list[str] | None = None
|
||||
) -> tuple[ImageDraw2.Draw | None, ModuleType]:
|
||||
"""
|
||||
(Experimental) A more advanced 2D drawing interface for PIL images,
|
||||
based on the WCK interface.
|
||||
|
||||
:param im: The image to draw in.
|
||||
:param hints: An optional list of hints.
|
||||
:param hints: An optional list of hints. Deprecated.
|
||||
:returns: A (drawing context, drawing resource factory) tuple.
|
||||
"""
|
||||
# FIXME: this needs more work!
|
||||
# FIXME: come up with a better 'hints' scheme.
|
||||
handler = None
|
||||
if not hints or "nicest" in hints:
|
||||
try:
|
||||
from . import _imagingagg as handler
|
||||
except ImportError:
|
||||
pass
|
||||
if handler is None:
|
||||
from . import ImageDraw2 as handler
|
||||
if im:
|
||||
im = handler.Draw(im)
|
||||
return im, handler
|
||||
if hints is not None:
|
||||
deprecate("'hints' parameter", 12)
|
||||
from . import ImageDraw2
|
||||
|
||||
draw = ImageDraw2.Draw(im) if im is not None else None
|
||||
return draw, ImageDraw2
|
||||
|
||||
|
||||
def floodfill(image: Image.Image, xy, value, border=None, thresh=0) -> None:
|
||||
def floodfill(
|
||||
image: Image.Image,
|
||||
xy: tuple[int, int],
|
||||
value: float | tuple[int, ...],
|
||||
border: float | tuple[int, ...] | None = None,
|
||||
thresh: float = 0,
|
||||
) -> None:
|
||||
"""
|
||||
(experimental) Fills a bounded region with a given color.
|
||||
.. warning:: This method is experimental.
|
||||
|
||||
Fills a bounded region with a given color.
|
||||
|
||||
:param image: Target image.
|
||||
:param xy: Seed position (a 2-item coordinate tuple). See
|
||||
@@ -918,6 +1027,7 @@ def floodfill(image: Image.Image, xy, value, border=None, thresh=0) -> None:
|
||||
# based on an implementation by Eric S. Raymond
|
||||
# amended by yo1995 @20180806
|
||||
pixel = image.load()
|
||||
assert pixel is not None
|
||||
x, y = xy
|
||||
try:
|
||||
background = pixel[x, y]
|
||||
@@ -955,12 +1065,12 @@ def floodfill(image: Image.Image, xy, value, border=None, thresh=0) -> None:
|
||||
|
||||
|
||||
def _compute_regular_polygon_vertices(
|
||||
bounding_circle, n_sides, rotation
|
||||
bounding_circle: Sequence[Sequence[float] | float], n_sides: int, rotation: float
|
||||
) -> list[tuple[float, float]]:
|
||||
"""
|
||||
Generate a list of vertices for a 2D regular polygon.
|
||||
|
||||
:param bounding_circle: The bounding circle is a tuple defined
|
||||
:param bounding_circle: The bounding circle is a sequence defined
|
||||
by a point and radius. The polygon is inscribed in this circle.
|
||||
(e.g. ``bounding_circle=(x, y, r)`` or ``((x, y), r)``)
|
||||
:param n_sides: Number of sides
|
||||
@@ -998,7 +1108,7 @@ def _compute_regular_polygon_vertices(
|
||||
# 1. Error Handling
|
||||
# 1.1 Check `n_sides` has an appropriate value
|
||||
if not isinstance(n_sides, int):
|
||||
msg = "n_sides should be an int"
|
||||
msg = "n_sides should be an int" # type: ignore[unreachable]
|
||||
raise TypeError(msg)
|
||||
if n_sides < 3:
|
||||
msg = "n_sides should be an int > 2"
|
||||
@@ -1010,9 +1120,24 @@ def _compute_regular_polygon_vertices(
|
||||
raise TypeError(msg)
|
||||
|
||||
if len(bounding_circle) == 3:
|
||||
*centroid, polygon_radius = bounding_circle
|
||||
elif len(bounding_circle) == 2:
|
||||
centroid, polygon_radius = bounding_circle
|
||||
if not all(isinstance(i, (int, float)) for i in bounding_circle):
|
||||
msg = "bounding_circle should only contain numeric data"
|
||||
raise ValueError(msg)
|
||||
|
||||
*centroid, polygon_radius = cast(List[float], list(bounding_circle))
|
||||
elif len(bounding_circle) == 2 and isinstance(bounding_circle[0], (list, tuple)):
|
||||
if not all(
|
||||
isinstance(i, (int, float)) for i in bounding_circle[0]
|
||||
) or not isinstance(bounding_circle[1], (int, float)):
|
||||
msg = "bounding_circle should only contain numeric data"
|
||||
raise ValueError(msg)
|
||||
|
||||
if len(bounding_circle[0]) != 2:
|
||||
msg = "bounding_circle centre should contain 2D coordinates (e.g. (x, y))"
|
||||
raise ValueError(msg)
|
||||
|
||||
centroid = cast(List[float], list(bounding_circle[0]))
|
||||
polygon_radius = cast(float, bounding_circle[1])
|
||||
else:
|
||||
msg = (
|
||||
"bounding_circle should contain 2D coordinates "
|
||||
@@ -1020,25 +1145,17 @@ def _compute_regular_polygon_vertices(
|
||||
)
|
||||
raise ValueError(msg)
|
||||
|
||||
if not all(isinstance(i, (int, float)) for i in (*centroid, polygon_radius)):
|
||||
msg = "bounding_circle should only contain numeric data"
|
||||
raise ValueError(msg)
|
||||
|
||||
if not len(centroid) == 2:
|
||||
msg = "bounding_circle centre should contain 2D coordinates (e.g. (x, y))"
|
||||
raise ValueError(msg)
|
||||
|
||||
if polygon_radius <= 0:
|
||||
msg = "bounding_circle radius should be > 0"
|
||||
raise ValueError(msg)
|
||||
|
||||
# 1.3 Check `rotation` has an appropriate value
|
||||
if not isinstance(rotation, (int, float)):
|
||||
msg = "rotation should be an int or float"
|
||||
msg = "rotation should be an int or float" # type: ignore[unreachable]
|
||||
raise ValueError(msg)
|
||||
|
||||
# 2. Define Helper Functions
|
||||
def _apply_rotation(point: list[float], degrees: float) -> tuple[int, int]:
|
||||
def _apply_rotation(point: list[float], degrees: float) -> tuple[float, float]:
|
||||
return (
|
||||
round(
|
||||
point[0] * math.cos(math.radians(360 - degrees))
|
||||
@@ -1054,7 +1171,7 @@ def _compute_regular_polygon_vertices(
|
||||
),
|
||||
)
|
||||
|
||||
def _compute_polygon_vertex(angle: float) -> tuple[int, int]:
|
||||
def _compute_polygon_vertex(angle: float) -> tuple[float, float]:
|
||||
start_point = [polygon_radius, 0]
|
||||
return _apply_rotation(start_point, angle)
|
||||
|
||||
@@ -1077,11 +1194,13 @@ def _compute_regular_polygon_vertices(
|
||||
return [_compute_polygon_vertex(angle) for angle in angles]
|
||||
|
||||
|
||||
def _color_diff(color1, color2: float | tuple[int, ...]) -> float:
|
||||
def _color_diff(
|
||||
color1: float | tuple[int, ...], color2: float | tuple[int, ...]
|
||||
) -> float:
|
||||
"""
|
||||
Uses 1-norm distance to calculate difference between two values.
|
||||
"""
|
||||
if isinstance(color2, tuple):
|
||||
return sum(abs(color1[i] - color2[i]) for i in range(0, len(color2)))
|
||||
else:
|
||||
return abs(color1 - color2)
|
||||
first = color1 if isinstance(color1, tuple) else (color1,)
|
||||
second = color2 if isinstance(color2, tuple) else (color2,)
|
||||
|
||||
return sum(abs(first[i] - second[i]) for i in range(0, len(second)))
|
||||
|
||||
Reference in New Issue
Block a user