Posted
Comments 0

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

Before continuing, one should pause to check the objectives and principles to be adopted in developing this library.

The library should be easy to use, and ideally more attractive to use than the option of writing one’s own library from scratch (for most users).

It should be unsurprising and predictable, with as few ‘gotchas’ as possible.

It should be useful, and cover the essential areas in terms of functionality.

It should focus on 3D, i.e. not cater for 2D or more than 3 dimensions.

It should not sacrifice usability/convenience in pursuit of flexibility or efficiency.

Author

Posted
Comments 0

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

Multiplying/dividing vectors by a scalar is probably the next overloaded operator to implement.

Thus we have the respective helper functions:

Vector multiplication(double m) const { return Vector(x*m,y*m,z*m); }
Vector division(double d) const { if (d) return Vector(x/d,y/d,z/d);
else throw runtime_error("Divide by Zero"); }

Note that it is here, in the division function, that we can throw an exception in the event that it is attempted to divide a vector by zero. If you know of a superior way of handling this, post it in a comment.

The corresponding modifying methods are:

void multiply_by(double m) { assign(multiplication(m)); }
void divide_by(double d) { assign(division(d)); }

The respective overloaded operators can be seen in the complete code here:

#include <iostream>	// For console output functionality

using namespace std;	// Avoids having to use std:: scoping prefix

class Vector	// A vector class with addition and subtraction functionality
{
public:
	double x,y,z;	// Representation

protected:
	// Non-modifying functions
	Vector addition(const Vector& v) const { return Vector(x+v.x,y+v.y,z+v.z); }
	Vector subtraction(const Vector& v) const { return Vector(x-v.x,y-v.y,z-v.z); }
	Vector multiplication(double m) const { return Vector(x*m,y*m,z*m); }
	Vector division(double d) const { if (d) return Vector(x/d,y/d,z/d); else throw std::runtime_error("Divide by Zero"); }

	// Modifying methods
	void assign(const Vector& v) { x=v.x; y=v.y; z=v.z; }
	void add(const Vector& v) { assign(addition(v)); }
	void subtract(const Vector& v) { assign(subtraction(v)); }
	void multiply_by(double m) { assign(multiplication(m)); }
	void divide_by(double d) { assign(division(d)); }

public:
	Vector(double a,double b,double c):x(a),y(b),z(c) { }

	Vector& operator+=(const Vector& v) { add(v); return *this; }
	Vector& operator-=(const Vector& v) { subtract(v); return *this; }

	friend Vector operator+(const Vector& u,const Vector& v) { return Vector(u.addition(v)); }
	friend Vector operator-(const Vector& u,const Vector& v) { return Vector(u.subtraction(v)); }

	Vector& operator*=(double m) { multiply_by(m); return *this; }
	Vector& operator/=(double d) { divide_by(d); return *this; }	// Throws div0

	friend Vector operator*(const Vector& v,double m) { return Vector(v.multiplication(m)); }
	friend Vector operator*(double m,const Vector& v) { return Vector(v.multiplication(m)); }
	friend Vector operator/(const Vector& v,double d) { return Vector(v.division(d)); }

	friend ostream& operator<<(ostream& os,const Vector& v)
	{ return os<<'('<<v.x<<','<<v.y<<','<<v.z<<')'; }	// Output, e.g. (1,2,3)
};

int main()	// The program
{	Vector v(1,2,3);	// A test vector

	cout<<"v="<<v<<"\n";
	cout<<"3*v="<<3*v<<"\n";
	cout<<"v*3="<<v*3<<"\n";
	cout<<"v/2="<<v/2<<"\n";
}

Note that because multiplication of a vector by a scalar should be commutative, both operator overloads are provided. However, division of a scalar by a vector is a different kettle of fish to a vector divided by a scalar – the latter being equivalent to multiplication by the reciprocal of the scalar divisor, thus: v/s == v*(1/s).

This program outputs the following:

v=(1,2,3)
3*v=(3,6,9)
v*3=(3,6,9)
v/2=(0.5,1,1.5)

Author

Posted
Comments 0

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

