import numpy as np
import ddg.datastructures.nets.utils as nutils
from ddg import nonexact
from ddg.datastructures.nets.net import EmptyNet, PointNet, SmoothCurve, SmoothNet
[docs]def subspace_to_smooth_net(subspace, affine=True, convex=True):
"""Convert a subspace to a smooth net.
Parameters
----------
subspace : ddg.geometry.subspaces.Subspace
affine : bool (default=True)
Whether the resulting smooth net should return homogeneous or affine
coordinates. Note that this works by parametrizing using one of the two
parametrizations explained below and then dehomogenizing, so if the
parametrization produces points at infinity, you will get errors.
convex : bool (default=True)
Which parametrization to use. Let k=subspace.dimension and let the ai
be the parameters given to the resulting net.
``convex=False`` takes k+1 parameters and gives ::
a0 * v0 + ... + ak * vk
where v0,...,vk are the k+1 homogeneous coordinate vectors spanning
the subspace given in ``subspace.points``.
``convex=True`` can only be used if the subspace is not at infinity. It
takes k parameters a1,...,ak and gives::
(1 - a1 * u1[-1] - ... - ak * uk[-1]) * u0 + a1 * u1 + ... + ak * uk.
The basis u0,...,uk is the given basis ``subspace.points``, permuted and
dehomogenized: The first vector not at infinity is moved to the first position
and last entries ui[-1] of vectors are normalized to either 0 or 1. Note that
this can also be written as::
u0 + a1 * (u1 - u1[-1] * u0) + ... + ak * (uk - uk[-1] * u0).
If `subspace` is a point, this parameter has no effect. The returned
net will just be a PointNet with the homogeneous or affine coordinates.
Returns
-------
SmoothNet, SmoothCurve, PointNet or EmptyNet
Raises
------
ValueError
If `convex` is True and subspace is at infinity.
"""
# Empty space
if subspace.dimension == -1:
# subspace.matrix is empty with shape (k+1, 0)
return EmptyNet(subspace.matrix)
# Point
# This is an exception to the convex=False parametrization. Strictly
# speaking, a converted Point should be returned as a smooth curve, but I
# feel this is more useful.
if subspace.dimension == 0:
if affine:
return PointNet(subspace.affine_point)
return PointNet(subspace.point)
if convex:
f = parametrize_subspace_convex(subspace.dehomogenize())
if subspace.dimension == 1:
net = SmoothCurve(f, [-np.inf, np.inf])
else:
net = SmoothNet(f, [[-np.inf, np.inf]] * subspace.dimension)
else:
f = parametrize_subspace_homogeneous(subspace)
net = SmoothNet(f, [[-np.inf, np.inf]] * (subspace.dimension + 1))
if affine:
net = nutils.dehomogenize(net)
return net
[docs]def parametrize_subspace_convex(subspace):
"""Get "convex" parametrization of subspace.
Let u0,...,uk be the given basis ``subspace.points``, but permuted: The first vector
not at infinity is moved to the first position. Then the parametrization is::
((1 - a1 * u1[-1] - ... - ak * uk[-1]) / u0[-1]) * u0
+ a1 * u1
+ ...
+ ak * uk
= u0 / u0[i]
+ a1 * (u1 - (u1[-1] / u0[-1]) * u0)
+ ...
+ ak * (uk - (uk[-1] / u0[-1]) * u0)
Parameters
----------
subspace : Subspace
Returns
-------
Callable float,...,float -> numpy.ndarray
Raises
------
ValueError
If `convex` is True and subspace is at infinity.
"""
if subspace.at_infinity():
raise ValueError(
"To use the 'convex' parametrization, the subspace can not be at infinity."
)
mask = np.full(subspace.dimension + 1, True)
# Search for a point not at infinity to use as center
for idx, u0 in enumerate(subspace.points):
if not nonexact.isclose(u0[-1], 0, atol=subspace.atol, rtol=subspace.rtol):
mask[idx] = False
break
def parametrization_convex(*coeffs):
"""Convex parametrization.
Parameters
----------
*coeffs : float
`dimension` parameters.
Returns
-------
numpy.ndarray of shape (n+1,)
Homogeneous coordinate vector.
"""
a0 = (1 - np.dot(coeffs, subspace.matrix[-1, mask])) / u0[-1]
return a0 * u0 + subspace.matrix[:, mask] @ coeffs
return parametrization_convex
[docs]def parametrize_subspace_homogeneous(subspace):
"""Get homogeneous parametrization of subspace.
See :py:func:`.subspace_to_smooth_net` for how this parametrization is defined.
Parameters
----------
subspace : Subspace
Returns
-------
Callable float,...,float -> numpy.ndarray
"""
def parametrization_homogeneous(*coeffs):
"""Homogeneous parametrization.
Parameters
----------
*coeffs : float
`dimension + 1` parameters. Not all can be 0, since this would not result in
a valid homogeneous coordinate vector.
Returns
-------
numpy.ndarray of shape (n+1,)
Homogeneous coordinate vector.
Raises
------
ValueError
If all coefficients are 0.
"""
coeffs = np.array(coeffs)
if nonexact.allclose(coeffs, 0.0, atol=subspace.atol, rtol=subspace.rtol):
raise ValueError(
"Zero vector is not a valid homogeneous coordinate vector."
)
return subspace.matrix @ coeffs
return parametrization_homogeneous