From dbc787bf61b8018c86b8db1590628a79089aff63 Mon Sep 17 00:00:00 2001 From: Razin Shaikh Date: Tue, 18 Jun 2024 18:19:42 +0100 Subject: [PATCH 1/5] trying to change the edge_map format --- zxlive/graphscene.py | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/zxlive/graphscene.py b/zxlive/graphscene.py index 0b0cfc7b..ca6c26ec 100644 --- a/zxlive/graphscene.py +++ b/zxlive/graphscene.py @@ -52,7 +52,7 @@ def __init__(self) -> None: self.setSceneRect(0, 0, 2*OFFSET_X, 2*OFFSET_Y) self.setBackgroundBrush(QBrush(QColor(255, 255, 255))) self.vertex_map: dict[VT, VItem] = {} - self.edge_map: dict[tuple[ET, int], EItem] = {} + self.edge_map: dict[ET, dict[int, EItem]] = {} @property def selected_vertices(self) -> Iterator[VT]: @@ -100,7 +100,7 @@ def update_graph(self, new: GraphT, select_new: bool = False) -> None: diff = GraphDiff(self.g, new) - removed_edges = set(diff.removed_edges) + removed_edges = diff.removed_edges for v in diff.removed_verts: v_item = self.vertex_map[v] @@ -109,19 +109,21 @@ def update_graph(self, new: GraphT, select_new: bool = False) -> None: for anim in v_item.active_animations.copy(): anim.stop() for s, t in self.g.incident_edges(v): + if s in diff.removed_verts and t in diff.removed_verts: + continue for e in self.g.edges(s, t): - removed_edges.add(e) + removed_edges.append(e) selected_vertices.discard(v) self.removeItem(v_item) for e in removed_edges: - edge_key = (e, self.g.graph[e[0]][e[1]].get_edge_count(e[2]) - 1) - e_item = self.edge_map[edge_key] + edge_idx = len(self.edge_map[e]) - 1 + e_item = self.edge_map[e][edge_idx] if e_item.selection_node: self.removeItem(e_item.selection_node) self.removeItem(e_item) - self.edge_map.pop(edge_key) + self.edge_map[e].pop(edge_idx) s, t = self.g.edge_st(e) self.update_edge_curves(s, t) @@ -143,10 +145,12 @@ def update_graph(self, new: GraphT, select_new: bool = False) -> None: for e, typ in diff.new_edges: s, t = self.g.edge_st(e) - cur_edge_type_idx = self.g.graph[s][t].get_edge_count(typ) - 1 e = (s,t,typ) + if e not in self.edge_map: + self.edge_map[e] = {} + idx = len(self.edge_map[e]) e_item = EItem(self, e, self.vertex_map[s], self.vertex_map[t]) - self.edge_map[(e, cur_edge_type_idx)] = e_item + self.edge_map[e][idx] = e_item self.update_edge_curves(s, t) self.addItem(e_item) self.addItem(e_item.selection_node) @@ -171,28 +175,27 @@ def update_graph(self, new: GraphT, select_new: bool = False) -> None: v_item.set_vitem_rotation() for e in diff.changed_edge_types: - for i in range(self.g.graph[e[0]][e[1]].get_edge_count(e[2])): - self.edge_map[(e, i)].refresh() + for i in self.edge_map[e]: + self.edge_map[e][i].refresh() self.select_vertices(selected_vertices) def update_edge_curves(self, s, t): edges = [] for e in set(self.g.edges(s, t)): - for i in range(self.g.graph[s][t].get_edge_count(e[2])): - edge_key = (e, i) - if edge_key in self.edge_map: - edges.append(edge_key) + for i in self.edge_map[e]: + edges.append(self.edge_map[e][i]) midpoint_index = 0.5 * (len(edges) - 1) for n, edge in enumerate(edges): - self.edge_map[edge].curve_distance = (n - midpoint_index) * 0.5 - self.edge_map[edge].refresh() + edge.curve_distance = (n - midpoint_index) * 0.5 + edge.refresh() def update_colors(self) -> None: for v in self.vertex_map.values(): v.refresh() for e in self.edge_map.values(): - e.refresh() + for ei in e.values(): + ei.refresh() def add_items(self) -> None: """Add QGraphicsItem's for all vertices and edges in the graph""" @@ -206,12 +209,12 @@ def add_items(self) -> None: self.edge_map = {} for e in set(self.g.edges()): s, t = self.g.edge_st(e) + self.edge_map[e] = {} for i in range(self.g.graph[s][t].get_edge_count(e[2])): ei = EItem(self, e, self.vertex_map[s], self.vertex_map[t]) self.addItem(ei) self.addItem(ei.selection_node) - edge_key = (e, i) - self.edge_map[edge_key] = ei + self.edge_map[e][i] = ei self.update_edge_curves(s, t) def select_all(self) -> None: From edcbdee84ca2969cc7afc95e1ec2dc3a18ab3be7 Mon Sep 17 00:00:00 2001 From: Razin Shaikh Date: Wed, 26 Jun 2024 16:21:19 +0100 Subject: [PATCH 2/5] GraphDiff from pyzx takes care of removing correct edges from commit 1593d31174dbb06f8ef1319230cb257abefd7186 --- zxlive/graphscene.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/zxlive/graphscene.py b/zxlive/graphscene.py index ca6c26ec..da871864 100644 --- a/zxlive/graphscene.py +++ b/zxlive/graphscene.py @@ -100,24 +100,16 @@ def update_graph(self, new: GraphT, select_new: bool = False) -> None: diff = GraphDiff(self.g, new) - removed_edges = diff.removed_edges - for v in diff.removed_verts: v_item = self.vertex_map[v] if v_item.phase_item: self.removeItem(v_item.phase_item) for anim in v_item.active_animations.copy(): anim.stop() - for s, t in self.g.incident_edges(v): - if s in diff.removed_verts and t in diff.removed_verts: - continue - for e in self.g.edges(s, t): - removed_edges.append(e) - selected_vertices.discard(v) self.removeItem(v_item) - for e in removed_edges: + for e in diff.removed_edges: edge_idx = len(self.edge_map[e]) - 1 e_item = self.edge_map[e][edge_idx] if e_item.selection_node: From 04839b8904de350a10d14090145af4cc0ab5980b Mon Sep 17 00:00:00 2001 From: Razin Shaikh Date: Wed, 26 Jun 2024 18:36:10 +0100 Subject: [PATCH 3/5] first step to self-loop --- zxlive/eitem.py | 8 ++++++-- zxlive/graphscene.py | 5 +---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/zxlive/eitem.py b/zxlive/eitem.py index bb19b340..92e646d6 100644 --- a/zxlive/eitem.py +++ b/zxlive/eitem.py @@ -142,10 +142,14 @@ def refresh(self) -> None: path.lineTo(self.mouse_pos) self.setPath(path) -def calculate_control_point(source_pos, target_pos, curve_distance): +def calculate_control_point(source_pos: QPointF, target_pos: QPointF, curve_distance: float): """Calculate the control point for the curve""" direction = target_pos - source_pos - direction /= sqrt(direction.x()**2 + direction.y()**2) # Normalize the direction + norm = sqrt(direction.x()**2 + direction.y()**2) + if norm == 0: + direction = QPointF(1, 0) + norm = 1. + direction = direction / norm perpendicular = QPointF(-direction.y(), direction.x()) midpoint = (source_pos + target_pos) / 2 offset = perpendicular * curve_distance * SCALE diff --git a/zxlive/graphscene.py b/zxlive/graphscene.py index da871864..bd11dd91 100644 --- a/zxlive/graphscene.py +++ b/zxlive/graphscene.py @@ -287,9 +287,6 @@ def add_edge(self, e: QGraphicsSceneMouseEvent) -> None: assert self._drag is not None self.removeItem(self._drag) for it in self.items(e.scenePos(), deviceTransform=QTransform()): - # TODO: Think about if we want to allow self loops here? - # For example, if had edge is selected this would mean that - # right clicking adds pi to the phase... - if isinstance(it, VItem) and it != self._drag.start: + if isinstance(it, VItem): self.edge_added.emit(self._drag.start.v, it.v) self._drag = None From ea5209054145dad032d365219918d7b1834a9665 Mon Sep 17 00:00:00 2001 From: Razin Shaikh Date: Wed, 26 Jun 2024 19:45:39 +0100 Subject: [PATCH 4/5] attempting to draw self-loop --- zxlive/eitem.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/zxlive/eitem.py b/zxlive/eitem.py index 92e646d6..7b3292a4 100644 --- a/zxlive/eitem.py +++ b/zxlive/eitem.py @@ -78,12 +78,20 @@ def refresh(self) -> None: self.setPen(QPen(pen)) path = QPainterPath() - control_point = calculate_control_point(self.s_item.pos(), self.t_item.pos(), self.curve_distance) - path.moveTo(self.s_item.pos()) - path.quadTo(control_point, self.t_item.pos()) + if self.s_item == self.t_item: + # self-loop + cd = self.curve_distance + 1 + path.moveTo(self.s_item.pos()) + path.cubicTo(self.s_item.pos() + QPointF(1, -1) * cd * SCALE, + self.s_item.pos() + QPointF(-1, -1) * cd * SCALE, + self.s_item.pos()) + curve_midpoint = self.s_item.pos() + QPointF(0, -0.75) * cd * SCALE + else: + control_point = calculate_control_point(self.s_item.pos(), self.t_item.pos(), self.curve_distance) + path.moveTo(self.s_item.pos()) + path.quadTo(control_point, self.t_item.pos()) + curve_midpoint = self.s_item.pos() * 0.25 + control_point * 0.5 + self.t_item.pos() * 0.25 self.setPath(path) - - curve_midpoint = self.s_item.pos() * 0.25 + control_point * 0.5 + self.t_item.pos() * 0.25 self.selection_node.setPos(curve_midpoint.x(), curve_midpoint.y()) self.selection_node.setVisible(self.isSelected()) @@ -146,9 +154,6 @@ def calculate_control_point(source_pos: QPointF, target_pos: QPointF, curve_dist """Calculate the control point for the curve""" direction = target_pos - source_pos norm = sqrt(direction.x()**2 + direction.y()**2) - if norm == 0: - direction = QPointF(1, 0) - norm = 1. direction = direction / norm perpendicular = QPointF(-direction.y(), direction.x()) midpoint = (source_pos + target_pos) / 2 From 740f98cf167e159099d353c3390b8e0827edb0b3 Mon Sep 17 00:00:00 2001 From: Razin Shaikh Date: Wed, 26 Jun 2024 20:16:25 +0100 Subject: [PATCH 5/5] curve distance is now used properly --- zxlive/eitem.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/zxlive/eitem.py b/zxlive/eitem.py index 7b3292a4..85abb47f 100644 --- a/zxlive/eitem.py +++ b/zxlive/eitem.py @@ -78,14 +78,15 @@ def refresh(self) -> None: self.setPen(QPen(pen)) path = QPainterPath() - if self.s_item == self.t_item: - # self-loop - cd = self.curve_distance + 1 - path.moveTo(self.s_item.pos()) - path.cubicTo(self.s_item.pos() + QPointF(1, -1) * cd * SCALE, - self.s_item.pos() + QPointF(-1, -1) * cd * SCALE, - self.s_item.pos()) - curve_midpoint = self.s_item.pos() + QPointF(0, -0.75) * cd * SCALE + if self.s_item == self.t_item: # self-loop + cd = self.curve_distance + cd = cd + 0.5 if cd >= 0 else cd - 0.5 + s_pos = self.s_item.pos() + path.moveTo(s_pos) + path.cubicTo(s_pos + QPointF(1, -1) * cd * SCALE, + s_pos + QPointF(-1, -1) * cd * SCALE, + s_pos) + curve_midpoint = s_pos + QPointF(0, -0.75) * cd * SCALE else: control_point = calculate_control_point(self.s_item.pos(), self.t_item.pos(), self.curve_distance) path.moveTo(self.s_item.pos())