Improved receptor

This commit is contained in:
Pascal Serrarens 2026-02-12 17:02:27 +01:00
parent 537064d84b
commit 19398ade98
2 changed files with 143 additions and 71 deletions

View File

@ -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();

View File

@ -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<int, Nucleus> thingReceivers = new();
@ -152,6 +170,8 @@ public class ReceptorArray : Nucleus {
private void CleanupReceivers() {
// Remove a thing-receiver connection when the nucleus is inactive
List<int> receiversToRemove = new();
thingReceivers ??= new();
foreach (KeyValuePair<int, Nucleus> 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);
}
}
}