def process(self): if not any(socket.is_linked for socket in self.outputs): return vertices_s = self.inputs['Vertices'].sv_get() degree_s = self.inputs['Degree'].sv_get() vertices_s = ensure_nesting_level(vertices_s, 3) degree_s = ensure_nesting_level(degree_s, 2) curve_out = [] points_out = [] for vertices, degree in zip_long_repeat(vertices_s, degree_s): if isinstance(degree, (tuple, list)): degree = degree[0] n = len(vertices) npoints = degree + 1 vertices = np.array(vertices) #xdata = np.linspace(0, 1, num=n) xdata = Spline.create_knots(vertices, metric=self.metric) ydata = np.ravel(vertices) p0 = init_guess(vertices, npoints) popt, pcov = curve_fit(goal, xdata, ydata, p0) control_points = popt.reshape((npoints, 3)) curve = SvBezierCurve(control_points) curve_out.append(curve) points_out.append(control_points.tolist()) self.outputs['Curve'].sv_set(curve_out) self.outputs['ControlPoints'].sv_set(points_out)
def interpolate(cls, points, metric='DISTANCE'): n = len(points) tknots = Spline.create_knots(points, metric=metric) matrix = np.zeros((3 * n, 3 * n)) for equation_idx, t in enumerate(tknots): for unknown_idx in range(n): coeff = SvBezierCurve.coefficient(n - 1, unknown_idx, np.array([t]))[0] #print(f"C[{equation_idx}][{unknown_idx}] = {coeff}") row = 3 * equation_idx col = 3 * unknown_idx matrix[row, col] = matrix[row + 1, col + 1] = matrix[row + 2, col + 2] = coeff #print(matrix) B = np.zeros((3 * n, 1)) for point_idx, point in enumerate(points): row = 3 * point_idx B[row:row + 3] = point[:, np.newaxis] #print(B) x = np.linalg.solve(matrix, B) #print(x) controls = [] for i in range(n): row = i * 3 control = x[row:row + 3, 0].T controls.append(control) #print(control) return SvBezierCurve(controls)
def interpolate_nurbs_curve(cls, degree, points, metric='DISTANCE', tknots=None): n = len(points) if points.ndim != 2: raise Exception( f"Array of points was expected, but got {points.shape}: {points}") ndim = points.shape[1] # 3 or 4 if ndim not in {3, 4}: raise Exception( f"Only 3D and 4D points are supported, but ndim={ndim}") #points3d = points[:,:3] #print("pts:", points) if tknots is None: tknots = Spline.create_knots( points, metric=metric) # In 3D or in 4D, in general? knotvector = sv_knotvector.from_tknots(degree, tknots) functions = SvNurbsBasisFunctions(knotvector) coeffs_by_row = [ functions.function(idx, degree)(tknots) for idx in range(n) ] A = np.zeros((ndim * n, ndim * n)) for equation_idx, t in enumerate(tknots): for unknown_idx in range(n): coeff = coeffs_by_row[unknown_idx][equation_idx] row = ndim * equation_idx col = ndim * unknown_idx for d in range(ndim): A[row + d, col + d] = coeff B = np.zeros((ndim * n, 1)) for point_idx, point in enumerate(points): row = ndim * point_idx B[row:row + ndim] = point[:, np.newaxis] x = np.linalg.solve(A, B) control_points = [] for i in range(n): row = i * ndim control = x[row:row + ndim, 0].T control_points.append(control) control_points = np.array(control_points) if ndim == 3: weights = np.ones((n, )) else: # 4 control_points, weights = from_homogenous(control_points) if type(cls) == type: return cls.build(cls.get_nurbs_implementation(), degree, knotvector, control_points, weights) elif isinstance(cls, str): return SvNurbsMaths.build_curve(cls, degree, knotvector, control_points, weights) else: raise TypeError(f"Unsupported type of `cls` parameter: {type(cls)}")
def scipy_nurbs_approximate(points, weights=None, metric='DISTANCE', degree=3, filter_doubles=None, smoothing=None, is_cyclic=False): points = np.asarray(points) if weights is not None and len(points) != len(weights): raise Exception("Number of weights must be equal to number of points") if filter_doubles is not None: good = np.where( np.linalg.norm(np.diff(points, axis=0), axis=1) > filter_doubles) points = np.r_[points[good], points[-1][np.newaxis]] if weights is not None: weights = np.r_[weights[good], weights[-1]] if is_cyclic: if (points[0] != points[-1]).any(): points = np.vstack((points, points[0])) if weights is not None: weights = np.insert(weights, -1, weights[0]) points_orig = points points = points.T kwargs = dict() if weights is not None: kwargs['w'] = np.asarray(weights) if metric is not None: tknots = Spline.create_knots(points.T, metric) if len(tknots) != len(points.T): raise Exception( f"Number of T knots ({len(tknots)}) is not equal to number of points ({len(points.T)})" ) kwargs['u'] = tknots if degree is not None: kwargs['k'] = degree if smoothing is not None: kwargs['s'] = smoothing if is_cyclic: kwargs['per'] = 1 tck, u = interpolate.splprep(points, **kwargs) knotvector = tck[0] control_points = np.stack(tck[1]).T degree = tck[2] curve = SvNurbsCurve.build(SvNurbsCurve.NATIVE, degree, knotvector, control_points) if is_cyclic: curve = curve.cut_segment(0.0, 1.00) #curve.u_bounds = (0.0, 1.0) return curve
def approximate(cls, verts, degree, metric='DISTANCE'): def init_guess(verts, n): return np.array([pi] + list(verts[0]) + [0, 0, 0] * 2 * n) def goal(ts, *xs): n3 = len(xs) - 1 n = n3 // 3 omega = xs[0] points = np.array(xs[1:]).reshape((n, 3)) curve = SvFourierCurve(omega, points[0], points[1:]) pts = curve.evaluate_array(ts) return np.ravel(pts) xdata = Spline.create_knots(verts, metric=metric) ydata = np.ravel(verts) p0 = init_guess(verts, degree) popt, pcov = curve_fit(goal, xdata, ydata, p0) n3 = len(popt) - 1 ncoeffs = n3 // 3 omega = popt[0] points = popt[1:].reshape((ncoeffs, 3)) curve = SvFourierCurve(omega, points[0], points[1:]) return curve
def interpolate(cls, verts, omega, metric='DISTANCE', is_cyclic=False): ndim = 3 n_verts = len(verts) verts = np.asarray(verts) if is_cyclic: verts = np.append(verts, verts[0][np.newaxis], axis=0) n_verts += 1 n_equations = n_verts + 1 else: n_equations = n_verts tknots = Spline.create_knots(verts, metric=metric) A = np.zeros((ndim * n_equations, ndim * n_equations)) for equation_idx, t in enumerate(tknots): for unknown_idx in range(n_equations): i = (unknown_idx // 2) + 1 if unknown_idx % 2 == 0: coeff = cos(omega * i * t) else: coeff = sin(omega * i * t) row = ndim * equation_idx col = ndim * unknown_idx for d in range(ndim): A[row + d, col + d] = coeff if is_cyclic: equation_idx = len(tknots) for unknown_idx in range(n_equations): i = (unknown_idx // 2) + 1 if unknown_idx % 2 == 0: coeff = -omega * i * sin(omega * i) # - 0 else: coeff = omega * i * cos(omega * i) - omega * i row = ndim * equation_idx col = ndim * unknown_idx for d in range(ndim): A[row + d, col + d] = coeff #print(A) B = np.empty((ndim * n_equations, 1)) for point_idx, point in enumerate(verts): row = ndim * point_idx B[row:row + ndim] = point[:, np.newaxis] if is_cyclic: point_idx = len(verts) row = ndim * point_idx B[row:row + ndim] = np.array([[0, 0, 0]]).T #print(B) x = np.linalg.solve(A, B) coeffs = [] for i in range(n_equations): row = i * ndim coeff = x[row:row + ndim, 0].T coeffs.append(coeff) coeffs = np.array(coeffs) #print(coeffs) return SvFourierCurve(omega, np.array([0.0, 0.0, 0.0]), coeffs)
def simple_loft(curves, degree_v=None, knots_u='UNIFY', metric='DISTANCE', tknots=None, implementation=SvNurbsSurface.NATIVE): """ Loft between given NURBS curves (a.k.a skinning). inputs: * degree_v - degree of resulting surface along V parameter; by default - use the same degree as provided curves * knots_u - one of: - 'UNIFY' - unify knotvectors of given curves by inserting additional knots - 'AVERAGE' - average knotvectors of given curves; this will work only if all curves have the same number of control points * metric - metric for interpolation; most useful are 'DISTANCE' and 'CENTRIPETAL' * implementation - NURBS maths implementation output: tuple: * list of curves - input curves after unification * list of NURBS curves along V direction * generated NURBS surface. """ if knots_u not in {'UNIFY', 'AVERAGE'}: raise Exception(f"Unsupported knots_u option: {knots_u}") curve_class = type(curves[0]) curves = unify_curves_degree(curves) if knots_u == 'UNIFY': curves = unify_curves(curves) else: kvs = [len(curve.get_control_points()) for curve in curves] max_kv, min_kv = max(kvs), min(kvs) if max_kv != min_kv: raise Exception( f"U knotvector averaging is not applicable: Curves have different number of control points: {kvs}" ) degree_u = curves[0].get_degree() if degree_v is None: degree_v = degree_u if degree_v > len(curves): raise Exception( f"V degree ({degree_v}) must be not greater than number of curves ({len(curves)}) minus 1" ) src_points = [curve.get_homogenous_control_points() for curve in curves] # lens = [len(pts) for pts in src_points] # max_len, min_len = max(lens), min(lens) # if max_len != min_len: # raise Exception(f"Unify error: curves have different number of control points: {lens}") src_points = np.array(src_points) #print("Src:", src_points) src_points = np.transpose(src_points, axes=(1, 0, 2)) v_curves = [ interpolate_nurbs_curve(curve_class, degree_v, points, metric=metric, tknots=tknots) for points in src_points ] control_points = [ curve.get_homogenous_control_points() for curve in v_curves ] control_points = np.array(control_points) #weights = [curve.get_weights() for curve in v_curves] #weights = np.array([curve.get_weights() for curve in curves]).T n, m, ndim = control_points.shape control_points = control_points.reshape((n * m, ndim)) control_points, weights = from_homogenous(control_points) control_points = control_points.reshape((n, m, 3)) weights = weights.reshape((n, m)) mean_v_vector = control_points.mean(axis=0) tknots_v = Spline.create_knots(mean_v_vector, metric=metric) knotvector_v = sv_knotvector.from_tknots(degree_v, tknots_v) if knots_u == 'UNIFY': knotvector_u = curves[0].get_knotvector() else: knotvectors = np.array([curve.get_knotvector() for curve in curves]) knotvector_u = knotvectors.mean(axis=0) surface = SvNurbsSurface.build(implementation, degree_u, degree_v, knotvector_u, knotvector_v, control_points, weights) surface.u_bounds = curves[0].get_u_bounds() return curves, v_curves, surface