diff --git a/Cluster.cs b/Cluster.cs index 0393422..e240d58 100644 --- a/Cluster.cs +++ b/Cluster.cs @@ -78,21 +78,21 @@ public class Cluster : Nucleus { } } - // Copy nucleus arrays + // Copy nucleus arrays for receptors for (int nucleusIx = 0; nucleusIx < prefabNuclei.Length; nucleusIx++) { - Nucleus prefabReceptor = prefabNuclei[nucleusIx]; - if (prefabReceptor is not Receptor prefabNucleus) + Nucleus prefabNucleus = prefabNuclei[nucleusIx]; + if (prefabNucleus is not IReceptor prefabReceptor) continue; - if (prefabNucleus.nucleiArray == null || prefabNucleus.nucleiArray.Length == 0) + if (prefabReceptor.nucleiArray == null || prefabReceptor.nucleiArray.Length == 0) continue; - Receptor clonedNucleus = clonedNuclei[nucleusIx] as Receptor; - if (prefabNucleus == prefabNucleus.nucleiArray[0]) { + IReceptor clonedNucleus = clonedNuclei[nucleusIx] as IReceptor; + if (prefabReceptor == prefabReceptor.nucleiArray[0]) { // We clone the array only for the first entry - NucleusArray clonedArray = new(prefabNucleus.nucleiArray.Length, "array"); + NucleusArray clonedArray = new(prefabReceptor.nucleiArray.Length, "array"); int arrayIx = 0; - foreach (Nucleus prefabArrayNucleus in prefabNucleus.nucleiArray) { + foreach (Nucleus prefabArrayNucleus in prefabReceptor.nucleiArray) { int arrayNucleusIx = GetNucleusIndex(prefabNuclei, prefabArrayNucleus); if (arrayNucleusIx >= 0) { Nucleus clonedArrayNucleus = clonedNuclei[arrayNucleusIx]; @@ -103,12 +103,13 @@ public class Cluster : Nucleus { } arrayIx++; } - clonedNucleus.array = clonedArray; + //clonedNucleus.array = clonedArray; + clonedNucleus.nucleiArray = clonedArray.nuclei; } else { // The others will refer to the array created for the first nucleus in the array - int firstNucleusIx = GetNucleusIndex(prefabNuclei, prefabNucleus.nucleiArray[0]); - Receptor clonedFirstNucleus = clonedNuclei[firstNucleusIx] as Receptor; + int firstNucleusIx = GetNucleusIndex(prefabNuclei, prefabReceptor.nucleiArray[0]); + IReceptor clonedFirstNucleus = clonedNuclei[firstNucleusIx] as IReceptor; clonedNucleus.nucleiArray = clonedFirstNucleus.nucleiArray; } } @@ -237,12 +238,13 @@ public class Cluster : Nucleus { return -1; } - protected int GetNucleusIndex(List nuclei, Nucleus nucleus) { + public int GetNucleusIndex(List nuclei, Nucleus nucleus) { int i = 0; foreach (Nucleus nucleiElement in nuclei) { //for (int i = 0; i < nuclei.Length; i++) { if (nucleus == nucleiElement) return i; + i++; } return -1; } diff --git a/ClusterReceptor.cs b/ClusterReceptor.cs index 66eabc7..48a2c85 100644 --- a/ClusterReceptor.cs +++ b/ClusterReceptor.cs @@ -25,9 +25,9 @@ public class ClusterReceptor : Cluster, IReceptor { public override Nucleus ShallowCloneTo(Cluster parent) { ClusterReceptor clone = new(this.prefab, parent, this.name) { clusterPrefab = this.clusterPrefab, - array = this.array + //array = this.array }; - + //CloneFields(clone); // This cloned the prefab with the clusternuclei, // but did not clone the receivers outside the cluster RestoreExternalReceivers(clone, this.clusterPrefab, parent); @@ -37,9 +37,10 @@ public class ClusterReceptor : Cluster, IReceptor { public override Nucleus Clone(ClusterPrefab parent) { ClusterReceptor clone = new(prefab, parent, this.name) { - array = this.array + array = this._array }; + //CloneFields(clone); foreach (Synapse synapse in this.synapses) { Synapse clonedSynapse = clone.AddSynapse(synapse.nucleus); clonedSynapse.weight = synapse.weight; @@ -61,10 +62,29 @@ public class ClusterReceptor : Cluster, IReceptor { return clone; } + public override List CollectReceivers() { + List receivers = new(); + foreach (Nucleus element in this.nucleiArray) { + if (element is not Cluster clusterElement) + continue; + + //receivers.AddRange(clusterElement.CollectReceivers()); + foreach (Neuron output in clusterElement.outputs) { + foreach (Nucleus receiver in output.receivers) { + // Only add receivers outside clusterElement cluster + if (receiver.clusterPrefab != clusterElement.prefab && + receivers.Contains(receiver) == false) + receivers.Add(receiver); + } + } + } + return receivers; + } + [SerializeReference] private NucleusArray _array; public NucleusArray array { - get { return _array; } + //get { return _array; } set { _array = value; } } @@ -76,11 +96,15 @@ public class ClusterReceptor : Cluster, IReceptor { } public void AddReceptorElement(ClusterPrefab prefab) { - this.nucleiArray = IReceptorHelpers.AddReceptorElement(this.nucleiArray, prefab); + IReceptorHelpers.AddReceptorElement(this, prefab); } public void RemoveReceptorElement() { - this.nucleiArray = IReceptorHelpers.RemoveReceptorElement(this.nucleiArray); + IReceptorHelpers.RemoveReceptorElement(this); + } + + public void AddArrayReceiver(Nucleus receiverToAdd, float weight = 1) { + IReceptorHelpers.AddArrayReceiver(this, receiverToAdd, weight); } public override void UpdateStateIsolated() { @@ -107,7 +131,7 @@ public class ClusterReceptor : Cluster, IReceptor { } public override void ProcessStimulus(Vector3 inputValue, int thingId = 0, string thingName = null) { - this.array ??= new NucleusArray(this.parent); - this.array.ProcessStimulus(thingId, inputValue, thingName); + this._array ??= new NucleusArray(this.parent); + this._array.ProcessStimulus(thingId, inputValue, thingName); } } \ No newline at end of file diff --git a/Editor/ClusterInspector.cs b/Editor/ClusterInspector.cs index 1c7f84a..7af110c 100644 --- a/Editor/ClusterInspector.cs +++ b/Editor/ClusterInspector.cs @@ -16,25 +16,9 @@ public class ClusterInspector : Editor { #region Start - private void OnEnable() { - // Load an icon from resources or assets - Texture2D icon = Resources.Load("ClusterIcon.png"); - - // Ensure the texture is valid; set the icon for the ScriptableObject - if (icon != null) { - EditorGUIUtility.SetIconForObject(target, icon); - } - } - public override VisualElement CreateInspectorGUI() { ClusterPrefab prefab = target as ClusterPrefab; - // string path = AssetDatabase.GetAssetPath(prefab); // or known path - // Debug.Log($"{path}"); - // ClusterPrefab currentWrapper = AssetDatabase.LoadAssetAtPath(path); - // if (currentWrapper == null) - // Debug.LogError("CreateInspectorGUI: Cluster Prefab is not found on disk"); - if (prefab != null) prefab.EnsureInitialization(); @@ -482,9 +466,9 @@ public class ClusterInspector : Editor { fontStyle = FontStyle.Bold, }; - if (nucleus is Receptor receptor1) { - // draw the array size label + if (nucleus is IReceptor receptor1) { if (expandArray) { + // Put array indices above elements style.alignment = TextAnchor.LowerCenter; Vector3 labelPos1 = position + Vector3.down * (size + 5); // below disc int colonPos1 = nucleus.name.IndexOf(":"); @@ -494,6 +478,7 @@ public class ClusterInspector : Editor { } } else { + // draw the array size label if (color.grayscale > 0.5f) style.normal.textColor = Color.black; else @@ -503,33 +488,14 @@ public class ClusterInspector : Editor { } } - if (nucleus is ClusterReceptor clusterReceptor) { - // draw the array size label - if (expandArray && clusterReceptor.array.nuclei.First() == this.currentNucleus) { - style.alignment = TextAnchor.LowerCenter; - Vector3 labelPos2 = position + Vector3.down * (size + 5); // below disc - int colonPos2 = nucleus.name.IndexOf(":"); - if (colonPos2 > 0) { - string extName = nucleus.name[(colonPos2 + 2)..]; - Handles.Label(labelPos2, extName, style); - } - } - else { - if (color.grayscale > 0.5f) - style.normal.textColor = Color.black; - else - style.normal.textColor = Color.white; - Handles.Label(labelPosition, clusterReceptor.array.nuclei.Length.ToString(), style); - style.normal.textColor = Color.white; - } - } - - if (!expandArray || nucleus is not Receptor) { + if (expandArray == false) { + // put name below nucleus Vector3 labelPos = position - Vector3.down * (size + 5); // below neuron style.alignment = TextAnchor.UpperCenter; int colonPos = nucleus.name.IndexOf(":"); if (colonPos > 0 && colonPos < nucleus.name.Length - 2) { + // if it is an array, we should not show the :0 of the first element string baseName = nucleus.name[..colonPos]; Handles.Label(labelPos, baseName, style); } @@ -652,22 +618,8 @@ public class ClusterInspector : Editor { if (this.currentNucleus is MemoryCell memory) { memory.staticMemory = EditorGUILayout.Toggle("Static Memory", memory.staticMemory); } - // if (this.currentNucleus is ReceptorArray receptor) { - // EditorGUILayout.BeginHorizontal(); - // EditorGUILayout.IntField("Receptor size", receptor.instances.Count()); - // if (GUILayout.Button("Add")) { - // Undo.RecordObject(prefabAsset, "Receptor add " + prefabAsset.name); - // receptor.AddReceptor(this.prefab); - // anythingChanged = true; - // } - // if (GUILayout.Button("Del")) { - // Undo.RecordObject(prefabAsset, "Receptor delete " + prefabAsset.name); - // receptor.RemoveReceptor(); - // anythingChanged = true; - // } - // EditorGUILayout.EndHorizontal(); - // } - if (this.currentNucleus is Receptor receptor1) { + + if (this.currentNucleus is IReceptor receptor1) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.IntField("Array size", receptor1.nucleiArray.Count()); if (GUILayout.Button("Add")) { @@ -684,21 +636,21 @@ public class ClusterInspector : Editor { } EditorGUILayout.EndHorizontal(); } - else if (this.currentNucleus is ClusterReceptor receptor2) { - EditorGUILayout.BeginHorizontal(); - EditorGUILayout.IntField("Array size", receptor2.array.nuclei.Count()); - if (GUILayout.Button("Add")) { - Undo.RecordObject(prefabAsset, "Array add " + prefabAsset.name); - receptor2.array.AddNucleus(this.prefab); - anythingChanged = true; - } - if (GUILayout.Button("Del")) { - Undo.RecordObject(prefabAsset, "Array delete " + prefabAsset.name); - receptor2.array.RemoveNucleus(); - anythingChanged = true; - } - EditorGUILayout.EndHorizontal(); - } + // else if (this.currentNucleus is ClusterReceptor receptor2) { + // EditorGUILayout.BeginHorizontal(); + // EditorGUILayout.IntField("Array size", receptor2.array.nuclei.Count()); + // if (GUILayout.Button("Add")) { + // Undo.RecordObject(prefabAsset, "Array add " + prefabAsset.name); + // receptor2.array.AddNucleus(this.prefab); + // anythingChanged = true; + // } + // if (GUILayout.Button("Del")) { + // Undo.RecordObject(prefabAsset, "Array delete " + prefabAsset.name); + // receptor2.array.RemoveNucleus(); + // anythingChanged = true; + // } + // EditorGUILayout.EndHorizontal(); + // } // Synapses @@ -749,6 +701,7 @@ public class ClusterInspector : Editor { EditorGUILayout.BeginHorizontal(); if (synapse.nucleus.parent != null && synapse.nucleus.parent != this.currentNucleus) { + // If it is a cluster GUIStyle labelStyle = new(GUI.skin.label); float labelWidth = 200; if (synapse.nucleus.clusterPrefab != null) { @@ -997,7 +950,7 @@ public class ClusterInspector : Editor { EditorGUILayout.EndHorizontal(); if (connecting) { Nucleus nucleus = nuclei.ElementAt(selectedConnectNucleus); - if (nucleus is Receptor receptor) + if (nucleus is IReceptor receptor) receptor.AddArrayReceiver(this.currentNucleus); else if (nucleus is Neuron neuron) neuron.AddReceiver(this.currentNucleus); @@ -1058,9 +1011,36 @@ public class ClusterInspector : Editor { protected virtual void ChangeSynapse(Synapse synapse, Neuron newNucleus) { Neuron synapseNeuron = synapse.nucleus as Neuron; if (synapse.nucleus.parent is Cluster subCluster && subCluster.prefab != this.prefab) { - // it is a neuron in a subcluster - synapseNeuron.RemoveReceiver(this.currentNucleus); - newNucleus.AddReceiver(this.currentNucleus); + if (synapse.nucleus.parent is ClusterReceptor receptor) { + // the new nucleus is part of a (cluster) receptor, + // so we have to change all synapses to this nucleus array elements + int oldNucleusIx = subCluster.GetNucleusIndex(subCluster.clusterNuclei, synapse.nucleus); + int newNucleusIx = subCluster.GetNucleusIndex(subCluster.clusterNuclei, newNucleus); + foreach(Nucleus element in receptor.nucleiArray) { + if (element is not ClusterReceptor clusterReceptor) + continue; + // Get the same neuron as the synapse.nucleus in a different element + // of the ClusterReceptor array + Nucleus oldElementNucleus = clusterReceptor.clusterNuclei[oldNucleusIx]; + if (oldElementNucleus is not Neuron oldElementNeuron) + continue; + // Get the same neuron as newNucleus in a different element + // of the ClusterReceptor array + Nucleus newElementNucleus = clusterReceptor.clusterNuclei[newNucleusIx]; + if (newElementNucleus is not Neuron newElementNeuron) + continue; + + oldElementNeuron.RemoveReceiver(this.currentNucleus); + newElementNeuron.AddReceiver(this.currentNucleus); + // Now find the synapse which pointed to the old Neuron + // Synapse synapseForUpdate = this.currentNucleus.GetSynapse(oldElementNeuron); + // synapseForUpdate.nucleus = newElementNeuron; + } + } else { + // it is a neuron in a subcluster + synapseNeuron.RemoveReceiver(this.currentNucleus); + newNucleus.AddReceiver(this.currentNucleus); + } } else { synapseNeuron.RemoveReceiver(this.currentNucleus); diff --git a/IReceptor.cs b/IReceptor.cs index 08bb505..5dee615 100644 --- a/IReceptor.cs +++ b/IReceptor.cs @@ -11,41 +11,66 @@ public interface IReceptor { public void AddReceptorElement(ClusterPrefab prefab); public void RemoveReceptorElement(); + public void AddArrayReceiver(Nucleus receiverToAdd, float weight = 1); + public void ProcessStimulus(Vector3 inputValue, int thingId = 0, string thingName = null); } public static class IReceptorHelpers { - public static Nucleus[] AddReceptorElement(Nucleus[] nucleiArray, ClusterPrefab prefab) { - if (nucleiArray.Length == 0) { + + public static void AddReceptorElement(IReceptor receptor, ClusterPrefab prefab) { + if (receptor.nucleiArray.Length == 0) { Debug.LogError("Empty perceptoid array, cannot add"); - return null; } - int newLength = nucleiArray.Length + 1; + int newLength = receptor.nucleiArray.Length + 1; Nucleus[] newArray = new Nucleus[newLength]; - for (int i = 0; i < nucleiArray.Length; i++) - newArray[i] = nucleiArray[i]; - if (nucleiArray[0] is Nucleus nucleus) { + string baseName = receptor.GetName(); + int colonPos = baseName.IndexOf(":"); + if (colonPos > 0) + baseName = baseName[..colonPos]; + + for (int i = 0; i < receptor.nucleiArray.Length; i++) + newArray[i] = receptor.nucleiArray[i]; + if (receptor.nucleiArray[0] is Nucleus nucleus) { newArray[newLength - 1] = nucleus.Clone(prefab); - newArray[newLength - 1].name += $": {newLength - 1}"; + newArray[newLength - 1].name = $"{baseName}: {newLength - 1}"; } - return newArray; + foreach (Nucleus element in receptor.nucleiArray) { + if (element is IReceptor receptorElement) { + receptorElement.nucleiArray = newArray; + } + } } - public static Nucleus[] RemoveReceptorElement(Nucleus[] nucleiArray) { - int newLength = nucleiArray.Length - 1; + public static void RemoveReceptorElement(IReceptor receptor) { + int newLength = receptor.nucleiArray.Length - 1; if (newLength == 0) { Debug.LogWarning("Perceptoid array cannot be empty"); - return null; } - Nucleus[] newPerceptei = new Nucleus[newLength]; + Nucleus[] newArray = new Nucleus[newLength]; for (int i = 0; i < newLength; i++) - newPerceptei[i] = nucleiArray[i]; + newArray[i] = receptor.nucleiArray[i]; // Delete the last perception - if (nucleiArray[newLength] is Nucleus nucleus) + if (receptor.nucleiArray[newLength] is Nucleus nucleus) Neuron.Delete(nucleus); //this._nuclei[newLength]); - return newPerceptei; + foreach (Nucleus element in receptor.nucleiArray) { + if (element is IReceptor receptorElement) { + receptorElement.nucleiArray = newArray; + } + } + + } + + public static void AddArrayReceiver(IReceptor receptor, Nucleus receiverToAdd, float weight = 1) { + foreach (Nucleus element in receptor.nucleiArray) { + if (element is Cluster cluster) + cluster.defaultOutput.AddReceiver(receiverToAdd, weight); + if (element is Neuron neuron) + neuron.AddReceiver(receiverToAdd, weight); + } + } } \ No newline at end of file diff --git a/Receptor.cs b/Receptor.cs index e89837c..48ecb5a 100644 --- a/Receptor.cs +++ b/Receptor.cs @@ -48,11 +48,13 @@ public class Receptor : Neuron, IReceptor { } public void AddReceptorElement(ClusterPrefab prefab) { - this.nucleiArray = IReceptorHelpers.AddReceptorElement(this.nucleiArray, prefab); + //this.nucleiArray = IReceptorHelpers.AddReceptorElement(this.nucleiArray, prefab); + IReceptorHelpers.AddReceptorElement(this, prefab); } public void RemoveReceptorElement() { - this.nucleiArray = IReceptorHelpers.RemoveReceptorElement(this.nucleiArray); + // this.nucleiArray = IReceptorHelpers.RemoveReceptorElement(this.nucleiArray); + IReceptorHelpers.RemoveReceptorElement(this); } // public override void AddReceiver(Nucleus receiverToAdd, float weight = 1) { @@ -65,11 +67,12 @@ public class Receptor : Neuron, IReceptor { // } public virtual void AddArrayReceiver(Nucleus receiverToAdd, float weight = 1) { - foreach (Nucleus element in this._array.nuclei) { - if (element is Neuron neuron) { - neuron.AddReceiver(receiverToAdd, weight); - } - } + IReceptorHelpers.AddArrayReceiver(this, receiverToAdd, weight); + // foreach (Nucleus element in this._array.nuclei) { + // if (element is Neuron neuron) { + // neuron.AddReceiver(receiverToAdd, weight); + // } + // } } public override void UpdateStateIsolated() {