|
3 | 3 | """ |
4 | 4 |
|
5 | 5 | from functools import lru_cache |
6 | | -import math |
| 6 | +from math import comb, factorial |
7 | 7 | import warnings |
8 | 8 |
|
9 | 9 | import numpy as np |
10 | 10 |
|
11 | 11 | from matplotlib import _api |
12 | 12 |
|
13 | 13 |
|
14 | | -# same algorithm as 3.8's math.comb |
15 | | -@np.vectorize |
16 | | -@lru_cache(maxsize=128) |
17 | | -def _comb(n, k): |
18 | | - if k > n: |
19 | | - return 0 |
20 | | - k = min(k, n - k) |
21 | | - i = np.arange(1, k + 1) |
22 | | - return np.prod((n + 1 - i)/i).astype(int) |
| 14 | +@lru_cache(maxsize=16) |
| 15 | +def _get_coeff_matrix(n): |
| 16 | + """ |
| 17 | + Compute the matrix for converting Bezier control points to polynomial |
| 18 | + coefficients for a curve of degree n. |
| 19 | +
|
| 20 | + The matrix M is such that M @ control_points gives polynomial coefficients. |
| 21 | + Entry M[j, i] = C(n, j) * (-1)^(i+j) * C(j, i) where C is the binomial |
| 22 | + coefficient. |
| 23 | + """ |
| 24 | + def _comb(n, k): |
| 25 | + return np.vectorize(comb)(n, k) |
| 26 | + |
| 27 | + j = np.arange(n + 1)[:, None] |
| 28 | + i = np.arange(n + 1)[None, :] # _comb is non-zero for i <= j |
| 29 | + prefactor = (-1) ** (i + j) * _comb(j, i) # j on axis 0, i on axis 1 |
| 30 | + return (_comb(n, j) * prefactor).astype(float) |
23 | 31 |
|
24 | 32 |
|
25 | 33 | class NonIntersectingPathException(ValueError): |
@@ -203,8 +211,8 @@ def __init__(self, control_points): |
203 | 211 | self._cpoints = np.asarray(control_points) |
204 | 212 | self._N, self._d = self._cpoints.shape |
205 | 213 | self._orders = np.arange(self._N) |
206 | | - coeff = [math.factorial(self._N - 1) |
207 | | - // (math.factorial(i) * math.factorial(self._N - 1 - i)) |
| 214 | + coeff = [factorial(self._N - 1) |
| 215 | + // (factorial(i) * factorial(self._N - 1 - i)) |
208 | 216 | for i in range(self._N)] |
209 | 217 | self._px = (self._cpoints.T * coeff).T |
210 | 218 |
|
@@ -279,11 +287,7 @@ def polynomial_coefficients(self): |
279 | 287 | if n > 10: |
280 | 288 | warnings.warn("Polynomial coefficients formula unstable for high " |
281 | 289 | "order Bezier curves!", RuntimeWarning) |
282 | | - P = self.control_points |
283 | | - j = np.arange(n+1)[:, None] |
284 | | - i = np.arange(n+1)[None, :] # _comb is non-zero for i <= j |
285 | | - prefactor = (-1)**(i + j) * _comb(j, i) # j on axis 0, i on axis 1 |
286 | | - return _comb(n, j) * prefactor @ P # j on axis 0, self.dimension on 1 |
| 290 | + return _get_coeff_matrix(n) @ self.control_points |
287 | 291 |
|
288 | 292 | def axis_aligned_extrema(self): |
289 | 293 | """ |
|
0 commit comments