Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add toggle to automatically connect new vertex to edge underneath it #337

Merged
merged 13 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion zxlive/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,23 @@ def undo(self) -> None:
self.update_graph_view()

def redo(self) -> None:
self.g.add_edge(((self.u, self.v)), self.ety)
self.g.add_edge((self.u, self.v), self.ety)
self.update_graph_view()

@dataclass
class AddEdges(BaseCommand):
"""Adds multiple edges of the same type to a graph."""
pairs: list[tuple[VT,VT]]
ety: EdgeType

def undo(self) -> None:
for u, v in self.pairs:
self.g.remove_edge((u, v, self.ety))
self.update_graph_view()

def redo(self) -> None:
for u, v in self.pairs:
self.g.add_edge((u, v), self.ety)
self.update_graph_view()


Expand Down
40 changes: 34 additions & 6 deletions zxlive/editor_base_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@
from zxlive.sfx import SFXEnum

from .base_panel import BasePanel, ToolbarSection
from .commands import (AddEdge, AddNode, AddNodeSnapped, AddWNode, ChangeEdgeColor,
from .commands import (AddEdge, AddEdges, AddNode, AddNodeSnapped, AddWNode, ChangeEdgeColor,
ChangeNodeType, ChangePhase, MoveNode, SetGraph,
UpdateGraph)
from .common import VT, GraphT, ToolType, get_data
from .dialogs import show_error_msg
from .eitem import EItem, HAD_EDGE_BLUE, EItemAnimation
from .vitem import VItem, BLACK, VItemAnimation
from .graphscene import EditGraphScene
from .settings import display_setting
from .vitem import BLACK

from . import animations


Expand Down Expand Up @@ -179,15 +180,41 @@ def add_vert(self, x: float, y: float, edges: list[EItem]) -> None:
self.play_sound_signal.emit(SFXEnum.THATS_A_SPIDER)
self.undo_stack.push(cmd)

def add_edge(self, u: VT, v: VT) -> None:
def add_edge(self, u: VT, v: VT, verts: list[VItem]) -> None:
"""Add an edge between vertices u and v. `verts` is a list of VItems that collide with the edge.
"""
graph = self.graph_view.graph_scene.g
if vertex_is_w(graph.type(u)) and get_w_partner(graph, u) == v:
return None
if graph.type(u) == VertexType.W_INPUT and len(graph.neighbors(u)) >= 2 or \
graph.type(v) == VertexType.W_INPUT and len(graph.neighbors(v)) >= 2:
return None
cmd = AddEdge(self.graph_view, u, v, self._curr_ety)
# We will try to connect all the vertices together in order
# First we filter out the vertices that are not compatible with the edge.
verts = [vitem for vitem in verts if not graph.type(vitem.v) == VertexType.W_INPUT] # we will be adding two edges, which is not compatible with W_INPUT
# but first we check if there any vertices that we do want to additionally connect.
if not self.snap_vertex_edge or not verts:
cmd = AddEdge(self.graph_view, u, v, self._curr_ety)
self.undo_stack.push(cmd)
return

ux, uy = graph.row(u), graph.qubit(u)
# Line was drawn from u to v, we want to order vs with the earlier items first.
def dist(vitem: VItem) -> float:
return (graph.row(vitem.v) - ux)**2 + (graph.row(vitem.v) - uy)**2
verts.sort(key=dist)
vs = [vitem.v for vitem in verts]
pairs = [(u, vs[0])]
for i in range(1, len(vs)):
pairs.append((vs[i-1],vs[i]))
pairs.append((vs[-1],v))
cmd = AddEdges(self.graph_view, pairs, self._curr_ety)
self.undo_stack.push(cmd)
group = QParallelAnimationGroup()
for vitem in verts:
anim = animations.scale(vitem,1.0,400,QEasingCurve(QEasingCurve.Type.InCubic),start=1.3)
group.addAnimation(anim)
self.undo_stack.set_anim(group)

def vert_moved(self, vs: list[tuple[VT, float, float]]) -> None:
self.undo_stack.push(MoveNode(self.graph_view, vs))
Expand Down Expand Up @@ -347,8 +374,9 @@ def toolbar_select_node_edge(parent: EditorBasePanel) -> Iterator[ToolbarSection
snap = QToolButton(parent)
snap.setCheckable(True)
snap.setChecked(True)
snap.setText("Vertex-edge snap")
snap.setToolTip("Snap newly added vertex to the edge beneath it (f)")
#snap.setText("Vertex-edge snap")
jvdwetering marked this conversation as resolved.
Show resolved Hide resolved
snap.setIcon(QIcon(get_data("icons/vertex-snap-to-edge.svg")))
snap.setToolTip("Snap vertices to the edge beneath them when adding vertices or edges (f)")
snap.setShortcut("f")
snap.clicked.connect(lambda: parent._snap_vertex_edge_clicked())
yield ToolbarSection(snap)
Expand Down
2 changes: 0 additions & 2 deletions zxlive/eitem.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,6 @@ def __init__(self, item: Union[EItem, ET], property: EItem.Properties,
self.e = None
self._it = None
self.scene: Optional[GraphScene] = None
#if refresh and property != VItem.Properties.Position:
# raise ValueError("Only position animations require refresh")
if isinstance(item, EItem):
self._it = item
elif scene is None:
Expand Down
32 changes: 25 additions & 7 deletions zxlive/graphscene.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,17 @@

from typing import Optional, Iterator, Iterable

from PySide6.QtCore import Qt, Signal
from PySide6.QtGui import QBrush, QColor, QTransform
from PySide6.QtCore import Qt, Signal, QRectF
from PySide6.QtGui import QBrush, QColor, QTransform, QPainterPath
from PySide6.QtWidgets import QGraphicsScene, QGraphicsSceneMouseEvent, QGraphicsItem

from pyzx.graph.base import EdgeType
from pyzx.graph import GraphDiff

from .common import VT, ET, GraphT, ToolType, pos_from_view, OFFSET_X, OFFSET_Y
from .common import SCALE, VT, ET, GraphT, ToolType, pos_from_view, OFFSET_X, OFFSET_Y
from .vitem import VItem
from .eitem import EItem, EDragItem
from .settings import display_setting


class GraphScene(QGraphicsScene):
Expand Down Expand Up @@ -234,7 +235,7 @@ class EditGraphScene(GraphScene):
# Note that we have to set the argument types to `object`,
# otherwise it doesn't work for some reason...
vertex_added = Signal(object, object, object) # Actual types: float, float, list[EItem]
edge_added = Signal(object, object) # Actual types: VT, VT
edge_added = Signal(object, object, object) # Actual types: VT, VT, list[VItem]

# Currently selected edge type for preview when dragging
# to add a new edge
Expand Down Expand Up @@ -293,14 +294,31 @@ def mouseReleaseEvent(self, e: QGraphicsSceneMouseEvent) -> None:

def add_vertex(self, e: QGraphicsSceneMouseEvent) -> None:
p = e.scenePos()
# create a rectangle around the mouse position which will be used to check of edge intersections
snap = display_setting.SNAP_DIVISION
rect = QRectF(p.x() - SCALE/(2*snap), p.y() - SCALE/(2*snap), SCALE/snap, SCALE/snap)
# edges under current mouse position
edges: list[EItem] = [e for e in self.items(p, deviceTransform=QTransform()) if isinstance(e,EItem)]
edges: list[EItem] = [e for e in self.items(rect, deviceTransform=QTransform()) if isinstance(e,EItem)]
self.vertex_added.emit(*pos_from_view(p.x(), p.y()), edges)

def add_edge(self, e: QGraphicsSceneMouseEvent) -> None:
assert self._drag is not None
self.removeItem(self._drag)
v1 = self._drag.start
self._drag = None
for it in self.items(e.scenePos(), deviceTransform=QTransform()):
if isinstance(it, VItem):
self.edge_added.emit(self._drag.start.v, it.v)
self._drag = None
v2 = it
break
#self.edge_added.emit(self._drag.start.v, it.v)
jvdwetering marked this conversation as resolved.
Show resolved Hide resolved
else:
e.ignore()
return
path = QPainterPath(v1.pos())
path.lineTo(e.scenePos())
colliding_verts = []
for it in self.items(path, Qt.IntersectsItemShape, Qt.DescendingOrder, deviceTransform=QTransform()):
if isinstance(it, VItem) and it not in (v1,v2):
colliding_verts.append(it)
self.edge_added.emit(v1.v,v2.v,colliding_verts)

11 changes: 11 additions & 0 deletions zxlive/icons/vertex-snap-to-edge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading