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