diff --git a/Editor/ClusterEditor.cs b/Editor/ClusterEditor.cs index 30365b3..2eb53dc 100644 --- a/Editor/ClusterEditor.cs +++ b/Editor/ClusterEditor.cs @@ -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); diff --git a/Editor/ClusterView.cs b/Editor/ClusterView.cs index 4b1ce6d..e9d147f 100644 --- a/Editor/ClusterView.cs +++ b/Editor/ClusterView.cs @@ -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; @@ -199,7 +201,7 @@ namespace NanoBrain.Unity { DrawOutputs(position); return; } - + Dag dag = GenerateGraph(this.selectedOutput); Dag.ComputeLayout(dag); @@ -269,7 +271,7 @@ namespace NanoBrain.Unity { Neuron receiverNeuron = receiver.nucleus as Neuron; if (receiverNeuron == null) return; - + foreach (Synapse synapse in receiverNeuron.synapses) { Nucleus nucleus = synapse.neuron; if (nucleus.parent != null && nucleus.parent != currentNucleus.parent) { @@ -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); } diff --git a/Editor/ClusterViewer.cs b/Editor/ClusterViewer.cs index 73b72ac..41e06ca 100644 --- a/Editor/ClusterViewer.cs +++ b/Editor/ClusterViewer.cs @@ -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; diff --git a/Editor/Cluster_Drawer.cs b/Editor/Cluster_Drawer.cs index 30f94a9..989d774 100644 --- a/Editor/Cluster_Drawer.cs +++ b/Editor/Cluster_Drawer.cs @@ -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 { diff --git a/Runtime/Scripts/Core/Cluster.cs b/Runtime/Scripts/Core/Cluster.cs index 44c1a6e..78f6504 100644 --- a/Runtime/Scripts/Core/Cluster.cs +++ b/Runtime/Scripts/Core/Cluster.cs @@ -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; /// @@ -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);