diff --git a/Editor/Brain_Editor.cs b/Editor/Brain_Editor.cs deleted file mode 100644 index 47e3a36..0000000 --- a/Editor/Brain_Editor.cs +++ /dev/null @@ -1,75 +0,0 @@ -/* -using UnityEditor; -using UnityEditor.UIElements; - -using UnityEngine; -using UnityEngine.UIElements; - -namespace NanoBrain.Unity { - - [CustomEditor(typeof(Brain))] - public class Brain_Editor : Editor { - protected static VisualElement mainContainer; - protected static VisualElement inspectorContainer; - - public Brain component; - private SerializedProperty brainProp; - - public void OnEnable() { - component = target as Brain; - - if (Application.isPlaying == false && serializedObject != null) { - string propertyName = nameof(Brain.brainPrefab); - brainProp = serializedObject.FindProperty(propertyName); - } - } - - public override VisualElement CreateInspectorGUI() { - - if (Application.isPlaying == false) - serializedObject.Update(); - - - VisualElement root = new() { - style = { - paddingLeft = 0, - paddingRight = 0, - paddingTop = 0, - paddingBottom = 0 - } - }; - root.styleSheets.Add(Resources.Load("GraphStyles")); - - PropertyField brainField = new(brainProp) { - label = "Cluster Prefab" - }; - root.Add(brainField); - - CreateViewer(root, component.brain, component.gameObject); - - if (Application.isPlaying == false) - serializedObject.ApplyModifiedProperties(); - return root; - } - - public ClusterViewer.GraphView CreateViewer(VisualElement root, Cluster cluster, GameObject gameObject) { - VisualElement mainContainer = new() { - style = { - flexDirection = FlexDirection.Row, - minHeight = 450 - } - }; - ClusterViewer.GraphView graph = new(cluster); - graph.style.flexGrow = 1; - - mainContainer.Add(graph); - root.Add(mainContainer); - - graph.SetGraph(gameObject); - - return graph; - } - } - -} -*/ \ No newline at end of file diff --git a/Editor/Brain_Editor.cs.meta b/Editor/Brain_Editor.cs.meta deleted file mode 100644 index c91bea9..0000000 --- a/Editor/Brain_Editor.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f05072314d39990639a2dbf99f322664 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/BrainPickerWindow.cs b/Editor/ClusterPickerWindow.cs similarity index 100% rename from Editor/BrainPickerWindow.cs rename to Editor/ClusterPickerWindow.cs diff --git a/Editor/BrainPickerWindow.cs.meta b/Editor/ClusterPickerWindow.cs.meta similarity index 100% rename from Editor/BrainPickerWindow.cs.meta rename to Editor/ClusterPickerWindow.cs.meta diff --git a/Editor/ClusterPrefab_Drawer.cs b/Editor/ClusterPrefab_Drawer.cs deleted file mode 100644 index fd97e80..0000000 --- a/Editor/ClusterPrefab_Drawer.cs +++ /dev/null @@ -1,130 +0,0 @@ - -using System.Linq; -using System.Collections.Generic; -using UnityEngine; -using UnityEngine.UIElements; -using UnityEditor; -using System; -using System.Reflection; - -namespace NanoBrain.Unity { -/* - [CustomPropertyDrawer(typeof(ClusterPrefab))] - class ClusterPrefab_Drawer : PropertyDrawer { - 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 - - public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { - float height = EditorGUIUtility.singleLineHeight + padding; - string key = property.propertyPath + "_" + property.serializedObject.targetObject.GetInstanceID();//GetEntityId(); - s_foldouts.TryGetValue(key, out bool isOpen); - if (property.objectReferenceValue != null && isOpen) { - height += padding + elementHeight; - height = 500; - } - else - height = 36; - return height; - } - - static readonly Dictionary s_foldouts = new(); - - public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { - label = EditorGUI.BeginProperty(position, label, property); - - // Begin indent block - int indent = EditorGUI.indentLevel; - EditorGUI.indentLevel = 0; - - // Draw the object field on the top line - Rect fieldRect = new(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight); - EditorGUI.PropertyField(fieldRect, property, label); - - if (property.objectReferenceValue is ClusterPrefab prefab) { - // key per field instance - string key = property.propertyPath + "_" + property.serializedObject.targetObject.GetInstanceID();//GetEntityId(); - if (!s_foldouts.TryGetValue(key, out bool isOpen)) - isOpen = true; - - // foldout header rect - Rect headerRect = new(fieldRect.x, fieldRect.yMax + 4f, fieldRect.width, EditorGUIUtility.singleLineHeight); - isOpen = EditorGUI.Foldout(headerRect, isOpen, "Gaph", true); - s_foldouts[key] = isOpen; - - if (isOpen) { - // content rect below header - Rect drawRect = new(fieldRect.x, headerRect.yMax + 2f, fieldRect.width, 450f); - - ClusterView.Render(drawRect, prefab.cluster, property); - //Debug.Log(prefab.cluster.defaultOutput.outputMagnitude); - } - } - - EditorGUI.indentLevel = indent; - EditorGUI.EndProperty(); - } - } - -/* - [InitializeOnLoad] - static class ClusterPrefabInspectorRepaints { - static ClusterPrefabInspectorRepaints() { - EditorApplication.update += OnEditorUpdate; - } - - static double lastRepaint = 0; - const double repaintInterval = 1.0 / 15.0; // up to 15 FPS in inspector - - static void OnEditorUpdate() { - if (!Application.isPlaying) return; - - // throttle repaint frequency - if (EditorApplication.timeSinceStartup - lastRepaint < repaintInterval) return; - lastRepaint = EditorApplication.timeSinceStartup; - - // Find all open inspectors (Editors) that target objects containing ClusterPrefab fields - var editors = Resources.FindObjectsOfTypeAll(); - foreach (var ed in editors) { - var targets = ed.targets; - if (targets == null) - continue; - bool shouldRepaint = targets.Any(t => ObjectHasClusterPrefabField(t)); - if (shouldRepaint) { - try { - ed.Repaint(); - } - catch { - // ignore - } - } - } - } - - static bool ObjectHasClusterPrefabField(UnityEngine.Object obj) { - if (obj == null) - return false; - Type type = obj.GetType(); - // search fields (instance, non-public/public) - FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - foreach (FieldInfo f in fields) { - if (f.FieldType == typeof(ClusterPrefab)) - return true; - // also handle arrays/lists of ClusterPrefab or serializable wrappers: - if (f.FieldType.IsArray && f.FieldType.GetElementType() == typeof(ClusterPrefab)) - return true; - if (f.FieldType.IsGenericType) { - Type[] gen = f.FieldType.GetGenericArguments(); - if (gen.Length == 1 && gen[0] == typeof(ClusterPrefab)) - return true; - } - } - return false; - } - } -*/ - -} diff --git a/Editor/ClusterPrefab_Drawer.cs.meta b/Editor/ClusterPrefab_Drawer.cs.meta deleted file mode 100644 index 6e85a28..0000000 --- a/Editor/ClusterPrefab_Drawer.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5f43b401b7d59dec7ac7d493cbc4927d -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/ClusterView.cs b/Editor/ClusterView.cs index 5c53682..d15cafb 100644 --- a/Editor/ClusterView.cs +++ b/Editor/ClusterView.cs @@ -7,6 +7,11 @@ namespace NanoBrain.Unity { public class ClusterView { + public ClusterView(string key) { + this.key = key; + ClusterView.clusterViews[this.key] = this; + } + public static ClusterPrefab previousPrefab; private static readonly float discRadius = 20; @@ -19,17 +24,17 @@ namespace NanoBrain.Unity { } public Mode mode = Mode.Focus; - static readonly Dictionary clusterViews = new(); + public static readonly Dictionary clusterViews = new(); public static ClusterView GetClusterView(SerializedProperty property) { string key = property.propertyPath + "_" + property.serializedObject.targetObject.GetInstanceID();//GetEntityId(); if (!clusterViews.TryGetValue(key, out ClusterView clusterView)) - clusterView = new() { key = key }; + clusterView = new(key);// { key = key }; return clusterView; } public static ClusterView GetClusterView(SerializedObject serializedObject) { string key = serializedObject.targetObject.GetInstanceID().ToString(); //GetEntityId().ToString(); if (!clusterViews.TryGetValue(key, out ClusterView clusterView)) - clusterView = new() { key = key }; + clusterView = new(key); // { key = key }; return clusterView; } diff --git a/Editor/ClusterViewer.cs b/Editor/ClusterViewer.cs deleted file mode 100644 index 41e06ca..0000000 --- a/Editor/ClusterViewer.cs +++ /dev/null @@ -1,902 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -using UnityEditor; -using UnityEngine; -using UnityEngine.UIElements; - -namespace NanoBrain.Unity { - - public class ClusterViewer : Editor { - - //public static ClusterPrefab previousPrefab; - - public class GraphView : VisualElement { - protected Cluster currentCluster; - protected SerializedObject serializedBrain; - protected Nucleus _currentNucleus; - protected virtual Nucleus currentNucleus { - get => _currentNucleus; - set => _currentNucleus = value; - } - //protected Nucleus currentNucleus; - protected Nucleus selectedOutput; - // Only used when selecting a synapse to a multi-cluster - protected Nucleus selectedSynapseNeuron; - - protected GameObject gameObject; - //private bool expandArray = false; - - protected ClusterPrefab prefabAsset; - protected VisualElement topMenuContainer; - protected ScrollView scrollView; - protected IMGUIContainer graphContainer; - //protected readonly PopupField outputsPopup; - - public enum Mode { - Focus, - Full - } - public Mode mode = Mode.Focus; - - public GraphView(Cluster cluster) { - this.currentCluster = cluster; - - name = "content"; - style.flexGrow = 1; - - topMenuContainer = new() { - style = { - flexDirection = FlexDirection.Row, - alignItems = Align.Center, - } - }; - - EnumField modePopup = new(mode); - modePopup.style.width = 80; - modePopup.RegisterValueChangedCallback(OnModeChange); - topMenuContainer.Add(modePopup); - - scrollView = new(ScrollViewMode.Horizontal); - scrollView.style.position = Position.Absolute; - scrollView.style.left = 0; scrollView.style.top = 0; - scrollView.style.right = 0; scrollView.style.bottom = 0; - scrollView.horizontalScrollerVisibility = ScrollerVisibility.Auto; // Auto shows when needed - scrollView.verticalScrollerVisibility = ScrollerVisibility.Hidden; - - graphContainer = new(OnIMGUI) { - pickingMode = PickingMode.Position, - focusable = true - }; - - scrollView.contentContainer.Add(graphContainer); - Add(scrollView); - Add(topMenuContainer); - - // Subscribe when added to panel (editor UI ready) - RegisterCallback(evt => Subscribe()); - RegisterCallback(evt => Unsubscribe()); - } - - protected virtual void OnModeChange(ChangeEvent changeEvent) { - this.mode = (Mode)changeEvent.newValue; - } - - private bool subscribed = false; - void Subscribe() { - if (subscribed) - return; - SceneView.duringSceneGui += OnSceneGUI; - subscribed = true; - SceneView.RepaintAll(); - } - - void Unsubscribe() { - if (!subscribed) - return; - SceneView.duringSceneGui -= OnSceneGUI; - subscribed = false; - } - - public void SetGraph(GameObject gameObject) { - this.gameObject = gameObject; - if (this.currentCluster == null) - return; - - if (Application.isPlaying == false) - this.serializedBrain = new SerializedObject(this.currentCluster.prefab); - this.selectedOutput = this.currentCluster.defaultOutput; - this.currentNucleus = this.selectedOutput; - Rebuild(); - } - - void Rebuild() { - if (this.currentNucleus == null) - return; - - string path = AssetDatabase.GetAssetPath(this.currentCluster.prefab); // or known path - this.prefabAsset = AssetDatabase.LoadAssetAtPath(path); - if (this.prefabAsset == null) { - // create in memory save if it doesn't exist - this.prefabAsset = CreateInstance(); - //Debug.LogError("Cluster Prefab is not found on disk"); - } - } - - public void OnIMGUI() { - var rect = graphContainer.layout; // container local size - int id = GUIUtility.GetControlID(123456, FocusType.Passive, new Rect(0, 0, rect.width, rect.height)); - - //int id = GUIUtility.GetControlID(FocusType.Passive); - - if (Application.isPlaying == false && serializedBrain != null) - serializedBrain.Update(); - - Rect r = new Rect(0, 0, rect.width, rect.height); - ClusterView.Render(r, currentCluster, serializedBrain); - // ClusterView clusterView = ClusterView.GetClusterView(serializedBrain); - // clusterView.currentCluster ??= currentCluster; - // clusterView.DrawGraph(id); - - // Handles.BeginGUI(); - // DrawGraph(); - // Handles.EndGUI(); - } - - #region Graph - - // public virtual void DrawGraph() { - // if (mode == Mode.Focus) - // DrawFocusGraph(); - // // else - // // DrawFullGraph(); - // } - - #region Full Graph - /* - protected void DrawFullGraph() { - //Dag dag = GenerateGraph(this.prefab); - Dag dag = GenerateGraph(this.selectedOutput); - Dag.ComputeLayout(dag); - // Draw edges - foreach (Dag.Edge e in dag.edges) { - Dag.Node from = dag.nodes.FirstOrDefault(x => x.id == e.fromId); - Dag.Node to = dag.nodes.FirstOrDefault(x => x.id == e.toId); - if (from == null || to == null) - continue; - - Vector2 fromPosition = from.position; - Vector2 toPosition = to.position; - DrawEdge(fromPosition, toPosition); - } - - // Draw nodes - foreach (Dag.Node n in dag.nodes) - DrawNucleus(n.nucleus, n.position, 1, n.radius); - - // Determine graph width - float width = 0; - float currentNucleusPosition = 0; - foreach (Dag.Node node in dag.nodes) { - if (node.position.x > width) - width = node.position.x; - if (node.nucleus == currentNucleus) - currentNucleusPosition = node.position.x; - } - - // Resize the graph container to the full graph width - float margin = 50f; - graphContainer.style.width = width + 2 * margin; - - // Scroll to the current nucleus - float viewportWidth = scrollView.layout.width; - // center currentNucleus in viewport - float desiredScrollX = currentNucleusPosition - viewportWidth * 0.5f; - // clamp between 0 and maximum scrollable range - float maxScrollX = Mathf.Max(0f, graphContainer.resolvedStyle.width - viewportWidth); - desiredScrollX = Mathf.Clamp(desiredScrollX, 0f, maxScrollX); - - Vector2 current = scrollView.scrollOffset; - scrollView.scrollOffset = new Vector2(desiredScrollX, current.y); - } - - public Dag GenerateGraph(Nucleus rootNucleus) { - Dag dag = new(); - if (rootNucleus == null) - return dag; - - int ix = 0; - Dag.Node receiver = new() { - id = ix, - //title = nucleus.name, - nucleus = rootNucleus - }; - dag.nodes.Add(receiver); - ix++; - DescendGraph(receiver, ref ix, dag); - return dag; - } - - private void DescendGraph(Dag.Node receiver, ref int ix, Dag dag) { - Neuron receiverNeuron = receiver.nucleus as Neuron; - foreach (Synapse synapse in receiverNeuron.synapses) { - Nucleus nucleus = synapse.neuron; - if (nucleus.parent != null && nucleus.parent != currentNucleus.parent) { - nucleus = nucleus.parent; - } - string nucleusName = nucleus.name; - Dag.Node synapseNode = dag.FindNode(nucleusName); - if (synapseNode == null) { - synapseNode = new() { - id = ix, - nucleus = nucleus - }; - dag.nodes.Add(synapseNode); - } - Dag.Edge edge = new() { - fromId = synapseNode.id, - toId = receiver.id - }; - dag.edges.Add(edge); - ix++; - DescendGraph(synapseNode, ref ix, dag); - } - } - */ - #endregion Full Graph - - #region Focus Graph - /* - 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 (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) { - 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(cluster, pos, maxValue, size); - 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, size); - 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, 20); - - } - } - 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, 20); - } - } - else { - DrawAllOutputs(position, size); - DrawOutputs(position, size); - } - graphContainer.style.width = 300; - } - - protected void DrawReceivers(Nucleus nucleus, Vector3 parentPos, float size) { - List 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, size); - row++; - } - if (nucleus == this.selectedOutput) { - Vector3 pos = new(50, margin + row * spacing, 0); - if (ClusterViewer.previousPrefab != null) { - DrawEdge(parentPos, pos); - DrawClusterPrefab(ClusterViewer.previousPrefab, pos, size); - row++; - } - pos = new(50, margin + row * spacing, 0); - DrawEdge(parentPos, pos); - DrawAllOutputs(pos, size); - } - } - - protected void DrawSynapses(Nucleus nucleus, Vector3 parentPos, float size) { - if (nucleus is not Neuron neuron) - return; - - if (this.selectedSynapseNeuron != null) { - DrawClusterSynapses(this.selectedSynapseNeuron, parentPos, size); - return; - } - if (nucleus == 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 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 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, size, color); - row++; - } - } - - protected void DrawClusterSynapses(Nucleus nucleus, Vector3 parentPos, float size) { - 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, size, color); - GUIStyle style = new(EditorStyles.label) { - alignment = TextAnchor.UpperCenter, - normal = { textColor = Color.white }, - fontStyle = FontStyle.Bold, - }; - Vector3 labelPos = position - Vector3.down * (size + 5); // below neuron - string name = $"{sibling.baseName}\n{nucleus.name}"; - Handles.Label(labelPos, name, style); - row++; - } - expandArray = false; - } - - protected void DrawOutputs(Vector2 parentPos, float size) { - 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 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, size, color); - row++; - } - } - */ - #endregion Focus Graph - /* - protected void DrawNucleus(Nucleus nucleus, Vector3 position, float maxValue, float size) { - 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, size, color); - } - - protected void DrawNucleus(Nucleus nucleus, Vector3 position, float size, 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); - } - - if (nucleus is MemoryCell) { - Handles.color = Color.white; - Handles.DrawWireDisc(position + Vector3.right * 10, Vector3.forward, size); - } - - Handles.color = color; - Handles.DrawSolidDisc(position, Vector3.forward, size); - - Handles.color = Color.white; - // Position the label in front of the disc - Vector3 labelPosition = position + (Vector3.forward * 0.1f); - - GUIStyle style = new(EditorStyles.label) { - alignment = TextAnchor.MiddleCenter, - normal = { textColor = Color.white }, - 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 (expandArray == false) {// || nucleus != currentNucleus) { - // put name below nucleus - Vector3 labelPos = position - Vector3.down * (size + 5); // below neuron - style.alignment = TextAnchor.UpperCenter; - - if (nucleus.parent != null && currentNucleus != null && nucleus.parent != currentNucleus.parent && nucleus.parent is Cluster parentCluster1) { - // This neuron is part of another cluster - parentCluster1.name ??= ""; - int colonPos = parentCluster1.name.IndexOf(":"); - string baseName; - if (colonPos > 0 && colonPos < parentCluster1.name.Length - 2) - baseName = parentCluster1.name[..colonPos] + "\n"; - else - baseName = parentCluster1.name + "\n"; - Handles.Label(labelPos, baseName + nucleus.name, style); - } - else { - nucleus.name ??= ""; - int colonPos = nucleus.name.IndexOf(":"); - if (colonPos > 0 && colonPos < nucleus.name.Length - 2) { - // if it is an array, we should not show the :0 of the first element - string baseName = nucleus.name[..colonPos]; - Handles.Label(labelPos, baseName, style); - } - else - Handles.Label(labelPos, nucleus.name, style); - } - } - - // Tooltip - Rect neuronRect = new(position.x - size, position.y - size, size * 2, size * 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); - } - } - } - - protected void DrawCluster(Cluster cluster, Vector3 position, Color color, float size) { - GUIStyle labelTextStyle = new(EditorStyles.label) { - normal = { textColor = Color.white }, - fontStyle = FontStyle.Bold, - }; - - if (expandArray) { - // Put array indices above the discs - labelTextStyle.alignment = TextAnchor.LowerCenter; - Vector3 labelPosition = position + Vector3.down * (size + 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, size + 5); - } - - protected void DrawClusterPrefab(ClusterPrefab prefab, Vector2 position, float size) { - Handles.color = Color.black; - Handles.DrawSolidDisc(position, Vector3.forward, size); - // Draw a circle around the disc to indicate this is a Cluster - Handles.color = Color.white; - Handles.DrawWireDisc(position, Vector3.forward, size + 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 * (size + 5); // below neuron - style.alignment = TextAnchor.UpperCenter; - Handles.Label(labelPos, prefab.name, style); - - Rect neuronRect = new(position.x - size, position.y - size, size * 2, size * 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; - CreateEditor(prefab); - } - } - } - - protected void DrawAllOutputs(Vector2 position, float size) { - 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 - size, position.y - size, size * 2, size * 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, float radius = 20) { - Handles.color = Color.white; - // Handles.DrawLine(from, to); - - Vector2 dir = to - from; - float len = dir.magnitude; - if (len <= 2f * radius || len <= Mathf.Epsilon) - // line too short - return; - - Vector2 n = dir / len; // normalized - Vector2 a = from + n * radius; - Vector2 b = to - n * radius; - Handles.DrawLine(a, b); - } - - protected 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; - expandArray = false; - } - else { - this.currentNucleus = nucleus; - if (this.currentNucleus is Neuron neuron && neuron.receivers.Count == 0) - this.selectedOutput = this.currentNucleus; - this.selectedSynapseNeuron = null; - expandArray = false; - } - - } - else { - // select the cluster, not the neuron in the cluster - this.currentNucleus = nucleus.parent; - expandArray = false; - } - } - else { - this.currentNucleus = nucleus; - if (this.currentNucleus is Neuron neuron && neuron.receivers.Count == 0) - this.selectedOutput = this.currentNucleus; - 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; - ClusterEditor newEditor = CreateEditor(subCluster.prefab) as ClusterEditor; - } - - protected void OnAllOutputsClick() { - this.currentNucleus = null; - this.selectedOutput = null; - expandArray = false; - } - */ - #endregion Graph - - void OnSceneGUI(SceneView sceneView) { - if (this.gameObject != null) { - - Handles.color = Color.yellow; - if (this.selectedSynapseNeuron != null) { - foreach (Cluster sibling in this.selectedSynapseNeuron.parent.instances) { - Neuron siblingNeuron = sibling.GetNucleus(this.selectedSynapseNeuron.name) as Neuron; - Vector3 worldVector = this.gameObject.transform.TransformVector(siblingNeuron.outputValue); - Handles.DrawLine(this.gameObject.transform.position, this.gameObject.transform.position + worldVector); - } - // if (this.currentNucleus is Cluster cluster) { - // foreach (Cluster sibling in cluster.siblingClusters) { - - // } - // } - // // if (this.currentNucleus is IReceptor receptor) { - // // foreach (Nucleus nucleus in receptor.nucleiArray) { - // // if (nucleus is Neuron neuron) { - // // Vector3 worldVector = this.gameObject.transform.TransformVector(neuron.outputValue); - // // Handles.color = Color.yellow; - // // Handles.DrawLine(this.gameObject.transform.position, this.gameObject.transform.position + worldVector); - // // } - // // } - } - else { - if (this.currentNucleus is Neuron currentNeuron) { - Vector3 worldVector = this.gameObject.transform.TransformVector(currentNeuron.outputValue); - Handles.DrawLine(this.gameObject.transform.position, this.gameObject.transform.position + worldVector); - } - } - } - } - - } - } - - public class NeuroidLayer { - public int ix = 0; - public List neuroids = new(); - } - - - -} \ No newline at end of file diff --git a/Editor/ClusterViewer.cs.meta b/Editor/ClusterViewer.cs.meta deleted file mode 100644 index 7859dec..0000000 --- a/Editor/ClusterViewer.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 4fe58945c76d153edacc220597474ad2 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/Cluster_Drawer.cs b/Editor/Cluster_Drawer.cs index d8be713..709bbcc 100644 --- a/Editor/Cluster_Drawer.cs +++ b/Editor/Cluster_Drawer.cs @@ -16,12 +16,8 @@ namespace NanoBrain.Unity { if (clusterView != null) clusterView.initialized = false; } - ~Cluster_Drawer() { - SceneView.duringSceneGui -= OnSceneGUI; - } - //static readonly Dictionary s_foldouts = new(); - static readonly Dictionary clusterViews = new(); + //static readonly Dictionary clusterViews = new(); public static void Insepctor(SerializedObject serializedObject, string propertyName) { EditorGUILayout.PropertyField(serializedObject.FindProperty(propertyName)); @@ -32,22 +28,22 @@ namespace NanoBrain.Unity { private static ClusterView clusterView; private static UnityEngine.Object selectedTarget; - public ClusterView GetClusterView(SerializedProperty property) { - string key = property.propertyPath + "_" + property.serializedObject.targetObject.GetInstanceID();//GetEntityId(); - if (clusterViews.TryGetValue(key, out ClusterView view)) - return view; + // public ClusterView GetClusterView(SerializedProperty property) { + // string key = property.propertyPath + "_" + property.serializedObject.targetObject.GetInstanceID();//GetEntityId(); + // if (clusterViews.TryGetValue(key, out ClusterView view)) + // return view; - view = new ClusterView(); - clusterViews[key] = view; - return view; - } + // view = new ClusterView(key); + // clusterViews[key] = view; + // return view; + // } public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { float height = EditorGUIUtility.singleLineHeight + padding; // string key = property.propertyPath + "_" + property.serializedObject.targetObject.GetInstanceID();//GetEntityId(); // //s_foldouts.TryGetValue(key, out bool isOpen); // clusterViews.TryGetValue(key, out ClusterView view); - ClusterView view = GetClusterView(property); + ClusterView view = ClusterView.GetClusterView(property); SerializedProperty prefabProp = property.FindPropertyRelative(nameof(Cluster.prefab)); if (prefabProp.objectReferenceValue != null && view.isOpen) { height += padding + elementHeight; @@ -60,7 +56,7 @@ namespace NanoBrain.Unity { public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { - ClusterView clusterView = GetClusterView(property); + ClusterView clusterView = ClusterView.GetClusterView(property); label = EditorGUI.BeginProperty(position, label, property); @@ -91,18 +87,18 @@ namespace NanoBrain.Unity { } } - if (prefabProp.objectReferenceValue is ClusterPrefab prefab) { + if (prefabProp.objectReferenceValue != null) { // // Graph is not shown when multi-editing if (property.serializedObject.targetObjects.Length == 1) { UnityEngine.Object targetObject = property.serializedObject.targetObject; Cluster_Drawer.selectedTarget = targetObject; - + Cluster cluster; - if (clusterView.initialized) { + if (clusterView.initialized || Application.isPlaying) { cluster = SerializedPropertyUtility.GetManagedObjectForProperty(targetObject, property.propertyPath) as Cluster; } else { - // This does not work property yet it seems + // This does not work properly yet it seems Debug.Log("Instantiate"); ClusterPrefab clusterPrefab = prefabProp.objectReferenceValue as ClusterPrefab; cluster = new(clusterPrefab); @@ -127,8 +123,13 @@ namespace NanoBrain.Unity { // content rect below header Rect drawRect = new(fieldRect.x, headerRect.yMax + 2f, fieldRect.width, 450f); + if (clusterView.currentCluster == null || clusterView.currentCluster != cluster) { + clusterView.currentCluster = cluster; + clusterView.currentNucleus = cluster.defaultOutput; + clusterView.selectedOutput = clusterView.currentNucleus; + } Cluster_Drawer.clusterView = clusterView; - ClusterView.Render(drawRect, cluster, property); + clusterView.Render(drawRect); //Debug.Log(prefab.cluster.defaultOutput.outputMagnitude); } } @@ -149,15 +150,15 @@ namespace NanoBrain.Unity { 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; + if (Cluster_Drawer.clusterView.selectedSynapseNeuron != null) { + foreach (Cluster sibling in Cluster_Drawer.clusterView.selectedSynapseNeuron.parent.instances) { + Neuron siblingNeuron = sibling.GetNucleus(Cluster_Drawer.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) { + if (Cluster_Drawer.clusterView.currentNucleus is Neuron currentNeuron) { Vector3 worldVector = gameObject.transform.TransformVector(currentNeuron.outputValue); Handles.DrawLine(gameObject.transform.position, gameObject.transform.position + worldVector); } @@ -165,7 +166,7 @@ namespace NanoBrain.Unity { } private static void OnSelectionChanged() { - foreach (ClusterView clusterView in clusterViews.Values) { + foreach (ClusterView clusterView in ClusterView.clusterViews.Values) { clusterView.initialized = false; } } diff --git a/Runtime/Scripts/Core/Cluster.cs b/Runtime/Scripts/Core/Cluster.cs index 9417ff1..96e6ece 100644 --- a/Runtime/Scripts/Core/Cluster.cs +++ b/Runtime/Scripts/Core/Cluster.cs @@ -109,6 +109,8 @@ namespace NanoBrain { /// Strange that this does not take any parameters or return values. /// Where which the clone be found??? private void ClonePrefab() { + this.name += " " + Time.time; + Debug.Log($"Clone Prefab {this.prefab.name} -> {this.name}"); if (this.prefab == null || this.prefab.cluster == null || this.prefab.cluster.nuclei == null) return; @@ -176,12 +178,12 @@ namespace NanoBrain { }; for (int instanceIx = 1; instanceIx < clonedCluster.instanceCount; instanceIx++) { // Create another sibling - // Debug.Log($"create {clonedCluster.prefab.name} sibling"); Cluster sibling = new(clonedCluster.prefab, this) { - name = $"{clonedCluster.baseName}: {instanceIx}", + name = $"{clonedCluster.baseName}: {instanceIx} [{Time.time}]", parent = this.parent, instanceCount = this.instanceCount, }; + Debug.Log($"create {clonedCluster.prefab.name} sibling: {sibling.name}"); siblings.Add(sibling); CopyAllExternalReceivers(clonedCluster, sibling, clonedCluster.prefab, this); }