Compare commits

..

3 Commits

Author SHA1 Message Date
87fbaeafbe coordinate.Shape: add __contains__()
coordinate.Line: add __contains__(), __len__() and proper __eq__()
NEW: coordinate.Polygon: deal with Polygons defined by a list of Coordinates (in (counter)?clockwise order. Currently allows for rectilinear decomposition and area calculation
2024-01-01 21:48:17 +01:00
94cffbbd74 visualization: Add Color Enum
visualization: add draw methods for points and rectangles
2024-01-01 21:41:08 +01:00
e3309415b7 tools.minmax(): remove broken code; why was that even there? 2024-01-01 21:40:00 +01:00
3 changed files with 681 additions and 22 deletions

View File

@ -2,7 +2,7 @@ from __future__ import annotations
from enum import Enum from enum import Enum
from math import gcd, sqrt, inf, atan2, degrees, isclose from math import gcd, sqrt, inf, atan2, degrees, isclose
from .math import round_half_up from .math import round_half_up
from typing import Union, List from typing import Union, List, Iterable
from .tools import minmax from .tools import minmax
@ -379,6 +379,16 @@ class Shape:
def __rand__(self, other): def __rand__(self, other):
return self.intersection(other) return self.intersection(other)
def __contains__(self, item: Coordinate) -> bool:
if not self.mode_3d:
return self.top_left.x <= item.x <= self.bottom_right.x and self.top_left.y <= item.y <= self.bottom_right.y
else:
return (
self.top_left.x <= item.x <= self.bottom_right.x
and self.top_left.y <= item.y <= self.bottom_right.y
and self.top_left.z <= item.z <= self.bottom_right.z
)
def __str__(self): def __str__(self):
return "%s(%s -> %s)" % ( return "%s(%s -> %s)" % (
self.__class__.__name__, self.__class__.__name__,
@ -394,9 +404,9 @@ class Shape:
) )
class Square(Shape): class Rectangle(Shape):
def __init__(self, top_left, bottom_right): def __init__(self, top_left, bottom_right):
super(Square, self).__init__(top_left, bottom_right) super(Rectangle, self).__init__(top_left, bottom_right)
self.mode_3d = False self.mode_3d = False
@ -407,6 +417,7 @@ class Cube(Shape):
super(Cube, self).__init__(top_left, bottom_right) super(Cube, self).__init__(top_left, bottom_right)
# FIXME: Line could probably also just be a subclass of Shape
class Line: class Line:
def __init__(self, start: Coordinate, end: Coordinate): def __init__(self, start: Coordinate, end: Coordinate):
if start[2] is not None or end[2] is not None: if start[2] is not None or end[2] is not None:
@ -422,12 +433,6 @@ class Line:
def connects_to(self, other: Line) -> bool: def connects_to(self, other: Line) -> bool:
return self.start == other.start or self.start == other.end or self.end == other.start or self.end == other.end return self.start == other.start or self.start == other.end or self.end == other.start or self.end == other.end
def contains(self, point: Coordinate | tuple) -> bool:
return isclose(
self.start.getDistanceTo(self.end),
self.start.getDistanceTo(point) + self.end.getDistanceTo(point),
)
def intersects(self, other: Line, strict: bool = True) -> bool: def intersects(self, other: Line, strict: bool = True) -> bool:
try: try:
self.get_intersection(other, strict=strict) self.get_intersection(other, strict=strict)
@ -454,7 +459,7 @@ class Line:
if not strict: if not strict:
return ret return ret
else: else:
if self.contains(ret) and other.contains(ret): if ret in self and ret in other:
return ret return ret
else: else:
raise ValueError("intersection out of bounds") raise ValueError("intersection out of bounds")
@ -462,11 +467,79 @@ class Line:
def __hash__(self): def __hash__(self):
return hash((self.start, self.end)) return hash((self.start, self.end))
def __eq__(self, other: Line) -> bool:
return hash(self) == hash(other)
def __lt__(self, other: Line) -> bool: def __lt__(self, other: Line) -> bool:
return self.start < other.start return self.start < other.start
def __contains__(self, point: Coordinate | tuple) -> bool:
return isclose(
self.start.getDistanceTo(self.end),
self.start.getDistanceTo(point) + self.end.getDistanceTo(point),
)
def __len__(self) -> int:
return int(self.start.getDistanceTo(self.end))
def __str__(self): def __str__(self):
return f"Line({self.start} -> {self.end})" return f"Line({self.start} -> {self.end})"
def __repr__(self): def __repr__(self):
return str(self) return str(self)
class Polygon:
def __init__(self, points: list[Coordinate]) -> None:
"""points have to be in (counter)clockwise order, not repeating the first coordinate"""
if len(set(points)) != len(points):
raise ValueError("Polygon contains repeated points")
self.points = points
self.lines = set()
for i in range(len(points) - 1):
self.lines.add(Line(points[i], points[i + 1]))
self.lines.add(Line(points[-1], points[0]))
def get_circumference(self) -> float:
return sum(len(x) for x in self.lines)
def get_area(self) -> float:
S = 0
for i in range(len(self.points)):
S += (
self.points[i].x * self.points[(i + 1) % len(self.points)].y
- self.points[(i + 1) % len(self.points)].x * self.points[i].y
)
return abs(S) / 2
def decompose(self) -> Iterable[Rectangle]:
points_left = list(self.points)
def flip(point: Coordinate):
if point in points_left:
points_left.remove(point)
else:
points_left.append(point)
while points_left:
pk, pl, pm = None, None, None
for c in sorted(points_left, key=lambda p: (p[1], p[0])):
if pk is None:
pk = c
continue
if pl is None:
pl = c
continue
if pk.x <= c.x < pl.x and pk.y < c.y:
pm = c
break
flip(pk)
flip(pl)
flip(Coordinate(pk.x, pm.y))
flip(Coordinate(pl.x, pm.y))
yield Rectangle(pk, Coordinate(pl.x, pm.y))

View File

@ -102,14 +102,7 @@ def minmax(*arr: Any) -> (Any, Any):
else: else:
return arr[0], arr[0] return arr[0], arr[0]
arr = set(arr) return min(arr), max(arr)
smallest = min(arr)
biggest = max(arr)
if smallest == biggest:
arr.remove(smallest)
biggest = max(arr)
return smallest, biggest
def human_readable_time_from_delta(delta: datetime.timedelta) -> str: def human_readable_time_from_delta(delta: datetime.timedelta) -> str:

View File

@ -1,10 +1,570 @@
from __future__ import annotations from __future__ import annotations
from .coordinate import Line, Coordinate, Rectangle
from enum import Enum
import tkinter as tk import tkinter as tk
from .coordinate import Line, Coordinate
class Color(str, Enum):
ALICEBLUE = "#F0F8FF"
ANTIQUEWHITE = "#FAEBD7"
ANTIQUEWHITE1 = "#FFEFDB"
ANTIQUEWHITE2 = "#EEDFCC"
ANTIQUEWHITE3 = "#CDC0B0"
ANTIQUEWHITE4 = "#8B8378"
AQUA = "#00FFFF"
AQUAMARINE1 = "#7FFFD4"
AQUAMARINE2 = "#76EEC6"
AQUAMARINE3 = "#66CDAA"
AQUAMARINE4 = "#458B74"
AZURE = "#007FFF"
AZURE1 = "#F0FFFF"
AZURE2 = "#E0EEEE"
AZURE3 = "#C1CDCD"
AZURE4 = "#838B8B"
BANANA = "#E3CF57"
BEIGE = "#F5F5DC"
BISQUE1 = "#FFE4C4"
BISQUE2 = "#EED5B7"
BISQUE3 = "#CDB79E"
BISQUE4 = "#8B7D6B"
BLACK = "#000000"
BLANCHEDALMOND = "#FFEBCD"
BLUE = "#0000FF"
BLUE2 = "#0000EE"
BLUE3 = "#0000CD"
BLUE4 = "#00008B"
BLUEVIOLET = "#8A2BE2"
BRICK = "#9C661F"
BROWN = "#A52A2A"
BROWN1 = "#FF4040"
BROWN2 = "#EE3B3B"
BROWN3 = "#CD3333"
BROWN4 = "#8B2323"
BURLYWOOD = "#DEB887"
BURLYWOOD1 = "#FFD39B"
BURLYWOOD2 = "#EEC591"
BURLYWOOD3 = "#CDAA7D"
BURLYWOOD4 = "#8B7355"
BURNTSIENNA = "#8A360F"
BURNTUMBER = "#8A3324"
CADETBLUE = "#5F9EA0"
CADETBLUE1 = "#98F5FF"
CADETBLUE2 = "#8EE5EE"
CADETBLUE3 = "#7AC5CD"
CADETBLUE4 = "#53868B"
CADMIUMORANGE = "#FF6103"
CADMIUMYELLOW = "#FF9912"
CARROT = "#ED9121"
CHARTREUSE = "#DFFF00"
CHARTREUSE1 = "#7FFF00"
CHARTREUSE2 = "#76EE00"
CHARTREUSE3 = "#66CD00"
CHARTREUSE4 = "#458B00"
CHOCOLATE = "#D2691E"
CHOCOLATE1 = "#FF7F24"
CHOCOLATE2 = "#EE7621"
CHOCOLATE3 = "#CD661D"
CHOCOLATE4 = "#8B4513"
COBALT = "#3D59AB"
COBALTGREEN = "#3D9140"
COLDGREY = "#808A87"
CORAL = "#FF7F50"
CORAL1 = "#FF7256"
CORAL2 = "#EE6A50"
CORAL3 = "#CD5B45"
CORAL4 = "#8B3E2F"
CORNFLOWERBLUE = "#6495ED"
CORNSILK = "#FFF8DC"
CORNSILK1 = "#FFF8DC"
CORNSILK2 = "#EEE8CD"
CORNSILK3 = "#CDC8B1"
CORNSILK4 = "#8B8878"
CRIMSON = "#DC143C"
CYAN = "#00FFFF"
CYAN2 = "#00EEEE"
CYAN3 = "#00CDCD"
CYAN4 = "#008B8B"
DARKGOLDENROD = "#B8860B"
DARKGOLDENROD1 = "#FFB90F"
DARKGOLDENROD2 = "#EEAD0E"
DARKGOLDENROD3 = "#CD950C"
DARKGOLDENROD4 = "#8B6508"
DARKGRAY = "#A9A9A9"
DARKGREEN = "#006400"
DARKKHAKI = "#BDB76B"
DARKOLIVEGREEN = "#556B2F"
DARKOLIVEGREEN1 = "#CAFF70"
DARKOLIVEGREEN2 = "#BCEE68"
DARKOLIVEGREEN3 = "#A2CD5A"
DARKOLIVEGREEN4 = "#6E8B3D"
DARKORANGE = "#FF8C00"
DARKORANGE1 = "#FF7F00"
DARKORANGE2 = "#EE7600"
DARKORANGE3 = "#CD6600"
DARKORANGE4 = "#8B4500"
DARKORCHID = "#9932CC"
DARKORCHID1 = "#BF3EFF"
DARKORCHID2 = "#B23AEE"
DARKORCHID3 = "#9A32CD"
DARKORCHID4 = "#68228B"
DARKSALMON = "#E9967A"
DARKSEAGREEN = "#8FBC8F"
DARKSEAGREEN1 = "#C1FFC1"
DARKSEAGREEN2 = "#B4EEB4"
DARKSEAGREEN3 = "#9BCD9B"
DARKSEAGREEN4 = "#698B69"
DARKSLATEBLUE = "#483D8B"
DARKSLATEGRAY = "#2F4F4F"
DARKSLATEGRAY1 = "#97FFFF"
DARKSLATEGRAY2 = "#8DEEEE"
DARKSLATEGRAY3 = "#79CDCD"
DARKSLATEGRAY4 = "#528B8B"
DARKTURQUOISE = "#00CED1"
DARKVIOLET = "#9400D3"
DEEPPINK1 = "#FF1493"
DEEPPINK2 = "#EE1289"
DEEPPINK3 = "#CD1076"
DEEPPINK4 = "#8B0A50"
DEEPSKYBLUE = "#00BFFF"
DEEPSKYBLUE1 = "#00BFFF"
DEEPSKYBLUE2 = "#00B2EE"
DEEPSKYBLUE3 = "#009ACD"
DEEPSKYBLUE4 = "#00688B"
DIMGRAY = "#696969"
DODGERBLUE1 = "#1E90FF"
DODGERBLUE2 = "#1C86EE"
DODGERBLUE3 = "#1874CD"
DODGERBLUE4 = "#104E8B"
EGGSHELL = "#FCE6C9"
EMERALDGREEN = "#00C957"
FIREBRICK = "#B22222"
FIREBRICK1 = "#FF3030"
FIREBRICK2 = "#EE2C2C"
FIREBRICK3 = "#CD2626"
FIREBRICK4 = "#8B1A1A"
FLESH = "#FF7D40"
FLORALWHITE = "#FFFAF0"
FORESTGREEN = "#228B22"
GAINSBORO = "#DCDCDC"
GHOSTWHITE = "#F8F8FF"
GOLD1 = "#FFD700"
GOLD2 = "#EEC900"
GOLD3 = "#CDAD00"
GOLD4 = "#8B7500"
GOLDENROD = "#DAA520"
GOLDENROD1 = "#FFC125"
GOLDENROD2 = "#EEB422"
GOLDENROD3 = "#CD9B1D"
GOLDENROD4 = "#8B6914"
GRAY = "#808080"
GRAY1 = "#030303"
GRAY10 = "#1A1A1A"
GRAY11 = "#1C1C1C"
GRAY12 = "#1F1F1F"
GRAY13 = "#212121"
GRAY14 = "#242424"
GRAY15 = "#262626"
GRAY16 = "#292929"
GRAY17 = "#2B2B2B"
GRAY18 = "#2E2E2E"
GRAY19 = "#303030"
GRAY2 = "#050505"
GRAY20 = "#333333"
GRAY21 = "#363636"
GRAY22 = "#383838"
GRAY23 = "#3B3B3B"
GRAY24 = "#3D3D3D"
GRAY25 = "#404040"
GRAY26 = "#424242"
GRAY27 = "#454545"
GRAY28 = "#474747"
GRAY29 = "#4A4A4A"
GRAY3 = "#080808"
GRAY30 = "#4D4D4D"
GRAY31 = "#4F4F4F"
GRAY32 = "#525252"
GRAY33 = "#545454"
GRAY34 = "#575757"
GRAY35 = "#595959"
GRAY36 = "#5C5C5C"
GRAY37 = "#5E5E5E"
GRAY38 = "#616161"
GRAY39 = "#636363"
GRAY4 = "#0A0A0A"
GRAY40 = "#666666"
GRAY42 = "#6B6B6B"
GRAY43 = "#6E6E6E"
GRAY44 = "#707070"
GRAY45 = "#737373"
GRAY46 = "#757575"
GRAY47 = "#787878"
GRAY48 = "#7A7A7A"
GRAY49 = "#7D7D7D"
GRAY5 = "#0D0D0D"
GRAY50 = "#7F7F7F"
GRAY51 = "#828282"
GRAY52 = "#858585"
GRAY53 = "#878787"
GRAY54 = "#8A8A8A"
GRAY55 = "#8C8C8C"
GRAY56 = "#8F8F8F"
GRAY57 = "#919191"
GRAY58 = "#949494"
GRAY59 = "#969696"
GRAY6 = "#0F0F0F"
GRAY60 = "#999999"
GRAY61 = "#9C9C9C"
GRAY62 = "#9E9E9E"
GRAY63 = "#A1A1A1"
GRAY64 = "#A3A3A3"
GRAY65 = "#A6A6A6"
GRAY66 = "#A8A8A8"
GRAY67 = "#ABABAB"
GRAY68 = "#ADADAD"
GRAY69 = "#B0B0B0"
GRAY7 = "#121212"
GRAY70 = "#B3B3B3"
GRAY71 = "#B5B5B5"
GRAY72 = "#B8B8B8"
GRAY73 = "#BABABA"
GRAY74 = "#BDBDBD"
GRAY75 = "#BFBFBF"
GRAY76 = "#C2C2C2"
GRAY77 = "#C4C4C4"
GRAY78 = "#C7C7C7"
GRAY79 = "#C9C9C9"
GRAY8 = "#141414"
GRAY80 = "#CCCCCC"
GRAY81 = "#CFCFCF"
GRAY82 = "#D1D1D1"
GRAY83 = "#D4D4D4"
GRAY84 = "#D6D6D6"
GRAY85 = "#D9D9D9"
GRAY86 = "#DBDBDB"
GRAY87 = "#DEDEDE"
GRAY88 = "#E0E0E0"
GRAY89 = "#E3E3E3"
GRAY9 = "#171717"
GRAY90 = "#E5E5E5"
GRAY91 = "#E8E8E8"
GRAY92 = "#EBEBEB"
GRAY93 = "#EDEDED"
GRAY94 = "#F0F0F0"
GRAY95 = "#F2F2F2"
GRAY97 = "#F7F7F7"
GRAY98 = "#FAFAFA"
GRAY99 = "#FCFCFC"
GREEN = "#008000"
GREEN1 = "#00FF00"
GREEN2 = "#00EE00"
GREEN3 = "#00CD00"
GREEN4 = "#008B00"
GREENYELLOW = "#ADFF2F"
HONEYDEW1 = "#F0FFF0"
HONEYDEW2 = "#E0EEE0"
HONEYDEW3 = "#C1CDC1"
HONEYDEW4 = "#838B83"
HOTPINK = "#FF69B4"
HOTPINK1 = "#FF6EB4"
HOTPINK2 = "#EE6AA7"
HOTPINK3 = "#CD6090"
HOTPINK4 = "#8B3A62"
INDIANRED = "#CD5C5C"
INDIANRED1 = "#FF6A6A"
INDIANRED2 = "#EE6363"
INDIANRED3 = "#CD5555"
INDIANRED4 = "#8B3A3A"
INDIGO = "#4B0082"
IVORY1 = "#FFFFF0"
IVORY2 = "#EEEEE0"
IVORY3 = "#CDCDC1"
IVORY4 = "#8B8B83"
IVORYBLACK = "#292421"
KHAKI = "#F0E68C"
KHAKI1 = "#FFF68F"
KHAKI2 = "#EEE685"
KHAKI3 = "#CDC673"
KHAKI4 = "#8B864E"
LAVENDER = "#E6E6FA"
LAVENDERBLUSH1 = "#FFF0F5"
LAVENDERBLUSH2 = "#EEE0E5"
LAVENDERBLUSH3 = "#CDC1C5"
LAVENDERBLUSH4 = "#8B8386"
LAWNGREEN = "#7CFC00"
LEMONCHIFFON1 = "#FFFACD"
LEMONCHIFFON2 = "#EEE9BF"
LEMONCHIFFON3 = "#CDC9A5"
LEMONCHIFFON4 = "#8B8970"
LIGHTBLUE = "#ADD8E6"
LIGHTBLUE1 = "#BFEFFF"
LIGHTBLUE2 = "#B2DFEE"
LIGHTBLUE3 = "#9AC0CD"
LIGHTBLUE4 = "#68838B"
LIGHTCORAL = "#F08080"
LIGHTCYAN1 = "#E0FFFF"
LIGHTCYAN2 = "#D1EEEE"
LIGHTCYAN3 = "#B4CDCD"
LIGHTCYAN4 = "#7A8B8B"
LIGHTGOLDENROD1 = "#FFEC8B"
LIGHTGOLDENROD2 = "#EEDC82"
LIGHTGOLDENROD3 = "#CDBE70"
LIGHTGOLDENROD4 = "#8B814C"
LIGHTGOLDENRODYELLOW = "#FAFAD2"
LIGHTGREY = "#D3D3D3"
LIGHTPINK = "#FFB6C1"
LIGHTPINK1 = "#FFAEB9"
LIGHTPINK2 = "#EEA2AD"
LIGHTPINK3 = "#CD8C95"
LIGHTPINK4 = "#8B5F65"
LIGHTSALMON1 = "#FFA07A"
LIGHTSALMON2 = "#EE9572"
LIGHTSALMON3 = "#CD8162"
LIGHTSALMON4 = "#8B5742"
LIGHTSEAGREEN = "#20B2AA"
LIGHTSKYBLUE = "#87CEFA"
LIGHTSKYBLUE1 = "#B0E2FF"
LIGHTSKYBLUE2 = "#A4D3EE"
LIGHTSKYBLUE3 = "#8DB6CD"
LIGHTSKYBLUE4 = "#607B8B"
LIGHTSLATEBLUE = "#8470FF"
LIGHTSLATEGRAY = "#778899"
LIGHTSTEELBLUE = "#B0C4DE"
LIGHTSTEELBLUE1 = "#CAE1FF"
LIGHTSTEELBLUE2 = "#BCD2EE"
LIGHTSTEELBLUE3 = "#A2B5CD"
LIGHTSTEELBLUE4 = "#6E7B8B"
LIGHTYELLOW1 = "#FFFFE0"
LIGHTYELLOW2 = "#EEEED1"
LIGHTYELLOW3 = "#CDCDB4"
LIGHTYELLOW4 = "#8B8B7A"
LIMEGREEN = "#32CD32"
LINEN = "#FAF0E6"
MAGENTA = "#FF00FF"
MAGENTA2 = "#EE00EE"
MAGENTA3 = "#CD00CD"
MAGENTA4 = "#8B008B"
MANGANESEBLUE = "#03A89E"
MAROON = "#800000"
MAROON1 = "#FF34B3"
MAROON2 = "#EE30A7"
MAROON3 = "#CD2990"
MAROON4 = "#8B1C62"
MEDIUMORCHID = "#BA55D3"
MEDIUMORCHID1 = "#E066FF"
MEDIUMORCHID2 = "#D15FEE"
MEDIUMORCHID3 = "#B452CD"
MEDIUMORCHID4 = "#7A378B"
MEDIUMPURPLE = "#9370DB"
MEDIUMPURPLE1 = "#AB82FF"
MEDIUMPURPLE2 = "#9F79EE"
MEDIUMPURPLE3 = "#8968CD"
MEDIUMPURPLE4 = "#5D478B"
MEDIUMSEAGREEN = "#3CB371"
MEDIUMSLATEBLUE = "#7B68EE"
MEDIUMSPRINGGREEN = "#00FA9A"
MEDIUMTURQUOISE = "#48D1CC"
MEDIUMVIOLETRED = "#C71585"
MELON = "#E3A869"
MIDNIGHTBLUE = "#191970"
MINT = "#BDFCC9"
MINTCREAM = "#F5FFFA"
MISTYROSE1 = "#FFE4E1"
MISTYROSE2 = "#EED5D2"
MISTYROSE3 = "#CDB7B5"
MISTYROSE4 = "#8B7D7B"
MOCCASIN = "#FFE4B5"
NAVAJOWHITE1 = "#FFDEAD"
NAVAJOWHITE2 = "#EECFA1"
NAVAJOWHITE3 = "#CDB38B"
NAVAJOWHITE4 = "#8B795E"
NAVY = "#000080"
OLDLACE = "#FDF5E6"
OLIVE = "#808000"
OLIVEDRAB = "#6B8E23"
OLIVEDRAB1 = "#C0FF3E"
OLIVEDRAB2 = "#B3EE3A"
OLIVEDRAB3 = "#9ACD32"
OLIVEDRAB4 = "#698B22"
ORANGE = "#FF8000"
ORANGE1 = "#FFA500"
ORANGE2 = "#EE9A00"
ORANGE3 = "#CD8500"
ORANGE4 = "#8B5A00"
ORANGERED1 = "#FF4500"
ORANGERED2 = "#EE4000"
ORANGERED3 = "#CD3700"
ORANGERED4 = "#8B2500"
ORCHID = "#DA70D6"
ORCHID1 = "#FF83FA"
ORCHID2 = "#EE7AE9"
ORCHID3 = "#CD69C9"
ORCHID4 = "#8B4789"
PALEGOLDENROD = "#EEE8AA"
PALEGREEN = "#98FB98"
PALEGREEN1 = "#9AFF9A"
PALEGREEN2 = "#90EE90"
PALEGREEN3 = "#7CCD7C"
PALEGREEN4 = "#548B54"
PALETURQUOISE1 = "#BBFFFF"
PALETURQUOISE2 = "#AEEEEE"
PALETURQUOISE3 = "#96CDCD"
PALETURQUOISE4 = "#668B8B"
PALEVIOLETRED = "#DB7093"
PALEVIOLETRED1 = "#FF82AB"
PALEVIOLETRED2 = "#EE799F"
PALEVIOLETRED3 = "#CD6889"
PALEVIOLETRED4 = "#8B475D"
PAPAYAWHIP = "#FFEFD5"
PEACHPUFF1 = "#FFDAB9"
PEACHPUFF2 = "#EECBAD"
PEACHPUFF3 = "#CDAF95"
PEACHPUFF4 = "#8B7765"
PEACOCK = "#33A1C9"
PINK = "#FFC0CB"
PINK1 = "#FFB5C5"
PINK2 = "#EEA9B8"
PINK3 = "#CD919E"
PINK4 = "#8B636C"
PLUM = "#DDA0DD"
PLUM1 = "#FFBBFF"
PLUM2 = "#EEAEEE"
PLUM3 = "#CD96CD"
PLUM4 = "#8B668B"
POWDERBLUE = "#B0E0E6"
PURPLE = "#800080"
PURPLE1 = "#9B30FF"
PURPLE2 = "#912CEE"
PURPLE3 = "#7D26CD"
PURPLE4 = "#551A8B"
RASPBERRY = "#872657"
RAWSIENNA = "#C76114"
RED1 = "#FF0000"
RED2 = "#EE0000"
RED3 = "#CD0000"
RED4 = "#8B0000"
ROSYBROWN = "#BC8F8F"
ROSYBROWN1 = "#FFC1C1"
ROSYBROWN2 = "#EEB4B4"
ROSYBROWN3 = "#CD9B9B"
ROSYBROWN4 = "#8B6969"
ROYALBLUE = "#4169E1"
ROYALBLUE1 = "#4876FF"
ROYALBLUE2 = "#436EEE"
ROYALBLUE3 = "#3A5FCD"
ROYALBLUE4 = "#27408B"
SALMON = "#FA8072"
SALMON1 = "#FF8C69"
SALMON2 = "#EE8262"
SALMON3 = "#CD7054"
SALMON4 = "#8B4C39"
SANDYBROWN = "#F4A460"
SAPGREEN = "#308014"
SEAGREEN1 = "#54FF9F"
SEAGREEN2 = "#4EEE94"
SEAGREEN3 = "#43CD80"
SEAGREEN4 = "#2E8B57"
SEASHELL1 = "#FFF5EE"
SEASHELL2 = "#EEE5DE"
SEASHELL3 = "#CDC5BF"
SEASHELL4 = "#8B8682"
SEPIA = "#5E2612"
SGIBEET = "#8E388E"
SGIBRIGHTGRAY = "#C5C1AA"
SGICHARTREUSE = "#71C671"
SGIDARKGRAY = "#555555"
SGIGRAY12 = "#1E1E1E"
SGIGRAY16 = "#282828"
SGIGRAY32 = "#515151"
SGIGRAY36 = "#5B5B5B"
SGIGRAY52 = "#848484"
SGIGRAY56 = "#8E8E8E"
SGIGRAY72 = "#B7B7B7"
SGIGRAY76 = "#C1C1C1"
SGIGRAY92 = "#EAEAEA"
SGIGRAY96 = "#F4F4F4"
SGILIGHTBLUE = "#7D9EC0"
SGILIGHTGRAY = "#AAAAAA"
SGIOLIVEDRAB = "#8E8E38"
SGISALMON = "#C67171"
SGISLATEBLUE = "#7171C6"
SGITEAL = "#388E8E"
SIENNA = "#A0522D"
SIENNA1 = "#FF8247"
SIENNA2 = "#EE7942"
SIENNA3 = "#CD6839"
SIENNA4 = "#8B4726"
SILVER = "#C0C0C0"
SKYBLUE = "#87CEEB"
SKYBLUE1 = "#87CEFF"
SKYBLUE2 = "#7EC0EE"
SKYBLUE3 = "#6CA6CD"
SKYBLUE4 = "#4A708B"
SLATEBLUE = "#6A5ACD"
SLATEBLUE1 = "#836FFF"
SLATEBLUE2 = "#7A67EE"
SLATEBLUE3 = "#6959CD"
SLATEBLUE4 = "#473C8B"
SLATEGRAY = "#708090"
SLATEGRAY1 = "#C6E2FF"
SLATEGRAY2 = "#B9D3EE"
SLATEGRAY3 = "#9FB6CD"
SLATEGRAY4 = "#6C7B8B"
SNOW1 = "#FFFAFA"
SNOW2 = "#EEE9E9"
SNOW3 = "#CDC9C9"
SNOW4 = "#8B8989"
SPRINGGREEN = "#00FF7F"
SPRINGGREEN1 = "#00EE76"
SPRINGGREEN2 = "#00CD66"
SPRINGGREEN3 = "#008B45"
STEELBLUE = "#4682B4"
STEELBLUE1 = "#63B8FF"
STEELBLUE2 = "#5CACEE"
STEELBLUE3 = "#4F94CD"
STEELBLUE4 = "#36648B"
TAN = "#D2B48C"
TAN1 = "#FFA54F"
TAN2 = "#EE9A49"
TAN3 = "#CD853F"
TAN4 = "#8B5A2B"
TEAL = "#008080"
THISTLE = "#D8BFD8"
THISTLE1 = "#FFE1FF"
THISTLE2 = "#EED2EE"
THISTLE3 = "#CDB5CD"
THISTLE4 = "#8B7B8B"
TOMATO1 = "#FF6347"
TOMATO2 = "#EE5C42"
TOMATO3 = "#CD4F39"
TOMATO4 = "#8B3626"
TURQUOISE = "#40E0D0"
TURQUOISE1 = "#00F5FF"
TURQUOISE2 = "#00E5EE"
TURQUOISE3 = "#00C5CD"
TURQUOISE4 = "#00868B"
TURQUOISEBLUE = "#00C78C"
VIOLET = "#EE82EE"
VIOLETRED = "#D02090"
VIOLETRED1 = "#FF3E96"
VIOLETRED2 = "#EE3A8C"
VIOLETRED3 = "#CD3278"
VIOLETRED4 = "#8B2252"
WARMGREY = "#808069"
WHEAT = "#F5DEB3"
WHEAT1 = "#FFE7BA"
WHEAT2 = "#EED8AE"
WHEAT3 = "#CDBA96"
WHEAT4 = "#8B7E66"
WHITE = "#FFFFFF"
WHITESMOKE = "#F5F5F5"
YELLOW1 = "#FFFF00"
YELLOW2 = "#EEEE00"
YELLOW3 = "#CDCD00"
YELLOW4 = "#8B8B00"
class Window: class Window:
def __init__(self, width: int = 1280, height: int = 1024, bg_color: str = "black", fg_color: str = "white"): def __init__(self, width: int = 1280, height: int = 1024, bg_color: str = Color.BLACK, fg_color: str = Color.WHITE):
self.width, self.height = width, height self.width, self.height = width, height
self.bg_color, self.fg_color = bg_color, fg_color self.bg_color, self.fg_color = bg_color, fg_color
@ -42,11 +602,44 @@ class Window:
if not keep_boundaries: if not keep_boundaries:
self.__boundary_box = [None, None, None, None] self.__boundary_box = [None, None, None, None]
def draw_line(self, line: Line) -> None: def draw_point(self, coord: Coordinate, size: int = 5, color: str = None):
self.__canvas.create_line(line.start.x, line.start.y, line.end.x, line.end.y, fill=self.fg_color, width=1) if color is None:
color = self.fg_color
offset = size / 2
self.__canvas.create_oval(coord.x - offset, coord.y - offset, coord.x + offset, coord.y + offset, fill=color)
self._update_boundaries(coord)
def draw_line(self, line: Line, thickness: int = 1, color: str = None) -> None:
if color is None:
color = self.fg_color
self.__canvas.create_line(line.start.x, line.start.y, line.end.x, line.end.y, fill=color, width=thickness)
self._update_boundaries(line.start) self._update_boundaries(line.start)
self._update_boundaries(line.end) self._update_boundaries(line.end)
def draw_rectangle(self, rectangle: Rectangle, thickness: int = 1, color: str = None, fill: bool = False) -> None:
if color is None:
color = self.fg_color
if fill:
fill = color
else:
fill = self.bg_color
self.__canvas.create_rectangle(
rectangle.top_left.x,
rectangle.top_left.y,
rectangle.bottom_right.x,
rectangle.bottom_right.y,
fill=fill,
outline=color,
width=thickness,
)
self._update_boundaries(rectangle.top_left)
self._update_boundaries(rectangle.bottom_right)
def realign(self, padding: int = 1) -> None: def realign(self, padding: int = 1) -> None:
if self.__boundary_box[0] is not None and self.__boundary_box[0] < 0: if self.__boundary_box[0] is not None and self.__boundary_box[0] < 0:
self.__canvas.move(tk.ALL, abs(self.__boundary_box[0]) + padding, 0) self.__canvas.move(tk.ALL, abs(self.__boundary_box[0]) + padding, 0)