Fix adding/removing cluster outputs

This commit is contained in:
Pascal Serrarens 2026-04-28 11:30:40 +02:00
parent 805b0f8489
commit fc8caa8a29
3 changed files with 100 additions and 89 deletions

View File

@ -70,24 +70,7 @@ namespace NanoBrain {
// In a Prefab editor, no instance exists but we need it for the ClusterViewer.
// So we create a temporary instance
Cluster cluster = new(prefab);
this.currentCluster = cluster;
Button addButton = new(() => OnAddClusterOutput()) {
text = "Add"
};
topMenuContainer?.Add(addButton);
Add(topMenuContainer);
}
void OnAddClusterOutput() {
Nucleus newOutput = new Neuron(this.prefab, "New Output");
this.prefab.RefreshOutputs();
// outputsPopup.choices = this.prefab.outputs.Select(output => output.name).ToList();
// outputsPopup.value = newOutput.name;
this.currentNucleus = newOutput;
this.currentCluster = new(prefab);
}
public void SetGraph(GameObject gameObject, VisualElement inspectorContainer) {
@ -151,69 +134,74 @@ namespace NanoBrain {
if (serializedObject == null || serializedObject.targetObject == null)
return;
if (this.currentNucleus == null)
return;
serializedObject.Update();
GUIStyle headerStyle = new(EditorStyles.boldLabel) {
alignment = TextAnchor.MiddleLeft,
margin = new RectOffset(10, 0, 4, 4)
};
GUIStyle boldTextFieldStyle = new(EditorStyles.textField) {
fontStyle = FontStyle.Bold
};
// Nucleus type
string nucleusType = this.currentNucleus.GetType().Name;
GUILayout.Label(nucleusType, headerStyle);
// Nucleus name
if (this.currentNucleus is Cluster parentCluster) {
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button(this.currentNucleus.parent.name))
OnClusterClick(parentCluster);
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.TextField(this.currentNucleus.name, boldTextFieldStyle);
EditorGUI.EndDisabledGroup();
if (GUILayout.Button("Reimport"))
ReimportCluster(parentCluster);
EditorGUILayout.EndHorizontal();
if (this.currentNucleus == null) {
OutputsInspector(ref anythingChanged);
return;
}
else {
string newName = EditorGUILayout.TextField(this.currentNucleus.name, boldTextFieldStyle);
if (newName != this.currentNucleus.name) {
this.currentNucleus.name = newName;
this.prefab.RefreshOutputs();
// outputsPopup.choices = this.prefab.outputs.Select(output => output.name).ToList();
anythingChanged = true;
}
}
GUIStyle headerStyle = new(EditorStyles.boldLabel) {
alignment = TextAnchor.MiddleLeft,
margin = new RectOffset(10, 0, 4, 4)
};
// Nucleus type
string nucleusType = this.currentNucleus.GetType().Name;
GUILayout.Label(nucleusType, headerStyle);
// Current output value
if (Application.isPlaying) {
if (currentNucleus is Neuron currentNeuron1) {
GUIContent nameLabel = new("Output", currentNeuron1.outputValue.ToString());
EditorGUILayout.FloatField(nameLabel, currentNeuron1.outputMagnitude);
// Nucleus name
Cluster cluster = this.currentNucleus as Cluster;
if (cluster != null) {
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button(this.currentNucleus.parent.name))
OnClusterClick(cluster);
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.TextField(this.currentNucleus.name, boldTextFieldStyle);
EditorGUI.EndDisabledGroup();
if (GUILayout.Button("Reimport"))
ReimportCluster(cluster);
EditorGUILayout.EndHorizontal();
}
else {
string newName = EditorGUILayout.TextField(this.currentNucleus.name, boldTextFieldStyle);
if (newName != this.currentNucleus.name) {
this.currentNucleus.name = newName;
this.prefab.RefreshOutputs();
// outputsPopup.choices = this.prefab.outputs.Select(output => output.name).ToList();
anythingChanged = true;
}
}
// Current output value
if (Application.isPlaying) {
if (currentNucleus is Neuron currentNeuron1) {
GUIContent nameLabel = new("Output", currentNeuron1.outputValue.ToString());
EditorGUILayout.FloatField(nameLabel, currentNeuron1.outputMagnitude);
}
else
EditorGUILayout.LabelField(" ");
}
else
EditorGUILayout.LabelField(" ");
// Memory cell
if (this.currentNucleus is MemoryCell memory)
MemoryCellInspector(memory, ref anythingChanged);
// Cluster
else if (cluster != null) //(this.currentNucleus is Cluster cluster)
ClusterInspector(cluster, ref anythingChanged);
// Other
else
NucleusInspector(this.currentNucleus, ref anythingChanged);
if (GUILayout.Button("Delete"))
DeleteNucleus(this.currentNucleus);
}
else
EditorGUILayout.LabelField(" ");
// Memory cell
if (this.currentNucleus is MemoryCell memory)
MemoryCellInspector(memory, ref anythingChanged);
// Cluster
else if (this.currentNucleus is Cluster cluster)
ClusterInspector(cluster, ref anythingChanged);
// Other
else
NucleusInspector(this.currentNucleus, ref anythingChanged);
if (GUILayout.Button("Delete"))
DeleteNucleus(this.currentNucleus);
serializedObject.ApplyModifiedProperties();
if (anythingChanged) {
@ -222,6 +210,24 @@ namespace NanoBrain {
}
}
protected void OutputsInspector(ref bool anythingChanged) {
GUIStyle headerStyle = new(EditorStyles.boldLabel) {
alignment = TextAnchor.MiddleLeft,
margin = new RectOffset(10, 0, 4, 4)
};
GUILayout.Label("Outputs", headerStyle);
bool connecting = GUILayout.Button("Add Output Neuron");
if (connecting) {
Nucleus newOutput = new Neuron(this.prefab, "New Output");
// Regenerate the temporary clsuter instance
// See also the constructor
this.currentCluster = new (this.prefab);
this.currentNucleus = newOutput;
this.selectedOutput = this.currentNucleus;
}
}
protected void MemoryCellInspector(MemoryCell memoryCell, ref bool anythingChanged) {
memoryCell.staticMemory = EditorGUILayout.Toggle("Static Memory", memoryCell.staticMemory);
NucleusInspector(memoryCell, ref anythingChanged);
@ -430,15 +436,6 @@ namespace NanoBrain {
case Nucleus.Type.Cluster:
AddClusterInput(nucleus);
break;
// case Nucleus.Type.Receptor:
// AddReceptorInput(nucleus);
// break;
// case Nucleus.Type.ClusterReceptor:
// AddClusterReceptorInput(nucleus);
// break;
// case Nucleus.Type.ClusterArray:
// AddClusterArrayInput(nucleus);
// break;
default:
break;
}
@ -535,15 +532,12 @@ namespace NanoBrain {
}
}
}
this.prefab.nuclei.Remove(nucleus);
this.currentCluster.DeleteNucleus(nucleus);//clusterNuclei.Remove(nucleus);
// if (outputsPopup.value == nucleus.name) {
// this.prefab.RefreshOutputs();
// // outputsPopup.choices = this.prefab.outputs.Select(output => output.name).ToList();
// // outputsPopup.index = 0;
// }
Neuron.Delete(nucleus);
// this.prefab.nuclei.Remove(nucleus);
// Neuron.Delete(nucleus);
this.prefab.RefreshOutputs();
this.currentNucleus = this.prefab.output;
this.selectedOutput = this.currentNucleus;

View File

@ -528,10 +528,6 @@ namespace NanoBrain {
#endregion ClusterArray
//public Dictionary<string, Nucleus> nucleiDict = new();
public List<Nucleus> _inputs = null;
public virtual List<Nucleus> inputs {
get {
@ -643,6 +639,9 @@ namespace NanoBrain {
return this._outputs;
}
}
public void RefreshOutputs() {
this._outputs = null;
}
public bool TryGetNucleus(string nucleusName, out Nucleus foundNucleus) {
foreach (Nucleus receptor in this.clusterNuclei) {
@ -685,6 +684,22 @@ namespace NanoBrain {
}
}
public bool DeleteNucleus(Nucleus nucleus) {
if (this.clusterNuclei.Contains(nucleus) == false) {
// Try to find the nucleus by name
if (TryGetNucleus(nucleus.name, out nucleus) == false)
return false;
}
Neuron.Delete(nucleus);
int nucleusIx = this.clusterNuclei.IndexOf(nucleus);
this.clusterNuclei.Remove(nucleus);
this.prefab.nuclei.RemoveAt(nucleusIx);
RefreshOutputs();
return true;
}
#region Receivers
public virtual List<Nucleus> CollectReceivers() {

View File

@ -33,8 +33,10 @@ namespace NanoBrain {
public Neuron(ClusterPrefab prefab, string name) {
this.clusterPrefab = prefab;
this.name = name;
if (this.clusterPrefab != null)
if (this.clusterPrefab != null) {
this.clusterPrefab.nuclei.Add(this);
this.clusterPrefab.RefreshOutputs();
}
else
Debug.LogError("No prefab when adding neuron to prefab");
}