ClusterPrefabDrawer seems to work
This commit is contained in:
parent
8d3d6d97b2
commit
daf93691c1
@ -6,29 +6,23 @@ namespace NanoBrain.Unity {
|
|||||||
|
|
||||||
public class ClusterView {
|
public class ClusterView {
|
||||||
|
|
||||||
private static readonly float size = 20;
|
private static readonly float discRadius = 20;
|
||||||
|
|
||||||
protected class ViewState {
|
static readonly Dictionary<string, ClusterView> viewStates = new();
|
||||||
public string key = null;
|
private static ClusterView GetClusterView(SerializedProperty property) {
|
||||||
public Vector2 scrollPos = Vector2.zero;
|
|
||||||
public bool expandArray = false;
|
|
||||||
public Nucleus currentNucleus = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Dictionary<string, ViewState> viewStates = new();
|
|
||||||
private static ViewState GetViewState(SerializedProperty property) {
|
|
||||||
string key = property.propertyPath + "_" + property.serializedObject.targetObject.GetEntityId();
|
string key = property.propertyPath + "_" + property.serializedObject.targetObject.GetEntityId();
|
||||||
if (!viewStates.TryGetValue(key, out ViewState state))
|
if (!viewStates.TryGetValue(key, out ClusterView state))
|
||||||
state = new() { key = key };
|
state = new() { key = key };
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void UpdateViewState(ViewState viewState) {
|
private static void UpdateViewState(ClusterView viewState) {
|
||||||
viewStates[viewState.key] = viewState;
|
viewStates[viewState.key] = viewState;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Render(Rect drawRect, Cluster cluster, SerializedProperty property) {
|
public static void Render(Rect drawRect, Cluster cluster, SerializedProperty property) {
|
||||||
ViewState state = GetViewState(property);
|
ClusterView clusterView = GetClusterView(property);
|
||||||
|
clusterView.currentCluster ??= cluster;
|
||||||
|
|
||||||
// background
|
// background
|
||||||
EditorGUI.DrawRect(drawRect, Color.black);
|
EditorGUI.DrawRect(drawRect, Color.black);
|
||||||
@ -37,10 +31,10 @@ namespace NanoBrain.Unity {
|
|||||||
Rect contentRect = new Rect(0f, 0f, contentWidth, drawRect.height);
|
Rect contentRect = new Rect(0f, 0f, contentWidth, drawRect.height);
|
||||||
|
|
||||||
// Begin horizontal-only scroll view
|
// Begin horizontal-only scroll view
|
||||||
state.scrollPos = GUI.BeginScrollView(drawRect, state.scrollPos, contentRect, true, false);
|
clusterView.scrollPos = GUI.BeginScrollView(drawRect, clusterView.scrollPos, contentRect, true, false);
|
||||||
|
|
||||||
// Local content group: draw GUI content using content-local coords (0..contentWidth)
|
// Local content group: draw GUI content using content-local coords (0..contentWidth)
|
||||||
GUI.BeginGroup(new Rect(-state.scrollPos.x, 0f, contentWidth, drawRect.height));
|
GUI.BeginGroup(new Rect(-clusterView.scrollPos.x, 0f, contentWidth, drawRect.height));
|
||||||
EditorGUI.DrawRect(new Rect(0f, 0f, contentWidth, drawRect.height), new Color(0.08f, 0.08f, 0.08f, 1f));
|
EditorGUI.DrawRect(new Rect(0f, 0f, contentWidth, drawRect.height), new Color(0.08f, 0.08f, 0.08f, 1f));
|
||||||
GUI.EndGroup();
|
GUI.EndGroup();
|
||||||
GUI.EndScrollView();
|
GUI.EndScrollView();
|
||||||
@ -49,35 +43,372 @@ namespace NanoBrain.Unity {
|
|||||||
GUI.BeginGroup(drawRect);
|
GUI.BeginGroup(drawRect);
|
||||||
|
|
||||||
// Inner group positions content origin so local coords match content space and respect scroll
|
// Inner group positions content origin so local coords match content space and respect scroll
|
||||||
GUI.BeginGroup(new Rect(-state.scrollPos.x, 0f, contentWidth, drawRect.height));
|
GUI.BeginGroup(new Rect(-clusterView.scrollPos.x, 0f, contentWidth, drawRect.height));
|
||||||
|
|
||||||
Handles.BeginGUI();
|
Handles.BeginGUI();
|
||||||
DrawNucleus(state, cluster, new Vector2(100, 100), Color.black);
|
clusterView.DrawFocusGraph();
|
||||||
Handles.EndGUI();
|
Handles.EndGUI();
|
||||||
|
|
||||||
GUI.EndGroup(); // end inner group
|
GUI.EndGroup(); // end inner group
|
||||||
GUI.EndGroup(); // end clipping group
|
GUI.EndGroup(); // end clipping group
|
||||||
|
|
||||||
UpdateViewState(state);
|
UpdateViewState(clusterView);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void DrawNucleus(ViewState state, Nucleus nucleus, Vector2 position, Color color) {
|
public string key = null;
|
||||||
|
public Vector2 scrollPos = Vector2.zero;
|
||||||
|
public bool expandArray = false;
|
||||||
|
public Cluster currentCluster;
|
||||||
|
public Nucleus currentNucleus = null;
|
||||||
|
public Nucleus selectedSynapseNeuron = null;
|
||||||
|
public Nucleus selectedOutput;
|
||||||
|
|
||||||
|
protected void DrawFocusGraph() {
|
||||||
|
float size = 20;
|
||||||
|
Vector3 position = new(150, 210, 0);
|
||||||
|
|
||||||
|
if (this.currentNucleus != null) {
|
||||||
|
DrawReceivers(this.currentNucleus, position, size);
|
||||||
|
DrawSynapses(this.currentNucleus, position, size);
|
||||||
|
|
||||||
|
// Draw selected Nucleus
|
||||||
|
if (this.expandArray) {
|
||||||
|
float maxValue = 1;
|
||||||
|
|
||||||
|
if (this.currentNucleus is Cluster cluster) {
|
||||||
|
float spacing = 400f / cluster.instanceCount;
|
||||||
|
float margin = 10 + spacing / 2;
|
||||||
|
float xMin = 150 - size;
|
||||||
|
float xMax = 150 + size;
|
||||||
|
float yMin = 10 + margin - size / 2;
|
||||||
|
float yMax = 400 - margin + size;
|
||||||
|
Vector3[] verts = new Vector3[4] {
|
||||||
|
new(xMin, yMin, 0),
|
||||||
|
new(xMax, yMin, 0),
|
||||||
|
new(xMax, yMax, 0),
|
||||||
|
new(xMin, yMax, 0)
|
||||||
|
};
|
||||||
|
Handles.color = Color.black;
|
||||||
|
Handles.DrawAAConvexPolygon(verts);
|
||||||
|
int row = 0;
|
||||||
|
if (cluster.instances == null) {
|
||||||
|
Vector2 pos = new(150, margin + row * spacing);
|
||||||
|
Handles.color = Color.white;
|
||||||
|
// The selected sibling highlight ring
|
||||||
|
Handles.DrawSolidDisc(pos, Vector3.forward, size + 2);
|
||||||
|
DrawNucleus(cluster, pos, maxValue);
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
foreach (Cluster sibling in cluster.instances) {
|
||||||
|
Vector3 pos = new(150, margin + row * spacing, 0.0f);
|
||||||
|
Handles.color = Color.white;
|
||||||
|
// The selected sibling highlight ring
|
||||||
|
Handles.DrawSolidDisc(pos, Vector3.forward, size + 2);
|
||||||
|
DrawNucleus(sibling, pos, maxValue);
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GUIStyle style = new(EditorStyles.label) {
|
||||||
|
alignment = TextAnchor.UpperCenter,
|
||||||
|
normal = { textColor = Color.white },
|
||||||
|
fontStyle = FontStyle.Bold,
|
||||||
|
};
|
||||||
|
Vector3 labelPos = new(150, yMax + size + 5, 0);
|
||||||
|
string clusterName = cluster.name;
|
||||||
|
int colonPos = clusterName.IndexOf(":");
|
||||||
|
if (colonPos > 0) {
|
||||||
|
string baseName = clusterName[..colonPos];
|
||||||
|
Handles.Label(labelPos, baseName, style);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Handles.Label(labelPos, clusterName, style);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (this.currentNucleus is Neuron neuron)
|
||||||
|
maxValue = neuron.outputMagnitude;
|
||||||
|
|
||||||
|
DrawNucleus(this.currentNucleus, position, maxValue);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float maxValue = 1;
|
||||||
|
if (this.currentNucleus is Neuron neuron)
|
||||||
|
maxValue = neuron.outputMagnitude;
|
||||||
|
else if (this.currentNucleus is Cluster cluster)
|
||||||
|
maxValue = cluster.defaultOutput.outputMagnitude;
|
||||||
|
DrawNucleus(this.currentNucleus, position, maxValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DrawAllOutputs(position);
|
||||||
|
DrawOutputs(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void DrawReceivers(Nucleus nucleus, Vector3 parentPos, float size) {
|
||||||
|
List<Nucleus> receivers;
|
||||||
|
if (nucleus is Neuron neuron)
|
||||||
|
receivers = neuron.receivers;
|
||||||
|
else if (nucleus is Cluster cluster)
|
||||||
|
receivers = cluster.CollectReceivers(true);
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
// For top-level nodes, add link to previous editor and/or 'Outputs'
|
||||||
|
int nodeCount = receivers.Count;
|
||||||
|
if (nucleus == this.selectedOutput) {
|
||||||
|
// Add link to 'Outpus'
|
||||||
|
nodeCount++;
|
||||||
|
if (ClusterViewer.previousPrefab != null)
|
||||||
|
// Add link to previous editor
|
||||||
|
nodeCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the maximum value in this layer
|
||||||
|
// This is used to 'scale' the output value colors of the nuclei
|
||||||
|
float maxValue = 0;
|
||||||
|
foreach (Nucleus receiver in receivers) {
|
||||||
|
if (receiver is Neuron neuroid) {
|
||||||
|
float value = neuroid.outputMagnitude;
|
||||||
|
if (value > maxValue)
|
||||||
|
maxValue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the spacing of the nuclei in the layer
|
||||||
|
float spacing = 400f / nodeCount;
|
||||||
|
float margin = 10 + spacing / 2;
|
||||||
|
|
||||||
|
int row = 0;
|
||||||
|
foreach (Nucleus receiver in receivers) {
|
||||||
|
Nucleus receiverNucleus = receiver;
|
||||||
|
if (receiverNucleus == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Vector3 pos = new(50, margin + row * spacing, 0.0f);
|
||||||
|
DrawEdge(parentPos, pos);
|
||||||
|
|
||||||
|
DrawNucleus(receiverNucleus, pos, maxValue);
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
if (nucleus == this.selectedOutput) {
|
||||||
|
Vector3 pos = new(50, margin + row * spacing, 0);
|
||||||
|
if (ClusterViewer.previousPrefab != null) {
|
||||||
|
DrawEdge(parentPos, pos);
|
||||||
|
DrawClusterPrefab(ClusterViewer.previousPrefab, pos);
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
pos = new(50, margin + row * spacing, 0);
|
||||||
|
DrawEdge(parentPos, pos);
|
||||||
|
DrawAllOutputs(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void DrawSynapses(Nucleus nucleus, Vector3 parentPos, float size) {
|
||||||
|
if (nucleus is not Neuron neuron)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this.selectedSynapseNeuron != null) {
|
||||||
|
DrawClusterSynapses(this.selectedSynapseNeuron, parentPos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (nucleus == null)
|
if (nucleus == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (nucleus == state.currentNucleus) {
|
// Determine the maximum value in this layer
|
||||||
|
// This is used to 'scale' the output value colors of the nuclei
|
||||||
|
float maxValue = 0;
|
||||||
|
int neuronCount = 0;
|
||||||
|
List<string> drawnNeuronNames = new();
|
||||||
|
foreach (Synapse synapse in neuron.synapses) {
|
||||||
|
if (synapse.neuron == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Count multiple synapses to the same neuron only once
|
||||||
|
string neuronName = synapse.neuron.name;
|
||||||
|
if (synapse.neuron.parent != null)
|
||||||
|
neuronName = synapse.neuron.parent.baseName + "." + neuronName;
|
||||||
|
|
||||||
|
if (drawnNeuronNames.Contains(neuronName))
|
||||||
|
continue;
|
||||||
|
drawnNeuronNames.Add(neuronName);
|
||||||
|
|
||||||
|
float value = synapse.neuron.outputMagnitude * synapse.weight;
|
||||||
|
if (value > maxValue)
|
||||||
|
maxValue = value;
|
||||||
|
|
||||||
|
neuronCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the spacing of the nuclei in the layer
|
||||||
|
float spacing = 400f / neuronCount;
|
||||||
|
float margin = 10 + spacing / 2;
|
||||||
|
|
||||||
|
int row = 0;
|
||||||
|
//List<Neuron> drawnNeurons = new();
|
||||||
|
drawnNeuronNames = new();
|
||||||
|
foreach (Synapse synapse in neuron.synapses) {
|
||||||
|
if (synapse.neuron is null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Draw multiple synapses to the same neuron only once
|
||||||
|
string neuronName = synapse.neuron.name;
|
||||||
|
if (synapse.neuron.parent != null)
|
||||||
|
neuronName = synapse.neuron.parent.baseName + "." + neuronName;
|
||||||
|
|
||||||
|
if (drawnNeuronNames.Contains(neuronName))
|
||||||
|
continue;
|
||||||
|
drawnNeuronNames.Add(neuronName);
|
||||||
|
|
||||||
|
Vector3 pos = new(250, margin + row * spacing, 0.0f);
|
||||||
|
DrawEdge(parentPos, pos);
|
||||||
|
// Handles.color = Color.white;
|
||||||
|
// Handles.DrawLine(parentPos, pos);
|
||||||
|
Color color = Color.black;
|
||||||
|
if (Application.isPlaying) {
|
||||||
|
if (maxValue == 0 || !float.IsFinite(maxValue))
|
||||||
|
maxValue = 1;
|
||||||
|
float brightness = synapse.neuron.outputMagnitude * synapse.weight / maxValue;
|
||||||
|
color = new Color(brightness, brightness, brightness, 1f);
|
||||||
|
}
|
||||||
|
DrawNucleus(synapse.neuron, pos, color);
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void DrawClusterSynapses(Nucleus nucleus, Vector3 parentPos) {
|
||||||
|
if (nucleus == null || nucleus.parent == null || nucleus.parent.instances == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Hack to disable showing labels
|
||||||
|
expandArray = true;
|
||||||
|
|
||||||
|
float maxValue = 0;
|
||||||
|
foreach (Cluster sibling in nucleus.parent.instances) {
|
||||||
|
Neuron siblingNeuron = sibling.GetNucleus(nucleus.name) as Neuron;
|
||||||
|
float value = siblingNeuron.outputMagnitude; // no need to add weight as they are all the same
|
||||||
|
if (value > maxValue)
|
||||||
|
maxValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the spacing of the nuclei in the layer
|
||||||
|
float spacing = 400f / nucleus.parent.instanceCount;
|
||||||
|
float margin = 10 + spacing / 2;
|
||||||
|
|
||||||
|
int row = 0;
|
||||||
|
foreach (Cluster sibling in nucleus.parent.instances) {
|
||||||
|
Neuron siblingNeuron = sibling.GetNucleus(nucleus.name) as Neuron;
|
||||||
|
Vector3 position = new(250, margin + row * spacing, 0.0f);
|
||||||
|
DrawEdge(parentPos, position);
|
||||||
|
Color color = Color.black;
|
||||||
|
if (Application.isPlaying) {
|
||||||
|
if (maxValue == 0 || !float.IsFinite(maxValue))
|
||||||
|
maxValue = 1;
|
||||||
|
float brightness = siblingNeuron.outputMagnitude / maxValue;
|
||||||
|
color = new Color(brightness, brightness, brightness, 1f);
|
||||||
|
}
|
||||||
|
DrawNucleus(siblingNeuron, position, color);
|
||||||
|
GUIStyle style = new(EditorStyles.label) {
|
||||||
|
alignment = TextAnchor.UpperCenter,
|
||||||
|
normal = { textColor = Color.white },
|
||||||
|
fontStyle = FontStyle.Bold,
|
||||||
|
};
|
||||||
|
Vector3 labelPos = position - Vector3.down * (discRadius + 5); // below neuron
|
||||||
|
string name = $"{sibling.baseName}\n{nucleus.name}";
|
||||||
|
Handles.Label(labelPos, name, style);
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
expandArray = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void DrawOutputs(Vector2 parentPos) {
|
||||||
|
if (this.currentCluster == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Determine the maximum value in this layer
|
||||||
|
// This is used to 'scale' the output value colors of the nuclei
|
||||||
|
float maxValue = 0;
|
||||||
|
int neuronCount = 0;
|
||||||
|
List<Nucleus> drawnNuclei = new();
|
||||||
|
foreach (Nucleus nucleus in this.currentCluster.outputs) {
|
||||||
|
if (nucleus is not Neuron neuron)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Draw multiple synapses to the same neuron only once
|
||||||
|
if (drawnNuclei.Contains(nucleus))
|
||||||
|
continue;
|
||||||
|
drawnNuclei.Add(nucleus);
|
||||||
|
|
||||||
|
float value = neuron.outputMagnitude;
|
||||||
|
if (value > maxValue)
|
||||||
|
maxValue = value;
|
||||||
|
|
||||||
|
neuronCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the spacing of the nuclei in the layer
|
||||||
|
float spacing = 400f / neuronCount;
|
||||||
|
float margin = 10 + spacing / 2;
|
||||||
|
|
||||||
|
int row = 0;
|
||||||
|
drawnNuclei = new();
|
||||||
|
foreach (Nucleus nucleus in this.currentCluster.outputs) {
|
||||||
|
if (nucleus is not Neuron neuron)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Draw multiple synapses to the same neuron only once
|
||||||
|
if (drawnNuclei.Contains(nucleus))
|
||||||
|
continue;
|
||||||
|
drawnNuclei.Add(nucleus);
|
||||||
|
|
||||||
|
Vector3 pos = new(250, margin + row * spacing, 0.0f);
|
||||||
|
DrawEdge(parentPos, pos);
|
||||||
|
Color color = Color.black;
|
||||||
|
if (Application.isPlaying) {
|
||||||
|
if (maxValue == 0 || !float.IsFinite(maxValue))
|
||||||
|
maxValue = 1;
|
||||||
|
float brightness = neuron.outputMagnitude / maxValue;
|
||||||
|
color = new Color(brightness, brightness, brightness, 1f);
|
||||||
|
}
|
||||||
|
DrawNucleus(nucleus, pos, color);
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void DrawNucleus(Nucleus nucleus, Vector3 position, float maxValue) {
|
||||||
|
Color color;
|
||||||
|
if (Application.isPlaying) {
|
||||||
|
float brightness = 0;
|
||||||
|
if (nucleus is Neuron neuron)
|
||||||
|
brightness = neuron.outputMagnitude / maxValue;
|
||||||
|
color = new Color(brightness, brightness, brightness, 1f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
color = Color.black;
|
||||||
|
DrawNucleus(nucleus, position, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void DrawNucleus(Nucleus nucleus, Vector2 position, Color color) {
|
||||||
|
if (nucleus == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (nucleus == this.currentNucleus) {
|
||||||
// The selected nucleus highlight ring
|
// The selected nucleus highlight ring
|
||||||
Handles.color = Color.white;
|
Handles.color = Color.white;
|
||||||
Handles.DrawSolidDisc(position, Vector3.forward, size + 2);
|
Handles.DrawSolidDisc(position, Vector3.forward, discRadius + 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nucleus is MemoryCell) {
|
if (nucleus is MemoryCell) {
|
||||||
Handles.color = Color.white;
|
Handles.color = Color.white;
|
||||||
Handles.DrawWireDisc(position + Vector2.right * 10, Vector3.forward, size);
|
Handles.DrawWireDisc(position + Vector2.right * 10, Vector3.forward, discRadius);
|
||||||
}
|
}
|
||||||
|
|
||||||
Handles.color = color;
|
Handles.color = color;
|
||||||
Handles.DrawSolidDisc(position, Vector3.forward, size);
|
Handles.DrawSolidDisc(position, Vector3.forward, discRadius);
|
||||||
|
|
||||||
Handles.color = Color.white;
|
Handles.color = Color.white;
|
||||||
// Position the label in front of the disc
|
// Position the label in front of the disc
|
||||||
@ -89,17 +420,17 @@ namespace NanoBrain.Unity {
|
|||||||
fontStyle = FontStyle.Bold,
|
fontStyle = FontStyle.Bold,
|
||||||
};
|
};
|
||||||
|
|
||||||
// if (nucleus.parent is Cluster parentCluster && currentNucleus != null && parentCluster != currentNucleus.parent)
|
if (nucleus.parent is Cluster parentCluster && this.currentNucleus != null && parentCluster != this.currentNucleus.parent)
|
||||||
// DrawCluster(parentCluster, position, color, size);
|
DrawCluster(parentCluster, position, color);
|
||||||
// else if (nucleus is Cluster cluster)
|
else if (nucleus is Cluster cluster)
|
||||||
// DrawCluster(cluster, position, color, size);
|
DrawCluster(cluster, position, color);
|
||||||
|
|
||||||
if (state.expandArray == false) {// || nucleus != currentNucleus) {
|
if (this.expandArray == false) {// || nucleus != currentNucleus) {
|
||||||
// put name below nucleus
|
// put name below nucleus
|
||||||
Vector3 labelPos = position - Vector2.down * (size + 5); // below neuron
|
Vector3 labelPos = position - Vector2.down * (discRadius + 5); // below neuron
|
||||||
style.alignment = TextAnchor.UpperCenter;
|
style.alignment = TextAnchor.UpperCenter;
|
||||||
|
|
||||||
if (nucleus.parent != null && state.currentNucleus != null && nucleus.parent != state.currentNucleus.parent && nucleus.parent is Cluster parentCluster1) {
|
if (nucleus.parent != null && this.currentNucleus != null && nucleus.parent != this.currentNucleus.parent && nucleus.parent is Cluster parentCluster1) {
|
||||||
// This neuron is part of another cluster
|
// This neuron is part of another cluster
|
||||||
parentCluster1.name ??= "";
|
parentCluster1.name ??= "";
|
||||||
int colonPos = parentCluster1.name.IndexOf(":");
|
int colonPos = parentCluster1.name.IndexOf(":");
|
||||||
@ -124,25 +455,222 @@ namespace NanoBrain.Unity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tooltip
|
// Tooltip
|
||||||
// Rect neuronRect = new(position.x - size, position.y - size, size * 2, size * 2);
|
Rect neuronRect = new(position.x - discRadius, position.y - discRadius, discRadius * 2, discRadius * 2);
|
||||||
|
|
||||||
// int id = GUIUtility.GetControlID(FocusType.Passive);
|
int id = GUIUtility.GetControlID(FocusType.Passive);
|
||||||
// Event e = Event.current;
|
Event e = Event.current;
|
||||||
// EventType et = e.GetTypeForControl(id);
|
EventType et = e.GetTypeForControl(id);
|
||||||
// if (e != null && neuronRect.Contains(e.mousePosition)) {
|
if (e != null && neuronRect.Contains(e.mousePosition)) {
|
||||||
// // Process Hover
|
// Process Hover
|
||||||
// HandleMouseHover(nucleus, neuronRect);
|
HandleMouseHover(nucleus, neuronRect);
|
||||||
// // Process click
|
// Process click
|
||||||
// if (e.type == EventType.MouseDown && e.button == 0) {
|
if (e.type == EventType.MouseDown && e.button == 0) {
|
||||||
// // Consume the event so the scene doesn't also handle it
|
// Consume the event so the scene doesn't also handle it
|
||||||
// e.Use();
|
e.Use();
|
||||||
// if (nucleus is Cluster parentCluster2)
|
if (nucleus is Cluster parentCluster2)
|
||||||
// OnNeuronClick(parentCluster2);
|
OnNeuronClick(parentCluster2);
|
||||||
// else
|
else
|
||||||
// OnNeuronClick(nucleus);
|
OnNeuronClick(nucleus);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void DrawCluster(Cluster cluster, Vector3 position, Color color) {
|
||||||
|
GUIStyle labelTextStyle = new(EditorStyles.label) {
|
||||||
|
normal = { textColor = Color.white },
|
||||||
|
fontStyle = FontStyle.Bold,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.expandArray) {
|
||||||
|
// Put array indices above the discs
|
||||||
|
labelTextStyle.alignment = TextAnchor.LowerCenter;
|
||||||
|
Vector3 labelPosition = position + Vector3.down * (discRadius + 5); // below disc
|
||||||
|
|
||||||
|
// Strip the instance number in the name
|
||||||
|
int colonPos1 = cluster.name.IndexOf(":");
|
||||||
|
if (colonPos1 > 0) {
|
||||||
|
string extName = cluster.name[(colonPos1 + 2)..];
|
||||||
|
Handles.Label(labelPosition, extName, labelTextStyle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Handles.Label(labelPosition, "0", labelTextStyle);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Put instance count inside the disc
|
||||||
|
labelTextStyle.alignment = TextAnchor.MiddleCenter;
|
||||||
|
Vector3 labelPosition = position + (Vector3.forward * 0.1f);
|
||||||
|
|
||||||
|
// Adjust text color based on disc color
|
||||||
|
if (color.grayscale > 0.5f)
|
||||||
|
labelTextStyle.normal.textColor = Color.black;
|
||||||
|
else
|
||||||
|
labelTextStyle.normal.textColor = Color.white;
|
||||||
|
|
||||||
|
if (cluster.instanceCount > 1) {
|
||||||
|
Handles.Label(labelPosition, cluster.instanceCount.ToString(), labelTextStyle);
|
||||||
|
labelTextStyle.normal.textColor = Color.white;
|
||||||
|
}
|
||||||
|
else if (cluster.instances != null && cluster.instances.Length > 1) {
|
||||||
|
Handles.Label(labelPosition, cluster.instances.Length.ToString(), labelTextStyle);
|
||||||
|
labelTextStyle.normal.textColor = Color.white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw a circle around the disc to indicate this is a Cluster
|
||||||
|
Handles.color = Color.white;
|
||||||
|
Handles.DrawWireDisc(position, Vector3.forward, discRadius + 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void DrawClusterPrefab(ClusterPrefab prefab, Vector2 position) {
|
||||||
|
Handles.color = Color.black;
|
||||||
|
Handles.DrawSolidDisc(position, Vector3.forward, discRadius);
|
||||||
|
// Draw a circle around the disc to indicate this is a Cluster
|
||||||
|
Handles.color = Color.white;
|
||||||
|
Handles.DrawWireDisc(position, Vector3.forward, discRadius + 5);
|
||||||
|
|
||||||
|
// put name below nucleus
|
||||||
|
GUIStyle style = new(EditorStyles.label) {
|
||||||
|
alignment = TextAnchor.MiddleCenter,
|
||||||
|
normal = { textColor = Color.white },
|
||||||
|
fontStyle = FontStyle.Bold,
|
||||||
|
};
|
||||||
|
Vector2 labelPos = position - Vector2.down * (discRadius + 5); // below neuron
|
||||||
|
style.alignment = TextAnchor.UpperCenter;
|
||||||
|
Handles.Label(labelPos, prefab.name, style);
|
||||||
|
|
||||||
|
Rect neuronRect = new(position.x - discRadius, position.y - discRadius, discRadius * 2, discRadius * 2);
|
||||||
|
int id = GUIUtility.GetControlID(FocusType.Passive);
|
||||||
|
Event e = Event.current;
|
||||||
|
EventType et = e.GetTypeForControl(id);
|
||||||
|
if (e != null && neuronRect.Contains(e.mousePosition)) {
|
||||||
|
// Process click
|
||||||
|
if (e.type == EventType.MouseDown && e.button == 0) {
|
||||||
|
// Consume the event so the scene doesn't also handle it
|
||||||
|
e.Use();
|
||||||
|
Selection.activeObject = prefab;
|
||||||
|
EditorGUIUtility.PingObject(prefab);
|
||||||
|
ClusterViewer.previousPrefab = null;
|
||||||
|
Editor.CreateEditor(prefab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void DrawAllOutputs(Vector2 position) {
|
||||||
|
GUIStyle labelTextStyle = new(EditorStyles.label) {
|
||||||
|
normal = { textColor = Color.white },
|
||||||
|
fontStyle = FontStyle.Bold,
|
||||||
|
alignment = TextAnchor.MiddleCenter,
|
||||||
|
};
|
||||||
|
Handles.Label(position, "Outputs", labelTextStyle);
|
||||||
|
|
||||||
|
Rect neuronRect = new(position.x - discRadius, position.y - discRadius, discRadius * 2, discRadius * 2);
|
||||||
|
Event e = Event.current;
|
||||||
|
if (e != null && neuronRect.Contains(e.mousePosition)) {
|
||||||
|
// Process click
|
||||||
|
if (e.type == EventType.MouseDown && e.button == 0) {
|
||||||
|
// Consume the event so the scene doesn't also handle it
|
||||||
|
e.Use();
|
||||||
|
OnAllOutputsClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void DrawEdge(Vector2 from, Vector2 to) {
|
||||||
|
Handles.color = Color.white;
|
||||||
|
// Handles.DrawLine(from, to);
|
||||||
|
|
||||||
|
Vector2 dir = to - from;
|
||||||
|
float len = dir.magnitude;
|
||||||
|
if (len <= 2f * discRadius || len <= Mathf.Epsilon)
|
||||||
|
// line too short
|
||||||
|
return;
|
||||||
|
|
||||||
|
Vector2 n = dir / len; // normalized
|
||||||
|
Vector2 a = from + n * discRadius;
|
||||||
|
Vector2 b = to - n * discRadius;
|
||||||
|
Handles.DrawLine(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Interaction
|
||||||
|
|
||||||
|
protected static void HandleMouseHover(Nucleus nucleus, Rect rect) {
|
||||||
|
GUIContent tooltip;
|
||||||
|
if (nucleus is Neuron neuron) {
|
||||||
|
tooltip = new(
|
||||||
|
$"{nucleus.name}" +
|
||||||
|
$"\nValue: {neuron.outputMagnitude}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tooltip = new($"{nucleus.name}");
|
||||||
|
|
||||||
|
Vector2 mousePosition = Event.current.mousePosition;
|
||||||
|
|
||||||
|
// Display tooltip with some offset
|
||||||
|
Vector2 tooltipSize = GUI.skin.box.CalcSize(tooltip);
|
||||||
|
Rect tooltipRect = new Rect(mousePosition.x + 10, mousePosition.y + 10, tooltipSize.x, tooltipSize.y);
|
||||||
|
|
||||||
|
GUI.Box(tooltipRect, tooltip);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void OnNeuronClick(Nucleus nucleus) {
|
||||||
|
if (nucleus == this.currentNucleus) {
|
||||||
|
this.selectedSynapseNeuron = null;
|
||||||
|
// if (Application.isPlaying) {
|
||||||
|
// if (nucleus is Cluster)
|
||||||
|
// expandArray = !expandArray;
|
||||||
|
// else
|
||||||
|
// expandArray = false;
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
if (nucleus is Cluster cluster)
|
||||||
|
OnClusterClick(cluster);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
else if (nucleus.parent != null && this.currentNucleus != null && nucleus.parent != this.currentNucleus.parent) {
|
||||||
|
// We go to a different cluster
|
||||||
|
if (Application.isPlaying) {
|
||||||
|
if (this.selectedSynapseNeuron == null && nucleus.parent.instanceCount > 1) {
|
||||||
|
this.selectedSynapseNeuron = nucleus;
|
||||||
|
this.expandArray = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.currentNucleus = nucleus;
|
||||||
|
if (this.currentNucleus is Neuron neuron && neuron.receivers.Count == 0)
|
||||||
|
this.selectedOutput = this.currentNucleus;
|
||||||
|
this.selectedSynapseNeuron = null;
|
||||||
|
this.expandArray = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// select the cluster, not the neuron in the cluster
|
||||||
|
this.currentNucleus = nucleus.parent;
|
||||||
|
this.expandArray = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.currentNucleus = nucleus;
|
||||||
|
if (this.currentNucleus is Neuron neuron && neuron.receivers.Count == 0)
|
||||||
|
this.selectedOutput = this.currentNucleus;
|
||||||
|
this.expandArray = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void OnClusterClick(Cluster subCluster) {
|
||||||
|
// May be used with storedPrefab...
|
||||||
|
Selection.activeObject = subCluster.prefab;
|
||||||
|
EditorGUIUtility.PingObject(subCluster.prefab);
|
||||||
|
ClusterViewer.previousPrefab = this.currentCluster.prefab;
|
||||||
|
Editor.CreateEditor(subCluster.prefab);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void OnAllOutputsClick() {
|
||||||
|
this.currentNucleus = null;
|
||||||
|
this.selectedOutput = null;
|
||||||
|
this.expandArray = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Interaction
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -13,14 +13,107 @@ MonoBehaviour:
|
|||||||
m_Name: Breitenberg
|
m_Name: Breitenberg
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::NanoBrain.Unity.ClusterPrefab
|
m_EditorClassIdentifier: Assembly-CSharp::NanoBrain.Unity.ClusterPrefab
|
||||||
cluster:
|
cluster:
|
||||||
name:
|
name: Breitenberg
|
||||||
parent:
|
parent:
|
||||||
rid: -2
|
rid: -2
|
||||||
prefab: {fileID: 0}
|
prefab: {fileID: 11400000}
|
||||||
instanceCount: 0
|
instanceCount: 1
|
||||||
nuclei: []
|
nuclei:
|
||||||
|
- rid: 4201949899492425781
|
||||||
|
- rid: 4201949899492425817
|
||||||
references:
|
references:
|
||||||
version: 2
|
version: 2
|
||||||
RefIds:
|
RefIds:
|
||||||
- rid: -2
|
- rid: -2
|
||||||
type: {class: , ns: , asm: }
|
type: {class: , ns: , asm: }
|
||||||
|
- rid: 4201949899492425781
|
||||||
|
type: {class: Neuron, ns: NanoBrain, asm: Assembly-CSharp}
|
||||||
|
data:
|
||||||
|
name: Output
|
||||||
|
parent:
|
||||||
|
rid: 4201949899492425816
|
||||||
|
bias: {x: 0, y: 0, z: 0}
|
||||||
|
_synapses:
|
||||||
|
- neuron:
|
||||||
|
rid: 4201949899492425817
|
||||||
|
weight: 1
|
||||||
|
combinator: 0
|
||||||
|
_activator: 0
|
||||||
|
curve:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Curve:
|
||||||
|
- serializedVersion: 3
|
||||||
|
time: 0
|
||||||
|
value: 0
|
||||||
|
inSlope: 0
|
||||||
|
outSlope: 1
|
||||||
|
tangentMode: 0
|
||||||
|
weightedMode: 0
|
||||||
|
inWeight: 0
|
||||||
|
outWeight: 0
|
||||||
|
- serializedVersion: 3
|
||||||
|
time: 1000
|
||||||
|
value: 1000
|
||||||
|
inSlope: 1
|
||||||
|
outSlope: 0
|
||||||
|
tangentMode: 0
|
||||||
|
weightedMode: 0
|
||||||
|
inWeight: 0
|
||||||
|
outWeight: 0
|
||||||
|
m_PreInfinity: 2
|
||||||
|
m_PostInfinity: 2
|
||||||
|
m_RotationOrder: 4
|
||||||
|
curveMax: 1
|
||||||
|
persistOutput: 0
|
||||||
|
lastUpdate: 0
|
||||||
|
_receivers: []
|
||||||
|
- rid: 4201949899492425816
|
||||||
|
type: {class: Cluster, ns: NanoBrain, asm: Assembly-CSharp}
|
||||||
|
data:
|
||||||
|
name: Breitenberg
|
||||||
|
parent:
|
||||||
|
rid: -2
|
||||||
|
prefab: {fileID: 11400000}
|
||||||
|
instanceCount: 1
|
||||||
|
nuclei:
|
||||||
|
- rid: 4201949899492425781
|
||||||
|
- rid: 4201949899492425817
|
||||||
|
- rid: 4201949899492425817
|
||||||
|
type: {class: Neuron, ns: NanoBrain, asm: Assembly-CSharp}
|
||||||
|
data:
|
||||||
|
name: Sensor
|
||||||
|
parent:
|
||||||
|
rid: 4201949899492425816
|
||||||
|
bias: {x: 0, y: 0, z: 0}
|
||||||
|
_synapses: []
|
||||||
|
combinator: 0
|
||||||
|
_activator: 0
|
||||||
|
curve:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Curve:
|
||||||
|
- serializedVersion: 3
|
||||||
|
time: 0
|
||||||
|
value: 0
|
||||||
|
inSlope: 0
|
||||||
|
outSlope: 1
|
||||||
|
tangentMode: 0
|
||||||
|
weightedMode: 0
|
||||||
|
inWeight: 0
|
||||||
|
outWeight: 0
|
||||||
|
- serializedVersion: 3
|
||||||
|
time: 1000
|
||||||
|
value: 1000
|
||||||
|
inSlope: 1
|
||||||
|
outSlope: 0
|
||||||
|
tangentMode: 0
|
||||||
|
weightedMode: 0
|
||||||
|
inWeight: 0
|
||||||
|
outWeight: 0
|
||||||
|
m_PreInfinity: 2
|
||||||
|
m_PostInfinity: 2
|
||||||
|
m_RotationOrder: 4
|
||||||
|
curveMax: 1
|
||||||
|
persistOutput: 0
|
||||||
|
lastUpdate: 0
|
||||||
|
_receivers:
|
||||||
|
- rid: 4201949899492425781
|
||||||
|
|||||||
@ -652,6 +652,10 @@ PrefabInstance:
|
|||||||
propertyPath: m_Name
|
propertyPath: m_Name
|
||||||
value: Vehicle
|
value: Vehicle
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 8280937452374640854, guid: c0398fc7a48853d47acb42e4e3498383, type: 3}
|
||||||
|
propertyPath: brain
|
||||||
|
value:
|
||||||
|
objectReference: {fileID: 11400000, guid: f6472e5b4459918cab23832c0545c832, type: 2}
|
||||||
- target: {fileID: 8280937452374640854, guid: c0398fc7a48853d47acb42e4e3498383, type: 3}
|
- target: {fileID: 8280937452374640854, guid: c0398fc7a48853d47acb42e4e3498383, type: 3}
|
||||||
propertyPath: turnTorque
|
propertyPath: turnTorque
|
||||||
value: 10
|
value: 10
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user