using System.Collections.Generic; using UnityEngine; using UnityEditor; namespace NanoBrain.Unity { public class ClusterView { private static readonly float size = 20; protected class ViewState { public string key = null; public Vector2 scrollPos = Vector2.zero; public bool expandArray = false; public Nucleus currentNucleus = null; } static Dictionary viewStates = new(); private static ViewState GetViewState(SerializedProperty property) { string key = property.propertyPath + "_" + property.serializedObject.targetObject.GetEntityId(); if (!viewStates.TryGetValue(key, out ViewState state)) state = new() { key = key }; return state; } private static void UpdateViewState(ViewState viewState) { viewStates[viewState.key] = viewState; } public static void Render(Rect drawRect, Cluster cluster, SerializedProperty property) { ViewState state = GetViewState(property); // background EditorGUI.DrawRect(drawRect, Color.black); const float contentWidth = 1000f; Rect contentRect = new Rect(0f, 0f, contentWidth, drawRect.height); // Begin horizontal-only scroll view state.scrollPos = GUI.BeginScrollView(drawRect, state.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)); EditorGUI.DrawRect(new Rect(0f, 0f, contentWidth, drawRect.height), new Color(0.08f, 0.08f, 0.08f, 1f)); GUI.EndGroup(); GUI.EndScrollView(); // Clip to drawRect so Handles are not drawn outside the black area 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)); Handles.BeginGUI(); DrawNucleus(state, cluster, new Vector2(100, 100), Color.black); Handles.EndGUI(); GUI.EndGroup(); // end inner group GUI.EndGroup(); // end clipping group UpdateViewState(state); } protected static void DrawNucleus(ViewState state, Nucleus nucleus, Vector2 position, Color color) { if (nucleus == null) return; if (nucleus == state.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 + Vector2.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; // + (Vector2.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 (state.expandArray == false) {// || nucleus != currentNucleus) { // put name below nucleus Vector3 labelPos = position - Vector2.down * (size + 5); // below neuron style.alignment = TextAnchor.UpperCenter; if (nucleus.parent != null && state.currentNucleus != null && nucleus.parent != state.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); // } // } } } }