(A little) Performance improvements

This commit is contained in:
Pascal Serrarens 2026-01-06 15:03:52 +01:00
parent 14a786246c
commit 2134495045
16 changed files with 589 additions and 823 deletions

View File

@ -1,23 +1,19 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true root = true
[*]
indent_style = space
indent_size = 4
end_of_line = crlf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = false
max_line_length = 80
[*.cs] [*.cs]
# Use tabs for indentation
indent_style = space # To use spaces, you could set this to 'space'
indent_size = 2 # Standard C# indent size
# Alignment and Style Rules
csharp_indent_case_contents = true
csharp_new_line_after_open_brace = all
csharp_new_line_before_open_brace = none csharp_new_line_before_open_brace = none
csharp_new_line_before_else = true # Suppress warnings everywhere
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true
# Limit the number of characters in a line
max_line_length = 100 # This setting does not enforce it; it's a guideline.
[*.{cs,vb}]
dotnet_diagnostic.IDE1006.severity = none dotnet_diagnostic.IDE1006.severity = none
dotnet_diagnostic.IDE0130.severity = none

View File

@ -3,7 +3,8 @@ using System;
using Vector3Float = UnityEngine.Vector3; using Vector3Float = UnityEngine.Vector3;
#endif #endif
namespace LinearAlgebra { namespace LinearAlgebra
{
/// <summary> /// <summary>
/// A direction in 3D space /// A direction in 3D space
@ -16,7 +17,8 @@ namespace LinearAlgebra {
/// rotation has been applied. /// rotation has been applied.
/// The angles are automatically normalized to stay within the abovenmentioned /// The angles are automatically normalized to stay within the abovenmentioned
/// ranges. /// ranges.
public struct Direction { public struct Direction
{
/// @brief horizontal angle, range = (-180..180] degrees /// @brief horizontal angle, range = (-180..180] degrees
public AngleFloat horizontal; public AngleFloat horizontal;
/// @brief vertical angle, range in degrees = (-90..90] degrees /// @brief vertical angle, range in degrees = (-90..90] degrees
@ -29,7 +31,8 @@ namespace LinearAlgebra {
/// <param name="vertical">The vertical angle</param> /// <param name="vertical">The vertical angle</param>
/// <remarks>The direction will be normalized automatically /// <remarks>The direction will be normalized automatically
/// to ensure the angles are within the allowed ranges</remarks> /// to ensure the angles are within the allowed ranges</remarks>
public Direction(AngleFloat horizontal, AngleFloat vertical) { public Direction(AngleFloat horizontal, AngleFloat vertical)
{
this.horizontal = horizontal; this.horizontal = horizontal;
this.vertical = vertical; this.vertical = vertical;
this.Normalize(); this.Normalize();
@ -43,8 +46,10 @@ namespace LinearAlgebra {
/// <returns>The direction</returns> /// <returns>The direction</returns>
/// <remarks>The direction will be normalized automatically /// <remarks>The direction will be normalized automatically
/// to ensure the angles are within the allowed ranges</remarks> /// to ensure the angles are within the allowed ranges</remarks>
public static Direction Degrees(float horizontal, float vertical) { public static Direction Degrees(float horizontal, float vertical)
Direction d = new() { {
Direction d = new()
{
horizontal = AngleFloat.Degrees(horizontal), horizontal = AngleFloat.Degrees(horizontal),
vertical = AngleFloat.Degrees(vertical) vertical = AngleFloat.Degrees(vertical)
}; };
@ -57,8 +62,10 @@ namespace LinearAlgebra {
/// <param name="horizontal">The horizontal angle in radians</param> /// <param name="horizontal">The horizontal angle in radians</param>
/// <param name="vertical">The vertical angle in radians</param> /// <param name="vertical">The vertical angle in radians</param>
/// <returns>The direction</returns> /// <returns>The direction</returns>
public static Direction Radians(float horizontal, float vertical) { public static Direction Radians(float horizontal, float vertical)
Direction d = new() { {
Direction d = new()
{
horizontal = AngleFloat.Radians(horizontal), horizontal = AngleFloat.Radians(horizontal),
vertical = AngleFloat.Radians(vertical) vertical = AngleFloat.Radians(vertical)
}; };
@ -66,7 +73,8 @@ namespace LinearAlgebra {
return d; return d;
} }
public override readonly string ToString() { public override readonly string ToString()
{
return $"Direction(h: {this.horizontal}, v: {this.vertical})"; return $"Direction(h: {this.horizontal}, v: {this.vertical})";
} }
@ -99,8 +107,10 @@ namespace LinearAlgebra {
/// </summary> /// </summary>
public readonly static Direction right = Degrees(90, 0); public readonly static Direction right = Degrees(90, 0);
private void Normalize() { private void Normalize()
if (this.vertical > AngleFloat.deg90 || this.vertical < -AngleFloat.deg90) { {
if (this.vertical > AngleFloat.deg90 || this.vertical < -AngleFloat.deg90)
{
this.horizontal += AngleFloat.deg180; this.horizontal += AngleFloat.deg180;
this.vertical = AngleFloat.deg180 - this.vertical; this.vertical = AngleFloat.deg180 - this.vertical;
} }
@ -142,11 +152,12 @@ namespace LinearAlgebra {
/// Convert the direction into a carthesian vector /// Convert the direction into a carthesian vector
/// </summary> /// </summary>
/// <returns>The carthesian vector corresponding to this direction.</returns> /// <returns>The carthesian vector corresponding to this direction.</returns>
public Vector3Float ToVector3() { public Vector3Float ToVector3()
{
// Quaternion q = Quaternion.Euler(90 - this.vertical.inDegrees, this.horizontal.inDegrees, 0); // Quaternion q = Quaternion.Euler(90 - this.vertical.inDegrees, this.horizontal.inDegrees, 0);
// Vector3Float v = q * Vector3Float.forward; // Vector3Float v = q * Vector3Float.forward;
// return v; // return v;
// Convert degrees to radians // Convert degrees to radians
float radH = this.horizontal.inRadians; float radH = this.horizontal.inRadians;
float radV = this.vertical.inRadians; float radV = this.vertical.inRadians;
@ -166,7 +177,8 @@ namespace LinearAlgebra {
/// <returns>The direction</returns> /// <returns>The direction</returns>
/// <remarks>Information about the length of the carthesian vector is not /// <remarks>Information about the length of the carthesian vector is not
/// included in this transformation</remarks> /// included in this transformation</remarks>
public static Direction FromVector3(Vector3Float v) { public static Direction FromVector3(Vector3Float v)
{
AngleFloat horizontal = AngleFloat.Atan2(v.horizontal, v.depth); AngleFloat horizontal = AngleFloat.Atan2(v.horizontal, v.depth);
AngleFloat vertical = AngleFloat.deg90 - AngleFloat.Acos(v.vertical); AngleFloat vertical = AngleFloat.deg90 - AngleFloat.Acos(v.vertical);
Direction d = new(horizontal, vertical); Direction d = new(horizontal, vertical);
@ -181,7 +193,8 @@ namespace LinearAlgebra {
/// <param name="d1"></param> /// <param name="d1"></param>
/// <param name="d2"></param> /// <param name="d2"></param>
/// <returns>True when the direction angles are equal, false otherwise.</returns> /// <returns>True when the direction angles are equal, false otherwise.</returns>
public static bool operator ==(Direction d1, Direction d2) { public static bool operator ==(Direction d1, Direction d2)
{
bool horizontalEq = d1.horizontal == d2.horizontal; bool horizontalEq = d1.horizontal == d2.horizontal;
bool verticalEq = d1.vertical == d2.vertical; bool verticalEq = d1.vertical == d2.vertical;
return horizontalEq && verticalEq; return horizontalEq && verticalEq;
@ -193,13 +206,15 @@ namespace LinearAlgebra {
/// <param name="d1"></param> /// <param name="d1"></param>
/// <param name="d2"></param> /// <param name="d2"></param>
/// <returns>True when the direction angles are not equal, false otherwise.</returns> /// <returns>True when the direction angles are not equal, false otherwise.</returns>
public static bool operator !=(Direction d1, Direction d2) { public static bool operator !=(Direction d1, Direction d2)
{
bool horizontalNEq = d1.horizontal != d2.horizontal; bool horizontalNEq = d1.horizontal != d2.horizontal;
bool verticalNEq = d1.vertical != d2.vertical; bool verticalNEq = d1.vertical != d2.vertical;
return horizontalNEq || verticalNEq; return horizontalNEq || verticalNEq;
} }
public override readonly bool Equals(object obj) { public override readonly bool Equals(object obj)
{
if (obj is not Direction d) if (obj is not Direction d)
return false; return false;
@ -209,10 +224,32 @@ namespace LinearAlgebra {
} }
public override readonly int GetHashCode() { public override readonly int GetHashCode()
{
return HashCode.Combine(horizontal, vertical); return HashCode.Combine(horizontal, vertical);
} }
public static AngleFloat UnsignedAngle(Direction d1, Direction d2)
{
// Convert angles from degrees to radians
float horizontal1Rad = d1.horizontal.inRadians;
float vertical1Rad = d1.vertical.inRadians;
float horizontal2Rad = d2.horizontal.inRadians;
float vertical2Rad = d2.vertical.inRadians;
// Calculate the cosine of the angle using the spherical law of cosines
float cosTheta = MathF.Sin(vertical1Rad) * MathF.Sin(vertical2Rad) +
MathF.Cos(vertical1Rad) * MathF.Cos(vertical2Rad) *
MathF.Cos(horizontal1Rad - horizontal2Rad);
// Clip cosTheta to the valid range for acos
cosTheta = Float.Clamp(cosTheta, -1.0f, 1.0f);
// Calculate the angle
AngleFloat angle = AngleFloat.Acos(cosTheta);
return angle;
}
} }
} }

View File

@ -137,5 +137,30 @@ namespace LinearAlgebra {
return r; return r;
} }
public static float Distance(Spherical v1, Spherical v2) {
// Convert degrees to radians
float thetaARadians = v1.direction.horizontal.inRadians;
float phiARadians = v1.direction.vertical.inRadians;// DegreesToRadians(phiA);
float thetaBRadians = v2.direction.horizontal.inRadians; // DegreesToRadians(thetaB);
float phiBRadians = v2.direction.vertical.inRadians; // DegreesToRadians(phiB);
// Calculate sine and cosine values
float sinPhiA = MathF.Sin(phiARadians);
float cosPhiA = MathF.Cos(phiARadians);
float sinPhiB = MathF.Sin(phiBRadians);
float cosPhiB = MathF.Cos(phiBRadians);
// Calculate the cosine of the difference in azimuthal angles
float cosThetaDifference = MathF.Cos(thetaARadians - thetaBRadians);
// Apply the spherical law of cosines
float distance = MathF.Sqrt(
v1.distance * v1.distance +
v2.distance * v2.distance -
2 * v1.distance * v2.distance * (sinPhiA * sinPhiB * cosThetaDifference + cosPhiA * cosPhiB)
);
return distance;
}
} }
} }

View File

@ -74,7 +74,7 @@ public class Neuroid : Nucleus {
} }
public virtual void UpdateState() { public virtual void UpdateState() {
Vector3 result = Vector3.zero; Vector3 resultVector = Vector3.zero;
foreach (Synapse synapse in this.synapses) { foreach (Synapse synapse in this.synapses) {
Nucleus synapseNucleus = synapse.nucleus; Nucleus synapseNucleus = synapse.nucleus;
if (synapseNucleus is Neuroid neuroid && neuroid.isSleeping) if (synapseNucleus is Neuroid neuroid && neuroid.isSleeping)
@ -87,14 +87,21 @@ public class Neuroid : Nucleus {
float magnitude = weight * activatedValue; float magnitude = weight * activatedValue;
//Debug.Log($"{this.name} {synapseNucleus.outputValue.direction} {synapseNucleus.outputValue.direction.horizontal}{synapseNucleus.outputValue.direction.vertical} {direction}"); //Debug.Log($"{this.name} {synapseNucleus.outputValue.direction} {synapseNucleus.outputValue.direction.horizontal}{synapseNucleus.outputValue.direction.vertical} {direction}");
result += direction * magnitude; resultVector += direction * magnitude;
} }
if (average && this.synapses.Count > 0) if (average && this.synapses.Count > 0)
result /= this.synapses.Count; resultVector /= this.synapses.Count;
this.outputValue = Spherical.FromVector3(result); Spherical result = Spherical.FromVector3(resultVector);
float d = Spherical.Distance(result, this.outputValue);
if (d < 0.1f) {
//Debug.Log($"insignificant update: {d}");
return;
}
this.outputValue = result; //Spherical.FromVector3(resultVector);
//this.stale = 0; //this.stale = 0;
this.Refresh(); //this.Refresh();
foreach (Receiver receiver in this.receivers) { foreach (Receiver receiver in this.receivers) {
if (receiver.nucleus is Neuroid neuroid) if (receiver.nucleus is Neuroid neuroid)

View File

@ -76,13 +76,12 @@ public class Nucleus {
} }
[System.NonSerialized] [System.NonSerialized]
private int stale = 0; private int stale = 1000;
public bool isSleeping => this.stale > 2; public bool isSleeping => this.stale > 2;
public void Refresh() {
//this.stale = 0;
}
public void IncreaseAge() { public void IncreaseAge() {
this.stale++; this.stale++;
if (isSleeping)
_outputValue = Spherical.zero;
} }
[System.NonSerialized] [System.NonSerialized]
public int layerIx; public int layerIx;

View File

@ -29,32 +29,32 @@ public class Perception : Nucleus {
return null; return null;
} }
public void SendPositions(Nucleus receivingNeuroid, int thingType = 0, float weight = 1.0f) { // public void SendPositions(Nucleus receivingNeuroid, int thingType = 0, float weight = 1.0f) {
Receiver receiver = new() { // Receiver receiver = new() {
thingType = thingType, // thingType = thingType,
neuroid = receivingNeuroid // neuroid = receivingNeuroid
}; // };
positionReceivers.Add(receiver); // positionReceivers.Add(receiver);
foreach (SensoryNeuroid neuroid in sensoryNeuroids) { // foreach (SensoryNeuroid neuroid in sensoryNeuroids) {
if (neuroid != null) { // if (neuroid != null) {
neuroid.AddReceiver(receivingNeuroid); // neuroid.AddReceiver(receivingNeuroid);
receivingNeuroid.SetWeight(neuroid, weight); // receivingNeuroid.SetWeight(neuroid, weight);
} // }
} // }
} // }
public void SendVelocities(Nucleus receivingNeuroid, int thingType = 0, float weight = 1.0f) { // public void SendVelocities(Nucleus receivingNeuroid, int thingType = 0, float weight = 1.0f) {
Receiver receiver = new() { // Receiver receiver = new() {
thingType = thingType, // thingType = thingType,
neuroid = receivingNeuroid // neuroid = receivingNeuroid
}; // };
velocityReceivers.Add(receiver); // velocityReceivers.Add(receiver);
foreach (SensoryNeuroid neuroid in sensoryNeuroids) { // foreach (SensoryNeuroid neuroid in sensoryNeuroids) {
if (neuroid != null && neuroid.velocityNeuroid != null) { // if (neuroid != null && neuroid.velocityNeuroid != null) {
neuroid.velocityNeuroid.AddReceiver(receivingNeuroid); // neuroid.velocityNeuroid.AddReceiver(receivingNeuroid);
receivingNeuroid.SetWeight(neuroid, weight); // receivingNeuroid.SetWeight(neuroid, weight);
} // }
} // }
} // }
public void ProcessStimulus(int thingId, int thingType, Vector3 localPosition, string name = "Sensing") { public void ProcessStimulus(int thingId, int thingType, Vector3 localPosition, string name = "Sensing") {
int availableIx = -1; int availableIx = -1;
@ -97,10 +97,10 @@ public class Perception : Nucleus {
receiver.neuroid.GetInputFrom(neuroid); receiver.neuroid.GetInputFrom(neuroid);
} }
} }
foreach (Receiver receiver in velocityReceivers) { // foreach (Receiver receiver in velocityReceivers) {
if (receiver.thingType == 0 || receiver.thingType == thingType) // if (receiver.thingType == 0 || receiver.thingType == thingType)
receiver.neuroid.GetInputFrom(neuroid.velocityNeuroid); // receiver.neuroid.GetInputFrom(neuroid.velocityNeuroid);
} // }
//neuroid.receptor.position = localPosition; //neuroid.receptor.position = localPosition;
neuroid.receptor.ProcessStimulus(333, localPosition); neuroid.receptor.ProcessStimulus(333, localPosition);

View File

@ -6,7 +6,7 @@ public class Perceptoid : Neuroid {
// A neuroid which has no neurons as input // A neuroid which has no neurons as input
// But receives value from a receptor // But receives value from a receptor
public Receptor receptor; public Receptor receptor;
public VelocityNeuroid velocityNeuroid; //public VelocityNeuroid velocityNeuroid;
#region Serialization #region Serialization
@ -16,12 +16,8 @@ public class Perceptoid : Neuroid {
public override void Rebuild(NanoBrainObj brain) { public override void Rebuild(NanoBrainObj brain) {
base.Rebuild(brain); base.Rebuild(brain);
//this.receptor = new Receptor(this); this.receptor = Receptor.GetReceptor(brain, thingType);
this.receptor = Perceptoid.GetReceptor(brain, thingType); this.receptor.perceptei.Add(this);
if (this.receptor == null)
this.receptor = new Receptor(this);
else
this.receptor.perceptei.Add(this);
} }
public override void Deserialize(Nucleus nucleus) { public override void Deserialize(Nucleus nucleus) {
@ -52,18 +48,18 @@ public class Perceptoid : Neuroid {
#endregion Serialization #endregion Serialization
public Perceptoid(NanoBrainObj brain, string name) : base(name) { // public Perceptoid(NanoBrainObj brain, string name) : base(name) {
this.brain = brain; // this.brain = brain;
if (this.brain != null) { // if (this.brain != null) {
this.brain.perceptei.Add(this); // this.brain.perceptei.Add(this);
} // }
else // else
Debug.LogError("No neuroid network"); // Debug.LogError("No neuroid network");
this.nucleusType = nameof(Perceptoid); // this.nucleusType = nameof(Perceptoid);
this.name = name; // this.name = name;
this.receptor = new Receptor(this); // this.receptor = new Receptor(this);
} // }
public Perceptoid(NanoBrainObj brain, int thingType, string name = "sensor") : base(name) { public Perceptoid(NanoBrainObj brain, int thingType, string name = "sensor") : base(name) {
this.brain = brain; this.brain = brain;
@ -76,26 +72,25 @@ public class Perceptoid : Neuroid {
this.nucleusType = nameof(Perceptoid); this.nucleusType = nameof(Perceptoid);
this.name = name; this.name = name;
this.thingType = thingType; this.thingType = thingType;
this.receptor = new Receptor(this) { this.receptor = Receptor.GetReceptor(brain, thingType);
thingType = thingType this.receptor.perceptei.Add(this);
}; // this.velocityNeuroid = new(brain, name + ": velocity");
this.velocityNeuroid = new(brain, name + ": velocity"); // // The velocity neuroid received position data from this
// The velocity neuroid received position data from this // this.AddReceiver(velocityNeuroid);
this.AddReceiver(velocityNeuroid);
} }
public void Replace(int thingType, string name = "sensor") { public void Replace(int thingType, string name = "sensor") {
this.name = name; this.name = name;
this.thingType = thingType; //this.thingType = thingType;
this.receptor.thingType = thingType; this.receptor.thingType = thingType;
this.receptor.localPosition = Spherical.zero; this.receptor.localPosition = Spherical.zero;
this.outputValue = Spherical.zero; this.outputValue = Spherical.zero;
this.receivers = new(); this.receivers = new();
this.AddReceiver(velocityNeuroid); // this.AddReceiver(velocityNeuroid);
this.velocityNeuroid.Replace(name + ": velocity"); // this.velocityNeuroid.Replace(name + ": velocity");
} }
public override void UpdateState() { public override void UpdateState() {
@ -119,7 +114,7 @@ public class Perceptoid : Neuroid {
if (receiver.nucleus is Neuroid neuroid) if (receiver.nucleus is Neuroid neuroid)
neuroid.SetInput(this); neuroid.SetInput(this);
//this.stale = 0; //this.stale = 0;
this.Refresh(); //this.Refresh();
} }
public void UpdateState(int thingId, Spherical receptorValue) { public void UpdateState(int thingId, Spherical receptorValue) {
@ -139,18 +134,23 @@ public class Perceptoid : Neuroid {
// if (average && this.synapses.Count > 0) // if (average && this.synapses.Count > 0)
// result /= this.synapses.Count + 1; // result /= this.synapses.Count + 1;
float d = Spherical.Distance(result, this.outputValue);
if (d < 0.1f) {
//Debug.Log($"insignificant update: {d}");
return;
}
this.outputValue = result; this.outputValue = result;
foreach (Receiver receiver in this.receivers) foreach (Receiver receiver in this.receivers)
if (receiver.nucleus is Neuroid neuroid) if (receiver.nucleus is Neuroid neuroid)
neuroid.SetInput(this); neuroid.SetInput(this);
//this.stale = 0; //this.stale = 0;
this.Refresh(); //this.Refresh();
} }
public static Perceptoid GetPerception(NanoBrainObj brain, int thingType = 0) { public static Perceptoid GetPerception(NanoBrainObj brain, int thingType = 0) {
foreach (Nucleus nucleus in brain.nuclei) { foreach (Nucleus nucleus in brain.nuclei) {
if (nucleus is Perceptoid perceptoid && (thingType == 0 || perceptoid.thingType == thingType)) if (nucleus is Perceptoid perceptoid && (thingType == 0 || perceptoid.receptor.thingType == thingType))
return perceptoid; return perceptoid;
} }
return null; return null;
@ -159,7 +159,7 @@ public class Perceptoid : Neuroid {
public static void ProcessStimulus(NanoBrainObj brain, int thingType, Vector3 localPosition) { public static void ProcessStimulus(NanoBrainObj brain, int thingType, Vector3 localPosition) {
Perceptoid selectedPerceptoid = null; Perceptoid selectedPerceptoid = null;
foreach (Perceptoid nucleus in brain.perceptei) { foreach (Perceptoid nucleus in brain.perceptei) {
if (nucleus is Perceptoid perceptoid && (thingType == 0 || perceptoid.thingType == thingType)) if (nucleus is Perceptoid perceptoid && (thingType == 0 || perceptoid.receptor.thingType == thingType))
if (selectedPerceptoid == null) { if (selectedPerceptoid == null) {
selectedPerceptoid = perceptoid; selectedPerceptoid = perceptoid;
if (perceptoid.isSleeping) { if (perceptoid.isSleeping) {
@ -176,11 +176,15 @@ public class Perceptoid : Neuroid {
//selectedPerceptoid.receptor.position = localPosition; //selectedPerceptoid.receptor.position = localPosition;
selectedPerceptoid.receptor.ProcessStimulus(888, localPosition); selectedPerceptoid.receptor.ProcessStimulus(888, localPosition);
} }
public static Receptor GetReceptor(NanoBrainObj brain, int thingType) {
foreach (Perceptoid perceptoid in brain.perceptei) { // public static Receptor GetReceptor(NanoBrainObj brain, int thingType) {
if (thingType == 0 || perceptoid.thingType == thingType) // foreach (Perceptoid perceptoid in brain.perceptei) {
return perceptoid.receptor; // if (perceptoid.receptor != null) {
} // if (thingType == 0 || perceptoid.receptor.thingType == thingType)
return null; // return perceptoid.receptor;
} // }
// }
// Receptor receptor = new(thingType);
// return receptor;
// }
} }

View File

@ -1,47 +1,92 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using UnityEngine; using UnityEngine;
using LinearAlgebra; using LinearAlgebra;
public class Receptor { public class Receptor {
/// <summary>
/// The list of perceptoid which can process stimuli from this receptor
/// </summary>
public List<Perceptoid> perceptei = new(); public List<Perceptoid> perceptei = new();
public int thingId; //public int thingId;
public int thingType; private int _thingType = 0;
public int thingType {
get { return _thingType; }
set {
_thingType = value;
foreach (Perceptoid perceptoid in perceptei) {
perceptoid.thingType = _thingType;
}
}
}
public Spherical localPosition; public Spherical localPosition;
public float distanceResolution = 0.1f;
public float directionResolution = 5;
public Receptor(Perceptoid perceptoid) { public Receptor(NanoBrainObj brain, int thingType) {
this.perceptei.Add(perceptoid); this.thingType = thingType;
//this.perceptei.Add(perceptoid);
brain.receptors.Add(this);
} }
public static Receptor GetReceptor(NanoBrainObj brain, int thingType) {
foreach (Receptor receptor in brain.receptors) {
if (thingType == 0 || receptor.thingType == thingType)
return receptor;
}
Receptor newReceptor = new(brain, thingType);
return newReceptor;
}
public virtual void ProcessStimulus(int thingId, Vector3 localPosition) { public virtual void ProcessStimulus(int thingId, Vector3 newLocalPositionVector) {
this.thingId = thingId; Spherical newLocalPosition = Spherical.FromVector3(newLocalPositionVector);
this.localPosition = Spherical.FromVector3(localPosition);
Spherical previousLocalPosition = this.localPosition;
this.localPosition = newLocalPosition;
Perceptoid selectedPerceptoid = null; Perceptoid selectedPerceptoid = null;
foreach (Perceptoid perceptoid in this.perceptei) { foreach (Perceptoid perceptoid in this.perceptei) {
if (perceptoid.thingId == this.thingId) { if (perceptoid.thingId == thingId) {
// We found an existing perceptoid for this thing
selectedPerceptoid = perceptoid; selectedPerceptoid = perceptoid;
// Do not look any further
// This does not do a lot....
float deltaDistance = newLocalPosition.distance - previousLocalPosition.distance;
// See if the change is significant
AngleFloat deltaDirection = Direction.UnsignedAngle(newLocalPosition.direction, previousLocalPosition.direction);
if (deltaDistance < this.distanceResolution && deltaDirection.inDegrees < directionResolution) {
// The difference is not significant we don't process this data.
this.localPosition = previousLocalPosition;
return;
}
break; break;
} }
else if (perceptoid.isSleeping) else if (perceptoid.isSleeping) {
// A sleeping perceptoid is not active and can therefore always be reused
selectedPerceptoid = perceptoid; selectedPerceptoid = perceptoid;
// Look further because we could find a existing perceptoid for this thing
}
else if (selectedPerceptoid == null) { else if (selectedPerceptoid == null) {
// If we haven't found a perceptoid yet, just start by taking the first
selectedPerceptoid = perceptoid; selectedPerceptoid = perceptoid;
} }
else if (selectedPerceptoid.isSleeping == false) { else if (selectedPerceptoid.isSleeping == false) {
if (perceptoid.receptor.localPosition.magnitude < selectedPerceptoid.receptor.localPosition.magnitude) // If no existing or sleeping perceptoid is found, we look for the perceptoid
// we the furthest (least interesting) stimulus
if (perceptoid.receptor.localPosition.magnitude < selectedPerceptoid.receptor.localPosition.magnitude) {
Debug.Log($"{selectedPerceptoid.name} {selectedPerceptoid.receptor.localPosition.magnitude} {perceptoid.receptor.localPosition.magnitude} ");
selectedPerceptoid = perceptoid; selectedPerceptoid = perceptoid;
}
} }
} }
if (selectedPerceptoid == null) { if (selectedPerceptoid == null) {
Debug.Log("No perceptoid selected, stimulus is ignored"); Debug.Log("No perceptoid selected, stimulus is ignored");
return; return;
} }
//Debug.Log($"Stimulus {thingId} {selectedPerceptoid.thingId}"); // Debug.Log($"Stimulus {thingType} {thingId} {selectedPerceptoid.name}");
selectedPerceptoid.UpdateState(this.thingId, this.localPosition); selectedPerceptoid.UpdateState(thingId, this.localPosition);
} }
} }

View File

@ -37,16 +37,16 @@ public class SensoryNeuroid : Neuroid {
// A neuroid which has no neurons as input // A neuroid which has no neurons as input
// But receives value from a receptor // But receives value from a receptor
public Receptor receptor; public Receptor receptor;
public VelocityNeuroid velocityNeuroid; //public VelocityNeuroid velocityNeuroid;
public SensoryNeuroid(NanoBrainObj brain, int thingId, string name = "sensor") : base(brain, name) { public SensoryNeuroid(NanoBrainObj brain, int thingId, string name = "sensor") : base(brain, name) {
this.name = name + ": position"; this.name = name + ": position";
// this.receptor = new Receptor(this) { // this.receptor = new Receptor(this) {
// thingType = thingId // thingType = thingId
// }; // };
this.velocityNeuroid = new(brain, name + ": velocity"); // this.velocityNeuroid = new(brain, name + ": velocity");
// The velocity neuroid received position data from this // // The velocity neuroid received position data from this
this.AddReceiver(velocityNeuroid); // this.AddReceiver(velocityNeuroid);
} }
public void Replace(int thingId, string name = "sensor") { public void Replace(int thingId, string name = "sensor") {
@ -57,11 +57,11 @@ public class SensoryNeuroid : Neuroid {
this.outputValue = Spherical.zero; this.outputValue = Spherical.zero;
this.receivers = new(); this.receivers = new();
this.AddReceiver(velocityNeuroid); // this.AddReceiver(velocityNeuroid);
// this.velocityNeuroid.name = name + ": velocity"; // // this.velocityNeuroid.name = name + ": velocity";
// this.velocityNeuroid.receivers = new(); // // this.velocityNeuroid.receivers = new();
this.velocityNeuroid.Replace(name + ": velocity"); // this.velocityNeuroid.Replace(name + ": velocity");
} }
public override void UpdateState() { public override void UpdateState() {
@ -88,49 +88,49 @@ public class SensoryNeuroid : Neuroid {
if (receiver.nucleus is Neuroid neuroid) if (receiver.nucleus is Neuroid neuroid)
neuroid.SetInput(this); neuroid.SetInput(this);
//this.stale = 0; //this.stale = 0;
this.Refresh(); //this.Refresh();
} }
} }
public class VelocityNeuroid : Neuroid { // public class VelocityNeuroid : Neuroid {
// Would be best if this was received through a synapse via a loop.... // // Would be best if this was received through a synapse via a loop....
private Vector3 lastPosition = Vector3.zero; // private Vector3 lastPosition = Vector3.zero;
private float lastValueTime = 0; // private float lastValueTime = 0;
public VelocityNeuroid(NanoBrainObj net, string name = "velocity") : base(net, name) { // public VelocityNeuroid(NanoBrainObj net, string name = "velocity") : base(net, name) {
} // }
public void Replace(string name = "velocity") { // public void Replace(string name = "velocity") {
this.name = name; // this.name = name;
this.receivers = new(); // this.receivers = new();
this.lastPosition = Vector3.zero; // this.lastPosition = Vector3.zero;
this.lastValueTime = 0; // this.lastValueTime = 0;
} // }
public override void UpdateState() { // public override void UpdateState() {
// Assuming only one synapse for now.... // // Assuming only one synapse for now....
//Vector3 currentPosition = this.synapses.First().Key.outputValue; // //Vector3 currentPosition = this.synapses.First().Key.outputValue;
Spherical currentPosition = this.synapses.First().nucleus.outputValue; // Spherical currentPosition = this.synapses.First().nucleus.outputValue;
Vector3 currentPositionV3 = currentPosition.ToVector3(); // Vector3 currentPositionV3 = currentPosition.ToVector3();
float currentValueTime = Time.time; // float currentValueTime = Time.time;
if (lastValueTime != 0) { // if (lastValueTime != 0) {
float deltaTime = currentValueTime - lastValueTime; // float deltaTime = currentValueTime - lastValueTime;
Vector3 translation = currentPositionV3 - lastPosition; // Vector3 translation = currentPositionV3 - lastPosition;
Vector3 velocity = translation / deltaTime; // Vector3 velocity = translation / deltaTime;
// No activation function... // // No activation function...
this.outputValue = Spherical.FromVector3(velocity); // this.outputValue = Spherical.FromVector3(velocity);
//this.stale = 0; // //this.stale = 0;
this.Refresh(); // //this.Refresh();
foreach (Receiver receiver in receivers) { // foreach (Receiver receiver in receivers) {
if (receiver.nucleus is Neuroid neuroid) // if (receiver.nucleus is Neuroid neuroid)
neuroid.SetInput(this); // neuroid.SetInput(this);
} // }
} // }
this.lastValueTime = currentValueTime; // this.lastValueTime = currentValueTime;
this.lastPosition = currentPositionV3; // this.lastPosition = currentPositionV3;
} // }
} // }

View File

@ -398,8 +398,9 @@ public class NanoBrainInspector : Editor {
return; return;
this.currentNucleus.name = EditorGUILayout.TextField(this.currentNucleus.name); this.currentNucleus.name = EditorGUILayout.TextField(this.currentNucleus.name);
if (this.currentNucleus is Perceptoid currentPerceptoid) if (this.currentNucleus is Perceptoid currentPerceptoid) {
currentPerceptoid.thingType = EditorGUILayout.IntField("Thing Type", currentPerceptoid.thingType); currentPerceptoid.receptor.thingType = EditorGUILayout.IntField("Thing Type", currentPerceptoid.receptor.thingType);
}
else if (this.currentNucleus is Neuroid neuroid) { else if (this.currentNucleus is Neuroid neuroid) {
EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Activation Curve", GUILayout.Width(150)); EditorGUILayout.LabelField("Activation Curve", GUILayout.Width(150));

View File

@ -12,6 +12,7 @@ public class NanoBrainObj : ScriptableObject, ISerializationCallbackReceiver {
public List<Neuroid> nuclei = new(); public List<Neuroid> nuclei = new();
public List<Perceptoid> perceptei = new(); public List<Perceptoid> perceptei = new();
public List<Receptor> receptors = new();
// This is probably always the first element in the nuclei list... // This is probably always the first element in the nuclei list...
[System.NonSerialized] [System.NonSerialized]
@ -34,14 +35,14 @@ public class NanoBrainObj : ScriptableObject, ISerializationCallbackReceiver {
foreach (Nucleus nucleus in nuclei) { foreach (Nucleus nucleus in nuclei) {
//nucleus.stale++; //nucleus.stale++;
nucleus.IncreaseAge(); nucleus.IncreaseAge();
if (nucleus.isSleeping) // if (nucleus.isSleeping)
nucleus.outputValue = Spherical.zero; // nucleus.outputValue = Spherical.zero;
} }
foreach (Perceptoid perception in perceptei) { foreach (Perceptoid perception in perceptei) {
//perception.stale++; //perception.stale++;
perception.IncreaseAge(); perception.IncreaseAge();
if (perception.isSleeping) // if (perception.isSleeping)
perception.outputValue = Spherical.zero; // perception.outputValue = Spherical.zero;
} }
} }
@ -84,6 +85,9 @@ public class NanoBrainObj : ScriptableObject, ISerializationCallbackReceiver {
if (nucleus.brain == null) if (nucleus.brain == null)
nucleus.brain = this; nucleus.brain = this;
if (nucleus.name == "Boid1")
Debug.Log(" Found boiid1");
visitedNuclei.Add(nucleus); visitedNuclei.Add(nucleus);
if (nucleus.synapses != null) { if (nucleus.synapses != null) {
HashSet<Synapse> visitedSynapses = new(); HashSet<Synapse> visitedSynapses = new();

View File

@ -680,7 +680,7 @@ AudioListener:
m_PrefabInstance: {fileID: 0} m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1633626499} m_GameObject: {fileID: 1633626499}
m_Enabled: 1 m_Enabled: 0
--- !u!20 &1633626501 --- !u!20 &1633626501
Camera: Camera:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0

View File

@ -27,9 +27,9 @@ public class Boid : MonoBehaviour {
this.id = this.GetInstanceID(); this.id = this.GetInstanceID();
nanoBrain = GetComponent<NanoBrainComponent>(); nanoBrain = GetComponent<NanoBrainComponent>();
boundaryReceptor = Perceptoid.GetReceptor(nanoBrain.brain, BoundaryType); boundaryReceptor = Receptor.GetReceptor(nanoBrain.brain, BoundaryType);
boidReceptor = Perceptoid.GetReceptor(nanoBrain.brain, BoidType); boidReceptor = Receptor.GetReceptor(nanoBrain.brain, BoidType);
boidVelocityReceptor = Perceptoid.GetReceptor(nanoBrain.brain, BoidVelocityType); boidVelocityReceptor = Receptor.GetReceptor(nanoBrain.brain, BoidVelocityType);
sc = FindFirstObjectByType<SwarmControl>(); sc = FindFirstObjectByType<SwarmControl>();

View File

@ -1,3 +1,4 @@
/*
public class Roaming : Nucleus { public class Roaming : Nucleus {
public Neuroid avoidance; public Neuroid avoidance;
@ -15,3 +16,4 @@ public class Roaming : Nucleus {
output.AddReceiver(receiver); output.AddReceiver(receiver);
} }
} }
*/

View File

@ -1,37 +1,37 @@
using UnityEngine; // using UnityEngine;
using LinearAlgebra; // using LinearAlgebra;
public class Swarming : Nucleus { // public class Swarming : Nucleus {
public Neuroid cohesion; // public Neuroid cohesion;
public Neuroid alignment; // public Neuroid alignment;
public Neuroid avoidance; // public Neuroid avoidance;
public Neuroid boundary; // public Neuroid boundary;
public Neuroid output; // public Neuroid output;
//public override Spherical outputValue { get => output.outputValue; set => output.outputValue = value; } // //public override Spherical outputValue { get => output.outputValue; set => output.outputValue = value; }
public Swarming(NanoBrainObj brain, Perception perception, SwarmControl sc) : base("Swarming Nucleus") { // public Swarming(NanoBrainObj brain, Perception perception, SwarmControl sc) : base("Swarming Nucleus") {
this.cohesion = new(brain, "Cohesion") { inverse = false }; // this.cohesion = new(brain, "Cohesion") { inverse = false };
perception.SendPositions(this.cohesion, Boid.BoidType); // perception.SendPositions(this.cohesion, Boid.BoidType);
this.alignment = new(brain, "Alignment") { average = true }; // this.alignment = new(brain, "Alignment") { average = true };
perception.SendVelocities(this.alignment, Boid.BoidType); // perception.SendVelocities(this.alignment, Boid.BoidType);
this.avoidance = new(brain, "Avoidance") { inverse = true }; // this.avoidance = new(brain, "Avoidance") { inverse = true };
perception.SendPositions(this.avoidance); // perception.SendPositions(this.avoidance);
this.boundary = new(brain, "Boundary"); // this.boundary = new(brain, "Boundary");
perception.SendPositions(this.boundary, Boid.BoundaryType); // perception.SendPositions(this.boundary, Boid.BoundaryType);
this.output = new(brain, "Swarming"); // this.output = new(brain, "Swarming");
this.output.GetInputFrom(alignment, sc.alignmentForce); // this.output.GetInputFrom(alignment, sc.alignmentForce);
this.output.GetInputFrom(cohesion, sc.cohesionForce); // this.output.GetInputFrom(cohesion, sc.cohesionForce);
this.output.GetInputFrom(avoidance, -sc.avoidanceForce); // this.output.GetInputFrom(avoidance, -sc.avoidanceForce);
this.output.GetInputFrom(boundary, -sc.avoidanceForce); // this.output.GetInputFrom(boundary, -sc.avoidanceForce);
} // }
public override void AddReceiver(Nucleus receiver) { // public override void AddReceiver(Nucleus receiver) {
this.output.AddReceiver(receiver); // this.output.AddReceiver(receiver);
} // }
} // }

File diff suppressed because it is too large Load Diff