Esempio n. 1
0
def global_curve_approx_errbnd(r, Q, p, E, ps=1):
    ''' Approximate (fit) a set of points {Qk}, k=0,...,r, to within a
    specified tolerance E.  The algorithm is a Type 2 approximation
    method, i.e. it starts with many control points, fit, check
    deviation, and discard control points if possible.

    While the least-squares based algorithm global_curve_approx_fixedn
    is appropriate for the fitting step, it can sometimes fail, either
    because degree elevation requires more control points than there are
    data points, or because the many multiple knots make the system of
    equations singular.  A simple fix is to restart the process with a
    higher degree curve.  The deviation checking is measured by the max
    norm deviation and, finally, control points are discarded by knot
    removal.

    Parameters
    ----------
    r + 1 = the number of data points to fit
    Q = the point set in object matrix form
    p = the degree of the fit
    E = the max norm deviation of the fit
    ps = the starting degree for the interpolating curve

    Returns
    -------
    Uh, Pwh = the knot vector and the object matrix of the fitted curve

    Source
    ------
    The NURBS Book (2nd Ed.), Pg. 431.

    '''

    if p < ps:
        raise ImproperInput(p, ps)
    Q = nurbs.obj_mat_to_3D(Q)
    uk = knot.chord_length_param(r, Q)
    U, Pw = global_curve_interp(r, Q, ps, uk)
    n = Pw.shape[0] - 1
    ek = np.zeros(r + 1)
    try:
        for deg in xrange(ps, p + 1):
            nh, Uh, dummy = remove_knots_bounds_curve(n, deg, U, Pw, uk, ek, E)
            if deg == p:
                break
            n, U = curve.mult_degree_elevate(nh, deg, Uh, 1)
            U, Pw = global_curve_approx_fixedn(r, Q, deg + 1, n, uk, U)
            update_errors(n, deg + 1, U, Pw, r, Q, uk, ek)
        if n == nh:
            return U, Pw
        U, n = Uh, nh
        U, Pw = global_curve_approx_fixedn(r, Q, p, n, uk, U)
        update_errors(n, p, U, Pw, r, Q, uk, ek)
        dummy, Uh, Pwh = remove_knots_bounds_curve(n, p, U, Pw, uk, ek, E)
        return Uh, Pwh
    except (RuntimeError, NotEnoughDataPoints):
        return global_curve_approx_errbnd(r, Q, p, E, ps + 1)
Esempio n. 2
0
def global_curve_interp_ders(n, Q, p, k, Ds, l, De, uk=None):
    ''' Idem to global_curve_interp, but, in addition to the data
    points, end derivatives are also given

               Ds^1,...,Ds^k    De^1,...,De^l    k,l < p

    where Ds^i denotes the ith derivative at the start point and De^j
    the jth derivative at the end.

    Parameters
    ----------
    n + 1 = the number of data points to interpolate
    Q = the point set in object matrix form
    p = the degree of the interpolant
    k, l = the number of start and end point derivatives
    Ds, De = the start and end point derivatives
    uk = the parameter values associated to each point (if available)

    Returns
    -------
    U, Pw = the knot vector and the object matrix of the interpolant

    Source
    ------
    Piegl & Tiller, Curve Interpolation with Arbitrary End Derivatives,
    Engineering with Computers, 2000.

    '''

    if p < 1 or not k < p or not l < p or n + k + l < p:
        raise ImproperInput(p, n, k, l)
    Q = nurbs.obj_mat_to_3D(Q)
    Ds, De = [np.asfarray(V) for V in Ds, De]
    nk = n + k
    nkl = nk + l
    P = np.zeros((nkl + 1, 3))
    if uk is None:
        uk = knot.chord_length_param(n, Q)
    U = knot.end_derivs_knot_vec(n, p, k, l, uk)
    A = scipy.sparse.lil_matrix((nkl + 1, nkl + 1))
    A[:k+1,:p+1] = \
            basis.ders_basis_funs(p, uk[0], p, k, U)
    for i in xrange(1, n):
        span = basis.find_span(nkl, p, U, uk[i])
        A[i + k, span - p:span + 1] = basis.basis_funs(span, uk[i], p, U)
    A[nkl+1:nk-1:-1,nkl-p:nkl+1] = \
            basis.ders_basis_funs(nkl, uk[-1], p, l, U)
    lu = scipy.sparse.linalg.splu(A.tocsc())
    for i in xrange(3):
        rhs = Q[:, i]
        ind = k * [1] + l * [n]
        der = np.hstack((Ds[:, i] if k else [], De[::-1, i] if l else []))
        rhs = np.insert(rhs, ind, der)
        P[:, i] = lu.solve(rhs)
    Pw = nurbs.obj_mat_to_4D(P)
    return U, Pw
