hosted by: Euclid++ at sourceforge.net

Euclid Overview

A short introduction into the Euclid library, a library providing orientational analysis and Euclidean vectors.
Andreas Harnack
ah8 at freenet dot de
November 2007

Rational

The primary objective of the library is to provide type safety for coordinate values in graphical applications. Please consider:

void set_position(int y, int x);

int x;
int y;

set_position(x, y); // error remains undetected

versus:

typedef typename euclid::dimension<int,0> dimX;
typedef typename euclid::dimension<int,1> dimY;

void set_position(dimY y, dimX x);

dimX x;
dimY y;

set_position(x, y); // Error: type mismatch
set_position(y, x); // OK

The name dimension is used to mark the values as being oriented along a specific direction in space, like a two‑dimensional space has two dimensions.

A second objective is to aggregate values of different dimensions into single objects and provide appropriate operations for them. This can significantly improve code readability by reducing error prone complexity. Please consider:

int cX0 = (width()-centralX)  / 2;
int cY0 = (height()-centralY) / 2;
int cX1 = (width()+centralX) / 2;
int cY1 = (height()+centralY) / 2;

int mX0 = mousePosX - movableX/2;
int mY0 = mousePosY - movableY/2;
int mX1 = mousePosX - movableX/2;
int mY1 = mousePosY - movableY/2;

int maxX0 = std::min(cX0, mX0);
int maxY0 = std::min(cY0, mY0);
int maxX0 = std::max(cX1, mX1);
int maxY0 = std::max(cY1, mY1);

int minX0 = std::max(cX0, mX0);
int minY0 = std::max(cY0, mY0);
int minX0 = std::min(cX1, mX1);
int minY0 = std::min(cY1, mY1);

drawRect(cX0, cY0, cX1, cY1, Line::Solid, Colour::black);
drawRect(mX0, mY0, mX1, mY1, Line::Solid, Colour::black);
drawRect(maxX0, maxY0, maxX1, maxY1, Line::Doted, Colour::blue);
drawRect(minX0, minY0, minX1, minY1, Line::Doted, Colour::red);

versus:

typedef typename euclid::dimension<int,0> dimX;
typedef typename euclid::dimension<int,1> dimY;
typedef typename euclid::vector<int,2> vect;
typedef typename euclid::cuboid<int,2> rect;

rect central((size()-sizeCentral) / 2, (size()+sizeCentral) / 2);
rect movable(mousePos - sizeMovable/2, mousePos + sizeMovable/2);

drawRect(central, Line::Solid, Colour::black);
drawRect(movable, Line::Solid, Colour::black);
drawRect(closure(central, movable), Line::Doted, Colour::blue);
drawRect(intersect(central, movable), Line::Doted, Colour::red);

In this example1 any arithmetic operations expands to two, the closure/intersection operation to four elementary operations. Although the compiler generates effectively the same target code, only two lines of source code are necessary, instead of 16. Despite of the additional typedefs, the resulting code is shorter, better readable and much less error prone then the former version.

The library is applicable not only to graphical programming but to all problems involving Euclidean geometry.

Resources

The library has been made a sourceforge.net project. Please also visit the project's web site at euclid-vector.sourceforge.net.

Concepts

Mathematically, aggregated dimensions are vectors. This leads to a vector model quiet different from the one commonly used in programming. Vector components are now type distinct, consequently such a vector can neither be an array-like structure, nor fit into any other container model having iterators or (value‑) parameter dependent access operators. Instead, components are accessed using type conversion. To distinguish the new vector model from the one implemented in std::vector its instances shall be called Euclidean vectors, referring to the intended application in Euclidean geometry.

Scope

The scope of the library is to provide type safety for coordinate values and a representation for Euclidean vectors. It can be completed by representations of geometrical objects , as it has already been demonstrated above. The library, however, is not intended to be a Linear Algebra library or provide any other graphical or geometrical functionality. It only provides type checks and mapping of elementary operations to vector components, it intentionally does not provide any non‑trivial algorithms.2

Framework

The type checking concept applied here is a special form of dimensional analysis. Dimensional analysis has recently been discussed with respect to physical dimensions. The library presented here is deals with orientational dimensions, as such it is an orientational analysis library. Dimensional analysis in general is a rather wide field and has many other aspects and appearances. The most commonly known one are probably complex numbers as implemented in std::complex, which also fit into this framework. An overview of dimensional analysis is given in the main documentation.

Implementation

Euclidean vectors are implemented as a recursive templates. The fundamental structure of the class templates is shown below. All templates are put into a name space euclid:

namespace euclid
{
// dimension class template
template<typename T, unsigned int D> class dim
{
T v;
public:
// operations allowed for dim ...
};

// vector class template
template<typename T, unsigned int D> class vec
{
dim<T, D-1> d;
vec<T, D-1> v;
public:
// element access
template <unsigned int I>
operator dim<T,I>() const { return dim<T, I>(v); }
operator dim<T,D-1>() const { return d; }

// operations allowed for vec ...
};

// vector class base case specialization
template<typename T> class vec<T,1>
{
dim<T,0> d;
public:
// element access
operator dim<T,0>() const { return d; }

// operations allowed for vec ...
};

// never defined
template<typename T> class vec<T,0>;
};

A detailed description can be found in the main documentation.

Usage

Many applications, modules or classes will need vectors and dimension of one specific base type only. As already demonstrated in the examples above, the instances can easily be typedefed to short and intuitive names, that can be used almost like built in types:

typedef ... scalar_type;
typedef typename euclid::dim<scalar_type,0> dimX;
typedef typename euclid::dim<scalar_type,1> dimY;
typedef typename euclid::vec<scalar_type,2> vect;

For classes, this can be done using template parameters, see cuboid.h for an example.

Examples

An example can be found in the example directory. There's also a small case study demonstrating the effect the library can have on code quality.


1 The complete code can be found in the example directory. It draws four rectangles in the current window: 1) one of size centralX/Y in the centre of the widget, 2) one of size movableX/Y around the current mouse pointer position, 3) the smallest rectangle enclosing both, 4) the smallest rectangle enclosing the overlapping area, if any.

2 Though any Linear Algebra, graphical or geometrical library should use it, obviously ;-)