Exemple #1
0
def refine_knot_vect_curve(n, p, uk, cpw, x):
    """
    Refine curve knot vector.

    :param int n: Number of control points - 1.
    :param int p: Degree.
    :param ndarray uk: Knot vector.
    :param ndarray cpw: Control points.
    :param ndarray x: Knots to add to existing knot vector.

    :return: New knot vector and control points (ubar, qw).
    :rtypeL tuple

    *Reference:* Algorithm A5.4 from "The NURBS Book."
    """
    x = filter_knot_vect(n, p, uk, x)
    m = n + p + 1
    r = len(x) - 1
    a = find_span(n, p, x[0], uk)
    b = find_span(n, p, x[r], uk) + 1
    nq = m + (r + 1) - p - 1
    qw = zeros((nq + 1, 4), dtype=float64)
    # Load unchanged control points.
    for j in range(0, a - p + 1):
        qw[j] = cpw[j]
    for j in range(b - 1, n + 1):
        qw[j + r + 1] = cpw[j]
    # Load unchanged knot vector.
    ubar = zeros(m + (r + 1) + 1, dtype=float64)
    for j in range(0, a + 1):
        ubar[j] = uk[j]
    for j in range(b + p, m + 1):
        ubar[j + r + 1] = uk[j]
    # Find new control points.
    i = b + p - 1
    k = b + p + r
    for j in range(r, -1, -1):
        while x[j] <= uk[i] and i > a:
            qw[k - p - 1] = cpw[i - p - 1]
            ubar[k] = uk[i]
            k -= 1
            i -= 1
        qw[k - p - 1] = qw[k - p]
        for t in range(1, p + 1):
            ind = k - p + t
            alpha = ubar[k + t] - x[j]
            if abs(alpha) == 0.0:
                qw[ind - 1] = qw[ind]
            else:
                alpha /= (ubar[k + t] - uk[i - p + t])
                qw[ind - 1] = alpha * qw[ind - 1] + (1.0 - alpha) * qw[ind]
        ubar[k] = x[j]
        k -= 1
    return ubar, qw
Exemple #2
0
def global_curve_interpolation(qp, p=3, method='chord'):
    """
    Global curve interpolation through n + 1 points.

    :param qp: List or array of points.
    :etype qp: Points or array_like
    :param int p: Degree. Will be adjusted if not enough interpolation points
        are provided (p = npoints - 1).
    :param str method: Option to specify method for selecting parametersexit
        ("uniform", "chord", or "centripetal").

    :return: NURBS curve data interpolating points (cp, uk, p).
    :rtype: tuple

    *Reference:* "The NURBS Book" Section 9.2.1.
    """
    # Get qp as array if not already.
    if not isinstance(qp, ndarray):
        qp = array(qp, dtype=float64)

    # Determine n from shape of qp.
    n = qp.shape[0] - 1

    # Check that enough points are provided for desired degree.
    if n < p:
        p = n

    # Compute parameters between [0, 1].
    m = n + p + 1
    if method.lower() in ['u', 'uniform']:
        u = uniform(qp, 0., 1.)
    elif method.lower() in ['ch', 'chord']:
        u = chord_length(qp, 0., 1.)
    else:
        u = centripetal(qp, 0., 1.)

    # Compute knot vector by averaging.
    uk = zeros(m + 1, dtype=float64)
    uk[m - p:] = 1.0
    for j in range(1, n - p + 1):
        temp = 0.
        for i in range(j, j + p):
            temp += u[i]
        uk[j + p] = 1.0 / p * temp

    # Set up system of linear equations.
    if p > 1:
        a = zeros((n + 1, n + 1), dtype=float64)
        for i in range(0, n + 1):
            span = find_span(n, p, u[i], uk)
            a[i, span - p:span + 1] = basis_funs(span, u[i], p, uk)
    else:
        a = identity(n + 1, dtype=float64)

    # Solve for [a][cp] = [qp] using LU decomposition.
    lu, piv = lu_factor(a, overwrite_a=True, check_finite=False)
    cp = lu_solve((lu, piv), qp, trans=0, overwrite_b=True, check_finite=True)

    return cp, uk, p
