You are on page 1of 12

C++ Point and Vector Class

by Dan Sunday
Design Decisions
Point Class API
Parameters
Constructors
Operators
Methods
Vector Class API
Parameters
Constructors
Operators
Methods
Implementation
point.h: Point class
point.c: Point methods
vector.h: Vector class
vector.c: Vector methods
DOWNLOAD
References
This algorithm is not really an algorithm, but a piece of code that has been missing from all the other
algorithms. All of our algorithm implementations have started with the following comment.
// Assume that C++ classes are already given for the objects:
// Point and Vector with
// coordinates {float x, y, z;}
// operators for:
...etc...
Although providing this was felt to be both application-specific and somewhat straightforward, many
programmers have found this to be an obstacle. Indeed, a clean general-purpose implementation of these
classes is not trivial, and some hard design decisions need to be made. In fact, while developing these
classes, we changed the symbols we had previously suggested for overloading certain operators. For
example the "*" operator for two vectors had denoted the cross product in past algorithms, but we now
prefer it to be used for the dot product, and prefer the cross product operator to be "^" (the Grassmann
"exterior product" wedge [Crowe, 1994]). As a result, we have done a complete redesign so that almost all
point and vector formulas can be given by clean algebraic expressions. The advantage of this redesign
outweighs the momentary confusion that may be created. Nevertheless, we hope to rectify this situation by
refactoring all prior algorithms so that they use these new classes, and then linking all refactored algorithms
to this page. Also, in time, we may expand and enhance the methods provided for these classes. If a
significant change is made, we will start to use version numbers. Until then, this is the unnumbered version
0.
Of course, there are other definitions available for such classes, but we feel that the ones presented here are
both more powerful and easier to use. Let us know what you think. We welcome all feedback concerning
our implementation, whether a suggestion for an improvement, a problem (bug or deficiency) report, or just
your opinion of its usefulness. For easy accessibility, you can download all source code in the file
geometry.tar which contains these class definitions plus test programs.
Design Decisions
Before describing the API for the new Point and Vector classes, we first summarize the rationale for a
number of design decisions that were made, as follows.
1. We wanted to make the classes as easy to use and remember as possible by minimizing the
complexity of their API's. Our other design decisions were guided by this overall goal. Note that this
differs from many other Point and Vector class definitions currently available.

2. Both Point and Vector classes are dimension-independent. We do not have separate classes for every
specific dimension (like, Point2D, Point3D, etc). The advantages of this dim-free design are:
a. Code is easier to understand without multiple instances for each dimension. The dimension
of a Point or Vector is given by a member variable that specifies the number of coordinates to
use.
b. Lower dimensions are treated as embedded in higher dimensions by appending 0-valued
coordinates. That is, a 2D point (x,y) is considered embedded in 3D as (x,y,0).
c. Using the dimension value and embedded dimensions, many representations, operations, and
algorithms can be defined generically for n dimensions [Note: despite this, we have only
implemented the Point and Vector classes for dimensions 1, 2, and 3]. For example, vector
sums, scalar operations, and the dot product work in any dimension. Thus, a line can be
represented as a dimension-free affine combination of two points; namely, P(t) = (1-t)*P
0

+ t*P
1
. Further, algorithms can often be implemented to be dimension-free.

3. Point is a base class, and the Vector class is derived from it. This is because many vector operations
(like the products) do not make sense for points, but all point operations apply also to vectors.

4. Although we try to be strict in defining appropriate point and vector operations, there is one area of
difficulty; namely, points can only be added as affine sums, whereas any weighted sum works for
vectors. In the end, we decided to allow scalar multiplication and addition of points, even though the
result depends on the chosen coordinate frame of reference. It's too bad that C++ operator
overloading does not permit detection of whether a sum is affine or not, and that's the rub. So, for
the convenience of being able to write concise algebraic expressions, we decided to allow these
operations for points. Caveat programmer, for you are now responsible for doing point arithmetic
correctly! However, for the cautious, there is a "sum(m,c[i],P[i])" member function to add the m
scalar products c[i]*P[i] for i=0,m-1; and it checks whether the sum of the coefficients c[i] is
equal to one. If not, an error flag is set (but the coordinate computation is done anyway, and
returned). If desired, it is up to the programmer to test the value of the error flag.