Esempio n. 3
0
def global_curve_interp(n, Q, p, uk=None, U=None):
    ''' Suppose a pth-degree nonrational B-spline curve interpolant to
    the set of points {Qk}, k = 0,...,n, is seeked.  If a parameter
    value ubk is assigned to each Qk, and an appropriate knot vector U =
    {u0,...,um} is selected, it is possible to set up a (n + 1) x (n +
    1) system of linear equations

               Qk = C(ubk) = sum_(i=0)^n (Nip(ubk) * Pi)

    The control points, Pi, are the (n + 1) unknowns.  Let r be the
    number of coordinates in the Qk (r < 4).  Note that this method is
    independent of r; there is only one coefficient matrix, with r
    right-hand sides and, correspondingly, r solution sets for the r
    coordinates.

    Parameters
    ----------
    n + 1 = the number of data points to interpolate
    Q = the point set in object matrix form
    p = the degree of the interpolant
    uk = the parameter values associated to each point (if available)
    U = the knot vector to use for the interpolation (if available)

    Returns
    -------
    U, Pw = the knot vector and the object matrix of the interpolant

    Source
    ------
    The NURBS Book (2nd Ed.), Pg. 369.

    '''

    if p < 1 or n < p:
        raise ImproperInput(p, n)
    Q = nurbs.obj_mat_to_3D(Q)
    P = np.zeros((n + 1, 3))
    if uk is None:
        uk = knot.chord_length_param(n, Q)
    if U is None:
        U = knot.averaging_knot_vec(n, p, uk)
    A = scipy.sparse.lil_matrix((n + 1, n + 1))
    for i in xrange(n + 1):
        span = basis.find_span(n, p, U, uk[i])
        A[i, span - p:span + 1] = basis.basis_funs(span, uk[i], p, U)
    lu = scipy.sparse.linalg.splu(A.tocsc())
    for i in xrange(3):
        rhs = Q[:, i]
        P[:, i] = lu.solve(rhs)
    Pw = nurbs.obj_mat_to_4D(P)
    return U, Pw
Esempio n. 4
0
def global_curve_approx_fixedn_fair(r, Q, p, n, B=60):
    ''' Idem to global_curve_approx_fixedn, but, in addition to fitting
    the data points, the curve energy functional

                    int_u ( B * || C''(u) ||^2 ) du

    is also minimized.  B is some user-defined coefficient; if set to 0
    then the problem reverts back to the normal least-squares fit.  Note
    that given the polynomial nature of B-splines the above integral is
    carried out exactly using Gauss-Legendre quadrature.

    Parameters
    ----------
    r + 1 = the number of data points to fit
    Q = the point set in object matrix form
    p = the degree of the fit
    n + 1 = the number of control points to use in the fit
    B = the bending coefficient

    Returns
    -------
    U, Pw = the knot vector and the object matrix of the fitted curve

    Source
    ------
    Park et al., A method for approximate NURBS curve compatibility
    based on multiple curve refitting, Computer-aided design, 2000.

    '''

    if p < 1 or n < p:
        raise ImproperInput(p, n)
    if not r > n:
        raise NotEnoughDataPoints(r, n)
    Q = nurbs.obj_mat_to_3D(Q)
    uk = knot.chord_length_param(r, Q)
    U = knot.approximating_knot_vec(n, p, r, uk)
    Uu = np.unique(U)
    ni = len(Uu) - 1
    # get the Gauss-Legendre roots and weights
    gn = (p + 2) // 2
    gx, gw = scipy.special.orthogonal.p_roots(gn)
    # precompute the N^(2) vector for each knot span
    u = []
    for i in xrange(ni):
        a, b = Uu[i], Uu[i + 1]
        uu = (b - a) * (gx + 1) / 2.0 + a
        u.append(uu)
    u = np.hstack(u)
    N2 = np.zeros((n + 1, gn * ni))
    for i in xrange(n + 1):
        N2[i] = basis.ders_one_basis_fun_v(p, U, i, u, 2, gn * ni)[2]
    # build the (normalized) stiffness matrix K
    K = np.zeros((n + 1, n + 1))
    for i in xrange(n + 1):
        for j in xrange(i, min(i + p + 1, n + 1)):
            kij = 0.0
            for k in xrange(ni):
                a, b = Uu[k], Uu[k + 1]
                kgn = k * gn
                NN = N2[i, kgn:kgn + gn] * N2[j, kgn:kgn + gn]
                kij += (b - a) / 2.0 * np.dot(gw, NN)
            K[i, j] = kij
    K += K.T
    K[np.diag_indices_from(K)] /= 2
    K /= np.max(K)
    # build the system matrix NTN
    N = np.zeros((r + 1, n + 1))
    spans = basis.find_span_v(n, p, U, uk, r + 1)
    bfuns = basis.basis_funs_v(spans, uk, p, U, r + 1)
    spans0, spans1 = spans - p, spans + 1
    for s in xrange(r + 1):
        N[s, spans0[s]:spans1[s]] = bfuns[:, s]
    NTN = np.dot(N.T, N)
    # build the matrix of constraints C
    C = np.zeros((2, n + 1))
    for i in (0, -1):
        span = basis.find_span(n, p, U, uk[i])
        bfun = basis.basis_funs(span, uk[i], p, U)
        C[i, span - p:span + 1] = bfun
    # build the rhs vector D
    D = np.zeros((2, 3))
    D[0], D[1] = Q[0], Q[r]
    # build and solve Eq. (12)
    A = scipy.sparse.lil_matrix((n + 3, n + 3))
    A[:n + 1, :n + 1] = B * K + NTN
    A[:n + 1, n + 1:] = C.T
    A[n + 1:, :n + 1] = C
    lu = scipy.sparse.linalg.splu(A.tocsc())
    P = np.zeros((n + 1, 3))
    rhs = np.zeros(n + 3)
    for i in xrange(3):
        rhs[:n + 1] = np.dot(N.T, Q[:, i])
        rhs[n + 1:] = D[:, i]
        sol = lu.solve(rhs)
        P[:, i] = sol[:n + 1]
    Pw = nurbs.obj_mat_to_4D(P)
    return U, Pw
