コード例 #1
0
ファイル: fit.py プロジェクト: sunstarchan/Genair
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)
コード例 #2
0
ファイル: fit.py プロジェクト: sunstarchan/Genair
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
コード例 #3
0
ファイル: fit.py プロジェクト: sunstarchan/Genair
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
コード例 #4
0
ファイル: fit.py プロジェクト: sunstarchan/Genair
def global_surf_approx_fixednm(r, s, Q, p, q, n, m, uk=None, vl=None):
    ''' Let {Qkl}, k=0,...,r and l=0,...,s, be the (r + 1) x (s + 1) set
    of points to be approximated by a (p,q)th degree nonrational
    surface, with (n + 1) x (m + 1) control points.  The algorithm
    interpolates the corner points Q00, Qr0, Q0s and Qrs precisely, and
    approximates the remaining {Qkl}.  It fits the (s + 1) rows of data
    first, then fits across the resulting control points to produce the
    (n + 1) x (m + 1) surface control points.

    Parameters
    ----------
    r + 1, s + 1 = the number of data points to fit in the u and v
                   directions, respectively
    Q = the point set in object matrix form
    p, q = the degrees of the fit in the u and v directions,
           respectively
    n + 1, m + 1 = the number of control points in the u and v
                   directions, respectively, to use in the fit
    uk, vk = the parameter values associated to each points in the u and
             v directions, respectively (if available)

    Returns
    -------
    U, V, Pw = the knot vectors and the object matrix of the fitted
               surface

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

    '''

    Q = nurbs.obj_mat_to_3D(Q)
    if uk is None or vl is None:
        uk2, vl2 = surf_mesh_params(r, s, Q)
        if uk is None: uk = uk2
        if vl is None: vl = vl2
    U = knot.approximating_knot_vec(n, p, r, uk)
    V = knot.approximating_knot_vec(m, q, s, vl)
    NTNu = build_decompose_NTN(r, p, n, uk, U)
    NTNv = build_decompose_NTN(s, q, m, vl, V)
    tmp = np.zeros((n + 1, s + 1, 4))
    Pw = np.zeros((n + 1, m + 1, 4))
    for j in xrange(s + 1):
        dummy, tmp[:,j] = \
                global_curve_approx_fixedn(r, Q[:,j], p, n, uk, U, NTNu)
    for i in xrange(n + 1):
        dummy, Pw[i,:] = \
                global_curve_approx_fixedn(s, tmp[i,:], q, m, vl, V, NTNv)
    return U, V, Pw
コード例 #5
0
ファイル: fit.py プロジェクト: sunstarchan/Genair
def global_surf_interp(n, m, Q, p, q, uk=None, vl=None):
    ''' A set of (n + 1) x (m + 1) data poins {Qkl}, k=0,...,n and
    l=0,...,m, is given, and it is desired to construct a nonrational
    (p,q)th-degree B-spline surface interpolating the points, i.e.

                          Qkl = S(ubk, vbk) =
          sum_(i=0)^n sum_(j=0)^m (Nip(ubk) * Njq(vbk) * Pij)

    The interpolation is conducted in two steps:

    1.  using U and the ub_k, do (m + 1) curve interpolations through
        Q0l,...,Qnl (for l=0,...,m); this yields the points {Ril};

    2.  using V and the vb_l, do (n + 1) curve interpolations through
        Ri0,...,Rim (for i=0,...,n); this yields the {Pij}.

    Parameters
    ----------
    n + 1, m + 1 = the number of data points to interpolate in the u and
                   v directions, respectively
    Q = the point set in object matrix form
    p, q = the degrees of the interpolant in the and v directions,
           respectively
    uk, vl = the parameter values associated to each points in the u and
             v directions, respectively (if available)

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

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

    '''

    Q = nurbs.obj_mat_to_3D(Q)
    Pw = np.zeros((n + 1, m + 1, 4))
    if uk is None or vl is None:
        uk2, vl2 = surf_mesh_params(n, m, Q)
        if uk is None: uk = uk2
        if vl is None: vl = vl2
    U = knot.averaging_knot_vec(n, p, uk)
    V = knot.averaging_knot_vec(m, q, vl)
    for l in xrange(m + 1):
        dummy, Pw[:, l] = global_curve_interp(n, Q[:, l], p, uk, U)
    for i in xrange(n + 1):
        dummy, Pw[i, :] = global_curve_interp(m, Pw[i, :], q, vl, V)
    return U, V, Pw