5. We wanted concise algebraic expressions for all common point and vector operations (see our
summary of Basic Linear Algebra (Sunday, 2002)). Aside from the straightforward operations ("+",
"-", etc), we decided to use:
a. "*" in all dimensions for both scalar multiplication ({int|double}*{Point|Vector}), and also
for the dot product (Vector*Vector). Both the scalar and dot products are defined in all
dimensions, and are frequently used operations, so we used the most familiar multiplication
operator symbol.
b. "^" is the Grassmann "exterior product" wedge symbol (Crowe, 1994), which corresponds in
3D to the cross product for two vectors. Thus, we use "^" as the cross product symbol. Non-
3D vectors are viewed as embedded in 3D when this operator is used. It would have been
nice to have this symbol also represent the 2D exterior product, which is the same as the
scalar "perp product" of (Hill, 1994). But then the "^" operator would have to return different
object types for different dimensions, and C++ does not allow this. In any case, embedding
vectors in 3D for applying "^" is a useful convention.
c. "|" is chosen to be the 2D vector perp product (since C++ has a limited choice of operator
symbols).
d. "~" is, similarly, the unary 2D vector perp operator. Thus, v|w = (~v)*w, for 2D vectors.

6. Some error detection was done by setting a class variable "err" with an error code. This is probably
not enough, and more error catching should be added.
Well, that is sufficient to indicate our approach, and to understand the rationale behind our definitions for
the Point and Vector classes. We now describe the application programmer interface (API) for each class.
Point Class API
In describing the Point API, we use the following notations:
1) P, P1, and P2 are instances of a Point
2) P1 has coordinates (x1,y1,z1)
3) P2 has coordinates (x2,y2,z2)
4) v is an instance of a Vector, and has coordinates (vx,vy,vz)
5) c denotes a scalar, and is either an integer, a float, or a double.
Parameters
The Point class has the following member parameters [Note: we list private ones to aid understanding].
Scope

Type

Name

Description

private int dimn
Dimension of the point. Valid range is 0<dimn<=3.
private enum Error err
Error flag. Defined enum values are:
Enot - No error (the default)
Edim - A dimension error, usually for an invalid
operation.
Esum - An affine summation error. The coefficients
of a weighted sum do not add to 1.
public double x, y, z
The coordinates of the point. Only use as many as the
point's dimension "dimn", and set extra coordinates to
zero. Thus, a 2D point (x,y) has dimn==2 and
coordinates (x,y,0), and all three coordinates can be
used in 3D operations (such as, the cross product).
Constructors

Point P;
Creates a default 3D point (dimn=3) with x=y=z=0. Sets err=Enot.



Point P(int a);
Point P(double a);
Creates a 1D point (dimn=1) with x=a, y=z=0. Sets err=Enot.



Point P(int a, int b);
Point P(double a, double b);
Creates a 2D point (dimn=2) with x=a, y=b, z=0. Sets err=Enot.



Point P(int a, int b, int c);
Point P(double a, double b, double c);
Creates a 3D point (dimn=3) with x=a, y=b, z=c. Sets err=Enot.



Point P(int n, int a[]);
Point P(int n, double a[]);
Creates an n-dim point (dimn=n, 0<n<=3) with the first n coordinates set to a[0],...,a[n-
1], and the remaining ones set to 0. Sets err=Enot.

Operators
Symbol

Returns

Format

>> Point cin >> P

Input and parse a formatted string into the parameters x,y,z of P. Currently
accepted formats are: "(x)", "(x,y)", and "(x,y,z)", for which the dimension of P
is also set to 1, 2, or 3 respectively.


<< String cout << P

Output the parameters x,y,z of P as one of the formatted strings: "(x)", "(x,y)",
or "(x,y,z)" according to whether the dimension of P is 1, 2, or 3.


= Point P1 = P2

Copy all P2 parameter values to the corresponding P1 parameters.


