From 19398ade9883ae7be07684970d490e3a8dd7457b Mon Sep 17 00:00:00 2001 From: Pascal Serrarens Date: Thu, 12 Feb 2026 17:02:27 +0100 Subject: [PATCH] Improved receptor --- Editor/ClusterInspector.cs | 120 ++++++++++++++++++++++++++----------- ReceptorArray.cs | 94 ++++++++++++++++++----------- 2 files changed, 143 insertions(+), 71 deletions(-) diff --git a/Editor/ClusterInspector.cs b/Editor/ClusterInspector.cs index 22c786b..0b5eed1 100644 --- a/Editor/ClusterInspector.cs +++ b/Editor/ClusterInspector.cs @@ -263,35 +263,69 @@ public class ClusterInspector : Editor { // Draw selected Nucleus if (expandArray) { - float maxValue = 0; - foreach (Nucleus nucleus in this.currentNucleus.array.nuclei) { - float value = length(nucleus.outputValue); - if (value > maxValue) - maxValue = value; - } + if (this.currentNucleus is Receptor receptor) { + float maxValue = 0; + foreach (Nucleus nucleus in receptor.instances) { + float value = length(nucleus.outputValue); + if (value > maxValue) + maxValue = value; + } - float spacing = 400f / this.currentNucleus.array.nuclei.Count(); - float margin = 10 + spacing / 2; - float xMin = 150 - size; - float xMax = 150 + size; - float yMin = 10 + margin - size / 2; - float yMax = 400 - margin + size; - Vector3[] verts = new Vector3[4] { + float spacing = 400f / receptor.instances.Count(); + float margin = 10 + spacing / 2; + float xMin = 150 - size; + float xMax = 150 + size; + float yMin = 10 + margin - size / 2; + float yMax = 400 - margin + size; + Vector3[] verts = new Vector3[4] { new(xMin, yMin, 0), new(xMax, yMin, 0), new(xMax, yMax, 0), new(xMin, yMax, 0) }; - Handles.color = Color.black; - Handles.DrawAAConvexPolygon(verts); - int row = 0; - foreach (Nucleus nucleus in this.currentNucleus.array.nuclei) { - Vector3 pos = new(150, margin + row * spacing, 0.0f); - Handles.color = Color.white; - // The selected nucleus highlight ring - Handles.DrawSolidDisc(pos, Vector3.forward, size + 2); - DrawNucleus(nucleus, pos, maxValue, size); - row++; + Handles.color = Color.black; + Handles.DrawAAConvexPolygon(verts); + int row = 0; + foreach (Nucleus nucleus in receptor.instances) { + Vector3 pos = new(150, margin + row * spacing, 0.0f); + Handles.color = Color.white; + // The selected nucleus highlight ring + Handles.DrawSolidDisc(pos, Vector3.forward, size + 2); + DrawNucleus(nucleus, pos, maxValue, size); + row++; + } + } + else { + float maxValue = 0; + foreach (Nucleus nucleus in this.currentNucleus.array.nuclei) { + float value = length(nucleus.outputValue); + if (value > maxValue) + maxValue = value; + } + + float spacing = 400f / this.currentNucleus.array.nuclei.Count(); + float margin = 10 + spacing / 2; + float xMin = 150 - size; + float xMax = 150 + size; + float yMin = 10 + margin - size / 2; + float yMax = 400 - margin + size; + Vector3[] verts = new Vector3[4] { + new(xMin, yMin, 0), + new(xMax, yMin, 0), + new(xMax, yMax, 0), + new(xMin, yMax, 0) + }; + Handles.color = Color.black; + Handles.DrawAAConvexPolygon(verts); + int row = 0; + foreach (Nucleus nucleus in this.currentNucleus.array.nuclei) { + Vector3 pos = new(150, margin + row * spacing, 0.0f); + Handles.color = Color.white; + // The selected nucleus highlight ring + Handles.DrawSolidDisc(pos, Vector3.forward, size + 2); + DrawNucleus(nucleus, pos, maxValue, size); + row++; + } } } else { @@ -431,9 +465,20 @@ public class ClusterInspector : Editor { if ((!expandArray || nucleus.array.nuclei.First() != this.currentNucleus) && nucleus.array.nuclei.Count() > 1) { Handles.Label(labelPosition, nucleus.array.nuclei.Count().ToString(), style); } - if (!expandArray && nucleus is ReceptorArray receptor) { - Handles.Label(labelPosition, receptor.receptors.Count().ToString(), style); + if (nucleus is Receptor receptor) { + Handles.Label(labelPosition, receptor.instances.Count().ToString(), style); } + // else if (nucleus is ReceptorInstance receptorI) { + // if (expandArray) { + // int arrayIx = 0; + // foreach (ReceptorInstance n in receptorI.receptorArray.receptors) { + // if (n == receptorI) + // break; + // arrayIx++; + // } + // Handles.Label(labelPosition, $"[{arrayIx}]", style); + // } + // } if (expandArray && nucleus.array.nuclei.First() == this.currentNucleus) { int arrayIx = 0; foreach (Nucleus n in nucleus.array.nuclei) { @@ -494,10 +539,12 @@ public class ClusterInspector : Editor { private void HandleClicked(Nucleus nucleus) { if (nucleus == this.currentNucleus) { - if (nucleus is Nucleus n) { - expandArray = !expandArray; - return; - } + expandArray = !expandArray; + } + else if (nucleus is ReceptorInstance receptor) { + expandArray = false; + this.currentNucleus = receptor.receptor; + BuildLayers(); } else if (nucleus is Nucleus n) { this.currentNucleus = n; @@ -562,9 +609,9 @@ public class ClusterInspector : Editor { if (this.currentNucleus is MemoryCell memory) { memory.staticMemory = EditorGUILayout.Toggle("Static Memory", memory.staticMemory); } - if (this.currentNucleus is ReceptorArray receptor) { + if (this.currentNucleus is Receptor receptor) { EditorGUILayout.BeginHorizontal(); - EditorGUILayout.IntField("Receptor size", receptor.receptors.Count()); + EditorGUILayout.IntField("Receptor size", receptor.instances.Count()); if (GUILayout.Button("Add")) { Undo.RecordObject(prefabAsset, "Receptor add " + prefabAsset.name); receptor.AddReceptor(this.prefab); @@ -621,8 +668,11 @@ public class ClusterInspector : Editor { else { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(synapse.nucleus.name); - if (GUILayout.Button("Disconnect")) + if (GUILayout.Button("Disconnect")) { synapse.nucleus.RemoveReceiver(this.currentNucleus); + this.prefab.GarbageCollection(); + anythingChanged = true; + } EditorGUILayout.EndHorizontal(); } @@ -701,8 +751,8 @@ public class ClusterInspector : Editor { void OnSceneGUI(SceneView sceneView) { if (this.gameObject != null) { - if (this.currentNucleus is ReceptorArray receptor) { - foreach (Nucleus nucleus in receptor.receptors) { + if (this.currentNucleus is Receptor receptor && expandArray) { + foreach (Nucleus nucleus in receptor.instances) { Vector3 worldVector = this.gameObject.transform.TransformVector(nucleus.outputValue); Handles.color = Color.yellow; Handles.DrawLine(this.gameObject.transform.position, this.gameObject.transform.position + worldVector); @@ -819,7 +869,7 @@ public class ClusterInspector : Editor { } protected virtual void AddReceptorInput(Nucleus nucleus) { - ReceptorArray newReceptor = new(this.prefab, "New Receptor"); + Receptor newReceptor = new(this.prefab, "New Receptor"); newReceptor.AddReceiver(nucleus); this.currentNucleus = newReceptor; BuildLayers(); diff --git a/ReceptorArray.cs b/ReceptorArray.cs index 27ba835..1cc0533 100644 --- a/ReceptorArray.cs +++ b/ReceptorArray.cs @@ -18,31 +18,43 @@ public class ReceptorInstance : Nucleus { // We explicitly do not add this to the prefab, as it is serialized in the ReceptorArray } public override Nucleus ShallowCloneTo(Cluster parent) { - ReceptorInstance clone = new(parent, name + " +1"); + ReceptorInstance clone = new(parent, name + " +1") { + receptor = this.receptor + }; return clone; } public override Nucleus Clone(ClusterPrefab prefab) { - ReceptorInstance clone = new(prefab, name); + ReceptorInstance clone = new(prefab, name) { + receptor = this.receptor + }; return clone; } + + [SerializeReference] + public Receptor receptor; + public override void UpdateStateIsolated() { } } [Serializable] -public class ReceptorArray : Nucleus { - public ReceptorArray(Cluster parent, string name) { +public class Receptor : Nucleus { + public Receptor(Cluster parent, string name) { this.parent = parent; this.name = name; - this._receptors = new ReceptorInstance[1]; - this._receptors[0] = new ReceptorInstance(parent, this.name + "[0]"); + this._instances = new ReceptorInstance[1]; + this._instances[0] = new ReceptorInstance(parent, this.name + "[0]") { + receptor = this + }; this.parent?.nuclei.Add(this); } - public ReceptorArray(ClusterPrefab prefab, string name) { + public Receptor(ClusterPrefab prefab, string name) { this.cluster = prefab; this.name = name; - this._receptors = new ReceptorInstance[1]; - this._receptors[0] = new ReceptorInstance(prefab, this.name + "[0]"); + this._instances = new ReceptorInstance[1]; + this._instances[0] = new ReceptorInstance(prefab, this.name + "[0]") { + receptor = this + }; if (this.cluster != null) this.cluster.nuclei.Add(this); else @@ -51,22 +63,26 @@ public class ReceptorArray : Nucleus { } public override Nucleus ShallowCloneTo(Cluster parent) { - ReceptorArray clone = new(parent, name) { - _receptors = new ReceptorInstance[this.receptors.Length] + Receptor clone = new(parent, name) { + _instances = new ReceptorInstance[this.instances.Length] }; - for (int ix = 0; ix < this.receptors.Length; ix++) { - clone._receptors[ix] = new ReceptorInstance(parent, $"{this.name} [{ix}]"); + for (int ix = 0; ix < this.instances.Length; ix++) { + clone._instances[ix] = new ReceptorInstance(parent, $"{this.name} [{ix}]") { + receptor = clone + }; } return clone; } public override Nucleus Clone(ClusterPrefab prefab) { - ReceptorArray clone = new(prefab, this.name) { + Receptor clone = new(prefab, this.name) { + _instances = new ReceptorInstance[this.instances.Length] }; - clone._receptors = new ReceptorInstance[this.receptors.Length]; - for (int ix = 0; ix < this.receptors.Length; ix++) { - clone._receptors[ix] = new ReceptorInstance(prefab, this.name); + for (int ix = 0; ix < this.instances.Length; ix++) { + clone._instances[ix] = new ReceptorInstance(prefab, this.name) { + receptor = this + }; } foreach (Synapse synapse in this.synapses) { @@ -80,42 +96,44 @@ public class ReceptorArray : Nucleus { } [SerializeReference] - private ReceptorInstance[] _receptors; - public ReceptorInstance[] receptors { + private ReceptorInstance[] _instances; + public ReceptorInstance[] instances { get { - return _receptors; + return _instances; } } public void AddReceptor(ClusterPrefab prefab) { - if (this._receptors.Length == 0) { + if (this._instances.Length == 0) { Debug.LogError("Empty receptor array, cannot add"); return; } - int newLength = this._receptors.Length + 1; + int newLength = this._instances.Length + 1; ReceptorInstance[] newArray = new ReceptorInstance[newLength]; - for (int i = 0; i < this._receptors.Length; i++) - newArray[i] = this._receptors[i]; - newArray[newLength - 1] = (ReceptorInstance)this._receptors[0].Clone(prefab); + for (int i = 0; i < this._instances.Length; i++) + newArray[i] = this._instances[i]; + ReceptorInstance newReceptor = (ReceptorInstance)this._instances[0].Clone(prefab); + newReceptor.name = $"{this.name} [{this._instances.Length}]"; + newArray[newLength - 1] = newReceptor; - this._receptors = newArray; + this._instances = newArray; } public void RemoveReceptor() { - int newLength = this._receptors.Length - 1; + int newLength = this._instances.Length - 1; if (newLength == 0) { Debug.LogWarning("Receptor array cannot be empty"); return; } ReceptorInstance[] newPerceptei = new ReceptorInstance[newLength]; for (int i = 0; i < newLength; i++) - newPerceptei[i] = this._receptors[i]; + newPerceptei[i] = this._instances[i]; // Delete the last perception - if (this._receptors[newLength] is Nucleus nucleus) + if (this._instances[newLength] is Nucleus nucleus) Neuron.Delete(nucleus); //this._nuclei[newLength]); - this._receptors = newPerceptei; + this._instances = newPerceptei; } private Dictionary thingReceivers = new(); @@ -152,6 +170,8 @@ public class ReceptorArray : Nucleus { private void CleanupReceivers() { // Remove a thing-receiver connection when the nucleus is inactive List receiversToRemove = new(); + thingReceivers ??= new(); + foreach (KeyValuePair item in thingReceivers) { if (item.Value.isSleeping) receiversToRemove.Add(item.Key); @@ -165,7 +185,8 @@ public class ReceptorArray : Nucleus { int colonPos = selectedReceiver.name.IndexOf(":"); if (colonPos > 0) selectedReceiver.name = selectedReceiver.name[..colonPos]; - } + } + } private Nucleus SelectReceptor(int thingId, float3 inputValue) { @@ -173,7 +194,7 @@ public class ReceptorArray : Nucleus { float inputMagnitude = length(inputValue); Nucleus selectedReceiver = null; float selectedMagnitude = 0; - foreach (Nucleus receiver in this._receptors) { + foreach (Nucleus receiver in this._instances) { if (thingReceivers.ContainsValue(receiver) == false) { // We found an unusued receiver Debug.Log($"{thingId} -> [{receiver.name}]"); @@ -217,21 +238,22 @@ public class ReceptorArray : Nucleus { float3 sum = this.bias; // Receptors do not have inputs, so we ignore the synapses - foreach (Nucleus nucleus in this._receptors) + foreach (Nucleus nucleus in this._instances) sum += nucleus.outputValue; - this.outputValue = sum / _receptors.Length; + this.outputValue = sum / _instances.Length; this.stale = 0; UpdateNuclei(); } public override void UpdateNuclei() { - foreach (Nucleus nucleus in this.receptors) { + foreach (Nucleus nucleus in this.instances) { nucleus.stale++; if (nucleus.stale > staleValueForSleep && lengthsq(nucleus.outputValue) > 0) { nucleus.outputValue = Vector3.zero; - this.UpdateStateIsolated(); + //this.UpdateStateIsolated(); + this.parent.UpdateFromNucleus(this); } } }