コード例 #6
0
ファイル: fit.py プロジェクト: sunstarchan/Genair
def local_curve_interp(n, Q, Ts=None, Te=None):
    ''' Let {Qk}, k=0,...,n, be a set of three-dimensional data points.
    This algorithm constructs a C1 cubic (p = 3) Bezier curve segment,
    Ck(u), between each pair of points Q_k, Q_(k+1).

    Parameters
    ----------
    n + 1 = the number of data points to interpolate
    Q = the point set in object matrix form
    Ts, Te = the tangent vectors at the start and end data points (if
             available)

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

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

    '''

    if n < 1:
        raise ImproperInput(n)
    Q = nurbs.obj_mat_to_3D(Q)
    T = compute_unit_tangents(n, Q)
    if Ts is not None: T[0] = util.normalize(Ts)
    if Te is not None: T[-1] = util.normalize(Te)
    uk = np.zeros(n + 1)
    P = np.zeros((2 * n + 2, 3))
    P[0] = Q[0]
    for k in xrange(n):
        a = 16.0 - util.norm(T[k] + T[k + 1])**2
        b = 12.0 * np.dot(Q[k + 1] - Q[k], T[k] + T[k + 1])
        c = -36.0 * util.norm(Q[k + 1] - Q[k])**2
        alfs = np.roots((a, b, c))
        dummy, alf = np.sort(alfs)
        P[2 * k + 1] = Q[k] + alf * T[k] / 3.0
        P[2 * k + 2] = Q[k + 1] - alf * T[k + 1] / 3.0
        uk[k + 1] = uk[k] + 3.0 * util.norm(P[2 * k + 1] - Q[k])
    P[-1] = Q[-1]
    Pw = nurbs.obj_mat_to_4D(P)
    uk = uk[1:n].repeat(2) / uk[-1]
    U = np.zeros(2 * n + 6)
    U[-4:] = 1.0
    U[4:-4] = uk
    return U, Pw
