Exemple #1
0
        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)
Exemple #2
0
 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)
Exemple #3
0
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)}")
Exemple #4
0
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
Exemple #5
0
    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
Exemple #6
0
    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)
Exemple #7
0
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