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.
The library has been made a sourceforge.net project. Please also visit the project's web site at euclid-vector.sourceforge.net.
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.
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
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.
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.
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.
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 ;-)