ddg.math.projective module

Utility functions for the projective module.

ddg.math.projective.homogenize(vector)[source]

Homogenize a coordinate vector by inserting a 1.

Parameters:
vectorarray_like of shape (n,)

The vector to be homogenized.

Returns:
numpy.ndarray of shape (n+1,)

Examples

>>> from ddg.math.projective import homogenize
>>> homogenize((3, 4, 5))
array([3, 4, 5, 1])
ddg.math.projective.dehomogenize(vector, atol=None, rtol=None)[source]

Pick an affine representative for a homogeneous coordinate vector.

Parameters:
vectornumpy.ndarray of shape (n+1,)

The vector to be dehomogenized.

atol, rtolfloat (default=None)

This function uses the global tolerance defaults if atol or rtol are set to None. See ddg.nonexact for details.

Returns:
numpy.ndarray of shape (n,)
Raises:
ValueError

If last value of vector is 0.

Notes

This function uses the global tolerance defaults if atol or rtol are set to None. See ddg.abc.NonExact for details.

Examples

>>> import numpy as np
>>> from ddg.math.projective import dehomogenize
>>> dehomogenize(np.array((1, 0, 1)))
array([1., 0.])
ddg.math.projective.in_general_position(points, atol=None, rtol=None)[source]

Checks whether a list of k+1 points in n-dimensional projective are in general position.

Parameters:
pointsarray_like

Array_like of numpy.ndarray of shape (n+1,).

Returns:
bool

True if the points are in general position, False otherwise.

Raises:
ValueError

If no points are given.

Notes

If k <= n, we test whether their lifts in (n+1)-dimensional space are linearly independent. If k > n, we test whether any (n+1)-subset of them is contained in a hyperplane of n-dimensional projective space.

Examples

>>> import numpy as np
>>> from ddg.math.projective import in_general_position
>>> points_2d = [
...     np.array([1.0, 0.0, 1.0]),
...     np.array([0.0, 1.0, 1.0]),
...     np.array([1.0, 1.0, 1.0]),
...     np.array([0.5, 0.5, 1.0]),
... ]
>>> in_general_position(points_2d)
False
>>> points_3d = [
...     np.array([1.0, 0.0, 0.0, 1.0]),
...     np.array([0.0, 1.0, 0.0, 1.0]),
...     np.array([0.0, 0.0, 1.0, 1.0]),
...     np.array([1.0, 1.0, 1.0, 1.0]),
...     np.array([0.5, 0.5, 0.5, 1.0]),
... ]
>>> in_general_position(points_3d)
True
ddg.math.projective.is_projective_frame(points)[source]

Checks whether a list of n+2 points in n-dimensional projective space form a projective frame.

Parameters:
pointsarray_like

Array_like of numpy.ndarray of shape (n+1,)

Returns:
bool

True if the points are a projective frame, False otherwise.

Raises:
ValueError

If no points are given.

Notes

A list of n+1 fundamental points and one unit point in projective space are called a projective frame if they are in general position.

Examples

>>> import numpy as np
>>> from ddg.math.projective import is_projective_frame
>>> points_2d = [
...     np.array([1.0, 0.0, 1.0]),
...     np.array([0.0, 1.0, 1.0]),
...     np.array([1.0, 1.0, 1.0]),
...     np.array([0.5, 0.5, 1.0]),
... ]
>>> is_projective_frame(points_2d)
False
>>> points_3d = [
...     np.array([1.0, 0.0, 0.0, 1.0]),
...     np.array([0.0, 1.0, 0.0, 1.0]),
...     np.array([0.0, 0.0, 1.0, 1.0]),
...     np.array([1.0, 1.0, 1.0, 1.0]),
...     np.array([0.5, 0.5, 0.5, 1.0]),
... ]
>>> is_projective_frame(points_3d)
True
ddg.math.projective.point_of_intersection(planes, homogeneous_coords=True)[source]

Computes the point of intersection of given hyperplanes (in dual coordinates). If more hyperplanes are given than the dimension of the space, a least square solution is computed.

E.g. in RP^3 it computes the intersection of (at least) three given planes.

Parameters:
planesnumpy.ndarray of shape (n, k)

Amount of planes n and dimension of each plane k.

homogeneous: bool, (default=True)

Determines whether a 1.0 will be added to the output vector.

Returns:
intersectionnumpy.ndarray of shape (k + 1,) or (k,)

Intersection of the given planes

