Showing posts with label Math. Show all posts
Showing posts with label Math. Show all posts

Friday, October 13, 2017

System.Math extension methods

Why

Because I've lately started to think a little more functional, this also allows for nice chaining of calls to the Math class.

How to use

Say you want to calculate the angle between 2 vectors by taking arc cosine of our dot product to give us the angle:
With normal Math usage..
var dot = vector1.X* vector2.X + vector1.Y * vector2.Y + vector1.Z * vector2.Z;
double radians = Math.Acos(dot);
Same example with our extensions
var dot = vector1.X* vector2.X + vector1.Y * vector2.Y + vector1.Z * vector2.Z;
double radians = dot.Acos();

The above example but with a method to calculate the dot product.
double radians = vector1.Dot(vector2).Acos();


Code for your pleasure

public static class MathExtensions
{
    public static decimal Abs(this decimal d)
    {
        return Math.Abs(d);
    }
    public static float Abs(this float d)
    {
        return Math.Abs(d);
    }
    public static double Abs(this double d)
    {
        return Math.Abs(d);
    }
    public static int Abs(this int d)
    {
        return Math.Abs(d);
    }
    public static long Abs(this long d)
    {
        return Math.Abs(d);
    }
    public static short Abs(this short d)
    {
        return Math.Abs(d);
    }
    public static short Abs(this byte d)
    {
        return Math.Abs(d);
    }
    public static decimal Max(this decimal d, decimal x)
    {
        return Math.Max(d, x);
    }
    public static float Max(this float d, float x)
    {
        return Math.Max(d, x);
    }
    public static double Max(this double d, double x)
    {
        return Math.Max(d, x);
    }
    public static int Max(this int d, int x)
    {
        return Math.Max(d, x);
    }
    public static long Max(this long d, long x)
    {
        return Math.Max(d, x);
    }
    public static short Max(this short d, short x)
    {
        return Math.Max(d, x);
    }
    public static short Max(this byte d, byte x)
    {
        return Math.Max(d, x);
    }
    public static decimal Min(this decimal d, decimal x)
    {
        return Math.Min(d, x);
    }
    public static float Min(this float d, float x)
    {
        return Math.Min(d, x);
    }
    public static double Min(this double d, double x)
    {
        return Math.Min(d, x);
    }
    public static int Min(this int d, int x)
    {
        return Math.Min(d, x);
    }
    public static long Min(this long d, long x)
    {
        return Math.Min(d, x);
    }
    public static short Min(this short d, short x)
    {
        return Math.Min(d, x);
    }
    public static short Min(this byte d, byte x)
    {
        return Math.Min(d, x);
    }

