# 51. `plugins.nurbs` — Using NURBS in pyFormex.

The `nurbs` module defines functions and classes to manipulate NURBS curves and surface in pyFormex.

## 51.1. Classes defined in module plugins.nurbs

class `plugins.nurbs.``Coords4`

  A collection of points represented by their homogeneous coordinates.

  While most of the pyFormex implementation is based on the 3D Cartesian coordinates class `Coords`, some applications may benefit from using homogeneous coordinates. The class `Coords4` provides some basic functions and conversion to and from cartesian coordinates. Through the conversion, all other pyFormex functions, such as transformations, are available.

  `Coords4` is implemented as a float type `numpy.ndarray` whose last axis has a length equal to 4. Each set of 4 values (x,y,z,w) along the last axis represents a single point in 3D space. The cartesian coordinates of the point are obtained by dividing the first three values by the fourth: (x/w, y/w, z/w). A zero w-value represents a point at infinity. Converting such points to `Coords` will result in Inf or NaN values in the resulting object.

  The float datatype is only checked at creation time. It is the responsibility of the user to keep this consistent throughout the lifetime of the object.

  Just like `Coords`, the class `Coords4` is derived from `numpy.ndarray`.

  Parameters:

  data: array_like
    If specified, data should evaluate to an array of floats, with the length of its last axis not larger than 4. When equal to four, each tuple along the last axis represents a ingle point in homogeneous coordinates. If smaller than four, the last axis will be expanded to four by adding values zero in the second and third position and values 1 in the last position. If no data are given, a single point (0.,0.,0.) will be created.
  w: array_like
    If specified, the w values are used to denormalize the homogeneous data such that the last component becomes w.
  dtyp: data-type
    The datatype to be used. It not specified, the datatype of data is used, or the default `Float` (which is equivalent to `numpy.float32`).
  copy: boolean
    If `True`, the data are copied. By default, the original data are used if possible, e.g. if a correctly shaped and typed `numpy.ndarray` is specified.
  `normalize`()

    Normalize the homogeneous coordinates.

    Two sets of homogeneous coordinates that differ only by a multiplicative constant refer to the same points in cartesian space. Normalization of the coordinates is a way to make the representation of a single point unique. Normalization is done so that the last component (w) is equal to 1.

    The normalization of the coordinates is done in place.

    Warning

    Normalizing points at infinity will result in Inf or NaN values.

  `deNormalize`(w)

    Denormalizes the homogeneous coordinates.

    This multiplies the homogeneous coordinates with the values w. w normally is a constant or an array with shape self.shape[:-1] + (1,). It then multiplies all 4 coordinates of a point with the same value, thus resulting in a denormalization while keeping the position of the point unchanged.

    The denormalization of the coordinates is done in place. If the Coords4 object was normalized, it will have precisely w as its 4-th coordinate value after the call.

  `toCoords`()

    Convert homogeneous coordinates to cartesian coordinates.

    Returns:

    A `Coords` object with the cartesian coordinates of the points. Points at infinity (w=0) will result in Inf or NaN value. If there are no points at infinity, the resulting `Coords` point set is equivalent to the `Coords4` one.

  `npoints`()

    Return the total number of points.

  `ncoords`()

    Return the total number of points.

  `x`()

    Return the x-plane

  `y`()

    Return the y-plane

  `z`()

    Return the z-plane

  `w`()

    Return the w-plane

  `bbox`()

    Return the bounding box of a set of points.

    Returns the bounding box of the cartesian coordinates of the object.

  `actor`(**kargs)

    Graphical representation

class `plugins.nurbs.``Geometry4`

  This is a preliminary class intended to provide some transforms in 4D

