Saturday, January 24, 2015

Orbital Aero Model: Vector Model

** Update 21 Jan 2017: This Vector class has been deprecated, new version posted here. **

I created a Vector class that is used extensively in the code. Absolute locations, relative locations, velocities, accelerations, and forces all use this same class to hold values in 3 dimensional space. My vector is a simple 1 dimensional array of 3 values. Using an array makes things easier when dealing with matrix math (which will be introduced later). Also keeping it generic means those values could be considered an x/y/z or phi/theta/psi (orientation).

Operator Overloading


Operator overloading allows the programmer to use standard math operators on structures or classes that the compiler would normally not know what to do with. I use them extensively with vectors in the Orbital Aero Model; it helps to both condense the code and make it more readable.

For example, I'm trying to add vectors b and c together to store them in a.

Instead of writing code such as:
a.x = b.x + c.x;
a.y = b.y + c.y;
a.z = b.z + c.z;

I could write:
a = b + c;

The overload for that case would be:
Vector Vector::operator+(const Vector &vectorToAdd)
{
return Vector(this->value[0] + vectorToAdd.value[0], this->value[1] + vectorToAdd.value[1], this->value[2] + vectorToAdd.value[2]);
}

The addition operation is being performed on vector b (this) and vector c (vectorToAdd). It is returning a new vector of the sum that is stored in a.

A similar example is to add the value of b into a.

Instead of writing code such as:
a.x += b.x;
a.y += b.y;
a.z += b.z;

I could write:
a += b;

The overload for that case would be:
Vector &Vector::operator+=(const Vector &vectorToAdd)
{
this->value[0] += vectorToAdd.value[0];
this->value[1] += vectorToAdd.value[1];
this->value[2] += vectorToAdd.value[2];
return *this;
}

The addition operation is being performed on vector a (this) and vector b (vectorToAdd). The summed value is stored directly in a.

Since so much of the code uses my vector class that has operator overloading, I figured it would be best to share that class first. As I extend the functionality of my vector math, I'll update this post to include the latest additions.

More Resources


For more information on vector math, I recommend learning the following concepts:
Vector
Dot Product
Cross Product

If you've never seen a Dot Product or Cross Product before, you may be questioning what they're for. They're actually both VERY important concepts when doing 3-dimensional math. You'll see examples of them in action in the code that follows.

VectorModel.h


#pragma once

class Vector
{
private:

public:
double value[3];

Vector();

Vector(double value0, double value1, double value2);

Vector &Vector::operator=(double newValue);

Vector &Vector::operator=(const Vector &thatVector);

Vector Vector::operator-();

Vector Vector::operator+(const Vector &vectorToAdd);

Vector Vector::operator-(const Vector &vectorToSubtract);

Vector Vector::operator*(double valueToMultiply);

Vector Vector::operator*(const Vector &vectorToMultiply);

Vector Vector::operator/(double valueToDivide);

Vector &Vector::operator+=(const Vector &vectorToAdd);

Vector &Vector::operator-=(const Vector &vectorToSubtract);

Vector &Vector::operator*=(double valueToMultiply);

Vector &Vector::operator/=(double valueToDivide);

double magnitude();

Vector unit();

Vector sign();

static double dotProduct(Vector vector1, Vector vector2);
};


VectorModel.c


#include "math.h"
#include "VectorModel.h"

Vector::Vector()
{
this->value[0] = 0.0;
this->value[1] = 0.0;
this->value[2] = 0.0;
return;
}

Vector::Vector(double value0, double value1, double value2)
{
this->value[0] = value0;
this->value[1] = value1;
this->value[2] = value2;
return;
}

Vector &Vector::operator=(double newValue)
{
this->value[0] = newValue;
this->value[1] = newValue;
this->value[2] = newValue;
return *this;
}

Vector &Vector::operator=(const Vector &thatVector)
{
// Protect against self-assignment. (Otherwise bad things happen when it's reading from memory it has cleared.)
if (this != &thatVector)
{
this->value[0] = thatVector.value[0];
this->value[1] = thatVector.value[1];
this->value[2] = thatVector.value[2];
}
return *this;
}

Vector Vector::operator-()
{
return Vector(-this->value[0], -this->value[1], -this->value[2]);
}

Vector Vector::operator+(const Vector &vectorToAdd)
{
return Vector(this->value[0] + vectorToAdd.value[0], this->value[1] + vectorToAdd.value[1], this->value[2] + vectorToAdd.value[2]);
}

Vector Vector::operator-(const Vector &vectorToSubtract)
{
return Vector(this->value[0] - vectorToSubtract.value[0], this->value[1] - vectorToSubtract.value[1], this->value[2] - vectorToSubtract.value[2]);
}

Vector Vector::operator*(double valueToMultiply)
{
return Vector(this->value[0] * valueToMultiply, this->value[1] * valueToMultiply, this->value[2] * valueToMultiply);
}

Vector Vector::operator*(const Vector &vectorToMultiply)
{
return Vector(this->value[0] * vectorToMultiply.value[0], this->value[1] * vectorToMultiply.value[1], this->value[2] * vectorToMultiply.value[2]);
}

Vector Vector::operator/(double valueToDivide)
{
return Vector(this->value[0] / valueToDivide, this->value[1] / valueToDivide, this->value[2] / valueToDivide);
}

Vector &Vector::operator+=(const Vector &vectorToAdd)
{
this->value[0] += vectorToAdd.value[0];
this->value[1] += vectorToAdd.value[1];
this->value[2] += vectorToAdd.value[2];
return *this;
}

Vector &Vector::operator-=(const Vector &vectorToSubtract)
{
this->value[0] -= vectorToSubtract.value[0];
this->value[1] -= vectorToSubtract.value[1];
this->value[2] -= vectorToSubtract.value[2];
return *this;
}

Vector &Vector::operator*=(double valueToMultiply)
{
this->value[0] *= valueToMultiply;
this->value[1] *= valueToMultiply;
this->value[2] *= valueToMultiply;
return *this;
}

Vector &Vector::operator/=(double valueToDivide)
{
this->value[0] /= valueToDivide;
this->value[1] /= valueToDivide;
this->value[2] /= valueToDivide;
return *this;
}

double Vector::magnitude()
{
return sqrt( (this->value[0] * this->value[0]) + (this->value[1] * this->value[1]) + (this->value[2] * this->value[2]) );
}

Vector Vector::unit()
{
double mag = magnitude();
Vector unitVector;
unitVector.value[0] = this->value[0] / mag;
unitVector.value[1] = this->value[1] / mag;
unitVector.value[2] = this->value[2] / mag;
return unitVector;
}

Vector Vector::sign()
{
Vector signVector;
signVector.value[0] = sgn(this->value[0]);
signVector.value[1] = sgn(this->value[1]);
signVector.value[2] = sgn(this->value[2]);
return signVector;
}

double Vector::dotProduct(Vector vector1, Vector vector2)
{
return ((vector1.value[0] * vector2.value[0]) + (vector1.value[1] * vector2.value[1]) + (vector1.value[2] * vector2.value[2]));
}


Copyright (c) 2015 Clinton Kam
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

No comments:

Post a Comment