Added curves

This commit is contained in:
Pascal Serrarens 2025-12-12 16:00:23 +01:00
parent 0adb71f306
commit 76af037a01
4 changed files with 111 additions and 51 deletions

View File

@ -6,13 +6,6 @@ public class Neuroid : Nucleus {
public bool inverse = false;
public float exponent = 1.0f;
// public Neuroid(NanoBrain brain, string name) : base(null, name) {
// this.brain = brain;
// if (this.brain != null)
// this.brain.neuroids.Add(this);
// else
// Debug.LogError("No neuroid network");
// }
public Neuroid(NanoBrainObj brain, string name) : base(name) {
this.brain = brain;
@ -26,16 +19,9 @@ public class Neuroid : Nucleus {
public Neuroid(string name): base(name) {}
public void SetWeight(Neuroid input, float weight) {
//this.synapses[input] = weight;
this.SetWeight((Nucleus)input, weight);
}
// public void GetInputFrom(Neuroid input, float weight = 1.0f) {
// input.AddReceiver(this);
// //this.synapses[input] = weight;
// this.SetWeight((Nucleus)input, weight);
// }
public void SetInput(Neuroid input) {
// if (this.synapses.ContainsKey(input) == false)
// this.synapses[input] = 1.0f;

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
[System.Serializable]
public class Nucleus {
@ -15,7 +16,7 @@ public class Nucleus {
}
[SerializeField]
public List<Synapse> synapses = new();
public List<Synapse> synapses = new();
[SerializeField]
public List<Receiver> receivers = new();
@ -54,7 +55,7 @@ public class Nucleus {
return nucleus;
}
public virtual void Deserialize(Nucleus nucleus) { }
public virtual void Deserialize(Nucleus nucleus) { }
#endregion Serialization
@ -152,6 +153,17 @@ public class Synapse {
public int nucleusId;
public float weight;
public enum CurvePresets {
Linear,
Power,
Sqrt,
Reciprocal,
Custom
}
public CurvePresets curvePreset;
public AnimationCurve curve;
public float curveMax = 1.0f;
public Synapse(Nucleus nucleus, float weight) {
this.nucleus = nucleus;
this.nucleusId = nucleus.id;
@ -177,6 +189,70 @@ public class Synapse {
}
Debug.LogError($"Synapse deserialization error: could not find nucleus with id {this.nucleusId}");
}
public AnimationCurve GenerateCurve() {
switch (this.curvePreset) {
case CurvePresets.Linear:
this.curveMax = this.weight;
return Presets.Linear(this.weight);
case CurvePresets.Power:
this.curveMax = this.weight;
return Presets.Power(2.0f, this.weight);
case CurvePresets.Sqrt:
this.curveMax = this.weight;
return Presets.Power(0.5f, this.weight);
case CurvePresets.Reciprocal:
this.curveMax = 1 / 0.01f * this.weight;
return Presets.Reciprocal(this.weight);
default:
this.curveMax = weight;
return AnimationCurve.Constant(0, 1, weight);
}
}
public static class Presets {
private const int samples = 32;
public static AnimationCurve Linear(float weight) {
return AnimationCurve.Linear(0f, 0f, 1f, weight);
}
public static AnimationCurve Power(float exponent, float weight) {
// build keyframes
Keyframe[] keys = new Keyframe[samples];
for (int i = 0; i < samples; i++) {
float t = i / (float)(samples - 1);
float v = Mathf.Pow(t, exponent) * weight;
keys[i] = new Keyframe(t, v);
}
AnimationCurve curve = new(keys);
// set tangent modes for each key to Auto (smooth). Use Linear if you prefer straight segments.
for (int i = 0; i < curve.length; i++) {
AnimationUtility.SetKeyLeftTangentMode(curve, i, AnimationUtility.TangentMode.Auto);
AnimationUtility.SetKeyRightTangentMode(curve, i, AnimationUtility.TangentMode.Auto);
}
return curve;
}
public static AnimationCurve Reciprocal(float weight) {
int samples = 128;
float xMin = 0.001f;
float xMax = 1;
var keys = new Keyframe[samples];
for (int i = 0; i < samples; i++) {
float t = i / (float)(samples - 1);
float x = Mathf.Lerp(xMin, xMax, t);
float y = 1f / x * weight;
keys[i] = new Keyframe(x, y);
}
var curve = new AnimationCurve(keys);
for (int i = 0; i < curve.length; i++) {
AnimationUtility.SetKeyLeftTangentMode(curve, i, AnimationUtility.TangentMode.Linear);
AnimationUtility.SetKeyRightTangentMode(curve, i, AnimationUtility.TangentMode.Linear);
}
return curve;
}
}
}
[System.Serializable]

View File

@ -403,33 +403,31 @@ public class NanoBrainInspector : Editor {
EditorGUILayout.Vector3Field(GUIContent.none, this.currentNucleus.outputValue);
EditorGUILayout.EndHorizontal();
if (this.currentNucleus.synapses.Count > 0) {
EditorGUILayout.LabelField("Synapses");
EditorGUI.indentLevel++;
foreach (Synapse synapse in this.currentNucleus.synapses) {
if (synapse.nucleus != null) {
EditorGUI.BeginDisabledGroup(synapse.nucleus.isSleeping);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(synapse.nucleus.name, GUILayout.Width(120));
EditorGUI.indentLevel--;
// if (synapse.nucleus is Perceptoid perceptoid) {
// EditorGUILayout.LabelField("Thing", GUILayout.Width(45));
// perceptoid.thingType = EditorGUILayout.IntField(perceptoid.thingType, GUILayout.Width(40));
// }
// else {
EditorGUILayout.LabelField("Weight", GUILayout.Width(45));
synapse.weight = EditorGUILayout.FloatField(synapse.weight, GUILayout.Width(40));
// }
EditorGUI.indentLevel++;
EditorGUILayout.Vector3Field(GUIContent.none, synapse.nucleus.outputValue, GUILayout.Width(180));
EditorGUILayout.LabelField(synapse.nucleus.name, GUILayout.Width(150));
EditorGUILayout.Vector3Field(GUIContent.none, synapse.nucleus.outputValue); //, GUILayout.Width(180));
EditorGUILayout.EndHorizontal();
EditorGUI.indentLevel++;
EditorGUI.BeginChangeCheck();
synapse.weight = EditorGUILayout.FloatField("Weight", synapse.weight);
synapse.curvePreset = (Synapse.CurvePresets)EditorGUILayout.EnumPopup("Preset", synapse.curvePreset);
if (EditorGUI.EndChangeCheck()) {
synapse.curve = synapse.GenerateCurve();
}
if (synapse.curveMax > 0)
EditorGUILayout.CurveField("Curve", synapse.curve, Color.cyan, new Rect(0, 0, 1, synapse.curveMax));
else
EditorGUILayout.CurveField("Curve", synapse.curve, Color.cyan, new Rect(0, synapse.curveMax, 1, -synapse.curveMax));
EditorGUI.indentLevel--;
EditorGUI.EndDisabledGroup();
}
}
EditorGUI.indentLevel--;
//EditorGUI.indentLevel--;
}
if (GUILayout.Button("Add Neuron"))
AddInputNeuron(this.currentNucleus);
@ -437,24 +435,9 @@ public class NanoBrainInspector : Editor {
AddPerceptoid(this.currentNucleus);
if (GUILayout.Button("Delete this neuron"))
DeleteNeuron(this.currentNucleus);
// if (GUILayout.Button("Connect to..."))
// ConnectNucleus(this.currentNucleus);
ConnectNucleus(this.currentNucleus);
DisconnectNucleus(this.currentNucleus);
// GUIStyle toggleButton = new("Button");
// if (connecting) {
// toggleButton.normal = toggleButton.active;
// }
// if (GUILayout.Button(connecting ? "Connecting..." : "Connect to...", toggleButton)) {
// connecting = !connecting;
// if (connecting) {
// names = this.currentNucleus.brain.perceptei.Select(i => i.name).ToArray();
// }
// }
// if (connecting)
// ConnectNucleus(this.currentNucleus);
});
inspectorContainer.Add(container);
@ -503,10 +486,8 @@ public class NanoBrainInspector : Editor {
int selectedIndex = -1;
selectedIndex = EditorGUILayout.Popup("Disconnect from", selectedIndex, names);
if (selectedIndex >= 0 && selectedIndex < this.currentNucleus.brain.perceptei.Count) {
Synapse synapse =this.currentNucleus.synapses[selectedIndex];
//n.AddReceiver(this.currentNucleus);
Synapse synapse = this.currentNucleus.synapses[selectedIndex];
synapse.nucleus.RemoveReceiver(this.currentNucleus);
//BuildLayers();
}
}

View File

@ -24,6 +24,8 @@ MonoBehaviour:
weight: 1
- nucleusId: 1938577052
weight: 10
- nucleusId: 1641120128
weight: -5
receivers: []
nucleusType:
average: 0
@ -53,6 +55,19 @@ MonoBehaviour:
average: 0
inverse: 0
exponent: 1
- id: 1641120128
_name: Separation
synapses:
- nucleusId: -1420275136
weight: 1
- nucleusId: -1266532688
weight: 1
receivers:
- nucleusId: -1707533328
nucleusType:
average: 0
inverse: 0
exponent: 1
perceptei:
- id: 407735232
_name: Boundary
@ -70,6 +85,7 @@ MonoBehaviour:
synapses: []
receivers:
- nucleusId: 1938577052
- nucleusId: 1641120128
nucleusType: Perceptoid
average: 0
inverse: 0
@ -81,6 +97,7 @@ MonoBehaviour:
synapses: []
receivers:
- nucleusId: 1938577052
- nucleusId: 1641120128
nucleusType: Perceptoid
average: 0
inverse: 0