Examples

>>> from ddg.math.projective import point_of_intersection
>>> plane1 = np.array([1.0, 0.0, 0.0, -1.0])
>>> plane2 = np.array([0.0, 1.0, 0.0, -2.0])
>>> plane3 = np.array([0.0, 0.0, 1.0, -3.0])
>>> planes = np.array((plane1, plane2, plane3))
>>> point_of_intersection(planes)
array([1., 2., 3., 1.])
ddg.math.projective.affine_transformation(A, b)[source]

Assemble affine transformation.

Given a matrix A and a vector b, assemble the projective transformation

     A | b
F =  --|---
     0 | 1

It corresponds in affine coordinates to the affine transformation x -> Ax + b.

Parameters:
Anumpy.ndarray of shape (n, m)
bnumpy.ndarray of shape (n,)
Returns:
Fnumpy.ndarray of shape (n+1, m+1)

Examples

>>> import numpy as np
>>> from ddg.math.projective import affine_transformation
>>> A = np.arange(9).reshape((3, 3))
>>> A
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])
>>> b = np.ones(3) * 15
>>> b
array([15., 15., 15.])
>>> affine_transformation(A, b)
array([[ 0.,  1.,  2., 15.],
       [ 3.,  4.,  5., 15.],
       [ 6.,  7.,  8., 15.],
       [ 0.,  0.,  0.,  1.]])
ddg.math.projective.decompose_affine_transformation(F, atol=None, rtol=None)[source]

Decompose affine transformation.

Given a projective transformation

     A | b
F =  --|---
     0 | s

disassemble it into a matrix A/s and a vector b/s.

Parameters:
Fnumpy.ndarray of shape (n+1, m+1)
atol, rtolfloat (default=None)

This function uses the global tolerance defaults if atol or rtol are set to None. See ddg.nonexact for details.

Returns:
Anumpy.ndarray of shape (n, m)
bnumpy.ndarray of shape (n,)

Examples

>>> import numpy as np
>>> from ddg.math.projective import decompose_affine_transformation
>>> F = np.array([[0, 1, 2, 15], [3, 4, 5, 15], [6, 7, 8, 15], [0, 0, 0, 1]])
>>> F
array([[ 0,  1,  2, 15],
       [ 3,  4,  5, 15],
       [ 6,  7,  8, 15],
       [ 0,  0,  0,  1]])
>>> A, b = decompose_affine_transformation(F)
>>> A
array([[0., 1., 2.],
       [3., 4., 5.],
       [6., 7., 8.]])
>>> b
array([15., 15., 15.])
ddg.math.projective.decompose_similarity(F, atol=None, rtol=None)[source]

Decompose a similarity transformation into scaling, orthogonal matrix and translation.

Parameters:
Fnumpy.ndarray of shape (n+1, m+1)
atol, rtolfloat (default=None)
Returns:
sfloat

Scaling factor

Onumpy.ndarray of shape (n, m)

Orthogonal matrix

bnumpy.ndarray of shape (n,)

Translation vector

atol, rtolfloat (default=None)
Raises:
ValueError

If F does not represent a similarity embedding.

Examples

>>> import numpy as np
>>> from ddg.math.projective import decompose_similarity
>>> F = np.array(
...     [
...         [2.0, 0.0, 0.0, 0.0],
...         [0.0, 2.0, 0.0, 0.0],
...         [0.0, 0.0, 2.0, 0.0],
...         [0.0, 0.0, 0.0, 1.0],
...     ]
... )
>>> s, O, b = decompose_similarity(F)
>>> s
2.0
>>> O
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])
>>> b
array([0., 0., 0.])
ddg.math.projective.affine_component_transformation(n, before, after=-1)[source]

Transformation transforming between affine pictures.

If an object dehomogenized wth affine component before produces a certain affine picture, the object transformed with F will produce the same picture when dehomogenized with affine component after.

Parameters:
nint

Size of returned matrix

beforeint
afterint (default=-1)
Returns:
Fnumpy.ndarray of shape (n, n)

Examples

>>> from ddg.math.projective import affine_component_transformation
>>> affine_component_transformation(4, 2)
array([[1., 0., 0., 0.],
       [0., 1., 0., 0.],
       [0., 0., 0., 1.],
       [0., 0., 1., 0.]])
>>> affine_component_transformation(5, 4, 2)
array([[1., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0.],
       [0., 0., 0., 0., 1.],
       [0., 0., 1., 0., 0.],
       [0., 0., 0., 1., 0.]])