Exemple #3
0
def move_nurbs_surface_seam(n, p, uk, m, q, vk, cpw, uv, d):
    """
    Move the seam of a closed surface to a new parameter value.

    :param int n: Number of control points - 1 in u-direction.
    :param int p: Degree in u-direction.
    :param ndarray uk: Knot vector in u-direction.
    :param int m: Number of control points - 1 in v-direction.
    :param int q: Degree in v-direction.
    :param ndarray vk: Knot vector in v-direction.
    :param ndarray cpw: Control points.
    :param float uv: New seam parameter.
    :param str d: Direction to move seam in (surface must be closed in that
        direction).

    :return: Knot vector and control points of new surface.
    :rtype: tuple
    """
    ptol = Settings.ptol
    if d.lower() in ['u']:
        if CmpFlt.le(uv, uk[p], ptol) or CmpFlt.ge(uv, uk[n + 1], ptol):
            return uk, vk, cpw

        # Check multiplicity of seam parameter.
        _, s = find_span_mult(n, p, uv, uk)

        # Insert seam parameter s - p times.
        uq, vq, qw = surface_knot_ins(n, p, uk, m, q, vk, cpw, 'u', uv, p - s)

        # Find span of seam parameter in new knot vector.
        nw = qw.shape[0] - 1
        k = find_span(nw, p, uv, uq)

        # Get index for first row of control points.
        n0 = k - p

        # Generate control points for new seam.
        qw_seam = array(qw)
        indx = 0
        # For new seam to end of old seam
        for i in range(n0, nw + 1):
            qw_seam[indx, :, :] = qw[i, :, :]
            indx += 1
        # From start of old seam to new seam.
        for i in range(1, n0):
            qw_seam[indx, :, :] = qw[i, :, :]
            indx += 1
        # Set last row.
        qw_seam[-1, :, :] = qw_seam[0, :, :]

        # Generate new knot vector.
        uq_seam = array(uq)
        # From seam to end
        indx = p + 1
        for i in range(k + 1, nw + p + 1):
            uq_seam[indx] = uq[i] - uv
            indx += 1
        # From start to seam
        for i in range(p + 1, k + 1 - p):
            uq_seam[indx] = uq_seam[indx - 1] + (uq[i] - uq[i - 1])
            indx += 1
        uq_seam[-p - 1:] = uq[-1]
        return uq_seam, vk, qw_seam

    if d.lower() in ['v']:
        if CmpFlt.le(uv, vk[q], ptol) or CmpFlt.ge(uv, vk[m + 1], ptol):
            return uk, vk, cpw

        # Check multiplicity of seam parameter.
        _, s = find_span_mult(m, q, uv, vk)

        # Insert seam parameter s - q times.
        uq, vq, qw = surface_knot_ins(n, p, uk, m, q, vk, cpw, 'v', uv, q - s)

        # Find span of seam parameter in new knot vector.
        mw = qw.shape[1] - 1
        k = find_span(mw, q, uv, vq)

        # Get index for first row of control points.
        m0 = k - q

        # Generate control points for new seam.
        qw_seam = array(qw)
        indx = 0
        # For new seam to end of old seam
        for i in range(m0, mw + 1):
            qw_seam[:, indx, :] = qw[:, i, :]
            indx += 1
        # From start of old seam to new seam.
        for i in range(1, m0):
            qw_seam[:, indx, :] = qw[:, i, :]
            indx += 1
        # Set last row.
        qw_seam[:, -1, :] = qw_seam[:, 0, :]

        # Generate new knot vector.
        vq_seam = array(vq)
        # From seam to end
        indx = q + 1
        for i in range(k + 1, mw + q + 1):
            vq_seam[indx] = vq[i] - uv
            indx += 1
        # From start to seam
        for i in range(q + 1, k + 1 - q):
            vq_seam[indx] = vq_seam[indx - 1] + (vq[i] - vq[i - 1])
            indx += 1
        vq_seam[-q - 1:] = vq[-1]
        return uk, vq_seam, qw_seam