class `plugins.nurbs.``KnotVector`(data=None, val=None, mul=None)

  A knot vector

  A knot vector is sequence of float values sorted in ascending order. Values can occur multiple times. In they typical use case for this class (Nurbs) most values do indeed occur multiple times, and the multiplicity of the values is an important quantity. Therefore, the knot vector is stored in two arrays of the same length:

  - v: the unique float values, a strictly ascending sequence
  - m: the multiplicity of each of the values

  Example:

      >>> K = KnotVector([0.,0.,0.,0.5,0.5,1.,1.,1.])
      >>> print(K.val)
      [ 0.   0.5  1. ]
      >>> print(K.mul)
      [3 2 3]
      >>> print(K)
      KnotVector: 0.0(3), 0.5(2), 1.0(3)
      >>> print([(v,m) for v,m in zip(K.val,K.mul)])
      [(0.0, 3), (0.5, 2), (1.0, 3)]
      >>> print(K.values())
      [ 0.   0.   0.   0.5  0.5  1.   1.   1. ]
      >>> K.index(0.5)
      1
      >>> K.span(1.0)
      5
      >>> K.mult(0.5)
      2
      >>> K.mult(0.7)
      0
      >>> K[4],K[-1]
      (0.5, 1.0)
      >>> K[4:6]
      array([ 0.5,  1. ], dtype=float32)

  `nknots`()

    Return the total number of knots

  `values`()

    Return the full list of knot values

  `index`(u)

    Find the index of knot value u.

    If the value does not exist, a ValueError is raised.

  `mult`(u)

    Return the multiplicity of knot value u.

    Returns an int with the multiplicity of the knot value u, or 0 if the value is not in the KNotVector.

  `span`(u)

    Find the (first) index of knot value u in the full knot values vector.

    If the value does not exist, a ValueError is raised.

  `copy`()

    Return a copy of the KnotVector.

    Changing the copy will not change the original.

  `reverse`()

    Return the reverse knot vector.

    Example:

        >>> print(KnotVector([0,0,0,1,3,6,6,8,8,8]).reverse().values())
        [ 0.  0.  0.  2.  2.  5.  7.  8.  8.  8.]

