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(); } }