initial multiple output support

This commit is contained in:
Pascal Serrarens 2026-02-06 18:06:36 +01:00
parent 278b861a84
commit 2cae7a6c41
3 changed files with 88 additions and 29 deletions

View File

@ -185,12 +185,10 @@ public class Cluster : Nucleus {
get { get {
if (this._inputs == null) { if (this._inputs == null) {
this._inputs = new(); this._inputs = new();
foreach (Nucleus receptor in this.nuclei) { foreach (Nucleus nucleus in this.nuclei) {
if (receptor is Nucleus nucleus) { // inputs have no synapses
// inputs have no incoming synapses yet. if (nucleus.synapses.Count == 0)
if (nucleus.synapses.Count == 0) this._inputs.Add(nucleus);
this._inputs.Add(nucleus);
}
} }
} }
return this._inputs; return this._inputs;
@ -204,6 +202,20 @@ public class Cluster : Nucleus {
return null; return null;
} }
} }
public List<Nucleus> _outputs = null;
public List<Nucleus> outputs {
get {
if (this._outputs == null) {
this._outputs = new();
foreach (Nucleus nucleus in this.nuclei) {
// outputs have not receivers
if (nucleus.receivers.Count == 0)
this._outputs.Add(nucleus);
}
}
return this._outputs;
}
}
public bool TryGetNucleus(string nucleusName, out Nucleus foundNucleus) { public bool TryGetNucleus(string nucleusName, out Nucleus foundNucleus) {
foreach (Nucleus receptor in this.nuclei) { foreach (Nucleus receptor in this.nuclei) {

View File

@ -28,6 +28,29 @@ public class ClusterPrefab : ScriptableObject {
return this._inputs; return this._inputs;
} }
} }
private List<Nucleus> _outputs = null;
public List<Nucleus> outputs {
get {
if (this._outputs == null)
RefreshOutputs();
return this._outputs;
}
}
public void RefreshOutputs() {
this._outputs = new();
foreach (Nucleus nucleus in this.nuclei) {
if (nucleus.receivers.Count == 0)
this._outputs.Add(nucleus);
}
}
public Nucleus GetNucleus(string nucleusName) {
foreach (Nucleus nucleus in this.nuclei) {
if (nucleus.name == nucleusName)
return nucleus;
}
return null;
}
// Call this function to ensure that there is at least one nucleus // Call this function to ensure that there is at least one nucleus
// This is an invariant and should be ensured before the nucleus is used // This is an invariant and should be ensured before the nucleus is used

View File

@ -46,7 +46,7 @@ public class ClusterInspector : Editor {
flexDirection = FlexDirection.Row flexDirection = FlexDirection.Row
} }
}; };
GraphView graph = new(); GraphView graph = new(cluster);
graph.style.flexGrow = 1; graph.style.flexGrow = 1;
inspectorContainer = new VisualElement { inspectorContainer = new VisualElement {
@ -61,14 +61,14 @@ public class ClusterInspector : Editor {
mainContainer.Add(inspectorContainer); mainContainer.Add(inspectorContainer);
root.Add(mainContainer); root.Add(mainContainer);
graph.SetGraph(null, cluster, cluster.output, inspectorContainer); graph.SetGraph(null, cluster.output, inspectorContainer);
return graph; return graph;
} }
public class GraphView : VisualElement { public class GraphView : VisualElement {
ClusterPrefab cluster; readonly ClusterPrefab cluster;
SerializedObject serializedBrain; SerializedObject serializedBrain;
Nucleus currentNucleus; Nucleus currentNucleus;
GameObject gameObject; GameObject gameObject;
@ -77,35 +77,59 @@ public class ClusterInspector : Editor {
private bool expandArray = false; private bool expandArray = false;
ClusterWrapper currentWrapper; ClusterWrapper currentWrapper;
PopupField<string> outputsField;
public enum OutputNodes { public GraphView(ClusterPrefab prefab) {
Output, this.cluster = prefab;
Output2,
Output3
}
public GraphView() {
name = "content"; name = "content";
style.flexGrow = 1; style.flexGrow = 1;
IMGUIContainer imguiContainer = new(OnIMGUI); IMGUIContainer graphContainer = new(OnIMGUI);
imguiContainer.style.position = Position.Absolute; graphContainer.style.position = Position.Absolute;
imguiContainer.style.left = 0; imguiContainer.style.top = 0; graphContainer.style.left = 0; graphContainer.style.top = 0;
imguiContainer.style.right = 0; imguiContainer.style.bottom = 0; graphContainer.style.right = 0; graphContainer.style.bottom = 0;
imguiContainer.pickingMode = PickingMode.Position; graphContainer.pickingMode = PickingMode.Position;
imguiContainer.focusable = true; graphContainer.focusable = true;
Add(imguiContainer); Add(graphContainer);
PopupField<OutputNodes> enumField = new(System.Enum.GetValues(typeof(OutputNodes)).Cast<OutputNodes>().ToList(), OutputNodes.Output); VisualElement outputContainer = new() {
enumField.RegisterValueChangedCallback(evt => OnOutputChanged(evt.newValue)); style = {
Add(enumField); flexDirection = FlexDirection.Row,
alignItems = Align.Center,
}
};
List<string> names = this.cluster.outputs.Select(output => output.name).ToList();
outputsField = new(names, names.First()) {
style = { flexGrow = 1 }
};
outputsField.RegisterValueChangedCallback(evt => OnOutputChanged(evt.newValue));
outputContainer.Add(outputsField);
Button addButton = new(() => OnAddClusterOutput()) {
text = "Add"
};
outputContainer.Add(addButton);
Add(outputContainer);
// Subscribe when added to panel (editor UI ready) // Subscribe when added to panel (editor UI ready)
RegisterCallback<AttachToPanelEvent>(evt => Subscribe()); RegisterCallback<AttachToPanelEvent>(evt => Subscribe());
RegisterCallback<DetachFromPanelEvent>(evt => Unsubscribe()); RegisterCallback<DetachFromPanelEvent>(evt => Unsubscribe());
} }
void OnOutputChanged(OutputNodes output) { void OnOutputChanged(string outputName) {
this.currentNucleus = this.cluster.GetNucleus(outputName);
}
void OnAddClusterOutput() {
Nucleus newOutput = new Neuron(this.cluster, "Output 2");
outputsField.choices = this.cluster.outputs.Select(output => output.name).ToList();
outputsField.value = newOutput.name;
this.currentNucleus = newOutput;
} }
bool subscribed = false; bool subscribed = false;
@ -122,11 +146,11 @@ public class ClusterInspector : Editor {
subscribed = false; subscribed = false;
} }
public void SetGraph(GameObject gameObject, ClusterPrefab brain, Nucleus nucleus, VisualElement inspectorContainer) { public void SetGraph(GameObject gameObject, Nucleus nucleus, VisualElement inspectorContainer) {
this.gameObject = gameObject; this.gameObject = gameObject;
this.cluster = brain; //this.cluster = brain;
if (Application.isPlaying == false) if (Application.isPlaying == false)
this.serializedBrain = new SerializedObject(brain); this.serializedBrain = new SerializedObject(this.cluster);
this.currentNucleus = nucleus; this.currentNucleus = nucleus;
Rebuild(inspectorContainer); Rebuild(inspectorContainer);
} }