Exemple #4
0
def refine_knot_vect_surface(n, p, uk, m, q, vk, cpw, x, d):
    """
    Refine surface knot vector.

    :param int n: Number of control points - 1 in u-direction.
    :param int p: Degree in u-direction.
    :param ndarray uk: Knot vector in u-direction.
    :param int m: Number of control points - 1 in v-direction.
    :param int q: Degree in v-direction.
    :param ndarray vk: Knot vector in v-direction.
    :param ndarray cpw: Control points.
    :param ndarray x: Knots to add to existing knot vector.
    :param str d: Direction to insert knot *uv* ("u": u-direction,
        "v": v-direction.

    :return: New knot vectors in both u- and v-direction and control points
        (ubar, vbar, qw).

    *Reference:* Algorithm A5.5 from "The NURBS Book."
    """
    if d.lower() in ['u']:
        x = filter_knot_vect(n, p, uk, x)
        a = find_span(n, p, x[0], uk)
        b = find_span(n, p, x[-1], uk) + 1
        r = len(x) - 1
        nn = n + p + 1
        # Save unchanged control points.
        qw = zeros((r + p + b - a + 1, m + 1, 4), dtype=float64)
        for col in range(0, m + 1):
            for i in range(0, a - p + 1):
                qw[i, col] = cpw[i, col]
            for i in range(b - 1, n + 1):
                qw[i + r + 1, col] = cpw[i, col]
        # Load unchanged knot vector.
        ubar = zeros(nn + r + 2, dtype=float64)
        vbar = array(vk)
        for j in range(0, a + 1):
            ubar[j] = uk[j]
        for j in range(b + p, nn + 1):
            ubar[j + r + 1] = uk[j]
        # Find new control points.
        i = b + p - 1
        k = b + p + r
        for j in range(r, -1, -1):
            while x[j] <= uk[i] and i > a:
                ubar[k] = uk[i]
                for col in range(0, m + 1):
                    qw[k - p - 1, col] = cpw[i - p - 1, col]
                k -= 1
                i -= 1
            for col in range(0, m + 1):
                qw[k - p - 1, col] = qw[k - p, col]
            for t in range(1, p + 1):
                ind = k - p + t
                alpha = ubar[k + t] - x[j]
                if abs(alpha) == 0.0:
                    for col in range(0, m + 1):
                        qw[ind - 1, col] = qw[ind, col]
                else:
                    alpha /= (ubar[k + t] - uk[i - p + t])
                    for col in range(0, m + 1):
                        qw[ind - 1, col] = (alpha * qw[ind - 1, col] +
                                            (1.0 - alpha) * qw[ind, col])
            ubar[k] = x[j]
            k -= 1
        return ubar, vbar, qw
    if d.lower() in ['v']:
        x = filter_knot_vect(m, q, vk, x)
        a = find_span(m, q, x[0], vk)
        b = find_span(m, q, x[-1], vk) + 1
        r = len(x) - 1
        mm = m + q + 1
        # Save unchanged control points.
        qw = zeros((n + 1, r + q + b - a + 1, 4), dtype=float64)
        for row in range(0, n + 1):
            for i in range(0, a - q + 1):
                qw[row, i] = cpw[row, i]
            for i in range(b - 1, m + 1):
                qw[row, i + r + 1] = cpw[row, i]
        # Load unchanged knot vector.
        vbar = zeros(mm + r + 2, dtype=float64)
        ubar = array(uk)
        for j in range(0, a + 1):
            vbar[j] = vk[j]
        for j in range(b + p, mm + 1):
            vbar[j + r + 1] = vk[j]
        # Find new control points.
        i = b + q - 1
        k = b + q + r
        for j in range(r, -1, -1):
            while x[j] <= vk[i] and i > a:
                vbar[k] = vk[i]
                for row in range(0, n + 1):
                    qw[row, k - q - 1] = cpw[row, i - q - 1]
                k -= 1
                i -= 1
            for row in range(0, n + 1):
                qw[row, k - q - 1] = qw[row, k - q]
            for t in range(1, q + 1):
                ind = k - q + t
                alpha = vbar[k + t] - x[j]
                if abs(alpha) == 0.0:
                    for row in range(0, n + 1):
                        qw[row, ind - 1] = qw[row, ind]
                else:
                    alpha /= (vbar[k + t] - vk[i - q + t])
                    for row in range(0, n + 1):
                        qw[row, ind - 1] = (alpha * qw[row, ind - 1] +
                                            (1.0 - alpha) * qw[row, ind])
            vbar[k] = x[j]
            k -= 1
        return ubar, vbar, qw
