bezier.hazmat.helpers module

Pure Python generic geometry and floating point helpers.

bezier.hazmat.helpers.vector_close(vec1, vec2, eps=9.094947017729282e-13)

Checks that two vectors are equal to some threshold.

Does so by computing \(s_1 = \|v_1\|_2\) and \(s_2 = \|v_2\|_2\) and then checking if

\[\|v_1 - v_2\|_2 \leq \varepsilon \min(s_1, s_2)\]

where \(\varepsilon = 2^{-40} \approx 10^{-12}\) is a fixed threshold. In the rare case that one of vec1 or vec2 is the zero vector (i.e. when \(\min(s_1, s_2) = 0\)) instead checks that the other vector is close enough to zero:

\[\|v_1\|_2 = 0 \Longrightarrow \|v_2\|_2 \leq \varepsilon\]

Note

This function assumes that both vectors have finite values, i.e. that no NaN or infinite numbers occur. NumPy provides numpy.allclose() for coverage of all cases.

Note

There is also a Fortran implementation of this function, which will be used if it can be built.

Parameters:
  • vec1 (numpy.ndarray) – First vector (1D) for comparison.

  • vec2 (numpy.ndarray) – Second vector (1D) for comparison.

  • eps (float) – Error threshold. Defaults to \(2^{-40}\).

Returns:

Flag indicating if they are close to precision.

Return type:

bool

bezier.hazmat.helpers.in_interval(value, start, end)

Checks if a value is an interval (inclusive).

Note

The current implementation does the most basic check, however, in the future, a more generic check may be desired that allows wiggle room around the endpoints to account for round-off.

Note

There is also a Fortran implementation of this function, which will be used if it can be built.

Parameters:
  • value (float) – The value to check.

  • start (float) – The (inclusive) start of the interval.

  • end (float) – The (inclusive) end of the interval.

Returns:

Indicating if the value is in the interval.

Return type:

bool

bezier.hazmat.helpers.bbox(nodes)

Get the bounding box for set of points.

Note

There is also a Fortran implementation of this function, which will be used if it can be built.

Parameters:

nodes (numpy.ndarray) – A set of points.

Returns:

The left, right, bottom and top bounds for the box.

Return type:

Tuplefloat, float, float, float

bezier.hazmat.helpers.contains_nd(nodes, point)

Predicate indicating if a point is within a bounding box.

Note

There is also a Fortran implementation of this function, which will be used if it can be built.

Parameters:
  • nodes (numpy.ndarray) – A set of points.

  • point (numpy.ndarray) – A 1D NumPy array representing a point in the same dimension as nodes.

Returns:

Indicating containment.

Return type:

bool

bezier.hazmat.helpers.cross_product(vec0, vec1)

Compute the cross product of vectors in \(\mathbf{R}^2\).

Utilizes the fact that

\[\begin{split}\left[\begin{array}{c} A \\ B \\ 0 \end{array}\right] \times \left[\begin{array}{c} C \\ D \\ 0 \end{array}\right] = \left[\begin{array}{c} 0 \\ 0 \\ AD - BC \end{array}\right]\end{split}\]

and just returns the \(z\) component.

Note

There is also a Fortran implementation of this function, which will be used if it can be built.

Parameters:
  • vec0 (numpy.ndarray) – A vector as a 1D NumPy array with two values.

  • vec1 (numpy.ndarray) – A vector as a 1D NumPy array with two values.

Returns:

The cross product (or rather, its \(z\) component).

Return type:

float

bezier.hazmat.helpers.matrix_product(mat1, mat2)

Compute the product of two Fortran contiguous matrices.

This is to avoid the overhead of NumPy converting to C-contiguous before computing a matrix product.

Does so via A B = (B^T A^T)^T since B^T and A^T will be C-contiguous without a copy, then the product P = B^T A^T will be C-contiguous and we can return the view P^T without a copy.

Parameters:
Returns:

The product of the two matrices.

Return type:

numpy.ndarray

bezier.hazmat.helpers.wiggle_interval(value, wiggle=5.684341886080802e-14)

Check if value is in \(\left[0, 1\right]\).

Allows a little bit of wiggle room outside the interval. A value within wiggle of 0.0 will be converted to 0.0 and similar for 1.0.

Note

There is also a Fortran implementation of this function, which will be used if it can be built.

Parameters:
  • value (float) – Value to check in interval.

  • wiggle (Optionalfloat) – The amount of wiggle room around the the endpoints 0.0 and 1.0. Defaults to \(2^{-44}\).

Returns:

Pair of

  • The value if it’s in the interval, or 0.0 or 1.0 if the value lies slightly outside. If the value is too far outside the unit interval, will be NaN.

  • Boolean indicating if the value is inside the unit interval.

