Skip to content

Commit 2961ca6

Browse files
committed
feat: allow to keep incoming/outgoing connections when duplicating nodes
fixes #62
1 parent b5d2899 commit 2961ca6

File tree

3 files changed

+54
-13
lines changed

3 files changed

+54
-13
lines changed

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [0.11.0] - 2023-09-21
8+
### Added
9+
- You can now hold the _Shift_ key when duplicating nodes to automatically connect the duplicates to the same nodes as the original nodes. ([#62](https://github.com/derkork/openscad-graph-editor/issues/62)).
10+
711
## [0.10.2] - 2023-09-05
812
### Fixed
913
- When having a min/step/max-constrained number in the customizer and the min value is different from zero the slider could not select any correct value anymore. This is an issue with the underlying Godot engine 3 which doesn't support this combination for sliders. As such this issue cannot be fixed without migrating to Godot 4. As a workaround, the editor will not show a number edit instead of a slider in this case, so it is at least possible to enter a valid value. ([#61](https://github.com/derkork/openscad-graph-editor/issues/61)).

Utils/KeyMap.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System.Security.Policy;
21
using Godot;
32

43
namespace OpenScadGraphEditor.Utils
@@ -72,7 +71,7 @@ public static bool IsCmdOrControlPressed()
7271
public static bool IsExtract(this InputEvent inputEvent) => inputEvent.IsCmdOrControlPressed() && inputEvent.IsKeyPressed(KeyList.E);
7372

7473
// undo/redo
75-
public static bool IsUndo(this InputEvent inputEvent) => inputEvent.IsCmdOrControlPressed() && inputEvent.IsKeyPressed(KeyList.Z);
74+
public static bool IsUndo(this InputEvent inputEvent) => inputEvent.IsCmdOrControlPressed() && inputEvent.IsKeyPressed(KeyList.Z) && !inputEvent.IsShiftPressed();
7675
public static bool IsRedo(this InputEvent inputEvent) => inputEvent.IsCmdOrControlPressed() && inputEvent.IsKeyPressed(KeyList.Z) && inputEvent.IsShiftPressed();
7776

7877
// straighten

Widgets/ScadGraphEdit.cs

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public override void _Ready()
6565
{
6666
foreach (var to in allPortTypes)
6767
{
68-
AddValidConnectionType((int) from, (int) to);
68+
AddValidConnectionType((int)from, (int)to);
6969
}
7070
}
7171

@@ -273,7 +273,7 @@ private void BuildConnectionLookupTree()
273273
var bezierPoints = BakeBezierLine(connectionPoints.Start, connectionPoints.End, 1);
274274
foreach (var bezierPoint in bezierPoints)
275275
{
276-
_connectionTree.Add(new[] {bezierPoint.x, bezierPoint.y}, (connection, bezierPoints));
276+
_connectionTree.Add(new[] { bezierPoint.x, bezierPoint.y }, (connection, bezierPoints));
277277
}
278278
}
279279
}
@@ -427,6 +427,7 @@ public override void _GuiInput(InputEvent evt)
427427

428428
if (evt.IsAlignRight())
429429
{
430+
GD.Print("Align right");
430431
AlignSelectionRight();
431432
return;
432433
}
@@ -443,7 +444,7 @@ public override void _GuiInput(InputEvent evt)
443444
return;
444445
}
445446

