Improve persisting changes
This commit is contained in:
parent
8c8d5a5a66
commit
c1bf54a1cc
@ -18,9 +18,17 @@ public class ClusterInspector : Editor {
|
||||
|
||||
public override VisualElement CreateInspectorGUI() {
|
||||
ClusterPrefab prefab = target as ClusterPrefab;
|
||||
|
||||
string path = AssetDatabase.GetAssetPath(prefab); // or known path
|
||||
Debug.Log($"{path}");
|
||||
ClusterPrefab currentWrapper = AssetDatabase.LoadAssetAtPath<ClusterPrefab>(path);
|
||||
if (currentWrapper == null)
|
||||
Debug.LogError("CreateInspectorGUI: Cluster Prefab is not found on disk");
|
||||
|
||||
if (prefab != null)
|
||||
prefab.EnsureInitialization();
|
||||
|
||||
|
||||
serializedObject.Update();
|
||||
|
||||
VisualElement root = new();
|
||||
@ -76,7 +84,7 @@ public class ClusterInspector : Editor {
|
||||
private readonly Dictionary<Nucleus, Vector2Int> neuroidPositions = new();
|
||||
private bool expandArray = false;
|
||||
|
||||
ClusterWrapper currentWrapper;
|
||||
ClusterPrefab prefabAsset;
|
||||
readonly PopupField<string> outputsField;
|
||||
|
||||
public GraphView(ClusterPrefab prefab) {
|
||||
@ -168,9 +176,20 @@ public class ClusterInspector : Editor {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentWrapper != null)
|
||||
DestroyImmediate(currentWrapper);
|
||||
currentWrapper = CreateInstance<ClusterWrapper>().Init(this.currentNucleus, prefab);
|
||||
// if (currentWrapper != null)
|
||||
// DestroyImmediate(currentWrapper);
|
||||
// currentWrapper = CreateInstance<ClusterWrapper>().Init(this.currentNucleus, prefab);
|
||||
|
||||
string path = AssetDatabase.GetAssetPath(this.prefab); // or known path
|
||||
this.prefabAsset = AssetDatabase.LoadAssetAtPath<ClusterPrefab>(path);
|
||||
if (this.prefabAsset == null) {
|
||||
// create and save if it doesn't exist
|
||||
this.prefabAsset = CreateInstance<ClusterPrefab>();
|
||||
// AssetDatabase.CreateAsset(currentWrapper, "Assets/ClusterPrefab.asset");
|
||||
// AssetDatabase.SaveAssets();
|
||||
Debug.LogError("Cluster Prefab is not found on disk");
|
||||
}
|
||||
//currentWrapper.Init(this.currentNucleus, prefab);
|
||||
DrawInspector(inspectorContainer);
|
||||
}
|
||||
|
||||
@ -510,146 +529,164 @@ public class ClusterInspector : Editor {
|
||||
return;
|
||||
|
||||
// create a SerializedObject wrapper so Unity inspector controls work (and Undo)
|
||||
SerializedObject so = new(currentWrapper);
|
||||
IMGUIContainer container = new(() => {
|
||||
if (so.targetObject == null)
|
||||
return;
|
||||
so.Update();
|
||||
|
||||
if (this.currentNucleus == null)
|
||||
return;
|
||||
|
||||
GUIStyle headerStyle = new(EditorStyles.boldLabel) {
|
||||
alignment = TextAnchor.MiddleLeft,
|
||||
margin = new RectOffset(10, 0, 4, 4)
|
||||
};
|
||||
GUIStyle boldTextFieldStyle = new(EditorStyles.textField) {
|
||||
fontStyle = FontStyle.Bold
|
||||
};
|
||||
|
||||
GUILayout.Label(this.currentNucleus.GetType().ToString(), headerStyle);
|
||||
if (this.currentNucleus is Neuron neuron1) {
|
||||
neuron1.type = (Nucleus.Type)EditorGUILayout.EnumPopup(neuron1.type);
|
||||
}
|
||||
string newName = EditorGUILayout.TextField(this.currentNucleus.name, boldTextFieldStyle);
|
||||
if (newName != this.currentNucleus.name) {
|
||||
this.currentNucleus.name = newName;
|
||||
this.prefab.RefreshOutputs();
|
||||
outputsField.choices = this.prefab.outputs.Select(output => output.name).ToList();
|
||||
//outputsField.value = newName;
|
||||
}
|
||||
|
||||
if (Application.isPlaying) {
|
||||
GUIContent nameLabel = new("Output", this.currentNucleus.outputValue.ToString());
|
||||
EditorGUILayout.FloatField(nameLabel, length(this.currentNucleus.outputValue));
|
||||
}
|
||||
else
|
||||
EditorGUILayout.LabelField(" ");
|
||||
|
||||
if (this.currentNucleus is MemoryCell memory) {
|
||||
memory.staticMemory = EditorGUILayout.Toggle("Static Memory", memory.staticMemory);
|
||||
}
|
||||
|
||||
// Synapses
|
||||
|
||||
showSynapses = EditorGUILayout.BeginFoldoutHeaderGroup(showSynapses, "Synapses");
|
||||
if (showSynapses) {
|
||||
ConnectNucleus(this.prefab, this.currentNucleus);
|
||||
AddSynapse(this.prefab, this.currentNucleus);
|
||||
|
||||
this.currentNucleus.bias = EditorGUILayout.Vector3Field("Bias", this.currentNucleus.bias);
|
||||
|
||||
NucleusArray array = null;
|
||||
if (this.currentNucleus.synapses.Count > 0) {
|
||||
Synapse[] synapses = this.currentNucleus.synapses.ToArray();
|
||||
foreach (Synapse synapse in synapses) {
|
||||
if (synapse.nucleus != null) {
|
||||
if (array != null) {
|
||||
if (array.nuclei.Contains(synapse.nucleus))
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
if (synapse.nucleus.array != null && synapse.nucleus.array.nuclei.Length > 1)
|
||||
array = synapse.nucleus.array;
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (Application.isPlaying) {
|
||||
Vector3 value = synapse.nucleus.outputValue * synapse.weight;
|
||||
GUIContent synapseValueLabel = new(synapse.nucleus.name, synapse.nucleus.outputValue.ToString());
|
||||
EditorGUILayout.FloatField(synapseValueLabel, length(synapse.nucleus.outputValue));
|
||||
}
|
||||
else {
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField(synapse.nucleus.name);
|
||||
if (GUILayout.Button("Disconnect"))
|
||||
synapse.nucleus.RemoveReceiver(this.currentNucleus);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel++;
|
||||
synapse.weight = EditorGUILayout.FloatField("Weight", synapse.weight);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndFoldoutHeaderGroup();
|
||||
|
||||
EditorGUILayout.Space();
|
||||
showActivation = EditorGUILayout.BeginFoldoutHeaderGroup(showActivation, "Activation");
|
||||
if (showActivation) {
|
||||
|
||||
if (this.currentNucleus is Neuron neuron) {
|
||||
if (this.currentNucleus is not MemoryCell) {
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Activation Curve", GUILayout.Width(150));
|
||||
if (neuron.curveMax > 0)
|
||||
EditorGUILayout.CurveField(neuron.curve, Color.cyan, new Rect(0, 0, 1, neuron.curveMax));
|
||||
else
|
||||
EditorGUILayout.CurveField(neuron.curve, Color.cyan, new Rect(0, neuron.curveMax, 1, -neuron.curveMax));
|
||||
neuron.curvePreset = (Neuron.CurvePresets)EditorGUILayout.EnumPopup(neuron.curvePreset, GUILayout.Width(100));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
if (neuron.array == null || neuron.array.nuclei == null || neuron.array.nuclei.Count() == 0)
|
||||
neuron.array = new NucleusArray(neuron);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.IntField("Array size", neuron.array.nuclei.Count());
|
||||
if (GUILayout.Button("Add"))
|
||||
neuron.array.AddNucleus(this.prefab);
|
||||
if (GUILayout.Button("Del"))
|
||||
neuron.array.RemoveNucleus();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
EditorGUILayout.EndFoldoutHeaderGroup();
|
||||
|
||||
|
||||
if (GUILayout.Button("Delete this neuron"))
|
||||
DeleteNeuron(this.currentNucleus);
|
||||
|
||||
if (this.currentNucleus is Cluster subCluster) {
|
||||
if (GUILayout.Button("Edit Cluster"))
|
||||
EditCluster(subCluster);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
breakOnWake = EditorGUILayout.Toggle("Break on wake", breakOnWake);
|
||||
if (breakOnWake) {
|
||||
if (this.currentNucleus.isSleeping == false)
|
||||
Debug.Break();
|
||||
}
|
||||
trace = EditorGUILayout.Toggle("Trace", trace);
|
||||
this.currentNucleus.trace = trace;
|
||||
|
||||
});
|
||||
SerializedObject so = new(prefabAsset);
|
||||
IMGUIContainer container = new(() => InspectorHandler(so));
|
||||
|
||||
inspectorContainer.Add(container);
|
||||
}
|
||||
|
||||
void InspectorHandler(SerializedObject serializedObject) {
|
||||
bool anythingChanged = false;
|
||||
|
||||
if (serializedObject == null || serializedObject.targetObject == null)
|
||||
return;
|
||||
|
||||
if (this.currentNucleus == null)
|
||||
return;
|
||||
|
||||
serializedObject.Update();
|
||||
|
||||
GUIStyle headerStyle = new(EditorStyles.boldLabel) {
|
||||
alignment = TextAnchor.MiddleLeft,
|
||||
margin = new RectOffset(10, 0, 4, 4)
|
||||
};
|
||||
GUIStyle boldTextFieldStyle = new(EditorStyles.textField) {
|
||||
fontStyle = FontStyle.Bold
|
||||
};
|
||||
|
||||
GUILayout.Label(this.currentNucleus.GetType().ToString(), headerStyle);
|
||||
string newName = EditorGUILayout.TextField(this.currentNucleus.name, boldTextFieldStyle);
|
||||
if (newName != this.currentNucleus.name) {
|
||||
this.currentNucleus.name = newName;
|
||||
this.prefab.RefreshOutputs();
|
||||
outputsField.choices = this.prefab.outputs.Select(output => output.name).ToList();
|
||||
//outputsField.value = newName;
|
||||
}
|
||||
|
||||
if (Application.isPlaying) {
|
||||
GUIContent nameLabel = new("Output", this.currentNucleus.outputValue.ToString());
|
||||
EditorGUILayout.FloatField(nameLabel, length(this.currentNucleus.outputValue));
|
||||
}
|
||||
else
|
||||
EditorGUILayout.LabelField(" ");
|
||||
|
||||
if (this.currentNucleus is MemoryCell memory) {
|
||||
memory.staticMemory = EditorGUILayout.Toggle("Static Memory", memory.staticMemory);
|
||||
}
|
||||
|
||||
// Synapses
|
||||
|
||||
showSynapses = EditorGUILayout.BeginFoldoutHeaderGroup(showSynapses, "Synapses");
|
||||
if (showSynapses) {
|
||||
|
||||
ConnectNucleus(this.prefab, this.currentNucleus);
|
||||
AddSynapse(this.prefab, this.currentNucleus);
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (this.currentNucleus is Neuron neuron2)
|
||||
neuron2.combinator = (Neuron.CombinatorType)EditorGUILayout.EnumPopup("Combinator", neuron2.combinator);
|
||||
|
||||
this.currentNucleus.bias = EditorGUILayout.Vector3Field("Bias", this.currentNucleus.bias);
|
||||
|
||||
NucleusArray array = null;
|
||||
if (this.currentNucleus.synapses.Count > 0) {
|
||||
Synapse[] synapses = this.currentNucleus.synapses.ToArray();
|
||||
foreach (Synapse synapse in synapses) {
|
||||
if (synapse.nucleus != null) {
|
||||
if (array != null) {
|
||||
if (array.nuclei.Contains(synapse.nucleus))
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
if (synapse.nucleus.array != null && synapse.nucleus.array.nuclei.Length > 1)
|
||||
array = synapse.nucleus.array;
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (Application.isPlaying) {
|
||||
Vector3 value = synapse.nucleus.outputValue * synapse.weight;
|
||||
GUIContent synapseValueLabel = new(synapse.nucleus.name, synapse.nucleus.outputValue.ToString());
|
||||
EditorGUILayout.FloatField(synapseValueLabel, length(synapse.nucleus.outputValue));
|
||||
}
|
||||
else {
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField(synapse.nucleus.name);
|
||||
if (GUILayout.Button("Disconnect"))
|
||||
synapse.nucleus.RemoveReceiver(this.currentNucleus);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel++;
|
||||
synapse.weight = EditorGUILayout.FloatField("Weight", synapse.weight);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndFoldoutHeaderGroup();
|
||||
|
||||
// Activation
|
||||
|
||||
EditorGUILayout.Space();
|
||||
showActivation = EditorGUILayout.BeginFoldoutHeaderGroup(showActivation, "Activation");
|
||||
if (showActivation) {
|
||||
if (this.currentNucleus is Neuron neuron) {
|
||||
if (this.currentNucleus is not MemoryCell) {
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Activation Curve", GUILayout.Width(150));
|
||||
if (neuron.curveMax > 0)
|
||||
EditorGUILayout.CurveField(neuron.curve, Color.cyan, new Rect(0, 0, 1, neuron.curveMax));
|
||||
else
|
||||
EditorGUILayout.CurveField(neuron.curve, Color.cyan, new Rect(0, neuron.curveMax, 1, -neuron.curveMax));
|
||||
neuron.curvePreset = (Neuron.CurvePresets)EditorGUILayout.EnumPopup(neuron.curvePreset, GUILayout.Width(100));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
if (neuron.array == null || neuron.array.nuclei == null || neuron.array.nuclei.Count() == 0)
|
||||
neuron.array = new NucleusArray(neuron);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.IntField("Array size", neuron.array.nuclei.Count());
|
||||
if (GUILayout.Button("Add")) {
|
||||
Undo.RecordObject(prefabAsset, "Array add " + prefabAsset.name);
|
||||
neuron.array.AddNucleus(this.prefab);
|
||||
anythingChanged = true;
|
||||
}
|
||||
if (GUILayout.Button("Del")) {
|
||||
Undo.RecordObject(prefabAsset, "Array delete " + prefabAsset.name);
|
||||
neuron.array.RemoveNucleus();
|
||||
anythingChanged = true;
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
EditorGUILayout.EndFoldoutHeaderGroup();
|
||||
|
||||
|
||||
if (GUILayout.Button("Delete this neuron"))
|
||||
DeleteNeuron(this.currentNucleus);
|
||||
|
||||
if (this.currentNucleus is Cluster subCluster) {
|
||||
if (GUILayout.Button("Edit Cluster"))
|
||||
EditCluster(subCluster);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
breakOnWake = EditorGUILayout.Toggle("Break on wake", breakOnWake);
|
||||
if (breakOnWake) {
|
||||
if (this.currentNucleus.isSleeping == false)
|
||||
Debug.Break();
|
||||
}
|
||||
trace = EditorGUILayout.Toggle("Trace", trace);
|
||||
this.currentNucleus.trace = trace;
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
if (anythingChanged) {
|
||||
EditorUtility.SetDirty(prefabAsset);
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
}
|
||||
|
||||
void OnSceneGUI(SceneView sceneView) {
|
||||
if (this.gameObject != null) {
|
||||
@ -829,28 +866,28 @@ public class NeuroidLayer {
|
||||
public List<Nucleus> neuroids = new();
|
||||
}
|
||||
|
||||
public class ClusterWrapper : ScriptableObject {
|
||||
// expose fields that map to GraphNode
|
||||
//public string title;
|
||||
public Vector2 position;
|
||||
Nucleus node;
|
||||
ClusterPrefab graph; // needed to write back and mark dirty
|
||||
// public class ClusterWrapper : ScriptableObject {
|
||||
// // expose fields that map to GraphNode
|
||||
// //public string title;
|
||||
// public Vector2 position;
|
||||
// Nucleus node;
|
||||
// ClusterPrefab graph; // needed to write back and mark dirty
|
||||
|
||||
public ClusterWrapper Init(Nucleus node, ClusterPrefab graphAsset) {
|
||||
this.node = node;
|
||||
this.graph = graphAsset;
|
||||
//this.title = " A " + node.name;
|
||||
//position = node.position;
|
||||
return this;
|
||||
}
|
||||
void OnValidate() {
|
||||
if (node != null) {
|
||||
//node.name = title;
|
||||
//node.position = position;
|
||||
#if UNITY_EDITOR
|
||||
if (graph != null)
|
||||
UnityEditor.EditorUtility.SetDirty(graph);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
// public ClusterWrapper Init(Nucleus node, ClusterPrefab graphAsset) {
|
||||
// this.node = node;
|
||||
// this.graph = graphAsset;
|
||||
// //this.title = " A " + node.name;
|
||||
// //position = node.position;
|
||||
// return this;
|
||||
// }
|
||||
// void OnValidate() {
|
||||
// if (node != null) {
|
||||
// //node.name = title;
|
||||
// //node.position = position;
|
||||
// #if UNITY_EDITOR
|
||||
// if (graph != null)
|
||||
// UnityEditor.EditorUtility.SetDirty(graph);
|
||||
// #endif
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
71
Neuron.cs
71
Neuron.cs
@ -26,7 +26,13 @@ public class Neuron : Nucleus {
|
||||
|
||||
#region Serialization
|
||||
|
||||
public Type type = Type.Neuron;
|
||||
//public Type type = Type.Neuron;
|
||||
public enum CombinatorType {
|
||||
Sum,
|
||||
Product,
|
||||
Max
|
||||
}
|
||||
public CombinatorType combinator = CombinatorType.Sum;
|
||||
|
||||
public enum CurvePresets {
|
||||
Linear,
|
||||
@ -150,9 +156,9 @@ public class Neuron : Nucleus {
|
||||
}
|
||||
|
||||
protected virtual void CloneFields(Neuron clone) {
|
||||
clone.array = null;
|
||||
clone.array = this.array;
|
||||
clone.bias = this.bias;
|
||||
clone.type = this.type;
|
||||
clone.combinator = this.combinator;
|
||||
clone.curve = this.curve;
|
||||
clone.curvePreset = this.curvePreset;
|
||||
clone.curveMax = this.curveMax;
|
||||
@ -184,17 +190,19 @@ public class Neuron : Nucleus {
|
||||
}
|
||||
|
||||
public override void UpdateStateIsolated() {
|
||||
switch (this.type) {
|
||||
case Type.Neuron:
|
||||
UpdateSum();
|
||||
break;
|
||||
case Type.Pulsar:
|
||||
UpdateProduct();
|
||||
break;
|
||||
default:
|
||||
UpdateSum();
|
||||
break;
|
||||
}
|
||||
float3 result = CombinatorAction();
|
||||
this.outputValue = Activation(result);
|
||||
// switch (this.type) {
|
||||
// case Type.Neuron:
|
||||
// UpdateSum();
|
||||
// break;
|
||||
// case Type.Pulsar:
|
||||
// UpdateProduct();
|
||||
// break;
|
||||
// default:
|
||||
// UpdateSum();
|
||||
// break;
|
||||
// }
|
||||
// Vector3 sum = this.bias;
|
||||
// int n = 0;
|
||||
|
||||
@ -219,20 +227,45 @@ public class Neuron : Nucleus {
|
||||
// this.outputValue = result;
|
||||
}
|
||||
|
||||
public void UpdateSum() {
|
||||
private Func<float3> CombinatorAction => combinator switch {
|
||||
CombinatorType.Sum => UpdateSum,
|
||||
CombinatorType.Product => UpdateProduct,
|
||||
CombinatorType.Max => UpdateMax,
|
||||
_ => UpdateSum
|
||||
};
|
||||
|
||||
|
||||
public float3 UpdateSum() {
|
||||
Vector3 sum = this.bias;
|
||||
foreach (Synapse synapse in this.synapses)
|
||||
sum += synapse.weight * synapse.nucleus.outputValue;
|
||||
|
||||
this.outputValue = Activation(sum);
|
||||
return sum;
|
||||
//this.outputValue = Activation(sum);
|
||||
}
|
||||
|
||||
public void UpdateProduct() {
|
||||
public float3 UpdateProduct() {
|
||||
float3 product = this.bias;
|
||||
foreach (Synapse synapse in this.synapses)
|
||||
product *= synapse.weight * synapse.nucleus.outputValue;
|
||||
return product;
|
||||
//this.outputValue = Activation(product);
|
||||
}
|
||||
|
||||
this.outputValue = Activation(product);
|
||||
public float3 UpdateMax() {
|
||||
float3 max = this.bias;
|
||||
float maxSqrLength = lengthsq(max);
|
||||
|
||||
//Applying the weight factors
|
||||
foreach (Synapse synapse in this.synapses) {
|
||||
float3 input = synapse.weight * synapse.nucleus.outputValue;
|
||||
|
||||
float inputSqrlength = lengthsq(input);
|
||||
if (inputSqrlength > maxSqrLength) {
|
||||
max = input;
|
||||
maxSqrLength = inputSqrlength;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
protected float3 Activation(float3 input) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user