From 9d43772b7975f9db401b0ae23b6bc3f78c17b90a Mon Sep 17 00:00:00 2001 From: Pascal Serrarens Date: Tue, 19 May 2026 09:04:43 +0200 Subject: [PATCH] drawer graph collapse fix --- Editor/ClusterPrefab_Drawer.cs | 15 +++-- Runtime/Scripts/Core/Neuron.cs | 33 ++++++---- .../Braitenberg/Brains/Braitenberg 1.asset | 14 ++--- Samples/Braitenberg/Braitenberg.unity | 62 +++++++++++++++++++ Samples/Braitenberg/Prefabs/Vehicle 1.prefab | 27 +++++++- Samples/Braitenberg/Prefabs/Vehicle 2a.prefab | 4 -- Samples/Braitenberg/Prefabs/Vehicle.prefab | 54 +++++++++++++++- Samples/Braitenberg/Scripts/LightSensor.cs | 5 +- Samples/Braitenberg/Scripts/Motor.cs | 3 +- Samples/Braitenberg/Scripts/Vehicle.cs | 10 +-- 10 files changed, 189 insertions(+), 38 deletions(-) diff --git a/Editor/ClusterPrefab_Drawer.cs b/Editor/ClusterPrefab_Drawer.cs index 133ccd2..a75c747 100644 --- a/Editor/ClusterPrefab_Drawer.cs +++ b/Editor/ClusterPrefab_Drawer.cs @@ -18,11 +18,15 @@ namespace NanoBrain.Unity { const float elementHeight = 64f; // height reserved for the VisualElement public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { - float height = EditorGUIUtility.singleLineHeight; - if (property.objectReferenceValue != null) { + float height = EditorGUIUtility.singleLineHeight + padding; + string key = property.propertyPath + "_" + property.serializedObject.targetObject.GetEntityId(); + s_foldouts.TryGetValue(key, out bool isOpen); + if (property.objectReferenceValue != null && isOpen) { height += padding + elementHeight; height = 500; } + else + height = 36; return height; } @@ -42,16 +46,17 @@ namespace NanoBrain.Unity { if (property.objectReferenceValue is ClusterPrefab prefab) { // key per field instance string key = property.propertyPath + "_" + property.serializedObject.targetObject.GetEntityId(); - if (!s_foldouts.TryGetValue(key, out bool isOpen)) isOpen = true; + if (!s_foldouts.TryGetValue(key, out bool isOpen)) + isOpen = true; // foldout header rect - Rect headerRect = new Rect(fieldRect.x, fieldRect.yMax + 4f, fieldRect.width, EditorGUIUtility.singleLineHeight); + Rect headerRect = new(fieldRect.x, fieldRect.yMax + 4f, fieldRect.width, EditorGUIUtility.singleLineHeight); isOpen = EditorGUI.Foldout(headerRect, isOpen, "Graph", true); s_foldouts[key] = isOpen; if (isOpen) { // content rect below header - Rect drawRect = new Rect(fieldRect.x, headerRect.yMax + 2f, fieldRect.width, 450f); + Rect drawRect = new(fieldRect.x, headerRect.yMax + 2f, fieldRect.width, 450f); // IMGUIContainer should be inserted here ClusterView.Render(drawRect, prefab.cluster, property); diff --git a/Runtime/Scripts/Core/Neuron.cs b/Runtime/Scripts/Core/Neuron.cs index 0e52ed5..7e9b9ae 100644 --- a/Runtime/Scripts/Core/Neuron.cs +++ b/Runtime/Scripts/Core/Neuron.cs @@ -462,9 +462,12 @@ namespace NanoBrain { /// protected float3 Combinator(float3 bias, List synapses) { switch (combinator) { - case CombinatorType.Sum: return CombinatorSum(bias, synapses); - case CombinatorType.Product: return CombinatorProduct(bias, synapses); - default: return CombinatorSum(bias, synapses); + case CombinatorType.Sum: + return CombinatorSum(bias, synapses); + case CombinatorType.Product: + return CombinatorProduct(bias, synapses); + default: + return CombinatorSum(bias, synapses); } } @@ -559,14 +562,22 @@ namespace NanoBrain { // This does not allocate memory and seems faster than a switch expression protected float3 Activator(float3 inputValue) { switch (activator) { - case ActivationType.Linear: return ActivatorLinear(inputValue); - case ActivationType.Sqrt: return ActivatorSqrt(inputValue); - case ActivationType.Power: return ActivatorPower(inputValue); - case ActivationType.Reciprocal: return ActivatorReciprocal(inputValue); - case ActivationType.Tanh: return ActivatorTanh(inputValue); - case ActivationType.Binary: return ActivatorBinary(inputValue); - case ActivationType.Normalized: return ActivatorNormalized(inputValue); - default: return ActivatorLinear(inputValue); + case ActivationType.Linear: + return ActivatorLinear(inputValue); + case ActivationType.Sqrt: + return ActivatorSqrt(inputValue); + case ActivationType.Power: + return ActivatorPower(inputValue); + case ActivationType.Reciprocal: + return ActivatorReciprocal(inputValue); + case ActivationType.Tanh: + return ActivatorTanh(inputValue); + case ActivationType.Binary: + return ActivatorBinary(inputValue); + case ActivationType.Normalized: + return ActivatorNormalized(inputValue); + default: + return ActivatorLinear(inputValue); } } diff --git a/Samples/Braitenberg/Brains/Braitenberg 1.asset b/Samples/Braitenberg/Brains/Braitenberg 1.asset index 62f631a..fa78a71 100644 --- a/Samples/Braitenberg/Brains/Braitenberg 1.asset +++ b/Samples/Braitenberg/Brains/Braitenberg 1.asset @@ -31,12 +31,12 @@ MonoBehaviour: data: name: Output parent: - rid: 4201950130928877723 + rid: 4201950130928878123 bias: {x: 0, y: 0, z: 1} _synapses: - neuron: rid: 4201949899492425817 - weight: 2 + weight: 5 combinator: 0 _activator: 0 curve: @@ -65,15 +65,15 @@ MonoBehaviour: m_RotationOrder: 4 curveMax: 1 persistOutput: 0 - lastUpdate: 8.396028 + lastUpdate: 16.366629 _receivers: [] - rid: 4201949899492425817 type: {class: Neuron, ns: NanoBrain, asm: Assembly-CSharp} data: name: Sensor parent: - rid: 4201950130928877723 - bias: {x: 0.00001, y: 0.00001, z: 0.00001} + rid: 4201950130928878123 + bias: {x: 0.061416026, y: 0.061416026, z: 0.061416026} _synapses: [] combinator: 0 _activator: 0 @@ -103,10 +103,10 @@ MonoBehaviour: m_RotationOrder: 4 curveMax: 1 persistOutput: 0 - lastUpdate: 8.396028 + lastUpdate: 16.366629 _receivers: - rid: 4201949899492425781 - - rid: 4201950130928877723 + - rid: 4201950130928878123 type: {class: Cluster, ns: NanoBrain, asm: Assembly-CSharp} data: name: Braitenberg 1 diff --git a/Samples/Braitenberg/Braitenberg.unity b/Samples/Braitenberg/Braitenberg.unity index c5d0bde..c521580 100644 --- a/Samples/Braitenberg/Braitenberg.unity +++ b/Samples/Braitenberg/Braitenberg.unity @@ -462,6 +462,10 @@ PrefabInstance: propertyPath: m_Name value: Vehicle 4a objectReference: {fileID: 0} + - target: {fileID: 1939863784866365225, guid: 7a7a20927b765988f8e0725d41f5022c, type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} - target: {fileID: 4961030105565435617, guid: 7a7a20927b765988f8e0725d41f5022c, type: 3} propertyPath: m_LocalPosition.x value: -2 @@ -863,6 +867,63 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 600786778} m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1001 &605735531 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + serializedVersion: 3 + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 1939863784866365225, guid: 83edc100c355dcd53bfb759dc07323c4, type: 3} + propertyPath: m_Name + value: Vehicle 1 + objectReference: {fileID: 0} + - target: {fileID: 4961030105565435617, guid: 83edc100c355dcd53bfb759dc07323c4, type: 3} + propertyPath: m_LocalPosition.x + value: -3 + objectReference: {fileID: 0} + - target: {fileID: 4961030105565435617, guid: 83edc100c355dcd53bfb759dc07323c4, type: 3} + propertyPath: m_LocalPosition.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 4961030105565435617, guid: 83edc100c355dcd53bfb759dc07323c4, type: 3} + propertyPath: m_LocalPosition.z + value: -3 + objectReference: {fileID: 0} + - target: {fileID: 4961030105565435617, guid: 83edc100c355dcd53bfb759dc07323c4, type: 3} + propertyPath: m_LocalRotation.w + value: 0.92387956 + objectReference: {fileID: 0} + - target: {fileID: 4961030105565435617, guid: 83edc100c355dcd53bfb759dc07323c4, type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4961030105565435617, guid: 83edc100c355dcd53bfb759dc07323c4, type: 3} + propertyPath: m_LocalRotation.y + value: 0.38268343 + objectReference: {fileID: 0} + - target: {fileID: 4961030105565435617, guid: 83edc100c355dcd53bfb759dc07323c4, type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4961030105565435617, guid: 83edc100c355dcd53bfb759dc07323c4, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4961030105565435617, guid: 83edc100c355dcd53bfb759dc07323c4, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 45 + objectReference: {fileID: 0} + - target: {fileID: 4961030105565435617, guid: 83edc100c355dcd53bfb759dc07323c4, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 83edc100c355dcd53bfb759dc07323c4, type: 3} --- !u!1 &655360912 GameObject: m_ObjectHideFlags: 0 @@ -2877,6 +2938,7 @@ SceneRoots: - {fileID: 36325788} - {fileID: 1849732293} - {fileID: 1604976778} + - {fileID: 605735531} - {fileID: 1687205049} - {fileID: 2006553073} - {fileID: 128572670} diff --git a/Samples/Braitenberg/Prefabs/Vehicle 1.prefab b/Samples/Braitenberg/Prefabs/Vehicle 1.prefab index 78d4d2d..1676496 100644 --- a/Samples/Braitenberg/Prefabs/Vehicle 1.prefab +++ b/Samples/Braitenberg/Prefabs/Vehicle 1.prefab @@ -48,12 +48,37 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 3243811853767288093, guid: c0398fc7a48853d47acb42e4e3498383, type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3243811853767288093, guid: c0398fc7a48853d47acb42e4e3498383, type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 3243811853767288093, guid: c0398fc7a48853d47acb42e4e3498383, type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 3243811853767288093, guid: c0398fc7a48853d47acb42e4e3498383, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5526084556521277228, guid: c0398fc7a48853d47acb42e4e3498383, type: 3} + propertyPath: m_Name + value: Sensor + objectReference: {fileID: 0} - target: {fileID: 6509051042471214143, guid: c0398fc7a48853d47acb42e4e3498383, type: 3} propertyPath: m_Name value: Vehicle 1 objectReference: {fileID: 0} + - target: {fileID: 8280937452374640854, guid: c0398fc7a48853d47acb42e4e3498383, type: 3} + propertyPath: sensorRight + value: + objectReference: {fileID: 0} m_RemovedComponents: [] - m_RemovedGameObjects: [] + m_RemovedGameObjects: + - {fileID: 449746320909641321, guid: c0398fc7a48853d47acb42e4e3498383, type: 3} m_AddedGameObjects: [] m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: c0398fc7a48853d47acb42e4e3498383, type: 3} diff --git a/Samples/Braitenberg/Prefabs/Vehicle 2a.prefab b/Samples/Braitenberg/Prefabs/Vehicle 2a.prefab index 206c549..dda77fd 100644 --- a/Samples/Braitenberg/Prefabs/Vehicle 2a.prefab +++ b/Samples/Braitenberg/Prefabs/Vehicle 2a.prefab @@ -72,10 +72,6 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 4375230766388568734, guid: c0398fc7a48853d47acb42e4e3498383, type: 3} - propertyPath: m_Constraints - value: 64 - objectReference: {fileID: 0} - target: {fileID: 6470252300495393990, guid: c0398fc7a48853d47acb42e4e3498383, type: 3} propertyPath: sensitivityAngle value: 180 diff --git a/Samples/Braitenberg/Prefabs/Vehicle.prefab b/Samples/Braitenberg/Prefabs/Vehicle.prefab index 0b51ee3..fd1dd8b 100644 --- a/Samples/Braitenberg/Prefabs/Vehicle.prefab +++ b/Samples/Braitenberg/Prefabs/Vehicle.prefab @@ -51,9 +51,32 @@ MonoBehaviour: serializedVersion: 2 m_Bits: 4294967295 _output: 0 + sensoryNeuron: + name: + parent: + rid: -2 + bias: {x: 0, y: 0, z: 0} + _synapses: [] + combinator: 0 + _activator: 0 + curve: + serializedVersion: 2 + m_Curve: [] + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + curveMax: 0 + persistOutput: 0 + lastUpdate: 0 + _receivers: [] useOcclusion: 1 sensitivityAngle: 90 multiplier: 1 + references: + version: 2 + RefIds: + - rid: -2 + type: {class: , ns: , asm: } --- !u!1 &1960572894750316796 GameObject: m_ObjectHideFlags: 0 @@ -192,7 +215,8 @@ MonoBehaviour: m_EditorClassIdentifier: Assembly-CSharp::Motor outputNeuronName: Output speed: 0 - maxTorque: 1 + maxTorque: 10 + wheelCollider: {fileID: 0} motorNeuron: name: parent: @@ -393,7 +417,8 @@ MonoBehaviour: m_EditorClassIdentifier: Assembly-CSharp::Motor outputNeuronName: Output speed: 0 - maxTorque: 1 + maxTorque: 10 + wheelCollider: {fileID: 0} motorNeuron: name: parent: @@ -507,9 +532,32 @@ MonoBehaviour: serializedVersion: 2 m_Bits: 4294967295 _output: 0 + sensoryNeuron: + name: + parent: + rid: -2 + bias: {x: 0, y: 0, z: 0} + _synapses: [] + combinator: 0 + _activator: 0 + curve: + serializedVersion: 2 + m_Curve: [] + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + curveMax: 0 + persistOutput: 0 + lastUpdate: 0 + _receivers: [] useOcclusion: 1 sensitivityAngle: 90 multiplier: 1 + references: + version: 2 + RefIds: + - rid: -2 + type: {class: , ns: , asm: } --- !u!1 &6345114338044386603 GameObject: m_ObjectHideFlags: 0 @@ -687,7 +735,7 @@ Rigidbody: m_UseGravity: 1 m_IsKinematic: 0 m_Interpolate: 0 - m_Constraints: 0 + m_Constraints: 64 m_CollisionDetection: 0 --- !u!114 &8280937452374640854 MonoBehaviour: diff --git a/Samples/Braitenberg/Scripts/LightSensor.cs b/Samples/Braitenberg/Scripts/LightSensor.cs index 59bc42a..bf9c9fe 100644 --- a/Samples/Braitenberg/Scripts/LightSensor.cs +++ b/Samples/Braitenberg/Scripts/LightSensor.cs @@ -67,7 +67,10 @@ namespace NanoBrain.Braitenberg { lightCount++; } - return (sum / lightCount) * multiplier; + if (lightCount == 0) + return 0; + else + return (sum / lightCount) * multiplier; } static float RGBToLuminance(Color c) { diff --git a/Samples/Braitenberg/Scripts/Motor.cs b/Samples/Braitenberg/Scripts/Motor.cs index 9fa7ca8..cfa7836 100644 --- a/Samples/Braitenberg/Scripts/Motor.cs +++ b/Samples/Braitenberg/Scripts/Motor.cs @@ -35,7 +35,8 @@ namespace NanoBrain.Braitenberg { float kp = 0.02f; // proportional gain — tune float torque = kp * rpmError; wheelCollider.motorTorque = Mathf.Clamp(torque, -maxTorque, maxTorque); - // Debug.Log($"{speed} {currentRpm / 60} {torque}"); + if (float.IsNaN(wheelCollider.rpm)) + Debug.Log($"{speed} {currentRpm / 60} {torque}"); } } diff --git a/Samples/Braitenberg/Scripts/Vehicle.cs b/Samples/Braitenberg/Scripts/Vehicle.cs index 4beed16..c7a3445 100644 --- a/Samples/Braitenberg/Scripts/Vehicle.cs +++ b/Samples/Braitenberg/Scripts/Vehicle.cs @@ -14,12 +14,12 @@ namespace NanoBrain.Braitenberg { public Sensor sensorLeft; public Sensor sensorRight; - // void FixedUpdate() { - // //Debug.Log($"L: {sensorLeft.output} R: {sensorRight.output}"); - // //Debug.Log($"L: {motorLeft.speed} R: {motorRight.speed}"); - // Debug.Log($"L: {motorLeft.wheelCollider.rpm} R: {motorRight.wheelCollider.rpm}"); + void FixedUpdate() { + //Debug.Log($"L: {sensorLeft.output} R: {sensorRight.output}"); + //Debug.Log($"L: {motorLeft.speed} R: {motorRight.speed}"); + Debug.Log($"L: {motorLeft.wheelCollider.rpm} R: {motorRight.wheelCollider.rpm}"); - // } + } } } \ No newline at end of file