multicluster visualization

This commit is contained in:
Pascal Serrarens 2026-05-20 15:08:42 +02:00
parent a87118fc23
commit 92bfb29ed0
5 changed files with 84 additions and 37 deletions

View File

@ -76,8 +76,15 @@ namespace NanoBrain.Unity {
margin = new RectOffset(10, 0, 4, 4)
};
// Nucleus type
string nucleusType = this.view.currentNucleus.GetType().Name;
GUILayout.Label(nucleusType, headerStyle);
System.Type nucleusType = this.view.currentNucleus.GetType();
if (nucleusType == typeof(Cluster)) {
Cluster cluster = this.view.currentNucleus as Cluster;
GUILayout.Label($"{cluster.prefab.name} {nucleusType.Name}", headerStyle);
}
else
GUILayout.Label(nucleusType.Name, headerStyle);
// string nucleusType = this.view.currentNucleus.GetType().Name;
// GUILayout.Label(nucleusType, headerStyle);
// Nucleus name
string newName = EditorGUILayout.TextField(this.view.currentNucleus.name, boldTextFieldStyle);

View File

@ -7,6 +7,8 @@ namespace NanoBrain.Unity {
public class ClusterView {
public static ClusterPrefab previousPrefab;
private static readonly float discRadius = 20;
private float viewWidth;
private float contentWidth = 1000;
@ -310,7 +312,7 @@ namespace NanoBrain.Unity {
if (nucleus == this.selectedOutput) {
// Add link to 'Outpus'
nodeCount++;
if (ClusterViewer.previousPrefab != null)
if (ClusterView.previousPrefab != null && ClusterView.previousPrefab != nucleus.parent.prefab)
// Add link to previous editor
nodeCount++;
}
@ -344,9 +346,9 @@ namespace NanoBrain.Unity {
}
if (nucleus == this.selectedOutput) {
Vector3 pos = new(50, margin + row * spacing, 0);
if (ClusterViewer.previousPrefab != null) {
if (ClusterView.previousPrefab != null && ClusterView.previousPrefab != nucleus.parent.prefab) {
DrawEdge(parentPos, pos);
DrawClusterPrefab(ClusterViewer.previousPrefab, pos);
DrawClusterPrefab(ClusterView.previousPrefab, pos);
row++;
}
pos = new(50, margin + row * spacing, 0);
@ -447,9 +449,10 @@ namespace NanoBrain.Unity {
float margin = 10 + spacing / 2;
int row = 0;
Vector3 position = Vector3.zero;
foreach (Cluster sibling in nucleus.parent.instances) {
Neuron siblingNeuron = sibling.GetNucleus(nucleus.name) as Neuron;
Vector3 position = new(250, margin + row * spacing, 0.0f);
position = new(250, margin + row * spacing, 0.0f);
DrawEdge(parentPos, position);
Color color = Color.black;
if (Application.isPlaying) {
@ -459,16 +462,16 @@ namespace NanoBrain.Unity {
color = new Color(brightness, brightness, brightness, 1f);
}
DrawNucleus(siblingNeuron, position, color);
row++;
}
Vector3 labelPos = position - Vector3.down * (discRadius + 5); // below neuron
string name = $"{nucleus.parent.instances[0].baseName}\n{nucleus.name}";
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++;
}
Handles.Label(labelPos, name, style);
expandArray = false;
}
@ -526,7 +529,6 @@ namespace NanoBrain.Unity {
}
}
protected void DrawNucleus(Nucleus nucleus, Vector3 position, float maxValue) {
maxValue = 1;
Color color;
@ -541,7 +543,6 @@ namespace NanoBrain.Unity {
DrawNucleus(nucleus, position, color);
}
protected void DrawNucleus(Nucleus nucleus, Vector2 position, Color color) {
if (nucleus == null)
return;
@ -699,7 +700,7 @@ namespace NanoBrain.Unity {
e.Use();
Selection.activeObject = prefab;
EditorGUIUtility.PingObject(prefab);
ClusterViewer.previousPrefab = null;
ClusterView.previousPrefab = null;
Editor.CreateEditor(prefab);
}
}
@ -765,16 +766,16 @@ namespace NanoBrain.Unity {
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);
// }
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
@ -795,6 +796,7 @@ namespace NanoBrain.Unity {
else {
// select the cluster, not the neuron in the cluster
this.currentNucleus = nucleus.parent;
this.selectedSynapseNeuron = null;
this.expandArray = false;
}
}
@ -802,6 +804,7 @@ namespace NanoBrain.Unity {
this.currentNucleus = nucleus;
if (this.currentNucleus is Neuron neuron && neuron.receivers.Count == 0)
this.selectedOutput = this.currentNucleus;
this.selectedSynapseNeuron = null;
this.expandArray = false;
}
}
@ -810,7 +813,7 @@ namespace NanoBrain.Unity {
// May be used with storedPrefab...
Selection.activeObject = subCluster.prefab;
EditorGUIUtility.PingObject(subCluster.prefab);
ClusterViewer.previousPrefab = this.currentCluster.prefab;
ClusterView.previousPrefab = this.currentCluster.prefab;
Editor.CreateEditor(subCluster.prefab);
}

View File

@ -9,7 +9,7 @@ namespace NanoBrain.Unity {
public class ClusterViewer : Editor {
public static ClusterPrefab previousPrefab;
//public static ClusterPrefab previousPrefab;
public class GraphView : VisualElement {
protected Cluster currentCluster;

View File

@ -12,12 +12,19 @@ namespace NanoBrain.Unity {
[CustomPropertyDrawer(typeof(Cluster))]
class Cluster_Drawer : PropertyDrawer {
static Cluster_Drawer() {
SceneView.duringSceneGui += OnSceneGUI;
}
public static void Insepctor(SerializedObject serializedObject, string propertyName) {
EditorGUILayout.PropertyField(serializedObject.FindProperty(propertyName));
}
const float padding = 4f;
const float elementHeight = 64f; // height reserved for the VisualElement
private static ClusterView clusterView;
private static UnityEngine.Object selectedTarget;
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
float height = EditorGUIUtility.singleLineHeight + padding;
@ -69,7 +76,8 @@ namespace NanoBrain.Unity {
if (property.serializedObject.targetObjects.Length == 1) {
// Graph is not shown when multi-editing
UnityEngine.Object targetObject = property.serializedObject.targetObject;
Cluster cluster = SerializedPropertyUtility.GetManagedObjectForProperty(targetObject, property.propertyPath) as Cluster;
Cluster_Drawer.selectedTarget = targetObject;
Cluster cluster = SerializedPropertyUtility.GetManagedObjectForProperty(targetObject, property.propertyPath) as Cluster;
// key per field instance
string key = property.propertyPath + "_" + property.serializedObject.targetObject.GetInstanceID();//GetEntityId();
@ -85,18 +93,40 @@ namespace NanoBrain.Unity {
// content rect below header
Rect drawRect = new(fieldRect.x, headerRect.yMax + 2f, fieldRect.width, 450f);
Cluster_Drawer.clusterView = ClusterView.GetClusterView(property);
ClusterView.Render(drawRect, cluster, property);
//Debug.Log(prefab.cluster.defaultOutput.outputMagnitude);
}
}
}
else {
}
EditorGUI.indentLevel = indent;
EditorGUI.EndProperty();
}
private static void OnSceneGUI(SceneView sceneView) {
GameObject gameObject = null;
if (selectedTarget is Component c)
gameObject = c.gameObject;
else if (selectedTarget is GameObject g)
gameObject = g;
Handles.color = Color.yellow;
if (clusterView.selectedSynapseNeuron != null) {
foreach (Cluster sibling in clusterView.selectedSynapseNeuron.parent.instances) {
Neuron siblingNeuron = sibling.GetNucleus(clusterView.selectedSynapseNeuron.name) as Neuron;
Vector3 worldVector = gameObject.transform.TransformVector(siblingNeuron.outputValue);
Handles.DrawLine(gameObject.transform.position, gameObject.transform.position + worldVector);
}
}
else {
if (clusterView.currentNucleus is Neuron currentNeuron) {
Vector3 worldVector = gameObject.transform.TransformVector(currentNeuron.outputValue);
Handles.DrawLine(gameObject.transform.position, gameObject.transform.position + worldVector);
}
}
}
}
public static class SerializedPropertyUtility {

View File

@ -42,7 +42,8 @@ namespace NanoBrain {
/// A cluster is a multi-cluster when there is more than one instance.
/// The actual instances are only created at runtime.
/// The value instanceCount determines how many instances will be present at runtime.
[NonSerialized]
//[NonSerialized]
[SerializeReference]
public Cluster[] instances;
/// <summary>
@ -164,7 +165,7 @@ namespace NanoBrain {
}
}
if (Application.isPlaying) {
//if (Application.isPlaying) {
// Only create cluster siblings at runtime
foreach (Nucleus clonedNucleus in clonedNuclei) {
if (clonedNucleus is not Cluster clonedCluster)
@ -194,7 +195,7 @@ namespace NanoBrain {
if (clonedNucleus is not Cluster)
clonedNucleus.UpdateStateIsolated();
}
}
//}
}
/// \copydoc NanoBrain::Nucleus::ShallowCloneTo
@ -503,6 +504,8 @@ namespace NanoBrain {
foreach (Nucleus receptor in this.nuclei) {
if (receptor is Nucleus nucleus)
if (nucleus.name == nucleusName) {
// if (nucleus is Cluster cluster)
// cluster.CheckInstances();
foundNucleus = nucleus;
return true;
}
@ -524,6 +527,7 @@ namespace NanoBrain {
foreach (Nucleus nucleus in this.nuclei) {
if (nucleus is Cluster cluster) {
if (cluster.name == clusterName || cluster.name == clusterName0) {
// cluster.CheckInstances();
string subNucleusName = nucleusName[(dotPosition + 1)..];
return cluster.GetNucleus(subNucleusName);
}
@ -534,9 +538,11 @@ namespace NanoBrain {
else {
string nucleusName0 = nucleusName + ": 0";
foreach (Nucleus nucleus in this.nuclei) {
if (nucleus is Cluster) {
if (nucleus.name == nucleusName || nucleus.name == nucleusName0)
if (nucleus is Cluster cluster) {
if (nucleus.name == nucleusName || nucleus.name == nucleusName0) {
// cluster.CheckInstances();
return nucleus;
}
}
else if (nucleus.name == nucleusName)
return nucleus;
@ -574,6 +580,7 @@ namespace NanoBrain {
return this.GetNeuron(neuronName);
// See if we are already using a cluster for thingId
thingClusters ??= new();
if (thingClusters.TryGetValue(thingId, out Cluster cluster))
return cluster.GetNeuron(neuronName);