From efe158f83fc7d36d7335c2e01d44bc4d64101e52 Mon Sep 17 00:00:00 2001 From: Toby Lane Date: Mon, 1 Jul 2024 23:15:54 +0200 Subject: [PATCH] added drag-selection --- src/editor.py | 74 +++++++++++++++++++++++++++++++++++++++------- src/graph/graph.py | 10 +++++-- 2 files changed, 71 insertions(+), 13 deletions(-) diff --git a/src/editor.py b/src/editor.py index 0549d90..1d3554c 100644 --- a/src/editor.py +++ b/src/editor.py @@ -39,17 +39,17 @@ class Editor: self.ZOOMS )) self.is_creating_node: bool = False - self.typing: bool = False self.state: State = State.STOPPING self.graph = Graph() self.typing_text: str = "" self.node_candidate_pos: tuple[int, int] = None 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.selected_nodes: list[int] = [] self.selected_edges: list[int] = [] self.previously_created_nodes: list[int] = [] + self.selection_rectangle: list[tuple[int, int], tuple[int, int]] = None def mainloop(self) -> None: self.state = State.LOADING @@ -61,6 +61,8 @@ class Editor: if not self.image_handler.loading: self.state = State.RUNNING elif self.state == State.RUNNING: + if self.selection_rectangle != None: + self.expand_selection_rect() self.render() self.clock.tick(30) @@ -75,9 +77,8 @@ class Editor: self.width = event.x self.height = event.y elif event.type == pygame.KEYDOWN: - if self.typing: + if self.is_creating_node: if event.key == pygame.K_ESCAPE: - self.typing = False self.is_creating_node = False self.node_candidate_pos = None self.typing_text = "" @@ -106,12 +107,13 @@ class Editor: elif event.button == 1: if keys[pygame.K_LCTRL]: 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: self.select_object(keys[pygame.K_LSHIFT] or keys[pygame.K_RSHIFT]) elif event.button == 3: self.node_candidate_pos = self.screen_to_world(event.pos[0], event.pos[1]) self.is_creating_node = True - self.typing = True elif event.button == 4: self.zoom_in() elif event.button == 5: @@ -120,7 +122,10 @@ class Editor: if event.button == 2: self.mid_drag_pos = None 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]: self.center[0] -= 4 / self.zoom @@ -183,6 +188,8 @@ class Editor: 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, h2 - self.CROSSHAIR_SIZE], [w2, h2 + self.CROSSHAIR_SIZE]) self.render_zoom_slider() @@ -262,9 +269,11 @@ class Editor: hover_index, is_node = self.get_hover_object() if is_node: self.render_nodes() - self.render_hover_node(hover_index) + if self.selection_rectangle == None: + self.render_hover_node(hover_index) else: - self.render_hover_edge(hover_index) + if self.selection_rectangle == None: + self.render_hover_edge(hover_index) self.render_nodes() 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) 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) + + 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: 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.typing_text = "" self.node_candidate_pos = None - self.typing = False self.is_creating_node = False if len(self.selected_nodes) == 1: self.previously_created_nodes.append(self.selected_nodes[0]) @@ -479,6 +497,41 @@ class Editor: if n != 0: self.selected_nodes.append(self.previously_created_nodes[n - 1]) 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): STOPPING = auto() LOADING = auto() - RUNNING = auto() - CREATING_NODE = auto() \ No newline at end of file + RUNNING = auto() \ No newline at end of file diff --git a/src/graph/graph.py b/src/graph/graph.py index bd8c926..763629a 100644 --- a/src/graph/graph.py +++ b/src/graph/graph.py @@ -9,8 +9,8 @@ class Graph: self.edges: list[Edge] = [] self.nodes: list[Node] = [] - def add_node(self, x: int, y: int, name: str) -> None: - self.nodes.append(Node(x, y, name, len(self.nodes))) + def add_node(self, x: int, z: int, name: str) -> None: + self.nodes.append(Node(x, z, name, len(self.nodes))) 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))) @@ -47,6 +47,12 @@ class Graph: def get_edge_nodes(self, edge: Edge) -> tuple[Node, Node]: 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]: return filter(lambda e: e.start == node_i or e.end == node_i, self.edges)