Introduction
For years I have seen people struggle with vector mathematics. This guide should walk you through the creation of a reusable Vector3
type and the mathematics behind it all. The postfixed 3 simply refers to the vector being in 3dimensions (x,y,z).
The code is not designed to be fast or efficient but is to be as simple and understandable as possible. To this end, and as a personal preference, the Vector3 type is packed with relevent functionality and multiple interfaces to methods (e.g. static and nonstatic variants of methods). Many would deem this bloated code, however, I think that it makes the code programmer friendly. Obviously, as a projects grows I would tend to refactor functionality back out as I create objects and types for which the functions better fit. I see this as achieving maximum cohesion and minimising coupling and dependencies.
I have used the Cartesian coordinate system in threedimensions (i.e. three perpendicular axis of x, y and z) and Euclidian geometry. Don't worry about these terms; they are just the formal names for some of the maths covered at senior school. The vector space is volumetric (cube); note that you can use other vector spaces, such as a cylindrical space where one axis (usually z) relates to the radius of the cylinder.
You may have guessed that computers are quite slow with this type of math. Matrix mathematics is more efficient but much harder to understand. You will need a basic grasp of trigonometry and algebra to understand this guide.
Unless stated otherwise I assume that the vector is positional, originating at point (0,0,0). Alternatives to positional vectors are: unit vectors, which can be interpreted as either having no magnitude or an infinite magnitude; and vector pairs where the origin of the vector is another vector, magnitude being a distance from the origin vector.
Please note that this guide is extremely verbose and may seem patronising to experienced C# programmers. Please do not be offended, I have written the guide for a wide audience.
A quick glossary:
 Operator, this is the symbol used to define an operation such as plus (
+
) in (a+b)
 Operand, these are the variables used in an operation such as (a) and (b) in (a+b). The lefthandside (LHS) operand is (a) where as the righthandside (RHS) operand is (b).
All of the equations in this guide assume A (or v1) and B (or v2) can be broken down into:
(a b c) = A (d e f) = B
Using the code
To begin with, let us define how the vector information will be stored. I don't often create structs when coding, but for our Vector3
this is perfect. If you are reading this article you probably already know that a vector represents values along a number of axes. For this tutorial we will be developing a threedimensional type so... thee variables and three axis.
public struct Vector3
{
private double x, y, z;
}
What orientation are the axes in? Being from a visualization background I always assume:
You may have noticed that Z is negative as you look down the axis. This is a common convention in graphics libraries such as OpenGL. This will become important later on when considering pitch, roll and yaw methods.
A quick diversion: Why a struct instead of a class?
The differences between struct and class:
 A struct is a value type created on the stack instead of the heap, thus reducing garbage collection overheads.
 They are passed by value not by reference.
 They are created and disposed of quickly and efficiently.
 You cannot derive other types from them (i.e. noninheritable).
 They are only appropriate for types with a small number of members (variables). Microsoft recommends a struct should be less than 16 bytes.
 You do not need the new keyword to instantiate a struct.