To avoid laboriously adding a vectors via addition of their elements, one can improve the facility of the vector class by adding arithmetic operator overloads for addition and subtraction.

My preferred approach is to implement additional functionality in protected const helper functions, that can be used directly by all other methods, whether const or non-const, public or friend.

Thus the first thing to do is to create a helper function that calculates the addition of a supplied vector to this object and returns the resulting vector.

Thus: Vector addition(const Vector& v) const { return Vector(x+v.x,y+v.y,z+v.z); }

A modifying helper method that adds the supplied vector to this object, setting this object with the result, can be implemented using a simple assignment function, thus:

void assign(const Vector& v) { x=v.x; y=v.y; z=v.z; }
void add(const Vector& v) { assign(addition(v)); }

The overloaded operators can then be written succinctly in terms of helpers, thus:

Vector& operator+=(const Vector& v) { add(v); return *this; }
friend Vector operator+(const Vector& u,const Vector& v) { return Vector(u.addition(v)); }

Subtraction is simply a matter of changing the sign.

The complete code is below:

#include <iostream>	// For console output functionality

using namespace std;	// Avoids having to use std:: scoping prefix

class Vector	// A vector class with addition and subtraction functionality
{
public:
	double x,y,z;	// Representation

protected:
	// Non-modifying functions
	Vector addition(const Vector& v) const { return Vector(x+v.x,y+v.y,z+v.z); }
	Vector subtraction(const Vector& v) const { return Vector(x-v.x,y-v.y,z-v.z); }

	// Modifying methods
	void assign(const Vector& v) { x=v.x; y=v.y; z=v.z; }
	void add(const Vector& v) { assign(addition(v)); }
	void subtract(const Vector& v) { assign(subtraction(v)); }

public:
	Vector(double a,double b,double c):x(a),y(b),z(c) { }

	Vector& operator+=(const Vector& v) { add(v); return *this; }
	Vector& operator-=(const Vector& v) { subtract(v); return *this; }

	friend Vector operator+(const Vector& u,const Vector& v) { return Vector(u.addition(v)); }
	friend Vector operator-(const Vector& u,const Vector& v) { return Vector(u.subtraction(v)); }

	friend ostream& operator<<(ostream& os,const Vector& v)
	{ return os<<'('<<v.x<<','<<v.y<<','<<v.z<<')'; }	// Output, e.g. (1,2,3)
};

int main()	// The program
{	Vector v(1,2,3);	// A test vector

    cout << "v="<<v<<"\n";	// Output it

	Vector u(-1,-3,-5);

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

	Vector s=Vector(u.x+v.x,u.y+v.y,u.z+v.z);	// Sum of two vectors

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

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

}

Program output is:

v=(1,2,3)
u=(-1,-3,-5)
u+v=(0,-1,-2)
u+v=(0,-1,-2)
u-v=(-2,-5,-8)

Author

Posted
Comments 0

If the first program that C++ coders write is “Hello World”, then a definition of a 3D vector class is probably the second.

Let’s have a go:

#include <iostream>	// For console output functionality

using namespace std;	// Avoids having to use std:: scoping prefix

class Vector
{
public:
	double x,y,z;	// Representation

	Vector(double a,double b,double c):x(a),y(b),z(c) { }	// Constructor

	friend ostream& operator<<(ostream& os,const Vector& v)	// Non-member, thus must be friend, but defined inline
	{ return os<<'('<<v.x<<','<<v.y<<','<<v.z<<')'; }	// Output, e.g. (1,2,3)
};

int main()	// The program
{	Vector v(1,2,3);	// A test vector

    cout << "v="<<v<<"\n";	// Output it
}

When this is run it produces the following output:

v=(1,2,3)

There’s not much we can do with this vector. We can read and write to the individual elements (x,y,z), but addition is labourious, e.g.

Vector u(-1,-3,-5);

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

Vector s=Vector(u.x+v.x,u.y+v.y,u.z+v.z);	// Sum of two vectors

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

When this is run it adds the following output:

u=(-1,-3,-5)
u+v=(0,-1,-2)

Author