class `plugins.nurbs.``NurbsCurve`(control, degree=None, wts=None, knots=None, closed=False, blended=True)

  A NURBS curve

  The Nurbs curve is defined by nctrl control points, a degree (>= 1) and a knot vector with nknots = nctrl+degree+1 parameter values.

  Parameters:

  - control: Coords-like (nctrl,3): the vertices of the control polygon.
  - degree: int: the degree of the Nurbs curve. If not specified, it is derived from the length of the knot vector (knots).
  - wts: float array (nctrl): weights to be attributed to the control points. Default is to attribute a weight 1.0 to all points. Using different weights allows for more versatile modeling (like perfect circles and arcs.)
  - knots: KnotVector or an ascending list of nknots float values. The values are only defined upon a multiplicative constant and will be normalized to set the last value to 1. If degree is specified, default values are constructed automatically by calling `genKnotVector()`. If no knots are given and no degree is specified, the degree is set to the nctrl-1 if the curve is blended. If not blended, the degree is not set larger than 3.
  - closed: bool: determines whether the curve is closed. Default False. The use of closed NurbsCurves is currently very limited.
  - blended: bool: determines that the curve is blended. Default is True. Set blended==False to define a nonblended curve. A nonblended curve is a chain of independent curves, Bezier curves if the weights are all ones. See also `decompose()`. The number of control points should be a multiple of the degree, plus one. This parameter is only used if no knots are specified.
  `knots`

    Return the full list of knot values

  `nctrl`()

    Return the number of control points

  `nknots`()

    Return the number of knots

  `order`()

    Return the order of the Nurbs curve

  `urange`()

    Return the parameter range on which the curve is defined.

    Returns a (2,) float array with the minimum and maximum parameter value for which the curve is defined.

  `isClamped`()

    Return True if the NurbsCurve uses a clamped knot vector.

    A clamped knot vector has a multiplicity p+1 for the first and last knot. All our generated knot vectors are clamped.

  `isUniform`()

    Return True if the NurbsCurve has a uniform knot vector.

    A uniform knot vector has a constant spacing between the knot values.

  `isRational`()

    Return True if the NurbsCurve is rational.

    The curve is rational if the weights are not constant. The curve is polygonal if the weights are constant.

    Returns True for a rational curve, False for a polygonal curve.

  `isBlended`()

    Return True if the NurbsCurve is blended.

    An clamped NurbsCurve is unblended (or decomposed) if it consists of a chain of independent Bezier curves. Such a curve has multiplicity p for all internal knots and p+1 for the end knots of an open curve. Any other NurbsCurve is blended.

    Returns True for a blended curve, False for an unblended one.

    Note: for testing whether an unclamped curve is blended or not, first clamp it.

  `bbox`()

    Return the bounding box of the NURBS curve.

  `copy`()

    Return a (deep) copy of self.

    Changing the copy will not change the original.

  `pointsAt`(u)

    Return the points on the Nurbs curve at given parametric values.

    Parameters:

    - u: (nu,) shaped float array, parametric values at which a point is to be placed. Note that valid points are only obtained for parameter values in the range self.range().

    Returns (nu,3) shaped Coords with nu points at the specified parametric values.

  `derivs`(u, d=1)

    Returns the points and derivatives up to d at parameter values u

    Parameters:

    - u: either of:
      - int: number of points (npts) at which to evaluate the points and derivatives. The points will be equally spaced in parameter space.
      - float array (npts): parameter values at which to compute points and derivatives.
    - d: int: highest derivative to compute.

    Returns a float array of shape (d+1,npts,3).

  `frenet`(u)

    Compute Frenet vectors, curvature and torsion at parameter values u

    Parameters:

    - u: either of:
      - int: number of points (npts) at which to evaluate the points and derivatives. The points will be equally spaced in parameter space.
      - float array (npts): parameter values at which to compute points and derivatives.

    Returns a float array of shape (d+1,npts,3).

    Returns a tuple of arrays at nu parameter values u:

    - T: normalized tangent vector (nu,3)
    - N: normalized normal vector (nu,3)
    - B: normalized binormal vector (nu,3)
    - k: curvature of the curve (nu)
    - t: torsion of the curve (nu)
  `curvature`(u, torsion=False)

    Compute Frenet vectors, curvature and torsion at parameter values u

    Parameters:

    - u: either of:
      - int: number of points (npts) at which to evaluate the points and derivatives. The points will be equally spaced in parameter space.
      - float array (npts): parameter values at which to compute points and derivatives.
    - torsion: bool. If True, also returns the torsion in the curve.

    If torsion is False (default), returns a float array with the curvature at parameter values u. If torsion is True, also returns a float array with the torsion at parameter values u.

  `knotPoints`(multiple=False)

    Returns the points at the knot values.

    If multiple is True, points are returned with their multiplicity. The default is to return all points just once.

  `insertKnots`(u)

    Insert a set of knots in the Nurbs curve.

    u is a vector with knot parameter values to be inserted into the curve. The control points are adapted to keep the curve unchanged.

    Returns:

    A Nurbs curve equivalent with the original but with the specified knot values inserted in the knot vector, and the control points adapted.

  `requireKnots`(val, mul)

    Insert knots until the required multiplicity reached.

    Inserts knot values only if they are currently not there or their multiplicity is lower than the required one.

    Parameters:

    - val: list of float (nval): knot values required in the knot vector.
    - mul: list of int (nval): multiplicities required for the knot values u.

    Returns:

    A Nurbs curve equivalent with the original but where the knot vector is guaranteed to contain the values in u with at least the corresponding multiplicity in m. If all requirements were already fulfilled at the beginning, returns self.

  `subCurve`(u1, u2)

    Extract the subcurve between parameter values u1 and u2

    Parameters:

    - u1, u2: two parameter values (u1 < u2), delimiting the part of the curve to extract. These values do not have to be knot values.

    Returns a NurbsCurve containing only the part between u1 and u2.

  `clamp`()

    Clamp the knot vector of the curve.

    A clamped knot vector starts and ends with multiplicities p-1. See also `isClamped()`.

    Returns self if the curve is already clamped, else returns an equivalent curve with clamped knot vector.

    Note: The use of unclamped knot vectors is deprecated. This method is provided only as a convenient method to import curves from legacy systems using unclamped knot vectors.

  `unclamp`()

    Unclamp the knot vector of the curve.

    An unclamped knot vector starts and ends with multiplicities p-1. See also `isClamped()`.

    Returns self if the curve is already clamped, else returns an equivalent curve with clamped knot vector.

    Note: The use of unclamped knot vectors is deprecated. This method is provided as a convenient method to export curves to legacy systems that only handle unclamped knot vectors.

  `unblend`()

    Decomposes a curve in subsequent Bezier curves.

    Returns an equivalent unblended Nurbs.

    See also `toBezier()`

  `decompose`()

    Decomposes a curve in subsequent Bezier curves.

    Returns an equivalent unblended Nurbs.

    See also `toBezier()`

  `toCurve`(force_Bezier=False)

    Convert a (nonrational) NurbsCurve to a BezierSpline or PolyLine.

    This decomposes the curve in a chain of Bezier curves and converts the chain to a BezierSpline or PolyLine.

    This only works for nonrational NurbsCurves, as the BezierSpline and PolyLine classes do not allow homogeneous coordinates required for rational curves.

    Returns a BezierSpline or PolyLine (if degree is 1) that is equivalent with the NurbsCurve.

    See also `unblend()` which decomposes both rational and nonrational NurbsCurves.

  `toBezier`()

    Convert a (nonrational) NurbsCurve to a BezierSpline.

    This is equivalent with toCurve(force_Bezier=True) and returns a BezierSpline in all cases.

  `removeKnot`(u, m, tol=1e-05)

    Remove a knot from the knot vector of the Nurbs curve.

    u: knot value to remove m: how many times to remove (if negative, remove maximally)

    Returns:

    A Nurbs curve equivalent with the original but with a knot vector where the specified value has been removed m times, if possible, or else as many times as possible. The control points are adapted accordingly.

  `removeAllKnots`(tol=1e-05)

    Remove all removable knots

    Parameters:

    - tol: float: acceptable error (distance between old and new curve).

    Returns an equivalent (if tol is small) NurbsCurve with all extraneous knots removed.

  `blend`(tol=1e-05)

    Remove all removable knots

    Parameters:

    - tol: float: acceptable error (distance between old and new curve).

    Returns an equivalent (if tol is small) NurbsCurve with all extraneous knots removed.

  `elevateDegree`(t=1)

    Elevate the degree of the Nurbs curve.

    t: how much to elevate the degree

    Returns:

    A Nurbs curve equivalent with the original but of a higher degree.

  `reduceDegree`(t=1)

    Reduce the degree of the Nurbs curve.

    t: how much to reduce the degree (max. = degree-1)

    Returns:

    A Nurbs curve approximating the original but of a lower degree.

  `projectPoint`(P, eps1=1e-05, eps2=1e-05, maxit=20, nseed=20)

    Project a given point on the Nurbs curve.

    This can also be used to determine the parameter value of a point lying on the curve.

    Parameters:

    - P: Coords-like (npts,3): one or more points is space.

    Returns a tuple (u,X):

    - u: float: parameter value of the base point X of the projection of P on the NurbsCurve.
    - X: Coords (3,): the base point of the projection of P on the NurbsCurve.

    The algorithm is based on the from The Nurbs Book.

  `approx`(ndiv=None, nseg=None, **kargs)

    Return a PolyLine approximation of the Nurbs curve

    If no nseg is given, the curve is approximated by a PolyLine through equidistant ndiv+1 point in parameter space. These points may be far from equidistant in Cartesian space.

    If nseg is given, a second approximation is computed with nseg straight segments of nearly equal length. The lengths are computed based on the first approximation with ndiv segments.

  `actor`(**kargs)

    Graphical representation

  `reverse`()

    Return the reversed Nurbs curve.

    The reversed curve is geometrically identical, but start and en point are interchanged and parameter values increase in the opposite direction.