ddg.math.projective.translation(normal, direction, atol=None, rtol=None)[source]

Projective translation.

Returns the matrix

I + direction @ normal.T

This transformation fixes all vectors v in the hyperplane orthogonal to normal. It can also be thought of as mapping

v -> v + dot(normal, v) * direction
Parameters:
normal, directionnumpy.ndarray of shape (n,)

direction must lie in the hyperplane orthogonal to normal.

atol, rtolfloat (default=None)

This function uses the global tolerance defaults if atol or rtol are set to None. See ddg.nonexact for details.

Returns:
numpy.ndarray of shape (n, n)
Raises:
ValueError
  • If the dimensions of normal and direction do not match.

  • If direction is not orthogonal to normal.

Examples

>>> from ddg.math.projective import translation
>>> translation(np.array((1, 0, 1)), np.array((-1, 0, 1)))
array([[ 0.,  0., -1.],
       [ 0.,  1.,  0.],
       [ 1.,  0.,  2.]])
>>> translation(np.array((1, 0, 1, 0)), np.array((-1, 0, 1, 0)))
array([[ 0.,  0., -1.,  0.],
       [ 0.,  1.,  0.,  0.],
       [ 1.,  0.,  2.,  0.],
       [ 0.,  0.,  0.,  1.]])
ddg.math.projective.translations_from_quad_3d(x00, x10, x11, x01, atol=None, rtol=None)[source]

Translations that map along a quadrilateral in RP2.

Returns two projective translations T1, T2 of RP2 such that

       T1
  x01 ---> x11
   ^        ^
T2 |        | T2
   |        |
  x00 ---> x10
       T1
Parameters:
x00, x10, x11, x01numpy.ndarray of shape (3,)
atol, rtolfloat (default=None)

This function uses the global tolerance defaults if atol or rtol are set to None. See ddg.nonexact for details.

Returns:
T1, T2numpy.ndarray of shape (4, 4)

Examples

>>> import numpy as np
>>> from ddg.math.projective import translations_from_quad_3d
>>> x00 = np.array([0.0, 0.0, 1.0])
>>> x10 = np.array([1.0, 0.0, 1.0])
>>> x11 = np.array([1.0, 1.0, 1.0])
>>> x01 = np.array([0.0, 1.0, 1.0])
>>> T1, T2 = translations_from_quad_3d(x00, x10, x11, x01)
>>> np.array(np.round(T1), dtype=int)
array([[1, 0, 1],
       [0, 1, 0],
       [0, 0, 1]])
>>> np.array(np.round(T2), dtype=int)
array([[1, 0, 0],
       [0, 1, 1],
       [0, 0, 1]])
ddg.math.projective.translations_from_quad_4d(x00, x10, x11, x01, n, atol=None, rtol=None)[source]

Translations that map along a quadrilateral in a plane in RP3.

Returns two projective translations T1, T2 of RP3 such that

       T1
  x01 ---> x11
   ^        ^
T2 |        | T2
   |        |
  x00 ---> x10
       T1
Parameters:
x00, x10, x11, x01, nnumpy.ndarray of shape (4,)

The corners of the quadrilateral must lie in the plane orthogonal to n.

atol, rtolfloat (default=None)

This function uses the global tolerance defaults if atol or rtol are set to None. See ddg.nonexact for details.

Returns:
T1, T2numpy.ndarray of shape (4, 4)

Examples

>>> import numpy as np
>>> from ddg.math.projective import translations_from_quad_4d
>>> x00 = np.array([0.0, 0.0, 0.0, 1.0])
>>> x10 = np.array([1.0, 0.0, 0.0, 1.0])
>>> x11 = np.array([1.0, 1.0, 0.0, 1.0])
>>> x01 = np.array([0.0, 1.0, 0.0, 1.0])
>>> n = np.array([0.0, 0.0, 1.0, 0.0])
>>> T1, T2 = translations_from_quad_4d(x00, x10, x11, x01, n)
>>> np.array(np.round(T1), dtype=int)
array([[1, 0, 0, 1],
       [0, 1, 0, 0],
       [0, 0, 1, 0],
       [0, 0, 0, 1]])
>>> np.array(np.round(T2), dtype=int)
array([[1, 0, 0, 0],
       [0, 1, 0, 1],
       [0, 0, 1, 0],
       [0, 0, 0, 1]])