UnitVector - A normalised vector - with unit magnitude

Posted
Comments 0

Part 8 of C++ tutorial – a 3D vector & transform library

There is one other common type of vector and this is the unit vector. It is by definition of unit magnitude, and thus intended for the purposes of indicating direction only. It has distinct semantics from the general vector, and the vector represented point.

While a UnitVector class would still be represented by three doubles (x,y,z), unlike either the Vector or Point, it makes no sense to permit the elements to be individually assigned (given their magnitude must always be unity). Therefore, one creates a new representational class where the elements are read-only, thus:

class XYZ_Const
{
public:
	const double x,y,z;

	XYZ_Const(double a,double b,double c):x(a),y(b),z(c) { }
	void Set(const XYZ_Const& xyz)
	{	const_cast<double&>(x)=xyz.x;
		const_cast<double&>(y)=xyz.y;
		const_cast<double&>(z)=xyz.z;
	}
};

It still has an assignment method, which must necessarily cast away the constness of the members in order to assign a new unit vector.

The implementation of the UnitVector is largely the same as the Vector, except that most methods are const, except for assignment.

To assure unit magnitude, constructors normalise supplied coordinates, however, they are not unity/sanity-checked, e.g. UnitVector(0.33,0.66,066) is not the UnitVector(0.33,0.66,0.66) that may have been intended.

Because a priori, a UnitVector has unity magnitude, an exception is thrown should it be attempted to construct one from a null vector (or null coordinates).

Typically used to represent direction, it may be helpful to provide constructors or methods that enable the creation of UnitVectors from angles of rotation, but I’ll leave these until later. However, at least the three axes can be defined as static constants.

A UnitVector is implicitly a vector, thus the implicit conversion operator is provided.

Code for the UnitVector class and an example program (with output) is below:

class UnitVector: protected VectorBase<XYZ_Const>
{
protected:
	explicit UnitVector(const VectorBase<XYZ_Const>& xyz):VectorBase<XYZ_Const>(xyz) { } 	// NOT CHECKED FOR UNITY
public:
	// Elements & Accessors

	using VectorBase<XYZ_Const>::x;	// READ ONLY
	using VectorBase<XYZ_Const>::y;	// [	Write access has unclear semantics.
	using VectorBase<XYZ_Const>::z;	//		How can you change one element of a unit vector? ]

	static const UnitVector axis_x;	// |(1,0,0)|
	static const UnitVector axis_y;	// |(0,1,0)|
	static const UnitVector axis_z;	// |(0,0,1)|

	explicit operator const double* () const { return array(); }	// Cast to const array

	const double& operator[](int i) const { return element(i); }	// Const array element accessor

	double* Read(double d[3]) const { return read(d); }

	// Constructors

	UnitVector(double a,double b,double c):VectorBase<XYZ_Const>(a,b,c) { normalise(); }	// Null vector will THROW
	UnitVector(const double d[3]):VectorBase<XYZ_Const>(d[0],d[1],d[2]) { normalise(); }				// Null vector will THROW

	explicit UnitVector(const Vector& v):VectorBase<XYZ_Const>(v.x,v.y,v.z) { normalise(); }			// Null vector will THROW

	// Assignment
	UnitVector& operator=(const UnitVector& u) { assign(u); return *this; }

	// Informational
	bool operator==(const UnitVector& u) const { return is_equal_to(u); }
	bool operator!=(const UnitVector& u) const { return !is_equal_to(u); }

	// Conversions
	operator const Vector& () const { return *reinterpret_cast<const Vector*>(this); }	// Implicitly a const vector

	// Scalar operations

	UnitVector operator-() const { return UnitVector(negation()); }	// Negation retains unity

	// Scalar operations resulting in a vector
	friend Vector operator*(const UnitVector& u,double s) { return Vector(u)*s; }
	friend Vector operator*(double s,const UnitVector& u) { return s*Vector(u); }
	friend Vector operator/(const UnitVector& u,double s) { return Vector(u)/s; }	// Throws div0

	friend std::ostream& operator<<(std::ostream& os,const UnitVector& u) { return os<<"|("<<u.x<<','<<u.y<<','<<u.z<<")|"; }

};

const UnitVector UnitVector::axis_x=UnitVector(1,0,0);
const UnitVector UnitVector::axis_y=UnitVector(0,1,0);
const UnitVector UnitVector::axis_z=UnitVector(0,0,1);

int main()	// The program
{	const Vector u(7,3,5),v(1,2,3);

	cout<<"u="<<u<<"\n";
	cout<<"v="<<v<<"\n";

	cout<<"u x v = "<<u.cross(v)<<"\n";

	UnitVector uv(u.cross(v));

	cout<<"Norm(u x v)="<<uv<<"\n";

	UnitVector h(0.33,0.66,066);

	cout<<"h = "<<h<<"\n";

	return 0;
}

Program output:

u=(7,3,5)
v=(1,2,3)
u x v = (-4,-9,11)
Norm(u x v)=|(-0.270914,-0.609557,0.745014)|
h = |(0.00611054,0.0122211,0.999907)|

Author

Comments

There are currently no comments on this article.

Comment

Enter your comment below. Fields marked * are required. You must preview your comment before submitting it.