Basically, it looks like, acts like, and is a primitive type. Although, there is no reason why the vector type could not be created as a class. A drawback of developing a struct is that collection classes in the .NET framework cast structs as classes. This means that a large collection of Vector3
's will have a high casting overhead.
A more indepth article on structs has been written by S. Senthil Kumar here
Accessing the variables
Did you notice that the variables were private?
While I have chosen to build a struct, I habitually hide my variables and create public accessor and mutator properties. This is not strictly good practice for structs, but I have created them in case I feel the need to convert to a class at a later date (this is good practice for class structures).
In addition to the properties, an array style interface has also been provided. This allows the user call the Vector3
with myVector[x]
, myVector[y]
, myVector[z]
. Additionally, the user can get or set all of the components as an array using the Array
property, i.e. myVector.Array = {x,y,z}
.
public double X
{
get{return x;}
set{x = value;}
}
public double Y
{
get{return y;}
set{y = value;}
}
public double Z
{
get{return z;}
set{z = value;}
}
public double[] Array
{
get{return new double[] {x,y,z};}
set
{
if(value.Length == 3)
{
x = value[0];
y = value[1];
z = value[2];
}
else
{
throw new ArgumentException(THREE_COMPONENTS);
}
}
}
public double this[ int index ]
{
get
{
switch (index)
{
case 0: {return X; }
case 1: {return Y; }
case 2: {return Z; }
default: throw new ArgumentException(THREE_COMPONENTS, "index");
}
}
set
{
switch (index)
{
case 0: {X = value; break;}
case 1: {Y = value; break;}
case 2: {Z = value; break;}
default: throw new ArgumentException(THREE_COMPONENTS, "index");
}
}
}
private const string THREE_COMPONENTS =
"Array must contain exactly three components, (x,y,z)";
A property has also been provided to access and manipulate the magnitude of a vector. The magnitude (or absolute value) of a vector is its length , irrespective of direction and can be determined using the formula:
The SumComponentSquares
method (used below) can be seen later in this article. Please note that the magnitude is always positive and the magnitude of a vector (0,0,0) cannot be set.
public double Magnitude
{
get
{
return Math.Sqrt ( SumComponentSqrs() );
}
set
{
if (value < 0)
{ throw new ArgumentOutOfRangeException("value", value,
NEGATIVE_MAGNITUDE); }
if (this == origin)
{ throw new ArgumentException(ORAGIN_VECTOR_MAGNITUDE, "this"); }
this = this * (value / Magnitude);
}
}
private const string NEGATIVE_MAGNITUDE =
"The magnitude of a Vector must be a positive value, (i.e. greater than 0)";
private const string ORAGIN_VECTOR_MAGNITUDE =
"Cannot change the magnitude of Vector(0,0,0)";
Constructing a Vector3
To construct the type using typical class syntax the following constructor methods have been provided:
public Vector3(double x, double y, double z)
{
this.x = 0;
this.y = 0;
this.z = 0;
X = x;
Y = y;
Z = z;
}
public Vector3 (double[] xyz)
{
this.x = 0;
this.y = 0;
this.z = 0;
Array = xyz;
}
public Vector3(Vector3 v1)
{
this.x = 0;
this.y = 0;
this.z = 0;
X = v1.X;
Y = v1.Y;
Z = v1.Z;
}
You may be wondering why I have set x, y, and z to 0 before assigning values. A struct will not allow properties (uppercase X, Y, Z) to be accessed before each of the variables (lowercase x, y, z) have been initialized. As Michał Bryłka commented, it is unusual to use properties from the constructor when assigning values. As a matter of personal taste, I always try to use properties in this way. It ensures that any validation code written into the set method is picked up by the constructor. I find that this is helpful when maintaining code. In the Vector3
type, as it stands, there is no validation in the properties so the issue is academic. Either paradigm would be valid.
Operator overloading
We now have a framework for storing, accessing and mutating the Vector3
and its components (x,y,z). We can now consider mathematical operations applicable to a vector. Let's begin by overloading the basic mathematical operators.
Overloading operators allows the programmer to define how a type is used in the code. Take, for example, the plus operator (+
). For numeric types this would suggest addition of two numbers. For strings it represents the concatenation of two strings. Operator overloading is of huge benefit to programmers when describing how a type should interact with the system. In C# the following operators can be overloaded:
 Addition, concatenation, and reinforcement (
+
)
 Subtraction and negation (

)
 Logical negation (
!
)
 Bitwise complement (
~
)
 Increment (
++
)
 Decrement (

)
 Boolean Truth (
true
)
 Boolean false (
false
)
 Multiplication (
*
)
 Division (
/
)
 Division remainder (
%
)
 Logical AND (
&
)
 Logical OR (

)
 Logical ExclusiveOR (
^
)
 Binary shift left (
<<
)
 Binary shift right (
>>
)
 Equality operators, equal and notequal (
==
and !=
)
 Difference\comparison operators, lessthan and greaterthan(
<
and >
)
 Difference\comparison operators, lessthan or equalto and greaterthan or equalto(
<=
and >=
)
Addition (v3 = v1 + v2)
The addition of two vectors is achieved by simply adding the x, y, and z components of one vector to the other (i.e. x+x, y+y, z+z).
public static Vector3 operator+(Vector3 v1, Vector3 v2)
{
return
(
new Vector3
(
v1.X + v2.X,
v1.Y + v2.Y,
v1.Z + v2.Z
)
);
}
Subtraction (v3 = v1  v2)
Subtraction of two vectors is simply the subtraction of the x, y, and z components of one vector from the other (i.e. xx, yy, zz).
public static Vector3 operator(Vector3 v1, Vector3 v2 )
{
return
(
new Vector3
(
v1.X  v2.X,
v1.Y  v2.Y,
v1.Z  v2.Z
)
);
}
Negation (v2 = v1)
Negation of a vector inverts its direction. This is achieved by simply negating each of the component parts of the vector.
public static Vector3 operator(Vector3 v1)
{
return
(
new Vector3
(
 v1.X,
 v1.Y,
 v1.Z
)
);
}
Reinforcement (v2 = +v1)
Reinforcement of a vector actually does nothing but return the original vector given the rules of addition, (i.e. +x = x and ++x = +x).
public static Vector3 operator+(Vector3 v1)
{
return
(
new Vector3
(
+ v1.X,
+ v1.Y,
+ v1.Z
)
);
}
Comparison (<, >, <=, and >=)
When comparing two vectors we use magnitude (as seen previously).
Lessthan (result = v1 < v2)
Lessthan compares two vectors, returning true only if the magnitude of the lefthandside vector (v1) is less than the magnitude of the other (v2).
public static bool operator<(Vector3 v1, Vector3 v2)
{
return v1.Magnitude < v2.Magnitude;
}
Lessthan or Equalto (result = v1 <= v2)
Lessthan or equalto compares two vectors returning true only if the magnitude of the lefthandside vector (v1) is less than the magnitude of the other (v2) or the two magnitudes are equal.
public static bool operator<=(Vector3 v1, Vector3 v2)
{
return v1.Magnitude <= v2.Magnitude;
}
Greaterthan (result = v1 > v2)
Greaterthan compares two vectors returning true only if the magnitude of the lefthandside vector (v1) is greater than the magnitude of the other (v2).
public static bool operator>(Vector3 v1, Vector3 v2)
{
return v1.Magnitude > v2.Magnitude;
}
Greaterthan or Equalto (result = v1 >= v2)
Greaterthan or equalto compares two vectors returning true only if the magnitude of the lefthandside vector (v1) is greater than the magnitude of the other (v2) or the two magnitudes are equal.
public static bool operator>(Vector3 v1, Vector3 v2)
{
return v1.Magnitude >= v2.Magnitude;
}
Equality (result = v1 == v2)
To check if two vectors are equal we simply check the component pairs. We AND the results so that any pair which is not equal will result in false.
public static bool operator==(Vector3 v1, Vector3 v2)
{
return
(
(v1.X == v2.X)&&
(v1.Y == v2.Y)&&
(v1.Z == v2.Z)
);
}
There has been a lot of discussion in the comments of this article regarding the use of a threshold when calculating equality. The need for this stems from precision problems associated with storing fractional numbers (i.e. float, double, and decimal variables). I personally agree with red Baron who suggests:
"... you should not implement a tolerance value inside your code.
What is a suitable value for this tolerance?
It is depending on the problem ..."
However, to compromise I have implemented equality tolerance as a constant which can be set to zero (ignored).
public static bool operator==(Vector3 v1, Vector3 v2)
{
return
(
Math.Abs(v1.X  v2.X) <= EqualityTolerence &&
Math.Abs(v1.Y  v2.Y) <= EqualityTolerence &&
Math.Abs(v1.Z  v2.Z) <= EqualityTolerence
);
}
public const double EqualityTolerence = Double.Epsilon;
You should now find that the following code produces:
line 1: false
line 2: true
static void Main(string[] args)
{
Vector3 vect = new Vector3(1, 2, 3);
Vector3 vect2 = new Vector3(1, 2, 3.000000000000001);
Vector3 vect3 = new Vector3(1, 2, 3.0000000000000001);
Console.WriteLine(vect == vect2);
Console.WriteLine(vect == vect3);
Console.ReadKey();
}
Inequality (result = v1 != v2)
If the operator == (equal) is overridden, C# forces us to override != (notequal). This is simply the inverse of equality.
public static bool operator!=(Vector3 v1, Vector3 v2)
{
return !(v1==v2);
}
Division (v3 = v1 / s2)
Division of a vector by a scalar number (e.g. 2) is achieved by dividing each of the component parts by the divisor (s2).
public static Vector3 operator/(Vector3 v1, double s2)
{
return
(
new Vector3
(
v1.X / s2,
v1.Y / s2,
v1.Z / s2
)
);
}
Multiplication (dot, cross, and by scalar)
Multiplication of vectors is tricky. There are three distinct types of vector multiplication:
 Multiplication by a scalar (v3 = v1 * s2)
 Dot product (s3 = v1 . v2)
 Cross product (v3 = v1 * v2)