    public static int Sign(this decimal d)
    {
        return Math.Sign(d);
    }
    public static int Sign(this float d)
    {
        return Math.Sign(d);
    }
    public static int Sign(this double d)
    {
        return Math.Sign(d);
    }
    public static int Sign(this int d)
    {
        return Math.Sign(d);
    }
    public static int Sign(this long d)
    {
        return Math.Sign(d);
    }
    public static int Sign(this short d)
    {
        return Math.Sign(d);
    }
    public static int Sign(this byte d)
    {
        return Math.Sign(d);
    }
    public static decimal Floor(this decimal d)
    {
        return Math.Floor(d);
    }
    public static double Floor(this float d)
    {
        return Math.Floor(d);
    }
    public static double Floor(this double d)
    {
        return Math.Floor(d);
    }
    public static decimal Round(this decimal d)
    {
        return Math.Round(d);
    }
    public static decimal Round(this decimal d, int x)
    {
        return Math.Round(d, x);
    }
    public static decimal Round(this decimal d, int x, MidpointRounding mr)
    {
        return Math.Round(d, x, mr);
    }
    public static decimal Round(this decimal d, MidpointRounding mr)
    {
        return Math.Round(d, mr);
    }
    public static double Round(this float d)
    {
        return Math.Round(d);
    }
    public static double Round(this double d)
    {
        return Math.Round(d);
    }
    public static double Round(this double d, int x)
    {
        return Math.Round(d, x);
    }
    public static double Round(this double d, int x, MidpointRounding mr)
    {
        return Math.Round(d, x, mr);
    }
    public static double Round(this double d, MidpointRounding mr)
    {
        return Math.Round(d, mr);
    }
    public static decimal Ceiling(this decimal d)
    {
        return Math.Ceiling(d);
    }
    public static double Ceiling(this float d)
    {
        return Math.Ceiling(d);
    }
    public static double Ceiling(this double d)
    {
        return Math.Ceiling(d);
    }
    public static double Acos(this double d)
    {
        return Math.Acos(d);
    }
    public static double Asin(this double d)
    {
        return Math.Asin(d);
    }
    public static double Atan(this double d)
    {
        return Math.Atan(d);
    }
    public static double Atan2(this double d, double x)
    {
        return Math.Atan2(d, x);
    }
    public static double Cos(this double d)
    {
        return Math.Cos(d);
    }
    public static double Cosh(this double d)
    {
        return Math.Cosh(d);
    }
    public static double Exp(this double d)
    {
        return Math.Exp(d);
    }
    public static double Sin(this double d)
    {
        return Math.Sin(d);
    }
    public static double Sinh(this double d)
    {
        return Math.Sinh(d);
    }
    public static double Tan(this double d)
    {
        return Math.Tan(d);
    }
    public static double Tanh(this double d)
    {
        return Math.Tanh(d);
    }
    public static long BigMul(this int a, int b)
    {
        return Math.BigMul(a, b);
    }
    public static double IEEERemainder(this double d, double y)
    {
        return Math.IEEERemainder(d, y);
    }
    public static double Log(this double d)
    {
        return Math.Log(d);
    }
    public static double Log(this double d, double x)
    {
        return Math.Log(d, x);
    }
    public static double Log10(this double d)
    {
        return Math.Log10(d);
    }
    public static double Sqrt(this double d)
    {
        return Math.Sqrt(d);
    }
    public static decimal Truncate(this decimal d)
    {
        return Math.Truncate(d);
    }
    public static double Truncate(this double d)
    {
        return Math.Truncate(d);
    }
    public static double Pow(this double d, double power)
    {
        return Math.Pow(d, power);
    }
}

Quite a lot of overloads, I think I got them all but if you find that I've missed one, please comment on this post and I will add it!

All code provided as-is. This is copied from my own code-base, May need some additional programming to work. Use for whatever you want, how you want! If you find this helpful, please leave a comment, not required but appreciated! :)

Hope this helps someone out there!


Friday, February 3, 2017

OpenGL 4 with OpenTK in C# Part 7: Vectors and Matrices


In this post we will look at vector and matrix math with OpenTK.

This is part 7 of my series on OpenGL4 with OpenTK.
For other posts in this series:
OpenGL 4 with OpenTK in C# Part 1: Initialize the GameWindow
OpenGL 4 with OpenTK in C# Part 2: Compiling shaders and linking them
OpenGL 4 with OpenTK in C# Part 3: Passing data to shaders
OpenGL 4 with OpenTK in C# Part 4: Refactoring and adding error handling
OpenGL 4 with OpenTK in C# Part 5: Buffers and Triangle
OpenGL 4 with OpenTK in C# Part 6: Rotations and Movement of objects
OpenGL 4 with OpenTK in C# Part 7: Vectors and Matrices
OpenGL 4 with OpenTK in C# Part 8: Drawing multiple objects
OpenGL 4 with OpenTK in C# Part 9: Texturing
OpenGL 4 with OpenTK in C# Part 10: Asteroid Invaders
Basic bullet movement patterns in Asteroid Invaders
OpenGL 4 with OpenTK in C# Part 11: Mipmap
OpenGL 4 with OpenTK in C# Part 12: Basic Moveable Camera
OpenGL 4 with OpenTK in C# Part 13: IcoSphere
OpenGL 4 with OpenTK in C# Part 14: Basic Text
OpenGL 4 with OpenTK in C# Part 15: Object picking by mouse

As stated in the previous post, I am in no way an expert in OpenGL, or math. I write these posts as a way to learn and if someone else finds these posts useful then all the better :)
If you think that the progress is slow, then know that I am a slow learner :P
This part will not really build on any previous code, we will instead look at the VectorN and MatrixN structs in OpenTK and see how to do basic calculations with help of their methods.

Vectors

