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
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
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
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
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