1import numpy as np 2 3from ..constants import res_path as res 4from ..constants import tol_path as tol 5 6 7def discretize_bezier(points, 8 count=None, 9 scale=1.0): 10 """ 11 Parameters 12 ---------- 13 points : (order, dimension) float 14 Control points of the bezier curve 15 For a 2D cubic bezier, order=3, dimension=2 16 count : int, or None 17 Number of segments 18 scale : float 19 Scale of curve 20 Returns 21 ---------- 22 discrete: (n, dimension) float 23 Points forming a a polyline representation 24 """ 25 # make sure we have a numpy array 26 points = np.asanyarray(points, dtype=np.float64) 27 28 if count is None: 29 # how much distance does a small percentage of the curve take 30 # this is so we can figure out how finely we have to sample t 31 norm = np.linalg.norm(np.diff(points, axis=0), axis=1).sum() 32 count = np.ceil(norm / (res.seg_frac * scale)) 33 count = int(np.clip(count, 34 res.min_sections * len(points), 35 res.max_sections * len(points))) 36 count = int(count) 37 38 # parameterize incrementing 0.0 - 1.0 39 t = np.linspace(0.0, 1.0, count) 40 # decrementing 1.0-0.0 41 t_d = 1.0 - t 42 n = len(points) - 1 43 # binomial coefficients, i, and each point 44 iterable = zip(binomial(n), np.arange(len(points)), points) 45 # run the actual interpolation 46 stacked = [((t**i) * (t_d**(n - i))).reshape((-1, 1)) 47 * p * c for c, i, p in iterable] 48 result = np.sum(stacked, axis=0) 49 50 # test to make sure end points are correct 51 test = np.sum((result[[0, -1]] - points[[0, -1]])**2, axis=1) 52 assert (test < tol.merge).all() 53 assert len(result) >= 2 54 55 return result 56 57 58def discretize_bspline(control, 59 knots, 60 count=None, 61 scale=1.0): 62 """ 63 Given a B-Splines control points and knot vector, return 64 a sampled version of the curve. 65 66 Parameters 67 ---------- 68 control : (o, d) float 69 Control points of the b- spline 70 knots : (j,) float 71 B-spline knots 72 count : int 73 Number of line segments to discretize the spline 74 If not specified will be calculated as something reasonable 75 76 Returns 77 ---------- 78 discrete : (count, dimension) float 79 Points on a polyline version of the B-spline 80 """ 81 82 # evaluate the b-spline using scipy/fitpack 83 from scipy.interpolate import splev 84 # (n, d) control points where d is the dimension of vertices 85 control = np.asanyarray(control, dtype=np.float64) 86 degree = len(knots) - len(control) - 1 87 if count is None: 88 norm = np.linalg.norm(np.diff(control, axis=0), axis=1).sum() 89 count = int(np.clip(norm / (res.seg_frac * scale), 90 res.min_sections * len(control), 91 res.max_sections * len(control))) 92 93 ipl = np.linspace(knots[0], knots[-1], count) 94 discrete = splev(ipl, [knots, control.T, degree]) 95 discrete = np.column_stack(discrete) 96 97 return discrete 98 99 100def binomial(n): 101 """ 102 Return all binomial coefficients for a given order. 103 104 For n > 5, scipy.special.binom is used, below we hardcode 105 to avoid the scipy.special dependency. 106 107 Parameters 108 -------------- 109 n : int 110 Order of binomial 111 112 Returns 113 --------------- 114 binom : (n + 1,) int 115 Binomial coefficients of a given order 116 """ 117 if n == 1: 118 return [1, 1] 119 elif n == 2: 120 return [1, 2, 1] 121 elif n == 3: 122 return [1, 3, 3, 1] 123 elif n == 4: 124 return [1, 4, 6, 4, 1] 125 elif n == 5: 126 return [1, 5, 10, 10, 5, 1] 127 else: 128 from scipy.special import binom 129 return binom(n, np.arange(n + 1)) 130