Lets start with a definition.
In mathematics, physics, and engineering, a Euclidean vector (sometimes called a geometric or spatial vector, or—as here—simply a vector) is a geometric object that has magnitude (or length) and direction - wikipedia
So the difference between a point (x, y, z) and a vector (x, y, z) is that where the point is just a point in space, the vector has its magnitude (distance from origin (0, 0, 0))
The distance can be calculated with following method:
public static double Magnitude(Vector3 v)
{
    return Math.Sqrt(Math.Pow(v.X, 2.0) + Math.Pow(v.Y, 2.0) + Math.Pow(v.Z, 2.0));
}
As we are using OpenTK, it already provides this for us.
var v = new Vector3(10, 10, 10);
v.Length;
So a point in space (x, y, z) can represent both a point, a vector and a vertex. This is great as we have the need them all in 3D.

Unit vectors

Unit vectors always have the magnitude 1.0.
One great usage for them is to store only direction, without the magnitude. They are also great for calculations that include the magnitude as it will always be 1.
In OpenTK there are 2 ways to normalize and 1 way to estimate a normalization if accuracy is not priority.
[TestMethod]
public void VectorTest_NormalizeVsNormalized()
{
    var normalize = new Vector4(2, 2, 2, 2);
    var fast = new Vector4(2, 2, 2, 2);
    var normalized = normalize.Normalized(); // returns a copy that is normalized
    normalize.Normalize(); // normalizes this vector
    fast.NormalizeFast(); // estimates a normalized vector, inaccurate
    Assert.AreEqual(normalize, normalized);
    // AreEqual fails Expected:<(0,5; 0,5; 0,5; 0,5)>. Actual:<(0,4991541; 0,4991541; 0,4991541; 0,4991541)>
    Assert.AreNotEqual(normalized, fast);  
}

Scalar multiplication
If you have a unit vector with a direction, you can scale it by multiplying it with a scalar value, in the example below we scale scale it with the original vectors length to get a vector that is equal to the original.
[TestMethod]
public void VectorTest_NormalizeMultipliedByLength()
{
    var original = new Vector4(2, 2, 2, 2);
    var normalized = original.Normalized();
    var scaled = normalized * original.Length;
    Assert.AreEqual(original, scaled);
}

Vector operations

Vectors can be added, subtracted multiplied just as any numerical value.
[TestMethod]
public void VectorTest_Addition()
{
    var v1 = new Vector4(2, 2, 2, 1);
    var v2 = new Vector4(2, 2, 2, 1);
    var actual = v1 + v2;
    Assert.AreEqual(4, actual.X);
    Assert.AreEqual(4, actual.Y);
    Assert.AreEqual(4, actual.Z);
    Assert.AreEqual(2, actual.W);
}
[TestMethod]
public void VectorTest_Subtraction()
{
    var v1 = new Vector4(2, 2, 2, 1);
    var v2 = new Vector4(2, 2, 2, 1);
    var actual = v1 - v2;
    Assert.AreEqual(0, actual.X);
    Assert.AreEqual(0, actual.Y);
    Assert.AreEqual(0, actual.Z);
    Assert.AreEqual(0, actual.W);
}
[TestMethod]
public void VectorTest_Multiplication()
{
    var v1 = new Vector4(2, 2, 2, 1);
    var v2 = new Vector4(2, 2, 2, 1);
    var actual = v1 * v2;
    Assert.AreEqual(4, actual.X);
    Assert.AreEqual(4, actual.Y);
    Assert.AreEqual(4, actual.Z);
    Assert.AreEqual(1, actual.W);
}
Vectors can only be divided with scalars, don't ask me why. Tried to understand a thread on it over at Physics Stack Exchange.
[TestMethod]
public void VectorTest_Division()
{
    var v1 = new Vector4(2, 2, 2, 1);
    var actual = v1 / 2;
    Assert.AreEqual(1, actual.X);
    Assert.AreEqual(1, actual.Y);
    Assert.AreEqual(1, actual.Z);
    Assert.AreEqual(0.5f, actual.W);
}

Dot Product (inner product)
Geometrically, it is the product of the Euclidean magnitudes of the two vectors and the cosine of the angle between them -wikipedia
If the two vectors are unit vectors, the value will be between -1 and 1, i.e. directly equal to the cosine of the angle between them.
Ok, so what will we use this for? Well, lightning..
So, how to do it
[TestMethod]
public void VectorTest_DotProduct()
{
    var v1 = new Vector2(1, 1).Normalized();
    var v2 = new Vector2(0, 1).Normalized();
    var actual = Vector2.Dot(v1, v2);
    // we know that the angle is 45 degrees, just need the radians
    Assert.AreEqual((float)(Math.Cos(45 * (Math.PI / 180f))), actual);
}
The order of vectors does not matter for the dot product.