Return type:

Tuplefloat, bool

bezier.hazmat.helpers.cross_product_compare(start, candidate1, candidate2)

Compare two relative changes by their cross-product.

This is meant to be a way to determine which vector is more “inside” relative to start.

Note

This is a helper for simple_convex_hull().

Parameters:
  • start (numpy.ndarray) – The start vector (as 1D NumPy array with 2 elements).

  • candidate1 (numpy.ndarray) – The first candidate vector (as 1D NumPy array with 2 elements).

  • candidate2 (numpy.ndarray) – The second candidate vector (as 1D NumPy array with 2 elements).

Returns:

The cross product of the two differences.

Return type:

float

bezier.hazmat.helpers.in_sorted(values, value)

Checks if a value is in a sorted list.

Uses the bisect builtin to find the insertion point for value.

Parameters:
  • values (Listint) – Integers sorted in ascending order.

  • value (int) – Value to check if contained in values.

Returns:

Indicating if the value is contained.

Return type:

bool

bezier.hazmat.helpers.simple_convex_hull(points)

Compute the convex hull for a set of points.

This uses Andrew’s monotone chain convex hull algorithm and this code used a wikibooks implementation as motivation. The code there is licensed CC BY-SA 3.0.

Note

There is also a Fortran implementation of this function, which will be used if it can be built. Note that scipy.spatial.ConvexHull can do this as well (via Qhull), but that would require a hard dependency on scipy and that helper computes much more than we need.

Note

This computes the convex hull in a “naive” way. It’s expected that internal callers of this function will have a small number of points so n log n vs. n^2 vs. n aren’t that relevant.

Parameters:

points (numpy.ndarray) – A 2 x N array (float64) of points.

Returns:

The 2 x N array (float64) of ordered points in the polygonal convex hull.

Return type:

numpy.ndarray

bezier.hazmat.helpers.is_separating(direction, polygon1, polygon2)

Checks if a given direction is a separating line for two polygons.

Note

This is a helper for polygon_collide().

Parameters:
  • direction (numpy.ndarray) – A 1D 2-array (float64) of a potential separating line for the two polygons.

  • polygon1 (numpy.ndarray) – A 2 x N array (float64) of ordered points in a polygon.

  • polygon2 (numpy.ndarray) – A 2 x N array (float64) of ordered points in a polygon.

Returns:

Flag indicating if direction is a separating line.

Return type:

bool

bezier.hazmat.helpers.polygon_collide(polygon1, polygon2)

Determines if two convex polygons collide.

This code uses the Separating axis theorem (SAT) to quickly determine if the polygons intersect. See also.

Note

There is also a Fortran implementation of this function, which will be used if it can be built.

Parameters:
  • polygon1 (numpy.ndarray) – A 2 x N array (float64) of ordered points in a polygon.

  • polygon2 (numpy.ndarray) – A 2 x N array (float64) of ordered points in a polygon.

Returns:

Flag indicating if the two polygons collide.

Return type:

bool

bezier.hazmat.helpers.solve2x2(lhs, rhs)

Solve a square 2 x 2 system via LU factorization.

This is meant to be a stand-in for LAPACK’s dgesv, which just wraps two calls to dgetrf and dgetrs. We wrap for two reasons:

  • We seek to avoid exceptions as part of the control flow (which is what numpy.linalg.solve() does).

  • We seek to avoid excessive type- and size-checking, since this special case is already known.

Parameters:
Returns:

A triple of

  • A flag indicating if lhs is a singular matrix.

  • The first component of the solution.

  • The second component of the solution.

Return type:

Tuplebool, float, float

exception bezier.hazmat.helpers.UnsupportedDegree(degree, supported=())

Bases: NotImplementedError

Custom exception to indicate the given degree is unsupported.

This is intentionally a subclass of a NotImplementedError since it’s intended to indicate a lack of an implementation. For example, Curve.reduce_() uses hard-coded matrices for a small subset of possible degrees, so the implementation is degree-specific:

>>> import bezier
>>> import numpy as np
>>> degree = 5
>>> nodes = np.empty((2, degree + 1), order="F")
>>> curve = bezier.Curve(nodes, degree=degree)
>>> curve.reduce_()
Traceback (most recent call last):
  ...
bezier.hazmat.helpers.UnsupportedDegree: The only degrees supported at
                                         this time are 1, 2, 3 and
                                         4 (degree=5)
Parameters:
  • degree (int) – The degree that is not possible to support.

  • supported (Tupleint, …) – The degrees that are actually supported by the failing method.

degree

The degree that the caller attempted to use.

Type:

int

add_note()

Exception.add_note(note) – add a note to the exception

args
with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

supported

The degrees supported by the failing method.

Type:

Tupleint, …