Migrated ClusterEditor to ClusterView
This commit is contained in:
parent
9d43772b79
commit
ebef711981
@ -1,3 +1,4 @@
|
||||
/*
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
|
||||
@ -71,3 +72,4 @@ namespace NanoBrain.Unity {
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
@ -8,6 +8,73 @@ using UnityEngine.UIElements;
|
||||
namespace NanoBrain.Unity {
|
||||
|
||||
[CustomEditor(typeof(ClusterPrefab))]
|
||||
public class ClusterEditor : Editor {
|
||||
const float drawAreaWidth = 300f; // adjust as needed
|
||||
const float padding = 6f;
|
||||
ClusterPrefab clusterPrefab;
|
||||
Nucleus currentNucleus {
|
||||
get { return clusterView.currentNucleus; }
|
||||
set { clusterView.currentNucleus = value; }
|
||||
}
|
||||
Cluster currentCluster => clusterView.currentCluster;
|
||||
protected Nucleus selectedOutput;
|
||||
ClusterView clusterView;
|
||||
|
||||
void OnEnable() {
|
||||
clusterPrefab = (ClusterPrefab)target;
|
||||
clusterView = ClusterView.GetClusterView(serializedObject);
|
||||
clusterView.currentCluster ??= clusterPrefab.cluster;
|
||||
clusterView.currentNucleus = clusterPrefab.cluster.defaultOutput;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI() {
|
||||
// Begin horizontal split
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
// Left: fixed-width drawing area
|
||||
GUILayoutOption[] leftOptions = { GUILayout.Width(drawAreaWidth) };
|
||||
Rect drawRect = GUILayoutUtility.GetRect(drawAreaWidth, 450f, leftOptions); // height adjustable
|
||||
|
||||
// add padding inside rect
|
||||
Rect innerRect = new(drawRect.x + padding, drawRect.y + padding,
|
||||
drawRect.width - padding * 2, drawRect.height - padding * 2);
|
||||
|
||||
clusterView.Render(innerRect);
|
||||
|
||||
// Right: info panel (takes remaining width)
|
||||
EditorGUILayout.BeginVertical(GUILayout.ExpandWidth(true));
|
||||
float prevLabelWidth = EditorGUIUtility.labelWidth;
|
||||
EditorGUIUtility.labelWidth = 100f; // smaller labels -> larger fields
|
||||
|
||||
InspectorHandler(serializedObject);
|
||||
|
||||
EditorGUIUtility.labelWidth = prevLabelWidth;
|
||||
EditorGUILayout.EndVertical(); // end right column
|
||||
EditorGUILayout.EndHorizontal(); // end split
|
||||
}
|
||||
|
||||
// public override void OnInspectorGUI() {
|
||||
// float totalWidth = EditorGUIUtility.currentViewWidth;
|
||||
// float leftW = drawAreaWidth;
|
||||
// float rightW = Mathf.Max(80f, totalWidth - leftW - padding);
|
||||
|
||||
// Rect row = GUILayoutUtility.GetRect(totalWidth, 450f); // request full width
|
||||
// Rect leftRect = new Rect(row.x, row.y, leftW, row.height);
|
||||
// Rect rightRect = new Rect(row.x + leftW + padding, row.y, rightW, 450f);
|
||||
|
||||
// Rect innerLeft = new Rect(leftRect.x + padding, leftRect.y + padding,
|
||||
// leftRect.width - padding*2, leftRect.height - padding*2);
|
||||
// clusterView.Render(innerLeft);
|
||||
|
||||
// GUILayout.BeginArea(rightRect);
|
||||
// float prev = EditorGUIUtility.labelWidth;
|
||||
// EditorGUIUtility.labelWidth = 100f;
|
||||
// InspectorHandler(serializedObject);
|
||||
// EditorGUIUtility.labelWidth = prev;
|
||||
// GUILayout.EndArea();
|
||||
// }
|
||||
//}
|
||||
/*
|
||||
public class ClusterEditor : ClusterViewer {
|
||||
|
||||
public override VisualElement CreateInspectorGUI() {
|
||||
@ -130,10 +197,10 @@ namespace NanoBrain.Unity {
|
||||
|
||||
inspectorContainer.Add(inspectorIMGUIContainer);
|
||||
}
|
||||
|
||||
*/
|
||||
#region Inspector
|
||||
|
||||
private VisualElement inspectorIMGUIContainer;
|
||||
//private VisualElement inspectorIMGUIContainer;
|
||||
private bool showSynapses = true;
|
||||
private bool showActivation = true;
|
||||
protected bool breakOnWake = false;
|
||||
@ -199,7 +266,7 @@ namespace NanoBrain.Unity {
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
if (anythingChanged) {
|
||||
EditorUtility.SetDirty(prefabAsset);
|
||||
EditorUtility.SetDirty(clusterPrefab);
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
}
|
||||
@ -238,12 +305,12 @@ namespace NanoBrain.Unity {
|
||||
EditorGUILayout.IntField("Instances", instanceCount, GUILayout.MinWidth(150));
|
||||
|
||||
if (GUILayout.Button("Add")) {
|
||||
Undo.RecordObject(prefabAsset, "Array add " + prefabAsset.name);
|
||||
Undo.RecordObject(clusterPrefab, "Array add " + clusterPrefab.name);
|
||||
cluster.AddInstance();
|
||||
anythingChanged = true;
|
||||
}
|
||||
if (GUILayout.Button("Del")) {
|
||||
Undo.RecordObject(prefabAsset, "Array delete " + prefabAsset.name);
|
||||
Undo.RecordObject(clusterPrefab, "Array delete " + clusterPrefab.name);
|
||||
cluster.RemoveInstance();
|
||||
anythingChanged = true;
|
||||
}
|
||||
@ -268,7 +335,9 @@ namespace NanoBrain.Unity {
|
||||
}
|
||||
|
||||
protected void SynapsesInspector(ref bool anythingChanged) {
|
||||
showSynapses = EditorGUILayout.BeginFoldoutHeaderGroup(showSynapses, "Synapses");
|
||||
EditorGUI.indentLevel++;
|
||||
//showSynapses = EditorGUILayout.BeginFoldoutHeaderGroup(showSynapses, "Synapses");
|
||||
showSynapses = EditorGUILayout.Foldout(showSynapses, "Synapses");
|
||||
if (showSynapses) {
|
||||
if (this.currentNucleus is Neuron neuron2) {
|
||||
Neuron.CombinatorType newCombinator = (Neuron.CombinatorType)EditorGUILayout.EnumPopup("Combinator", neuron2.combinator);
|
||||
@ -326,7 +395,9 @@ namespace NanoBrain.Unity {
|
||||
}
|
||||
}
|
||||
else {
|
||||
float indentPx = EditorGUI.indentLevel * EditorGUIUtility.singleLineHeight;
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Space(indentPx);
|
||||
|
||||
if (synapse.neuron.parent != this.currentNucleus.parent) {
|
||||
// If it is a different cluster
|
||||
@ -367,15 +438,18 @@ namespace NanoBrain.Unity {
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
anythingChanged |= ConnectNucleus(this.prefab, this.currentNucleus);
|
||||
anythingChanged |= AddSynapse(this.prefab, this.currentNucleus);
|
||||
anythingChanged |= ConnectNucleus(this.clusterPrefab, this.currentNucleus);
|
||||
anythingChanged |= AddSynapse(this.clusterPrefab, this.currentNucleus);
|
||||
}
|
||||
EditorGUILayout.EndFoldoutHeaderGroup();
|
||||
//EditorGUILayout.EndFoldoutHeaderGroup();
|
||||
EditorGUI.indentLevel--;
|
||||
|
||||
}
|
||||
|
||||
protected void ActivationInspector(ref bool anythingChanged) {
|
||||
EditorGUILayout.Space();
|
||||
showActivation = EditorGUILayout.BeginFoldoutHeaderGroup(showActivation, "Activation");
|
||||
EditorGUI.indentLevel++;
|
||||
showActivation = EditorGUILayout.Foldout(showActivation, "Activation");
|
||||
if (showActivation) {
|
||||
if (this.currentNucleus is Neuron neuron) {
|
||||
if (this.currentNucleus is not MemoryCell) {
|
||||
@ -398,7 +472,8 @@ namespace NanoBrain.Unity {
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
EditorGUILayout.EndFoldoutHeaderGroup();
|
||||
//EditorGUILayout.EndFoldoutHeaderGroup();
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
#region Synapses
|
||||
@ -427,7 +502,7 @@ namespace NanoBrain.Unity {
|
||||
}
|
||||
|
||||
protected virtual void AddMemoryCellInput(Nucleus nucleus) {
|
||||
MemoryCell newMemory = new(this.prefab.cluster, "New memory cell");
|
||||
MemoryCell newMemory = new(this.clusterPrefab.cluster, "New memory cell");
|
||||
newMemory.AddReceiver(nucleus);
|
||||
this.currentNucleus = newMemory;
|
||||
}
|
||||
@ -519,10 +594,10 @@ namespace NanoBrain.Unity {
|
||||
|
||||
// this.prefab.nuclei.Remove(nucleus);
|
||||
// Neuron.Delete(nucleus);
|
||||
this.prefab.cluster.RefreshOutputs();
|
||||
this.clusterPrefab.cluster.RefreshOutputs();
|
||||
|
||||
|
||||
this.currentNucleus = this.prefab.cluster.defaultOutput;
|
||||
this.currentNucleus = this.clusterPrefab.cluster.defaultOutput;
|
||||
this.selectedOutput = this.currentNucleus;
|
||||
}
|
||||
|
||||
@ -544,7 +619,7 @@ namespace NanoBrain.Unity {
|
||||
|
||||
protected virtual void ChangeSynapse(Synapse synapse, Neuron newNucleus) {
|
||||
Neuron synapseNeuron = synapse.neuron;
|
||||
if (synapse.neuron.parent is Cluster subCluster && subCluster.prefab != this.prefab) {
|
||||
if (synapse.neuron.parent is Cluster subCluster && subCluster.prefab != this.clusterPrefab) {
|
||||
// if (synapse.neuron.parent is ClusterReceptor receptor) {
|
||||
// // the new nucleus is part of a (cluster) receptor,
|
||||
// // so we have to change all synapses to this nucleus array elements
|
||||
@ -586,7 +661,10 @@ namespace NanoBrain.Unity {
|
||||
#endregion Synapses
|
||||
|
||||
#endregion Inspector
|
||||
/*
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,7 +1,10 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace NanoBrain.Unity {
|
||||
|
||||
@ -58,7 +61,6 @@ namespace NanoBrain.Unity {
|
||||
// content rect below header
|
||||
Rect drawRect = new(fieldRect.x, headerRect.yMax + 2f, fieldRect.width, 450f);
|
||||
|
||||
// IMGUIContainer should be inserted here
|
||||
ClusterView.Render(drawRect, prefab.cluster, property);
|
||||
}
|
||||
}
|
||||
@ -86,4 +88,62 @@ namespace NanoBrain.Unity {
|
||||
// }
|
||||
}
|
||||
|
||||
/*
|
||||
[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<Editor>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
@ -9,32 +9,46 @@ namespace NanoBrain.Unity {
|
||||
private static readonly float discRadius = 20;
|
||||
|
||||
static readonly Dictionary<string, ClusterView> viewStates = new();
|
||||
private static ClusterView GetClusterView(SerializedProperty property) {
|
||||
public static ClusterView GetClusterView(SerializedProperty property) {
|
||||
string key = property.propertyPath + "_" + property.serializedObject.targetObject.GetEntityId();
|
||||
if (!viewStates.TryGetValue(key, out ClusterView state))
|
||||
state = new() { key = key };
|
||||
return state;
|
||||
}
|
||||
public static ClusterView GetClusterView(SerializedObject serializedObject) {
|
||||
string key = serializedObject.targetObject.GetEntityId().ToString();
|
||||
if (!viewStates.TryGetValue(key, out ClusterView state))
|
||||
state = new() { key = key };
|
||||
return state;
|
||||
}
|
||||
|
||||
private static void UpdateViewState(ClusterView viewState) {
|
||||
viewStates[viewState.key] = viewState;
|
||||
private void UpdateViewState() {
|
||||
viewStates[this.key] = this;
|
||||
}
|
||||
|
||||
public static void Render(Rect drawRect, Cluster cluster, SerializedProperty property) {
|
||||
ClusterView clusterView = GetClusterView(property);
|
||||
clusterView.currentCluster ??= cluster;
|
||||
clusterView.Render(drawRect);
|
||||
}
|
||||
public static void Render(Rect drawRect, Cluster cluster, SerializedObject obj) {
|
||||
ClusterView clusterView = GetClusterView(obj);
|
||||
clusterView.currentCluster ??= cluster;
|
||||
clusterView.Render(drawRect);
|
||||
}
|
||||
|
||||
public void Render(Rect drawRect) {
|
||||
// background
|
||||
EditorGUI.DrawRect(drawRect, Color.black);
|
||||
|
||||
const float contentWidth = 1000f;
|
||||
Rect contentRect = new Rect(0f, 0f, contentWidth, drawRect.height);
|
||||
Rect contentRect = new(0f, 0f, contentWidth, drawRect.height - 20);
|
||||
|
||||
// Begin horizontal-only scroll view
|
||||
clusterView.scrollPos = GUI.BeginScrollView(drawRect, clusterView.scrollPos, contentRect, true, false);
|
||||
this.scrollPos = GUI.BeginScrollView(drawRect, this.scrollPos, contentRect, false, false);
|
||||
|
||||
// Local content group: draw GUI content using content-local coords (0..contentWidth)
|
||||
GUI.BeginGroup(new Rect(-clusterView.scrollPos.x, 0f, contentWidth, drawRect.height));
|
||||
GUI.BeginGroup(new Rect(-this.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();
|
||||
@ -43,16 +57,16 @@ namespace NanoBrain.Unity {
|
||||
GUI.BeginGroup(drawRect);
|
||||
|
||||
// Inner group positions content origin so local coords match content space and respect scroll
|
||||
GUI.BeginGroup(new Rect(-clusterView.scrollPos.x, 0f, contentWidth, drawRect.height));
|
||||
GUI.BeginGroup(new Rect(-this.scrollPos.x, 0f, contentWidth, drawRect.height));
|
||||
|
||||
Handles.BeginGUI();
|
||||
clusterView.DrawFocusGraph();
|
||||
this.DrawFocusGraph();
|
||||
Handles.EndGUI();
|
||||
|
||||
GUI.EndGroup(); // end inner group
|
||||
GUI.EndGroup(); // end clipping group
|
||||
|
||||
UpdateViewState(clusterView);
|
||||
UpdateViewState();
|
||||
}
|
||||
|
||||
public string key = null;
|
||||
|
||||
@ -73,7 +73,6 @@ namespace NanoBrain.Unity {
|
||||
Add(scrollView);
|
||||
Add(topMenuContainer);
|
||||
|
||||
|
||||
// Subscribe when added to panel (editor UI ready)
|
||||
RegisterCallback<AttachToPanelEvent>(evt => Subscribe());
|
||||
RegisterCallback<DetachFromPanelEvent>(evt => Unsubscribe());
|
||||
@ -83,16 +82,18 @@ namespace NanoBrain.Unity {
|
||||
this.mode = (Mode)changeEvent.newValue;
|
||||
}
|
||||
|
||||
bool subscribed = false;
|
||||
private bool subscribed = false;
|
||||
void Subscribe() {
|
||||
if (subscribed) return;
|
||||
if (subscribed)
|
||||
return;
|
||||
SceneView.duringSceneGui += OnSceneGUI;
|
||||
subscribed = true;
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
|
||||
void Unsubscribe() {
|
||||
if (!subscribed) return;
|
||||
if (!subscribed)
|
||||
return;
|
||||
SceneView.duringSceneGui -= OnSceneGUI;
|
||||
subscribed = false;
|
||||
}
|
||||
@ -123,25 +124,36 @@ namespace NanoBrain.Unity {
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
Handles.BeginGUI();
|
||||
DrawGraph();
|
||||
Handles.EndGUI();
|
||||
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();
|
||||
}
|
||||
// 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);
|
||||
@ -230,11 +242,11 @@ namespace NanoBrain.Unity {
|
||||
DescendGraph(synapseNode, ref ix, dag);
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
#endregion Full Graph
|
||||
|
||||
#region Focus Graph
|
||||
|
||||
/*
|
||||
protected void DrawFocusGraph() {
|
||||
float size = 20;
|
||||
Vector3 position = new(150, 210, 0);
|
||||
@ -549,9 +561,9 @@ namespace NanoBrain.Unity {
|
||||
row++;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
#endregion Focus Graph
|
||||
|
||||
/*
|
||||
protected void DrawNucleus(Nucleus nucleus, Vector3 position, float maxValue, float size) {
|
||||
Color color;
|
||||
if (Application.isPlaying) {
|
||||
@ -841,7 +853,7 @@ namespace NanoBrain.Unity {
|
||||
this.selectedOutput = null;
|
||||
expandArray = false;
|
||||
}
|
||||
|
||||
*/
|
||||
#endregion Graph
|
||||
|
||||
void OnSceneGUI(SceneView sceneView) {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
/*
|
||||
namespace NanoBrain.Unity {
|
||||
|
||||
/// <summary>
|
||||
@ -66,3 +66,4 @@ namespace NanoBrain.Unity {
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
Loading…
x
Reference in New Issue
Block a user