Only multiplication by scalar and division by scalar have been implemented as operator overloads. I have seen operators such as ~
overloaded for the dot product to distinguish it from cross product; I believe that this can lead to confusion and have chosen not to provide operators for dot and cross products leaving the user to call the appropriate method instead.
Multiplication by scalar is achieved by multiplying each of the component parts by the scalar value.
public static Vector3 operator*(Vector3 v1, double s2)
{
return
(
new Vector3
(
v1.X * s2,
v1.Y * s2,
v1.Z * s2
)
);
}
The order of operands in multiplication can be reversed; this is known as being commutable.
public static Vector3 operator*(double s1, Vector3 v2)
{
return v2 * s1;
}
The cross product of two vectors produces a normal to the plane created by the two vectors given.
The formula for this (where v1 = A and v2 = B) is
This equation always produces a vector as the result.
The sine of theta is used to account for the direction of the vector. Theta always takes the smallest angle between A and B (i.e. ).
The right hand side of the formula is arrived at by expanding and simplifying the left hand side using the rules:
Sin 0° = 0
Sin 90° = 1
In a matrix style notation this looks like:
You should be aware that this equation is noncommutable. This means that v1 crossproduct v2 is NOT the same as v2 crossproduct v1.
The C# code for all of this is:
public static Vector3 CrossProduct(Vector3 v1, Vector3 v2)
{
return
(
new Vector3
(
v1.Y * v2.Z  v1.Z * v2.Y,
v1.Z * v2.X  v1.X * v2.Z,
v1.X * v2.Y  v1.Y * v2.X
)
);
}
Where possible I have created static methods to extend the programmer's options when making use of the type. Methods which directly affect or are effected by the instance simply call the static methods. As such the instance counterpart of the static method is:
public Vector3 CrossProduct(Vector3 other)
{
return CrossProduct(this, other);
}
Note that this instance method does not affect the instance from which it is called but returns a new Vector3
object. I have chosen to implement cross product in this fashion for two reasons; one, to make it consistent with dot product which cannot produce a vector, and two, because cross product is usually used to generate a normal used somewhere else, the original Vector3
needing to be left intact.
[Side note] A quick template for manually calculating the cross product of two vectors is:
The dot product of two vectors is a scalar value defined by the formula;
The equation should always produce a scalar as the result.
Cosine theta is used to account for the direction of the vector. Theta always takes the smallest angle between A and B (i.e. ).
The right hand side of the formula is arrived at by expanding and simplifying the left hand side using the rules:
Cos 0° =1
Cos 90° = 0
The C# code for this is:
public static double DotProduct(Vector3 v1, Vector3 v2)
{
return
(
v1.X * v2.X +
v1.Y * v2.Y +
v1.Z * v2.Z
);
}
And its counterpart:
public double DotProduct(Vector3 other)
{
return DotProduct(this, other);
}
Extended functionality
We now have all the basic functionality required of a Vector3
type. To make this type really useful I have provided additional functionality.
Normalisation and Unit Vector
A unit vector has a magnitude of 1. To test if a vector is a unit vector we simply check for 1 against the magnitude method already defined.
public static bool IsUnitVector(Vector3 v1)
{
return v1.Magnitude == 1;
}
public bool IsUnitVector()
{
return IsUnitVector(this);
}
Having altered the equality (==) operator to allow a threshold, we must do the same with the static IsUnitVector
method (the instance method remains unchanged):
public static bool IsUnitVector(Vector3 v1)
{
return Math.Abs(v1.Magnitude 1) <= EqualityTolerence;
}
Normalization is the process of converting some vector to a unit vector. The formula for this is:
public static Vector3 Normalize(Vector3 v1)
{
if ( v1.Magnitude == 0 )
{
throw new DivideByZeroException( NORMALIZE_0 );
}
else
{
double inverse = 1 / v1.Magnitude;
return
(
new Vector3
(
v1.X * inverse,
v1.Y * inverse,
v1.Z * inverse
)
);
}
}
public void Normalize()
{
this = Normalize(this);
}
private const string NORMALIZE_0 = "Can not normalize a vector when" +
"it's magnitude is zero";
The normalization instance method directly affects the instance.
Interpolation
This method takes an interpolated value from between two vectors. This method takes three arguments, a starting point (vector v1), and end point (Vector v2), and a control which is a fraction between 1 and 0. The control determines which point between v1 and v2 is taken. A control of 0 will return v1 and a control of 1 will return v2.
n = n1(1t) + n2t
or:
n = n1 + t(n2n1)
or:
n = n1 + tn2 tn1
or:
where:
n = Current value
n_{1 }= Initial value (v1)
n_{2 }= Final value (v2)
t = Control parameter, where , and where, ,
public static Vector3 Interpolate(Vector3 v1, Vector3 v2, double control)
{
if (control >1  control <0)
{
throw new ArgumentOutOfRangeException
(
"control",
control,
INTERPOLATION_RANGE + "\n" + ARGUMENT_VALUE + control
);
}
else
{
return
(
new Vector3
(
v1.X * (1control) + v2.X * control,
v1.Y * (1control) + v2.Y * control,
v1.Z * (1control) + v2.Z * control
)
);
}
}
public Vector3 Interpolate(Vector3 other, double control)
{
return Interpolate(this, other, control);
}
private const string INTERPOLATION_RANGE = "Control parameter must be a" +
"value between 0 & 1";
Distance
This method finds the distance between two positional vectors using Pythagoras theorem.
public static double Distance(Vector3 v1, Vector3 v2)
{
return
(
Math.Sqrt
(
(v1.X  v2.X) * (v1.X  v2.X) +
(v1.Y  v2.Y) * (v1.Y  v2.Y) +
(v1.Z  v2.Z) * (v1.Z  v2.Z)
)
);
}
public double Distance(Vector3 other)
{
return Distance(this, other);
}
Absolute
The absolute value of a vector is its magnitude. The Abs
method has been provided to help programmers who are not aware that the two functions are the same and provide a static interface to the magnitude operator.
public static Double Abs(Vector3 v1)
{
return v1.Magnitude;
}
</implementation />
public double Abs()
{
return this.Magnitude;
}
Angle
This method finds the angle between two vectors using normalization and dot product.
^ refers to a normalized (unit) vector.
 refers to the magnitude of a Vector.
