-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathhash_grid_arcade_example.py
More file actions
125 lines (98 loc) · 3.66 KB
/
hash_grid_arcade_example.py
File metadata and controls
125 lines (98 loc) · 3.66 KB
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
118
119
120
121
122
123
124
125
import dataclasses
import uuid
from typing import Callable
from misc.hashgrid import HashGrid
import arcade as arc
from shapely import geometry as geo
from misc.vector import Vector
class BoxBounds:
__slots__ = ('box', 'min_x', 'min_y', 'max_x', 'max_y', 'uuid')
def __init__(self, min_x, min_y, max_x, max_y):
self.min_x = min_x
self.min_y = min_y
self.max_x = max_x
self.max_y = max_y
self.box = geo.box(min_x, min_y, max_x, max_y)
self.uuid = uuid.uuid4()
@property
def bottom_left(self):
return Vector(self.min_x, self.min_y)
@property
def top_left(self):
return Vector(self.min_x, self.max_y)
@property
def bottom_right(self):
return Vector(self.max_x, self.min_y)
@property
def top_right(self):
return Vector(self.max_x, self.max_y)
@property
def center(self):
return Vector((self.min_x + self.max_x) / 2, (self.min_y + self.max_y) / 2)
@property
def size(self):
return Vector(self.max_x - self.min_x, self.max_y - self.min_y)
def __contains__(self, item):
match item:
case Vector():
return self.box.intersects(geo.Point(item.x, item.y))
case BoxBounds() | geo.Polygon() | geo.Point():
return self.box.intersects(item)
case _:
raise ValueError(f'invalid type for __contains__ in {self.__class__.__name__}: {type(item)}')
def __hash__(self):
return hash((int(self.min_x), int(self.max_x), int(self.min_y), int(self.max_y), self.uuid))
def create_box(v1, v2):
return BoxBounds(
min(v1.x, v2.x),
min(v1.y, v2.y),
max(v1.x, v2.x),
max(v1.y, v2.y)
)
def create_box_args(x1, y1, x2, y2):
return BoxBounds(
min(x1, x2),
min(y1, y2),
max(x1, x2),
max(y1, y2)
)
def render_bounds(bb: BoxBounds, color, thickness=1):
arc.draw_rectangle_outline(bb.center.x, bb.center.y, bb.size.x, bb.size.y, color, border_width=thickness)
@dataclasses.dataclass(unsafe_hash=True)
class Interactable:
id: str = dataclasses.field(hash=True)
bounds: BoxBounds = dataclasses.field(hash=True)
render: Callable = dataclasses.field(hash=False, default=None)
@property
def bounds_tuple(self):
return self.bounds.bottom_left, self.bounds.top_right
def __repr__(self):
return f'<{type(self).__name__} id={self.id} size={self.bounds.size}>'
class View(arc.View):
def __init__(self):
super().__init__()
arc.set_background_color((0, 0, 0))
self.grid = HashGrid(100)
self.interactables: list[Interactable] = [
Interactable('0', create_box_args(100, 100, 200, 200)),
Interactable('1', create_box_args(300, 400, 500, 700)),
Interactable('2', create_box_args(400, 600, 0, 100)),
Interactable('3', create_box_args(200, 800, 400, 300)),
Interactable('3', create_box_args(300, 400, 600, 0)),
]
for interactable in self.interactables:
self.grid.add(interactable, interactable.bounds_tuple)
print(*self.grid.get_all_objects())
@property
def mouse(self):
return Vector(self.window._mouse_x, self.window._mouse_y)
def on_draw(self):
arc.start_render()
for obj in self.interactables:
render_bounds(obj.bounds, (155, 155, 0, 100))
for interactable in filter(lambda x: self.mouse in x.bounds, self.grid.get_objects_for_point(self.mouse)):
box = interactable.bounds
render_bounds(box, (255, 255, 0), 5)
win = arc.Window(width=800, height=600)
win.show_view(View())
win.run()