├── .gitignore
├── Java
├── test.bat
└── FixPointTest.java
├── Examples
├── FixMath
│ ├── App.config
│ ├── FixMath.csproj
│ ├── F64Quat.cs
│ ├── F64Vec2.cs
│ ├── F32Vec2.cs
│ ├── F32.cs
│ ├── F64.cs
│ ├── F32Vec3.cs
│ └── F64Vec3.cs
└── FixedTracer
│ ├── App.config
│ ├── FixedTracer.csproj
│ ├── Program.cs
│ ├── FixedTracer.cs
│ └── DoubleTracer.cs
├── FixPointCSTest
├── App.config
└── FixPointCSTest.csproj
├── Transpiler
├── App.config
├── Transpiler.csproj
├── Program.cs
├── GenerateJava.cs
├── Util.cs
└── GenerateCpp.cs
├── FixPointCS
└── FixPointCS.csproj
├── LICENSE.txt
├── Cpp
├── CppTest.sln
├── CppTest.vcxproj.filters
├── UnitTest.h
├── CppTest.cpp
└── CppTest.vcxproj
├── CHANGELOG.md
├── README.md
├── functions.md
├── Polyfit
├── polyfit.py
└── remez.py
└── FixPointCS.sln
/.gitignore:
--------------------------------------------------------------------------------
1 | .vs
2 | *.user
3 | *.class
4 | Cpp/x64
5 | obj
6 | bin
7 | UpgradeLog.htm
8 |
--------------------------------------------------------------------------------
/Java/test.bat:
--------------------------------------------------------------------------------
1 | @mkdir classes
2 | @javac -d classes *.java || exit /b !ERRORLEVEL!
3 | @java -cp classes fixpointcs.test.FixPointTest
4 |
--------------------------------------------------------------------------------
/Examples/FixMath/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/FixPointCSTest/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Examples/FixedTracer/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Transpiler/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/FixPointCS/FixPointCS.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net6.0
4 | FixPointCS
5 | AnyCPU;x64;x86
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Examples/FixMath/FixMath.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net6.0
4 | FixPointCS
5 | AnyCPU;x64;x86
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Transpiler/Transpiler.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | Exe
6 | AnyCPU;x64;x86
7 | .
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Examples/FixedTracer/FixedTracer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0-windows
5 | Exe
6 | AnyCPU;x64;x86
7 | true
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/FixPointCSTest/FixPointCSTest.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | Exe
6 | AnyCPU;x64;x86
7 | .
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright(c) Jere Sanisalo, Petri Kero
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Cpp/CppTest.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27130.2027
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CppTest", "CppTest.vcxproj", "{C8358827-2A34-4AC0-8E43-F6C3D9C0EB9E}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|x64 = Debug|x64
11 | Release|x64 = Release|x64
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {C8358827-2A34-4AC0-8E43-F6C3D9C0EB9E}.Debug|x64.ActiveCfg = Debug|x64
15 | {C8358827-2A34-4AC0-8E43-F6C3D9C0EB9E}.Debug|x64.Build.0 = Debug|x64
16 | {C8358827-2A34-4AC0-8E43-F6C3D9C0EB9E}.Release|x64.ActiveCfg = Release|x64
17 | {C8358827-2A34-4AC0-8E43-F6C3D9C0EB9E}.Release|x64.Build.0 = Release|x64
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {7D05C426-C690-4393-BC4B-719898A90413}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/Cpp/CppTest.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
10 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
11 |
12 |
13 |
14 |
15 | Source Files
16 |
17 |
18 | Source Files
19 |
20 |
21 |
22 |
23 | Source Files
24 |
25 |
26 | Source Files
27 |
28 |
29 | Source Files
30 |
31 |
32 | Source Files
33 |
34 |
35 |
--------------------------------------------------------------------------------
/Cpp/UnitTest.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #ifndef __UNITTEST_H
3 | #define __UNITTEST_H
4 |
5 | #include
6 | #include
7 |
8 | #include "Fixed32.h"
9 | #include "Fixed64.h"
10 |
11 | class Util
12 | {
13 | public:
14 | static void Check(const char* opName, int64_t output, int64_t expected, int64_t input0)
15 | {
16 | if (output != expected)
17 | printf("MISMATCH in %s: got %" PRId64 ", expected %" PRId64 ", inputs %" PRId64 "\n", opName, output, expected, input0);
18 | }
19 |
20 | static void Check(const char* opName, int64_t output, int64_t expected, int64_t input0, int64_t input1)
21 | {
22 | if (output != expected)
23 | printf("MISMATCH in %s: got %" PRId64 ", expected %" PRId64 ", inputs %" PRId64 " %" PRId64 "\n", opName, output, expected, input0, input1);
24 | }
25 |
26 | // static void Check(const char* opName, int32_t output, int32_t expected, int32_t input0)
27 | // {
28 | // if (output != expected)
29 | // printf("MISMATCH in %s: got %d, expected %d, inputs %d\n", opName, output, expected, input0);
30 | // }
31 | //
32 | // static void Check(const char* opName, int32_t output, int32_t expected, int32_t input0, int32_t input1)
33 | // {
34 | // if (output != expected)
35 | // printf("MISMATCH in %s: got %d, expected %d, inputs %d %d\n", opName, output, expected, input0, input1);
36 | // }
37 | };
38 |
39 | void UnitTest_TestAll();
40 |
41 | #endif
42 |
--------------------------------------------------------------------------------
/Java/FixPointTest.java:
--------------------------------------------------------------------------------
1 | package fixpointcs.test;
2 |
3 | import fixpointcs.Fixed32;
4 | import fixpointcs.Fixed64;
5 |
6 | class Util
7 | {
8 | public static void Check(String opName, long output, long expected, long input0)
9 | {
10 | if (output != expected)
11 | System.out.printf("MISMATCH in %s: got %d, expected %d, inputs %d\n", opName, output, expected, input0);
12 | }
13 |
14 | public static void Check(String opName, long output, long expected, long input0, long input1)
15 | {
16 | if (output != expected)
17 | System.out.printf("MISMATCH in %s: got %d, expected %d, inputs %d %d\n", opName, output, expected, input0, input1);
18 | }
19 |
20 | public static void Check(String opName, int output, int expected, int input0)
21 | {
22 | if (output != expected)
23 | System.out.printf("MISMATCH in %s: got %d, expected %d, inputs %d\n", opName, output, expected, input0);
24 | }
25 |
26 | public static void Check(String opName, int output, int expected, int input0, int input1)
27 | {
28 | if (output != expected)
29 | System.out.printf("MISMATCH in %s: got %d, expected %d, inputs %d %d\n", opName, output, expected, input0, input1);
30 | }
31 | }
32 |
33 | public class FixPointTest
34 | {
35 | public static void main(String[] args)
36 | {
37 | System.out.println("Executing all unit tests..");
38 | UnitTest.TestAll();
39 | System.out.println("Unit tests finished!");
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | # Changelog
3 |
4 | ## 0.3 (2022-03-26)
5 |
6 | ### Changes
7 |
8 | - Upgrade all C# projects to .NET 6.
9 | - FixMath: Replace F32 and F64 constructors with explicit FromInt(), FromFloat(), FromDouble() to avoid accidental implicit casts.
10 | - FixMath: F32 and F64 now implement IComparable to make them usable in unit test assertion macros.
11 | - FixMath: Optimize Lerp() operations with a simpler formula.
12 | - FixMath: Introduce Clamp01() method for F32 and F64 which clamps the input to [0, 1] range.
13 | - FixMath: Introduce some extra inlining to speed up code using the library.
14 | - FixMath: F32 family of Length*() and Normalize*() methods now use 64-bit intermediate results to avoid overflows.
15 | - FixMath: F32Vec*.LengthSqr() now returns F64 to avoid overflows.
16 | - Fixed32.Nlz(): Disabled the System.Numerics.BitOperations.LeadingZeroCount() because it's slower in benchmarks.
17 | - Fixed32,64.ToString() now uses CultureInfo.InvariantCulture.
18 |
19 | ## 0.2 (2021-11-14)
20 |
21 | ### Changes
22 |
23 | - Converted C# projects to .NET 5. The sources are still compatible with .NET Framework and Unity as well.
24 | - Fix C++ transpiled code to use INT64_MIN and INT64_MAX.
25 | - Generate only LF in transpiler.
26 | - Removed extra logic from F32.GetHashCode(), still outputs the same value.
27 | - Use System.Numerics.BitOperations.LeadingZeroCount() with .NET 5 or higher. Boosts performance by about 10% for methods relying on it.
28 |
29 | ### Breaking (these can cause math function outputs to change and thus break determinism)
30 |
31 | - Changed F64.GetHashCode() to use System.Int64.GetHashCode().
32 | - Fixed32 and Fixed64.Mod() checks for div-by-zero. Simplify the code by using the modulo operator (%).
33 | - Fixed all Pow*(X, 0) to always return precisely 1.0, including Pow*(0, 0) which previously returned 0.
34 |
35 | ## 0.1 (2019-06-22)
36 |
37 | - First released version (tagged after-the-fact)
38 |
--------------------------------------------------------------------------------
/Transpiler/Program.cs:
--------------------------------------------------------------------------------
1 | //
2 | // FixPointCS
3 | //
4 | // Copyright(c) Jere Sanisalo, Petri Kero
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | using System.IO;
25 |
26 | namespace Transpiler
27 | {
28 | class Program
29 | {
30 | const string InputPath = "../FixPointCS";
31 | const string CppOutputPath = "../Cpp";
32 | const string JavaOutputPath = "../Java";
33 |
34 | static void Main(string[] args)
35 | {
36 | // Generate C++ files
37 | GenerateCpp.ConvertFile(Path.Combine(InputPath, "FixedUtil.cs"), Path.Combine(CppOutputPath, "FixedUtil.h"), GenerateCpp.Mode.Util);
38 | GenerateCpp.ConvertFile(Path.Combine(InputPath, "Fixed32.cs"), Path.Combine(CppOutputPath, "Fixed32.h"), GenerateCpp.Mode.Fp32);
39 | GenerateCpp.ConvertFile(Path.Combine(InputPath, "Fixed64.cs"), Path.Combine(CppOutputPath, "Fixed64.h"), GenerateCpp.Mode.Fp64);
40 |
41 | // Generate Java files
42 | GenerateJava.ConvertFile(Path.Combine(InputPath, "FixedUtil.cs"), Path.Combine(JavaOutputPath, "FixedUtil.java"));
43 | GenerateJava.ConvertFile(Path.Combine(InputPath, "Fixed32.cs"), Path.Combine(JavaOutputPath, "Fixed32.java"));
44 | GenerateJava.ConvertFile(Path.Combine(InputPath, "Fixed64.cs"), Path.Combine(JavaOutputPath, "Fixed64.java"));
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Cpp/CppTest.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // FixPointCS
3 | //
4 | // Copyright(c) Jere Sanisalo, Petri Kero
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | #include
25 |
26 | #include "FixedUtil.h"
27 | #include "Fixed32.h"
28 | #include "Fixed64.h"
29 |
30 | #include "UnitTest.h"
31 |
32 | void Test32()
33 | {
34 | double v = 0.0001f;
35 |
36 | while (v < 150.f)
37 | {
38 | Fixed32::FP_INT fv = Fixed32::FromDouble(v);
39 | Fixed32::FP_INT fv_div = Fixed32::DivPrecise(fv, Fixed32::FromDouble(-2.34));
40 | Fixed32::FP_INT fv_sqrt = Fixed32::Sqrt(fv);
41 | Fixed32::FP_INT fv_sin = Fixed32::Sin(fv);
42 | Fixed32::FP_INT fv_rcp = Fixed32::RcpFast(fv);
43 |
44 | std::cout << v
45 | << ": div_by_-2.34: " << Fixed32::ToDouble(fv_div)
46 | << ", sqrt: " << Fixed32::ToDouble(fv_sqrt)
47 | << ", sin: " << Fixed32::ToDouble(fv_sin)
48 | << ", rcp: " << Fixed32::ToDouble(fv_rcp)
49 | << std::endl;
50 |
51 | // Next number
52 | v *= 1.5f;
53 | }
54 | }
55 |
56 | void Test64()
57 | {
58 | double v = 0.0001f;
59 |
60 | while (v < 150.f)
61 | {
62 | Fixed64::FP_LONG fv = Fixed64::FromDouble(v);
63 | Fixed64::FP_LONG fv_div = Fixed64::DivPrecise(fv, Fixed64::FromDouble(-2.34));
64 | Fixed64::FP_LONG fv_sqrt = Fixed64::Sqrt(fv);
65 | Fixed64::FP_LONG fv_sin = Fixed64::Sin(fv);
66 | Fixed64::FP_LONG fv_rcp = Fixed64::RcpFast(fv);
67 |
68 | std::cout << v
69 | << ": div_by_-2.34: " << Fixed64::ToDouble(fv_div)
70 | << ", sqrt: " << Fixed64::ToDouble(fv_sqrt)
71 | << ", sin: " << Fixed64::ToDouble(fv_sin)
72 | << ", rcp: " << Fixed64::ToDouble(fv_rcp)
73 | << std::endl;
74 |
75 | // Next number
76 | v *= 1.5f;
77 | }
78 | }
79 |
80 | int main()
81 | {
82 | std::cout << "Testing 16.16 fixed point numbers.." << std::endl;
83 | Test32();
84 |
85 | std::cout << std::endl;
86 | std::cout << "Testing 32.32 fixed point numbers.." << std::endl;
87 | Test64();
88 |
89 | std::cout << std::endl;
90 | std::cout << "Executing all unit tests.." << std::endl;
91 | UnitTest_TestAll();
92 | std::cout << "Unit tests finished!" << std::endl;
93 |
94 | return 0;
95 | }
96 |
--------------------------------------------------------------------------------
/Examples/FixedTracer/Program.cs:
--------------------------------------------------------------------------------
1 | //
2 | // FixPointCS
3 | //
4 | // Copyright(c) Jere Sanisalo, Petri Kero
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 |
25 | using System;
26 | using System.Diagnostics;
27 | using System.Drawing;
28 | using System.Windows.Forms;
29 |
30 | namespace RayTracer
31 | {
32 | public partial class RayTracerForm : Form
33 | {
34 | Bitmap bitmap;
35 | PictureBox pictureBox;
36 | const int width = 600;
37 | const int height = 600;
38 |
39 | public RayTracerForm()
40 | {
41 | bitmap = new Bitmap(width,height);
42 |
43 | pictureBox = new PictureBox();
44 | pictureBox.Dock = DockStyle.Fill;
45 | pictureBox.SizeMode = PictureBoxSizeMode.StretchImage;
46 | pictureBox.Image = bitmap;
47 |
48 | ClientSize = new System.Drawing.Size(width, height + 24);
49 | Controls.Add(pictureBox);
50 | Text = "Ray Tracer";
51 | Load += RayTracerForm_Load;
52 |
53 | Show();
54 | }
55 |
56 | private void RayTracerForm_Load(object sender, EventArgs e)
57 | {
58 | this.Show();
59 |
60 | for (int ndx = 0; ndx < 10; ndx++)
61 | {
62 | Stopwatch sw = Stopwatch.StartNew();
63 | System.Drawing.Color[,] pixels = FixedTracer.RayTracer.RenderDefaultScene(width, height, 16);
64 | //System.Drawing.Color[,] pixels = DoubleTracer.RayTracer.RenderDefaultScene(width, height);
65 | sw.Stop();
66 | Console.WriteLine("Elapsed: {0:0.00}s", sw.Elapsed.TotalSeconds);
67 |
68 | for (int y = 0; y < height; y++)
69 | for (int x = 0; x < width; x++)
70 | bitmap.SetPixel(x, y, pixels[x, y]);
71 |
72 | pictureBox.Refresh();
73 | pictureBox.Invalidate();
74 | }
75 | }
76 |
77 | [STAThread]
78 | static void Main() {
79 | Application.EnableVisualStyles();
80 |
81 | Application.Run(new RayTracerForm());
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Transpiler/GenerateJava.cs:
--------------------------------------------------------------------------------
1 | //
2 | // FixPointCS
3 | //
4 | // Copyright(c) Jere Sanisalo, Petri Kero
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | using System;
25 | using System.Collections.Generic;
26 | using System.IO;
27 | using System.Linq;
28 | using System.Text;
29 | using System.Text.RegularExpressions;
30 | using System.Threading.Tasks;
31 |
32 | namespace Transpiler
33 | {
34 | public static class GenerateJava
35 | {
36 | public static void ConvertFile(string inPath, string outPath)
37 | {
38 | Console.WriteLine("Generating {0}..", outPath);
39 |
40 | // Read the file
41 | string[] lines = File.ReadAllLines(inPath);
42 |
43 | // Preprocess
44 | lines = Util.Preprocess(lines, "JAVA");
45 | lines = Util.Unindent(lines, 1);
46 |
47 | // Prepare output
48 | StringBuilder sb = new StringBuilder();
49 |
50 | // \todo [petri] actual conversion!
51 | foreach (string lineIn in lines)
52 | {
53 | string line = lineIn;
54 | string trimmed = line.Trim();
55 |
56 | // Ignore [...] -lines (attributes)
57 | if (trimmed.StartsWith("["))
58 | continue;
59 |
60 | // Process line
61 | line = Util.ReplaceWholeWord(line, "// PREFIX", g_prefix);
62 | line = Util.ReplaceWholeWord(line, "// SUFFIX", g_suffix);
63 | line = Util.ReplaceWholeWord(line, "static class", "class"); // static classes
64 | line = Util.ReplaceWholeWord(line, "static readonly", "static final"); // const arrays
65 | line = Util.ReplaceWholeWord(line, "public const", "public static final"); // global variables
66 | line = Util.ReplaceWholeWord(line, "private const", "private static final"); // global variables
67 | line = Util.ReplaceWholeWord(line, "const", "final"); // method-local constants
68 | line = Util.ReplaceWholeWord(line, "Debug.Assert", "assert");
69 | line = line.Replace("Nlz((ulong)", "Nlz(");
70 | line = line.Replace("Nlz((uint)", "Nlz(");
71 |
72 | sb.AppendLine(line);
73 | }
74 |
75 | // Save the file
76 | File.WriteAllText(outPath, sb.ToString().Replace("\r", ""), Encoding.ASCII);
77 | }
78 |
79 | // Prefix for the generated file
80 | private static string g_prefix = @"//
81 | // GENERATED FILE!!!
82 | //
83 | // Generated from Fixed64.cs, part of the FixPointCS project (MIT license).
84 | //
85 | ";
86 |
87 | private static string g_suffix = @"// END GENERATED FILE";
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FixPointCS
2 |
3 | #### A fast, multi-language, multi-precision fixed-point library!
4 |
5 | ---
6 |
7 | ## Key Features
8 |
9 | - **Deterministic**: Operations produce bit-identical results across languages and compilers
10 | - **Fast**: All operations use efficient algorithms and are highly optimized
11 | - **Multi-Language**: Supports C#, Java, and C++
12 | - **Multiple Types**: Supports signed 32.32 and 16.16 fixed-point numbers
13 | - **Multiple Precisions**: Operations provide variants with 24, 16, and 10 bits of precision
14 | - **Extensive**: All standard math functions supported, except hyperbolic trigonometry
15 | - **Well Tested**: The library comes with a comprehensive performance and precision testing suite
16 |
17 | ## Overview
18 |
19 | FixPointCS aims to provide as robust and efficient core math operations as possible, while keeping
20 | the API simple to stay compatible with multiple languages. It is intended to be a building block
21 | for higher-level math libraries, not to be used directly from application code.
22 |
23 | For convenience, FixPointCS does include a higher-level math library as well (only for C#). It is
24 | provided as an example on how to utilize the core primitives. It also be used as-is, or customized
25 | to a project's needs. The convenience library is located in *Examples/FixMath*.
26 |
27 | ## Getting Started
28 |
29 | ### Core Math Library (all languages)
30 |
31 | The simplest and recommended way to use FixPointCS is to copy the relevant source files directly into
32 | your project. The core math operations reside in the following files:
33 | - For **C#**: FixPointCS/Fixed32.cs, FixPointCS/Fixed64.cs and FixPointCS/FixedUtil.cs
34 | - For **Java**: Java/Fixed32.java, Java/Fixed64.java and Java/FixedUtil.java
35 | - For **C++**: Cpp/Fixed64.h, Cpp/Fixed32.h and Cpp/FixedUtil.h
36 |
37 | ### FixMath Convenience Library (C# only)
38 |
39 | For C#, you can also use the provided higher-level math library, located under *Examples/FixMath*. In
40 | order to get started writing applications as easily as possible, using the example library is recommended.
41 |
42 | The FixMath library provides basic scalar types (*F32* and *F64*, signed 16.16 and 32.32 fixed-point
43 | values, respectively), as well as vector types (*F32VecN* and *F64VecN*).
44 |
45 | For examples on how to use the FixMath library, see *Examples/FixedTracer/FixedTracer.cs*, which
46 | implements a simple fixed-point raytracer.
47 |
48 | ## Supported Functions
49 |
50 | Supported operations include:
51 | - Arithmetic: Add(), Sub(), Mul(), Div(), Rcp() (reciprocal), Mod() (modulo)
52 | - Trigonometry: Sin(), Cos(), Tan(), Asin(), Acos(), Atan(), Atan2()
53 | - Exponential: Exp(), Exp2(), Log(), Log2(), Pow()
54 | - Square root: Sqrt(), RSqrt() (reciprocal square root)
55 | - Utility: Abs(), Nabs(), Sign(), Ceil(), Floor(), Round(), Fract(), Min(), Max(), Clamp(), Lerp()
56 | - Conversions: CeilToInt(), FloorToInt(), RoundToInt(), FromDouble(), FromFloat(), ToDouble(), ToFloat()
57 |
58 | Note that the conversions to/from floating point values are not guaranteed to be deterministic.
59 |
60 | For full list of supported operations, see [functions.md](functions.md).
61 |
62 | ## Precision Guide
63 |
64 | The library supports both signed 32.32 fixed-point type (Fixed64), and signed 16.16 fixed-point (Fixed32).
65 | For each operation, for both types, each approximate function comes with three precision variants with
66 | different speed/precision trade off. For example, *Sin()* has 24 bits of precision, *SinFast()* has 16
67 | bits of precision and *SinFastest()* has 10 bits of precision.
68 |
69 | The precision is relative to the output value, so for example 10 bits of precision means that the answer
70 | is accurate to about 1 part in 1000 (or has error margin of 0.1%). The Fast variant is typically about
71 | 10-15% faster than the highest precision, and the Fastest variant about 20-30% faster. See
72 | [functions.md](functions.md) for details.
73 |
74 | Div and Sqrt also come with a Precise variant (*DivPrecise()*, *SqrtPrecise()*), which produce a result
75 | that is exactly correct within representable fixed-point numbers.
76 |
77 | ## Known Limitations
78 |
79 | - Few operations are much slower without a 64-bit CPU, most notably s32.32 multiply and division
80 | - The library opts for performance and determinism over full correctness in edge cases like overflows
81 |
82 | ## License
83 |
84 | Available under MIT license, see LICENSE.txt for details.
85 |
--------------------------------------------------------------------------------
/functions.md:
--------------------------------------------------------------------------------
1 |
2 | # Full Function List
3 |
4 | Below is a table listing all the supported functions and their relative performance. The performance was measured
5 | with C# (Release build) on Windows 10 using a Core i7-4700MQ @ 2.40GHz using a single thread.
6 |
7 | ## Fixed64 (signed 32.32 fixed-point)
8 |
9 | | Operation | Mops/s
10 | |----------------|-----------:
11 | | Add | 1013.76
12 | | Sub | 1011.74
13 | | Mul | 354.42
14 | | Mod | 99.64
15 | | Min | 571.16
16 | | Max | 622.08
17 | | Ceil | 901.12
18 | | Floor | 998.40
19 | | Round | 516.09
20 | | Fract | 1123.84
21 | | CeilToInt | 1118.72
22 | | FloorToInt | 1456.64
23 | | RoundToInt | 1116.16
24 | | Abs | 952.32
25 | | Nabs | 691.20
26 | | Rcp | 86.87
27 | | RcpFast | 102.50
28 | | RcpFastest | 103.72
29 | | DivPrecise | 33.81
30 | | Div | 78.42
31 | | DivFast | 93.97
32 | | DivFastest | 102.40
33 | | SqrtPrecise | 7.29
34 | | Sqrt | 56.83
35 | | SqrtFast | 113.16
36 | | SqrtFastest | 119.60
37 | | RSqrt | 67.76
38 | | RSqrtFast | 106.25
39 | | RSqrtFastest | 111.97
40 | | Exp | 216.30
41 | | ExpFast | 214.61
42 | | ExpFastest | 215.87
43 | | Exp2 | 374.82
44 | | Exp2Fast | 385.79
45 | | Exp2Fastest | 349.32
46 | | Log | 90.18
47 | | LogFast | 108.35
48 | | LogFastest | 121.18
49 | | Log2 | 102.20
50 | | Log2Fast | 102.70
51 | | Log2Fastest | 120.32
52 | | Pow | 43.89
53 | | PowFast | 57.17
54 | | PowFastest | 57.39
55 | | Sin | 160.32
56 | | SinFast | 187.19
57 | | SinFastest | 220.16
58 | | Cos | 151.78
59 | | CosFast | 169.82
60 | | CosFastest | 202.24
61 | | Tan | 42.01
62 | | TanFast | 50.69
63 | | TanFastest | 63.24
64 | | Asin | 23.79
65 | | AsinFast | 29.15
66 | | AsinFastest | 37.87
67 | | Acos | 23.53
68 | | AcosFast | 28.24
69 | | AcosFastest | 38.32
70 | | Atan | 34.59
71 | | AtanFast | 51.10
72 | | AtanFastest | 76.04
73 | | Atan2 | 41.29
74 | | Atan2Fast | 51.00
75 | | Atan2Fastest | 74.85
76 |
77 | ## Fixed32 (signed 16.16 fixed-point)
78 |
79 | | Operation | Mops/s
80 | |----------------|-----------:
81 | | Add | 993.28
82 | | Sub | 993.28
83 | | Mul | 740.92
84 | | Mod | 246.35
85 | | Min | 783.36
86 | | Max | 629.76
87 | | Ceil | 1128.96
88 | | Floor | 1226.24
89 | | Round | 1123.84
90 | | Fract | 1221.12
91 | | CeilToInt | 1328.64
92 | | FloorToInt | 1249.28
93 | | RoundToInt | 1320.96
94 | | Abs | 686.08
95 | | Nabs | 707.70
96 | | Rcp | 107.52
97 | | RcpFast | 125.19
98 | | RcpFastest | 130.04
99 | | DivPrecise | 101.99
100 | | Div | 100.99
101 | | DivFast | 111.97
102 | | DivFastest | 138.58
103 | | SqrtPrecise | 33.28
104 | | Sqrt | 112.42
105 | | SqrtFast | 131.02
106 | | SqrtFastest | 135.68
107 | | RSqrt | 70.83
108 | | RSqrtFast | 137.96
109 | | RSqrtFastest | 150.74
110 | | Exp | 386.56
111 | | ExpFast | 395.22
112 | | ExpFastest | 362.07
113 | | Exp2 | 411.34
114 | | Exp2Fast | 413.89
115 | | Exp2Fastest | 433.47
116 | | Log | 108.77
117 | | LogFast | 129.27
118 | | LogFastest | 166.40
119 | | Log2 | 110.65
120 | | Log2Fast | 129.01
121 | | Log2Fastest | 157.77
122 | | Pow | 69.12
123 | | PowFast | 88.54
124 | | PowFastest | 100.39
125 | | Sin | 176.29
126 | | SinFast | 210.38
127 | | SinFastest | 246.84
128 | | Cos | 172.36
129 | | CosFast | 203.98
130 | | CosFastest | 246.35
131 | | Tan | 50.59
132 | | TanFast | 58.88
133 | | TanFastest | 78.57
134 | | Asin | 26.03
135 | | AsinFast | 29.37
136 | | AsinFastest | 39.77
137 | | Acos | 25.35
138 | | AcosFast | 28.98
139 | | AcosFastest | 39.69
140 | | Atan | 34.26
141 | | AtanFast | 52.81
142 | | AtanFastest | 68.57
143 | | Atan2 | 43.97
144 | | Atan2Fast | 57.39
145 | | Atan2Fastest | 81.92
146 |
--------------------------------------------------------------------------------
/Polyfit/polyfit.py:
--------------------------------------------------------------------------------
1 | #
2 | # FixPointCS
3 | #
4 | # Copyright(c) Jere Sanisalo, Petri Kero
5 | #
6 | # Permission is hereby granted, free of charge, to any person obtaining a copy
7 | # of this software and associated documentation files (the "Software"), to deal
8 | # in the Software without restriction, including without limitation the rights
9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | # copies of the Software, and to permit persons to whom the Software is
11 | # furnished to do so, subject to the following conditions:
12 | #
13 | # The above copyright notice and this permission notice shall be included in all
14 | # copies or substantial portions of the Software.
15 | #
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | # SOFTWARE.
23 | #
24 |
25 | # OBSOLETE -- remez.py is used instead
26 |
27 | # Utility script for generating fitted polynomials to approximate various
28 | # trascendental functions for use in Fixed64.cs.
29 | #
30 | # The current algorithm optimize least squares error (LSE), which produces
31 | # good results. Better polynomials could be generated by optimizing for the
32 | # worst case error, for example with the Remez algorithm.
33 | #
34 | # The generated code may require some modifications like renaming the input
35 | # variable or dropping some zero constants. Also, not all the generated
36 | # polynomials can be calculated with s2.30 due to overflows.
37 |
38 | import numpy as np
39 | import numpy.polynomial.polynomial as poly
40 | import matplotlib.pyplot as plt
41 |
42 | FIT_SAMPLES = 500 # number of samples to use for fitting polynomials
43 | ERR_SAMPLES = 50000 # number of samples to use for computing error in fitted polynomials
44 |
45 | # see: https://stackoverflow.com/questions/15191088/how-to-do-a-polynomial-fit-with-fixed-points
46 | def polyfitWithFixedPoints(n, x, y, xf, yf):
47 | mat = np.empty((n + 1 + len(xf),) * 2)
48 | vec = np.empty((n + 1 + len(xf),))
49 | x_n = x**np.arange(2 * n + 1)[:, None]
50 | yx_n = np.sum(x_n[:n + 1] * y, axis=1)
51 | x_n = np.sum(x_n, axis=1)
52 | idx = np.arange(n + 1) + np.arange(n + 1)[:, None]
53 | mat[:n + 1, :n + 1] = np.take(x_n, idx)
54 | xf_n = xf**np.arange(n + 1)[:, None]
55 | mat[:n + 1, n + 1:] = xf_n / 2
56 | mat[n + 1:, :n + 1] = xf_n.T
57 | mat[n + 1:, n + 1:] = 0
58 | vec[:n + 1] = yx_n
59 | vec[n + 1:] = yf
60 | params = np.linalg.solve(mat, vec)
61 | return params[:n + 1]
62 |
63 | def genQmul(inputName, ndx, order):
64 | if ndx == order:
65 | return f"Qmul30(C{ndx}, {inputName})"
66 | else:
67 | return f"Qmul30({genQmul(inputName, ndx+1, order)} + C{ndx}, {inputName})"
68 |
69 | def fitFunc(name, order, func, domain, fixed, inputName):
70 | (mn, mx) = domain
71 | x = mn + (mx - mn) * (np.arange(FIT_SAMPLES * 1.0) / (FIT_SAMPLES - 1))
72 | y = func(x)
73 |
74 | # fit polynomial (with fixed points)
75 | coefs = polyfitWithFixedPoints(order, x, y, fixed, func(np.array(fixed)))
76 | fitted = poly.Polynomial(coefs)
77 |
78 | # compute max error
79 | ex = mn + (mx - mn) * (np.arange(ERR_SAMPLES * 1.0) / (ERR_SAMPLES - 1))
80 | ey = func(ex)
81 | err = ey - fitted(ex)
82 | errmax = np.max(err)
83 |
84 | # print polynomial error and code
85 | print(f' order {order}: {-np.log2(errmax):0.4} bits')
86 |
87 | for ndx in range(len(coefs)):
88 | coef = coefs[ndx]
89 | if np.abs(coef) < 1e-12:
90 | coef = 0
91 | print(f" const int C{ndx} = {int(coef * (1 << 30))}; // {coef}")
92 |
93 | print(f" int y = {genQmul(inputName, 1, order)} + C0;")
94 | print()
95 |
96 | return fitted
97 |
98 | FUNCS = {
99 | 'sin': ((-1.0, 1.0), 'z', lambda x: np.sin(x * 0.5*np.pi), [-1.0, 0.0, 1.0]),
100 | # 'cos': ((0.0, 1.0), 'z', lambda x: np.cos(x * 0.5*np.pi), [0.0, 1.0]),
101 | 'atan': ((0.0, 1.0), 'k', lambda x: np.arctan(x), [0.0]),
102 | 'rcpm1': ((0.0, 1.0), 'k', lambda x: 1.0 / (x + 1), [0.0, 1.0]),
103 | 'sqrt': ((1.0, 2.0), 'n', lambda x: np.sqrt(x), [1.0, 2.0]),
104 | 'rsqrtm1': ((0.0, 1.0), 'k', lambda x: 1.0 / np.sqrt(x + 1), [0.0, 1.0]),
105 | 'logm1': ((0.0, 1.0), 'k', lambda x: np.log(x+1), [0.0, 1.0]),
106 | 'exp2': ((0.0, 1.0), 'k', lambda x: np.exp2(x), [0.0, 1.0]),
107 | }
108 |
109 | for name in FUNCS.keys():
110 | if name != 'logm1':
111 | continue
112 |
113 | # extract func domain and function
114 | (domain, inputName, func, fixed) = FUNCS[name]
115 | (mn, mx) = domain
116 |
117 | print('')
118 | print(f'{name}({mn} .. {mx}):')
119 |
120 | for order in [3, 4, 5, 6, 7, 8, 9]:
121 | fitted = fitFunc(name, order, func, domain, fixed, inputName)
122 | if order >= 9:
123 | mne = mn + 0.0001
124 | x = mne + (mx - mne) * (np.arange(ERR_SAMPLES * 1.0) / (ERR_SAMPLES - 1))
125 | plt.plot(x, (fitted(x) - func(x)) / func(x))
126 |
127 | plt.show()
128 |
--------------------------------------------------------------------------------
/Transpiler/Util.cs:
--------------------------------------------------------------------------------
1 | //
2 | // FixPointCS
3 | //
4 | // Copyright(c) Jere Sanisalo, Petri Kero
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 |
25 | using System;
26 | using System.Collections.Generic;
27 | using System.Diagnostics;
28 | using System.Linq;
29 | using System.Text;
30 |
31 | namespace Transpiler
32 | {
33 | public static class Util
34 | {
35 | public static string[] Preprocess(string[] lines, string languageDefine)
36 | {
37 | List output = new List();
38 |
39 | // Process the lines
40 | bool included = true;
41 | bool langChecked = false;
42 | foreach (string line in lines)
43 | {
44 | // Poor mans preprocessor handling
45 | if (line.StartsWith("#"))
46 | {
47 | Debug.Assert(line == line.Trim(), "Must not have whitespace on preprocessor lines");
48 | if (line == $"#if {languageDefine}" || line == $"#elif {languageDefine}")
49 | {
50 | included = true; // matching language block
51 | langChecked = true;
52 | }
53 | else if (line.StartsWith("#if ") || line.StartsWith("#elif "))
54 | included = false; // non-matching language block
55 | else if (line == "#if !TRANSPILE")
56 | included = false;
57 | else if (line.StartsWith("#else"))
58 | included = !langChecked;
59 | else if (line.StartsWith("#endif"))
60 | {
61 | included = true;
62 | langChecked = false;
63 | }
64 | else
65 | Debug.Fail("Unrecognized preprocessor line: '{0}'", line);
66 |
67 | continue;
68 | }
69 |
70 | if (!included)
71 | continue;
72 |
73 | output.Add(line);
74 | }
75 |
76 | return output.ToArray();
77 | }
78 |
79 | public static string[] Unindent(string[] lines, int amount)
80 | {
81 | return lines.Select(line =>
82 | {
83 | for (int i = 0; i < amount; i++)
84 | {
85 | if (line.StartsWith(" "))
86 | line = line.Substring(4);
87 | else if (line.StartsWith("\t"))
88 | line = line.Substring(1);
89 | }
90 |
91 | return line;
92 | }).ToArray();
93 | }
94 |
95 | // Replaces all occurances of the whole word (in the C# sense) with the given text
96 | public static string ReplaceWholeWord(string str, string old_word, string new_word)
97 | {
98 | int old_len = old_word.Length;
99 |
100 | // Early exits
101 | if (old_len == 0)
102 | return str;
103 | if (!str.Contains(old_word))
104 | return str;
105 |
106 | StringBuilder sb = new StringBuilder();
107 | int search_pos = 0;
108 |
109 | while (true)
110 | {
111 | // Find the next hit
112 | search_pos = Math.Min(search_pos, str.Length); // Clamp the search area
113 | int next_hit = str.IndexOf(old_word, search_pos);
114 |
115 | // No more hits?
116 | if (next_hit == -1)
117 | {
118 | sb.Append(str.Substring(search_pos));
119 | return sb.ToString();
120 | }
121 |
122 | // Copy to the hit
123 | if (next_hit > search_pos)
124 | {
125 | sb.Append(str.Substring(search_pos, next_hit - search_pos));
126 | search_pos = next_hit;
127 | }
128 |
129 | // Check if the hit is on a word boundary
130 | bool is_ws_start = (next_hit == 0) || (IsWordBoundary(str[next_hit - 1]));
131 | bool is_ws_end = ((next_hit + old_len) == str.Length) || (IsWordBoundary(str[next_hit + old_len]));
132 |
133 | if (is_ws_start && is_ws_end)
134 | { // On a word boundary
135 | if (new_word.Length > 0)
136 | { // Fit the new word in
137 | sb.Append(new_word);
138 | }
139 | else
140 | { // Replacing with empty; skip the next whitespace in the source
141 | search_pos += 1;
142 | }
143 | }
144 | else
145 | { // Not on a word boundary
146 | sb.Append(str.Substring(next_hit, old_len));
147 | }
148 | search_pos += old_len;
149 | }
150 | }
151 |
152 | // Tests if the character is a valid C# identifier boundary character
153 | private static bool IsWordBoundary(char c)
154 | {
155 | if (Char.IsLetterOrDigit(c) || c == '_')
156 | return false;
157 | return true;
158 | }
159 | }
160 | }
--------------------------------------------------------------------------------
/FixPointCS.sln:
--------------------------------------------------------------------------------
1 | Microsoft Visual Studio Solution File, Format Version 12.00
2 | # Visual Studio Version 16
3 | VisualStudioVersion = 16.0.31729.503
4 | MinimumVisualStudioVersion = 10.0.40219.1
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FixPointCS", "FixPointCS\FixPointCS.csproj", "{53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}"
6 | EndProject
7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FixPointCSTest", "FixPointCSTest\FixPointCSTest.csproj", "{9BDCDAF4-3D43-46C2-8085-E59749AB0592}"
8 | ProjectSection(ProjectDependencies) = postProject
9 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2} = {37DF7BEA-1665-482E-9BD5-63D09FF426B2}
10 | EndProjectSection
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Transpiler", "Transpiler\Transpiler.csproj", "{FAA807BE-4DA8-4A03-9C65-22AC74A83C92}"
13 | EndProject
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FixMath", "Examples\FixMath\FixMath.csproj", "{37DF7BEA-1665-482E-9BD5-63D09FF426B2}"
15 | ProjectSection(ProjectDependencies) = postProject
16 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1} = {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}
17 | EndProjectSection
18 | EndProject
19 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FixedTracer", "Examples\FixedTracer\FixedTracer.csproj", "{657E3D4B-38AD-4961-9987-F27D14B677C5}"
20 | ProjectSection(ProjectDependencies) = postProject
21 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2} = {37DF7BEA-1665-482E-9BD5-63D09FF426B2}
22 | EndProjectSection
23 | EndProject
24 | Global
25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
26 | Debug|Any CPU = Debug|Any CPU
27 | Debug|x64 = Debug|x64
28 | Debug|x86 = Debug|x86
29 | Release|Any CPU = Release|Any CPU
30 | Release|x64 = Release|x64
31 | Release|x86 = Release|x86
32 | EndGlobalSection
33 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
34 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Debug|x64.ActiveCfg = Debug|x64
37 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Debug|x64.Build.0 = Debug|x64
38 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Debug|x86.ActiveCfg = Debug|x86
39 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Debug|x86.Build.0 = Debug|x86
40 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
41 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Release|Any CPU.Build.0 = Release|Any CPU
42 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Release|x64.ActiveCfg = Release|x64
43 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Release|x64.Build.0 = Release|x64
44 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Release|x86.ActiveCfg = Release|x86
45 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Release|x86.Build.0 = Release|x86
46 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
47 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Debug|Any CPU.Build.0 = Debug|Any CPU
48 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Debug|x64.ActiveCfg = Debug|x64
49 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Debug|x64.Build.0 = Debug|x64
50 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Debug|x86.ActiveCfg = Debug|x86
51 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Debug|x86.Build.0 = Debug|x86
52 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Release|Any CPU.ActiveCfg = Release|Any CPU
53 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Release|Any CPU.Build.0 = Release|Any CPU
54 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Release|x64.ActiveCfg = Release|x64
55 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Release|x64.Build.0 = Release|x64
56 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Release|x86.ActiveCfg = Release|x86
57 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Release|x86.Build.0 = Release|x86
58 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
59 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Debug|Any CPU.Build.0 = Debug|Any CPU
60 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Debug|x64.ActiveCfg = Debug|x64
61 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Debug|x64.Build.0 = Debug|x64
62 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Debug|x86.ActiveCfg = Debug|x86
63 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Debug|x86.Build.0 = Debug|x86
64 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Release|Any CPU.ActiveCfg = Release|Any CPU
65 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Release|Any CPU.Build.0 = Release|Any CPU
66 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Release|x64.ActiveCfg = Release|x64
67 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Release|x64.Build.0 = Release|x64
68 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Release|x86.ActiveCfg = Release|x86
69 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Release|x86.Build.0 = Release|x86
70 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
71 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
72 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Debug|x64.ActiveCfg = Debug|x64
73 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Debug|x64.Build.0 = Debug|x64
74 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Debug|x86.ActiveCfg = Debug|x86
75 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Debug|x86.Build.0 = Debug|x86
76 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
77 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Release|Any CPU.Build.0 = Release|Any CPU
78 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Release|x64.ActiveCfg = Release|x64
79 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Release|x64.Build.0 = Release|x64
80 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Release|x86.ActiveCfg = Release|x86
81 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Release|x86.Build.0 = Release|x86
82 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
83 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
84 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Debug|x64.ActiveCfg = Debug|x64
85 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Debug|x64.Build.0 = Debug|x64
86 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Debug|x86.ActiveCfg = Debug|x86
87 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Debug|x86.Build.0 = Debug|x86
88 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
89 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Release|Any CPU.Build.0 = Release|Any CPU
90 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Release|x64.ActiveCfg = Release|x64
91 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Release|x64.Build.0 = Release|x64
92 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Release|x86.ActiveCfg = Release|x86
93 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Release|x86.Build.0 = Release|x86
94 | EndGlobalSection
95 | GlobalSection(SolutionProperties) = preSolution
96 | HideSolutionNode = FALSE
97 | EndGlobalSection
98 | GlobalSection(ExtensibilityGlobals) = postSolution
99 | SolutionGuid = {E163824E-0159-48F8-AF9E-8030860D1890}
100 | EndGlobalSection
101 | EndGlobal
102 |
--------------------------------------------------------------------------------
/Cpp/CppTest.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | 15.0
23 | {C8358827-2A34-4AC0-8E43-F6C3D9C0EB9E}
24 | Win32Proj
25 | CppTest
26 | 10.0
27 |
28 |
29 |
30 | Application
31 | true
32 | v142
33 | Unicode
34 |
35 |
36 | Application
37 | false
38 | v142
39 | true
40 | Unicode
41 |
42 |
43 | Application
44 | true
45 | v142
46 | Unicode
47 |
48 |
49 | Application
50 | false
51 | v142
52 | true
53 | Unicode
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | true
75 |
76 |
77 | true
78 |
79 |
80 | false
81 |
82 |
83 | false
84 |
85 |
86 |
87 | NotUsing
88 | Level3
89 | Disabled
90 | true
91 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
92 | true
93 |
94 |
95 |
96 | Console
97 | true
98 |
99 |
100 |
101 |
102 | NotUsing
103 | Level3
104 | Disabled
105 | true
106 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
107 | true
108 |
109 |
110 |
111 | Console
112 | true
113 |
114 |
115 |
116 |
117 | NotUsing
118 | Level3
119 | MaxSpeed
120 | true
121 | true
122 | true
123 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
124 | true
125 |
126 |
127 |
128 | Console
129 | true
130 | true
131 | true
132 |
133 |
134 |
135 |
136 | NotUsing
137 | Level3
138 | MaxSpeed
139 | true
140 | true
141 | true
142 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
143 | true
144 |
145 |
146 |
147 | Console
148 | true
149 | true
150 | true
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
--------------------------------------------------------------------------------
/Transpiler/GenerateCpp.cs:
--------------------------------------------------------------------------------
1 | //
2 | // FixPointCS
3 | //
4 | // Copyright(c) Jere Sanisalo, Petri Kero
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | using System;
25 | using System.Collections.Generic;
26 | using System.IO;
27 | using System.Linq;
28 | using System.Text;
29 | using System.Text.RegularExpressions;
30 | using System.Threading.Tasks;
31 |
32 | namespace Transpiler
33 | {
34 | public static class GenerateCpp
35 | {
36 | public enum Mode
37 | {
38 | Fp32,
39 | Fp64,
40 | Util,
41 | }
42 |
43 | public static void ConvertFile(string inPath, string outPath, Mode mode)
44 | {
45 | Console.WriteLine("Generating {0}..", outPath);
46 |
47 | // Read the file
48 | string[] lines = File.ReadAllLines(inPath);
49 |
50 | // Prepare output
51 | StringBuilder sb = new StringBuilder();
52 |
53 | // Create the prefix and suffix
54 | string prefix = MakePrefix(mode);
55 | string suffix = MakeSuffix(mode);
56 |
57 | // Run hacky preprocessor
58 | lines = Util.Preprocess(lines, "CPP");
59 | lines = Util.Unindent(lines, 1);
60 |
61 | foreach (string line_in in lines)
62 | {
63 | string line = line_in;
64 | string line_trimmed = line.Trim();
65 |
66 | // Ignore [...] -lines (attributes)
67 | if (line_trimmed.StartsWith("["))
68 | continue;
69 |
70 | // Process the line
71 | line = Util.ReplaceWholeWord(line, "public", "");
72 | line = Util.ReplaceWholeWord(line, "private", "");
73 | line = Util.ReplaceWholeWord(line, "const", "static const");
74 | line = Util.ReplaceWholeWord(line, "out int", "int&");
75 | line = Util.ReplaceWholeWord(line, "out uint", "uint&");
76 | line = Util.ReplaceWholeWord(line, "out long", "long&");
77 | line = Util.ReplaceWholeWord(line, "out ulong", "ulong&");
78 | line = Util.ReplaceWholeWord(line, "out", "");
79 | line = Util.ReplaceWholeWord(line, "int", "FP_INT");
80 | line = Util.ReplaceWholeWord(line, "uint", "FP_UINT");
81 | line = Util.ReplaceWholeWord(line, "long", "FP_LONG");
82 | line = Util.ReplaceWholeWord(line, "ulong", "FP_ULONG");
83 | line = Util.ReplaceWholeWord(line, "Debug.Assert", "FP_ASSERT");
84 | line = line.Replace("FixedUtil.", "FixedUtil::");
85 | line = line.Replace("Fixed64.", "Fixed64::");
86 | line = Util.ReplaceWholeWord(line, "-2147483648", "INT32_MIN");
87 | line = Util.ReplaceWholeWord(line, "2147483647", "INT32_MAX");
88 | line = Util.ReplaceWholeWord(line, "-9223372036854775808L", "INT64_MIN");
89 | line = Util.ReplaceWholeWord(line, "9223372036854775807L", "INT64_MAX");
90 | line = Util.ReplaceWholeWord(line, "// PREFIX", prefix);
91 | line = Util.ReplaceWholeWord(line, "// SUFFIX", suffix);
92 | line = ConvertArrays(line);
93 | line = Convert64bitConstants(line);
94 |
95 | // Add the line
96 | sb.AppendLine(line);
97 | }
98 |
99 | // Save the file
100 | File.WriteAllText(outPath, sb.ToString().Replace("\r", ""), Encoding.ASCII);
101 | }
102 |
103 | private static string ConvertArrays(string str)
104 | {
105 | return Regex.Replace(str, "static readonly FP_INT\\[\\] ([a-zA-Z0-9_]+)", "static FP_INT $1[]");
106 | }
107 |
108 | // Converts all 64bit constants into C++ form.
109 | // A bit hacky, requires a leading white space
110 | private static string Convert64bitConstants(string str)
111 | {
112 | return Regex.Replace(str, " (-?0?x?[0-9a-fA-F]+)L", " INT64_C($1)", RegexOptions.Singleline);
113 | }
114 |
115 | private static string GetModeShortDesc(Mode mode)
116 | {
117 | switch (mode)
118 | {
119 | case Mode.Fp32: return "32";
120 | case Mode.Fp64: return "64";
121 | case Mode.Util: return "Util";
122 | default: throw new Exception();
123 | }
124 | }
125 |
126 | // Prefix for the generated file
127 | private static string MakePrefix(Mode mode)
128 | {
129 | string desc = GetModeShortDesc(mode);
130 |
131 | string includes = "";
132 | if (mode != Mode.Util)
133 | includes += "#include \"FixedUtil.h\"\n";
134 | if (mode == Mode.Fp32)
135 | includes += "#include \"Fixed64.h\"\n";
136 |
137 | // Main header
138 | string header = $@"//
139 | // GENERATED FILE!!!
140 | //
141 | // Generated from Fixed{desc}.cs, part of the FixPointCS project (MIT license).
142 | //
143 | #pragma once
144 | #ifndef __FIXED{desc.ToUpperInvariant()}_H
145 | #define __FIXED{desc.ToUpperInvariant()}_H
146 |
147 | // Include numeric types
148 | #include
149 | {includes}
150 |
151 | // If FP_ASSERT is not custom-defined, then use the standard one
152 | #ifndef FP_ASSERT
153 | # include
154 | # define FP_ASSERT(x) assert(x)
155 | #endif
156 |
157 | // If FP_CUSTOM_INVALID_ARGS is defined, then the used is expected to implement the following functions in
158 | // the FixedUtil namespace:
159 | // void InvalidArgument(const char* funcName, const char* argName, FP_INT argValue);
160 | // void InvalidArgument(const char* funcName, const char* argName, FP_INT argValue1, FP_INT argValue2);
161 | // void InvalidArgument(const char* funcName, const char* argName, FP_LONG argValue);
162 | // void InvalidArgument(const char* funcName, const char* argName, FP_LONG argValue1, FP_LONG argValue2);
163 | // These functions should handle the cases for invalid arguments in any desired way (assert, exception, log, ignore etc).
164 | //#define FP_CUSTOM_INVALID_ARGS
165 |
166 | namespace Fixed{desc}
167 | {{
168 | typedef int32_t FP_INT;
169 | typedef uint32_t FP_UINT;
170 | typedef int64_t FP_LONG;
171 | typedef uint64_t FP_ULONG;
172 |
173 | static_assert(sizeof(FP_INT) == 4, ""Wrong bytesize for FP_INT"");
174 | static_assert(sizeof(FP_UINT) == 4, ""Wrong bytesize for FP_UINT"");
175 | static_assert(sizeof(FP_LONG) == 8, ""Wrong bytesize for FP_LONG"");
176 | static_assert(sizeof(FP_ULONG) == 8, ""Wrong bytesize for FP_ULONG"");
177 | ";
178 | // Additional Util headers
179 | if (mode == Mode.Util)
180 | {
181 | header += $@"
182 | #ifdef FP_CUSTOM_INVALID_ARGS
183 | extern void InvalidArgument(const char* funcName, const char* argName, FP_INT argValue);
184 | extern void InvalidArgument(const char* funcName, const char* argName, FP_INT argValue1, FP_INT argValue2);
185 | extern void InvalidArgument(const char* funcName, const char* argName, FP_LONG argValue);
186 | extern void InvalidArgument(const char* funcName, const char* argName, FP_LONG argValue1, FP_LONG argValue2);
187 | #else
188 | static inline void InvalidArgument(const char* funcName, const char* argName, FP_INT argValue) {{ }}
189 | static inline void InvalidArgument(const char* funcName, const char* argName, FP_INT argValue1, FP_INT argValue2) {{ }}
190 | static inline void InvalidArgument(const char* funcName, const char* argName, FP_LONG argValue) {{ }}
191 | static inline void InvalidArgument(const char* funcName, const char* argName, FP_LONG argValue1, FP_LONG argValue2) {{ }}
192 | #endif
193 | ";
194 | }
195 |
196 | return header;
197 | }
198 |
199 | private static string MakeSuffix(Mode mode)
200 | {
201 | string desc = GetModeShortDesc(mode);
202 | return $@"
203 |
204 | #undef FP_ASSERT
205 | }};
206 | #endif // __FIXED{desc.ToUpperInvariant()}_H
207 | ";
208 | }
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/Examples/FixedTracer/FixedTracer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 |
4 | using FixPointCS;
5 | using FixMath;
6 |
7 | // Based on https://blogs.msdn.microsoft.com/lukeh/2007/04/03/a-ray-tracer-in-c3-0/
8 |
9 | namespace FixedTracer
10 | {
11 | public static class RayTracer
12 | {
13 | private const int MaxDepth = 5;
14 |
15 | public static System.Drawing.Color[,] RenderDefaultScene(int width, int height, int numSamples)
16 | {
17 | return Render(width, height, numSamples, DefaultScene);
18 | }
19 |
20 | private static ISect IntersectRay(Ray ray, Scene scene)
21 | {
22 | F64 bestDist = F64.MaxValue;
23 | ISect best = null;
24 |
25 | foreach (SceneObject obj in scene.Things)
26 | {
27 | ISect isect = obj.Intersect(ray);
28 | if (isect != null)
29 | {
30 | if (isect.Dist < bestDist)
31 | {
32 | best = isect;
33 | bestDist = isect.Dist;
34 | }
35 | }
36 | }
37 |
38 | return best;
39 | }
40 |
41 | private static Color TraceRay(Ray ray, Scene scene, int depth)
42 | {
43 | ISect isect = IntersectRay(ray, scene);
44 | if (isect == null)
45 | return Color.Background;
46 | return Shade(ray, isect, scene, depth);
47 | }
48 |
49 | private static Color GetNaturalColor(Material material, F64Vec3 pos, F64Vec3 norm, F64Vec3 rd, Scene scene)
50 | {
51 | Color ret = Color.Black;
52 | foreach (Light light in scene.Lights)
53 | {
54 | F64Vec3 ldis = light.Pos - pos;
55 | F64Vec3 livec = F64Vec3.NormalizeFastest(ldis);
56 | ISect isect = IntersectRay(new Ray(pos, livec), scene);
57 | bool isInShadow = (isect != null) && (isect.Dist * isect.Dist) < F64Vec3.LengthSqr(ldis);
58 | if (!isInShadow)
59 | {
60 | F32 illum = F32.Max(F32.Zero, F64Vec3.Dot(livec, norm).F32);
61 | Color lcolor = illum * light.Color;
62 | F32 specular = F64Vec3.Dot(livec, F64Vec3.NormalizeFastest(rd)).F32;
63 | Color scolor = specular > 0 ? F32.PowFastest(specular, material.Roughness) * light.Color : Color.Black;
64 | ret += material.Diffuse * lcolor + material.Specular * scolor;
65 | }
66 | }
67 | return ret;
68 | }
69 |
70 | private static Color GetReflectionColor(Material material, F64Vec3 pos, F64Vec3 norm, F64Vec3 rd, Scene scene, int depth)
71 | {
72 | return material.Reflect * TraceRay(new Ray(pos, rd), scene, depth + 1);
73 | }
74 |
75 | private static Color Shade(Ray ray, ISect isect, Scene scene, int depth)
76 | {
77 | var pos = isect.Pos;
78 | var normal = isect.Normal;
79 | var reflectDir = ray.Dir - 2 * F64Vec3.Dot(normal, ray.Dir) * normal;
80 | Color ret = Color.Black;
81 | Material material = isect.Material;
82 | ret += GetNaturalColor(material, pos, normal, reflectDir, scene);
83 | if (depth >= MaxDepth)
84 | return ret + new Color(F32.Half, F32.Half, F32.Half);
85 | return ret + GetReflectionColor(material, pos + F64.Ratio(1, 1000) * reflectDir, normal, reflectDir, scene, depth);
86 | }
87 |
88 | internal static System.Drawing.Color[,] Render(int screenWidth, int screenHeight, int numSamples, Scene scene)
89 | {
90 | System.Drawing.Color[,] pixels = new System.Drawing.Color[screenWidth, screenHeight];
91 | Camera camera = scene.Camera;
92 | F64 sx = F64.Ratio(1, 2 * screenWidth);
93 | F64 ox = F64.Half * F64.FromInt(screenWidth);
94 | F64 sy = -F64.Ratio(1, 2 * screenHeight);
95 | F64 oy = F64.Half * F64.FromInt(screenHeight);
96 | F32 ooNumSamples = F32.Ratio(1, numSamples);
97 |
98 | //for (int y = 0; y < screenHeight; y++)
99 | Parallel.For(0, screenHeight, y =>
100 | {
101 | Random rnd = new Random(y * 29827341 + 23427343);
102 | for (int x = 0; x < screenWidth; x++)
103 | {
104 | Color accum = Color.Black;
105 | for (int i = 0; i < numSamples; i++)
106 | {
107 | // rx, ry == random offset in range [-0.5, 0.5]
108 | F64 rx = F64.FromRaw(rnd.Next()) * 2 - F64.Half;
109 | F64 ry = F64.FromRaw(rnd.Next()) * 2 - F64.Half;
110 | F64 xx = (F64.FromInt(x) + rx - ox) * sx;
111 | F64 yy = (F64.FromInt(y) + ry - oy) * sy;
112 | F64Vec3 rayDir = F64Vec3.Normalize(camera.Forward + xx * camera.Right + yy * camera.Up);
113 | Color color = TraceRay(new Ray(camera.Pos, rayDir), scene, 0);
114 | accum += color;
115 | }
116 | pixels[x, y] = (ooNumSamples * accum).ToDrawingColor();
117 | }
118 | });
119 | return pixels;
120 | }
121 |
122 | static F64Vec3 F64Vec3Ratio100(int x, int y, int z)
123 | {
124 | return new F64Vec3(F64.Ratio100(x), F64.Ratio100(y), F64.Ratio100(z));
125 | }
126 |
127 | internal static readonly Scene DefaultScene =
128 | new Scene()
129 | {
130 | Things = new SceneObject[]
131 | {
132 | new Checkerboard(F64Vec3Ratio100(0_00, 1_00, 0_00), F64.Zero, Materials.White, Materials.Black),
133 | new Sphere(F64Vec3Ratio100(0_00, 1_00, 0_00), F64.One, Materials.Shiny),
134 | new Sphere(F64Vec3Ratio100(-1_00, 0_50, 1_50), F64.Half, Materials.Shiny),
135 | },
136 | Lights = new Light[]
137 | {
138 | new Light(F64Vec3Ratio100(-2_00, 2_50, 0_00), Color.Ratio100(49, 7, 7)),
139 | new Light(F64Vec3Ratio100(1_50, 2_50, 1_50), Color.Ratio100(7, 7, 49)),
140 | new Light(F64Vec3Ratio100(1_50, 2_50, -1_50), Color.Ratio100(7, 49, 7)),
141 | new Light(F64Vec3Ratio100(0_00, 3_50, 0_00), Color.Ratio100(21, 21, 35)),
142 | },
143 | Camera = Camera.Create(F64Vec3.FromInt(3, 2, 4), F64Vec3Ratio100(-1_00, 0_50, 0_00))
144 | };
145 | }
146 |
147 | static class Materials
148 | {
149 | public static readonly Material White = new Material
150 | {
151 | Diffuse = Color.White,
152 | Specular = Color.White,
153 | Reflect = F32.Ratio(1, 10),
154 | Roughness = F32.FromInt(150)
155 | };
156 |
157 | public static readonly Material Black = new Material
158 | {
159 | Diffuse = Color.Black,
160 | Specular = Color.White,
161 | Reflect = F32.Ratio(7, 10),
162 | Roughness = F32.FromInt(150)
163 | };
164 |
165 | public static readonly Material Shiny = new Material
166 | {
167 | Diffuse = Color.White,
168 | Specular = Color.Ratio100(50, 50, 50),
169 | Reflect = F32.Ratio(6, 10),
170 | Roughness = F32.FromInt(50)
171 | };
172 | }
173 |
174 | public struct Color
175 | {
176 | public int RawR;
177 | public int RawG;
178 | public int RawB;
179 |
180 | private Color(int r, int g, int b) { RawR = r; RawG = g; RawB = b; }
181 | public Color(F32 r, F32 g, F32 b) { RawR = r.Raw; RawG = g.Raw; RawB = b.Raw; }
182 |
183 | public static Color Ratio100(int r, int g, int b) { return new Color((r << 16) / 100, (g << 16) / 100, (b << 16) / 100); }
184 |
185 | public static Color operator +(Color a, Color b) { return new Color(a.RawR + b.RawR, a.RawG + b.RawG, a.RawB + b.RawB); }
186 | public static Color operator -(Color a, Color b) { return new Color(a.RawR - b.RawR, a.RawG - b.RawG, a.RawB - b.RawB); }
187 | public static Color operator *(Color a, Color b) { return new Color(Fixed32.Mul(a.RawR, b.RawR), Fixed32.Mul(a.RawG, b.RawG), Fixed32.Mul(a.RawB, b.RawB)); }
188 | public static Color operator *(F32 a, Color b) { return new Color(Fixed32.Mul(a.Raw, b.RawR), Fixed32.Mul(a.Raw, b.RawG), Fixed32.Mul(a.Raw, b.RawB)); }
189 | public static Color operator *(Color a, F32 b) { return new Color(Fixed32.Mul(a.RawR, b.Raw), Fixed32.Mul(a.RawG, b.Raw), Fixed32.Mul(a.RawB, b.Raw)); }
190 |
191 | public static Color Black { get { return new Color(0, 0, 0); } }
192 | public static Color White { get { return new Color(Fixed32.One, Fixed32.One, Fixed32.One); } }
193 | public static Color Background { get { return new Color(F32.Ratio(4, 10), F32.Ratio(6, 10), F32.One); } }
194 |
195 | private int ToInt(int raw)
196 | {
197 | int clamped = Fixed32.Clamp(raw, 0, Fixed32.One);
198 | return Fixed32.RoundToInt(clamped * 255);
199 | }
200 |
201 | internal System.Drawing.Color ToDrawingColor()
202 | {
203 | return System.Drawing.Color.FromArgb(ToInt(RawR), ToInt(RawG), ToInt(RawB));
204 | }
205 | }
206 |
207 | struct Ray
208 | {
209 | public readonly F64Vec3 Start;
210 | public readonly F64Vec3 Dir;
211 |
212 | public Ray(F64Vec3 start, F64Vec3 dir)
213 | {
214 | Start = start;
215 | Dir = dir;
216 | }
217 | }
218 |
219 | class ISect
220 | {
221 | public Material Material;
222 | public F64 Dist;
223 | public F64Vec3 Pos;
224 | public F64Vec3 Normal;
225 | }
226 |
227 | class Material
228 | {
229 | public Color Diffuse;
230 | public Color Specular;
231 | public F32 Reflect;
232 | public F32 Roughness;
233 | }
234 |
235 | class Camera
236 | {
237 | public F64Vec3 Pos;
238 | public F64Vec3 Forward;
239 | public F64Vec3 Up;
240 | public F64Vec3 Right;
241 |
242 | public static Camera Create(F64Vec3 pos, F64Vec3 lookAt)
243 | {
244 | F64Vec3 forward = F64Vec3.Normalize(lookAt - pos);
245 | F64Vec3 down = new F64Vec3(F64.Zero, F64.Neg1, F64.Zero);
246 | F64Vec3 right = F64.Ratio(15, 10) * F64Vec3.Normalize(F64Vec3.Cross(forward, down));
247 | F64Vec3 up = F64.Ratio(15, 10) * F64Vec3.Normalize(F64Vec3.Cross(forward, right));
248 |
249 | return new Camera() { Pos = pos, Forward = forward, Up = up, Right = right };
250 | }
251 | }
252 |
253 | class Light
254 | {
255 | public F64Vec3 Pos;
256 | public Color Color;
257 |
258 | public Light(F64Vec3 pos, Color color)
259 | {
260 | Pos = pos;
261 | Color = color;
262 | }
263 | }
264 |
265 | abstract class SceneObject
266 | {
267 | public abstract ISect Intersect(Ray ray);
268 | }
269 |
270 | class Sphere : SceneObject
271 | {
272 | public readonly F64Vec3 Center;
273 | public readonly F64 Radius;
274 | public readonly Material Material;
275 |
276 | public Sphere(F64Vec3 center, F64 radius, Material material)
277 | {
278 | Center = center;
279 | Radius = radius;
280 | Material = material;
281 | }
282 |
283 | public override ISect Intersect(Ray ray)
284 | {
285 | F64Vec3 eo = Center - ray.Start;
286 | F64 v = F64Vec3.Dot(eo, ray.Dir);
287 | if (v < F64.Zero)
288 | return null;
289 |
290 | F64 disc = (Radius * Radius) - (F64Vec3.Dot(eo, eo) - (v * v));
291 | F64 dist = disc < F64.Zero ? F64.Zero : v - F64.SqrtFast(disc);
292 |
293 | if (dist == F64.Zero)
294 | return null;
295 |
296 | F64Vec3 pos = ray.Start + dist * ray.Dir;
297 | return new ISect
298 | {
299 | Material = Material,
300 | Dist = dist,
301 | Pos = pos,
302 | Normal = F64Vec3.Normalize(pos - Center)
303 | };
304 | }
305 | }
306 |
307 | class Checkerboard : SceneObject
308 | {
309 | public F64Vec3 Norm;
310 | public F64 Offset;
311 | public Material White;
312 | public Material Black;
313 |
314 | public Checkerboard(F64Vec3 normal, F64 offset, Material white, Material black)
315 | {
316 | Norm = normal;
317 | Offset = offset;
318 | White = white;
319 | Black = black;
320 | }
321 |
322 | public override ISect Intersect(Ray ray)
323 | {
324 | F64 epsilon = F64.FromRaw(1 << 24);
325 | F64 denom = F64Vec3.Dot(Norm, ray.Dir);
326 | if (denom > -epsilon) return null;
327 |
328 | F64 dist = (F64Vec3.Dot(Norm, ray.Start) + Offset) * F64.RcpFast(-denom);
329 | F64Vec3 pos = ray.Start + dist * ray.Dir;
330 | bool isWhite = ((F64.FloorToInt(pos.Z) + F64.FloorToInt(pos.X)) & 1) != 0;
331 | return new ISect
332 | {
333 | Material = isWhite ? White : Black,
334 | Pos = pos,
335 | Dist = dist,
336 | Normal = Norm
337 | };
338 | }
339 | }
340 |
341 | class Scene
342 | {
343 | public SceneObject[] Things;
344 | public Light[] Lights;
345 | public Camera Camera;
346 | }
347 | }
348 |
--------------------------------------------------------------------------------
/Examples/FixMath/F64Quat.cs:
--------------------------------------------------------------------------------
1 | //
2 | // FixPointCS
3 | //
4 | // Copyright(c) Jere Sanisalo, Petri Kero
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | using FixPointCS;
25 | using System;
26 |
27 | namespace FixMath
28 | {
29 | ///
30 | /// Quaternion struct with signed 32.32 fixed point components.
31 | ///
32 | [Serializable]
33 | public struct F64Quat : IEquatable
34 | {
35 | // Constants
36 | public static F64Quat Identity { get { return new F64Quat(Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, Fixed64.One); } }
37 |
38 | public long RawX;
39 | public long RawY;
40 | public long RawZ;
41 | public long RawW;
42 |
43 | public F64 X { get { return F64.FromRaw(RawX); } set { RawX = value.Raw; } }
44 | public F64 Y { get { return F64.FromRaw(RawY); } set { RawY = value.Raw; } }
45 | public F64 Z { get { return F64.FromRaw(RawZ); } set { RawZ = value.Raw; } }
46 | public F64 W { get { return F64.FromRaw(RawW); } set { RawW = value.Raw; } }
47 |
48 | public F64Quat(F64 x, F64 y, F64 z, F64 w)
49 | {
50 | RawX = x.Raw;
51 | RawY = y.Raw;
52 | RawZ = z.Raw;
53 | RawW = w.Raw;
54 | }
55 |
56 | public F64Quat(F64Vec3 v, F64 w)
57 | {
58 | RawX = v.RawX;
59 | RawY = v.RawY;
60 | RawZ = v.RawZ;
61 | RawW = w.Raw;
62 | }
63 |
64 | private F64Quat(long x, long y, long z, long w)
65 | {
66 | RawX = x;
67 | RawY = y;
68 | RawZ = z;
69 | RawW = w;
70 | }
71 |
72 | public static F64Quat FromAxisAngle(F64Vec3 axis, F64 angle)
73 | {
74 | F64 half_angle = F64.Div2(angle);
75 | return new F64Quat(axis * F64.SinFastest(half_angle), F64.CosFastest(half_angle));
76 | }
77 |
78 | public static F64Quat FromYawPitchRoll(F64 yaw_y, F64 pitch_x, F64 roll_z)
79 | {
80 | // Roll first, about axis the object is facing, then
81 | // pitch upward, then yaw to face into the new heading
82 | F64 half_roll = F64.Div2(roll_z);
83 | F64 sr = F64.SinFastest(half_roll);
84 | F64 cr = F64.CosFastest(half_roll);
85 |
86 | F64 half_pitch = F64.Div2(pitch_x);
87 | F64 sp = F64.SinFastest(half_pitch);
88 | F64 cp = F64.CosFastest(half_pitch);
89 |
90 | F64 half_yaw = F64.Div2(yaw_y);
91 | F64 sy = F64.SinFastest(half_yaw);
92 | F64 cy = F64.CosFastest(half_yaw);
93 |
94 | return new F64Quat(
95 | cy * sp * cr + sy * cp * sr,
96 | sy * cp * cr - cy * sp * sr,
97 | cy * cp * sr - sy * sp * cr,
98 | cy * cp * cr + sy * sp * sr);
99 | }
100 |
101 | // Creates a unit quaternion that represents the rotation from a to b. a and b do not need to be normalized.
102 | public static F64Quat FromTwoVectors(F64Vec3 a, F64Vec3 b)
103 | { // From: http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final
104 | F64 epsilon = F64.Ratio(1, 1000000);
105 |
106 | F64 norm_a_norm_b = F64.SqrtFastest(F64Vec3.LengthSqr(a) * F64Vec3.LengthSqr(b));
107 | F64 real_part = norm_a_norm_b + F64Vec3.Dot(a, b);
108 |
109 | F64Vec3 v;
110 |
111 | if (real_part < (epsilon * norm_a_norm_b))
112 | {
113 | /* If u and v are exactly opposite, rotate 180 degrees
114 | * around an arbitrary orthogonal axis. Axis normalization
115 | * can happen later, when we normalize the quaternion. */
116 | real_part = F64.Zero;
117 | bool cond = F64.Abs(a.X) > F64.Abs(a.Z);
118 | v = cond ? new F64Vec3(-a.Y, a.X, F64.Zero)
119 | : new F64Vec3(F64.Zero, -a.Z, a.Y);
120 | }
121 | else
122 | {
123 | /* Otherwise, build quaternion the standard way. */
124 | v = F64Vec3.Cross(a, b);
125 | }
126 |
127 | return NormalizeFastest(new F64Quat(v, real_part));
128 | }
129 |
130 | public static F64Quat LookRotation(F64Vec3 dir, F64Vec3 up)
131 | { // From: https://answers.unity.com/questions/819699/calculate-quaternionlookrotation-manually.html
132 | if (dir == F64Vec3.Zero)
133 | return Identity;
134 |
135 | if (up != dir)
136 | {
137 | up = F64Vec3.NormalizeFastest(up);
138 | F64Vec3 v = dir + up * -F64Vec3.Dot(up, dir);
139 | F64Quat q = FromTwoVectors(F64Vec3.AxisZ, v);
140 | return FromTwoVectors(v, dir) * q;
141 | }
142 | else
143 | return FromTwoVectors(F64Vec3.AxisZ, dir);
144 | }
145 |
146 | public static F64Quat LookAtRotation(F64Vec3 from, F64Vec3 to, F64Vec3 up)
147 | {
148 | F64Vec3 dir = F64Vec3.NormalizeFastest(to - from);
149 | return LookRotation(dir, up);
150 | }
151 |
152 | // Operators
153 | public static F64Quat operator *(F64Quat a, F64Quat b) { return Multiply(a, b); }
154 |
155 | public static bool operator ==(F64Quat a, F64Quat b) { return a.RawX == b.RawX && a.RawY == b.RawY && a.RawZ == b.RawZ && a.RawW == b.RawW; }
156 | public static bool operator !=(F64Quat a, F64Quat b) { return a.RawX != b.RawX || a.RawY != b.RawY || a.RawZ != b.RawZ || a.RawW != b.RawW; }
157 |
158 | public static F64Quat Negate(F64Quat a) { return new F64Quat(-a.RawX, -a.RawY, -a.RawZ, -a.RawW); }
159 | public static F64Quat Conjugate(F64Quat a) { return new F64Quat(-a.RawX, -a.RawY, -a.RawZ, a.RawW); }
160 | public static F64Quat Inverse(F64Quat a)
161 | {
162 | long inv_norm = F64.Rcp(LengthSqr(a)).Raw;
163 | return new F64Quat(
164 | -Fixed64.Mul(a.RawX, inv_norm),
165 | -Fixed64.Mul(a.RawY, inv_norm),
166 | -Fixed64.Mul(a.RawZ, inv_norm),
167 | Fixed64.Mul(a.RawW, inv_norm));
168 | }
169 | // Inverse for unit quaternions
170 | public static F64Quat InverseUnit(F64Quat a) { return new F64Quat(-a.RawX, -a.RawY, -a.RawZ, a.RawW); }
171 |
172 | public static F64Quat Multiply(F64Quat value1, F64Quat value2)
173 | {
174 | F64 q1x = value1.X;
175 | F64 q1y = value1.Y;
176 | F64 q1z = value1.Z;
177 | F64 q1w = value1.W;
178 |
179 | F64 q2x = value2.X;
180 | F64 q2y = value2.Y;
181 | F64 q2z = value2.Z;
182 | F64 q2w = value2.W;
183 |
184 | // cross(av, bv)
185 | F64 cx = q1y * q2z - q1z * q2y;
186 | F64 cy = q1z * q2x - q1x * q2z;
187 | F64 cz = q1x * q2y - q1y * q2x;
188 |
189 | F64 dot = q1x * q2x + q1y * q2y + q1z * q2z;
190 |
191 | return new F64Quat(
192 | q1x * q2w + q2x * q1w + cx,
193 | q1y * q2w + q2y * q1w + cy,
194 | q1z * q2w + q2z * q1w + cz,
195 | q1w * q2w - dot);
196 | }
197 |
198 | public static F64 Length(F64Quat a) { return F64.Sqrt(LengthSqr(a)); }
199 | public static F64 LengthFast(F64Quat a) { return F64.SqrtFast(LengthSqr(a)); }
200 | public static F64 LengthFastest(F64Quat a) { return F64.SqrtFastest(LengthSqr(a)); }
201 | public static F64 LengthSqr(F64Quat a) { return F64.FromRaw(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY) + Fixed64.Mul(a.RawZ, a.RawZ) + Fixed64.Mul(a.RawW, a.RawW)); }
202 | public static F64Quat Normalize(F64Quat a)
203 | {
204 | long inv_norm = F64.Rcp(Length(a)).Raw;
205 | return new F64Quat(
206 | Fixed64.Mul(a.RawX, inv_norm),
207 | Fixed64.Mul(a.RawY, inv_norm),
208 | Fixed64.Mul(a.RawZ, inv_norm),
209 | Fixed64.Mul(a.RawW, inv_norm));
210 | }
211 | public static F64Quat NormalizeFast(F64Quat a)
212 | {
213 | long inv_norm = F64.RcpFast(LengthFast(a)).Raw;
214 | return new F64Quat(
215 | Fixed64.Mul(a.RawX, inv_norm),
216 | Fixed64.Mul(a.RawY, inv_norm),
217 | Fixed64.Mul(a.RawZ, inv_norm),
218 | Fixed64.Mul(a.RawW, inv_norm));
219 | }
220 | public static F64Quat NormalizeFastest(F64Quat a)
221 | {
222 | long inv_norm = F64.RcpFastest(LengthFastest(a)).Raw;
223 | return new F64Quat(
224 | Fixed64.Mul(a.RawX, inv_norm),
225 | Fixed64.Mul(a.RawY, inv_norm),
226 | Fixed64.Mul(a.RawZ, inv_norm),
227 | Fixed64.Mul(a.RawW, inv_norm));
228 | }
229 |
230 | public static F64Quat Slerp(F64Quat q1, F64Quat q2, F64 t)
231 | {
232 | F64 epsilon = F64.Ratio(1, 1000000);
233 | F64 cos_omega = q1.X * q2.X + q1.Y * q2.Y + q1.Z * q2.Z + q1.W * q2.W;
234 |
235 | bool flip = false;
236 |
237 | if (cos_omega < 0)
238 | {
239 | flip = true;
240 | cos_omega = -cos_omega;
241 | }
242 |
243 | F64 s1, s2;
244 | if (cos_omega > (F64.One - epsilon))
245 | {
246 | // Too close, do straight linear interpolation.
247 | s1 = F64.One - t;
248 | s2 = (flip) ? -t : t;
249 | }
250 | else
251 | {
252 | F64 omega = F64.AcosFastest(cos_omega);
253 | F64 inv_sin_omega = F64.RcpFastest(F64.SinFastest(omega));
254 |
255 | s1 = F64.SinFastest((F64.One - t) * omega) * inv_sin_omega;
256 | s2 = (flip)
257 | ? -F64.SinFastest(t * omega) * inv_sin_omega
258 | : F64.SinFastest(t * omega) * inv_sin_omega;
259 | }
260 |
261 | return new F64Quat(
262 | s1 * q1.X + s2 * q2.X,
263 | s1 * q1.Y + s2 * q2.Y,
264 | s1 * q1.Z + s2 * q2.Z,
265 | s1 * q1.W + s2 * q2.W);
266 | }
267 |
268 | public static F64Quat Lerp(F64Quat q1, F64Quat q2, F64 t)
269 | {
270 | F64 t1 = F64.One - t;
271 | F64 dot = q1.X * q2.X + q1.Y * q2.Y + q1.Z * q2.Z + q1.W * q2.W;
272 |
273 | F64Quat r;
274 | if (dot >= 0)
275 | r = new F64Quat(
276 | t1 * q1.X + t * q2.X,
277 | t1 * q1.Y + t * q2.Y,
278 | t1 * q1.Z + t * q2.Z,
279 | t1 * q1.W + t * q2.W);
280 | else
281 | r = new F64Quat(
282 | t1 * q1.X - t * q2.X,
283 | t1 * q1.Y - t * q2.Y,
284 | t1 * q1.Z - t * q2.Z,
285 | t1 * q1.W - t * q2.W);
286 |
287 | return NormalizeFastest(r);
288 | }
289 |
290 | // Concatenates two Quaternions; the result represents the value1 rotation followed by the value2 rotation.
291 | public static F64Quat Concatenate(F64Quat value1, F64Quat value2)
292 | {
293 | // Concatenate rotation is actually q2 * q1 instead of q1 * q2.
294 | // So that's why value2 goes q1 and value1 goes q2.
295 | return Multiply(value2, value1);
296 | }
297 |
298 | // Rotates a vector by the unit quaternion.
299 | public static F64Vec3 RotateVector(F64Quat rot, F64Vec3 v)
300 | { // From https://gamedev.stackexchange.com/questions/28395/rotating-vector3-by-a-quaternion
301 | F64Vec3 u = new F64Vec3(rot.X, rot.Y, rot.Z);
302 | F64 s = rot.W;
303 |
304 | return
305 | (F64.Two * F64Vec3.Dot(u, v)) * u +
306 | (s * s - F64Vec3.Dot(u, u)) * v +
307 | (F64.Two * s) * F64Vec3.Cross(u, v);
308 | }
309 |
310 | public bool Equals(F64Quat other)
311 | {
312 | return (this == other);
313 | }
314 |
315 | public override bool Equals(object obj)
316 | {
317 | if (!(obj is F64Quat))
318 | return false;
319 | return ((F64Quat)obj) == this;
320 | }
321 |
322 | public override string ToString()
323 | {
324 | return "(" + Fixed64.ToString(RawX) + ", " + Fixed64.ToString(RawY) + ", " + Fixed64.ToString(RawZ) + ", " + Fixed64.ToString(RawW) + ")";
325 | }
326 |
327 | public override int GetHashCode()
328 | {
329 | return X.GetHashCode() ^ (Y.GetHashCode() * 7919) ^ (Z.GetHashCode() * 4513) ^ (W.GetHashCode() * 8923);
330 | }
331 | }
332 | }
333 |
--------------------------------------------------------------------------------
/Examples/FixMath/F64Vec2.cs:
--------------------------------------------------------------------------------
1 | //
2 | // FixPointCS
3 | //
4 | // Copyright(c) Jere Sanisalo, Petri Kero
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | using FixPointCS;
25 | using System;
26 | using System.Runtime.CompilerServices;
27 |
28 | namespace FixMath
29 | {
30 | ///
31 | /// Vector2 struct with signed 32.32 fixed point components.
32 | ///
33 | [Serializable]
34 | public struct F64Vec2 : IEquatable
35 | {
36 | // Constants
37 | public static F64Vec2 Zero { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec2(Fixed64.Zero, Fixed64.Zero); } }
38 | public static F64Vec2 One { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec2(Fixed64.One, Fixed64.One); } }
39 | public static F64Vec2 Down { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec2(Fixed64.Zero, Fixed64.Neg1); } }
40 | public static F64Vec2 Up { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec2(Fixed64.Zero, Fixed64.One); } }
41 | public static F64Vec2 Left { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec2(Fixed64.Neg1, Fixed64.Zero); } }
42 | public static F64Vec2 Right { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec2(Fixed64.One, Fixed64.Zero); } }
43 | public static F64Vec2 AxisX { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec2(Fixed64.One, Fixed64.Zero); } }
44 | public static F64Vec2 AxisY { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec2(Fixed64.Zero, Fixed64.One); } }
45 |
46 | // Raw components
47 | public long RawX;
48 | public long RawY;
49 |
50 | // F64 accessors
51 | public F64 X { get { return F64.FromRaw(RawX); } set { RawX = value.Raw; } }
52 | public F64 Y { get { return F64.FromRaw(RawY); } set { RawY = value.Raw; } }
53 |
54 | public F64Vec2(F64 x, F64 y)
55 | {
56 | RawX = x.Raw;
57 | RawY = y.Raw;
58 | }
59 |
60 | // raw ctor only for internal usage
61 | private F64Vec2(long x, long y)
62 | {
63 | RawX = x;
64 | RawY = y;
65 | }
66 |
67 | public static F64Vec2 FromRaw(long rawX, long rawY) { return new F64Vec2(rawX, rawY); }
68 | public static F64Vec2 FromInt(int x, int y) { return new F64Vec2(Fixed64.FromInt(x), Fixed64.FromInt(y)); }
69 | public static F64Vec2 FromFloat(float x, float y) { return new F64Vec2(Fixed64.FromFloat(x), Fixed64.FromFloat(y)); }
70 | public static F64Vec2 FromDouble(double x, double y) { return new F64Vec2(Fixed64.FromDouble(x), Fixed64.FromDouble(y)); }
71 |
72 | public static F64Vec2 operator -(F64Vec2 a) { return new F64Vec2(-a.RawX, -a.RawY); }
73 | public static F64Vec2 operator +(F64Vec2 a, F64Vec2 b) { return new F64Vec2(a.RawX + b.RawX, a.RawY + b.RawY); }
74 | public static F64Vec2 operator -(F64Vec2 a, F64Vec2 b) { return new F64Vec2(a.RawX - b.RawX, a.RawY - b.RawY); }
75 | public static F64Vec2 operator *(F64Vec2 a, F64Vec2 b) { return new F64Vec2(Fixed64.Mul(a.RawX, b.RawX), Fixed64.Mul(a.RawY, b.RawY)); }
76 | public static F64Vec2 operator /(F64Vec2 a, F64Vec2 b) { return new F64Vec2(Fixed64.DivPrecise(a.RawX, b.RawX), Fixed64.DivPrecise(a.RawY, b.RawY)); }
77 | public static F64Vec2 operator %(F64Vec2 a, F64Vec2 b) { return new F64Vec2(a.RawX % b.RawX, a.RawY % b.RawY); }
78 |
79 | public static F64Vec2 operator +(F64 a, F64Vec2 b) { return new F64Vec2(a.Raw + b.RawX, a.Raw + b.RawY); }
80 | public static F64Vec2 operator +(F64Vec2 a, F64 b) { return new F64Vec2(a.RawX + b.Raw, a.RawY + b.Raw); }
81 | public static F64Vec2 operator -(F64 a, F64Vec2 b) { return new F64Vec2(a.Raw - b.RawX, a.Raw - b.RawY); }
82 | public static F64Vec2 operator -(F64Vec2 a, F64 b) { return new F64Vec2(a.RawX - b.Raw, a.RawY - b.Raw); }
83 | public static F64Vec2 operator *(F64 a, F64Vec2 b) { return new F64Vec2(Fixed64.Mul(a.Raw, b.RawX), Fixed64.Mul(a.Raw, b.RawY)); }
84 | public static F64Vec2 operator *(F64Vec2 a, F64 b) { return new F64Vec2(Fixed64.Mul(a.RawX, b.Raw), Fixed64.Mul(a.RawY, b.Raw)); }
85 | public static F64Vec2 operator /(F64 a, F64Vec2 b) { return new F64Vec2(Fixed64.DivPrecise(a.Raw, b.RawX), Fixed64.DivPrecise(a.Raw, b.RawY)); }
86 | public static F64Vec2 operator /(F64Vec2 a, F64 b) { return new F64Vec2(Fixed64.DivPrecise(a.RawX, b.Raw), Fixed64.DivPrecise(a.RawY, b.Raw)); }
87 | public static F64Vec2 operator %(F64 a, F64Vec2 b) { return new F64Vec2(a.Raw % b.RawX, a.Raw % b.RawY); }
88 | public static F64Vec2 operator %(F64Vec2 a, F64 b) { return new F64Vec2(a.RawX % b.Raw, a.RawY % b.Raw); }
89 |
90 | public static bool operator ==(F64Vec2 a, F64Vec2 b) { return a.RawX == b.RawX && a.RawY == b.RawY; }
91 | public static bool operator !=(F64Vec2 a, F64Vec2 b) { return a.RawX != b.RawX || a.RawY != b.RawY; }
92 |
93 | public static F64Vec2 Div(F64Vec2 a, F64 b) { long oob = Fixed64.Rcp(b.Raw); return new F64Vec2(Fixed64.Mul(a.RawX, oob), Fixed64.Mul(a.RawY, oob)); }
94 | public static F64Vec2 DivFast(F64Vec2 a, F64 b) { long oob = Fixed64.RcpFast(b.Raw); return new F64Vec2(Fixed64.Mul(a.RawX, oob), Fixed64.Mul(a.RawY, oob)); }
95 | public static F64Vec2 DivFastest(F64Vec2 a, F64 b) { long oob = Fixed64.RcpFastest(b.Raw); return new F64Vec2(Fixed64.Mul(a.RawX, oob), Fixed64.Mul(a.RawY, oob)); }
96 | public static F64Vec2 Div(F64Vec2 a, F64Vec2 b) { return new F64Vec2(Fixed64.Div(a.RawX, b.RawX), Fixed64.Div(a.RawY, b.RawY)); }
97 | public static F64Vec2 DivFast(F64Vec2 a, F64Vec2 b) { return new F64Vec2(Fixed64.DivFast(a.RawX, b.RawX), Fixed64.DivFast(a.RawY, b.RawY)); }
98 | public static F64Vec2 DivFastest(F64Vec2 a, F64Vec2 b) { return new F64Vec2(Fixed64.DivFastest(a.RawX, b.RawX), Fixed64.DivFastest(a.RawY, b.RawY)); }
99 | public static F64Vec2 SqrtPrecise(F64Vec2 a) { return new F64Vec2(Fixed64.SqrtPrecise(a.RawX), Fixed64.SqrtPrecise(a.RawY)); }
100 | public static F64Vec2 Sqrt(F64Vec2 a) { return new F64Vec2(Fixed64.Sqrt(a.RawX), Fixed64.Sqrt(a.RawY)); }
101 | public static F64Vec2 SqrtFast(F64Vec2 a) { return new F64Vec2(Fixed64.SqrtFast(a.RawX), Fixed64.SqrtFast(a.RawY)); }
102 | public static F64Vec2 SqrtFastest(F64Vec2 a) { return new F64Vec2(Fixed64.SqrtFastest(a.RawX), Fixed64.SqrtFastest(a.RawY)); }
103 | public static F64Vec2 RSqrt(F64Vec2 a) { return new F64Vec2(Fixed64.RSqrt(a.RawX), Fixed64.RSqrt(a.RawY)); }
104 | public static F64Vec2 RSqrtFast(F64Vec2 a) { return new F64Vec2(Fixed64.RSqrtFast(a.RawX), Fixed64.RSqrtFast(a.RawY)); }
105 | public static F64Vec2 RSqrtFastest(F64Vec2 a) { return new F64Vec2(Fixed64.RSqrtFastest(a.RawX), Fixed64.RSqrtFastest(a.RawY)); }
106 | public static F64Vec2 Rcp(F64Vec2 a) { return new F64Vec2(Fixed64.Rcp(a.RawX), Fixed64.Rcp(a.RawY)); }
107 | public static F64Vec2 RcpFast(F64Vec2 a) { return new F64Vec2(Fixed64.RcpFast(a.RawX), Fixed64.RcpFast(a.RawY)); }
108 | public static F64Vec2 RcpFastest(F64Vec2 a) { return new F64Vec2(Fixed64.RcpFastest(a.RawX), Fixed64.RcpFastest(a.RawY)); }
109 | public static F64Vec2 Exp(F64Vec2 a) { return new F64Vec2(Fixed64.Exp(a.RawX), Fixed64.Exp(a.RawY)); }
110 | public static F64Vec2 ExpFast(F64Vec2 a) { return new F64Vec2(Fixed64.ExpFast(a.RawX), Fixed64.ExpFast(a.RawY)); }
111 | public static F64Vec2 ExpFastest(F64Vec2 a) { return new F64Vec2(Fixed64.ExpFastest(a.RawX), Fixed64.ExpFastest(a.RawY)); }
112 | public static F64Vec2 Exp2(F64Vec2 a) { return new F64Vec2(Fixed64.Exp2(a.RawX), Fixed64.Exp2(a.RawY)); }
113 | public static F64Vec2 Exp2Fast(F64Vec2 a) { return new F64Vec2(Fixed64.Exp2Fast(a.RawX), Fixed64.Exp2Fast(a.RawY)); }
114 | public static F64Vec2 Exp2Fastest(F64Vec2 a) { return new F64Vec2(Fixed64.Exp2Fastest(a.RawX), Fixed64.Exp2Fastest(a.RawY)); }
115 | public static F64Vec2 Log(F64Vec2 a) { return new F64Vec2(Fixed64.Log(a.RawX), Fixed64.Log(a.RawY)); }
116 | public static F64Vec2 LogFast(F64Vec2 a) { return new F64Vec2(Fixed64.LogFast(a.RawX), Fixed64.LogFast(a.RawY)); }
117 | public static F64Vec2 LogFastest(F64Vec2 a) { return new F64Vec2(Fixed64.LogFastest(a.RawX), Fixed64.LogFastest(a.RawY)); }
118 | public static F64Vec2 Log2(F64Vec2 a) { return new F64Vec2(Fixed64.Log2(a.RawX), Fixed64.Log2(a.RawY)); }
119 | public static F64Vec2 Log2Fast(F64Vec2 a) { return new F64Vec2(Fixed64.Log2Fast(a.RawX), Fixed64.Log2Fast(a.RawY)); }
120 | public static F64Vec2 Log2Fastest(F64Vec2 a) { return new F64Vec2(Fixed64.Log2Fastest(a.RawX), Fixed64.Log2Fastest(a.RawY)); }
121 | public static F64Vec2 Sin(F64Vec2 a) { return new F64Vec2(Fixed64.Sin(a.RawX), Fixed64.Sin(a.RawY)); }
122 | public static F64Vec2 SinFast(F64Vec2 a) { return new F64Vec2(Fixed64.SinFast(a.RawX), Fixed64.SinFast(a.RawY)); }
123 | public static F64Vec2 SinFastest(F64Vec2 a) { return new F64Vec2(Fixed64.SinFastest(a.RawX), Fixed64.SinFastest(a.RawY)); }
124 | public static F64Vec2 Cos(F64Vec2 a) { return new F64Vec2(Fixed64.Cos(a.RawX), Fixed64.Cos(a.RawY)); }
125 | public static F64Vec2 CosFast(F64Vec2 a) { return new F64Vec2(Fixed64.CosFast(a.RawX), Fixed64.CosFast(a.RawY)); }
126 | public static F64Vec2 CosFastest(F64Vec2 a) { return new F64Vec2(Fixed64.CosFastest(a.RawX), Fixed64.CosFastest(a.RawY)); }
127 |
128 | public static F64Vec2 Pow(F64Vec2 a, F64 b) { return new F64Vec2(Fixed64.Pow(a.RawX, b.Raw), Fixed64.Pow(a.RawY, b.Raw)); }
129 | public static F64Vec2 PowFast(F64Vec2 a, F64 b) { return new F64Vec2(Fixed64.PowFast(a.RawX, b.Raw), Fixed64.PowFast(a.RawY, b.Raw)); }
130 | public static F64Vec2 PowFastest(F64Vec2 a, F64 b) { return new F64Vec2(Fixed64.PowFastest(a.RawX, b.Raw), Fixed64.PowFastest(a.RawY, b.Raw)); }
131 | public static F64Vec2 Pow(F64 a, F64Vec2 b) { return new F64Vec2(Fixed64.Pow(a.Raw, b.RawX), Fixed64.Pow(a.Raw, b.RawY)); }
132 | public static F64Vec2 PowFast(F64 a, F64Vec2 b) { return new F64Vec2(Fixed64.PowFast(a.Raw, b.RawX), Fixed64.PowFast(a.Raw, b.RawY)); }
133 | public static F64Vec2 PowFastest(F64 a, F64Vec2 b) { return new F64Vec2(Fixed64.PowFastest(a.Raw, b.RawX), Fixed64.PowFastest(a.Raw, b.RawY)); }
134 | public static F64Vec2 Pow(F64Vec2 a, F64Vec2 b) { return new F64Vec2(Fixed64.Pow(a.RawX, b.RawX), Fixed64.Pow(a.RawY, b.RawY)); }
135 | public static F64Vec2 PowFast(F64Vec2 a, F64Vec2 b) { return new F64Vec2(Fixed64.PowFast(a.RawX, b.RawX), Fixed64.PowFast(a.RawY, b.RawY)); }
136 | public static F64Vec2 PowFastest(F64Vec2 a, F64Vec2 b) { return new F64Vec2(Fixed64.PowFastest(a.RawX, b.RawX), Fixed64.PowFastest(a.RawY, b.RawY)); }
137 |
138 | public static F64 Length(F64Vec2 a) { return F64.FromRaw(Fixed64.Sqrt(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY))); }
139 | public static F64 LengthFast(F64Vec2 a) { return F64.FromRaw(Fixed64.SqrtFast(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY))); }
140 | public static F64 LengthFastest(F64Vec2 a) { return F64.FromRaw(Fixed64.SqrtFastest(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY))); }
141 | public static F64 LengthSqr(F64Vec2 a) { return F64.FromRaw(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY)); }
142 | public static F64Vec2 Normalize(F64Vec2 a) { F64 ooLen = F64.FromRaw(Fixed64.RSqrt(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY))); return ooLen * a; }
143 | public static F64Vec2 NormalizeFast(F64Vec2 a) { F64 ooLen = F64.FromRaw(Fixed64.RSqrtFast(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY))); return ooLen * a; }
144 | public static F64Vec2 NormalizeFastest(F64Vec2 a) { F64 ooLen = F64.FromRaw(Fixed64.RSqrtFastest(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY))); return ooLen * a; }
145 |
146 | public static F64 Dot(F64Vec2 a, F64Vec2 b) { return F64.FromRaw(Fixed64.Mul(a.RawX, b.RawX) + Fixed64.Mul(a.RawY, b.RawY)); }
147 | public static F64 Distance(F64Vec2 a, F64Vec2 b) { return Length(a - b); }
148 | public static F64 DistanceFast(F64Vec2 a, F64Vec2 b) { return LengthFast(a - b); }
149 | public static F64 DistanceFastest(F64Vec2 a, F64Vec2 b) { return LengthFastest(a - b); }
150 |
151 | public static F64Vec2 Min(F64Vec2 a, F64Vec2 b) { return new F64Vec2(Fixed64.Min(a.RawX, b.RawX), Fixed64.Min(a.RawY, b.RawY)); }
152 | public static F64Vec2 Max(F64Vec2 a, F64Vec2 b) { return new F64Vec2(Fixed64.Max(a.RawX, b.RawX), Fixed64.Max(a.RawY, b.RawY)); }
153 |
154 | public static F64Vec2 Clamp(F64Vec2 a, F64 min, F64 max)
155 | {
156 | return new F64Vec2(
157 | Fixed64.Clamp(a.RawX, min.Raw, max.Raw),
158 | Fixed64.Clamp(a.RawY, min.Raw, max.Raw));
159 | }
160 |
161 | public static F64Vec2 Clamp(F64Vec2 a, F64Vec2 min, F64Vec2 max)
162 | {
163 | return new F64Vec2(
164 | Fixed64.Clamp(a.RawX, min.RawX, max.RawX),
165 | Fixed64.Clamp(a.RawY, min.RawY, max.RawY));
166 | }
167 |
168 | public static F64Vec2 Lerp(F64Vec2 a, F64Vec2 b, F64 t)
169 | {
170 | long tb = t.Raw;
171 | long ta = Fixed64.One - tb;
172 | return new F64Vec2(
173 | Fixed64.Mul(a.RawX, ta) + Fixed64.Mul(b.RawX, tb),
174 | Fixed64.Mul(a.RawY, ta) + Fixed64.Mul(b.RawY, tb));
175 | }
176 |
177 | public bool Equals(F64Vec2 other)
178 | {
179 | return (this == other);
180 | }
181 |
182 | public override bool Equals(object obj)
183 | {
184 | if (!(obj is F64Vec2))
185 | return false;
186 | return ((F64Vec2)obj) == this;
187 | }
188 |
189 | public override string ToString()
190 | {
191 | return "(" + Fixed64.ToString(RawX) + ", " + Fixed64.ToString(RawY) + ")";
192 | }
193 |
194 | public override int GetHashCode()
195 | {
196 | return RawX.GetHashCode() ^ RawY.GetHashCode() * 7919;
197 | }
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/Examples/FixMath/F32Vec2.cs:
--------------------------------------------------------------------------------
1 | //
2 | // FixPointCS
3 | //
4 | // Copyright(c) Jere Sanisalo, Petri Kero
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | using FixPointCS;
25 | using System;
26 | using System.Runtime.CompilerServices;
27 |
28 | namespace FixMath
29 | {
30 | ///
31 | /// Vector2 struct with signed 16.16 fixed point components.
32 | ///
33 | [Serializable]
34 | public struct F32Vec2 : IEquatable
35 | {
36 | // Constants
37 | public static F32Vec2 Zero { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec2(Fixed32.Zero, Fixed32.Zero); } }
38 | public static F32Vec2 One { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec2(Fixed32.One, Fixed32.One); } }
39 | public static F32Vec2 Half { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec2(Fixed32.Half, Fixed32.Half); } }
40 | public static F32Vec2 Down { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec2(Fixed32.Zero, Fixed32.Neg1); } }
41 | public static F32Vec2 Up { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec2(Fixed32.Zero, Fixed32.One); } }
42 | public static F32Vec2 Left { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec2(Fixed32.Neg1, Fixed32.Zero); } }
43 | public static F32Vec2 Right { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec2(Fixed32.One, Fixed32.Zero); } }
44 | public static F32Vec2 AxisX { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec2(Fixed32.One, Fixed32.Zero); } }
45 | public static F32Vec2 AxisY { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec2(Fixed32.Zero, Fixed32.One); } }
46 |
47 | // Raw components
48 | public int RawX;
49 | public int RawY;
50 |
51 | // F32 accessors
52 | public F32 X { get { return F32.FromRaw(RawX); } set { RawX = value.Raw; } }
53 | public F32 Y { get { return F32.FromRaw(RawY); } set { RawY = value.Raw; } }
54 |
55 | public F32Vec2(F32 x, F32 y)
56 | {
57 | RawX = x.Raw;
58 | RawY = y.Raw;
59 | }
60 |
61 | // raw ctor only for internal usage
62 | private F32Vec2(int x, int y)
63 | {
64 | RawX = x;
65 | RawY = y;
66 | }
67 |
68 | public static F32Vec2 FromRaw(int rawX, int rawY) { return new F32Vec2(rawX, rawY); }
69 | public static F32Vec2 FromInt(int x, int y) { return new F32Vec2(Fixed32.FromInt(x), Fixed32.FromInt(y)); }
70 | public static F32Vec2 FromFloat(float x, float y) { return new F32Vec2(Fixed32.FromFloat(x), Fixed32.FromFloat(y)); }
71 | public static F32Vec2 FromDouble(double x, double y) { return new F32Vec2(Fixed32.FromDouble(x), Fixed32.FromDouble(y)); }
72 |
73 | public static F32Vec2 operator -(F32Vec2 a) { return new F32Vec2(-a.RawX, -a.RawY); }
74 | public static F32Vec2 operator +(F32Vec2 a, F32Vec2 b) { return new F32Vec2(a.RawX + b.RawX, a.RawY + b.RawY); }
75 | public static F32Vec2 operator -(F32Vec2 a, F32Vec2 b) { return new F32Vec2(a.RawX - b.RawX, a.RawY - b.RawY); }
76 | public static F32Vec2 operator *(F32Vec2 a, F32Vec2 b) { return new F32Vec2(Fixed32.Mul(a.RawX, b.RawX), Fixed32.Mul(a.RawY, b.RawY)); }
77 | public static F32Vec2 operator /(F32Vec2 a, F32Vec2 b) { return new F32Vec2(Fixed32.DivPrecise(a.RawX, b.RawX), Fixed32.DivPrecise(a.RawY, b.RawY)); }
78 | public static F32Vec2 operator %(F32Vec2 a, F32Vec2 b) { return new F32Vec2(a.RawX % b.RawX, a.RawY % b.RawY); }
79 |
80 | public static F32Vec2 operator +(F32 a, F32Vec2 b) { return new F32Vec2(a.Raw + b.RawX, a.Raw + b.RawY); }
81 | public static F32Vec2 operator +(F32Vec2 a, F32 b) { return new F32Vec2(a.RawX + b.Raw, a.RawY + b.Raw); }
82 | public static F32Vec2 operator -(F32 a, F32Vec2 b) { return new F32Vec2(a.Raw - b.RawX, a.Raw - b.RawY); }
83 | public static F32Vec2 operator -(F32Vec2 a, F32 b) { return new F32Vec2(a.RawX - b.Raw, a.RawY - b.Raw); }
84 | public static F32Vec2 operator *(F32 a, F32Vec2 b) { return new F32Vec2(Fixed32.Mul(a.Raw, b.RawX), Fixed32.Mul(a.Raw, b.RawY)); }
85 | public static F32Vec2 operator *(F32Vec2 a, F32 b) { return new F32Vec2(Fixed32.Mul(a.RawX, b.Raw), Fixed32.Mul(a.RawY, b.Raw)); }
86 | public static F32Vec2 operator /(F32 a, F32Vec2 b) { return new F32Vec2(Fixed32.DivPrecise(a.Raw, b.RawX), Fixed32.DivPrecise(a.Raw, b.RawY)); }
87 | public static F32Vec2 operator /(F32Vec2 a, F32 b) { return new F32Vec2(Fixed32.DivPrecise(a.RawX, b.Raw), Fixed32.DivPrecise(a.RawY, b.Raw)); }
88 | public static F32Vec2 operator %(F32 a, F32Vec2 b) { return new F32Vec2(a.Raw % b.RawX, a.Raw % b.RawY); }
89 | public static F32Vec2 operator %(F32Vec2 a, F32 b) { return new F32Vec2(a.RawX % b.Raw, a.RawY % b.Raw); }
90 |
91 | public static bool operator ==(F32Vec2 a, F32Vec2 b) { return a.RawX == b.RawX && a.RawY == b.RawY; }
92 | public static bool operator !=(F32Vec2 a, F32Vec2 b) { return a.RawX != b.RawX || a.RawY != b.RawY; }
93 |
94 | public static F32Vec2 Div(F32Vec2 a, F32 b) { int oob = Fixed32.Rcp(b.Raw); return new F32Vec2(Fixed32.Mul(a.RawX, oob), Fixed32.Mul(a.RawY, oob)); }
95 | public static F32Vec2 DivFast(F32Vec2 a, F32 b) { int oob = Fixed32.RcpFast(b.Raw); return new F32Vec2(Fixed32.Mul(a.RawX, oob), Fixed32.Mul(a.RawY, oob)); }
96 | public static F32Vec2 DivFastest(F32Vec2 a, F32 b) { int oob = Fixed32.RcpFastest(b.Raw); return new F32Vec2(Fixed32.Mul(a.RawX, oob), Fixed32.Mul(a.RawY, oob)); }
97 | public static F32Vec2 Div(F32Vec2 a, F32Vec2 b) { return new F32Vec2(Fixed32.Div(a.RawX, b.RawX), Fixed32.Div(a.RawY, b.RawY)); }
98 | public static F32Vec2 DivFast(F32Vec2 a, F32Vec2 b) { return new F32Vec2(Fixed32.DivFast(a.RawX, b.RawX), Fixed32.DivFast(a.RawY, b.RawY)); }
99 | public static F32Vec2 DivFastest(F32Vec2 a, F32Vec2 b) { return new F32Vec2(Fixed32.DivFastest(a.RawX, b.RawX), Fixed32.DivFastest(a.RawY, b.RawY)); }
100 | public static F32Vec2 SqrtPrecise(F32Vec2 a) { return new F32Vec2(Fixed32.SqrtPrecise(a.RawX), Fixed32.SqrtPrecise(a.RawY)); }
101 | public static F32Vec2 Sqrt(F32Vec2 a) { return new F32Vec2(Fixed32.Sqrt(a.RawX), Fixed32.Sqrt(a.RawY)); }
102 | public static F32Vec2 SqrtFast(F32Vec2 a) { return new F32Vec2(Fixed32.SqrtFast(a.RawX), Fixed32.SqrtFast(a.RawY)); }
103 | public static F32Vec2 SqrtFastest(F32Vec2 a) { return new F32Vec2(Fixed32.SqrtFastest(a.RawX), Fixed32.SqrtFastest(a.RawY)); }
104 | public static F32Vec2 RSqrt(F32Vec2 a) { return new F32Vec2(Fixed32.RSqrt(a.RawX), Fixed32.RSqrt(a.RawY)); }
105 | public static F32Vec2 RSqrtFast(F32Vec2 a) { return new F32Vec2(Fixed32.RSqrtFast(a.RawX), Fixed32.RSqrtFast(a.RawY)); }
106 | public static F32Vec2 RSqrtFastest(F32Vec2 a) { return new F32Vec2(Fixed32.RSqrtFastest(a.RawX), Fixed32.RSqrtFastest(a.RawY)); }
107 | public static F32Vec2 Rcp(F32Vec2 a) { return new F32Vec2(Fixed32.Rcp(a.RawX), Fixed32.Rcp(a.RawY)); }
108 | public static F32Vec2 RcpFast(F32Vec2 a) { return new F32Vec2(Fixed32.RcpFast(a.RawX), Fixed32.RcpFast(a.RawY)); }
109 | public static F32Vec2 RcpFastest(F32Vec2 a) { return new F32Vec2(Fixed32.RcpFastest(a.RawX), Fixed32.RcpFastest(a.RawY)); }
110 | public static F32Vec2 Exp(F32Vec2 a) { return new F32Vec2(Fixed32.Exp(a.RawX), Fixed32.Exp(a.RawY)); }
111 | public static F32Vec2 ExpFast(F32Vec2 a) { return new F32Vec2(Fixed32.ExpFast(a.RawX), Fixed32.ExpFast(a.RawY)); }
112 | public static F32Vec2 ExpFastest(F32Vec2 a) { return new F32Vec2(Fixed32.ExpFastest(a.RawX), Fixed32.ExpFastest(a.RawY)); }
113 | public static F32Vec2 Exp2(F32Vec2 a) { return new F32Vec2(Fixed32.Exp2(a.RawX), Fixed32.Exp2(a.RawY)); }
114 | public static F32Vec2 Exp2Fast(F32Vec2 a) { return new F32Vec2(Fixed32.Exp2Fast(a.RawX), Fixed32.Exp2Fast(a.RawY)); }
115 | public static F32Vec2 Exp2Fastest(F32Vec2 a) { return new F32Vec2(Fixed32.Exp2Fastest(a.RawX), Fixed32.Exp2Fastest(a.RawY)); }
116 | public static F32Vec2 Log(F32Vec2 a) { return new F32Vec2(Fixed32.Log(a.RawX), Fixed32.Log(a.RawY)); }
117 | public static F32Vec2 LogFast(F32Vec2 a) { return new F32Vec2(Fixed32.LogFast(a.RawX), Fixed32.LogFast(a.RawY)); }
118 | public static F32Vec2 LogFastest(F32Vec2 a) { return new F32Vec2(Fixed32.LogFastest(a.RawX), Fixed32.LogFastest(a.RawY)); }
119 | public static F32Vec2 Log2(F32Vec2 a) { return new F32Vec2(Fixed32.Log2(a.RawX), Fixed32.Log2(a.RawY)); }
120 | public static F32Vec2 Log2Fast(F32Vec2 a) { return new F32Vec2(Fixed32.Log2Fast(a.RawX), Fixed32.Log2Fast(a.RawY)); }
121 | public static F32Vec2 Log2Fastest(F32Vec2 a) { return new F32Vec2(Fixed32.Log2Fastest(a.RawX), Fixed32.Log2Fastest(a.RawY)); }
122 | public static F32Vec2 Sin(F32Vec2 a) { return new F32Vec2(Fixed32.Sin(a.RawX), Fixed32.Sin(a.RawY)); }
123 | public static F32Vec2 SinFast(F32Vec2 a) { return new F32Vec2(Fixed32.SinFast(a.RawX), Fixed32.SinFast(a.RawY)); }
124 | public static F32Vec2 SinFastest(F32Vec2 a) { return new F32Vec2(Fixed32.SinFastest(a.RawX), Fixed32.SinFastest(a.RawY)); }
125 | public static F32Vec2 Cos(F32Vec2 a) { return new F32Vec2(Fixed32.Cos(a.RawX), Fixed32.Cos(a.RawY)); }
126 | public static F32Vec2 CosFast(F32Vec2 a) { return new F32Vec2(Fixed32.CosFast(a.RawX), Fixed32.CosFast(a.RawY)); }
127 | public static F32Vec2 CosFastest(F32Vec2 a) { return new F32Vec2(Fixed32.CosFastest(a.RawX), Fixed32.CosFastest(a.RawY)); }
128 |
129 | public static F32Vec2 Pow(F32Vec2 a, F32 b) { return new F32Vec2(Fixed32.Pow(a.RawX, b.Raw), Fixed32.Pow(a.RawY, b.Raw)); }
130 | public static F32Vec2 PowFast(F32Vec2 a, F32 b) { return new F32Vec2(Fixed32.PowFast(a.RawX, b.Raw), Fixed32.PowFast(a.RawY, b.Raw)); }
131 | public static F32Vec2 PowFastest(F32Vec2 a, F32 b) { return new F32Vec2(Fixed32.PowFastest(a.RawX, b.Raw), Fixed32.PowFastest(a.RawY, b.Raw)); }
132 | public static F32Vec2 Pow(F32 a, F32Vec2 b) { return new F32Vec2(Fixed32.Pow(a.Raw, b.RawX), Fixed32.Pow(a.Raw, b.RawY)); }
133 | public static F32Vec2 PowFast(F32 a, F32Vec2 b) { return new F32Vec2(Fixed32.PowFast(a.Raw, b.RawX), Fixed32.PowFast(a.Raw, b.RawY)); }
134 | public static F32Vec2 PowFastest(F32 a, F32Vec2 b) { return new F32Vec2(Fixed32.PowFastest(a.Raw, b.RawX), Fixed32.PowFastest(a.Raw, b.RawY)); }
135 | public static F32Vec2 Pow(F32Vec2 a, F32Vec2 b) { return new F32Vec2(Fixed32.Pow(a.RawX, b.RawX), Fixed32.Pow(a.RawY, b.RawY)); }
136 | public static F32Vec2 PowFast(F32Vec2 a, F32Vec2 b) { return new F32Vec2(Fixed32.PowFast(a.RawX, b.RawX), Fixed32.PowFast(a.RawY, b.RawY)); }
137 | public static F32Vec2 PowFastest(F32Vec2 a, F32Vec2 b) { return new F32Vec2(Fixed32.PowFastest(a.RawX, b.RawX), Fixed32.PowFastest(a.RawY, b.RawY)); }
138 |
139 | public static F32 Length(F32Vec2 a) { return F32.FromRaw((int)(Fixed64.Sqrt((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY) >> 16)); }
140 | public static F32 LengthFast(F32Vec2 a) { return F32.FromRaw((int)(Fixed64.SqrtFast((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY) >> 16)); }
141 | public static F32 LengthFastest(F32Vec2 a) { return F32.FromRaw((int)(Fixed64.SqrtFastest((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY) >> 16)); }
142 | public static F64 LengthSqr(F32Vec2 a) { return F64.FromRaw((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY); }
143 | public static F32Vec2 Normalize(F32Vec2 a) { F32 ooLen = F32.FromRaw((int)(Fixed64.RSqrt((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY) >> 16)); return ooLen * a; }
144 | public static F32Vec2 NormalizeFast(F32Vec2 a) { F32 ooLen = F32.FromRaw((int)(Fixed64.RSqrtFast((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY) >> 16)); return ooLen * a; }
145 | public static F32Vec2 NormalizeFastest(F32Vec2 a) { F32 ooLen = F32.FromRaw((int)(Fixed64.RSqrtFastest((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY) >> 16)); return ooLen * a; }
146 |
147 | public static F32 Dot(F32Vec2 a, F32Vec2 b) { return F32.FromRaw(Fixed32.Mul(a.RawX, b.RawX) + Fixed32.Mul(a.RawY, b.RawY)); }
148 | public static F32 Distance(F32Vec2 a, F32Vec2 b) { return Length(a - b); }
149 | public static F32 DistanceFast(F32Vec2 a, F32Vec2 b) { return LengthFast(a - b); }
150 | public static F32 DistanceFastest(F32Vec2 a, F32Vec2 b) { return LengthFastest(a - b); }
151 |
152 | public static F32Vec2 Min(F32Vec2 a, F32Vec2 b) { return new F32Vec2(Fixed32.Min(a.RawX, b.RawX), Fixed32.Min(a.RawY, b.RawY)); }
153 | public static F32Vec2 Max(F32Vec2 a, F32Vec2 b) { return new F32Vec2(Fixed32.Max(a.RawX, b.RawX), Fixed32.Max(a.RawY, b.RawY)); }
154 |
155 | public static F32Vec2 Clamp(F32Vec2 a, F32 min, F32 max)
156 | {
157 | return new F32Vec2(
158 | Fixed32.Clamp(a.RawX, min.Raw, max.Raw),
159 | Fixed32.Clamp(a.RawY, min.Raw, max.Raw));
160 | }
161 |
162 | public static F32Vec2 Clamp(F32Vec2 a, F32Vec2 min, F32Vec2 max)
163 | {
164 | return new F32Vec2(
165 | Fixed32.Clamp(a.RawX, min.RawX, max.RawX),
166 | Fixed32.Clamp(a.RawY, min.RawY, max.RawY));
167 | }
168 |
169 | public static F32Vec2 Lerp(F32Vec2 a, F32Vec2 b, F32 t)
170 | {
171 | int tb = t.Raw;
172 | int ta = Fixed32.One - tb;
173 | return new F32Vec2(
174 | Fixed32.Mul(a.RawX, ta) + Fixed32.Mul(b.RawX, tb),
175 | Fixed32.Mul(a.RawY, ta) + Fixed32.Mul(b.RawY, tb));
176 | }
177 |
178 | public bool Equals(F32Vec2 other)
179 | {
180 | return (this == other);
181 | }
182 |
183 | public override bool Equals(object obj)
184 | {
185 | if (!(obj is F32Vec2))
186 | return false;
187 | return ((F32Vec2)obj) == this;
188 | }
189 |
190 | public override string ToString()
191 | {
192 | return "(" + Fixed32.ToString(RawX) + ", " + Fixed32.ToString(RawY) + ")";
193 | }
194 |
195 | public override int GetHashCode()
196 | {
197 | return RawX.GetHashCode() ^ RawY.GetHashCode() * 7919;
198 | }
199 | }
200 | }
201 |
--------------------------------------------------------------------------------
/Examples/FixMath/F32.cs:
--------------------------------------------------------------------------------
1 | //
2 | // FixPointCS
3 | //
4 | // Copyright(c) Jere Sanisalo, Petri Kero
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | using FixPointCS;
25 | using System;
26 | using System.Runtime.CompilerServices;
27 |
28 | namespace FixMath
29 | {
30 | ///
31 | /// Signed 16.16 fixed point value struct.
32 | ///
33 | [Serializable]
34 | public struct F32 : IComparable, IEquatable, IComparable
35 | {
36 | // Constants
37 | public static F32 Neg1 { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.Neg1); } }
38 | public static F32 Zero { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.Zero); } }
39 | public static F32 Half { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.Half); } }
40 | public static F32 One { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.One); } }
41 | public static F32 Two { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.Two); } }
42 | public static F32 Pi { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.Pi); } }
43 | public static F32 Pi2 { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.Pi2); } }
44 | public static F32 PiHalf { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.PiHalf); } }
45 | public static F32 E { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.E); } }
46 |
47 | public static F32 MinValue { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.MinValue); } }
48 | public static F32 MaxValue { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.MaxValue); } }
49 |
50 | // Raw fixed point value
51 | public int Raw;
52 |
53 | // Construction
54 | [MethodImpl(FixedUtil.AggressiveInlining)] public static F32 FromRaw(int raw) { F32 v; v.Raw = raw; return v; }
55 | [MethodImpl(FixedUtil.AggressiveInlining)] public static F32 FromInt(int v) { return FromRaw(Fixed32.FromInt(v)); }
56 | [MethodImpl(FixedUtil.AggressiveInlining)] public static F32 FromFloat(float v) { return FromRaw(Fixed32.FromFloat(v)); }
57 | [MethodImpl(FixedUtil.AggressiveInlining)] public static F32 FromDouble(double v) { return FromRaw(Fixed32.FromDouble(v)); }
58 | [MethodImpl(FixedUtil.AggressiveInlining)] public static F32 FromF64(F64 v) { return FromRaw((int)(v.Raw >> 16)); }
59 |
60 | // Conversions
61 | public static int FloorToInt(F32 a) { return Fixed32.FloorToInt(a.Raw); }
62 | public static int CeilToInt(F32 a) { return Fixed32.CeilToInt(a.Raw); }
63 | public static int RoundToInt(F32 a) { return Fixed32.RoundToInt(a.Raw); }
64 | public float Float { get { return Fixed32.ToFloat(Raw); } }
65 | public double Double { get { return Fixed32.ToDouble(Raw); } }
66 |
67 | // Creates the fixed point number that's a divided by b.
68 | public static F32 Ratio(int a, int b) { return F32.FromRaw((int)(((long)a << 16) / b)); }
69 | // Creates the fixed point number that's a divided by 10.
70 | public static F32 Ratio10(int a) { return F32.FromRaw((int)(((long)a << 16) / 10)); }
71 | // Creates the fixed point number that's a divided by 100.
72 | public static F32 Ratio100(int a) { return F32.FromRaw((int)(((long)a << 16) / 100)); }
73 | // Creates the fixed point number that's a divided by 1000.
74 | public static F32 Ratio1000(int a) { return F32.FromRaw((int)(((long)a << 16) / 1000)); }
75 |
76 | // Operators
77 | public static F32 operator -(F32 v1) { return FromRaw(-v1.Raw); }
78 |
79 | //public static F32 operator +(F32 v1, F32 v2) { F32 r; r.raw = v1.raw + v2.raw; return r; }
80 | public static F32 operator +(F32 v1, F32 v2) { return FromRaw(v1.Raw + v2.Raw); }
81 | public static F32 operator -(F32 v1, F32 v2) { return FromRaw(v1.Raw - v2.Raw); }
82 | public static F32 operator *(F32 v1, F32 v2) { return FromRaw(Fixed32.Mul(v1.Raw, v2.Raw)); }
83 | public static F32 operator /(F32 v1, F32 v2) { return FromRaw(Fixed32.DivPrecise(v1.Raw, v2.Raw)); }
84 | public static F32 operator %(F32 v1, F32 v2) { return FromRaw(Fixed32.Mod(v1.Raw, v2.Raw)); }
85 |
86 | public static F32 operator +(F32 v1, int v2) { return FromRaw(v1.Raw + Fixed32.FromInt(v2)); }
87 | public static F32 operator +(int v1, F32 v2) { return FromRaw(Fixed32.FromInt(v1) + v2.Raw); }
88 | public static F32 operator -(F32 v1, int v2) { return FromRaw(v1.Raw - Fixed32.FromInt(v2)); }
89 | public static F32 operator -(int v1, F32 v2) { return FromRaw(Fixed32.FromInt(v1) - v2.Raw); }
90 | public static F32 operator *(F32 v1, int v2) { return FromRaw(v1.Raw * (int)v2); }
91 | public static F32 operator *(int v1, F32 v2) { return FromRaw((int)v1 * v2.Raw); }
92 | public static F32 operator /(F32 v1, int v2) { return FromRaw(v1.Raw / (int)v2); }
93 | public static F32 operator /(int v1, F32 v2) { return FromRaw(Fixed32.DivPrecise(Fixed32.FromInt(v1), v2.Raw)); }
94 | public static F32 operator %(F32 v1, int v2) { return FromRaw(Fixed32.Mod(v1.Raw, Fixed32.FromInt(v2))); }
95 | public static F32 operator %(int v1, F32 v2) { return FromRaw(Fixed32.Mod(Fixed32.FromInt(v1), v2.Raw)); }
96 |
97 | public static F32 operator ++(F32 v1) { return FromRaw(v1.Raw + Fixed32.One); }
98 | public static F32 operator --(F32 v1) { return FromRaw(v1.Raw - Fixed32.One); }
99 |
100 | public static bool operator ==(F32 v1, F32 v2) { return v1.Raw == v2.Raw; }
101 | public static bool operator !=(F32 v1, F32 v2) { return v1.Raw != v2.Raw; }
102 | public static bool operator <(F32 v1, F32 v2) { return v1.Raw < v2.Raw; }
103 | public static bool operator <=(F32 v1, F32 v2) { return v1.Raw <= v2.Raw; }
104 | public static bool operator >(F32 v1, F32 v2) { return v1.Raw > v2.Raw; }
105 | public static bool operator >=(F32 v1, F32 v2) { return v1.Raw >= v2.Raw; }
106 |
107 | public static bool operator ==(int v1, F32 v2) { return Fixed32.FromInt(v1) == v2.Raw; }
108 | public static bool operator ==(F32 v1, int v2) { return v1.Raw == Fixed32.FromInt(v2); }
109 | public static bool operator !=(int v1, F32 v2) { return Fixed32.FromInt(v1) != v2.Raw; }
110 | public static bool operator !=(F32 v1, int v2) { return v1.Raw != Fixed32.FromInt(v2); }
111 | public static bool operator <(int v1, F32 v2) { return Fixed32.FromInt(v1) < v2.Raw; }
112 | public static bool operator <(F32 v1, int v2) { return v1.Raw < Fixed32.FromInt(v2); }
113 | public static bool operator <=(int v1, F32 v2) { return Fixed32.FromInt(v1) <= v2.Raw; }
114 | public static bool operator <=(F32 v1, int v2) { return v1.Raw <= Fixed32.FromInt(v2); }
115 | public static bool operator >(int v1, F32 v2) { return Fixed32.FromInt(v1) > v2.Raw; }
116 | public static bool operator >(F32 v1, int v2) { return v1.Raw > Fixed32.FromInt(v2); }
117 | public static bool operator >=(int v1, F32 v2) { return Fixed32.FromInt(v1) >= v2.Raw; }
118 | public static bool operator >=(F32 v1, int v2) { return v1.Raw >= Fixed32.FromInt(v2); }
119 |
120 | public static F32 RadToDeg(F32 a) { return FromRaw(Fixed32.Mul(a.Raw, 3754943)); } // 180 / F32.Pi
121 | public static F32 DegToRad(F32 a) { return FromRaw(Fixed32.Mul(a.Raw, 1143)); } // F32.Pi / 180
122 |
123 | public static F32 Div2(F32 a) { return FromRaw(a.Raw >> 1); }
124 | public static F32 Abs(F32 a) { return FromRaw(Fixed32.Abs(a.Raw)); }
125 | public static F32 Nabs(F32 a) { return FromRaw(Fixed32.Nabs(a.Raw)); }
126 | public static int Sign(F32 a) { return Fixed32.Sign(a.Raw); }
127 | public static F32 Ceil(F32 a) { return FromRaw(Fixed32.Ceil(a.Raw)); }
128 | public static F32 Floor(F32 a) { return FromRaw(Fixed32.Floor(a.Raw)); }
129 | public static F32 Round(F32 a) { return FromRaw(Fixed32.Round(a.Raw)); }
130 | public static F32 Fract(F32 a) { return FromRaw(Fixed32.Fract(a.Raw)); }
131 | public static F32 Div(F32 a, F32 b) { return FromRaw(Fixed32.Div(a.Raw, b.Raw)); }
132 | public static F32 DivFast(F32 a, F32 b) { return FromRaw(Fixed32.DivFast(a.Raw, b.Raw)); }
133 | public static F32 DivFastest(F32 a, F32 b) { return FromRaw(Fixed32.DivFastest(a.Raw, b.Raw)); }
134 | public static F32 SqrtPrecise(F32 a) { return FromRaw(Fixed32.SqrtPrecise(a.Raw)); }
135 | public static F32 Sqrt(F32 a) { return FromRaw(Fixed32.Sqrt(a.Raw)); }
136 | public static F32 SqrtFast(F32 a) { return FromRaw(Fixed32.SqrtFast(a.Raw)); }
137 | public static F32 SqrtFastest(F32 a) { return FromRaw(Fixed32.SqrtFastest(a.Raw)); }
138 | public static F32 RSqrt(F32 a) { return FromRaw(Fixed32.RSqrt(a.Raw)); }
139 | public static F32 RSqrtFast(F32 a) { return FromRaw(Fixed32.RSqrtFast(a.Raw)); }
140 | public static F32 RSqrtFastest(F32 a) { return FromRaw(Fixed32.RSqrtFastest(a.Raw)); }
141 | public static F32 Rcp(F32 a) { return FromRaw(Fixed32.Rcp(a.Raw)); }
142 | public static F32 RcpFast(F32 a) { return FromRaw(Fixed32.RcpFast(a.Raw)); }
143 | public static F32 RcpFastest(F32 a) { return FromRaw(Fixed32.RcpFastest(a.Raw)); }
144 | public static F32 Exp(F32 a) { return FromRaw(Fixed32.Exp(a.Raw)); }
145 | public static F32 ExpFast(F32 a) { return FromRaw(Fixed32.ExpFast(a.Raw)); }
146 | public static F32 ExpFastest(F32 a) { return FromRaw(Fixed32.ExpFastest(a.Raw)); }
147 | public static F32 Exp2(F32 a) { return FromRaw(Fixed32.Exp2(a.Raw)); }
148 | public static F32 Exp2Fast(F32 a) { return FromRaw(Fixed32.Exp2Fast(a.Raw)); }
149 | public static F32 Exp2Fastest(F32 a) { return FromRaw(Fixed32.Exp2Fastest(a.Raw)); }
150 | public static F32 Log(F32 a) { return FromRaw(Fixed32.Log(a.Raw)); }
151 | public static F32 LogFast(F32 a) { return FromRaw(Fixed32.LogFast(a.Raw)); }
152 | public static F32 LogFastest(F32 a) { return FromRaw(Fixed32.LogFastest(a.Raw)); }
153 | public static F32 Log2(F32 a) { return FromRaw(Fixed32.Log2(a.Raw)); }
154 | public static F32 Log2Fast(F32 a) { return FromRaw(Fixed32.Log2Fast(a.Raw)); }
155 | public static F32 Log2Fastest(F32 a) { return FromRaw(Fixed32.Log2Fastest(a.Raw)); }
156 |
157 | public static F32 Sin(F32 a) { return FromRaw(Fixed32.Sin(a.Raw)); }
158 | public static F32 SinFast(F32 a) { return FromRaw(Fixed32.SinFast(a.Raw)); }
159 | public static F32 SinFastest(F32 a) { return FromRaw(Fixed32.SinFastest(a.Raw)); }
160 | public static F32 Cos(F32 a) { return FromRaw(Fixed32.Cos(a.Raw)); }
161 | public static F32 CosFast(F32 a) { return FromRaw(Fixed32.CosFast(a.Raw)); }
162 | public static F32 CosFastest(F32 a) { return FromRaw(Fixed32.CosFastest(a.Raw)); }
163 | public static F32 Tan(F32 a) { return FromRaw(Fixed32.Tan(a.Raw)); }
164 | public static F32 TanFast(F32 a) { return FromRaw(Fixed32.TanFast(a.Raw)); }
165 | public static F32 TanFastest(F32 a) { return FromRaw(Fixed32.TanFastest(a.Raw)); }
166 | public static F32 Asin(F32 a) { return FromRaw(Fixed32.Asin(a.Raw)); }
167 | public static F32 AsinFast(F32 a) { return FromRaw(Fixed32.AsinFast(a.Raw)); }
168 | public static F32 AsinFastest(F32 a) { return FromRaw(Fixed32.AsinFastest(a.Raw)); }
169 | public static F32 Acos(F32 a) { return FromRaw(Fixed32.Acos(a.Raw)); }
170 | public static F32 AcosFast(F32 a) { return FromRaw(Fixed32.AcosFast(a.Raw)); }
171 | public static F32 AcosFastest(F32 a) { return FromRaw(Fixed32.AcosFastest(a.Raw)); }
172 | public static F32 Atan(F32 a) { return FromRaw(Fixed32.Atan(a.Raw)); }
173 | public static F32 AtanFast(F32 a) { return FromRaw(Fixed32.AtanFast(a.Raw)); }
174 | public static F32 AtanFastest(F32 a) { return FromRaw(Fixed32.AtanFastest(a.Raw)); }
175 | public static F32 Atan2(F32 y, F32 x) { return FromRaw(Fixed32.Atan2(y.Raw, x.Raw)); }
176 | public static F32 Atan2Fast(F32 y, F32 x) { return FromRaw(Fixed32.Atan2Fast(y.Raw, x.Raw)); }
177 | public static F32 Atan2Fastest(F32 y, F32 x) { return FromRaw(Fixed32.Atan2Fastest(y.Raw, x.Raw)); }
178 | public static F32 Pow(F32 a, F32 b) { return FromRaw(Fixed32.Pow(a.Raw, b.Raw)); }
179 | public static F32 PowFast(F32 a, F32 b) { return FromRaw(Fixed32.PowFast(a.Raw, b.Raw)); }
180 | public static F32 PowFastest(F32 a, F32 b) { return FromRaw(Fixed32.PowFastest(a.Raw, b.Raw)); }
181 |
182 | public static F32 Min(F32 a, F32 b) { return FromRaw(Fixed32.Min(a.Raw, b.Raw)); }
183 | public static F32 Max(F32 a, F32 b) { return FromRaw(Fixed32.Max(a.Raw, b.Raw)); }
184 | public static F32 Clamp(F32 a, F32 min, F32 max) { return FromRaw(Fixed32.Clamp(a.Raw, min.Raw, max.Raw)); }
185 | public static F32 Clamp01(F32 a) { return FromRaw(Fixed32.Clamp(a.Raw, Fixed32.Zero, Fixed32.One)); }
186 |
187 | public static F32 Lerp(F32 a, F32 b, F32 t)
188 | {
189 | int tb = t.Raw;
190 | int ta = Fixed32.One - tb;
191 | return FromRaw(Fixed32.Mul(a.Raw, ta) + Fixed32.Mul(b.Raw, tb));
192 | }
193 |
194 | public bool Equals(F32 other)
195 | {
196 | return (Raw == other.Raw);
197 | }
198 |
199 | public override bool Equals(object obj)
200 | {
201 | if (!(obj is F32))
202 | return false;
203 | return ((F32)obj).Raw == Raw;
204 | }
205 |
206 | public int CompareTo(F32 other)
207 | {
208 | if (Raw < other.Raw) return -1;
209 | if (Raw > other.Raw) return +1;
210 | return 0;
211 | }
212 |
213 | public override string ToString()
214 | {
215 | return Fixed32.ToString(Raw);
216 | }
217 |
218 | public override int GetHashCode()
219 | {
220 | return Raw;
221 | }
222 |
223 | int IComparable.CompareTo(object obj)
224 | {
225 | if (obj is F32 other)
226 | return CompareTo(other);
227 | else if (obj is null)
228 | return 1;
229 | // don't allow comparisons with other numeric or non-numeric types.
230 | throw new ArgumentException("F32 can only be compared against another F32.");
231 | }
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/Polyfit/remez.py:
--------------------------------------------------------------------------------
1 | #
2 | # FixPointCS
3 | #
4 | # Copyright(c) Jere Sanisalo, Petri Kero
5 | #
6 | # Permission is hereby granted, free of charge, to any person obtaining a copy
7 | # of this software and associated documentation files (the "Software"), to deal
8 | # in the Software without restriction, including without limitation the rights
9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | # copies of the Software, and to permit persons to whom the Software is
11 | # furnished to do so, subject to the following conditions:
12 | #
13 | # The above copyright notice and this permission notice shall be included in all
14 | # copies or substantial portions of the Software.
15 | #
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | # SOFTWARE.
23 | #
24 |
25 | # Implementation of Remez algorithm for optimal polynomial fitting for usage in
26 | # function approximations.
27 |
28 | # References:
29 | # - https://github.com/samhocevar/lolremez
30 | # - https://www.cs.ox.ac.uk/files/1948/NA-08-20.pdf
31 | # - https://news.ycombinator.com/item?id=10115336
32 | # - http://sollya.gforge.inria.fr/
33 |
34 | import math
35 | import random
36 | import mpmath as mp
37 | import numpy.polynomial as P
38 | import numpy.polynomial.chebyshev as C
39 |
40 | # set precision
41 | #dps = 30
42 | dps = 120
43 | mp.mp.dps = dps
44 | mp.mp.pretty = True
45 |
46 | def vzeros(size):
47 | return mp.matrix([0.0] * size)
48 |
49 | def vones(size):
50 | return mp.matrix([1.0] * size)
51 |
52 | def nth(size):
53 | return mp.matrix([0.0] * size + [1.0])
54 |
55 | class Remez:
56 | def __init__(self, func, weightFunc, domain, order):
57 | #print('Remez()')
58 | self.func = func
59 | self.weightFunc = weightFunc
60 | self.domain = domain
61 | self.order = order
62 | self.limit = mp.mpf(f'1e-{dps}')
63 | self.errorThreshold = mp.mpf(f'1e-{dps}')
64 | self.initRemez()
65 |
66 | def initRemez(self):
67 | #print('Remez.init()')
68 |
69 | # constants for domain
70 | (xMin, xMax) = self.domain
71 | self.k1 = (xMax + xMin) * 0.5
72 | self.k2 = (xMax - xMin) * 0.5
73 |
74 | # initial estimates for function roots (where error == 0.0)
75 | size = self.order + 1
76 | roots = vzeros(size)
77 | fxn = vzeros(size)
78 | # \todo [petri] use linspace
79 | for i in range(size):
80 | roots[i] = (2.0 * i - self.order) / size
81 | fxn[i] = self.evalFunc(roots[i])
82 |
83 | # build matrix of Chebyshev evaluations
84 | system = mp.zeros(size)
85 | for order in range(size):
86 | for i in range(size):
87 | system[i,order] = mp.chebyt(order, roots[i])
88 |
89 | # solve system
90 | solved = system ** -1
91 |
92 | # compute Chebyshev weights of new approximation
93 | weights = vzeros(size)
94 | for n in range(size):
95 | weights[n] = mp.fdot(solved[n,:], fxn)
96 | #print(f' weights: {weights.T}')
97 |
98 | # store roots & weights
99 | self.roots = roots
100 | self.weights = weights
101 | self.maxError = 1000.0
102 |
103 | # estimate error
104 | #est = [self.evalEstimate(x) for x in roots]
105 | #print(' est:', est)
106 | #print(' fxn:', fxn.T)
107 |
108 | def step(self):
109 | (control, maxErr) = self.findExtrema(self.roots)
110 | #print(f' maxErr: {maxErr} ({-math.log(maxErr, 2.0)} bits)')
111 | # for x in control:
112 | # print(f' ctrl: {x}')
113 | self.weights = self.remezStep(control)
114 |
115 | # update error
116 | errDiff = mp.fabs(maxErr - self.maxError)
117 | self.maxError = maxErr
118 |
119 | if errDiff < self.errorThreshold:
120 | return True
121 |
122 | self.roots = self.findRoots(control)
123 | return False
124 |
125 | # for (ax, bx, rx, rerr) in scannedRoots:
126 | # print(f' scanned: {rx} in [{ax, bx}]')
127 |
128 | # scannedRoots = self.scanRoots(1000)
129 | # for ndx in range(len(self.roots)):
130 | # x = self.roots[ndx]
131 | # (ax, bx, rx, rerr) = scannedRoots[ndx]
132 | # print(f' root: {x} [{ax}, {bx}] {rx}')
133 | # if x < ax or x > bx:
134 | # print(' ROOT FIND MISMATCH')
135 |
136 | def findRoot(self, ax, bx, aerr, berr):
137 | cx = (ax + bx) * 0.5
138 | cerr = self.evalEstimate(cx) - self.evalFunc(cx)
139 |
140 | if cerr == 0.0 or (bx - ax) <= self.limit:
141 | return (cx, cerr)
142 | else:
143 | if (aerr < 0.0 and cerr < 0.0) or (aerr > 0.0 and cerr > 0.0):
144 | (ax, aerr) = (cx, cerr)
145 | else:
146 | (bx, berr) = (cx, cerr)
147 | return self.findRoot(ax, bx, aerr, berr)
148 |
149 | def findRoots(self, control):
150 | roots = vzeros(self.order + 1)
151 | for n in range(self.order + 1):
152 | ax = control[n]
153 | bx = control[n+1]
154 | aerr = self.evalEstimate(ax) - self.evalFunc(ax)
155 | berr = self.evalEstimate(bx) - self.evalFunc(bx)
156 | (rx, rerr) = self.findRoot(ax, bx, aerr, berr)
157 | assert(rx >= ax and rx <= bx) # root must be inside search range
158 | roots[n] = rx
159 | #print(f' root[{n}]: {rx} <{rerr}> || {ax} {bx}')
160 | return roots
161 |
162 | def remezStep(self, control):
163 | # eval at control points
164 | fxn = mp.matrix([self.evalFunc(c) for c in control])
165 |
166 | # create linear system with chebyshev polynomials
167 | size = self.order + 2
168 | system = mp.zeros(size)
169 | for n in range(self.order + 1):
170 | for i in range(self.order + 2):
171 | system[i,n] = mp.chebyt(n, control[i])
172 |
173 | # last column is oscillating error
174 | for i in range(size):
175 | sign = -1 if ((i & 1) == 0) else +1
176 | scale = 0.0 if i in [0, size-1] else 1.0
177 | system[i,size-1] = sign * scale * mp.fabs(self.evalWeight(control[i]))
178 | #print(system)
179 |
180 | # solve the system
181 | solved = system ** -1
182 | #print(solved)
183 |
184 | # compute polynomial estimate (as Chebyshev weights)
185 | weights = vzeros(size-1)
186 | for n in range(size-1):
187 | weights[n] = mp.fdot(solved[n,:], fxn)
188 | #print(f' weights: {weights}')
189 |
190 | # estimate error
191 | # self.weights = weights
192 | # est = [self.evalEstimate(x) for x in control]
193 | # print(' est:', est)
194 | # print(' fxn:', fxn.T)
195 |
196 | return weights
197 |
198 | def findExtremum(self, ax, bx, cx, aerr, berr, cerr):
199 | # \todo [petri] implement golden ratio search?
200 |
201 | dx = (ax + bx) * 0.5
202 | derr = self.evalError(dx)
203 |
204 | # update coords based on error
205 | if derr < cerr:
206 | if dx > cx:
207 | (bx, berr) = (dx, derr)
208 | else:
209 | (ax, aerr) = (dx, derr)
210 | else:
211 | if dx > cx:
212 | (ax, aerr) = (cx, cerr)
213 | else:
214 | (bx, berr) = (cx, cerr)
215 | (cx, cerr) = (dx, derr)
216 |
217 | # check if limit reached
218 | if (bx - ax) <= self.limit:
219 | #print(f' fin: {cx} <{cerr}> || {ax} {bx}')
220 | return (cx, cerr)
221 | else:
222 | #print(f' cur: {cx} <{cerr}>')
223 | return self.findExtremum(ax, bx, cx, aerr, berr, cerr)
224 |
225 | def findExtrema(self, roots):
226 | control = vzeros(self.order + 2)
227 | control[0] = -1
228 | control[self.order + 1] = 1
229 |
230 | maxErr = 0.0
231 |
232 | for n in range(self.order):
233 | ax = roots[n]
234 | bx = roots[n+1]
235 | cx = ax + (bx - ax) * random.uniform(0.4, 0.6)
236 | aerr = self.evalError(ax)
237 | berr = self.evalError(bx)
238 | cerr = self.evalError(cx)
239 | #print(f' find[{n}]: {ax}, {bx}')
240 | (rx, rerr) = self.findExtremum(ax, bx, cx, aerr, berr, cerr)
241 | assert(rx >= ax and rx <= bx) # extremum must be inside search range
242 | #print(f' extrema[{n}]: {rx} <{rerr}> || {ax} {bx}')
243 |
244 | control[n + 1] = rx
245 | maxErr = max(maxErr, rerr)
246 |
247 | return (control, maxErr)
248 |
249 | def scanRoots(self, numSteps):
250 | found = []
251 | coords = mp.linspace(-1.0, 1.0, numSteps)
252 | for ndx in range(len(coords)-1):
253 | ax = coords[ndx]
254 | bx = coords[ndx+1]
255 | aerr = self.evalEstimate(ax) - self.evalFunc(ax)
256 | berr = self.evalEstimate(bx) - self.evalFunc(bx)
257 | #print(f'bucket: {ax} <{aerr}>')
258 | #print(f' to: {bx} <{berr}>')
259 | if mp.sign(aerr) != mp.sign(berr):
260 | (rx, rerr) = self.findRoot(ax, bx, aerr, berr)
261 | #print(f' root in range: [{ax}, {bx}]: {rx} <{rerr}>')
262 | found.append((ax, bx, rx, rerr))
263 | return found
264 |
265 | def evalFunc(self, x):
266 | return self.func(x * self.k2 + self.k1)
267 |
268 | def evalWeight(self, x):
269 | return self.weightFunc(x * self.k2 + self.k1)
270 |
271 | def evalEstimate(self, x):
272 | sum = mp.mpf(0.0)
273 | for i in range(len(self.weights)):
274 | sum += self.weights[i] * mp.chebyt(i, x)
275 | return sum
276 |
277 | def evalError(self, x):
278 | return mp.fabs(self.evalEstimate(x) - self.evalFunc(x)) / self.evalWeight(x)
279 |
280 | # Main
281 |
282 | MAX_REMEZ_ITER = 10
283 |
284 | # convert polynomials to assume input in [0.0, 1.0] range (instead of [-1.0, 1.0])
285 | rebase = P.Polynomial([-1.0, 2.0])
286 |
287 | def remezFit(name, func, weightFunc, domain, order):
288 | remez = Remez(func, weightFunc, domain, order)
289 |
290 | for iter in range(MAX_REMEZ_ITER):
291 | #print(f'ITER {iter}:')
292 | if remez.step():
293 | break
294 |
295 | return remez
296 |
297 | def remezToPoly(remez):
298 | cheby = C.Chebyshev(remez.weights)
299 | p = cheby.convert(kind=P.Polynomial)
300 | p = p(rebase)
301 | # (x0, x1) = remez.domain
302 | # p = p(P.Polynomial([-x0 / (x1 - x0), 1.0 / (x1 - x0)]))
303 | return p
304 |
305 | def writeCoefficients(file, name, maxError, order, segments):
306 | file.write('\n')
307 |
308 | if len(segments) == 1:
309 | precision = -math.log(maxError, 2.0)
310 | file.write(f'\t\t// Precision: {precision:.2f} bits\n')
311 | file.write('\t\t[MethodImpl(AggressiveInlining)]\n')
312 | file.write(f'\t\tpublic static int {name}Poly{order}(int a)\n')
313 | file.write('\t\t{\n')
314 |
315 | # get polynomial
316 | p = remezToPoly(segments[0])
317 |
318 | for ndx in reversed(range(order + 1)):
319 | c = p.coef[ndx]
320 | ic = (int)(c * (1 << 30) + 0.5)
321 | # file.write(f'\t\t\tconst int C{ndx} = {ic}; // {c}\n')
322 |
323 | if ndx == len(p.coef)-1:
324 | file.write(f'\t\t\tint y = Qmul30(a, {ic}); // {c}\n')
325 | elif ndx > 0:
326 | file.write(f'\t\t\ty = Qmul30(a, y + {ic}); // {c}\n')
327 | else:
328 | file.write(f'\t\t\ty = y + {ic}; // {c}\n')
329 |
330 | file.write('\t\t\treturn y;\n')
331 |
332 | else:
333 | numSegments = len(segments)
334 | precision = -math.log(maxError, 2.0)
335 | funcName = f'{name}Poly{order}Lut{numSegments}'
336 | tableName = f'{funcName}Table'
337 |
338 | # write constant table
339 | file.write(f'\t\tprivate static readonly int[] {tableName} =\n')
340 | file.write('\t\t{\n')
341 |
342 | for remez in segments:
343 | # get polynomial
344 | p = remezToPoly(remez)
345 | # map [x0, x1] to [0.0, 1.0]
346 | (x0, x1) = remez.domain
347 | p = p(P.Polynomial([-x0 / (x1 - x0), 1.0 / (x1 - x0)]))
348 | # write coefficients
349 | coefs = ' '.join(f'{int(c * (1<<30) + 0.5)},' for c in reversed(p.coef))
350 | file.write(f'\t\t\t{coefs}\n')
351 |
352 | file.write('\t\t};\n')
353 |
354 | # write function
355 | file.write('\n')
356 | file.write(f'\t\t// Precision: {precision:.2f} bits\n')
357 | file.write('\t\t[MethodImpl(AggressiveInlining)]\n')
358 | file.write(f'\t\tpublic static int {funcName}(int a)\n')
359 | file.write('\t\t{\n')
360 |
361 | segmentBits = int(math.log2(numSegments))
362 | file.write(f'\t\t\tint offset = (a >> {30 - segmentBits}) * {order + 1};\n')
363 |
364 | for ndx in reversed(range(order + 1)):
365 | if ndx == order:
366 | file.write(f'\t\t\tint y = Qmul30(a, {tableName}[offset + {0}]);\n')
367 | elif ndx > 0:
368 | file.write(f'\t\t\ty = Qmul30(a, y + {tableName}[offset + {order - ndx}]);\n')
369 | else:
370 | file.write(f'\t\t\ty = y + {tableName}[offset + {order}];\n')
371 |
372 | file.write('\t\t\treturn y;\n')
373 |
374 | file.write('\t\t}\n')
375 | file.flush()
376 |
377 | def remezFitSegmented(name, func, weightFunc, domain, numSegments, order):
378 | (xMin, xMax) = domain
379 | maxError = 0.0
380 |
381 | segments = []
382 | for segment in range(numSegments):
383 | x0 = xMin + (xMax - xMin) * mp.mpf(segment) / numSegments
384 | x1 = xMin + (xMax - xMin) * mp.mpf(segment + 1) / numSegments
385 | remez = remezFit(name, func, weightFunc, (x0, x1), order)
386 | #print(f' segment[{segment}]: {-math.log(remez.maxError, 2.0):.2f} bits [{x0} .. {x1}]')
387 | maxError = max(maxError, remez.maxError)
388 | segments.append(remez)
389 |
390 | return (maxError, segments)
391 |
392 | epsilon = mp.mpf(f'1e-{dps//3}')
393 |
394 | # Implementation of sin() with the assumption that input has been normalized to [-1.0, 1.0] range
395 | # and squared. Also assumes the output will be multiplied by x once more (to make it odd).
396 | # See: https://github.com/samhocevar/lolremez/wiki/Tutorial-3-of-5:-changing-variables-for-simpler-polynomials
397 | def sinSqr(x):
398 | x = x * 0.25 * mp.pi * mp.pi
399 | xx = mp.sqrt(x)
400 | y = mp.sin(xx) / xx
401 | return y * 0.5*mp.pi
402 |
403 | FUNCS = [
404 | ('Exp', lambda x: mp.exp(x), lambda x: mp.exp(x), (0.0, 1.0)),
405 | ('Exp2', lambda x: 2.0**x, lambda x: 2.0**x, (0.0, 1.0)),
406 | ('Log', lambda x: mp.log(x+1), lambda x: mp.log(x+1) * (mp.log(2.0) - mp.log(x+1)), (epsilon, 1.0-epsilon)),
407 | ('Log2', lambda x: mp.log(x+1, 2.0), lambda x: mp.log(x+1, 2.0) * (1 - mp.log(x+1, 2.0)), (epsilon, 1.0-epsilon)),
408 | ('Rcp', lambda x: 1.0 / (x+1), lambda x: 1.0 / (x+1), (0.0, 1.0)),
409 | ('Sqrt', lambda x: mp.sqrt(x+1), lambda x: mp.sqrt(x+1), (0.0, 1.0)),
410 | ('RSqrt', lambda x: 1.0 / mp.sqrt(x+1), lambda x: 1.0 / mp.sqrt(x+1), (0.0, 1.0)),
411 | ('Atan', lambda x: mp.atan(x), lambda x: mp.atan(x), (0.0, 1.0)),
412 | ('Sin', sinSqr, sinSqr, (epsilon, 1.0)),
413 | ]
414 |
415 | def generateCode():
416 | with open(f'fitted.txt', 'w', newline='\n') as file:
417 | file.write('\t// AUTO-GENERATED POLYNOMIAL APPROXIMATIONS\n')
418 | file.write('\n')
419 | file.write('\tpublic static class Util\n')
420 | file.write('\t{\n')
421 |
422 | for (name, func, weightFunc, domain) in FUNCS:
423 | print()
424 | print(f'{name}():')
425 | orderRange = range(3, 10) if name != 'Sin' else range(1, 5)
426 | # orderRange = range(3, 6) if name != 'sin' else range(1, 4)
427 | for order in orderRange:
428 | remez = remezFit(name, func, weightFunc, domain, order)
429 | print(f' {name}<{order}>(): {-math.log(remez.maxError, 2.0):.2f} bits')
430 | writeCoefficients(file, name, remez.maxError, order, [remez])
431 |
432 | for numSegments in [4, 8, 16, 32]:
433 | orders = [2, 3, 4, 5] if name != 'Sin' else [1, 2, 3]
434 | # orders = [3, 4] if name != 'sin' else [1, 2, 3]
435 | for order in orders:
436 | (maxError, segments) = remezFitSegmented(name, func, weightFunc, domain, numSegments, order)
437 | print(f' {name}<{order}>[{numSegments}](): {-math.log(maxError, 2.0):.2f} bits')
438 | writeCoefficients(file, name, maxError, order, segments)
439 |
440 | file.write('\t}\n')
441 |
442 | def plotError():
443 | print('Plotting error..')
444 | #func = lambda x: 1.0 / (x + 1.0)
445 | func = sinSqr
446 | remez = remezFit('Sin', func, func, (epsilon, 1.0), 4)
447 | # remez = remezFit('Rcp', func, func, (1.0, 2.0), 3)
448 | print('err:', remez.maxError)
449 | est = remezToPoly(remez)
450 | err = lambda x: (func(x) - est(x)) / func(x)
451 | mp.plot([err], [0.0, 1.0])
452 | #mp.plot([func], [0.0, 1.0])
453 |
454 | # Main
455 |
456 | generateCode()
457 | #plotError()
458 |
--------------------------------------------------------------------------------
/Examples/FixedTracer/DoubleTracer.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System;
3 | using System.Collections.Generic;
4 |
5 | // Based on https://blogs.msdn.microsoft.com/lukeh/2007/04/03/a-ray-tracer-in-c3-0/
6 |
7 | namespace DoubleTracer
8 | {
9 | public class RayTracer
10 | {
11 | private int screenWidth;
12 | private int screenHeight;
13 | private const int MaxDepth = 5;
14 |
15 | public RayTracer(int screenWidth, int screenHeight)
16 | {
17 | this.screenWidth = screenWidth;
18 | this.screenHeight = screenHeight;
19 | }
20 |
21 | public static System.Drawing.Color[,] RenderDefaultScene(int width, int height)
22 | {
23 | RayTracer tracer = new RayTracer(width, height);
24 | return tracer.Render(tracer.DefaultScene);
25 | }
26 |
27 | private ISect IntersectRay(Ray ray, Scene scene)
28 | {
29 | ISect best = null;
30 | foreach (SceneObject obj in scene.Things)
31 | {
32 | ISect isect = obj.Intersect(ray);
33 | if (isect != null)
34 | {
35 | if (best == null || isect.Dist < best.Dist)
36 | best = isect;
37 | }
38 | }
39 |
40 | return best;
41 | }
42 |
43 | private double TestRay(Ray ray, Scene scene)
44 | {
45 | ISect isect = IntersectRay(ray, scene);
46 | if (isect == null)
47 | return 0;
48 | return isect.Dist;
49 | }
50 |
51 | private Color TraceRay(Ray ray, Scene scene, int depth)
52 | {
53 | ISect isect = IntersectRay(ray, scene);
54 | if (isect == null)
55 | return Color.Background;
56 | return Shade(isect, scene, depth);
57 | }
58 |
59 | private Color GetNaturalColor(SceneObject thing, Vector pos, Vector norm, Vector rd, Scene scene)
60 | {
61 | Color ret = Color.Make(0, 0, 0);
62 | foreach (Light light in scene.Lights)
63 | {
64 | Vector ldis = Vector.Minus(light.Pos, pos);
65 | Vector livec = Vector.Norm(ldis);
66 | double neatIsect = TestRay(new Ray() { Start = pos, Dir = livec }, scene);
67 | bool isInShadow = !((neatIsect > Vector.Mag(ldis)) || (neatIsect == 0));
68 | if (!isInShadow)
69 | {
70 | double illum = Vector.Dot(livec, norm);
71 | Color lcolor = illum > 0 ? Color.Times(illum, light.Color) : Color.Make(0, 0, 0);
72 | double specular = Vector.Dot(livec, Vector.Norm(rd));
73 | Color scolor = specular > 0 ? Color.Times(Math.Pow(specular, thing.Surface.Roughness), light.Color) : Color.Make(0, 0, 0);
74 | ret = Color.Plus(ret, Color.Plus(Color.Times(thing.Surface.Diffuse(pos), lcolor),
75 | Color.Times(thing.Surface.Specular(pos), scolor)));
76 | }
77 | }
78 | return ret;
79 | }
80 |
81 | private Color GetReflectionColor(SceneObject thing, Vector pos, Vector norm, Vector rd, Scene scene, int depth)
82 | {
83 | return Color.Times(thing.Surface.Reflect(pos), TraceRay(new Ray() { Start = pos, Dir = rd }, scene, depth + 1));
84 | }
85 |
86 | private Color Shade(ISect isect, Scene scene, int depth)
87 | {
88 | var d = isect.Ray.Dir;
89 | var pos = Vector.Plus(Vector.Times(isect.Dist, isect.Ray.Dir), isect.Ray.Start);
90 | var normal = isect.Thing.Normal(pos);
91 | var reflectDir = Vector.Minus(d, Vector.Times(2 * Vector.Dot(normal, d), normal));
92 | Color ret = Color.DefaultColor;
93 | ret = Color.Plus(ret, GetNaturalColor(isect.Thing, pos, normal, reflectDir, scene));
94 | if (depth >= MaxDepth)
95 | {
96 | return Color.Plus(ret, Color.Make(.5, .5, .5));
97 | }
98 | return Color.Plus(ret, GetReflectionColor(isect.Thing, Vector.Plus(pos, Vector.Times(.001, reflectDir)), normal, reflectDir, scene, depth));
99 | }
100 |
101 | private double RecenterX(double x)
102 | {
103 | return (x - (screenWidth / 2.0)) / (2.0 * screenWidth);
104 | }
105 | private double RecenterY(double y)
106 | {
107 | return -(y - (screenHeight / 2.0)) / (2.0 * screenHeight);
108 | }
109 |
110 | private Vector GetPoint(double x, double y, Camera camera)
111 | {
112 | return Vector.Norm(Vector.Plus(camera.Forward, Vector.Plus(Vector.Times(RecenterX(x), camera.Right),
113 | Vector.Times(RecenterY(y), camera.Up))));
114 | }
115 |
116 | internal System.Drawing.Color[,] Render(Scene scene)
117 | {
118 | System.Drawing.Color[,] pixels = new System.Drawing.Color[screenWidth, screenHeight];
119 | for (int y = 0; y < screenHeight; y++)
120 | {
121 | for (int x = 0; x < screenWidth; x++)
122 | {
123 | Color color = TraceRay(new Ray() { Start = scene.Camera.Pos, Dir = GetPoint(x, y, scene.Camera) }, scene, 0);
124 | pixels[x, y] = color.ToDrawingColor();
125 | }
126 | }
127 | return pixels;
128 | }
129 |
130 | internal readonly Scene DefaultScene =
131 | new Scene()
132 | {
133 | Things = new SceneObject[] {
134 | new Plane() {
135 | Norm = Vector.Make(0,1,0),
136 | Offset = 0,
137 | Surface = Surfaces.CheckerBoard
138 | },
139 | new Sphere() {
140 | Center = Vector.Make(0,1,0),
141 | Radius = 1,
142 | Surface = Surfaces.Shiny
143 | },
144 | new Sphere() {
145 | Center = Vector.Make(-1,.5,1.5),
146 | Radius = .5,
147 | Surface = Surfaces.Shiny
148 | }},
149 | Lights = new Light[] {
150 | new Light() {
151 | Pos = Vector.Make(-2,2.5,0),
152 | Color = Color.Make(.49,.07,.07)
153 | },
154 | new Light() {
155 | Pos = Vector.Make(1.5,2.5,1.5),
156 | Color = Color.Make(.07,.07,.49)
157 | },
158 | new Light() {
159 | Pos = Vector.Make(1.5,2.5,-1.5),
160 | Color = Color.Make(.07,.49,.071)
161 | },
162 | new Light() {
163 | Pos = Vector.Make(0,3.5,0),
164 | Color = Color.Make(.21,.21,.35)
165 | }},
166 | Camera = Camera.Create(Vector.Make(3, 2, 4), Vector.Make(-1, .5, 0))
167 | };
168 | }
169 |
170 | static class Surfaces
171 | {
172 | // Only works with X-Z plane.
173 | public static readonly Surface CheckerBoard =
174 | new Surface()
175 | {
176 | Diffuse = pos => ((Math.Floor(pos.Z) + Math.Floor(pos.X)) % 2 != 0)
177 | ? Color.Make(1, 1, 1)
178 | : Color.Make(0, 0, 0),
179 | Specular = pos => Color.Make(1, 1, 1),
180 | Reflect = pos => ((Math.Floor(pos.Z) + Math.Floor(pos.X)) % 2 != 0)
181 | ? .1
182 | : .7,
183 | Roughness = 150
184 | };
185 |
186 |
187 | public static readonly Surface Shiny =
188 | new Surface()
189 | {
190 | Diffuse = pos => Color.Make(1, 1, 1),
191 | Specular = pos => Color.Make(.5, .5, .5),
192 | Reflect = pos => .6,
193 | Roughness = 50
194 | };
195 | }
196 |
197 | struct Vector
198 | {
199 | public double X;
200 | public double Y;
201 | public double Z;
202 |
203 | public Vector(double x, double y, double z) { X = x; Y = y; Z = z; }
204 | public Vector(string str)
205 | {
206 | string[] nums = str.Split(',');
207 | if (nums.Length != 3) throw new ArgumentException();
208 | X = double.Parse(nums[0]);
209 | Y = double.Parse(nums[1]);
210 | Z = double.Parse(nums[2]);
211 | }
212 | public static Vector Make(double x, double y, double z) { return new Vector(x, y, z); }
213 | public static Vector Times(double n, Vector v)
214 | {
215 | return new Vector(v.X * n, v.Y * n, v.Z * n);
216 | }
217 | public static Vector Minus(Vector v1, Vector v2)
218 | {
219 | return new Vector(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z);
220 | }
221 | public static Vector Plus(Vector v1, Vector v2)
222 | {
223 | return new Vector(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
224 | }
225 | public static double Dot(Vector v1, Vector v2)
226 | {
227 | return (v1.X * v2.X) + (v1.Y * v2.Y) + (v1.Z * v2.Z);
228 | }
229 | public static double Mag(Vector v) { return Math.Sqrt(Dot(v, v)); }
230 | public static Vector Norm(Vector v)
231 | {
232 | double mag = Mag(v);
233 | double div = mag == 0 ? double.PositiveInfinity : 1 / mag;
234 | return Times(div, v);
235 | }
236 | public static Vector Cross(Vector v1, Vector v2)
237 | {
238 | return new Vector(((v1.Y * v2.Z) - (v1.Z * v2.Y)),
239 | ((v1.Z * v2.X) - (v1.X * v2.Z)),
240 | ((v1.X * v2.Y) - (v1.Y * v2.X)));
241 | }
242 | public static bool Equals(Vector v1, Vector v2)
243 | {
244 | return (v1.X == v2.X) && (v1.Y == v2.Y) && (v1.Z == v2.Z);
245 | }
246 | }
247 |
248 | public struct Color
249 | {
250 | public double R;
251 | public double G;
252 | public double B;
253 |
254 | public Color(double r, double g, double b) { R = r; G = g; B = b; }
255 | public Color(string str)
256 | {
257 | string[] nums = str.Split(',');
258 | if (nums.Length != 3) throw new ArgumentException();
259 | R = double.Parse(nums[0]);
260 | G = double.Parse(nums[1]);
261 | B = double.Parse(nums[2]);
262 | }
263 |
264 | public static Color Make(double r, double g, double b) { return new Color(r, g, b); }
265 |
266 | public static Color Times(double n, Color v)
267 | {
268 | return new Color(n * v.R, n * v.G, n * v.B);
269 | }
270 | public static Color Times(Color v1, Color v2)
271 | {
272 | return new Color(v1.R * v2.R, v1.G * v2.G, v1.B * v2.B);
273 | }
274 |
275 | public static Color Plus(Color v1, Color v2)
276 | {
277 | return new Color(v1.R + v2.R, v1.G + v2.G, v1.B + v2.B);
278 | }
279 | public static Color Minus(Color v1, Color v2)
280 | {
281 | return new Color(v1.R - v2.R, v1.G - v2.G, v1.B - v2.B);
282 | }
283 |
284 | public static readonly Color Background = Make(0, 0, 0);
285 | public static readonly Color DefaultColor = Make(0, 0, 0);
286 |
287 | public double Legalize(double d)
288 | {
289 | return d > 1 ? 1 : d;
290 | }
291 |
292 | internal System.Drawing.Color ToDrawingColor()
293 | {
294 | return System.Drawing.Color.FromArgb((int)(Legalize(R) * 255), (int)(Legalize(G) * 255), (int)(Legalize(B) * 255));
295 | }
296 |
297 | }
298 |
299 | struct Ray
300 | {
301 | public Vector Start;
302 | public Vector Dir;
303 | }
304 |
305 | class ISect
306 | {
307 | public SceneObject Thing;
308 | public Ray Ray;
309 | public double Dist;
310 | }
311 |
312 | class Surface
313 | {
314 | public Func Diffuse;
315 | public Func Specular;
316 | public Func Reflect;
317 | public double Roughness;
318 | }
319 |
320 | class Camera
321 | {
322 | public Vector Pos;
323 | public Vector Forward;
324 | public Vector Up;
325 | public Vector Right;
326 |
327 | public static Camera Create(Vector pos, Vector lookAt)
328 | {
329 | Vector forward = Vector.Norm(Vector.Minus(lookAt, pos));
330 | Vector down = new Vector(0, -1, 0);
331 | Vector right = Vector.Times(1.5, Vector.Norm(Vector.Cross(forward, down)));
332 | Vector up = Vector.Times(1.5, Vector.Norm(Vector.Cross(forward, right)));
333 |
334 | return new Camera() { Pos = pos, Forward = forward, Up = up, Right = right };
335 | }
336 | }
337 |
338 | class Light
339 | {
340 | public Vector Pos;
341 | public Color Color;
342 | }
343 |
344 | abstract class SceneObject
345 | {
346 | public Surface Surface;
347 | public abstract ISect Intersect(Ray ray);
348 | public abstract Vector Normal(Vector pos);
349 | }
350 |
351 | class Sphere : SceneObject
352 | {
353 | public Vector Center;
354 | public double Radius;
355 |
356 | public override ISect Intersect(Ray ray)
357 | {
358 | Vector eo = Vector.Minus(Center, ray.Start);
359 | double v = Vector.Dot(eo, ray.Dir);
360 | double dist;
361 | if (v < 0)
362 | {
363 | dist = 0;
364 | }
365 | else
366 | {
367 | double disc = Math.Pow(Radius, 2) - (Vector.Dot(eo, eo) - Math.Pow(v, 2));
368 | dist = disc < 0 ? 0 : v - Math.Sqrt(disc);
369 | }
370 | if (dist == 0) return null;
371 | return new ISect()
372 | {
373 | Thing = this,
374 | Ray = ray,
375 | Dist = dist
376 | };
377 | }
378 |
379 | public override Vector Normal(Vector pos)
380 | {
381 | return Vector.Norm(Vector.Minus(pos, Center));
382 | }
383 | }
384 |
385 | class Plane : SceneObject
386 | {
387 | public Vector Norm;
388 | public double Offset;
389 |
390 | public override ISect Intersect(Ray ray)
391 | {
392 | double denom = Vector.Dot(Norm, ray.Dir);
393 | if (denom > 0) return null;
394 | return new ISect()
395 | {
396 | Thing = this,
397 | Ray = ray,
398 | Dist = (Vector.Dot(Norm, ray.Start) + Offset) / (-denom)
399 | };
400 | }
401 |
402 | public override Vector Normal(Vector pos)
403 | {
404 | return Norm;
405 | }
406 | }
407 |
408 | class Scene
409 | {
410 | public SceneObject[] Things;
411 | public Light[] Lights;
412 | public Camera Camera;
413 |
414 | public IEnumerable Intersect(Ray r)
415 | {
416 | return from thing in Things
417 | select thing.Intersect(r);
418 | }
419 | }
420 | }
421 |
--------------------------------------------------------------------------------
/Examples/FixMath/F64.cs:
--------------------------------------------------------------------------------
1 | //
2 | // FixPointCS
3 | //
4 | // Copyright(c) Jere Sanisalo, Petri Kero
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | using FixPointCS;
25 | using System;
26 | using System.Runtime.CompilerServices;
27 |
28 | namespace FixMath
29 | {
30 | ///
31 | /// Signed 32.32 fixed point value struct.
32 | ///
33 | [Serializable]
34 | public struct F64 : IComparable, IEquatable, IComparable
35 | {
36 | // Constants
37 | public static F64 Neg1 { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.Neg1); } }
38 | public static F64 Zero { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.Zero); } }
39 | public static F64 Half { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.Half); } }
40 | public static F64 One { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.One); } }
41 | public static F64 Two { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.Two); } }
42 | public static F64 Pi { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.Pi); } }
43 | public static F64 Pi2 { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.Pi2); } }
44 | public static F64 PiHalf { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.PiHalf); } }
45 | public static F64 E { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.E); } }
46 |
47 | public static F64 MinValue { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.MinValue); } }
48 | public static F64 MaxValue { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.MaxValue); } }
49 |
50 | // Raw fixed point value
51 | public long Raw;
52 |
53 | // Construction
54 | [MethodImpl(FixedUtil.AggressiveInlining)] public static F64 FromRaw(long raw) { F64 v; v.Raw = raw; return v; }
55 | [MethodImpl(FixedUtil.AggressiveInlining)] public static F64 FromInt(int v) { return FromRaw(Fixed64.FromInt(v)); }
56 | [MethodImpl(FixedUtil.AggressiveInlining)] public static F64 FromFloat(float v) { return FromRaw(Fixed64.FromFloat(v)); }
57 | [MethodImpl(FixedUtil.AggressiveInlining)] public static F64 FromDouble(double v) { return FromRaw(Fixed64.FromDouble(v)); }
58 | [MethodImpl(FixedUtil.AggressiveInlining)] public static F64 FromF32(F32 v) { return FromRaw((long)v.Raw << 16); }
59 |
60 | // Conversions
61 | public static int FloorToInt(F64 a) { return Fixed64.FloorToInt(a.Raw); }
62 | public static int CeilToInt(F64 a) { return Fixed64.CeilToInt(a.Raw); }
63 | public static int RoundToInt(F64 a) { return Fixed64.RoundToInt(a.Raw); }
64 | public float Float { get { return Fixed64.ToFloat(Raw); } }
65 | public double Double { get { return Fixed64.ToDouble(Raw); } }
66 | public F32 F32 { get { return F32.FromRaw((int)(Raw >> 16)); } }
67 |
68 | // Creates the fixed point number that's a divided by b.
69 | public static F64 Ratio(int a, int b) { return F64.FromRaw(((long)a << 32) / b); }
70 | // Creates the fixed point number that's a divided by 10.
71 | public static F64 Ratio10(int a) { return F64.FromRaw(((long)a << 32) / 10); }
72 | // Creates the fixed point number that's a divided by 100.
73 | public static F64 Ratio100(int a) { return F64.FromRaw(((long)a << 32) / 100); }
74 | // Creates the fixed point number that's a divided by 1000.
75 | public static F64 Ratio1000(int a) { return F64.FromRaw(((long)a << 32) / 1000); }
76 |
77 | // Operators
78 | public static F64 operator -(F64 v1) { return FromRaw(-v1.Raw); }
79 |
80 | public static F64 operator +(F64 v1, F64 v2) { return FromRaw(v1.Raw + v2.Raw); }
81 | public static F64 operator -(F64 v1, F64 v2) { return FromRaw(v1.Raw - v2.Raw); }
82 | public static F64 operator *(F64 v1, F64 v2) { return FromRaw(Fixed64.Mul(v1.Raw, v2.Raw)); }
83 | public static F64 operator /(F64 v1, F64 v2) { return FromRaw(Fixed64.DivPrecise(v1.Raw, v2.Raw)); }
84 | public static F64 operator %(F64 v1, F64 v2) { return FromRaw(Fixed64.Mod(v1.Raw, v2.Raw)); }
85 |
86 | public static F64 operator +(F64 v1, int v2) { return FromRaw(v1.Raw + Fixed64.FromInt(v2)); }
87 | public static F64 operator +(int v1, F64 v2) { return FromRaw(Fixed64.FromInt(v1) + v2.Raw); }
88 | public static F64 operator -(F64 v1, int v2) { return FromRaw(v1.Raw - Fixed64.FromInt(v2)); }
89 | public static F64 operator -(int v1, F64 v2) { return FromRaw(Fixed64.FromInt(v1) - v2.Raw); }
90 | public static F64 operator *(F64 v1, int v2) { return FromRaw(v1.Raw * (long)v2); }
91 | public static F64 operator *(int v1, F64 v2) { return FromRaw((long)v1 * v2.Raw); }
92 | public static F64 operator /(F64 v1, int v2) { return FromRaw(v1.Raw / (long)v2); }
93 | public static F64 operator /(int v1, F64 v2) { return FromRaw(Fixed64.DivPrecise(Fixed64.FromInt(v1), v2.Raw)); }
94 | public static F64 operator %(F64 v1, int v2) { return FromRaw(Fixed64.Mod(v1.Raw, Fixed64.FromInt(v2))); }
95 | public static F64 operator %(int v1, F64 v2) { return FromRaw(Fixed64.Mod(Fixed64.FromInt(v1), v2.Raw)); }
96 |
97 | public static F64 operator ++(F64 v1) { return FromRaw(v1.Raw + Fixed64.One); }
98 | public static F64 operator --(F64 v1) { return FromRaw(v1.Raw - Fixed64.One); }
99 |
100 | public static bool operator ==(F64 v1, F64 v2) { return v1.Raw == v2.Raw; }
101 | public static bool operator !=(F64 v1, F64 v2) { return v1.Raw != v2.Raw; }
102 | public static bool operator <(F64 v1, F64 v2) { return v1.Raw < v2.Raw; }
103 | public static bool operator <=(F64 v1, F64 v2) { return v1.Raw <= v2.Raw; }
104 | public static bool operator >(F64 v1, F64 v2) { return v1.Raw > v2.Raw; }
105 | public static bool operator >=(F64 v1, F64 v2) { return v1.Raw >= v2.Raw; }
106 |
107 | public static bool operator ==(int v1, F64 v2) { return Fixed64.FromInt(v1) == v2.Raw; }
108 | public static bool operator ==(F64 v1, int v2) { return v1.Raw == Fixed64.FromInt(v2); }
109 | public static bool operator !=(int v1, F64 v2) { return Fixed64.FromInt(v1) != v2.Raw; }
110 | public static bool operator !=(F64 v1, int v2) { return v1.Raw != Fixed64.FromInt(v2); }
111 | public static bool operator <(int v1, F64 v2) { return Fixed64.FromInt(v1) < v2.Raw; }
112 | public static bool operator <(F64 v1, int v2) { return v1.Raw < Fixed64.FromInt(v2); }
113 | public static bool operator <=(int v1, F64 v2) { return Fixed64.FromInt(v1) <= v2.Raw; }
114 | public static bool operator <=(F64 v1, int v2) { return v1.Raw <= Fixed64.FromInt(v2); }
115 | public static bool operator >(int v1, F64 v2) { return Fixed64.FromInt(v1) > v2.Raw; }
116 | public static bool operator >(F64 v1, int v2) { return v1.Raw > Fixed64.FromInt(v2); }
117 | public static bool operator >=(int v1, F64 v2) { return Fixed64.FromInt(v1) >= v2.Raw; }
118 | public static bool operator >=(F64 v1, int v2) { return v1.Raw >= Fixed64.FromInt(v2); }
119 |
120 | public static bool operator ==(F32 a, F64 b) { return F64.FromF32(a) == b; }
121 | public static bool operator ==(F64 a, F32 b) { return a == F64.FromF32(b); }
122 | public static bool operator !=(F32 a, F64 b) { return F64.FromF32(a) != b; }
123 | public static bool operator !=(F64 a, F32 b) { return a != F64.FromF32(b); }
124 | public static bool operator <(F32 a, F64 b) { return F64.FromF32(a) < b; }
125 | public static bool operator <(F64 a, F32 b) { return a < F64.FromF32(b); }
126 | public static bool operator <=(F32 a, F64 b) { return F64.FromF32(a) <= b; }
127 | public static bool operator <=(F64 a, F32 b) { return a <= F64.FromF32(b); }
128 | public static bool operator >(F32 a, F64 b) { return F64.FromF32(a) > b; }
129 | public static bool operator >(F64 a, F32 b) { return a > F64.FromF32(b); }
130 | public static bool operator >=(F32 a, F64 b) { return F64.FromF32(a) >= b; }
131 | public static bool operator >=(F64 a, F32 b) { return a >= F64.FromF32(b); }
132 |
133 | public static F64 RadToDeg(F64 a) { return FromRaw(Fixed64.Mul(a.Raw, 246083499198)); } // 180 / F64.Pi
134 | public static F64 DegToRad(F64 a) { return FromRaw(Fixed64.Mul(a.Raw, 74961320)); } // F64.Pi / 180
135 |
136 | public static F64 Div2(F64 a) { return FromRaw(a.Raw >> 1); }
137 | public static F64 Abs(F64 a) { return FromRaw(Fixed64.Abs(a.Raw)); }
138 | public static F64 Nabs(F64 a) { return FromRaw(Fixed64.Nabs(a.Raw)); }
139 | public static int Sign(F64 a) { return Fixed64.Sign(a.Raw); }
140 | public static F64 Ceil(F64 a) { return FromRaw(Fixed64.Ceil(a.Raw)); }
141 | public static F64 Floor(F64 a) { return FromRaw(Fixed64.Floor(a.Raw)); }
142 | public static F64 Round(F64 a) { return FromRaw(Fixed64.Round(a.Raw)); }
143 | public static F64 Fract(F64 a) { return FromRaw(Fixed64.Fract(a.Raw)); }
144 | public static F64 Div(F64 a, F64 b) { return FromRaw(Fixed64.Div(a.Raw, b.Raw)); }
145 | public static F64 DivFast(F64 a, F64 b) { return FromRaw(Fixed64.DivFast(a.Raw, b.Raw)); }
146 | public static F64 DivFastest(F64 a, F64 b) { return FromRaw(Fixed64.DivFastest(a.Raw, b.Raw)); }
147 | public static F64 SqrtPrecise(F64 a) { return FromRaw(Fixed64.SqrtPrecise(a.Raw)); }
148 | public static F64 Sqrt(F64 a) { return FromRaw(Fixed64.Sqrt(a.Raw)); }
149 | public static F64 SqrtFast(F64 a) { return FromRaw(Fixed64.SqrtFast(a.Raw)); }
150 | public static F64 SqrtFastest(F64 a) { return FromRaw(Fixed64.SqrtFastest(a.Raw)); }
151 | public static F64 RSqrt(F64 a) { return FromRaw(Fixed64.RSqrt(a.Raw)); }
152 | public static F64 RSqrtFast(F64 a) { return FromRaw(Fixed64.RSqrtFast(a.Raw)); }
153 | public static F64 RSqrtFastest(F64 a) { return FromRaw(Fixed64.RSqrtFastest(a.Raw)); }
154 | public static F64 Rcp(F64 a) { return FromRaw(Fixed64.Rcp(a.Raw)); }
155 | public static F64 RcpFast(F64 a) { return FromRaw(Fixed64.RcpFast(a.Raw)); }
156 | public static F64 RcpFastest(F64 a) { return FromRaw(Fixed64.RcpFastest(a.Raw)); }
157 | public static F64 Exp(F64 a) { return FromRaw(Fixed64.Exp(a.Raw)); }
158 | public static F64 ExpFast(F64 a) { return FromRaw(Fixed64.ExpFast(a.Raw)); }
159 | public static F64 ExpFastest(F64 a) { return FromRaw(Fixed64.ExpFastest(a.Raw)); }
160 | public static F64 Exp2(F64 a) { return FromRaw(Fixed64.Exp2(a.Raw)); }
161 | public static F64 Exp2Fast(F64 a) { return FromRaw(Fixed64.Exp2Fast(a.Raw)); }
162 | public static F64 Exp2Fastest(F64 a) { return FromRaw(Fixed64.Exp2Fastest(a.Raw)); }
163 | public static F64 Log(F64 a) { return FromRaw(Fixed64.Log(a.Raw)); }
164 | public static F64 LogFast(F64 a) { return FromRaw(Fixed64.LogFast(a.Raw)); }
165 | public static F64 LogFastest(F64 a) { return FromRaw(Fixed64.LogFastest(a.Raw)); }
166 | public static F64 Log2(F64 a) { return FromRaw(Fixed64.Log2(a.Raw)); }
167 | public static F64 Log2Fast(F64 a) { return FromRaw(Fixed64.Log2Fast(a.Raw)); }
168 | public static F64 Log2Fastest(F64 a) { return FromRaw(Fixed64.Log2Fastest(a.Raw)); }
169 |
170 | public static F64 Sin(F64 a) { return FromRaw(Fixed64.Sin(a.Raw)); }
171 | public static F64 SinFast(F64 a) { return FromRaw(Fixed64.SinFast(a.Raw)); }
172 | public static F64 SinFastest(F64 a) { return FromRaw(Fixed64.SinFastest(a.Raw)); }
173 | public static F64 Cos(F64 a) { return FromRaw(Fixed64.Cos(a.Raw)); }
174 | public static F64 CosFast(F64 a) { return FromRaw(Fixed64.CosFast(a.Raw)); }
175 | public static F64 CosFastest(F64 a) { return FromRaw(Fixed64.CosFastest(a.Raw)); }
176 | public static F64 Tan(F64 a) { return FromRaw(Fixed64.Tan(a.Raw)); }
177 | public static F64 TanFast(F64 a) { return FromRaw(Fixed64.TanFast(a.Raw)); }
178 | public static F64 TanFastest(F64 a) { return FromRaw(Fixed64.TanFastest(a.Raw)); }
179 | public static F64 Asin(F64 a) { return FromRaw(Fixed64.Asin(a.Raw)); }
180 | public static F64 AsinFast(F64 a) { return FromRaw(Fixed64.AsinFast(a.Raw)); }
181 | public static F64 AsinFastest(F64 a) { return FromRaw(Fixed64.AsinFastest(a.Raw)); }
182 | public static F64 Acos(F64 a) { return FromRaw(Fixed64.Acos(a.Raw)); }
183 | public static F64 AcosFast(F64 a) { return FromRaw(Fixed64.AcosFast(a.Raw)); }
184 | public static F64 AcosFastest(F64 a) { return FromRaw(Fixed64.AcosFastest(a.Raw)); }
185 | public static F64 Atan(F64 a) { return FromRaw(Fixed64.Atan(a.Raw)); }
186 | public static F64 AtanFast(F64 a) { return FromRaw(Fixed64.AtanFast(a.Raw)); }
187 | public static F64 AtanFastest(F64 a) { return FromRaw(Fixed64.AtanFastest(a.Raw)); }
188 | public static F64 Atan2(F64 y, F64 x) { return FromRaw(Fixed64.Atan2(y.Raw, x.Raw)); }
189 | public static F64 Atan2Fast(F64 y, F64 x) { return FromRaw(Fixed64.Atan2Fast(y.Raw, x.Raw)); }
190 | public static F64 Atan2Fastest(F64 y, F64 x) { return FromRaw(Fixed64.Atan2Fastest(y.Raw, x.Raw)); }
191 | public static F64 Pow(F64 a, F64 b) { return FromRaw(Fixed64.Pow(a.Raw, b.Raw)); }
192 | public static F64 PowFast(F64 a, F64 b) { return FromRaw(Fixed64.PowFast(a.Raw, b.Raw)); }
193 | public static F64 PowFastest(F64 a, F64 b) { return FromRaw(Fixed64.PowFastest(a.Raw, b.Raw)); }
194 |
195 | public static F64 Min(F64 a, F64 b) { return FromRaw(Fixed64.Min(a.Raw, b.Raw)); }
196 | public static F64 Max(F64 a, F64 b) { return FromRaw(Fixed64.Max(a.Raw, b.Raw)); }
197 | public static F64 Clamp(F64 a, F64 min, F64 max) { return FromRaw(Fixed64.Clamp(a.Raw, min.Raw, max.Raw)); }
198 | public static F64 Clamp01(F64 a) { return FromRaw(Fixed64.Clamp(a.Raw, Fixed64.Zero, Fixed64.One)); }
199 |
200 | public static F64 Lerp(F64 a, F64 b, F64 t)
201 | {
202 | long tb = t.Raw;
203 | long ta = Fixed64.One - tb;
204 | return FromRaw(Fixed64.Mul(a.Raw, ta) + Fixed64.Mul(b.Raw, tb));
205 | }
206 |
207 | public bool Equals(F64 other)
208 | {
209 | return (Raw == other.Raw);
210 | }
211 |
212 | public override bool Equals(object obj)
213 | {
214 | if (!(obj is F64))
215 | return false;
216 | return ((F64)obj).Raw == Raw;
217 | }
218 |
219 | public int CompareTo(F64 other)
220 | {
221 | if (Raw < other.Raw) return -1;
222 | if (Raw > other.Raw) return +1;
223 | return 0;
224 | }
225 |
226 | public override string ToString()
227 | {
228 | return Fixed64.ToString(Raw);
229 | }
230 |
231 | public override int GetHashCode()
232 | {
233 | return Raw.GetHashCode();
234 | }
235 |
236 | int IComparable.CompareTo(object obj)
237 | {
238 | if (obj is F64 other)
239 | return CompareTo(other);
240 | else if (obj is null)
241 | return 1;
242 | // don't allow comparisons with other numeric or non-numeric types.
243 | throw new ArgumentException("F64 can only be compared against another F64.");
244 | }
245 | }
246 | }
247 |
--------------------------------------------------------------------------------
/Examples/FixMath/F32Vec3.cs:
--------------------------------------------------------------------------------
1 | //
2 | // FixPointCS
3 | //
4 | // Copyright(c) Jere Sanisalo, Petri Kero
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | using FixPointCS;
25 | using System;
26 | using System.Runtime.CompilerServices;
27 |
28 | namespace FixMath
29 | {
30 | ///
31 | /// Vector3 struct with signed 16.16 fixed point components.
32 | ///
33 | [Serializable]
34 | public struct F32Vec3 : IEquatable
35 | {
36 | // Constants
37 | public static F32Vec3 Zero { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.Zero, Fixed32.Zero, Fixed32.Zero); } }
38 | public static F32Vec3 One { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.One, Fixed32.One, Fixed32.One); } }
39 | public static F32Vec3 Down { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.Zero, Fixed32.Neg1, Fixed32.Zero); } }
40 | public static F32Vec3 Up { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.Zero, Fixed32.One, Fixed32.Zero); } }
41 | public static F32Vec3 Left { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.Neg1, Fixed32.Zero, Fixed32.Zero); } }
42 | public static F32Vec3 Right { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.One, Fixed32.Zero, Fixed32.Zero); } }
43 | public static F32Vec3 Forward { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.Zero, Fixed32.Zero, Fixed32.One); } }
44 | public static F32Vec3 Back { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.Zero, Fixed32.Zero, Fixed32.Neg1); } }
45 | public static F32Vec3 AxisX { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.One, Fixed32.Zero, Fixed32.Zero); } }
46 | public static F32Vec3 AxisY { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.Zero, Fixed32.One, Fixed32.Zero); } }
47 | public static F32Vec3 AxisZ { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.Zero, Fixed32.Zero, Fixed32.One); } }
48 |
49 | // Raw components
50 | public int RawX;
51 | public int RawY;
52 | public int RawZ;
53 |
54 | // F32 accessors
55 | public F32 X { get { return F32.FromRaw(RawX); } set { RawX = value.Raw; } }
56 | public F32 Y { get { return F32.FromRaw(RawY); } set { RawY = value.Raw; } }
57 | public F32 Z { get { return F32.FromRaw(RawZ); } set { RawZ = value.Raw; } }
58 |
59 | public F32Vec3(F32 x, F32 y, F32 z)
60 | {
61 | RawX = x.Raw;
62 | RawY = y.Raw;
63 | RawZ = z.Raw;
64 | }
65 |
66 | // raw ctor only for internal usage
67 | private F32Vec3(int x, int y, int z)
68 | {
69 | RawX = x;
70 | RawY = y;
71 | RawZ = z;
72 | }
73 |
74 | public static F32Vec3 FromRaw(int rawX, int rawY, int rawZ) { return new F32Vec3(rawX, rawY, rawZ); }
75 | public static F32Vec3 FromInt(int x, int y, int z) { return new F32Vec3(F32.FromInt(x), F32.FromInt(y), F32.FromInt(z)); }
76 | public static F32Vec3 FromFloat(float x, float y, float z) { return new F32Vec3(F32.FromFloat(x), F32.FromFloat(y), F32.FromFloat(z)); }
77 | public static F32Vec3 FromDouble(double x, double y, double z) { return new F32Vec3(F32.FromDouble(x), F32.FromDouble(y), F32.FromDouble(z)); }
78 |
79 | public static F32Vec3 operator -(F32Vec3 a) { return new F32Vec3(-a.RawX, -a.RawY, -a.RawZ); }
80 | public static F32Vec3 operator +(F32Vec3 a, F32Vec3 b) { return new F32Vec3(a.RawX + b.RawX, a.RawY + b.RawY, a.RawZ + b.RawZ); }
81 | public static F32Vec3 operator -(F32Vec3 a, F32Vec3 b) { return new F32Vec3(a.RawX - b.RawX, a.RawY - b.RawY, a.RawZ - b.RawZ); }
82 | public static F32Vec3 operator *(F32Vec3 a, F32Vec3 b) { return new F32Vec3(Fixed32.Mul(a.RawX, b.RawX), Fixed32.Mul(a.RawY, b.RawY), Fixed32.Mul(a.RawZ, b.RawZ)); }
83 | public static F32Vec3 operator /(F32Vec3 a, F32Vec3 b) { return new F32Vec3(Fixed32.DivPrecise(a.RawX, b.RawX), Fixed32.DivPrecise(a.RawY, b.RawY), Fixed32.DivPrecise(a.RawZ, b.RawZ)); }
84 | public static F32Vec3 operator %(F32Vec3 a, F32Vec3 b) { return new F32Vec3(a.RawX % b.RawX, a.RawY % b.RawY, a.RawZ % b.RawZ); }
85 |
86 | public static F32Vec3 operator +(F32 a, F32Vec3 b) { return new F32Vec3(a.Raw + b.RawX, a.Raw + b.RawY, a.Raw + b.RawZ); }
87 | public static F32Vec3 operator +(F32Vec3 a, F32 b) { return new F32Vec3(a.RawX + b.Raw, a.RawY + b.Raw, a.RawZ + b.Raw); }
88 | public static F32Vec3 operator -(F32 a, F32Vec3 b) { return new F32Vec3(a.Raw - b.RawX, a.Raw - b.RawY, a.Raw - b.RawZ); }
89 | public static F32Vec3 operator -(F32Vec3 a, F32 b) { return new F32Vec3(a.RawX - b.Raw, a.RawY - b.Raw, a.RawZ - b.Raw); }
90 | public static F32Vec3 operator *(F32 a, F32Vec3 b) { return new F32Vec3(Fixed32.Mul(a.Raw, b.RawX), Fixed32.Mul(a.Raw, b.RawY), Fixed32.Mul(a.Raw, b.RawZ)); }
91 | public static F32Vec3 operator *(F32Vec3 a, F32 b) { return new F32Vec3(Fixed32.Mul(a.RawX, b.Raw), Fixed32.Mul(a.RawY, b.Raw), Fixed32.Mul(a.RawZ, b.Raw)); }
92 | public static F32Vec3 operator /(F32 a, F32Vec3 b) { return new F32Vec3(Fixed32.DivPrecise(a.Raw, b.RawX), Fixed32.DivPrecise(a.Raw, b.RawY), Fixed32.DivPrecise(a.Raw, b.RawZ)); }
93 | public static F32Vec3 operator /(F32Vec3 a, F32 b) { return new F32Vec3(Fixed32.DivPrecise(a.RawX, b.Raw), Fixed32.DivPrecise(a.RawY, b.Raw), Fixed32.DivPrecise(a.RawZ, b.Raw)); }
94 | public static F32Vec3 operator %(F32 a, F32Vec3 b) { return new F32Vec3(a.Raw % b.RawX, a.Raw % b.RawY, a.Raw % b.RawZ); }
95 | public static F32Vec3 operator %(F32Vec3 a, F32 b) { return new F32Vec3(a.RawX % b.Raw, a.RawY % b.Raw, a.RawZ % b.Raw); }
96 |
97 | public static bool operator ==(F32Vec3 a, F32Vec3 b) { return a.RawX == b.RawX && a.RawY == b.RawY && a.RawZ == b.RawZ; }
98 | public static bool operator !=(F32Vec3 a, F32Vec3 b) { return a.RawX != b.RawX || a.RawY != b.RawY || a.RawZ != b.RawZ; }
99 |
100 | public static F32Vec3 Div(F32Vec3 a, F32 b) { int oob = Fixed32.Rcp(b.Raw); return new F32Vec3(Fixed32.Mul(a.RawX, oob), Fixed32.Mul(a.RawY, oob), Fixed32.Mul(a.RawZ, oob)); }
101 | public static F32Vec3 DivFast(F32Vec3 a, F32 b) { int oob = Fixed32.RcpFast(b.Raw); return new F32Vec3(Fixed32.Mul(a.RawX, oob), Fixed32.Mul(a.RawY, oob), Fixed32.Mul(a.RawZ, oob)); }
102 | public static F32Vec3 DivFastest(F32Vec3 a, F32 b) { int oob = Fixed32.RcpFastest(b.Raw); return new F32Vec3(Fixed32.Mul(a.RawX, oob), Fixed32.Mul(a.RawY, oob), Fixed32.Mul(a.RawZ, oob)); }
103 | public static F32Vec3 Div(F32Vec3 a, F32Vec3 b) { return new F32Vec3(Fixed32.Div(a.RawX, b.RawX), Fixed32.Div(a.RawY, b.RawY), Fixed32.Div(a.RawZ, b.RawZ)); }
104 | public static F32Vec3 DivFast(F32Vec3 a, F32Vec3 b) { return new F32Vec3(Fixed32.DivFast(a.RawX, b.RawX), Fixed32.DivFast(a.RawY, b.RawY), Fixed32.DivFast(a.RawZ, b.RawZ)); }
105 | public static F32Vec3 DivFastest(F32Vec3 a, F32Vec3 b) { return new F32Vec3(Fixed32.DivFastest(a.RawX, b.RawX), Fixed32.DivFastest(a.RawY, b.RawY), Fixed32.DivFastest(a.RawZ, b.RawZ)); }
106 | public static F32Vec3 SqrtPrecise(F32Vec3 a) { return new F32Vec3(Fixed32.SqrtPrecise(a.RawX), Fixed32.SqrtPrecise(a.RawY), Fixed32.SqrtPrecise(a.RawZ)); }
107 | public static F32Vec3 Sqrt(F32Vec3 a) { return new F32Vec3(Fixed32.Sqrt(a.RawX), Fixed32.Sqrt(a.RawY), Fixed32.Sqrt(a.RawZ)); }
108 | public static F32Vec3 SqrtFast(F32Vec3 a) { return new F32Vec3(Fixed32.SqrtFast(a.RawX), Fixed32.SqrtFast(a.RawY), Fixed32.SqrtFast(a.RawZ)); }
109 | public static F32Vec3 SqrtFastest(F32Vec3 a) { return new F32Vec3(Fixed32.SqrtFastest(a.RawX), Fixed32.SqrtFastest(a.RawY), Fixed32.SqrtFastest(a.RawZ)); }
110 | public static F32Vec3 RSqrt(F32Vec3 a) { return new F32Vec3(Fixed32.RSqrt(a.RawX), Fixed32.RSqrt(a.RawY), Fixed32.RSqrt(a.RawZ)); }
111 | public static F32Vec3 RSqrtFast(F32Vec3 a) { return new F32Vec3(Fixed32.RSqrtFast(a.RawX), Fixed32.RSqrtFast(a.RawY), Fixed32.RSqrtFast(a.RawZ)); }
112 | public static F32Vec3 RSqrtFastest(F32Vec3 a) { return new F32Vec3(Fixed32.RSqrtFastest(a.RawX), Fixed32.RSqrtFastest(a.RawY), Fixed32.RSqrtFastest(a.RawZ)); }
113 | public static F32Vec3 Rcp(F32Vec3 a) { return new F32Vec3(Fixed32.Rcp(a.RawX), Fixed32.Rcp(a.RawY), Fixed32.Rcp(a.RawZ)); }
114 | public static F32Vec3 RcpFast(F32Vec3 a) { return new F32Vec3(Fixed32.RcpFast(a.RawX), Fixed32.RcpFast(a.RawY), Fixed32.RcpFast(a.RawZ)); }
115 | public static F32Vec3 RcpFastest(F32Vec3 a) { return new F32Vec3(Fixed32.RcpFastest(a.RawX), Fixed32.RcpFastest(a.RawY), Fixed32.RcpFastest(a.RawZ)); }
116 | public static F32Vec3 Exp(F32Vec3 a) { return new F32Vec3(Fixed32.Exp(a.RawX), Fixed32.Exp(a.RawY), Fixed32.Exp(a.RawZ)); }
117 | public static F32Vec3 ExpFast(F32Vec3 a) { return new F32Vec3(Fixed32.ExpFast(a.RawX), Fixed32.ExpFast(a.RawY), Fixed32.ExpFast(a.RawZ)); }
118 | public static F32Vec3 ExpFastest(F32Vec3 a) { return new F32Vec3(Fixed32.ExpFastest(a.RawX), Fixed32.ExpFastest(a.RawY), Fixed32.ExpFastest(a.RawZ)); }
119 | public static F32Vec3 Exp2(F32Vec3 a) { return new F32Vec3(Fixed32.Exp2(a.RawX), Fixed32.Exp2(a.RawY), Fixed32.Exp2(a.RawZ)); }
120 | public static F32Vec3 Exp2Fast(F32Vec3 a) { return new F32Vec3(Fixed32.Exp2Fast(a.RawX), Fixed32.Exp2Fast(a.RawY), Fixed32.Exp2Fast(a.RawZ)); }
121 | public static F32Vec3 Exp2Fastest(F32Vec3 a) { return new F32Vec3(Fixed32.Exp2Fastest(a.RawX), Fixed32.Exp2Fastest(a.RawY), Fixed32.Exp2Fastest(a.RawZ)); }
122 | public static F32Vec3 Log(F32Vec3 a) { return new F32Vec3(Fixed32.Log(a.RawX), Fixed32.Log(a.RawY), Fixed32.Log(a.RawZ)); }
123 | public static F32Vec3 LogFast(F32Vec3 a) { return new F32Vec3(Fixed32.LogFast(a.RawX), Fixed32.LogFast(a.RawY), Fixed32.LogFast(a.RawZ)); }
124 | public static F32Vec3 LogFastest(F32Vec3 a) { return new F32Vec3(Fixed32.LogFastest(a.RawX), Fixed32.LogFastest(a.RawY), Fixed32.LogFastest(a.RawZ)); }
125 | public static F32Vec3 Log2(F32Vec3 a) { return new F32Vec3(Fixed32.Log2(a.RawX), Fixed32.Log2(a.RawY), Fixed32.Log2(a.RawZ)); }
126 | public static F32Vec3 Log2Fast(F32Vec3 a) { return new F32Vec3(Fixed32.Log2Fast(a.RawX), Fixed32.Log2Fast(a.RawY), Fixed32.Log2Fast(a.RawZ)); }
127 | public static F32Vec3 Log2Fastest(F32Vec3 a) { return new F32Vec3(Fixed32.Log2Fastest(a.RawX), Fixed32.Log2Fastest(a.RawY), Fixed32.Log2Fastest(a.RawZ)); }
128 | public static F32Vec3 Sin(F32Vec3 a) { return new F32Vec3(Fixed32.Sin(a.RawX), Fixed32.Sin(a.RawY), Fixed32.Sin(a.RawZ)); }
129 | public static F32Vec3 SinFast(F32Vec3 a) { return new F32Vec3(Fixed32.SinFast(a.RawX), Fixed32.SinFast(a.RawY), Fixed32.SinFast(a.RawZ)); }
130 | public static F32Vec3 SinFastest(F32Vec3 a) { return new F32Vec3(Fixed32.SinFastest(a.RawX), Fixed32.SinFastest(a.RawY), Fixed32.SinFastest(a.RawZ)); }
131 | public static F32Vec3 Cos(F32Vec3 a) { return new F32Vec3(Fixed32.Cos(a.RawX), Fixed32.Cos(a.RawY), Fixed32.Cos(a.RawZ)); }
132 | public static F32Vec3 CosFast(F32Vec3 a) { return new F32Vec3(Fixed32.CosFast(a.RawX), Fixed32.CosFast(a.RawY), Fixed32.CosFast(a.RawZ)); }
133 | public static F32Vec3 CosFastest(F32Vec3 a) { return new F32Vec3(Fixed32.CosFastest(a.RawX), Fixed32.CosFastest(a.RawY), Fixed32.CosFastest(a.RawZ)); }
134 |
135 | public static F32Vec3 Pow(F32Vec3 a, F32 b) { return new F32Vec3(Fixed32.Pow(a.RawX, b.Raw), Fixed32.Pow(a.RawY, b.Raw), Fixed32.Pow(a.RawZ, b.Raw)); }
136 | public static F32Vec3 PowFast(F32Vec3 a, F32 b) { return new F32Vec3(Fixed32.PowFast(a.RawX, b.Raw), Fixed32.PowFast(a.RawY, b.Raw), Fixed32.PowFast(a.RawZ, b.Raw)); }
137 | public static F32Vec3 PowFastest(F32Vec3 a, F32 b) { return new F32Vec3(Fixed32.PowFastest(a.RawX, b.Raw), Fixed32.PowFastest(a.RawY, b.Raw), Fixed32.PowFastest(a.RawZ, b.Raw)); }
138 | public static F32Vec3 Pow(F32 a, F32Vec3 b) { return new F32Vec3(Fixed32.Pow(a.Raw, b.RawX), Fixed32.Pow(a.Raw, b.RawY), Fixed32.Pow(a.Raw, b.RawZ)); }
139 | public static F32Vec3 PowFast(F32 a, F32Vec3 b) { return new F32Vec3(Fixed32.PowFast(a.Raw, b.RawX), Fixed32.PowFast(a.Raw, b.RawY), Fixed32.PowFast(a.Raw, b.RawZ)); }
140 | public static F32Vec3 PowFastest(F32 a, F32Vec3 b) { return new F32Vec3(Fixed32.PowFastest(a.Raw, b.RawX), Fixed32.PowFastest(a.Raw, b.RawY), Fixed32.PowFastest(a.Raw, b.RawZ)); }
141 | public static F32Vec3 Pow(F32Vec3 a, F32Vec3 b) { return new F32Vec3(Fixed32.Pow(a.RawX, b.RawX), Fixed32.Pow(a.RawY, b.RawY), Fixed32.Pow(a.RawZ, b.RawZ)); }
142 | public static F32Vec3 PowFast(F32Vec3 a, F32Vec3 b) { return new F32Vec3(Fixed32.PowFast(a.RawX, b.RawX), Fixed32.PowFast(a.RawY, b.RawY), Fixed32.PowFast(a.RawZ, b.RawZ)); }
143 | public static F32Vec3 PowFastest(F32Vec3 a, F32Vec3 b) { return new F32Vec3(Fixed32.PowFastest(a.RawX, b.RawX), Fixed32.PowFastest(a.RawY, b.RawY), Fixed32.PowFastest(a.RawZ, b.RawZ)); }
144 |
145 | public static F32 Length(F32Vec3 a) { return F32.FromRaw((int)(Fixed64.Sqrt((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY + (long)a.RawZ * (long)a.RawZ) >> 16)); }
146 | public static F32 LengthFast(F32Vec3 a) { return F32.FromRaw((int)(Fixed64.SqrtFast((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY + (long)a.RawZ * (long)a.RawZ) >> 16)); }
147 | public static F32 LengthFastest(F32Vec3 a) { return F32.FromRaw((int)(Fixed64.SqrtFastest((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY + (long)a.RawZ * (long)a.RawZ) >> 16)); }
148 | public static F64 LengthSqr(F32Vec3 a) { return F64.FromRaw((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY + (long)a.RawZ * (long)a.RawZ); }
149 | public static F32Vec3 Normalize(F32Vec3 a) { F32 ooLen = F32.FromRaw((int)(Fixed64.RSqrt((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY + (long)a.RawZ * (long)a.RawZ) >> 16)); return ooLen * a; }
150 | public static F32Vec3 NormalizeFast(F32Vec3 a) { F32 ooLen = F32.FromRaw((int)(Fixed64.RSqrtFast((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY + (long)a.RawZ * (long)a.RawZ) >> 16)); return ooLen * a; }
151 | public static F32Vec3 NormalizeFastest(F32Vec3 a) { F32 ooLen = F32.FromRaw((int)(Fixed64.RSqrtFastest((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY + (long)a.RawZ * (long)a.RawZ) >> 16)); return ooLen * a; }
152 |
153 | public static F32 Dot(F32Vec3 a, F32Vec3 b) { return F32.FromRaw(Fixed32.Mul(a.RawX, b.RawX) + Fixed32.Mul(a.RawY, b.RawY) + Fixed32.Mul(a.RawZ, b.RawZ)); }
154 | public static F32 Distance(F32Vec3 a, F32Vec3 b) { return Length(a - b); }
155 | public static F32 DistanceFast(F32Vec3 a, F32Vec3 b) { return LengthFast(a - b); }
156 | public static F32 DistanceFastest(F32Vec3 a, F32Vec3 b) { return LengthFastest(a - b); }
157 |
158 | public static F32Vec3 Min(F32Vec3 a, F32Vec3 b) { return new F32Vec3(Fixed32.Min(a.RawX, b.RawX), Fixed32.Min(a.RawY, b.RawY), Fixed32.Min(a.RawZ, b.RawZ)); }
159 | public static F32Vec3 Max(F32Vec3 a, F32Vec3 b) { return new F32Vec3(Fixed32.Max(a.RawX, b.RawX), Fixed32.Max(a.RawY, b.RawY), Fixed32.Max(a.RawZ, b.RawZ)); }
160 |
161 | public static F32Vec3 Clamp(F32Vec3 a, F32 min, F32 max)
162 | {
163 | return new F32Vec3(
164 | Fixed32.Clamp(a.RawX, min.Raw, max.Raw),
165 | Fixed32.Clamp(a.RawY, min.Raw, max.Raw),
166 | Fixed32.Clamp(a.RawZ, min.Raw, max.Raw));
167 | }
168 |
169 | public static F32Vec3 Clamp(F32Vec3 a, F32Vec3 min, F32Vec3 max)
170 | {
171 | return new F32Vec3(
172 | Fixed32.Clamp(a.RawX, min.RawX, max.RawX),
173 | Fixed32.Clamp(a.RawY, min.RawY, max.RawY),
174 | Fixed32.Clamp(a.RawZ, min.RawZ, max.RawZ));
175 | }
176 |
177 | public static F32Vec3 Lerp(F32Vec3 a, F32Vec3 b, F32 t)
178 | {
179 | int tb = t.Raw;
180 | int ta = Fixed32.One - tb;
181 | return new F32Vec3(
182 | Fixed32.Mul(a.RawX, ta) + Fixed32.Mul(b.RawX, tb),
183 | Fixed32.Mul(a.RawY, ta) + Fixed32.Mul(b.RawY, tb),
184 | Fixed32.Mul(a.RawZ, ta) + Fixed32.Mul(b.RawZ, tb));
185 | }
186 |
187 | public static F32Vec3 Cross(F32Vec3 a, F32Vec3 b)
188 | {
189 | return new F32Vec3(
190 | Fixed32.Mul(a.RawY, b.RawZ) - Fixed32.Mul(a.RawZ, b.RawY),
191 | Fixed32.Mul(a.RawZ, b.RawX) - Fixed32.Mul(a.RawX, b.RawZ),
192 | Fixed32.Mul(a.RawX, b.RawY) - Fixed32.Mul(a.RawY, b.RawX));
193 | }
194 |
195 | public bool Equals(F32Vec3 other)
196 | {
197 | return (this == other);
198 | }
199 |
200 | public override bool Equals(object obj)
201 | {
202 | if (!(obj is F32Vec3))
203 | return false;
204 | return ((F32Vec3)obj) == this;
205 | }
206 |
207 | public override string ToString()
208 | {
209 | return "(" + Fixed32.ToString(RawX) + ", " + Fixed32.ToString(RawY) + ", " + Fixed32.ToString(RawZ) + ")";
210 | }
211 |
212 | public override int GetHashCode()
213 | {
214 | return RawX.GetHashCode() ^ RawY.GetHashCode() * 7919 ^ RawZ.GetHashCode() * 4513;
215 | }
216 | }
217 | }
218 |
--------------------------------------------------------------------------------
/Examples/FixMath/F64Vec3.cs:
--------------------------------------------------------------------------------
1 | //
2 | // FixPointCS
3 | //
4 | // Copyright(c) Jere Sanisalo, Petri Kero
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | using FixPointCS;
25 | using System;
26 | using System.Runtime.CompilerServices;
27 |
28 | namespace FixMath
29 | {
30 | ///
31 | /// Vector3 struct with signed 32.32 fixed point components.
32 | ///
33 | [Serializable]
34 | public struct F64Vec3 : IEquatable
35 | {
36 | public static F64Vec3 Zero { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.Zero, Fixed64.Zero, Fixed64.Zero); } }
37 | public static F64Vec3 One { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.One, Fixed64.One, Fixed64.One); } }
38 | public static F64Vec3 Down { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.Zero, Fixed64.Neg1, Fixed64.Zero); } }
39 | public static F64Vec3 Up { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.Zero, Fixed64.One, Fixed64.Zero); } }
40 | public static F64Vec3 Left { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.Neg1, Fixed64.Zero, Fixed64.Zero); } }
41 | public static F64Vec3 Right { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.One, Fixed64.Zero, Fixed64.Zero); } }
42 | public static F64Vec3 Forward { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.Zero, Fixed64.Zero, Fixed64.One); } }
43 | public static F64Vec3 Back { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.Zero, Fixed64.Zero, Fixed64.Neg1); } }
44 | public static F64Vec3 AxisX { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.One, Fixed64.Zero, Fixed64.Zero); } }
45 | public static F64Vec3 AxisY { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.Zero, Fixed64.One, Fixed64.Zero); } }
46 | public static F64Vec3 AxisZ { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.Zero, Fixed64.Zero, Fixed64.One); } }
47 |
48 | // Raw components
49 | public long RawX;
50 | public long RawY;
51 | public long RawZ;
52 |
53 | public F64 X { get { return F64.FromRaw(RawX); } set { RawX = value.Raw; } }
54 | public F64 Y { get { return F64.FromRaw(RawY); } set { RawY = value.Raw; } }
55 | public F64 Z { get { return F64.FromRaw(RawZ); } set { RawZ = value.Raw; } }
56 |
57 | public F64Vec3(F64 x, F64 y, F64 z)
58 | {
59 | RawX = x.Raw;
60 | RawY = y.Raw;
61 | RawZ = z.Raw;
62 | }
63 |
64 | public F64Vec3(F32Vec3 src)
65 | {
66 | RawX = src.RawX << 16;
67 | RawY = src.RawY << 16;
68 | RawZ = src.RawZ << 16;
69 | }
70 |
71 | // raw ctor for internal use only
72 | private F64Vec3(long x, long y, long z)
73 | {
74 | RawX = x;
75 | RawY = y;
76 | RawZ = z;
77 | }
78 |
79 | public static F64Vec3 FromRaw(long rawX, long rawY, long rawZ) { return new F64Vec3(rawX, rawY, rawZ); }
80 | public static F64Vec3 FromInt(int x, int y, int z) { return new F64Vec3(Fixed64.FromInt(x), Fixed64.FromInt(y), Fixed64.FromInt(z)); }
81 | public static F64Vec3 FromFloat(float x, float y, float z) { return new F64Vec3(Fixed64.FromFloat(x), Fixed64.FromFloat(y), Fixed64.FromFloat(z)); }
82 | public static F64Vec3 FromDouble(double x, double y, double z) { return new F64Vec3(Fixed64.FromDouble(x), Fixed64.FromDouble(y), Fixed64.FromDouble(z)); }
83 |
84 | public static F64Vec3 operator -(F64Vec3 a) { return new F64Vec3(-a.RawX, -a.RawY, -a.RawZ); }
85 | public static F64Vec3 operator +(F64Vec3 a, F64Vec3 b) { return new F64Vec3(a.RawX + b.RawX, a.RawY + b.RawY, a.RawZ + b.RawZ); }
86 | public static F64Vec3 operator -(F64Vec3 a, F64Vec3 b) { return new F64Vec3(a.RawX - b.RawX, a.RawY - b.RawY, a.RawZ - b.RawZ); }
87 | public static F64Vec3 operator *(F64Vec3 a, F64Vec3 b) { return new F64Vec3(Fixed64.Mul(a.RawX, b.RawX), Fixed64.Mul(a.RawY, b.RawY), Fixed64.Mul(a.RawZ, b.RawZ)); }
88 | public static F64Vec3 operator /(F64Vec3 a, F64Vec3 b) { return new F64Vec3(Fixed64.DivPrecise(a.RawX, b.RawX), Fixed64.DivPrecise(a.RawY, b.RawY), Fixed64.DivPrecise(a.RawZ, b.RawZ)); }
89 | public static F64Vec3 operator %(F64Vec3 a, F64Vec3 b) { return new F64Vec3(a.RawX % b.RawX, a.RawY % b.RawY, a.RawZ % b.RawZ); }
90 |
91 | public static F64Vec3 operator +(F64 a, F64Vec3 b) { return new F64Vec3(a.Raw + b.RawX, a.Raw + b.RawY, a.Raw + b.RawZ); }
92 | public static F64Vec3 operator +(F64Vec3 a, F64 b) { return new F64Vec3(a.RawX + b.Raw, a.RawY + b.Raw, a.RawZ + b.Raw); }
93 | public static F64Vec3 operator -(F64 a, F64Vec3 b) { return new F64Vec3(a.Raw - b.RawX, a.Raw - b.RawY, a.Raw - b.RawZ); }
94 | public static F64Vec3 operator -(F64Vec3 a, F64 b) { return new F64Vec3(a.RawX - b.Raw, a.RawY - b.Raw, a.RawZ - b.Raw); }
95 | public static F64Vec3 operator *(F64 a, F64Vec3 b) { return new F64Vec3(Fixed64.Mul(a.Raw, b.RawX), Fixed64.Mul(a.Raw, b.RawY), Fixed64.Mul(a.Raw, b.RawZ)); }
96 | public static F64Vec3 operator *(F64Vec3 a, F64 b) { return new F64Vec3(Fixed64.Mul(a.RawX, b.Raw), Fixed64.Mul(a.RawY, b.Raw), Fixed64.Mul(a.RawZ, b.Raw)); }
97 | public static F64Vec3 operator /(F64 a, F64Vec3 b) { return new F64Vec3(Fixed64.DivPrecise(a.Raw, b.RawX), Fixed64.DivPrecise(a.Raw, b.RawY), Fixed64.DivPrecise(a.Raw, b.RawZ)); }
98 | public static F64Vec3 operator /(F64Vec3 a, F64 b) { return new F64Vec3(Fixed64.DivPrecise(a.RawX, b.Raw), Fixed64.DivPrecise(a.RawY, b.Raw), Fixed64.DivPrecise(a.RawZ, b.Raw)); }
99 | public static F64Vec3 operator %(F64 a, F64Vec3 b) { return new F64Vec3(a.Raw % b.RawX, a.Raw % b.RawY, a.Raw % b.RawZ); }
100 | public static F64Vec3 operator %(F64Vec3 a, F64 b) { return new F64Vec3(a.RawX % b.Raw, a.RawY % b.Raw, a.RawZ % b.Raw); }
101 |
102 | public static bool operator ==(F64Vec3 a, F64Vec3 b) { return a.RawX == b.RawX && a.RawY == b.RawY && a.RawZ == b.RawZ; }
103 | public static bool operator !=(F64Vec3 a, F64Vec3 b) { return a.RawX != b.RawX || a.RawY != b.RawY || a.RawZ != b.RawZ; }
104 |
105 | public static F64Vec3 Div(F64Vec3 a, F64 b) { long oob = Fixed64.Rcp(b.Raw); return new F64Vec3(Fixed64.Mul(a.RawX, oob), Fixed64.Mul(a.RawY, oob), Fixed64.Mul(a.RawZ, oob)); }
106 | public static F64Vec3 DivFast(F64Vec3 a, F64 b) { long oob = Fixed64.RcpFast(b.Raw); return new F64Vec3(Fixed64.Mul(a.RawX, oob), Fixed64.Mul(a.RawY, oob), Fixed64.Mul(a.RawZ, oob)); }
107 | public static F64Vec3 DivFastest(F64Vec3 a, F64 b) { long oob = Fixed64.RcpFastest(b.Raw); return new F64Vec3(Fixed64.Mul(a.RawX, oob), Fixed64.Mul(a.RawY, oob), Fixed64.Mul(a.RawZ, oob)); }
108 | public static F64Vec3 Div(F64Vec3 a, F64Vec3 b) { return new F64Vec3(Fixed64.Div(a.RawX, b.RawX), Fixed64.Div(a.RawY, b.RawY), Fixed64.Div(a.RawZ, b.RawZ)); }
109 | public static F64Vec3 DivFast(F64Vec3 a, F64Vec3 b) { return new F64Vec3(Fixed64.DivFast(a.RawX, b.RawX), Fixed64.DivFast(a.RawY, b.RawY), Fixed64.DivFast(a.RawZ, b.RawZ)); }
110 | public static F64Vec3 DivFastest(F64Vec3 a, F64Vec3 b) { return new F64Vec3(Fixed64.DivFastest(a.RawX, b.RawX), Fixed64.DivFastest(a.RawY, b.RawY), Fixed64.DivFastest(a.RawZ, b.RawZ)); }
111 | public static F64Vec3 SqrtPrecise(F64Vec3 a) { return new F64Vec3(Fixed64.SqrtPrecise(a.RawX), Fixed64.SqrtPrecise(a.RawY), Fixed64.SqrtPrecise(a.RawZ)); }
112 | public static F64Vec3 Sqrt(F64Vec3 a) { return new F64Vec3(Fixed64.Sqrt(a.RawX), Fixed64.Sqrt(a.RawY), Fixed64.Sqrt(a.RawZ)); }
113 | public static F64Vec3 SqrtFast(F64Vec3 a) { return new F64Vec3(Fixed64.SqrtFast(a.RawX), Fixed64.SqrtFast(a.RawY), Fixed64.SqrtFast(a.RawZ)); }
114 | public static F64Vec3 SqrtFastest(F64Vec3 a) { return new F64Vec3(Fixed64.SqrtFastest(a.RawX), Fixed64.SqrtFastest(a.RawY), Fixed64.SqrtFastest(a.RawZ)); }
115 | public static F64Vec3 RSqrt(F64Vec3 a) { return new F64Vec3(Fixed64.RSqrt(a.RawX), Fixed64.RSqrt(a.RawY), Fixed64.RSqrt(a.RawZ)); }
116 | public static F64Vec3 RSqrtFast(F64Vec3 a) { return new F64Vec3(Fixed64.RSqrtFast(a.RawX), Fixed64.RSqrtFast(a.RawY), Fixed64.RSqrtFast(a.RawZ)); }
117 | public static F64Vec3 RSqrtFastest(F64Vec3 a) { return new F64Vec3(Fixed64.RSqrtFastest(a.RawX), Fixed64.RSqrtFastest(a.RawY), Fixed64.RSqrtFastest(a.RawZ)); }
118 | public static F64Vec3 Rcp(F64Vec3 a) { return new F64Vec3(Fixed64.Rcp(a.RawX), Fixed64.Rcp(a.RawY), Fixed64.Rcp(a.RawZ)); }
119 | public static F64Vec3 RcpFast(F64Vec3 a) { return new F64Vec3(Fixed64.RcpFast(a.RawX), Fixed64.RcpFast(a.RawY), Fixed64.RcpFast(a.RawZ)); }
120 | public static F64Vec3 RcpFastest(F64Vec3 a) { return new F64Vec3(Fixed64.RcpFastest(a.RawX), Fixed64.RcpFastest(a.RawY), Fixed64.RcpFastest(a.RawZ)); }
121 | public static F64Vec3 Exp(F64Vec3 a) { return new F64Vec3(Fixed64.Exp(a.RawX), Fixed64.Exp(a.RawY), Fixed64.Exp(a.RawZ)); }
122 | public static F64Vec3 ExpFast(F64Vec3 a) { return new F64Vec3(Fixed64.ExpFast(a.RawX), Fixed64.ExpFast(a.RawY), Fixed64.ExpFast(a.RawZ)); }
123 | public static F64Vec3 ExpFastest(F64Vec3 a) { return new F64Vec3(Fixed64.ExpFastest(a.RawX), Fixed64.ExpFastest(a.RawY), Fixed64.ExpFastest(a.RawZ)); }
124 | public static F64Vec3 Exp2(F64Vec3 a) { return new F64Vec3(Fixed64.Exp2(a.RawX), Fixed64.Exp2(a.RawY), Fixed64.Exp2(a.RawZ)); }
125 | public static F64Vec3 Exp2Fast(F64Vec3 a) { return new F64Vec3(Fixed64.Exp2Fast(a.RawX), Fixed64.Exp2Fast(a.RawY), Fixed64.Exp2Fast(a.RawZ)); }
126 | public static F64Vec3 Exp2Fastest(F64Vec3 a) { return new F64Vec3(Fixed64.Exp2Fastest(a.RawX), Fixed64.Exp2Fastest(a.RawY), Fixed64.Exp2Fastest(a.RawZ)); }
127 | public static F64Vec3 Log(F64Vec3 a) { return new F64Vec3(Fixed64.Log(a.RawX), Fixed64.Log(a.RawY), Fixed64.Log(a.RawZ)); }
128 | public static F64Vec3 LogFast(F64Vec3 a) { return new F64Vec3(Fixed64.LogFast(a.RawX), Fixed64.LogFast(a.RawY), Fixed64.LogFast(a.RawZ)); }
129 | public static F64Vec3 LogFastest(F64Vec3 a) { return new F64Vec3(Fixed64.LogFastest(a.RawX), Fixed64.LogFastest(a.RawY), Fixed64.LogFastest(a.RawZ)); }
130 | public static F64Vec3 Log2(F64Vec3 a) { return new F64Vec3(Fixed64.Log2(a.RawX), Fixed64.Log2(a.RawY), Fixed64.Log2(a.RawZ)); }
131 | public static F64Vec3 Log2Fast(F64Vec3 a) { return new F64Vec3(Fixed64.Log2Fast(a.RawX), Fixed64.Log2Fast(a.RawY), Fixed64.Log2Fast(a.RawZ)); }
132 | public static F64Vec3 Log2Fastest(F64Vec3 a) { return new F64Vec3(Fixed64.Log2Fastest(a.RawX), Fixed64.Log2Fastest(a.RawY), Fixed64.Log2Fastest(a.RawZ)); }
133 | public static F64Vec3 Sin(F64Vec3 a) { return new F64Vec3(Fixed64.Sin(a.RawX), Fixed64.Sin(a.RawY), Fixed64.Sin(a.RawZ)); }
134 | public static F64Vec3 SinFast(F64Vec3 a) { return new F64Vec3(Fixed64.SinFast(a.RawX), Fixed64.SinFast(a.RawY), Fixed64.SinFast(a.RawZ)); }
135 | public static F64Vec3 SinFastest(F64Vec3 a) { return new F64Vec3(Fixed64.SinFastest(a.RawX), Fixed64.SinFastest(a.RawY), Fixed64.SinFastest(a.RawZ)); }
136 | public static F64Vec3 Cos(F64Vec3 a) { return new F64Vec3(Fixed64.Cos(a.RawX), Fixed64.Cos(a.RawY), Fixed64.Cos(a.RawZ)); }
137 | public static F64Vec3 CosFast(F64Vec3 a) { return new F64Vec3(Fixed64.CosFast(a.RawX), Fixed64.CosFast(a.RawY), Fixed64.CosFast(a.RawZ)); }
138 | public static F64Vec3 CosFastest(F64Vec3 a) { return new F64Vec3(Fixed64.CosFastest(a.RawX), Fixed64.CosFastest(a.RawY), Fixed64.CosFastest(a.RawZ)); }
139 |
140 | public static F64Vec3 Pow(F64Vec3 a, F64 b) { return new F64Vec3(Fixed64.Pow(a.RawX, b.Raw), Fixed64.Pow(a.RawY, b.Raw), Fixed64.Pow(a.RawZ, b.Raw)); }
141 | public static F64Vec3 PowFast(F64Vec3 a, F64 b) { return new F64Vec3(Fixed64.PowFast(a.RawX, b.Raw), Fixed64.PowFast(a.RawY, b.Raw), Fixed64.PowFast(a.RawZ, b.Raw)); }
142 | public static F64Vec3 PowFastest(F64Vec3 a, F64 b) { return new F64Vec3(Fixed64.PowFastest(a.RawX, b.Raw), Fixed64.PowFastest(a.RawY, b.Raw), Fixed64.PowFastest(a.RawZ, b.Raw)); }
143 | public static F64Vec3 Pow(F64 a, F64Vec3 b) { return new F64Vec3(Fixed64.Pow(a.Raw, b.RawX), Fixed64.Pow(a.Raw, b.RawY), Fixed64.Pow(a.Raw, b.RawZ)); }
144 | public static F64Vec3 PowFast(F64 a, F64Vec3 b) { return new F64Vec3(Fixed64.PowFast(a.Raw, b.RawX), Fixed64.PowFast(a.Raw, b.RawY), Fixed64.PowFast(a.Raw, b.RawZ)); }
145 | public static F64Vec3 PowFastest(F64 a, F64Vec3 b) { return new F64Vec3(Fixed64.PowFastest(a.Raw, b.RawX), Fixed64.PowFastest(a.Raw, b.RawY), Fixed64.PowFastest(a.Raw, b.RawZ)); }
146 | public static F64Vec3 Pow(F64Vec3 a, F64Vec3 b) { return new F64Vec3(Fixed64.Pow(a.RawX, b.RawX), Fixed64.Pow(a.RawY, b.RawY), Fixed64.Pow(a.RawZ, b.RawZ)); }
147 | public static F64Vec3 PowFast(F64Vec3 a, F64Vec3 b) { return new F64Vec3(Fixed64.PowFast(a.RawX, b.RawX), Fixed64.PowFast(a.RawY, b.RawY), Fixed64.PowFast(a.RawZ, b.RawZ)); }
148 | public static F64Vec3 PowFastest(F64Vec3 a, F64Vec3 b) { return new F64Vec3(Fixed64.PowFastest(a.RawX, b.RawX), Fixed64.PowFastest(a.RawY, b.RawY), Fixed64.PowFastest(a.RawZ, b.RawZ)); }
149 |
150 | public static F64 Length(F64Vec3 a) { return F64.FromRaw(Fixed64.Sqrt(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY) + Fixed64.Mul(a.RawZ, a.RawZ))); }
151 | public static F64 LengthFast(F64Vec3 a) { return F64.FromRaw(Fixed64.SqrtFast(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY) + Fixed64.Mul(a.RawZ, a.RawZ))); }
152 | public static F64 LengthFastest(F64Vec3 a) { return F64.FromRaw(Fixed64.SqrtFastest(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY) + Fixed64.Mul(a.RawZ, a.RawZ))); }
153 | public static F64 LengthSqr(F64Vec3 a) { return F64.FromRaw(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY) + Fixed64.Mul(a.RawZ, a.RawZ)); }
154 | public static F64Vec3 Normalize(F64Vec3 a) { F64 ooLen = F64.FromRaw(Fixed64.RSqrt(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY) + Fixed64.Mul(a.RawZ, a.RawZ))); return ooLen * a; }
155 | public static F64Vec3 NormalizeFast(F64Vec3 a) { F64 ooLen = F64.FromRaw(Fixed64.RSqrtFast(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY) + Fixed64.Mul(a.RawZ, a.RawZ))); return ooLen * a; }
156 | public static F64Vec3 NormalizeFastest(F64Vec3 a) { F64 ooLen = F64.FromRaw(Fixed64.RSqrtFastest(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY) + Fixed64.Mul(a.RawZ, a.RawZ))); return ooLen * a; }
157 |
158 | public static F64 Dot(F64Vec3 a, F64Vec3 b) { return F64.FromRaw(Fixed64.Mul(a.RawX, b.RawX) + Fixed64.Mul(a.RawY, b.RawY) + Fixed64.Mul(a.RawZ, b.RawZ)); }
159 | public static F64 Distance(F64Vec3 a, F64Vec3 b) { return Length(a - b); }
160 | public static F64 DistanceFast(F64Vec3 a, F64Vec3 b) { return LengthFast(a - b); }
161 | public static F64 DistanceFastest(F64Vec3 a, F64Vec3 b) { return LengthFastest(a - b); }
162 |
163 | public static F64Vec3 Min(F64Vec3 a, F64Vec3 b) { return new F64Vec3(Fixed64.Min(a.RawX, b.RawX), Fixed64.Min(a.RawY, b.RawY), Fixed64.Min(a.RawZ, b.RawZ)); }
164 | public static F64Vec3 Max(F64Vec3 a, F64Vec3 b) { return new F64Vec3(Fixed64.Max(a.RawX, b.RawX), Fixed64.Max(a.RawY, b.RawY), Fixed64.Max(a.RawZ, b.RawZ)); }
165 |
166 | public static F64Vec3 Clamp(F64Vec3 a, F64 min, F64 max)
167 | {
168 | return new F64Vec3(
169 | Fixed64.Clamp(a.RawX, min.Raw, max.Raw),
170 | Fixed64.Clamp(a.RawY, min.Raw, max.Raw),
171 | Fixed64.Clamp(a.RawZ, min.Raw, max.Raw));
172 | }
173 |
174 | public static F64Vec3 Clamp(F64Vec3 a, F64Vec3 min, F64Vec3 max)
175 | {
176 | return new F64Vec3(
177 | Fixed64.Clamp(a.RawX, min.RawX, max.RawX),
178 | Fixed64.Clamp(a.RawY, min.RawY, max.RawY),
179 | Fixed64.Clamp(a.RawZ, min.RawZ, max.RawZ));
180 | }
181 |
182 | public static F64Vec3 Lerp(F64Vec3 a, F64Vec3 b, F64 t)
183 | {
184 | long tb = t.Raw;
185 | long ta = Fixed64.One - tb;
186 | return new F64Vec3(
187 | Fixed64.Mul(a.RawX, ta) + Fixed64.Mul(b.RawX, tb),
188 | Fixed64.Mul(a.RawY, ta) + Fixed64.Mul(b.RawY, tb),
189 | Fixed64.Mul(a.RawZ, ta) + Fixed64.Mul(b.RawZ, tb));
190 | }
191 |
192 | public static F64Vec3 Cross(F64Vec3 a, F64Vec3 b)
193 | {
194 | return new F64Vec3(
195 | Fixed64.Mul(a.RawY, b.RawZ) - Fixed64.Mul(a.RawZ, b.RawY),
196 | Fixed64.Mul(a.RawZ, b.RawX) - Fixed64.Mul(a.RawX, b.RawZ),
197 | Fixed64.Mul(a.RawX, b.RawY) - Fixed64.Mul(a.RawY, b.RawX));
198 | }
199 |
200 | public bool Equals(F64Vec3 other)
201 | {
202 | return (this == other);
203 | }
204 |
205 | public override bool Equals(object obj)
206 | {
207 | if (!(obj is F64Vec3))
208 | return false;
209 | return ((F64Vec3)obj) == this;
210 | }
211 |
212 | public override string ToString()
213 | {
214 | return "(" + Fixed64.ToString(RawX) + ", " + Fixed64.ToString(RawY) + ", " + Fixed64.ToString(RawZ) + ")";
215 | }
216 |
217 | public override int GetHashCode()
218 | {
219 | return RawX.GetHashCode() ^ RawY.GetHashCode() * 7919 ^ RawZ.GetHashCode() * 4513;
220 | }
221 | }
222 | }
223 |
--------------------------------------------------------------------------------