== int P1 == P2

Equality Test - parameters x,y,z and dimn are all equal (err is not tested).


!= int P1 != P2

Inequality Test - parameters x,y,z and dimn are not all equal (err is not tested).


- Vector P1 - P2

Vector difference between two points.
The resulting vector has coordinates (x1-x2, y1-y2, z1-z2) and has the
maximum dimension of the two points.


+ Point P + v

Translate point P by vector v.
The resulting new point has coordinates (x+vx, y+vy, z+vz) and has the
maximum dimension of P and v.


- Point P - v

Translate point P by vector -v.
The resulting new point has coordinates (x-vx, y-vy, z-vz) and has the
maximum dimension of P and v.


+= Point P += v

Translate point P by vector v.
The point P has its coordinates changed to (x+vx, y+vy, z+vz), and has the
maximum dimension of P and v.


-= Point P -= v

Translate point P by vector -v.
The point P has its coordinates changed to (x-vx, y-vy, z-vz), and has the
maximum dimension of P and v.


* Point c * P
P * c

Scalar multiplication of point P by scalar c.
The resulting new point has coordinates (c*x, c*y, c*z).
NOTE: this operation is only valid as part of an affine sum of points.


/ Point P / c

Scalar division of point P by scalar c.
The resulting new point has coordinates (x/c, y/c, z/c).
NOTE: this operation is only valid as part of an affine sum of points.


+ Point P1 + P2

Addition of two points.
The resulting new point has coordinates (x1+x2, y1+y2, z1+z2) and has the
maximum dimension of the two points.
NOTE: this operation is only valid as part of an affine sum of points.
An "affine" sum of points is a weighted sum c
1
*P
1
+...+c
m
*P
m
where the coefficients add to 1, namely
c
1
+...+c
m
=1. An affine sum of points defines another point object, which is in a unique spatial location no
matter which coordinate frame of reference is used. Thus, affine sums of points make geometric sense. But,
non-affine sums of points do not make sense, and give different geometric results for different frames of
reference. Unfortunately, C++ operator overloading does not allow one to check for this condition in an
algebraic expression. The cautious programmer may want to use the "sum(...)" member function instead.
Methods

int dim()
P.dim() returns the dimension of point P.



int setdim( int n)
P.setdim(n) sets the dimension of point P to n. The first n coordinates are unchanged, and
the rest are set to 0.



Point asum( int m, int c[], Point P[])
Point asum( int m, double c[], Point P[])
asum(m, c[], P[]) computes and returns c[0]*P[0]+...+c[m-1]*P[m-1], the weighted
sum of the points P[i], which is also a point when the sum of the coefficients
c[0]+...+c[m-1] = 1, and the point sum is said to be "affine". When the sum is not affine,
then the error flag err is set to Esum, although the weighted sum is computed and returned
anyway. When needed, the programmer can and should check for the error.



double d( Point P1, Point P2)
d(P1,P2) returns the Euclidean distance between points P1 and P2. This is the square root of
the sum of the squared coordinates of their difference vector (P1-P2).



double d2( Point P1, Point P2)
d2(P1,P2) returns the squared Euclidean distance between points P1 and P2. This is faster
than d(P1,P2) since the square root is not computed.



double isLeft( Point P1, Point P2)
P.isLeft(P1,P2) returns >0, 0, or <0 for the point P being "left of", "on", or "right of" the
directed line going from P1 to P2. This is only meaningful for 2D points, and err will be set
to Edim if any of the points P, P1, or P2 does not have dimension 2. In any case, the
determination is done based on only the (x,y) values of the points, that is, based on the
projection of the points into the xy-plane.

Error Handling Methods

void clerr()
P.clerr() clears the error flag err of P to Enot.



int geterr()
P.geterr() returns the current value of the error flag err for P.



char* errstr()
P.errstr() returns a string describing the current value of the error flag err for P.