Esempio n. 5
0
def global_curve_approx_fixedn_ders(r, Q, p, n, k, Ds, l, De, uk=None):
    ''' Idem to global_curve_approx_fixedn, but, in addition to the data
    points, end derivatives are also given

               Ds^1,...,Ds^k    De^1,...,De^l    k,l < p + 1

    where Ds^i denotes the ith derivative at the start point and De^j
    the jth derivative at the end.

    Parameters
    ----------
    r + 1 = the number of data points to fit
    Q = the point set in object matrix form
    p = the degree of the fit
    n + 1 = the number of control points to use in the fit
    k, l = the number of start and end point derivatives
    Ds, De = the start and end point derivatives
    uk = the parameter values associated to each data point (if available)

    Returns
    -------
    U, Pw = the knot vector and the object matrix of the fitted curve

    Source
    ------
    Piegl & Tiller, Least-Squares B-spline Curve Approximation with
    Arbitrary End Derivatives, Engineering with Computers, 2000.

    '''

    if p < 1 or n < p or p < k or p < l or n < k + l + 1:
        raise ImproperInput(p, n, k, l)
    if not r > n:
        raise NotEnoughDataPoints(r, n)
    Q = nurbs.obj_mat_to_3D(Q)
    Ds, De = [np.asfarray(V) for V in Ds, De]
    if uk is None:
        uk = knot.chord_length_param(r, Q)
    U = knot.approximating_knot_vec_end(n, p, r, k, l, uk)
    P = np.zeros((n + 1, 3))
    P[0], P[n] = Q[0], Q[r]
    if k > 0:
        ders = basis.ders_basis_funs(p, uk[0], p, k, U)
        for i in xrange(1, k + 1):
            Pi = Ds[i - 1]
            for h in xrange(i):
                Pi -= ders[i, h] * P[h]
            P[i] = Pi / ders[i, i]
    if l > 0:
        ders = basis.ders_basis_funs(n, uk[-1], p, l, U)
        for j in xrange(1, l + 1):
            Pj = De[j - 1]
            for h in xrange(j):
                Pj -= ders[j, -h - 1] * P[-h - 1]
            P[-j - 1] = Pj / ders[j, -j - 1]
    if n > k + l + 1:
        Ps = [P[i][:, np.newaxis] for i in xrange(k + 1)]
        Pe = [P[-j - 1][:, np.newaxis] for j in xrange(l + 1)]
        lu, NT, Os, Oe = build_decompose_NTN(r, p, n, uk, U, k, l)
        R = Q.copy()
        for i in xrange(k + 1):
            R -= (Os[i] * Ps[i]).T
        for j in xrange(l + 1):
            R -= (Oe[j] * Pe[j]).T
        for i in xrange(3):
            rhs = np.dot(NT, R[1:-1, i])
            P[k + 1:-l - 1, i] = lu.solve(rhs)
    Pw = nurbs.obj_mat_to_4D(P)
    return U, Pw
Esempio n. 6
0
def global_curve_approx_fixedn(r, Q, p, n, uk=None, U=None, NTN=None):
    ''' Assume the degree, number of control points (minus one) and data
    points are given.  A pth-degree nonrational curve

            C(u) = sum_(i=0)^n (Nip(u) * Pi)    u in [0, 1]

    is seeked, satisfying that:

    - Q0 = C(0) and Qm = C(1);
    - the remaining Qk are approximated in the least-squares sense, i.e.

                    sum_(k=1)^(m-1) |Qk - C(ubk)|^2

      is a minimum with respect to the (n + 1) variables, Pi; the {ubk}
      are the precomputed parameter values.

    The resulting curve generally does not pass precisely through Qk,
    and C(ubk) is not the closest points on C(u) to Qk.

    Parameters
    ----------
    r + 1 = the number of data points to fit
    Q = the point set in object matrix form
    p = the degree of the fit
    n + 1 = the number of control points to use in the fit
    uk = the parameter values associated to each data point (if available)
    U = the knot vector to use in the fit (if available)
    NTN = the decomposed matrix of scalars (if available)

    Returns
    -------
    U, Pw = the knot vector and the object matrix of the fitted curve

    Source
    ------
    The NURBS Book (2nd Ed.), Pg. 410.

    '''

    if p < 1 or n < p:
        raise ImproperInput(p, n)
    if not r > n:
        raise NotEnoughDataPoints(r, n)
    Q = nurbs.obj_mat_to_3D(Q)
    if uk is None:
        uk = knot.chord_length_param(r, Q)
    if U is None:
        U = knot.approximating_knot_vec(n, p, r, uk)
    if NTN is None:
        lu, NT, Os, Oe = build_decompose_NTN(r, p, n, uk, U)
    else:
        lu, NT, Os, Oe = NTN
    R = Q - (Os[0] * Q[0][:,np.newaxis]).T - \
            (Oe[0] * Q[r][:,np.newaxis]).T
    P = np.zeros((n + 1, 3))
    P[0], P[n] = Q[0], Q[r]
    for i in xrange(3):
        rhs = np.dot(NT, R[1:-1, i])
        P[1:-1, i] = lu.solve(rhs)
    Pw = nurbs.obj_mat_to_4D(P)
    return U, Pw