Merge commit '595a7be03d5dbc8d4f7da28c55c676105ad91799' as 'Assets/NanoBrain'

This commit is contained in:
Pascal Serrarens 2026-01-29 09:11:13 +01:00
commit e7dd1e50cb
140 changed files with 11552 additions and 8726 deletions

10
.gitignore vendored
View File

@ -1,10 +0,0 @@
.vscode
Library
Assets/Settings
Logs
Temp
UserSettings
Assets/DefaultVolumeProfile.asset
Assets/DefaultVolumeProfile.asset.meta
Assets/UniversalRenderPipelineGlobalSettings.asset
Assets/UniversalRenderPipelineGlobalSettings.asset.meta

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +0,0 @@
fileFormatVersion: 2
guid: 052faaac586de48259a63d0c4782560b
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 8404be70184654265930450def6a9037, type: 3}
generateWrapperCode: 0
wrapperCodePath:
wrapperClassName:
wrapperCodeNamespace:

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: e8c08a710b183a848a4da458a0f6be88
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,84 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Red
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _BumpScale: 1
- _Cutoff: 0.5
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _GlossMapScale: 1
- _Glossiness: 0.5
- _GlossyReflections: 1
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _UVSec: 0
- _ZWrite: 1
m_Colors:
- _Color: {r: 1, g: 0, b: 0, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

@ -1,139 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-4785697467283256856
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: White
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords:
- _GLOSSYREFLECTIONS_OFF
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 0
- _Glossiness: 0
- _GlossyReflections: 0
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.005
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0.5
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _UVSec: 0
- _WorkflowMode: 1
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 1, g: 0.9858491, b: 0.9858491, a: 1}
- _Color: {r: 0.41509432, g: 0.41509432, b: 0.41509432, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: a79ccc131cb43254cb8575d1cedb537e
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,18 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 60a957541c24c57e78018c202ebb1d9b, type: 3}
m_Name: New Cluster
m_EditorClassIdentifier: Assembly-CSharp::Cluster
nuclei: []
references:
version: 2
RefIds: []

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 7045991fd12f8ed4eaecee534a30ca5f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,182 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &8702527963799169112
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8702527963799169119}
- component: {fileID: 8702527963799169116}
- component: {fileID: 8702527963799169117}
- component: {fileID: 8702527963799169118}
m_Layer: 0
m_Name: Capsule
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &8702527963799169119
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8702527963799169112}
serializedVersion: 2
m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0.1, y: 0.1, z: 0.1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8702527964058765412}
m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
--- !u!33 &8702527963799169116
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8702527963799169112}
m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &8702527963799169117
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8702527963799169112}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: a79ccc131cb43254cb8575d1cedb537e, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!136 &8702527963799169118
CapsuleCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8702527963799169112}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 2
m_Radius: 0.5
m_Height: 2
m_Direction: 1
m_Center: {x: 0, y: 0, z: 0}
--- !u!1 &8702527964058765413
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8702527964058765412}
- component: {fileID: 9169912378811971808}
- component: {fileID: 85370558829139006}
m_Layer: 0
m_Name: Boid
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &8702527964058765412
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8702527964058765413}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 10, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8702527963799169119}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &9169912378811971808
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8702527964058765413}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe92e8a3728a1e444a25b79acf6b1d00, type: 3}
m_Name:
m_EditorClassIdentifier:
sc: {fileID: 0}
velocity: {x: 0, y: 0, z: 0}
acceleration: {x: 0, y: 0, z: 0}
nanoBrain: {fileID: 0}
id: 0
red: {fileID: 2100000, guid: 2a7557e54580b6a8b976f12aa6cc761c, type: 2}
gray: {fileID: 2100000, guid: a79ccc131cb43254cb8575d1cedb537e, type: 2}
--- !u!114 &85370558829139006
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8702527964058765413}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 92f34a5e4027a1dc39efd8ce63cf6aba, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::NanoBrainComponent
defaultBrain: {fileID: 11400000, guid: 83e4ef8976534236989bcb1a9342dbf8, type: 2}

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: f9c706268554ce449a8773675b2864b8
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,87 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1001 &1401715754342779814
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 8702527964058765412, guid: f9c706268554ce449a8773675b2864b8, type: 3}
propertyPath: m_LocalPosition.x
value: -1.00054
objectReference: {fileID: 0}
- target: {fileID: 8702527964058765412, guid: f9c706268554ce449a8773675b2864b8, type: 3}
propertyPath: m_LocalPosition.y
value: -1.02912
objectReference: {fileID: 0}
- target: {fileID: 8702527964058765412, guid: f9c706268554ce449a8773675b2864b8, type: 3}
propertyPath: m_LocalPosition.z
value: 0.82188
objectReference: {fileID: 0}
- target: {fileID: 8702527964058765412, guid: f9c706268554ce449a8773675b2864b8, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 8702527964058765412, guid: f9c706268554ce449a8773675b2864b8, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8702527964058765412, guid: f9c706268554ce449a8773675b2864b8, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8702527964058765412, guid: f9c706268554ce449a8773675b2864b8, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8702527964058765412, guid: f9c706268554ce449a8773675b2864b8, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8702527964058765412, guid: f9c706268554ce449a8773675b2864b8, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8702527964058765412, guid: f9c706268554ce449a8773675b2864b8, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8702527964058765413, guid: f9c706268554ce449a8773675b2864b8, type: 3}
propertyPath: m_Name
value: Boid1 Variant
objectReference: {fileID: 0}
m_RemovedComponents:
- {fileID: 9169912378811971808, guid: f9c706268554ce449a8773675b2864b8, type: 3}
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents:
- targetCorrespondingSourceObject: {fileID: 8702527964058765413, guid: f9c706268554ce449a8773675b2864b8, type: 3}
insertIndex: -1
addedObject: {fileID: 8788086714463777302}
m_SourcePrefab: {fileID: 100100000, guid: f9c706268554ce449a8773675b2864b8, type: 3}
--- !u!1 &7761516481062093763 stripped
GameObject:
m_CorrespondingSourceObject: {fileID: 8702527964058765413, guid: f9c706268554ce449a8773675b2864b8, type: 3}
m_PrefabInstance: {fileID: 1401715754342779814}
m_PrefabAsset: {fileID: 0}
--- !u!114 &8788086714463777302
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7761516481062093763}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c156ab45bfd15d213b1be7451d0c0151, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::StationaryBoid
sc: {fileID: 0}
velocity: {x: 0, y: 0, z: 0}
acceleration: {x: 0, y: 0, z: 0}
nanoBrain: {fileID: 0}
id: 0
red: {fileID: 2100000, guid: 2a7557e54580b6a8b976f12aa6cc761c, type: 2}
gray: {fileID: 2100000, guid: a79ccc131cb43254cb8575d1cedb537e, type: 2}

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 6860355b30724b5ddb35781dcaf3b57e
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 097bca7cdd87e7e4b955ee8624c82da0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,108 +0,0 @@
using UnityEngine;
//[RequireComponent(typeof(NanoBrain))]
[RequireComponent(typeof(NanoBrainComponent))]
public class Boid : MonoBehaviour {
public static int BoundaryType = 1;
public static int BoidType = 2;
public static int BoidVelocityType =3;
public SwarmControl sc;
public Vector3 velocity = Vector3.zero;
public Vector3 acceleration = Vector3.zero;
protected Bounds innerBounds;
public NanoBrainComponent nanoBrain;
public Receptor boundaryReceptor;
public Receptor boidReceptor;
public Receptor boidVelocityReceptor;
public int id;
public Material red;
public Material gray;
void Awake() {
this.id = this.GetInstanceID();
nanoBrain = GetComponent<NanoBrainComponent>();
// boundaryReceptor = Receptor.GetReceptor(nanoBrain.brain, BoundaryType);
// boidReceptor = Receptor.GetReceptor(nanoBrain.brain, BoidType);
// boidVelocityReceptor = Receptor.GetReceptor(nanoBrain.brain, BoidVelocityType);
boundaryReceptor = Receptor.CreateReceptor(nanoBrain.brain, "Avoidance");
boundaryReceptor.name = "Boundary";
boidReceptor = Receptor.CreateReceptor(nanoBrain.brain, "Boid");
boidVelocityReceptor = Receptor.CreateReceptor(nanoBrain.brain, "Alignment");
sc = FindFirstObjectByType<SwarmControl>();
innerBounds = new(sc.transform.position, sc.spaceSize - 2 * sc.boundaryWidth);
}
protected virtual void Update() {
Collider[] results = Physics.OverlapSphere(this.transform.position, sc.perceptionDistance);
foreach (Collider c in results) {
if (c as CapsuleCollider != null) {
Boid neighbour = c.GetComponentInParent<Boid>();
if (neighbour == null || neighbour == this)
continue;
int thingId = neighbour.GetInstanceID();
Vector3 localPosition = this.transform.InverseTransformPoint(neighbour.transform.position);
float d = localPosition.magnitude;
if (d <= sc.separationDistance)
localPosition = localPosition.normalized * 0.01f;
else
localPosition = localPosition.normalized * (localPosition.magnitude - sc.separationDistance);
if (localPosition.sqrMagnitude > 0)
boidReceptor?.ProcessStimulus(thingId, localPosition, neighbour.name);
Vector3 localVelocity = this.transform.InverseTransformVector(neighbour.velocity);
if (localVelocity.sqrMagnitude > 0)
boidVelocityReceptor?.ProcessStimulus(thingId, localVelocity);
}
}
if (!innerBounds.Contains(this.transform.position)) {
Vector3 point = this.transform.position;
Vector3 pointOnBounds = innerBounds.ClosestPoint(point);
Vector3 desiredWorldSpace = (pointOnBounds - point).normalized * sc.speed;
Vector3 desiredLocalSpace = -this.transform.InverseTransformPoint(desiredWorldSpace);
boundaryReceptor.ProcessStimulus(777, desiredLocalSpace);
}
Vector3 worldForce = this.transform.TransformDirection(nanoBrain.root.outputValue);
this.velocity = (1 - sc.inertia) * (worldForce * Time.deltaTime) + sc.inertia * velocity;
if (this.velocity.magnitude > 0)
this.velocity = this.velocity.normalized * sc.speed;
else
this.velocity = this.transform.forward * sc.speed;
//Debug.DrawRay(this.transform.position, this.velocity, Color.blue);
this.transform.position += this.velocity * Time.deltaTime;
if (this.velocity != Vector3.zero) {
Quaternion targetRotation = Quaternion.LookRotation(this.velocity);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 5f); // Adjust the speed of rotation
}
nanoBrain.brain.UpdateNuclei();
// Renderer renderer = GetComponentInChildren<Renderer>();
// results = Physics.OverlapSphere(this.transform.position, 0.1f);
// if (results.Length > 1) {
// // string s= this.name;
// // foreach (Collider c in results)
// // s += " " + c.transform.parent.gameObject.name;
// // Debug.Log(s);
// renderer.sharedMaterial = red;
// }
// else {
// renderer.sharedMaterial = gray;
// }
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: fe92e8a3728a1e444a25b79acf6b1d00

View File

@ -1,33 +0,0 @@
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(SwarmControl))]
public class SwarmControl_Editor : Editor {
public override void OnInspectorGUI() {
EditorGUI.BeginChangeCheck();
DrawDefaultInspector();
if (EditorGUI.EndChangeCheck()) {
SwarmControl swarmControl = (SwarmControl)target;
ClusterPrefab[] nanoBrains = FindObjectsByType<ClusterPrefab>(FindObjectsSortMode.None);
foreach (ClusterPrefab brain in nanoBrains) {
NanoBrainComponent.UpdateWeight(brain, "Avoidance", swarmControl.avoidanceForce);
NanoBrainComponent.UpdateWeight(brain, "Cohesion", swarmControl.cohesionForce);
NanoBrainComponent.UpdateWeight(brain, "Separation", swarmControl.separationForce);
NanoBrainComponent.UpdateWeight(brain, "Alignment", swarmControl.alignmentForce);
}
Debug.Log("Updated weights");
}
}
// protected void UpdateWeight(NanoBrain brain, string name, float weight) {
// Nucleus root = brain.root;
// foreach (Synapse synapse in root.synapses) {
// if (synapse.nucleus.name == name) {
// synapse.weight = weight;
// }
// }
// }
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: df2bd18155543a1a09fefea3252981e0

View File

@ -1,75 +0,0 @@
using UnityEngine;
public class StationaryBoid : Boid
{
protected override void Update()
{
Collider[] results = Physics.OverlapSphere(this.transform.position, sc.perceptionDistance);
foreach (Collider c in results)
{
if (c as CapsuleCollider != null)
{
Boid neighbour = c.GetComponentInParent<Boid>();
if (neighbour == null || neighbour == this)
continue;
int thingId = neighbour.GetInstanceID();
Vector3 localPosition = this.transform.InverseTransformPoint(neighbour.transform.position);
float d = localPosition.magnitude;
if (d <= sc.separationDistance)
localPosition = localPosition.normalized * 0.01f;
else
localPosition = localPosition.normalized * (localPosition.magnitude - sc.separationDistance);
if (localPosition.sqrMagnitude > 0)
boidReceptor?.ProcessStimulus(thingId, localPosition, neighbour.name);
Vector3 localVelocity = this.transform.InverseTransformVector(neighbour.velocity);
if (localVelocity.sqrMagnitude > 0)
boidVelocityReceptor?.ProcessStimulus(thingId, localVelocity);
}
}
if (!innerBounds.Contains(this.transform.position))
{
Vector3 point = this.transform.position;
Vector3 pointOnBounds = innerBounds.ClosestPoint(point);
Vector3 desiredWorldSpace = (pointOnBounds - point).normalized * sc.speed;
Vector3 desiredLocalSpace = -this.transform.InverseTransformPoint(desiredWorldSpace);
boundaryReceptor.ProcessStimulus(777, desiredLocalSpace);
}
Vector3 worldForce = this.transform.TransformDirection(nanoBrain.root.outputValue);
this.velocity = (1 - sc.inertia) * (worldForce * Time.deltaTime) + sc.inertia * velocity;
if (this.velocity.magnitude > 0)
this.velocity = this.velocity.normalized * sc.speed;
else
this.velocity = this.transform.forward * sc.speed;
//Debug.DrawRay(this.transform.position, this.velocity, Color.blue);
// this.transform.position += this.velocity * Time.deltaTime;
// if (this.velocity != Vector3.zero)
// {
// Quaternion targetRotation = Quaternion.LookRotation(this.velocity);
// transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 5f); // Adjust the speed of rotation
// }
nanoBrain.brain.UpdateNuclei();
// Renderer renderer = GetComponentInChildren<Renderer>();
// results = Physics.OverlapSphere(this.transform.position, 0.1f);
// if (results.Length > 1) {
// // string s= this.name;
// // foreach (Collider c in results)
// // s += " " + c.transform.parent.gameObject.name;
// // Debug.Log(s);
// renderer.sharedMaterial = red;
// }
// else {
// renderer.sharedMaterial = gray;
// }
}
}

View File

@ -1,20 +0,0 @@
using UnityEngine;
public class SwarmControl : MonoBehaviour
{
public float speed = 0.5f;
//public int neighbourCount = 0;
public float inertia = 0.1f;
public float alignmentForce = 0.0f;
public float cohesionForce = 10.0f;
public float separationForce = 5.0f;
public float avoidanceForce = 5.0f;
public float separationDistance = 0.5f;
// public float bodyForce = 20;
public float perceptionDistance = 1.0f;
//public float boundaryForce = 2.0f;
public Vector3 spaceSize = new (10, 10, 10);
public Vector3 boundaryWidth = Vector3.one * 1.0f;
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 0464906885ae3494f8fd0314719fb2db

View File

@ -1,33 +0,0 @@
using System.Collections;
using UnityEngine;
public class SwarmSpawn : MonoBehaviour {
public int count = 1;
public GameObject boidPrefab;
public Vector3 spawnAreaSize = new(0.5f, 0.5f, 0.5f); // Size of the area to spawn the prefab
public float minDelay = 1f; // Minimum delay between spawns
public float maxDelay = 5f; // Maximum delay between spawns
void Start() {
StartCoroutine(SpawnPrefab());
}
IEnumerator SpawnPrefab() {
for (int i = 0; i < count; i++) {
float delay = Random.Range(minDelay, maxDelay);
yield return new WaitForSeconds(delay);
// Generate a random local position within the specified area
Vector3 randomPosition = new Vector3(
Random.Range(-spawnAreaSize.x / 2, spawnAreaSize.x / 2),
Random.Range(-spawnAreaSize.y / 2, spawnAreaSize.y / 2),
Random.Range(-spawnAreaSize.z / 2, spawnAreaSize.z / 2)
);
// Instantiate the prefab at the random position relative to the spawner
GameObject boid = Instantiate(boidPrefab, transform.position + randomPosition, Random.rotation);
boid.name = "Boid " + i;
}
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: ec888ca5333d45a438f9f417fa5ce135

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: a0b8e46f4a21ce343b2509b34c712dab
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 709f11a7f3c4041caa4ef136ea32d874
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

259
Cluster.cs Normal file
View File

@ -0,0 +1,259 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using Unity.Mathematics;
using static Unity.Mathematics.math;
[System.Serializable]
public class Cluster : INucleus {
// The ScriptableObject asset from which the runtime object has been created
public readonly ClusterPrefab prefab;
public ClusterPrefab cluster { get; set; }
[SerializeField]
protected string _name;
public virtual string name {
get => _name;
set => _name = value;
}
// Hmm, a cluster instance can never be part of a scriptable object...(Cluster)
public Cluster(ClusterPrefab parent, ClusterPrefab prefab) {
this.prefab = prefab;
this.cluster = parent;
if (this.cluster != null)
this.cluster.nuclei.Add(this);
// foreach (IReceptor nucleus in this.prefab.nuclei) {
// IReceptor clone = nucleus.CloneTo(null);
// this.dynamicNuclei.Add(clone);
// }
}
public virtual IReceptor Clone() {
Neuron clone = new(this.cluster, this.name) {
array = this.array,
};
foreach (Synapse synapse in this.synapses) {
Synapse clonedSynapse = clone.AddSynapse(synapse.nucleus);
clonedSynapse.weight = synapse.weight;
}
foreach (INucleus receiver in this.receivers) {
clone.AddReceiver(receiver);
}
return clone;
}
// Not sure if this belongs here...
[SerializeReference]
private NucleusArray _array;
public NucleusArray array {
get { return _array; }
set { _array = value; }
}
#region Synapses
// class ClusterSynapse : Synapse {
// public IReceptor receptor;
// public ClusterSynapse(IReceptor nucleus, INucleus receptor, float weight = 1.0f) : base(nucleus, weight) {
// this.receptor = receptor;
// }
// }
[SerializeField]
private List<Synapse> _synapses = new();
public List<Synapse> synapses => _synapses;
public Synapse AddSynapse(IReceptor sendingNucleus) {
Synapse synapse = new(sendingNucleus); //, this.prefab.inputs[0]);
this._synapses.Add(synapse);
return synapse;
// else {
// INucleus receptor = (INucleus)this.prefab.nuclei.Find(nucleus => nucleus is INucleus n && nucleus.name == nucleusName);
// ClusterSynapse synapse = new(sendingNucleus, receptor);
// receptor.AddSynapse(sendingNucleus);
// }
// // Add synapse to which neuron?
// return null;
}
// Does this even exist already?
public void RemoveSynapse() {
}
#endregion Synapses
#region Receivers
[SerializeReference]
private List<INucleus> _receivers = new();
public List<INucleus> receivers {
get { return _receivers; }
set { _receivers = value; }
}
public virtual void AddReceiver(INucleus receivingNucleus) {
this._receivers.Add(receivingNucleus);
receivingNucleus.AddSynapse(this);
}
public void RemoveReceiver(INucleus receiverNucleus) {
this._receivers.RemoveAll(receiver => receiver == receiverNucleus);
receiverNucleus.synapses.RemoveAll(synapse => synapse.nucleus == this);
}
// public void AddReceiver(INucleus receivingNucleus) {
// int newLength = this._receivers.Count + 1;
// INucleus[] newReceivers = new INucleus[newLength];
// // Copy the existing receivers
// for (int ix = 0; ix < this._receivers.Count; ix++)
// newReceivers[ix] = this._receivers[ix];
// // Add the new receivers
// newReceivers[this._receivers.Count] = receivingNucleus;
// // Replace the receivers with the new receivers
// this._receivers = new(newReceivers);
// receivingNucleus.AddSynapse(this);
// }
// public void RemoveReceiver(INucleus receivingNucleus) {
// Debug.Log("Clusterinstance. remote receiver");
// int newLength = this._receivers.Count - 1;
// if (newLength < 0)
// // Array was empty, so we cannot remove anything
// return;
// INucleus[] newReceivers = new INucleus[newLength];
// int newIx = 0;
// // Copy all receivers except receivingNucleus
// for (int ix = 0; ix < this._receivers.Count; ix++) {
// if (this._receivers[ix] == receivingNucleus)
// // skip the receiver we want to remote
// continue;
// if (newIx >= newLength)
// // We want to copy more elements than expected
// // the receivingNucleus is not found
// // and the original array is returned
// return;
// newReceivers[newIx] = this._receivers[ix];
// newIx++;
// }
// this._receivers = new(newReceivers);
// }
#endregion Receivers
#region Runtime
[NonSerialized]
private int stale = 1000;
public bool isSleeping => lengthsq(this.outputValue) == 0;
[NonSerialized]
protected float3 _outputValue;
public virtual float3 outputValue {
get { return _outputValue; }
set {
this.stale = 0;
_outputValue = value;
}
}
#region Update
public virtual void UpdateState() {
UpdateState(new float3(0, 0, 0));
}
public void UpdateState(float3 inputValue) {
float3 sum = inputValue; // new(0, 0, 0);
//Applying the weight factgors
foreach (Synapse synapse in this.synapses) {
sum += synapse.weight * synapse.nucleus.outputValue;
}
// This does not work because the prefab nucleus does not have a state
this.prefab.inputs[0].UpdateState(sum);
}
public void UpdateNuclei() {
this.stale++;
if (this.stale > 2)
_outputValue = Vector3.zero;
foreach (IReceptor nucleus in this.prefab.nuclei)
nucleus.UpdateNuclei();
}
#endregion Update
#endregion Runtime
/*
[SerializeField]
private List<IReceptor> _dynamicNuclei;
public List<IReceptor> dynamicNuclei {// = new();
get {
if (_dynamicNuclei == null) {
this._dynamicNuclei = new();
foreach (IReceptor nucleus in this.prefab.nuclei) {
IReceptor clone = nucleus.CloneTo(null);
this._dynamicNuclei.Add(clone);
}
}
return this._dynamicNuclei;
}
}
public List<INucleus> _inputs = null;
public List<INucleus> inputs {
get {
this._inputs = new();
if (this.dynamicNuclei != null) {
foreach (IReceptor receptor in this.dynamicNuclei) {
if (receptor is INucleus nucleus)
this._inputs.Add(nucleus);
}
}
return this._inputs;
}
}
public INucleus output => this.dynamicNuclei[0] as INucleus;
public float3 outputValue => this.output.outputValue;
public IReceptor CloneTo(ClusterPrefab parent) {
Cluster clone = new(parent, this.prefab);
return clone;
}
public IReceptor Clone() {
Cluster clone = new(this.cluster, this.prefab);
return clone;
}
#region Properties
public string name {
get { return prefab.name; }
set { prefab.name = value; }
}
public bool isSleeping => lengthsq(this.outputValue) == 0;
public NucleusArray array { get; set; }
#endregion Properties
*/
}

2
Cluster.cs.meta Normal file
View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f13cdc4a175a9f379a00317ae68d8bea

84
ClusterPrefab.cs Normal file
View File

@ -0,0 +1,84 @@
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "Passer/Cluster")]
public class ClusterPrefab : ScriptableObject {
//public virtual Cluster cluster {get;set;}
// The ScriptableObject asset from which the runtime object has been created
//public Cluster asset;
[SerializeReference]
public List<IReceptor> nuclei = new();
// public List<Cluster> subClusters = new();
// public void AddSubCluster(ClusterInstance subCluster) {
// this.nuclei.Add(subCluster);
// }
public virtual INucleus output => this.nuclei[0] as INucleus;
public List<INucleus> _inputs = null;
public virtual List<INucleus> inputs {
get {
if (this._inputs == null) {
this._inputs = new();
foreach (IReceptor receptor in this.nuclei) {
if (receptor is INucleus nucleus)
this._inputs.Add(nucleus);
}
}
return this._inputs;
}
}
// Call this function to ensure that there is at least one nucleus
// This is an invariant and should be ensured before the nucleus is used
// because output requires it.
public void EnsureInitialization() {
nuclei ??= new List<IReceptor>();
if (nuclei.Count == 0)
new Neuron(this, "Output"); // Every cluster should have at least 1 neuron
}
public void GarbageCollection() {
HashSet<INucleus> visitedNuclei = new();
MarkNuclei(visitedNuclei, this.output);
//Debug.Log($"Garbage collection found {visitedNuclei.Count} Nuclei");
this.nuclei.RemoveAll(nucleus => nucleus is INucleus n && visitedNuclei.Contains(n) == false);
}
public void MarkNuclei(HashSet<INucleus> visitedNuclei, INucleus nucleus) {
if (nucleus is null)
return;
visitedNuclei.Add(nucleus);
if (nucleus.synapses != null) {
HashSet<Synapse> visitedSynapses = new();
foreach (Synapse synapse in nucleus.synapses) {
if (synapse != null && synapse.nucleus != null) {
visitedSynapses.Add(synapse);
if (synapse.nucleus is INucleus synapse_nucleus)
MarkNuclei(visitedNuclei, synapse_nucleus);
}
}
nucleus.synapses.RemoveAll(synapse => visitedSynapses.Contains(synapse) == false);
}
if (nucleus.receivers != null) {
HashSet<INucleus> visitedReceivers = new();
foreach (INucleus receiver in nucleus.receivers) {
if (receiver != null && receiver != null) {
visitedReceivers.Add(receiver);
visitedNuclei.Add(receiver);
}
}
nucleus.receivers.RemoveAll(receiver => visitedReceivers.Contains(receiver) == false);
}
}
public virtual void UpdateNuclei() {
foreach (IReceptor nucleus in this.nuclei)
nucleus.UpdateNuclei();
}
}

2
ClusterPrefab.cs.meta Normal file
View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 60a957541c24c57e78018c202ebb1d9b

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 2b86e8b28533d905899af9666a98e804
guid: 3aedf57a50b6dfa46a59457c87b8ef9d
folderAsset: yes
DefaultImporter:
externalObjects: {}

302
Editor/NeuroidWindow.cs Normal file
View File

@ -0,0 +1,302 @@
/*
using UnityEditor;
using UnityEngine;
using System.Linq;
using System.Collections.Generic;
public class NeuroidLayer {
public int ix = 0;
public List<Nucleus> neuroids = new();
}
public class GraphEditorWindow : EditorWindow {
private Nucleus currentNucleus;
private List<Neuroid> allNeuroids;
private Dictionary<Nucleus, Vector2Int> neuroidPositions = new();
private List<NeuroidLayer> 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<Neuroid> neuroids) {
if (neuroids == null)
return;
// A temporary list to track what's been added to layers
this.layers = new();
HashSet<Neuroid> 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<Boid>();
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<GraphEditorWindow>("Neuroid Visualizer");
}
}
*/

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 26e68838038ea5243ae57bc81f4db8a8

54
INucleus.cs Normal file
View File

@ -0,0 +1,54 @@
using System.Collections.Generic;
using Unity.Mathematics;
public interface INucleus : IReceptor {
#region static struct
// Cluster
public ClusterPrefab cluster { get; }
// Senders
public List<Synapse> synapses { get; }
public Synapse AddSynapse(IReceptor sender);
public NucleusArray array { get; set; }
#endregion static struct
#region dynamic state
public void UpdateState();
public void UpdateState(float3 inputValue);
#endregion dynamic state
}
public interface IReceptor {
#region static
public string name { get; set; }
// Receivers
public List<INucleus> receivers { get; set; }
public void AddReceiver(INucleus receiver);
public void RemoveReceiver(INucleus receiverNucleus);
#endregion static
#region dynamic
// float3 to prepare for SIMD
public float3 outputValue { get; }
public void UpdateNuclei();
public bool isSleeping { get; }
#endregion dynamic
//public IReceptor CloneTo(ClusterPrefab parent);
public IReceptor Clone();
}

2
INucleus.cs.meta Normal file
View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6a8a0e8965cea660abff254cab8a4723

60
Identity.asset Normal file
View File

@ -0,0 +1,60 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 60a957541c24c57e78018c202ebb1d9b, type: 3}
m_Name: Identity
m_EditorClassIdentifier: Assembly-CSharp::Cluster
nuclei:
- rid: 2243601383627161705
references:
version: 2
RefIds:
- rid: 2243601383627161705
type: {class: Neuron, ns: , asm: Assembly-CSharp}
data:
_name: Output
_synapses: []
_receivers: []
_array:
rid: 2243601383627161706
_curvePreset: 0
curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1000
value: 1000
inSlope: 1
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
curveMax: 1
average: 0
- rid: 2243601383627161706
type: {class: NucleusArray, ns: , asm: Assembly-CSharp}
data:
_nuclei:
- rid: 2243601383627161705
name: Output

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 4160a4557d16d7c03833982ab779d28e
guid: 5f4d2ea0d0115b3549f8e9aa5e669163
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 9c53962885c2c4f449125a979d6ad240
guid: d98555a675e8e5e879de17db950b55fe
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@ -0,0 +1,37 @@
name: Build and Run C# Unit Tests
on:
push:
branches:
- '**'
pull_request:
branches:
- '**'
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: '8.0.x' # Specify the .NET SDK version
- name: Check Current Directory
run: pwd # Logs the current working directory
- name: List Files
run: ls -la # Lists all files in the current directory
- name: Restore Dependencies
run: dotnet restore ./LinearAlgebra-csharp.sln # Restore NuGet packages
- name: Build the Project
run: dotnet build ./LinearAlgebra-csharp.sln --configuration Release # Build the C# project
- name: Run Unit Tests
run: dotnet test ./test/LinearAlgebra_Test.csproj --configuration Release # Execute unit tests

5
LinearAlgebra/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
DoxyGen/DoxyWarnLogfile.txt
.vscode/settings.json
**bin
**obj
**.meta

View File

@ -0,0 +1,30 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.2.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LinearAlgebra", "src\LinearAlgebra.csproj", "{ECB58727-0354-924D-AE7B-22F6B21097EB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LinearAlgebra_Test", "test\LinearAlgebra_Test.csproj", "{715BB399-5FC4-2AC9-3757-177CA0C80774}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{ECB58727-0354-924D-AE7B-22F6B21097EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ECB58727-0354-924D-AE7B-22F6B21097EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ECB58727-0354-924D-AE7B-22F6B21097EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ECB58727-0354-924D-AE7B-22F6B21097EB}.Release|Any CPU.Build.0 = Release|Any CPU
{715BB399-5FC4-2AC9-3757-177CA0C80774}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{715BB399-5FC4-2AC9-3757-177CA0C80774}.Debug|Any CPU.Build.0 = Debug|Any CPU
{715BB399-5FC4-2AC9-3757-177CA0C80774}.Release|Any CPU.ActiveCfg = Release|Any CPU
{715BB399-5FC4-2AC9-3757-177CA0C80774}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E93B8294-87D4-4887-83B7-182A623D5833}
EndGlobalSection
EndGlobal

341
LinearAlgebra/src/Angle.cs Normal file
View File

@ -0,0 +1,341 @@
using System;
namespace LinearAlgebra {
public struct AngleFloat {
public const float Rad2Deg = 360.0f / ((float)Math.PI * 2); //0.0174532924F;
public const float Deg2Rad = (float)Math.PI * 2 / 360.0f; //57.29578F;
private AngleFloat(float degrees) {
this.value = degrees;
}
private readonly float value;
public static AngleFloat Degrees(float degrees) {
// Reduce it to (-180..180]
if (float.IsFinite(degrees)) {
while (degrees < -180)
degrees += 360;
while (degrees >= 180)
degrees -= 360;
}
return new AngleFloat(degrees);
}
public static AngleFloat Radians(float radians) {
// Reduce it to (-pi..pi]
if (float.IsFinite(radians)) {
while (radians <= -Math.PI)
radians += 2 * (float)Math.PI;
while (radians > Math.PI)
radians -= 2 * (float)Math.PI;
}
return new AngleFloat(radians * Rad2Deg);
}
public static AngleFloat Revolutions(float revolutions) {
// reduce it to (-0.5 .. 0.5]
if (float.IsFinite(revolutions)) {
// Get the integer part
int integerPart = (int)revolutions;
// Get the decimal part
revolutions -= integerPart;
if (revolutions < -0.5)
revolutions += 1;
if (revolutions >= 0.5)
revolutions -= 1;
}
return new AngleFloat(revolutions * 360);
}
public readonly float inDegrees => this.value;
public readonly float inRadians => this.value * Deg2Rad;
public readonly float inRevolutions => this.value / 360.0f;
public override string ToString() {
return $"{this.inDegrees}\u00B0";
}
public static readonly AngleFloat zero = Degrees(0);
public static readonly AngleFloat deg90 = Degrees(90);
public static readonly AngleFloat deg180 = Degrees(180);
/// <summary>
/// Get the sign of the angle
/// </summary>
/// <param name="a">The angle</param>
/// <returns>-1 when the angle is negative, 1 when it is positive and 0 in all other cases</returns>
public static int Sign(AngleFloat a) {
if (a.value < 0)
return -1;
if (a.value > 0)
return 1;
return 0;
}
/// <summary>
/// Returns the magnitude of the angle
/// </summary>
/// <param name="a">The angle</param>
/// <returns>The positive magnitude of the angle</returns>
/// Negative values are negated to get a positive result
public static AngleFloat Abs(AngleFloat a) {
if (Sign(a) < 0)
return -a;
else
return a;
}
/// <summary>
/// Tests the equality of two angles
/// </summary>
/// <param name="a1"></param>
/// <param name="a2"></param>
/// <returns>True when the angles are equal, false otherwise</returns>
/// <remarks>The equality is determine within the limits of precision of a float</remarks>
public static bool operator ==(AngleFloat a1, AngleFloat a2) {
return a1.value == a2.value;
}
/// <summary>
/// Tests the inequality of two angles
/// </summary>
/// <param name="a1"></param>
/// <param name="a2"></param>
/// <returns>True when the angles are not equal, false otherwise</returns>
/// <remarks>The equality is determine within the limits of precision of a float</remarks>
public static bool operator !=(AngleFloat a1, AngleFloat a2) {
return a1.value != a2.value;
}
public override readonly bool Equals(object obj) {
if (obj is AngleFloat other) {
return this == other;
}
return false;
}
public override readonly int GetHashCode() {
return this.value.GetHashCode();
}
/// <summary>
/// Tests if the first angle is greater than the second
/// </summary>
/// <param name="a1"></param>
/// <param name="a2"></param>
/// <returns>True when a1 is greater than a2, False otherwise</returns>
public static bool operator >(AngleFloat a1, AngleFloat a2) {
return a1.value > a2.value;
}
/// <summary>
/// Tests if the first angle is greater than or equal to the second
/// </summary>
/// <param name="a1"></param>
/// <param name="a2"></param>
/// <returns>True when a1 is greater than or equal to a2, False otherwise</returns>
public static bool operator >=(AngleFloat a1, AngleFloat a2) {
return a1.value >= a2.value;
}
/// <summary>
/// Tests if the first angle is less than the second
/// </summary>
/// <param name="a1"></param>
/// <param name="a2"></param>
/// <returns>True when a1 is less than a2, False otherwise</returns>
public static bool operator <(AngleFloat a1, AngleFloat a2) {
return a1.value < a2.value;
}
/// <summary>
/// Tests if the first angle is less than or equal to the second
/// </summary>
/// <param name="a1"></param>
/// <param name="a2"></param>
/// <returns>True when a1 is less than or equal to a2, False otherwise</returns>
public static bool operator <=(AngleFloat a1, AngleFloat a2) {
return a1.value <= a2.value;
}
/// <summary>
/// Negate the angle
/// </summary>
/// <param name="a">The angle</param>
/// <returns>The negated angle</returns>
/// The negation of -180 is still -180 because the range is (-180..180]
public static AngleFloat operator -(AngleFloat a) {
AngleFloat r = new(-a.value);
return r;
}
/// <summary>
/// Subtract two angles
/// </summary>
/// <param name="a1">Angle 1</param>
/// <param name="a2">Angle 2</param>
/// <returns>The result of the subtraction</returns>
public static AngleFloat operator -(AngleFloat a1, AngleFloat a2) {
AngleFloat r = new(a1.value - a2.value);
return r;
}
/// <summary>
/// Add two angles
/// </summary>
/// <param name="a1">Angle 1</param>
/// <param name="a2">Angle 2</param>
/// <returns>The result of the addition</returns>
public static AngleFloat operator +(AngleFloat a1, AngleFloat a2) {
AngleFloat r = new(a1.value + a2.value);
return r;
}
/// <summary>
/// Multiplies the angle
/// </summary>
/// <param name="a">The angle to multiply</param>
/// <param name="factor">The factor by which the angle is multiplied</param>
/// <returns>The multiplied angle</returns>
public static AngleFloat operator *(AngleFloat a, float factor) {
return Degrees(a.inDegrees * factor);
}
public static AngleFloat operator *(float factor, AngleFloat a) {
return Degrees(factor * a.inDegrees);
}
/// <summary>
/// Clamp the angle between the given min and max values
/// </summary>
/// <param name="angle">The angle to clamp</param>
/// <param name="min">The minimum angle</param>
/// <param name="max">The maximum angle</param>
/// <returns>The clamped angle</returns>
/// Angles are normalized
public static float Clamp(AngleFloat angle, AngleFloat min, AngleFloat max) {
return Float.Clamp(angle.inDegrees, min.inDegrees, max.inDegrees);
}
/// @brief Calculates the cosine of an angle
/// @param angle The given angle
/// @return The cosine of the angle
public static float Cos(AngleFloat angle) {
return MathF.Cos(angle.inRadians);
}
/// @brief Calculates the sine of an angle
/// @param angle The given angle
/// @return The sine of the angle
public static float Sin(AngleFloat angle) {
return MathF.Sin(angle.inRadians);
}
/// @brief Calculates the tangent of an angle
/// @param angle The given angle
/// @return The tangent of the angle
public static float Tan(AngleFloat angle) {
return MathF.Tan(angle.inRadians);
}
/// @brief Calculates the arc cosine angle
/// @param f The value
/// @return The arc cosine for the given value
public static AngleFloat Acos(float f) {
return Radians(MathF.Acos(f));
}
/// @brief Calculates the arc sine angle
/// @param f The value
/// @return The arc sine for the given value
public static AngleFloat Asin(float f) {
return Radians(MathF.Asin(f));
}
/// @brief Calculates the arc tangent angle
/// @param f The value
/// @return The arc tangent for the given value
public static AngleFloat Atan(float f) {
return Radians(MathF.Atan(f));
}
/// @brief Calculates the tangent for the given values
/// @param y The vertical value
/// @param x The horizontal value
/// @return The tanget for the given values
/// Uses the y and x signs to compute the quadrant
public static AngleFloat Atan2(float y, float x) {
return Radians(MathF.Atan2(y, x));
}
/// <summary>
/// Rotate from one angle to the other with a maximum degrees
/// </summary>
/// <param name="fromAngle">Starting angle</param>
/// <param name="toAngle">Target angle</param>
/// <param name="maxAngle">Maximum angle to rotate</param>
/// <returns>The resulting angle</returns>
/// This function is compatible with radian and degrees angles
public static AngleFloat MoveTowards(AngleFloat fromAngle, AngleFloat toAngle, float maxDegrees) {
maxDegrees = Math.Max(0, maxDegrees); // filter out negative distances
AngleFloat d = toAngle - fromAngle;
float dDegrees = Abs(d).inDegrees;
d = Degrees(Float.Clamp(dDegrees, 0, maxDegrees));
if (Sign(d) < 0)
d = -d;
return fromAngle + d;
}
}
/// <summary>
/// %Angle utilities
/// </summary>
public static class Angles {
public const float pi = 3.1415927410125732421875F;
// public static float Rad2Deg = 360.0f / ((float)Math.PI * 2);
// public static float Deg2Rad = ((float)Math.PI * 2) / 360.0f;
/// <summary>
/// Determine the angle difference, result is a normalized angle
/// </summary>
/// <param name="a">First first angle</param>
/// <param name="b">The second angle</param>
/// <returns>the angle between the two angles</returns>
/// Angle values should be degrees
public static float Difference(float a, float b) {
float r = Normalize(b - a);
return r;
}
/// <summary>
/// Normalize an angle to the range -180 < angle <= 180
/// </summary>
/// <param name="angle">The angle to normalize</param>
/// <returns>The normalized angle in interval (-180..180] </returns>
/// Angle values should be in degrees
public static float Normalize(float angle) {
if (float.IsInfinity(angle))
return angle;
while (angle <= -180) angle += 360;
while (angle > 180) angle -= 360;
return angle;
}
/// <summary>
/// Map interval of angles between vectors [0..Pi] to interval [0..1]
/// </summary>
/// <param name="v1">The first vector</param>
/// <param name="v2">The second vector</param>
/// <returns>The resulting factor in interval [0..1]</returns>
/// Vectors a and b must be normalized
/// \deprecated Please use Vector2.ToFactor instead.
// [Obsolete("Please use Vector2.ToFactor instead.")]
// public static float ToFactor(Vector2Float v1, Vector2Float v2) {
// return (1 - Vector2Float.Dot(v1, v2)) / 2;
// }
}
}

View File

@ -0,0 +1,287 @@
using System;
namespace LinearAlgebra {
class QR {
// QR Decomposition of a matrix A
public static (Matrix2 Q, Matrix2 R) Decomposition(Matrix2 A) {
int nRows = A.nRows;
int nCols = A.nCols;
float[,] Q = new float[nRows, nCols];
float[,] R = new float[nCols, nCols];
// Perform Gram-Schmidt orthogonalization
for (uint colIx = 0; colIx < nCols; colIx++) {
// Step 1: v = column(ix) of A
float[] v = new float[nRows];
for (int rowIx = 0; rowIx < nRows; rowIx++)
v[rowIx] = A.data[rowIx, colIx];
// Step 2: Subtract projections of v onto previous q's (orthogonalize)
for (uint colIx2 = 0; colIx2 < colIx; colIx2++) {
float dotProd = 0;
for (int i = 0; i < nRows; i++)
dotProd += Q[i, colIx2] * v[i];
for (int i = 0; i < nRows; i++)
v[i] -= dotProd * Q[i, colIx2];
}
// Step 3: Normalize v to get column(ix) of Q
float norm = 0;
for (int rowIx = 0; rowIx < nRows; rowIx++)
norm += v[rowIx] * v[rowIx];
norm = (float)Math.Sqrt(norm);
for (int rowIx = 0; rowIx < nRows; rowIx++)
Q[rowIx, colIx] = v[rowIx] / norm;
// Store the coefficients of R
for (int colIx2 = 0; colIx2 <= colIx; colIx2++) {
R[colIx2, colIx] = 0;
for (int k = 0; k < nRows; k++)
R[colIx2, colIx] += Q[k, colIx2] * A.data[k, colIx];
}
}
return (new Matrix2(Q), new Matrix2(R));
}
// Reduced QR Decomposition of a matrix A
public static (Matrix2 Q, Matrix2 R) ReducedDecomposition(Matrix2 A) {
int nRows = A.nRows;
int nCols = A.nCols;
float[,] Q = new float[nRows, nCols];
float[,] R = new float[nCols, nCols];
// Perform Gram-Schmidt orthogonalization
for (int colIx = 0; colIx < nCols; colIx++) {
// Step 1: v = column(colIx) of A
float[] columnIx = new float[nRows];
bool isZeroColumn = true;
for (int rowIx = 0; rowIx < nRows; rowIx++) {
columnIx[rowIx] = A.data[rowIx, colIx];
if (columnIx[rowIx] != 0)
isZeroColumn = false;
}
if (isZeroColumn) {
for (int rowIx = 0; rowIx < nRows; rowIx++)
Q[rowIx, colIx] = 0;
// Set corresponding R element to 0
R[colIx, colIx] = 0;
Console.WriteLine($"zero column {colIx}");
continue;
}
// Step 2: Subtract projections of v onto previous q's (orthogonalize)
for (int colIx2 = 0; colIx2 < colIx; colIx2++) {
// Compute the dot product of v and column(colIx2) of Q
float dotProduct = 0;
for (int rowIx2 = 0; rowIx2 < nRows; rowIx2++)
dotProduct += columnIx[rowIx2] * Q[rowIx2, colIx2];
// Subtract the projection from v
for (int rowIx2 = 0; rowIx2 < nRows; rowIx2++)
columnIx[rowIx2] -= dotProduct * Q[rowIx2, colIx2];
}
// Step 3: Normalize v to get column(colIx) of Q
float norm = 0;
for (int rowIx = 0; rowIx < nRows; rowIx++)
norm += columnIx[rowIx] * columnIx[rowIx];
if (norm == 0)
throw new Exception("invalid value");
norm = (float)Math.Sqrt(norm);
for (int rowIx = 0; rowIx < nRows; rowIx++)
Q[rowIx, colIx] = columnIx[rowIx] / norm;
// Here is where it deviates from the Full QR Decomposition !
// Step 4: Compute the row(colIx) of R
for (int colIx2 = colIx; colIx2 < nCols; colIx2++) {
float dotProduct = 0;
for (int rowIx2 = 0; rowIx2 < nRows; rowIx2++)
dotProduct += Q[rowIx2, colIx] * A.data[rowIx2, colIx2];
R[colIx, colIx2] = dotProduct;
}
}
if (!float.IsFinite(R[0, 0]))
throw new Exception("invalid value");
return (new Matrix2(Q), new Matrix2(R));
}
}
class SVD {
// According to ChatGPT, Mathnet uses Golub-Reinsch SVD algorithm
// 1. Bidiagonalization: The input matrix AA is reduced to a bidiagonal form using Golub-Kahan bidiagonalization.
// This process involves applying a sequence of Householder reflections to AA to create a bidiagonal matrix.
// This step reduces the complexity by making the matrix simpler while retaining the essential structure needed for SVD.
//
// 2. Diagonalization: Once the matrix is in bidiagonal form,
// the singular values are computed using an iterative process
// (typically involving QR factorization or Jacobi rotations) until convergence.
// This process diagonalizes the bidiagonal matrix and allows extraction of the singular values.
//
// 3. Computing UU and VTVT: After obtaining the singular values,
// the left singular vectors UU and right singular vectors VTVT are computed
// using the accumulated transformations (such as Householder reflections) from the bidiagonalization step.
// Bidiagnolizations through Householder transformations
public static (Matrix2 U1, Matrix2 B, Matrix2 V1) Bidiagonalization(Matrix2 A) {
int m = A.nRows; // Rows of A
int n = A.nCols; // Columns of A
float[,] U1 = new float[m, m]; // Left orthogonal matrix
float[,] V1 = new float[n, n]; // Right orthogonal matrix
float[,] B = A.Clone().data; // Copy A to B for transformation
// Initialize U1 and V1 as identity matrices
for (int i = 0; i < m; i++)
U1[i, i] = 1;
for (int i = 0; i < n; i++)
V1[i, i] = 1;
// Perform Householder reflections to create a bidiagonal matrix B
for (int j = 0; j < n; j++) {
// Step 1: Construct the Householder vector y
float[] y = new float[m - j];
for (int i = j; i < m; i++)
y[i - j] = B[i, j];
// Step 2: Compute the norm and scalar alpha
float norm = 0;
for (int i = 0; i < y.Length; i++)
norm += y[i] * y[i];
norm = (float)Math.Sqrt(norm);
if (B[j, j] > 0)
norm = -norm;
float alpha = (float)Math.Sqrt(0.5 * (norm * (norm - B[j, j])));
float r = (float)Math.Sqrt(0.5 * (norm * (norm + B[j, j])));
// Step 3: Apply the reflection to zero out below diagonal
for (int k = j; k < n; k++) {
float dot = 0;
for (int i = j; i < m; i++)
dot += y[i - j] * B[i, k];
dot /= r;
for (int i = j; i < m; i++)
B[i, k] -= 2 * dot * y[i - j];
}
// Step 4: Update U1 with the Householder reflection (U1 * Householder)
for (int i = j; i < m; i++)
U1[i, j] = y[i - j] / alpha;
// Step 5: Update V1 (storing the Householder vector y)
// Correct indexing: we only need to store part of y in V1 from index j to n
for (int i = j; i < n; i++)
V1[j, i] = B[j, i];
// Repeat steps for further columns if necessary
}
return (new Matrix2(U1), new Matrix2(B), new Matrix2(V1));
}
public static Matrix2 Bidiagonalize(Matrix2 A) {
int m = A.nRows; // Rows of A
int n = A.nCols; // Columns of A
float[,] B = A.Clone().data; // Copy A to B for transformation
// Perform Householder reflections to create a bidiagonal matrix B
for (int j = 0; j < n; j++) {
// Step 1: Construct the Householder vector y
float[] y = new float[m - j];
for (int i = j; i < m; i++)
y[i - j] = B[i, j];
// Step 2: Compute the norm and scalar alpha
float norm = 0;
for (int i = 0; i < y.Length; i++)
norm += y[i] * y[i];
norm = (float)Math.Sqrt(norm);
if (B[j, j] > 0)
norm = -norm;
float r = (float)Math.Sqrt(0.5 * (norm * (norm + B[j, j])));
// Step 3: Apply the reflection to zero out below diagonal
for (int k = j; k < n; k++) {
float dot = 0;
for (int i = j; i < m; i++)
dot += y[i - j] * B[i, k];
dot /= r;
for (int i = j; i < m; i++)
B[i, k] -= 2 * dot * y[i - j];
}
// Repeat steps for further columns if necessary
}
return new Matrix2(B);
}
// QR Iteration for diagonalization of a bidiagonal matrix B
public static (Matrix1 singularValues, Matrix2 U, Matrix2 Vt) QRIteration(Matrix2 B) {
int m = B.nRows;
int n = B.nCols;
Matrix2 U = new(m, m); // Left singular vectors (U)
Matrix2 Vt = new(n, n); // Right singular vectors (V^T)
float[] singularValues = new float[Math.Min(m, n)]; // Singular values
// Initialize U and Vt as identity matrices
for (int i = 0; i < m; i++)
U.data[i, i] = 1;
for (int i = 0; i < n; i++)
Vt.data[i, i] = 1;
// Perform QR iterations
float tolerance = 1e-7f; //1e-12f; for double
bool converged = false;
while (!converged) {
// Perform QR decomposition on the matrix B
(Matrix2 Q, Matrix2 R) = QR.Decomposition(B);
// Update B to be the product Q * R //R * Q
B = R * Q;
// Accumulate the transformations in U and Vt
U *= Q;
Vt *= R;
// Check convergence by looking at the off-diagonal elements of B
converged = true;
for (int i = 0; i < m - 1; i++) {
for (int j = i + 1; j < n; j++) {
if (Math.Abs(B.data[i, j]) > tolerance) {
converged = false;
break;
}
}
}
}
// Extract singular values (diagonal elements of B)
for (int i = 0; i < Math.Min(m, n); i++)
singularValues[i] = B.data[i, i];
return (new Matrix1(singularValues), U, Vt);
}
public static (Matrix2 U, Matrix1 S, Matrix2 Vt) Decomposition(Matrix2 A) {
if (A.nRows != A.nCols)
throw new ArgumentException("SVD: matrix A has to be square.");
Matrix2 B = Bidiagonalize(A);
(Matrix1 S, Matrix2 U, Matrix2 Vt) = QRIteration(B);
return (U, S, Vt);
}
}
}

View File

@ -0,0 +1,261 @@
using System;
#if UNITY_5_3_OR_NEWER
using Vector3Float = UnityEngine.Vector3;
#endif
namespace LinearAlgebra
{
/// <summary>
/// A direction in 3D space
/// </summary>
/// A direction is represented using two angles:
/// * The horizontal angle ranging from -180 (inclusive) to 180 (exclusive)
/// degrees which is a rotation in the horizontal plane
/// * A vertical angle ranging from -90 (inclusive) to 90 (exclusive) degrees
/// which is the rotation in the up/down direction applied after the horizontal
/// rotation has been applied.
/// The angles are automatically normalized to stay within the abovenmentioned
/// ranges.
public struct Direction
{
/// @brief horizontal angle, range = (-180..180] degrees
public AngleFloat horizontal;
/// @brief vertical angle, range in degrees = (-90..90] degrees
public AngleFloat vertical;
/// <summary>
/// Create a new direction
/// </summary>
/// <param name="horizontal">The horizontal angle</param>
/// <param name="vertical">The vertical angle</param>
/// <remarks>The direction will be normalized automatically
/// to ensure the angles are within the allowed ranges</remarks>
public Direction(AngleFloat horizontal, AngleFloat vertical)
{
this.horizontal = horizontal;
this.vertical = vertical;
this.Normalize();
}
/// <summary>
/// Create a direction using angle values in degrees
/// </summary>
/// <param name="horizontal">The horizontal angle in degrees</param>
/// <param name="vertical">The vertical angle in degrees</param>
/// <returns>The direction</returns>
/// <remarks>The direction will be normalized automatically
/// to ensure the angles are within the allowed ranges</remarks>
public static Direction Degrees(float horizontal, float vertical)
{
Direction d = new()
{
horizontal = AngleFloat.Degrees(horizontal),
vertical = AngleFloat.Degrees(vertical)
};
d.Normalize();
return d;
}
/// <summary>
/// Create a direction using angle values in radians
/// </summary>
/// <param name="horizontal">The horizontal angle in radians</param>
/// <param name="vertical">The vertical angle in radians</param>
/// <returns>The direction</returns>
public static Direction Radians(float horizontal, float vertical)
{
Direction d = new()
{
horizontal = AngleFloat.Radians(horizontal),
vertical = AngleFloat.Radians(vertical)
};
d.Normalize();
return d;
}
public override readonly string ToString() {
return $"Direction(h: {this.horizontal}, v: {this.vertical})";
}
/// <summary>
/// A forward direction with zero for both angles
/// </summary>
public readonly static Direction forward = Degrees(0, 0);
/// <summary>
/// A backward direction with horizontal angle -180 and zero vertical
/// angle
/// </summary>
public readonly static Direction backward = Degrees(-180, 0);
/// <summary>
/// A upward direction with zero horizontal angle and vertical angle 90
/// </summary>
public readonly static Direction up = Degrees(0, 90);
/// <summary>
/// A downward direction with zero horizontal angle and vertical angle
/// -90
/// </summary>
public readonly static Direction down = Degrees(0, -90);
/// <summary>
/// A left-pointing direction with horizontal angle -90 and zero
/// vertical angle
/// </summary>
public readonly static Direction left = Degrees(-90, 0);
/// <summary>
/// A right-pointing direction with horizontal angle 90 and zero
/// vertical angle
/// </summary>
public readonly static Direction right = Degrees(90, 0);
private void Normalize()
{
if (this.vertical > AngleFloat.deg90 || this.vertical < -AngleFloat.deg90)
{
this.horizontal += AngleFloat.deg180;
this.vertical = AngleFloat.Degrees(180 - this.vertical.inDegrees);
}
}
#if UNITY_5_3_OR_NEWER
/// <summary>
/// Convert the direction into a carthesian vector
/// </summary>
/// <returns>The carthesian vector corresponding to this direction.</returns>
public readonly UnityEngine.Vector3 ToVector3() {
// Convert degrees to radians
float radH = this.horizontal.inRadians;
float radV = this.vertical.inRadians;
// Calculate Vector
float cosV = MathF.Cos(radV);
float sinV = MathF.Sin(radV);
float x = cosV * MathF.Sin(radH);
float y = sinV;
float z = cosV * MathF.Cos(radH);
return new UnityEngine.Vector3(x, y, z);
}
/// <summary>
/// Convert a carthesian vector into a direction
/// </summary>
/// <param name="v">The carthesian vector</param>
/// <returns>The direction</returns>
/// <remarks>Information about the length of the carthesian vector is not
/// included in this transformation</remarks>
public static Direction FromVector3(UnityEngine.Vector3 v) {
AngleFloat horizontal = AngleFloat.Atan2(v.x, v.z);
AngleFloat vertical = AngleFloat.deg90 - AngleFloat.Acos(v.y);
Direction d = new(horizontal, vertical);
return d;
}
#else
/// <summary>
/// Convert the direction into a carthesian vector
/// </summary>
/// <returns>The carthesian vector corresponding to this direction.</returns>
public readonly Vector3Float ToVector3() {
// Quaternion q = Quaternion.Euler(90 - this.vertical.inDegrees, this.horizontal.inDegrees, 0);
// Vector3Float v = q * Vector3Float.forward;
// return v;
float radH = this.horizontal.inRadians;
float radV = this.vertical.inRadians;
float cosV = MathF.Cos(radV);
float sinV = MathF.Sin(radV);
float horizontal = cosV * MathF.Sin(radH);
float vertical = sinV;
float depth = cosV * MathF.Cos(radH);
return new Vector3Float(horizontal, vertical, depth);
}
/// <summary>
/// Convert a carthesian vector into a direction
/// </summary>
/// <param name="v">The carthesian vector</param>
/// <returns>The direction</returns>
/// <remarks>Information about the length of the carthesian vector is not
/// included in this transformation</remarks>
public static Direction FromVector3(Vector3Float v)
{
AngleFloat horizontal = AngleFloat.Atan2(v.horizontal, v.depth);
AngleFloat vertical = AngleFloat.deg90 - AngleFloat.Acos(v.vertical);
Direction d = new(horizontal, vertical);
return d;
}
#endif
public static Direction operator -(Direction d) {
AngleFloat horizontal = d.horizontal + AngleFloat.deg180;
AngleFloat vertical = -d.vertical;
return new Direction(horizontal, vertical);
}
/// <summary>
/// Tests the equality of two directions
/// </summary>
/// <param name="d1"></param>
/// <param name="d2"></param>
/// <returns>True when the direction angles are equal, false otherwise.</returns>
public static bool operator ==(Direction d1, Direction d2)
{
bool horizontalEq = d1.horizontal == d2.horizontal;
bool verticalEq = d1.vertical == d2.vertical;
return horizontalEq && verticalEq;
}
/// <summary>
/// Tests the inequality of two directions
/// </summary>
/// <param name="d1"></param>
/// <param name="d2"></param>
/// <returns>True when the direction angles are not equal, false otherwise.</returns>
public static bool operator !=(Direction d1, Direction d2)
{
bool horizontalNEq = d1.horizontal != d2.horizontal;
bool verticalNEq = d1.vertical != d2.vertical;
return horizontalNEq || verticalNEq;
}
public override readonly bool Equals(object obj)
{
if (obj is not Direction d)
return false;
bool horizontalEq = this.horizontal == d.horizontal;
bool verticalEq = this.vertical == d.vertical;
return horizontalEq && verticalEq;
}
public override readonly int GetHashCode()
{
return HashCode.Combine(horizontal, vertical);
}
public static AngleFloat UnsignedAngle(Direction d1, Direction d2) {
// Convert angles from degrees to radians
float horizontal1Rad = d1.horizontal.inRadians;
float vertical1Rad = d1.vertical.inRadians;
float horizontal2Rad = d2.horizontal.inRadians;
float vertical2Rad = d2.vertical.inRadians;
// Calculate the cosine of the angle using the spherical law of cosines
float cosTheta = MathF.Sin(vertical1Rad) * MathF.Sin(vertical2Rad) +
MathF.Cos(vertical1Rad) * MathF.Cos(vertical2Rad) *
MathF.Cos(horizontal1Rad - horizontal2Rad);
// Clip cosTheta to the valid range for acos
cosTheta = Float.Clamp(cosTheta, -1.0f, 1.0f);
// Calculate the angle
AngleFloat angle = AngleFloat.Acos(cosTheta);
return angle;
}
}
}

View File

@ -0,0 +1,41 @@
namespace LinearAlgebra {
/// <summary>
/// Float number utilities
/// </summary>
public class Float {
/// <summary>
/// The precision of float numbers
/// </summary>
public const float epsilon = 1E-05f;
/// <summary>
/// The square of the float number precision
/// </summary>
public const float sqrEpsilon = 1e-10f;
/// <summary>
/// Clamp the value between the given minimum and maximum values
/// </summary>
/// <param name="f">The value to clamp</param>
/// <param name="min">The minimum value</param>
/// <param name="max">The maximum value</param>
/// <returns>The clamped value</returns>
public static float Clamp(float f, float min, float max) {
if (f < min)
return min;
if (f > max)
return max;
return f;
}
/// <summary>
/// Clamp the value between to the interval [0..1]
/// </summary>
/// <param name="f">The value to clamp</param>
/// <returns>The clamped value</returns>
public static float Clamp01(float f) {
return Clamp(f, 0, 1);
}
}
}

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
</ItemGroup>
</Project>

689
LinearAlgebra/src/Matrix.cs Normal file
View File

@ -0,0 +1,689 @@
using System;
#if UNITY_5_3_OR_NEWER
using Vector3Float = UnityEngine.Vector3;
using Vector2Float = UnityEngine.Vector2;
using Quaternion = UnityEngine.Quaternion;
#endif
namespace LinearAlgebra {
public readonly struct Slice
{
public int start { get; }
public int stop { get; }
public Slice(int start, int stop)
{
this.start = start;
this.stop = stop;
}
}
public class Matrix2 {
public float[,] data { get; }
public int nRows => data.GetLength(0);
public int nCols => data.GetLength(1);
public Matrix2(int nRows, int nCols)
{
this.data = new float[nRows, nCols];
}
public Matrix2(float[,] data) {
this.data = data;
}
public Matrix2 Clone() {
float[,] data = new float[this.nRows, nCols];
for (int rowIx = 0; rowIx < this.nRows; rowIx++) {
for (int colIx = 0; colIx < this.nCols; colIx++)
data[rowIx, colIx] = this.data[rowIx, colIx];
}
return new Matrix2(data);
}
public static Matrix2 Zero(int nRows, int nCols)
{
return new Matrix2(nRows, nCols);
}
public static Matrix2 FromVector3(Vector3Float v) {
float[,] result = new float[3, 1];
result[0, 0] = v.horizontal;
result[1, 0] = v.vertical;
result[2, 0] = v.depth;
return new Matrix2(result);
}
public static Matrix2 Identity(int size)
{
return Diagonal(1, size);
}
public static Matrix2 Identity(int nRows, int nCols)
{
Matrix2 m = Zero(nRows, nCols);
m.FillDiagonal(1);
return m;
}
public static Matrix2 Diagonal(Matrix1 v) {
float[,] resultData = new float[v.size, v.size];
for (int ix = 0; ix < v.size; ix++)
resultData[ix, ix] = v.data[ix];
return new Matrix2(resultData);
}
public static Matrix2 Diagonal(float f, int size)
{
float[,] resultData = new float[size, size];
for (int ix = 0; ix < size; ix++)
resultData[ix, ix] = f;
return new Matrix2(resultData);
}
public void FillDiagonal(Matrix1 v)
{
int n = (int)Math.Min(Math.Min(this.nRows, this.nCols), v.size);
for (int ix = 0; ix < n; ix++)
this.data[ix, ix] = v.data[ix];
}
public void FillDiagonal(float f)
{
int n = Math.Min(this.nRows, this.nCols);
for (int ix = 0; ix < n; ix++)
this.data[ix, ix] = f;
}
public static Matrix2 SkewMatrix(Vector3Float v) {
float[,] result = new float[3, 3] {
{0, -v.depth, v.vertical},
{v.depth, 0, -v.horizontal},
{-v.vertical, v.horizontal, 0}
};
return new Matrix2(result);
}
public Matrix1 GetRow(int rowIx) {
float[] row = new float[this.nCols];
for (int colIx = 0; colIx < this.nCols; colIx++) {
row[colIx] = this.data[rowIx, colIx];
}
return new Matrix1(row);
}
#if UNITY_5_3_OR_NEWER
public UnityEngine.Vector3 GetRow3(int rowIx) {
int cols = this.nCols;
UnityEngine.Vector3 row = new() {
x = this.data[rowIx, 0],
y = this.data[rowIx, 1],
z = this.data[rowIx, 2]
};
return row;
}
#endif
public void SetRow(int rowIx, Matrix1 v) {
for (uint ix = 0; ix < v.size; ix++)
this.data[rowIx, ix] = v.data[ix];
}
public void SetRow3(int rowIx, Vector3Float v) {
this.data[rowIx, 0] = v.horizontal;
this.data[rowIx, 1] = v.vertical;
this.data[rowIx, 2] = v.depth;
}
public void SwapRows(int row1, int row2) {
for (uint ix = 0; ix < this.nCols; ix++) {
float temp = this.data[row1, ix];
this.data[row1, ix] = this.data[row2, ix];
this.data[row2, ix] = temp;
}
}
public Matrix1 GetColumn(int colIx)
{
float[] column = new float[this.nRows];
for (int i = 0; i < this.nRows; i++) {
column[i] = this.data[i, colIx];
}
return new Matrix1(column);
}
public void SetColumn(int colIx, Matrix1 v) {
for (uint ix = 0; ix < v.size; ix++)
this.data[ix, colIx] = v.data[ix];
}
public void SetColumn(int colIx, Vector3Float v) {
this.data[0, colIx] = v.horizontal;
this.data[1, colIx] = v.vertical;
this.data[2, colIx] = v.depth;
}
public static bool AllClose(Matrix2 A, Matrix2 B, float atol = 1e-08f) {
for (int i = 0; i < A.nRows; i++) {
for (int j = 0; j < A.nCols; j++) {
float d = MathF.Abs(A.data[i, j] - B.data[i, j]);
if (d > atol)
return false;
}
}
return true;
}
public Matrix2 Transpose() {
float[,] resultData = new float[this.nCols, this.nRows];
for (uint rowIx = 0; rowIx < this.nRows; rowIx++) {
for (uint colIx = 0; colIx < this.nCols; colIx++)
resultData[colIx, rowIx] = this.data[rowIx, colIx];
}
return new Matrix2(resultData);
// double checked code
}
public Matrix2 transposed {
get => Transpose();
}
public static Matrix2 operator -(Matrix2 m) {
float[,] result = new float[m.nRows, m.nCols];
for (int i = 0; i < m.nRows; i++) {
for (int j = 0; j < m.nCols; j++)
result[i, j] = -m.data[i, j];
}
return new Matrix2(result);
}
public static Matrix2 operator -(Matrix2 A, Matrix2 B) {
if (A.nRows != B.nRows || A.nCols != B.nCols)
throw new System.ArgumentException("Size of A must match size of B.");
float[,] result = new float[A.nRows, B.nCols];
for (int i = 0; i < A.nRows; i++) {
for (int j = 0; j < A.nCols; j++)
result[i, j] = A.data[i, j] - B.data[i, j];
}
return new Matrix2(result);
}
public static Matrix2 operator +(Matrix2 A, Matrix2 B) {
if (A.nRows != B.nRows || A.nCols != B.nCols)
throw new System.ArgumentException("Size of A must match size of B.");
float[,] result = new float[A.nRows, B.nCols];
for (int i = 0; i < A.nRows; i++) {
for (int j = 0; j < A.nCols; j++)
result[i, j] = A.data[i, j] + B.data[i, j];
}
return new Matrix2(result);
}
public static Matrix2 operator *(Matrix2 A, Matrix2 B) {
if (A.nCols != B.nRows)
throw new System.ArgumentException("Number of columns in A must match number of rows in B.");
float[,] result = new float[A.nRows, B.nCols];
for (int i = 0; i < A.nRows; i++) {
for (int j = 0; j < B.nCols; j++) {
float sum = 0.0f;
for (int k = 0; k < A.nCols; k++)
sum += A.data[i, k] * B.data[k, j];
result[i, j] = sum;
}
}
return new Matrix2(result);
// double checked code
}
public static Matrix1 operator *(Matrix2 A, Matrix1 v) {
float[] result = new float[A.nRows];
for (int i = 0; i < A.nRows; i++) {
for (int j = 0; j < A.nCols; j++) {
result[i] += A.data[i, j] * v.data[j];
}
}
return new Matrix1(result);
}
public static Vector3Float operator *(Matrix2 A, Vector3Float v) {
return new Vector3Float(
A.data[0, 0] * v.horizontal + A.data[0, 1] * v.vertical + A.data[0, 2] * v.depth,
A.data[1, 0] * v.horizontal + A.data[1, 1] * v.vertical + A.data[1, 2] * v.depth,
A.data[2, 0] * v.horizontal + A.data[2, 1] * v.vertical + A.data[2, 2] * v.depth
);
}
public static Matrix2 operator *(Matrix2 A, float s) {
float[,] result = new float[A.nRows, A.nCols];
for (int i = 0; i < A.nRows; i++) {
for (int j = 0; j < A.nCols; j++)
result[i, j] = A.data[i, j] * s;
}
return new Matrix2(result);
}
public static Matrix2 operator *(float s, Matrix2 A) {
return A * s;
}
public static Matrix2 operator /(Matrix2 A, float s) {
float[,] result = new float[A.nRows, A.nCols];
for (int i = 0; i < A.nRows; i++) {
for (int j = 0; j < A.nCols; j++)
result[i, j] = A.data[i, j] / s;
}
return new Matrix2(result);
}
public static Matrix2 operator /(float s, Matrix2 A) {
float[,] result = new float[A.nRows, A.nCols];
for (int i = 0; i < A.nRows; i++) {
for (int j = 0; j < A.nCols; j++)
result[i, j] = s / A.data[i, j];
}
return new Matrix2(result);
}
public Matrix2 GetRows(Slice slice) {
return GetRows(slice.start, slice.stop);
}
public Matrix2 GetRows(int from, int to) {
if (from < 0 || to >= this.nRows)
throw new System.ArgumentException("Slice index out of range.");
float[,] result = new float[to - from, this.nCols];
int resultRowIx = 0;
for (int rowIx = from; rowIx < to; rowIx++) {
for (int colIx = 0; colIx < this.nCols; colIx++)
result[resultRowIx, colIx] = this.data[rowIx, colIx];
resultRowIx++;
}
return new Matrix2(result);
}
public Matrix2 Slice(Slice slice)
{
return Slice(slice.start, slice.stop);
}
public Matrix2 Slice(int from, int to)
{
if (from < 0 || to >= this.nRows)
throw new System.ArgumentException("Slice index out of range.");
float[,] result = new float[to - from, this.nCols];
int resultRowIx = 0;
for (int rowIx = from; rowIx < to; rowIx++)
{
for (int colIx = 0; colIx < this.nCols; colIx++)
{
result[resultRowIx, colIx] = this.data[rowIx, colIx];
}
resultRowIx++;
}
return new Matrix2(result);
}
public Matrix2 Slice(Slice rowRange, Slice colRange) {
return Slice((rowRange.start, rowRange.stop), (colRange.start, colRange.stop));
}
public Matrix2 Slice((int start, int stop) rowRange, (int start, int stop) colRange)
{
float[,] result = new float[rowRange.stop - rowRange.start, colRange.stop - colRange.start];
int resultRowIx = 0;
int resultColIx = 0;
for (int i = rowRange.start; i < rowRange.stop; i++)
{
for (int j = colRange.start; j < colRange.stop; j++)
result[resultRowIx, resultColIx] = this.data[i, j];
}
return new Matrix2(result);
}
public void UpdateSlice(Slice slice, Matrix2 m) {
UpdateSlice((slice.start, slice.stop), m);
}
public void UpdateSlice((int start, int stop) slice, Matrix2 m) {
// if (slice.start == slice.stop)
// Console.WriteLine("WARNING: no data is updates when start equals stop in a slice!");
int mRowIx = 0;
for (int rowIx = slice.start; rowIx < slice.stop; rowIx++, mRowIx++) {
for (int colIx = 0; colIx < this.nCols; colIx++)
this.data[rowIx, colIx] = m.data[mRowIx, colIx];
}
}
public void UpdateSlice(Slice rowRange, Slice colRange, Matrix2 m)
{
UpdateSlice((rowRange.start, rowRange.stop), (colRange.start, colRange.stop), m);
}
public void UpdateSlice((int start, int stop) rowRange, (int start, int stop) colRange, Matrix2 m)
{
for (int i = rowRange.start; i < rowRange.stop; i++)
{
for (int j = colRange.start; j < colRange.stop; j++)
this.data[i, j] = m.data[i - rowRange.start, j - colRange.start];
}
}
public Matrix2 Inverse() {
Matrix2 A = this;
// unchecked
int n = A.nRows;
// Create an identity matrix of the same size as the original matrix
float[,] augmentedMatrix = new float[n, 2 * n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
augmentedMatrix[i, j] = A.data[i, j];
augmentedMatrix[i, j + n] = (i == j) ? 1 : 0; // Identity matrix
}
}
// Perform Gaussian elimination
for (int i = 0; i < n; i++) {
// Find the pivot row
float pivot = augmentedMatrix[i, i];
if (Math.Abs(pivot) < 1e-10) // Check for singular matrix
throw new InvalidOperationException("Matrix is singular and cannot be inverted.");
// Normalize the pivot row
for (int j = 0; j < 2 * n; j++)
augmentedMatrix[i, j] /= pivot;
// Eliminate the column below the pivot
for (int j = i + 1; j < n; j++) {
float factor = augmentedMatrix[j, i];
for (int k = 0; k < 2 * n; k++)
augmentedMatrix[j, k] -= factor * augmentedMatrix[i, k];
}
}
// Back substitution
for (int i = n - 1; i >= 0; i--)
{
// Eliminate the column above the pivot
for (int j = i - 1; j >= 0; j--)
{
float factor = augmentedMatrix[j, i];
for (int k = 0; k < 2 * n; k++)
augmentedMatrix[j, k] -= factor * augmentedMatrix[i, k];
}
}
// Extract the inverse matrix from the augmented matrix
float[,] inverse = new float[n, n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++)
inverse[i, j] = augmentedMatrix[i, j + n];
}
return new Matrix2(inverse);
}
public float Determinant()
{
int n = this.nRows;
if (n != this.nCols)
throw new System.ArgumentException("Matrix must be square.");
if (n == 1)
return this.data[0, 0]; // Base case for 1x1 matrix
if (n == 2) // Base case for 2x2 matrix
return this.data[0, 0] * this.data[1, 1] - this.data[0, 1] * this.data[1, 0];
float det = 0;
for (int col = 0; col < n; col++)
det += (col % 2 == 0 ? 1 : -1) * this.data[0, col] * this.Minor(0, col).Determinant();
return det;
}
// Helper function to compute the minor of a matrix
private Matrix2 Minor(int rowToRemove, int colToRemove)
{
int n = this.nRows;
float[,] minor = new float[n - 1, n - 1];
int r = 0, c = 0;
for (int i = 0; i < n; i++) {
if (i == rowToRemove) continue;
c = 0;
for (int j = 0; j < n; j++) {
if (j == colToRemove) continue;
minor[r, c] = this.data[i, j];
c++;
}
r++;
}
return new Matrix2(minor);
}
public static Matrix2 DeleteRows(Matrix2 A, Slice rowRange) {
float[,] result = new float[A.nRows - (rowRange.stop - rowRange.start), A.nCols];
int resultRowIx = 0;
for (int i = 0; i < A.nRows; i++) {
if (i >= rowRange.start && i < rowRange.stop)
continue;
for (int j = 0; j < A.nCols; j++)
result[resultRowIx, j] = A.data[i, j];
resultRowIx++;
}
return new Matrix2(result);
}
internal static Matrix2 DeleteColumns(Matrix2 A, Slice colRange) {
float[,] result = new float[A.nRows, A.nCols - (colRange.stop - colRange.start)];
for (int i = 0; i < A.nRows; i++) {
int resultColIx = 0;
for (int j = 0; j < A.nCols; j++) {
if (j >= colRange.start && j < colRange.stop)
continue;
result[i, resultColIx++] = A.data[i, j];
}
}
return new Matrix2(result);
}
}
public class Matrix1
{
public float[] data { get; }
public int size => data.GetLength(0);
public Matrix1(int size)
{
this.data = new float[size];
}
public Matrix1(float[] data) {
this.data = data;
}
public static Matrix1 Zero(int size)
{
return new Matrix1(size);
}
public static Matrix1 FromVector2(Vector2Float v) {
float[] result = new float[2];
result[0] = v.horizontal;
result[1] = v.vertical;
return new Matrix1(result);
}
public static Matrix1 FromVector3(Vector3Float v) {
float[] result = new float[3];
result[0] = v.horizontal;
result[1] = v.vertical;
result[2] = v.depth;
return new Matrix1(result);
}
#if UNITY_5_3_OR_NEWER
public static Matrix1 FromQuaternion(Quaternion q) {
float[] result = new float[4];
result[0] = q.x;
result[1] = q.y;
result[2] = q.z;
result[3] = q.w;
return new Matrix1(result);
}
#endif
public Vector2Float vector2 {
get {
if (this.size != 2)
throw new System.ArgumentException("Matrix1 must be of size 2");
return new Vector2Float(this.data[0], this.data[1]);
}
}
public Vector3Float vector3 {
get {
if (this.size != 3)
throw new System.ArgumentException("Matrix1 must be of size 3");
return new Vector3Float(this.data[0], this.data[1], this.data[2]);
}
}
#if UNITY_5_3_OR_NEWER
public Quaternion quaternion {
get {
if (this.size != 4)
throw new System.ArgumentException("Matrix1 must be of size 4");
return new Quaternion(this.data[0], this.data[1], this.data[2], this.data[3]);
}
}
#endif
public Matrix1 Clone() {
float[] data = new float[this.size];
for (int rowIx = 0; rowIx < this.size; rowIx++)
data[rowIx] = this.data[rowIx];
return new Matrix1(data);
}
public float magnitude {
get {
float sum = 0;
foreach (var elm in data)
sum += elm;
return sum / data.Length;
}
}
public static Matrix1 operator +(Matrix1 A, Matrix1 B) {
if (A.size != B.size)
throw new System.ArgumentException("Size of A must match size of B.");
float[] result = new float[A.size];
for (int i = 0; i < A.size; i++) {
result[i] = A.data[i] + B.data[i];
}
return new Matrix1(result);
}
public Matrix2 Transpose() {
float[,] r = new float[1, this.size];
for (uint colIx = 0; colIx < this.size; colIx++)
r[1, colIx] = this.data[colIx];
return new Matrix2(r);
}
public static float Dot(Matrix1 a, Matrix1 b) {
if (a.size != b.size)
throw new System.ArgumentException("Vectors must be of the same length.");
float result = 0.0f;
for (int i = 0; i < a.size; i++) {
result += a.data[i] * b.data[i];
}
return result;
}
public static Matrix1 operator -(Matrix1 A, Matrix1 B) {
if (A.size != B.size)
throw new System.ArgumentException("Size of A must match size of B.");
float[] result = new float[A.size];
for (int i = 0; i < A.size; i++) {
result[i] = A.data[i] - B.data[i];
}
return new Matrix1(result);
}
public static Matrix1 operator *(Matrix1 A, float f)
{
float[] result = new float[A.size];
for (int i = 0; i < A.size; i++)
result[i] += A.data[i] * f;
return new Matrix1(result);
}
public static Matrix1 operator *(float f, Matrix1 A) {
return A * f;
}
public static Matrix1 operator /(Matrix1 A, float f) {
float[] result = new float[A.size];
for (int i = 0; i < A.size; i++)
result[i] = A.data[i] / f;
return new Matrix1(result);
}
public static Matrix1 operator /(float f, Matrix1 A) {
float[] result = new float[A.size];
for (int i = 0; i < A.size; i++)
result[i] = f / A.data[i];
return new Matrix1(result);
}
public Matrix1 Slice(Slice range)
{
return Slice(range.start, range.stop);
}
public Matrix1 Slice(int from, int to)
{
if (from < 0 || to >= this.size)
throw new System.ArgumentException("Slice index out of range.");
float[] result = new float[to - from];
int resultIx = 0;
for (int ix = from; ix < to; ix++)
result[resultIx++] = this.data[ix];
return new Matrix1(result);
}
public void UpdateSlice(Slice slice, Matrix1 v) {
int vIx = 0;
for (int ix = slice.start; ix < slice.stop; ix++, vIx++)
this.data[ix] = v.data[vIx];
}
}
}

View File

@ -0,0 +1,87 @@
using System;
namespace LinearAlgebra {
public class Quat32 {
public float x;
public float y;
public float z;
public float w;
public Quat32() {
this.x = 0;
this.y = 0;
this.z = 0;
this.w = 1;
}
public Quat32(float x, float y, float z, float w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
public static Quat32 FromSwingTwist(SwingTwist s) {
Quat32 q32 = Quat32.Euler(-s.swing.vertical.inDegrees, s.swing.horizontal.inDegrees, s.twist.inDegrees);
return q32;
}
public static Quat32 Euler(float yaw, float pitch, float roll) {
float rollOver2 = roll * AngleFloat.Deg2Rad * 0.5f;
float sinRollOver2 = (float)Math.Sin((float)rollOver2);
float cosRollOver2 = (float)Math.Cos((float)rollOver2);
float pitchOver2 = pitch * 0.5f;
float sinPitchOver2 = (float)Math.Sin((float)pitchOver2);
float cosPitchOver2 = (float)Math.Cos((float)pitchOver2);
float yawOver2 = yaw * 0.5f;
float sinYawOver2 = (float)Math.Sin((float)yawOver2);
float cosYawOver2 = (float)Math.Cos((float)yawOver2);
Quat32 result = new Quat32() {
w = cosYawOver2 * cosPitchOver2 * cosRollOver2 +
sinYawOver2 * sinPitchOver2 * sinRollOver2,
x = sinYawOver2 * cosPitchOver2 * cosRollOver2 +
cosYawOver2 * sinPitchOver2 * sinRollOver2,
y = cosYawOver2 * sinPitchOver2 * cosRollOver2 -
sinYawOver2 * cosPitchOver2 * sinRollOver2,
z = cosYawOver2 * cosPitchOver2 * sinRollOver2 -
sinYawOver2 * sinPitchOver2 * cosRollOver2
};
return result;
}
public void ToAngles(out float right, out float up, out float forward) {
float test = this.x * this.y + this.z * this.w;
if (test > 0.499f) { // singularity at north pole
right = 0;
up = 2 * (float)Math.Atan2(this.x, this.w) * AngleFloat.Rad2Deg;
forward = 90;
return;
//return Vector3(0, 2 * (float)atan2(this.x, this.w) * Angle.Rad2Deg, 90);
}
else if (test < -0.499f) { // singularity at south pole
right = 0;
up = -2 * (float)Math.Atan2(this.x, this.w) * AngleFloat.Rad2Deg;
forward = -90;
return;
//return Vector3(0, -2 * (float)atan2(this.x, this.w) * Angle.Rad2Deg, -90);
}
else {
float sqx = this.x * this.x;
float sqy = this.y * this.y;
float sqz = this.z * this.z;
right = (float)Math.Atan2(2 * this.x * this.w - 2 * this.y * this.z, 1 - 2 * sqx - 2 * sqz) * AngleFloat.Rad2Deg;
up = (float)Math.Atan2(2 * this.y * this.w - 2 * this.x * this.z, 1 - 2 * sqy - 2 * sqz) * AngleFloat.Rad2Deg;
forward = (float)Math.Asin(2 * test) * AngleFloat.Rad2Deg;
return;
// return Vector3(
// atan2f(2 * this.x * this.w - 2 * this.y * this.z, 1 - 2 * sqx - 2 * sqz) *
// Rad2Deg,
// atan2f(2 * this.y * this.w - 2 * this.x * this.z, 1 - 2 * sqy - 2 * sqz) *
// Rad2Deg,
// asinf(2 * test) * Angle.Rad2Deg);
}
}
}
}

View File

@ -0,0 +1,582 @@
using System;
#if UNITY_5_3_OR_NEWER
using Quaternion = UnityEngine.Quaternion;
#endif
namespace LinearAlgebra {
#if UNITY_5_3_OR_NEWER
public class QuaternionExtensions {
public static Quaternion Reflect(Quaternion q) {
return new(-q.x, -q.y, -q.z, q.w);
}
public static Matrix2 ToRotationMatrix(Quaternion q) {
float w = q.x, x = q.y, y = q.z, z = q.w;
float[,] result = new float[,]
{
{ 1 - 2 * (y * y + z * z), 2 * (x * y - w * z), 2 * (x * z + w * y) },
{ 2 * (x * y + w * z), 1 - 2 * (x * x + z * z), 2 * (y * z - w * x) },
{ 2 * (x * z - w * y), 2 * (y * z + w * x), 1 - 2 * (x * x + y * y) }
};
return new Matrix2(result);
}
public static Quaternion FromRotationMatrix(Matrix2 m) {
float trace = m.data[0, 0] + m.data[1, 1] + m.data[2, 2];
float w, x, y, z;
if (trace > 0) {
float s = 0.5f / (float)Math.Sqrt(trace + 1.0f);
w = 0.25f / s;
x = (m.data[2, 1] - m.data[1, 2]) * s;
y = (m.data[0, 2] - m.data[2, 0]) * s;
z = (m.data[1, 0] - m.data[0, 1]) * s;
}
else {
if (m.data[0, 0] > m.data[1, 1] && m.data[0, 0] > m.data[2, 2]) {
float s = 2.0f * (float)Math.Sqrt(1.0f + m.data[0, 0] - m.data[1, 1] - m.data[2, 2]);
w = (m.data[2, 1] - m.data[1, 2]) / s;
x = 0.25f * s;
y = (m.data[0, 1] + m.data[1, 0]) / s;
z = (m.data[0, 2] + m.data[2, 0]) / s;
}
else if (m.data[1, 1] > m.data[2, 2]) {
float s = 2.0f * (float)Math.Sqrt(1.0f + m.data[1, 1] - m.data[0, 0] - m.data[2, 2]);
w = (m.data[0, 2] - m.data[2, 0]) / s;
x = (m.data[0, 1] + m.data[1, 0]) / s;
y = 0.25f * s;
z = (m.data[1, 2] + m.data[2, 1]) / s;
}
else {
float s = 2.0f * (float)Math.Sqrt(1.0f + m.data[2, 2] - m.data[0, 0] - m.data[1, 1]);
w = (m.data[1, 0] - m.data[0, 1]) / s;
x = (m.data[0, 2] + m.data[2, 0]) / s;
y = (m.data[1, 2] + m.data[2, 1]) / s;
z = 0.25f * s;
}
}
return new Quaternion(x, y, z, w);
}
}
#else
public struct Quaternion {
public float x;
public float y;
public float z;
public float w;
/// <summary>
/// create a new quaternion with the given values
/// </summary>
/// <param name="x">x component</param>
/// <param name="y">y component</param>
/// <param name="z">z component</param>
/// <param name="w">w component</param>
public Quaternion(float x, float y, float z, float w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
/// <summary>
/// An identity quaternion
/// </summary>
public static readonly Quaternion identity = new(0, 0, 0, 1);
private readonly Vector3Float xyz => new(x, y, z);
private readonly float magnitude => MathF.Sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
private readonly float sqrMagnitude => x * x + y * y + z * z + w * w;
/// <summary>
/// Convert to unit quaternion
/// </summary>
/// This will preserve the orientation,
/// but ensures that it is a unit quaternion.
public readonly Quaternion normalized {
get {
float length = this.magnitude;
Quaternion q = new(this.x / length, this.y / length, this.z / length, this.w / length);
return q;
}
}
/// <summary>
/// Convert to unity quaternion
/// </summary>
/// <param name="q">The quaternion to convert</param>
/// <returns>A unit quaternion</returns>
/// This will preserve the orientation,
/// but ensures that it is a unit quaternion.
public static Quaternion Normalize(Quaternion q) {
return q.normalized;
}
/// <summary>
/// Convert to euler angles
/// </summary>
/// <param name="q">The quaternion to convert</param>
/// <returns>A vector containing euler angles</returns>
/// The euler angles performed in the order: Z, X, Y
public static Vector3Float ToAngles(Quaternion q) {
// Extract Euler angles in Unity order (X = pitch, Y = yaw, Z = roll), returned in degrees as (x,pitch),(y,yaw),(z,roll)
// Handle singularities/gimbal lock
float test = 2f * (q.w * q.x - q.y * q.z);
// clamp
if (test >= 1f) test = 1f;
if (test <= -1f) test = -1f;
float pitch = MathF.Asin(test); // X
float roll = MathF.Atan2(2f * (q.w * q.z + q.x * q.y),
1f - 2f * (q.x * q.x + q.z * q.z)); // Z
float yaw = MathF.Atan2(2f * (q.w * q.y + q.x * q.z),
1f - 2f * (q.y * q.y + q.x * q.x)); // Y
const float rad2deg = 180f / MathF.PI;
return new Vector3Float(pitch * rad2deg, yaw * rad2deg, roll * rad2deg);
// float test = q.x * q.y + q.z * q.w;
// if (test > 0.499f) // singularity at north pole
// return new Vector3Float(0, 2 * MathF.Atan2(q.x, q.w) * AngleFloat.Rad2Deg, 90);
// else if (test < -0.499f) // singularity at south pole
// return new Vector3Float(0, -2 * MathF.Atan2(q.x, q.w) * AngleFloat.Rad2Deg, -90);
// else {
// float sqx = q.x * q.x;
// float sqy = q.y * q.y;
// float sqz = q.z * q.z;
// return new Vector3Float(
// MathF.Atan2(2 * q.x * q.w - 2 * q.y * q.z, 1 - 2 * sqx - 2 * sqz) *
// AngleFloat.Rad2Deg,
// MathF.Atan2(2 * q.y * q.w - 2 * q.x * q.z, 1 - 2 * sqy - 2 * sqz) *
// AngleFloat.Rad2Deg,
// MathF.Asin(2 * test) * AngleFloat.Rad2Deg);
// }
}
/// <summary>
/// Create a rotation from euler angles
/// </summary>
/// <param name="x">The angle around the right axis</param>
/// <param name="y">The angle around the upward axis</param>
/// <param name="z">The angle around the forward axis</param>
/// <returns>The resulting quaternion</returns>
/// Rotation are appied in the order Z, X, Y.
public static Quaternion Euler(float x, float y, float z) {
return Quaternion.Euler(new Vector3Float(x, y, z));
}
/// <summary>
/// Create a rotation from a vector containing euler angles
/// </summary>
/// <param name="eulerAngles">Vector with the euler angles</param>
/// <returns>The resulting quaternion</returns>
/// Rotation are appied in the order Z, X, Y.
public static Quaternion Euler(Vector3Float angles) {
Vector3Float euler = angles * AngleFloat.Deg2Rad;
float cx = MathF.Cos(euler.horizontal * 0.5f);
float sx = MathF.Sin(euler.horizontal * 0.5f);
float cy = MathF.Cos(euler.vertical * 0.5f);
float sy = MathF.Sin(euler.vertical * 0.5f);
float cz = MathF.Cos(euler.depth * 0.5f);
float sz = MathF.Sin(euler.depth * 0.5f);
// Unity uses intrinsic Z, then X, then Y -> q = Qy * Qx * Qz
Quaternion q;
q.w = cy * cx * cz + sy * sx * sz;
q.x = cy * sx * cz + sy * cx * sz;
q.y = sy * cx * cz - cy * sx * sz;
q.z = cy * cx * sz - sy * sx * cz;
return q;
}
/// <summary>
/// Multiply two quaternions
/// </summary>
/// <param name="q1"></param>
/// <param name="q2"></param>
/// <returns>The resulting rotation</returns>
public static Quaternion operator *(Quaternion q1, Quaternion q2) {
return new Quaternion(
q1.x * q2.w + q1.y * q2.z - q1.z * q2.y + q1.w * q2.x,
-q1.x * q2.z + q1.y * q2.w + q1.z * q2.x + q1.w * q2.y,
q1.x * q2.y - q1.y * q2.x + q1.z * q2.w + q1.w * q2.z,
-q1.x * q2.x - q1.y * q2.y - q1.z * q2.z + q1.w * q2.w);
}
/// <summary>
/// Rotate a vector using this quaterion
/// </summary>
/// <param name="q">The rotation</param>
/// <param name="v">The vector to rotate</param>
/// <returns>The rotated vector</returns>
public static Vector3Float operator *(Quaternion q, Vector3Float v) {
float num = q.x * 2;
float num2 = q.y * 2;
float num3 = q.z * 2;
float num4 = q.x * num;
float num5 = q.y * num2;
float num6 = q.z * num3;
float num7 = q.x * num2;
float num8 = q.x * num3;
float num9 = q.y * num3;
float num10 = q.w * num;
float num11 = q.w * num2;
float num12 = q.w * num3;
float px = v.horizontal;
float py = v.vertical;
float pz = v.depth;
float rx =
(1 - (num5 + num6)) * px + (num7 - num12) * py + (num8 + num11) * pz;
float ry =
(num7 + num12) * px + (1 - (num4 + num6)) * py + (num9 - num10) * pz;
float rz =
(num8 - num11) * px + (num9 + num10) * py + (1 - (num4 + num5)) * pz;
Vector3Float result = new(rx, ry, rz);
return result;
}
/// <summary>
/// The inverse of quaterion
/// </summary>
/// <param name="quaternion">The quaternion for which the inverse is
/// needed</param> <returns>The inverted quaternion</returns>
public static Quaternion Inverse(Quaternion q) {
float n = MathF.Sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
return new Quaternion(-q.x / n, -q.y / n, -q.z / n, q.w / n);
}
/// <summary>
/// A rotation which looks in the given direction
/// </summary>
/// <param name="forward">The look direction</param>
/// <param name="upwards">The up direction</param>
/// <returns>The look rotation</returns>
public static Quaternion LookRotation(Vector3Float forward, Vector3Float up) {
Vector3Float nForward = forward.normalized;
Vector3Float nRight = Vector3Float.Normalize(Vector3Float.Cross(up, nForward));
Vector3Float nUp = Vector3Float.Cross(nForward, nRight);
float m00 = nRight.horizontal; // x;
float m01 = nRight.vertical; // y;
float m02 = nRight.depth; // z;
float m10 = nUp.horizontal; // x;
float m11 = nUp.vertical; // y;
float m12 = nUp.depth; // z;
float m20 = nForward.horizontal; // x;
float m21 = nForward.vertical; // y;
float m22 = nForward.depth; // z;
float num8 = (m00 + m11) + m22;
float x, y, z, w;
if (num8 > 0) {
float num = MathF.Sqrt(num8 + 1);
w = num * 0.5f;
num = 0.5f / num;
x = (m12 - m21) * num;
y = (m20 - m02) * num;
z = (m01 - m10) * num;
return new Quaternion(x, y, z, w);
}
if ((m00 >= m11) && (m00 >= m22)) {
float num7 = MathF.Sqrt(((1 + m00) - m11) - m22);
float num4 = 0.5F / num7;
x = 0.5f * num7;
y = (m01 + m10) * num4;
z = (m02 + m20) * num4;
w = (m12 - m21) * num4;
return new Quaternion(x, y, z, w);
}
if (m11 > m22) {
float num6 = MathF.Sqrt(((1 + m11) - m00) - m22);
float num3 = 0.5F / num6;
x = (m10 + m01) * num3;
y = 0.5F * num6;
z = (m21 + m12) * num3;
w = (m20 - m02) * num3;
return new Quaternion(x, y, z, w);
}
float num5 = MathF.Sqrt(((1 + m22) - m00) - m11);
float num2 = 0.5F / num5;
x = (m20 + m02) * num2;
y = (m21 + m12) * num2;
z = 0.5F * num5;
w = (m01 - m10) * num2;
return new Quaternion(x, y, z, w);
}
/// <summary>
/// Creates a quaternion with the given forward direction with up =
/// Vector3::up
/// </summary>
/// <param name="forward">The look direction</param>
/// <returns>The rotation for this direction</returns>
/// For the rotation, Vector::up is used for the up direction.
/// Note: if the forward direction == Vector3::up, the result is
/// Quaternion::identity
public static Quaternion LookRotation(Vector3Float forward) {
Vector3Float up = new(0, 1, 0);
return LookRotation(forward, up);
}
/// <summary>
/// Calculat the rotation from on vector to another
/// </summary>
/// <param name="fromDirection">The from direction</param>
/// <param name="toDirection">The to direction</param>
/// <returns>The rotation from the first to the second vector</returns>
public static Quaternion FromToRotation(Vector3Float fromDirection, Vector3Float toDirection) {
Vector3Float axis = Vector3Float.Cross(fromDirection, toDirection);
axis = axis.normalized;
AngleFloat angle = Vector3Float.SignedAngle(fromDirection, toDirection, axis);
Quaternion rotation = AngleAxis(angle, axis);
return rotation;
}
/// <summary>
/// Rotate form one orientation to anther with a maximum amount of degrees
/// </summary>
/// <param name="from">The from rotation</param>
/// <param name="to">The destination rotation</param>
/// <param name="maxDegreesDelta">The maximum amount of degrees to
/// rotate</param> <returns>The possibly limited rotation</returns>
public static Quaternion RotateTowards(Quaternion from, Quaternion to,
float maxDegreesDelta) {
float num = Quaternion.UnsignedAngle(from, to);
if (num == 0) {
return to;
}
float t = MathF.Min(1, maxDegreesDelta / num);
return SlerpUnclamped(from, to, t);
}
/// <summary>
/// Convert an angle/axis representation to a quaternion
/// </summary>
/// <param name="angle">The angle</param>
/// <param name="axis">The axis</param>
/// <returns>The resulting quaternion</returns>
public static Quaternion AngleAxis(AngleFloat angle, Vector3Float axis) {
if (axis.sqrMagnitude == 0.0f)
return Quaternion.identity;
float radians = angle.inRadians;
radians *= 0.5f;
Vector3Float axis2 = axis * MathF.Sin(radians);
float x = axis2.horizontal; // x;
float y = axis2.vertical; // y;
float z = axis2.depth; // z;
float w = MathF.Cos(radians);
return new Quaternion(x, y, z, w).normalized;
}
/// <summary>
/// Convert this quaternion to angle/axis representation
/// </summary>
/// <param name="angle">A pointer to the angle for the result</param>
/// <param name="axis">A pointer to the axis for the result</param>
public readonly void ToAngleAxis(out AngleFloat angle, out Vector3Float axis) {
Quaternion q1 = (MathF.Abs(this.w) > 1.0f) ? this.normalized : this;
angle = AngleFloat.Radians(2.0f * MathF.Acos(q1.w)); // angle
float den = MathF.Sqrt(1.0F - q1.w * q1.w);
if (den > 0.0001f) {
axis = Vector3Float.Normalize(q1.xyz / den);
}
else {
// This occurs when the angle is zero.
// Not a problem: just set an arbitrary normalized axis.
axis = Vector3Float.right;
}
}
/// <summary>
/// Get the angle between two orientations
/// </summary>
/// <param name="orientation1">The first orientation</param>
/// <param name="orientation2">The second orientation</param>
/// <returns>The smallest angle in degrees between the two
/// orientations</returns>
public static float UnsignedAngle(Quaternion q1, Quaternion q2) {
// float f = Dot(q1, q2);
// return MathF.Acos(MathF.Min(MathF.Abs(f), 1)) * 2 * AngleFloat.Rad2Deg;
float dot = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;
dot = MathF.Min(MathF.Max(dot, -1f), 1f);
return 2f * MathF.Acos(MathF.Abs(dot)) * (180f / MathF.PI);
}
/// <summary>
/// Sherical lerp between two rotations
/// </summary>
/// <param name="rotation1">The first rotation</param>
/// <param name="rotation2">The second rotation</param>
/// <param name="factor">The factor between 0 and 1.</param>
/// <returns>The resulting rotation</returns>
/// A factor 0 returns rotation1, factor1 returns rotation2.
public static Quaternion Slerp(Quaternion a,
Quaternion b, float t) {
if (t > 1)
t = 1;
if (t < 0)
t = 0;
return SlerpUnclamped(a, b, t);
}
/// <summary>
/// Unclamped sherical lerp between two rotations
/// </summary>
/// <param name="rotation1">The first rotation</param>
/// <param name="rotation2">The second rotation</param>
/// <param name="factor">The factor</param>
/// <returns>The resulting rotation</returns>
/// A factor 0 returns rotation1, factor1 returns rotation2.
/// Values outside the 0..1 range will result in extrapolated rotations
public static Quaternion SlerpUnclamped(Quaternion a,
Quaternion b, float t) {
// if either input is zero, return the other.
if (a.sqrMagnitude == 0.0f) {
if (b.sqrMagnitude == 0.0f) {
return identity;
}
return b;
}
else if (b.sqrMagnitude == 0.0f) {
return a;
}
Vector3Float axyz = a.xyz;
Vector3Float bxyz = b.xyz;
float cosHalfAngle = a.w * b.w + Vector3Float.Dot(axyz, bxyz);
Quaternion b2 = b;
if (cosHalfAngle >= 1.0f || cosHalfAngle <= -1.0f) {
// angle = 0.0f, so just return one input.
return a;
}
else if (cosHalfAngle < 0.0f) {
b2.x = -b.x;
b2.y = -b.y;
b2.z = -b.z;
b2.w = -b.w;
cosHalfAngle = -cosHalfAngle;
}
float blendA;
float blendB;
if (cosHalfAngle < 0.99f) {
// do proper slerp for big angles
float halfAngle = MathF.Acos(cosHalfAngle);
float sinHalfAngle = MathF.Sin(halfAngle);
float oneOverSinHalfAngle = 1.0F / sinHalfAngle;
blendA = MathF.Sin(halfAngle * (1.0F - t)) * oneOverSinHalfAngle;
blendB = MathF.Sin(halfAngle * t) * oneOverSinHalfAngle;
}
else {
// do lerp if angle is really small.
blendA = 1.0f - t;
blendB = t;
}
Vector3Float v = axyz * blendA + b2.xyz * blendB;
Quaternion result =
new(v.horizontal, v.vertical, v.depth, blendA * a.w + blendB * b2.w);
if (result.sqrMagnitude > 0.0f)
return result.normalized;
else
return Quaternion.identity;
}
/// <summary>
/// Convert this quaternion to angle/axis representation
/// </summary>
/// <param name="angle">A pointer to the angle for the result</param>
/// <param name="axis">A pointer to the axis for the result</param>
public readonly void ToAngleAxis(out float angle, out Vector3Float axis) {
ToAxisAngleRad(this, out axis, out angle);
angle *= AngleFloat.Rad2Deg;
}
private static void ToAxisAngleRad(Quaternion q,
out Vector3Float axis,
out float angle) {
Quaternion q1 = (MathF.Abs(q.w) > 1.0f) ? Quaternion.Normalize(q) : q;
angle = 2.0f * MathF.Acos(q1.w); // angle
float den = MathF.Sqrt(1.0F - q1.w * q1.w);
if (den > 0.0001f) {
axis = (q1.xyz / den).normalized;
}
else {
// This occurs when the angle is zero.
// Not a problem: just set an arbitrary normalized axis.
axis = new Vector3Float(1, 0, 0);
}
}
/// <summary>
/// Returns the angle of around the give axis for a rotation
/// </summary>
/// <param name="axis">The axis around which the angle should be
/// computed</param> <param name="rotation">The source rotation</param>
/// <returns>The signed angle around the axis</returns>
public static float GetAngleAround(Vector3Float axis, Quaternion rotation) {
Quaternion secondaryRotation = GetRotationAround(axis, rotation);
secondaryRotation.ToAngleAxis(out float rotationAngle, out Vector3Float rotationAxis);
// Do the axis point in opposite directions?
if (Vector3Float.Dot(axis, rotationAxis) < 0)
rotationAngle = -rotationAngle;
return rotationAngle;
}
/// <summary>
/// Returns the rotation limited around the given axis
/// </summary>
/// <param name="axis">The axis which which the rotation should be
/// limited</param> <param name="rotation">The source rotation</param>
/// <returns>The rotation around the given axis</returns>
public static Quaternion GetRotationAround(Vector3Float axis, Quaternion rotation) {
Vector3Float ra = new(rotation.x, rotation.y, rotation.z); // rotation axis
Vector3Float p = Vector3Float.Project(
ra, axis); // return projection ra on to axis (parallel component)
Quaternion twist = new(p.horizontal, p.vertical, p.depth, rotation.w);
twist = Normalize(twist);
return twist;
}
/// <summary>
/// Swing-twist decomposition of a rotation
/// </summary>
/// <param name="axis">The base direction for the decomposition</param>
/// <param name="q">The source rotation</param>
/// <param name="swing">A pointer to the quaternion for the swing
/// result</param> <param name="twist">A pointer to the quaternion for the
/// twist result</param>
static void GetSwingTwist(Vector3Float axis, Quaternion q,
out Quaternion swing, out Quaternion twist) {
twist = GetRotationAround(axis, q);
swing = q * Inverse(twist);
}
/// <summary>
/// Calculate the dot product of two quaternions
/// </summary>
/// <param name="rotation1">The first rotation</param>
/// <param name="rotation2">The second rotation</param>
/// <returns></returns>
public static float Dot(Quaternion q1, Quaternion q2) {
return q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;
}
}
#endif
}

View File

@ -0,0 +1,279 @@
using System;
using System.Collections.Generic;
#if UNITY_5_3_OR_NEWER
using Vector3 = UnityEngine.Vector3;
#endif
namespace LinearAlgebra {
/// <summary>
/// A spherical vector
/// </summary>
/// <remark>This is a struct such that it is a value type and cannot be null
public struct Spherical {
/// <summary>
/// Create a spherical vector
/// </summary>
/// <param name="distance">The distance in meters</param>
/// <param name="direction">The direction of the vector</param>
public Spherical(float distance, Direction direction) {
if (distance > 0) {
this.distance = distance;
this.direction = direction;
}
else {
this.distance = -distance;
this.direction = -direction;
}
}
/// <summary>
/// Create spherical vector. All given angles are in degrees
/// </summary>
/// <param name="distance">The distance in meters</param>
/// <param name="horizontal">The horizontal angle in degrees</param>
/// <param name="vertical">The vertical angle in degrees</param>
/// <returns></returns>
public static Spherical Degrees(float distance, float horizontal, float vertical) {
Direction direction = Direction.Degrees(horizontal, vertical);
Spherical s = new(distance, direction);
return s;
}
public static Spherical Radians(float distance, float horizontal, float vertical) {
Direction direction = Direction.Radians(horizontal, vertical);
Spherical s = new(distance, direction);
return s;
}
/// <summary>
/// The distance in meters
/// </summary>
/// @remark The distance should never be negative
public float distance;
/// <summary>
/// The direction of the vector
/// </summary>
public Direction direction;
/// <summary>
/// A spherical vector with zero degree angles and distance
/// </summary>
public readonly static Spherical zero = new(0, Direction.forward);
/// <summary>
/// A normalized forward-oriented vector
/// </summary>
public readonly static Spherical forward = new(1, Direction.forward);
#if UNITY_5_3_OR_NEWER
public static Spherical FromVector3(Vector3 v) {
float distance = v.magnitude;
Direction direction = Direction.FromVector3(v / distance);
return new Spherical(distance, direction);
}
public readonly Vector3 ToVector3() {
Vector3 v = this.direction.ToVector3();
v *= this.distance;
return v;
}
#else
public static Spherical FromVector3(Vector3Float v) {
float distance = v.magnitude;
if (distance == 0.0f)
return Spherical.zero;
else {
float verticalAngle = (float)(Math.PI / 2 - Math.Acos(v.vertical / distance)) * AngleFloat.Rad2Deg;
float horizontalAngle = (float)Math.Atan2(v.horizontal, v.depth) * AngleFloat.Rad2Deg;
return Degrees(distance, horizontalAngle, verticalAngle);
}
}
public readonly Vector3Float ToVector3() {
// float verticalRad = (AngleFloat.deg90 - this.direction.vertical).inRadians;
// float horizontalRad = this.direction.horizontal.inRadians;
// float cosVertical = (float)Math.Cos(verticalRad);
// float sinVertical = (float)Math.Sin(verticalRad);
// float cosHorizontal = (float)Math.Cos(horizontalRad);
// float sinHorizontal = (float)Math.Sin(horizontalRad);
// float x = this.distance * sinVertical * sinHorizontal;
// float y = this.distance * cosVertical;
// float z = this.distance * sinVertical * cosHorizontal;
// Vector3Float v = new(x, y, z);
Vector3Float v = this.direction.ToVector3();
v *= this.distance;
return v;
}
#endif
public override readonly string ToString() {
return $"Spherical({this.distance}, h: {this.direction.horizontal}, v: {this.direction.vertical})";
}
public readonly float magnitude => this.distance;
public Spherical normalized {
get {
Spherical r = new() {
distance = 1,
direction = this.direction
};
return r;
}
}
public static Spherical operator +(Spherical s1, Spherical s2) {
// let's do it the easy way...
// using vars to be compatible with both unity (Vector3) and native (Vector3Float)
var v1 = s1.ToVector3();
var v2 = s2.ToVector3();
var v = v1 + v2;
Spherical r = FromVector3(v);
return r;
}
public static Spherical operator *(Spherical v, float d) {
Spherical r = new(v.distance * d, v.direction);
return r;
}
public static bool operator ==(Spherical v1, Spherical v2) {
return (v1.distance == v2.distance && v1.direction == v2.direction);
}
public static bool operator !=(Spherical v1, Spherical v2) {
return (v1.distance != v2.distance || v1.direction != v2.direction);
}
public override readonly bool Equals(object o) {
if (o is Spherical s)
return this == s;
return false;
}
public override readonly int GetHashCode() {
return HashCode.Combine(this.distance, this.direction);
}
public static float Distance(Spherical v1, Spherical v2) {
// Convert degrees to radians
float thetaARadians = v1.direction.horizontal.inRadians;
float phiARadians = v1.direction.vertical.inRadians;// DegreesToRadians(phiA);
float thetaBRadians = v2.direction.horizontal.inRadians; // DegreesToRadians(thetaB);
float phiBRadians = v2.direction.vertical.inRadians; // DegreesToRadians(phiB);
// Calculate sine and cosine values
float sinPhiA = MathF.Sin(phiARadians);
float cosPhiA = MathF.Cos(phiARadians);
float sinPhiB = MathF.Sin(phiBRadians);
float cosPhiB = MathF.Cos(phiBRadians);
// Calculate the cosine of the difference in azimuthal angles
float cosThetaDifference = MathF.Cos(thetaARadians - thetaBRadians);
// Apply the spherical law of cosines
float distance = MathF.Sqrt(
v1.distance * v1.distance +
v2.distance * v2.distance -
2 * v1.distance * v2.distance * (sinPhiA * sinPhiB * cosThetaDifference + cosPhiA * cosPhiB)
);
return distance;
}
public static Spherical Average(Spherical v1, Spherical v2) {
const float EPS = 1e-6f;
// Angles in radians
float a1 = v1.direction.horizontal.inRadians;
float a2 = v2.direction.horizontal.inRadians;
float e1 = v1.direction.vertical.inRadians;
float e2 = v2.direction.vertical.inRadians;
// Fast path: exactly same direction (allowing wrap for azimuth) -> preserve exact angles
bool sameAz = MathF.Abs(MathF.IEEERemainder(a1 - a2, MathF.PI * 2f)) < EPS;
bool sameEl = MathF.Abs(e1 - e2) < EPS;
if (sameAz && sameEl) {
// Distances may differ; average distance but keep exact angles from v1
float rAvgExact = 0.5f * (v1.distance + v2.distance);
return new Spherical(rAvgExact, v1.direction);
}
// Horizontal unit-circle sum
float cx = MathF.Cos(a1) + MathF.Cos(a2);
float cy = MathF.Sin(a1) + MathF.Sin(a2);
// Vertical as z = sin(el)
float z1 = MathF.Sin(e1);
float z2 = MathF.Sin(e2);
float cz = z1 + z2;
// Magnitude of summed unit-direction vectors
float sumX = cx;
float sumY = cy;
float sumZ = cz;
float magSum = MathF.Sqrt(sumX * sumX + sumY * sumY + sumZ * sumZ);
// If the two direction unit-vectors cancel (or nearly), return zero distance.
if (magSum < EPS) {
return Spherical.Radians(0f, 0f, 0f);
}
// Normalized averaged direction components
float ux = sumX / magSum;
float uy = sumY / magSum;
float uz = sumZ / magSum;
// Compute averaged angles from normalized vector
float azAvgRad = MathF.Atan2(uy, ux);
float elAvgRad = MathF.Asin(Float.Clamp(uz, -1f, 1f));
// Average distance (arithmetic mean)
float rAvg = 0.5f * (v1.distance + v2.distance);
return Spherical.Radians(rAvg, azAvgRad, elAvgRad);
}
public static Spherical Sum(List<Spherical> vectors) {
if (vectors == null || vectors.Count == 0)
throw new ArgumentException("vectors must contain at least one element", nameof(vectors));
#if UNITY_5_3_OR_NEWER
Vector3 sum = Vector3.zero;
#else
Vector3Float sum = Vector3Float.zero;
#endif
foreach (Spherical v in vectors)
sum += v.ToVector3();
return FromVector3(sum);
}
public static Spherical Average(List<Spherical> vectors) {
if (vectors == null || vectors.Count == 0)
throw new ArgumentException("vectors must contain at least one element", nameof(vectors));
#if UNITY_5_3_OR_NEWER
Vector3 sum = Vector3.zero;
#else
Vector3Float sum = Vector3Float.zero;
#endif
int n = 0;
foreach (Spherical v in vectors) {
sum += v.ToVector3();
n++;
}
var avg = sum / n;
// if (avg.sqrMagnitude == 0f)
// return new Spherical(0f, new Direction(AngleFloat.Radians(0f), AngleFloat.Radians(0f)));
// else
return FromVector3(avg);
}
}
}

View File

@ -0,0 +1,136 @@
// #if !UNITY_5_3_OR_NEWER
// using UnityEngine;
// #endif
namespace LinearAlgebra {
/// <summary>
/// An orientation using swing and twist angles
/// </summary>
/// <param name="swing">The swing rotation</param>
/// <param name="twist">The twist rotation</param>
public struct SwingTwist {
public Direction swing;
public AngleFloat twist;
public SwingTwist(Direction swing, AngleFloat twist) {
this.swing = swing;
this.twist = twist;
}
/// <summary>
/// Create a swing/twist rotation using angles in degrees
/// </summary>
/// <param name="horizontalSwing">The swing angle in the horizontal plane in degrees</param>
/// <param name="verticalSwing">The swing angle in the vertical plan in degrees</param>
/// <param name="twist">The twist angle in degrees</param>
/// <returns>The swing/twist rotation</returns>
public static SwingTwist Degrees(float horizontalSwing, float verticalSwing, float twist) {
Direction swing = Direction.Degrees(horizontalSwing, verticalSwing);
AngleFloat twistAngle = AngleFloat.Degrees(twist);
SwingTwist s = new(swing, twistAngle);
return s;
}
/// <summary>
/// Create a swing/twist rotation using angles in degrees
/// </summary>
/// <param name="horizontalSwing">The swing angle in the horizontal plane in degrees</param>
/// <param name="verticalSwing">The swing angle in the vertical plan in degrees</param>
/// <param name="twist">The twist angle in degrees</param>
/// <returns>The swing/twist rotation</returns>
public static SwingTwist Radians(float horizontalSwing, float verticalSwing, float twist) {
Direction swing = Direction.Radians(horizontalSwing, verticalSwing);
AngleFloat twistAngle = AngleFloat.Radians(twist);
SwingTwist s = new(swing, twistAngle);
return s;
}
#if UNITY_5_3_OR_NEWER
/// <summary>
/// A zero angle rotation
/// </summary>
public static readonly SwingTwist zero = Degrees(0, 0, 0);
public Spherical ToAngleAxis() {
UnityEngine.Quaternion q = this.ToQuaternion();
q.ToAngleAxis(out float angle, out UnityEngine.Vector3 axis);
Direction direction = Direction.FromVector3(axis);
Spherical r = new(angle, direction);
return r;
}
public static SwingTwist FromAngleAxis(Spherical r) {
UnityEngine.Vector3 vectorAxis = r.direction.ToVector3();
UnityEngine.Quaternion q = UnityEngine.Quaternion.AngleAxis(r.distance, vectorAxis);
return FromQuaternion(q);
}
/// <summary>
/// Convert a quaternion in a swing/twist rotation
/// </summary>
/// <param name="q">The quaternion to convert</param>
/// <returns>The swing/twist rotation</returns>
public static SwingTwist FromQuaternion(UnityEngine.Quaternion q) {
UnityEngine.Vector3 angles = q.eulerAngles;
SwingTwist r = Degrees(angles.y, -angles.x, -angles.z);
return r;
}
public UnityEngine.Quaternion ToQuaternion() {
UnityEngine.Quaternion q = UnityEngine.Quaternion.Euler(this.swing.vertical.inDegrees,
this.swing.horizontal.inDegrees,
this.twist.inDegrees);
return q;
}
#else
/// <summary>
/// A zero angle rotation
/// </summary>
public static readonly SwingTwist zero = Degrees(0, 0, 0);
public Spherical ToAngleAxis() {
LinearAlgebra.Quaternion q = this.ToQuaternion();
q.ToAngleAxis(out float angle, out Vector3Float axis);
Direction direction = Direction.FromVector3(axis);
Spherical r = new(angle, direction);
return r;
}
public static SwingTwist FromAngleAxis(Spherical r) {
Vector3Float vectorAxis = r.direction.ToVector3();
LinearAlgebra.Quaternion q = LinearAlgebra.Quaternion.AngleAxis(AngleFloat.Degrees(r.distance), vectorAxis);
return FromQuaternion(q);
}
/// <summary>
/// Convert a quaternion in a swing/twist rotation
/// </summary>
/// <param name="q">The quaternion to convert</param>
/// <returns>The swing/twist rotation</returns>
public static SwingTwist FromQuaternion(LinearAlgebra.Quaternion q) {
Vector3Float v = LinearAlgebra.Quaternion.ToAngles(q);
SwingTwist r = Degrees(v.vertical, v.horizontal, v.depth);
return r;
}
public LinearAlgebra.Quaternion ToQuaternion() {
LinearAlgebra.Quaternion q = LinearAlgebra.Quaternion.Euler(this.swing.vertical.inDegrees,
this.swing.horizontal.inDegrees,
this.twist.inDegrees);
return q;
}
public static SwingTwist FromQuat32(Quat32 q32) {
q32.ToAngles(out float right, out float up, out float forward);
SwingTwist r = Degrees(up, right, forward);
return r;
}
#endif
}
}

View File

@ -0,0 +1,479 @@
using System;
using System.Numerics;
namespace LinearAlgebra {
/*
public struct Vector2Int {
public int horizontal;
public int vertical;
public Vector2Int(int horizontal, int vertical) {
this.horizontal = horizontal;
this.vertical = vertical;
}
/// <summary>
/// A vector with zero for all axis
/// </summary>
public static readonly Vector2Int zero = new(0, 0);
/// <summary>
/// A vector with values (1, 1)
/// </summary>
public static readonly Vector2Int one = new(1, 1);
/// <summary>
/// A vector with values (0, 1)
/// </summary>
public static readonly Vector2Int up = new(0, 1);
/// <summary>
/// A vector with values (0, -1)
/// </summary>
public static readonly Vector2Int down = new(0, -1);
/// <summary>
/// A vector with values (0, 1)
/// </summary>
public static readonly Vector2Int forward = new(0, 1);
/// <summary>
/// A vector with values (0, -1)
/// </summary>
public static readonly Vector2Int back = new(0, -1);
/// <summary>
/// A vector3 with values (-1, 0)
/// </summary>
public static readonly Vector2Int left = new(-1, 0);
/// <summary>
/// A vector with values (1, 0)
/// </summary>
public static readonly Vector2Int right = new(1, 0);
/// <summary>
/// Tests if the vector has equal values as the given vector
/// </summary>
/// <param name="v1">The vector to compare to</param>
/// <returns><em>true</em> if the vector values are equal</returns>
public readonly bool Equals(Vector2Int v) => this.horizontal == v.horizontal && vertical == v.vertical;
/// <summary>
/// Tests if the vector is equal to the given object
/// </summary>
/// <param name="obj">The object to compare to</param>
/// <returns><em>false</em> when the object is not a Vector2 or does not have equal values</returns>
public override readonly bool Equals(object obj) {
if (obj is not Vector2Int v)
return false;
return (this.horizontal == v.horizontal && this.vertical == v.vertical);
}
/// <summary>
/// Tests if the two vectors have equal values
/// </summary>
/// <param name="v1">The first vector</param>
/// <param name="v2">The second vector</param>
/// <returns><em>true</em>when the vectors have equal values</returns>
/// Note that this uses a Float equality check which cannot be not exact in all cases.
/// In most cases it is better to check if the Vector2.Distance between the vectors is smaller than Float.epsilon
/// Or more efficient: (v1 - v2).sqrMagnitude < Float.sqrEpsilon
public static bool operator ==(Vector2Int v1, Vector2Int v2) {
return (v1.horizontal == v2.horizontal && v1.vertical == v2.vertical);
}
/// <summary>
/// Tests if two vectors have different values
/// </summary>
/// <param name="v1">The first vector</param>
/// <param name="v2">The second vector</param>
/// <returns><em>true</em>when the vectors have different values</returns>
/// Note that this uses a Float equality check which cannot be not exact in all case.
/// In most cases it is better to check if the Vector2.Distance between the vectors is smaller than Float.epsilon.
/// Or more efficient: (v1 - v2).sqrMagnitude < Float.sqrEpsilon
public static bool operator !=(Vector2Int v1, Vector2Int v2) {
return (v1.horizontal != v2.horizontal || v1.vertical != v2.vertical);
}
public readonly float magnitude {
get {
int h = this.horizontal;
int v = this.vertical;
return MathF.Sqrt(h * h + v * v);
}
}
public static float MagnitudeOf(Vector2Int v) {
return v.magnitude;
}
public static Vector2Int operator -(Vector2Int v1, Vector2Int v2) {
return new Vector2Int(v1.horizontal - v2.horizontal, v1.vertical - v2.vertical);
}
public static Vector2Int operator +(Vector2Int v1, Vector2Int v2) {
return new Vector2Int(v1.horizontal + v2.horizontal, v1.vertical + v2.vertical);
}
public static float Distance(Vector2Int v1, Vector2Int v2) {
return (v1 - v2).magnitude;
}
}
public struct Vector2Float {
public float horizontal;
public float vertical;
public Vector2Float(float horizontal, float vertical) {
this.horizontal = horizontal;
this.vertical = vertical;
}
public readonly float magnitude {
get {
float h = this.horizontal;
float v = this.vertical;
return MathF.Sqrt(h * h + v * v);
}
}
public static Vector2Float operator -(Vector2Float v1, Vector2Float v2) {
return new Vector2Float(v1.horizontal - v2.horizontal, v1.vertical - v2.vertical);
}
public static float Distance(Vector2Float v1, Vector2Float v2) {
return (v1 - v2).magnitude;
}
}
*/
/// <summary>
/// 2-dimensional vectors
/// </summary>
public struct Vector2Float {
/// <summary>
/// The right axis of the vector
/// </summary>
public float horizontal; // left/right
/// <summary>
/// The upward/forward axis of the vector
/// </summary>
public float vertical; // forward/backward
// directions are to be inline with Vector3 as much as possible...
/// <summary>
/// Create a new 2-dimensional vector
/// </summary>
/// <param name="x">x axis value</param>
/// <param name="y">y axis value</param>
public Vector2Float(float x, float y) {
this.horizontal = x;
this.vertical = y;
}
/// <summary>
/// Convert a Vector2Int into a Vector2Float
/// </summary>
/// <param name="v">The Vector2Int</param>
public Vector2Float(Vector2Int v) {
this.horizontal = v.horizontal;
this.vertical = v.vertical;
}
/// <summary>
/// A vector with zero for all axis
/// </summary>
public static readonly Vector2Float zero = new Vector2Float(0, 0);
/// <summary>
/// A vector with values (1, 1)
/// </summary>
public static readonly Vector2Float one = new Vector2Float(1, 1);
/// <summary>
/// A vector with values (0, 1)
/// </summary>
public static readonly Vector2Float up = new Vector2Float(0, 1);
/// <summary>
/// A vector with values (0, -1)
/// </summary>
public static readonly Vector2Float down = new Vector2Float(0, -1);
/// <summary>
/// A vector with values (0, 1)
/// </summary>
public static readonly Vector2Float forward = new Vector2Float(0, 1);
/// <summary>
/// A vector with values (0, -1)
/// </summary>
public static readonly Vector2Float back = new Vector2Float(0, -1);
/// <summary>
/// A vector3 with values (-1, 0)
/// </summary>
public static readonly Vector2Float left = new Vector2Float(-1, 0);
/// <summary>
/// A vector with values (1, 0)
/// </summary>
public static readonly Vector2Float right = new Vector2Float(1, 0);
/// <summary>
/// The squared length of this vector
/// </summary>
/// <returns>The squared length</returns>
/// The squared length is computationally simpler than the real length.
/// Think of Pythagoras A^2 + B^2 = C^2.
/// This leaves out the calculation of the squared root of C.
public readonly float sqrMagnitude => horizontal * horizontal + vertical * vertical;
public static float SqrMagnitudeOf(Vector2Float v) {
return v.sqrMagnitude;
}
/// <summary>
/// The length of this vector
/// </summary>
/// <returns>The length of this vector</returns>
public readonly float magnitude => MathF.Sqrt(horizontal * horizontal + vertical * vertical);
public static float MagnitudeOf(Vector2Float v) {
return v.magnitude;
}
/// <summary>
/// Convert the vector to a length of a 1
/// </summary>
/// <returns>The vector with length 1</returns>
public Vector2Float normalized {
get {
float l = magnitude;
Vector2Float v = zero;
if (l > Float.epsilon)
v = this / l;
return v;
}
}
public static Vector2Float Normalize(Vector2Float v) {
return v.normalized;
}
/// <summary>
/// Add two vectors
/// </summary>
/// <param name="v1">The first vector</param>
/// <param name="v2">The second vector</param>
/// <returns>The result of adding the two vectors</returns>
public static Vector2Float operator +(Vector2Float v1, Vector2Float v2) {
Vector2Float v = new Vector2Float(v1.horizontal + v2.horizontal, v1.vertical + v2.vertical);
return v;
}
/// <summary>
/// Subtract two vectors
/// </summary>
/// <param name="v1">The first vector</param>
/// <param name="v2">The second vector</param>
/// <returns>The result of adding the two vectors</returns>
public static Vector2Float operator -(Vector2Float v1, Vector2Float v2) {
Vector2Float v = new Vector2Float(v1.horizontal - v2.horizontal, v1.vertical - v2.vertical);
return v;
}
/// <summary>
/// Negate the vector
/// </summary>
/// <param name="v1">The vector to negate</param>
/// <returns>The negated vector</returns>
/// This will result in a vector pointing in the opposite direction
public static Vector2Float operator -(Vector2Float v1) {
Vector2Float v = new Vector2Float(-v1.horizontal, -v1.vertical);
return v;
}
/// <summary>
/// Scale a vector uniformly down
/// </summary>
/// <param name="v">The vector to scale</param>
/// <param name="f">The scaling factor</param>
/// <returns>The scaled vector</returns>
/// Each component of the vector will be devided by the same factor.
public static Vector2Float operator /(Vector2Float v, float f) {
Vector2Float r = new(v.horizontal / f, v.vertical / f);
return r;
}
/// <summary>
/// Scale a vector uniformly up
/// </summary>
/// <param name="v1">The vector to scale</param>
/// <param name="f">The scaling factor</param>
/// <returns>The scaled vector</returns>
/// Each component of the vector will be multipled with the same factor.
public static Vector2Float operator *(Vector2Float v1, float f) {
Vector2Float v = new Vector2Float(v1.horizontal * f, v1.vertical * f);
return v;
}
/// <summary>
/// Scale a vector uniformly up
/// </summary>
/// <param name="f">The scaling factor</param>
/// <param name="v1">The vector to scale</param>
/// <returns>The scaled vector</returns>
/// Each component of the vector will be multipled with the same factor.
public static Vector2Float operator *(float f, Vector2Float v1) {
Vector2Float v = new Vector2Float(f * v1.horizontal, f * v1.vertical);
return v;
}
/// @brief Scale the vector using another vector
/// @param v1 The vector to scale
/// @param v2 A vector with the scaling factors
/// @return The scaled vector
/// @remark Each component of the vector v1 will be multiplied with the
/// matching component from the scaling vector v2.
public static Vector2Float Scale(Vector2Float v1, Vector2Float v2) {
return new Vector2Float(v1.horizontal * v2.horizontal, v1.vertical * v2.vertical);
}
/// <summary>
/// Tests if the vector has equal values as the given vector
/// </summary>
/// <param name="v1">The vector to compare to</param>
/// <returns><em>true</em> if the vector values are equal</returns>
//public readonly bool Equals(Vector2Float v1) => horizontal == v1.horizontal && vertical == v1.vertical;
/// <summary>
/// Tests if the two vectors have equal values
/// </summary>
/// <param name="v1">The first vector</param>
/// <param name="v2">The second vector</param>
/// <returns><em>true</em>when the vectors have equal values</returns>
/// Note that this uses a Float equality check which cannot be not exact in all cases.
/// In most cases it is better to check if the Vector2.Distance between the vectors is smaller than Float.epsilon
/// Or more efficient: (v1 - v2).sqrMagnitude < Float.sqrEpsilon
public static bool operator ==(Vector2Float v1, Vector2Float v2) {
return (v1.horizontal == v2.horizontal && v1.vertical == v2.vertical);
}
/// <summary>
/// Tests if two vectors have different values
/// </summary>
/// <param name="v1">The first vector</param>
/// <param name="v2">The second vector</param>
/// <returns><em>true</em>when the vectors have different values</returns>
/// Note that this uses a Float equality check which cannot be not exact in all case.
/// In most cases it is better to check if the Vector2.Distance between the vectors is smaller than Float.epsilon.
/// Or more efficient: (v1 - v2).sqrMagnitude < Float.sqrEpsilon
public static bool operator !=(Vector2Float v1, Vector2Float v2) {
return (v1.horizontal != v2.horizontal || v1.vertical != v2.vertical);
}
/// <summary>
/// Tests if the vector is equal to the given object
/// </summary>
/// <param name="obj">The object to compare to</param>
/// <returns><em>false</em> when the object is not a Vector2 or does not have equal values</returns>
public override readonly bool Equals(object obj) {
if (obj is not Vector2Float v)
return false;
return (horizontal == v.horizontal && vertical == v.vertical);
}
/// <summary>
/// Get an hash code for the vector
/// </summary>
/// <returns>The hash code</returns>
public override readonly int GetHashCode() {
return HashCode.Combine(horizontal, vertical);
}
/// <summary>
/// Get the distance between two vectors
/// </summary>
/// <param name="v1">The first vector</param>
/// <param name="v2">The second vector</param>
/// <returns>The distance between the two vectors</returns>
public static float Distance(Vector2Float v1, Vector2Float v2) {
float x = v1.horizontal - v2.horizontal;
float y = v1.vertical - v2.vertical;
float d = (float)Math.Sqrt(x * x + y * y);
return d;
}
/// <summary>
/// The dot product of two vectors
/// </summary>
/// <param name="v1">The first vector</param>
/// <param name="v2">The second vector</param>
/// <returns>The dot product of the two vectors</returns>
public static float Dot(Vector2Float v1, Vector2Float v2) {
return v1.horizontal * v2.horizontal + v1.vertical * v2.vertical;
}
/// <summary>
/// Calculate the signed angle between two vectors.
/// </summary>
/// <param name="from">The starting vector</param>
/// <param name="to">The ending vector</param>
/// <param name="axis">The axis to rotate around</param>
/// <returns>The signed angle in degrees</returns>
public static float SignedAngle(Vector2Float from, Vector2Float to) {
//float sign = Math.Sign(v1.y * v2.x - v1.x * v2.y);
//return Vector2.Angle(v1, v2) * sign;
float sqrMagFrom = from.sqrMagnitude;
float sqrMagTo = to.sqrMagnitude;
if (sqrMagFrom == 0 || sqrMagTo == 0)
return 0;
//if (!isfinite(sqrMagFrom) || !isfinite(sqrMagTo))
// return nanf("");
float angleFrom = (float)Math.Atan2(from.vertical, from.horizontal);
float angleTo = (float)Math.Atan2(to.vertical, to.horizontal);
return -(angleTo - angleFrom) * AngleFloat.Rad2Deg;
}
public static float UnsignedAngle(Vector2Float from, Vector2Float to) {
return MathF.Abs(SignedAngle(from, to));
}
/// <summary>
/// Rotates the vector with the given angle
/// </summary>
/// <param name="v1">The vector to rotate</param>
/// <param name="angle">The angle in degrees</param>
/// <returns></returns>
public static Vector2Float Rotate(Vector2Float v1, AngleFloat angle) {
float sin = (float)Math.Sin(angle.inRadians);
float cos = (float)Math.Cos(angle.inRadians);
// float sin = AngleFloat.Sin(angle);
// float cos = AngleFloat.Cos(angle);
float tx = v1.horizontal;
float ty = v1.vertical;
Vector2Float v = new Vector2Float() {
horizontal = (cos * tx) - (sin * ty),
vertical = (sin * tx) + (cos * ty)
};
return v;
}
/// <summary>
/// Lerp between two vectors
/// </summary>
/// <param name="v1">The from vector</param>
/// <param name="v2">The to vector</param>
/// <param name="f">The interpolation distance [0..1]</param>
/// <returns>The lerped vector</returns>
/// The factor f is unclamped. Value 0 matches the *v1* vector, Value 1
/// matches the *v2* vector Value -1 is *v1* vector minus the difference
/// between *v1* and *v2* etc.
public static Vector2Float Lerp(Vector2Float v1, Vector2Float v2, float f) {
Vector2Float v = v1 + (v2 - v1) * f;
return v;
}
/// <summary>
/// Map interval of angles between vectors [0..Pi] to interval [0..1]
/// </summary>
/// <param name="v1">The first vector</param>
/// <param name="v2">The second vector</param>
/// <returns>The resulting factor in interval [0..1]</returns>
/// Vectors a and b must be normalized
public static float ToFactor(Vector2Float v1, Vector2Float v2) {
return (1 - Vector2Float.Dot(v1, v2)) / 2;
}
}
}

View File

@ -0,0 +1,185 @@
using System;
namespace LinearAlgebra {
public struct Vector2Int {
public int horizontal;
public int vertical;
public Vector2Int(int horizontal, int vertical) {
this.horizontal = horizontal;
this.vertical = vertical;
}
/// <summary>
/// A vector with zero for all axis
/// </summary>
public static readonly Vector2Int zero = new(0, 0);
/// <summary>
/// A vector with values (1, 1)
/// </summary>
public static readonly Vector2Int one = new(1, 1);
/// <summary>
/// A vector with values (0, 1)
/// </summary>
public static readonly Vector2Int up = new(0, 1);
/// <summary>
/// A vector with values (0, -1)
/// </summary>
public static readonly Vector2Int down = new(0, -1);
/// <summary>
/// A vector with values (0, 1)
/// </summary>
public static readonly Vector2Int forward = new(0, 1);
/// <summary>
/// A vector with values (0, -1)
/// </summary>
public static readonly Vector2Int back = new(0, -1);
/// <summary>
/// A vector3 with values (-1, 0)
/// </summary>
public static readonly Vector2Int left = new(-1, 0);
/// <summary>
/// A vector with values (1, 0)
/// </summary>
public static readonly Vector2Int right = new(1, 0);
/*
/// <summary>
/// Get an hash code for the vector
/// </summary>
/// <returns>The hash code</returns>
public override int GetHashCode() {
return (this.horizontal, this.vertical).GetHashCode();
}
/// <summary>
/// Tests if the vector has equal values as the given vector
/// </summary>
/// <param name="v1">The vector to compare to</param>
/// <returns><em>true</em> if the vector values are equal</returns>
public readonly bool Equals(Vector2Int v) => this.horizontal == v.horizontal && vertical == v.vertical;
*/
/// <summary>
/// Tests if the two vectors have equal values
/// </summary>
/// <param name="v1">The first vector</param>
/// <param name="v2">The second vector</param>
/// <returns><em>true</em>when the vectors have equal values</returns>
/// Note that this uses a Float equality check which cannot be not exact in all cases.
/// In most cases it is better to check if the Vector2.Distance between the vectors is smaller than Float.epsilon
/// Or more efficient: (v1 - v2).sqrMagnitude < Float.sqrEpsilon
public static bool operator ==(Vector2Int v1, Vector2Int v2) {
return (v1.horizontal == v2.horizontal && v1.vertical == v2.vertical);
}
/// <summary>
/// Tests if two vectors have different values
/// </summary>
/// <param name="v1">The first vector</param>
/// <param name="v2">The second vector</param>
/// <returns><em>true</em>when the vectors have different values</returns>
/// Note that this uses a Float equality check which cannot be not exact in all case.
/// In most cases it is better to check if the Vector2.Distance between the vectors is smaller than Float.epsilon.
/// Or more efficient: (v1 - v2).sqrMagnitude < Float.sqrEpsilon
public static bool operator !=(Vector2Int v1, Vector2Int v2) {
return (v1.horizontal != v2.horizontal || v1.vertical != v2.vertical);
}
/// <summary>
/// Tests if the vector is equal to the given object
/// </summary>
/// <param name="obj">The object to compare to</param>
/// <returns><em>false</em> when the object is not a Vector2 or does not have equal values</returns>
public override readonly bool Equals(object obj) {
if (obj is not Vector2Int v)
return false;
return (this.horizontal == v.horizontal && this.vertical == v.vertical);
}
/// <summary>
/// Get an hash code for the vector
/// </summary>
/// <returns>The hash code</returns>
public override readonly int GetHashCode() {
return HashCode.Combine(horizontal, vertical);
}
public readonly float sqrMagnitude => this.horizontal * this.horizontal + this.vertical * this.vertical;
public static float SqrMagnitudeOf(Vector2Int v) {
return v.sqrMagnitude;
}
public readonly float magnitude =>
MathF.Sqrt(this.horizontal * this.horizontal + this.vertical * this.vertical);
public static float MagnitudeOf(Vector2Int v) {
return v.magnitude;
}
/// @brief Convert the vector to a length of 1
/// @return The vector normalized to a length of 1
public readonly Vector2Float normalized {
get {
float l = magnitude;
Vector2Float v = Vector2Float.zero;
if (l > Float.epsilon)
v = new Vector2Float(this) / l;
return v;
}
}
/// @brief Convert the vector to a length of 1
/// @param v The vector to convert
/// @return The vector normalized to a length of 1
public static Vector2Float Normalize(Vector2Int v) {
float num = v.magnitude;
Vector2Float result = Vector2Float.zero;
if (num > Float.epsilon)
result = new Vector2Float(v) / num;
return result;
}
public static Vector2Int operator -(Vector2Int v) {
return new Vector2Int(-v.horizontal, -v.vertical);
}
public static Vector2Int operator -(Vector2Int v1, Vector2Int v2) {
return new Vector2Int(v1.horizontal - v2.horizontal, v1.vertical - v2.vertical);
}
public static Vector2Int operator +(Vector2Int v1, Vector2Int v2) {
return new Vector2Int(v1.horizontal + v2.horizontal, v1.vertical + v2.vertical);
}
public static Vector2Int operator /(Vector2Int v, int f) {
return new Vector2Int(v.horizontal / f, v.vertical / f);
}
public static Vector2Int operator *(Vector2Int v1, int d) {
return new Vector2Int(v1.horizontal * d, v1.vertical * d);
}
public static Vector2Int operator *(int d, Vector2Int v1) {
return new Vector2Int(d * v1.horizontal, d * v1.vertical);
}
public static Vector2Int Scale(Vector2Int v1, Vector2Int v2) {
return new Vector2Int(v1.horizontal * v2.horizontal, v1.vertical * v2.vertical);
}
/// @brief The dot product of two vectors
/// @param v1 The first vector
/// @param v2 The second vector
/// @return The dot product of the two vectors
public static int Dot(Vector2Int v1, Vector2Int v2) {
return v1.horizontal * v2.horizontal + v1.vertical * v2.vertical;
}
public static float Distance(Vector2Int v1, Vector2Int v2) {
return (v1 - v2).magnitude;
}
}
}

View File

@ -0,0 +1,402 @@
//#if !UNITY_5_3_OR_NEWER
using System;
namespace LinearAlgebra {
/*
public struct Vector3Float {
public float horizontal;
public float vertical;
public float depth;
public Vector3Float(float horizontal, float vertical, float depth) {
this.horizontal = horizontal;
this.vertical = vertical;
this.depth = depth;
}
/// <summary>
/// A vector with zero for all axis
/// </summary>
public static readonly Vector3Float zero = new(0, 0, 0);
public readonly float magnitude {
get => (float)Math.Sqrt(this.horizontal * this.horizontal + this.vertical * this.vertical + this.depth * this.depth);
}
/// <summary>
/// Convert the vector to a length of a 1
/// </summary>
/// <returns>The vector with length 1</returns>
public readonly Vector3Float normalized {
get {
float l = magnitude;
Vector3Float v = zero;
if (l > Float.epsilon)
v = this / l;
return v;
}
}
public static Vector3Float operator *(Vector3Float v, float f) {
Vector3Float r = new(v.horizontal * f, v.vertical * f, v.depth * f);
return r;
}
public static Vector3Float operator /(Vector3Float v, float f) {
Vector3Float r = new(v.horizontal / f, v.vertical / f, v.depth / f);
return r;
}
public static float Dot(Vector3Float v1, Vector3Float v2) {
return v1.horizontal * v2.horizontal + v1.vertical * v2.vertical +
v1.depth * v2.depth;
}
const float epsilon = 1E-05f;
public static Vector3Float Project(Vector3Float v, Vector3Float n) {
float sqrMagnitude = Dot(n, n);
if (sqrMagnitude < epsilon)
return zero;
else {
float dot = Dot(v, n);
Vector3Float r = n * dot;
r /= sqrMagnitude;
return r;
}
}
}
*/
/// <summary>
/// 3-dimensional vectors
/// </summary>
/// This uses the right-handed coordinate system.
public struct Vector3Float {
/// <summary>
/// The right axis of the vector
/// </summary>
public float horizontal; //> left/right
/// <summary>
/// The upward axis of the vector
/// </summary>
public float vertical; //> up/down
/// <summary>
/// The forward axis of the vector
/// </summary>
public float depth; //> forward/backward
/// <summary>
/// Create a new 3-dimensional vector
/// </summary>
/// <param name="horizontal">x axis value</param>
/// <param name="vertical">y axis value</param>
/// <param name="depth">z axis value</param>
public Vector3Float(float horizontal, float vertical, float depth) {
this.horizontal = horizontal;
this.vertical = vertical;
this.depth = depth;
}
public Vector3Float(Vector3Int v) {
this.horizontal = v.horizontal;
this.vertical = v.vertical;
this.depth = v.depth;
}
public static Vector3Float FromSpherical(Spherical s) {
float verticalRad = (AngleFloat.deg90 - s.direction.vertical).inRadians;
float horizontalRad = s.direction.horizontal.inRadians;
float cosVertical = MathF.Cos(verticalRad);
float sinVertical = MathF.Sin(verticalRad);
float cosHorizontal = MathF.Cos(horizontalRad);
float sinHorizontal = MathF.Sin(horizontalRad);
float horizontal = s.distance * sinVertical * sinHorizontal;
float vertical = s.distance * cosVertical;
float depth = s.distance * sinVertical * cosHorizontal;
return new Vector3Float(horizontal, vertical, depth);
}
public override string ToString() {
return $"({this.horizontal}, {this.vertical}, {this.depth})";
}
/// <summary>
/// A vector with zero for all axis
/// </summary>
public static readonly Vector3Float zero = new Vector3Float(0, 0, 0);
/// <summary>
/// A vector with one for all axis
/// </summary>
public static readonly Vector3Float one = new Vector3Float(1, 1, 1);
/// <summary>
/// A Vector3Float with values (-1, 0, 0)
/// </summary>
public static readonly Vector3Float left = new Vector3Float(-1, 0, 0);
/// <summary>
/// A vector with values (1, 0, 0)
/// </summary>
public static readonly Vector3Float right = new Vector3Float(1, 0, 0);
/// <summary>
/// A vector with values (0, -1, 0)
/// </summary>
public static readonly Vector3Float down = new Vector3Float(0, -1, 0);
/// <summary>
/// A vector with values (0, 1, 0)
/// </summary>
public static readonly Vector3Float up = new Vector3Float(0, 1, 0);
/// <summary>
/// A vector with values (0, 0, -1)
/// </summary>
public static readonly Vector3Float back = new Vector3Float(0, -1, 0);
/// <summary>
/// A vector with values (0, 0, 1)
/// </summary>
public static readonly Vector3Float forward = new Vector3Float(0, 1, 0);
/// @brief The vector length
/// @return The vector length
public readonly float magnitude => MathF.Sqrt(horizontal * horizontal + vertical * vertical + depth * depth);
/// <summary>
/// The vector length
/// </summary>
/// <param name="v">The vector for which you need the length</param>
/// <returns>The vector length</returns>
public static float MagnitudeOf(Vector3Float v) {
return v.magnitude;
}
/// @brief The squared vector length
/// @return The squared vector length
/// @remark The squared length is computationally simpler than the real
/// length. Think of Pythagoras A^2 + B^2 = C^2. This leaves out the
/// calculation of the squared root of C.
public readonly float sqrMagnitude => (horizontal * horizontal + vertical * vertical + depth * depth);
/// <summary>
/// The squared vector length
/// </summary>
/// <param name="v">The vector for which you need the squared length</param>
/// <returns>The squared vector length</returns>
/// <remarks>The squared length is computationally simpler than the real
/// length. Think of Pythagoras A^2 + B^2 = C^2. This leaves out the
/// calculation of the squared root of C.</remarks>
public static float SqrMagnitudeOf(Vector3Float v) {
return v.sqrMagnitude;
}
/// @brief Convert the vector to a length of 1
/// @return The vector normalized to a length of 1
public readonly Vector3Float normalized {
get {
float l = magnitude;
Vector3Float v = zero;
if (l > Float.epsilon)
v = this / l;
return v;
}
}
/// @brief Convert the vector to a length of 1
/// @param v The vector to convert
/// @return The vector normalized to a length of 1
public static Vector3Float Normalize(Vector3Float v) {
float num = v.magnitude;
Vector3Float result = zero;
if (num > Float.epsilon)
result = v / num;
return result;
}
/// <summary>
/// Negate te vector such that it points in the opposite direction
/// </summary>
/// <param name="v1"></param>
/// <returns>The negated vector</returns>
public static Vector3Float operator -(Vector3Float v1) {
Vector3Float v = new(-v1.horizontal, -v1.vertical, -v1.depth);
return v;
}
/// <summary>
/// Subtract two vectors
/// </summary>
/// <param name="v1"></param>
/// <param name="v2"></param>
/// <returns>The result of the subtraction</returns>
public static Vector3Float operator -(Vector3Float v1, Vector3Float v2) {
Vector3Float v = new(v1.horizontal - v2.horizontal, v1.vertical - v2.vertical, v1.depth - v2.depth);
return v;
}
/// <summary>
/// Add two vectors
/// </summary>
/// <param name="v1"></param>
/// <param name="v2"></param>
/// <returns>The result of the addition</returns>
public static Vector3Float operator +(Vector3Float v1, Vector3Float v2) {
Vector3Float v = new(v1.horizontal + v2.horizontal, v1.vertical + v2.vertical, v1.depth + v2.depth);
return v;
}
/// @brief Scale the vector using another vector
/// @param v1 The vector to scale
/// @param v2 A vector with the scaling factors
/// @return The scaled vector
/// @remark Each component of the vector v1 will be multiplied with the
/// matching component from the scaling vector v2.
public static Vector3Float Scale(Vector3Float v1, Vector3Float v2) {
return new Vector3Float(v1.horizontal * v2.horizontal, v1.vertical * v2.vertical, v1.depth * v2.depth);
}
public static Vector3Float operator *(Vector3Float v1, float d) {
Vector3Float v = new(v1.horizontal * d, v1.vertical * d, v1.depth * d);
return v;
}
public static Vector3Float operator *(float d, Vector3Float v1) {
Vector3Float v = new(d * v1.horizontal, d * v1.vertical, d * v1.depth);
return v;
}
public static Vector3Float operator /(Vector3Float v1, float d) {
Vector3Float v = new(v1.horizontal / d, v1.vertical / d, v1.depth / d);
return v;
}
//public bool Equals(Vector3Float v) => (horizontal == v.horizontal && vertical == v.vertical && depth == v.depth);
public static bool operator ==(Vector3Float v1, Vector3Float v2) {
return (v1.horizontal == v2.horizontal && v1.vertical == v2.vertical && v1.depth == v2.depth);
}
public static bool operator !=(Vector3Float v1, Vector3Float v2) {
return (v1.horizontal != v2.horizontal || v1.vertical != v2.vertical || v1.depth != v2.depth);
}
public override readonly bool Equals(object obj) {
if (obj is not Vector3Float v)
return false;
return (horizontal == v.horizontal && vertical == v.vertical && depth == v.depth);
}
public override readonly int GetHashCode() {
return HashCode.Combine(horizontal, vertical, depth);
}
/// @brief The distance between two vectors
/// @param v1 The first vector
/// @param v2 The second vector
/// @return The distance between the two vectors
public static float Distance(Vector3Float v1, Vector3Float v2) {
return (v2 - v1).magnitude;
}
/// @brief The dot product of two vectors
/// @param v1 The first vector
/// @param v2 The second vector
/// @return The dot product of the two vectors
public static float Dot(Vector3Float v1, Vector3Float v2) {
return v1.horizontal * v2.horizontal + v1.vertical * v2.vertical + v1.depth * v2.depth;
}
/// @brief The cross product of two vectors
/// @param v1 The first vector
/// @param v2 The second vector
/// @return The cross product of the two vectors
public static Vector3Float Cross(Vector3Float v1, Vector3Float v2) {
return new Vector3Float(v1.vertical * v2.depth - v1.depth * v2.vertical, v1.depth * v2.horizontal - v1.horizontal * v2.depth,
v1.horizontal * v2.vertical - v1.vertical * v2.horizontal);
}
/// @brief Project the vector on another vector
/// @param v The vector to project
/// @param n The normal vecto to project on
/// @return The projected vector
public static Vector3Float Project(Vector3Float v, Vector3Float n) {
float sqrMagnitude = Dot(n, n);
if (sqrMagnitude < Float.epsilon)
return zero;
else {
float dot = Dot(v, n);
Vector3Float r = n * dot / sqrMagnitude;
return r;
}
}
/// @brief Project the vector on a plane defined by a normal orthogonal to the
/// plane.
/// @param v The vector to project
/// @param n The normal of the plane to project on
/// @return Teh projected vector
public static Vector3Float ProjectOnPlane(Vector3Float v, Vector3Float n) {
Vector3Float r = v - Project(v, n);
return r;
}
/// @brief The angle between two vectors
/// @param v1 The first vector
/// @param v2 The second vector
/// @return The angle between the two vectors
/// @remark This reterns an unsigned angle which is the shortest distance
/// between the two vectors. Use Vector3::SignedAngle if a signed angle is
/// needed.
public static AngleFloat UnsignedAngle(Vector3Float v1, Vector3Float v2) {
float denominator = MathF.Sqrt(v1.sqrMagnitude * v2.sqrMagnitude);
if (denominator < Float.epsilon)
return AngleFloat.zero;
float dot = Dot(v1, v2);
float fraction = dot / denominator;
if (float.IsNaN(fraction))
return AngleFloat.Degrees(
fraction); // short cut to returning NaN universally
float cdot = Float.Clamp(fraction, -1.0f, 1.0f);
float r = MathF.Acos(cdot);
return AngleFloat.Radians(r);
}
/// @brief The signed angle between two vectors
/// @param v1 The starting vector
/// @param v2 The ending vector
/// @param axis The axis to rotate around
/// @return The signed angle between the two vectors
public static AngleFloat SignedAngle(Vector3Float v1, Vector3Float v2,
Vector3Float axis) {
// angle in [0,180]
AngleFloat angle = UnsignedAngle(v1, v2);
Vector3Float cross = Cross(v1, v2);
float b = Dot(axis, cross);
float signd = b < 0 ? -1.0F : (b > 0 ? 1.0F : 0.0F);
// angle in [-179,180]
AngleFloat signed_angle = angle * signd;
return signed_angle;
}
/// @brief Lerp (linear interpolation) between two vectors
/// @param v1 The starting vector
/// @param v2 The ending vector
/// @param f The interpolation distance
/// @return The lerped vector
/// @remark The factor f is unclamped. Value 0 matches the vector *v1*, Value
/// 1 matches vector *v2*. Value -1 is vector *v1* minus the difference
/// between *v1* and *v2* etc.
public static Vector3Float Lerp(Vector3Float v1, Vector3Float v2, float f) {
Vector3Float v = v1 + (v2 - v1) * f;
return v;
}
}
}
//#endif

View File

@ -0,0 +1,273 @@
//#if !UNITY_5_3_OR_NEWER
using System;
namespace LinearAlgebra {
/// <summary>
/// 3-dimensional vectors
/// </summary>
/// This uses the right-handed coordinate system.
/// <remarks>
/// Create a new 3-dimensional vector
/// </remarks>
/// <param name="horizontal">x axis value</param>
/// <param name="vertical">y axis value</param>
/// <param name="depth">z axis value</param>
public struct Vector3Int {
/// <summary>
/// The right axis of the vector
/// </summary>
public int horizontal; //> left/right
/// <summary>
/// The upward axis of the vector
/// </summary>
public int vertical; //> up/down
/// <summary>
/// The forward axis of the vector
/// </summary>
public int depth; //> forward/backward
public Vector3Int(int horizontal, int vertical, int depth) {
this.horizontal = horizontal;
this.vertical = vertical;
this.depth = depth;
}
/// <summary>
/// A vector with zero for all axis
/// </summary>
public static readonly Vector3Int zero = new(0, 0, 0);
/// <summary>
/// A vector with one for all axis
/// </summary>
public static readonly Vector3Int one = new(1, 1, 1);
/// <summary>
/// A Vector3Int with values (-1, 0, 0)
/// </summary>
public static readonly Vector3Int left = new(-1, 0, 0);
/// <summary>
/// A vector with values (1, 0, 0)
/// </summary>
public static readonly Vector3Int right = new(1, 0, 0);
/// <summary>
/// A vector with values (0, -1, 0)
/// </summary>
public static readonly Vector3Int down = new(0, -1, 0);
/// <summary>
/// A vector with values (0, 1, 0)
/// </summary>
public static readonly Vector3Int up = new(0, 1, 0);
/// <summary>
/// A vector with values (0, 0, -1)
/// </summary>
public static readonly Vector3Int back = new(0, -1, 0);
/// <summary>
/// A vector with values (0, 0, 1)
/// </summary>
public static readonly Vector3Int forward = new(0, 1, 0);
/// @brief The vector length
/// @return The vector length
public readonly float magnitude => MathF.Sqrt(horizontal * horizontal + vertical * vertical + depth * depth);
/// <summary>
/// The vector length
/// </summary>
/// <param name="v">The vector for which you need the length</param>
/// <returns>The vector length</returns>
public static float MagnitudeOf(Vector3Int v) {
return v.magnitude;
}
/// @brief The squared vector length
/// @return The squared vector length
/// @remark The squared length is computationally simpler than the real
/// length. Think of Pythagoras A^2 + B^2 = C^2. This leaves out the
/// calculation of the squared root of C.
public readonly float sqrMagnitude => (horizontal * horizontal + vertical * vertical + depth * depth);
/// <summary>
/// The squared vector length
/// </summary>
/// <param name="v">The vector for which you need the squared length</param>
/// <returns>The squared vector length</returns>
/// <remarks>The squared length is computationally simpler than the real
/// length. Think of Pythagoras A^2 + B^2 = C^2. This leaves out the
/// calculation of the squared root of C.</remarks>
public static float SqrMagnitudeOf(Vector3Int v) {
return v.sqrMagnitude;
}
/// @brief Convert the vector to a length of 1
/// @return The vector normalized to a length of 1
public readonly Vector3Float normalized {
get {
float l = magnitude;
Vector3Float v = Vector3Float.zero;
if (l > Float.epsilon)
v = new Vector3Float(this) / l;
return v;
}
}
/// @brief Convert the vector to a length of 1
/// @param v The vector to convert
/// @return The vector normalized to a length of 1
public static Vector3Float Normalize(Vector3Int v) {
float num = v.magnitude;
Vector3Float result = Vector3Float.zero;
if (num > Float.epsilon)
result = new Vector3Float(v) / num;
return result;
}
/// <summary>
/// Negate te vector such that it points in the opposite direction
/// </summary>
/// <param name="v1"></param>
/// <returns>The negated vector</returns>
public static Vector3Int operator -(Vector3Int v1) {
Vector3Int v = new(-v1.horizontal, -v1.vertical, -v1.depth);
return v;
}
/// <summary>
/// Subtract two vectors
/// </summary>
/// <param name="v1"></param>
/// <param name="v2"></param>
/// <returns>The result of the subtraction</returns>
public static Vector3Int operator -(Vector3Int v1, Vector3Int v2) {
Vector3Int v = new(v1.horizontal - v2.horizontal, v1.vertical - v2.vertical, v1.depth - v2.depth);
return v;
}
/// <summary>
/// Add two vectors
/// </summary>
/// <param name="v1"></param>
/// <param name="v2"></param>
/// <returns>The result of the addition</returns>
public static Vector3Int operator +(Vector3Int v1, Vector3Int v2) {
Vector3Int v = new(v1.horizontal + v2.horizontal, v1.vertical + v2.vertical, v1.depth + v2.depth);
return v;
}
/// @brief Scale the vector using another vector
/// @param v1 The vector to scale
/// @param v2 A vector with the scaling factors
/// @return The scaled vector
/// @remark Each component of the vector v1 will be multiplied with the
/// matching component from the scaling vector v2.
public static Vector3Int Scale(Vector3Int v1, Vector3Int v2) {
return new Vector3Int(v1.horizontal * v2.horizontal, v1.vertical * v2.vertical, v1.depth * v2.depth);
}
public static Vector3Int operator *(Vector3Int v1, int d) {
Vector3Int v = new(v1.horizontal * d, v1.vertical * d, v1.depth * d);
return v;
}
public static Vector3Int operator *(int d, Vector3Int v1) {
Vector3Int v = new(d * v1.horizontal, d * v1.vertical, d * v1.depth);
return v;
}
public static Vector3Int operator /(Vector3Int v1, int d) {
Vector3Int v = new(v1.horizontal / d, v1.vertical / d, v1.depth / d);
return v;
}
public bool Equals(Vector3Int v) => (horizontal == v.horizontal && vertical == v.vertical && depth == v.depth);
public override bool Equals(object obj) {
if (!(obj is Vector3Int v))
return false;
return (horizontal == v.horizontal && vertical == v.vertical && depth == v.depth);
}
public static bool operator ==(Vector3Int v1, Vector3Int v2) {
return (v1.horizontal == v2.horizontal && v1.vertical == v2.vertical && v1.depth == v2.depth);
}
public static bool operator !=(Vector3Int v1, Vector3Int v2) {
return (v1.horizontal != v2.horizontal || v1.vertical != v2.vertical || v1.depth != v2.depth);
}
public override int GetHashCode() {
return (horizontal, vertical, depth).GetHashCode();
}
/// @brief The distance between two vectors
/// @param v1 The first vector
/// @param v2 The second vector
/// @return The distance between the two vectors
public static float Distance(Vector3Int v1, Vector3Int v2) {
return (v2 - v1).magnitude;
}
/// @brief The dot product of two vectors
/// @param v1 The first vector
/// @param v2 The second vector
/// @return The dot product of the two vectors
public static float Dot(Vector3Int v1, Vector3Int v2) {
return v1.horizontal * v2.horizontal + v1.vertical * v2.vertical + v1.depth * v2.depth;
}
/// @brief The cross product of two vectors
/// @param v1 The first vector
/// @param v2 The second vector
/// @return The cross product of the two vectors
public static Vector3Int Cross(Vector3Int v1, Vector3Int v2) {
return new Vector3Int(v1.vertical * v2.depth - v1.depth * v2.vertical, v1.depth * v2.horizontal - v1.horizontal * v2.depth,
v1.horizontal * v2.vertical - v1.vertical * v2.horizontal);
}
/// @brief The angle between two vectors
/// @param v1 The first vector
/// @param v2 The second vector
/// @return The angle between the two vectors
/// @remark This reterns an unsigned angle which is the shortest distance
/// between the two vectors. Use Vector3::SignedAngle if a signed angle is
/// needed.
public static AngleFloat UnsignedAngle(Vector3Int v1, Vector3Int v2) {
float denominator = MathF.Sqrt(v1.sqrMagnitude * v2.sqrMagnitude);
if (denominator < Float.epsilon)
return AngleFloat.zero;
float dot = Dot(v1, v2);
float fraction = dot / denominator;
if (float.IsNaN(fraction))
return AngleFloat.Degrees(
fraction); // short cut to returning NaN universally
float cdot = Float.Clamp(fraction, -1.0f, 1.0f);
float r = MathF.Acos(cdot);
return AngleFloat.Radians(r);
}
/// @brief The signed angle between two vectors
/// @param v1 The starting vector
/// @param v2 The ending vector
/// @param axis The axis to rotate around
/// @return The signed angle between the two vectors
public static AngleFloat SignedAngle(Vector3Int v1, Vector3Int v2,
Vector3Int axis) {
// angle in [0,180]
AngleFloat angle = UnsignedAngle(v1, v2);
Vector3Int cross = Cross(v1, v2);
float b = Dot(axis, cross);
float signd = b < 0 ? -1.0F : (b > 0 ? 1.0F : 0.0F);
// angle in [-179,180]
AngleFloat signed_angle = angle * signd;
return signed_angle;
}
}
}
//#endif

View File

@ -0,0 +1,322 @@
using System;
namespace LinearAlgebra {
public class float16 {
//
// FILE: float16.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.8
// PURPOSE: library for Float16s for Arduino
// URL: http://en.wikipedia.org/wiki/Half-precision_floating-point_format
ushort _value;
public float16() { _value = 0; }
public float16(float f) {
//_value = f32tof16(f);
_value = F32ToF16__(f);
}
public float toFloat() {
return f16tof32(_value);
}
public ushort GetBinary() { return _value; }
public void SetBinary(ushort value) { _value = value; }
//////////////////////////////////////////////////////////
//
// EQUALITIES
//
/*
bool float16::operator ==(const float16 &f) { return (_value == f._value); }
bool float16::operator !=(const float16 &f) { return (_value != f._value); }
bool float16::operator >(const float16 &f) {
if ((_value & 0x8000) && (f._value & 0x8000))
return _value < f._value;
if (_value & 0x8000)
return false;
if (f._value & 0x8000)
return true;
return _value > f._value;
}
bool float16::operator >=(const float16 &f) {
if ((_value & 0x8000) && (f._value & 0x8000))
return _value <= f._value;
if (_value & 0x8000)
return false;
if (f._value & 0x8000)
return true;
return _value >= f._value;
}
bool float16::operator <(const float16 &f) {
if ((_value & 0x8000) && (f._value & 0x8000))
return _value > f._value;
if (_value & 0x8000)
return true;
if (f._value & 0x8000)
return false;
return _value < f._value;
}
bool float16::operator <=(const float16 &f) {
if ((_value & 0x8000) && (f._value & 0x8000))
return _value >= f._value;
if (_value & 0x8000)
return true;
if (f._value & 0x8000)
return false;
return _value <= f._value;
}
//////////////////////////////////////////////////////////
//
// NEGATION
//
float16 float16::operator -() {
float16 f16;
f16.setBinary(_value ^ 0x8000);
return f16;
}
//////////////////////////////////////////////////////////
//
// MATH
//
float16 float16::operator +(const float16 &f) {
return float16(this->toDouble() + f.toDouble());
}
float16 float16::operator -(const float16 &f) {
return float16(this->toDouble() - f.toDouble());
}
float16 float16::operator *(const float16 &f) {
return float16(this->toDouble() * f.toDouble());
}
float16 float16::operator /(const float16 &f) {
return float16(this->toDouble() / f.toDouble());
}
float16 & float16::operator+=(const float16 &f) {
*this = this->toDouble() + f.toDouble();
return *this;
}
float16 & float16::operator-=(const float16 &f) {
*this = this->toDouble() - f.toDouble();
return *this;
}
float16 & float16::operator*=(const float16 &f) {
*this = this->toDouble() * f.toDouble();
return *this;
}
float16 & float16::operator/=(const float16 &f) {
*this = this->toDouble() / f.toDouble();
return *this;
}
//////////////////////////////////////////////////////////
//
// MATH HELPER FUNCTIONS
//
int float16::sign() {
if (_value & 0x8000)
return -1;
if (_value & 0xFFFF)
return 1;
return 0;
}
bool float16::isZero() { return ((_value & 0x7FFF) == 0x0000); }
bool float16::isNaN() {
if ((_value & 0x7C00) != 0x7C00)
return false;
if ((_value & 0x03FF) == 0x0000)
return false;
return true;
}
bool float16::isInf() { return ((_value == 0x7C00) || (_value == 0xFC00)); }
*/
//////////////////////////////////////////////////////////
//
// CORE CONVERSION
//
float f16tof32(ushort _value) {
//ushort sgn;
ushort man;
int exp;
float f;
//Debug.Log($"{_value}");
bool sgn = (_value & 0x8000) > 0;
exp = (_value & 0x7C00) >> 10;
man = (ushort)(_value & 0x03FF);
//Debug.Log($"{sgn} {exp} {man}");
// ZERO
if ((_value & 0x7FFF) == 0) {
return sgn ? -0 : 0;
}
// NAN & INF
if (exp == 0x001F) {
if (man == 0)
return sgn ? float.NegativeInfinity : float.PositiveInfinity; //-INFINITY : INFINITY;
else
return float.NaN; // NAN;
}
// SUBNORMAL/NORMAL
if (exp == 0)
f = 0;
else
f = 1;
// PROCESS MANTISSE
for (int i = 9; i >= 0; i--) {
f *= 2;
if ((man & (1 << i)) != 0)
f = f + 1;
}
//Debug.Log($"{f}");
f = f * (float)Math.Pow(2.0f, exp - 25);
if (exp == 0) {
f = f * (float)Math.Pow(2.0f, -13); // 5.96046447754e-8;
}
//Debug.Log($"{f}");
return sgn ? -f : f;
}
public static uint SingleToInt32Bits(float value) {
byte[] bytes = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes); // If the system is little-endian, reverse the byte order
return BitConverter.ToUInt32(bytes, 0);
}
public ushort F32ToF16__(float f) {
uint t = BitConverter.ToUInt32(BitConverter.GetBytes(f), 0);
ushort man = (ushort)((t & 0x007FFFFF) >> 12);
int exp = (int)((t & 0x7F800000) >> 23);
bool sgn = (t & 0x80000000) != 0;
// handle 0
if ((t & 0x7FFFFFFF) == 0) {
return sgn ? (ushort)0x8000 : (ushort)0x0000;
}
// denormalized float32 does not fit in float16
if (exp == 0x00) {
return sgn ? (ushort)0x8000 : (ushort)0x0000;
}
// handle infinity & NAN
if (exp == 0x00FF) {
if (man != 0)
return 0xFE00; // NAN
return sgn ? (ushort)0xFC00 : (ushort)0x7C00; // -INF : INF
}
// normal numbers
exp = exp - 127 + 15;
// overflow does not fit => INF
if (exp > 30) {
return sgn ? (ushort)0xFC00 : (ushort)0x7C00; // -INF : INF
}
// subnormal numbers
if (exp < -38) {
return sgn ? (ushort)0x8000 : (ushort)0x0000; // -0 or 0 ? just 0 ?
}
if (exp <= 0) // subnormal
{
man >>= (exp + 14);
// rounding
man++;
man >>= 1;
if (sgn)
return (ushort)(0x8000 | man);
return man;
}
// normal
// TODO rounding
exp <<= 10;
man++;
man >>= 1;
if (sgn)
return (ushort)(0x8000 | exp | man);
return (ushort)(exp | man);
}
//This function is faulty!!!!
ushort f32tof16(float f) {
//uint t = *(uint*)&f;
//uint t = (uint)BitConverter.SingleToInt32Bits(f);
uint t = SingleToInt32Bits(f);
// man bits = 10; but we keep 11 for rounding
ushort man = (ushort)((t & 0x007FFFFF) >> 12);
short exp = (short)((t & 0x7F800000) >> 23);
bool sgn = (t & 0x80000000) != 0;
// handle 0
if ((t & 0x7FFFFFFF) == 0) {
return sgn ? (ushort)0x8000 : (ushort)0x0000;
}
// denormalized float32 does not fit in float16
if (exp == 0x00) {
return sgn ? (ushort)0x8000 : (ushort)0x0000;
}
// handle infinity & NAN
if (exp == 0x00FF) {
if (man != 0)
return 0xFE00; // NAN
return sgn ? (ushort)0xFC00 : (ushort)0x7C00; // -INF : INF
}
// normal numbers
exp = (short)(exp - 127 + 15);
// overflow does not fit => INF
if (exp > 30) {
return sgn ? (ushort)0xFC00 : (ushort)0x7C00; // -INF : INF
}
// subnormal numbers
if (exp < -38) {
return sgn ? (ushort)0x8000 : (ushort)0x0000; // -0 or 0 ? just 0 ?
}
if (exp <= 0) // subnormal
{
man >>= (exp + 14);
// rounding
man++;
man >>= 1;
if (sgn)
return (ushort)(0x8000 | man);
return man;
}
// normal
// TODO rounding
exp <<= 10;
man++;
man >>= 1;
ushort uexp = (ushort)exp;
if (sgn)
return (ushort)(0x8000 | uexp | man);
return (ushort)(uexp | man);
}
// -- END OF FILE --
}
}

View File

@ -0,0 +1,501 @@
#if !UNITY_5_6_OR_NEWER
using System;
using System.Formats.Asn1;
using NUnit.Framework;
namespace LinearAlgebra.Test {
public class AngleTests {
[SetUp]
public void Setup() {
}
[Test]
public void Construct() {
// Degrees
float angle = 0.0f;
AngleFloat a = AngleFloat.Degrees(angle);
Assert.AreEqual(angle, a.inDegrees);
angle = -180.0f;
a = AngleFloat.Degrees(angle);
Assert.AreEqual(angle, a.inDegrees);
angle = 270.0f;
a = AngleFloat.Degrees(angle);
Assert.AreEqual(-90, a.inDegrees);
angle = -270.0f;
a = AngleFloat.Degrees(angle);
Assert.AreEqual(90, a.inDegrees);
// Radians
angle = 0.0f;
a = AngleFloat.Radians(angle);
Assert.AreEqual(angle, a.inRadians);
angle = (float)-Math.PI;
a = AngleFloat.Radians(angle);
Assert.AreEqual(angle, a.inRadians);
angle = (float)Math.PI * 1.5f;
a = AngleFloat.Radians(angle);
Assert.AreEqual(-Math.PI * 0.5f, a.inRadians, 1.0E-05F);
// Revolutions
angle = 0.0f;
a = AngleFloat.Revolutions(angle);
Assert.AreEqual(angle, a.inRevolutions);
angle = -0.5f;
a = AngleFloat.Revolutions(angle);
Assert.AreEqual(angle, a.inRevolutions);
angle = 0.75f;
a = AngleFloat.Revolutions(angle);
Assert.AreEqual(-0.25f, a.inRevolutions);
}
[Test]
public void Revolutions() {
AngleFloat a;
// Test zero
a = AngleFloat.Revolutions(0.0f);
Assert.AreEqual(0.0f, a.inRevolutions);
// Test positive values within range
a = AngleFloat.Revolutions(0.25f);
Assert.AreEqual(0.25f, a.inRevolutions);
a = AngleFloat.Revolutions(0.5f);
Assert.AreEqual(-0.5f, a.inRevolutions);
// Test negative values within range
a = AngleFloat.Revolutions(-0.25f);
Assert.AreEqual(-0.25f, a.inRevolutions);
a = AngleFloat.Revolutions(-0.5f);
Assert.AreEqual(-0.5f, a.inRevolutions);
// Test values outside range (positive)
a = AngleFloat.Revolutions(1.0f);
Assert.AreEqual(0.0f, a.inRevolutions);
a = AngleFloat.Revolutions(1.25f);
Assert.AreEqual(0.25f, a.inRevolutions);
a = AngleFloat.Revolutions(1.75f);
Assert.AreEqual(-0.25f, a.inRevolutions);
// Test values outside range (negative)
a = AngleFloat.Revolutions(-1.0f);
Assert.AreEqual(0.0f, a.inRevolutions);
a = AngleFloat.Revolutions(-1.25f);
Assert.AreEqual(-0.25f, a.inRevolutions);
a = AngleFloat.Revolutions(-1.75f);
Assert.AreEqual(0.25f, a.inRevolutions);
// Test infinity
a = AngleFloat.Revolutions(float.PositiveInfinity);
Assert.AreEqual(float.PositiveInfinity, a.inRevolutions);
a = AngleFloat.Revolutions(float.NegativeInfinity);
Assert.AreEqual(float.NegativeInfinity, a.inRevolutions);
}
[Test]
public void Equality() {
// Test equality operator
Assert.IsTrue(AngleFloat.Degrees(90) == AngleFloat.Degrees(90), "90 == 90");
Assert.IsFalse(AngleFloat.Degrees(90) == AngleFloat.Degrees(45), "90 == 45");
Assert.IsTrue(AngleFloat.Degrees(0) == AngleFloat.Degrees(0), "0 == 0");
Assert.IsTrue(AngleFloat.Degrees(-180) == AngleFloat.Degrees(-180), "-180 == -180");
// Test inequality operator
Assert.IsTrue(AngleFloat.Degrees(90) != AngleFloat.Degrees(45), "90 != 45");
Assert.IsFalse(AngleFloat.Degrees(90) != AngleFloat.Degrees(90), "90 != 90");
Assert.IsTrue(AngleFloat.Degrees(0) != AngleFloat.Degrees(1), "0 != 1");
// Test greater than operator
Assert.IsTrue(AngleFloat.Degrees(90) > AngleFloat.Degrees(45), "90 > 45");
Assert.IsFalse(AngleFloat.Degrees(45) > AngleFloat.Degrees(90), "45 > 90");
Assert.IsFalse(AngleFloat.Degrees(90) > AngleFloat.Degrees(90), "90 > 90");
// Test greater than or equal operator
Assert.IsTrue(AngleFloat.Degrees(90) >= AngleFloat.Degrees(45), "90 >= 45");
Assert.IsTrue(AngleFloat.Degrees(90) >= AngleFloat.Degrees(90), "90 >= 90");
Assert.IsFalse(AngleFloat.Degrees(45) >= AngleFloat.Degrees(90), "45 >= 90");
// Test less than operator
Assert.IsTrue(AngleFloat.Degrees(45) < AngleFloat.Degrees(90), "45 < 90");
Assert.IsFalse(AngleFloat.Degrees(90) < AngleFloat.Degrees(45), "90 < 45");
Assert.IsFalse(AngleFloat.Degrees(90) < AngleFloat.Degrees(90), "90 < 90");
// Test less than or equal operator
Assert.IsTrue(AngleFloat.Degrees(45) <= AngleFloat.Degrees(90), "45 <= 90");
Assert.IsTrue(AngleFloat.Degrees(90) <= AngleFloat.Degrees(90), "90 <= 90");
Assert.IsFalse(AngleFloat.Degrees(90) <= AngleFloat.Degrees(45), "90 <= 45");
}
// [Test]
// public void Normalize() {
// float r = 0;
// r = Angle.Normalize(90);
// Assert.AreEqual(r, 90, "Normalize 90");
// r = Angle.Normalize(-90);
// Assert.AreEqual(r, -90, "Normalize -90");
// r = Angle.Normalize(270);
// Assert.AreEqual(r, -90, "Normalize 270");
// r = Angle.Normalize(270 + 360);
// Assert.AreEqual(r, -90, "Normalize 270+360");
// r = Angle.Normalize(-270);
// Assert.AreEqual(r, 90, "Normalize -270");
// r = Angle.Normalize(-270 - 360);
// Assert.AreEqual(r, 90, "Normalize -270-360");
// r = Angle.Normalize(0);
// Assert.AreEqual(r, 0, "Normalize 0");
// r = Angle.Normalize(float.PositiveInfinity);
// Assert.AreEqual(r, float.PositiveInfinity, "Normalize INFINITY");
// r = Angle.Normalize(float.NegativeInfinity);
// Assert.AreEqual(r, float.NegativeInfinity, "Normalize INFINITY");
// }
[Test]
public void Clamp() {
float r = 0;
r = AngleFloat.Clamp(AngleFloat.Degrees(1), AngleFloat.Degrees(0), AngleFloat.Degrees(2));
Assert.AreEqual(1, r, "Clamp 1 0 2");
r = AngleFloat.Clamp(AngleFloat.Degrees(-1), AngleFloat.Degrees(0), AngleFloat.Degrees(2));
Assert.AreEqual(0, r, "Clamp -1 0 2");
r = AngleFloat.Clamp(AngleFloat.Degrees(3), AngleFloat.Degrees(0), AngleFloat.Degrees(2));
Assert.AreEqual(2, r, "Clamp 3 0 2");
r = AngleFloat.Clamp(AngleFloat.Degrees(1), AngleFloat.Degrees(0), AngleFloat.Degrees(0));
Assert.AreEqual(0, r, "Clamp 1 0 0");
r = AngleFloat.Clamp(AngleFloat.Degrees(0), AngleFloat.Degrees(0), AngleFloat.Degrees(0));
Assert.AreEqual(0, r, "Clamp 0 0 0");
r = AngleFloat.Clamp(AngleFloat.Degrees(0), AngleFloat.Degrees(1), AngleFloat.Degrees(-1));
Assert.AreEqual(1, r, "Clamp 0 1 -1");
r = AngleFloat.Clamp(AngleFloat.Degrees(1), AngleFloat.Degrees(0), AngleFloat.Degrees(float.PositiveInfinity));
Assert.AreEqual(1, r, "Clamp 1 0 INFINITY");
r = AngleFloat.Clamp(AngleFloat.Degrees(1), AngleFloat.Degrees(float.NegativeInfinity), AngleFloat.Degrees(1));
Assert.AreEqual(1, r, "Clamp 1 -INFINITY 1");
}
[Test]
public void Cos() {
// Test zero
Assert.AreEqual(1.0f, AngleFloat.Cos(AngleFloat.Degrees(0)), 1.0E-05F, "Cos(0°)");
// Test 90 degrees
Assert.AreEqual(0.0f, AngleFloat.Cos(AngleFloat.Degrees(90)), 1.0E-05F, "Cos(90°)");
// Test 180 degrees
Assert.AreEqual(-1.0f, AngleFloat.Cos(AngleFloat.Degrees(180)), 1.0E-05F, "Cos(180°)");
// Test 270 degrees
Assert.AreEqual(0.0f, AngleFloat.Cos(AngleFloat.Degrees(270)), 1.0E-05F, "Cos(270°)");
// Test 45 degrees
Assert.AreEqual(MathF.Sqrt(2) / 2, AngleFloat.Cos(AngleFloat.Degrees(45)), 1.0E-05F, "Cos(45°)");
// Test negative angle
Assert.AreEqual(1.0f, AngleFloat.Cos(AngleFloat.Degrees(-360)), 1.0E-05F, "Cos(-360°)");
// Test using radians
Assert.AreEqual(1.0f, AngleFloat.Cos(AngleFloat.Radians(0)), 1.0E-05F, "Cos(0 rad)");
Assert.AreEqual(0.0f, AngleFloat.Cos(AngleFloat.Radians((float)Math.PI / 2)), 1.0E-05F, "Cos(π/2)");
Assert.AreEqual(-1.0f, AngleFloat.Cos(AngleFloat.Radians((float)Math.PI)), 1.0E-05F, "Cos(π)");
}
[Test]
public void Sin() {
// Test zero
Assert.AreEqual(0.0f, AngleFloat.Sin(AngleFloat.Degrees(0)), 1.0E-05F, "Sin(0°)");
// Test 90 degrees
Assert.AreEqual(1.0f, AngleFloat.Sin(AngleFloat.Degrees(90)), 1.0E-05F, "Sin(90°)");
// Test 180 degrees
Assert.AreEqual(0.0f, AngleFloat.Sin(AngleFloat.Degrees(180)), 1.0E-05F, "Sin(180°)");
// Test 270 degrees
Assert.AreEqual(-1.0f, AngleFloat.Sin(AngleFloat.Degrees(270)), 1.0E-05F, "Sin(270°)");
// Test 45 degrees
Assert.AreEqual(MathF.Sqrt(2) / 2, AngleFloat.Sin(AngleFloat.Degrees(45)), 1.0E-05F, "Sin(45°)");
// Test negative angle
Assert.AreEqual(0.0f, AngleFloat.Sin(AngleFloat.Degrees(-360)), 1.0E-05F, "Sin(-360°)");
// Test using radians
Assert.AreEqual(0.0f, AngleFloat.Sin(AngleFloat.Radians(0)), 1.0E-05F, "Sin(0 rad)");
Assert.AreEqual(1.0f, AngleFloat.Sin(AngleFloat.Radians((float)Math.PI / 2)), 1.0E-05F, "Sin(π/2)");
Assert.AreEqual(0.0f, AngleFloat.Sin(AngleFloat.Radians((float)Math.PI)), 1.0E-05F, "Sin(π)");
}
[Test]
public void Tan() {
// Test zero
Assert.AreEqual(0.0f, AngleFloat.Tan(AngleFloat.Degrees(0)), 1.0E-05F, "Tan(0°)");
// Test 45 degrees
Assert.AreEqual(1.0f, AngleFloat.Tan(AngleFloat.Degrees(45)), 1.0E-05F, "Tan(45°)");
// Test -45 degrees
Assert.AreEqual(-1.0f, AngleFloat.Tan(AngleFloat.Degrees(-45)), 1.0E-05F, "Tan(-45°)");
// Test using radians
Assert.AreEqual(0.0f, AngleFloat.Tan(AngleFloat.Radians(0)), 1.0E-05F, "Tan(0 rad)");
Assert.AreEqual(1.0f, AngleFloat.Tan(AngleFloat.Radians((float)Math.PI / 4)), 1.0E-05F, "Tan(π/4)");
}
[Test]
public void Acos() {
// Test 1 (0 degrees)
Assert.AreEqual(0.0f, AngleFloat.Acos(1.0f).inRadians, 1.0E-05F, "Acos(1)");
// Test 0 (90 degrees or π/2 radians)
Assert.AreEqual((float)Math.PI / 2, AngleFloat.Acos(0.0f).inRadians, 1.0E-05F, "Acos(0)");
// Test -1 (-180 degrees or π radians)
Assert.AreEqual((float)-Math.PI, AngleFloat.Acos(-1.0f).inRadians, 1.0E-05F, "Acos(-1)");
// Test 0.5 (60 degrees or π/3 radians)
Assert.AreEqual((float)Math.PI / 3, AngleFloat.Acos(0.5f).inRadians, 1.0E-05F, "Acos(0.5)");
// Test sqrt(2)/2 (45 degrees or π/4 radians)
Assert.AreEqual((float)Math.PI / 4, AngleFloat.Acos(MathF.Sqrt(2) / 2).inRadians, 1.0E-05F, "Acos(√2/2)");
}
[Test]
public void Asin() {
// Test 0 (0 degrees)
Assert.AreEqual(0.0f, AngleFloat.Asin(0.0f).inRadians, 1.0E-05F, "Asin(0)");
// Test 1 (90 degrees or π/2 radians)
Assert.AreEqual((float)Math.PI / 2, AngleFloat.Asin(1.0f).inRadians, 1.0E-05F, "Asin(1)");
// Test -1 (-90 degrees or -π/2 radians)
Assert.AreEqual(-(float)Math.PI / 2, AngleFloat.Asin(-1.0f).inRadians, 1.0E-05F, "Asin(-1)");
// Test 0.5 (30 degrees or π/6 radians)
Assert.AreEqual((float)Math.PI / 6, AngleFloat.Asin(0.5f).inRadians, 1.0E-05F, "Asin(0.5)");
// Test sqrt(2)/2 (45 degrees or π/4 radians)
Assert.AreEqual((float)Math.PI / 4, AngleFloat.Asin(MathF.Sqrt(2) / 2).inRadians, 1.0E-05F, "Asin(√2/2)");
}
[Test]
public void Atan() {
// Test zero
Assert.AreEqual(0.0f, AngleFloat.Atan(0.0f).inRadians, 1.0E-05F, "Atan(0)");
// Test 1 (45 degrees or π/4 radians)
Assert.AreEqual((float)Math.PI / 4, AngleFloat.Atan(1.0f).inRadians, 1.0E-05F, "Atan(1)");
// Test -1 (-45 degrees or -π/4 radians)
Assert.AreEqual(-(float)Math.PI / 4, AngleFloat.Atan(-1.0f).inRadians, 1.0E-05F, "Atan(-1)");
// Test sqrt(3) (60 degrees or π/3 radians)
Assert.AreEqual((float)Math.PI / 3, AngleFloat.Atan(MathF.Sqrt(3)).inRadians, 1.0E-05F, "Atan(√3)");
// Test 1/sqrt(3) (30 degrees or π/6 radians)
Assert.AreEqual((float)Math.PI / 6, AngleFloat.Atan(1.0f / MathF.Sqrt(3)).inRadians, 1.0E-05F, "Atan(1/√3)");
// Test positive infinity
Assert.AreEqual((float)Math.PI / 2, AngleFloat.Atan(float.PositiveInfinity).inRadians, 1.0E-05F, "Atan(+∞)");
// Test negative infinity
Assert.AreEqual(-(float)Math.PI / 2, AngleFloat.Atan(float.NegativeInfinity).inRadians, 1.0E-05F, "Atan(-∞)");
}
[Test]
public void Atan2() {
// Test basic quadrant I
Assert.AreEqual((float)Math.PI / 4, AngleFloat.Atan2(1.0f, 1.0f).inRadians, 1.0E-05F, "Atan2(1, 1)");
// Test quadrant II
Assert.AreEqual(3 * (float)Math.PI / 4, AngleFloat.Atan2(1.0f, -1.0f).inRadians, 1.0E-05F, "Atan2(1, -1)");
// Test quadrant III
Assert.AreEqual(-(float)Math.PI * 0.75f, AngleFloat.Atan2(-1.0f, -1.0f).inRadians, 1.0E-05F, "Atan2(-1, -1)");
// Test quadrant IV
Assert.AreEqual(-(float)Math.PI / 4, AngleFloat.Atan2(-1.0f, 1.0f).inRadians, 1.0E-05F, "Atan2(-1, 1)");
// Test positive x-axis
Assert.AreEqual(0.0f, AngleFloat.Atan2(0.0f, 1.0f).inRadians, 1.0E-05F, "Atan2(0, 1)");
// Test positive y-axis
Assert.AreEqual((float)Math.PI / 2, AngleFloat.Atan2(1.0f, 0.0f).inRadians, 1.0E-05F, "Atan2(1, 0)");
// Test negative y-axis
Assert.AreEqual(-(float)Math.PI / 2, AngleFloat.Atan2(-1.0f, 0.0f).inRadians, 1.0E-05F, "Atan2(-1, 0)");
// Test origin
Assert.AreEqual(0.0f, AngleFloat.Atan2(0.0f, 0.0f).inRadians, 1.0E-05F, "Atan2(0, 0)");
// Test with different magnitudes
Assert.AreEqual((float)Math.PI / 3, AngleFloat.Atan2(MathF.Sqrt(3), 1.0f).inRadians, 1.0E-05F, "Atan2(√3, 1)");
// Test negative x-axis
Assert.AreEqual((float)-Math.PI, AngleFloat.Atan2(0.0f, -1.0f).inRadians, 1.0E-05F, "Atan2(0, -1)");
}
[Test]
public void Multiplication() {
AngleFloat r = AngleFloat.zero;
// Angle * float
r = AngleFloat.Degrees(90) * 2;
Assert.AreEqual(-180, r.inDegrees, "Multiply 90 * 2");
r = AngleFloat.Degrees(45) * 0.5f;
Assert.AreEqual(22.5f, r.inDegrees, "Multiply 45 * 0.5");
r = AngleFloat.Degrees(90) * 0;
Assert.AreEqual(0, r.inDegrees, "Multiply 90 * 0");
r = AngleFloat.Degrees(-90) * 2;
Assert.AreEqual(-180, r.inDegrees, "Multiply -90 * 2");
r = AngleFloat.Degrees(270) * 2;
Assert.AreEqual(-180, r.inDegrees, "Multiply 270 * 2 (normalized)");
// float * Angle
r = 2 * AngleFloat.Degrees(90);
Assert.AreEqual(-180, r.inDegrees, "Multiply 2 * 90");
r = 0.5f * AngleFloat.Degrees(45);
Assert.AreEqual(22.5, r.inDegrees, "Multiply 0.5 * 45");
r = 0 * AngleFloat.Degrees(90);
Assert.AreEqual(0, r.inDegrees, "Multiply 0 * 90");
r = 2 * AngleFloat.Degrees(-90);
Assert.AreEqual(-180, r.inDegrees, "Multiply 2 * -90");
// Negative factor
r = AngleFloat.Degrees(90) * -1;
Assert.AreEqual(-90, r.inDegrees, "Multiply 90 * -1");
r = -1 * AngleFloat.Degrees(90);
Assert.AreEqual(-90, r.inDegrees, "Multiply -1 * 90");
}
[Test]
public void MoveTowards() {
AngleFloat r = AngleFloat.zero;
r = AngleFloat.MoveTowards(AngleFloat.Degrees(0), AngleFloat.Degrees(90), 30);
Assert.AreEqual(30, r.inDegrees, "MoveTowards 0 90 30");
r = AngleFloat.MoveTowards(AngleFloat.Degrees(0), AngleFloat.Degrees(90), 90);
Assert.AreEqual(90, r.inDegrees, "MoveTowards 0 90 90");
r = AngleFloat.MoveTowards(AngleFloat.Degrees(0), AngleFloat.Degrees(90), 180);
Assert.AreEqual(90, r.inDegrees, "MoveTowards 0 90 180");
r = AngleFloat.MoveTowards(AngleFloat.Degrees(0), AngleFloat.Degrees(90), 270);
Assert.AreEqual(90, r.inDegrees, "MoveTowrads 0 90 270");
r = AngleFloat.MoveTowards(AngleFloat.Degrees(0), AngleFloat.Degrees(90), -30);
Assert.AreEqual(0, r.inDegrees, "MoveTowards 0 90 -30");
r = AngleFloat.MoveTowards(AngleFloat.Degrees(0), AngleFloat.Degrees(-90), -30);
Assert.AreEqual(0, r.inDegrees, "MoveTowards 0 -90 -30");
r = AngleFloat.MoveTowards(AngleFloat.Degrees(0), AngleFloat.Degrees(-90), -90);
Assert.AreEqual(0, r.inDegrees, "MoveTowards 0 -90 -90");
r = AngleFloat.MoveTowards(AngleFloat.Degrees(0), AngleFloat.Degrees(-90), -180);
Assert.AreEqual(0, r.inDegrees, "MoveTowards 0 -90 -180");
r = AngleFloat.MoveTowards(AngleFloat.Degrees(0), AngleFloat.Degrees(-90), -270);
Assert.AreEqual(0, r.inDegrees, "MoveTowrads 0 -90 -270");
r = AngleFloat.MoveTowards(AngleFloat.Degrees(0), AngleFloat.Degrees(90), 0);
Assert.AreEqual(0, r.inDegrees, "MoveTowards 0 90 0");
r = AngleFloat.MoveTowards(AngleFloat.Degrees(0), AngleFloat.Degrees(0), 0);
Assert.AreEqual(0, r.inDegrees, "MoveTowards 0 0 0");
r = AngleFloat.MoveTowards(AngleFloat.Degrees(0), AngleFloat.Degrees(0), 30);
Assert.AreEqual(0, r.inDegrees, "MoveTowrads 0 0 30");
r = AngleFloat.MoveTowards(AngleFloat.Degrees(0), AngleFloat.Degrees(90), float.PositiveInfinity);
Assert.AreEqual(90, r.inDegrees, "MoveTowards 0 90 INFINITY");
r = AngleFloat.MoveTowards(AngleFloat.Degrees(0), AngleFloat.Degrees(float.PositiveInfinity), 30);
Assert.AreEqual(30, r.inDegrees, "MoveTowrads 0 INFINITY 30");
r = AngleFloat.MoveTowards(AngleFloat.Degrees(0), AngleFloat.Degrees(-90), float.NegativeInfinity);
Assert.AreEqual(0, r.inDegrees, "MoveTowards 0 -90 -INFINITY");
r = AngleFloat.MoveTowards(AngleFloat.Degrees(0), AngleFloat.Degrees(float.NegativeInfinity), -30);
Assert.AreEqual(0, r.inDegrees, "MoveTowrads 0 -INFINITY -30");
}
[Test]
public void Difference() {
float r = 0;
r = Angles.Difference(0, 90);
Assert.AreEqual(90, r, "Difference 0 90");
r = Angles.Difference(0, -90);
Assert.AreEqual(-90, r, "Difference 0 -90");
r = Angles.Difference(0, 270);
Assert.AreEqual(-90, r, "Difference 0 270");
r = Angles.Difference(0, -270);
Assert.AreEqual(90, r, "Difference 0 -270");
r = Angles.Difference(90, 0);
Assert.AreEqual(-90, r, "Difference 90 0");
r = Angles.Difference(-90, 0);
Assert.AreEqual(90, r, "Difference -90 0");
r = Angles.Difference(0, 0);
Assert.AreEqual(0, r, "Difference 0 0");
r = Angles.Difference(90, 90);
Assert.AreEqual(0, r, "Difference 90 90");
r = Angles.Difference(0, float.PositiveInfinity);
Assert.AreEqual(float.PositiveInfinity, r, "Difference 0 INFINITY");
r = Angles.Difference(0, float.NegativeInfinity);
Assert.AreEqual(float.NegativeInfinity, r, "Difference 0 -INFINITY");
r = Angles.Difference(float.NegativeInfinity, float.PositiveInfinity);
Assert.AreEqual(float.PositiveInfinity, r, "Difference -INFINITY INFINITY");
}
}
}
#endif

View File

@ -0,0 +1,226 @@
#if !UNITY_5_6_OR_NEWER
using System;
using NUnit.Framework;
namespace LinearAlgebra.Test {
public class DirectionTest {
[Test]
public void RadiansForward() {
Direction d = Direction.Radians(0, 0);
Assert.AreEqual(0, d.horizontal.inDegrees, 0.0001f);
Assert.AreEqual(0, d.vertical.inDegrees, 0.0001f);
}
[Test]
public void RadiansUp() {
Direction d = Direction.Radians(0, (float)Math.PI / 2);
Assert.AreEqual(0, d.horizontal.inDegrees, 0.0001f);
Assert.AreEqual(90, d.vertical.inDegrees, 0.0001f);
}
[Test]
public void RadiansDown() {
Direction d = Direction.Radians(0, -(float)Math.PI / 2);
Assert.AreEqual(0, d.horizontal.inDegrees, 0.0001f);
Assert.AreEqual(-90, d.vertical.inDegrees, 0.0001f);
}
[Test]
public void RadiansArbitrary() {
Direction d = Direction.Radians((float)Math.PI / 4, (float)Math.PI / 6);
Assert.AreEqual(45, d.horizontal.inDegrees, 0.0001f);
Assert.AreEqual(30, d.vertical.inDegrees, 0.0001f);
}
[Test]
public void DegreesNormalize1() {
Direction d = Direction.Degrees(112, 91);
Assert.AreEqual(-68, d.horizontal.inDegrees, 0.0001f);
Assert.AreEqual(89, d.vertical.inDegrees, 0.0001f);
}
[Test]
public void RadiansEquivalentToDegreesConversion() {
Direction d1 = Direction.Radians((float)Math.PI / 3, (float)Math.PI / 4);
Direction d2 = Direction.Degrees(60, 45);
Assert.AreEqual(d1.horizontal.inDegrees, d2.horizontal.inDegrees, 0.0001f);
Assert.AreEqual(d1.vertical.inDegrees, d2.vertical.inDegrees, 0.0001f);
}
[Test]
public void ToVector3Forward() {
Direction d = Direction.forward;
Vector3Float v = d.ToVector3();
Assert.AreEqual(0, v.horizontal, 0.0001f);
Assert.AreEqual(0, v.vertical, 0.0001f);
Assert.AreEqual(1, v.depth, 0.0001f);
}
[Test]
public void ToVector3Up() {
Direction d = Direction.up;
Vector3Float v = d.ToVector3();
Assert.AreEqual(0, v.horizontal, 0.0001f);
Assert.AreEqual(1, v.vertical, 0.0001f);
Assert.AreEqual(0, v.depth, 0.0001f);
}
[Test]
public void ToVector3Down() {
Direction d = Direction.down;
Vector3Float v = d.ToVector3();
Assert.AreEqual(0, v.horizontal, 0.0001f);
Assert.AreEqual(-1, v.vertical, 0.0001f);
Assert.AreEqual(0, v.depth, 0.0001f);
}
[Test]
public void ToVector3Left() {
Direction d = Direction.left;
Vector3Float v = d.ToVector3();
Assert.AreEqual(-1, v.horizontal, 0.0001f);
Assert.AreEqual(0, v.vertical, 0.0001f);
Assert.AreEqual(0, v.depth, 0.0001f);
}
[Test]
public void FromVector3Forward() {
Vector3Float v = new(0, 0, 1);
Direction d = Direction.FromVector3(v);
Assert.AreEqual(0, d.horizontal.inDegrees, 0.0001f);
Assert.AreEqual(0, d.vertical.inDegrees, 0.0001f);
}
[Test]
public void ToVector3AndBack() {
Direction d1 = Direction.Degrees(45, 30);
Vector3Float v = d1.ToVector3();
Direction d2 = Direction.FromVector3(v);
Assert.AreEqual(d1.horizontal.inDegrees, d2.horizontal.inDegrees, 0.0001f);
Assert.AreEqual(d1.vertical.inDegrees, d2.vertical.inDegrees, 0.0001f);
}
[Test]
public void ToVector3AndBack2() {
Direction d1 = Direction.Degrees(-135, 85);
Vector3Float v = d1.ToVector3();
Direction d2 = Direction.FromVector3(v);
Assert.AreEqual(d1.horizontal.inDegrees, d2.horizontal.inDegrees, 0.0001f);
Assert.AreEqual(d1.vertical.inDegrees, d2.vertical.inDegrees, 0.0001f);
}
[Test]
public void Compare() {
Direction d1 = Direction.Degrees(45, 135);
Direction d2 = new(AngleFloat.Degrees(45), AngleFloat.Degrees(135));
bool r;
r = d1 == d2;
Assert.True(r);
Assert.AreEqual(d1, d2);
}
[Test]
public void NotEqualWithDifferentHorizontal() {
Direction d1 = Direction.Degrees(45, 30);
Direction d2 = Direction.Degrees(90, 30);
Assert.True(d1 != d2);
}
[Test]
public void NotEqualWithDifferentVertical() {
Direction d1 = Direction.Degrees(45, 30);
Direction d2 = Direction.Degrees(45, 60);
Assert.True(d1 != d2);
}
[Test]
public void NotEqualWithDifferentBoth() {
Direction d1 = Direction.Degrees(45, 30);
Direction d2 = Direction.Degrees(90, 60);
Assert.True(d1 != d2);
}
[Test]
public void NotEqualWithSameValues() {
Direction d1 = Direction.Degrees(45, 30);
Direction d2 = Direction.Degrees(45, 30);
Assert.False(d1 != d2);
}
[Test]
public void EqualsWithSameValues() {
Direction d1 = Direction.Degrees(45, 30);
Direction d2 = Direction.Degrees(45, 30);
Assert.True(d1.Equals(d2));
}
[Test]
public void EqualsWithDifferentHorizontal() {
Direction d1 = Direction.Degrees(45, 30);
Direction d2 = Direction.Degrees(90, 30);
Assert.False(d1.Equals(d2));
}
[Test]
public void EqualsWithDifferentVertical() {
Direction d1 = Direction.Degrees(45, 30);
Direction d2 = Direction.Degrees(45, 60);
Assert.False(d1.Equals(d2));
}
[Test]
public void EqualsWithDifferentBoth() {
Direction d1 = Direction.Degrees(45, 30);
Direction d2 = Direction.Degrees(90, 60);
Assert.False(d1.Equals(d2));
}
[Test]
public void EqualsWithNonDirectionObject() {
Direction d = Direction.Degrees(45, 30);
Assert.False(d.Equals("not a direction"));
}
[Test]
public void EqualsWithNull() {
Direction d = Direction.Degrees(45, 30);
Assert.False(d.Equals(null));
}
[Test]
public void EqualsWithZeros() {
Direction d1 = Direction.forward;
Direction d2 = Direction.Degrees(0, 0);
Assert.True(d1.Equals(d2));
}
[Test]
public void HashCode() {
Direction d1 = Direction.Degrees(45, 30);
Direction d2 = Direction.Degrees(45, 30);
Assert.AreEqual(d1.GetHashCode(), d2.GetHashCode());
d1 = Direction.Degrees(45, 30);
d2 = Direction.Degrees(90, 30);
Assert.AreNotEqual(d1.GetHashCode(), d2.GetHashCode());
d1 = Direction.Degrees(45, 30);
d2 = Direction.Degrees(45, 60);
Assert.AreNotEqual(d1.GetHashCode(), d2.GetHashCode());
Direction d = Direction.Degrees(45, 30);
int hash1 = d.GetHashCode();
int hash2 = d.GetHashCode();
Assert.AreEqual(hash1, hash2);
d1 = Direction.forward;
d2 = Direction.Degrees(0, 0);
Assert.AreEqual(d1.GetHashCode(), d2.GetHashCode());
}
};
}
#endif

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\src\LinearAlgebra.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,185 @@
#if !UNITY_5_6_OR_NEWER
using NUnit.Framework;
namespace LinearAlgebra.Test {
public class QuaternionTest {
[SetUp]
public void Setup() {
}
[Test]
public void Normalize() {
Quaternion q1 = new(0, 0, 0, 1);
Quaternion r = Quaternion.identity;
r = q1.normalized;
Assert.AreEqual(r, q1, "q.normalized 0 0 0 1");
r = Quaternion.Normalize(q1);
Assert.AreEqual(r, q1, "q.normalized 0 0 0 1");
}
[Test]
public void ToAngles() {
Quaternion q1 = new(0, 0, 0, 1);
Vector3Float v = Vector3Float.zero;
v = Quaternion.ToAngles(q1);
Assert.AreEqual(v, new Vector3Float(0, 0, 0), "ToAngles 0 0 0 1");
q1 = new(1, 0, 0, 0);
v = Quaternion.ToAngles(q1);
Assert.AreEqual(0, v.horizontal, "1 0 0 0 H");
Assert.AreEqual(180, v.vertical, "1 0 0 0 V");
Assert.AreEqual(180, v.depth, "1 0 0 0 D");
}
[Test]
public void Multiplication() {
Quaternion q1 = new(0, 0, 0, 1);
Quaternion q2 = new(1, 0, 0, 0);
Quaternion r;
r = q1 * q2;
Assert.AreEqual(r, new Quaternion(1, 0, 0, 0), "0 0 0 1 * 1 0 0 0 ");
}
[Test]
public void MultiplicationVector() {
Quaternion q1 = new(0, 0, 0, 1);
Vector3Float v1 = new(0, 1, 0);
Vector3Float r;
r = q1 * v1;
Assert.AreEqual(r, new Vector3Float(0, 1, 0), "0 0 0 1 * Vector 0 1 0");
q1 = new(1, 0, 0, 0);
r = q1 * v1;
Assert.AreEqual(r, new Vector3Float(0, -1, 0), "1 0 0 0 * Vector 0 1 0");
}
[Test]
public void Equality() {
Quaternion q1 = new(0, 0, 0, 1);
Quaternion q2 = new(1, 0, 0, 0);
Assert.AreNotEqual(q1, q2, "0 0 0 1 == 1 0 0 0");
q2 = new(0, 0, 0, 1);
Assert.AreEqual(q1, q2, "0 0 0 1 == 1 0 0 0");
}
[Test, Ignore("ToDo")]
public void Inverse() { }
[Test, Ignore("ToDo")]
public void LookRotation() { }
[Test, Ignore("ToDo")]
public void FromToRotation() { }
[Test, Ignore("ToDo")]
public void RotateTowards() { }
[Test, Ignore("ToDo")]
public void AngleAxis() { }
[Test, Ignore("ToDo")]
public void Angle() { }
[Test, Ignore("ToDo")]
public void Slerp() { }
[Test, Ignore("ToDo")]
public void SlerpUnclamped() { }
[Test]
public void Euler() {
Vector3Float v1 = new(0, 0, 0);
Quaternion q;
q = Quaternion.Euler(v1);
Assert.AreEqual(q, Quaternion.identity, "Euler Vector 0 0 0");
q = Quaternion.Euler(0, 0, 0);
Assert.AreEqual(q, Quaternion.identity, "Euler 0 0 0");
v1 = new(90, 90, -90);
q = Quaternion.Euler(v1);
Assert.AreEqual(q, new Quaternion(0, 0.707106709F, -0.707106709F, 0), "Euler Vector 90 90 -90");
q = Quaternion.Euler(90, 90, -90);
Assert.AreEqual(q, new Quaternion(0, 0.707106709F, -0.707106709F, 0), "Euler 90 90 -90");
}
[Test]
public void EulerToAngles() {
Vector3Float v;
Quaternion q;
Quaternion r;
//v = new(0, 0, 0);
q = Quaternion.Euler(0, 0 , 0);
v = Quaternion.ToAngles(q);
r = Quaternion.Euler(v);
Assert.AreEqual(0, Quaternion.UnsignedAngle(q, r), 10e-2f, "0 0 0");
q = Quaternion.Euler(-45, -30, -15);
v = Quaternion.ToAngles(q);
r = Quaternion.Euler(v);
Assert.AreEqual(0, Quaternion.UnsignedAngle(q, r), 10e-2f, "-45, -30, -15");
// Gimball lock
// q = Quaternion.Euler(90, 90, -90);
// v = Quaternion.ToAngles(q);
// r = Quaternion.Euler(v);
// Assert.AreEqual(0, Quaternion.UnsignedAngle(q, r), 10e-2f, "0 0 0");
}
[Test]
public void GetAngleAround() {
Vector3Float v1 = new(0, 1, 0);
Quaternion q1 = new(0, 0, 0, 1);
float f = Quaternion.GetAngleAround(v1, q1);
Assert.AreEqual(f, 0, "GetAngleAround 0 1 0 , 0 0 0 1");
q1 = new(0, 0.707106709F, -0.707106709F, 0);
f = Quaternion.GetAngleAround(v1, q1);
Assert.AreEqual(f, 180, "GetAngleAround 0 1 0 , 0 0.7 -0.7 0");
v1 = new(0, 0, 0);
f = Quaternion.GetAngleAround(v1, q1);
Assert.IsTrue(float.IsNaN(f), "GetAngleAround 0 0 0 , 0 0.7 -0.7 0");
}
[Test]
public void GetRotationAround() {
Vector3Float v1 = new(0, 1, 0);
Quaternion q1 = new(0, 0, 0, 1);
Quaternion q = Quaternion.GetRotationAround(v1, q1);
Assert.AreEqual(q, new Quaternion(0, 0, 0, 1), "GetRotationAround 0 1 0 , 0 0 0 1");
q1 = new(0, 0.707106709F, -0.707106709F, 0);
q = Quaternion.GetRotationAround(v1, q1);
Assert.AreEqual(q, new Quaternion(0, 1, 0, 0), "GetRotationAround 0 1 0 , 0 0.7 -0.7 0");
v1 = new(0, 0, 0);
q = Quaternion.GetRotationAround(v1, q1);
bool r = float.IsNaN(q.x) && float.IsNaN(q.y) && float.IsNaN(q.z) && float.IsNaN(q.w);
Assert.IsTrue(r, "GetRotationAround 0 0 0 , 0 0.7 -0.7 0");
}
[Test, Ignore("ToDo")]
public void GetSwingTwist() { }
[Test, Ignore("ToDo")]
public void Dot() { }
}
}
#endif

View File

@ -0,0 +1,271 @@
//#if !UNITY_5_6_OR_NEWER
using System;
using System.Collections.Generic;
using NUnit.Framework;
namespace LinearAlgebra.Test {
public class SphericalTest {
[SetUp]
public void Setup() {
}
[Test]
public void FromVector3() {
#if UNITY_5_6_OR_NEWER
UnityEngine.Vector3 v = new(0, 0, 1);
#else
Vector3Float v = new(0, 0, 1);
#endif
Spherical s = Spherical.FromVector3(v);
Assert.AreEqual(1.0f, s.distance, "s.distance 0 0 1");
Assert.AreEqual(0.0f, s.direction.horizontal.inDegrees, "s.hor 0 0 1");
Assert.AreEqual(0.0f, s.direction.vertical.inDegrees, 1.0E-05F, "s.vert 0 0 1");
v = new(0, 1, 0);
s = Spherical.FromVector3(v);
Assert.AreEqual(1.0f, s.distance, "s.distance 0 1 0");
Assert.AreEqual(0.0f, s.direction.horizontal.inDegrees, "s.hor 0 1 0");
Assert.AreEqual(90.0f, s.direction.vertical.inDegrees, "s.vert 0 1 0");
v = new(1, 0, 0);
s = Spherical.FromVector3(v);
Assert.AreEqual(1.0f, s.distance, "s.distance 1 0 0");
Assert.AreEqual(90.0f, s.direction.horizontal.inDegrees, "s.hor 1 0 0");
Assert.AreEqual(0.0f, s.direction.vertical.inDegrees, 1.0E-05F, "s.vert 1 0 0");
}
[Test]
public void Addition() {
Spherical v1 = Spherical.Degrees(1, 45, 0);
Spherical v2 = Spherical.zero;
Spherical r = Spherical.zero;
r = v1 + v2;
Assert.AreEqual(v1.distance, r.distance, 1.0E-05F, "Addition(0,0,0)");
r = v1;
r += v2;
Assert.AreEqual(v1.distance, r.distance, 1.0E-05F, "Addition(0,0,0)");
v2 = Spherical.Degrees(1, 0, 90);
r = v1 + v2;
Assert.AreEqual(Math.Sqrt(2), r.distance, 1.0E-05F, "Addition(1 0 90)");
Assert.AreEqual(45.0f, r.direction.horizontal.inDegrees, 1e-5f, "Addition(1 0 90)");
Assert.AreEqual(45.0f, r.direction.vertical.inDegrees, 1.0E-05F, "Addition(1 0 90)");
}
[Test]
public void Average2_IdenticalVectors() {
Direction dir = Direction.Radians(MathF.PI / 4f, MathF.PI / 6f);
Spherical v = new(2.5f, dir);
Spherical avg = Spherical.Average(v, v);
Assert.AreEqual(2.5f, avg.distance, 1e-5f);
Assert.AreEqual(dir.horizontal, avg.direction.horizontal);
Assert.AreEqual(dir.vertical, avg.direction.vertical);
}
[Test]
public void Average2_OppositeUnitVectors() {
// Two opposite vectors: same distance, horizontal opposite (pi apart), same vertical
Spherical v1 = Spherical.Radians(1f, 0f, 0f);
Spherical v2 = Spherical.Radians(1f, MathF.PI, 0f);
Spherical avg = Spherical.Average(v1, v2);
Assert.AreEqual(0f, avg.distance, 1e-4f);
// When distance is zero, angles may be undefined; allow any angle but ensure near-zero magnitude
}
[Test]
public void Average2_WeightedByDistance() {
// Two vectors same direction but different distances -> weighted average distance
Direction dir = Direction.Radians(MathF.PI / 3f, MathF.PI / 4f);
Spherical a = new(1f, dir);
Spherical b = new(3f, dir);
Spherical avg = Spherical.Average(a, b);
// average distance should be (1+3)/2 = 2
Assert.AreEqual(2f, avg.distance, 1e-5f);
Assert.AreEqual(dir.horizontal.inRadians, avg.direction.horizontal.inRadians, 1e-5f);
Assert.AreEqual(dir.vertical.inRadians, avg.direction.vertical.inRadians, 1e-5f);
}
[Test]
public void Average2_OppositeButNotExact_NotZero() {
// Nearly opposite but not exact; expect a valid averaged direction and averaged distance
Direction d1 = Direction.Radians(0f, 0f);
Direction d2 = Direction.Radians(MathF.PI - 1e-3f, 0.0f); // slight offset
Spherical v1 = new(2.0f, d1);
Spherical v2 = new(4.0f, d2);
Spherical avg = Spherical.Average(v1, v2);
// Distance is arithmetic mean
Assert.AreEqual(3.0f, avg.distance, 1e-5f);
// Averaged azimuth should be near +pi/2 or -pi/2? we can check it's not NaN and unit-vector properties hold
float ux = MathF.Cos(avg.direction.horizontal.inRadians) * MathF.Cos(avg.direction.vertical.inRadians);
float uy = MathF.Sin(avg.direction.horizontal.inRadians) * MathF.Cos(avg.direction.vertical.inRadians);
float uz = MathF.Sin(avg.direction.vertical.inRadians);
float mag = MathF.Sqrt(ux * ux + uy * uy + uz * uz);
Assert.IsTrue(mag > 0.999f && mag < 1.001f);
}
[Test]
public void Average2_BasicAverageDirectionAndDistance() {
// Two different directions not cancelling: expect vector-average result
Direction d1 = Direction.Radians(MathF.PI / 6f, MathF.PI / 12f); // 30°, 15°
Direction d2 = Direction.Radians(MathF.PI / 3f, MathF.PI / 18f); // 60°, 10°
Spherical v1 = new(2.0f, d1);
Spherical v2 = new(4.0f, d2);
Spherical avg = Spherical.Average(v1, v2);
// Distance is arithmetic mean
Assert.AreEqual(3.0f, avg.distance, 1e-5f);
// Check averaged unit-vector equals normalized sum of unit vectors computed here
float a1 = d1.horizontal.inRadians;
float a2 = d2.horizontal.inRadians;
float e1 = d1.vertical.inRadians;
float e2 = d2.vertical.inRadians;
float cx = MathF.Cos(a1) + MathF.Cos(a2);
float cy = MathF.Sin(a1) + MathF.Sin(a2);
float z1 = MathF.Sin(e1);
float z2 = MathF.Sin(e2);
float cz = z1 + z2;
float mag = MathF.Sqrt(cx * cx + cy * cy + cz * cz);
Assert.IsTrue(mag > 1e-6f);
float ux = cx / mag;
float uy = cy / mag;
float uz = cz / mag;
// Reconstruct direction from avg result
float uxAvg = MathF.Cos(avg.direction.horizontal.inRadians) * MathF.Cos(avg.direction.vertical.inRadians);
float uyAvg = MathF.Sin(avg.direction.horizontal.inRadians) * MathF.Cos(avg.direction.vertical.inRadians);
float uzAvg = MathF.Sin(avg.direction.vertical.inRadians);
Assert.AreEqual(ux, uxAvg, 1e-4f);
Assert.AreEqual(uy, uyAvg, 1e-4f);
Assert.AreEqual(uz, uzAvg, 1e-4f);
}
[Test]
public void Average_IdenticalVectors() {
var dir = Direction.Radians(MathF.PI / 4f, MathF.PI / 6f);
var v = new Spherical(2.5f, dir);
var list = new List<Spherical> { v, v, v };
var avg = Spherical.Average(list);
Assert.AreEqual(2.5f, avg.distance, 1e-5f);
Assert.AreEqual(dir.horizontal, avg.direction.horizontal);
Assert.AreEqual(dir.vertical, avg.direction.vertical);
}
[Test]
public void Average_SingleElement() {
Spherical s = Spherical.Radians(1.234f, 0.3f, -0.7f);
Spherical avg = Spherical.Average(new List<Spherical> { s });
Assert.AreEqual(s.distance, avg.distance, 1e-5f);
Assert.AreEqual(s.direction.horizontal.inRadians, avg.direction.horizontal.inRadians, 1e-5f);
Assert.AreEqual(s.direction.vertical.inRadians, avg.direction.vertical.inRadians, 1e-5f);
}
[Test]
public void Average_OppositeUnitVectors() {
// Two opposite vectors: same distance, horizontal opposite (pi apart), same vertical
Spherical v1 = Spherical.Radians(1f, 0f, 0f);
Spherical v2 = Spherical.Radians(1f, MathF.PI, 0f);
Spherical avg = Spherical.Average(new List<Spherical> { v1, v2 });
Assert.AreEqual(0f, avg.distance, 1e-4f);
// When distance is zero, angles may be undefined; allow any angle but ensure near-zero magnitude
}
[Test]
public void Average_WeightedByDistance() {
// Two vectors same direction but different distances -> weighted average distance
Direction dir = Direction.Radians(MathF.PI / 3f, MathF.PI / 4f);
Spherical a = new(1f, dir);
Spherical b = new(3f, dir);
Spherical avg = Spherical.Average(new List<Spherical> { a, b });
// average distance should be (1+3)/2 = 2
Assert.AreEqual(2f, avg.distance, 1e-5f);
Assert.AreEqual(dir.horizontal.inRadians, avg.direction.horizontal.inRadians, 1e-5f);
Assert.AreEqual(dir.vertical.inRadians, avg.direction.vertical.inRadians, 1e-5f);
}
[Test]
public void Average_AxisSymmetricAroundVertical() {
// Four vectors around azimuth 0, pi/2, pi, 3pi/2 at same elevation (vertical) angle phi
float phi = MathF.PI / 6f; // elevation from horizontal plane
var dirs = new List<Spherical> {
new(1f, Direction.Radians(0f, phi)),
new(1f, Direction.Radians(MathF.PI/2, phi)),
new(1f, Direction.Radians(MathF.PI, phi)),
new(1f, Direction.Radians(3*MathF.PI/2, phi))
};
Spherical avg = Spherical.Average(dirs);
// rAvg should equal r * sin(elevation) = sin(phi)
Assert.AreEqual(MathF.Sin(phi), avg.distance, 1e-4f);
// vertical angle undefined when horizontal xy components cancel; allow any angle but ensure r matches
}
[Test]
public void Average_AxisSymmetricAroundVertical2() {
// Four vectors around azimuth 0, pi/2, pi, 3pi/2 at same polar angle from vertical (alpha)
float alpha = MathF.PI / 6f; // polar angle from vertical
float elevation = MathF.PI / 2f - alpha; // convert polar-from-vertical to elevation
var dirs = new List<Spherical> {
new(1f, Direction.Radians(0f, elevation)),
new(1f, Direction.Radians(MathF.PI/2, elevation)),
new(1f, Direction.Radians(MathF.PI, elevation)),
new(1f, Direction.Radians(3*MathF.PI/2, elevation))
};
Spherical avg = Spherical.Average(dirs);
// rAvg should equal r * sin(elevation) which equals cos(alpha)
Assert.AreEqual(MathF.Cos(alpha), avg.distance, 1e-4f);
}
[Test]
public void Average_CompareWithVector3() {
// Four vectors around azimuth 0, pi/2, pi, 3pi/2 at same polar angle from vertical (alpha)
float alpha = MathF.PI / 6f; // polar angle from vertical
float elevation = MathF.PI / 2f - alpha; // convert polar-from-vertical to elevation
List<Spherical> dirs = new List<Spherical> {
new(1f, Direction.Radians(0f, elevation)),
new(2f, Direction.Radians(MathF.PI/2, elevation+1)),
new(3f, Direction.Radians(MathF.PI, elevation+2)),
new(4f, Direction.Radians(3*MathF.PI/2, elevation+3))
};
Spherical avg = Spherical.Average(dirs);
#if UNITY_5_3_OR_NEWER
UnityEngine.Vector3 r = UnityEngine.Vector3.zero;
#else
Vector3Float r = Vector3Float.zero;
#endif
foreach (Spherical dir in dirs) {
r += dir.ToVector3();
}
r = r / 4;
Spherical avg2 = Spherical.FromVector3(r);
Assert.AreEqual(avg, avg2);
}
}
}
//#endif

View File

@ -0,0 +1,131 @@
#if !UNITY_5_6_OR_NEWER
using NUnit.Framework;
namespace LinearAlgebra.Test {
[TestFixture]
public class SwingTwistTest {
[Test]
public void Degrees_CreatesSwingTwistWithDegreeAngles() {
SwingTwist st = SwingTwist.Degrees(45, 30, 15);
Assert.IsNotNull(st);
Assert.AreEqual(45, st.swing.horizontal.inDegrees, 0.01f);
Assert.AreEqual(30, st.swing.vertical.inDegrees, 0.01f);
Assert.AreEqual(15, st.twist.inDegrees, 0.01f);
}
[Test]
public void Radians_CreatesSwingTwistWithRadianAngles() {
float pi = (float)System.Math.PI;
SwingTwist st = SwingTwist.Radians(pi / 4, pi / 6, pi / 12);
Assert.IsNotNull(st);
Assert.AreEqual(45, st.swing.horizontal.inDegrees, 0.01f);
Assert.AreEqual(30, st.swing.vertical.inDegrees, 0.01f);
Assert.AreEqual(15, st.twist.inDegrees, 0.01f);
}
[Test]
public void Zero_CreatesZeroRotation() {
SwingTwist st = SwingTwist.zero;
Assert.AreEqual(0, st.swing.horizontal.inDegrees, 0.01f);
Assert.AreEqual(0, st.swing.vertical.inDegrees, 0.01f);
Assert.AreEqual(0, st.twist.inDegrees, 0.01f);
}
[Test]
public void QuaternionTest() {
Quaternion q;
SwingTwist s;
Quaternion r;
q = Quaternion.identity;
s = SwingTwist.FromQuaternion(q);
r = s.ToQuaternion();
Assert.AreEqual(q, r);
q = Quaternion.Euler(90, 0, 0);
s = SwingTwist.FromQuaternion(q);
Assert.AreEqual(0, s.swing.horizontal.inDegrees, 10e-2f);
Assert.AreEqual(90, s.swing.vertical.inDegrees, 10e-2f);
Assert.AreEqual(0, s.twist.inDegrees, 0.01f);
r = s.ToQuaternion();
Assert.AreEqual(0, Quaternion.UnsignedAngle(q, r), 10e-2f);
q = Quaternion.Euler(0, 90, 0);
s = SwingTwist.FromQuaternion(q);
Assert.AreEqual(90, s.swing.horizontal.inDegrees,10e-2f);
Assert.AreEqual(0, s.swing.vertical.inDegrees, 0.01f);
Assert.AreEqual(0, s.twist.inDegrees, 0.01f);
r = s.ToQuaternion();
Assert.AreEqual(0, Quaternion.UnsignedAngle(q, r), 10e-2f);
q = Quaternion.Euler(0, 0, 90);
s = SwingTwist.FromQuaternion(q);
Assert.AreEqual(0, s.swing.horizontal.inDegrees, 0.01f);
Assert.AreEqual(0, s.swing.vertical.inDegrees, 0.01f);
Assert.AreEqual(90, s.twist.inDegrees, 0.01f);
r = s.ToQuaternion();
Assert.AreEqual(0, Quaternion.UnsignedAngle(q, r), 10e-2f);
q = Quaternion.Euler(0, 180, 0);
s = SwingTwist.FromQuaternion(q);
Assert.AreEqual(-180, s.swing.horizontal.inDegrees, 0.01f);
Assert.AreEqual(0, s.swing.vertical.inDegrees, 0.01f);
Assert.AreEqual(0, s.twist.inDegrees, 0.01f);
r = s.ToQuaternion();
Assert.AreEqual(0, Quaternion.UnsignedAngle(q, r), 10e-2f);
q = Quaternion.Euler(0, 135, 0);
s = SwingTwist.FromQuaternion(q);
Assert.AreEqual(135, s.swing.horizontal.inDegrees, 0.01f);
Assert.AreEqual(0, s.swing.vertical.inDegrees, 0.01f);
Assert.AreEqual(0, s.twist.inDegrees, 0.01f);
r = s.ToQuaternion();
Assert.AreEqual(0, Quaternion.UnsignedAngle(q, r), 10e-2f);
q = Quaternion.Euler(60, 45, 30);
s = SwingTwist.FromQuaternion(q);
Assert.AreEqual(45, s.swing.horizontal.inDegrees, 0.01f);
Assert.AreEqual(60, s.swing.vertical.inDegrees, 0.01f);
Assert.AreEqual(30, s.twist.inDegrees, 0.01f);
// r = s.ToQuaternion();
// Assert.AreEqual(0, Quaternion.UnsignedAngle(q, r), 10e-2f);
// q = Quaternion.Euler(-45, -30, -15);
// s = SwingTwist.FromQuaternion(q);
// Assert.AreEqual(-30, s.swing.horizontal.inDegrees, 0.01f);
// Assert.AreEqual(-45, s.swing.vertical.inDegrees, 0.01f);
// Assert.AreEqual(-15, s.twist.inDegrees, 0.01f);
// r = s.ToQuaternion();
// Assert.AreEqual(0, Quaternion.UnsignedAngle(q, r), 10e-2f);
// q = Quaternion.Euler(180, 180, 180);
// s = SwingTwist.FromQuaternion(q);
// Assert.AreEqual(-180, s.swing.horizontal.inDegrees, 0.01f);
// Assert.AreEqual(-180, s.swing.vertical.inDegrees, 0.01f);
// Assert.AreEqual(-180, s.twist.inDegrees, 0.01f);
// r = s.ToQuaternion();
// Assert.AreEqual(0, Quaternion.UnsignedAngle(q, r), 10e-2f);
}
[Test]
public void ToAngleAxis_ConvertsToSpherical() {
SwingTwist st = SwingTwist.Degrees(45, 30, 15);
Spherical s = st.ToAngleAxis();
Assert.IsNotNull(s);
}
[Test]
public void FromAngleAxis_ConvertsFromSpherical() {
Spherical s = new(90, Direction.Degrees(45, 0));
SwingTwist st = SwingTwist.FromAngleAxis(s);
Assert.IsNotNull(st);
}
}
}
#endif

View File

@ -0,0 +1,364 @@
#if !UNITY_5_6_OR_NEWER
using NUnit.Framework;
namespace LinearAlgebra.Test {
using Vector2 = Vector2Float;
public class Vector2FloatTest {
[SetUp]
public void Setup() {
}
[Test]
public void FromPolar() {
}
[Test]
public void Equality() {
Vector2 v1 = new(4, 5);
Vector2 v2 = new(1, 2);
Assert.IsFalse(v1 == v2, "4 5 == 1 2");
Assert.IsTrue(v1 != v2, "4 5 != 1 2");
v2 = new(4, 5);
Assert.IsTrue(v1 == v2, "4 5 == 4 5");
Assert.IsFalse(v1 != v2, "4 5 != 4 5");
}
[Test]
public void Magnitude() {
Vector2 v = new(1, 2);
float m = 0;
m = v.magnitude;
Assert.AreEqual(m, 2.236068F, "v.magnitude 1 2");
m = Vector2.MagnitudeOf(v);
Assert.AreEqual(m, 2.236068F, "MagnitudeOf 1 2");
v = new(-1, -2);
m = v.magnitude;
Assert.AreEqual(m, 2.236068F, "v.magnitude -1 -2");
v = new(0, 0);
m = v.magnitude;
Assert.AreEqual(m, 0, "v.magnitude 0 0");
}
[Test]
public void SqrMagnitude() {
Vector2 v = new(1, 2);
float m = 0;
m = v.sqrMagnitude;
Assert.AreEqual(m, 5, "v.sqrMagnitude 1 2");
m = Vector2.SqrMagnitudeOf(v);
Assert.AreEqual(m, 5, "SqrMagnitudeOf 1 2");
v = new(-1, -2);
m = v.sqrMagnitude;
Assert.AreEqual(m, 5, "v.sqrMagnitude -1 -2");
v = new(0, 0);
m = v.sqrMagnitude;
Assert.AreEqual(m, 0, "v.sqrMagnitude 0 0");
}
[Test]
public void Distance() {
Vector2 v1 = new(4, 5);
Vector2 v2 = new(1, 2);
float f = 0;
f = Vector2.Distance(v1, v2);
Assert.AreEqual(f, 4.24264002f, 1.0E-05F, "Distance(4 5, 1 2)");
v2 = new(-1, -2);
f = Vector2.Distance(v1, v2);
Assert.AreEqual(f, 8.602325F, "Distance(4 5, 1 2)");
v2 = new(0, 0);
f = Vector2.Distance(v1, v2);
Assert.AreEqual(f, 6.403124F, 1.0E-05F, "Distance(4 5, 1 2)");
}
[Test]
public void Normalize() {
Vector2 v = new(0, 3);
Vector2Float r;
r = v.normalized;
Assert.AreEqual(0, r.horizontal, "normalized 0 3 H");
Assert.AreEqual(1, r.vertical, "normalized 0 3 V");
r = Vector2.Normalize(v);
Assert.AreEqual(0, r.horizontal, "Normalize 0 3 H");
Assert.AreEqual(1, r.vertical, "Normalize 0 3 V");
v = new(0, -3);
r = v.normalized;
Assert.AreEqual(0, r.horizontal, "normalized 0 -3 H");
Assert.AreEqual(-1, r.vertical, "normalized 0 -3 V");
v = new(0, 0);
r = v.normalized;
Assert.AreEqual(0, r.horizontal, "normalized 0 0 H");
Assert.AreEqual(0, r.vertical, "normalized 0 0 V");
}
[Test]
public void Negate() {
Vector2 v = new(4, 5);
Vector2 r;
r = -v;
Assert.AreEqual(-4, r.horizontal, "- 4 5 H");
Assert.AreEqual(-5, r.vertical, "- 4 5 V");
v = new(-4, -5);
r = -v;
Assert.AreEqual(4, r.horizontal, "- -4 -5 H");
Assert.AreEqual(5, r.vertical, "- -4 -5 V");
v = new(0, 0);
r = -v;
Assert.AreEqual(0, r.horizontal, "- 0 0 H");
Assert.AreEqual(0, r.vertical, "- 0 0 V");
}
[Test]
public void Subtract() {
Vector2 v1 = new(4, 5);
Vector2 v2 = new(1, 2);
Vector2 r = Vector2.zero;
r = v1 - v2;
Assert.IsTrue(r == new Vector2(3, 3), "4 5 - 1 2");
v2 = new(-1, -2);
r = v1 - v2;
Assert.IsTrue(r == new Vector2(5, 7), "4 5 - -1 -2");
v2 = new(4, 5);
r = v1 - v2;
Assert.IsTrue(r == new Vector2(0, 0), "4 5 - 4 5");
r = v1;
r -= v2;
Assert.AreEqual(r, new Vector2(0, 0), "4 5 - 4 5");
v2 = new(0, 0);
r = v1 - v2;
Assert.AreEqual(r, new Vector2(4, 5), "4 5 - 0 0");
r -= v2;
Assert.AreEqual(r, new Vector2(4, 5), "4 5 - 0 0");
}
[Test]
public void Addition() {
Vector2 v1 = new(4, 5);
Vector2 v2 = new(1, 2);
Vector2 r = Vector2.zero;
r = v1 + v2;
Assert.IsTrue(r == new Vector2(5, 7), "4 5 + 1 2");
v2 = new(-1, -2);
r = v1 + v2;
Assert.IsTrue(r == new Vector2(3, 3), "4 5 + -1 -2");
r = v1;
r += v2;
Assert.AreEqual(r, new Vector2(3, 3), "4 5 + -1 -2");
v2 = new(0, 0);
r = v1 + v2;
Assert.AreEqual(r, new Vector2(4, 5), "4 5 + 0 0");
r += v2;
Assert.AreEqual(r, new Vector2(4, 5), "4 5 + 0 0");
}
[Test]
public void Scale() {
Vector2 v1 = new(4, 5);
Vector2 v2 = new(1, 2);
Vector2 r;
r = Vector2.Scale(v1, v2);
Assert.AreEqual(4, r.horizontal, "Scale 4 5 , 1 2 H");
Assert.AreEqual(10, r.vertical, "Scale 4 5 , 1 2 V");
v2 = new(-1, -2);
r = Vector2.Scale(v1, v2);
Assert.AreEqual(-4, r.horizontal, "Scale 4 5 , -1 -2 H");
Assert.AreEqual(-10, r.vertical, "Scale 4 5 , -1 -2 V");
v2 = new(0, 0);
r = Vector2.Scale(v1, v2);
Assert.AreEqual(0, r.horizontal, "Scale 4 5 , 0 0 H");
Assert.AreEqual(0, r.vertical, "Scale 4 5 , 0 0 V");
}
[Test]
public void Multiply() {
Vector2 v1 = new(4, 5);
int f = 3;
Vector2 r;
r = v1 * f;
Assert.AreEqual(12, r.horizontal, "4 5 * 3 H");
Assert.AreEqual(15, r.vertical, "4 5 * 3 V");
r = f * v1;
Assert.AreEqual(12, r.horizontal, "3 * 4 5 H");
Assert.AreEqual(15, r.vertical, "3 * 4 5 V");
f = -3;
r = v1 * f;
Assert.AreEqual(-12, r.horizontal, "4 5 * -3 H");
Assert.AreEqual(-15, r.vertical, "4 5 * -3 V");
f = 0;
r = v1 * f;
Assert.AreEqual(0, r.horizontal, "4 5 * 0 H");
Assert.AreEqual(0, r.vertical, "4 5 * 0 V");
}
[Test]
public void Divide() {
Vector2 v1 = new(4, 5);
float f = 2;
Vector2 r;
r = v1 / f;
Assert.AreEqual(2, r.horizontal, "4 5 / 2 H");
Assert.AreEqual(2.5, r.vertical, "4 5 / 2 V");
f = -2;
r = v1 / f;
Assert.AreEqual(-2, r.horizontal, "4 5 / -2 H");
Assert.AreEqual(-2.5, r.vertical, "4 5 / -2 V");
f = 0;
r = v1 / f;
Assert.AreEqual(float.PositiveInfinity, r.horizontal, "4 5 / 0 H");
Assert.AreEqual(float.PositiveInfinity, r.vertical, "4 5 / 0 V");
}
[Test]
public void Dot() {
Vector2 v1 = new(4, 5);
Vector2 v2 = new(1, 2);
float f;
f = Vector2.Dot(v1, v2);
Assert.AreEqual(14, f, "Dot(4 5, 1 2)");
v2 = new(-1, -2);
f = Vector2.Dot(v1, v2);
Assert.AreEqual(-14, f, "Dot(4 5, -1 -2)");
v2 = new(0, 0);
f = Vector2.Dot(v1, v2);
Assert.AreEqual(0, f, "Dot(4 5, 0 0)");
}
[Test]
public void SignedAngle() {
Vector2 v1 = new(4, 5);
Vector2 v2 = new(1, 2);
float f;
f = Vector2.SignedAngle(v1, v2);
Assert.AreEqual(-12.094758f, f);
v2 = new(-1, -2);
f = Vector2.SignedAngle(v1, v2);
Assert.AreEqual(167.905228f, f);
v2 = new(0, 0);
f = Vector2.SignedAngle(v1, v2);
Assert.AreEqual(0, f);
v1 = new(0, 1);
v2 = new(1, 0);
f = Vector2.SignedAngle(v1, v2);
Assert.AreEqual(90, f);
v1 = new(0, 1);
v2 = new(0, -1);
f = Vector2.SignedAngle(v1, v2);
Assert.AreEqual(180, f);
}
[Test]
public void UnsignedAngle() {
Vector2 v1 = new(4, 5);
Vector2 v2 = new(1, 2);
float f;
f = Vector2.UnsignedAngle(v1, v2);
Assert.AreEqual(12.094758f, f);
v2 = new(-1, -2);
f = Vector2.UnsignedAngle(v1, v2);
Assert.AreEqual(167.905228f, f);
v2 = new(0, 0);
f = Vector2.UnsignedAngle(v1, v2);
Assert.AreEqual(0, f);
v1 = new(0, 1);
v2 = new(1, 0);
f = Vector2.UnsignedAngle(v1, v2);
Assert.AreEqual(90, f);
v1 = new(0, 1);
v2 = new(0, -1);
f = Vector2.UnsignedAngle(v1, v2);
Assert.AreEqual(180, f);
}
[Test]
public void Rotate() {
Vector2 v1 = new(1, 2);
Vector2 r;
r = Vector2.Rotate(v1, AngleFloat.Degrees(0));
Assert.AreEqual(0, Vector2.Distance(r, v1));
r = Vector2.Rotate(v1, AngleFloat.Degrees(180));
Assert.AreEqual(0, Vector2.Distance(r, new Vector2(-1, -2)), 1.0e-06);
r = Vector2.Rotate(v1, AngleFloat.Degrees(-90));
Assert.AreEqual(0, Vector2.Distance(r, new Vector2(2, -1)), 1.0e-06);
r = Vector2.Rotate(v1, AngleFloat.Degrees(270));
Assert.AreEqual(0, Vector2.Distance(r, new Vector2(2, -1)), 1.0e-06);
}
[Test]
public void Lerp() {
Vector2 v1 = new(4, 5);
Vector2 v2 = new(1, 2);
Vector2 r;
r = Vector2.Lerp(v1, v2, 0);
Assert.AreEqual(0, Vector2.Distance(r, v1), 0);
r = Vector2.Lerp(v1, v2, 1);
Assert.AreEqual(0, Vector2.Distance(r, v2), 0);
r = Vector2.Lerp(v1, v2, 0.5f);
Assert.AreEqual(0, Vector2.Distance(r, new Vector2(2.5f, 3.5f)), 0);
r = Vector2.Lerp(v1, v2, -1);
Assert.AreEqual(0, Vector2.Distance(r, new Vector2(7, 8)), 0);
r = Vector2.Lerp(v1, v2, 2);
Assert.AreEqual(0, Vector2.Distance(r, new Vector2(-2, -1)), 0);
}
}
}
#endif

View File

@ -0,0 +1,270 @@
#if !UNITY_5_6_OR_NEWER
using NUnit.Framework;
namespace LinearAlgebra.Test {
using Vector2 = Vector2Int;
public class Vector2IntTest {
[SetUp]
public void Setup() {
}
[Test]
public void FromPolar() {
}
[Test]
public void Equality() {
Vector2 v1 = new(4, 5);
Vector2 v2 = new(1, 2);
Assert.IsFalse(v1 == v2, "4 5 == 1 2");
Assert.IsTrue(v1 != v2, "4 5 != 1 2");
v2 = new(4, 5);
Assert.IsTrue(v1 == v2, "4 5 == 4 5");
Assert.IsFalse(v1 != v2, "4 5 != 4 5");
}
[Test]
public void Magnitude() {
Vector2 v = new(1, 2);
float m = 0;
m = v.magnitude;
Assert.AreEqual(m, 2.236068F, "v.magnitude 1 2");
m = Vector2.MagnitudeOf(v);
Assert.AreEqual(m, 2.236068F, "MagnitudeOf 1 2");
v = new(-1, -2);
m = v.magnitude;
Assert.AreEqual(m, 2.236068F, "v.magnitude -1 -2");
v = new(0, 0);
m = v.magnitude;
Assert.AreEqual(m, 0, "v.magnitude 0 0");
}
[Test]
public void SqrMagnitude() {
Vector2 v = new(1, 2);
float m = 0;
m = v.sqrMagnitude;
Assert.AreEqual(m, 5, "v.sqrMagnitude 1 2");
m = Vector2.SqrMagnitudeOf(v);
Assert.AreEqual(m, 5, "SqrMagnitudeOf 1 2");
v = new(-1, -2);
m = v.sqrMagnitude;
Assert.AreEqual(m, 5, "v.sqrMagnitude -1 -2");
v = new(0, 0);
m = v.sqrMagnitude;
Assert.AreEqual(m, 0, "v.sqrMagnitude 0 0");
}
[Test]
public void Distance() {
Vector2 v1 = new(4, 5);
Vector2 v2 = new(1, 2);
float f = 0;
f = Vector2.Distance(v1, v2);
Assert.AreEqual(f, 4.24264002f, 1.0E-05F, "Distance(4 5, 1 2)");
v2 = new(-1, -2);
f = Vector2.Distance(v1, v2);
Assert.AreEqual(f, 8.602325F, "Distance(4 5, 1 2)");
v2 = new(0, 0);
f = Vector2.Distance(v1, v2);
Assert.AreEqual(f, 6.403124F, 1.0E-05F, "Distance(4 5, 1 2)");
}
[Test]
public void Normalize() {
Vector2 v = new(0, 3);
Vector2Float r;
r = v.normalized;
Assert.AreEqual(0, r.horizontal, "normalized 0 3 H");
Assert.AreEqual(1, r.vertical, "normalized 0 3 V");
r = Vector2.Normalize(v);
Assert.AreEqual(0, r.horizontal, "Normalize 0 3 H");
Assert.AreEqual(1, r.vertical, "Normalize 0 3 V");
v = new(0, -3);
r = v.normalized;
Assert.AreEqual(0, r.horizontal, "normalized 0 -3 H");
Assert.AreEqual(-1, r.vertical, "normalized 0 -3 V");
v = new(0, 0);
r = v.normalized;
Assert.AreEqual(0, r.horizontal, "normalized 0 0 H");
Assert.AreEqual(0, r.vertical, "normalized 0 0 V");
}
[Test]
public void Negate() {
Vector2 v = new(4, 5);
Vector2 r;
r = -v;
Assert.AreEqual(-4, r.horizontal, "- 4 5 H");
Assert.AreEqual(-5, r.vertical, "- 4 5 V");
v = new(-4, -5);
r = -v;
Assert.AreEqual(4, r.horizontal, "- -4 -5 H");
Assert.AreEqual(5, r.vertical, "- -4 -5 V");
v = new(0, 0);
r = -v;
Assert.AreEqual(0, r.horizontal, "- 0 0 H");
Assert.AreEqual(0, r.vertical, "- 0 0 V");
}
[Test]
public void Subtract() {
Vector2 v1 = new(4, 5);
Vector2 v2 = new(1, 2);
Vector2 r = Vector2.zero;
r = v1 - v2;
Assert.IsTrue(r == new Vector2(3, 3), "4 5 - 1 2");
v2 = new(-1, -2);
r = v1 - v2;
Assert.IsTrue(r == new Vector2(5, 7), "4 5 - -1 -2");
v2 = new(4, 5);
r = v1 - v2;
Assert.IsTrue(r == new Vector2(0, 0), "4 5 - 4 5");
r = v1;
r -= v2;
Assert.AreEqual(r, new Vector2(0, 0), "4 5 - 4 5");
v2 = new(0, 0);
r = v1 - v2;
Assert.AreEqual(r, new Vector2(4, 5), "4 5 - 0 0");
r -= v2;
Assert.AreEqual(r, new Vector2(4, 5), "4 5 - 0 0");
}
[Test]
public void Addition() {
Vector2 v1 = new(4, 5);
Vector2 v2 = new(1, 2);
Vector2 r = Vector2.zero;
r = v1 + v2;
Assert.IsTrue(r == new Vector2(5, 7), "4 5 + 1 2");
v2 = new(-1, -2);
r = v1 + v2;
Assert.IsTrue(r == new Vector2(3, 3), "4 5 + -1 -2");
r = v1;
r += v2;
Assert.AreEqual(r, new Vector2(3, 3), "4 5 + -1 -2");
v2 = new(0, 0);
r = v1 + v2;
Assert.AreEqual(r, new Vector2(4, 5), "4 5 + 0 0");
r += v2;
Assert.AreEqual(r, new Vector2(4, 5), "4 5 + 0 0");
}
[Test]
public void Scale() {
Vector2 v1 = new(4, 5);
Vector2 v2 = new(1, 2);
Vector2 r;
r = Vector2.Scale(v1, v2);
Assert.AreEqual(4, r.horizontal, "Scale 4 5 , 1 2 H");
Assert.AreEqual(10, r.vertical, "Scale 4 5 , 1 2 V");
v2 = new(-1, -2);
r = Vector2.Scale(v1, v2);
Assert.AreEqual(-4, r.horizontal, "Scale 4 5 , -1 -2 H");
Assert.AreEqual(-10, r.vertical, "Scale 4 5 , -1 -2 V");
v2 = new(0, 0);
r = Vector2.Scale(v1, v2);
Assert.AreEqual(0, r.horizontal, "Scale 4 5 , 0 0 H");
Assert.AreEqual(0, r.vertical, "Scale 4 5 , 0 0 V");
}
[Test]
public void Multiply() {
Vector2 v1 = new(4, 5);
int f = 3;
Vector2 r;
r = v1 * f;
Assert.AreEqual(12, r.horizontal, "4 5 * 3 H");
Assert.AreEqual(15, r.vertical, "4 5 * 3 V");
r = f * v1;
Assert.AreEqual(12, r.horizontal, "3 * 4 5 H");
Assert.AreEqual(15, r.vertical, "3 * 4 5 V");
f = -3;
r = v1 * f;
Assert.AreEqual(-12, r.horizontal, "4 5 * -3 H");
Assert.AreEqual(-15, r.vertical, "4 5 * -3 V");
f = 0;
r = v1 * f;
Assert.AreEqual(0, r.horizontal, "4 5 * 0 H");
Assert.AreEqual(0, r.vertical, "4 5 * 0 V");
}
[Test]
public void Divide() {
Vector2 v1 = new(4, 5);
int f = 2;
Vector2 r;
r = v1 / f;
Assert.AreEqual(2, r.horizontal, "4 5 / 2 H");
Assert.AreEqual(2, r.vertical, "4 5 / 2 V");
f = -2;
r = v1 / f;
Assert.AreEqual(-2, r.horizontal, "4 5 / -2 H");
Assert.AreEqual(-2, r.vertical, "4 5 / -2 V");
Assert.Throws<System.DivideByZeroException>(() => {
f = 0;
r = v1 / f;
Assert.AreEqual(float.PositiveInfinity, r.horizontal, "4 5 / 0 H");
Assert.AreEqual(float.PositiveInfinity, r.vertical, "4 5 / 0 V");
});
}
[Test]
public void Dot() {
Vector2 v1 = new(4, 5);
Vector2 v2 = new(1, 2);
int f;
f = Vector2.Dot(v1, v2);
Assert.AreEqual(14, f, "Dot(4 5, 1 2)");
v2 = new(-1, -2);
f = Vector2.Dot(v1, v2);
Assert.AreEqual(-14, f, "Dot(4 5, -1 -2)");
v2 = new(0, 0);
f = Vector2.Dot(v1, v2);
Assert.AreEqual(0, f, "Dot(4 5, 0 0)");
}
}
}
#endif

View File

@ -0,0 +1,581 @@
#if !UNITY_5_6_OR_NEWER
using NUnit.Framework;
namespace LinearAlgebra.Test {
using Vector3 = Vector3Float;
public class Vector3FloatTest {
[Test]
public void FromSpherical() {
Vector3 v = new(0, 0, 1);
Spherical s = Spherical.FromVector3(v);
Vector3 r = Vector3.FromSpherical(s);
Assert.AreEqual(0, r.horizontal, "0 0 1");
Assert.AreEqual(0, r.vertical, 1.0e-06, "0 0 1");
Assert.AreEqual(1, r.depth, "0 0 1");
v = new(0, 1, 0);
s = Spherical.FromVector3(v);
r = Vector3.FromSpherical(s);
Assert.AreEqual(0, r.horizontal, "0 0 1");
Assert.AreEqual(1, r.vertical, "0 0 1");
Assert.AreEqual(0, r.depth, 1.0e-06, "0 0 1");
v = new(1, 0, 0);
s = Spherical.FromVector3(v);
r = Vector3.FromSpherical(s);
Assert.AreEqual(1, r.horizontal, "0 0 1");
Assert.AreEqual(0, r.vertical, 1.0e-06, "0 0 1");
Assert.AreEqual(0, r.depth, 1.0e-06, "0 0 1");
}
[Test]
public void Magnitude() {
Vector3 v = new(1, 2, 3);
float m = 0;
m = v.magnitude;
Assert.AreEqual(3.7416575f, m, "magnitude 1 2 3");
m = Vector3.MagnitudeOf(v);
Assert.AreEqual(3.7416575f, m, "MagnitudeOf 1 2 3");
v = new(-1, -2, -3);
m = v.magnitude;
Assert.AreEqual(3.7416575f, m, "magnitude -1 -2 -3");
v = new(0, 0, 0);
m = v.magnitude;
Assert.AreEqual(0, m, "magnitude 0 0 0");
// Infinity tests are still missing
}
[Test]
public void SqrMagnitude() {
Vector3 v = new(1, 2, 3);
float m = 0;
m = v.sqrMagnitude;
Assert.AreEqual(14, m, "sqrMagnitude 1 2 3");
m = Vector3.SqrMagnitudeOf(v);
Assert.AreEqual(14, m, "SqrMagnitudeOf 1 2 3");
v = new(-1, -2, -3);
m = v.sqrMagnitude;
Assert.AreEqual(14, m, "sqrMagnitude -1 -2 -3");
v = new(0, 0, 0);
m = v.sqrMagnitude;
Assert.AreEqual(0, m, "sqrMagnitude 0 0 0");
// Infinity tests are still missing
}
[Test]
public void Normalize() {
Vector3 v = new(0, 2, 0);
Vector3 r;
r = v.normalized;
Assert.AreEqual(new Vector3(0, 1, 0), r, "normalized 0 2 0");
r = Vector3.Normalize(v);
Assert.AreEqual(new Vector3(0, 1, 0), r, "Normalize 0 2 0");
v = new(0, -2, 0);
r = v.normalized;
Assert.AreEqual(new Vector3(0, -1, 0), r, "normalized 0 -2 0");
v = new(0, 0, 0);
r = v.normalized;
Assert.AreEqual(new Vector3(0, 0, 0), r, "normalized 0 0 0");
v = new(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
r = v.normalized;
Assert.IsTrue(float.IsNaN(r.horizontal), "normalized infinity infinity infinity");
Assert.IsTrue(float.IsNaN(r.vertical), "normalized infinity infinity infinity");
Assert.IsTrue(float.IsNaN(r.depth), "normalized infinity infinity infinity");
v = new(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
r = v.normalized;
Assert.IsTrue(float.IsNaN(r.horizontal), "normalized -infinity -infinity -infinity");
Assert.IsTrue(float.IsNaN(r.vertical), "normalized -infinity -infinity -infinity");
Assert.IsTrue(float.IsNaN(r.depth), "normalized -infinity -infinity -infinity");
}
[Test]
public void Negate() {
Vector3 v = new(4, 5, 6);
Vector3 r;
r = -v;
Assert.AreEqual(-4, r.horizontal, "- 4 5 6 H");
Assert.AreEqual(-5, r.vertical, "- 4 5 6 V");
Assert.AreEqual(-6, r.depth, "- 4 5 6 D");
v = new(-4, -5, -6);
r = -v;
Assert.AreEqual(4, r.horizontal, "- -4 -5 -6 H");
Assert.AreEqual(5, r.vertical, "- -4 -5 -6 V");
Assert.AreEqual(6, r.depth, "- -4 -5 -6 D");
v = new(0, 0, 0);
r = -v;
Assert.AreEqual(new Vector3(0, 0, 0), r, "- 0 0 0");
Assert.AreEqual(0, r.horizontal, "- 0 0 0 H");
Assert.AreEqual(0, r.vertical, "- 0 0 0 V");
Assert.AreEqual(0, r.depth, "- 0 0 0 D");
v = new(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
r = -v;
Assert.AreEqual(new Vector3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity), r, "- inifinty infinity infinity");
v = new(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
r = -v;
Assert.AreEqual(new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity), r, "- -inifinty -infinity -infinity");
}
[Test]
public void Subtract() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
Vector3 r = Vector3.zero;
r = v1 - v2;
Assert.IsTrue(r == new Vector3(3, 3, 3), "4 5 6 - 1 2 3");
v2 = new(-1, -2, -3);
r = v1 - v2;
Assert.IsTrue(r == new Vector3(5, 7, 9), "4 5 6 - -1 -2 -3");
v2 = new(4, 5, 6);
r = v1 - v2;
Assert.IsTrue(r == new Vector3(0, 0, 0), "4 5 6 - 4 5 6");
r = v1;
r -= v2;
Assert.AreEqual(r, new Vector3(0, 0, 0), "4 5 6 - 4 5 6");
v2 = new(0, 0, 0);
r = v1 - v2;
Assert.AreEqual(r, new Vector3(4, 5, 6), "4 5 6 - 0 0 0");
r -= v2;
Assert.AreEqual(r, new Vector3(4, 5, 6), "4 5 6 - 0 0 0");
// Infinity tests are still missing
}
[Test]
public void Addition() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
Vector3 r = Vector3.zero;
r = v1 + v2;
Assert.IsTrue(r == new Vector3(5, 7, 9), "4 5 6 + 1 2 3");
v2 = new(-1, -2, -3);
r = v1 + v2;
Assert.IsTrue(r == new Vector3(3, 3, 3), "4 5 6 + -1 -2 -3");
r = v1;
r += v2;
Assert.AreEqual(r, new Vector3(3, 3, 3), "4 5 6 + -1 -2 -3");
v2 = new(0, 0, 0);
r = v1 + v2;
Assert.AreEqual(r, new Vector3(4, 5, 6), "4 5 6 + 0 0 0");
r += v2;
Assert.AreEqual(r, new Vector3(4, 5, 6), "4 5 6 + 0 0 0");
// Infinity tests are still missing
}
[Test]
public void Scale() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
Vector3 r;
r = Vector3.Scale(v1, v2);
Assert.AreEqual(4, r.horizontal, "Scale 4 5 6 , 1 2 3 H");
Assert.AreEqual(10, r.vertical, "Scale 4 5 6 , 1 2 3 V");
Assert.AreEqual(18, r.depth, "Scale 4 5 6 , 1 2 3 D");
v2 = new(-1, -2, -3);
r = Vector3.Scale(v1, v2);
Assert.AreEqual(-4, r.horizontal, "Scale 4 5 6 , -1 -2 -3 H");
Assert.AreEqual(-10, r.vertical, "Scale 4 5 6 , -1 -2 -3 V");
Assert.AreEqual(-18, r.depth, "Scale 4 5 6 , -1 -2 -3 D");
v2 = new(0, 0, 0);
r = Vector3.Scale(v1, v2);
Assert.AreEqual(0, r.horizontal, "Scale 4 5 6 , 0 0 0 H");
Assert.AreEqual(0, r.vertical, "Scale 4 5 6 , 0 0 0 V");
Assert.AreEqual(0, r.depth, "Scale 4 5 6 , 0 0 0 D");
v2 = new(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
r = Vector3.Scale(v1, v2);
Assert.AreEqual(float.PositiveInfinity, r.horizontal, "Scale 4 5 6 , inf inf inf H");
Assert.AreEqual(float.PositiveInfinity, r.vertical, "Scale 4 5 6 , inf inf inf V");
Assert.AreEqual(float.PositiveInfinity, r.depth, "Scale 4 5 6 , inf inf inf D");
v2 = new(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
r = Vector3.Scale(v1, v2);
Assert.AreEqual(float.NegativeInfinity, r.horizontal, "Scale 4 5 6 , -inf -inf -inf H");
Assert.AreEqual(float.NegativeInfinity, r.vertical, "Scale 4 5 6 , -inf -inf -inf V");
Assert.AreEqual(float.NegativeInfinity, r.depth, "Scale 4 5 6 , -inf -inf -inf D");
}
[Test]
public void Multiply() {
Vector3 v1 = new(4, 5, 6);
float f = 3;
Vector3 r;
r = v1 * f;
Assert.AreEqual(12, r.horizontal, "4 5 6 * 3 H");
Assert.AreEqual(15, r.vertical, "4 5 6 * 3 V");
Assert.AreEqual(18, r.depth, "4 5 6 * 3 D");
f = -3;
r = v1 * f;
Assert.AreEqual(-12, r.horizontal, "4 5 6 * -3 H");
Assert.AreEqual(-15, r.vertical, "4 5 6 * -3 V");
Assert.AreEqual(-18, r.depth, "4 5 6 * -3 D");
f = 0;
r = v1 * f;
Assert.AreEqual(0, r.horizontal, "4 5 6 * 0 H");
Assert.AreEqual(0, r.vertical, "4 5 6 * 0 V");
Assert.AreEqual(0, r.depth, "4 5 6 * 0 D");
f = float.PositiveInfinity;
r = v1 * f;
Assert.AreEqual(float.PositiveInfinity, r.horizontal, "4 5 6 * inf H");
Assert.AreEqual(float.PositiveInfinity, r.vertical, "4 5 6 * inf V");
Assert.AreEqual(float.PositiveInfinity, r.depth, "4 5 6 * inf D");
f = float.NegativeInfinity;
r = v1 * f;
Assert.AreEqual(float.NegativeInfinity, r.horizontal, "4 5 6 * -inf H");
Assert.AreEqual(float.NegativeInfinity, r.vertical, "4 5 6 * -inf V");
Assert.AreEqual(float.NegativeInfinity, r.depth, "4 5 6 * -inf D");
}
[Test]
public void Divide() {
Vector3 v1 = new(4, 5, 6);
float f = 2;
Vector3 r;
r = v1 / f;
Assert.AreEqual(2, r.horizontal, "4 5 6 / 2 H");
Assert.AreEqual(2.5, r.vertical, "4 5 6 / 2 V");
Assert.AreEqual(3, r.depth, "4 5 6 / 2 D");
f = -2;
r = v1 / f;
Assert.AreEqual(-2, r.horizontal, "4 5 6 / -2 H");
Assert.AreEqual(-2.5, r.vertical, "4 5 6 / -2 V");
Assert.AreEqual(-3, r.depth, "4 5 6 / -2 D");
f = 0;
r = v1 / f;
Assert.AreEqual(float.PositiveInfinity, r.horizontal, "4 5 6 / 0 H");
Assert.AreEqual(float.PositiveInfinity, r.vertical, "4 5 6 / 0 V");
Assert.AreEqual(float.PositiveInfinity, r.depth, "4 5 6 / 0 D");
f = float.PositiveInfinity;
r = v1 / f;
Assert.AreEqual(0, r.horizontal, "4 5 6 / inf H");
Assert.AreEqual(0, r.vertical, "4 5 6 / inf V");
Assert.AreEqual(0, r.depth, "4 5 6 / inf D");
f = float.NegativeInfinity;
r = v1 / f;
Assert.AreEqual(0, r.horizontal, "4 5 6 / -inf H");
Assert.AreEqual(0, r.vertical, "4 5 6 / -inf V");
Assert.AreEqual(0, r.depth, "4 5 6 / -inf D");
}
[Test]
public void Dot() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
float f;
f = Vector3.Dot(v1, v2);
Assert.AreEqual(32, f, "Dot(4 5 6, 1 2 3)");
v2 = new(-1, -2, -3);
f = Vector3.Dot(v1, v2);
Assert.AreEqual(-32, f, "Dot(4 5 6, -1 -2 -3)");
v2 = new(0, 0, 0);
f = Vector3.Dot(v1, v2);
Assert.AreEqual(0, f, "Dot(4 5 6, 0 0 0)");
v2 = new(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
f = Vector3.Dot(v1, v2);
Assert.AreEqual(float.PositiveInfinity, f, "Dot(4 5 6, inf inf inf)");
v2 = new(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
f = Vector3.Dot(v1, v2);
Assert.AreEqual(float.NegativeInfinity, f, "Dot(4 5 6, -inf -inf -inf)");
}
[Test]
public void Equality() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
bool r;
r = v1 == v2;
Assert.IsFalse(r, "4 5 6 == 1 2 3");
v2 = new(4, 5, 6);
r = v1 == v2;
Assert.IsTrue(r, "4 5 6 == 4 5 6");
v2 = new(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
r = v1 == v2;
Assert.IsFalse(r, "4 5 6 == inf inf inf");
v1 = new(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
r = v1 == v2;
Assert.IsFalse(r, "-inf -inf -inf == inf inf inf");
}
[Test]
public void Distance() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
float f;
f = Vector3.Distance(v1, v2);
Assert.AreEqual(5.19615221F, f, "Distance(4 5 6, 1 2 3)");
v2 = new(-1, -2, -3);
f = Vector3.Distance(v1, v2);
Assert.AreEqual(12.4498997F, f, "Distance(4 5 6, -1 -2 -3)");
v2 = new(0, 0, 0);
f = Vector3.Distance(v1, v2);
Assert.AreEqual(v1.magnitude, f, "Distance(4 5 6, 0 0 0)");
v2 = new(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
f = Vector3.Distance(v1, v2);
Assert.AreEqual(float.PositiveInfinity, f, "Distance(4 5 6, inf inf inf)");
v2 = new(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
f = Vector3.Distance(v1, v2);
Assert.AreEqual(float.PositiveInfinity, f, "Distance(4 5 6, -inf -inf -inf)");
}
[Test]
public void Cross() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
Vector3 r;
r = Vector3.Cross(v1, v2);
Assert.AreEqual(3, r.horizontal, "Cross(4 5 6, 1 2 3) H");
Assert.AreEqual(-6, r.vertical, "Cross(4 5 6, 1 2 3) V");
Assert.AreEqual(3, r.depth, "Cross(4 5 6, 1 2 3) D");
v2 = new(-1, -2, -3);
r = Vector3.Cross(v1, v2);
Assert.AreEqual(-3, r.horizontal, "Cross(4 5 6, -1 -2 -3) H");
Assert.AreEqual(6, r.vertical, "Cross(4 5 6, -1 -2 -3) V");
Assert.AreEqual(-3, r.depth, "Cross(4 5 6, -1 -2 -3) D");
v2 = new(0, 0, 0);
r = Vector3.Cross(v1, v2);
Assert.AreEqual(0, r.horizontal, "Cross(4 5 6, 0 0 0) H");
Assert.AreEqual(0, r.vertical, "Cross(4 5 6, 0 0 0) V");
Assert.AreEqual(0, r.depth, "Cross(4 5 6, 0 0 0) D");
v2 = new(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
r = Vector3.Cross(v1, v2);
Assert.IsTrue(float.IsNaN(r.horizontal), "Cross(4 5 6, inf inf inf) H");
Assert.IsTrue(float.IsNaN(r.vertical), "Cross(4 5 6, inf inf inf) V");
Assert.IsTrue(float.IsNaN(r.depth), "Cross(4 5 6, inf inf inf) D");
v2 = new(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
r = Vector3.Cross(v1, v2);
Assert.IsTrue(float.IsNaN(r.horizontal), "Cross(4 5 6, -inf -inf -inf) H");
Assert.IsTrue(float.IsNaN(r.vertical), "Cross(4 5 6, -inf -inf -inf) V");
Assert.IsTrue(float.IsNaN(r.depth), "Cross(4 5 6, -inf -inf -inf) D");
}
[Test]
public void Project() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
Vector3 r;
r = Vector3.Project(v1, v2);
Assert.AreEqual(2.28571439F, r.horizontal, "Project(4 5 6, 1 2 3) H");
Assert.AreEqual(4.57142878F, r.vertical, "Project(4 5 6, 1 2 3) V");
Assert.AreEqual(6.85714293F, r.depth, "Project(4 5 6, 1 2 3) D");
v2 = new(-1, -2, -3);
r = Vector3.Project(v1, v2);
Assert.AreEqual(2.28571439F, r.horizontal, "Project(4 5 6, -1 -2 -3) H");
Assert.AreEqual(4.57142878F, r.vertical, "Project(4 5 6, -1 -2 -3) V");
Assert.AreEqual(6.85714293F, r.depth, "Project(4 5 6, -1 -2 -3) D");
v2 = new(0, 0, 0);
r = Vector3.Project(v1, v2);
Assert.AreEqual(0, r.horizontal, "Project(4 5 6, 0 0 0) H");
Assert.AreEqual(0, r.vertical, "Project(4 5 6, 0 0 0) V");
Assert.AreEqual(0, r.depth, "Project(4 5 6, 0 0 0) D");
r = Vector3.Project(v2, v1);
Assert.AreEqual(0, r.horizontal, "Project(0 0 0, 4 5 6) H");
Assert.AreEqual(0, r.vertical, "Project(0 0 0, 4 5 6) V");
Assert.AreEqual(0, r.depth, "Project(0 0 0, 4 5 6) D");
v2 = new(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
r = Vector3.Project(v1, v2);
Assert.IsTrue(float.IsNaN(r.horizontal), "Project(4 5 6, inf inf inf) H");
Assert.IsTrue(float.IsNaN(r.vertical), "Project(4 5 6, inf inf inf) V");
Assert.IsTrue(float.IsNaN(r.depth), "Project(4 5 6, inf inf inf) D");
v2 = new(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
r = Vector3.Project(v1, v2);
Assert.IsTrue(float.IsNaN(r.horizontal), "Project(4 5 6, -inf -inf -inf) H");
Assert.IsTrue(float.IsNaN(r.vertical), "Project(4 5 6, -inf -inf -inf) V");
Assert.IsTrue(float.IsNaN(r.depth), "Project(4 5 6, -inf -inf -inf) D");
}
[Test]
public void ProjectOnPlane() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
Vector3 r;
r = Vector3.ProjectOnPlane(v1, v2);
Assert.AreEqual(1.71428561F, r.horizontal, "ProjectOnPlane(4 5 6, 1 2 3) H");
Assert.AreEqual(0.428571224F, r.vertical, "ProjectOnPlane(4 5 6, 1 2 3) V");
Assert.AreEqual(-0.857142925F, r.depth, "ProjectOnPlane(4 5 6, 1 2 3) D");
v2 = new(-1, -2, -3);
r = Vector3.ProjectOnPlane(v1, v2);
Assert.AreEqual(1.71428561F, r.horizontal, "ProjectOnPlane(4 5 6, -1 -2 -3) H");
Assert.AreEqual(0.428571224F, r.vertical, "ProjectOnPlane(4 5 6, -1 -2 -3) V");
Assert.AreEqual(-0.857142925F, r.depth, "ProjectOnPlane(4 5 6, -1 -2 -3) D");
v2 = new(0, 0, 0);
r = Vector3.ProjectOnPlane(v1, v2);
Assert.AreEqual(4, r.horizontal, "ProjectOnPlane(4 5 6, 0 0 0) H");
Assert.AreEqual(5, r.vertical, "ProjectOnPlane(4 5 6, 0 0 0) V");
Assert.AreEqual(6, r.depth, "ProjectOnPlane(4 5 6, 0 0 0) D");
r = Vector3.ProjectOnPlane(v2, v1);
Assert.AreEqual(0, r.horizontal, "ProjectOnPlane(0 0 0, 4 5 6) H");
Assert.AreEqual(0, r.vertical, "ProjectOnPlane(0 0 0, 4 5 6) V");
Assert.AreEqual(0, r.depth, "ProjectOnPlane(0 0 0, 4 5 6) D");
v2 = new(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
r = Vector3.ProjectOnPlane(v1, v2);
Assert.IsTrue(float.IsNaN(r.horizontal), "ProjectOnPlane(4 5 6, inf inf inf) H");
Assert.IsTrue(float.IsNaN(r.vertical), "ProjectOnPlane(4 5 6, inf inf inf) V");
Assert.IsTrue(float.IsNaN(r.depth), "ProjectOnPlane(4 5 6, inf inf inf) D");
v2 = new(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
r = Vector3.ProjectOnPlane(v1, v2);
Assert.IsTrue(float.IsNaN(r.horizontal), "ProjectOnPlane(4 5 6, -inf -inf -inf) H");
Assert.IsTrue(float.IsNaN(r.vertical), "ProjectOnPlane(4 5 6, -inf -inf -inf) V");
Assert.IsTrue(float.IsNaN(r.depth), "ProjectOnPlane(4 5 6, -inf -inf -inf) D");
}
[Test]
public void UnsignedAngle() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
AngleFloat a;
a = Vector3.UnsignedAngle(v1, v2);
Assert.AreEqual(12.9331379F, a.inDegrees, "Angle(4 5 6, 1 2 3)");
v2 = new(-1, -2, -3);
a = Vector3.UnsignedAngle(v1, v2);
Assert.AreEqual(167.066849F, a.inDegrees, "Angle(4 5 6, -1 -2 -3)");
v2 = new(0, 0, 0);
a = Vector3.UnsignedAngle(v1, v2);
Assert.AreEqual(0, a.inDegrees, "Angle(4 5 6, 0 0 0)");
v2 = new(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
a = Vector3.UnsignedAngle(v1, v2);
Assert.IsTrue(float.IsNaN(a.inDegrees), "Angle(4 5 6, inf inf inf)");
v2 = new(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
a = Vector3.UnsignedAngle(v1, v2);
Assert.IsTrue(float.IsNaN(a.inDegrees), "Angle(4 5 6, inf inf inf)");
}
[Test]
public void SignedAngle() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
Vector3 v3 = new(7, 8, -9);
AngleFloat a;
a = Vector3.SignedAngle(v1, v2, v3);
Assert.AreEqual(-12.9331379F, a.inDegrees, "SignedAngle(4 5 6, 1 2 3, 7 8 -9)");
v2 = new(-1, -2, -3);
a = Vector3.SignedAngle(v1, v2, v3);
Assert.AreEqual(167.066849F, a.inDegrees, "SignedAngle(4 5 6, -1 -2 -3, 7 8 -9)");
v2 = new(0, 0, 0);
a = Vector3.SignedAngle(v1, v2, v3);
Assert.AreEqual(0, a.inDegrees, "SignedAngle(4 5 6, 0 0 0, 7 8 -9)");
v2 = new(1, 2, 3);
v3 = new(-7, -8, 9);
a = Vector3.SignedAngle(v1, v2, v3);
Assert.AreEqual(12.9331379F, a.inDegrees, "SignedAngle(4 5 6, 1 2 3, -7 -8 9)");
v3 = new(0, 0, 0);
a = Vector3.SignedAngle(v1, v2, v3);
Assert.AreEqual(0, a.inDegrees, "SignedAngle(4 5 6, 1 2 3, 0 0 0)");
v2 = new(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
a = Vector3.SignedAngle(v1, v2, v3);
Assert.IsTrue(float.IsNaN(a.inDegrees), "SignedAngle(4 5 6, inf inf inf, 0 0 0)");
v2 = new(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
a = Vector3.SignedAngle(v1, v2, v3);
Assert.IsTrue(float.IsNaN(a.inDegrees), "SignedAngle(4 5 6, -inf -inf -inf, 0 0 0)");
}
[Test]
public void Lerp() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
Vector3 r;
r = Vector3.Lerp(v1, v2, 0);
Assert.AreEqual(0, Vector3.Distance(r, v1), 0);
r = Vector3.Lerp(v1, v2, 1);
Assert.AreEqual(0, Vector3.Distance(r, v2), 0);
r = Vector3.Lerp(v1, v2, 0.5f);
Assert.AreEqual(0, Vector3.Distance(r, new Vector3(2.5f, 3.5f, 4.5f)), 0);
r = Vector3.Lerp(v1, v2, -1);
Assert.AreEqual(0, Vector3.Distance(r, new Vector3(7, 8, 9)), 0);
r = Vector3.Lerp(v1, v2, 2);
Assert.AreEqual(0, Vector3.Distance(r, new Vector3(-2, -1, 0)), 0);
}
}
}
#endif

View File

@ -0,0 +1,349 @@
#if !UNITY_5_6_OR_NEWER
using NUnit.Framework;
namespace LinearAlgebra.Test {
using Vector3 = Vector3Int;
public class Vector3IntTest {
[Test]
public void Equality() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
Assert.IsFalse(v1 == v2, "4 5 6 == 1 2 3");
Assert.IsTrue(v1 != v2, "4 5 6 != 1 2 3");
v2 = new(4, 5, 6);
Assert.IsTrue(v1 == v2, "4 5 6 == 4 5 6");
Assert.IsFalse(v1 != v2, "4 5 6 != 4 5 6");
}
[Test]
public void Magnitude() {
Vector3 v = new(1, 2, 3);
float m = 0;
m = v.magnitude;
Assert.AreEqual(3.7416575f, m, "magnitude 1 2 3");
m = Vector3.MagnitudeOf(v);
Assert.AreEqual(3.7416575f, m, "MagnitudeOf 1 2 3");
v = new(-1, -2, -3);
m = v.magnitude;
Assert.AreEqual(3.7416575f, m, "magnitude -1 -2 -3");
v = new(0, 0, 0);
m = v.magnitude;
Assert.AreEqual(0, m, "magnitude 0 0 0");
// Infinity tests are still missing
}
[Test]
public void SqrMagnitude() {
Vector3 v = new(1, 2, 3);
float m = 0;
m = v.sqrMagnitude;
Assert.AreEqual(14, m, "sqrMagnitude 1 2 3");
m = Vector3.SqrMagnitudeOf(v);
Assert.AreEqual(14, m, "SqrMagnitudeOf 1 2 3");
v = new(-1, -2, -3);
m = v.sqrMagnitude;
Assert.AreEqual(14, m, "sqrMagnitude -1 -2 -3");
v = new(0, 0, 0);
m = v.sqrMagnitude;
Assert.AreEqual(0, m, "sqrMagnitude 0 0 0");
// Infinity tests are still missing
}
[Test]
public void Distance() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
float f;
f = Vector3.Distance(v1, v2);
Assert.AreEqual(5.19615221F, f, "Distance(4 5 6, 1 2 3)");
v2 = new(-1, -2, -3);
f = Vector3.Distance(v1, v2);
Assert.AreEqual(12.4498997F, f, "Distance(4 5 6, -1 -2 -3)");
v2 = new(0, 0, 0);
f = Vector3.Distance(v1, v2);
Assert.AreEqual(v1.magnitude, f, "Distance(4 5 6, 0 0 0)");
}
[Test]
public void Normalize() {
Vector3 v = new(0, 2, 0);
Vector3Float r;
r = v.normalized;
//Assert.AreEqual(new Vector3(0, 1, 0), r, "normalized 0 2 0");
Assert.AreEqual(0, r.horizontal, "normalized 0 2 0");
Assert.AreEqual(1, r.vertical, "normalized 0 2 0");
Assert.AreEqual(0, r.depth, "normalized 0 2 0");
r = Vector3.Normalize(v);
Assert.AreEqual(new Vector3Float(0, 1, 0), r, "Normalize 0 2 0");
v = new(0, -2, 0);
r = v.normalized;
Assert.AreEqual(new Vector3Float(0, -1, 0), r, "normalized 0 -2 0");
v = new(0, 0, 0);
r = v.normalized;
Assert.AreEqual(new Vector3Float(0, 0, 0), r, "normalized 0 0 0");
}
[Test]
public void Negate() {
Vector3 v = new(4, 5, 6);
Vector3 r;
r = -v;
Assert.AreEqual(-4, r.horizontal, "- 4 5 6 H");
Assert.AreEqual(-5, r.vertical, "- 4 5 6 V");
Assert.AreEqual(-6, r.depth, "- 4 5 6 D");
v = new(-4, -5, -6);
r = -v;
Assert.AreEqual(4, r.horizontal, "- -4 -5 -6 H");
Assert.AreEqual(5, r.vertical, "- -4 -5 -6 V");
Assert.AreEqual(6, r.depth, "- -4 -5 -6 D");
v = new(0, 0, 0);
r = -v;
Assert.AreEqual(new Vector3(0, 0, 0), r, "- 0 0 0");
Assert.AreEqual(0, r.horizontal, "- 0 0 0 H");
Assert.AreEqual(0, r.vertical, "- 0 0 0 V");
Assert.AreEqual(0, r.depth, "- 0 0 0 D");
}
[Test]
public void Subtract() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
Vector3 r = Vector3.zero;
r = v1 - v2;
Assert.IsTrue(r == new Vector3(3, 3, 3), "4 5 6 - 1 2 3");
v2 = new(-1, -2, -3);
r = v1 - v2;
Assert.IsTrue(r == new Vector3(5, 7, 9), "4 5 6 - -1 -2 -3");
v2 = new(4, 5, 6);
r = v1 - v2;
Assert.IsTrue(r == new Vector3(0, 0, 0), "4 5 6 - 4 5 6");
r = v1;
r -= v2;
Assert.AreEqual(r, new Vector3(0, 0, 0), "4 5 6 - 4 5 6");
v2 = new(0, 0, 0);
r = v1 - v2;
Assert.AreEqual(r, new Vector3(4, 5, 6), "4 5 6 - 0 0 0");
r -= v2;
Assert.AreEqual(r, new Vector3(4, 5, 6), "4 5 6 - 0 0 0");
// Infinity tests are still missing
}
[Test]
public void Addition() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
Vector3 r = Vector3.zero;
r = v1 + v2;
Assert.IsTrue(r == new Vector3(5, 7, 9), "4 5 6 + 1 2 3");
v2 = new(-1, -2, -3);
r = v1 + v2;
Assert.IsTrue(r == new Vector3(3, 3, 3), "4 5 6 + -1 -2 -3");
r = v1;
r += v2;
Assert.AreEqual(r, new Vector3(3, 3, 3), "4 5 6 + -1 -2 -3");
v2 = new(0, 0, 0);
r = v1 + v2;
Assert.AreEqual(r, new Vector3(4, 5, 6), "4 5 6 + 0 0 0");
r += v2;
Assert.AreEqual(r, new Vector3(4, 5, 6), "4 5 6 + 0 0 0");
// Infinity tests are still missing
}
[Test]
public void Scale() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
Vector3 r;
r = Vector3.Scale(v1, v2);
Assert.AreEqual(4, r.horizontal, "Scale 4 5 6 , 1 2 3 H");
Assert.AreEqual(10, r.vertical, "Scale 4 5 6 , 1 2 3 V");
Assert.AreEqual(18, r.depth, "Scale 4 5 6 , 1 2 3 D");
v2 = new(-1, -2, -3);
r = Vector3.Scale(v1, v2);
Assert.AreEqual(-4, r.horizontal, "Scale 4 5 6 , -1 -2 -3 H");
Assert.AreEqual(-10, r.vertical, "Scale 4 5 6 , -1 -2 -3 V");
Assert.AreEqual(-18, r.depth, "Scale 4 5 6 , -1 -2 -3 D");
v2 = new(0, 0, 0);
r = Vector3.Scale(v1, v2);
Assert.AreEqual(0, r.horizontal, "Scale 4 5 6 , 0 0 0 H");
Assert.AreEqual(0, r.vertical, "Scale 4 5 6 , 0 0 0 V");
Assert.AreEqual(0, r.depth, "Scale 4 5 6 , 0 0 0 D");
}
[Test]
public void Multiply() {
Vector3 v1 = new(4, 5, 6);
int f = 3;
Vector3 r;
r = v1 * f;
Assert.AreEqual(12, r.horizontal, "4 5 6 * 3 H");
Assert.AreEqual(15, r.vertical, "4 5 6 * 3 V");
Assert.AreEqual(18, r.depth, "4 5 6 * 3 D");
r = f * v1;
Assert.AreEqual(12, r.horizontal, "3 * 4 5 6 H");
Assert.AreEqual(15, r.vertical, "3 * 4 5 6 V");
Assert.AreEqual(18, r.depth, "3 * 4 5 6 D");
f = -3;
r = v1 * f;
Assert.AreEqual(-12, r.horizontal, "4 5 6 * -3 H");
Assert.AreEqual(-15, r.vertical, "4 5 6 * -3 V");
Assert.AreEqual(-18, r.depth, "4 5 6 * -3 D");
f = 0;
r = v1 * f;
Assert.AreEqual(0, r.horizontal, "4 5 6 * 0 H");
Assert.AreEqual(0, r.vertical, "4 5 6 * 0 V");
Assert.AreEqual(0, r.depth, "4 5 6 * 0 D");
}
[Test]
public void Divide() {
Vector3 v1 = new(4, 5, 6);
int f = 2;
Vector3 r;
r = v1 / f;
Assert.AreEqual(2, r.horizontal, "4 5 6 / 2 H");
Assert.AreEqual(2, r.vertical, "4 5 6 / 2 V");
Assert.AreEqual(3, r.depth, "4 5 6 / 2 D");
f = -2;
r = v1 / f;
Assert.AreEqual(-2, r.horizontal, "4 5 6 / -2 H");
Assert.AreEqual(-2, r.vertical, "4 5 6 / -2 V");
Assert.AreEqual(-3, r.depth, "4 5 6 / -2 D");
Assert.Throws<System.DivideByZeroException>(() => {
f = 0;
r = v1 / f;
});
}
[Test]
public void Dot() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
float f;
f = Vector3.Dot(v1, v2);
Assert.AreEqual(32, f, "Dot(4 5 6, 1 2 3)");
v2 = new(-1, -2, -3);
f = Vector3.Dot(v1, v2);
Assert.AreEqual(-32, f, "Dot(4 5 6, -1 -2 -3)");
v2 = new(0, 0, 0);
f = Vector3.Dot(v1, v2);
Assert.AreEqual(0, f, "Dot(4 5 6, 0 0 0)");
}
[Test]
public void Cross() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
Vector3 r;
r = Vector3.Cross(v1, v2);
Assert.AreEqual(3, r.horizontal, "Cross(4 5 6, 1 2 3) H");
Assert.AreEqual(-6, r.vertical, "Cross(4 5 6, 1 2 3) V");
Assert.AreEqual(3, r.depth, "Cross(4 5 6, 1 2 3) D");
v2 = new(-1, -2, -3);
r = Vector3.Cross(v1, v2);
Assert.AreEqual(-3, r.horizontal, "Cross(4 5 6, -1 -2 -3) H");
Assert.AreEqual(6, r.vertical, "Cross(4 5 6, -1 -2 -3) V");
Assert.AreEqual(-3, r.depth, "Cross(4 5 6, -1 -2 -3) D");
v2 = new(0, 0, 0);
r = Vector3.Cross(v1, v2);
Assert.AreEqual(0, r.horizontal, "Cross(4 5 6, 0 0 0) H");
Assert.AreEqual(0, r.vertical, "Cross(4 5 6, 0 0 0) V");
Assert.AreEqual(0, r.depth, "Cross(4 5 6, 0 0 0) D");
}
[Test]
public void UnsignedAngle() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
AngleFloat a;
a = Vector3.UnsignedAngle(v1, v2);
Assert.AreEqual(12.9331379F, a.inDegrees, "Angle(4 5 6, 1 2 3)");
v2 = new(-1, -2, -3);
a = Vector3.UnsignedAngle(v1, v2);
Assert.AreEqual(167.066849F, a.inDegrees, "Angle(4 5 6, -1 -2 -3)");
v2 = new(0, 0, 0);
a = Vector3.UnsignedAngle(v1, v2);
Assert.AreEqual(0, a.inDegrees, "Angle(4 5 6, 0 0 0)");
}
[Test]
public void SignedAngle() {
Vector3 v1 = new(4, 5, 6);
Vector3 v2 = new(1, 2, 3);
Vector3 v3 = new(7, 8, -9);
AngleFloat a;
a = Vector3.SignedAngle(v1, v2, v3);
Assert.AreEqual(-12.9331379F, a.inDegrees, "SignedAngle(4 5 6, 1 2 3, 7 8 -9)");
v2 = new(-1, -2, -3);
a = Vector3.SignedAngle(v1, v2, v3);
Assert.AreEqual(167.066849F, a.inDegrees, "SignedAngle(4 5 6, -1 -2 -3, 7 8 -9)");
v2 = new(0, 0, 0);
a = Vector3.SignedAngle(v1, v2, v3);
Assert.AreEqual(0, a.inDegrees, "SignedAngle(4 5 6, 0 0 0, 7 8 -9)");
v2 = new(1, 2, 3);
v3 = new(-7, -8, 9);
a = Vector3.SignedAngle(v1, v2, v3);
Assert.AreEqual(12.9331379F, a.inDegrees, "SignedAngle(4 5 6, 1 2 3, -7 -8 9)");
v3 = new(0, 0, 0);
a = Vector3.SignedAngle(v1, v2, v3);
Assert.AreEqual(0, a.inDegrees, "SignedAngle(4 5 6, 1 2 3, 0 0 0)");
}
}
}
#endif

65
MemoryCell.cs Normal file
View File

@ -0,0 +1,65 @@
using System;
using UnityEngine;
using Unity.Mathematics;
using static Unity.Mathematics.math;
[Serializable]
public class MemoryCell : Neuron {
public MemoryCell(ClusterPrefab cluster, string name) : base(cluster, name) {}
#region Parameters
// Returns the memorized value weighted by time
// return lastValue * (current time - last time)
[SerializeField]
public bool deltaValue = false;
#endregion Parameters
#region State
private float3 _memorizedValue;
private float _memorizedTime;
public override void UpdateState() {
// A memorycell does not have an activation function
float3 result = new(0, 0, 0);
int n = 0;
//Applying the weight factgors
foreach (Synapse synapse in this.synapses) {
if (synapse.nucleus == this) {
float deltaTime = Time.time - this.lastTime;
synapse.weight = deltaTime;
}
result += synapse.weight * synapse.nucleus.outputValue;
if (lengthsq(synapse.nucleus.outputValue) != 0)
n++;
}
if (this.average)
result /= n;
UpdateResult(result);
}
public override void UpdateResult(Vector3 result) {
// output value is the previous value
if (this.deltaValue) {
float deltaTime = Time.time - this._memorizedTime;
this._outputValue = this._memorizedValue * deltaTime;
}
else
this._outputValue = this._memorizedValue;
// Store the result for the next time
this._memorizedValue = result;
this._memorizedTime = Time.time;
foreach (INucleus receiver in this.receivers)
receiver.UpdateState();
}
#endregion State
}

2
MemoryCell.cs.meta Normal file
View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 29633aa3fe5cd9dcc8d886051f45d4d8

View File

@ -1,19 +1,12 @@
{
"folders": [
{
"path": "."
"path": "../.."
},
{
"name": "LinearAlgebra-csharp",
"path": "LinearAlgebra-csharp"
}
],
"settings": {
"files.associations": {
"*.asset": "yaml",
"*.meta": "yaml",
"*.prefab": "yaml",
"*.unity": "yaml"
},
"dotnet.defaultSolution": "NanoBrain-Unity.sln",
"dotnet.server.useOmnisharp": true,
"omnisharp.useModernNet": false,
"dotnet.automaticallyCreateSolutionInWorkspace": false
}
"settings": {}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: c156ab45bfd15d213b1be7451d0c0151
guid: cfec45da5945b94d684a763d86b0dcf8
DefaultImporter:
externalObjects: {}
userData:

View File

@ -1,4 +0,0 @@
<Solution>
<Project Path="Assembly-CSharp.csproj" />
<Project Path="Assembly-CSharp-Editor.csproj" />
</Solution>

View File

@ -1,26 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Assembly-CSharp", "Assembly-CSharp.csproj", "{421F1D6D-0E9B-E088-7112-FB87A00D7B68}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Assembly-CSharp-Editor", "Assembly-CSharp-Editor.csproj", "{4FC2AA99-AF4E-7981-91DB-25688AE496F4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{421F1D6D-0E9B-E088-7112-FB87A00D7B68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{421F1D6D-0E9B-E088-7112-FB87A00D7B68}.Debug|Any CPU.Build.0 = Debug|Any CPU
{421F1D6D-0E9B-E088-7112-FB87A00D7B68}.Release|Any CPU.ActiveCfg = Release|Any CPU
{421F1D6D-0E9B-E088-7112-FB87A00D7B68}.Release|Any CPU.Build.0 = Release|Any CPU
{4FC2AA99-AF4E-7981-91DB-25688AE496F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4FC2AA99-AF4E-7981-91DB-25688AE496F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4FC2AA99-AF4E-7981-91DB-25688AE496F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4FC2AA99-AF4E-7981-91DB-25688AE496F4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

82
Neuroid.cs Normal file
View File

@ -0,0 +1,82 @@
/*
using UnityEngine;
using Unity.Mathematics;
using static Unity.Mathematics.math;
[System.Serializable]
public class Neuroid : Neuron {
public bool average = false;
public Neuroid(Cluster brain, string name) : base(name) {
this.cluster = brain;
if (this.cluster != null) {
this.cluster.nuclei.Add(this);
}
else
Debug.LogError("No neuroid network");
}
public Neuroid(string name) : base(name) { }
public override INucleus Clone() {
Neuroid clone = new(this.name) {
cluster = this.cluster,
array = this.array,
curve = this.curve,
curvePreset = this.curvePreset,
curveMax = this.curveMax,
average = this.average
};
if (clone.cluster != null)
clone.cluster.nuclei.Add(clone);
foreach (Synapse synapse in this.synapses) {
Synapse clonedSynapse = clone.AddSynapse(synapse.nucleus);
clonedSynapse.weight = synapse.weight;
}
foreach (INucleus receiver in this.receivers) {
clone.AddReceiver(receiver);
}
return clone;
}
public override void UpdateState() {
float3 sum = new(0, 0, 0);
int n = 0;
//Applying the weight factgors
foreach (Synapse synapse in this.synapses) {
sum = sum + (synapse.weight * synapse.nucleus.outputValue);
if (lengthsq(synapse.nucleus.outputValue) != 0)
n++;
}
if (average)
sum /= n;
// Activation function
Vector3 result;
switch (this.curvePreset) {
case CurvePresets.Linear:
result = sum;
break;
case CurvePresets.Sqrt:
result = normalize(sum) * System.MathF.Sqrt(length(sum));
break;
case CurvePresets.Power:
result = normalize(sum) * System.MathF.Pow(length(sum), 2);
break;
case CurvePresets.Reciprocal:
result = normalize(sum) * (1 / length(sum));
break;
default:
float activatedValue = this.curve.Evaluate(length(sum));
result = normalize(sum) * activatedValue;
break;
}
UpdateResult(result);
}
}
*/

2
Neuroid.cs.meta Normal file
View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 771f64aec709af240a39b1d918bbc829

321
Neuron.cs Normal file
View File

@ -0,0 +1,321 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
using Unity.Mathematics;
using static Unity.Mathematics.math;
[Serializable]
public class Neuron : INucleus {
[SerializeField]
protected string _name;
public virtual string name {
get => _name;
set => _name = value;
}
[SerializeField]
private List<Synapse> _synapses = new();
public List<Synapse> synapses => _synapses;
[SerializeReference]
private List<INucleus> _receivers = new();
public List<INucleus> receivers {
get { return _receivers; }
set { _receivers = value; }
}
[SerializeReference]
private NucleusArray _array;
public NucleusArray array {
get { return _array; }
set { _array = value; }
}
#region Serialization
public enum CurvePresets {
Linear,
Power,
Sqrt,
Reciprocal,
Custom
}
[SerializeField]
private CurvePresets _curvePreset;
public CurvePresets curvePreset {
get { return _curvePreset; }
set {
_curvePreset = value;
this.curve = GenerateCurve();
}
}
public AnimationCurve curve;
public float curveMax = 1.0f;
#region Parameters
public bool average = false;
#endregion Parameters
public AnimationCurve GenerateCurve() {
switch (this.curvePreset) {
case CurvePresets.Linear:
this.curveMax = 1;
return Presets.Linear(1);
case CurvePresets.Power:
this.curveMax = 1;
return Presets.Power(2.0f, 1);
case CurvePresets.Sqrt:
this.curveMax = 1;
return Presets.Power(0.5f, 1);
case CurvePresets.Reciprocal:
this.curveMax = 1 / 0.01f * 1;
return Presets.Reciprocal(1);
default:
this.curveMax = 1;
return this.curve;
}
}
public virtual void Deserialize(Neuron nucleus) { }
#endregion Serialization
#region Runtime state (not serialized)
public ClusterPrefab cluster { get; set; }
#region Activation
public static class Presets {
private const int samples = 32;
public static AnimationCurve Linear(float weight) {
return AnimationCurve.Linear(0f, 0f, 1000f, weight * 1000);
}
public static AnimationCurve Power(float exponent, float weight) {
// build keyframes
Keyframe[] keys = new Keyframe[samples];
for (int i = 0; i < samples; i++) {
float t = i / (float)(samples - 1);
float v = Mathf.Pow(t, exponent) * weight;
keys[i] = new Keyframe(t, v);
}
AnimationCurve curve = new(keys);
// set tangent modes for each key to Auto (smooth). Use Linear if you prefer straight segments.
for (int i = 0; i < curve.length; i++) {
AnimationUtility.SetKeyLeftTangentMode(curve, i, AnimationUtility.TangentMode.Auto);
AnimationUtility.SetKeyRightTangentMode(curve, i, AnimationUtility.TangentMode.Auto);
}
return curve;
}
public static AnimationCurve Reciprocal(float weight) {
int samples = 128;
float xMin = 0.001f;
float xMax = 1;
var keys = new Keyframe[samples];
for (int i = 0; i < samples; i++) {
float t = i / (float)(samples - 1);
float x = Mathf.Lerp(xMin, xMax, t);
float y = 1f / x * weight;
keys[i] = new Keyframe(x, y);
}
var curve = new AnimationCurve(keys);
for (int i = 0; i < curve.length; i++) {
AnimationUtility.SetKeyLeftTangentMode(curve, i, AnimationUtility.TangentMode.Linear);
AnimationUtility.SetKeyRightTangentMode(curve, i, AnimationUtility.TangentMode.Linear);
}
return curve;
}
}
#endregion Activation
protected float3 _outputValue;
public virtual float3 outputValue {
get { return _outputValue; }
set {
this.stale = 0;
// this._isSleeping = false;
_outputValue = value;
}
}
[NonSerialized]
private int stale = 1000;
// private bool _isSleeping = false;
// public bool isSleeping => _isSleeping;
public bool isSleeping => lengthsq(this.outputValue) == 0;
public float lastTime { get; private set; }
public void UpdateNuclei() {
this.stale++;
// this._isSleeping = this.stale > 2;
// if (isSleeping)
if (this.stale > 2)
_outputValue = Vector3.zero;
}
#endregion Runtime state
public Neuron(ClusterPrefab parent, string name) {
this.cluster = parent;
this.name = name;
if (this.cluster != null) {
this.cluster.nuclei.Add(this);
}
// else
// Debug.LogError("No neuroid network");
}
// public Neuron(string name) {
// this._name = name;
// }
public virtual IReceptor CloneTo(ClusterPrefab parent) {
Neuron clone = new(parent, this.name) {
array = this.array,
curve = this.curve,
curvePreset = this.curvePreset,
curveMax = this.curveMax,
average = this.average
};
// if (clone.cluster != null)
// clone.cluster.nuclei.Add(clone);
foreach (Synapse synapse in this.synapses) {
Synapse clonedSynapse = clone.AddSynapse(synapse.nucleus);
clonedSynapse.weight = synapse.weight;
}
foreach (INucleus receiver in this.receivers) {
clone.AddReceiver(receiver);
}
return clone;
}
public virtual IReceptor Clone() {
Neuron clone = new(this.cluster, this.name) {
array = this.array,
curve = this.curve,
curvePreset = this.curvePreset,
curveMax = this.curveMax,
average = this.average
};
// if (clone.cluster != null)
// clone.cluster.nuclei.Add(clone);
foreach (Synapse synapse in this.synapses) {
Synapse clonedSynapse = clone.AddSynapse(synapse.nucleus);
clonedSynapse.weight = synapse.weight;
}
foreach (INucleus receiver in this.receivers) {
clone.AddReceiver(receiver);
}
return clone;
}
public virtual void AddReceiver(INucleus receivingNucleus) {
this._receivers.Add(receivingNucleus);
receivingNucleus.AddSynapse(this);
}
public void RemoveReceiver(INucleus receiverNucleus) {
this._receivers.RemoveAll(receiver => receiver == receiverNucleus);
receiverNucleus.synapses.RemoveAll(synapse => synapse.nucleus == this);
}
public static void Delete(INucleus nucleus) {
foreach (Synapse synapse in nucleus.synapses) {
if (synapse.nucleus is Neuron synapse_nucleus) {
if (synapse_nucleus._receivers.Count > 1) {
// there is another nucleus feeding into this input nucleus
synapse_nucleus._receivers.RemoveAll(r => r == nucleus);
}
else {
// No other links, delete it.
Neuron.Delete(synapse_nucleus);
}
}
}
foreach (INucleus receiver in nucleus.receivers) {
if (receiver != null && receiver.synapses != null)
receiver.synapses.RemoveAll(s => s.nucleus == nucleus);
}
if (nucleus.cluster != null) {
nucleus.cluster.nuclei.RemoveAll(n => n == nucleus);
nucleus.cluster.GarbageCollection();
}
}
public Synapse AddSynapse(IReceptor sendingNucleus) {
Synapse synapse = new(sendingNucleus);
this.synapses.Add(synapse);
return synapse;
}
public virtual void UpdateState() {
UpdateState(new float3(0, 0, 0));
}
public virtual void UpdateState(float3 inputValue) {
float3 sum = inputValue;//new(0, 0, 0);
int n = 0;
//Applying the weight factgors
foreach (Synapse synapse in this.synapses) {
if (synapse.nucleus == this) {
float deltaTime = Time.time - this.lastTime;
synapse.weight = deltaTime;
}
sum += synapse.weight * synapse.nucleus.outputValue;
// Perhaps synapses should be removed when the output value goes to 0....
if (lengthsq(synapse.nucleus.outputValue) != 0)
n++;
}
if (this.average && n > 0)
sum /= n;
// Activation function
Vector3 result;
switch (this.curvePreset) {
case CurvePresets.Linear:
result = sum;
break;
case CurvePresets.Sqrt:
result = normalize(sum) * System.MathF.Sqrt(length(sum));
break;
case CurvePresets.Power:
result = normalize(sum) * System.MathF.Pow(length(sum), 2);
break;
case CurvePresets.Reciprocal:
result = normalize(sum) * (1 / length(sum));
break;
default:
float activatedValue = this.curve.Evaluate(length(sum));
result = normalize(sum) * activatedValue;
break;
}
UpdateResult(result);
}
public virtual void UpdateResult(Vector3 result) {
// float d = Vector3.Distance(result, this.outputValue);
// if (d < 0.5f) {
// //Debug.Log($"insignificant update: {d}");
// return;
// }
this.outputValue = result;
this.lastTime = Time.time;
foreach (INucleus receiver in this.receivers)
receiver.UpdateState();
}
}

2
Neuron.cs.meta Normal file
View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 750748f3f0e7d472fbf88ab02987074c

67
NucleusArray.cs Normal file
View File

@ -0,0 +1,67 @@
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class NucleusArray {
[SerializeReference]
private INucleus[] _nuclei;
private ClusterPrefab[] _clusters;
public IEnumerable<INucleus> nuclei {
get {
// if (_nuclei == null)
// return _clusters;
// else if (_clusters == null)
return _nuclei;
// else
// return _nuclei.Concat(_clusters);
}
}
public string name;
public NucleusArray(INucleus nucleus) {
this.name = nucleus.name;
this._nuclei = new INucleus[1];
this._nuclei[0] = nucleus;
this._clusters = new ClusterPrefab[0];
}
public NucleusArray(ClusterPrefab cluster) {
this.name = cluster.name;
this._nuclei = new INucleus[0];
this._clusters = new ClusterPrefab[1];
this._clusters[0] = cluster;
}
public void AddNucleus() {
if (this._nuclei.Length == 0) {
Debug.LogError("Empty perceptoid array, cannot add");
return;
}
int newLength = this._nuclei.Length + 1;
INucleus[] newArray = new INucleus[newLength];
for (int i = 0; i < this._nuclei.Length; i++)
newArray[i] = this._nuclei[i];
if (this._nuclei[0] is INucleus nucleus)
newArray[newLength - 1] = (INucleus) nucleus.Clone();
this._nuclei = newArray;
}
public void RemoveNucleus() {
int newLength = this._nuclei.Length - 1;
if (newLength == 0) {
Debug.LogWarning("Perceptoid array cannot be empty");
return;
}
INucleus[] newPerceptei = new INucleus[newLength];
for (int i = 0; i < newLength; i++)
newPerceptei[i] = this._nuclei[i];
// Delete the last perception
Neuron.Delete(this._nuclei[newLength]);
this._nuclei = newPerceptei;
}
}

2
NucleusArray.cs.meta Normal file
View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f8cac60bd79854595a8571c042f77998

View File

@ -1,46 +0,0 @@
{
"dependencies": {
"com.unity.ai.navigation": "2.0.9",
"com.unity.collab-proxy": "2.10.2",
"com.unity.ide.visualstudio": "2.0.26",
"com.unity.inputsystem": "1.17.0",
"com.unity.multiplayer.center": "1.0.1",
"com.unity.render-pipelines.universal": "17.3.0",
"com.unity.timeline": "1.8.9",
"com.unity.ugui": "2.0.0",
"com.unity.modules.accessibility": "1.0.0",
"com.unity.modules.adaptiveperformance": "1.0.0",
"com.unity.modules.ai": "1.0.0",
"com.unity.modules.androidjni": "1.0.0",
"com.unity.modules.animation": "1.0.0",
"com.unity.modules.assetbundle": "1.0.0",
"com.unity.modules.audio": "1.0.0",
"com.unity.modules.cloth": "1.0.0",
"com.unity.modules.director": "1.0.0",
"com.unity.modules.imageconversion": "1.0.0",
"com.unity.modules.imgui": "1.0.0",
"com.unity.modules.jsonserialize": "1.0.0",
"com.unity.modules.particlesystem": "1.0.0",
"com.unity.modules.physics": "1.0.0",
"com.unity.modules.physics2d": "1.0.0",
"com.unity.modules.screencapture": "1.0.0",
"com.unity.modules.terrain": "1.0.0",
"com.unity.modules.terrainphysics": "1.0.0",
"com.unity.modules.tilemap": "1.0.0",
"com.unity.modules.ui": "1.0.0",
"com.unity.modules.uielements": "1.0.0",
"com.unity.modules.umbra": "1.0.0",
"com.unity.modules.unityanalytics": "1.0.0",
"com.unity.modules.unitywebrequest": "1.0.0",
"com.unity.modules.unitywebrequestassetbundle": "1.0.0",
"com.unity.modules.unitywebrequestaudio": "1.0.0",
"com.unity.modules.unitywebrequesttexture": "1.0.0",
"com.unity.modules.unitywebrequestwww": "1.0.0",
"com.unity.modules.vectorgraphics": "1.0.0",
"com.unity.modules.vehicles": "1.0.0",
"com.unity.modules.video": "1.0.0",
"com.unity.modules.vr": "1.0.0",
"com.unity.modules.wind": "1.0.0",
"com.unity.modules.xr": "1.0.0"
}
}

View File

@ -1,453 +0,0 @@
{
"dependencies": {
"com.unity.ai.navigation": {
"version": "2.0.9",
"depth": 0,
"source": "registry",
"dependencies": {
"com.unity.modules.ai": "1.0.0"
},
"url": "https://packages.unity.com"
},
"com.unity.burst": {
"version": "1.8.26",
"depth": 2,
"source": "registry",
"dependencies": {
"com.unity.mathematics": "1.2.1",
"com.unity.modules.jsonserialize": "1.0.0"
},
"url": "https://packages.unity.com"
},
"com.unity.collab-proxy": {
"version": "2.10.2",
"depth": 0,
"source": "registry",
"dependencies": {},
"url": "https://packages.unity.com"
},
"com.unity.collections": {
"version": "2.6.2",
"depth": 2,
"source": "registry",
"dependencies": {
"com.unity.burst": "1.8.23",
"com.unity.mathematics": "1.3.2",
"com.unity.test-framework": "1.4.6",
"com.unity.nuget.mono-cecil": "1.11.5",
"com.unity.test-framework.performance": "3.0.3"
},
"url": "https://packages.unity.com"
},
"com.unity.ext.nunit": {
"version": "2.0.5",
"depth": 2,
"source": "builtin",
"dependencies": {}
},
"com.unity.ide.visualstudio": {
"version": "2.0.26",
"depth": 0,
"source": "registry",
"dependencies": {
"com.unity.test-framework": "1.1.33"
},
"url": "https://packages.unity.com"
},
"com.unity.inputsystem": {
"version": "1.17.0",
"depth": 0,
"source": "registry",
"dependencies": {
"com.unity.modules.uielements": "1.0.0"
},
"url": "https://packages.unity.com"
},
"com.unity.mathematics": {
"version": "1.3.3",
"depth": 2,
"source": "registry",
"dependencies": {},
"url": "https://packages.unity.com"
},
"com.unity.multiplayer.center": {
"version": "1.0.1",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.uielements": "1.0.0"
}
},
"com.unity.nuget.mono-cecil": {
"version": "1.11.6",
"depth": 3,
"source": "registry",
"dependencies": {},
"url": "https://packages.unity.com"
},
"com.unity.render-pipelines.core": {
"version": "17.3.0",
"depth": 1,
"source": "builtin",
"dependencies": {
"com.unity.burst": "1.8.14",
"com.unity.mathematics": "1.3.2",
"com.unity.ugui": "2.0.0",
"com.unity.collections": "2.4.3",
"com.unity.modules.physics": "1.0.0",
"com.unity.modules.terrain": "1.0.0",
"com.unity.modules.jsonserialize": "1.0.0"
}
},
"com.unity.render-pipelines.universal": {
"version": "17.3.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.render-pipelines.core": "17.3.0",
"com.unity.shadergraph": "17.3.0",
"com.unity.render-pipelines.universal-config": "17.0.3"
}
},
"com.unity.render-pipelines.universal-config": {
"version": "17.0.3",
"depth": 1,
"source": "builtin",
"dependencies": {
"com.unity.render-pipelines.core": "17.0.3"
}
},
"com.unity.searcher": {
"version": "4.9.4",
"depth": 2,
"source": "registry",
"dependencies": {},
"url": "https://packages.unity.com"
},
"com.unity.shadergraph": {
"version": "17.3.0",
"depth": 1,
"source": "builtin",
"dependencies": {
"com.unity.render-pipelines.core": "17.3.0",
"com.unity.searcher": "4.9.3"
}
},
"com.unity.test-framework": {
"version": "1.6.0",
"depth": 1,
"source": "builtin",
"dependencies": {
"com.unity.ext.nunit": "2.0.3",
"com.unity.modules.imgui": "1.0.0",
"com.unity.modules.jsonserialize": "1.0.0"
}
},
"com.unity.test-framework.performance": {
"version": "3.2.0",
"depth": 3,
"source": "registry",
"dependencies": {
"com.unity.test-framework": "1.1.33",
"com.unity.modules.jsonserialize": "1.0.0"
},
"url": "https://packages.unity.com"
},
"com.unity.timeline": {
"version": "1.8.9",
"depth": 0,
"source": "registry",
"dependencies": {
"com.unity.modules.audio": "1.0.0",
"com.unity.modules.director": "1.0.0",
"com.unity.modules.animation": "1.0.0",
"com.unity.modules.particlesystem": "1.0.0"
},
"url": "https://packages.unity.com"
},
"com.unity.ugui": {
"version": "2.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.ui": "1.0.0",
"com.unity.modules.imgui": "1.0.0"
}
},
"com.unity.modules.accessibility": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.adaptiveperformance": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.subsystems": "1.0.0"
}
},
"com.unity.modules.ai": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.androidjni": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.animation": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.assetbundle": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.audio": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.cloth": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.physics": "1.0.0"
}
},
"com.unity.modules.director": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.audio": "1.0.0",
"com.unity.modules.animation": "1.0.0"
}
},
"com.unity.modules.hierarchycore": {
"version": "1.0.0",
"depth": 1,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.imageconversion": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.imgui": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.jsonserialize": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.particlesystem": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.physics": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.physics2d": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.screencapture": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.imageconversion": "1.0.0"
}
},
"com.unity.modules.subsystems": {
"version": "1.0.0",
"depth": 1,
"source": "builtin",
"dependencies": {
"com.unity.modules.jsonserialize": "1.0.0"
}
},
"com.unity.modules.terrain": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.terrainphysics": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.physics": "1.0.0",
"com.unity.modules.terrain": "1.0.0"
}
},
"com.unity.modules.tilemap": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.physics2d": "1.0.0"
}
},
"com.unity.modules.ui": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.uielements": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.ui": "1.0.0",
"com.unity.modules.imgui": "1.0.0",
"com.unity.modules.jsonserialize": "1.0.0",
"com.unity.modules.hierarchycore": "1.0.0",
"com.unity.modules.physics": "1.0.0"
}
},
"com.unity.modules.umbra": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.unityanalytics": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.unitywebrequest": "1.0.0",
"com.unity.modules.jsonserialize": "1.0.0"
}
},
"com.unity.modules.unitywebrequest": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.unitywebrequestassetbundle": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.assetbundle": "1.0.0",
"com.unity.modules.unitywebrequest": "1.0.0"
}
},
"com.unity.modules.unitywebrequestaudio": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.unitywebrequest": "1.0.0",
"com.unity.modules.audio": "1.0.0"
}
},
"com.unity.modules.unitywebrequesttexture": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.unitywebrequest": "1.0.0",
"com.unity.modules.imageconversion": "1.0.0"
}
},
"com.unity.modules.unitywebrequestwww": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.unitywebrequest": "1.0.0",
"com.unity.modules.unitywebrequestassetbundle": "1.0.0",
"com.unity.modules.unitywebrequestaudio": "1.0.0",
"com.unity.modules.audio": "1.0.0",
"com.unity.modules.assetbundle": "1.0.0",
"com.unity.modules.imageconversion": "1.0.0"
}
},
"com.unity.modules.vectorgraphics": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.uielements": "1.0.0",
"com.unity.modules.imageconversion": "1.0.0",
"com.unity.modules.imgui": "1.0.0"
}
},
"com.unity.modules.vehicles": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.physics": "1.0.0"
}
},
"com.unity.modules.video": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.audio": "1.0.0",
"com.unity.modules.ui": "1.0.0",
"com.unity.modules.unitywebrequest": "1.0.0"
}
},
"com.unity.modules.vr": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.jsonserialize": "1.0.0",
"com.unity.modules.physics": "1.0.0",
"com.unity.modules.xr": "1.0.0"
}
},
"com.unity.modules.wind": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.xr": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.physics": "1.0.0",
"com.unity.modules.jsonserialize": "1.0.0",
"com.unity.modules.subsystems": "1.0.0"
}
}
}
}

105
Perceptoid.cs Normal file
View File

@ -0,0 +1,105 @@
/*
using UnityEngine;
[System.Serializable]
public class Perceptoid : Neuroid {
// A neuroid which has no neurons as input
// But receives value from a receptor
public NanoBrain brain;
public Receptor receptor;
public string baseName;
public int thingId;
//[SerializeField]
// Needs serialization!!!!
[SerializeReference]
public PercepteiArray array;
#region Serialization
[SerializeField]
public int thingType;
public override void Rebuild(NanoBrain brain) {
base.Rebuild(brain);
this.receptor = Receptor.GetReceptor(brain, thingType);
this.receptor.perceptei.Add(this);
if (string.IsNullOrEmpty(this.baseName))
this.baseName = this.name;
}
public override void Deserialize(Nucleus nucleus) {
base.Deserialize(nucleus);
if (nucleus is Perceptoid perceptoid)
this.receptor.thingType = perceptoid.thingType;
// Point all receivers to this perceptoid instead of the default nucleus
foreach (INucleus receiver in nucleus.receivers) {
foreach (Synapse synapse in receiver.synapses) {
if (synapse.nucleus == nucleus)
synapse.nucleus = this;
}
}
// Point all synapses to this perceptoid instead of the default nucleus
// foreach (Synapse synapse in nucleus.synapses) {
// foreach (INucleus r in synapse.nucleus.receivers) {
// if (r == nucleus)
// this.receiver = this;
// }
// }
// Copying disabled for now
// // Copy all the synapses
// this.synapses = nucleus.synapses;
// // Copy all receivers
// this.receivers = nucleus.receivers;
}
#endregion Serialization
public Perceptoid(NanoBrain brain, int thingType, string name = "sensor") : base(name) {
this.brain = brain;
this.cluster = brain.cluster;
if (this.cluster != null) {
brain.perceptei.Add(this);
}
else
Debug.LogError("No neuroid network");
this.nucleusType = nameof(Perceptoid);
this.name = name;
this.baseName = name;
this.thingType = thingType;
this.receptor = Receptor.GetReceptor(brain, thingType);
this.receptor.perceptei.Add(this);
this.array = new PercepteiArray(this);
}
public Perceptoid(PercepteiArray array) : base(array.name) {
this.array = array;
Perceptoid source = array.perceptei[0];
this.brain = source.brain;
this.cluster = source.cluster;
if (this.brain != null) {
this.brain.perceptei.Add(this);
}
else
Debug.LogError("No neuroid network");
this.nucleusType = nameof(Perceptoid);
this.name = source.baseName;
this.baseName = source.baseName;
this.thingType = source.thingType;
this.receptor = Receptor.GetReceptor(this.brain, this.thingType);
this.receptor.perceptei.Add(this);
}
public override void UpdateState() {
Vector3 result = this.receptor.localPosition;
UpdateResult(result);
}
}
*/

2
Perceptoid.cs.meta Normal file
View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 702f634001a21a9d7ae1057c8ce356e9

View File

@ -1,19 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!11 &1
AudioManager:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Volume: 1
Rolloff Scale: 1
Doppler Factor: 1
Default Speaker Mode: 2
m_SampleRate: 0
m_DSPBufferSize: 1024
m_VirtualVoiceCount: 512
m_RealVoiceCount: 32
m_SpatializerPlugin:
m_AmbisonicDecoderPlugin:
m_DisableAudio: 0
m_VirtualizeEffects: 1
m_RequestedDSPBufferSize: 0

View File

@ -1,6 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!236 &1
ClusterInputManager:
m_ObjectHideFlags: 0
m_Inputs: []

View File

@ -1,36 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!55 &1
PhysicsManager:
m_ObjectHideFlags: 0
serializedVersion: 13
m_Gravity: {x: 0, y: -9.81, z: 0}
m_DefaultMaterial: {fileID: 0}
m_BounceThreshold: 2
m_SleepThreshold: 0.005
m_DefaultContactOffset: 0.01
m_DefaultSolverIterations: 6
m_DefaultSolverVelocityIterations: 1
m_QueriesHitBackfaces: 0
m_QueriesHitTriggers: 1
m_EnableAdaptiveForce: 0
m_ClothInterCollisionDistance: 0.1
m_ClothInterCollisionStiffness: 0.2
m_ContactsGeneration: 1
m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
m_AutoSimulation: 1
m_AutoSyncTransforms: 0
m_ReuseCollisionCallbacks: 1
m_ClothInterCollisionSettingsToggle: 0
m_ClothGravity: {x: 0, y: -9.81, z: 0}
m_ContactPairsMode: 0
m_BroadphaseType: 0
m_WorldBounds:
m_Center: {x: 0, y: 0, z: 0}
m_Extent: {x: 250, y: 250, z: 250}
m_WorldSubdivisions: 8
m_FrictionType: 0
m_EnableEnhancedDeterminism: 0
m_EnableUnifiedHeightmaps: 1
m_SolverType: 0
m_DefaultMaxAngularSpeed: 50

View File

@ -1,13 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1045 &1
EditorBuildSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Scenes:
- enabled: 1
path: Assets/Scenes/SampleScene.unity
guid: 99c9720ab356a0642a771bea13969a05
m_configObjects:
com.unity.input.settings.actions: {fileID: -944628639613478452, guid: 052faaac586de48259a63d0c4782560b, type: 3}
m_UseUCBPForAssetBundles: 0

View File

@ -1,50 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!159 &1
EditorSettings:
m_ObjectHideFlags: 0
serializedVersion: 15
m_SerializationMode: 2
m_LineEndingsForNewScripts: 0
m_DefaultBehaviorMode: 0
m_PrefabRegularEnvironment: {fileID: 0}
m_PrefabUIEnvironment: {fileID: 0}
m_SpritePackerMode: 0
m_SpritePackerCacheSize: 10
m_SpritePackerPaddingPower: 1
m_Bc7TextureCompressor: 0
m_EtcTextureCompressorBehavior: 1
m_EtcTextureFastCompressor: 1
m_EtcTextureNormalCompressor: 2
m_EtcTextureBestCompressor: 4
m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;rsp;asmref
m_ProjectGenerationRootNamespace:
m_EnableTextureStreamingInEditMode: 1
m_EnableTextureStreamingInPlayMode: 1
m_EnableEditorAsyncCPUTextureLoading: 0
m_AsyncShaderCompilation: 1
m_PrefabModeAllowAutoSave: 1
m_EnterPlayModeOptionsEnabled: 1
m_EnterPlayModeOptions: 0
m_GameObjectNamingDigits: 1
m_GameObjectNamingScheme: 0
m_AssetNamingUsesSpace: 1
m_InspectorUseIMGUIDefaultInspector: 0
m_UseLegacyProbeSampleCount: 0
m_SerializeInlineMappingsOnOneLine: 1
m_DisableCookiesInLightmapper: 0
m_ShadowmaskStitching: 0
m_AssetPipelineMode: 1
m_RefreshImportMode: 0
m_CacheServerMode: 0
m_CacheServerEndpoint:
m_CacheServerNamespacePrefix: default
m_CacheServerEnableDownload: 1
m_CacheServerEnableUpload: 1
m_CacheServerEnableAuth: 0
m_CacheServerEnableTls: 0
m_CacheServerValidationMode: 2
m_CacheServerDownloadBatchSize: 128
m_EnableEnlightenBakedGI: 0
m_ReferencedClipsExactNaming: 1
m_ForceAssetUnloadAndGCOnSceneLoad: 1

View File

@ -1,67 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!30 &1
GraphicsSettings:
m_ObjectHideFlags: 0
serializedVersion: 16
m_Deferred:
m_Mode: 1
m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0}
m_DeferredReflections:
m_Mode: 1
m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0}
m_ScreenSpaceShadows:
m_Mode: 1
m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0}
m_DepthNormals:
m_Mode: 1
m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0}
m_MotionVectors:
m_Mode: 1
m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0}
m_LightHalo:
m_Mode: 1
m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0}
m_LensFlare:
m_Mode: 1
m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0}
m_VideoShadersIncludeMode: 2
m_AlwaysIncludedShaders:
- {fileID: 7, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 10783, guid: 0000000000000000f000000000000000, type: 0}
m_PreloadedShaders: []
m_PreloadShadersBatchTimeLimit: -1
m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0}
m_CustomRenderPipeline: {fileID: 11400000, guid: 4b83569d67af61e458304325a23e5dfd, type: 2}
m_TransparencySortMode: 0
m_TransparencySortAxis: {x: 0, y: 0, z: 1}
m_DefaultRenderingPath: 1
m_DefaultMobileRenderingPath: 1
m_TierSettings: []
m_LightmapStripping: 0
m_FogStripping: 0
m_InstancingStripping: 0
m_BrgStripping: 0
m_LightmapKeepPlain: 1
m_LightmapKeepDirCombined: 1
m_LightmapKeepDynamicPlain: 1
m_LightmapKeepDynamicDirCombined: 1
m_LightmapKeepShadowMask: 1
m_LightmapKeepSubtractive: 1
m_FogKeepLinear: 1
m_FogKeepExp: 1
m_FogKeepExp2: 1
m_AlbedoSwatchInfos: []
m_RenderPipelineGlobalSettingsMap:
UnityEngine.Rendering.Universal.UniversalRenderPipeline: {fileID: 11400000, guid: 370d27fa5af5291e18529fa336759ac9, type: 2}
m_LightsUseLinearIntensity: 1
m_LightsUseColorTemperature: 1
m_LogWhenShaderIsCompiled: 0
m_LightProbeOutsideHullStrategy: 0
m_CameraRelativeLightCulling: 0
m_CameraRelativeShadowCulling: 0

View File

@ -1,487 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!13 &1
InputManager:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Axes:
- serializedVersion: 3
m_Name: Horizontal
descriptiveName:
descriptiveNegativeName:
negativeButton: left
positiveButton: right
altNegativeButton: a
altPositiveButton: d
gravity: 3
dead: 0.001
sensitivity: 3
snap: 1
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Vertical
descriptiveName:
descriptiveNegativeName:
negativeButton: down
positiveButton: up
altNegativeButton: s
altPositiveButton: w
gravity: 3
dead: 0.001
sensitivity: 3
snap: 1
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Fire1
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: left ctrl
altNegativeButton:
altPositiveButton: mouse 0
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Fire2
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: left alt
altNegativeButton:
altPositiveButton: mouse 1
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Fire3
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: left shift
altNegativeButton:
altPositiveButton: mouse 2
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Jump
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: space
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Mouse X
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton:
altNegativeButton:
altPositiveButton:
gravity: 0
dead: 0
sensitivity: 0.1
snap: 0
invert: 0
type: 1
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Mouse Y
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton:
altNegativeButton:
altPositiveButton:
gravity: 0
dead: 0
sensitivity: 0.1
snap: 0
invert: 0
type: 1
axis: 1
joyNum: 0
- serializedVersion: 3
m_Name: Mouse ScrollWheel
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton:
altNegativeButton:
altPositiveButton:
gravity: 0
dead: 0
sensitivity: 0.1
snap: 0
invert: 0
type: 1
axis: 2
joyNum: 0
- serializedVersion: 3
m_Name: Horizontal
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton:
altNegativeButton:
altPositiveButton:
gravity: 0
dead: 0.19
sensitivity: 1
snap: 0
invert: 0
type: 2
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Vertical
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton:
altNegativeButton:
altPositiveButton:
gravity: 0
dead: 0.19
sensitivity: 1
snap: 0
invert: 1
type: 2
axis: 1
joyNum: 0
- serializedVersion: 3
m_Name: Fire1
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: joystick button 0
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Fire2
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: joystick button 1
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Fire3
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: joystick button 2
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Jump
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: joystick button 3
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Submit
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: return
altNegativeButton:
altPositiveButton: joystick button 0
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Submit
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: enter
altNegativeButton:
altPositiveButton: space
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Cancel
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: escape
altNegativeButton:
altPositiveButton: joystick button 1
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Enable Debug Button 1
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: left ctrl
altNegativeButton:
altPositiveButton: joystick button 8
gravity: 0
dead: 0
sensitivity: 0
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Enable Debug Button 2
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: backspace
altNegativeButton:
altPositiveButton: joystick button 9
gravity: 0
dead: 0
sensitivity: 0
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Debug Reset
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: left alt
altNegativeButton:
altPositiveButton: joystick button 1
gravity: 0
dead: 0
sensitivity: 0
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Debug Next
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: page down
altNegativeButton:
altPositiveButton: joystick button 5
gravity: 0
dead: 0
sensitivity: 0
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Debug Previous
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: page up
altNegativeButton:
altPositiveButton: joystick button 4
gravity: 0
dead: 0
sensitivity: 0
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Debug Validate
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: return
altNegativeButton:
altPositiveButton: joystick button 0
gravity: 0
dead: 0
sensitivity: 0
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Debug Persistent
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: right shift
altNegativeButton:
altPositiveButton: joystick button 2
gravity: 0
dead: 0
sensitivity: 0
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Debug Multiplier
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: left shift
altNegativeButton:
altPositiveButton: joystick button 3
gravity: 0
dead: 0
sensitivity: 0
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Debug Horizontal
descriptiveName:
descriptiveNegativeName:
negativeButton: left
positiveButton: right
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Debug Vertical
descriptiveName:
descriptiveNegativeName:
negativeButton: down
positiveButton: up
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Debug Vertical
descriptiveName:
descriptiveNegativeName:
negativeButton: down
positiveButton: up
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 2
axis: 6
joyNum: 0
- serializedVersion: 3
m_Name: Debug Horizontal
descriptiveName:
descriptiveNegativeName:
negativeButton: left
positiveButton: right
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 2
axis: 5
joyNum: 0

View File

@ -1,35 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!387306366 &1
MemorySettings:
m_ObjectHideFlags: 0
m_EditorMemorySettings:
m_MainAllocatorBlockSize: -1
m_ThreadAllocatorBlockSize: -1
m_MainGfxBlockSize: -1
m_ThreadGfxBlockSize: -1
m_CacheBlockSize: -1
m_TypetreeBlockSize: -1
m_ProfilerBlockSize: -1
m_ProfilerEditorBlockSize: -1
m_BucketAllocatorGranularity: -1
m_BucketAllocatorBucketsCount: -1
m_BucketAllocatorBlockSize: -1
m_BucketAllocatorBlockCount: -1
m_ProfilerBucketAllocatorGranularity: -1
m_ProfilerBucketAllocatorBucketsCount: -1
m_ProfilerBucketAllocatorBlockSize: -1
m_ProfilerBucketAllocatorBlockCount: -1
m_TempAllocatorSizeMain: -1
m_JobTempAllocatorBlockSize: -1
m_BackgroundJobTempAllocatorBlockSize: -1
m_JobTempAllocatorReducedBlockSize: -1
m_TempAllocatorSizeGIBakingWorker: -1
m_TempAllocatorSizeNavMeshWorker: -1
m_TempAllocatorSizeAudioWorker: -1
m_TempAllocatorSizeCloudWorker: -1
m_TempAllocatorSizeGfx: -1
m_TempAllocatorSizeJobWorker: -1
m_TempAllocatorSizeBackgroundWorker: -1
m_TempAllocatorSizePreloadManager: -1
m_PlatformMemorySettings: {}

View File

@ -1,7 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!655991488 &1
MultiplayerManager:
m_ObjectHideFlags: 0
m_EnableMultiplayerRoles: 0
m_StrippingTypes: {}

View File

@ -1,91 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!126 &1
NavMeshProjectSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
areas:
- name: Walkable
cost: 1
- name: Not Walkable
cost: 1
- name: Jump
cost: 2
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
m_LastAgentTypeID: -887442657
m_Settings:
- serializedVersion: 2
agentTypeID: 0
agentRadius: 0.5
agentHeight: 2
agentSlope: 45
agentClimb: 0.75
ledgeDropHeight: 0
maxJumpAcrossDistance: 0
minRegionArea: 2
manualCellSize: 0
cellSize: 0.16666667
manualTileSize: 0
tileSize: 256
accuratePlacement: 0
debug:
m_Flags: 0
m_SettingNames:
- Humanoid

View File

@ -1,43 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &1
MonoBehaviour:
m_ObjectHideFlags: 61
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_EnablePreviewPackages: 0
m_EnablePackageDependencies: 0
m_AdvancedSettingsExpanded: 1
m_ScopedRegistriesSettingsExpanded: 1
oneTimeWarningShown: 0
m_Registries:
- m_Id: main
m_Name:
m_Url: https://packages.unity.com
m_Scopes: []
m_IsDefault: 1
m_Capabilities: 7
m_UserSelectedRegistryName:
m_UserAddingNewScopedRegistry: 0
m_RegistryInfoDraft:
m_ErrorMessage:
m_Original:
m_Id:
m_Name:
m_Url:
m_Scopes: []
m_IsDefault: 0
m_Capabilities: 0
m_Modified: 0
m_Name:
m_Url:
m_Scopes:
-
m_SelectedScopeIndex: 0

View File

@ -1,17 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &1
MonoBehaviour:
m_ObjectHideFlags: 53
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 15023, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier: UnityEditor.MultiplayerModule.dll::UnityEditor.Multiplayer.Internal.MultiplayerRolesSettings
m_MultiplayerRoleForClassicProfile:
m_Keys: []
m_Values:

View File

@ -1,56 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!19 &1
Physics2DSettings:
m_ObjectHideFlags: 0
serializedVersion: 4
m_Gravity: {x: 0, y: -9.81}
m_DefaultMaterial: {fileID: 0}
m_VelocityIterations: 8
m_PositionIterations: 3
m_VelocityThreshold: 1
m_MaxLinearCorrection: 0.2
m_MaxAngularCorrection: 8
m_MaxTranslationSpeed: 100
m_MaxRotationSpeed: 360
m_BaumgarteScale: 0.2
m_BaumgarteTimeOfImpactScale: 0.75
m_TimeToSleep: 0.5
m_LinearSleepTolerance: 0.01
m_AngularSleepTolerance: 2
m_DefaultContactOffset: 0.01
m_JobOptions:
serializedVersion: 2
useMultithreading: 0
useConsistencySorting: 0
m_InterpolationPosesPerJob: 100
m_NewContactsPerJob: 30
m_CollideContactsPerJob: 100
m_ClearFlagsPerJob: 200
m_ClearBodyForcesPerJob: 200
m_SyncDiscreteFixturesPerJob: 50
m_SyncContinuousFixturesPerJob: 50
m_FindNearestContactsPerJob: 100
m_UpdateTriggerContactsPerJob: 100
m_IslandSolverCostThreshold: 100
m_IslandSolverBodyCostScale: 1
m_IslandSolverContactCostScale: 10
m_IslandSolverJointCostScale: 10
m_IslandSolverBodiesPerJob: 50
m_IslandSolverContactsPerJob: 50
m_AutoSimulation: 1
m_QueriesHitTriggers: 1
m_QueriesStartInColliders: 1
m_CallbacksOnDisable: 1
m_ReuseCollisionCallbacks: 0
m_AutoSyncTransforms: 0
m_AlwaysShowColliders: 0
m_ShowColliderSleep: 1
m_ShowColliderContacts: 0
m_ShowColliderAABB: 0
m_ContactArrowScale: 0.2
m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412}
m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432}
m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745}
m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804}
m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

View File

@ -1,7 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1386491679 &1
PresetManager:
m_ObjectHideFlags: 0
serializedVersion: 2
m_DefaultPresets: {}

View File

@ -1,942 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!129 &1
PlayerSettings:
m_ObjectHideFlags: 0
serializedVersion: 28
productGUID: ae734b3b09caebd4aa3fc5596884a057
AndroidProfiler: 0
AndroidFilterTouchesWhenObscured: 0
AndroidEnableSustainedPerformanceMode: 0
defaultScreenOrientation: 4
targetDevice: 2
useOnDemandResources: 0
accelerometerFrequency: 60
companyName: DefaultCompany
productName: NanoBrain
defaultCursor: {fileID: 0}
cursorHotspot: {x: 0, y: 0}
m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1}
m_ShowUnitySplashScreen: 1
m_ShowUnitySplashLogo: 1
m_SplashScreenOverlayOpacity: 1
m_SplashScreenAnimation: 1
m_SplashScreenLogoStyle: 1
m_SplashScreenDrawMode: 0
m_SplashScreenBackgroundAnimationZoom: 1
m_SplashScreenLogoAnimationZoom: 1
m_SplashScreenBackgroundLandscapeAspect: 1
m_SplashScreenBackgroundPortraitAspect: 1
m_SplashScreenBackgroundLandscapeUvs:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
m_SplashScreenBackgroundPortraitUvs:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
m_SplashScreenLogos: []
m_VirtualRealitySplashScreen: {fileID: 0}
m_HolographicTrackingLossScreen: {fileID: 0}
defaultScreenWidth: 1024
defaultScreenHeight: 768
defaultScreenWidthWeb: 960
defaultScreenHeightWeb: 600
m_StereoRenderingPath: 0
m_ActiveColorSpace: 1
unsupportedMSAAFallback: 0
m_SpriteBatchMaxVertexCount: 65535
m_SpriteBatchVertexThreshold: 300
m_MTRendering: 1
mipStripping: 0
numberOfMipsStripped: 0
numberOfMipsStrippedPerMipmapLimitGroup: {}
m_StackTraceTypes: 010000000100000001000000010000000100000001000000
iosShowActivityIndicatorOnLoading: -1
androidShowActivityIndicatorOnLoading: -1
iosUseCustomAppBackgroundBehavior: 0
allowedAutorotateToPortrait: 1
allowedAutorotateToPortraitUpsideDown: 1
allowedAutorotateToLandscapeRight: 1
allowedAutorotateToLandscapeLeft: 1
useOSAutorotation: 1
use32BitDisplayBuffer: 1
preserveFramebufferAlpha: 0
disableDepthAndStencilBuffers: 0
androidStartInFullscreen: 1
androidRenderOutsideSafeArea: 1
androidUseSwappy: 0
androidDisplayOptions: 1
androidBlitType: 0
androidResizeableActivity: 1
androidDefaultWindowWidth: 1920
androidDefaultWindowHeight: 1080
androidMinimumWindowWidth: 400
androidMinimumWindowHeight: 300
androidFullscreenMode: 1
androidAutoRotationBehavior: 1
androidPredictiveBackSupport: 1
androidApplicationEntry: 2
defaultIsNativeResolution: 1
macRetinaSupport: 1
runInBackground: 0
muteOtherAudioSources: 0
Prepare IOS For Recording: 0
Force IOS Speakers When Recording: 0
audioSpatialExperience: 0
deferSystemGesturesMode: 0
hideHomeButton: 0
submitAnalytics: 1
usePlayerLog: 1
dedicatedServerOptimizations: 1
bakeCollisionMeshes: 0
forceSingleInstance: 0
useFlipModelSwapchain: 1
resizableWindow: 0
useMacAppStoreValidation: 0
macAppStoreCategory: public.app-category.games
gpuSkinning: 1
meshDeformation: 2
xboxPIXTextureCapture: 0
xboxEnableAvatar: 0
xboxEnableKinect: 0
xboxEnableKinectAutoTracking: 0
xboxEnableFitness: 0
visibleInBackground: 1
allowFullscreenSwitch: 1
fullscreenMode: 1
xboxSpeechDB: 0
xboxEnableHeadOrientation: 0
xboxEnableGuest: 0
xboxEnablePIXSampling: 0
metalFramebufferOnly: 0
xboxOneResolution: 0
xboxOneSResolution: 0
xboxOneXResolution: 3
xboxOneMonoLoggingLevel: 0
xboxOneLoggingLevel: 1
xboxOneDisableEsram: 0
xboxOneEnableTypeOptimization: 0
xboxOnePresentImmediateThreshold: 0
switchQueueCommandMemory: 1048576
switchQueueControlMemory: 16384
switchQueueComputeMemory: 262144
switchNVNShaderPoolsGranularity: 33554432
switchNVNDefaultPoolsGranularity: 16777216
switchNVNOtherPoolsGranularity: 16777216
switchGpuScratchPoolGranularity: 2097152
switchAllowGpuScratchShrinking: 0
switchNVNMaxPublicTextureIDCount: 0
switchNVNMaxPublicSamplerIDCount: 0
switchMaxWorkerMultiple: 8
switchNVNGraphicsFirmwareMemory: 32
switchGraphicsJobsSyncAfterKick: 1
vulkanNumSwapchainBuffers: 3
vulkanEnableSetSRGBWrite: 0
vulkanEnablePreTransform: 1
vulkanEnableLateAcquireNextImage: 0
vulkanEnableCommandBufferRecycling: 1
loadStoreDebugModeEnabled: 0
visionOSBundleVersion: 1.0
tvOSBundleVersion: 1.0
bundleVersion: 0.1.0
preloadedAssets: []
metroInputSource: 0
wsaTransparentSwapchain: 0
m_HolographicPauseOnTrackingLoss: 1
xboxOneDisableKinectGpuReservation: 1
xboxOneEnable7thCore: 1
vrSettings:
enable360StereoCapture: 0
isWsaHolographicRemotingEnabled: 0
enableFrameTimingStats: 0
enableOpenGLProfilerGPURecorders: 1
allowHDRDisplaySupport: 0
useHDRDisplay: 0
hdrBitDepth: 0
m_ColorGamuts: 00000000
targetPixelDensity: 30
resolutionScalingMode: 0
resetResolutionOnWindowResize: 0
androidSupportedAspectRatio: 1
androidMaxAspectRatio: 2.4
androidMinAspectRatio: 1
applicationIdentifier:
Android: com.UnityTechnologies.com.unity.template.urpblank
Standalone: com.Unity-Technologies.com.unity.template.urp-blank
iPhone: com.Unity-Technologies.com.unity.template.urp-blank
buildNumber:
Standalone: 0
VisionOS: 0
iPhone: 0
tvOS: 0
overrideDefaultApplicationIdentifier: 1
AndroidBundleVersionCode: 1
AndroidMinSdkVersion: 25
AndroidTargetSdkVersion: 0
AndroidPreferredInstallLocation: 1
AndroidPreferredDataLocation: 1
aotOptions:
stripEngineCode: 1
iPhoneStrippingLevel: 0
iPhoneScriptCallOptimization: 0
ForceInternetPermission: 0
ForceSDCardPermission: 0
CreateWallpaper: 0
androidSplitApplicationBinary: 0
keepLoadedShadersAlive: 0
StripUnusedMeshComponents: 0
strictShaderVariantMatching: 0
VertexChannelCompressionMask: 4054
iPhoneSdkVersion: 988
iOSSimulatorArchitecture: 0
iOSTargetOSVersionString: 15.0
tvOSSdkVersion: 0
tvOSSimulatorArchitecture: 0
tvOSRequireExtendedGameController: 0
tvOSTargetOSVersionString: 15.0
VisionOSSdkVersion: 0
VisionOSTargetOSVersionString: 1.0
uIPrerenderedIcon: 0
uIRequiresPersistentWiFi: 0
uIRequiresFullScreen: 1
uIStatusBarHidden: 1
uIExitOnSuspend: 0
uIStatusBarStyle: 0
appleTVSplashScreen: {fileID: 0}
appleTVSplashScreen2x: {fileID: 0}
tvOSSmallIconLayers: []
tvOSSmallIconLayers2x: []
tvOSLargeIconLayers: []
tvOSLargeIconLayers2x: []
tvOSTopShelfImageLayers: []
tvOSTopShelfImageLayers2x: []
tvOSTopShelfImageWideLayers: []
tvOSTopShelfImageWideLayers2x: []
iOSLaunchScreenType: 0
iOSLaunchScreenPortrait: {fileID: 0}
iOSLaunchScreenLandscape: {fileID: 0}
iOSLaunchScreenBackgroundColor:
serializedVersion: 2
rgba: 0
iOSLaunchScreenFillPct: 100
iOSLaunchScreenSize: 100
iOSLaunchScreeniPadType: 0
iOSLaunchScreeniPadImage: {fileID: 0}
iOSLaunchScreeniPadBackgroundColor:
serializedVersion: 2
rgba: 0
iOSLaunchScreeniPadFillPct: 100
iOSLaunchScreeniPadSize: 100
iOSLaunchScreenCustomStoryboardPath:
iOSLaunchScreeniPadCustomStoryboardPath:
iOSDeviceRequirements: []
iOSURLSchemes: []
macOSURLSchemes: []
iOSBackgroundModes: 0
iOSMetalForceHardShadows: 0
metalEditorSupport: 1
metalAPIValidation: 1
metalCompileShaderBinary: 0
iOSRenderExtraFrameOnPause: 0
iosCopyPluginsCodeInsteadOfSymlink: 0
appleDeveloperTeamID:
iOSManualSigningProvisioningProfileID:
tvOSManualSigningProvisioningProfileID:
VisionOSManualSigningProvisioningProfileID:
iOSManualSigningProvisioningProfileType: 0
tvOSManualSigningProvisioningProfileType: 0
VisionOSManualSigningProvisioningProfileType: 0
appleEnableAutomaticSigning: 0
iOSRequireARKit: 0
iOSAutomaticallyDetectAndAddCapabilities: 1
appleEnableProMotion: 0
shaderPrecisionModel: 0
clonedFromGUID: 3c72c65a16f0acb438eed22b8b16c24a
templatePackageId: com.unity.template.urp-blank@17.0.14
templateDefaultScene: Assets/Scenes/SampleScene.unity
useCustomMainManifest: 0
useCustomLauncherManifest: 0
useCustomMainGradleTemplate: 0
useCustomLauncherGradleManifest: 0
useCustomBaseGradleTemplate: 0
useCustomGradlePropertiesTemplate: 0
useCustomGradleSettingsTemplate: 0
useCustomProguardFile: 0
AndroidTargetArchitectures: 2
AndroidAllowedArchitectures: -1
AndroidSplashScreenScale: 0
androidSplashScreen: {fileID: 0}
AndroidKeystoreName:
AndroidKeyaliasName:
AndroidEnableArmv9SecurityFeatures: 0
AndroidEnableArm64MTE: 0
AndroidBuildApkPerCpuArchitecture: 0
AndroidTVCompatibility: 0
AndroidIsGame: 1
androidAppCategory: 3
useAndroidAppCategory: 1
androidAppCategoryOther:
AndroidEnableTango: 0
androidEnableBanner: 1
androidUseLowAccuracyLocation: 0
androidUseCustomKeystore: 0
m_AndroidBanners:
- width: 320
height: 180
banner: {fileID: 0}
androidGamepadSupportLevel: 0
AndroidMinifyRelease: 0
AndroidMinifyDebug: 0
AndroidValidateAppBundleSize: 1
AndroidAppBundleSizeToValidate: 150
AndroidReportGooglePlayAppDependencies: 1
androidSymbolsSizeThreshold: 800
m_BuildTargetIcons: []
m_BuildTargetPlatformIcons:
- m_BuildTarget: iPhone
m_Icons:
- m_Textures: []
m_Width: 180
m_Height: 180
m_Kind: 0
m_SubKind: iPhone
- m_Textures: []
m_Width: 120
m_Height: 120
m_Kind: 0
m_SubKind: iPhone
- m_Textures: []
m_Width: 167
m_Height: 167
m_Kind: 0
m_SubKind: iPad
- m_Textures: []
m_Width: 152
m_Height: 152
m_Kind: 0
m_SubKind: iPad
- m_Textures: []
m_Width: 76
m_Height: 76
m_Kind: 0
m_SubKind: iPad
- m_Textures: []
m_Width: 120
m_Height: 120
m_Kind: 3
m_SubKind: iPhone
- m_Textures: []
m_Width: 80
m_Height: 80
m_Kind: 3
m_SubKind: iPhone
- m_Textures: []
m_Width: 80
m_Height: 80
m_Kind: 3
m_SubKind: iPad
- m_Textures: []
m_Width: 40
m_Height: 40
m_Kind: 3
m_SubKind: iPad
- m_Textures: []
m_Width: 87
m_Height: 87
m_Kind: 1
m_SubKind: iPhone
- m_Textures: []
m_Width: 58
m_Height: 58
m_Kind: 1
m_SubKind: iPhone
- m_Textures: []
m_Width: 29
m_Height: 29
m_Kind: 1
m_SubKind: iPhone
- m_Textures: []
m_Width: 58
m_Height: 58
m_Kind: 1
m_SubKind: iPad
- m_Textures: []
m_Width: 29
m_Height: 29
m_Kind: 1
m_SubKind: iPad
- m_Textures: []
m_Width: 60
m_Height: 60
m_Kind: 2
m_SubKind: iPhone
- m_Textures: []
m_Width: 40
m_Height: 40
m_Kind: 2
m_SubKind: iPhone
- m_Textures: []
m_Width: 40
m_Height: 40
m_Kind: 2
m_SubKind: iPad
- m_Textures: []
m_Width: 20
m_Height: 20
m_Kind: 2
m_SubKind: iPad
- m_Textures: []
m_Width: 1024
m_Height: 1024
m_Kind: 4
m_SubKind: App Store
- m_BuildTarget: Android
m_Icons:
- m_Textures: []
m_Width: 432
m_Height: 432
m_Kind: 2
m_SubKind:
- m_Textures: []
m_Width: 324
m_Height: 324
m_Kind: 2
m_SubKind:
- m_Textures: []
m_Width: 216
m_Height: 216
m_Kind: 2
m_SubKind:
- m_Textures: []
m_Width: 162
m_Height: 162
m_Kind: 2
m_SubKind:
- m_Textures: []
m_Width: 108
m_Height: 108
m_Kind: 2
m_SubKind:
- m_Textures: []
m_Width: 81
m_Height: 81
m_Kind: 2
m_SubKind:
- m_Textures: []
m_Width: 192
m_Height: 192
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 144
m_Height: 144
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 96
m_Height: 96
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 72
m_Height: 72
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 48
m_Height: 48
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 36
m_Height: 36
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 192
m_Height: 192
m_Kind: 0
m_SubKind:
- m_Textures: []
m_Width: 144
m_Height: 144
m_Kind: 0
m_SubKind:
- m_Textures: []
m_Width: 96
m_Height: 96
m_Kind: 0
m_SubKind:
- m_Textures: []
m_Width: 72
m_Height: 72
m_Kind: 0
m_SubKind:
- m_Textures: []
m_Width: 48
m_Height: 48
m_Kind: 0
m_SubKind:
- m_Textures: []
m_Width: 36
m_Height: 36
m_Kind: 0
m_SubKind:
- m_BuildTarget: tvOS
m_Icons:
- m_Textures: []
m_Width: 1280
m_Height: 768
m_Kind: 0
m_SubKind:
- m_Textures: []
m_Width: 800
m_Height: 480
m_Kind: 0
m_SubKind:
- m_Textures: []
m_Width: 400
m_Height: 240
m_Kind: 0
m_SubKind:
- m_Textures: []
m_Width: 4640
m_Height: 1440
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 2320
m_Height: 720
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 3840
m_Height: 1440
m_Kind: 1
m_SubKind:
- m_Textures: []
m_Width: 1920
m_Height: 720
m_Kind: 1
m_SubKind:
m_BuildTargetBatching: []
m_BuildTargetShaderSettings: []
m_BuildTargetGraphicsJobs: []
m_BuildTargetGraphicsJobMode: []
m_BuildTargetGraphicsAPIs:
- m_BuildTarget: iOSSupport
m_APIs: 10000000
m_Automatic: 1
- m_BuildTarget: AndroidPlayer
m_APIs: 150000000b000000
m_Automatic: 0
m_BuildTargetVRSettings: []
m_DefaultShaderChunkSizeInMB: 16
m_DefaultShaderChunkCount: 0
openGLRequireES31: 0
openGLRequireES31AEP: 0
openGLRequireES32: 0
m_TemplateCustomTags: {}
mobileMTRendering:
Android: 1
iPhone: 1
tvOS: 1
m_BuildTargetGroupLightmapEncodingQuality:
- serializedVersion: 2
m_BuildTarget: Android
m_EncodingQuality: 1
m_BuildTargetGroupHDRCubemapEncodingQuality: []
m_BuildTargetGroupLightmapSettings: []
m_BuildTargetGroupLoadStoreDebugModeSettings: []
m_BuildTargetNormalMapEncoding:
- m_BuildTarget: Android
m_Encoding: 1
m_BuildTargetDefaultTextureCompressionFormat:
- serializedVersion: 3
m_BuildTarget: Android
m_Formats: 03000000
playModeTestRunnerEnabled: 0
runPlayModeTestAsEditModeTest: 0
actionOnDotNetUnhandledException: 1
editorGfxJobOverride: 1
enableInternalProfiler: 0
logObjCUncaughtExceptions: 1
enableCrashReportAPI: 0
cameraUsageDescription:
locationUsageDescription:
microphoneUsageDescription:
bluetoothUsageDescription:
macOSTargetOSVersion: 12.0
switchNMETAOverride:
switchNetLibKey:
switchSocketMemoryPoolSize: 6144
switchSocketAllocatorPoolSize: 128
switchSocketConcurrencyLimit: 14
switchScreenResolutionBehavior: 2
switchUseCPUProfiler: 0
switchEnableFileSystemTrace: 0
switchLTOSetting: 0
switchApplicationID: 0x01004b9000490000
switchNSODependencies:
switchCompilerFlags:
switchTitleNames_0:
switchTitleNames_1:
switchTitleNames_2:
switchTitleNames_3:
switchTitleNames_4:
switchTitleNames_5:
switchTitleNames_6:
switchTitleNames_7:
switchTitleNames_8:
switchTitleNames_9:
switchTitleNames_10:
switchTitleNames_11:
switchTitleNames_12:
switchTitleNames_13:
switchTitleNames_14:
switchTitleNames_15:
switchPublisherNames_0:
switchPublisherNames_1:
switchPublisherNames_2:
switchPublisherNames_3:
switchPublisherNames_4:
switchPublisherNames_5:
switchPublisherNames_6:
switchPublisherNames_7:
switchPublisherNames_8:
switchPublisherNames_9:
switchPublisherNames_10:
switchPublisherNames_11:
switchPublisherNames_12:
switchPublisherNames_13:
switchPublisherNames_14:
switchPublisherNames_15:
switchIcons_0: {fileID: 0}
switchIcons_1: {fileID: 0}
switchIcons_2: {fileID: 0}
switchIcons_3: {fileID: 0}
switchIcons_4: {fileID: 0}
switchIcons_5: {fileID: 0}
switchIcons_6: {fileID: 0}
switchIcons_7: {fileID: 0}
switchIcons_8: {fileID: 0}
switchIcons_9: {fileID: 0}
switchIcons_10: {fileID: 0}
switchIcons_11: {fileID: 0}
switchIcons_12: {fileID: 0}
switchIcons_13: {fileID: 0}
switchIcons_14: {fileID: 0}
switchIcons_15: {fileID: 0}
switchSmallIcons_0: {fileID: 0}
switchSmallIcons_1: {fileID: 0}
switchSmallIcons_2: {fileID: 0}
switchSmallIcons_3: {fileID: 0}
switchSmallIcons_4: {fileID: 0}
switchSmallIcons_5: {fileID: 0}
switchSmallIcons_6: {fileID: 0}
switchSmallIcons_7: {fileID: 0}
switchSmallIcons_8: {fileID: 0}
switchSmallIcons_9: {fileID: 0}
switchSmallIcons_10: {fileID: 0}
switchSmallIcons_11: {fileID: 0}
switchSmallIcons_12: {fileID: 0}
switchSmallIcons_13: {fileID: 0}
switchSmallIcons_14: {fileID: 0}
switchSmallIcons_15: {fileID: 0}
switchManualHTML:
switchAccessibleURLs:
switchLegalInformation:
switchMainThreadStackSize: 1048576
switchPresenceGroupId:
switchLogoHandling: 0
switchReleaseVersion: 0
switchDisplayVersion: 1.0.0
switchStartupUserAccount: 0
switchSupportedLanguagesMask: 0
switchLogoType: 0
switchApplicationErrorCodeCategory:
switchUserAccountSaveDataSize: 0
switchUserAccountSaveDataJournalSize: 0
switchApplicationAttribute: 0
switchCardSpecSize: -1
switchCardSpecClock: -1
switchRatingsMask: 0
switchRatingsInt_0: 0
switchRatingsInt_1: 0
switchRatingsInt_2: 0
switchRatingsInt_3: 0
switchRatingsInt_4: 0
switchRatingsInt_5: 0
switchRatingsInt_6: 0
switchRatingsInt_7: 0
switchRatingsInt_8: 0
switchRatingsInt_9: 0
switchRatingsInt_10: 0
switchRatingsInt_11: 0
switchRatingsInt_12: 0
switchLocalCommunicationIds_0:
switchLocalCommunicationIds_1:
switchLocalCommunicationIds_2:
switchLocalCommunicationIds_3:
switchLocalCommunicationIds_4:
switchLocalCommunicationIds_5:
switchLocalCommunicationIds_6:
switchLocalCommunicationIds_7:
switchParentalControl: 0
switchAllowsScreenshot: 1
switchAllowsVideoCapturing: 1
switchAllowsRuntimeAddOnContentInstall: 0
switchDataLossConfirmation: 0
switchUserAccountLockEnabled: 0
switchSystemResourceMemory: 16777216
switchSupportedNpadStyles: 22
switchNativeFsCacheSize: 32
switchIsHoldTypeHorizontal: 0
switchSupportedNpadCount: 8
switchEnableTouchScreen: 1
switchSocketConfigEnabled: 0
switchTcpInitialSendBufferSize: 32
switchTcpInitialReceiveBufferSize: 64
switchTcpAutoSendBufferSizeMax: 256
switchTcpAutoReceiveBufferSizeMax: 256
switchUdpSendBufferSize: 9
switchUdpReceiveBufferSize: 42
switchSocketBufferEfficiency: 4
switchSocketInitializeEnabled: 1
switchNetworkInterfaceManagerInitializeEnabled: 1
switchDisableHTCSPlayerConnection: 0
switchUseNewStyleFilepaths: 0
switchUseLegacyFmodPriorities: 0
switchUseMicroSleepForYield: 1
switchEnableRamDiskSupport: 0
switchMicroSleepForYieldTime: 25
switchRamDiskSpaceSize: 12
switchUpgradedPlayerSettingsToNMETA: 0
ps4NPAgeRating: 12
ps4NPTitleSecret:
ps4NPTrophyPackPath:
ps4ParentalLevel: 11
ps4ContentID: ED1633-NPXX51362_00-0000000000000000
ps4Category: 0
ps4MasterVersion: 01.00
ps4AppVersion: 01.00
ps4AppType: 0
ps4ParamSfxPath:
ps4VideoOutPixelFormat: 0
ps4VideoOutInitialWidth: 1920
ps4VideoOutBaseModeInitialWidth: 1920
ps4VideoOutReprojectionRate: 60
ps4PronunciationXMLPath:
ps4PronunciationSIGPath:
ps4BackgroundImagePath:
ps4StartupImagePath:
ps4StartupImagesFolder:
ps4IconImagesFolder:
ps4SaveDataImagePath:
ps4SdkOverride:
ps4BGMPath:
ps4ShareFilePath:
ps4ShareOverlayImagePath:
ps4PrivacyGuardImagePath:
ps4ExtraSceSysFile:
ps4NPtitleDatPath:
ps4RemotePlayKeyAssignment: -1
ps4RemotePlayKeyMappingDir:
ps4PlayTogetherPlayerCount: 0
ps4EnterButtonAssignment: 2
ps4ApplicationParam1: 0
ps4ApplicationParam2: 0
ps4ApplicationParam3: 0
ps4ApplicationParam4: 0
ps4DownloadDataSize: 0
ps4GarlicHeapSize: 2048
ps4ProGarlicHeapSize: 2560
playerPrefsMaxSize: 32768
ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ
ps4pnSessions: 1
ps4pnPresence: 1
ps4pnFriends: 1
ps4pnGameCustomData: 1
playerPrefsSupport: 0
enableApplicationExit: 0
resetTempFolder: 1
restrictedAudioUsageRights: 0
ps4UseResolutionFallback: 0
ps4ReprojectionSupport: 0
ps4UseAudio3dBackend: 0
ps4UseLowGarlicFragmentationMode: 1
ps4SocialScreenEnabled: 0
ps4ScriptOptimizationLevel: 2
ps4Audio3dVirtualSpeakerCount: 14
ps4attribCpuUsage: 0
ps4PatchPkgPath:
ps4PatchLatestPkgPath:
ps4PatchChangeinfoPath:
ps4PatchDayOne: 0
ps4attribUserManagement: 0
ps4attribMoveSupport: 0
ps4attrib3DSupport: 0
ps4attribShareSupport: 0
ps4attribExclusiveVR: 0
ps4disableAutoHideSplash: 0
ps4videoRecordingFeaturesUsed: 0
ps4contentSearchFeaturesUsed: 0
ps4CompatibilityPS5: 0
ps4AllowPS5Detection: 0
ps4GPU800MHz: 1
ps4attribEyeToEyeDistanceSettingVR: 0
ps4IncludedModules: []
ps4attribVROutputEnabled: 0
monoEnv:
splashScreenBackgroundSourceLandscape: {fileID: 0}
splashScreenBackgroundSourcePortrait: {fileID: 0}
blurSplashScreenBackground: 1
spritePackerPolicy:
webGLMemorySize: 32
webGLExceptionSupport: 1
webGLNameFilesAsHashes: 0
webGLShowDiagnostics: 0
webGLDataCaching: 1
webGLDebugSymbols: 0
webGLEmscriptenArgs:
webGLModulesDirectory:
webGLTemplate: APPLICATION:Default
webGLAnalyzeBuildSize: 0
webGLUseEmbeddedResources: 0
webGLCompressionFormat: 0
webGLWasmArithmeticExceptions: 0
webGLLinkerTarget: 1
webGLThreadsSupport: 0
webGLDecompressionFallback: 0
webGLInitialMemorySize: 32
webGLMaximumMemorySize: 2048
webGLMemoryGrowthMode: 2
webGLMemoryLinearGrowthStep: 16
webGLMemoryGeometricGrowthStep: 0.2
webGLMemoryGeometricGrowthCap: 96
webGLPowerPreference: 2
webGLWebAssemblyTable: 0
webGLWebAssemblyBigInt: 0
webGLCloseOnQuit: 0
webWasm2023: 0
webEnableSubmoduleStrippingCompatibility: 0
scriptingDefineSymbols: {}
additionalCompilerArguments: {}
platformArchitecture: {}
scriptingBackend:
Android: 1
il2cppCompilerConfiguration: {}
il2cppCodeGeneration: {}
il2cppStacktraceInformation: {}
managedStrippingLevel: {}
incrementalIl2cppBuild: {}
suppressCommonWarnings: 1
allowUnsafeCode: 0
useDeterministicCompilation: 1
additionalIl2CppArgs:
scriptingRuntimeVersion: 1
gcIncremental: 1
gcWBarrierValidation: 0
apiCompatibilityLevelPerPlatform: {}
editorAssembliesCompatibilityLevel: 2
m_RenderingPath: 1
m_MobileRenderingPath: 1
metroPackageName: NanoBrain
metroPackageVersion:
metroCertificatePath:
metroCertificatePassword:
metroCertificateSubject:
metroCertificateIssuer:
metroCertificateNotAfter: 0000000000000000
metroApplicationDescription: NanoBrain
wsaImages: {}
metroTileShortName:
metroTileShowName: 0
metroMediumTileShowName: 0
metroLargeTileShowName: 0
metroWideTileShowName: 0
metroSupportStreamingInstall: 0
metroLastRequiredScene: 0
metroDefaultTileSize: 1
metroTileForegroundText: 2
metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0}
metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, a: 1}
metroSplashScreenUseBackgroundColor: 0
syncCapabilities: 0
platformCapabilities: {}
metroTargetDeviceFamilies: {}
metroFTAName:
metroFTAFileTypes: []
metroProtocolName:
vcxProjDefaultLanguage:
XboxOneProductId:
XboxOneUpdateKey:
XboxOneSandboxId:
XboxOneContentId:
XboxOneTitleId:
XboxOneSCId:
XboxOneGameOsOverridePath:
XboxOnePackagingOverridePath:
XboxOneAppManifestOverridePath:
XboxOneVersion: 1.0.0.0
XboxOnePackageEncryption: 0
XboxOnePackageUpdateGranularity: 2
XboxOneDescription:
XboxOneLanguage:
- enus
XboxOneCapability: []
XboxOneGameRating: {}
XboxOneIsContentPackage: 0
XboxOneEnhancedXboxCompatibilityMode: 0
XboxOneEnableGPUVariability: 1
XboxOneSockets: {}
XboxOneSplashScreen: {fileID: 0}
XboxOneAllowedProductIds: []
XboxOnePersistentLocalStorageSize: 0
XboxOneXTitleMemory: 8
XboxOneOverrideIdentityName:
XboxOneOverrideIdentityPublisher:
vrEditorSettings: {}
cloudServicesEnabled: {}
luminIcon:
m_Name:
m_ModelFolderPath:
m_PortalFolderPath:
luminCert:
m_CertPath:
m_SignPackage: 1
luminIsChannelApp: 0
luminVersion:
m_VersionCode: 1
m_VersionName:
hmiPlayerDataPath:
hmiForceSRGBBlit: 1
embeddedLinuxEnableGamepadInput: 0
hmiCpuConfiguration:
hmiLogStartupTiming: 0
qnxGraphicConfPath:
apiCompatibilityLevel: 3
captureStartupLogs: {}
activeInputHandler: 1
windowsGamepadBackendHint: 0
cloudProjectId:
framebufferDepthMemorylessMode: 0
qualitySettingsNames: []
projectName:
organizationId:
cloudEnabled: 0
legacyClampBlendShapeWeights: 0
hmiLoadingImage: {fileID: 0}
platformRequiresReadableAssets: 0
virtualTexturingSupportEnabled: 0
insecureHttpOption: 0
androidVulkanDenyFilterList: []
androidVulkanAllowFilterList: []
androidVulkanDeviceFilterListAsset: {fileID: 0}
d3d12DeviceFilterListAsset: {fileID: 0}
allowedHttpConnections: 3

Some files were not shown because too many files have changed in this diff Show More