Vector Class API
The Vector class is derived from the Point base class.
In describing the Vector API, we use the following notations:
1) v, v1, and v2 are instances of a Vector
2) v has coordinates (vx,vy,vz)
3) v1 has coordinates (vx1,vy1,vz1)
4) v2 has coordinates (vx2,vy2,vz2)
5) c denotes a scalar, and is either an integer, a float, or a double.
Parameters
The Vector class inherits its parameters from the Point class parameters.
Constructors
The Vector class inherits the Point class constructors. All Vector constructors are given in the following
table.

Vector v;
Creates a default 3D vector (dimn=3) with x=y=z=0. Sets err=Enot.



Vector v(int a);
Vector v(double a);
Creates a 1D vector (dimn=1) with x=a, y=z=0. Sets err=Enot.



Vector v(int a, int b);
Vector v(double a, double b);
Creates a 2D vector (dimn=2) with x=a, y=b, z=0. Sets err=Enot.



Vector v(int a, int b, int c);
Vector v(double a, double b, double c);
Creates a 3D vector (dimn=3) with x=a, y=b, z=c. Sets err=Enot.



Vector v(int n, int a[]);
Vector v(int n, double a[]);
Creates an n-dim vector (dimn=n, 0<n<=3) with the first n coordinates set to a[0],...,a[n-
1], and the remaining ones set to 0. Sets err=Enot.

Operators
Some Vector operators are inherited from the Point class operators, some are redefined, and some are
new. All useful Vector operators are given in the following table.
Symbol

Returns

Format

>> Vector cin >> v

Inherited from the Point class.


<< String cout << v

Inherited from the Point class.


= Vector v1 = v2

Inherited from the Point class.


== int v1 == v2

Inherited from the Point class.


!= int v1 != v2

Inherited from the Point class.


- Vector -v

Unary negative vector.
The resulting new vector has coordinates (-vx, -vy, -vz).


~ Vector ~v

Unary 2D perp vector.
The resulting new vector has coordinates (-vy, vx, vz). Typically used with 2D
vectors, where it yields the leftward normal vector to v; namely, (-vy, vx).


+ Vector v1 + v2

Sum of two vectors.
The resulting new vector has coordinates (vx1+vx2, vy1+vy2, vz1+vz2) and has
the maximum dimension of v1 and v2.


- Vector v1 - v2

Difference of two vectors.
The resulting new vector has coordinates (vx1-vx2, vy1-vy2, vz1-vz2) and has
the maximum dimension of v1 and v2.


+= Vector v1 += v2

Increment vector v1 by vector v2.
The vector v1 has its coordinates changed to (vx1+vx2, vy1+vy2, vz1+vz2), and
has the maximum dimension of v1 and v2.


-= Vector v1 -= v2

Decrement vector v1 by vector v2.
The vector v1 has its coordinates changed to (vx1-vx2, vy1-vy2, vz1-vz2), and
has the maximum dimension of v1 and v2.


* Vector c * v
v * c

Scalar multiplication of vector v by scalar c.
The resulting new vector has coordinates (c*vx, c*vy, c*vz).


/ Vector v / c

Scalar division of vector v by scalar c.
The resulting new vector has coordinates (vx/c, vy/c, vz/c).


* double v1 * v2

Dot product of two vectors.
The resulting scalar value = vx1*vx2 + vy1*vy2 + vz1*vz2.


^ Vector v1 ^ v2

3D cross product of vectors v1 and v2.
The resulting new vector has coordinates =
(vy1*vz2-vz1*vy2, vz1*vx2-vx1*vz2, vx1*vy2-vy1*vx2).


| double v1 | v2

2D perp product of vectors v1 and v2.
The resulting scalar value = vx1*vy2 - vy1*vx2. Mostly used for 2D vectors for
which this is the Grassmann exterior product.


*= Vector v *= c

Scalar multiplication of vector v by scalar c.
The vector v has its coordinates changed to (c*vx, c*vy, c*vz).


/= Vector v /= c

Scalar division of vector v by scalar c.
The vector v has its coordinates changed to (vx/c, vy/c, vz/c).


^= Vector v1 ^= v2

