Beispiel #1
0
def extract_nurbs_isocurve(n, p, uk, m, q, vk, cpw, u=None, v=None):
    """
    Extract iso-curve from NURBS surface.

    :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 u: Location of *u* split (in v-direction).
    :param float v: Location of *v* split (in u-direction).

    :return: Knot vector and control points for NURBS iso-curve in specified
        direction (uq, qw).
    :rtype: tuple
    """
    if u is not None:
        # Split at u in v-direction.
        uq = array(vk, dtype=float64)
        # Special case if a >= u >= b.
        if u <= uk[p]:
            return uq, cpw[0, :]
        if u >= uk[n + 1]:
            return uq, cpw[n, :]
        # Determine multiplicity.
        k, s = find_span_mult(n, p, u, uk)
        qw = zeros((m + 1, 4), dtype=float64)
        for j in range(0, m + 1):
            _, qwi = curve_knot_ins(n, p, uk, cpw[:, j], u, p - s)
            qw[j] = qwi[k - s]
        return uq, qw
    elif v is not None:
        # Split at v in u-direction.
        uq = array(uk, dtype=float64)
        # Special case if a >= v >= b.
        if v <= vk[q]:
            return uq, cpw[:, 0]
        if v >= vk[m + 1]:
            return uq, cpw[:, m]
        # Determine multiplicity.
        k, s = find_span_mult(m, q, v, vk)
        qw = zeros((n + 1, 4), dtype=float64)
        for i in range(0, n + 1):
            _, qwi = curve_knot_ins(m, q, vk, cpw[i, :], v, q - s)
            qw[i] = qwi[k - s]
        return uq, qw
Beispiel #2
0
def curve_knot_ins(n, p, uk, cpw, u, r):
    """
    Curve knot insertion.

    :param int n: Number of control points - 1.
    :param int p: Degree.
    :param ndarray uk: Knot vector.
    :param ndarray cpw: Control points.
    :param float u: Knot value to insert.
    :param int r: Number of times to insert knot.

    :return: Knot vector and control points after knot insertion
        (knots, cpw).
    :rtype: tuple

    *Reference:* Algorithm A5.1 from "The NURBS Book."
    """
    # Find span and multiplicity.
    k, s = find_span_mult(n, p, u, uk)
    if r + s > p:
        r = p - s

    # Generate knot vector
    nq = n + r
    uq = zeros(nq + p + 2, dtype=float64)
    for i in range(0, k + 1):
        uq[i] = uk[i]
    for i in range(1, r + 1):
        uq[k + i] = u
    for i in range(k + 1, n + p + 2):
        uq[i + r] = uk[i]
    # Save unchanged control points.
    qw = zeros((nq + 1, 4), dtype=float64)
    for i in range(0, k - p + 1):
        qw[i] = cpw[i]
    for i in range(k - s, n + 1):
        qw[i + r] = cpw[i]
    rw = zeros((p + 1, 4), dtype=float64)
    for i in range(0, p - s + 1):
        rw[i] = cpw[k - p + i]
    # Insert knot r times.
    t = k - p
    for j in range(1, r + 1):
        t = k - p + j
        for i in range(0, p - j - s + 1):
            alpha = (u - uk[t + i]) / (uk[i + k + 1] - uk[t + i])
            rw[i] = alpha * rw[i + 1] + (1.0 - alpha) * rw[i]
        qw[t] = rw[0]
        qw[k + r - j - s] = rw[p - j - s]
    # Load remaining control points.
    for i in range(t + 1, k - s):
        qw[i] = rw[i - t]
    return uq, qw
Beispiel #3
0
def split_nurbs_curve(n, p, uk, cpw, u):
    """
    Split NURBS curve into two segments at *u*.

    :param int n: Number of control points - 1.
    :param int p: Degree.
    :param ndarray uk: Knot vector.
    :param ndarray cpw: Control points.
    :param float u: Parametric point to split curve at.

    :return: New knot vector and control points for two curves
        (uk1, qw1, uk2, qw2).
    :rtype: tuple
    """
    # Special case if a >= u >= b.
    # if u <= uk[p]:
    ptol = Settings.ptol
    if CmpFlt.le(u, uk[p], ptol):
        qw1 = zeros((n + 1, 4), dtype=float64)
        qw1[:] = cpw[0]
        uk1 = array(uk, dtype=float64)
        uk1[:] = uk[p]
        qw2 = array(cpw, dtype=float64)
        uk2 = array(uk, dtype=float64)
        return uk1, qw1, uk2, qw2
    # elif u >= uk[n + 1]:
    elif CmpFlt.ge(u, uk[n + 1], ptol):
        qw1 = array(cpw, dtype=float64)
        uk1 = array(uk, dtype=float64)
        qw2 = zeros((n + 1, 4), dtype=float64)
        qw2[:] = cpw[n]
        uk2 = array(uk, dtype=float64)
        uk2[:] = uk[n + 1]
        return uk1, qw1, uk2, qw2
    # Find multiplicity of knot.
    k, s = find_span_mult(n, p, u, uk)
    # Insert knot p - s times and update knot vector and control points.
    if s >= p:
        uq, qw = array(uk, dtype=float64), array(cpw, dtype=float64)
    else:
        uq, qw = curve_knot_ins(n, p, uk, cpw, u, p - s)
    # Control points and knot vectors.
    qw1 = qw[:k - s + 1]
    qw2 = qw[k - s:]
    m = n + p + 1
    uk1 = zeros((k + 1) + (p - s + 1), dtype=float64)
    uk2 = zeros((m - k) + p + 1, dtype=float64)
    uk1[:-1] = uq[:k + (p - s) + 1]
    uk1[-1] = u
    uk2[1:] = uq[k - s + 1:]
    uk2[0] = u
    return uk1, qw1, uk2, qw2