public static double Angle(Vector3 v1, Vector3 v2)
{
return
(
Math.Acos
(
Normalize(v1).DotProduct(Normalize(v2))
)
);
}
public double Angle(Vector3 other)
{
return Angle(this, other);
}
Max and Min
These methods compare the magnitude of two vectors and return the vector with the largest or smallest magnitude respectively.
public static Vector3 Max(Vector3 v1, Vector3 v2)
{
if (v1 >= v2){return v1;}
return v2;
}
public Vector3 Max(Vector3 other)
{
return Max(this, other);
}
public static Vector3 Min(Vector3 v1, Vector3 v2)
{
if (v1 <= v2){return v1;}
return v2;
}
public Vector3 Min(Vector3 other)
{
return Min(this, other);
}
Rotation
Euler rotation around axis x,y,z is performed using the methods pitch, yaw, and roll respectively.
Eric__ commented that he would expect a different configuration.
"... Roll, pitch and yaw refer back to the concept of an aircraft's motion.
It is standard notation that X is forward (out the nose), Y is out the right wing and Z is down (toward Earth for level flight).
Therefore it follows that roll is positive about +X, Pitch is positive about +Y (pitchup means climb), and Yaw is positive around +Z (positive yaw is when the aircraft nose moves to the right)."
To illustrate his point consider the following diagram:
This would appear to make perfect sense. It does! But only when considering the single aeroplane object. When we consider a virtual scene with multiple objects (for example a computer game or virtual reality environment), all objects must be relevant to the user perceiving them. The standard axis for a virtual scene have the user look down the Z axis.
So taking the aeroplane example, it is quite probable that we will be following the aircraft as it flies into the scene:
Hopefully, this explains why the axis are as described and the pitch, yaw, roll configuration is such.
Pitch
This method rotates a vector around the X axis by a given number of degrees (Euler rotation around X).
The hypotenuse (R) cancels out in the equation.
public static Vector3 Pitch(Vector3 v1, double degree)
{
double x = v1.X;
double y = ( v1.Y * Math.Cos(degree) )  ( v1.Z * Math.Sin(degree) );
double z = ( v1.Y * Math.Sin(degree) ) + ( v1.Z * Math.Cos(degree) );
return new Vector3(x, y, z);
}
public void Pitch(double degree)
{
this = Pitch(this, degree);
}
This method directly affects the instance from which the method was called.
Yaw
This method rotates a vector around the Y axis by a given number of degrees (Euler rotation around Y).
The hypotenuse (R) cancels out in the equation.
public static Vector3 Yaw(Vector3 v1, double degree)
{
double x = ( v1.Z * Math.Sin(degree) ) + ( v1.X * Math.Cos(degree) );
double y = v1.Y;
double z = ( v1.Z * Math.Cos(degree) )  ( v1.X * Math.Sin(degree) );
return new Vector(x, y, z);
}
public void Yaw(double degree)
{
this = Yaw(this, degree);
}
This method directly affects the instance from which the method was called.
Roll
This method rotates a vector around the Z axis by a given number of degrees (Euler rotation around Z).
The hypotenuse (R) cancels out in the equation.
public static Vector3 Roll(Vector3 v1, double degree)
{
double x = ( v1.X * Math.Cos(degree) )  ( v1.Y * Math.Sin(degree) );
double y = ( v1.X * Math.Sin(degree) ) + ( v1.Y * Math.Cos(degree) );
double z = v1.Z;
return new Vector3(x, y, z);
}
public void Roll(double degree)
{
this = Roll(this, degree);
}
This method directly affects the instance from which the method was called.
Backface
This method interprets a vector as a face normal and determines whether the normal represents a back facing plane given a lineofsight vector. A back facing plane will be invisible in a rendered scene and as such can be except from many scene calculations.
If then if
If then if
public static bool IsBackFace(Vector3 normal, Vector3 lineOfSight)
{
return normal.DotProduct(lineOfSight) < 0;
}
public bool IsBackFace(Vector3 lineOfSight)
{
return IsBackFace(this, lineOfSight);
}
Perpendicular
This method checks if two vectors are perpendicular (i.e. if one vector is the normal of the other).
public static bool IsPerpendicular(Vector3 v1, Vector3 v2)
{
return v1.DotProduct(v2) == 0;
}
public bool IsPerpendicular(Vector3 other)
{
return IsPerpendicular(this, other);
}
Mixed Product
The code for this method was provided by Michał Bryłka. The method calculates the scalar triple product of three vectors. This is the volume of a parallelepiped geometric shape. More information is available on Wikipedia. This method is noncommutable.
public static double MixedProduct(Vector3 v1, Vector3 v2, Vector3 v3)
{
return DotProduct(CrossProduct(v1, v2), v3);
}
public double MixedProduct(Vector3 other_v1, Vector3 other_v2)
{
return DotProduct(CrossProduct(this, other_v1), other_v2);
}
Component Functions
I have provided a number of functions which target the vectors components. These are not mathematically valid for the vector as a whole. For example, there is no concept of raising a vector to a power (that I know of) however the PowComponents
method can be used to raise each of x,y,z to a given power.
Sum components
This method simply adds together the vector components (x, y, z).
public static double SumComponents(Vector3 v1)
{
return (v1.X + v1.Y + v1.Z);
}
public double SumComponents()
{
return SumComponents(this);
}
To Power
This method multiplies the vectors components to a given power.
public static Vector3 PowComponents(Vector3 v1, double power)
{
return
(
new Vector
(
Math.Pow(v1.X, power),
Math.Pow(v1.Y, power),
Math.Pow(v1.Z, power)
)
);
}
public void PowComponents(double power)
{
this = PowComponents(this, power);
}
Square root
This method applies the square root function to each of the vectors components.
public static Vector3 SqrtComponents(Vector3 v1)
{
return
(
new Vector3
(
Math.Sqrt(v1.X),
Math.Sqrt(v1.Y),
Math.Sqrt(v1.Z)
)
);
}
public void SqrtComponents()
{
this = SqrtComponents(this);
}
Square
This method squares to each of the vectors components.
public static Vector3 SqrComponents(Vector3 v1)
{
return
(
new Vector3
(
v1.X * v1.X,
v1.Y * v1.Y,
v1.Z * v1.Z
)
);
}
public void SqrComponents()
{
this = SqrtComponents(this);
}
Sum of squares
This method finds the sum of each of the vectors components squared.
public static double SumComponentSqrs(Vector3 v1)
{
Vector3 v2 = SqrComponents(v1);
return v2.SumComponents();
}
public double SumComponentSqrs()
{
return SumComponentSqrs(this);
}
Usability functions
For completeness a number of standardised methods have been added complete the type. These implement IComparable
, IComparable<Vector3>
, IEquatable<Vector3>
, IFormattable
.
Methods to get a textual description of the type and implement IFormattable.
VerbString
provides a verbose textual description. ToString
can accept a numeric format string optionally proceeded by a character x, y or z which indicates the relevant vector component to describe.
public string ToVerbString()
{
string output = null;
if (IsUnitVector())
{
output += UNIT_VECTOR;
}
else
{
output += POSITIONAL_VECTOR;
}
output += string.Format("( x={0}, y={1}, z={2})", X, Y, Z);
output += MAGNITUDE + Magnitude;
return output;
}
private const string UNIT_VECTOR =
"Unit vector composing of ";
private const string POSITIONAL_VECTOR =
"Positional vector composing of ";
private const string MAGNITUDE =
" of magnitude ";
public string ToString(string format, IFormatProvider formatProvider)
{
if (format == null  format == "")
return String.Format("({0}, {1}, {2})", X, Y, Z);
char firstChar = format[0];
string remainder = null;
if (format.Length > 1)
remainder = format.Substring(1);
switch (firstChar)
{
case 'x':
return X.ToString(remainder, formatProvider);
case 'y':
return Y.ToString(remainder, formatProvider);
case 'z':
return Z.ToString(remainder, formatProvider);
default:
return
String.Format
(
"({0}, {1}, {2})",
X.ToString(format, formatProvider),
Y.ToString(format, formatProvider),
Z.ToString(format, formatProvider)
);
}
}
public override string ToString()
{
return ToString(null, null);
}
To produce a hash code for system use (required in order to implement comparator operations (i.e. ==, !=)):
public override int GetHashCode()
{
return
(
(int)((X + Y + Z) % Int32.MaxValue)
);
}
Check for equality (standardised version of == operator) and implement IEquatable<Vector3>
:
public override bool Equals(object other)
{
if(other is Vector3)
{
Vector3 otherVector = (Vector3)other;
return otherVector == this;
}
else
{
return false;
}
}
public bool Equals(Vector3 other)
{
return other == this;
}
Comparison method for two vectors which returns:
 1 if the magnitude is less than the others magnitude
 0 if the magnitude equals the magnitude of the other
 1 if the magnitude is greater than the magnitude of the other
