Skip to content

Commit 694c983

Browse files
committed
Prevent cyclical dependencies from occuring.
Fixes #81.
1 parent 307149f commit 694c983

File tree

7 files changed

+79
-27
lines changed

7 files changed

+79
-27
lines changed

Nodexr/Nodexr.xml

Lines changed: 16 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Nodexr/Shared/Components/NoodleCollection.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
@foreach (var node in Nodes)
99
{
10-
var inputsWithNoodles = node.GetInputsRecursive()
10+
var inputsWithNoodles = node.GetAllInputs()
1111
.OfType<InputProcedural>()
1212
.Where(input => input.ConnectedNode != null);
1313

Nodexr/Shared/NodeInputs/InputProcedural.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace Nodexr.Shared.NodeInputs
77
public class InputProcedural : NodeInput, INoodleData
88
{
99
private INodeOutput connectedNode;
10+
1011
public INodeOutput ConnectedNode
1112
{
1213
get => connectedNode;

Nodexr/Shared/Nodes/Node.cs

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using Nodexr.Shared.NodeInputs;
77
using Nodexr.Shared;
88

9-
109
namespace Nodexr.Shared.Nodes
1110
{
1211
public interface INodeOutput
@@ -27,12 +26,27 @@ public interface INode : IPositionable, INodeOutput
2726

2827
IEnumerable<NodeInput> NodeInputs { get; }
2928
InputProcedural Previous { get; }
29+
30+
/// <summary>
31+
/// The node connected to the 'Previous' input.
32+
/// </summary>
3033
INodeOutput PreviousNode { get; set; }
3134

3235
void CalculateInputsPos();
36+
37+
/// <summary>
38+
/// Get the height of the node, in pixels. Disabled inputs do not contribute to the height.
39+
/// </summary>
3340
int GetHeight();
34-
IEnumerable<NodeInput> GetInputsRecursive();
41+
42+
/// <summary>
43+
/// Get all of the inputs to the node, including the 'previous' input and the sub-inputs of any InputCollections.
44+
/// InputCollections themselves are not returned.
45+
/// </summary>
46+
IEnumerable<NodeInput> GetAllInputs();
3547
void OnLayoutChanged(object sender, EventArgs e);
48+
bool IsDependentOn(INodeInput childInput);
49+
3650
event EventHandler LayoutChanged;
3751
}
3852

@@ -49,14 +63,16 @@ public Vector2L Pos
4963
CalculateInputsPos();
5064
}
5165
}
66+
5267
public InputProcedural Previous { get; } = new InputProcedural();
68+
5369
public INodeOutput PreviousNode
5470
{
5571
get => Previous.ConnectedNode;
5672
set => Previous.ConnectedNode = value;
5773
}
5874

59-
public IEnumerable<NodeInput> NodeInputs { get; private set; }
75+
public IEnumerable<NodeInput> NodeInputs { get; }
6076
public abstract string Title { get; }
6177
public abstract string NodeInfo { get; }
6278

@@ -77,7 +93,7 @@ protected virtual void OnOutputChanged(EventArgs e)
7793
public void OnLayoutChanged(object sender, EventArgs e)
7894
{
7995
CalculateInputsPos();
80-
foreach(var input in GetInputsRecursive().OfType<InputProcedural>())
96+
foreach(var input in GetAllInputs().OfType<InputProcedural>())
8197
{
8298
input.Refresh();
8399
}
@@ -168,11 +184,8 @@ public void CalculateInputsPos()
168184
}
169185
}
170186

171-
/// <summary>
172-
/// Get all of the inputs to the node, including the 'previous' input and the sub-inputs of any InputCollections.
173-
/// InputCollections themselves are not returned.
174-
/// </summary>
175-
public IEnumerable<NodeInput> GetInputsRecursive()
187+
/// <inheritdoc/>
188+
public IEnumerable<NodeInput> GetAllInputs()
176189
{
177190
yield return Previous;
178191
foreach(var input in NodeInputs)
@@ -189,9 +202,28 @@ public IEnumerable<NodeInput> GetInputsRecursive()
189202
}
190203
}
191204

