(A little) Performance improvements
This commit is contained in:
parent
14a786246c
commit
2134495045
@ -1,23 +1,19 @@
|
||||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
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]
|
||||
# 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_else = true
|
||||
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
|
||||
# Suppress warnings everywhere
|
||||
dotnet_diagnostic.IDE1006.severity = none
|
||||
dotnet_diagnostic.IDE0130.severity = none
|
||||
@ -3,7 +3,8 @@ using System;
|
||||
using Vector3Float = UnityEngine.Vector3;
|
||||
#endif
|
||||
|
||||
namespace LinearAlgebra {
|
||||
namespace LinearAlgebra
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// A direction in 3D space
|
||||
@ -16,7 +17,8 @@ namespace LinearAlgebra {
|
||||
/// rotation has been applied.
|
||||
/// The angles are automatically normalized to stay within the abovenmentioned
|
||||
/// ranges.
|
||||
public struct Direction {
|
||||
public struct Direction
|
||||
{
|
||||
/// @brief horizontal angle, range = (-180..180] degrees
|
||||
public AngleFloat horizontal;
|
||||
/// @brief vertical angle, range in degrees = (-90..90] degrees
|
||||
@ -29,7 +31,8 @@ namespace LinearAlgebra {
|
||||
/// <param name="vertical">The vertical angle</param>
|
||||
/// <remarks>The direction will be normalized automatically
|
||||
/// 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.vertical = vertical;
|
||||
this.Normalize();
|
||||
@ -43,8 +46,10 @@ namespace LinearAlgebra {
|
||||
/// <returns>The direction</returns>
|
||||
/// <remarks>The direction will be normalized automatically
|
||||
/// to ensure the angles are within the allowed ranges</remarks>
|
||||
public static Direction Degrees(float horizontal, float vertical) {
|
||||
Direction d = new() {
|
||||
public static Direction Degrees(float horizontal, float vertical)
|
||||
{
|
||||
Direction d = new()
|
||||
{
|
||||
horizontal = AngleFloat.Degrees(horizontal),
|
||||
vertical = AngleFloat.Degrees(vertical)
|
||||
};
|
||||
@ -57,8 +62,10 @@ namespace LinearAlgebra {
|
||||
/// <param name="horizontal">The horizontal angle in radians</param>
|
||||
/// <param name="vertical">The vertical angle in radians</param>
|
||||
/// <returns>The direction</returns>
|
||||
public static Direction Radians(float horizontal, float vertical) {
|
||||
Direction d = new() {
|
||||
public static Direction Radians(float horizontal, float vertical)
|
||||
{
|
||||
Direction d = new()
|
||||
{
|
||||
horizontal = AngleFloat.Radians(horizontal),
|
||||
vertical = AngleFloat.Radians(vertical)
|
||||
};
|
||||
@ -66,7 +73,8 @@ namespace LinearAlgebra {
|
||||
return d;
|
||||
}
|
||||
|
||||
public override readonly string ToString() {
|
||||
public override readonly string ToString()
|
||||
{
|
||||
return $"Direction(h: {this.horizontal}, v: {this.vertical})";
|
||||
}
|
||||
|
||||
@ -99,8 +107,10 @@ namespace LinearAlgebra {
|
||||
/// </summary>
|
||||
public readonly static Direction right = Degrees(90, 0);
|
||||
|
||||
private void Normalize() {
|
||||
if (this.vertical > AngleFloat.deg90 || this.vertical < -AngleFloat.deg90) {
|
||||
private void Normalize()
|
||||
{
|
||||
if (this.vertical > AngleFloat.deg90 || this.vertical < -AngleFloat.deg90)
|
||||
{
|
||||
this.horizontal += AngleFloat.deg180;
|
||||
this.vertical = AngleFloat.deg180 - this.vertical;
|
||||
}
|
||||
@ -142,11 +152,12 @@ namespace LinearAlgebra {
|
||||
/// Convert the direction into a carthesian vector
|
||||
/// </summary>
|
||||
/// <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);
|
||||
// Vector3Float v = q * Vector3Float.forward;
|
||||
// return v;
|
||||
// Convert degrees to radians
|
||||
// Convert degrees to radians
|
||||
float radH = this.horizontal.inRadians;
|
||||
float radV = this.vertical.inRadians;
|
||||
|
||||
@ -166,7 +177,8 @@ namespace LinearAlgebra {
|
||||
/// <returns>The direction</returns>
|
||||
/// <remarks>Information about the length of the carthesian vector is not
|
||||
/// 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 vertical = AngleFloat.deg90 - AngleFloat.Acos(v.vertical);
|
||||
Direction d = new(horizontal, vertical);
|
||||
@ -181,7 +193,8 @@ namespace LinearAlgebra {
|
||||
/// <param name="d1"></param>
|
||||
/// <param name="d2"></param>
|
||||
/// <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 verticalEq = d1.vertical == d2.vertical;
|
||||
return horizontalEq && verticalEq;
|
||||
@ -193,13 +206,15 @@ namespace LinearAlgebra {
|
||||
/// <param name="d1"></param>
|
||||
/// <param name="d2"></param>
|
||||
/// <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 verticalNEq = d1.vertical != d2.vertical;
|
||||
return horizontalNEq || verticalNEq;
|
||||
}
|
||||
|
||||
public override readonly bool Equals(object obj) {
|
||||
public override readonly bool Equals(object obj)
|
||||
{
|
||||
if (obj is not Direction d)
|
||||
return false;
|
||||
|
||||
@ -209,10 +224,32 @@ namespace LinearAlgebra {
|
||||
}
|
||||
|
||||
|
||||
public override readonly int GetHashCode() {
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -137,5 +137,30 @@ namespace LinearAlgebra {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -74,7 +74,7 @@ public class Neuroid : Nucleus {
|
||||
}
|
||||
|
||||
public virtual void UpdateState() {
|
||||
Vector3 result = Vector3.zero;
|
||||
Vector3 resultVector = Vector3.zero;
|
||||
foreach (Synapse synapse in this.synapses) {
|
||||
Nucleus synapseNucleus = synapse.nucleus;
|
||||
if (synapseNucleus is Neuroid neuroid && neuroid.isSleeping)
|
||||
@ -87,14 +87,21 @@ public class Neuroid : Nucleus {
|
||||
float magnitude = weight * activatedValue;
|
||||
//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)
|
||||
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.Refresh();
|
||||
//this.Refresh();
|
||||
|
||||
foreach (Receiver receiver in this.receivers) {
|
||||
if (receiver.nucleus is Neuroid neuroid)
|
||||
|
||||
@ -76,13 +76,12 @@ public class Nucleus {
|
||||
}
|
||||
|
||||
[System.NonSerialized]
|
||||
private int stale = 0;
|
||||
private int stale = 1000;
|
||||
public bool isSleeping => this.stale > 2;
|
||||
public void Refresh() {
|
||||
//this.stale = 0;
|
||||
}
|
||||
public void IncreaseAge() {
|
||||
this.stale++;
|
||||
if (isSleeping)
|
||||
_outputValue = Spherical.zero;
|
||||
}
|
||||
[System.NonSerialized]
|
||||
public int layerIx;
|
||||
|
||||
@ -29,32 +29,32 @@ public class Perception : Nucleus {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SendPositions(Nucleus receivingNeuroid, int thingType = 0, float weight = 1.0f) {
|
||||
Receiver receiver = new() {
|
||||
thingType = thingType,
|
||||
neuroid = receivingNeuroid
|
||||
};
|
||||
positionReceivers.Add(receiver);
|
||||
foreach (SensoryNeuroid neuroid in sensoryNeuroids) {
|
||||
if (neuroid != null) {
|
||||
neuroid.AddReceiver(receivingNeuroid);
|
||||
receivingNeuroid.SetWeight(neuroid, weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void SendVelocities(Nucleus receivingNeuroid, int thingType = 0, float weight = 1.0f) {
|
||||
Receiver receiver = new() {
|
||||
thingType = thingType,
|
||||
neuroid = receivingNeuroid
|
||||
};
|
||||
velocityReceivers.Add(receiver);
|
||||
foreach (SensoryNeuroid neuroid in sensoryNeuroids) {
|
||||
if (neuroid != null && neuroid.velocityNeuroid != null) {
|
||||
neuroid.velocityNeuroid.AddReceiver(receivingNeuroid);
|
||||
receivingNeuroid.SetWeight(neuroid, weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
// public void SendPositions(Nucleus receivingNeuroid, int thingType = 0, float weight = 1.0f) {
|
||||
// Receiver receiver = new() {
|
||||
// thingType = thingType,
|
||||
// neuroid = receivingNeuroid
|
||||
// };
|
||||
// positionReceivers.Add(receiver);
|
||||
// foreach (SensoryNeuroid neuroid in sensoryNeuroids) {
|
||||
// if (neuroid != null) {
|
||||
// neuroid.AddReceiver(receivingNeuroid);
|
||||
// receivingNeuroid.SetWeight(neuroid, weight);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// public void SendVelocities(Nucleus receivingNeuroid, int thingType = 0, float weight = 1.0f) {
|
||||
// Receiver receiver = new() {
|
||||
// thingType = thingType,
|
||||
// neuroid = receivingNeuroid
|
||||
// };
|
||||
// velocityReceivers.Add(receiver);
|
||||
// foreach (SensoryNeuroid neuroid in sensoryNeuroids) {
|
||||
// if (neuroid != null && neuroid.velocityNeuroid != null) {
|
||||
// neuroid.velocityNeuroid.AddReceiver(receivingNeuroid);
|
||||
// receivingNeuroid.SetWeight(neuroid, weight);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
public void ProcessStimulus(int thingId, int thingType, Vector3 localPosition, string name = "Sensing") {
|
||||
int availableIx = -1;
|
||||
@ -97,10 +97,10 @@ public class Perception : Nucleus {
|
||||
receiver.neuroid.GetInputFrom(neuroid);
|
||||
}
|
||||
}
|
||||
foreach (Receiver receiver in velocityReceivers) {
|
||||
if (receiver.thingType == 0 || receiver.thingType == thingType)
|
||||
receiver.neuroid.GetInputFrom(neuroid.velocityNeuroid);
|
||||
}
|
||||
// foreach (Receiver receiver in velocityReceivers) {
|
||||
// if (receiver.thingType == 0 || receiver.thingType == thingType)
|
||||
// receiver.neuroid.GetInputFrom(neuroid.velocityNeuroid);
|
||||
// }
|
||||
|
||||
//neuroid.receptor.position = localPosition;
|
||||
neuroid.receptor.ProcessStimulus(333, localPosition);
|
||||
|
||||
@ -6,7 +6,7 @@ public class Perceptoid : Neuroid {
|
||||
// A neuroid which has no neurons as input
|
||||
// But receives value from a receptor
|
||||
public Receptor receptor;
|
||||
public VelocityNeuroid velocityNeuroid;
|
||||
//public VelocityNeuroid velocityNeuroid;
|
||||
|
||||
#region Serialization
|
||||
|
||||
@ -16,12 +16,8 @@ public class Perceptoid : Neuroid {
|
||||
|
||||
public override void Rebuild(NanoBrainObj brain) {
|
||||
base.Rebuild(brain);
|
||||
//this.receptor = new Receptor(this);
|
||||
this.receptor = Perceptoid.GetReceptor(brain, thingType);
|
||||
if (this.receptor == null)
|
||||
this.receptor = new Receptor(this);
|
||||
else
|
||||
this.receptor.perceptei.Add(this);
|
||||
this.receptor = Receptor.GetReceptor(brain, thingType);
|
||||
this.receptor.perceptei.Add(this);
|
||||
}
|
||||
|
||||
public override void Deserialize(Nucleus nucleus) {
|
||||
@ -52,18 +48,18 @@ public class Perceptoid : Neuroid {
|
||||
|
||||
#endregion Serialization
|
||||
|
||||
public Perceptoid(NanoBrainObj brain, string name) : base(name) {
|
||||
this.brain = brain;
|
||||
if (this.brain != null) {
|
||||
this.brain.perceptei.Add(this);
|
||||
}
|
||||
else
|
||||
Debug.LogError("No neuroid network");
|
||||
// public Perceptoid(NanoBrainObj brain, string name) : base(name) {
|
||||
// this.brain = brain;
|
||||
// if (this.brain != null) {
|
||||
// this.brain.perceptei.Add(this);
|
||||
// }
|
||||
// else
|
||||
// Debug.LogError("No neuroid network");
|
||||
|
||||
this.nucleusType = nameof(Perceptoid);
|
||||
this.name = name;
|
||||
this.receptor = new Receptor(this);
|
||||
}
|
||||
// this.nucleusType = nameof(Perceptoid);
|
||||
// this.name = name;
|
||||
// this.receptor = new Receptor(this);
|
||||
// }
|
||||
|
||||
public Perceptoid(NanoBrainObj brain, int thingType, string name = "sensor") : base(name) {
|
||||
this.brain = brain;
|
||||
@ -76,26 +72,25 @@ public class Perceptoid : Neuroid {
|
||||
this.nucleusType = nameof(Perceptoid);
|
||||
this.name = name;
|
||||
this.thingType = thingType;
|
||||
this.receptor = new Receptor(this) {
|
||||
thingType = thingType
|
||||
};
|
||||
this.velocityNeuroid = new(brain, name + ": velocity");
|
||||
// The velocity neuroid received position data from this
|
||||
this.AddReceiver(velocityNeuroid);
|
||||
this.receptor = Receptor.GetReceptor(brain, thingType);
|
||||
this.receptor.perceptei.Add(this);
|
||||
// this.velocityNeuroid = new(brain, name + ": velocity");
|
||||
// // The velocity neuroid received position data from this
|
||||
// this.AddReceiver(velocityNeuroid);
|
||||
}
|
||||
|
||||
public void Replace(int thingType, string name = "sensor") {
|
||||
this.name = name;
|
||||
|
||||
this.thingType = thingType;
|
||||
//this.thingType = thingType;
|
||||
this.receptor.thingType = thingType;
|
||||
this.receptor.localPosition = Spherical.zero;
|
||||
|
||||
this.outputValue = Spherical.zero;
|
||||
this.receivers = new();
|
||||
this.AddReceiver(velocityNeuroid);
|
||||
// this.AddReceiver(velocityNeuroid);
|
||||
|
||||
this.velocityNeuroid.Replace(name + ": velocity");
|
||||
// this.velocityNeuroid.Replace(name + ": velocity");
|
||||
}
|
||||
|
||||
public override void UpdateState() {
|
||||
@ -119,7 +114,7 @@ public class Perceptoid : Neuroid {
|
||||
if (receiver.nucleus is Neuroid neuroid)
|
||||
neuroid.SetInput(this);
|
||||
//this.stale = 0;
|
||||
this.Refresh();
|
||||
//this.Refresh();
|
||||
}
|
||||
|
||||
public void UpdateState(int thingId, Spherical receptorValue) {
|
||||
@ -139,18 +134,23 @@ public class Perceptoid : Neuroid {
|
||||
// if (average && this.synapses.Count > 0)
|
||||
// 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;
|
||||
foreach (Receiver receiver in this.receivers)
|
||||
if (receiver.nucleus is Neuroid neuroid)
|
||||
neuroid.SetInput(this);
|
||||
//this.stale = 0;
|
||||
this.Refresh();
|
||||
//this.Refresh();
|
||||
}
|
||||
|
||||
|
||||
public static Perceptoid GetPerception(NanoBrainObj brain, int thingType = 0) {
|
||||
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 null;
|
||||
@ -159,7 +159,7 @@ public class Perceptoid : Neuroid {
|
||||
public static void ProcessStimulus(NanoBrainObj brain, int thingType, Vector3 localPosition) {
|
||||
Perceptoid selectedPerceptoid = null;
|
||||
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) {
|
||||
selectedPerceptoid = perceptoid;
|
||||
if (perceptoid.isSleeping) {
|
||||
@ -176,11 +176,15 @@ public class Perceptoid : Neuroid {
|
||||
//selectedPerceptoid.receptor.position = localPosition;
|
||||
selectedPerceptoid.receptor.ProcessStimulus(888, localPosition);
|
||||
}
|
||||
public static Receptor GetReceptor(NanoBrainObj brain, int thingType) {
|
||||
foreach (Perceptoid perceptoid in brain.perceptei) {
|
||||
if (thingType == 0 || perceptoid.thingType == thingType)
|
||||
return perceptoid.receptor;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// public static Receptor GetReceptor(NanoBrainObj brain, int thingType) {
|
||||
// foreach (Perceptoid perceptoid in brain.perceptei) {
|
||||
// if (perceptoid.receptor != null) {
|
||||
// if (thingType == 0 || perceptoid.receptor.thingType == thingType)
|
||||
// return perceptoid.receptor;
|
||||
// }
|
||||
// }
|
||||
// Receptor receptor = new(thingType);
|
||||
// return receptor;
|
||||
// }
|
||||
}
|
||||
|
||||
@ -1,47 +1,92 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using LinearAlgebra;
|
||||
|
||||
public class Receptor {
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The list of perceptoid which can process stimuli from this receptor
|
||||
/// </summary>
|
||||
public List<Perceptoid> perceptei = new();
|
||||
|
||||
public int thingId;
|
||||
public int thingType;
|
||||
//public int thingId;
|
||||
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 float distanceResolution = 0.1f;
|
||||
public float directionResolution = 5;
|
||||
|
||||
public Receptor(Perceptoid perceptoid) {
|
||||
this.perceptei.Add(perceptoid);
|
||||
public Receptor(NanoBrainObj brain, int thingType) {
|
||||
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) {
|
||||
this.thingId = thingId;
|
||||
this.localPosition = Spherical.FromVector3(localPosition);
|
||||
public virtual void ProcessStimulus(int thingId, Vector3 newLocalPositionVector) {
|
||||
Spherical newLocalPosition = Spherical.FromVector3(newLocalPositionVector);
|
||||
|
||||
Spherical previousLocalPosition = this.localPosition;
|
||||
this.localPosition = newLocalPosition;
|
||||
|
||||
Perceptoid selectedPerceptoid = null;
|
||||
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;
|
||||
// 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;
|
||||
}
|
||||
else if (perceptoid.isSleeping)
|
||||
else if (perceptoid.isSleeping) {
|
||||
// A sleeping perceptoid is not active and can therefore always be reused
|
||||
selectedPerceptoid = perceptoid;
|
||||
// Look further because we could find a existing perceptoid for this thing
|
||||
}
|
||||
|
||||
else if (selectedPerceptoid == null) {
|
||||
// If we haven't found a perceptoid yet, just start by taking the first
|
||||
selectedPerceptoid = perceptoid;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (selectedPerceptoid == null) {
|
||||
Debug.Log("No perceptoid selected, stimulus is ignored");
|
||||
return;
|
||||
}
|
||||
//Debug.Log($"Stimulus {thingId} {selectedPerceptoid.thingId}");
|
||||
selectedPerceptoid.UpdateState(this.thingId, this.localPosition);
|
||||
// Debug.Log($"Stimulus {thingType} {thingId} {selectedPerceptoid.name}");
|
||||
selectedPerceptoid.UpdateState(thingId, this.localPosition);
|
||||
}
|
||||
}
|
||||
@ -37,16 +37,16 @@ public class SensoryNeuroid : Neuroid {
|
||||
// A neuroid which has no neurons as input
|
||||
// But receives value from a receptor
|
||||
public Receptor receptor;
|
||||
public VelocityNeuroid velocityNeuroid;
|
||||
//public VelocityNeuroid velocityNeuroid;
|
||||
|
||||
public SensoryNeuroid(NanoBrainObj brain, int thingId, string name = "sensor") : base(brain, name) {
|
||||
this.name = name + ": position";
|
||||
// this.receptor = new Receptor(this) {
|
||||
// thingType = thingId
|
||||
// };
|
||||
this.velocityNeuroid = new(brain, name + ": velocity");
|
||||
// The velocity neuroid received position data from this
|
||||
this.AddReceiver(velocityNeuroid);
|
||||
// this.velocityNeuroid = new(brain, name + ": velocity");
|
||||
// // The velocity neuroid received position data from this
|
||||
// this.AddReceiver(velocityNeuroid);
|
||||
}
|
||||
|
||||
public void Replace(int thingId, string name = "sensor") {
|
||||
@ -57,11 +57,11 @@ public class SensoryNeuroid : Neuroid {
|
||||
|
||||
this.outputValue = Spherical.zero;
|
||||
this.receivers = new();
|
||||
this.AddReceiver(velocityNeuroid);
|
||||
// this.AddReceiver(velocityNeuroid);
|
||||
|
||||
// this.velocityNeuroid.name = name + ": velocity";
|
||||
// this.velocityNeuroid.receivers = new();
|
||||
this.velocityNeuroid.Replace(name + ": velocity");
|
||||
// // this.velocityNeuroid.name = name + ": velocity";
|
||||
// // this.velocityNeuroid.receivers = new();
|
||||
// this.velocityNeuroid.Replace(name + ": velocity");
|
||||
}
|
||||
|
||||
public override void UpdateState() {
|
||||
@ -88,49 +88,49 @@ public class SensoryNeuroid : Neuroid {
|
||||
if (receiver.nucleus is Neuroid neuroid)
|
||||
neuroid.SetInput(this);
|
||||
//this.stale = 0;
|
||||
this.Refresh();
|
||||
//this.Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public class VelocityNeuroid : Neuroid {
|
||||
// Would be best if this was received through a synapse via a loop....
|
||||
private Vector3 lastPosition = Vector3.zero;
|
||||
private float lastValueTime = 0;
|
||||
// public class VelocityNeuroid : Neuroid {
|
||||
// // Would be best if this was received through a synapse via a loop....
|
||||
// private Vector3 lastPosition = Vector3.zero;
|
||||
// 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") {
|
||||
this.name = name;
|
||||
this.receivers = new();
|
||||
this.lastPosition = Vector3.zero;
|
||||
this.lastValueTime = 0;
|
||||
}
|
||||
// public void Replace(string name = "velocity") {
|
||||
// this.name = name;
|
||||
// this.receivers = new();
|
||||
// this.lastPosition = Vector3.zero;
|
||||
// this.lastValueTime = 0;
|
||||
// }
|
||||
|
||||
public override void UpdateState() {
|
||||
// Assuming only one synapse for now....
|
||||
//Vector3 currentPosition = this.synapses.First().Key.outputValue;
|
||||
Spherical currentPosition = this.synapses.First().nucleus.outputValue;
|
||||
Vector3 currentPositionV3 = currentPosition.ToVector3();
|
||||
float currentValueTime = Time.time;
|
||||
// public override void UpdateState() {
|
||||
// // Assuming only one synapse for now....
|
||||
// //Vector3 currentPosition = this.synapses.First().Key.outputValue;
|
||||
// Spherical currentPosition = this.synapses.First().nucleus.outputValue;
|
||||
// Vector3 currentPositionV3 = currentPosition.ToVector3();
|
||||
// float currentValueTime = Time.time;
|
||||
|
||||
if (lastValueTime != 0) {
|
||||
float deltaTime = currentValueTime - lastValueTime;
|
||||
Vector3 translation = currentPositionV3 - lastPosition;
|
||||
Vector3 velocity = translation / deltaTime;
|
||||
// if (lastValueTime != 0) {
|
||||
// float deltaTime = currentValueTime - lastValueTime;
|
||||
// Vector3 translation = currentPositionV3 - lastPosition;
|
||||
// Vector3 velocity = translation / deltaTime;
|
||||
|
||||
// No activation function...
|
||||
this.outputValue = Spherical.FromVector3(velocity);
|
||||
//this.stale = 0;
|
||||
this.Refresh();
|
||||
// // No activation function...
|
||||
// this.outputValue = Spherical.FromVector3(velocity);
|
||||
// //this.stale = 0;
|
||||
// //this.Refresh();
|
||||
|
||||
foreach (Receiver receiver in receivers) {
|
||||
if (receiver.nucleus is Neuroid neuroid)
|
||||
neuroid.SetInput(this);
|
||||
}
|
||||
}
|
||||
// foreach (Receiver receiver in receivers) {
|
||||
// if (receiver.nucleus is Neuroid neuroid)
|
||||
// neuroid.SetInput(this);
|
||||
// }
|
||||
// }
|
||||
|
||||
this.lastValueTime = currentValueTime;
|
||||
this.lastPosition = currentPositionV3;
|
||||
}
|
||||
}
|
||||
// this.lastValueTime = currentValueTime;
|
||||
// this.lastPosition = currentPositionV3;
|
||||
// }
|
||||
// }
|
||||
@ -398,8 +398,9 @@ public class NanoBrainInspector : Editor {
|
||||
return;
|
||||
|
||||
this.currentNucleus.name = EditorGUILayout.TextField(this.currentNucleus.name);
|
||||
if (this.currentNucleus is Perceptoid currentPerceptoid)
|
||||
currentPerceptoid.thingType = EditorGUILayout.IntField("Thing Type", currentPerceptoid.thingType);
|
||||
if (this.currentNucleus is Perceptoid currentPerceptoid) {
|
||||
currentPerceptoid.receptor.thingType = EditorGUILayout.IntField("Thing Type", currentPerceptoid.receptor.thingType);
|
||||
}
|
||||
else if (this.currentNucleus is Neuroid neuroid) {
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Activation Curve", GUILayout.Width(150));
|
||||
|
||||
@ -12,6 +12,7 @@ public class NanoBrainObj : ScriptableObject, ISerializationCallbackReceiver {
|
||||
|
||||
public List<Neuroid> nuclei = new();
|
||||
public List<Perceptoid> perceptei = new();
|
||||
public List<Receptor> receptors = new();
|
||||
|
||||
// This is probably always the first element in the nuclei list...
|
||||
[System.NonSerialized]
|
||||
@ -34,14 +35,14 @@ public class NanoBrainObj : ScriptableObject, ISerializationCallbackReceiver {
|
||||
foreach (Nucleus nucleus in nuclei) {
|
||||
//nucleus.stale++;
|
||||
nucleus.IncreaseAge();
|
||||
if (nucleus.isSleeping)
|
||||
nucleus.outputValue = Spherical.zero;
|
||||
// if (nucleus.isSleeping)
|
||||
// nucleus.outputValue = Spherical.zero;
|
||||
}
|
||||
foreach (Perceptoid perception in perceptei) {
|
||||
//perception.stale++;
|
||||
perception.IncreaseAge();
|
||||
if (perception.isSleeping)
|
||||
perception.outputValue = Spherical.zero;
|
||||
// if (perception.isSleeping)
|
||||
// perception.outputValue = Spherical.zero;
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,6 +85,9 @@ public class NanoBrainObj : ScriptableObject, ISerializationCallbackReceiver {
|
||||
if (nucleus.brain == null)
|
||||
nucleus.brain = this;
|
||||
|
||||
if (nucleus.name == "Boid1")
|
||||
Debug.Log(" Found boiid1");
|
||||
|
||||
visitedNuclei.Add(nucleus);
|
||||
if (nucleus.synapses != null) {
|
||||
HashSet<Synapse> visitedSynapses = new();
|
||||
|
||||
@ -680,7 +680,7 @@ AudioListener:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1633626499}
|
||||
m_Enabled: 1
|
||||
m_Enabled: 0
|
||||
--- !u!20 &1633626501
|
||||
Camera:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@ -27,9 +27,9 @@ public class Boid : MonoBehaviour {
|
||||
this.id = this.GetInstanceID();
|
||||
|
||||
nanoBrain = GetComponent<NanoBrainComponent>();
|
||||
boundaryReceptor = Perceptoid.GetReceptor(nanoBrain.brain, BoundaryType);
|
||||
boidReceptor = Perceptoid.GetReceptor(nanoBrain.brain, BoidType);
|
||||
boidVelocityReceptor = Perceptoid.GetReceptor(nanoBrain.brain, BoidVelocityType);
|
||||
boundaryReceptor = Receptor.GetReceptor(nanoBrain.brain, BoundaryType);
|
||||
boidReceptor = Receptor.GetReceptor(nanoBrain.brain, BoidType);
|
||||
boidVelocityReceptor = Receptor.GetReceptor(nanoBrain.brain, BoidVelocityType);
|
||||
|
||||
sc = FindFirstObjectByType<SwarmControl>();
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
/*
|
||||
public class Roaming : Nucleus {
|
||||
public Neuroid avoidance;
|
||||
|
||||
@ -14,4 +15,5 @@ public class Roaming : Nucleus {
|
||||
public override void AddReceiver(Nucleus receiver) {
|
||||
output.AddReceiver(receiver);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
@ -1,37 +1,37 @@
|
||||
using UnityEngine;
|
||||
using LinearAlgebra;
|
||||
// using UnityEngine;
|
||||
// using LinearAlgebra;
|
||||
|
||||
public class Swarming : Nucleus {
|
||||
public Neuroid cohesion;
|
||||
public Neuroid alignment;
|
||||
public Neuroid avoidance;
|
||||
public Neuroid boundary;
|
||||
// public class Swarming : Nucleus {
|
||||
// public Neuroid cohesion;
|
||||
// public Neuroid alignment;
|
||||
// public Neuroid avoidance;
|
||||
// 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") {
|
||||
this.cohesion = new(brain, "Cohesion") { inverse = false };
|
||||
perception.SendPositions(this.cohesion, Boid.BoidType);
|
||||
// public Swarming(NanoBrainObj brain, Perception perception, SwarmControl sc) : base("Swarming Nucleus") {
|
||||
// this.cohesion = new(brain, "Cohesion") { inverse = false };
|
||||
// perception.SendPositions(this.cohesion, Boid.BoidType);
|
||||
|
||||
this.alignment = new(brain, "Alignment") { average = true };
|
||||
perception.SendVelocities(this.alignment, Boid.BoidType);
|
||||
// this.alignment = new(brain, "Alignment") { average = true };
|
||||
// perception.SendVelocities(this.alignment, Boid.BoidType);
|
||||
|
||||
this.avoidance = new(brain, "Avoidance") { inverse = true };
|
||||
perception.SendPositions(this.avoidance);
|
||||
// this.avoidance = new(brain, "Avoidance") { inverse = true };
|
||||
// perception.SendPositions(this.avoidance);
|
||||
|
||||
this.boundary = new(brain, "Boundary");
|
||||
perception.SendPositions(this.boundary, Boid.BoundaryType);
|
||||
// this.boundary = new(brain, "Boundary");
|
||||
// perception.SendPositions(this.boundary, Boid.BoundaryType);
|
||||
|
||||
this.output = new(brain, "Swarming");
|
||||
this.output.GetInputFrom(alignment, sc.alignmentForce);
|
||||
this.output.GetInputFrom(cohesion, sc.cohesionForce);
|
||||
this.output.GetInputFrom(avoidance, -sc.avoidanceForce);
|
||||
this.output.GetInputFrom(boundary, -sc.avoidanceForce);
|
||||
}
|
||||
// this.output = new(brain, "Swarming");
|
||||
// this.output.GetInputFrom(alignment, sc.alignmentForce);
|
||||
// this.output.GetInputFrom(cohesion, sc.cohesionForce);
|
||||
// this.output.GetInputFrom(avoidance, -sc.avoidanceForce);
|
||||
// this.output.GetInputFrom(boundary, -sc.avoidanceForce);
|
||||
// }
|
||||
|
||||
public override void AddReceiver(Nucleus receiver) {
|
||||
this.output.AddReceiver(receiver);
|
||||
}
|
||||
}
|
||||
// public override void AddReceiver(Nucleus receiver) {
|
||||
// this.output.AddReceiver(receiver);
|
||||
// }
|
||||
// }
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user