3D cross product of vectors v1 and v2.
The vector v1 has its coordinates changed to
(vy1*vz2-vz1*vy2, vz1*vx2-vx1*vz2, vx1*vy2-vy1*vx2).
Methods
The Vector class inherits the Point class methods.
Additional Vector class methods are as follows.

double len()
v.len() returns the Euclidean length of vector v. This is the square root of the sum of its
squared coordinates.



double len2()
v.len2() returns the squared Euclidean length of vector v. This is faster than len() since
the square root is not computed.



Vector normalize()
v.normalize() results in the vector v being positively scaled to a unit vector. The vector v
has its coordinates changed to those of v/v.len().


Vector sum( int m, int c[], Vector v[])
Vector sum( int m, double c[], Vector v[])

sum(m, c[], v[]) computes and returns c[0]*v[0]+...+c[m-1]*v[m-1], the weighted
sum of the vectors v[i], which is always a vector.


Implementation
The "C++" implementations of the Point and Vector classes is contained in the files:
common.h - common definition header file
point.h - Point class definition
point.c - Point class methods
vector.h - Vector class definition
vector.c - Vector class methods
DOWNLOAD: These files (plus test programs and a Makefile) are available in a tar file: geometry.tar.
NOTE: All source code is copyrighted as follows.
// Copyright 2003, softSurfer (www.softsurfer.com)
// This code may be freely used and modified for any purpose
// providing that this copyright notice is included with it.
// SoftSurfer makes no warranty for this code, and cannot be held
// liable for any real or imagined damage resulting from its use.
// Users of this code must verify correctness for their application.
//--------------------------------------------------------------------
// common.h - common definition header file
//--------------------------------------------------------------------
#ifndef SS_Common_H
#define SS_Common_H
#include <iostream.h>
enum {FALSE=0, TRUE=1, ERROR=(-1)};
// Error codes
enum Error {
Enot, // no error
Edim, // error: dim of point invalid for operation
Esum // error: sum not affine (cooefs add to 1)
};
// utility macros
#define abs(x) ((x) >= 0 ? x : -(x));
#define min(x,y) ((x) < (y) ? (x) : (y));
#define max(x,y) ((x) > (y) ? (x) : (y));
#endif SS_Common_H
//--------------------------------------------------------------------
// point.h - Point class definition
//--------------------------------------------------------------------
#ifndef SS_Point_H
#define SS_Point_H
#include "common.h"
class Point {
friend class Vector;
private:
int dimn; // # coords (1, 2, or 3 max here)
Error err; // error indicator
public:
double x, y, z; // z=0 for 2D, y=z=0 for 1D
//----------------------------------------------------------
// Lots of Constructors (add more as needed)
Point() { dimn=3; x=y=z=0; err=Enot; }
// 1D Point
Point( int a) {
dimn=1; x=a; y=z=0; err=Enot; }
Point( double a) {
dimn=1; x=a; y=z=0; err=Enot; }
// 2D Point
Point( int a, int b) {
dimn=2; x=a; y=b; z=0; err=Enot; }
Point( double a, double b) {
dimn=2; x=a; y=b; z=0; err=Enot; }
// 3D Point
Point( int a, int b, int c) {
dimn=3; x=a; y=b; z=c; err=Enot; }
Point( double a, double b, double c) {
dimn=3; x=a; y=b; z=c; err=Enot; }
// n-dim Point
Point( int n, int a[]);
Point( int n, double a[]);
// Destructor
~Point() {};
//----------------------------------------------------------
// Input/Output streams
friend istream& operator>>( istream&, Point&);
friend ostream& operator<<( ostream&, Point);
//----------------------------------------------------------
// Assignment "=": use the default to copy all members
int dim() { return dimn; } // get dimension
int setdim( int); // set new dimension
//----------------------------------------------------------
// Comparison (dimension must match, or not)
int operator==( Point);
int operator!=( Point);
//----------------------------------------------------------
// Point and Vector Operations (always valid)
Vector operator-( Point); // Vector difference
Point operator+( Vector); // +translate
Point operator-( Vector); // -translate
Point& operator+=( Vector); // inc translate
Point& operator-=( Vector); // dec translate
//----------------------------------------------------------
// Point Scalar Operations (convenient but often illegal)
// using any type of scalar (int, float, or double)
// are not valid for points in general,
// unless they are 'affine' as coeffs of
// a sum in which all the coeffs add to 1,
// such as: the sum (a*P + b*Q) with (a+b == 1).
// The programmer must enforce this (if they want to).
// Scalar Multiplication
friend Point operator*( int, Point);
friend Point operator*( double, Point);
friend Point operator*( Point, int);
friend Point operator*( Point, double);
// Scalar Division
friend Point operator/( Point, int);
friend Point operator/( Point, double);
//----------------------------------------------------------
// Point Addition (also convenient but often illegal)
// is not valid unless part of an affine sum.
// The programmer must enforce this (if they want to).
friend Point operator+( Point, Point); // add points
// Affine Sum
// Returns weighted sum, even when not affine, but...
// Tests if coeffs add to 1. If not, sets: err = Esum.
friend Point asum( int, int[], Point[]);
friend Point asum( int, double[], Point[]);
//----------------------------------------------------------
// Point Relations
friend double d( Point, Point); // Distance
friend double d2( Point, Point); // Distance^2
double isLeft( Point, Point); // 2D only
//----------------------------------------------------------
// Error Handling
void clerr() { err = Enot;} // clear error
int geterr() { return err;} // get error
char* errstr(); // return error string
};
#endif SS_Point_H
//--------------------------------------------------------------------
// vector.h - Vector class definition
//--------------------------------------------------------------------
#ifndef SS_Vector_H
#define SS_Vector_H
#include "common.h"
class Vector : public Point {
public:
// Constructors same as Point class
Vector() : Point() {};
Vector( int a) : Point(a) {};
Vector( double a) : Point(a) {};
Vector( int a, int b) : Point(a,b) {};
Vector( double a, double b) : Point(a,b) {};
Vector( int a, int b, int c) : Point(a,b,c) {};
Vector( double a, double b, double c) : Point(a,b,c) {};
Vector( int n, int a[]) : Point(n,a) {};
Vector( int n, double a[]) : Point(n,a) {};
~Vector() {};
//----------------------------------------------------------
// IO streams and Comparisons: inherit from Point class
//----------------------------------------------------------
// Vector Unary Operations
Vector operator-(); // unary minus
Vector operator~(); // unary 2D perp operator
//----------------------------------------------------------
// Scalar Multiplication
friend Vector operator*( int, Vector);
friend Vector operator*( double, Vector);
friend Vector operator*( Vector, int);
friend Vector operator*( Vector, double);
// Scalar Division
friend Vector operator/( Vector, int);
friend Vector operator/( Vector, double);
//----------------------------------------------------------
// Vector Arithmetic Operations
Vector operator+( Vector); // vector add
Vector operator-( Vector); // vector subtract
double operator*( Vector); // inner dot product
double operator|( Vector); // 2D exterior perp product
Vector operator^( Vector); // 3D exterior cross product
Vector& operator*=( double); // vector scalar mult
Vector& operator/=( double); // vector scalar div
Vector& operator+=( Vector); // vector increment
Vector& operator-=( Vector); // vector decrement
Vector& operator^=( Vector); // 3D exterior cross product
//----------------------------------------------------------
// Vector Properties
double len() { // vector length
return sqrt(x*x + y*y + z*z);
}
double len2() { // vector length squared (faster)
return (x*x + y*y + z*z);
}
//----------------------------------------------------------
// Special Operations
void normalize(); // convert vector to unit length
friend Vector sum( int, int[], Vector[]); // vector sum
friend Vector sum( int, double[], Vector[]); // vector sum
};
#endif SS_Vector_H

References
Michael Crowe, A History of Vector Analysis, (1967, revised reprint 1994)
Francis Hill, "The Pleasures of 'Perp Dot' Products" in Graphics Gems IV (1994)
[Note: the first critical definition has a typo, and should be: a

= (a
y
, a
x
).]
Dan Sunday, "Basic Linear Algebra", softsurfer.com (2002)

You might also like