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:
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:
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:
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:
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:
exception bezier.hazmat.helpers.UnsupportedDegree(degree, supported=())

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

The degree that the caller attempted to use.

Type:

int