From 3c4a908fd5001e047092680de7329efde64730a3 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Tue, 14 Apr 2020 15:51:28 -0700 Subject: [PATCH] updates to support multiple links between nodes in pygui, initially handling multiple wireless links --- daemon/core/api/grpc/events.py | 1 + daemon/core/api/grpc/grpcutils.py | 1 + daemon/core/gui/coreclient.py | 10 +++- daemon/core/gui/graph/edges.py | 85 +++++++++++++++++++++++---- daemon/core/gui/graph/graph.py | 19 ++++-- daemon/proto/core/api/grpc/core.proto | 1 + 6 files changed, 97 insertions(+), 20 deletions(-) diff --git a/daemon/core/api/grpc/events.py b/daemon/core/api/grpc/events.py index c4317e2e..1d766424 100644 --- a/daemon/core/api/grpc/events.py +++ b/daemon/core/api/grpc/events.py @@ -84,6 +84,7 @@ def handle_link_event(event: LinkData) -> core_pb2.LinkEvent: interface_one=interface_one, interface_two=interface_two, options=options, + network_id=event.network_id, ) return core_pb2.LinkEvent(message_type=event.message_type.value, link=link) diff --git a/daemon/core/api/grpc/grpcutils.py b/daemon/core/api/grpc/grpcutils.py index 0aa5a553..7b01517d 100644 --- a/daemon/core/api/grpc/grpcutils.py +++ b/daemon/core/api/grpc/grpcutils.py @@ -370,6 +370,7 @@ def convert_link(session: Session, link_data: LinkData) -> core_pb2.Link: interface_one=interface_one, interface_two=interface_two, options=options, + network_id=link_data.network_id, ) diff --git a/daemon/core/gui/coreclient.py b/daemon/core/gui/coreclient.py index 58efa55d..4a7cff64 100644 --- a/daemon/core/gui/coreclient.py +++ b/daemon/core/gui/coreclient.py @@ -207,13 +207,17 @@ class CoreClient: logging.debug("Link event: %s", event) node_one_id = event.link.node_one_id node_two_id = event.link.node_two_id + network_id = event.link.network_id canvas_node_one = self.canvas_nodes[node_one_id] canvas_node_two = self.canvas_nodes[node_two_id] - if event.message_type == core_pb2.MessageType.ADD: - self.app.canvas.add_wireless_edge(canvas_node_one, canvas_node_two) + self.app.canvas.add_wireless_edge( + canvas_node_one, canvas_node_two, network_id + ) elif event.message_type == core_pb2.MessageType.DELETE: - self.app.canvas.delete_wireless_edge(canvas_node_one, canvas_node_two) + self.app.canvas.delete_wireless_edge( + canvas_node_one, canvas_node_two, network_id + ) else: logging.warning("unknown link event: %s", event.message_type) diff --git a/daemon/core/gui/graph/edges.py b/daemon/core/gui/graph/edges.py index 1bd1bffd..728026db 100644 --- a/daemon/core/gui/graph/edges.py +++ b/daemon/core/gui/graph/edges.py @@ -17,6 +17,7 @@ EDGE_WIDTH = 3 EDGE_COLOR = "#ff0000" WIRELESS_WIDTH = 1.5 WIRELESS_COLOR = "#009933" +ARC_DISTANCE = 50 def interface_label(interface: core_pb2.Interface) -> str: @@ -28,8 +29,40 @@ def interface_label(interface: core_pb2.Interface) -> str: return label -def create_edge_token(src: int, dst: int) -> Tuple[int, ...]: - return tuple(sorted([src, dst])) +def create_edge_token(src: int, dst: int, network: int = None) -> Tuple[int, ...]: + values = [src, dst] + if network is not None: + values.append(network) + return tuple(sorted(values)) + + +def arc_edges(edges) -> None: + if not edges: + return + mid_index = len(edges) // 2 + if mid_index == 0: + arc_step = ARC_DISTANCE + else: + arc_step = ARC_DISTANCE / mid_index + # below edges + arc = 0 + for edge in edges[:mid_index]: + arc -= arc_step + edge.arc = arc + edge.redraw() + # mid edge + if len(edges) % 2 != 0: + arc = 0 + edge = edges[mid_index] + edge.arc = arc + edge.redraw() + mid_index += 1 + # above edges + arc = 0 + for edge in edges[mid_index:]: + arc += arc_step + edge.arc = arc + edge.redraw() class Edge: @@ -49,21 +82,40 @@ class Edge: def create_token(cls, src: int, dst: int) -> Tuple[int, ...]: return tuple(sorted([src, dst])) - def _get_midpoint( + def _get_arcpoint( self, src_pos: Tuple[float, float], dst_pos: Tuple[float, float] ) -> Tuple[float, float]: src_x, src_y = src_pos dst_x, dst_y = dst_pos - t = math.atan2(dst_y - src_y, dst_x - src_y) - x_mp = (src_x + dst_x) / 2 + self.arc * math.sin(t) - y_mp = (src_y + dst_y) / 2 - self.arc * math.cos(t) - return x_mp, y_mp + mp_x = (src_x + dst_x) / 2 + mp_y = (src_y + dst_y) / 2 + slope_denominator = src_x - dst_x + slope_numerator = src_y - dst_y + # vertical line + if slope_denominator == 0: + return mp_x + self.arc, mp_y + # horizontal line + if slope_numerator == 0: + return mp_x, mp_y + self.arc + # everything else + m = slope_numerator / slope_denominator + perp_m = -1 / m + b = mp_y - (perp_m * mp_x) + # get arc x and y + offset = math.sqrt(self.arc ** 2 / (1 + (1 / m ** 2))) + arc_x = mp_x + if self.arc >= 0: + arc_x += offset + else: + arc_x -= offset + arc_y = (perp_m * arc_x) + b + return arc_x, arc_y def draw(self, src_pos: Tuple[float, float], dst_pos: Tuple[float, float]) -> None: - mid_pos = self._get_midpoint(src_pos, dst_pos) + arc_pos = self._get_arcpoint(src_pos, dst_pos) self.id = self.canvas.create_line( *src_pos, - *mid_pos, + *arc_pos, *dst_pos, smooth=True, tags=self.tag, @@ -71,6 +123,12 @@ class Edge: fill=self.color, ) + def redraw(self): + width = self.width * self.canvas.app.app_scale + self.canvas.itemconfig(self.id, width=width, fill=self.color) + src_x, src_y, _, _, _, _ = self.canvas.coords(self.id) + self.move_src(src_x, src_y) + def move_node(self, node_id: int, x: float, y: float) -> None: if self.src == node_id: self.move_src(x, y) @@ -81,15 +139,15 @@ class Edge: dst_pos = (x, y) src_x, src_y, _, _, _, _ = self.canvas.coords(self.id) src_pos = (src_x, src_y) - mid_pos = self._get_midpoint(src_pos, dst_pos) - self.canvas.coords(self.id, *src_pos, *mid_pos, *dst_pos) + arc_pos = self._get_arcpoint(src_pos, dst_pos) + self.canvas.coords(self.id, *src_pos, *arc_pos, *dst_pos) def move_src(self, x: float, y: float) -> None: src_pos = (x, y) _, _, _, _, dst_x, dst_y = self.canvas.coords(self.id) dst_pos = (dst_x, dst_y) - mid_pos = self._get_midpoint(src_pos, dst_pos) - self.canvas.coords(self.id, *src_pos, *mid_pos, *dst_pos) + arc_pos = self._get_arcpoint(src_pos, dst_pos) + self.canvas.coords(self.id, *src_pos, *arc_pos, *dst_pos) def delete(self) -> None: self.canvas.delete(self.id) @@ -201,6 +259,7 @@ class CanvasEdge(Edge): ) def redraw(self) -> None: + super().redraw() label_one, label_two = self.create_labels() self.canvas.itemconfig(self.text_src, text=label_one) self.canvas.itemconfig(self.text_dst, text=label_two) diff --git a/daemon/core/gui/graph/graph.py b/daemon/core/gui/graph/graph.py index 4c0d1abf..5f672ead 100644 --- a/daemon/core/gui/graph/graph.py +++ b/daemon/core/gui/graph/graph.py @@ -11,6 +11,7 @@ from core.gui.graph.edges import ( EDGE_WIDTH, CanvasEdge, CanvasWirelessEdge, + arc_edges, create_edge_token, ) from core.gui.graph.enums import GraphMode, ScaleOption @@ -198,11 +199,13 @@ class CanvasGraph(tk.Canvas): self.tag_lower(tags.GRIDLINE) self.tag_lower(self.grid) - def add_wireless_edge(self, src: CanvasNode, dst: CanvasNode): + def add_wireless_edge( + self, src: CanvasNode, dst: CanvasNode, network_id: int = None + ): """ add a wireless edge between 2 canvas nodes """ - token = create_edge_token(src.id, dst.id) + token = create_edge_token(src.id, dst.id, network_id) src_pos = self.coords(src.id) dst_pos = self.coords(dst.id) edge = CanvasWirelessEdge(self, src.id, dst.id, src_pos, dst_pos, token) @@ -211,13 +214,21 @@ class CanvasGraph(tk.Canvas): dst.wireless_edges.add(edge) self.tag_raise(src.id) self.tag_raise(dst.id) + # update arcs when there are multiple links + common_edges = list(src.wireless_edges & dst.wireless_edges) + arc_edges(common_edges) - def delete_wireless_edge(self, src: CanvasNode, dst: CanvasNode): - token = create_edge_token(src.id, dst.id) + def delete_wireless_edge( + self, src: CanvasNode, dst: CanvasNode, network_id: int = None + ): + token = create_edge_token(src.id, dst.id, network_id) edge = self.wireless_edges.pop(token) edge.delete() src.wireless_edges.remove(edge) dst.wireless_edges.remove(edge) + # update arcs when there are multiple links + common_edges = list(src.wireless_edges & dst.wireless_edges) + arc_edges(common_edges) def draw_session(self, session: core_pb2.Session): """ diff --git a/daemon/proto/core/api/grpc/core.proto b/daemon/proto/core/api/grpc/core.proto index e7bac450..4c0f3db6 100644 --- a/daemon/proto/core/api/grpc/core.proto +++ b/daemon/proto/core/api/grpc/core.proto @@ -686,6 +686,7 @@ message Link { Interface interface_one = 4; Interface interface_two = 5; LinkOptions options = 6; + int32 network_id = 7; } message LinkOptions {