├── .editorconfig ├── .github └── workflows │ └── tests.yml ├── .gitignore ├── GenericVectors ├── GenericVectors.csproj ├── Program.cs ├── TestingHelpers │ └── AssertExtensions.cs ├── Tests │ ├── GenericAssertExtensions.cs │ ├── GenericMathHelper.cs │ ├── GenericMatrix3x2DoubleTests.cs │ ├── GenericMatrix3x2SingleTests.cs │ ├── GenericMatrix4x4DoubleTests.cs │ ├── GenericMatrix4x4SingleTests.cs │ ├── GenericPlaneDoubleTests.cs │ ├── GenericPlaneSingleTests.cs │ ├── GenericQuaternionDoubleTests.cs │ ├── GenericQuaternionSingleTests.cs │ ├── GenericVector2DoubleTests.cs │ ├── GenericVector2SingleTests.cs │ ├── GenericVector2Tests.cs │ ├── GenericVector3Double.cs │ ├── GenericVector3SingleTests.cs │ ├── GenericVector4DoubleTests.cs │ └── GenericVector4SingleTests.cs └── src │ ├── Matrix3x2S.cs │ ├── Matrix3x2_1.cs │ ├── Matrix4x4S.cs │ ├── Matrix4x4_1.cs │ ├── PlaneS.cs │ ├── Plane_1.cs │ ├── QuaternionS.cs │ ├── Quaternion_1.cs │ ├── SRInternals.cs │ ├── Vector2S.cs │ ├── Vector2_1.cs │ ├── Vector3S.cs │ ├── Vector3_1.cs │ ├── Vector4S.cs │ └── Vector4_1.cs ├── LICENSE.TXT ├── README.md └── generic-vectors-matrices.sln /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Default settings: 7 | # A newline ending every file 8 | # Use 4 spaces as indentation 9 | [*] 10 | insert_final_newline = true 11 | indent_style = space 12 | indent_size = 4 13 | trim_trailing_whitespace = true 14 | 15 | [project.json] 16 | indent_size = 2 17 | 18 | # Generated code 19 | [*{_AssemblyInfo.cs,.notsupported.cs}] 20 | generated_code = true 21 | 22 | # C# files 23 | [*.cs] 24 | # New line preferences 25 | csharp_new_line_before_open_brace = all 26 | csharp_new_line_before_else = true 27 | csharp_new_line_before_catch = true 28 | csharp_new_line_before_finally = true 29 | csharp_new_line_before_members_in_object_initializers = true 30 | csharp_new_line_before_members_in_anonymous_types = true 31 | csharp_new_line_between_query_expression_clauses = true 32 | 33 | # Indentation preferences 34 | csharp_indent_block_contents = true 35 | csharp_indent_braces = false 36 | csharp_indent_case_contents = true 37 | csharp_indent_case_contents_when_block = true 38 | csharp_indent_switch_labels = true 39 | csharp_indent_labels = one_less_than_current 40 | 41 | # Modifier preferences 42 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion 43 | 44 | # avoid this. unless absolutely necessary 45 | dotnet_style_qualification_for_field = false:suggestion 46 | dotnet_style_qualification_for_property = false:suggestion 47 | dotnet_style_qualification_for_method = false:suggestion 48 | dotnet_style_qualification_for_event = false:suggestion 49 | 50 | # Types: use keywords instead of BCL types, and permit var only when the type is clear 51 | csharp_style_var_for_built_in_types = false:suggestion 52 | csharp_style_var_when_type_is_apparent = false:none 53 | csharp_style_var_elsewhere = false:suggestion 54 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion 55 | dotnet_style_predefined_type_for_member_access = true:suggestion 56 | 57 | # name all constant fields using PascalCase 58 | dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion 59 | dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields 60 | dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style 61 | dotnet_naming_symbols.constant_fields.applicable_kinds = field 62 | dotnet_naming_symbols.constant_fields.required_modifiers = const 63 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case 64 | 65 | # static fields should have s_ prefix 66 | dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion 67 | dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields 68 | dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style 69 | dotnet_naming_symbols.static_fields.applicable_kinds = field 70 | dotnet_naming_symbols.static_fields.required_modifiers = static 71 | dotnet_naming_symbols.static_fields.applicable_accessibilities = private, internal, private_protected 72 | dotnet_naming_style.static_prefix_style.required_prefix = s_ 73 | dotnet_naming_style.static_prefix_style.capitalization = camel_case 74 | 75 | # internal and private fields should be _camelCase 76 | dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion 77 | dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields 78 | dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style 79 | dotnet_naming_symbols.private_internal_fields.applicable_kinds = field 80 | dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal 81 | dotnet_naming_style.camel_case_underscore_style.required_prefix = _ 82 | dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case 83 | 84 | # Code style defaults 85 | csharp_using_directive_placement = outside_namespace:suggestion 86 | dotnet_sort_system_directives_first = true 87 | csharp_prefer_braces = true:silent 88 | csharp_preserve_single_line_blocks = true:none 89 | csharp_preserve_single_line_statements = false:none 90 | csharp_prefer_static_local_function = true:suggestion 91 | csharp_prefer_simple_using_statement = false:none 92 | csharp_style_prefer_switch_expression = true:suggestion 93 | 94 | # Code quality 95 | dotnet_style_readonly_field = true:suggestion 96 | dotnet_code_quality_unused_parameters = non_public:suggestion 97 | 98 | # Expression-level preferences 99 | dotnet_style_object_initializer = true:suggestion 100 | dotnet_style_collection_initializer = true:suggestion 101 | dotnet_style_explicit_tuple_names = true:suggestion 102 | dotnet_style_coalesce_expression = true:suggestion 103 | dotnet_style_null_propagation = true:suggestion 104 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion 105 | dotnet_style_prefer_inferred_tuple_names = true:suggestion 106 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion 107 | dotnet_style_prefer_auto_properties = true:suggestion 108 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent 109 | dotnet_style_prefer_conditional_expression_over_return = true:silent 110 | csharp_prefer_simple_default_expression = true:suggestion 111 | 112 | # Expression-bodied members 113 | csharp_style_expression_bodied_methods = true:silent 114 | csharp_style_expression_bodied_constructors = true:silent 115 | csharp_style_expression_bodied_operators = true:silent 116 | csharp_style_expression_bodied_properties = true:silent 117 | csharp_style_expression_bodied_indexers = true:silent 118 | csharp_style_expression_bodied_accessors = true:silent 119 | csharp_style_expression_bodied_lambdas = true:silent 120 | csharp_style_expression_bodied_local_functions = true:silent 121 | 122 | # Pattern matching 123 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 124 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 125 | csharp_style_inlined_variable_declaration = true:suggestion 126 | 127 | # Null checking preferences 128 | csharp_style_throw_expression = true:suggestion 129 | csharp_style_conditional_delegate_call = true:suggestion 130 | 131 | # Other features 132 | csharp_style_prefer_index_operator = false:none 133 | csharp_style_prefer_range_operator = false:none 134 | csharp_style_pattern_local_over_anonymous_function = false:none 135 | 136 | # Space preferences 137 | csharp_space_after_cast = false 138 | csharp_space_after_colon_in_inheritance_clause = true 139 | csharp_space_after_comma = true 140 | csharp_space_after_dot = false 141 | csharp_space_after_keywords_in_control_flow_statements = true 142 | csharp_space_after_semicolon_in_for_statement = true 143 | csharp_space_around_binary_operators = before_and_after 144 | csharp_space_around_declaration_statements = do_not_ignore 145 | csharp_space_before_colon_in_inheritance_clause = true 146 | csharp_space_before_comma = false 147 | csharp_space_before_dot = false 148 | csharp_space_before_open_square_brackets = false 149 | csharp_space_before_semicolon_in_for_statement = false 150 | csharp_space_between_empty_square_brackets = false 151 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 152 | csharp_space_between_method_call_name_and_opening_parenthesis = false 153 | csharp_space_between_method_call_parameter_list_parentheses = false 154 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 155 | csharp_space_between_method_declaration_name_and_open_parenthesis = false 156 | csharp_space_between_method_declaration_parameter_list_parentheses = false 157 | csharp_space_between_parentheses = false 158 | csharp_space_between_square_brackets = false 159 | 160 | # Analyzers 161 | dotnet_code_quality.CA1052.api_surface = private, internal 162 | dotnet_code_quality.CA1802.api_surface = private, internal 163 | dotnet_code_quality.CA1822.api_surface = private, internal 164 | dotnet_code_quality.CA2208.api_surface = public 165 | 166 | # License header 167 | file_header_template = Licensed to the .NET Foundation under one or more agreements.\nThe .NET Foundation licenses this file to you under the MIT license. 168 | 169 | # C++ Files 170 | [*.{cpp,h,in}] 171 | curly_bracket_next_line = true 172 | indent_brace_style = Allman 173 | 174 | # Xml project files 175 | [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}] 176 | indent_size = 2 177 | 178 | [*.{csproj,vbproj,proj,nativeproj,locproj}] 179 | charset = utf-8 180 | 181 | # Xml build files 182 | [*.builds] 183 | indent_size = 2 184 | 185 | # Xml files 186 | [*.{xml,stylecop,resx,ruleset}] 187 | indent_size = 2 188 | 189 | # Xml config files 190 | [*.{props,targets,config,nuspec}] 191 | indent_size = 2 192 | 193 | # YAML config files 194 | [*.{yml,yaml}] 195 | indent_size = 2 196 | 197 | # Shell scripts 198 | [*.sh] 199 | end_of_line = lf 200 | [*.{cmd,bat}] 201 | end_of_line = crlf -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: .NET 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: windows-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Setup .NET 17 | uses: actions/setup-dotnet@v1 18 | with: 19 | dotnet-version: '7.0.x' 20 | include-prerelease: true 21 | - name: Restore dependencies 22 | run: dotnet restore 23 | - name: Build 24 | run: dotnet build --no-restore 25 | - name: Test 26 | run: dotnet test --no-build --verbosity normal 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd -------------------------------------------------------------------------------- /GenericVectors/GenericVectors.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | generic_vectors_matrices 7 | enable 8 | AllEnabledByDefault 9 | 11 10 | true 11 | true 12 | 13 | 14 | 15 | True 16 | 17 | 18 | 19 | True 20 | 21 | 22 | 23 | 24 | 25 | 26 | runtime; build; native; contentfiles; analyzers; buildtransitive 27 | all 28 | 29 | 30 | runtime; build; native; contentfiles; analyzers; buildtransitive 31 | all 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /GenericVectors/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Numerics; 3 | 4 | var transform = Matrix3x2S.CreateTranslation(-100.0, -100.0); 5 | transform *= Matrix3x2S.CreateScale(2.0, 2.0); 6 | transform *= Matrix3x2S.CreateTranslation(100.0, 100.0); 7 | 8 | var point = new Vector2(105, 110); 9 | var transformed = Vector2S.Transform(point, transform); 10 | 11 | // <105, 110> => <110, 120> 12 | Console.Write($"{point} => {transformed}"); 13 | -------------------------------------------------------------------------------- /GenericVectors/Tests/GenericAssertExtensions.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Numerics; 5 | using Xunit; 6 | 7 | namespace System 8 | { 9 | public static class GenericAssertExtensions 10 | { 11 | private const int Precision = 5; 12 | 13 | public static void Equal(Vector2 expected, Vector2 actual) 14 | { 15 | Assert.Equal((float)expected.X, (float)actual.X, Precision); 16 | Assert.Equal((float)expected.Y, (float)actual.Y, Precision); 17 | } 18 | 19 | public static void Equal(Vector2 expected, Vector2 actual) 20 | { 21 | Assert.Equal(expected.X, actual.X, Precision); 22 | Assert.Equal(expected.Y, actual.Y, Precision); 23 | } 24 | 25 | public static void Equal(Vector2 expected, Vector2 actual) 26 | { 27 | Assert.Equal(expected.X, actual.X, Precision); 28 | Assert.Equal(expected.Y, actual.Y, Precision); 29 | } 30 | 31 | public static void Equal(Vector3 expected, Vector3 actual) 32 | { 33 | Assert.Equal((float)expected.X, (float)actual.X, Precision); 34 | Assert.Equal((float)expected.Y, (float)actual.Y, Precision); 35 | Assert.Equal((float)expected.Z, (float)actual.Z, Precision); 36 | } 37 | 38 | public static void Equal(Vector3 expected, Vector3 actual) 39 | { 40 | Assert.Equal(expected.X, actual.X, Precision); 41 | Assert.Equal(expected.Y, actual.Y, Precision); 42 | Assert.Equal(expected.Z, actual.Z, Precision); 43 | } 44 | 45 | public static void Equal(Vector3 expected, Vector3 actual) 46 | { 47 | Assert.Equal(expected.X, actual.X, Precision); 48 | Assert.Equal(expected.Y, actual.Y, Precision); 49 | Assert.Equal(expected.Z, actual.Z, Precision); 50 | } 51 | 52 | public static void Equal(Vector4 expected, Vector4 actual) 53 | { 54 | Assert.Equal((float)expected.X, (float)actual.X, Precision); 55 | Assert.Equal((float)expected.Y, (float)actual.Y, Precision); 56 | Assert.Equal((float)expected.Z, (float)actual.Z, Precision); 57 | Assert.Equal((float)expected.W, (float)actual.W, Precision); 58 | } 59 | 60 | public static void Equal(Vector4 expected, Vector4 actual) 61 | { 62 | Assert.Equal(expected.X, actual.X, Precision); 63 | Assert.Equal(expected.Y, actual.Y, Precision); 64 | Assert.Equal(expected.Z, actual.Z, Precision); 65 | Assert.Equal(expected.W, actual.W, Precision); 66 | } 67 | 68 | public static void Equal(Vector4 expected, Vector4 actual) 69 | { 70 | Assert.Equal(expected.X, actual.X, Precision); 71 | Assert.Equal(expected.Y, actual.Y, Precision); 72 | Assert.Equal(expected.Z, actual.Z, Precision); 73 | Assert.Equal(expected.W, actual.W, Precision); 74 | } 75 | 76 | public static void Equal(Quaternion expected, Quaternion actual) 77 | { 78 | Assert.Equal((float)expected.X, (float)actual.X, Precision); 79 | Assert.Equal((float)expected.Y, (float)actual.Y, Precision); 80 | Assert.Equal((float)expected.Z, (float)actual.Z, Precision); 81 | Assert.Equal((float)expected.W, (float)actual.W, Precision); 82 | } 83 | 84 | public static void Equal(Quaternion expected, Quaternion actual) 85 | { 86 | Assert.Equal(expected.X, actual.X, Precision); 87 | Assert.Equal(expected.Y, actual.Y, Precision); 88 | Assert.Equal(expected.Z, actual.Z, Precision); 89 | Assert.Equal(expected.W, actual.W, Precision); 90 | } 91 | 92 | public static void Equal(Quaternion expected, Quaternion actual) 93 | { 94 | Assert.Equal(expected.X, actual.X, Precision); 95 | Assert.Equal(expected.Y, actual.Y, Precision); 96 | Assert.Equal(expected.Z, actual.Z, Precision); 97 | Assert.Equal(expected.W, actual.W, Precision); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /GenericVectors/Tests/GenericMathHelper.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace System.Numerics.Tests 5 | { 6 | static class GenericMathHelper 7 | { 8 | public const float Pi = MathF.PI; 9 | public const float PiOver2 = Pi / 2f; 10 | public const float PiOver4 = Pi / 4f; 11 | 12 | // Angle conversion helper. 13 | public static T ToRadians(T degrees) 14 | where T : struct, IFloatingPointIeee754 15 | { 16 | return degrees * T.CreateChecked(Math.PI / 180.0); 17 | } 18 | 19 | // Comparison helpers with small tolerance to allow for floating point rounding during computations. 20 | public static bool Equal(T a, T b) 21 | where T : struct, IFloatingPointIeee754 22 | { 23 | return (T.Abs(a - b) < T.CreateChecked(1e-5)); 24 | } 25 | 26 | public static bool Equal(Vector2 a, Vector2 b) 27 | where T : struct, IFloatingPointIeee754 28 | { 29 | return Equal(a.X, b.X) && Equal(a.Y, b.Y); 30 | } 31 | 32 | public static bool Equal(Vector3 a, Vector3 b) 33 | where T : struct, IFloatingPointIeee754 34 | { 35 | return Equal(a.X, b.X) && Equal(a.Y, b.Y) && Equal(a.Z, b.Z); 36 | } 37 | 38 | public static bool Equal(Vector4 a, Vector4 b) 39 | where T : struct, IFloatingPointIeee754 40 | { 41 | return Equal(a.X, b.X) && Equal(a.Y, b.Y) && Equal(a.Z, b.Z) && Equal(a.W, b.W); ; 42 | } 43 | 44 | public static bool Equal(Matrix4x4 a, Matrix4x4 b) 45 | where T : struct, IFloatingPointIeee754 46 | { 47 | return 48 | Equal(a.M11, b.M11) && Equal(a.M12, b.M12) && Equal(a.M13, b.M13) && Equal(a.M14, b.M14) && 49 | Equal(a.M21, b.M21) && Equal(a.M22, b.M22) && Equal(a.M23, b.M23) && Equal(a.M24, b.M24) && 50 | Equal(a.M31, b.M31) && Equal(a.M32, b.M32) && Equal(a.M33, b.M33) && Equal(a.M34, b.M34) && 51 | Equal(a.M41, b.M41) && Equal(a.M42, b.M42) && Equal(a.M43, b.M43) && Equal(a.M44, b.M44); 52 | } 53 | 54 | public static bool Equal(Matrix3x2 a, Matrix3x2 b) 55 | where T : struct, IFloatingPointIeee754 56 | { 57 | return 58 | Equal(a.M11, b.M11) && Equal(a.M12, b.M12) && 59 | Equal(a.M21, b.M21) && Equal(a.M22, b.M22) && 60 | Equal(a.M31, b.M31) && Equal(a.M32, b.M32); 61 | } 62 | 63 | public static bool Equal(Plane a, Plane b) 64 | where T : struct, IFloatingPointIeee754 65 | { 66 | return Equal(a.Normal, b.Normal) && Equal(a.Distance, b.Distance); 67 | } 68 | 69 | public static bool Equal(Quaternion a, Quaternion b) 70 | where T : struct, IFloatingPointIeee754 71 | { 72 | return Equal(a.X, b.X) && Equal(a.Y, b.Y) && Equal(a.Z, b.Z) && Equal(a.W, b.W); 73 | } 74 | 75 | public static bool EqualRotation(Quaternion a, Quaternion b) 76 | where T : struct, IFloatingPointIeee754 77 | { 78 | return Equal(a, b) || Equal(a, -b); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /GenericVectors/Tests/GenericPlaneDoubleTests.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Globalization; 5 | using System.Runtime.InteropServices; 6 | using Xunit; 7 | using GMatrix4x4 = System.Numerics.Matrix4x4; 8 | using GPlane = System.Numerics.Plane; 9 | using GQuaternion = System.Numerics.Quaternion; 10 | using GScalar = System.Double; 11 | using GVector3 = System.Numerics.Vector3; 12 | using GVector4 = System.Numerics.Vector4; 13 | 14 | namespace System.Numerics.Tests 15 | { 16 | public class GenericPlaneDoubleTests 17 | { 18 | // A test for Equals (Plane) 19 | [Fact] 20 | public void PlaneEqualsTest1() 21 | { 22 | GPlane a = new GPlane(1, 2, 3, 4); 23 | GPlane b = new GPlane(1, 2, 3, 4); 24 | 25 | // case 1: compare between same values 26 | bool expected = true; 27 | bool actual = a.Equals(b); 28 | Assert.Equal(expected, actual); 29 | 30 | // case 2: compare between different values 31 | b = b with { Normal = new GVector3(10, b.Normal.Y, b.Normal.Z) }; 32 | expected = false; 33 | actual = a.Equals(b); 34 | Assert.Equal(expected, actual); 35 | } 36 | 37 | // A test for Equals (object) 38 | [Fact] 39 | public void PlaneEqualsTest() 40 | { 41 | GPlane a = new GPlane(1.0f, 2.0f, 3.0f, 4.0f); 42 | GPlane b = new GPlane(1.0f, 2.0f, 3.0f, 4.0f); 43 | 44 | // case 1: compare between same values 45 | object? obj = b; 46 | 47 | bool expected = true; 48 | bool actual = a.Equals(obj); 49 | Assert.Equal(expected, actual); 50 | 51 | // case 2: compare between different values 52 | b = b with { Normal = new GVector3(10.0f, b.Normal.Y, b.Normal.Z) }; 53 | 54 | obj = b; 55 | expected = false; 56 | actual = a.Equals(obj); 57 | Assert.Equal(expected, actual); 58 | 59 | // case 3: compare between different types. 60 | obj = new GQuaternion(); 61 | expected = false; 62 | actual = a.Equals(obj); 63 | Assert.Equal(expected, actual); 64 | 65 | // case 3: compare against null. 66 | obj = null; 67 | expected = false; 68 | #pragma warning disable CA1508 // Avoid dead conditional code 69 | actual = a.Equals(obj); 70 | #pragma warning restore CA1508 // Avoid dead conditional code 71 | Assert.Equal(expected, actual); 72 | } 73 | 74 | // A test for operator != (Plane, Plane) 75 | [Fact] 76 | public void PlaneInequalityTest() 77 | { 78 | GPlane a = new GPlane(1.0f, 2.0f, 3.0f, 4.0f); 79 | GPlane b = new GPlane(1.0f, 2.0f, 3.0f, 4.0f); 80 | 81 | // case 1: compare between same values 82 | bool expected = false; 83 | bool actual = a != b; 84 | Assert.Equal(expected, actual); 85 | 86 | // case 2: compare between different values 87 | b = b with { Normal = new GVector3(10.0f, b.Normal.Y, b.Normal.Z) }; 88 | expected = true; 89 | actual = a != b; 90 | Assert.Equal(expected, actual); 91 | } 92 | 93 | // A test for operator == (Plane, Plane) 94 | [Fact] 95 | public void PlaneEqualityTest() 96 | { 97 | GPlane a = new GPlane(1.0f, 2.0f, 3.0f, 4.0f); 98 | GPlane b = new GPlane(1.0f, 2.0f, 3.0f, 4.0f); 99 | 100 | // case 1: compare between same values 101 | bool expected = true; 102 | bool actual = a == b; 103 | Assert.Equal(expected, actual); 104 | 105 | // case 2: compare between different values 106 | b = b with { Normal = new GVector3(10.0f, b.Normal.Y, b.Normal.Z) }; 107 | expected = false; 108 | actual = a == b; 109 | Assert.Equal(expected, actual); 110 | } 111 | 112 | // A test for Plane (scalar, scalar, scalar, scalar) 113 | [Fact] 114 | public void PlaneConstructorTest1() 115 | { 116 | GScalar a = 1.0f, b = 2.0f, c = 3.0f, d = 4.0f; 117 | GPlane target = new GPlane(a, b, c, d); 118 | 119 | Assert.True( 120 | target.Normal.X == a && target.Normal.Y == b && target.Normal.Z == c && target.Distance == d, 121 | "GPlane.cstor did not return the expected value."); 122 | } 123 | 124 | // A test for Plane.CreateFromVertices 125 | [Fact] 126 | public void PlaneCreateFromVerticesTest() 127 | { 128 | GVector3 point1 = new GVector3(0, 1, 1); 129 | GVector3 point2 = new GVector3(0, 0, 1); 130 | GVector3 point3 = new GVector3(1, 0, 1); 131 | 132 | GPlane target = PlaneS.CreateFromVertices(point1, point2, point3); 133 | GPlane expected = new GPlane(new GVector3(0, 0, 1), -1); 134 | Assert.Equal(target, expected); 135 | } 136 | 137 | // A test for Plane.CreateFromVertices 138 | [Fact] 139 | public void PlaneCreateFromVerticesTest2() 140 | { 141 | GVector3 point1 = new GVector3(0, 0, 1); 142 | GVector3 point2 = new GVector3(1, 0, 0); 143 | GVector3 point3 = new GVector3(1, 1, 0); 144 | 145 | GPlane target = PlaneS.CreateFromVertices(point1, point2, point3); 146 | GScalar invRoot2 = (GScalar)(1.0 / Math.Sqrt(2)); 147 | 148 | GPlane expected = new GPlane(new GVector3(invRoot2, 0, invRoot2), -invRoot2); 149 | Assert.True(GenericMathHelper.Equal(target, expected), "GPlane.cstor did not return the expected value."); 150 | } 151 | 152 | // A test for Plane (Vector3, scalar) 153 | [Fact] 154 | public void PlaneConstructorTest3() 155 | { 156 | GVector3 normal = new GVector3(1, 2, 3); 157 | GScalar d = 4; 158 | 159 | GPlane target = new GPlane(normal, d); 160 | Assert.True( 161 | target.Normal == normal && target.Distance == d, 162 | "GPlane.cstor did not return the expected value."); 163 | } 164 | 165 | // A test for GPlane (Vector4) 166 | [Fact] 167 | public void PlaneConstructorTest() 168 | { 169 | GVector4 value = new GVector4(1, 2, 3, 4); 170 | GPlane target = new GPlane(value); 171 | 172 | Assert.True( 173 | target.Normal.X == value.X && target.Normal.Y == value.Y && target.Normal.Z == value.Z && target.Distance == value.W, 174 | "GPlane.cstor did not return the expected value."); 175 | } 176 | 177 | [Fact] 178 | public void PlaneDotTest() 179 | { 180 | GPlane target = new GPlane(2, 3, 4, 5); 181 | GVector4 value = new GVector4(5, 4, 3, 2); 182 | 183 | GScalar expected = 10 + 12 + 12 + 10; 184 | GScalar actual = PlaneS.Dot(target, value); 185 | Assert.True(GenericMathHelper.Equal(expected, actual), "GPlane.Dot returns unexpected value."); 186 | } 187 | 188 | [Fact] 189 | public void PlaneDotCoordinateTest() 190 | { 191 | GPlane target = new GPlane(2, 3, 4, 5); 192 | GVector3 value = new GVector3(5, 4, 3); 193 | 194 | GScalar expected = 10 + 12 + 12 + 5; 195 | GScalar actual = PlaneS.DotCoordinate(target, value); 196 | Assert.True(GenericMathHelper.Equal(expected, actual), "GPlane.DotCoordinate returns unexpected value."); 197 | } 198 | 199 | [Fact] 200 | public void PlaneDotNormalTest() 201 | { 202 | GPlane target = new GPlane(2, 3, 4, 5); 203 | GVector3 value = new GVector3(5, 4, 3); 204 | 205 | GScalar expected = 10 + 12 + 12; 206 | GScalar actual = PlaneS.DotNormal(target, value); 207 | Assert.True(GenericMathHelper.Equal(expected, actual), "GPlane.DotCoordinate returns unexpected value."); 208 | } 209 | 210 | [Fact] 211 | public void PlaneNormalizeTest() 212 | { 213 | GPlane target = new GPlane(1, 2, 3, 4); 214 | 215 | GScalar f = target.Normal.LengthSquared(); 216 | GScalar invF = (GScalar)(1.0 / Math.Sqrt(f)); 217 | GPlane expected = new GPlane(target.Normal * invF, target.Distance * invF); 218 | 219 | GPlane actual = PlaneS.Normalize(target); 220 | Assert.True(GenericMathHelper.Equal(expected, actual), "GPlane.Normalize returns unexpected value."); 221 | 222 | // normalize, normalized normal. 223 | actual = PlaneS.Normalize(actual); 224 | Assert.True(GenericMathHelper.Equal(expected, actual), "GPlane.Normalize returns unexpected value."); 225 | } 226 | 227 | [Fact] 228 | // Transform by matrix 229 | public void PlaneTransformTest1() 230 | { 231 | GPlane target = new GPlane(1, 2, 3, 4); 232 | target = PlaneS.Normalize(target); 233 | 234 | GMatrix4x4 m = 235 | Matrix4x4S.CreateRotationX(GenericMathHelper.ToRadians((GScalar)30)) * 236 | Matrix4x4S.CreateRotationY(GenericMathHelper.ToRadians((GScalar)30)) * 237 | Matrix4x4S.CreateRotationZ(GenericMathHelper.ToRadians((GScalar)30)); 238 | m = m with { M41 = 10, M42 = 20, M43 = 30 }; 239 | 240 | GPlane expected = new GPlane(); 241 | GMatrix4x4 inv; 242 | Matrix4x4S.Invert(m, out inv); 243 | GMatrix4x4 itm = Matrix4x4S.Transpose(inv); 244 | GScalar x = target.Normal.X, y = target.Normal.Y, z = target.Normal.Z, w = target.Distance; 245 | expected = expected with 246 | { 247 | Normal = new GVector3( 248 | x * itm.M11 + y * itm.M21 + z * itm.M31 + w * itm.M41, 249 | x * itm.M12 + y * itm.M22 + z * itm.M32 + w * itm.M42, 250 | x * itm.M13 + y * itm.M23 + z * itm.M33 + w * itm.M43) 251 | }; 252 | expected = expected with { Distance = x * itm.M14 + y * itm.M24 + z * itm.M34 + w * itm.M44 }; 253 | 254 | GPlane actual; 255 | actual = PlaneS.Transform(target, m); 256 | Assert.True(GenericMathHelper.Equal(expected, actual), "GPlane.Transform did not return the expected value."); 257 | } 258 | 259 | [Fact] 260 | // Transform by quaternion 261 | public void PlaneTransformTest2() 262 | { 263 | GPlane target = new GPlane(1, 2, 3, 4); 264 | target = PlaneS.Normalize(target); 265 | 266 | GMatrix4x4 m = 267 | Matrix4x4S.CreateRotationX(GenericMathHelper.ToRadians((GScalar)30)) * 268 | Matrix4x4S.CreateRotationY(GenericMathHelper.ToRadians((GScalar)30)) * 269 | Matrix4x4S.CreateRotationZ(GenericMathHelper.ToRadians((GScalar)30)); 270 | GQuaternion q = QuaternionS.CreateFromRotationMatrix(m); 271 | 272 | GPlane expected = new GPlane(); 273 | GScalar x = target.Normal.X, y = target.Normal.Y, z = target.Normal.Z, w = target.Distance; 274 | expected = expected with 275 | { 276 | Normal = new GVector3( 277 | x * m.M11 + y * m.M21 + z * m.M31 + w * m.M41, 278 | x * m.M12 + y * m.M22 + z * m.M32 + w * m.M42, 279 | x * m.M13 + y * m.M23 + z * m.M33 + w * m.M43) 280 | }; 281 | expected = expected with { Distance = x * m.M14 + y * m.M24 + z * m.M34 + w * m.M44 }; 282 | 283 | GPlane actual; 284 | actual = PlaneS.Transform(target, q); 285 | Assert.True(GenericMathHelper.Equal(expected, actual), "GPlane.Transform did not return the expected value."); 286 | } 287 | 288 | // A test for Plane comparison involving NaN values 289 | [Fact] 290 | public void PlaneEqualsNanTest() 291 | { 292 | GPlane a = new GPlane(GScalar.NaN, 0, 0, 0); 293 | GPlane b = new GPlane(0, GScalar.NaN, 0, 0); 294 | GPlane c = new GPlane(0, 0, GScalar.NaN, 0); 295 | GPlane d = new GPlane(0, 0, 0, GScalar.NaN); 296 | 297 | Assert.False(a == new GPlane(0, 0, 0, 0)); 298 | Assert.False(b == new GPlane(0, 0, 0, 0)); 299 | Assert.False(c == new GPlane(0, 0, 0, 0)); 300 | Assert.False(d == new GPlane(0, 0, 0, 0)); 301 | 302 | Assert.True(a != new GPlane(0, 0, 0, 0)); 303 | Assert.True(b != new GPlane(0, 0, 0, 0)); 304 | Assert.True(c != new GPlane(0, 0, 0, 0)); 305 | Assert.True(d != new GPlane(0, 0, 0, 0)); 306 | 307 | Assert.False(a.Equals(new GPlane(0, 0, 0, 0))); 308 | Assert.False(b.Equals(new GPlane(0, 0, 0, 0))); 309 | Assert.False(c.Equals(new GPlane(0, 0, 0, 0))); 310 | Assert.False(d.Equals(new GPlane(0, 0, 0, 0))); 311 | 312 | // Counterintuitive result - IEEE rules for NaN comparison are weird! 313 | Assert.False(a.Equals(a)); 314 | Assert.False(b.Equals(b)); 315 | Assert.False(c.Equals(c)); 316 | Assert.False(d.Equals(d)); 317 | } 318 | 319 | // A test to make sure these types are blittable directly into GPU buffer memory layouts 320 | [Fact] 321 | public unsafe void PlaneSizeofTest() 322 | { 323 | int scalarSize = sizeof(GScalar); 324 | #pragma warning disable xUnit2000 // Constants and literals should be the expected argument 325 | Assert.Equal(scalarSize * 4, sizeof(GPlane)); 326 | Assert.Equal(scalarSize * 8, sizeof(Plane_2x)); 327 | Assert.Equal(scalarSize * 5, sizeof(PlanePlusSingle)); 328 | Assert.Equal(scalarSize * 10, sizeof(PlanePlusFloatSingle_2x)); 329 | #pragma warning restore xUnit2000 // Constants and literals should be the expected argument 330 | } 331 | 332 | [Fact] 333 | public void PlaneToStringTest() 334 | { 335 | GPlane target = new GPlane(1, 2, 3, 4); 336 | string expected = string.Format( 337 | CultureInfo.CurrentCulture, 338 | "{{Normal:{0:G} Distance:{1}}}", 339 | target.Normal, 340 | target.Distance); 341 | 342 | Assert.Equal(expected, target.ToString()); 343 | } 344 | 345 | [StructLayout(LayoutKind.Sequential)] 346 | struct Plane_2x 347 | { 348 | private GPlane _a; 349 | private GPlane _b; 350 | } 351 | 352 | [StructLayout(LayoutKind.Sequential)] 353 | struct PlanePlusSingle 354 | { 355 | private GPlane _v; 356 | private GScalar _f; 357 | } 358 | 359 | [StructLayout(LayoutKind.Sequential)] 360 | struct PlanePlusFloatSingle_2x 361 | { 362 | private PlanePlusSingle _a; 363 | private PlanePlusSingle _b; 364 | } 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /GenericVectors/Tests/GenericPlaneSingleTests.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Globalization; 5 | using System.Runtime.InteropServices; 6 | using Xunit; 7 | using GMatrix4x4 = System.Numerics.Matrix4x4; 8 | using GPlane = System.Numerics.Plane; 9 | using GQuaternion = System.Numerics.Quaternion; 10 | using GScalar = System.Single; 11 | using GVector3 = System.Numerics.Vector3; 12 | using GVector4 = System.Numerics.Vector4; 13 | 14 | namespace System.Numerics.Tests 15 | { 16 | public class GenericPlaneSingleTests 17 | { 18 | // A test for Equals (Plane) 19 | [Fact] 20 | public void PlaneEqualsTest1() 21 | { 22 | GPlane a = new GPlane(1, 2, 3, 4); 23 | GPlane b = new GPlane(1, 2, 3, 4); 24 | 25 | // case 1: compare between same values 26 | bool expected = true; 27 | bool actual = a.Equals(b); 28 | Assert.Equal(expected, actual); 29 | 30 | // case 2: compare between different values 31 | b = b with { Normal = new GVector3(10, b.Normal.Y, b.Normal.Z) }; 32 | expected = false; 33 | actual = a.Equals(b); 34 | Assert.Equal(expected, actual); 35 | } 36 | 37 | // A test for Equals (object) 38 | [Fact] 39 | public void PlaneEqualsTest() 40 | { 41 | GPlane a = new GPlane(1.0f, 2.0f, 3.0f, 4.0f); 42 | GPlane b = new GPlane(1.0f, 2.0f, 3.0f, 4.0f); 43 | 44 | // case 1: compare between same values 45 | object? obj = b; 46 | 47 | bool expected = true; 48 | bool actual = a.Equals(obj); 49 | Assert.Equal(expected, actual); 50 | 51 | // case 2: compare between different values 52 | b = b with { Normal = new GVector3(10.0f, b.Normal.Y, b.Normal.Z) }; 53 | 54 | obj = b; 55 | expected = false; 56 | actual = a.Equals(obj); 57 | Assert.Equal(expected, actual); 58 | 59 | // case 3: compare between different types. 60 | obj = new GQuaternion(); 61 | expected = false; 62 | actual = a.Equals(obj); 63 | Assert.Equal(expected, actual); 64 | 65 | // case 3: compare against null. 66 | obj = null; 67 | expected = false; 68 | #pragma warning disable CA1508 // Avoid dead conditional code 69 | actual = a.Equals(obj); 70 | #pragma warning restore CA1508 // Avoid dead conditional code 71 | Assert.Equal(expected, actual); 72 | } 73 | 74 | // A test for operator != (Plane, Plane) 75 | [Fact] 76 | public void PlaneInequalityTest() 77 | { 78 | GPlane a = new GPlane(1.0f, 2.0f, 3.0f, 4.0f); 79 | GPlane b = new GPlane(1.0f, 2.0f, 3.0f, 4.0f); 80 | 81 | // case 1: compare between same values 82 | bool expected = false; 83 | bool actual = a != b; 84 | Assert.Equal(expected, actual); 85 | 86 | // case 2: compare between different values 87 | b = b with { Normal = new GVector3(10.0f, b.Normal.Y, b.Normal.Z) }; 88 | expected = true; 89 | actual = a != b; 90 | Assert.Equal(expected, actual); 91 | } 92 | 93 | // A test for operator == (Plane, Plane) 94 | [Fact] 95 | public void PlaneEqualityTest() 96 | { 97 | GPlane a = new GPlane(1.0f, 2.0f, 3.0f, 4.0f); 98 | GPlane b = new GPlane(1.0f, 2.0f, 3.0f, 4.0f); 99 | 100 | // case 1: compare between same values 101 | bool expected = true; 102 | bool actual = a == b; 103 | Assert.Equal(expected, actual); 104 | 105 | // case 2: compare between different values 106 | b = b with { Normal = new GVector3(10.0f, b.Normal.Y, b.Normal.Z) }; 107 | expected = false; 108 | actual = a == b; 109 | Assert.Equal(expected, actual); 110 | } 111 | 112 | // A test for Plane (scalar, scalar, scalar, scalar) 113 | [Fact] 114 | public void PlaneConstructorTest1() 115 | { 116 | GScalar a = 1.0f, b = 2.0f, c = 3.0f, d = 4.0f; 117 | GPlane target = new GPlane(a, b, c, d); 118 | 119 | Assert.True( 120 | target.Normal.X == a && target.Normal.Y == b && target.Normal.Z == c && target.Distance == d, 121 | "GPlane.cstor did not return the expected value."); 122 | } 123 | 124 | // A test for Plane.CreateFromVertices 125 | [Fact] 126 | public void PlaneCreateFromVerticesTest() 127 | { 128 | GVector3 point1 = new GVector3(0, 1, 1); 129 | GVector3 point2 = new GVector3(0, 0, 1); 130 | GVector3 point3 = new GVector3(1, 0, 1); 131 | 132 | GPlane target = PlaneS.CreateFromVertices(point1, point2, point3); 133 | GPlane expected = new GPlane(new GVector3(0, 0, 1), -1); 134 | Assert.Equal(target, expected); 135 | } 136 | 137 | // A test for Plane.CreateFromVertices 138 | [Fact] 139 | public void PlaneCreateFromVerticesTest2() 140 | { 141 | GVector3 point1 = new GVector3(0, 0, 1); 142 | GVector3 point2 = new GVector3(1, 0, 0); 143 | GVector3 point3 = new GVector3(1, 1, 0); 144 | 145 | GPlane target = PlaneS.CreateFromVertices(point1, point2, point3); 146 | GScalar invRoot2 = (GScalar)(1.0 / Math.Sqrt(2)); 147 | 148 | GPlane expected = new GPlane(new GVector3(invRoot2, 0, invRoot2), -invRoot2); 149 | Assert.True(GenericMathHelper.Equal(target, expected), "GPlane.cstor did not return the expected value."); 150 | } 151 | 152 | // A test for Plane (Vector3, scalar) 153 | [Fact] 154 | public void PlaneConstructorTest3() 155 | { 156 | GVector3 normal = new GVector3(1, 2, 3); 157 | GScalar d = 4; 158 | 159 | GPlane target = new GPlane(normal, d); 160 | Assert.True( 161 | target.Normal == normal && target.Distance == d, 162 | "GPlane.cstor did not return the expected value."); 163 | } 164 | 165 | // A test for GPlane (Vector4) 166 | [Fact] 167 | public void PlaneConstructorTest() 168 | { 169 | GVector4 value = new GVector4(1, 2, 3, 4); 170 | GPlane target = new GPlane(value); 171 | 172 | Assert.True( 173 | target.Normal.X == value.X && target.Normal.Y == value.Y && target.Normal.Z == value.Z && target.Distance == value.W, 174 | "GPlane.cstor did not return the expected value."); 175 | } 176 | 177 | [Fact] 178 | public void PlaneDotTest() 179 | { 180 | GPlane target = new GPlane(2, 3, 4, 5); 181 | GVector4 value = new GVector4(5, 4, 3, 2); 182 | 183 | GScalar expected = 10 + 12 + 12 + 10; 184 | GScalar actual = PlaneS.Dot(target, value); 185 | Assert.True(GenericMathHelper.Equal(expected, actual), "GPlane.Dot returns unexpected value."); 186 | } 187 | 188 | [Fact] 189 | public void PlaneDotCoordinateTest() 190 | { 191 | GPlane target = new GPlane(2, 3, 4, 5); 192 | GVector3 value = new GVector3(5, 4, 3); 193 | 194 | GScalar expected = 10 + 12 + 12 + 5; 195 | GScalar actual = PlaneS.DotCoordinate(target, value); 196 | Assert.True(GenericMathHelper.Equal(expected, actual), "GPlane.DotCoordinate returns unexpected value."); 197 | } 198 | 199 | [Fact] 200 | public void PlaneDotNormalTest() 201 | { 202 | GPlane target = new GPlane(2, 3, 4, 5); 203 | GVector3 value = new GVector3(5, 4, 3); 204 | 205 | GScalar expected = 10 + 12 + 12; 206 | GScalar actual = PlaneS.DotNormal(target, value); 207 | Assert.True(GenericMathHelper.Equal(expected, actual), "GPlane.DotCoordinate returns unexpected value."); 208 | } 209 | 210 | [Fact] 211 | public void PlaneNormalizeTest() 212 | { 213 | GPlane target = new GPlane(1, 2, 3, 4); 214 | 215 | GScalar f = target.Normal.LengthSquared(); 216 | GScalar invF = (GScalar)(1.0 / Math.Sqrt(f)); 217 | GPlane expected = new GPlane(target.Normal * invF, target.Distance * invF); 218 | 219 | GPlane actual = PlaneS.Normalize(target); 220 | Assert.True(GenericMathHelper.Equal(expected, actual), "GPlane.Normalize returns unexpected value."); 221 | 222 | // normalize, normalized normal. 223 | actual = PlaneS.Normalize(actual); 224 | Assert.True(GenericMathHelper.Equal(expected, actual), "GPlane.Normalize returns unexpected value."); 225 | } 226 | 227 | [Fact] 228 | // Transform by matrix 229 | public void PlaneTransformTest1() 230 | { 231 | GPlane target = new GPlane(1, 2, 3, 4); 232 | target = PlaneS.Normalize(target); 233 | 234 | GMatrix4x4 m = 235 | Matrix4x4S.CreateRotationX(GenericMathHelper.ToRadians((GScalar)30)) * 236 | Matrix4x4S.CreateRotationY(GenericMathHelper.ToRadians((GScalar)30)) * 237 | Matrix4x4S.CreateRotationZ(GenericMathHelper.ToRadians((GScalar)30)); 238 | m = m with { M41 = 10, M42 = 20, M43 = 30 }; 239 | 240 | GPlane expected = new GPlane(); 241 | GMatrix4x4 inv; 242 | Matrix4x4S.Invert(m, out inv); 243 | GMatrix4x4 itm = Matrix4x4S.Transpose(inv); 244 | GScalar x = target.Normal.X, y = target.Normal.Y, z = target.Normal.Z, w = target.Distance; 245 | expected = expected with 246 | { 247 | Normal = new GVector3( 248 | x * itm.M11 + y * itm.M21 + z * itm.M31 + w * itm.M41, 249 | x * itm.M12 + y * itm.M22 + z * itm.M32 + w * itm.M42, 250 | x * itm.M13 + y * itm.M23 + z * itm.M33 + w * itm.M43) 251 | }; 252 | expected = expected with { Distance = x * itm.M14 + y * itm.M24 + z * itm.M34 + w * itm.M44 }; 253 | 254 | GPlane actual; 255 | actual = PlaneS.Transform(target, m); 256 | Assert.True(GenericMathHelper.Equal(expected, actual), "GPlane.Transform did not return the expected value."); 257 | } 258 | 259 | [Fact] 260 | // Transform by quaternion 261 | public void PlaneTransformTest2() 262 | { 263 | GPlane target = new GPlane(1, 2, 3, 4); 264 | target = PlaneS.Normalize(target); 265 | 266 | GMatrix4x4 m = 267 | Matrix4x4S.CreateRotationX(GenericMathHelper.ToRadians((GScalar)30)) * 268 | Matrix4x4S.CreateRotationY(GenericMathHelper.ToRadians((GScalar)30)) * 269 | Matrix4x4S.CreateRotationZ(GenericMathHelper.ToRadians((GScalar)30)); 270 | GQuaternion q = QuaternionS.CreateFromRotationMatrix(m); 271 | 272 | GPlane expected = new GPlane(); 273 | GScalar x = target.Normal.X, y = target.Normal.Y, z = target.Normal.Z, w = target.Distance; 274 | expected = expected with 275 | { 276 | Normal = new GVector3( 277 | x * m.M11 + y * m.M21 + z * m.M31 + w * m.M41, 278 | x * m.M12 + y * m.M22 + z * m.M32 + w * m.M42, 279 | x * m.M13 + y * m.M23 + z * m.M33 + w * m.M43) 280 | }; 281 | expected = expected with { Distance = x * m.M14 + y * m.M24 + z * m.M34 + w * m.M44 }; 282 | 283 | GPlane actual; 284 | actual = PlaneS.Transform(target, q); 285 | Assert.True(GenericMathHelper.Equal(expected, actual), "GPlane.Transform did not return the expected value."); 286 | } 287 | 288 | // A test for Plane comparison involving NaN values 289 | [Fact] 290 | public void PlaneEqualsNanTest() 291 | { 292 | GPlane a = new GPlane(GScalar.NaN, 0, 0, 0); 293 | GPlane b = new GPlane(0, GScalar.NaN, 0, 0); 294 | GPlane c = new GPlane(0, 0, GScalar.NaN, 0); 295 | GPlane d = new GPlane(0, 0, 0, GScalar.NaN); 296 | 297 | Assert.False(a == new GPlane(0, 0, 0, 0)); 298 | Assert.False(b == new GPlane(0, 0, 0, 0)); 299 | Assert.False(c == new GPlane(0, 0, 0, 0)); 300 | Assert.False(d == new GPlane(0, 0, 0, 0)); 301 | 302 | Assert.True(a != new GPlane(0, 0, 0, 0)); 303 | Assert.True(b != new GPlane(0, 0, 0, 0)); 304 | Assert.True(c != new GPlane(0, 0, 0, 0)); 305 | Assert.True(d != new GPlane(0, 0, 0, 0)); 306 | 307 | Assert.False(a.Equals(new GPlane(0, 0, 0, 0))); 308 | Assert.False(b.Equals(new GPlane(0, 0, 0, 0))); 309 | Assert.False(c.Equals(new GPlane(0, 0, 0, 0))); 310 | Assert.False(d.Equals(new GPlane(0, 0, 0, 0))); 311 | 312 | // Counterintuitive result - IEEE rules for NaN comparison are weird! 313 | Assert.False(a.Equals(a)); 314 | Assert.False(b.Equals(b)); 315 | Assert.False(c.Equals(c)); 316 | Assert.False(d.Equals(d)); 317 | } 318 | 319 | // A test to make sure these types are blittable directly into GPU buffer memory layouts 320 | [Fact] 321 | public unsafe void PlaneSizeofTest() 322 | { 323 | int scalarSize = sizeof(GScalar); 324 | #pragma warning disable xUnit2000 // Constants and literals should be the expected argument 325 | Assert.Equal(scalarSize * 4, sizeof(GPlane)); 326 | Assert.Equal(scalarSize * 8, sizeof(Plane_2x)); 327 | Assert.Equal(scalarSize * 5, sizeof(PlanePlusSingle)); 328 | Assert.Equal(scalarSize * 10, sizeof(PlanePlusFloatSingle_2x)); 329 | #pragma warning restore xUnit2000 // Constants and literals should be the expected argument 330 | } 331 | 332 | [Fact] 333 | public void PlaneToStringTest() 334 | { 335 | GPlane target = new GPlane(1, 2, 3, 4); 336 | string expected = string.Format( 337 | CultureInfo.CurrentCulture, 338 | "{{Normal:{0:G} Distance:{1}}}", 339 | target.Normal, 340 | target.Distance); 341 | 342 | Assert.Equal(expected, target.ToString()); 343 | } 344 | 345 | [StructLayout(LayoutKind.Sequential)] 346 | struct Plane_2x 347 | { 348 | private GPlane _a; 349 | private GPlane _b; 350 | } 351 | 352 | [StructLayout(LayoutKind.Sequential)] 353 | struct PlanePlusSingle 354 | { 355 | private GPlane _v; 356 | private GScalar _f; 357 | } 358 | 359 | [StructLayout(LayoutKind.Sequential)] 360 | struct PlanePlusFloatSingle_2x 361 | { 362 | private PlanePlusSingle _a; 363 | private PlanePlusSingle _b; 364 | } 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /GenericVectors/Tests/GenericVector2Tests.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using Xunit; 5 | 6 | namespace System.Numerics.Tests 7 | { 8 | public class GenericVector2Tests 9 | { 10 | // A test for operator + (Vector2, Vector2) 11 | [Fact] 12 | public void Vector2AdditionTestHalf() => Vector2AdditionTest(); 13 | [Fact] 14 | public void Vector2AdditionTestFloat() => Vector2AdditionTest(); 15 | [Fact] 16 | public void Vector2AdditionTestDouble() => Vector2AdditionTest(); 17 | 18 | private static void Vector2AdditionTest() 19 | where T : struct, IFloatingPointIeee754 20 | { 21 | Vector2 a = new Vector2(T.One, T.CreateChecked(2.0)); 22 | Vector2 b = new Vector2(T.CreateChecked(3.0), T.CreateChecked(4.0)); 23 | 24 | Vector2 expected = new Vector2(T.CreateChecked(4.0), T.CreateChecked(6.0)); 25 | Vector2 actual; 26 | 27 | actual = a + b; 28 | 29 | Assert.True(GenericMathHelper.Equal(expected, actual), "Vector2.operator + did not return the expected value."); 30 | } 31 | 32 | [Fact] 33 | public void Vector2AdditiveIdentityHalfTest() => Vector2AdditiveIdentityTest(); 34 | [Fact] 35 | public void Vector2AdditiveIdentitySingleTest() => Vector2AdditiveIdentityTest(); 36 | [Fact] 37 | public void Vector2AdditiveIdentityDoubleTest() => Vector2AdditiveIdentityTest(); 38 | 39 | static void Vector2AdditiveIdentityTest() 40 | where T : struct, IFloatingPointIeee754 41 | { 42 | Vector2 v1 = new Vector2(T.CreateChecked(42), T.CreateChecked(142)); 43 | Vector2 actual = Add(v1); 44 | Vector2 expected = v1; 45 | Assert.Equal(actual, expected); 46 | 47 | static T Add(T value) 48 | where T : IAdditiveIdentity, IAdditionOperators 49 | { 50 | return T.AdditiveIdentity + value; 51 | } 52 | } 53 | 54 | [Fact] 55 | public void Vector2MultiplicativeIdentityHalfTest() => Vector2MultiplicativeIdentityTest(); 56 | [Fact] 57 | public void Vector2MultiplicativeIdentitySingleTest() => Vector2MultiplicativeIdentityTest(); 58 | [Fact] 59 | public void Vector2MultiplicativeIdentityDoubleTest() => Vector2MultiplicativeIdentityTest(); 60 | 61 | static void Vector2MultiplicativeIdentityTest() 62 | where T : struct, IFloatingPointIeee754 63 | { 64 | Vector2 v1 = new Vector2(T.CreateChecked(42), T.CreateChecked(142)); 65 | Vector2 actual = Multiply(v1); 66 | Vector2 expected = v1; 67 | Assert.Equal(actual, expected); 68 | 69 | static T Multiply(T value) 70 | where T : IMultiplicativeIdentity, IMultiplyOperators 71 | { 72 | return T.MultiplicativeIdentity * value; 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /GenericVectors/src/Matrix3x2S.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace System.Numerics 7 | { 8 | public partial struct Matrix3x2S 9 | { 10 | // "Friendly" Operators 11 | 12 | /// Computes the unary plus of a value. 13 | /// The value for which to compute its unary plus. 14 | /// The unary plus of . 15 | public static Matrix3x2 Plus(in Matrix3x2 value) 16 | where T : struct, IFloatingPointIeee754 17 | { 18 | return value; 19 | } 20 | 21 | /// Negates the specified matrix by multiplying all its values by -1. 22 | /// The matrix to negate. 23 | /// The negated matrix. 24 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 25 | public static Matrix3x2 Negate(in Matrix3x2 value) 26 | where T : struct, IFloatingPointIeee754 27 | { 28 | return -value; 29 | } 30 | 31 | /// Adds each element in one matrix with its corresponding element in a second matrix. 32 | /// The first matrix. 33 | /// The second matrix. 34 | /// The matrix that contains the summed values of and . 35 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 36 | public static Matrix3x2 Add(in Matrix3x2 left, in Matrix3x2 right) 37 | where T : struct, IFloatingPointIeee754 38 | { 39 | return left + right; 40 | } 41 | 42 | /// Subtracts each element in a second matrix from its corresponding element in a first matrix. 43 | /// The first matrix. 44 | /// The second matrix. 45 | /// The matrix containing the values that result from subtracting each element in from its corresponding element in . 46 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 47 | public static Matrix3x2 Subtract(in Matrix3x2 left, in Matrix3x2 right) 48 | where T : struct, IFloatingPointIeee754 49 | { 50 | return left - right; 51 | } 52 | 53 | /// Multiplies two matrices together to compute the product. 54 | /// The first matrix. 55 | /// The second matrix. 56 | /// The product matrix. 57 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 58 | public static Matrix3x2 Multiply(in Matrix3x2 left, in Matrix3x2 right) 59 | where T : struct, IFloatingPointIeee754 60 | { 61 | return left * right; 62 | } 63 | 64 | /// 65 | /// Scales all elements in a matrix by the given scalar factor. 66 | /// 67 | /// The source matrix. 68 | /// The scaling value to use. 69 | /// The resulting matrix. 70 | public static Matrix3x2 Multiply(in Matrix3x2 left, T right) 71 | where T : struct, IFloatingPointIeee754 72 | { 73 | return left * right; 74 | } 75 | 76 | // Static Methods 77 | 78 | /// Creates a rotation matrix using the given rotation in radians. 79 | /// The amount of rotation, in radians. 80 | /// The rotation matrix. 81 | public static Matrix3x2 CreateRotation(T radians) 82 | where T : struct, IFloatingPointIeee754 83 | { 84 | T epsilon = T.CreateChecked(0.001 * Math.PI / 180.0); // 0.1% of a degree 85 | 86 | radians = T.Ieee754Remainder(radians, T.Tau); 87 | 88 | T c, s; 89 | if (radians > -epsilon && radians < epsilon) 90 | { 91 | // Exact case for zero rotation. 92 | c = T.One; 93 | s = T.Zero; 94 | } 95 | else if (radians > T.Pi / T.CreateChecked(2.0) - epsilon && radians < T.Pi / T.CreateChecked(2.0) + epsilon) 96 | { 97 | // Exact case for 90 degree rotation. 98 | c = T.Zero; 99 | s = T.One; 100 | } 101 | else if (radians < -T.Pi + epsilon || radians > T.Pi - epsilon) 102 | { 103 | // Exact case for 180 degree rotation. 104 | c = -T.One; 105 | s = T.Zero; 106 | } 107 | else if (radians > -T.Pi / T.CreateChecked(2.0) - epsilon && radians < -T.Pi / T.CreateChecked(2.0) + epsilon) 108 | { 109 | // Exact case for 270 degree rotation. 110 | c = T.Zero; 111 | s = -T.One; 112 | } 113 | else 114 | { 115 | // Arbitrary rotation. 116 | c = T.Cos(radians); 117 | s = T.Sin(radians); 118 | } 119 | 120 | // [ c s ] 121 | // [ -s c ] 122 | // [ 0 0 ] 123 | return new( 124 | c, s, 125 | -s, c, 126 | T.Zero, T.Zero); 127 | } 128 | 129 | /// Creates a rotation matrix using the specified rotation in radians and a center point. 130 | /// The amount of rotation, in radians. 131 | /// The center point. 132 | /// The rotation matrix. 133 | public static Matrix3x2 CreateRotation(T radians, in Vector2 centerPoint) 134 | where T : struct, IFloatingPointIeee754 135 | { 136 | T epsilon = T.CreateChecked(0.001 * Math.PI / 180.0); // 0.1% of a degree 137 | 138 | radians = T.Ieee754Remainder(radians, T.Tau); 139 | 140 | T c, s; 141 | if (radians > -epsilon && radians < epsilon) 142 | { 143 | // Exact case for zero rotation. 144 | c = T.One; 145 | s = T.Zero; 146 | } 147 | else if (radians > T.Pi / T.CreateChecked(2.0) - epsilon && radians < T.Pi / T.CreateChecked(2.0) + epsilon) 148 | { 149 | // Exact case for 90 degree rotation. 150 | c = T.Zero; 151 | s = T.One; 152 | } 153 | else if (radians < -T.Pi + epsilon || radians > T.Pi - epsilon) 154 | { 155 | // Exact case for 180 degree rotation. 156 | c = -T.One; 157 | s = T.Zero; 158 | } 159 | else if (radians > -T.Pi / T.CreateChecked(2.0) - epsilon && radians < -T.Pi / T.CreateChecked(2.0) + epsilon) 160 | { 161 | // Exact case for 270 degree rotation. 162 | c = T.Zero; 163 | s = -T.One; 164 | } 165 | else 166 | { 167 | // Arbitrary rotation. 168 | c = T.Cos(radians); 169 | s = T.Sin(radians); 170 | } 171 | 172 | T tx = centerPoint.X * (T.One - c) + centerPoint.Y * s; 173 | T ty = centerPoint.Y * (T.One - c) - centerPoint.X * s; 174 | 175 | // [ c s ] 176 | // [ -s c ] 177 | // [ tx ty ] 178 | return new( 179 | c, s, 180 | -s, c, 181 | tx, ty); 182 | } 183 | 184 | /// Creates a scaling matrix that scales uniformly with the given scale. 185 | /// The uniform scale to use. 186 | /// The scaling matrix. 187 | public static Matrix3x2 CreateScale(T scale) 188 | where T : struct, IFloatingPointIeee754 189 | { 190 | return new( 191 | scale, T.Zero, 192 | T.Zero, scale, 193 | T.Zero, T.Zero); 194 | } 195 | 196 | /// Creates a scaling matrix that scales uniformly with the specified scale with an offset from the specified center. 197 | /// The uniform scale to use. 198 | /// The center offset. 199 | /// The scaling matrix. 200 | public static Matrix3x2 CreateScale(T scale, in Vector2 centerPoint) 201 | where T : struct, IFloatingPointIeee754 202 | { 203 | T tx = centerPoint.X * (T.One - scale); 204 | T ty = centerPoint.Y * (T.One - scale); 205 | 206 | return new( 207 | scale, T.Zero, 208 | T.Zero, scale, 209 | tx, ty); 210 | } 211 | 212 | /// Creates a scaling matrix from the specified X and Y components. 213 | /// The value to scale by on the X axis. 214 | /// The value to scale by on the Y axis. 215 | /// The scaling matrix. 216 | public static Matrix3x2 CreateScale(T scaleX, T scaleY) 217 | where T : struct, IFloatingPointIeee754 218 | { 219 | return new( 220 | scaleX, T.Zero, 221 | T.Zero, scaleY, 222 | T.Zero, T.Zero); 223 | } 224 | 225 | /// Creates a scaling matrix that is offset by a given center point. 226 | /// The value to scale by on the X axis. 227 | /// The value to scale by on the Y axis. 228 | /// The center point. 229 | /// The scaling matrix. 230 | public static Matrix3x2 CreateScale(T scaleX, T scaleY, in Vector2 centerPoint) 231 | where T : struct, IFloatingPointIeee754 232 | { 233 | T tx = centerPoint.X * (T.One - scaleX); 234 | T ty = centerPoint.Y * (T.One - scaleY); 235 | 236 | return new( 237 | scaleX, T.Zero, 238 | T.Zero, scaleY, 239 | tx, ty); 240 | } 241 | 242 | /// Creates a scaling matrix from the specified vector scale. 243 | /// The scale to use. 244 | /// The scaling matrix. 245 | public static Matrix3x2 CreateScale(in Vector2 scale) 246 | where T : struct, IFloatingPointIeee754 247 | { 248 | return new( 249 | scale.X, T.Zero, 250 | T.Zero, scale.Y, 251 | T.Zero, T.Zero); 252 | } 253 | 254 | /// Creates a scaling matrix from the specified vector scale with an offset from the specified center point. 255 | /// The scale to use. 256 | /// The center offset. 257 | /// The scaling matrix. 258 | public static Matrix3x2 CreateScale(in Vector2 scale, in Vector2 centerPoint) 259 | where T : struct, IFloatingPointIeee754 260 | { 261 | T tx = centerPoint.X * (T.One - scale.X); 262 | T ty = centerPoint.Y * (T.One - scale.Y); 263 | 264 | return new( 265 | scale.X, T.Zero, 266 | T.Zero, scale.Y, 267 | tx, ty); 268 | } 269 | 270 | /// Creates a skew matrix from the specified angles in radians. 271 | /// The X angle, in radians. 272 | /// The Y angle, in radians. 273 | /// The skew matrix. 274 | public static Matrix3x2 CreateSkew(T radiansX, T radiansY) 275 | where T : struct, IFloatingPointIeee754 276 | { 277 | T xTan = T.Tan(radiansX); 278 | T yTan = T.Tan(radiansY); 279 | 280 | return new( 281 | T.One, yTan, 282 | xTan, T.One, 283 | T.Zero, T.Zero); 284 | } 285 | 286 | /// Creates a skew matrix from the specified angles in radians and a center point. 287 | /// The X angle, in radians. 288 | /// The Y angle, in radians. 289 | /// The center point. 290 | /// The skew matrix. 291 | public static Matrix3x2 CreateSkew(T radiansX, T radiansY, in Vector2 centerPoint) 292 | where T : struct, IFloatingPointIeee754 293 | { 294 | T xTan = T.Tan(radiansX); 295 | T yTan = T.Tan(radiansY); 296 | 297 | T tx = -centerPoint.Y * xTan; 298 | T ty = -centerPoint.X * yTan; 299 | 300 | return new( 301 | T.One, yTan, 302 | xTan, T.One, 303 | tx, ty); 304 | } 305 | 306 | /// Creates a translation matrix from the specified X and Y components. 307 | /// The X position. 308 | /// The Y position. 309 | /// The translation matrix. 310 | public static Matrix3x2 CreateTranslation(T positionX, T positionY) 311 | where T : struct, IFloatingPointIeee754 312 | { 313 | return new( 314 | T.One, T.Zero, 315 | T.Zero, T.One, 316 | positionX, positionY); 317 | } 318 | 319 | /// Creates a translation matrix from the specified 2-dimensional vector. 320 | /// The translation position. 321 | /// The translation matrix. 322 | public static Matrix3x2 CreateTranslation(in Vector2 position) 323 | where T : struct, IFloatingPointIeee754 324 | { 325 | return new( 326 | T.One, T.Zero, 327 | T.Zero, T.One, 328 | position.X, position.Y); 329 | } 330 | 331 | /// Tries to invert the specified matrix. The return value indicates whether the operation succeeded. 332 | /// The matrix to invert. 333 | /// When this method returns, contains the inverted matrix if the operation succeeded. 334 | /// if was converted successfully; otherwise, . 335 | public static bool Invert(in Matrix3x2 matrix, out Matrix3x2 result) 336 | where T : struct, IFloatingPointIeee754 337 | { 338 | T det = (matrix.M11 * matrix.M22) - (matrix.M21 * matrix.M12); 339 | 340 | if (T.Abs(det) < T.Epsilon) 341 | { 342 | result = new(T.NaN, T.NaN, T.NaN, T.NaN, T.NaN, T.NaN); 343 | return false; 344 | } 345 | 346 | T invDet = T.One / det; 347 | 348 | result = new( 349 | matrix.M22 * invDet, 350 | -matrix.M12 * invDet, 351 | -matrix.M21 * invDet, 352 | matrix.M11 * invDet, 353 | (matrix.M21 * matrix.M32 - matrix.M31 * matrix.M22) * invDet, 354 | (matrix.M31 * matrix.M12 - matrix.M11 * matrix.M32) * invDet); 355 | 356 | return true; 357 | } 358 | 359 | /// Performs a linear interpolation from one matrix to a second matrix based on a value that specifies the weighting of the second matrix. 360 | /// The first matrix. 361 | /// The second matrix. 362 | /// The relative weighting of . 363 | /// The interpolated matrix. 364 | public static Matrix3x2 Lerp(in Matrix3x2 min, in Matrix3x2 max, T amount) 365 | where T : struct, IFloatingPointIeee754 366 | { 367 | return new( 368 | // First row 369 | min.M11 + (max.M11 - min.M11) * amount, 370 | min.M12 + (max.M12 - min.M12) * amount, 371 | 372 | // Second row 373 | min.M21 + (max.M21 - min.M21) * amount, 374 | min.M22 + (max.M22 - min.M22) * amount, 375 | 376 | // Third row 377 | min.M31 + (max.M31 - min.M31) * amount, 378 | min.M32 + (max.M32 - min.M32) * amount); 379 | } 380 | } 381 | } 382 | -------------------------------------------------------------------------------- /GenericVectors/src/Matrix3x2_1.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Globalization; 5 | using System.Runtime.CompilerServices; 6 | using System.Text; 7 | 8 | namespace System.Numerics 9 | { 10 | /// Represents a 3x2 matrix. 11 | /// 14 | [Intrinsic] 15 | public readonly record struct Matrix3x2 : 16 | IAdditionOperators, Matrix3x2, Matrix3x2>, 17 | IAdditiveIdentity, Matrix3x2>, 18 | IEquatable>, 19 | IEqualityOperators, Matrix3x2, bool>, 20 | IFormattable, 21 | IMultiplicativeIdentity, Matrix3x2>, 22 | IMultiplyOperators, Matrix3x2, Matrix3x2>, 23 | IMultiplyOperators, T, Matrix3x2>, 24 | ISubtractionOperators, Matrix3x2, Matrix3x2>, 25 | IUnaryNegationOperators, Matrix3x2>, 26 | IUnaryPlusOperators, Matrix3x2> 27 | where T : struct, IFloatingPointIeee754 28 | { 29 | // Fields 30 | 31 | /// The first element of the first row. 32 | public T M11 { get; init; } 33 | 34 | /// The second element of the first row. 35 | public T M12 { get; init; } 36 | 37 | /// The first element of the second row. 38 | public T M21 { get; init; } 39 | 40 | /// The second element of the second row. 41 | public T M22 { get; init; } 42 | 43 | /// The first element of the third row. 44 | public T M31 { get; init; } 45 | 46 | /// The second element of the third row. 47 | public T M32 { get; init; } 48 | 49 | // Constructors 50 | 51 | /// Creates a 3x2 matrix from the specified components. 52 | /// The value to assign to the first element in the first row. 53 | /// The value to assign to the second element in the first row. 54 | /// The value to assign to the first element in the second row. 55 | /// The value to assign to the second element in the second row. 56 | /// The value to assign to the first element in the third row. 57 | /// The value to assign to the second element in the third row. 58 | public Matrix3x2(T m11, T m12, T m21, T m22, T m31, T m32) 59 | { 60 | M11 = m11; 61 | M12 = m12; 62 | M21 = m21; 63 | M22 = m22; 64 | M31 = m31; 65 | M32 = m32; 66 | } 67 | 68 | // Static Properties 69 | 70 | /// Gets the multiplicative identity matrix. 71 | /// The multiplicative identify matrix. 72 | public static Matrix3x2 Identity { get; } = new( 73 | T.One, T.Zero, 74 | T.Zero, T.One, 75 | T.Zero, T.Zero); 76 | 77 | static Matrix3x2 IAdditiveIdentity, Matrix3x2>.AdditiveIdentity 78 | => new(T.Zero, T.Zero, T.Zero, T.Zero, T.Zero, T.Zero); 79 | 80 | static Matrix3x2 IMultiplicativeIdentity, Matrix3x2>.MultiplicativeIdentity => Identity; 81 | 82 | // Properties 83 | 84 | 85 | /// Gets a value that indicates whether the current matrix is the identity matrix. 86 | /// if the current matrix is the identity matrix; otherwise, . 87 | public bool IsIdentity 88 | { 89 | get 90 | { 91 | return M11 == T.One && M22 == T.One && // Check diagonal element first for early out. 92 | M12 == T.Zero && M21 == T.Zero && 93 | M31 == T.Zero && M32 == T.Zero; 94 | } 95 | } 96 | 97 | /// Gets or sets the translation component of this matrix. 98 | /// The translation component of the current instance. 99 | public Vector2 Translation => new(M31, M32); 100 | 101 | // Operators 102 | 103 | /// Computes the unary plus of a value. 104 | /// The value for which to compute its unary plus. 105 | /// The unary plus of . 106 | public static Matrix3x2 operator +(Matrix3x2 value) 107 | { 108 | return new( 109 | value.M11, 110 | value.M12, 111 | value.M21, 112 | value.M22, 113 | value.M31, 114 | value.M32); 115 | } 116 | 117 | /// Negates the specified matrix by multiplying all its values by -1. 118 | /// The matrix to negate. 119 | /// The negated matrix. 120 | /// 121 | public static Matrix3x2 operator -(Matrix3x2 value) 122 | { 123 | return new( 124 | -value.M11, 125 | -value.M12, 126 | -value.M21, 127 | -value.M22, 128 | -value.M31, 129 | -value.M32); 130 | } 131 | 132 | /// Adds each element in one matrix with its corresponding element in a second matrix. 133 | /// The first matrix. 134 | /// The second matrix. 135 | /// The matrix that contains the summed values. 136 | /// The method defines the operation of the addition operator for objects. 137 | public static Matrix3x2 operator +(Matrix3x2 left, Matrix3x2 right) 138 | { 139 | return new( 140 | left.M11 + right.M11, 141 | left.M12 + right.M12, 142 | left.M21 + right.M21, 143 | left.M22 + right.M22, 144 | left.M31 + right.M31, 145 | left.M32 + right.M32); 146 | } 147 | 148 | /// Subtracts each element in a second matrix from its corresponding element in a first matrix. 149 | /// The first matrix. 150 | /// The second matrix. 151 | /// The matrix containing the values that result from subtracting each element in from its corresponding element in . 152 | /// The method defines the operation of the subtraction operator for objects. 153 | public static Matrix3x2 operator -(Matrix3x2 left, Matrix3x2 right) 154 | { 155 | return new( 156 | left.M11 - right.M11, 157 | left.M12 - right.M12, 158 | left.M21 - right.M21, 159 | left.M22 - right.M22, 160 | left.M31 - right.M31, 161 | left.M32 - right.M32); 162 | } 163 | 164 | /// Multiplies two matrices together to compute the product. 165 | /// The first matrix. 166 | /// The second matrix. 167 | /// The product matrix. 168 | /// The method defines the operation of the multiplication operator for objects. 169 | public static Matrix3x2 operator *(Matrix3x2 left, Matrix3x2 right) 170 | { 171 | return new( 172 | // First row 173 | left.M11 * right.M11 + left.M12 * right.M21, 174 | left.M11 * right.M12 + left.M12 * right.M22, 175 | 176 | // Second row 177 | left.M21 * right.M11 + left.M22 * right.M21, 178 | left.M21 * right.M12 + left.M22 * right.M22, 179 | 180 | // Third row 181 | left.M31 * right.M11 + left.M32 * right.M21 + right.M31, 182 | left.M31 * right.M12 + left.M32 * right.M22 + right.M32); 183 | } 184 | 185 | /// Multiplies a matrix by a float to compute the product. 186 | /// The matrix to scale. 187 | /// The scaling value to use. 188 | /// The scaled matrix. 189 | /// The method defines the operation of the multiplication operator for objects. 190 | public static Matrix3x2 operator *(Matrix3x2 left, T right) 191 | { 192 | return new( 193 | left.M11 * right, 194 | left.M12 * right, 195 | left.M21 * right, 196 | left.M22 * right, 197 | left.M31 * right, 198 | left.M32 * right); 199 | } 200 | 201 | // Methods 202 | 203 | /// Returns a value that indicates whether this instance and another 3x2 matrix are equal. 204 | /// The other matrix. 205 | /// if the two matrices are equal; otherwise, . 206 | /// Two matrices are equal if all their corresponding elements are equal. 207 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 208 | public readonly bool Equals(Matrix3x2 other) 209 | { 210 | // Check diagonal element first for early out. 211 | return (M11 == other.M11 212 | && M22 == other.M22 213 | && M12 == other.M12 214 | && M21 == other.M21 215 | && M31 == other.M31 216 | && M32 == other.M32); 217 | } 218 | 219 | /// Calculates the determinant for this matrix. 220 | /// The determinant. 221 | /// The determinant is calculated by expanding the matrix with a third column whose values are (0,0,1). 222 | public T GetDeterminant() 223 | { 224 | // There isn't actually any such thing as a determinant for a non-square matrix, 225 | // but this 3x2 type is really just an optimization of a 3x3 where we happen to 226 | // know the rightmost column is always (0, 0, 1). So we expand to 3x3 format: 227 | // 228 | // [ M11, M12, 0 ] 229 | // [ M21, M22, 0 ] 230 | // [ M31, M32, 1 ] 231 | // 232 | // Sum the diagonal products: 233 | // (M11 * M22 * 1) + (M12 * 0 * M31) + (0 * M21 * M32) 234 | // 235 | // Subtract the opposite diagonal products: 236 | // (M31 * M22 * 0) + (M32 * 0 * M11) + (1 * M21 * M12) 237 | // 238 | // Collapse out the constants and oh look, this is just a 2x2 determinant! 239 | 240 | return (M11 * M22) - (M21 * M12); 241 | } 242 | 243 | /// Returns a string that represents this matrix. 244 | /// The string representation of this matrix. 245 | /// The numeric values in the returned string are formatted by using the conventions of the current culture. For example, for the en-US culture, the returned string might appear as { {M11:1.1 M12:1.2} {M21:2.1 M22:2.2} {M31:3.1 M32:3.2} }. 246 | public override string ToString() 247 | => $"{{ {{M11:{M11} M12:{M12}}} {{M21:{M21} M22:{M22}}} {{M31:{M31} M32:{M32}}} }}"; 248 | 249 | /// Returns the string representation of the current instance using the specified format string to format individual elements. 250 | /// A standard or custom numeric format string that defines the format of individual elements. 251 | /// The string representation of the current instance. 252 | /// This method returns a string in which each element of the vector is formatted using and the current culture's formatting conventions. The "<" and ">" characters are used to begin and end the string, and the current culture's property followed by a space is used to separate each element. 253 | /// Standard Numeric Format Strings 254 | /// Custom Numeric Format Strings 255 | public readonly string ToString(string? format) 256 | { 257 | return ToString(format, CultureInfo.CurrentCulture); 258 | } 259 | /// Returns the string representation of the current instance using the specified format string to format individual elements and the specified format provider to define culture-specific formatting. 260 | /// A standard or custom numeric format string that defines the format of individual elements. 261 | /// A format provider that supplies culture-specific formatting information. 262 | /// The string representation of the current instance. 263 | /// This method returns a string in which each element of the vector is formatted using and . The "<" and ">" characters are used to begin and end the string, and the format provider's property followed by a space is used to separate each element. 264 | /// Custom Numeric Format Strings 265 | /// Standard Numeric Format Strings 266 | public readonly string ToString(string? format, IFormatProvider? formatProvider) 267 | { 268 | StringBuilder sb = new StringBuilder(); 269 | sb.Append("{ "); 270 | 271 | sb.Append('{'); 272 | sb.Append(M11.ToString(format, formatProvider)); 273 | sb.Append(' '); 274 | sb.Append(M12.ToString(format, formatProvider)); 275 | sb.Append('}'); 276 | 277 | sb.Append(' '); 278 | 279 | sb.Append('{'); 280 | sb.Append(M21.ToString(format, formatProvider)); 281 | sb.Append(' '); 282 | sb.Append(M22.ToString(format, formatProvider)); 283 | sb.Append('}'); 284 | 285 | sb.Append(' '); 286 | 287 | sb.Append('{'); 288 | sb.Append(M31.ToString(format, formatProvider)); 289 | sb.Append(' '); 290 | sb.Append(M32.ToString(format, formatProvider)); 291 | sb.Append('}'); 292 | 293 | sb.Append(" }"); 294 | return sb.ToString(); 295 | } 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /GenericVectors/src/PlaneS.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace System.Numerics 7 | { 8 | public partial struct PlaneS 9 | { 10 | // Static Methods 11 | 12 | /// Creates a object that contains three specified points. 13 | /// The first point defining the plane. 14 | /// The second point defining the plane. 15 | /// The third point defining the plane. 16 | /// The plane containing the three points. 17 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 18 | public static Plane CreateFromVertices(in Vector3 point1, in Vector3 point2, in Vector3 point3) 19 | where T : struct, IFloatingPointIeee754 20 | { 21 | T ax = point2.X - point1.X; 22 | T ay = point2.Y - point1.Y; 23 | T az = point2.Z - point1.Z; 24 | 25 | T bx = point3.X - point1.X; 26 | T by = point3.Y - point1.Y; 27 | T bz = point3.Z - point1.Z; 28 | 29 | // N=Cross(a,b) 30 | T nx = ay * bz - az * by; 31 | T ny = az * bx - ax * bz; 32 | T nz = ax * by - ay * bx; 33 | 34 | // Normalize(N) 35 | T ls = nx * nx + ny * ny + nz * nz; 36 | T invNorm = T.One / T.Sqrt(ls); 37 | 38 | Vector3 normal = new( 39 | nx * invNorm, 40 | ny * invNorm, 41 | nz * invNorm); 42 | 43 | return new( 44 | normal, 45 | -(normal.X * point1.X + normal.Y * point1.Y + normal.Z * point1.Z)); 46 | } 47 | 48 | /// Calculates the dot product of a plane and a 4-dimensional vector. 49 | /// The plane. 50 | /// The four-dimensional vector. 51 | /// The dot product. 52 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 53 | public static T Dot(in Plane left, in Vector4 right) 54 | where T : struct, IFloatingPointIeee754 55 | { 56 | return left.Normal.X * right.X + 57 | left.Normal.Y * right.Y + 58 | left.Normal.Z * right.Z + 59 | left.Distance * right.W; 60 | } 61 | 62 | /// Returns the dot product of a specified three-dimensional vector and the normal vector of this plane plus the distance () value of the plane. 63 | /// The plane. 64 | /// The 3-dimensional vector. 65 | /// The dot product. 66 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 67 | public static T DotCoordinate(in Plane left, in Vector3 right) 68 | where T : struct, IFloatingPointIeee754 69 | { 70 | return left.Normal.X * right.X + 71 | left.Normal.Y * right.Y + 72 | left.Normal.Z * right.Z + 73 | left.Distance; 74 | } 75 | 76 | /// Returns the dot product of a specified three-dimensional vector and the vector of this plane. 77 | /// The plane. 78 | /// The three-dimensional vector. 79 | /// The dot product. 80 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 81 | public static T DotNormal(in Plane left, in Vector3 right) 82 | where T : struct, IFloatingPointIeee754 83 | { 84 | return left.Normal.X * right.X + 85 | left.Normal.Y * right.Y + 86 | left.Normal.Z * right.Z; 87 | } 88 | 89 | /// Creates a new object whose normal vector is the source plane's normal vector normalized. 90 | /// The source plane. 91 | /// The normalized plane. 92 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 93 | public static Plane Normalize(in Plane value) 94 | where T : struct, IFloatingPointIeee754 95 | { 96 | T normalizeEpsilon = T.CreateChecked(1.192092896e-07); // smallest such that 1.0+NormalizeEpsilon != 1.0 97 | 98 | T f = value.Normal.X * value.Normal.X + value.Normal.Y * value.Normal.Y + value.Normal.Z * value.Normal.Z; 99 | 100 | if (T.Abs(f - T.One) < normalizeEpsilon) 101 | { 102 | return value; // It already normalized, so we don't need to further process. 103 | } 104 | 105 | T fInv = T.One / T.Sqrt(f); 106 | 107 | return new( 108 | value.Normal.X * fInv, 109 | value.Normal.Y * fInv, 110 | value.Normal.Z * fInv, 111 | value.Distance * fInv); 112 | } 113 | 114 | /// Transforms a normalized plane by a 4x4 matrix. 115 | /// The normalized plane to transform. 116 | /// The transformation matrix to apply to . 117 | /// The transformed plane. 118 | /// must already be normalized so that its vector is of unit length before this method is called. 119 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 120 | public static Plane Transform(in Plane plane, in Matrix4x4 matrix) 121 | where T : struct, IFloatingPointIeee754 122 | { 123 | Matrix4x4S.Invert(matrix, out Matrix4x4 m); 124 | 125 | T x = plane.Normal.X, y = plane.Normal.Y, z = plane.Normal.Z, w = plane.Distance; 126 | 127 | return new( 128 | x * m.M11 + y * m.M12 + z * m.M13 + w * m.M14, 129 | x * m.M21 + y * m.M22 + z * m.M23 + w * m.M24, 130 | x * m.M31 + y * m.M32 + z * m.M33 + w * m.M34, 131 | x * m.M41 + y * m.M42 + z * m.M43 + w * m.M44); 132 | } 133 | 134 | /// Transforms a normalized plane by a Quaternion rotation. 135 | /// The normalized plane to transform. 136 | /// The Quaternion rotation to apply to the plane. 137 | /// A new plane that results from applying the Quaternion rotation. 138 | /// must already be normalized so that its vector is of unit length before this method is called. 139 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 140 | public static Plane Transform(in Plane plane, in Quaternion rotation) 141 | where T : struct, IFloatingPointIeee754 142 | { 143 | // Compute rotation matrix. 144 | T x2 = rotation.X + rotation.X; 145 | T y2 = rotation.Y + rotation.Y; 146 | T z2 = rotation.Z + rotation.Z; 147 | 148 | T wx2 = rotation.W * x2; 149 | T wy2 = rotation.W * y2; 150 | T wz2 = rotation.W * z2; 151 | T xx2 = rotation.X * x2; 152 | T xy2 = rotation.X * y2; 153 | T xz2 = rotation.X * z2; 154 | T yy2 = rotation.Y * y2; 155 | T yz2 = rotation.Y * z2; 156 | T zz2 = rotation.Z * z2; 157 | 158 | T m11 = T.One - yy2 - zz2; 159 | T m21 = xy2 - wz2; 160 | T m31 = xz2 + wy2; 161 | 162 | T m12 = xy2 + wz2; 163 | T m22 = T.One - xx2 - zz2; 164 | T m32 = yz2 - wx2; 165 | 166 | T m13 = xz2 - wy2; 167 | T m23 = yz2 + wx2; 168 | T m33 = T.One - xx2 - yy2; 169 | 170 | T x = plane.Normal.X, y = plane.Normal.Y, z = plane.Normal.Z; 171 | 172 | return new( 173 | x * m11 + y * m21 + z * m31, 174 | x * m12 + y * m22 + z * m32, 175 | x * m13 + y * m23 + z * m33, 176 | plane.Distance); 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /GenericVectors/src/Plane_1.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Globalization; 5 | using System.Runtime.CompilerServices; 6 | using System.Text; 7 | 8 | namespace System.Numerics 9 | { 10 | /// Represents a plane in three-dimensional space. 11 | /// 14 | [Intrinsic] 15 | public readonly record struct Plane : 16 | IEquatable>, 17 | IEqualityOperators, Plane, bool>, 18 | IFormattable 19 | where T : struct, IFloatingPointIeee754 20 | { 21 | // Fields 22 | 23 | /// The distance of the plane along its normal from the origin. 24 | public T Distance { get; init; } 25 | 26 | /// The normal vector of the plane. 27 | public Vector3 Normal { get; init; } 28 | 29 | // Constructors 30 | 31 | /// Creates a object from the X, Y, and Z components of its normal, and its distance from the origin on that normal. 32 | /// The X component of the normal. 33 | /// The Y component of the normal. 34 | /// The Z component of the normal. 35 | /// The distance of the plane along its normal from the origin. 36 | public Plane(T x, T y, T z, T distance) 37 | { 38 | Normal = new(x, y, z); 39 | Distance = distance; 40 | } 41 | 42 | /// Creates a object from a specified normal and the distance along the normal from the origin. 43 | /// The plane's normal vector. 44 | /// The plane's distance from the origin along its normal vector. 45 | public Plane(in Vector3 normal, T distance) 46 | { 47 | Normal = normal; 48 | Distance = distance; 49 | } 50 | 51 | /// Creates a object from a specified four-dimensional vector. 52 | /// A vector whose first three elements describe the normal vector, and whose defines the distance along that normal from the origin. 53 | public Plane(in Vector4 value) 54 | { 55 | Normal = new(value.X, value.Y, value.Z); 56 | Distance = value.W; 57 | } 58 | 59 | // Operators 60 | 61 | // Methods 62 | 63 | /// Returns a value that indicates whether this instance and another plane are equal. 64 | /// The other plane. 65 | /// if the two planes are equal; otherwise, . 66 | /// Two objects are equal if their and fields are equal. 67 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 68 | public bool Equals(Plane other) 69 | { 70 | return Normal == other.Normal && Distance == other.Distance; 71 | } 72 | 73 | /// Returns the string representation of this plane object. 74 | /// A string that represents this object. 75 | /// The string representation of a object use the formatting conventions of the current culture to format the numeric values in the returned string. For example, a object whose string representation is formatted by using the conventions of the en-US culture might appear as {Normal:<1.1, 2.2, 3.3> D:4.4}. 76 | public override readonly string ToString() => $"{{Normal:{Normal} Distance:{Distance}}}"; 77 | 78 | /// Returns the string representation of the current instance using the specified format string to format individual elements. 79 | /// A standard or custom numeric format string that defines the format of individual elements. 80 | /// The string representation of the current instance. 81 | /// This method returns a string in which each element of the vector is formatted using and the current culture's formatting conventions. The "<" and ">" characters are used to begin and end the string, and the current culture's property followed by a space is used to separate each element. 82 | /// Standard Numeric Format Strings 83 | /// Custom Numeric Format Strings 84 | public readonly string ToString(string? format) 85 | { 86 | return ToString(format, CultureInfo.CurrentCulture); 87 | } 88 | 89 | /// Returns the string representation of the current instance using the specified format string to format individual elements and the specified format provider to define culture-specific formatting. 90 | /// A standard or custom numeric format string that defines the format of individual elements. 91 | /// A format provider that supplies culture-specific formatting information. 92 | /// The string representation of the current instance. 93 | /// This method returns a string in which each element of the vector is formatted using and . The "<" and ">" characters are used to begin and end the string, and the format provider's property followed by a space is used to separate each element. 94 | /// Custom Numeric Format Strings 95 | /// Standard Numeric Format Strings 96 | public readonly string ToString(string? format, IFormatProvider? formatProvider) 97 | { 98 | StringBuilder sb = new StringBuilder(); 99 | sb.Append("{Normal:"); 100 | sb.Append(Normal.ToString(format, formatProvider)); 101 | sb.Append(" Distance:"); 102 | sb.Append(Distance.ToString(format, formatProvider)); 103 | sb.Append('}'); 104 | return sb.ToString(); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /GenericVectors/src/QuaternionS.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace System.Numerics 7 | { 8 | public partial struct QuaternionS 9 | { 10 | // "Friendly" Operators 11 | 12 | /// Computes the unary plus of a value. 13 | /// The value for which to compute its unary plus. 14 | /// The unary plus of . 15 | public static Quaternion Plus(in Quaternion value) 16 | where T : struct, IFloatingPointIeee754 17 | { 18 | return value; 19 | } 20 | 21 | /// Reverses the sign of each component of the quaternion. 22 | /// The quaternion to negate. 23 | /// The negated quaternion. 24 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 25 | public static Quaternion Negate(in Quaternion value) 26 | where T : struct, IFloatingPointIeee754 27 | { 28 | return -value; 29 | } 30 | 31 | /// Adds each element in one quaternion with its corresponding element in a second quaternion. 32 | /// The first quaternion. 33 | /// The second quaternion. 34 | /// The quaternion that contains the summed values of and . 35 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 36 | public static Quaternion Add(in Quaternion left, in Quaternion right) 37 | where T : struct, IFloatingPointIeee754 38 | { 39 | return left + right; 40 | } 41 | 42 | /// Subtracts each element in a second quaternion from its corresponding element in a first quaternion. 43 | /// The first quaternion. 44 | /// The second quaternion. 45 | /// The quaternion containing the values that result from subtracting each element in from its corresponding element in . 46 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 47 | public static Quaternion Subtract(in Quaternion left, in Quaternion right) 48 | where T : struct, IFloatingPointIeee754 49 | { 50 | return left - right; 51 | } 52 | 53 | /// Returns the quaternion that results from multiplying two quaternions together. 54 | /// The first quaternion. 55 | /// The second quaternion. 56 | /// The product quaternion. 57 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 58 | public static Quaternion Multiply(in Quaternion left, in Quaternion right) 59 | where T : struct, IFloatingPointIeee754 60 | { 61 | return left * right; 62 | } 63 | 64 | /// Divides one quaternion by a second quaternion. 65 | /// The dividend. 66 | /// The divisor. 67 | /// The quaternion that results from dividing by . 68 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 69 | public static Quaternion Divide(in Quaternion left, in Quaternion right) 70 | where T : struct, IFloatingPointIeee754 71 | { 72 | return left / right; 73 | } 74 | 75 | /// Returns the quaternion that results from scaling all the components of a specified quaternion by a scalar factor. 76 | /// The source quaternion. 77 | /// The scalar value. 78 | /// The scaled quaternion. 79 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 80 | public static Quaternion Multiply(in Quaternion left, T right) 81 | where T : struct, IFloatingPointIeee754 82 | { 83 | return left * right; 84 | } 85 | 86 | // Static Methods 87 | 88 | /// Concatenates two quaternions. 89 | /// The first quaternion rotation in the series. 90 | /// The second quaternion rotation in the series. 91 | /// A new quaternion representing the concatenation of the rotation followed by the rotation. 92 | public static Quaternion Concatenate(in Quaternion left, in Quaternion right) 93 | where T : struct, IFloatingPointIeee754 94 | { 95 | // Concatenate rotation is actually q2 * q1 instead of q1 * q2. 96 | // So that's why value2 goes q1 and value1 goes q2. 97 | T q1x = right.X; 98 | T q1y = right.Y; 99 | T q1z = right.Z; 100 | T q1w = right.W; 101 | 102 | T q2x = left.X; 103 | T q2y = left.Y; 104 | T q2z = left.Z; 105 | T q2w = left.W; 106 | 107 | // cross(av, bv) 108 | T cx = q1y * q2z - q1z * q2y; 109 | T cy = q1z * q2x - q1x * q2z; 110 | T cz = q1x * q2y - q1y * q2x; 111 | 112 | T dot = q1x * q2x + q1y * q2y + q1z * q2z; 113 | 114 | return new( 115 | q1x * q2w + q2x * q1w + cx, 116 | q1y * q2w + q2y * q1w + cy, 117 | q1z * q2w + q2z * q1w + cz, 118 | q1w * q2w - dot); 119 | } 120 | 121 | /// Returns the conjugate of a specified quaternion. 122 | /// The quaternion. 123 | /// A new quaternion that is the conjugate of . 124 | public static Quaternion Conjugate(in Quaternion value) 125 | where T : struct, IFloatingPointIeee754 126 | { 127 | return new( 128 | -value.X, 129 | -value.Y, 130 | -value.Z, 131 | value.W); 132 | } 133 | 134 | /// Creates a quaternion from a unit vector and an angle to rotate around the vector. 135 | /// The unit vector to rotate around. 136 | /// The angle, in radians, to rotate around the vector. 137 | /// The newly created quaternion. 138 | /// vector must be normalized before calling this method or the resulting will be incorrect. 139 | public static Quaternion CreateFromAxisAngle(in Vector3 axis, T angle) 140 | where T : struct, IFloatingPointIeee754 141 | { 142 | T halfAngle = angle * T.CreateChecked(0.5); 143 | T s = T.Sin(halfAngle); 144 | T c = T.Cos(halfAngle); 145 | 146 | return new( 147 | axis.X * s, 148 | axis.Y * s, 149 | axis.Z * s, 150 | c); 151 | } 152 | 153 | /// Creates a quaternion from the specified rotation matrix. 154 | /// The rotation matrix. 155 | /// The newly created quaternion. 156 | public static Quaternion CreateFromRotationMatrix(in Matrix4x4 matrix) 157 | where T : struct, IFloatingPointIeee754 158 | { 159 | T trace = matrix.M11 + matrix.M22 + matrix.M33; 160 | 161 | if (trace > T.Zero) 162 | { 163 | T s = T.Sqrt(trace + T.One); 164 | T invS = T.CreateChecked(0.5) / s; 165 | return new( 166 | (matrix.M23 - matrix.M32) * invS, 167 | (matrix.M31 - matrix.M13) * invS, 168 | (matrix.M12 - matrix.M21) * invS, 169 | s * T.CreateChecked(0.5)); 170 | } 171 | else 172 | { 173 | if (matrix.M11 >= matrix.M22 && matrix.M11 >= matrix.M33) 174 | { 175 | T s = T.Sqrt(T.One + matrix.M11 - matrix.M22 - matrix.M33); 176 | T invS = T.CreateChecked(0.5) / s; 177 | return new( 178 | T.CreateChecked(0.5) * s, 179 | (matrix.M12 + matrix.M21) * invS, 180 | (matrix.M13 + matrix.M31) * invS, 181 | (matrix.M23 - matrix.M32) * invS); 182 | } 183 | if (matrix.M22 > matrix.M33) 184 | { 185 | T s = T.Sqrt(T.One + matrix.M22 - matrix.M11 - matrix.M33); 186 | T invS = T.CreateChecked(0.5) / s; 187 | return new( 188 | (matrix.M21 + matrix.M12) * invS, 189 | T.CreateChecked(0.5) * s, 190 | (matrix.M32 + matrix.M23) * invS, 191 | (matrix.M31 - matrix.M13) * invS); 192 | } 193 | else 194 | { 195 | T s = T.Sqrt(T.One + matrix.M33 - matrix.M11 - matrix.M22); 196 | T invS = T.CreateChecked(0.5) / s; 197 | return new( 198 | (matrix.M31 + matrix.M13) * invS, 199 | (matrix.M32 + matrix.M23) * invS, 200 | T.CreateChecked(0.5) * s, 201 | (matrix.M12 - matrix.M21) * invS); 202 | } 203 | } 204 | } 205 | 206 | /// Creates a new quaternion from the given yaw, pitch, and roll. 207 | /// The yaw angle, in radians, around the Y axis. 208 | /// The pitch angle, in radians, around the X axis. 209 | /// The roll angle, in radians, around the Z axis. 210 | /// The resulting quaternion. 211 | public static Quaternion CreateFromYawPitchRoll(T yaw, T pitch, T roll) 212 | where T : struct, IFloatingPointIeee754 213 | { 214 | // Roll first, about axis the object is facing, then 215 | // pitch upward, then yaw to face into the new heading 216 | T sr, cr, sp, cp, sy, cy; 217 | 218 | T halfRoll = roll * T.CreateChecked(0.5); 219 | sr = T.Sin(halfRoll); 220 | cr = T.Cos(halfRoll); 221 | 222 | T halfPitch = pitch * T.CreateChecked(0.5); 223 | sp = T.Sin(halfPitch); 224 | cp = T.Cos(halfPitch); 225 | 226 | T halfYaw = yaw * T.CreateChecked(0.5); 227 | sy = T.Sin(halfYaw); 228 | cy = T.Cos(halfYaw); 229 | 230 | return new( 231 | cy * sp * cr + sy * cp * sr, 232 | sy * cp * cr - cy * sp * sr, 233 | cy * cp * sr - sy * sp * cr, 234 | cy * cp * cr + sy * sp * sr); 235 | } 236 | 237 | /// Calculates the dot product of two quaternions. 238 | /// The first quaternion. 239 | /// The second quaternion. 240 | /// The dot product. 241 | public static T Dot(in Quaternion left, in Quaternion right) 242 | where T : struct, IFloatingPointIeee754 243 | { 244 | return left.X * right.X + 245 | left.Y * right.Y + 246 | left.Z * right.Z + 247 | left.W * right.W; 248 | } 249 | 250 | /// Returns the inverse of a quaternion. 251 | /// The quaternion. 252 | /// The inverted quaternion. 253 | public static Quaternion Inverse(in Quaternion value) 254 | where T : struct, IFloatingPointIeee754 255 | { 256 | // -1 ( a -v ) 257 | // q = ( ------------- ------------- ) 258 | // ( a^2 + |v|^2 , a^2 + |v|^2 ) 259 | 260 | T ls = value.X * value.X + value.Y * value.Y + value.Z * value.Z + value.W * value.W; 261 | T invNorm = T.One / ls; 262 | 263 | return new( 264 | -value.X * invNorm, 265 | -value.Y * invNorm, 266 | -value.Z * invNorm, 267 | value.W * invNorm); 268 | } 269 | 270 | /// Performs a linear interpolation between two quaternions based on a value that specifies the weighting of the second quaternion. 271 | /// The first quaternion. 272 | /// The second quaternion. 273 | /// The relative weight of in the interpolation. 274 | /// The interpolated quaternion. 275 | public static Quaternion Lerp(in Quaternion min, in Quaternion max, T amount) 276 | where T : struct, IFloatingPointIeee754 277 | { 278 | T t = amount; 279 | T t1 = T.One - t; 280 | 281 | T dot = min.X * max.X + min.Y * max.Y + 282 | min.Z * max.Z + min.W * max.W; 283 | 284 | Quaternion r; 285 | if (dot >= T.Zero) 286 | { 287 | r = new( 288 | t1 * min.X + t * max.X, 289 | t1 * min.Y + t * max.Y, 290 | t1 * min.Z + t * max.Z, 291 | t1 * min.W + t * max.W); 292 | } 293 | else 294 | { 295 | r = new( 296 | t1 * min.X - t * max.X, 297 | t1 * min.Y - t * max.Y, 298 | t1 * min.Z - t * max.Z, 299 | t1 * min.W - t * max.W); 300 | } 301 | 302 | // Normalize it. 303 | T ls = r.X * r.X + r.Y * r.Y + r.Z * r.Z + r.W * r.W; 304 | T invNorm = T.One / T.Sqrt(ls); 305 | return new( 306 | r.X * invNorm, 307 | r.Y * invNorm, 308 | r.Z * invNorm, 309 | r.W * invNorm); 310 | } 311 | 312 | /// Interpolates between two quaternions, using spherical linear interpolation. 313 | /// The first quaternion. 314 | /// The second quaternion. 315 | /// The relative weight of the second quaternion in the interpolation. 316 | /// The interpolated quaternion. 317 | public static Quaternion Slerp(in Quaternion min, in Quaternion max, T amount) 318 | where T : struct, IFloatingPointIeee754 319 | { 320 | T slerpEpsilon = T.CreateChecked(1e-6); 321 | 322 | T t = amount; 323 | 324 | T cosOmega = min.X * max.X + min.Y * max.Y + 325 | min.Z * max.Z + min.W * max.W; 326 | 327 | bool flip = false; 328 | 329 | if (cosOmega < T.Zero) 330 | { 331 | flip = true; 332 | cosOmega = -cosOmega; 333 | } 334 | 335 | T s1, s2; 336 | 337 | if (cosOmega > (T.One - slerpEpsilon)) 338 | { 339 | // Too close, do straight linear interpolation. 340 | s1 = T.One - t; 341 | s2 = (flip) ? -t : t; 342 | } 343 | else 344 | { 345 | T omega = T.Acos(cosOmega); 346 | T invSinOmega = T.One / T.Sin(omega); 347 | 348 | s1 = T.Sin((T.One - t) * omega) * invSinOmega; 349 | s2 = (flip) 350 | ? -T.Sin(t * omega) * invSinOmega 351 | : T.Sin(t * omega) * invSinOmega; 352 | } 353 | 354 | return new( 355 | s1 * min.X + s2 * max.X, 356 | s1 * min.Y + s2 * max.Y, 357 | s1 * min.Z + s2 * max.Z, 358 | s1 * min.W + s2 * max.W); 359 | } 360 | 361 | /// Divides each component of a specified by its length. 362 | /// The quaternion to normalize. 363 | /// The normalized quaternion. 364 | public static Quaternion Normalize(in Quaternion value) 365 | where T : struct, IFloatingPointIeee754 366 | { 367 | T ls = value.X * value.X + value.Y * value.Y + value.Z * value.Z + value.W * value.W; 368 | 369 | T invNorm = T.One / T.Sqrt(ls); 370 | 371 | return new( 372 | value.X * invNorm, 373 | value.Y * invNorm, 374 | value.Z * invNorm, 375 | value.W * invNorm); 376 | } 377 | } 378 | } 379 | -------------------------------------------------------------------------------- /GenericVectors/src/Quaternion_1.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Globalization; 5 | using System.Runtime.CompilerServices; 6 | using System.Text; 7 | 8 | namespace System.Numerics 9 | { 10 | /// Represents a vector that is used to encode three-dimensional physical rotations. 11 | /// The structure is used to efficiently rotate an object about the (x,y,z) vector by the angle theta, where: 12 | /// w = cos(theta/2) 13 | [Intrinsic] 14 | public readonly record struct Quaternion : 15 | IAdditionOperators, Quaternion, Quaternion>, 16 | IAdditiveIdentity, Quaternion>, 17 | IDivisionOperators, Quaternion, Quaternion>, 18 | IEquatable>, 19 | IEqualityOperators, Quaternion, bool>, 20 | IFormattable, 21 | IMultiplicativeIdentity, Quaternion>, 22 | IMultiplyOperators, Quaternion, Quaternion>, 23 | IMultiplyOperators, T, Quaternion>, 24 | ISubtractionOperators, Quaternion, Quaternion>, 25 | IUnaryNegationOperators, Quaternion>, 26 | IUnaryPlusOperators, Quaternion> 27 | where T : struct, IFloatingPointIeee754 28 | { 29 | // Fields 30 | 31 | /// The X value of the vector component of the quaternion. 32 | public T X { get; init; } 33 | 34 | /// The Y value of the vector component of the quaternion. 35 | public T Y { get; init; } 36 | 37 | /// The Z value of the vector component of the quaternion. 38 | public T Z { get; init; } 39 | 40 | /// The rotation component of the quaternion. 41 | public T W { get; init; } 42 | 43 | // Constructors 44 | 45 | /// Constructs a quaternion from the specified components. 46 | /// The value to assign to the X component of the quaternion. 47 | /// The value to assign to the Y component of the quaternion. 48 | /// The value to assign to the Z component of the quaternion. 49 | /// The value to assign to the W component of the quaternion. 50 | public Quaternion(T x, T y, T z, T w) 51 | { 52 | X = x; 53 | Y = y; 54 | Z = z; 55 | W = w; 56 | } 57 | 58 | /// Creates a quaternion from the specified vector and rotation parts. 59 | /// The vector part of the quaternion. 60 | /// The rotation part of the quaternion. 61 | public Quaternion(in Vector3 vector, T rotation) 62 | { 63 | X = vector.X; 64 | Y = vector.Y; 65 | Z = vector.Z; 66 | W = rotation; 67 | } 68 | 69 | // Static Properties 70 | 71 | /// Gets a quaternion that represents no rotation. 72 | /// A quaternion whose values are (0, 0, 0, 1). 73 | public static Quaternion Identity { get; } = new Quaternion(T.Zero, T.Zero, T.Zero, T.One); 74 | 75 | // Properties 76 | 77 | /// Gets a value that indicates whether the current instance is the identity quaternion. 78 | /// if the current instance is the identity quaternion; otherwise, . 79 | /// 80 | public readonly bool IsIdentity => this == Identity; 81 | 82 | static Quaternion IAdditiveIdentity, Quaternion>.AdditiveIdentity 83 | => new Quaternion(T.Zero, T.Zero, T.Zero, T.Zero); 84 | 85 | static Quaternion IMultiplicativeIdentity, Quaternion>.MultiplicativeIdentity => Identity; 86 | 87 | 88 | 89 | // Operators 90 | 91 | /// Computes the unary plus of a value. 92 | /// The value for which to compute its unary plus. 93 | /// The unary plus of . 94 | public static Quaternion operator +(Quaternion value) => value; 95 | 96 | /// Reverses the sign of each component of the quaternion. 97 | /// The quaternion to negate. 98 | /// The negated quaternion. 99 | /// The method defines the operation of the unary negation operator for objects. 100 | public static Quaternion operator -(Quaternion value) 101 | { 102 | return new( 103 | -value.X, 104 | -value.Y, 105 | -value.Z, 106 | -value.W); 107 | } 108 | 109 | /// Adds each element in one quaternion with its corresponding element in a second quaternion. 110 | /// The first quaternion. 111 | /// The second quaternion. 112 | /// The quaternion that contains the summed values of and . 113 | /// The method defines the operation of the addition operator for objects. 114 | public static Quaternion operator +(Quaternion left, Quaternion right) 115 | { 116 | return new( 117 | left.X + right.X, 118 | left.Y + right.Y, 119 | left.Z + right.Z, 120 | left.W + right.W); 121 | } 122 | 123 | /// Subtracts each element in a second quaternion from its corresponding element in a first quaternion. 124 | /// The first quaternion. 125 | /// The second quaternion. 126 | /// The quaternion containing the values that result from subtracting each element in from its corresponding element in . 127 | /// The method defines the operation of the subtraction operator for objects. 128 | public static Quaternion operator -(Quaternion left, Quaternion right) 129 | { 130 | return new( 131 | left.X - right.X, 132 | left.Y - right.Y, 133 | left.Z - right.Z, 134 | left.W - right.W); 135 | } 136 | 137 | /// Returns the quaternion that results from multiplying two quaternions together. 138 | /// The first quaternion. 139 | /// The second quaternion. 140 | /// The product quaternion. 141 | /// The method defines the operation of the multiplication operator for objects. 142 | public static Quaternion operator *(Quaternion left, Quaternion right) 143 | { 144 | T q1x = left.X; 145 | T q1y = left.Y; 146 | T q1z = left.Z; 147 | T q1w = left.W; 148 | 149 | T q2x = right.X; 150 | T q2y = right.Y; 151 | T q2z = right.Z; 152 | T q2w = right.W; 153 | 154 | // cross(av, bv) 155 | T cx = q1y * q2z - q1z * q2y; 156 | T cy = q1z * q2x - q1x * q2z; 157 | T cz = q1x * q2y - q1y * q2x; 158 | 159 | T dot = q1x * q2x + q1y * q2y + q1z * q2z; 160 | 161 | return new( 162 | q1x * q2w + q2x * q1w + cx, 163 | q1y * q2w + q2y * q1w + cy, 164 | q1z * q2w + q2z * q1w + cz, 165 | q1w * q2w - dot); 166 | } 167 | 168 | /// Divides one quaternion by a second quaternion. 169 | /// The dividend. 170 | /// The divisor. 171 | /// The quaternion that results from dividing by . 172 | /// The method defines the division operation for objects. 173 | public static Quaternion operator /(Quaternion left, Quaternion right) 174 | { 175 | T q1x = left.X; 176 | T q1y = left.Y; 177 | T q1z = left.Z; 178 | T q1w = left.W; 179 | 180 | //------------------------------------- 181 | // Inverse part. 182 | T ls = right.X * right.X + right.Y * right.Y + 183 | right.Z * right.Z + right.W * right.W; 184 | T invNorm = T.One / ls; 185 | 186 | T q2x = -right.X * invNorm; 187 | T q2y = -right.Y * invNorm; 188 | T q2z = -right.Z * invNorm; 189 | T q2w = right.W * invNorm; 190 | 191 | //------------------------------------- 192 | // Multiply part. 193 | 194 | // cross(av, bv) 195 | T cx = q1y * q2z - q1z * q2y; 196 | T cy = q1z * q2x - q1x * q2z; 197 | T cz = q1x * q2y - q1y * q2x; 198 | 199 | T dot = q1x * q2x + q1y * q2y + q1z * q2z; 200 | 201 | return new( 202 | q1x * q2w + q2x * q1w + cx, 203 | q1y * q2w + q2y * q1w + cy, 204 | q1z * q2w + q2z * q1w + cz, 205 | q1w * q2w - dot); 206 | } 207 | 208 | /// Returns the quaternion that results from scaling all the components of a specified quaternion by a scalar factor. 209 | /// The source quaternion. 210 | /// The scalar value. 211 | /// The scaled quaternion. 212 | /// The method defines the operation of the multiplication operator for objects. 213 | public static Quaternion operator *(Quaternion left, T right) 214 | { 215 | return new( 216 | left.X * right, 217 | left.Y * right, 218 | left.Z * right, 219 | left.W * right); 220 | } 221 | 222 | // Methods 223 | 224 | /// Returns a value that indicates whether this instance and another quaternion are equal. 225 | /// The other quaternion. 226 | /// if the two quaternions are equal; otherwise, . 227 | /// Two quaternions are equal if each of their corresponding components is equal. 228 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 229 | public bool Equals(Quaternion other) 230 | { 231 | return X == other.X && Y == other.Y && Z == other.Z && W == other.W; 232 | } 233 | 234 | /// Calculates the length of the quaternion. 235 | /// The computed length of the quaternion. 236 | public readonly T Length() 237 | { 238 | T lengthSquared = LengthSquared(); 239 | return T.Sqrt(lengthSquared); 240 | } 241 | 242 | /// Calculates the squared length of the quaternion. 243 | /// The length squared of the quaternion. 244 | public readonly T LengthSquared() 245 | { 246 | return X * X + Y * Y + Z * Z + W * W; 247 | } 248 | 249 | /// Returns a string that represents this quaternion. 250 | /// The string representation of this quaternion. 251 | /// The numeric values in the returned string are formatted by using the conventions of the current culture. For example, for the en-US culture, the returned string might appear as {X:1.1 Y:2.2 Z:3.3 W:4.4}. 252 | public override readonly string ToString() => 253 | $"{{X:{X} Y:{Y} Z:{Z} W:{W}}}"; 254 | 255 | /// Returns the string representation of the current instance using the specified format string to format individual elements. 256 | /// A standard or custom numeric format string that defines the format of individual elements. 257 | /// The string representation of the current instance. 258 | /// This method returns a string in which each element of the vector is formatted using and the current culture's formatting conventions. The "<" and ">" characters are used to begin and end the string, and the current culture's property followed by a space is used to separate each element. 259 | /// Standard Numeric Format Strings 260 | /// Custom Numeric Format Strings 261 | public readonly string ToString(string? format) 262 | { 263 | return ToString(format, CultureInfo.CurrentCulture); 264 | } 265 | 266 | /// Returns the string representation of the current instance using the specified format string to format individual elements and the specified format provider to define culture-specific formatting. 267 | /// A standard or custom numeric format string that defines the format of individual elements. 268 | /// A format provider that supplies culture-specific formatting information. 269 | /// The string representation of the current instance. 270 | /// This method returns a string in which each element of the vector is formatted using and . The "<" and ">" characters are used to begin and end the string, and the format provider's property followed by a space is used to separate each element. 271 | /// Custom Numeric Format Strings 272 | /// Standard Numeric Format Strings 273 | public readonly string ToString(string? format, IFormatProvider? formatProvider) 274 | { 275 | StringBuilder sb = new StringBuilder(); 276 | string separator = NumberFormatInfo.GetInstance(formatProvider).NumberGroupSeparator; 277 | sb.Append("{X:"); 278 | sb.Append(X.ToString(format, formatProvider)); 279 | sb.Append(separator); 280 | sb.Append(" Y:"); 281 | sb.Append(Y.ToString(format, formatProvider)); 282 | sb.Append(separator); 283 | sb.Append(" Z:"); 284 | sb.Append(Z.ToString(format, formatProvider)); 285 | sb.Append(separator); 286 | sb.Append(" W:"); 287 | sb.Append(W.ToString(format, formatProvider)); 288 | sb.Append('}'); 289 | return sb.ToString(); 290 | } 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /GenericVectors/src/SRInternals.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Diagnostics; 5 | using System.Diagnostics.CodeAnalysis; 6 | 7 | // A collection of helper routines that are normally internal to System.Runtime 8 | 9 | 10 | namespace System.Runtime.CompilerServices 11 | { 12 | // Calls to methods or references to fields marked with this attribute may be replaced at 13 | // some call sites with jit intrinsic expansions. 14 | // Types marked with this attribute may be specially treated by the runtime/compiler. 15 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Field, Inherited = false)] 16 | internal sealed class IntrinsicAttribute : Attribute 17 | { 18 | } 19 | } 20 | 21 | namespace System.Runtime.CompilerServices 22 | { 23 | static class SR 24 | { 25 | public static string Arg_NullArgumentNullRef => "Arg_NullArgumentNullRef"; 26 | public static string Arg_ArgumentOutOfRangeException => "Arg_ArgumentOutOfRangeException"; 27 | public static string Arg_ElementsInSourceIsGreaterThanDestination => "Arg_ElementsInSourceIsGreaterThanDestination"; 28 | 29 | public static string Format(string str, object? arg0) 30 | => string.Format(System.Globalization.CultureInfo.InvariantCulture, str, arg0); 31 | 32 | public static string Format(string str, object? arg0, object? arg1) 33 | => string.Format(System.Globalization.CultureInfo.InvariantCulture, str, arg0, arg1); 34 | 35 | public static string Format(string str, object? arg0, object? arg1, object? arg2) 36 | => string.Format(System.Globalization.CultureInfo.InvariantCulture, str, arg0, arg1, arg2); 37 | } 38 | } 39 | 40 | namespace System.Numerics 41 | { 42 | internal class VectorS 43 | { 44 | [DoesNotReturn] 45 | internal static void ThrowInsufficientNumberOfElementsException(int requiredElementCount) 46 | { 47 | throw new IndexOutOfRangeException(); 48 | } 49 | } 50 | } 51 | 52 | namespace System 53 | { 54 | [StackTraceHidden] 55 | internal static class ThrowHelper 56 | { 57 | [DoesNotReturn] 58 | internal static void ThrowArgumentException_DestinationTooShort() 59 | { 60 | throw new ArgumentException("SR.Argument_DestinationTooShort", "destination"); 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /GenericVectors/src/Vector2S.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace System.Numerics 7 | { 8 | public partial struct Vector2S 9 | { 10 | // "Friendly" Operators 11 | 12 | /// Computes the unary plus of a value. 13 | /// The value for which to compute its unary plus. 14 | /// The unary plus of . 15 | public static Vector2 Plus(in Vector2 value) 16 | where T : struct, IFloatingPointIeee754 17 | { 18 | return value; 19 | } 20 | 21 | /// Negates a specified vector. 22 | /// The vector to negate. 23 | /// The negated vector. 24 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 25 | public static Vector2 Negate(in Vector2 value) 26 | where T : struct, IFloatingPointIeee754 27 | { 28 | return -value; 29 | } 30 | 31 | /// Adds two vectors together. 32 | /// The first vector to add. 33 | /// The second vector to add. 34 | /// The summed vector. 35 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 36 | public static Vector2 Add(in Vector2 left, in Vector2 right) 37 | where T : struct, IFloatingPointIeee754 38 | { 39 | return left + right; 40 | } 41 | 42 | /// Subtracts the second vector from the first. 43 | /// The first vector. 44 | /// The second vector. 45 | /// The difference vector. 46 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 47 | public static Vector2 Subtract(in Vector2 left, in Vector2 right) 48 | where T : struct, IFloatingPointIeee754 49 | { 50 | return left - right; 51 | } 52 | 53 | /// Returns a new vector whose values are the product of each pair of elements in two specified vectors. 54 | /// The first vector. 55 | /// The second vector. 56 | /// The element-wise product vector. 57 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 58 | public static Vector2 Multiply(in Vector2 left, in Vector2 right) 59 | where T : struct, IFloatingPointIeee754 60 | { 61 | return left * right; 62 | } 63 | 64 | /// Divides the first vector by the second. 65 | /// The first vector. 66 | /// The second vector. 67 | /// The vector resulting from the division. 68 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 69 | public static Vector2 Divide(in Vector2 left, in Vector2 right) 70 | where T : struct, IFloatingPointIeee754 71 | { 72 | return left / right; 73 | } 74 | 75 | /// Multiplies a vector by a specified scalar. 76 | /// The vector to multiply. 77 | /// The scalar value. 78 | /// The scaled vector. 79 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 80 | public static Vector2 Multiply(in Vector2 left, T right) 81 | where T : struct, IFloatingPointIeee754 82 | { 83 | return left * right; 84 | } 85 | 86 | /// Divides the specified vector by a specified scalar value. 87 | /// The vector. 88 | /// The scalar value. 89 | /// The vector that results from the division. 90 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 91 | public static Vector2 Divide(in Vector2 left, T right) 92 | where T : struct, IFloatingPointIeee754 93 | { 94 | return left / right; 95 | } 96 | 97 | /// Multiplies a scalar value by a specified vector. 98 | /// The scaled value. 99 | /// The vector. 100 | /// The scaled vector. 101 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 102 | public static Vector2 Multiply(T left, in Vector2 right) 103 | where T : struct, IFloatingPointIeee754 104 | { 105 | return left * right; 106 | } 107 | 108 | // Static Methods 109 | 110 | /// Returns a vector whose elements are the absolute values of each of the specified vector's elements. 111 | /// A vector. 112 | /// The absolute value vector. 113 | [Intrinsic] 114 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 115 | public static Vector2 Abs(in Vector2 value) 116 | where T : struct, IFloatingPointIeee754 117 | { 118 | return new(T.Abs(value.X), T.Abs(value.Y)); 119 | } 120 | 121 | /// Restricts a vector between a minimum and a maximum value. 122 | /// The vector to restrict. 123 | /// The minimum value. 124 | /// The maximum value. 125 | /// The restricted vector. 126 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 127 | public static Vector2 Clamp(in Vector2 value, in Vector2 min, in Vector2 max) 128 | where T : struct, IFloatingPointIeee754 129 | { 130 | // We must follow HLSL behavior in the case user specified min value is bigger than max value. 131 | return Min(Max(value, min), max); 132 | } 133 | 134 | /// Computes the Euclidean distance between the two given points. 135 | /// The first point. 136 | /// The second point. 137 | /// The distance. 138 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 139 | public static T Distance(in Vector2 left, in Vector2 right) 140 | where T : struct, IFloatingPointIeee754 141 | { 142 | T distanceSquared = DistanceSquared(left, right); 143 | return T.Sqrt(distanceSquared); 144 | } 145 | 146 | /// Returns the Euclidean distance squared between two specified points. 147 | /// The first point. 148 | /// The second point. 149 | /// The distance squared. 150 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 151 | public static T DistanceSquared(in Vector2 left, in Vector2 right) 152 | where T : struct, IFloatingPointIeee754 153 | { 154 | Vector2 difference = left - right; 155 | return Dot(difference, difference); 156 | } 157 | 158 | /// Returns the dot product of two vectors. 159 | /// The first vector. 160 | /// The second vector. 161 | /// The dot product. 162 | [Intrinsic] 163 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 164 | public static T Dot(in Vector2 left, in Vector2 right) 165 | where T : struct, IFloatingPointIeee754 166 | { 167 | return left.X * right.X + left.Y * right.Y; 168 | } 169 | 170 | /// Performs a linear interpolation between two vectors based on the given weighting. 171 | /// The first vector. 172 | /// The second vector. 173 | /// A value between 0 and 1 that indicates the weight of . 174 | /// The interpolated vector. 175 | /// 178 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 179 | public static Vector2 Lerp(in Vector2 min, in Vector2 max, T amount) 180 | where T : struct, IFloatingPointIeee754 181 | { 182 | return (min * (T.One - amount)) + (max * amount); 183 | } 184 | 185 | /// Returns a vector whose elements are the minimum of each of the pairs of elements in two specified vectors. 186 | /// The first vector. 187 | /// The second vector. 188 | /// The minimized vector. 189 | [Intrinsic] 190 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 191 | public static Vector2 Min(in Vector2 left, in Vector2 right) 192 | where T : struct, IFloatingPointIeee754 193 | { 194 | return new( 195 | (left.X < right.X) ? left.X : right.X, 196 | (left.Y < right.Y) ? left.Y : right.Y); 197 | } 198 | 199 | /// Returns a vector whose elements are the maximum of each of the pairs of elements in two specified vectors. 200 | /// The first vector. 201 | /// The second vector. 202 | /// The maximized vector. 203 | [Intrinsic] 204 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 205 | public static Vector2 Max(in Vector2 left, in Vector2 right) 206 | where T : struct, IFloatingPointIeee754 207 | { 208 | return new( 209 | (left.X > right.X) ? left.X : right.X, 210 | (left.Y > right.Y) ? left.Y : right.Y); 211 | } 212 | 213 | /// Returns a vector with the same direction as the specified vector, but with a length of one. 214 | /// The vector to normalize. 215 | /// The normalized vector. 216 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 217 | public static Vector2 Normalize(in Vector2 value) 218 | where T : struct, IFloatingPointIeee754 219 | { 220 | T length = Distance(Vector2.Zero, value); 221 | return value / length; 222 | } 223 | 224 | /// Returns the reflection of a vector off a surface that has the specified normal. 225 | /// The source vector. 226 | /// The normal of the surface being reflected off. 227 | /// The reflected vector. 228 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 229 | public static Vector2 Reflect(in Vector2 incident, in Vector2 normal) 230 | where T : struct, IFloatingPointIeee754 231 | { 232 | T dot = Dot(incident, normal); 233 | Vector2 dotNormal = dot * normal; 234 | return incident - dotNormal - dotNormal; 235 | } 236 | 237 | /// Returns a vector whose elements are the square root of each of a specified vector's elements. 238 | /// A vector. 239 | /// The square root vector. 240 | [Intrinsic] 241 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 242 | public static Vector2 SquareRoot(in Vector2 value) 243 | where T : struct, IFloatingPointIeee754 244 | { 245 | return new(T.Sqrt(value.X), T.Sqrt(value.Y)); 246 | } 247 | 248 | /// Transforms a vector by a specified 3x2 matrix. 249 | /// The vector to transform. 250 | /// The transformation matrix. 251 | /// The transformed vector. 252 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 253 | public static Vector2 Transform(in Vector2 position, in Matrix3x2 matrix) 254 | where T : struct, IFloatingPointIeee754 255 | { 256 | return new Vector2( 257 | (position.X * matrix.M11) + (position.Y * matrix.M21) + matrix.M31, 258 | (position.X * matrix.M12) + (position.Y * matrix.M22) + matrix.M32 259 | ); 260 | } 261 | 262 | /// Transforms a vector by a specified 4x4 matrix. 263 | /// The vector to transform. 264 | /// The transformation matrix. 265 | /// The transformed vector. 266 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 267 | public static Vector2 Transform(in Vector2 position, in Matrix4x4 matrix) 268 | where T : struct, IFloatingPointIeee754 269 | { 270 | return new Vector2( 271 | (position.X * matrix.M11) + (position.Y * matrix.M21) + matrix.M41, 272 | (position.X * matrix.M12) + (position.Y * matrix.M22) + matrix.M42 273 | ); 274 | } 275 | 276 | /// Transforms a vector by the specified Quaternion rotation value. 277 | /// The vector to rotate. 278 | /// The rotation to apply. 279 | /// The transformed vector. 280 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 281 | public static Vector2 Transform(in Vector2 value, in Quaternion rotation) 282 | where T : struct, IFloatingPointIeee754 283 | { 284 | T x2 = rotation.X + rotation.X; 285 | T y2 = rotation.Y + rotation.Y; 286 | T z2 = rotation.Z + rotation.Z; 287 | 288 | T wz2 = rotation.W * z2; 289 | T xx2 = rotation.X * x2; 290 | T xy2 = rotation.X * y2; 291 | T yy2 = rotation.Y * y2; 292 | T zz2 = rotation.Z * z2; 293 | 294 | return new Vector2( 295 | value.X * (T.One - yy2 - zz2) + value.Y * (xy2 - wz2), 296 | value.X * (xy2 + wz2) + value.Y * (T.One - xx2 - zz2) 297 | ); 298 | } 299 | 300 | /// Transforms a vector normal by the given 3x2 matrix. 301 | /// The source vector. 302 | /// The matrix. 303 | /// The transformed vector. 304 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 305 | public static Vector2 TransformNormal(in Vector2 normal, in Matrix3x2 matrix) 306 | where T : struct, IFloatingPointIeee754 307 | { 308 | return new Vector2( 309 | (normal.X * matrix.M11) + (normal.Y * matrix.M21), 310 | (normal.X * matrix.M12) + (normal.Y * matrix.M22) 311 | ); 312 | } 313 | 314 | /// Transforms a vector normal by the given 4x4 matrix. 315 | /// The source vector. 316 | /// The matrix. 317 | /// The transformed vector. 318 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 319 | public static Vector2 TransformNormal(in Vector2 normal, in Matrix4x4 matrix) 320 | where T : struct, IFloatingPointIeee754 321 | { 322 | return new Vector2( 323 | (normal.X * matrix.M11) + (normal.Y * matrix.M21), 324 | (normal.X * matrix.M12) + (normal.Y * matrix.M22) 325 | ); 326 | } 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /GenericVectors/src/Vector2_1.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Globalization; 5 | using System.Runtime.CompilerServices; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | 9 | #pragma warning disable CA2201 // Do not raise reserved exception types 10 | namespace System.Numerics 11 | { 12 | /// Represents a vector with two single-precision floating-point values. 13 | /// structure provides support for hardware acceleration. 15 | /// [!INCLUDE[vectors-are-rows-paragraph](~/includes/system-numerics-vectors-are-rows.md)] 16 | /// ]]> 17 | [Intrinsic] 18 | public readonly record struct Vector2 : 19 | IAdditionOperators, Vector2, Vector2>, 20 | IAdditiveIdentity, Vector2>, 21 | IDivisionOperators, Vector2, Vector2>, 22 | IDivisionOperators, T, Vector2>, 23 | IEquatable>, 24 | IEqualityOperators, Vector2, bool>, 25 | IFormattable, 26 | IMultiplicativeIdentity, Vector2>, 27 | IMultiplyOperators, Vector2, Vector2>, 28 | IMultiplyOperators, T, Vector2>, 29 | ISubtractionOperators, Vector2, Vector2>, 30 | IUnaryNegationOperators, Vector2>, 31 | IUnaryPlusOperators, Vector2> 32 | where T : struct, IFloatingPointIeee754 33 | { 34 | // Fields 35 | 36 | /// The X component of the vector. 37 | public T X { get; init; } 38 | 39 | /// The Y component of the vector. 40 | public T Y { get; init; } 41 | 42 | // Constructors 43 | 44 | /// Creates a new object whose two elements have the same value. 45 | /// The value to assign to both elements. 46 | [Intrinsic] 47 | public Vector2(T value) 48 | { 49 | X = value; 50 | Y = value; 51 | } 52 | 53 | /// Creates a vector whose elements have the specified values. 54 | /// The value to assign to the field. 55 | /// The value to assign to the field. 56 | [Intrinsic] 57 | public Vector2(T x, T y) 58 | { 59 | X = x; 60 | Y = y; 61 | } 62 | 63 | public Vector2(T[] value) 64 | { 65 | if (value is null) 66 | { 67 | throw new ArgumentNullException(nameof(value)); 68 | } 69 | 70 | X = value[0]; 71 | Y = value[1]; 72 | } 73 | 74 | public Vector2(T[] value, int offset) 75 | { 76 | if (value is null) 77 | { 78 | throw new ArgumentNullException(nameof(value)); 79 | } 80 | 81 | X = value[0 + offset]; 82 | Y = value[1 + offset]; 83 | } 84 | 85 | /// Constructs a vector from the given . The span must contain at least 2 elements. 86 | /// The span of elements to assign to the vector. 87 | public Vector2(ReadOnlySpan values) 88 | { 89 | if (values.Length < 2) 90 | { 91 | VectorS.ThrowInsufficientNumberOfElementsException(2); 92 | } 93 | 94 | this = Unsafe.ReadUnaligned>(ref Unsafe.As(ref MemoryMarshal.GetReference(values))); 95 | } 96 | 97 | // Static Properties 98 | 99 | /// Gets a vector whose 2 elements are equal to one. 100 | /// A vector whose two elements are equal to one (that is, it returns the vector (1,1). 101 | public static Vector2 One 102 | { 103 | [Intrinsic] 104 | get => new Vector2(T.One); 105 | } 106 | 107 | /// Gets the vector (1,0). 108 | /// The vector (1,0). 109 | public static Vector2 UnitX { get; } = new(T.One, T.Zero); 110 | 111 | /// Gets the vector (0,1). 112 | /// The vector (0,1). 113 | public static Vector2 UnitY { get; } = new(T.Zero, T.One); 114 | 115 | /// Returns a vector whose 2 elements are equal to zero. 116 | /// A vector whose two elements are equal to zero (that is, it returns the vector (0,0). 117 | public static Vector2 Zero 118 | { 119 | [Intrinsic] 120 | get => default; 121 | } 122 | 123 | static Vector2 IAdditiveIdentity, Vector2>.AdditiveIdentity => Zero; 124 | 125 | static Vector2 IMultiplicativeIdentity, Vector2>.MultiplicativeIdentity => One; 126 | 127 | // Operators 128 | 129 | /// Computes the unary plus of a value. 130 | /// The value for which to compute its unary plus. 131 | /// The unary plus of . 132 | public static Vector2 operator +(Vector2 value) => value; 133 | 134 | /// Negates the specified vector. 135 | /// The vector to negate. 136 | /// The negated vector. 137 | /// The method defines the unary negation operation for objects. 138 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 139 | public static Vector2 operator -(Vector2 value) => Zero - value; 140 | 141 | /// Adds two vectors together. 142 | /// The first vector to add. 143 | /// The second vector to add. 144 | /// The summed vector. 145 | /// The method defines the addition operation for objects. 146 | [Intrinsic] 147 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 148 | public static Vector2 operator +(Vector2 left, Vector2 right) => new(left.X + right.X, left.Y + right.Y); 149 | 150 | /// Subtracts the second vector from the first. 151 | /// The first vector. 152 | /// The second vector. 153 | /// The vector that results from subtracting from . 154 | /// The method defines the subtraction operation for objects. 155 | [Intrinsic] 156 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 157 | public static Vector2 operator -(Vector2 left, Vector2 right) => new(left.X - right.X, left.Y - right.Y); 158 | 159 | /// Returns a new vector whose values are the product of each pair of elements in two specified vectors. 160 | /// The first vector. 161 | /// The second vector. 162 | /// The element-wise product vector. 163 | /// The method defines the multiplication operation for objects. 164 | [Intrinsic] 165 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 166 | public static Vector2 operator *(Vector2 left, Vector2 right) => new(left.X * right.X, left.Y * right.Y); 167 | 168 | /// Divides the first vector by the second. 169 | /// The first vector. 170 | /// The second vector. 171 | /// The vector that results from dividing by . 172 | /// The method defines the division operation for objects. 173 | [Intrinsic] 174 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 175 | public static Vector2 operator /(Vector2 left, Vector2 right) => new(left.X / right.X, left.Y / right.Y); 176 | 177 | /// Multiplies the specified vector by the specified scalar value. 178 | /// The vector. 179 | /// The scalar value. 180 | /// The scaled vector. 181 | /// The method defines the multiplication operation for objects. 182 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 183 | public static Vector2 operator *(Vector2 left, T right) => new(left.X * right, left.Y * right); 184 | 185 | /// Divides the specified vector by a specified scalar value. 186 | /// The vector. 187 | /// The scalar value. 188 | /// The result of the division. 189 | /// The method defines the division operation for objects. 190 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 191 | public static Vector2 operator /(Vector2 left, T right) => new(left.X / right, left.Y / right); 192 | 193 | /// Multiplies the scalar value by the specified vector. 194 | /// The vector. 195 | /// The scalar value. 196 | /// The scaled vector. 197 | /// The method defines the multiplication operation for objects. 198 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 199 | public static Vector2 operator *(T left, Vector2 right) => new(left * right.X, left * right.Y); 200 | 201 | // Methods 202 | 203 | /// Copies the elements of the vector to a specified array. 204 | /// The destination array. 205 | /// must have at least two elements. The method copies the vector's elements starting at index 0. 206 | /// is . 207 | /// The number of elements in the current instance is greater than in the array. 208 | /// is multidimensional. 209 | //[Intrinsic] 210 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 211 | public readonly void CopyTo(T[] array) 212 | { 213 | CopyTo(array, 0); 214 | } 215 | 216 | /// Copies the elements of the vector to a specified array starting at a specified index position. 217 | /// The destination array. 218 | /// The index at which to copy the first element of the vector. 219 | /// must have a sufficient number of elements to accommodate the two vector elements. In other words, elements and + 1 must already exist in . 220 | /// is . 221 | /// The number of elements in the current instance is greater than in the array. 222 | /// is less than zero. 223 | /// -or- 224 | /// is greater than or equal to the array length. 225 | /// is multidimensional. 226 | public readonly void CopyTo(T[] array, int index) 227 | { 228 | if (array is null) 229 | { 230 | // Match the JIT's exception type here. For perf, a NullReference is thrown instead of an ArgumentNull. 231 | throw new NullReferenceException(SR.Arg_NullArgumentNullRef); 232 | } 233 | 234 | if ((index < 0) || (index >= array.Length)) 235 | { 236 | throw new ArgumentOutOfRangeException(nameof(index), SR.Format(SR.Arg_ArgumentOutOfRangeException, index)); 237 | } 238 | 239 | if ((array.Length - index) < 2) 240 | { 241 | throw new ArgumentException(SR.Format(SR.Arg_ElementsInSourceIsGreaterThanDestination, index)); 242 | } 243 | 244 | array[index] = X; 245 | array[index + 1] = Y; 246 | } 247 | 248 | /// Attempts to copy the vector to the given . The length of the destination span must be at least 2. 249 | /// The destination span which the values are copied into. 250 | /// if the source vector was successfully copied to . if is not large enough to hold the source vector. 251 | public readonly bool TryCopyTo(Span destination) 252 | { 253 | if (destination.Length < 2) 254 | { 255 | return false; 256 | } 257 | 258 | Unsafe.WriteUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(destination)), this); 259 | 260 | return true; 261 | } 262 | 263 | /// Copies the vector to the given .The length of the destination span must be at least 2. 264 | /// The destination span which the values are copied into. 265 | /// If number of elements in source vector is greater than those available in destination span. 266 | public readonly void CopyTo(Span destination) 267 | { 268 | if (destination.Length < 2) 269 | { 270 | ThrowHelper.ThrowArgumentException_DestinationTooShort(); 271 | } 272 | 273 | Unsafe.WriteUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(destination)), this); 274 | } 275 | 276 | /// Returns a value that indicates whether this instance and another vector are equal. 277 | /// The other vector. 278 | /// if the two vectors are equal; otherwise, . 279 | /// Two vectors are equal if their and elements are equal. 280 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 281 | public bool Equals(Vector2 other) 282 | { 283 | return X == other.X && Y == other.Y; 284 | } 285 | 286 | /// Returns the length of the vector. 287 | /// The vector's length. 288 | /// 289 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 290 | public readonly T Length() 291 | { 292 | T lengthSquared = LengthSquared(); 293 | return T.Sqrt(lengthSquared); 294 | } 295 | 296 | /// Returns the length of the vector squared. 297 | /// The vector's length squared. 298 | /// This operation offers better performance than a call to the method. 299 | /// 300 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 301 | public readonly T LengthSquared() 302 | { 303 | return X * X + Y * Y; 304 | } 305 | 306 | /// Returns the string representation of the current instance using default formatting. 307 | /// The string representation of the current instance. 308 | /// This method returns a string in which each element of the vector is formatted using the "G" (general) format string and the formatting conventions of the current thread culture. The "<" and ">" characters are used to begin and end the string, and the current culture's property followed by a space is used to separate each element. 309 | public override readonly string ToString() 310 | { 311 | return ToString("G", CultureInfo.CurrentCulture); 312 | } 313 | 314 | /// Returns the string representation of the current instance using the specified format string to format individual elements. 315 | /// A standard or custom numeric format string that defines the format of individual elements. 316 | /// The string representation of the current instance. 317 | /// This method returns a string in which each element of the vector is formatted using and the current culture's formatting conventions. The "<" and ">" characters are used to begin and end the string, and the current culture's property followed by a space is used to separate each element. 318 | /// Standard Numeric Format Strings 319 | /// Custom Numeric Format Strings 320 | public readonly string ToString(string? format) 321 | { 322 | return ToString(format, CultureInfo.CurrentCulture); 323 | } 324 | 325 | /// Returns the string representation of the current instance using the specified format string to format individual elements and the specified format provider to define culture-specific formatting. 326 | /// A standard or custom numeric format string that defines the format of individual elements. 327 | /// A format provider that supplies culture-specific formatting information. 328 | /// The string representation of the current instance. 329 | /// This method returns a string in which each element of the vector is formatted using and . The "<" and ">" characters are used to begin and end the string, and the format provider's property followed by a space is used to separate each element. 330 | /// Custom Numeric Format Strings 331 | /// Standard Numeric Format Strings 332 | public readonly string ToString(string? format, IFormatProvider? formatProvider) 333 | { 334 | StringBuilder sb = new StringBuilder(); 335 | string separator = NumberFormatInfo.GetInstance(formatProvider).NumberGroupSeparator; 336 | sb.Append('<'); 337 | sb.Append(X.ToString(format, formatProvider)); 338 | sb.Append(separator); 339 | sb.Append(' '); 340 | sb.Append(Y.ToString(format, formatProvider)); 341 | sb.Append('>'); 342 | return sb.ToString(); 343 | } 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /GenericVectors/src/Vector3S.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace System.Numerics 7 | { 8 | public partial struct Vector3S 9 | { 10 | // "Friendly" Operators 11 | 12 | /// Computes the unary plus of a value. 13 | /// The value for which to compute its unary plus. 14 | /// The unary plus of . 15 | public static Vector3 Plus(in Vector3 value) 16 | where T : struct, IFloatingPointIeee754 17 | { 18 | return value; 19 | } 20 | 21 | /// Negates a specified vector. 22 | /// The vector to negate. 23 | /// The negated vector. 24 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 25 | public static Vector3 Negate(in Vector3 value) 26 | where T : struct, IFloatingPointIeee754 27 | { 28 | return -value; 29 | } 30 | 31 | /// Adds two vectors together. 32 | /// The first vector to add. 33 | /// The second vector to add. 34 | /// The summed vector. 35 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 36 | public static Vector3 Add(in Vector3 left, in Vector3 right) 37 | where T : struct, IFloatingPointIeee754 38 | { 39 | return left + right; 40 | } 41 | 42 | /// Subtracts the second vector from the first. 43 | /// The first vector. 44 | /// The second vector. 45 | /// The difference vector. 46 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 47 | public static Vector3 Subtract(in Vector3 left, in Vector3 right) 48 | where T : struct, IFloatingPointIeee754 49 | { 50 | return left - right; 51 | } 52 | 53 | /// Returns a new vector whose values are the product of each pair of elements in two specified vectors. 54 | /// The first vector. 55 | /// The second vector. 56 | /// The element-wise product vector. 57 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 58 | public static Vector3 Multiply(in Vector3 left, in Vector3 right) 59 | where T : struct, IFloatingPointIeee754 60 | { 61 | return left * right; 62 | } 63 | 64 | /// Divides the first vector by the second. 65 | /// The first vector. 66 | /// The second vector. 67 | /// The vector resulting from the division. 68 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 69 | public static Vector3 Divide(in Vector3 left, in Vector3 right) 70 | where T : struct, IFloatingPointIeee754 71 | { 72 | return left / right; 73 | } 74 | 75 | /// Multiplies a vector by a specified scalar. 76 | /// The vector to multiply. 77 | /// The scalar value. 78 | /// The scaled vector. 79 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 80 | public static Vector3 Multiply(in Vector3 left, T right) 81 | where T : struct, IFloatingPointIeee754 82 | { 83 | return left * right; 84 | } 85 | 86 | /// Divides the specified vector by a specified scalar value. 87 | /// The vector. 88 | /// The scalar value. 89 | /// The vector that results from the division. 90 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 91 | public static Vector3 Divide(in Vector3 left, T right) 92 | where T : struct, IFloatingPointIeee754 93 | { 94 | return left / right; 95 | } 96 | 97 | /// Multiplies a scalar value by a specified vector. 98 | /// The scaled value. 99 | /// The vector. 100 | /// The scaled vector. 101 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 102 | public static Vector3 Multiply(T left, in Vector3 right) 103 | where T : struct, IFloatingPointIeee754 104 | { 105 | return left * right; 106 | } 107 | 108 | // Static Methods 109 | 110 | /// Returns a vector whose elements are the absolute values of each of the specified vector's elements. 111 | /// A vector. 112 | /// The absolute value vector. 113 | [Intrinsic] 114 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 115 | public static Vector3 Abs(in Vector3 value) 116 | where T : struct, IFloatingPointIeee754 117 | { 118 | return new( 119 | T.Abs(value.X), 120 | T.Abs(value.Y), 121 | T.Abs(value.Z) 122 | ); 123 | } 124 | 125 | /// Restricts a vector between a minimum and a maximum value. 126 | /// The vector to restrict. 127 | /// The minimum value. 128 | /// The maximum value. 129 | /// The restricted vector. 130 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 131 | public static Vector3 Clamp(in Vector3 value, in Vector3 min, in Vector3 max) 132 | where T : struct, IFloatingPointIeee754 133 | { 134 | // We must follow HLSL behavior in the case user specified min value is bigger than max value. 135 | return Min(Max(value, min), max); 136 | } 137 | 138 | /// Computes the Euclidean distance between the two given points. 139 | /// The first point. 140 | /// The second point. 141 | /// The distance. 142 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 143 | public static T Distance(in Vector3 left, in Vector3 right) 144 | where T : struct, IFloatingPointIeee754 145 | { 146 | T distanceSquared = DistanceSquared(left, right); 147 | return T.Sqrt(distanceSquared); 148 | } 149 | 150 | /// Returns the Euclidean distance squared between two specified points. 151 | /// The first point. 152 | /// The second point. 153 | /// The distance squared. 154 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 155 | public static T DistanceSquared(in Vector3 left, in Vector3 right) 156 | where T : struct, IFloatingPointIeee754 157 | { 158 | Vector3 difference = left - right; 159 | return Dot(difference, difference); 160 | } 161 | 162 | /// Computes the cross product of two vectors. 163 | /// The first vector. 164 | /// The second vector. 165 | /// The cross product. 166 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 167 | public static Vector3 Cross(in Vector3 left, in Vector3 right) 168 | where T : struct, IFloatingPointIeee754 169 | { 170 | var result = new Vector3( 171 | (left.Y * right.Z) - (left.Z * right.Y), 172 | (left.Z * right.X) - (left.X * right.Z), 173 | (left.X * right.Y) - (left.Y * right.X) 174 | ); 175 | return result; 176 | } 177 | 178 | /// Returns the dot product of two vectors. 179 | /// The first vector. 180 | /// The second vector. 181 | /// The dot product. 182 | [Intrinsic] 183 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 184 | public static T Dot(in Vector3 left, in Vector3 right) 185 | where T : struct, IFloatingPointIeee754 186 | { 187 | return (left.X * right.X) 188 | + (left.Y * right.Y) 189 | + (left.Z * right.Z); 190 | } 191 | 192 | /// Performs a linear interpolation between two vectors based on the given weighting. 193 | /// The first vector. 194 | /// The second vector. 195 | /// A value between 0 and 1 that indicates the weight of . 196 | /// The interpolated vector. 197 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 198 | public static Vector3 Lerp(in Vector3 min, in Vector3 max, T amount) 199 | where T : struct, IFloatingPointIeee754 200 | { 201 | return (min * (T.One - amount)) + (max * amount); 202 | } 203 | 204 | /// Returns a vector whose elements are the minimum of each of the pairs of elements in two specified vectors. 205 | /// The first vector. 206 | /// The second vector. 207 | /// The minimized vector. 208 | [Intrinsic] 209 | public static Vector3 Min(in Vector3 left, in Vector3 right) 210 | where T : struct, IFloatingPointIeee754 211 | { 212 | return new( 213 | (left.X < right.X) ? left.X : right.X, 214 | (left.Y < right.Y) ? left.Y : right.Y, 215 | (left.Z < right.Z) ? left.Z : right.Z); 216 | } 217 | 218 | /// Returns a vector whose elements are the maximum of each of the pairs of elements in two specified vectors. 219 | /// The first vector. 220 | /// The second vector. 221 | /// The maximized vector. 222 | [Intrinsic] 223 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 224 | public static Vector3 Max(in Vector3 left, in Vector3 right) 225 | where T : struct, IFloatingPointIeee754 226 | { 227 | return new( 228 | (left.X > right.X) ? left.X : right.X, 229 | (left.Y > right.Y) ? left.Y : right.Y, 230 | (left.Z > right.Z) ? left.Z : right.Z 231 | ); 232 | } 233 | 234 | /// Returns a vector with the same direction as the specified vector, but with a length of one. 235 | /// The vector to normalize. 236 | /// The normalized vector. 237 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 238 | public static Vector3 Normalize(in Vector3 value) 239 | where T : struct, IFloatingPointIeee754 240 | { 241 | return value / value.Length(); 242 | } 243 | 244 | /// Returns the reflection of a vector off a surface that has the specified normal. 245 | /// The source vector. 246 | /// The normal of the surface being reflected off. 247 | /// The reflected vector. 248 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 249 | public static Vector3 Reflect(in Vector3 incident, in Vector3 normal) 250 | where T : struct, IFloatingPointIeee754 251 | { 252 | T dot = Dot(incident, normal); 253 | return incident - (T.CreateChecked(2.0) * dot * normal); 254 | } 255 | 256 | /// Returns a vector whose elements are the square root of each of a specified vector's elements. 257 | /// A vector. 258 | /// The square root vector. 259 | [Intrinsic] 260 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 261 | public static Vector3 SquareRoot(in Vector3 value) 262 | where T : struct, IFloatingPointIeee754 263 | { 264 | return new( 265 | T.Sqrt(value.X), 266 | T.Sqrt(value.Y), 267 | T.Sqrt(value.Z) 268 | ); 269 | } 270 | 271 | /// Transforms a vector by a specified 4x4 matrix. 272 | /// The vector to transform. 273 | /// The transformation matrix. 274 | /// The transformed vector. 275 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 276 | public static Vector3 Transform(in Vector3 position, in Matrix4x4 matrix) 277 | where T : struct, IFloatingPointIeee754 278 | { 279 | return new( 280 | (position.X * matrix.M11) + (position.Y * matrix.M21) + (position.Z * matrix.M31) + matrix.M41, 281 | (position.X * matrix.M12) + (position.Y * matrix.M22) + (position.Z * matrix.M32) + matrix.M42, 282 | (position.X * matrix.M13) + (position.Y * matrix.M23) + (position.Z * matrix.M33) + matrix.M43 283 | ); 284 | } 285 | 286 | /// Transforms a vector by the specified Quaternion rotation value. 287 | /// The vector to rotate. 288 | /// The rotation to apply. 289 | /// The transformed vector. 290 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 291 | public static Vector3 Transform(in Vector3 position, in Quaternion rotation) 292 | where T : struct, IFloatingPointIeee754 293 | { 294 | T x2 = rotation.X + rotation.X; 295 | T y2 = rotation.Y + rotation.Y; 296 | T z2 = rotation.Z + rotation.Z; 297 | 298 | T wx2 = rotation.W * x2; 299 | T wy2 = rotation.W * y2; 300 | T wz2 = rotation.W * z2; 301 | T xx2 = rotation.X * x2; 302 | T xy2 = rotation.X * y2; 303 | T xz2 = rotation.X * z2; 304 | T yy2 = rotation.Y * y2; 305 | T yz2 = rotation.Y * z2; 306 | T zz2 = rotation.Z * z2; 307 | 308 | return new( 309 | position.X * (T.One - yy2 - zz2) + position.Y * (xy2 - wz2) + position.Z * (xz2 + wy2), 310 | position.X * (xy2 + wz2) + position.Y * (T.One - xx2 - zz2) + position.Z * (yz2 - wx2), 311 | position.X * (xz2 - wy2) + position.Y * (yz2 + wx2) + position.Z * (T.One - xx2 - yy2) 312 | ); 313 | } 314 | 315 | /// Transforms a vector normal by the given 4x4 matrix. 316 | /// The source vector. 317 | /// The matrix. 318 | /// The transformed vector. 319 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 320 | public static Vector3 TransformNormal(in Vector3 normal, in Matrix4x4 matrix) 321 | where T : struct, IFloatingPointIeee754 322 | { 323 | return new( 324 | (normal.X * matrix.M11) + (normal.Y * matrix.M21) + (normal.Z * matrix.M31), 325 | (normal.X * matrix.M12) + (normal.Y * matrix.M22) + (normal.Z * matrix.M32), 326 | (normal.X * matrix.M13) + (normal.Y * matrix.M23) + (normal.Z * matrix.M33) 327 | ); 328 | } 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /GenericVectors/src/Vector4S.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace System.Numerics 7 | { 8 | public partial struct Vector4S 9 | { 10 | // "Friendly" Operators 11 | 12 | /// Computes the unary plus of a value. 13 | /// The value for which to compute its unary plus. 14 | /// The unary plus of . 15 | public static Vector4 Plus(in Vector4 value) 16 | where T : struct, IFloatingPointIeee754 17 | { 18 | return value; 19 | } 20 | 21 | /// Negates a specified vector. 22 | /// The vector to negate. 23 | /// The negated vector. 24 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 25 | public static Vector4 Negate(in Vector4 value) 26 | where T : struct, IFloatingPointIeee754 27 | { 28 | return -value; 29 | } 30 | 31 | /// Adds two vectors together. 32 | /// The first vector to add. 33 | /// The second vector to add. 34 | /// The summed vector. 35 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 36 | public static Vector4 Add(in Vector4 left, in Vector4 right) 37 | where T : struct, IFloatingPointIeee754 38 | { 39 | return left + right; 40 | } 41 | 42 | /// Subtracts the second vector from the first. 43 | /// The first vector. 44 | /// The second vector. 45 | /// The difference vector. 46 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 47 | public static Vector4 Subtract(in Vector4 left, in Vector4 right) 48 | where T : struct, IFloatingPointIeee754 49 | { 50 | return left - right; 51 | } 52 | 53 | /// Returns a new vector whose values are the product of each pair of elements in two specified vectors. 54 | /// The first vector. 55 | /// The second vector. 56 | /// The element-wise product vector. 57 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 58 | public static Vector4 Multiply(in Vector4 left, in Vector4 right) 59 | where T : struct, IFloatingPointIeee754 60 | { 61 | return left * right; 62 | } 63 | 64 | /// Divides the first vector by the second. 65 | /// The first vector. 66 | /// The second vector. 67 | /// The vector resulting from the division. 68 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 69 | public static Vector4 Divide(in Vector4 left, in Vector4 right) 70 | where T : struct, IFloatingPointIeee754 71 | { 72 | return left / right; 73 | } 74 | 75 | /// Multiplies a vector by a specified scalar. 76 | /// The vector to multiply. 77 | /// The scalar value. 78 | /// The scaled vector. 79 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 80 | public static Vector4 Multiply(in Vector4 left, T right) 81 | where T : struct, IFloatingPointIeee754 82 | { 83 | return left * right; 84 | } 85 | 86 | /// Divides the specified vector by a specified scalar value. 87 | /// The vector. 88 | /// The scalar value. 89 | /// The vector that results from the division. 90 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 91 | public static Vector4 Divide(in Vector4 left, T right) 92 | where T : struct, IFloatingPointIeee754 93 | { 94 | return left / right; 95 | } 96 | 97 | /// Multiplies a scalar value by a specified vector. 98 | /// The scaled value. 99 | /// The vector. 100 | /// The scaled vector. 101 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 102 | public static Vector4 Multiply(T left, in Vector4 right) 103 | where T : struct, IFloatingPointIeee754 104 | { 105 | return left * right; 106 | } 107 | 108 | // Static Methods 109 | 110 | /// Returns a vector whose elements are the absolute values of each of the specified vector's elements. 111 | /// A vector. 112 | /// The absolute value vector. 113 | [Intrinsic] 114 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 115 | public static Vector4 Abs(in Vector4 value) 116 | where T : struct, IFloatingPointIeee754 117 | { 118 | return new( 119 | T.Abs(value.X), 120 | T.Abs(value.Y), 121 | T.Abs(value.Z), 122 | T.Abs(value.W) 123 | ); 124 | } 125 | 126 | /// Restricts a vector between a minimum and a maximum value. 127 | /// The vector to restrict. 128 | /// The minimum value. 129 | /// The maximum value. 130 | /// The restricted vector. 131 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 132 | public static Vector4 Clamp(in Vector4 value, in Vector4 min, in Vector4 max) 133 | where T : struct, IFloatingPointIeee754 134 | { 135 | // We must follow HLSL behavior in the case user specified min value is bigger than max value. 136 | return Min(Max(value, min), max); 137 | } 138 | 139 | /// Computes the Euclidean distance between the two given points. 140 | /// The first point. 141 | /// The second point. 142 | /// The distance. 143 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 144 | public static T Distance(in Vector4 left, in Vector4 right) 145 | where T : struct, IFloatingPointIeee754 146 | { 147 | T distanceSquared = DistanceSquared(left, right); 148 | return T.Sqrt(distanceSquared); 149 | } 150 | 151 | /// Returns the Euclidean distance squared between two specified points. 152 | /// The first point. 153 | /// The second point. 154 | /// The distance squared. 155 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 156 | public static T DistanceSquared(in Vector4 left, in Vector4 right) 157 | where T : struct, IFloatingPointIeee754 158 | { 159 | Vector4 difference = left - right; 160 | return Dot(difference, difference); 161 | } 162 | 163 | /// Returns the dot product of two vectors. 164 | /// The first vector. 165 | /// The second vector. 166 | /// The dot product. 167 | [Intrinsic] 168 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 169 | public static T Dot(in Vector4 left, in Vector4 right) 170 | where T : struct, IFloatingPointIeee754 171 | { 172 | return (left.X * right.X) 173 | + (left.Y * right.Y) 174 | + (left.Z * right.Z) 175 | + (left.W * right.W); 176 | } 177 | 178 | /// Performs a linear interpolation between two vectors based on the given weighting. 179 | /// The first vector. 180 | /// The second vector. 181 | /// A value between 0 and 1 that indicates the weight of . 182 | /// The interpolated vector. 183 | /// 186 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 187 | public static Vector4 Lerp(in Vector4 min, in Vector4 max, T amount) 188 | where T : struct, IFloatingPointIeee754 189 | { 190 | return (min * (T.One - amount)) + (max * amount); 191 | } 192 | 193 | /// Returns a vector whose elements are the minimum of each of the pairs of elements in two specified vectors. 194 | /// The first vector. 195 | /// The second vector. 196 | /// The minimized vector. 197 | [Intrinsic] 198 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 199 | public static Vector4 Min(in Vector4 left, in Vector4 right) 200 | where T : struct, IFloatingPointIeee754 201 | { 202 | return new( 203 | (left.X < right.X) ? left.X : right.X, 204 | (left.Y < right.Y) ? left.Y : right.Y, 205 | (left.Z < right.Z) ? left.Z : right.Z, 206 | (left.W < right.W) ? left.W : right.W); 207 | } 208 | 209 | /// Returns a vector whose elements are the maximum of each of the pairs of elements in two specified vectors. 210 | /// The first vector. 211 | /// The second vector. 212 | /// The maximized vector. 213 | [Intrinsic] 214 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 215 | public static Vector4 Max(in Vector4 left, in Vector4 right) 216 | where T : struct, IFloatingPointIeee754 217 | { 218 | return new( 219 | (left.X > right.X) ? left.X : right.X, 220 | (left.Y > right.Y) ? left.Y : right.Y, 221 | (left.Z > right.Z) ? left.Z : right.Z, 222 | (left.W > right.W) ? left.W : right.W); 223 | } 224 | 225 | /// Returns a vector with the same direction as the specified vector, but with a length of one. 226 | /// The vector to normalize. 227 | /// The normalized vector. 228 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 229 | public static Vector4 Normalize(in Vector4 value) 230 | where T : struct, IFloatingPointIeee754 231 | { 232 | return value / value.Length(); 233 | } 234 | 235 | /// Returns the reflection of a vector off a surface that has the specified normal. 236 | /// The source vector. 237 | /// The normal of the surface being reflected off. 238 | /// The reflected vector. 239 | public static Vector4 Reflect(in Vector4 incident, in Vector4 normal) 240 | where T : struct, IFloatingPointIeee754 241 | { 242 | T dot = Dot(incident, normal); 243 | return incident - (T.CreateChecked(2.0) * dot * normal); 244 | } 245 | 246 | /// Returns a vector whose elements are the square root of each of a specified vector's elements. 247 | /// A vector. 248 | /// The square root vector. 249 | [Intrinsic] 250 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 251 | public static Vector4 SquareRoot(in Vector4 value) 252 | where T : struct, IFloatingPointIeee754 253 | { 254 | return new( 255 | T.Sqrt(value.X), 256 | T.Sqrt(value.Y), 257 | T.Sqrt(value.Z), 258 | T.Sqrt(value.W) 259 | ); 260 | } 261 | 262 | /// Transforms a two-dimensional vector by a specified 4x4 matrix. 263 | /// The vector to transform. 264 | /// The transformation matrix. 265 | /// The transformed vector. 266 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 267 | public static Vector4 Transform(in Vector2 position, in Matrix4x4 matrix) 268 | where T : struct, IFloatingPointIeee754 269 | { 270 | return new( 271 | (position.X * matrix.M11) + (position.Y * matrix.M21) + matrix.M41, 272 | (position.X * matrix.M12) + (position.Y * matrix.M22) + matrix.M42, 273 | (position.X * matrix.M13) + (position.Y * matrix.M23) + matrix.M43, 274 | (position.X * matrix.M14) + (position.Y * matrix.M24) + matrix.M44 275 | ); 276 | } 277 | 278 | /// Transforms a three-dimensional vector by a specified 4x4 matrix. 279 | /// The vector to transform. 280 | /// The transformation matrix. 281 | /// The transformed vector. 282 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 283 | public static Vector4 Transform(in Vector3 position, in Matrix4x4 matrix) 284 | where T : struct, IFloatingPointIeee754 285 | { 286 | return new( 287 | (position.X * matrix.M11) + (position.Y * matrix.M21) + (position.Z * matrix.M31) + matrix.M41, 288 | (position.X * matrix.M12) + (position.Y * matrix.M22) + (position.Z * matrix.M32) + matrix.M42, 289 | (position.X * matrix.M13) + (position.Y * matrix.M23) + (position.Z * matrix.M33) + matrix.M43, 290 | (position.X * matrix.M14) + (position.Y * matrix.M24) + (position.Z * matrix.M34) + matrix.M44 291 | ); 292 | } 293 | 294 | /// Transforms a four-dimensional vector by a specified 4x4 matrix. 295 | /// The vector to transform. 296 | /// The transformation matrix. 297 | /// The transformed vector. 298 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 299 | public static Vector4 Transform(in Vector4 position, in Matrix4x4 matrix) 300 | where T : struct, IFloatingPointIeee754 301 | { 302 | return new( 303 | (position.X * matrix.M11) + (position.Y * matrix.M21) + (position.Z * matrix.M31) + (position.W * matrix.M41), 304 | (position.X * matrix.M12) + (position.Y * matrix.M22) + (position.Z * matrix.M32) + (position.W * matrix.M42), 305 | (position.X * matrix.M13) + (position.Y * matrix.M23) + (position.Z * matrix.M33) + (position.W * matrix.M43), 306 | (position.X * matrix.M14) + (position.Y * matrix.M24) + (position.Z * matrix.M34) + (position.W * matrix.M44) 307 | ); 308 | } 309 | 310 | /// Transforms a two-dimensional vector by the specified Quaternion rotation value. 311 | /// The vector to rotate. 312 | /// The rotation to apply. 313 | /// The transformed vector. 314 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 315 | public static Vector4 Transform(in Vector2 position, in Quaternion rotation) 316 | where T : struct, IFloatingPointIeee754 317 | { 318 | T x2 = rotation.X + rotation.X; 319 | T y2 = rotation.Y + rotation.Y; 320 | T z2 = rotation.Z + rotation.Z; 321 | 322 | T wx2 = rotation.W * x2; 323 | T wy2 = rotation.W * y2; 324 | T wz2 = rotation.W * z2; 325 | T xx2 = rotation.X * x2; 326 | T xy2 = rotation.X * y2; 327 | T xz2 = rotation.X * z2; 328 | T yy2 = rotation.Y * y2; 329 | T yz2 = rotation.Y * z2; 330 | T zz2 = rotation.Z * z2; 331 | 332 | return new( 333 | position.X * (T.One - yy2 - zz2) + position.Y * (xy2 - wz2), 334 | position.X * (xy2 + wz2) + position.Y * (T.One - xx2 - zz2), 335 | position.X * (xz2 - wy2) + position.Y * (yz2 + wx2), 336 | T.One 337 | ); 338 | } 339 | 340 | /// Transforms a three-dimensional vector by the specified Quaternion rotation value. 341 | /// The vector to rotate. 342 | /// The rotation to apply. 343 | /// The transformed vector. 344 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 345 | public static Vector4 Transform(in Vector3 position, in Quaternion rotation) 346 | where T : struct, IFloatingPointIeee754 347 | { 348 | T x2 = rotation.X + rotation.X; 349 | T y2 = rotation.Y + rotation.Y; 350 | T z2 = rotation.Z + rotation.Z; 351 | 352 | T wx2 = rotation.W * x2; 353 | T wy2 = rotation.W * y2; 354 | T wz2 = rotation.W * z2; 355 | T xx2 = rotation.X * x2; 356 | T xy2 = rotation.X * y2; 357 | T xz2 = rotation.X * z2; 358 | T yy2 = rotation.Y * y2; 359 | T yz2 = rotation.Y * z2; 360 | T zz2 = rotation.Z * z2; 361 | 362 | return new( 363 | position.X * (T.One - yy2 - zz2) + position.Y * (xy2 - wz2) + position.Z * (xz2 + wy2), 364 | position.X * (xy2 + wz2) + position.Y * (T.One - xx2 - zz2) + position.Z * (yz2 - wx2), 365 | position.X * (xz2 - wy2) + position.Y * (yz2 + wx2) + position.Z * (T.One - xx2 - yy2), 366 | T.One 367 | ); 368 | } 369 | 370 | /// Transforms a four-dimensional vector by the specified Quaternion rotation value. 371 | /// The vector to rotate. 372 | /// The rotation to apply. 373 | /// The transformed vector. 374 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 375 | public static Vector4 Transform(in Vector4 position, in Quaternion rotation) 376 | where T : struct, IFloatingPointIeee754 377 | { 378 | T x2 = rotation.X + rotation.X; 379 | T y2 = rotation.Y + rotation.Y; 380 | T z2 = rotation.Z + rotation.Z; 381 | 382 | T wx2 = rotation.W * x2; 383 | T wy2 = rotation.W * y2; 384 | T wz2 = rotation.W * z2; 385 | T xx2 = rotation.X * x2; 386 | T xy2 = rotation.X * y2; 387 | T xz2 = rotation.X * z2; 388 | T yy2 = rotation.Y * y2; 389 | T yz2 = rotation.Y * z2; 390 | T zz2 = rotation.Z * z2; 391 | 392 | return new( 393 | position.X * (T.One - yy2 - zz2) + position.Y * (xy2 - wz2) + position.Z * (xz2 + wy2), 394 | position.X * (xy2 + wz2) + position.Y * (T.One - xx2 - zz2) + position.Z * (yz2 - wx2), 395 | position.X * (xz2 - wy2) + position.Y * (yz2 + wx2) + position.Z * (T.One - xx2 - yy2), 396 | position.W); 397 | } 398 | 399 | /// Transforms a vector normal by the given 4x4 matrix. 400 | /// The source vector. 401 | /// The matrix. 402 | /// The transformed vector. 403 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 404 | public static Vector4 TransformNormal(in Vector4 normal, in Matrix4x4 matrix) 405 | where T : struct, IFloatingPointIeee754 406 | { 407 | return new( 408 | (normal.X * matrix.M11) + (normal.Y * matrix.M21) + (normal.Z * matrix.M31), 409 | (normal.X * matrix.M12) + (normal.Y * matrix.M22) + (normal.Z * matrix.M32), 410 | (normal.X * matrix.M13) + (normal.Y * matrix.M23) + (normal.Z * matrix.M33), 411 | normal.W 412 | ); 413 | } 414 | } 415 | } 416 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) .NET Foundation and Contributors 4 | 5 | All rights reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # .NET Generic Vectors and Matrices 2 | 3 | THIS REPO IS EXPERIMENTAL AND SUBJECT TO BREAKING CHANGES! 4 | 5 | [![.NET](https://github.com/sparkie108/generic-vectors-matrices/actions/workflows/tests.yml/badge.svg)](https://github.com/sparkie108/generic-vectors-matrices/actions/workflows/tests.yml) 6 | 7 | This repo contains an implementation of generic versions of the following .NET types using the new generic math feature added as preview in .NET 6: 8 | 9 | | Existing type | Generic implementation (this repo) | 10 | |---------------------------------|---------------------------------| 11 | System.Numerics.Matrix3x2.cs | System.Numerics.Matrix3x2\.cs 12 | System.Numerics.Matrix4x4.cs | System.Numerics.Matrix4x4\.cs 13 | System.Numerics.Plane.cs | System.Numerics.Plane\.cs 14 | System.Numerics.Quaternion.cs | System.Numerics.Quaternion\.cs 15 | System.Numerics.Vector2.cs | System.Numerics.Vector2\.cs 16 | System.Numerics.Vector3.cs | System.Numerics.Vector3\.cs 17 | System.Numerics.Vector4.cs | System.Numerics.Vector4\.cs 18 | 19 | For further details of .NET Generic Math see: 20 | [Preview Features in .NET 6 – Generic Math](https://devblogs.microsoft.com/dotnet/preview-features-in-net-6-generic-math/). 21 | 22 | The generic argument is constrained to types implementing System.IFloatingPoint which, as of .NET 6 Preview 7, are: 23 | * System.Half 24 | * System.Single (float) 25 | * System.Double (double) 26 | 27 | The code is based on the [non-generic implementations](https://github.com/dotnet/runtime/tree/main/src/libraries/System.Private.CoreLib/src/System/Numerics). 28 | 29 | The added generic vector and matrix types are designed to satisfy this existing .NET issue: 30 | [Add support System.Numerics.Vectors types with double precision](https://github.com/dotnet/runtime/issues/24168). 31 | 32 | As the Vector and Matrix classes themselves implement some of the generic math interfaces they can be used with generic math functions: 33 | ```c# 34 | Vector3[] data = { new(1.0, 2.0, 3.0), new(4.0, 5.0, 6.0) }; 35 | var sum = Sum(data); 36 | 37 | public static T Sum(IEnumerable data) 38 | where T : IAdditiveIdentity, IAdditionOperators 39 | { 40 | T sum = T.AdditiveIdentity; 41 | foreach (var item in data) 42 | sum += item; 43 | return sum; 44 | } 45 | ``` 46 | ## Using in other projects 47 | 48 | To use outside this repo copy the files in the src directory to your own project and follow the [directions in the blog post](https://devblogs.microsoft.com/dotnet/preview-features-in-net-6-generic-math/#trying-out-the-features) to enable generic math. 49 | 50 | Note that in order to get the code to compile you must be using at least .NET 6 Preview 7. 51 | 52 | ## Static methods 53 | 54 | The ideal is to add the static methods to the existing non-generic classes, for example: 55 | ```c# 56 | Matrix3x2 transform = Matrix3x2.CreateTranslation(-100.0, -100.0); 57 | ``` 58 | However that is not possible in this repo, so they have been added to a class with an added 'S' on the end, for example: 59 | ```c# 60 | Matrix3x2 transform = Matrix3x2S.CreateTranslation(-100.0, -100.0); 61 | ``` 62 | -------------------------------------------------------------------------------- /generic-vectors-matrices.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31606.5 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenericVectors", "GenericVectors\GenericVectors.csproj", "{26C9D975-CA60-4BB5-8343-9874C8A89CAE}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {26C9D975-CA60-4BB5-8343-9874C8A89CAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {26C9D975-CA60-4BB5-8343-9874C8A89CAE}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {26C9D975-CA60-4BB5-8343-9874C8A89CAE}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {26C9D975-CA60-4BB5-8343-9874C8A89CAE}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {9DA7FBCA-70FC-477A-B559-30E52DCBDE86} 24 | EndGlobalSection 25 | EndGlobal 26 | --------------------------------------------------------------------------------