Skip to content

Commit

Permalink
Merge pull request #265 from zxcalc/curved-edges
Browse files Browse the repository at this point in the history
Curved edges
  • Loading branch information
RazinShaikh authored Jun 29, 2024
2 parents 88d6696 + ab3f93b commit 62adb69
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 6 deletions.
5 changes: 4 additions & 1 deletion zxlive/base_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
QToolBar, QVBoxLayout, QWidget)

from .animations import AnimatedUndoStack
from .commands import SetGraph
from .commands import ChangeEdgeCurve, SetGraph
from .common import GraphT, new_graph
from .dialogs import FileFormat
from .graphscene import GraphScene
Expand Down Expand Up @@ -115,3 +115,6 @@ def sync_splitter_sizes(self):
def set_splitter_size(self):
if self.__class__ in self.splitter_sizes:
self.splitter.setSizes(self.splitter_sizes[self.__class__])

def change_edge_curves(self, eitem, new_distance, old_distance):
self.undo_stack.push(ChangeEdgeCurve(self.graph_view, eitem, new_distance, old_distance))
17 changes: 17 additions & 0 deletions zxlive/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from pyzx.utils import EdgeType, VertexType, get_w_partner, vertex_is_w, get_w_io, get_z_box_label, set_z_box_label

from .common import ET, VT, W_INPUT_OFFSET, GraphT, setting
from .eitem import EItem
from .graphview import GraphView
from .proof import ProofModel, Rewrite

Expand Down Expand Up @@ -269,6 +270,22 @@ def redo(self) -> None:
self.update_graph_view()


@dataclass
class ChangeEdgeCurve(BaseCommand):
"""Changes the curve of an edge."""
eitem: EItem
new_distance: float
old_distance: float

def undo(self) -> None:
self.eitem.curve_distance = self.old_distance
self.eitem.refresh()

def redo(self) -> None:
self.eitem.curve_distance = self.new_distance
self.eitem.refresh()


@dataclass
class AddIdentity(BaseCommand):
"""Adds an X or Z identity spider on an edge between two vertices."""
Expand Down
1 change: 1 addition & 0 deletions zxlive/edit_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def __init__(self, graph: GraphT, *actions: QAction) -> None:
self.graph_scene.vertex_double_clicked.connect(self.vert_double_clicked)
self.graph_scene.vertex_added.connect(self.add_vert)
self.graph_scene.edge_added.connect(self.add_edge)
self.graph_scene.edge_dragged.connect(self.change_edge_curves)

