18 from scipy.misc
import factorial
19 except ImportError
as error:
21 from scipy.special
import factorial
27 Implementation of [Bezier curves](https://en.wikipedia.org/wiki/B%C3%A9zier_curve) 28 of orders 3, 4 and 5 based on [1]. 32 * `pnts` (*type:* `list`): List of 3D points as a vector (example: `[[0, 0, 0], [0, 1, 2]]`) 33 * `order` (*type:* `int`): Order of the Bezier curve, options are 3, 4 or 5 34 * `tangents` (*type:* `list`, *default:* `None`): Optional input of the tangent 35 vectors for each of the input points. In case only two points are provided, 36 the tangents have to be provided, too. Otherwise, the tangents will be calculated. 37 * `normals` (*type:* `list`, *default:* `None`): Optional input of the normal 38 vectors for each of the input points. In case only two points are provided, 39 the normals have to be provided, too. Otherwise, the normals will be calculated. 43 [1] Biagiotti, Luigi, and Claudio Melchiorri. Trajectory planning for 44 automatic machines and robots. Springer Science & Business Media, 2008. 46 def __init__(self, pnts, order, tangents=None, normals=None):
47 assert order
in [3, 4, 5],
'Invalid Bezier curve order' 48 assert type(pnts) == list
and len(pnts) >= 2,
'At least two points are needed to calculate the curve' 53 assert len(pnt) == 3,
'Point must have three elements' 54 self._pnts.append(np.array(pnt))
55 elif type(pnt) == np.ndarray:
56 assert pnt.size == 3,
'Point must have three elements' 57 self._pnts.append(pnt)
59 raise TypeError(
'Point in list is neither a list or an array')
61 if tangents
is not None:
62 assert type(tangents) == list
and len(tangents) == 2,
'Tangent vectors must be provided' 65 assert len(t) == 3,
'Tangent vector must have three elements' 66 elif type(t) == np.ndarray:
67 assert t.size == 3,
'Tangent vector must have three elements' 69 raise TypeError(
'Tangent vector is neither a list or an array')
75 assert len(self.
_pnts) == 2,
'Two points are needed for the curve to be computed' 80 a = 16 - np.linalg.norm(tangents[0] + tangents[1])**2
83 alpha = np.roots([a, b, c]).max()
89 assert len(self.
_pnts) == 3,
'Three points are needed for the curve to be computed' 95 radius = np.linalg.norm(self.
_pnts[0] - self.
_pnts[1])
97 tangents.append((self.
_pnts[1] - self.
_pnts[0]) / radius)
98 tangents.append((self.
_pnts[2] - self.
_pnts[1]) / radius)
101 a = 4 - (1.0 / 4) * np.linalg.norm(tangents[0] + tangents[1])**2
104 alpha = np.roots([a, b, c]).max()
110 if len(self.
_pnts) == 3:
115 radius = np.linalg.norm(self.
_pnts[0] - self.
_pnts[1])
117 tangents.append((self.
_pnts[1] - self.
_pnts[0]) / radius)
118 tangents.append((self.
_pnts[2] - self.
_pnts[1]) / radius)
121 a = 256 - 49 * np.linalg.norm(tangents[0] + tangents[1])**2
124 alpha = np.roots([a, b, c]).max()
131 elif len(self.
_pnts) == 2:
132 assert tangents
is not None and normals
is not None 133 assert isinstance(tangents, list)
and len(tangents) == 2,
'Tangent vectors must be provided' 134 assert isinstance(normals, list)
and len(normals) == 2,
'Normal vectors must be provided' 138 a = beta_hat**2 * np.linalg.norm(normals[1] - normals[0])**2
139 b = -28 * beta_hat * np.dot((tangents[0] + tangents[1]), normals[1] - normals[0])
140 c = 196 * np.linalg.norm(tangents[0] + tangents[1])**2 + 120 * beta_hat * np.dot(self.
_pnts[1] - self.
_pnts[0], normals[1] - normals[0]) - 1024
141 d = -1680 * np.dot(self.
_pnts[1] - self.
_pnts[0], tangents[0] + tangents[1])
142 e = 3600 * np.linalg.norm(self.
_pnts[1] - self.
_pnts[0])**2
144 alpha_k = np.real(np.roots([a, b, c, d, e])).max()
157 """Compute the distance between two 3D points. 161 * `p1` (*type:* list of `float` or `numpy.array`): Point 1 162 * `p2` (*type:* list of `float` or `numpy.array`): Point 2 166 Distance between points as a `float` 171 assert p1.size == 3
and p2.size == 3, \
172 'Both input points must be three elements' 173 return np.sqrt(np.sum((p2 - p1)**2))
177 """Generate cubic Bezier curve segments from a list of points. 181 * `pnts` (*type:* list of `float` or of `numpy.array`): List of points 185 List of `BezierCurve` segments 187 assert isinstance(pnts, list),
'List of points is invalid' 188 tangents = [np.zeros(3)
for _
in range(len(pnts))]
190 lengths = [BezierCurve.distance(pnts[i + 1], pnts[i])
for i
in range(len(pnts) - 1)]
191 lengths = [0] + lengths
194 u = [l / np.sum(lengths)
for l
in np.cumsum(lengths)]
195 delta_u =
lambda k: u[k] - u[k - 1]
196 delta_q =
lambda k: pnts[k] - pnts[k - 1]
197 lamb_k =
lambda k: delta_q(k) / delta_u(k)
198 alpha_k =
lambda k: delta_u(k) / (delta_u(k) + delta_u(k + 1))
200 for i
in range(1, len(u) - 1):
201 tangents[i] = (1 - alpha_k(i)) * lamb_k(i) + alpha_k(i) * lamb_k(i + 1)
203 tangents[0] = 2 * lamb_k(i) - tangents[1]
205 tangents[-1] = 2 * lamb_k(len(u) - 1) - tangents[-2]
208 for i
in range(len(tangents)):
209 tangents[i] = tangents[i] / np.linalg.norm(tangents[i])
213 for i
in range(len(tangents) - 1):
214 segments.append(
BezierCurve([pnts[i], pnts[i + 1]], 3, [tangents[i], tangents[i + 1]]))
216 return segments, tangents
220 """Generate quintic Bezier curve segments from a list of points. 224 * `pnts` (*type:* list of `float` or of `numpy.array`): List of points 228 List of `BezierCurve` segments 230 assert isinstance(pnts, list),
'List of points is invalid' 231 tangents = [np.zeros(3)
for _
in range(len(pnts))]
232 normals = [np.zeros(3)
for _
in range(len(pnts))]
234 lengths = [BezierCurve.distance(pnts[i + 1], pnts[i])
for i
in range(len(pnts) - 1)]
235 lengths = [0] + lengths
237 u = np.cumsum(lengths) / np.sum(lengths)
239 delta_u =
lambda k: u[k] - u[k - 1]
240 delta_q =
lambda k: pnts[k] - pnts[k - 1]
241 lamb_k =
lambda k: delta_q(k) / delta_u(k)
242 alpha_k =
lambda k: delta_u(k) / (delta_u(k) + delta_u(k + 1))
243 normal_k =
lambda k: ( ((pnts[k + 1] - pnts[k]) / (u[k + 1] - u[k])) - ((pnts[k] - pnts[k - 1]) / (u[k] - u[k - 1])) ) / (u[k + 1] - u[k - 1])
245 for i
in range(1, len(u) - 1):
246 tangents[i] = (1 - alpha_k(i)) * lamb_k(i) + alpha_k(i) * lamb_k(i + 1)
247 normals[i] = normal_k(i)
249 tangents[0] = 2 * lamb_k(i) - tangents[1]
250 normals[0] = normal_k(i)
252 tangents[-1] = 2 * lamb_k(len(u) - 1) - tangents[-2]
253 normals[-1] = normal_k(len(u) - 3)
256 for i
in range(len(tangents)):
257 tangents[i] /= np.linalg.norm(tangents[i])
258 normals[i] /= np.linalg.norm(normals[i])
262 for i
in range(len(tangents) - 1):
263 segments.append(
BezierCurve([pnts[i], pnts[i + 1]], 5,
264 [tangents[i], tangents[i + 1]],
265 [normals[i], normals[i + 1]]))
270 """Return the list of control points of the Bezier curve. 274 List of 3D points as `list` 279 """Interpolate the Bezier curve using the input parametric variable `u`. 283 * `u` (*type:* `float`): Curve parametric input in the interval `[0, 1]` 287 3D point from the Bezier curve as `numpy.array` 298 """Compute the derivative of the Bezier curve using the input parametric 303 * `u` (*type:* `float`): Curve parametric input in the interval `[0, 1]` 304 * `order` (*type:* `int`, *default:* `1`): Order of the derivative 308 `numpy.array`: 3D derivative value from the Bezier curve 320 """Get length of the Bezier curve segment. 324 `float`: Length of the curve 329 """Compute the Bernstein polynomial 332 \mathbf{B} = {n\choose i} (1 - u)^{(n - i)} u^{i} 337 * `n` (*type:* `int`): Degree of the Bezier curve 338 * `i` (*type:* `int`): Index of the control point 339 * `u` (*type:* `float`): Parametric input of the curve in interval [0, 1] 343 `float`: Bernstein polynomial result 349 """Compute binomial function $\binom{n}{i}$ 353 * `n` (*type:* `int`) 354 * `i` (*type:* `int`) 356 return factorial(n) / (factorial(i) * factorial(n - i))
def generate_cubic_curve(pnts)
def get_derivative(self, u, order=1)
def generate_quintic_curve(pnts)
def compute_polynomial(self, n, i, u)
def __init__(self, pnts, order, tangents=None, normals=None)