/* using UnityEditor; using UnityEngine; using System.Linq; using System.Collections.Generic; public class NeuroidLayer { public int ix = 0; public List neuroids = new(); } public class GraphEditorWindow : EditorWindow { private Nucleus currentNucleus; private List allNeuroids; private Dictionary neuroidPositions = new(); private List layers = new(); private void OnEnable() { EditorApplication.update += EditorUpdate; Selection.selectionChanged += OnSelectionChange; SelectNeuron(); } private void AddToLayer(NeuroidLayer layer, Nucleus nucleus) { layer.neuroids.Add(nucleus); nucleus.layerIx = layer.ix; // Store its position Vector2Int neuroidPosition = new(layer.ix, layer.neuroids.Count - 1); neuroidPositions[nucleus] = neuroidPosition; } private void BuildLayers() { // A temporary list to track what's been added to layers this.layers = new(); int layerIx = 0; Nucleus selectedNucleus = this.currentNucleus; if (selectedNucleus == null) return; NeuroidLayer currentLayer = new() { ix = layerIx }; //foreach (Nucleus outputNeuroid in selectedNucleus.receivers) { foreach (Receiver receiver in selectedNucleus.receivers) { Nucleus outputNeuroid = receiver.nucleus; if (outputNeuroid != null) { AddToLayer(currentLayer, outputNeuroid); Debug.Log($"layer {layerIx} nucleus {outputNeuroid.name}"); } } if (currentLayer.neuroids.Count > 0) { this.layers.Add(currentLayer); layerIx++; currentLayer = new() { ix = layerIx }; } AddToLayer(currentLayer, selectedNucleus); this.layers.Add(currentLayer); Debug.Log($"layer {layerIx} nucleus {selectedNucleus.name}"); layerIx++; currentLayer = new() { ix = layerIx }; int six = 0; // foreach (Synapse synapse in selectedNucleus.synapses.Values) { // Debug.Log($"Synapse {six}"); // Nucleus input = synapse.neuroid; //foreach ((Nucleus input, Synapse synapse) in selectedNucleus.synapses) { //foreach ((Nucleus input, float weight) in selectedNucleus.synapses) { foreach (Synapse synapse in selectedNucleus.synapses) { Nucleus input = synapse.nucleus; if (input != null) { AddToLayer(currentLayer, input); Debug.Log($"layer {layerIx} nucleus {input.name}"); } six++; } if (currentLayer.neuroids.Count > 0) { this.layers.Add(currentLayer); } } private void BuildLayers_old(List neuroids) { if (neuroids == null) return; // A temporary list to track what's been added to layers this.layers = new(); HashSet neuronVisited = new(); int layerIx = 0; // While there are unvisited neuroid while (neuroids.Any(neuroid => !neuronVisited.Contains(neuroid))) { // Create the next layer NeuroidLayer currentLayer = new() { ix = layerIx }; int neuroidIx = 0; foreach (Neuroid neuroid in neuroids) { // Skip neurons we already processed if (neuronVisited.Contains(neuroid)) continue; // if (neuroid.IsStale()) { // Debug.Log($"neuron {neuroid.name} is stale {neuroid.stale}"); // neuronVisited.Add(neuroid); // continue; // } // If the output neuroid is visited // Note: this does not yet work for multiple outputs yet (see the use of First()) // if (neuroid.receivers.Count == 0 // make sure the root neuroids are processed directly // || (neuronVisited.Contains(neuroid.receivers.First()) && neuroid.receivers.First().layerIx == layerIx - 1)) { if (neuroid.receivers.Count == 0 // make sure the root neuroids are processed directly || (neuronVisited.Contains(neuroid.receivers.First().nucleus) && neuroid.receivers.First().nucleus.layerIx == layerIx - 1)) { // Add it to the next layer currentLayer.neuroids.Add(neuroid); neuroid.layerIx = layerIx; // Register it as visited neuronVisited.Add(neuroid); // Store its position Vector2Int neuroidPosition = new(layerIx, neuroidIx); neuroidPositions[neuroid] = neuroidPosition; neuroidIx++; Debug.Log($"Layer {layerIx} neuron {neuroidIx} name {neuroid.name}"); } } if (currentLayer.neuroids.Count > 0) { this.layers.Add(currentLayer); layerIx++; } } } private void OnDisable() { EditorApplication.update -= EditorUpdate; Selection.selectionChanged -= OnSelectionChange; } private void OnSelectionChange() { SelectNeuron(); Repaint(); } private void EditorUpdate() { if (EditorApplication.isPlaying) Repaint(); } private void OnGUI() { GUILayout.Label("Graph Visualizer", EditorStyles.boldLabel); DrawGraph(); } private void DrawGraph() { if (currentNucleus == null) return; foreach (NeuroidLayer layer in layers) DrawLayer(layer); } private void DrawLayer(NeuroidLayer layer) { int column = layer.ix * 100; int nodeCount = layer.neuroids.Count; float maxValue = 0; foreach (Nucleus nucleus in layer.neuroids) { if (nucleus is Neuroid neuroid) { float value = neuroid.outputValue.magnitude; if (value > maxValue) maxValue = value; } } float spacing = 400f / nodeCount; float margin = 100 + spacing / 2; foreach (Nucleus layerNucleus in layer.neuroids) { if (layerNucleus is Neuroid layerNeuroid) { Vector2Int layerNeuroidPos = this.neuroidPositions[layerNeuroid]; Vector3 parentPos = new(100 + layerNeuroidPos.x * 100, margin + layerNeuroidPos.y * spacing, 0.1f); int i = 0; float inputSpacing = 400f / layerNeuroid.synapses.Count; float inputMargin = 100 + inputSpacing / 2; // foreach (Synapse synapse in layerNeuroid.synapses.Values) { // if (synapse.neuroid != null) { // if (this.neuroidPositions.ContainsKey(synapse.neuroid)) { // Vector2Int inputNeuroidPos = this.neuroidPositions[synapse.neuroid]; //foreach ((Nucleus neuroid, Synapse synapse) in layerNeuroid.synapses) { //foreach ((Nucleus neuroid, float weight) in layerNeuroid.synapses) { foreach (Synapse synapse in layerNeuroid.synapses) { Nucleus neuroid = synapse.nucleus; float weight = synapse.weight; if (neuroid != null) { if (this.neuroidPositions.ContainsKey(neuroid)) { Vector2Int inputNeuroidPos = this.neuroidPositions[neuroid]; if (inputNeuroidPos.x == layerNeuroidPos.x + 1) { Vector3 pos = new(100 + inputNeuroidPos.x * 100, inputMargin + inputNeuroidPos.y * inputSpacing, 0.0f); //float brightness = synapse.weight / 10.0f; float brightness = weight / 10.0f; Handles.color = new Color(brightness, brightness, brightness); Handles.DrawLine(parentPos, pos); } } } } float size = 20; if (layerNeuroid.isSleeping) Handles.color = Color.black; else { float brightness = layerNeuroid.outputValue.magnitude / maxValue; Handles.color = new Color(brightness, brightness, brightness); } Handles.DrawSolidDisc(parentPos, Vector3.forward, size); Vector3 labelPos = parentPos - Vector3.down * (size + 0.2f); // below disc along up axis GUIStyle style = new GUIStyle(EditorStyles.label) { alignment = TextAnchor.UpperCenter, normal = { textColor = Color.white }, fontStyle = FontStyle.Bold }; Handles.Label(labelPos, layerNeuroid.name, style); Rect neuronRect = new(parentPos.x - size, parentPos.y - size, size * 2, size * 2); Event e = Event.current; if (e != null && neuronRect.Contains(e.mousePosition)) { HandleMouseHover(layerNeuroid, neuronRect); // Process click if (e.type == EventType.MouseDown && e.button == 0) { // Consume the event so the scene doesn't also handle it e.Use(); HandleDiscClicked(layerNeuroid); } } i++; } } } private void HandleMouseHover(Neuroid neuroid, Rect rect) { GUIContent tooltip; // if (neuroid is SensoryNeuroid sensoryNeuroid) { // tooltip = new( // $"{sensoryNeuroid.name}" + // $"\nThing {sensoryNeuroid.receptor.thingType}" + // $"\nValue: {neuroid.outputValue}"); // } // else { tooltip = new( $"{neuroid.name}" + $"\nsynapse count {neuroid.synapses.Count}" + $"\nValue: {neuroid.outputValue}"); // } 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); } private void HandleDiscClicked(Nucleus nucleus) { this.currentNucleus = nucleus; BuildLayers(); } // Update node colors based on selected GameObjects private void SelectNeuron() { GameObject[] selectedObjects = Selection.gameObjects; if (selectedObjects.Length == 0) return; GameObject selectedObject = selectedObjects[0]; Boid boid = selectedObject.GetComponent(); if (boid == null) return; // Nucleus neuroid = boid.behaviour; // this.currentNucleus = neuroid; // if (neuroid == null) // this.allNeuroids = new(); // else // this.allNeuroids = neuroid.brain.neuroids; // Debug.Log($"Neuroncount = {this.allNeuroids.Count}"); // BuildLayers(); // Debug.Log($"Layercount = {this.layers.Count}"); } [MenuItem("Window/Neuroid Visualizer")] public static void ShowWindow() { GetWindow("Neuroid Visualizer"); } } */