Exemple #5
0
def interpolate_curves(curves,
                       q=3,
                       method='chord',
                       inplace=False,
                       auto_reverse=True):
    """
    Create a surface by interpolating the list of section curves.

    :param list curves: List of NURBS curves to skin. Order of the curves in
        the list will determine the longitudinal (*v*) direction.
    :param int q: Degree in v-direction. Will be adjusted if not enough
        skinning curves are provided (q = ncurves - 1).
    :param str method: Option to specify method for selecting parameters
        ("uniform", "chord", or "centripetal").
    :param bool inplace: Option to modify the curves in the list in-place. If
        this option is *False*, then new curves will be created. If this option
        is *True*, then the curves in the list will be modified in-place.
    :param bool auto_reverse: Option to check direction of curves and
        reverse if necessary.

    :return: NURBS surface data (cpw, uk, vk, p, q).
    :rtype: tuple

    *Reference:* "The NURBS Book" Section 10.3.
    """
    # Step 0: Adjust desired degree in case not enough curves are provided.
    if len(curves) - 1 < q:
        q = len(curves) - 1

    # Step 1: Make sure each curve in a NURBS curve.
    nurbs_curves = []
    for c in curves:
        if inplace:
            nurbs_curves.append(c)
        else:
            nurbs_curves.append(c.copy())
    if not nurbs_curves:
        return None

    # Check direction of curves by comparing the derivatives at the midpoint.
    if auto_reverse:
        cu_0 = nurbs_curves[0].deriv(0.5, 1, rtype='ndarray')
        for c in nurbs_curves[1:]:
            cu_i = c.deriv(0.5, 1, rtype='ndarray')
            if dot(cu_0, cu_i) < 0.:
                c.reverse()
            cu_0 = cu_i

    # Step 2: Make sure each curve has a common degree.
    pmax = 0
    for c in nurbs_curves:
        if c.p > pmax:
            pmax = c.p
    for c in nurbs_curves:
        if c.p < pmax:
            c.elevate_degree(pmax, inplace=True)

    # Step 3: Make sure each curve has a similar knot vector.
    # Find each unique interior knot value in every curve.
    a, b = [], []
    for c in nurbs_curves:
        a.append(c.a)
        b.append(c.b)
    amin = min(a)
    bmax = max(b)
    for c in nurbs_curves:
        c.set_domain(amin, bmax)
    all_knots = []
    for c in nurbs_curves:
        nu, _, knots = c.get_mult_and_knots()
        for uk in knots[:nu]:
            all_knots.append(uk)
    all_knots.sort()
    unique_knots = []
    m = len(all_knots) - 1
    i = 0
    while i <= m:
        unique_knots.append(all_knots[i])
        # while i <= m and all_knots[i] == unique_knots[-1]:
        while i <= m and CmpFlt.eq(all_knots[i], unique_knots[-1],
                                   Settings.ptol):
            i += 1
    # For each curve, find the multiplicity and then the max multiplicity of
    # each unique knot value.
    all_mult = []
    for c in nurbs_curves:
        row = []
        for ui in unique_knots:
            _, mult = find_span_mult(c.n, c.p, ui, c.uk)
            row.append(mult)
        all_mult.append(row)
    mult_arr = array(all_mult, dtype=int32)
    max_mult = amax(mult_arr, axis=0)
    # For each curve, insert the knot the required number of times, if any.
    for mult, ui in zip(max_mult, unique_knots):
        for c in nurbs_curves:
            _, c_mult = find_span_mult(c.n, c.p, ui, c.uk)
            if c_mult < mult:
                c.insert_knot(ui, mult - c_mult, inplace=True, domain='global')

    # Step 4: Compute v-direction parameters between [0, 1].
    # Find parameters between each curve by averaging each segment.
    temp = array([c.cpw for c in nurbs_curves], dtype=float64)
    pnts_matrix = temp.transpose((1, 0, 2))
    c0 = nurbs_curves[0]
    n = c0.n
    m = len(nurbs_curves) - 1
    v_matrix = zeros((n + 1, m + 1), dtype=float64)
    for i in range(0, n + 1):
        if method.lower() in ['u', 'uniform']:
            v = uniform(pnts_matrix[i, :], 0., 1.)
        elif method.lower() in ['ch', 'chord']:
            v = chord_length(pnts_matrix[i, :], 0., 1.)
        else:
            v = centripetal(pnts_matrix[i, :], 0., 1.)
        v_matrix[i] = v
    # Average each column.
    v = mean(v_matrix, axis=0, dtype=float64)
    v[0] = 0.0
    v[-1] = 1.0

    # Step 5: Compute knot vector by averaging.
    uk = c0.uk
    s = m + q + 1
    vk = zeros(s + 1, dtype=float64)
    vk[s - q:] = 1.0
    for j in range(1, m - q + 1):
        temp = 0.
        for i in range(j, j + q):
            temp += v[i]
        vk[j + q] = 1.0 / q * temp

    # Step 6: Perform n + 1 interpolations for v-direction.
    cpw = zeros((n + 1, m + 1, 4), dtype=float64)
    for i in range(0, n + 1):
        qp = pnts_matrix[i]
        # Set up system of linear equations.
        a = zeros((m + 1, m + 1), dtype=float64)
        for j in range(0, m + 1):
            span = find_span(m, q, v[j], vk)
            a[j, span - q:span + 1] = basis_funs(span, v[j], q, vk)
        # Solve for [a][cp] = [qp] using LU decomposition.
        lu, piv = lu_factor(a, overwrite_a=True, check_finite=False)
        cpw[i, :] = lu_solve((lu, piv),
                             qp,
                             trans=0,
                             overwrite_b=True,
                             check_finite=True)

    return cpw, uk, vk, pmax, q