def test_from_tknots(self): tknots = np.array([0, 5, 9, 14, 17.0]) / 17.0 degree = 3 knotvector = sv_knotvector.from_tknots(degree, tknots) u4 = 0.5490196078431373 expected = np.array([0, 0, 0, 0, u4, 1, 1, 1, 1]) self.assert_numpy_arrays_equal(knotvector, expected, precision=6)
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 interpolate_list(cls, degree, points, metric='DISTANCE'): n_curves, n_points, _ = points.shape tknots = [ Spline.create_knots(points[i], metric=metric) for i in range(n_curves) ] knotvectors = [ sv_knotvector.from_tknots(degree, tknots[i]) for i in range(n_curves) ] functions = [ SvNurbsBasisFunctions(knotvectors[i]) for i in range(n_curves) ] coeffs_by_row = [[ functions[curve_idx].function(idx, degree)(tknots[curve_idx]) for idx in range(n_points) ] for curve_idx in range(n_curves)] coeffs_by_row = np.array(coeffs_by_row) A = np.zeros((n_curves, 3 * n_points, 3 * n_points)) for curve_idx in range(n_curves): for equation_idx, t in enumerate(tknots[curve_idx]): for unknown_idx in range(n_points): coeff = coeffs_by_row[curve_idx][unknown_idx][equation_idx] row = 3 * equation_idx col = 3 * unknown_idx A[curve_idx, row, col] = A[curve_idx, row + 1, col + 1] = A[curve_idx, row + 2, col + 2] = coeff B = np.zeros((n_curves, 3 * n_points, 1)) for curve_idx in range(n_curves): for point_idx, point in enumerate(points[curve_idx]): row = 3 * point_idx B[curve_idx, row:row + 3] = point[:, np.newaxis] x = np.linalg.solve(A, B) curves = [] weights = np.ones((n_points, )) for curve_idx in range(n_curves): control_points = [] for i in range(n_points): row = i * 3 control = x[curve_idx][row:row + 3, 0].T control_points.append(control) control_points = np.array(control_points) curve = SvNurbsCurve.build(cls.get_nurbs_implementation(), degree, knotvectors[curve_idx], control_points, weights) curves.append(curve) return curves
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