"""Conversion module for geometries.
This module contains functions for conversion between different types of
geometric objects.
"""
import numpy as np
import ddg.math.projective as pmath
import ddg.math.symmetric_matrices as sm
from ddg.geometry import euclidean_models
from ddg.geometry.intersection import join
from ddg.geometry.quadrics import (
Quadric,
cayley_klein_sphere,
generalized_cayley_klein_sphere,
intersect_quadric_subspace,
)
from ddg.geometry.subspaces import Point, subspace_from_affine_points
[docs]def quadric_to_euclidean_sphere(quadric, atol=None, rtol=None):
"""Convert quadric to a Euclidean sphere, if it is one.
Parameters
----------
quadric : Quadric
atol, rtol : float (default=None)
If None is given, the global defaults are used. See :py:mod:`ddg.nonexact` for
details.
Returns
-------
Sphere
Raises
------
ValueError
If quadric is not a sphere.
"""
# Check signature
sgn = quadric.signature(affine=True)
k = quadric.subspace.dimension
if sgn != sm.AffineSignature(k, 1, last_entry=-1):
raise ValueError(
f"Signature of quadric is {sgn}, but has to be (k, 1) with -1 as "
"last entry for it to be a sphere."
)
Q = quadric.normalize(affine=True)
B = Q.subspace.matrix
try:
r, _, c = pmath.decompose_similarity(B, atol=atol, rtol=rtol)
except ValueError:
raise ValueError("Quadric is not a Euclidean sphere.")
c = subspace_from_affine_points(c)
return euclidean_models.ProjectiveModel.sphere(c, r, subspace=Q.subspace)
[docs]def euclidean_sphere_to_quadric(sphere):
"""Convert Euclidean sphere to quadric
Parameters
----------
sphere : Sphere
Returns
-------
Quadric
"""
r = sphere.radius
c = sphere.center.affine_point
k = sphere.ambient_dimension
Q = np.eye(k + 1)
Q[-1, -1] = -1
Q = Quadric(Q)
F = pmath.affine_transformation(r * np.eye(k), c)
Q = Q.transform(F)
if sphere.subspace.codimension > 0:
Q = intersect_quadric_subspace(Q, sphere.subspace)
return Q
[docs]def cayley_klein_sphere_to_quadric(sphere):
"""Convert Cayley-Klein sphere to quadric.
Parameters
----------
sphere : CayleyKleinSphere or MetricCayleyKleinSphere
Returns
-------
Quadric
"""
return cayley_klein_sphere(
sphere.center, sphere.cayley_klein_radius(), absolute=sphere.absolute
)
[docs]def generalized_cayley_klein_sphere_to_quadric(sphere):
"""Convert generalized Cayley-Klein sphere to quadric.
Parameters
----------
sphere : GeneralizedCayleyKleinSphere
Returns
-------
quadric
"""
return generalized_cayley_klein_sphere(
sphere.center, sphere.generalized_radius(), absolute=sphere.absolute
)
[docs]def quadric_to_subspaces(quadric):
"""If a quadric is equal to a finite collection of subspaces, convert it.
This is the case if and only if Q = quadric.matrix is semidefinite or its
rank is less than or equal to 2. In the first case, return ker(Q).
Otherwise, return join(ker(Q), P1) and join(ker(Q), P2), where P1, P2 are
the two points making up the non-degenerate part of the quadric.
Parameters
----------
quadric : Quadric
Returns
-------
tuple of Subspace
Notes
-----
No non-degenerate quadric with ambient dimension at least 2 is equal to a
collection of subspaces. A degenerate quadric Q is the join of its kernel
with its non-degenerate part, i.e. the quadric obtained by restricting the
quadratic form to a subspace complementary to the kernel. For this set to
be a collection of subspaces, the non-degenerate part must itself be a
collection of subspaces. This is only possible if it is contained in a line
(i.e. the rank of Q is 2) or it is empty (i.e. Q is positive semidefinite).
"""
sgn = quadric.signature()
if sgn.is_semi_definite:
return (quadric.singular_subspace,)
if quadric.rank > 2:
raise ValueError("Quadric is not equal to a collection of subspaces.")
Q = quadric.normalize()
kernel = Q.singular_subspace
# The only case remaining is diag(1, -1, 0,...,0)
v1, v2 = Q.subspace.points[:2]
P1 = Point(v1 + v2)
P2 = Point(v1 - v2)
return join(kernel, P1), join(kernel, P2)