self._curr_vty = VertexType.Z
self._curr_ety = EdgeType.SIMPLE
Expand Down
42 changes: 38 additions & 4 deletions zxlive/eitem.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ def __init__(self, graph_scene: GraphScene, e: ET, s_item: VItem, t_item: VItem,
self.selection_node.setPen(pen)
self.selection_node.setOpacity(0.5)
# self.selection_node.setVisible(False)
self.is_mouse_pressed = False
self.is_dragging = False

self.refresh()

Expand Down Expand Up @@ -114,9 +116,35 @@ def itemChange(self, change: QGraphicsItem.GraphicsItemChange, value: Any) -> An

return super().itemChange(change, value)


def mousePressEvent(self, e: QGraphicsSceneMouseEvent) -> None:
super().mousePressEvent(e)
self.refresh()
self._old_pos = e.pos()
self._old_curve_distance = self.curve_distance
self.is_mouse_pressed = True

def mouseMoveEvent(self, e: QGraphicsSceneMouseEvent) -> None:
super().mouseMoveEvent(e)
scene = self.scene()
if TYPE_CHECKING: assert isinstance(scene, GraphScene)
if self.is_mouse_pressed and len(scene.selectedItems()) == 1 and self._old_pos is not None:
self.is_dragging = True
distance = e.pos() - self._old_pos
perpendicular = compute_perpendicular_direction(self.s_item.pos(), self.t_item.pos())
self.curve_distance += 2 * QPointF.dotProduct(distance, perpendicular) / SCALE
self._old_pos = e.pos()
self.refresh()
e.ignore()

def mouseReleaseEvent(self, e: QGraphicsSceneMouseEvent) -> None:
super().mouseReleaseEvent(e)
if self.is_dragging:
self.graph_scene.edge_dragged.emit(self, self.curve_distance, self._old_curve_distance)
self._old_pos = None
self.is_dragging = False
self.is_mouse_pressed = False



# TODO: This is essentially a clone of EItem. We should common it up!
Expand Down Expand Up @@ -153,11 +181,17 @@ def refresh(self) -> None:

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
norm = sqrt(direction.x()**2 + direction.y()**2)
direction = direction / norm
perpendicular = QPointF(-direction.y(), direction.x())
perpendicular = compute_perpendicular_direction(source_pos, target_pos)
midpoint = (source_pos + target_pos) / 2
offset = perpendicular * curve_distance * SCALE
control_point = midpoint + offset
return control_point

def compute_perpendicular_direction(source_pos, target_pos):
if source_pos == target_pos:
return QPointF(0, -2/3)
direction = target_pos - source_pos
norm = sqrt(direction.x()**2 + direction.y()**2)
direction = direction / norm
perpendicular = QPointF(-direction.y(), direction.x())
return perpendicular
3 changes: 3 additions & 0 deletions zxlive/graphscene.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ class GraphScene(QGraphicsScene):
# Triggers when a vertex is dropped onto another vertex. Actual types: VT, VT
vertex_dropped_onto = Signal(object, object)

# Triggers when an edge is dragged. Actual types: EItem, float (old curve_distance), float (new curve_distance)
edge_dragged = Signal(object, object, object)

def __init__(self) -> None:
super().__init__()
self.setSceneRect(0, 0, 2*OFFSET_X, 2*OFFSET_Y)
Expand Down
2 changes: 1 addition & 1 deletion zxlive/proof_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ def __init__(self, graph: GraphT, *actions: QAction) -> None:
self.graph_scene.vertices_moved.connect(self._vert_moved)
self.graph_scene.vertex_double_clicked.connect(self._vert_double_clicked)


self.graph_view = ProofGraphView(self.graph_scene)
self.splitter.addWidget(self.graph_view)
self.graph_view.set_graph(graph)
Expand All @@ -53,6 +52,7 @@ def __init__(self, graph: GraphT, *actions: QAction) -> None:
self.graph_view.wand_trace_finished.connect(self._wand_trace_finished)
self.graph_scene.vertex_dragged.connect(self._vertex_dragged)
self.graph_scene.vertex_dropped_onto.connect(self._vertex_dropped_onto)
self.graph_scene.edge_dragged.connect(self.change_edge_curves)

self.step_view = QListView(self)
self.proof_model = ProofModel(self.graph_view.graph_scene.g)
Expand Down
2 changes: 2 additions & 0 deletions zxlive/rule_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,13 @@ def __init__(self, graph1: GraphT, graph2: GraphT, name: str, description: str,
self.graph_scene_left.vertex_double_clicked.connect(self.vert_double_clicked)
self.graph_scene_left.vertex_added.connect(self.add_vert)
self.graph_scene_left.edge_added.connect(self.add_edge)
self.graph_scene_left.edge_dragged.connect(self.change_edge_curves)

self.graph_scene_right.vertices_moved.connect(self.vert_moved)
self.graph_scene_right.vertex_double_clicked.connect(self.vert_double_clicked)
self.graph_scene_right.vertex_added.connect(self.add_vert)
self.graph_scene_right.edge_added.connect(self.add_edge)
self.graph_scene_right.edge_dragged.connect(self.change_edge_curves)

self.create_side_bar()
self.splitter.addWidget(self.sidebar)
Expand Down

0 comments on commit 62adb69

Please sign in to comment.