-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathConnection.cs
More file actions
184 lines (146 loc) · 6.4 KB
/
Copy pathConnection.cs
File metadata and controls
184 lines (146 loc) · 6.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
using NodeDev.Core.Nodes;
using NodeDev.Core.Types;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
namespace NodeDev.Core.Connections
{
[System.Diagnostics.DebuggerDisplay("{Parent.Name}:{Name} - {Type}, {Connections.Count}")]
public class Connection
{
public string Id { get; }
public string Name { get; set; }
public Node Parent { get; }
public TypeBase Type { get; private set; }
/// <summary>
/// Initial type of the connection. Used to remember the type before generics were resolved in case we need to re-resolve them differently.
/// </summary>
public TypeBase InitialType { get; private set; }
internal readonly List<Connection> _Connections = [];
public IReadOnlyList<Connection> Connections => _Connections;
/// <summary>
/// Vertices of the connection. Used for drawing connections with multiple segments.
/// This is defined either if the connection is an input AND not exec, or if the connection is an output AND exec.
/// This is because for every possible types of inputs there is always only one output connected to it. Except for execs since multiple output paths can be connected to it.
/// </summary>
public readonly List<Vector2> Vertices = [];
public string? TextboxValue { get; private set; }
public object? ParsedTextboxValue { get; set; }
/// <summary>
/// Global index of this connection in the graph. Each connection of each node in a graph has a unique index.
/// </summary>
public int GraphIndex { get; set; } = -1;
/// <summary>
/// LinkedExec can be used to make sure a connection can only ever be used while on a path inside the linked exec connection.
/// Ex. during a 'foreach' loop, the 'Item' connection can only be used inside the 'Loop Exec' connection.
/// </summary>
public Connection? LinkedExec { get; }
public bool IsInput => Parent.Inputs.Contains(this);
public bool IsOutput => Parent.Outputs.Contains(this);
public Connection(string name, Node parent, TypeBase type, string? id = null, Connection? linkedExec = null)
{
Id = id ?? Guid.NewGuid().ToString();
Name = name;
Parent = parent;
Type = type;
InitialType = type;
LinkedExec = linkedExec;
}
#region Serialization
public record class SerializedConnectionVertex(float X, float Y);
public record SerializedConnection(string Id, string Name, TypeBase.SerializedType SerializedType, List<string> Connections, string? TextboxValue, List<SerializedConnectionVertex>? Vertices, string? LinkedExec);
internal SerializedConnection Serialize()
{
var connections = Connections.ToList();
var serializedConnection = new SerializedConnection(Id, Name, Type.SerializeWithFullTypeName(), Connections.Select(x => x.Id).ToList(), TextboxValue, Vertices.Select(x => new SerializedConnectionVertex(x.X, x.Y)).ToList(), LinkedExec?.Id);
return serializedConnection;
}
internal static Connection Deserialize(Node parent, SerializedConnection serializedConnectionObj, bool isInput)
{
// Find the LinkedExec connection, if any
Connection? linkedExec = null;
if (linkedExec != null)
linkedExec = parent.Graph.Nodes.SelectMany(x => x.Value.InputsAndOutputs).FirstOrDefault(x => x.Id == serializedConnectionObj.LinkedExec);
var type = TypeBase.Deserialize(parent.TypeFactory, serializedConnectionObj.SerializedType);
var connection = new Connection(serializedConnectionObj.Name, parent, type, serializedConnectionObj.Id, linkedExec);
connection.TextboxValue = serializedConnectionObj.TextboxValue;
if (connection.TextboxValue != null && isInput)
connection.ParsedTextboxValue = connection.Type.ParseTextboxEdit(connection.TextboxValue);
if (serializedConnectionObj.Vertices != null)
connection.Vertices.AddRange(serializedConnectionObj.Vertices.Select(x => new Vector2(x.X, x.Y)));
foreach (var connectionId in serializedConnectionObj.Connections)
{
var otherConnection = parent.Graph.Nodes.SelectMany(x => x.Value.InputsAndOutputs).FirstOrDefault(x => x.Id == connectionId);
if (otherConnection == null)
continue;
if (!connection.Connections.Contains(otherConnection))
connection._Connections.Add(otherConnection);
if (!otherConnection.Connections.Contains(connection))
otherConnection._Connections.Add(connection);
}
return connection;
}
#endregion
public bool IsAssignableTo(Connection other, bool alsoValidateInitialTypeSource, bool alsoValidateInitialTypeDestination, [MaybeNullWhen(false)] out Dictionary<string, TypeBase> changedGenericsLeft, [MaybeNullWhen(false)] out Dictionary<string, TypeBase> changedGenericsRight, out bool usedInitialTypes)
{
if (Type.IsAssignableTo(other.Type, out var changedGenericsLeft1, out var changedGenericsRight1, out var depth1))
{
if (alsoValidateInitialTypeSource || alsoValidateInitialTypeDestination)
{
var initialType = alsoValidateInitialTypeSource ? InitialType : Type;
var otherInitialType = alsoValidateInitialTypeDestination ? other.InitialType : other.Type;
if ((initialType != Type || otherInitialType != other.Type) && initialType.IsAssignableTo(otherInitialType, out var changedGenericsLeft2, out var changedGenericsRight2, out var depth2))
{
if ((changedGenericsLeft2.Count != 0 || changedGenericsRight2.Count != 0) && depth2 < depth1)
{
changedGenericsLeft = changedGenericsLeft2;
changedGenericsRight = changedGenericsRight2;
usedInitialTypes = true;
return true;
}
}
}
changedGenericsLeft = changedGenericsLeft1;
changedGenericsRight = changedGenericsRight1;
usedInitialTypes = false;
return true;
}
changedGenericsLeft = changedGenericsRight = null;
usedInitialTypes = false;
return false;
}
public void UpdateVertices(IEnumerable<Vector2> vertices)
{
Vertices.Clear();
Vertices.AddRange(vertices);
}
public void UpdateTypeAndTextboxVisibility(TypeBase newType, bool overrideInitialType)
{
Type = newType;
if (overrideInitialType)
InitialType = newType;
if (Type.AllowTextboxEdit)
TextboxValue = Type.DefaultTextboxValue;
else
TextboxValue = null;
Parent.Graph.GraphCanvas?.Refresh(Parent);
}
public void UpdateTextboxText(string? text)
{
if (Type.AllowTextboxEdit)
{
TextboxValue = text;
if (text == null)
ParsedTextboxValue = null;
else
{
try
{
ParsedTextboxValue = Type.ParseTextboxEdit(text);
}
catch (Exception)
{ }
}
}
}
}
}