updates to support multiple links between nodes in pygui, initially handling multiple wireless links
This commit is contained in:
parent
cd8157eff7
commit
3c4a908fd5
6 changed files with 97 additions and 20 deletions
daemon
|
@ -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)
|
||||
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -686,6 +686,7 @@ message Link {
|
|||
Interface interface_one = 4;
|
||||
Interface interface_two = 5;
|
||||
LinkOptions options = 6;
|
||||
int32 network_id = 7;
|
||||
}
|
||||
|
||||
message LinkOptions {
|
||||
|
|
Loading…
Add table
Reference in a new issue