def smooth(points=[ (20, 0), (40, 0), (80, 40), (80, 10), (100, 10), ], radius=4, corner_fun=euler, **kwargs): """ Create a smooth path from a series of waypoints. Corners will be rounded using `corner_fun` and any additional key word arguments (for example, `use_eff = True` when `corner_fun = pp.euler`) Parameters ---------- points : array-like[N][2] List of waypoints for the path to follow radius : int or float Radius of curvature, this argument will be passed to `corner_fun` corner_fun : function The function that controls how the corners are rounded. Typically either `arc()` or `euler()` **kwargs : dict Extra keyword arguments that will be passed to `corner_fun` Returns ------- Path A Path object with the specified smoothed path. """ points = np.asfarray(points) normals = np.diff(points, axis=0) normals = (normals.T / np.linalg.norm(normals, axis=1)).T # normals_rev = normals*np.array([1,-1]) dx = np.diff(points[:, 0]) dy = np.diff(points[:, 1]) ds = np.sqrt(dx**2 + dy**2) theta = np.degrees(np.arctan2(dy, dx)) dtheta = np.diff(theta) # FIXME add caching # Create arcs paths = [] radii = [] for dt in dtheta: P = corner_fun(radius=radius, angle=dt, **kwargs) chord = np.linalg.norm(P.points[-1, :] - P.points[0, :]) r = (chord / 2) / np.sin(np.radians(dt / 2)) r = np.abs(r) radii.append(r) paths.append(P) d = np.abs(np.array(radii) / np.tan(np.radians(180 - dtheta) / 2)) encroachment = np.concatenate([[0], d]) + np.concatenate([d, [0]]) if np.any(encroachment > ds): raise ValueError( '[PHIDL] smooth(): Not enough distance between points to to fit curves. Try reducing the radius or spacing the points out farther' ) p1 = points[1:-1, :] - normals[:-1, :] * d[:, np.newaxis] # Move arcs into position new_points = [] new_points.append([points[0, :]]) for n, dt in enumerate(dtheta): P = paths[n] P.rotate(theta[n] - 0) P.move(p1[n]) new_points.append(P.points) new_points.append([points[-1, :]]) new_points = np.concatenate(new_points) P = Path(new_points) P.move(points[0, :]) return P
def smooth(points=[ (20, 0), (40, 0), (80, 40), (80, 10), (100, 10), ], radius=4, corner_fun=euler, **kwargs): """ Create a smooth path from a series of waypoints. Corners will be rounded using `corner_fun` and any additional key word arguments (for example, `use_eff = True` when `corner_fun = pp.euler`) Parameters ---------- points : array-like[N][2] List of waypoints for the path to follow radius : int or float Radius of curvature, this argument will be passed to `corner_fun` corner_fun : function The function that controls how the corners are rounded. Typically either `arc()` or `euler()` **kwargs : dict Extra keyword arguments that will be passed to `corner_fun` Returns ------- Path A Path object with the specified smoothed path. """ points, normals, ds, theta, dtheta = _compute_segments(points) colinear_elements = np.concatenate([[False], np.abs(dtheta) < 1e-6, [False]]) if np.any(colinear_elements): new_points = points[~colinear_elements, :] points, normals, ds, theta, dtheta = _compute_segments(new_points) if np.any(np.abs(np.abs(dtheta) - 180) < 1e-6): raise ValueError( '[PHIDL] smooth() received points which double-back on themselves' + '--turns cannot be computed when going forwards then exactly backwards.' ) # FIXME add caching # Create arcs paths = [] radii = [] for dt in dtheta: P = corner_fun(radius=radius, angle=dt, **kwargs) chord = np.linalg.norm(P.points[-1, :] - P.points[0, :]) r = (chord / 2) / np.sin(np.radians(dt / 2)) r = np.abs(r) radii.append(r) paths.append(P) d = np.abs(np.array(radii) / np.tan(np.radians(180 - dtheta) / 2)) encroachment = np.concatenate([[0], d]) + np.concatenate([d, [0]]) if np.any(encroachment > ds): raise ValueError( '[PHIDL] smooth(): Not enough distance between points to to fit curves. Try reducing the radius or spacing the points out farther' ) p1 = points[1:-1, :] - normals[:-1, :] * d[:, np.newaxis] # Move arcs into position new_points = [] new_points.append([points[0, :]]) for n, dt in enumerate(dtheta): P = paths[n] P.rotate(theta[n] - 0) P.move(p1[n]) new_points.append(P.points) new_points.append([points[-1, :]]) new_points = np.concatenate(new_points) P = Path() P.rotate(theta[0]) P.append(new_points) P.move(points[0, :]) return P