B-Splines surfaces

B-Splines surfaces#

The B-spline surface in \(\mathbb{R}^d\) associated to knots \((T_u, T_v)\) where \(T_u=(u_i)_{0\leqslant i \leqslant n_u + p_u + 1}\) and \(T_v=(v_i)_{0\leqslant i \leqslant n_v + p_v + 1}\), and control points \((\mathbf{P}_{ij})_{ 0 \leqslant i \leqslant n_u, 0 \leqslant j \leqslant n_v}\) is defined by :

\[ \mathcal{C}(u,v) = \sum_{i=0}^{n_u} \sum_{j=0}^{n_v} N_i^{p_u}(u) N_j^{p_v}(v) \textbf{P}_{i,j} \]
# needed imports
import numpy as np
import matplotlib.pyplot as plt

from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
# importing bsplines utilities
from bsplines_utilities import find_span, all_bsplines, point_on_nurbs_surface, point_on_bspline_surface, insert_knot_bspline_surface, insert_knot_nurbs_surface

Example 1:

In this first example, we consider a surface that approximate the function \((u,v) \mapsto \sin(\pi u) \sin(\pi v)\)

def example_1():    
    Tu = [0., 0., 0., 1., 1., 1.]
    pu = 2
    nu = len(Tu) - pu - 1

    Tv = [0., 0., 0., 1., 1., 1.]
    pv = 2
    nv = len(Tv) - pv - 1

    P = np.zeros((nu, nv,1))
    
    gridu = np.linspace(0., 1., nu)
    gridv = np.linspace(0., 1., nv)
    
    for i,u in enumerate(gridu):
        for j,v in enumerate(gridv):
            P[i, j, 0] = np.sin(np.pi*u)*np.sin(np.pi*v)
    
    nx = 101
    xs = np.linspace(0., 1., nx)
    
    ny = 101
    ys = np.linspace(0., 1., ny)
    
    Q = np.zeros((nx, ny, 1))
    for i,x in enumerate(xs):
        for j,y in enumerate(ys):
            Q[i,j,:] = point_on_bspline_surface(Tu, Tv, P, x, y)
            
    fig = plt.figure()
    ax = fig.gca(projection='3d')
  
    X, Y = np.meshgrid(xs, ys)
    Z = Q[:,:,0]

    # Plot the surface.
    surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm,
                           linewidth=0, antialiased=False)
    
    # Add a color bar which maps values to colors.
    fig.colorbar(surf, shrink=0.5, aspect=5)

example_1()

png

Example 2:

This example shows the construction of a square in 2D. The B-Spline surface is created using the function make_square

def make_square(origin=(0,0), length=1.):
    Tu  = [0., 0., 1., 1.]
    Tv  = [0., 0., 1., 1.]
    pu = 1
    pv = 1
    nu = len(Tu) - pu - 1
    nv = len(Tv) - pv - 1
    gridu = np.unique(Tu)
    gridv = np.unique(Tv)

    origin = np.asarray(origin)

    P = np.asarray([[[0.,0.],[0.,1.]],[[1.,0.],[1.,1.]]])
    for i in range(0, 2):
        for j in range(0, 2):
            P[i,j,:] = origin + P[i,j,:]*length

    return (Tu, Tv), (pu, pv), P
def plot_surface(knots, degrees, P, weights=None, Nu=101, Nv=101, color='b'):
    Tu, Tv = knots
    pu, pv = degrees

    nu = len(Tu) - pu - 1
    nv = len(Tv) - pv - 1
    gridu = np.unique(Tu)
    gridv = np.unique(Tv)

    us = np.linspace(0., 1., Nu)
    vs = np.linspace(0., 1., Nv)

    # ...
    Q = np.zeros((Nv, 2))
    if weights is None:
        for i,u in enumerate(gridu):
            for j,v in enumerate(vs):
                Q[j,:] = point_on_bspline_surface(Tu, Tv, P, u, v)
                
            plt.plot(Q[:,0], Q[:,1], '-'+color)

    else:
        for i,u in enumerate(gridu):
            for j,v in enumerate(vs):
                Q[j,:] = point_on_nurbs_surface(Tu, Tv, P, weights, u, v)
                
            plt.plot(Q[:,0], Q[:,1], '-'+color)
    # ...

    # ...
    Q = np.zeros((Nu, 2))
    if weights is None:
        for j,v in enumerate(gridv):
            for i,u in enumerate(us):
                Q[i,:] = point_on_bspline_surface(Tu, Tv, P, u, v)

            plt.plot(Q[:,0], Q[:,1], '-'+color)                

    else:
        for j,v in enumerate(gridv):
            for i,u in enumerate(us):
                Q[i,:] = point_on_nurbs_surface(Tu, Tv, P, weights, u, v)
                
            plt.plot(Q[:,0], Q[:,1], '-'+color)                
    # ...
# ...

def example_2():    
    knots, degrees, P = make_square(origin=(0,0), 
                                    length=1.)
    
    t = 0.5
    Tu, Tv, pu, pv, P = insert_knot_bspline_surface(*knots, *degrees, P, t, 
                                times=1, 
                                axis=None)
    
    plot_surface((Tu, Tv), (pu, pv), P, 
                 weights=None, 
                 Nu=101, Nv=101, 
                 color='b')
    
example_2()

png

Example 3:

The following example shows how to create and plots a circle using NURBS.

We shall need the following function:

def make_circle(center=(0.,0.), radius=1.):
    Tu  = [0., 0., 0., 1, 1., 1.]
    Tv  = [0., 0., 0., 1, 1., 1.]
    pu = 2
    pv = 2
    nu = len(Tu) - pu - 1
    nv = len(Tv) - pv - 1
    gridu = np.unique(Tu)
    gridv = np.unique(Tv)


    s = 1./np.sqrt(2)
    P          = np.zeros((nu,nv,2))
    P[0,0,:]   = np.asarray([-s   , -s   ])
    P[1,0,:]   = np.asarray([-2*s , 0.   ])
    P[2,0,:]   = np.asarray([-s   , s    ])
    P[0,1,:]   = np.asarray([0.   , -2*s ])
    P[1,1,:]   = np.asarray([0.   , 0.0  ])
    P[2,1,:]   = np.asarray([0.   , 2*s  ])
    P[0,2,:]   = np.asarray([s    , -s   ])
    P[1,2,:]   = np.asarray([2*s  , 0.   ])
    P[2,2,:]   = np.asarray([s    , s    ])

    P *= radius
    P[:,:,0] += center[0]
    P[:,:,1] += center[1]

    W       = np.zeros((3,3))
    W[0,0]  = 1.
    W[1,0]  = s
    W[2,0]  = 1.
    W[0,1]  = s
    W[1,1]  = 1.
    W[2,1]  = s
    W[0,2]  = 1.
    W[1,2]  = s
    W[2,2]  = 1.

    return (Tu, Tv), (pu, pv), P, W

def example_3():    
    knots, degrees, P, W = make_circle(center=(0.,0.), radius=1.)
    
    t = 0.5
    Tu, Tv, pu, pv, P, W = insert_knot_nurbs_surface(*knots, *degrees, P, W, t, 
                                times=1, 
                                axis=None)
    
    plot_surface((Tu, Tv), (pu, pv), P, 
                 weights=W, 
                 Nu=101, Nv=101, 
                 color='b')
    
example_3()

png