import numpy as np
from ddg.datastructures.nets.net import (SmoothNet, SmoothCurve, PointNet,
EmptyNet)
import ddg.datastructures.nets.utils as nutils
[docs]def subspace_to_smooth_net(subspace, affine=False, convex=False,
affine_component=-1):
"""Convert a subspace to a smooth net.
Parameters
----------
subspace : ddg.geometry.subspaces.Subspace
affine : bool (default=False)
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=False)
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 and gives::
((1 - a1 * u1[i] - ... - ak * uk[i]) / u0[i]) * u0
+ a1 * u1
+ ...
+ ak * uk
where `i` is `affine_component`. The basis u0,...,uk is
just the given basis ``subspace.points``, but permuted: The first
vector not at infinity is moved to the first position. Note that this
can also be written as::
u0 / u0[i]
+ a1 * (u1 - (u1[i] / u0[i]) * u0)
+ ...
+ ak * (uk - (uk[i] / u0[i]) * 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.
affine_component : int (default=-1)
Used to dehomogenize.
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.
elif subspace.dimension == 0:
if affine:
return PointNet(subspace.affine_point(affine_component))
else:
return PointNet(subspace.point)
if convex:
if subspace.at_infinity(affine_component):
raise ValueError("To use the 'convex' parametrization, the "
"subspace can not be at infinity.")
i = affine_component
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 np.isclose(u0[i], 0, atol=subspace.atol,
rtol=subspace.rtol):
mask[idx] = False
break
def f(*coeffs):
a0 = (1 - np.dot(coeffs, subspace.matrix[i, mask])) / u0[i]
return a0 * u0 + subspace.matrix[:, mask] @ coeffs
if subspace.dimension == 1:
net = SmoothCurve(f, [-np.inf, np.inf])
else:
net = SmoothNet(f, [[-np.inf, np.inf]] * subspace.dimension)
else:
def f(*coeffs):
if all([c == 0 for c in coeffs]):
raise ValueError("Zero vector is not a valid homogeneous "
"coordinate vector.")
return subspace.matrix @ coeffs
net = SmoothNet(f, [[-np.inf, np.inf]] * (subspace.dimension + 1))
if affine:
nutils.dehomogenize(net, affine_component=affine_component)
return net