-
Notifications
You must be signed in to change notification settings - Fork 12
/
hexagon.py
117 lines (97 loc) · 4.07 KB
/
hexagon.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# -*- coding: utf-8 -*-
"""
Created on Sun Jan 23 14:07:18 2022
@author: richa
"""
from __future__ import annotations
import math
from dataclasses import dataclass
from typing import List
from typing import Tuple
import pygame
@dataclass
class HexagonTile:
"""Hexagon class"""
radius: float
position: Tuple[float, float]
colour: Tuple[int, ...]
highlight_offset: int = 3
max_highlight_ticks: int = 15
def __post_init__(self):
self.vertices = self.compute_vertices()
self.highlight_tick = 0
def update(self):
"""Updates tile highlights"""
if self.highlight_tick > 0:
self.highlight_tick -= 1
def compute_vertices(self) -> List[Tuple[float, float]]:
"""Returns a list of the hexagon's vertices as x, y tuples"""
# pylint: disable=invalid-name
x, y = self.position
half_radius = self.radius / 2
minimal_radius = self.minimal_radius
return [
(x, y),
(x - minimal_radius, y + half_radius),
(x - minimal_radius, y + 3 * half_radius),
(x, y + 2 * self.radius),
(x + minimal_radius, y + 3 * half_radius),
(x + minimal_radius, y + half_radius),
]
def compute_neighbours(self, hexagons: List[HexagonTile]) -> List[HexagonTile]:
"""Returns hexagons whose centres are two minimal radiuses away from self.centre"""
# could cache results for performance
return [hexagon for hexagon in hexagons if self.is_neighbour(hexagon)]
def collide_with_point(self, point: Tuple[float, float]) -> bool:
"""Returns True if distance from centre to point is less than horizontal_length"""
return math.dist(point, self.centre) < self.minimal_radius
def is_neighbour(self, hexagon: HexagonTile) -> bool:
"""Returns True if hexagon centre is approximately
2 minimal radiuses away from own centre
"""
distance = math.dist(hexagon.centre, self.centre)
return math.isclose(distance, 2 * self.minimal_radius, rel_tol=0.05)
def render(self, screen) -> None:
"""Renders the hexagon on the screen"""
pygame.draw.polygon(screen, self.highlight_colour, self.vertices)
def render_highlight(self, screen, border_colour) -> None:
"""Draws a border around the hexagon with the specified colour"""
self.highlight_tick = self.max_highlight_ticks
# pygame.draw.polygon(screen, self.highlight_colour, self.vertices)
pygame.draw.aalines(screen, border_colour, closed=True, points=self.vertices)
@property
def centre(self) -> Tuple[float, float]:
"""Centre of the hexagon"""
x, y = self.position # pylint: disable=invalid-name
return (x, y + self.radius)
@property
def minimal_radius(self) -> float:
"""Horizontal length of the hexagon"""
# https://en.wikipedia.org/wiki/Hexagon#Parameters
return self.radius * math.cos(math.radians(30))
@property
def highlight_colour(self) -> Tuple[int, ...]:
"""Colour of the hexagon tile when rendering highlight"""
offset = self.highlight_offset * self.highlight_tick
brighten = lambda x, y: x + y if x + y < 255 else 255
return tuple(brighten(x, offset) for x in self.colour)
class FlatTopHexagonTile(HexagonTile):
def compute_vertices(self) -> List[Tuple[float, float]]:
"""Returns a list of the hexagon's vertices as x, y tuples"""
# pylint: disable=invalid-name
x, y = self.position
half_radius = self.radius / 2
minimal_radius = self.minimal_radius
return [
(x, y),
(x - half_radius, y + minimal_radius),
(x, y + 2 * minimal_radius),
(x + self.radius, y + 2 * minimal_radius),
(x + 3 * half_radius, y + minimal_radius),
(x + self.radius, y),
]
@property
def centre(self) -> Tuple[float, float]:
"""Centre of the hexagon"""
x, y = self.position # pylint: disable=invalid-name
return (x + self.radius / 2, y + self.minimal_radius)