.. _geometries: Geometries ========== The geometry model modules like :py:mod:`~.geometry.hyperbolic_models`, :py:mod:`~.geometry.euclidean_models` etc. define geometries by adding additional structure to a projective space. This can include metrics, angle measurements, etc. Each geometry module has the following contents: - Classes representing models for the geometry. These classes define methods that deal specifically with objects represented in that model. - Functions to convert geometric objects between different models, if there is more than one. For some geometry model classes, there are shortcuts, for example: :py:class:`ddg.geometry.euclidean` for :py:class:`ddg.geometry.euclidean_models.ProjectiveModel`. These are the "projective models" (and their duals, if applicable), which make the relation to projective geometry most obvious and are often nice to work with. How exactly objects are represented in a model is documented in the docstrings of the geometry model classes. .. contents:: Table of contents :local: :backlinks: none Example: Euclidean geometry (and its lift to Moebius geometry) -------------------------------------------------------------- The most familiar geometry is of course Euclidean geometry. We will create two points in the :py:class:`~.euclidean_models.ProjectiveModel` of Euclidean geometry, in which we compute distances as norms of differences in affine coordinates. We will then convert these points to the :py:class:`~.euclidean_models.MoebiusModel`, which is obtained by stereographically projecting to the 3-sphere in 4-space. In this other model, the distance is computed with a different formula, but should match the distance in the projective model. As a geometry that comes with a metric, all models of Euclidean geometry implement the method :py:meth:`~.geometry_model_templates.MetricGeometry.d` that computes the Euclidean distance between objects. All geometries that have a metric work this way, see `Metric geometries`_. .. doctest:: >>> import ddg >>> euc = ddg.geometry.euclidean(3) >>> print(euc) Projective model of 3D Euclidean geometry >>> euc.dimension 3 >>> euc.ambient_dimension 3 >>> p1 = ddg.geometry.subspace_from_affine_points([0, 0, 0]) >>> p2 = ddg.geometry.subspace_from_affine_points([1, 1, 1]) >>> euc.d(p1, p2) 1.732... >>> mob = euc.moebius() >>> mob.dimension 3 >>> mob.ambient_dimension 4 >>> print(mob.absolute) quadric in 4D projective space signature: (4, 1) matrix: [[ 1. 0. 0. 0. 0.] [ 0. 1. 0. 0. 0.] [ 0. 0. 1. 0. 0.] [ 0. 0. 0. 1. 0.] [ 0. 0. 0. 0. -1.]] >>> print(mob.fixed_point) Point in 4D projective space Homogeneous coordinates: [0. 0. 0. 0.5 0.5] >>> p1_mob = euc.to_moebius(p1) >>> p2_mob = euc.to_moebius(p2) >>> mob.d(p1_mob, p2_mob) 1.732... Note that the attribute ``dimension`` always means the "intuitive" dimension of the geometry. ``ambient_dimension`` is the dimension of the projective space the model lives in. The geometry models also provide a simple way to check whether a point in projective space is actually contained in the model of the geometry. For example, the projective model of Euclidean geometry consists of all points not at infinity: .. doctest:: >>> p1 in euc True >>> p_inf = ddg.geometry.Point([0, 0, 1, 0]) >>> p_inf.at_infinity() True >>> p_inf in euc False Spheres ------- Spheres are created using factory methods of the geometry model classes. Geometries for which spheres are implemented have methods :py:meth:`~.SphereFactory.sphere` that can be used to create them. Geometries defined by quadrics additionally have methods :py:meth:`~.CayleyKleinSphereFactory.cayley_klein_sphere` and :py:meth:`~.CayleyKleinSphereFactory.generalized_cayley_klein_sphere`. .. doctest:: >>> s1 = euc.sphere(p1, 1.0) >>> print(s1) Sphere in: Projective model of 3D Euclidean geometry Center: [0. 0. 0. 1.] Radius: 1.0 The lift to Moebius geometry is represented by a hyperplanar section of the absolute quadric, which is a quadric lying in a subspace. .. doctest:: >>> s1_mob = euc.to_moebius(s1) >>> print(s1_mob) quadric contained in 3D subspace in 4D projective space signature: (3, 1) matrix: [[ 1. 0. 0. 0.] [ 0. 1. 0. 0.] [ 0. 0. 1. 0.] [ 0. 0. 0. -1.]] basis of containing subspace (columns): ... >>> euc.from_moebius(s1_mob) == s1 True Three spheres in Euclidean 3-space have a unique common orthogonal circle. Let us define two more spheres and compute this circle. .. doctest:: >>> s2 = euc.sphere(p2, 0.1) >>> s3 = euc.sphere_from_affine_point_and_normals([1, 0, 0], 0.3) >>> c = euc.orthogonal_sphere(s1, s2, s3) >>> print(c) Circle in: Projective model of 3D Euclidean geometry Center: [0.955 0.52 0.52 1. ] Radius: 0.672... Containing subspace basis (columns): ... In the lift to Moebius geometry this circle may be obtained by polarity with respect to the absolute quadric. Let us check that we obtain the same circle this way. .. doctest:: >>> s2_mob = euc.to_moebius(s2) >>> s3_mob = euc.to_moebius(s3) >>> plane = mob.absolute.polarize( ... ddg.geometry.intersect(s1_mob.subspace, s2_mob.subspace, s3_mob.subspace) ... ) >>> c_mob = ddg.geometry.intersect(plane, mob.absolute) >>> c == euc.from_moebius(c_mob) True Example: Hyperbolic geometry ---------------------------- .. doctest:: >>> hyp = ddg.geometry.hyperbolic(2) >>> print(hyp) Projective model of 2D hyperbolic geometry >>> p = ddg.geometry.subspace_from_affine_points([0, 0.5]) >>> p in hyp True >>> c = hyp.sphere(p, 0.2) >>> print(c) Circle in: Projective model of 2D hyperbolic geometry Center: [0. 0.5 1. ] Radius: 0.2 Note that in the Poincare disk model hyperbolic circles look like Euclidean circles. The result after the transformation is thus treated like a Euclidean circle. .. doctest:: >>> print(hyp.to_poincare(c)) Circle in: Projective model of 2D Euclidean geometry ... The lift to Möbius geometry is non-unique and thus always gives two results .. doctest:: >>> p1_mob, p2_mob = hyp.to_moebius(p) >>> p == hyp.from_moebius(p1_mob) True >>> p == hyp.from_moebius(p2_mob) True >>> c1_mob, c2_mob = hyp.to_moebius(c) >>> c == hyp.from_moebius(c1_mob) True >>> c == hyp.from_moebius(c2_mob) True Example: Bisectors ------------------ Let us look at the creation of `bisections `_. Given two hyperplanes, the function :py:func:`~ddg.geometry.euclidean_models.ProjectiveModel.angle_bisectors` computes the two angle bisecting hyperplanes. Alternatively one can also use :py:func:`~ddg.geometry.euclidean_models.ProjectiveModel.angle_bisector_orientation_preserving` or :py:func:`~ddg.geometry.euclidean_models.ProjectiveModel.angle_bisector_orientation_reversing` for a specific one of the two bisectors. .. doctest:: >>> euc = ddg.geometry.euclidean(3) >>> h1 = ddg.geometry.hyperplane_from_normal((1, 0, 0), level=0) >>> h2 = ddg.geometry.hyperplane_from_normal((0, 1, 0), level=0) >>> h_preserving, h_reversing = euc.angle_bisectors(h1, h2) >>> ddg.geometry.normal(h_preserving) array([ 0.70710678, 0.70710678, -0. ]) >>> ddg.geometry.normal(h_reversing) array([ 0.70710678, -0.70710678, 0. ]) This will result in the following image. .. image:: angle_bisectors.png :align: center :width: 65% Similarly, you can use :py:func:`~ddg.geometry.euclidean_models.ProjectiveModel.perpendicular_bisector` to obtain an orthogonal hyperplane intersecting the join of two points in their (affine) midpoint. .. doctest:: >>> p1 = ddg.geometry.Point((0, 0, 1)) >>> p2 = ddg.geometry.Point((1, 1, 1)) >>> orthogonal_line = euc.perpendicular_bisector(p1, p2) >>> ddg.geometry.normal(orthogonal_line) array([-0.70710678, -0.70710678]) Which will result in the following image. .. image:: perpendicular_bisector.png :align: center :width: 65% This works for arbitrary dimensions. .. doctest:: >>> p1 = ddg.geometry.Point((0, 0, 0, 1)) >>> p2 = ddg.geometry.Point((1, 1, 1, 1)) >>> orthogonal_hyperplane = euc.perpendicular_bisector(p1, p2) >>> orthogonal_hyperplane.affine_points [array([-2.1941919 , 4.36085856, -0.66666667]), array([-2.1941919 , -0.66666667, 4.36085856]), array([0.93303028, 0.28348486, 0.28348486])] >>> ddg.geometry.normal(orthogonal_hyperplane) array([0.57735027, 0.57735027, 0.57735027]) Example: Reflections in hyperplane ---------------------------------- Similarly `reflections `_ in hyperplanes can be obtained in the following way. Given a subspace and an hyperplane, the function :py:func:`~ddg.geometry.euclidean_models.ProjectiveModel.reflect_in_hyperplane` computes the reflection of the subspace in the hyperplane. .. literalinclude:: ../../../examples/blender/geometry/subspaces/reflection_in_hyperplane.py :language: python :start-after: [construction] :end-before: [construction] This will result in the following image. .. image:: reflection.gif :align: center This works for arbitrary dimensions. The different types of geometries --------------------------------- There are three basic types of geometries, implemented as abstract base classes: Metric geometries ~~~~~~~~~~~~~~~~~ :py:class:`~.MetricGeometry`. Geometries of this type just have a dimension and define a metric :py:meth:`~.MetricGeometry.d`. Cayley-Klein geometries ~~~~~~~~~~~~~~~~~~~~~~~ :py:class:`~.CayleyKleinGeometry`. Geometries of this type have a quadric and its induced structure. For example, there is a Cayley-Klein distance, which is defined like this: Let :math:`Q` be a quadric in :math:`\RP^n` called the *absolute quadric* or just *absolute* and let :math:`\langle\cdot,\cdot\rangle` be its associated scalar product. We define the *Cayley-Klein distance* (not a metric!) on :math:`\RP^n \setminus Q` as .. math:: K_Q([x], [y]) \coloneqq \frac{\langle x, y \rangle_Q^2} {\langle x, x \rangle_Q \langle y, y\rangle_Q} The presence of a Cayley-Klein distance means that (generalized) Cayley-Klein spheres can be created in these geometries using the factory methods ``cayley_klein_sphere`` and ``generalized_cayley_klein_sphere``. .. note:: Sometimes in the literature (for example Wikipedia), Cayley-Klein geometries are required to have a metric. We do not require this, because the Cayley-Klein distance and Cayley-Klein spheres still make sense, even without a metric. Metric Cayley-Klein geometries ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :py:class:`~.MetricCayleyKleinGeometry`. Geometries of this type are both Metric and Cayley-Klein geometries, as you would expect. A *Cayley-Klein metric* is a metric :math:`d` derived from such a Cayley-Klein distance, namely one that is a function of :math:`K_Q`. In the projective model of hyperbolic geometry for example, the relation is given by the equation .. math:: \cosh^2(d([x],[y])) = K_Q([x],[y]) where :math:`Q` is a quadric of signature :math:`(n,1)` and the metric can be defined for two points which are on the same side of the quadric. You can see that the metric can always be converted to a Cayley-Klein distance, but not vice versa. Which Cayley-Klein distances correspond to metric distances depends on the geometry. In these geometries, both metric spheres (using the method ``sphere``) and Cayley-Klein spheres can be created.