From 0e408faabc26dd60e2ef49732dd8c266a26fdfa1 Mon Sep 17 00:00:00 2001 From: David Mosbach Date: Sat, 26 Aug 2023 04:40:44 +0200 Subject: [PATCH] replace ghost nodes by real nodes on drag end --- editor.ts | 75 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 17 deletions(-) diff --git a/editor.ts b/editor.ts index 5a0cc01..a45e919 100644 --- a/editor.ts +++ b/editor.ts @@ -428,6 +428,7 @@ var selection : WF.WFNode | WF.WFGhostNode | WF.WFEdge | null = null; // The cur var rightSelection : WF.WFNode | WF.WFEdge | null = null; // The currently right clicked node/edge. var edgeTo : WF.WFNode | null = null; // Target of an edge to be created. var edgeFrom : WF.WFNode | null = null; // Start on an edge to be created. +var edgeTarget : WF.WFNode | null = null; // Possible source/target for an edge after dragging the respective ghost node. //Utility elements const curtain = document.getElementById('curtain'); const submenuBackdrop = document.getElementById('submenu-backdrop'); @@ -572,14 +573,16 @@ export function addState() { var nodeId = stateIdCounter ++; var x = newStateCoords.x; var y = newStateCoords.y; - var state : WF.WFNode = { id: 'state_' + nodeId, - x: x, - y: y, - name: 'state_' + nodeId, - fx: x, - fy: y, - val: 5, - stateData: new WF.StateData({ abbreviation: `S${nodeId}`, final: 'false' }) }; + var state = new WF.WFNode ({ + id: 'state_' + nodeId, + x: x, + y: y, + name: 'state_' + nodeId, + fx: x, + fy: y, + val: 5, + stateData: { abbreviation: `S${nodeId}`, final: 'false' } + }); workflow.states.push(state); updateGraph(); select(state); @@ -587,8 +590,8 @@ export function addState() { } export function addEdge() { - var x = newStateCoords.x; - var y = newStateCoords.y; + var x = newStateCoords.x - 20; + var y = newStateCoords.y + 20; var ghostState = new WF.WFGhostNode({ id: `@@ghost@(${x},${y})`, x: x, @@ -596,11 +599,13 @@ export function addEdge() { fx: x, fy: y, val: 7 }); + var x = newStateCoords.x + 20; + var y = newStateCoords.y - 20; var ghostState2 = new WF.WFGhostNode({ - id: `@@ghost@(${x+50},${y})`, - x: x + 50, + id: `@@ghost@(${x},${y})`, + x: x, y: y, - fx: x + 50, + fx: x, fy: y, val: 7 }); workflow.states.push(ghostState, ghostState2); @@ -712,14 +717,15 @@ function removeAction(action: WF.WFEdge) { * Removes a state from the workflow. * @param {*} state The state to remove. */ -function removeState(state: WF.WFNode) { +function removeState(state: WF.WFNode | WF.WFGhostNode) { workflow.actions .filter(edge => edge.source === state || edge.target === state) .forEach(edge => removeAction(edge)); workflow.states.splice(workflow.states.indexOf(state), 1); - var abbreviation = state.stateData && state.stateData.abbreviation; - abbreviation && stateAbbreviations.splice(stateAbbreviations.indexOf(abbreviation), 1); - nodeIndex.remove(state.id); + if (state instanceof WF.WFNode) { + stateAbbreviations.splice(stateAbbreviations.indexOf(state.stateData.abbreviation), 1); + nodeIndex.remove(state.id); + } } var selfLoops: Map = new Map(); // All edges whose targets equal their sources. @@ -1186,6 +1192,13 @@ function getEdgeColour(edge: LinkObject) { ctx.lineWidth = 1; ctx.stroke(); ctx.restore(); + } else if (edgeTarget === node) { + ctx.save(); + ctx.lineCap = 'round'; + ctx.lineWidth = 2; + ctx.strokeStyle = nodeColourDefaultFinal.value(); + ctx.stroke(); + ctx.restore(); } else if (node === selection || node === rightSelection) { ctx.save(); ctx.lineCap = 'round'; @@ -1207,9 +1220,37 @@ function getEdgeColour(edge: LinkObject) { ctx.fillText(wfNode.text, wfNode.x, wfNode.y); } }) + .onNodeDrag((node: NodeObject, delta: { x: number, y: number }) => { + edgeTarget = null; + if (!(node instanceof WF.WFGhostNode)) return; + const fineTuningThreshold = 0; + if (Math.sqrt(Math.round(Math.abs(delta.x * delta.y))) > fineTuningThreshold) return; + for (const node2 of workflow.states) { + if (!(node2 instanceof WF.WFNode)) continue; + if (Math.sqrt(Math.pow(node.x - node2.x, 2) + Math.pow(node.y - node2.y, 2)) <= 2*node2.val) { + edgeTarget = node2; + break; + } + } + console.log('close:', edgeTarget); + + }) .onNodeDragEnd((node: NodeObject) => { node.fx = node.x; node.fy = node.y; + if (node instanceof WF.WFGhostNode && edgeTarget) { + var edgesFrom : WF.WFEdge[] = []; + var edgesTo : WF.WFEdge[] = []; + workflow.actions.forEach(edge => { + edge.source === node && edgesFrom.push(edge); + edge.target === node && edgesTo.push(edge); + }); + if (!(edgesFrom || edgesTo)) throw new Error('Could not find an edge for the dragged ghost node'); + edgesFrom.forEach(edge => edge.source = edgeTarget); + edgesTo.forEach(edge => edge.target = edgeTarget); + removeState(node); + updateGraph(); + } }) .onNodeClick((node: NodeObject, _: MouseEvent) => { const wfNode = node as (WF.WFNode | WF.WFGhostNode);