added drag-selection

This commit is contained in:
Toby Lane 2024-07-01 23:15:54 +02:00
parent 77f11a7440
commit efe158f83f
2 changed files with 71 additions and 13 deletions

View File

@ -39,17 +39,17 @@ class Editor:
self.ZOOMS self.ZOOMS
)) ))
self.is_creating_node: bool = False self.is_creating_node: bool = False
self.typing: bool = False
self.state: State = State.STOPPING self.state: State = State.STOPPING
self.graph = Graph() self.graph = Graph()
self.typing_text: str = "" self.typing_text: str = ""
self.node_candidate_pos: tuple[int, int] = None self.node_candidate_pos: tuple[int, int] = None
self.node_radius: int = 10 self.node_radius: int = 10
self.line_size: int = (int) (self.node_radius / 5) self.line_size: int = int(self.node_radius / 5)
self.edge_detect_radius: int = 3 * self.line_size self.edge_detect_radius: int = 3 * self.line_size
self.selected_nodes: list[int] = [] self.selected_nodes: list[int] = []
self.selected_edges: list[int] = [] self.selected_edges: list[int] = []
self.previously_created_nodes: list[int] = [] self.previously_created_nodes: list[int] = []
self.selection_rectangle: list[tuple[int, int], tuple[int, int]] = None
def mainloop(self) -> None: def mainloop(self) -> None:
self.state = State.LOADING self.state = State.LOADING
@ -61,6 +61,8 @@ class Editor:
if not self.image_handler.loading: if not self.image_handler.loading:
self.state = State.RUNNING self.state = State.RUNNING
elif self.state == State.RUNNING: elif self.state == State.RUNNING:
if self.selection_rectangle != None:
self.expand_selection_rect()
self.render() self.render()
self.clock.tick(30) self.clock.tick(30)
@ -75,9 +77,8 @@ class Editor:
self.width = event.x self.width = event.x
self.height = event.y self.height = event.y
elif event.type == pygame.KEYDOWN: elif event.type == pygame.KEYDOWN:
if self.typing: if self.is_creating_node:
if event.key == pygame.K_ESCAPE: if event.key == pygame.K_ESCAPE:
self.typing = False
self.is_creating_node = False self.is_creating_node = False
self.node_candidate_pos = None self.node_candidate_pos = None
self.typing_text = "" self.typing_text = ""
@ -106,12 +107,13 @@ class Editor:
elif event.button == 1: elif event.button == 1:
if keys[pygame.K_LCTRL]: if keys[pygame.K_LCTRL]:
self.left_drag_pos = event.pos self.left_drag_pos = event.pos
elif keys[pygame.K_LALT]:
self.create_selection_rect(keys[pygame.K_LSHIFT] or keys[pygame.K_RSHIFT])
else: else:
self.select_object(keys[pygame.K_LSHIFT] or keys[pygame.K_RSHIFT]) self.select_object(keys[pygame.K_LSHIFT] or keys[pygame.K_RSHIFT])
elif event.button == 3: elif event.button == 3:
self.node_candidate_pos = self.screen_to_world(event.pos[0], event.pos[1]) self.node_candidate_pos = self.screen_to_world(event.pos[0], event.pos[1])
self.is_creating_node = True self.is_creating_node = True
self.typing = True
elif event.button == 4: elif event.button == 4:
self.zoom_in() self.zoom_in()
elif event.button == 5: elif event.button == 5:
@ -120,7 +122,10 @@ class Editor:
if event.button == 2: if event.button == 2:
self.mid_drag_pos = None self.mid_drag_pos = None
elif event.button == 1: elif event.button == 1:
self.left_drag_pos = None if keys[pygame.K_LCTRL]:
self.left_drag_pos = None
elif self.selection_rectangle != None:
self.release_selection_rect(keys[pygame.K_LSHIFT] or keys[pygame.K_RSHIFT])
if keys[pygame.K_LEFT]: if keys[pygame.K_LEFT]:
self.center[0] -= 4 / self.zoom self.center[0] -= 4 / self.zoom
@ -183,6 +188,8 @@ class Editor:
self.render_graph() self.render_graph()
self.render_selection_rect()
pygame.draw.line(self.win, (150, 150, 150), [w2 - self.CROSSHAIR_SIZE, h2], [w2 + self.CROSSHAIR_SIZE, h2]) pygame.draw.line(self.win, (150, 150, 150), [w2 - self.CROSSHAIR_SIZE, h2], [w2 + self.CROSSHAIR_SIZE, h2])
pygame.draw.line(self.win, (150, 150, 150), [w2, h2 - self.CROSSHAIR_SIZE], [w2, h2 + self.CROSSHAIR_SIZE]) pygame.draw.line(self.win, (150, 150, 150), [w2, h2 - self.CROSSHAIR_SIZE], [w2, h2 + self.CROSSHAIR_SIZE])
self.render_zoom_slider() self.render_zoom_slider()
@ -262,9 +269,11 @@ class Editor:
hover_index, is_node = self.get_hover_object() hover_index, is_node = self.get_hover_object()
if is_node: if is_node:
self.render_nodes() self.render_nodes()
self.render_hover_node(hover_index) if self.selection_rectangle == None:
self.render_hover_node(hover_index)
else: else:
self.render_hover_edge(hover_index) if self.selection_rectangle == None:
self.render_hover_edge(hover_index)
self.render_nodes() self.render_nodes()
def render_edges(self) -> None: def render_edges(self) -> None:
@ -296,6 +305,16 @@ class Editor:
pygame.draw.line(self.win, (0, 0, 0), self.world_to_screen(node_1.x, node_1.z), self.world_to_screen(node_2.x, node_2.z), self.edge_detect_radius) pygame.draw.line(self.win, (0, 0, 0), self.world_to_screen(node_1.x, node_1.z), self.world_to_screen(node_2.x, node_2.z), self.edge_detect_radius)
color = (0, 255, 255) if edge_index in self.selected_edges else (255, 0, 0) color = (0, 255, 255) if edge_index in self.selected_edges else (255, 0, 0)
pygame.draw.line(self.win, color, self.world_to_screen(node_1.x, node_1.z), self.world_to_screen(node_2.x, node_2.z), self.line_size) pygame.draw.line(self.win, color, self.world_to_screen(node_1.x, node_1.z), self.world_to_screen(node_2.x, node_2.z), self.line_size)
def render_selection_rect(self):
rect = self.selection_rectangle
if rect != None:
left = min(rect[0][0], rect[1][0])
top = min(rect[0][1], rect[1][1])
width = abs(rect[0][0] - rect[1][0])
height = abs(rect[0][1] - rect[1][1])
pygame.draw.rect(self.win, (32, 32, 32), pygame.Rect(left, top, width, height), self.line_size)
def set_zoom(self, zoom_i: int) -> None: def set_zoom(self, zoom_i: int) -> None:
self.zoom_i = max(0, min(len(self.ZOOMS) - 1, zoom_i)) self.zoom_i = max(0, min(len(self.ZOOMS) - 1, zoom_i))
@ -383,7 +402,6 @@ class Editor:
self.graph.add_node(self.node_candidate_pos[0], self.node_candidate_pos[1], self.typing_text) self.graph.add_node(self.node_candidate_pos[0], self.node_candidate_pos[1], self.typing_text)
self.typing_text = "" self.typing_text = ""
self.node_candidate_pos = None self.node_candidate_pos = None
self.typing = False
self.is_creating_node = False self.is_creating_node = False
if len(self.selected_nodes) == 1: if len(self.selected_nodes) == 1:
self.previously_created_nodes.append(self.selected_nodes[0]) self.previously_created_nodes.append(self.selected_nodes[0])
@ -479,6 +497,41 @@ class Editor:
if n != 0: if n != 0:
self.selected_nodes.append(self.previously_created_nodes[n - 1]) self.selected_nodes.append(self.previously_created_nodes[n - 1])
self.previously_created_nodes.pop() self.previously_created_nodes.pop()
def create_selection_rect(self, shifting = False):
if not shifting:
self.clear_selection()
self.previously_created_nodes = []
mouse_pos = pygame.mouse.get_pos()
self.selection_rectangle = [mouse_pos, mouse_pos]
def expand_selection_rect(self):
self.selection_rectangle[1] = pygame.mouse.get_pos()
def release_selection_rect(self, shifting = False):
if not shifting:
self.clear_selection()
self.previously_created_nodes = []
rect = self.selection_rectangle
left = min(rect[0][0], rect[1][0])
top = min(rect[0][1], rect[1][1])
right = max(rect[0][0], rect[1][0])
bottom = max(rect[0][1], rect[1][1])
for node in self.graph.nodes:
pos = self.world_to_screen(node.x, node.z)
if left <= pos[0] <= right and top <= pos[1] <= bottom:
if node.index not in self.selected_nodes:
self.selected_nodes.append(node.index)
print(left, "<=", pos[0], "<=", right)
print(top, "<=", pos[1], "<=", bottom)
for edge in self.graph.edges:
pos = self.world_to_screen(*self.graph.get_edge_center(edge.index))
if left <= pos[0] <= right and top <= pos[1] <= bottom:
if edge.index not in self.selected_edges:
self.selected_edges.append(edge.index)
print(left, "<=", pos[0], "<=", right)
print(top, "<=", pos[1], "<=", bottom)
self.selection_rectangle = None
@ -489,5 +542,4 @@ class Editor:
class State(Enum): class State(Enum):
STOPPING = auto() STOPPING = auto()
LOADING = auto() LOADING = auto()
RUNNING = auto() RUNNING = auto()
CREATING_NODE = auto()

