From f295da9c5559700735ae9ef1b6eee0fc0f1aca8c Mon Sep 17 00:00:00 2001 From: Pascal Serrarens Date: Fri, 13 Feb 2026 14:52:17 +0100 Subject: [PATCH] Further improve receptor / nucleusarray combination --- Editor/ClusterInspector.cs | 63 ++++++++++---- Neuron.cs | 4 +- Nucleus.cs | 7 +- Receptor.cs | 172 ++++++------------------------------- ReceptorArray.cs | 17 ++-- 5 files changed, 91 insertions(+), 172 deletions(-) diff --git a/Editor/ClusterInspector.cs b/Editor/ClusterInspector.cs index be6cfa9..55efb1e 100644 --- a/Editor/ClusterInspector.cs +++ b/Editor/ClusterInspector.cs @@ -263,7 +263,7 @@ public class ClusterInspector : Editor { // Draw selected Nucleus if (expandArray) { - if (this.currentNucleus is Receptor receptor) { + if (this.currentNucleus is ReceptorArray receptor) { float maxValue = 0; foreach (Nucleus nucleus in receptor.instances) { float value = length(nucleus.outputValue); @@ -467,7 +467,7 @@ 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 (nucleus is Receptor receptor) { + if (nucleus is ReceptorArray receptor) { Handles.Label(labelPosition, receptor.instances.Count().ToString(), style); } // else if (nucleus is ReceptorInstance receptorI) { @@ -611,7 +611,7 @@ public class ClusterInspector : Editor { if (this.currentNucleus is MemoryCell memory) { memory.staticMemory = EditorGUILayout.Toggle("Static Memory", memory.staticMemory); } - if (this.currentNucleus is Receptor receptor) { + if (this.currentNucleus is ReceptorArray receptor) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.IntField("Receptor size", receptor.instances.Count()); if (GUILayout.Button("Add")) { @@ -626,6 +626,22 @@ public class ClusterInspector : Editor { } EditorGUILayout.EndHorizontal(); } + if (this.currentNucleus is Receptor receptor1) { + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.IntField("Array size", receptor1.array.nuclei.Count()); + if (GUILayout.Button("Add")) { + Undo.RecordObject(prefabAsset, "Array add " + prefabAsset.name); + receptor1.array.AddNucleus(this.prefab); + anythingChanged = true; + } + if (GUILayout.Button("Del")) { + Undo.RecordObject(prefabAsset, "Array delete " + prefabAsset.name); + receptor1.array.RemoveNucleus(); + anythingChanged = true; + } + EditorGUILayout.EndHorizontal(); + } + // Synapses @@ -707,19 +723,22 @@ public class ClusterInspector : Editor { } if (neuron.array == null || neuron.array.nuclei == null || neuron.array.nuclei.Count() == 0) neuron.array = new NucleusArray(neuron); - EditorGUILayout.BeginHorizontal(); - EditorGUILayout.IntField("Array size", neuron.array.nuclei.Count()); - if (GUILayout.Button("Add")) { - Undo.RecordObject(prefabAsset, "Array add " + prefabAsset.name); - neuron.array.AddNucleus(this.prefab); - anythingChanged = true; - } - if (GUILayout.Button("Del")) { - Undo.RecordObject(prefabAsset, "Array delete " + prefabAsset.name); - neuron.array.RemoveNucleus(); - anythingChanged = true; - } - EditorGUILayout.EndHorizontal(); + + // if (this.currentNucleus is Receptor receptor1) { + // EditorGUILayout.BeginHorizontal(); + // EditorGUILayout.IntField("Array size", receptor1.array.nuclei.Count()); + // if (GUILayout.Button("Add")) { + // Undo.RecordObject(prefabAsset, "Array add " + prefabAsset.name); + // receptor1.array.AddNucleus(this.prefab); + // anythingChanged = true; + // } + // if (GUILayout.Button("Del")) { + // Undo.RecordObject(prefabAsset, "Array delete " + prefabAsset.name); + // receptor1.array.RemoveNucleus(); + // anythingChanged = true; + // } + // EditorGUILayout.EndHorizontal(); + // } } EditorGUILayout.Space(); @@ -753,7 +772,7 @@ public class ClusterInspector : Editor { void OnSceneGUI(SceneView sceneView) { if (this.gameObject != null) { - if (this.currentNucleus is Receptor receptor && expandArray) { + if (this.currentNucleus is ReceptorArray receptor && expandArray) { foreach (Nucleus nucleus in receptor.instances) { Vector3 worldVector = this.gameObject.transform.TransformVector(nucleus.outputValue); Handles.color = Color.yellow; @@ -791,6 +810,9 @@ public class ClusterInspector : Editor { case Nucleus.Type.Receptor: AddReceptorInput(nucleus); break; + case Nucleus.Type.ReceptorArray: + AddReceptorArrayInput(nucleus); + break; default: break; } @@ -877,6 +899,13 @@ public class ClusterInspector : Editor { BuildLayers(); } + protected virtual void AddReceptorArrayInput(Nucleus nucleus) { + ReceptorArray newReceptor = new(this.prefab, "New Receptor"); + newReceptor.AddReceiver(nucleus); + this.currentNucleus = newReceptor; + BuildLayers(); + } + private void OnClusterPicked(Nucleus nucleus, ClusterPrefab prefab) { Cluster subclusterInstance = new(prefab, this.prefab); subclusterInstance.AddReceiver(nucleus); diff --git a/Neuron.cs b/Neuron.cs index 461240a..7719547 100644 --- a/Neuron.cs +++ b/Neuron.cs @@ -14,8 +14,8 @@ public class Neuron : Nucleus { this.name = name; this.parent?.nuclei.Add(this); } - public Neuron(ClusterPrefab parent, string name) { - this.cluster = parent; + public Neuron(ClusterPrefab prefab, string name) { + this.cluster = prefab; this.name = name; if (this.cluster != null) this.cluster.nuclei.Add(this); diff --git a/Nucleus.cs b/Nucleus.cs index 83fb34a..2eead83 100644 --- a/Nucleus.cs +++ b/Nucleus.cs @@ -41,7 +41,8 @@ public abstract class Nucleus { Selector, Cluster, Pulsar, - Receptor + Receptor, + ReceptorArray } #region Synapses @@ -111,6 +112,10 @@ public abstract class Nucleus { } } + public virtual void ProcessStimulus(Vector3 inputValue, int thingId = 0, string thingName = null) { + this.array.ProcessStimulus(thingId, inputValue, thingName); + } + public virtual void ProcessStimulus(int thingId, Vector3 inputValue, string thingName = null) { this.array.ProcessStimulus(thingId, inputValue, thingName); } diff --git a/Receptor.cs b/Receptor.cs index 71a1d0a..38c6181 100644 --- a/Receptor.cs +++ b/Receptor.cs @@ -1,159 +1,43 @@ -/* -using System; -using System.Collections.Generic; using UnityEngine; using Unity.Mathematics; using static Unity.Mathematics.math; -public class Receptor : IReceptor { +[System.Serializable] +public class Receptor : Neuron { + public Receptor(Cluster parent, string name) : base(parent, name) { } + public Receptor(ClusterPrefab prefab, string name) : base(prefab, name) { } - [SerializeField] - protected string _name; - public virtual string name { - get => _name; - set => _name = value; - } - - public Receptor(Cluster parent) { - this.parent = parent; - parent?.nuclei.Add(this); - } - - public Receptor(Cluster parent, string name, string nucleusName) { - this.parent = parent ?? throw new ArgumentNullException(nameof(parent), "Parent cannot be null."); - - this.name = name; - this.parent.nuclei.Add(this); - foreach (INucleus nucleus in parent.inputs) { - if (nucleus != null && nucleus.name == nucleusName) { - this.AddReceiver(nucleus); - } - } - } - - private readonly Cluster parent; - - public virtual IReceptor ShallowCloneTo(Cluster parent) { - Receptor clone = new(parent); + public override Nucleus ShallowCloneTo(Cluster parent) { + Receptor clone = new(parent, name); return clone; } - - public virtual IReceptor Clone() { - Receptor clone = new(this.parent); - - foreach (INucleus receiver in this.receivers) { + public override Nucleus Clone(ClusterPrefab prefab) { + Receptor clone = new(prefab, name); + CloneFields(clone); + // Adding receivers will also add synapses to the receivers + foreach (Nucleus receiver in this.receivers) clone.AddReceiver(receiver); - } - return clone; } - class Receiver { - public INucleus nucleus; - public int thingId; - public string thingName; - public Receiver(INucleus nucleus, int thingId, string thingName) { - this.nucleus = nucleus; - this.thingId = thingId; - this.thingName = thingName; - } + // [SerializeReference] + // private NucleusArray _array; + // public NucleusArray array { + // get { return _array; } + // set { _array = value; } + // } + + + public override void UpdateStateIsolated() { + this.outputValue = this.bias; } - [SerializeReference] - private List _receivers = new(); - public List receivers { - get { return _receivers; } - set { _receivers = value; } - } - - protected int[] thingIds; // every receiver can handle a thing with this id - - public virtual void AddReceiver(INucleus receivingNucleus, float weight = 1) { - this._receivers.Add(receivingNucleus); - receivingNucleus.AddSynapse(this, weight); - } - - public void RemoveReceiver(INucleus receiverNucleus) { - this._receivers.RemoveAll(receiver => receiver == receiverNucleus); - receiverNucleus.synapses.RemoveAll(synapse => synapse.nucleus == this); - } - - private int stale = 1000; - - private bool _isSleeping = false; - public bool isSleeping => _isSleeping; - - private float3 _outputValue; - public float3 outputValue { - get { return this._outputValue; } - set { - this.stale = 0; - this._isSleeping = false; - this._outputValue = value; - } - } - - public virtual void ProcessStimulus(int thingId, Vector3 newLocalPositionVector, string thingName = null) { - this.outputValue = newLocalPositionVector; - if (this._receivers == null) - return; - - thingIds ??= new int[this._receivers.Count]; - - int receiverIx = 0; - INucleus selectedReceiver = null; - int selectedReceiverIx = 0; - foreach (INucleus receiver in this.receivers) { - if (thingIds[receiverIx] == thingId) { - // We found an existing receiver for this thing - selectedReceiver = receiver; - selectedReceiverIx = receiverIx; - // Do not look any further - break; - } - else if (receiver.isSleeping) { - // A sleeping receiver is not active and can therefore always be used - selectedReceiver = receiver; - selectedReceiverIx = receiverIx; - // Look further because we may find an existing receiver for this thing - } - else if (selectedReceiver == null) { - // If we haven't found a receiver yet, just start by taking the first - selectedReceiver = receiver; - selectedReceiverIx = receiverIx; - } - else if (selectedReceiver.isSleeping == false) { - // If no existing or sleeping receiver is found, we look for - // the receiver with the furthest/least interesting stimulus - if (length(receiver.outputValue) < length(selectedReceiver.outputValue)) { - // Debug.Log($"{selectedReceiver.name}[{selectedReceiverIx}] {length(selectedReceiver.outputValue)}" + - // $" {receiver.name}[{receiverIx}] {length(receiver.outputValue)} "); - selectedReceiver = receiver; - selectedReceiverIx = receiverIx; - } - } - receiverIx++; - } - if (selectedReceiverIx >= thingIds.Length) - return; - - thingIds[selectedReceiverIx] = thingId; - if (thingName != null) { - string baseName = selectedReceiver.name; - int colonPos = selectedReceiver.name.IndexOf(":"); - if (colonPos > 0) - baseName = selectedReceiver.name.Substring(0, colonPos); - selectedReceiver.name = baseName + ": " + thingName; - } - Debug.Log($"Receiver {selectedReceiver.name}[{selectedReceiverIx}] for thing {thingId}"); - selectedReceiver.parent.UpdateStateIsolated(); - } - - public void UpdateNuclei() { + public override void UpdateNuclei() { this.stale++; - this._isSleeping = this.stale > 2; - if (isSleeping) - this._outputValue = Vector3.zero; + if (this.stale > staleValueForSleep && lengthsq(this.bias) > 0) { + this.bias = new float3(0, 0, 0); + this.parent.UpdateFromNucleus(this); + } } -} -*/ \ No newline at end of file + +} \ No newline at end of file diff --git a/ReceptorArray.cs b/ReceptorArray.cs index c9e6702..baf2dc2 100644 --- a/ReceptorArray.cs +++ b/ReceptorArray.cs @@ -31,15 +31,15 @@ public class ReceptorInstance : Nucleus { } [SerializeReference] - public Receptor receptor; + public ReceptorArray receptor; public override void UpdateStateIsolated() { } } [Serializable] -public class Receptor : Nucleus { - public Receptor(Cluster parent, string name) { +public class ReceptorArray : Nucleus { + public ReceptorArray(Cluster parent, string name) { this.parent = parent; this.name = name; this._instances = new ReceptorInstance[1]; @@ -48,7 +48,7 @@ public class Receptor : Nucleus { }; this.parent?.nuclei.Add(this); } - public Receptor(ClusterPrefab prefab, string name) { + public ReceptorArray(ClusterPrefab prefab, string name) { this.cluster = prefab; this.name = name; this._instances = new ReceptorInstance[1]; @@ -63,7 +63,7 @@ public class Receptor : Nucleus { } public override Nucleus ShallowCloneTo(Cluster parent) { - Receptor clone = new(parent, name) { + ReceptorArray clone = new(parent, name) { _instances = new ReceptorInstance[this.instances.Length] }; for (int ix = 0; ix < this.instances.Length; ix++) { @@ -76,7 +76,7 @@ public class Receptor : Nucleus { } public override Nucleus Clone(ClusterPrefab prefab) { - Receptor clone = new(prefab, this.name) { + ReceptorArray clone = new(prefab, this.name) { _instances = new ReceptorInstance[this.instances.Length] }; for (int ix = 0; ix < this.instances.Length; ix++) { @@ -96,7 +96,7 @@ public class Receptor : Nucleus { } [SerializeReference] - private ReceptorInstance[] _instances; + private ReceptorInstance[] _instances = new ReceptorInstance[0]; public ReceptorInstance[] instances { get { return _instances; @@ -141,7 +141,7 @@ public class Receptor : Nucleus { public override void ProcessStimulus(int thingId, Vector3 inputValue, string thingName = null) { ProcessStimulus(inputValue, thingId, thingName); } - public void ProcessStimulus(Vector3 inputValue, int thingId = 0, string thingName = null) { + public override void ProcessStimulus(Vector3 inputValue, int thingId = 0, string thingName = null) { CleanupReceivers(); if (!thingReceivers.TryGetValue(thingId, out Nucleus selectedReceiver)) { //Debug.Log($" no receiver found for {thingId}"); @@ -194,6 +194,7 @@ public class Receptor : Nucleus { float inputMagnitude = length(inputValue); Nucleus selectedReceiver = null; float selectedMagnitude = 0; + this._instances ??= new ReceptorInstance[0]; foreach (Nucleus receiver in this._instances) { if (thingReceivers.ContainsValue(receiver) == false) { // We found an unusued receiver