class `plugins.nurbs.``NurbsSurface`(control, degree=(None, None), wts=None, knots=(None, None), closed=(False, False), blended=(True, True))

  A NURBS surface

  The Nurbs surface is defined as a tensor product of NURBS curves in two parametrical directions u and v. The control points form a grid of (nctrlu,nctrlv) points. The other data are like those for a NURBS curve, but need to be specified as a tuple for the (u,v) directions.

  The knot values are only defined upon a multiplicative constant, equal to the largest value. Sensible default values are constructed automatically by a call to the `genKnotVector()` function.

  If no knots are given and no degree is specified, the degree is set to the number of control points - 1 if the curve is blended. If not blended, the degree is not set larger than 3.

  Warning

  This is a class under development!

  `urange`()

    Return the u-parameter range on which the curve is defined.

    Returns a (2,) float array with the minimum and maximum parameter value u for which the curve is defined.

  `vrange`()

    Return the v-parameter range on which the curve is defined.

    Returns a (2,) float array with the minimum and maximum parameter value v for which the curve is defined.

  `bbox`()

    Return the bounding box of the NURBS surface.

  `pointsAt`(u)

    Return the points on the Nurbs surface at given parametric values.

    Parameters:

    - u: (nu,2) shaped float array: nu parametric values (u,v) at which a point is to be placed.

    Returns (nu,3) shaped Coords with nu points at the specified parametric values.

  `derivs`(u, m)

    Return points and derivatives at given parametric values.

    Parameters:

    - u: (nu,2) shaped float array: nu parametric values (u,v) at which the points and derivatives are evaluated.
    - m: tuple of two int values (mu,mv). The points and derivatives up to order mu in u direction and mv in v direction are returned.

    Returns:

    (nu+1,nv+1,nu,3) shaped Coords with nu points at the specified parametric values. The slice (0,0,:,:) contains the points.

  `approx`(ndiv=None, **kargs)

    Return a Quad4 Mesh approximation of the Nurbs surface

    Parameters:

    - ndiv: number of divisions of the parametric space.
  `actor`(**kargs)

    Graphical representation

