Introduction to geometry
The projective approach
Our library takes an approach to geometry following Felix Klein’s Erlangen program.[1] In the following paragraphs, we will attempt to introduce this approach to those unfamiliar with it:
We consider all of our geometric objects (everything that is defined in the
ddg.geometry package) as subsets of a projective space \(\RP^n\) or in
some special cases of \(\CP^n\). All other geometries, including Euclidean,
hyperbolic and spherical geometry, are realized as subgeometries of projective
geometry. This means that the model space for them is a subset of \(\RP^n\)
and they define additional structure like angles and distances. For example,
Euclidean geometry is \(\R^n \subset \RP^n\) (identified with the points
“not at infinity”, see the Conventions section below) together with the usual
Euclidean metric and angles.
More abstractly, a geometry can be thought of as a set \(X\) called the model space together with a group \(G\) of transformations acting on the space, which preserves the relevant structure like distances and angles. Euclidean geometry could be defined in this way as \(\R^n\) together with the group of isometries \(x \mapsto Ax + b\) with \(A \in \operatorname{O}(n)\) and \(b \in \R^n\). A subgeometry is then a subset \(Y \subseteq X\) together with a subgroup \(H < G\). It turns out that projective geometry, i.e. \(\RP^n\) together with the group of projective transformations \(\operatorname{PGL}(n+1,\R)\), contains many other familiar geometries as subgeometries. This is the motivation for our approach.
Conventions
In the geometry package, all points are assumed to be given in homogeneous
coordinates, unless stated otherwise. Exceptions are functions like
subspace_from_affine_points().
When switching from homogeneous coordinates to affine coordinates, we follow the convention of homogenizing and dehomogenizing with respect to the last component:
>>> import numpy as np
>>> from ddg.math.projective import homogenize, dehomogenize
>>> print(homogenize(np.array([1.0, 2.0, 3.0])))
[1. 2. 3. 1.]
>>> print(dehomogenize(np.array([1.0, 2.0, 3.0, 1.0])))
[1. 2. 3.]
>>> print(dehomogenize(np.array([2.0, 4.0, 6.0, 2.0])))
[1. 2. 3.]
So “points at infinity” for us are points whose last component is 0.
To see what an object would look like in a different affine chart, use the
method change_affine_picture() of
ddg.abc.LinearTransformable objects to transform the object with an
appropriate projective transformation before visualizing it in the usual way.
Footnotes