205+
public bool IsDependentOn(INodeInput childInput)
206+
{
207+
return GetAllProceduralInputsRecursive(this).Any(input => input == childInput);
208+
209+
static IEnumerable<InputProcedural> GetAllProceduralInputsRecursive(INode parent)
210+
{
211+
foreach(var input in parent.GetAllInputs().OfType<InputProcedural>())
212+
{
213+
yield return input;
214+
215+
if (input.ConnectedNode is INode childNode)
216+
{
217+
foreach (var input2 in GetAllProceduralInputsRecursive(childNode))
218+
yield return input2;
219+
}
220+
}
221+
}
222+
}
223+
192224
public int GetHeight()
193225
{
194-
int baseHeight = 28;
226+
const int baseHeight = 28;
195227

196228
int inputHeight = NodeInputs
197229
.Where(input => input.IsEnabled())

Nodexr/Shared/Nodes/NodeTree.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ private void DeleteOutputNoodles(INode nodeToRemove)
7777
{
7878
foreach (var node in nodes)
7979
{
80-
foreach (var input in node.GetInputsRecursive().OfType<InputProcedural>())
80+
foreach (var input in node.GetAllInputs().OfType<InputProcedural>())
8181
{
8282
DeleteNoodlesBetween(nodeToRemove, input);
8383
}

Nodexr/Shared/RegexParsers/NodeTreeBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ void FillColumns(Node endNode)
4343

4444
private static List<Node> GetChildren(Node node)
4545
{
46-
var inputs = node.GetInputsRecursive().OfType<InputProcedural>();
46+
var inputs = node.GetAllInputs().OfType<InputProcedural>();
4747
var children = inputs.Select(input => input.ConnectedNode).OfType<Node>().ToList();
4848
return children;
4949
}

Nodexr/Shared/Services/NoodleDragService.cs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System;
99
using System.Threading.Tasks;
1010
using System.Linq;
11+
using Blazored.Toast.Services;
1112

1213
namespace Nodexr.Shared.Services
1314
{
@@ -24,13 +25,14 @@ public interface INoodleDragService
2425
public class NoodleDragService : INoodleDragService
2526
{
2627
public INode NodeToDrag { get; set; }
27-
public NoodleDataCustom TempNoodle { get; private set; } = new NoodleDataCustom() { Enabled = false };
28+
public NoodleDataCustom TempNoodle { get; } = new NoodleDataCustom() { Enabled = false };
2829

29-
readonly INodeHandler nodeHandler;
30-
readonly IJSRuntime jsRuntime;
31-
public NoodleDragService(INodeHandler nodeHandler, IJSRuntime jsRuntime)
30+
private readonly IToastService toastService;
31+
private readonly IJSRuntime jsRuntime;
32+
33+
public NoodleDragService(IToastService toastService, IJSRuntime jsRuntime)
3234
{
33-
this.nodeHandler = nodeHandler;
35+
this.toastService = toastService;
3436
this.jsRuntime = jsRuntime;
3537
}
3638

@@ -46,9 +48,7 @@ public void OnStartNoodleDrag(INodeOutput nodeToDrag, DragEventArgs e, Vector2L
4648
{
4749
NodeToDrag = node;
4850
TempNoodle.Enabled = true;
49-
//nodeHandler.OnRequireNoodleRefresh?.Invoke();
5051

51-
//Console.WriteLine("Start Noodle Drag");
5252
jsRuntime.InvokeAsync<object>("tempNoodle.startNoodleDrag",
5353
nodeToDrag.OutputPos.x, nodeToDrag.OutputPos.y,
5454
noodleEndPos.x, noodleEndPos.y);
@@ -59,14 +59,20 @@ public void OnStartNoodleDrag(INodeOutput nodeToDrag, DragEventArgs e, Vector2L
5959

6060
public void OnDropNoodle(InputProcedural nodeInput)
6161
{
62-
Console.WriteLine("Drop noodle");
6362
TempNoodle.Enabled = false;
6463

65-
//TODO: Properly check for cyclic dependencies
66-
if (NodeToDrag != null && !NodeToDrag.GetInputsRecursive().Contains(nodeInput))
64+
if (NodeToDrag is null) return;
65+
66+
if (NodeToDrag.IsDependentOn(nodeInput))
67+
{
68+
toastService.ShowError(
69+
"Cyclical dependencies would create a rift in the space-time continuum and are therefore not allowed. " +
70+
"If you want to use the same node multiple times in a row, connect it to multiple inputs of a 'Concatenate' node.",
71+
"Can't connect these nodes");
72+
}
73+
else
6774
{
6875
nodeInput.ConnectedNode = NodeToDrag;
69-
//nodeHandler.OnRequireNodeGraphRefresh?.Invoke();
7076
}
7177

7278
NodeToDrag = null;

0 commit comments

Comments
 (0)