コード例 #7
0
ファイル: fit.py プロジェクト: sunstarchan/Genair
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
コード例 #8
0
ファイル: fit.py プロジェクト: sunstarchan/Genair
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
コード例 #9
0
ファイル: fit.py プロジェクト: sunstarchan/Genair
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
コード例 #10
0
ファイル: fit.py プロジェクト: sunstarchan/Genair
def local_surf_interp(n, m, Q):
    ''' A C11 (C1 continuous in both the u and v directions), bicubic,
    local surface interpolation scheme.  Let {Qkl}, k=0,...,n and
    l=0,...,m, be a set of data points, and let {(ubk, vbl)} be the
    corresponding parameter pairs, computed by chord length averaging.
    This algorithm produces a bicubic surface, S(u,v), satisfying

                              S(ubk,vbl) =
     sum_(i=0)^(2n+1) sum_(j=0)^(2m+1) (Ni3(ubk) * Nj3(vbl) * Pij)

    This surface is obtained by constructing nm bicubic Bezier patches,
    {Bkl(u,v)}, k=0,...,n-1, l=0,...,m-1, where Q_(k,l), Q_(k+1,l),
    Q_(k,l+1), Q_(k+1,l+1) are the corner points of the patch, and the
    patches join with C11 continuity across their boundaries.

    Parameters
    ----------
    n + 1, m + 1 = the number of data points to interpolate in the u and
                   v directions, respectively
    Q = the point set in object matrix form

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

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

    '''

    if n < 1 or m < 1:
        raise ImproperInput(n, m)
    Q = nurbs.obj_mat_to_3D(Q)
    uk, vl = np.zeros(n + 1), np.zeros(m + 1)
    r, s = np.zeros(m + 1), np.zeros(n + 1)
    Td = np.zeros((n + 1, m + 1, 3, 3))
    U, V = np.zeros(2 * n + 6), np.zeros(2 * m + 6)
    P = np.zeros((3 * n + 1, 3 * m + 1, 3))
    total = 0.0
    for l in xrange(m + 1):
        Td[:, l, 0] = compute_unit_tangents(n, Q[:, l])
        r[l] = 0.0
        for k in xrange(1, n + 1):
            d = util.distance(Q[k, l], Q[k - 1, l])
            uk[k] += d
            r[l] += d
        total += r[l]
    for k in xrange(1, n):
        uk[k] = uk[k - 1] + uk[k] / total
    uk[n] = 1.0
    total = 0.0
    for k in xrange(n + 1):
        Td[k, :, 1] = compute_unit_tangents(m, Q[k, :])
        s[k] = 0.0
        for l in xrange(1, m + 1):
            d = util.distance(Q[k, l], Q[k, l - 1])
            vl[l] += d
            s[k] += d
        total += s[k]
    for l in xrange(1, m):
        vl[l] = vl[l - 1] + vl[l] / total
    vl[m] = 1.0
    U[4:-4], V[4:-4] = uk[1:n].repeat(2), vl[1:m].repeat(2)
    U[-4:], V[-4:] = 1.0, 1.0
    for l in xrange(m + 1):
        for k in xrange(n + 1):
            P[3 * k, 3 * l] = Q[k, l]
    for l in xrange(m + 1):
        for k in xrange(n):
            a = r[l] * (uk[k + 1] - uk[k]) / 3.0
            P[3 * k + 1, 3 * l] = Q[k, l] + a * Td[k, l, 0]
            P[3 * k + 2, 3 * l] = Q[k + 1, l] - a * Td[k + 1, l, 0]
    for k in xrange(n + 1):
        for l in xrange(m):
            a = s[k] * (vl[l + 1] - vl[l]) / 3.0
            P[3 * k, 3 * l + 1] = Q[k, l] + a * Td[k, l, 1]
            P[3 * k, 3 * l + 2] = Q[k, l + 1] - a * Td[k, l + 1, 1]
    Dkl = np.zeros((n + 1, m + 1, 2, 3))
    for l in xrange(m + 1):
        for k in xrange(n + 1):
            Dkl[k, l, 0] = r[l] * Td[k, l, 0]
            Dkl[k, l, 1] = s[k] * Td[k, l, 1]
    ak = np.ones(n + 1)
    for k in xrange(1, n):
        duk, duk1 = uk[k] - uk[k - 1], uk[k + 1] - uk[k]
        ak[k] = duk / (duk + duk1)
    bl = np.ones(m + 1)
    for l in xrange(1, m):
        dvl, dvl1 = vl[l] - vl[l - 1], vl[l + 1] - vl[l]
        bl[l] = dvl / (dvl + dvl1)
    dkl = np.zeros((n + 1, m + 1, 2, 3))
    for k in xrange(1, n):
        for l in xrange(m + 1):
            duk, duk1 = uk[k] - uk[k - 1], uk[k + 1] - uk[k]
            dkl[k, l,
                0] = ((1.0 - ak[k]) * (Dkl[k, l, 1] - Dkl[k - 1, l, 1]) / duk +
                      ak[k] * (Dkl[k + 1, l, 1] - Dkl[k, l, 1]) / duk1)
    for l in xrange(1, m):
        for k in xrange(n + 1):
            dvl, dvl1 = vl[l] - vl[l - 1], vl[l + 1] - vl[l]
            dkl[k, l,
                1] = ((1.0 - bl[l]) * (Dkl[k, l, 0] - Dkl[k, l - 1, 0]) / dvl +
                      bl[l] * (Dkl[k, l + 1, 0] - Dkl[k, l, 0]) / dvl1)
    for k in xrange(n + 1):
        dvl1, dvln = vl[1] - vl[0], vl[-1] - vl[-2]
        dkl[k, 0,
            1] = 2.0 * (Dkl[k, 1, 0] - Dkl[k, 0, 0]) / dvl1 - dkl[k, 1, 0]
        dkl[k, -1,
            1] = 2.0 * (Dkl[k, -1, 0] - Dkl[k, -2, 0]) / dvln - dkl[k, -2, 0]
    for l in xrange(m + 1):
        duk1, dukn = uk[1] - uk[0], uk[-1] - uk[-2]
        dkl[0, l,
            0] = 2.0 * (Dkl[1, l, 1] - Dkl[0, l, 1]) / duk1 - dkl[1, l, 1]
        dkl[-1, l,
            0] = 2.0 * (Dkl[-1, l, 1] - Dkl[-2, l, 1]) / dukn - dkl[-2, l, 1]
    for l in xrange(m + 1):
        for k in xrange(n + 1):
            Td[k, l, 2] = ((ak[k] * dkl[k, l, 1] + bl[l] * dkl[k, l, 0]) /
                           (ak[k] + bl[l]))
    for l in xrange(m):
        for k in xrange(n):
            gamma = (uk[k + 1] - uk[k]) * (vl[l + 1] - vl[l]) / 9.0
            P[3 * k + 1,
              3 * l + 1] = (gamma * Td[k, l, 2] + P[3 * k, 3 * l + 1] +
                            P[3 * k + 1, 3 * l] - P[3 * k, 3 * l])
            P[3 * k + 2, 3 * l +
              1] = (-gamma * Td[k + 1, l, 2] + P[3 * k + 3, 3 * l + 1] -
                    P[3 * k + 3, 3 * l] + P[3 * k + 2, 3 * l])
            P[3 * k + 1, 3 * l +
              2] = (-gamma * Td[k, l + 1, 2] + P[3 * k + 1, 3 * l + 3] -
                    P[3 * k, 3 * l + 3] + P[3 * k, 3 * l + 2])
            P[3 * k + 2, 3 * l +
              2] = (gamma * Td[k + 1, l + 1, 2] + P[3 * k + 2, 3 * l + 3] +
                    P[3 * k + 3, 3 * l + 2] - P[3 * k + 3, 3 * l + 3])
    P = np.delete(P, np.s_[3:-3:3], axis=0)
    P = np.delete(P, np.s_[3:-3:3], axis=1)
    Pw = nurbs.obj_mat_to_4D(P)
    return U, V, Pw