## 51.2. Functions defined in module plugins.nurbs

`plugins.nurbs.``genKnotVector`(nctrl, degree, blended=True, closed=False)

  Compute sensible knot vector for a Nurbs curve.

  A knot vector is a sequence of non-decreasing parametric values. These values define the knots, i.e. the points where the analytical expression of the Nurbs curve may change. The knot values are only meaningful upon a multiplicative constant, and they are usually normalized to the range [0.0..1.0].

  A Nurbs curve with `nctrl` points and of given `degree` needs a knot vector with `nknots = nctrl+degree+1` values. A `degree` curve needs at least `nctrl = degree+1` control points, and thus at least `nknots = 2*(degree+1)` knot values.

  To make an open curve start and end in its end points, it needs knots with multiplicity `degree+1` at its ends. Thus, for an open blended curve, the default policy is to set the knot values at the ends to 0.0, resp. 1.0, both with multiplicity `degree+1`, and to spread the remaining `nctrl - degree - 1` values equally over the interval.

  For a closed (blended) curve, the knots are equally spread over the interval, all having a multiplicity 1 for maximum continuity of the curve.

  For an open unblended curve, all internal knots get multiplicity `degree`. This results in a curve that is only one time continuously derivable at the knots, thus the curve is smooth, but the curvature may be discontinuous. There is an extra requirement in this case: `nctrl` should be a multiple of `degree` plus 1.

  Returns a KnotVector instance.

  Example:

      >>> print(genKnotVector(7,3))
      KnotVector: 0.0(4), 0.25(1), 0.5(1), 0.75(1), 1.0(4)
      >>> print(genKnotVector(7,3,blended=False))
      KnotVector: 0.0(4), 1.0(3), 2.0(4)
      >>> print(genKnotVector(3,2,closed=True))
      KnotVector: 0.0(1), 0.2(1), 0.4(1), 0.6(1), 0.8(1), 1.0(1)