Cross Product (vector product)
Lets check wikipedia here as well
Given two linearly independent vectors a and b, the cross product, a × b, is a vector that is perpendicular to both a and b and therefore normal to the plane containing them -wikipedia
The order of vectors matters for the cross product, if you change the order the resulting vector would be inversed.
[TestMethod]
public void VectorTest_CrossProduct()
{
    var v1 = new Vector3(1, 0, 0).Normalized();
    var v2 = new Vector3(0, 1, 0).Normalized();
    var actual = Vector3.Cross(v1, v2);

    Assert.AreEqual(0, actual.X);
    Assert.AreEqual(0, actual.Y);
    Assert.AreEqual(1, actual.Z);
}
[TestMethod]
public void VectorTest_CrossProduct_Inverse()
{
    var v1 = new Vector3(1, 0, 0).Normalized();
    var v2 = new Vector3(0, 1, 0).Normalized();
    var actual = Vector3.Cross(v2, v1);

    Assert.AreEqual(0, actual.X);
    Assert.AreEqual(0, actual.Y);
    Assert.AreEqual(-1, actual.Z);
}

Matrices

In mathematics, a matrix (plural matrices) is a rectangular array of numbers, symbols, or expressions, arranged in rows and columns. -wikipedia
We saw in the previous post that matrices can be really handy when working with 3D when we did the rotations and translations (move) of the object.

The reason we are using a Matrix4 object to store our transformation matrices in is because they can store both the rotation part and the translation part in the same structure.

Identity

Basically a square matrix with its diagonal set to 1 and the rest to 0. It is its own inverse. For more info, wikipedia.
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
In OpenTK
var m = Matrix4.Identity;

Translation

1 0 0 tx
0 1 0 ty
0 0 1 tz
0 0 0 1
Built from a identity matrix, the last column contains the x, y, z values of where you want to move the vertex.
[TestMethod]
public void MatrixTest_Translate()
{
    var translate = Matrix4.CreateTranslation(1, 1, 1);

    var v1 = new Vector4(0, 0, 0, 1);
    var actual = Vector4.Transform(v1, translate);
    Assert.AreEqual(1, actual.X);
    Assert.AreEqual(1, actual.Y);
    Assert.AreEqual(1, actual.Z);
}
Or in a shader, just v * m as we did in previous post.

Rotation

Rotation.X
1 0 0 0
0 cos(a) sin(a) 0
0 -sin(a) cos(a) 0
0 0 0 1
Rotation.Y
cos(a) 0 -sin(a) 0
0 1 0 0
sin(a) 0 cos(a) 0
0 0 0 1
Rotation.Z
cos(a) -sin(a) 0 0
sin(a) cos(a) 0 0
0 0 1 0
0 0 0 1
Where a being the angle.
All these can be multiplied together to form a rotation matrix for all x, y, z.
var rx = Matrix4.CreateRotationX(k * 13.0f);
var ry = Matrix4.CreateRotationY(k * 13.0f);
var rz = Matrix4.CreateRotationZ(k * 3.0f);
var rotation = rx*ry*rz;
Not going to write a test for rotation just now :)

Scaling

sx 0 0 0
0 sy 0 0
0 0 sz 0
0 0 0 1
To scale an object, we generate a scaling matrix that is basically a diagonal with the x, y and z scaling factors set for the 3 first items.
[TestMethod]
public void MatrixTest_Scale()
{
    var translate = Matrix4.CreateScale(2, 4, 8);

    var v1 = new Vector4(2, 2, 2, 1);
    var actual = Vector4.Transform(v1, translate);
    Assert.AreEqual(4, actual.X);
    Assert.AreEqual(8, actual.Y);
    Assert.AreEqual(16, actual.Z);
}

So this has been a long post, at least I am starting to get a hang of things. Hopefully this has helped someone out there.
To get you in a better mood after all the theory, here is a GIF of 2 of our cats fighting.. Again.