import numpy as np from ..constants import res_path as res from ..constants import tol_path as tol def discretize_bezier(points, count=None, scale=1.0): """ Parameters ---------- points : (order, dimension) float Control points of the bezier curve For a 2D cubic bezier, order=3, dimension=2 count : int, or None Number of segments scale : float Scale of curve Returns ---------- discrete: (n, dimension) float Points forming a a polyline representation """ # make sure we have a numpy array points = np.asanyarray(points, dtype=np.float64) if count is None: # how much distance does a small percentage of the curve take # this is so we can figure out how finely we have to sample t norm = np.linalg.norm(np.diff(points, axis=0), axis=1).sum() count = np.ceil(norm / (res.seg_frac * scale)) count = int(np.clip(count, res.min_sections * len(points), res.max_sections * len(points))) count = int(count) # parameterize incrementing 0.0 - 1.0 t = np.linspace(0.0, 1.0, count) # decrementing 1.0-0.0 t_d = 1.0 - t n = len(points) - 1 # binomial coefficients, i, and each point iterable = zip(binomial(n), np.arange(len(points)), points) # run the actual interpolation stacked = [((t**i) * (t_d**(n - i))).reshape((-1, 1)) * p * c for c, i, p in iterable] result = np.sum(stacked, axis=0) # test to make sure end points are correct test = np.sum((result[[0, -1]] - points[[0, -1]])**2, axis=1) assert (test < tol.merge).all() assert len(result) >= 2 return result def discretize_bspline(control, knots, count=None, scale=1.0): """ Given a B-Splines control points and knot vector, return a sampled version of the curve. Parameters ---------- control : (o, d) float Control points of the b- spline knots : (j,) float B-spline knots count : int Number of line segments to discretize the spline If not specified will be calculated as something reasonable Returns ---------- discrete : (count, dimension) float Points on a polyline version of the B-spline """ # evaluate the b-spline using scipy/fitpack from scipy.interpolate import splev # (n, d) control points where d is the dimension of vertices control = np.asanyarray(control, dtype=np.float64) degree = len(knots) - len(control) - 1 if count is None: norm = np.linalg.norm(np.diff(control, axis=0), axis=1).sum() count = int(np.clip(norm / (res.seg_frac * scale), res.min_sections * len(control), res.max_sections * len(control))) ipl = np.linspace(knots[0], knots[-1], count) discrete = splev(ipl, [knots, control.T, degree]) discrete = np.column_stack(discrete) return discrete def binomial(n): """ Return all binomial coefficients for a given order. For n > 5, scipy.special.binom is used, below we hardcode to avoid the scipy.special dependency. Parameters -------------- n : int Order of binomial Returns --------------- binom : (n + 1,) int Binomial coefficients of a given order """ if n == 1: return [1, 1] elif n == 2: return [1, 2, 1] elif n == 3: return [1, 3, 3, 1] elif n == 4: return [1, 4, 6, 4, 1] elif n == 5: return [1, 5, 10, 10, 5, 1] else: from scipy.special import binom return binom(n, np.arange(n + 1))