View File

@ -9,8 +9,8 @@ class Graph:
self.edges: list[Edge] = [] self.edges: list[Edge] = []
self.nodes: list[Node] = [] self.nodes: list[Node] = []
def add_node(self, x: int, y: int, name: str) -> None: def add_node(self, x: int, z: int, name: str) -> None:
self.nodes.append(Node(x, y, name, len(self.nodes))) self.nodes.append(Node(x, z, name, len(self.nodes)))
def add_edge(self, start_index: int, end_index: int, length: float) -> None: def add_edge(self, start_index: int, end_index: int, length: float) -> None:
self.edges.append(Edge(start_index, end_index, length, len(self.edges))) self.edges.append(Edge(start_index, end_index, length, len(self.edges)))
@ -47,6 +47,12 @@ class Graph:
def get_edge_nodes(self, edge: Edge) -> tuple[Node, Node]: def get_edge_nodes(self, edge: Edge) -> tuple[Node, Node]:
return self.nodes[edge.start], self.nodes[edge.end] return self.nodes[edge.start], self.nodes[edge.end]
def get_edge_center(self, edge_index: int) -> tuple[float, float]:
edge = self.edges[edge_index]
start_n = self.nodes[edge.start]
end_n = self.nodes[edge.end]
return (start_n.x + end_n.x) / 2, (start_n.z + end_n.z) / 2
def edges_adjacent_to(self, node_i: int) -> Iterator[Edge]: def edges_adjacent_to(self, node_i: int) -> Iterator[Edge]:
return filter(lambda e: e.start == node_i or e.end == node_i, self.edges) return filter(lambda e: e.start == node_i or e.end == node_i, self.edges)