446-
if (evt is InputEventMouseButton {Pressed: false} &&
447+
if (evt is InputEventMouseButton { Pressed: false } &&
447448
_pendingDisconnect != null)
448449
{
449450
Log.Debug("Resolving pending disconnect");
@@ -467,7 +468,7 @@ public override void _GuiInput(InputEvent evt)
467468
// we need to convert them to screen space but relative to the graph widget.
468469
var mousePosition = mousePositionRelativeToTheGraph * Zoom;
469470

470-
var closest = _connectionTree.GetNearestNeighbours(new[] {mousePosition.x, mousePosition.y}, 1);
471+
var closest = _connectionTree.GetNearestNeighbours(new[] { mousePosition.x, mousePosition.y }, 1);
471472
if (closest.Length <= 0)
472473
{
473474
ClearHighlightedConnection();
@@ -503,12 +504,49 @@ public void CopySelection()
503504

504505
public void DuplicateSelection()
505506
{
507+
var selectedNodes = GetSelectedNodes().ToHashSet();
506508
var duplicatedNodes =
507-
Graph.CloneSelection(_context.CurrentProject, GetSelectedNodes(), out _);
509+
Graph.CloneSelection(_context.CurrentProject, selectedNodes, out var mappedIds);
510+
511+
// make a list of all refactorings that need to be performed
512+
var refactorings = new List<Refactoring>();
508513

509514
var pastePosition = CalculatePastePosition();
510-
var refactoringData = _context.PerformRefactoring(
511-
"Duplicate nodes", new PasteNodesRefactoring(Graph, duplicatedNodes, pastePosition));
515+
// since we already make a clone of the nodes, we set "pasteCopy" to false here. This will also help
516+
// with the ID-remapping for the extra connections.
517+
var pasteNodesRefactoring = new PasteNodesRefactoring(Graph, duplicatedNodes, pastePosition, false);
518+
519+
refactorings.Add(pasteNodesRefactoring);
520+
521+
// when shift is pressed we want to also duplicate all connections that go into or out of the duplicated nodes
522+
// https://github.com/derkork/openscad-graph-editor/issues/62
523+
if (KeyMap.IsShiftPressed())
524+
{
525+
// get a list of all connections that go into or out of the duplicated nodes
526+
// but only to nodes which are not part of the selection.
527+
var connectionsToDuplicate = Graph.GetAllConnections()
528+
// the connection can either be from or to the duplicated nodes but not both
529+
.Where(it => selectedNodes.Contains(it.From) ^ selectedNodes.Contains(it.To))
530+
.ToList();
531+
532+
// now walk over the connections and create a copy of each, replacing either the from or
533+
// to node with the new node.
534+
var duplicatedConnections = connectionsToDuplicate
535+
.Select(it =>
536+
{
537+
var from = selectedNodes.Contains(it.From)
538+
? duplicatedNodes.ById(mappedIds[it.From.Id])
539+
: it.From;
540+
var to = selectedNodes.Contains(it.To) ? duplicatedNodes.ById(mappedIds[it.To.Id]) : it.To;
541+
return new ScadConnection(Graph, from, it.FromPort, to, it.ToPort);
542+
})
543+
.Select(it => new AddConnectionRefactoring(it))
544+
.ToList();
545+
546+
refactorings.AddRange(duplicatedConnections);
547+
}
548+
549+
var refactoringData = _context.PerformRefactorings("Duplicate nodes", refactorings);
512550

513551
// select the pasted nodes
514552
if (refactoringData.TryGetData(PasteNodesRefactoring.PastedNodes, out var pastedNodes))
@@ -601,7 +639,7 @@ public void AlignSelectionLeft()
601639
_graphLayout.AlignNodesLeft(GetSelectedWidgets());
602640
}
603641

604-
642+
605643
private void ClearHighlightedConnection()
606644
{
607645
if (_highlightedConnection == null)
@@ -681,7 +719,7 @@ private void OnDisconnectionRequest(string fromWidgetName, int fromSlot, string
681719

682720
// the disconnect is not done until the user has released the mouse button, so in case this is called
683721
// while the mouse is still down, just visually disconnect, but don't do a refactoring yet.
684-
if (Input.IsMouseButtonPressed((int) ButtonList.Left))
722+
if (Input.IsMouseButtonPressed((int)ButtonList.Left))
685723
{
686724
DisconnectNode(fromWidgetName, fromSlot, toWidgetName, toSlot);
687725
_pendingDisconnect = connection;
@@ -729,7 +767,7 @@ private void HighlightBoundNodes()
729767
var partnerNodeId = node.OtherNodeId;
730768
var partnerNodeWidget = _widgets[partnerNodeId];
731769
// if the node's partner node is not currently selected, but the node is selected then highlight the node's partner node, otherwise clear it.
732-
if (!_selection.Contains(partnerNodeId) && _selection.Contains(((ScadNode) node).Id))
770+
if (!_selection.Contains(partnerNodeId) && _selection.Contains(((ScadNode)node).Id))
733771
{
734772
partnerNodeWidget.Modulate = Colors.Yellow;
735773
}
@@ -901,7 +939,7 @@ private List<Vector2> BakeBezierLine(Vector2 fromPoint, Vector2 toPoint, float b
901939

902940
var lines = 0;
903941

904-
var points = new List<Vector2> {fromPoint};
942+
var points = new List<Vector2> { fromPoint };
905943
BakeSegment2D(points, 0, 1, fromPoint, c1, toPoint, c2, 0, 3, 9, 3, 20, ref lines);
906944
points.Add(toPoint);
907945

0 commit comments

Comments
 (0)