Compare commits
2 Commits
f7d1fde5b7
...
0aedd1c612
| Author | SHA1 | Date | |
|---|---|---|---|
| 0aedd1c612 | |||
| 8dea09c30f |
@ -1,6 +1,5 @@
|
|||||||
bs4~=0.0.1
|
bs4~=0.0.1
|
||||||
beautifulsoup4~=4.12.2
|
beautifulsoup4~=4.12.2
|
||||||
fishhook~=0.2.9
|
fishhook~=0.2.9
|
||||||
pygame~=2.5.2
|
|
||||||
requests~=2.31.0
|
requests~=2.31.0
|
||||||
tqdm~=4.66
|
tqdm~=4.66
|
||||||
@ -3,6 +3,7 @@ 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
|
||||||
|
from .tools import minmax
|
||||||
|
|
||||||
|
|
||||||
class DistanceAlgorithm(Enum):
|
class DistanceAlgorithm(Enum):
|
||||||
@ -49,14 +50,10 @@ class Coordinate(tuple):
|
|||||||
"""
|
"""
|
||||||
if algorithm == DistanceAlgorithm.EUCLIDEAN:
|
if algorithm == DistanceAlgorithm.EUCLIDEAN:
|
||||||
if self[2] is None:
|
if self[2] is None:
|
||||||
return sqrt(
|
return sqrt(abs(self[0] - target[0]) ** 2 + abs(self[1] - target[1]) ** 2)
|
||||||
abs(self[0] - target[0]) ** 2 + abs(self[1] - target[1]) ** 2
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
return sqrt(
|
return sqrt(
|
||||||
abs(self[0] - target[0]) ** 2
|
abs(self[0] - target[0]) ** 2 + abs(self[1] - target[1]) ** 2 + abs(self[2] - target[2]) ** 2
|
||||||
+ abs(self[1] - target[1]) ** 2
|
|
||||||
+ abs(self[2] - target[2]) ** 2
|
|
||||||
)
|
)
|
||||||
elif algorithm == DistanceAlgorithm.CHEBYSHEV:
|
elif algorithm == DistanceAlgorithm.CHEBYSHEV:
|
||||||
if self[2] is None:
|
if self[2] is None:
|
||||||
@ -72,11 +69,7 @@ class Coordinate(tuple):
|
|||||||
if self[2] is None:
|
if self[2] is None:
|
||||||
return abs(self[0] - target[0]) + abs(self[1] - target[1])
|
return abs(self[0] - target[0]) + abs(self[1] - target[1])
|
||||||
else:
|
else:
|
||||||
return (
|
return abs(self[0] - target[0]) + abs(self[1] - target[1]) + abs(self[2] - target[2])
|
||||||
abs(self[0] - target[0])
|
|
||||||
+ abs(self[1] - target[1])
|
|
||||||
+ abs(self[2] - target[2])
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
dist = [abs(self[0] - target[0]), abs(self[1] - target[1])]
|
dist = [abs(self[0] - target[0]), abs(self[1] - target[1])]
|
||||||
if self[2] is None:
|
if self[2] is None:
|
||||||
@ -102,11 +95,7 @@ class Coordinate(tuple):
|
|||||||
if self[2] is None:
|
if self[2] is None:
|
||||||
return minX <= self[0] <= maxX and minY <= self[1] <= maxY
|
return minX <= self[0] <= maxX and minY <= self[1] <= maxY
|
||||||
else:
|
else:
|
||||||
return (
|
return minX <= self[0] <= maxX and minY <= self[1] <= maxY and minZ <= self[2] <= maxZ
|
||||||
minX <= self[0] <= maxX
|
|
||||||
and minY <= self[1] <= maxY
|
|
||||||
and minZ <= self[2] <= maxZ
|
|
||||||
)
|
|
||||||
|
|
||||||
def getCircle(
|
def getCircle(
|
||||||
self,
|
self,
|
||||||
@ -126,11 +115,7 @@ class Coordinate(tuple):
|
|||||||
target = Coordinate(x, y)
|
target = Coordinate(x, y)
|
||||||
if not target.inBoundaries(minX, minY, maxX, maxY):
|
if not target.inBoundaries(minX, minY, maxX, maxY):
|
||||||
continue
|
continue
|
||||||
dist = round_half_up(
|
dist = round_half_up(self.getDistanceTo(target, algorithm=algorithm, includeDiagonals=False))
|
||||||
self.getDistanceTo(
|
|
||||||
target, algorithm=algorithm, includeDiagonals=False
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if dist == radius:
|
if dist == radius:
|
||||||
ret.append(target)
|
ret.append(target)
|
||||||
|
|
||||||
@ -141,11 +126,7 @@ class Coordinate(tuple):
|
|||||||
target = Coordinate(x, y)
|
target = Coordinate(x, y)
|
||||||
if not target.inBoundaries(minX, minY, maxX, maxY, minZ, maxZ):
|
if not target.inBoundaries(minX, minY, maxX, maxY, minZ, maxZ):
|
||||||
continue
|
continue
|
||||||
dist = round_half_up(
|
dist = round_half_up(self.getDistanceTo(target, algorithm=algorithm, includeDiagonals=False))
|
||||||
self.getDistanceTo(
|
|
||||||
target, algorithm=algorithm, includeDiagonals=False
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if dist == radius:
|
if dist == radius:
|
||||||
ret.append(target)
|
ret.append(target)
|
||||||
|
|
||||||
@ -195,12 +176,7 @@ class Coordinate(tuple):
|
|||||||
yield self.__class__(self[0] + dx, self[1] + dy)
|
yield self.__class__(self[0] + dx, self[1] + dy)
|
||||||
else:
|
else:
|
||||||
if includeDiagonal:
|
if includeDiagonal:
|
||||||
nb_list = [
|
nb_list = [(x, y, z) for x in [-dist, 0, dist] for y in [-dist, 0, dist] for z in [-dist, 0, dist]]
|
||||||
(x, y, z)
|
|
||||||
for x in [-dist, 0, dist]
|
|
||||||
for y in [-dist, 0, dist]
|
|
||||||
for z in [-dist, 0, dist]
|
|
||||||
]
|
|
||||||
nb_list.remove((0, 0, 0))
|
nb_list.remove((0, 0, 0))
|
||||||
else:
|
else:
|
||||||
nb_list = [
|
nb_list = [
|
||||||
@ -213,11 +189,7 @@ class Coordinate(tuple):
|
|||||||
]
|
]
|
||||||
|
|
||||||
for dx, dy, dz in nb_list:
|
for dx, dy, dz in nb_list:
|
||||||
if (
|
if minX <= self[0] + dx <= maxX and minY <= self[1] + dy <= maxY and minZ <= self[2] + dz <= maxZ:
|
||||||
minX <= self[0] + dx <= maxX
|
|
||||||
and minY <= self[1] + dy <= maxY
|
|
||||||
and minZ <= self[2] + dz <= maxZ
|
|
||||||
):
|
|
||||||
yield self.__class__(self[0] + dx, self[1] + dy, self[2] + dz)
|
yield self.__class__(self[0] + dx, self[1] + dy, self[2] + dz)
|
||||||
|
|
||||||
def getAngleTo(self, target: Coordinate | tuple, normalized: bool = False) -> float:
|
def getAngleTo(self, target: Coordinate | tuple, normalized: bool = False) -> float:
|
||||||
@ -246,19 +218,14 @@ class Coordinate(tuple):
|
|||||||
steps = gcd(diff[0], diff[1])
|
steps = gcd(diff[0], diff[1])
|
||||||
step_x = diff[0] // steps
|
step_x = diff[0] // steps
|
||||||
step_y = diff[1] // steps
|
step_y = diff[1] // steps
|
||||||
return [
|
return [self.__class__(self[0] + step_x * i, self[1] + step_y * i) for i in range(steps + 1)]
|
||||||
self.__class__(self[0] + step_x * i, self[1] + step_y * i)
|
|
||||||
for i in range(steps + 1)
|
|
||||||
]
|
|
||||||
else:
|
else:
|
||||||
steps = gcd(diff[0], diff[1], diff[2])
|
steps = gcd(diff[0], diff[1], diff[2])
|
||||||
step_x = diff[0] // steps
|
step_x = diff[0] // steps
|
||||||
step_y = diff[1] // steps
|
step_y = diff[1] // steps
|
||||||
step_z = diff[2] // steps
|
step_z = diff[2] // steps
|
||||||
return [
|
return [
|
||||||
self.__class__(
|
self.__class__(self[0] + step_x * i, self[1] + step_y * i, self[2] + step_z * i)
|
||||||
self[0] + step_x * i, self[1] + step_y * i, self[2] + step_z * i
|
|
||||||
)
|
|
||||||
for i in range(steps + 1)
|
for i in range(steps + 1)
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -281,17 +248,13 @@ class Coordinate(tuple):
|
|||||||
if self[2] is None:
|
if self[2] is None:
|
||||||
return self.__class__(self[0] + other[0], self[1] + other[1])
|
return self.__class__(self[0] + other[0], self[1] + other[1])
|
||||||
else:
|
else:
|
||||||
return self.__class__(
|
return self.__class__(self[0] + other[0], self[1] + other[1], self[2] + other[2])
|
||||||
self[0] + other[0], self[1] + other[1], self[2] + other[2]
|
|
||||||
)
|
|
||||||
|
|
||||||
def __sub__(self, other: Coordinate | tuple) -> Coordinate:
|
def __sub__(self, other: Coordinate | tuple) -> Coordinate:
|
||||||
if self[2] is None:
|
if self[2] is None:
|
||||||
return self.__class__(self[0] - other[0], self[1] - other[1])
|
return self.__class__(self[0] - other[0], self[1] - other[1])
|
||||||
else:
|
else:
|
||||||
return self.__class__(
|
return self.__class__(self[0] - other[0], self[1] - other[1], self[2] - other[2])
|
||||||
self[0] - other[0], self[1] - other[1], self[2] - other[2]
|
|
||||||
)
|
|
||||||
|
|
||||||
def __mul__(self, other: int | float) -> Coordinate:
|
def __mul__(self, other: int | float) -> Coordinate:
|
||||||
if self[2] is None:
|
if self[2] is None:
|
||||||
@ -346,11 +309,7 @@ class Coordinate(tuple):
|
|||||||
step: int | float = 1,
|
step: int | float = 1,
|
||||||
) -> List[Coordinate]:
|
) -> List[Coordinate]:
|
||||||
if from_z is None or to_z is None:
|
if from_z is None or to_z is None:
|
||||||
return [
|
return [cls(x, y) for x in range(from_x, to_x + step, step) for y in range(from_y, to_y + step, step)]
|
||||||
cls(x, y)
|
|
||||||
for x in range(from_x, to_x + step, step)
|
|
||||||
for y in range(from_y, to_y + step, step)
|
|
||||||
]
|
|
||||||
else:
|
else:
|
||||||
return [
|
return [
|
||||||
cls(x, y, z)
|
cls(x, y, z)
|
||||||
@ -375,9 +334,7 @@ class Shape:
|
|||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
if not self.mode_3d:
|
if not self.mode_3d:
|
||||||
return (self.bottom_right.x - self.top_left.x + 1) * (
|
return (self.bottom_right.x - self.top_left.x + 1) * (self.bottom_right.y - self.top_left.y + 1)
|
||||||
self.bottom_right.y - self.top_left.y + 1
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
return (
|
return (
|
||||||
(self.bottom_right.x - self.top_left.x + 1)
|
(self.bottom_right.x - self.top_left.x + 1)
|
||||||
@ -394,43 +351,23 @@ class Shape:
|
|||||||
|
|
||||||
if not self.mode_3d:
|
if not self.mode_3d:
|
||||||
intersect_top_left = Coordinate(
|
intersect_top_left = Coordinate(
|
||||||
self.top_left.x
|
self.top_left.x if self.top_left.x > other.top_left.x else other.top_left.x,
|
||||||
if self.top_left.x > other.top_left.x
|
self.top_left.y if self.top_left.y > other.top_left.y else other.top_left.y,
|
||||||
else other.top_left.x,
|
|
||||||
self.top_left.y
|
|
||||||
if self.top_left.y > other.top_left.y
|
|
||||||
else other.top_left.y,
|
|
||||||
)
|
)
|
||||||
intersect_bottom_right = Coordinate(
|
intersect_bottom_right = Coordinate(
|
||||||
self.bottom_right.x
|
self.bottom_right.x if self.bottom_right.x < other.bottom_right.x else other.bottom_right.x,
|
||||||
if self.bottom_right.x < other.bottom_right.x
|
self.bottom_right.y if self.bottom_right.y < other.bottom_right.y else other.bottom_right.y,
|
||||||
else other.bottom_right.x,
|
|
||||||
self.bottom_right.y
|
|
||||||
if self.bottom_right.y < other.bottom_right.y
|
|
||||||
else other.bottom_right.y,
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
intersect_top_left = Coordinate(
|
intersect_top_left = Coordinate(
|
||||||
self.top_left.x
|
self.top_left.x if self.top_left.x > other.top_left.x else other.top_left.x,
|
||||||
if self.top_left.x > other.top_left.x
|
self.top_left.y if self.top_left.y > other.top_left.y else other.top_left.y,
|
||||||
else other.top_left.x,
|
self.top_left.z if self.top_left.z > other.top_left.z else other.top_left.z,
|
||||||
self.top_left.y
|
|
||||||
if self.top_left.y > other.top_left.y
|
|
||||||
else other.top_left.y,
|
|
||||||
self.top_left.z
|
|
||||||
if self.top_left.z > other.top_left.z
|
|
||||||
else other.top_left.z,
|
|
||||||
)
|
)
|
||||||
intersect_bottom_right = Coordinate(
|
intersect_bottom_right = Coordinate(
|
||||||
self.bottom_right.x
|
self.bottom_right.x if self.bottom_right.x < other.bottom_right.x else other.bottom_right.x,
|
||||||
if self.bottom_right.x < other.bottom_right.x
|
self.bottom_right.y if self.bottom_right.y < other.bottom_right.y else other.bottom_right.y,
|
||||||
else other.bottom_right.x,
|
self.bottom_right.z if self.bottom_right.z < other.bottom_right.z else other.bottom_right.z,
|
||||||
self.bottom_right.y
|
|
||||||
if self.bottom_right.y < other.bottom_right.y
|
|
||||||
else other.bottom_right.y,
|
|
||||||
self.bottom_right.z
|
|
||||||
if self.bottom_right.z < other.bottom_right.z
|
|
||||||
else other.bottom_right.z,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if intersect_top_left <= intersect_bottom_right:
|
if intersect_top_left <= intersect_bottom_right:
|
||||||
@ -474,8 +411,16 @@ 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:
|
||||||
raise NotImplementedError("3D Lines are hard(er)")
|
raise NotImplementedError("3D Lines are hard(er)")
|
||||||
self.start = start
|
self.start, self.end = minmax(start, end)
|
||||||
self.end = end
|
|
||||||
|
def is_horizontal(self) -> bool:
|
||||||
|
return self.start[1] == self.end[1]
|
||||||
|
|
||||||
|
def is_vertical(self) -> bool:
|
||||||
|
return self.start[0] == self.end[0]
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
def contains(self, point: Coordinate | tuple) -> bool:
|
def contains(self, point: Coordinate | tuple) -> bool:
|
||||||
return isclose(
|
return isclose(
|
||||||
@ -513,3 +458,15 @@ class Line:
|
|||||||
return ret
|
return ret
|
||||||
else:
|
else:
|
||||||
raise ValueError("intersection out of bounds")
|
raise ValueError("intersection out of bounds")
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash((self.start, self.end))
|
||||||
|
|
||||||
|
def __lt__(self, other: Line) -> bool:
|
||||||
|
return self.start < other.start
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Line({self.start} -> {self.end})"
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return str(self)
|
||||||
|
|||||||
59
src/tools/visualization.py
Normal file
59
src/tools/visualization.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
import tkinter as tk
|
||||||
|
from .coordinate import Line, Coordinate
|
||||||
|
|
||||||
|
|
||||||
|
class Window:
|
||||||
|
def __init__(self, width: int = 1280, height: int = 1024, bg_color: str = "black", fg_color: str = "white"):
|
||||||
|
self.width, self.height = width, height
|
||||||
|
self.bg_color, self.fg_color = bg_color, fg_color
|
||||||
|
|
||||||
|
self.__tk_root = tk.Tk()
|
||||||
|
self.__canvas = tk.Canvas(master=self.__tk_root, width=width, height=height, bg=bg_color)
|
||||||
|
self.__canvas.pack(fill=tk.BOTH, expand=tk.YES)
|
||||||
|
self.__canvas.bind("<MouseWheel>", self._zoom)
|
||||||
|
self.__canvas.bind("<ButtonPress-1>", self._scroll_start)
|
||||||
|
self.__canvas.bind("<B1-Motion>", self._scroll_move)
|
||||||
|
|
||||||
|
self.__boundary_box = [0, 0, 0, 0]
|
||||||
|
|
||||||
|
def _update_boundaries(self, coord: Coordinate | tuple[int, int]) -> None:
|
||||||
|
if coord[0] < self.__boundary_box[0]:
|
||||||
|
self.__boundary_box[0] = coord[0]
|
||||||
|
elif coord[0] > self.__boundary_box[2]:
|
||||||
|
self.__boundary_box[2] = coord[0]
|
||||||
|
|
||||||
|
if coord[1] < self.__boundary_box[1]:
|
||||||
|
self.__boundary_box[1] = coord[1]
|
||||||
|
elif coord[1] > self.__boundary_box[3]:
|
||||||
|
self.__boundary_box[3] = coord[1]
|
||||||
|
|
||||||
|
def _zoom(self, event: tk.Event) -> None:
|
||||||
|
amount = 0.95 if event.delta < 0 else 1.05
|
||||||
|
self.__canvas.scale(tk.ALL, 0, 0, amount, amount)
|
||||||
|
|
||||||
|
def _scroll_start(self, event: tk.Event) -> None:
|
||||||
|
self.__canvas.scan_mark(event.x, event.y)
|
||||||
|
|
||||||
|
def _scroll_move(self, event: tk.Event) -> None:
|
||||||
|
self.__canvas.scan_dragto(event.x, event.y, gain=1)
|
||||||
|
|
||||||
|
def clear(self) -> None:
|
||||||
|
self.__canvas.delete(tk.ALL)
|
||||||
|
|
||||||
|
def draw_line(self, line: Line) -> None:
|
||||||
|
self.__canvas.create_line(line.start.x, line.start.y, line.end.x, line.end.y, fill=self.fg_color, width=1)
|
||||||
|
self._update_boundaries(line.start)
|
||||||
|
self._update_boundaries(line.end)
|
||||||
|
|
||||||
|
def realign(self) -> None:
|
||||||
|
print(self.__boundary_box)
|
||||||
|
if self.__boundary_box[0] < 0:
|
||||||
|
self.__canvas.move(tk.ALL, abs(self.__boundary_box[0]) + 10, 0)
|
||||||
|
if self.__boundary_box[1] < 0:
|
||||||
|
self.__canvas.move(tk.ALL, 0, abs(self.__boundary_box[1]) + 10)
|
||||||
|
self.__canvas.update()
|
||||||
|
|
||||||
|
def done(self) -> None:
|
||||||
|
self.realign()
|
||||||
|
self.__canvas.mainloop()
|
||||||
Loading…
Reference in New Issue
Block a user