Beispiel #4
0
def filter_knot_vect(n, p, uk, x):
    """
    Filter the knot vector *x* so that the knot multiplicity is less than or
    equal to the degree *p* when inserted into *uk*.

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

    :return: Filtered knot vector *x*.
    :rtype: ndarray
    """
    temp = array(uk)
    xnew = []
    for i in range(0, len(x)):
        span, s = find_span_mult(n, p, x[i], temp)
        if s <= p:
            temp = insert(temp, span, x[i])
            n += 1
            xnew.append(x[i])
    return array(xnew, dtype=float64)
Beispiel #5
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
Beispiel #6
0
def surface_knot_ins(n, p, uk, m, q, vk, cpw, d, uv, r):
    """
    Surface knot insertion.

    :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 str d: Direction to insert knot *uv* ("u": u-direction,
        "v": v-direction.
    :param float uv: Knot to insert.
    :param int r: Number of times to insert knot.

    :return: Knot vector and control points after knot insertion (uq, vq, cpw).
    :rtype: tuple

    *Reference:* Algorithm A5.3 from "The NURBS Book."
    """
    if d.lower() in ['u']:
        # Generate u-direction knot vector.
        k, s = find_span_mult(n, p, uv, uk)
        if r + s > p:
            r = p - s
        nq = n + r
        uq = zeros(nq + p + 2, dtype=float64)
        for i in range(0, k + 1):
            uq[i] = uk[i]
        for i in range(1, r + 1):
            uq[k + i] = uv
        for i in range(k + 1, n + p + 2):
            uq[i + r] = uk[i]
        # Copy v-direction knot vector.
        vq = array(vk)
        # Save alphas.
        alpha = zeros((p + 1, r + 1), dtype=float64)
        for j in range(1, r + 1):
            t = k - p + j
            for i in range(0, p - j - s + 1):
                alpha[i, j] = (uv - uk[t + i]) / (uk[i + k + 1] - uk[t + i])
        # Insert knot for each row at each column.
        qw = zeros((nq + 1, m + 1, 4), dtype=float64)
        for col in range(0, m + 1):
            # Save unchanged control points.
            for i in range(0, k - p + 1):
                qw[i, col] = cpw[i, col]
            for i in range(k - s, n + 1):
                qw[i + r, col] = cpw[i, col]
            # Load auxiliary control points.
            rw = zeros((p + 1, 4), dtype=float64)
            for i in range(0, p - s + 1):
                rw[i] = cpw[k - p + i, col]
            # Insert the knot r times.
            t = k - p
            for j in range(1, r + 1):
                t = k - p + j
                for i in range(0, p - j - s + 1):
                    rw[i] = (alpha[i, j] * rw[i + 1] +
                             (1.0 - alpha[i, j]) * rw[i])
                qw[t, col] = rw[0]
                qw[k + r - j - s, col] = rw[p - j - s]
            # Load remaining control points.
            for i in range(t + 1, k - s):
                qw[i, col] = rw[i - t]
        return uq, vq, qw
    if d.lower() in ['v']:
        # Generate v-direction knot vector.
        k, s = find_span_mult(m, q, uv, vk)
        if r + s > q:
            r = q - s
        mq = m + r
        vq = zeros(mq + q + 2, dtype=float64)
        for i in range(0, k + 1):
            vq[i] = vk[i]
        for i in range(1, r + 1):
            vq[k + i] = uv
        for i in range(k + 1, m + q + 2):
            vq[i + r] = vk[i]
        # Copy u-direction knot vector.
        uq = array(uk)
        # Save alphas.
        alpha = zeros((q + 1, r + 1), dtype=float64)
        for j in range(1, r + 1):
            t = k - q + j
            for i in range(0, q - j - s + 1):
                alpha[i, j] = (uv - vk[t + i]) / (vk[i + k + 1] - vk[t + i])
        # Insert knot for each column at each row.
        qw = zeros((n + 1, mq + 1, 4), dtype=float64)
        for row in range(0, n + 1):
            # Save unchanged control points.
            for i in range(0, k - q + 1):
                qw[row, i] = cpw[row, i]
            for i in range(k - s, m + 1):
                qw[row, i + r] = cpw[row, i]
            # Load auxiliary control points.
            rw = zeros((q + 1, 4), dtype=float64)
            for i in range(0, q - s + 1):
                rw[i] = cpw[row, k - q + i]
            # Insert the knot r times.
            t = k - q
            for j in range(1, r + 1):
                t = k - q + j
                for i in range(0, q - j - s + 1):
                    rw[i] = (alpha[i, j] * rw[i + 1] +
                             (1.0 - alpha[i, j]) * rw[i])
                qw[row, t] = rw[0]
                qw[row, k + r - j - s] = rw[q - j - s]
            # Load remaining control points.
            for i in range(t + 1, k - s):
                qw[row, i] = rw[i - t]
        return uq, vq, qw