This allows the Vector type to implement the IComparable
and IComparable<Vector3>
interfaces.
Public int CompareTo(object other)
{
if(other is Vector3)
{
Vector3 otherVector = (Vector3)other;
if( this < otherVector ) { return 1; }
else if( this > otherVector ) { return 1; }
return 0;
}
else
{
throw new ArgumentException
(
NON_VECTOR_COMPARISON + "\n" + ARGUMENT_TYPE +
other.GetType().ToString(),
"other"
);
}
}
public int CompareTo(Vector3 other)
{
if (this < other)
{
return 1;
}
else if (this > other)
{
return 1;
}
return 0;
}
private const string NON_VECTOR_COMPARISON =
"Cannot compare a Vector to a nonVector";
private const string ARGUMENT_TYPE =
"The argument provided is a type of ";
Standard Cartesian vectors and constants
Finally four standard vector constants are defined:
public static readonly Vector3 origin = new Vector3(0,0,0);
public static readonly Vector3 xAxis = new Vector3(1,0,0);
public static readonly Vector3 yAxis = new Vector3(0,1,0);
public static readonly Vector3 zAxis = new Vector3(0,0,1);
And miscellaneous readonly values:
public static readonly Vector3 MinValue =
new Vector3(Double.MinValue, Double.MinValue, Double.MinValue);
public static readonly Vector3 MaxValue =
new Vector3(Double.MaxValue, Double.MaxValue, Double.MaxValue);
public static readonly Vector3 Epsilon =
new Vector3(Double.Epsilon, Double.Epsilon, Double.Epsilon);
Serialization
Vector3
implements the [Serializable]
attribute and can therefore be written to file. I suggest the following:
static void Main(string[] args)
{
Vector3 vect = new Vector3(1, 2, 3);
XmlSerializer x = new XmlSerializer(vect.GetType());
x.Serialize
(
new System.IO.FileStream("test.xml", System.IO.FileMode.Create),
vect
);
}
Which produces an XML file containing:
="1.0"
<Vector
xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<X>1</X>
<Y>2</Y>
<Z>3</Z>
<Magnitude>3.7416573867739413</Magnitude>
</Vector>
Summary
We now have a Vector3
type with the following functionality:

