ClusterPrefabDrawer seems to work
This commit is contained in:
parent
8d3d6d97b2
commit
daf93691c1
@ -6,29 +6,23 @@ namespace NanoBrain.Unity {
|
||||
|
||||
public class ClusterView {
|
||||
|
||||
private static readonly float size = 20;
|
||||
private static readonly float discRadius = 20;
|
||||
|
||||
protected class ViewState {
|
||||
public string key = null;
|
||||
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) {
|
||||
static readonly Dictionary<string, ClusterView> viewStates = new();
|
||||
private static ClusterView GetClusterView(SerializedProperty property) {
|
||||
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 };
|
||||
return state;
|
||||
}
|
||||
|
||||
private static void UpdateViewState(ViewState viewState) {
|
||||
private static void UpdateViewState(ClusterView viewState) {
|
||||
viewStates[viewState.key] = viewState;
|
||||
}
|
||||
|
||||
public static void Render(Rect drawRect, Cluster cluster, SerializedProperty property) {
|
||||
ViewState state = GetViewState(property);
|
||||
ClusterView clusterView = GetClusterView(property);
|
||||
clusterView.currentCluster ??= cluster;
|
||||
|
||||
// background
|
||||
EditorGUI.DrawRect(drawRect, Color.black);
|
||||
@ -37,10 +31,10 @@ namespace NanoBrain.Unity {
|
||||
Rect contentRect = new Rect(0f, 0f, contentWidth, drawRect.height);
|
||||
|
||||
// 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)
|
||||
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));
|
||||
GUI.EndGroup();
|
||||
GUI.EndScrollView();
|
||||
@ -49,35 +43,372 @@ namespace NanoBrain.Unity {
|
||||
GUI.BeginGroup(drawRect);
|
||||
|
||||
// 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();
|
||||
DrawNucleus(state, cluster, new Vector2(100, 100), Color.black);
|
||||
clusterView.DrawFocusGraph();
|
||||
Handles.EndGUI();
|
||||
|
||||
GUI.EndGroup(); // end inner 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)
|
||||
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
|
||||
Handles.color = Color.white;
|
||||
Handles.DrawSolidDisc(position, Vector3.forward, size + 2);
|
||||
Handles.DrawSolidDisc(position, Vector3.forward, discRadius + 2);
|
||||
}
|
||||
|
||||
if (nucleus is MemoryCell) {
|
||||
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.DrawSolidDisc(position, Vector3.forward, size);
|
||||
Handles.DrawSolidDisc(position, Vector3.forward, discRadius);
|
||||
|
||||
Handles.color = Color.white;
|
||||
// Position the label in front of the disc
|
||||
@ -89,17 +420,17 @@ namespace NanoBrain.Unity {
|
||||
fontStyle = FontStyle.Bold,
|
||||
};
|
||||
|
||||
// if (nucleus.parent is Cluster parentCluster && currentNucleus != null && parentCluster != currentNucleus.parent)
|
||||
// DrawCluster(parentCluster, position, color, size);
|
||||
// else if (nucleus is Cluster cluster)
|
||||
// DrawCluster(cluster, position, color, size);
|
||||
if (nucleus.parent is Cluster parentCluster && this.currentNucleus != null && parentCluster != this.currentNucleus.parent)
|
||||
DrawCluster(parentCluster, position, color);
|
||||
else if (nucleus is Cluster cluster)
|
||||
DrawCluster(cluster, position, color);
|
||||
|
||||
if (state.expandArray == false) {// || nucleus != currentNucleus) {
|
||||
// put name below nucleus
|
||||
Vector3 labelPos = position - Vector2.down * (size + 5); // below neuron
|
||||
if (this.expandArray == false) {// || nucleus != currentNucleus) {
|
||||
// put name below nucleus
|
||||
Vector3 labelPos = position - Vector2.down * (discRadius + 5); // below neuron
|
||||
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
|
||||
parentCluster1.name ??= "";
|
||||
int colonPos = parentCluster1.name.IndexOf(":");
|
||||
@ -124,25 +455,222 @@ namespace NanoBrain.Unity {
|
||||
}
|
||||
|
||||
// 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);
|
||||
// Event e = Event.current;
|
||||
// EventType et = e.GetTypeForControl(id);
|
||||
// if (e != null && neuronRect.Contains(e.mousePosition)) {
|
||||
// // Process Hover
|
||||
// HandleMouseHover(nucleus, neuronRect);
|
||||
// // Process click
|
||||
// if (e.type == EventType.MouseDown && e.button == 0) {
|
||||
// // Consume the event so the scene doesn't also handle it
|
||||
// e.Use();
|
||||
// if (nucleus is Cluster parentCluster2)
|
||||
// OnNeuronClick(parentCluster2);
|
||||
// else
|
||||
// OnNeuronClick(nucleus);
|
||||
// }
|
||||
// }
|
||||
int id = GUIUtility.GetControlID(FocusType.Passive);
|
||||
Event e = Event.current;
|
||||
EventType et = e.GetTypeForControl(id);
|
||||
if (e != null && neuronRect.Contains(e.mousePosition)) {
|
||||
// Process Hover
|
||||
HandleMouseHover(nucleus, neuronRect);
|
||||
// Process click
|
||||
if (e.type == EventType.MouseDown && e.button == 0) {
|
||||
// Consume the event so the scene doesn't also handle it
|
||||
e.Use();
|
||||
if (nucleus is Cluster parentCluster2)
|
||||
OnNeuronClick(parentCluster2);
|
||||
else
|
||||
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_EditorClassIdentifier: Assembly-CSharp::NanoBrain.Unity.ClusterPrefab
|
||||
cluster:
|
||||
name:
|
||||
name: Breitenberg
|
||||
parent:
|
||||
rid: -2
|
||||
prefab: {fileID: 0}
|
||||
instanceCount: 0
|
||||
nuclei: []
|
||||
prefab: {fileID: 11400000}
|
||||
instanceCount: 1
|
||||
nuclei:
|
||||
- rid: 4201949899492425781
|
||||
- rid: 4201949899492425817
|
||||
references:
|
||||
version: 2
|
||||
RefIds:
|
||||
- rid: -2
|
||||
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
|
||||
value: Vehicle
|
||||
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}
|
||||
propertyPath: turnTorque
|
||||
value: 10
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user