`plugins.nurbs.``globalInterpolationCurve`(Q, degree=3, strategy=0.5)

  Create a global interpolation NurbsCurve.

  Given an ordered set of points Q, the globalInterpolationCurve is a NURBS curve of the given degree, passing through all the points.

  Returns:

  A NurbsCurve through the given point set. The number of control points is the same as the number of input points.

  Warning

  Currently there is the limitation that two consecutive points should not coincide. If they do, a warning is shown and the double points will be removed.

  The procedure works by computing the control points that will produce a NurbsCurve with the given points occurring at predefined parameter values. The strategy to set this values uses a parameter as exponent. Different values produce (slighly) different curves. Typical values are:

  0.0: equally spaced (not recommended) 0.5: centripetal (default, recommended) 1.0: chord length (often used)

`plugins.nurbs.``NurbsCircle`(O=[0.0, 0.0, 0.0], r=1.0, X=[1.0, 0.0, 0.0], Y=[0.0, 1.0, 0.0], ths=0.0, the=360.0)

  Create a NurbsCurve representing a perfect circle or arc.

  Parameters:

  - O: float (3,): center of the circle
  - r: float: radius
  - X: unit vector in the plane of the circle
  - ‘Y’: unit vector in the plane of the circle and perpendicular to X
  - ths: start angle, measured from the X axis, coungerclockwise in X-Y plane
  - the: end angle, measured from the X axis

  Returns a NurbsCurve that is a perfect circle or arc.

`plugins.nurbs.``toCoords4`(x)

  Convert cartesian coordinates to homogeneous

  x: `Coords`
    Array with cartesian coordinates.

  Returns a Coords4 object corresponding to the input cartesian coordinates.

`plugins.nurbs.``pointsOnBezierCurve`(P, u)

  Compute points on a Bezier curve

  Parameters:

  P is an array with n+1 points defining a Bezier curve of degree n. u is a vector with nu parameter values between 0 and 1.

  Returns:

  An array with the nu points of the Bezier curve corresponding with the specified parametric values. ERROR: currently u is a single paramtric value!

  See also: examples BezierCurve, Casteljau

`plugins.nurbs.``deCasteljau`(P, u)

  Compute points on a Bezier curve using deCasteljau algorithm

  Parameters:

  P is an array with n+1 points defining a Bezier curve of degree n. u is a single parameter value between 0 and 1.

  Returns:

  A list with point sets obtained in the subsequent deCasteljau approximations. The first one is the set of control points, the last one is the point on the Bezier curve.

  This function works with Coords as well as Coords4 points.

`plugins.nurbs.``splitBezierCurve`(P, u)

  Split a Bezier curve at parametric values

  Parameters:

  P is an array with n+1 points defining a Bezier curve of degree n. u is a single parameter value between 0 and 1.

  Returns two arrays of n+1 points, defining the Bezier curves of degree n obtained by splitting the input curve at parametric value u. These results can be used with the control argument of BezierSpline to create the corresponding curve.

`plugins.nurbs.``frenet`(d1, d2, d3=None)

  Compute Frenet vectors, curvature and torsion.

  Parameters:

  - d1: first derivative at npts points of a nurbs curve
  - d2: second derivative at npts points of a nurbs curve
  - d3: (optional) third derivative at npts points of a nurbs curve

  The derivatives of the nurbs curve are normally obtained from `NurbsCurve.deriv()`.

  Returns:

  - T: normalized tangent vector to the curve at npts points
  - N: normalized normal vector to the curve at npts points
  - B: normalized binormal vector to the curve at npts points
  - k: curvature of the curve at npts points
  - t: (only if d3 was specified) torsion of the curve at npts points

  Curvature is found from | d1 x d2 | / |d1|**3