Constructors
Vector3(double x, double y, double z)
Vector3(double[] xyz)
Vector3(Vector v1)
Properties
X
Y
Z
Magnitude
Operators
this[] Indexer
+

==
!=
*
/
<
>
<=
>=
Static methods
CrossProduct
DotProduct
MixedProduct
Normalize
IsUnitVector
Interpolate
Distance
Abs
Angle
Max
Min
Yaw
Pitch
Roll
IsBackFace
IsPerpendicular
SumComponents
SumComponentSqrs
SqrComponents
SqrtComponents
PowComponents
Instance methods which directly affect the instance variables
Normalize
Yaw
Pitch
Roll
SqrComponents
SqrtComponents
PowComponents
Instance methods which return a new object or type
CrossProduct
DotProduct
MixedProduct
IsUnitVector
Interpolate
Distance
Abs
Angle
Max
Min
IsBackFace
IsPerpendicular
SumComponents
SumComponentSqrs
CompareTo
Equals
ToString
GetHashCode
Readonly and constant values
MaxValue
MinValue
Epsilon
origin
xAxis
yAxis
zAxis

Points of Interest
There were a number of resources I used during the development of this article and source code provided, I would like to acknowledge the following:
 CSOpenGL Project  Lucas Viñas Livschitz
 Exocortex Project  Ben Houston
 Essential Mathematics for Computer Graphics  John Vince (ISBN 1852333804)
History
ToDo:
 Method to reflect a
Vector3
about a given normal
 A graphical testing\demonstration application
Changes: (v1.00v1.20)
Magnitude
methods are now encapsulated in a propertiy
 Incorrect serialization attributes have been removed
Equality
and IsUnitVector
methods allow a tolerance
Abs
method now returns magnitude
 Generic
IEquatable
and IComparable
interfaces have been implemented
IFormattable
interface has been implemented
 Mixed Product function implemented
 Added and renamed additional component based functions (e.g.
SumComponentSquares
)
Vector
renamed to Vector3