Beispiel #7
0
def split_nurbs_surface(n, p, uk, m, q, vk, cpw, u=None, v=None):
    """
    Split NURBS surface into two segments in specified direction.

    :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 u: Location of *u* split (in v-direction).
    :type u: float or None
    :param v: Location of *v* split (in u-direction).
    :type v: float or None

    :return: New knot vectors and control points for two surfaces
        (uk1, vk1, qw1, uk2, vk2, qw2).
    :rtype: tuple
    """
    if u is not None:
        # Split at u in v-direction.
        vk12 = array(vk, dtype=float64)
        if u <= uk[p]:
            qw1 = zeros((n + 1, m + 1, 4), dtype=float64)
            for i in range(0, n + 1):
                qw1[i, :] = cpw[0, :]
            qw2 = array(cpw, dtype=float64)
            uk1 = array(uk, dtype=float64)
            uk1[:] = uk[p]
            uk2 = array(uk, dtype=float64)
            return uk1, vk12, qw1, uk2, vk12, qw2
        if u >= uk[n + 1]:
            qw1 = array(cpw, dtype=float64)
            qw2 = zeros((n + 1, m + 1, 4), dtype=float64)
            for i in range(0, n + 1):
                qw2[i, :] = cpw[n, :]
            uk1 = array(uk, dtype=float64)
            uk2 = array(uk, dtype=float64)
            uk2[:] = uk[n + 1]
            return uk1, vk12, qw1, uk2, vk12, qw2
        # Find multiplicity of knot.
        k, s = find_span_mult(n, p, u, uk)
        # Insert knot p - s times and update knot vector and control points.
        if s >= p:
            uq, qw = array(uk, dtype=float64), array(cpw, dtype=float64)
        else:
            uq, _, qw = surface_knot_ins(n, p, uk, m, q, vk, cpw, 'u', u,
                                         p - s)
        qw1 = qw[:k - s + 1, :]
        qw2 = qw[k - s:, :]
        r = n + p + 1
        uk1 = zeros((k + 1) + (p - s + 1), dtype=float64)
        uk2 = zeros((r - k) + p + 1, dtype=float64)
        uk1[:-1] = uq[:k + (p - s) + 1]
        uk1[-1] = u
        uk2[1:] = uq[k - s + 1:]
        uk2[0] = u
        return uk1, vk12, qw1, uk2, vk12, qw2
    elif v is not None:
        # Split at v in u-direction.
        uk12 = array(uk, dtype=float64)
        if v <= vk[q]:
            qw1 = zeros((n + 1, m + 1, 4), dtype=float64)
            for j in range(0, m + 1):
                qw1[:, j] = cpw[:, 0]
            qw2 = array(cpw, dtype=float64)
            vk1 = array(vk, dtype=float64)
            vk1[:] = vk[q]
            vk2 = array(vk, dtype=float64)
            return uk12, vk1, qw1, uk12, vk2, qw2
        if v >= vk[m + 1]:
            qw1 = array(cpw, dtype=float64)
            qw2 = zeros((n + 1, m + 1, 4), dtype=float64)
            for j in range(0, m + 1):
                qw2[:, j] = cpw[:, m]
            vk1 = array(vk, dtype=float64)
            vk2 = array(vk, dtype=float64)
            vk2[:] = vk[m + 1]
            return uk12, vk1, qw1, uk12, vk2, qw2
        # Find multiplicity of knot.
        k, s = find_span_mult(m, q, v, vk)
        # Insert knot q - s times and update knot vector and control points.
        if s >= q:
            vq, qw = array(vk, dtype=float64), array(cpw, dtype=float64)
        else:
            _, vq, qw = surface_knot_ins(n, p, uk, m, q, vk, cpw, 'v', v,
                                         q - s)
        qw1 = qw[:, :k - s + 1]
        qw2 = qw[:, k - s:]
        r = m + q + 1
        vk1 = zeros((k + 1) + (q - s + 1), dtype=float64)
        vk2 = zeros((r - k) + q + 1, dtype=float64)
        vk1[:-1] = vq[:k + (q - s) + 1]
        vk1[-1] = v
        vk2[1:] = vq[k - s + 1:]
        vk2[0] = v
        return uk12, vk1, qw1, uk12, vk2, qw2
Beispiel #8
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