Example #1
0
 def test_greville(self):
     b = BSplineBasis(4, [0, 0, 0, 0, 1, 2, 3, 3, 3, 3])
     self.assertAlmostEqual(b.greville(0), 0.0)
     self.assertAlmostEqual(b.greville(1), 1.0 / 3.0)
     self.assertAlmostEqual(b.greville(2), 1.0)
     self.assertAlmostEqual(b.greville(),
                            [0.0, 1.0 / 3.0, 1.0, 2.0, 8.0 / 3.0, 3.0])
Example #2
0
    def rebuild(self, p, n):
        """  Creates an approximation to this curve by resampling it using a
        uniform knot vector of order *p* with *n* control points.

        :param int p: Polynomial discretization order
        :param int n: Number of control points
        :return: A new approximate curve
        :rtype: Curve
        """
        # establish uniform open knot vector
        knot = [0] * p + list(range(1, n - p + 1)) + [n - p + 1] * p
        basis = BSplineBasis(p, knot)
        # set parametric range of the new basis to be the same as the old one
        basis.normalize()
        t0 = self.bases[0].start()
        t1 = self.bases[0].end()
        basis *= (t1 - t0)
        basis += t0
        # fetch evaluation points and solve interpolation problem
        t = basis.greville()
        N = basis.evaluate(t, sparse=True)
        controlpoints = splinalg.spsolve(N, self.evaluate(t))

        # return new resampled curve
        return Curve(basis, controlpoints)
Example #3
0
    def rebuild(self, p, n):
        """Creates an approximation to this curve by resampling it using a
        uniform knot vector of order *p* with *n* control points.

        :param int p: Polynomial discretization order
        :param int n: Number of control points
        :return: A new approximate curve
        :rtype: Curve
        """
        # establish uniform open knot vector
        knot = [0] * p + list(range(1, n - p + 1)) + [n - p + 1] * p
        basis = BSplineBasis(p, knot)
        # set parametric range of the new basis to be the same as the old one
        basis.normalize()
        t0 = self.bases[0].start()
        t1 = self.bases[0].end()
        basis *= t1 - t0
        basis += t0
        # fetch evaluation points and solve interpolation problem
        t = basis.greville()
        N = basis.evaluate(t)
        controlpoints = np.linalg.solve(N, self.evaluate(t))

        # return new resampled curve
        return Curve(basis, controlpoints)
Example #4
0
def loft(*curves):
    if len(curves) == 1:
        curves = curves[0]

    # clone input, so we don't change those references
    # make sure everything has the same dimension since we need to compute length
    curves = [c.clone().set_dimension(3) for c in curves]
    if len(curves)==2:
        return edge_curves(curves)
    elif len(curves)==3:
        # can't do cubic spline interpolation, so we'll do quadratic
        basis2 = BSplineBasis(3)
        dist  = basis2.greville()
    else:
        x = [c.center() for c in curves]

        # create knot vector from the euclidian length between the curves
        dist = [0]
        for (x1,x0) in zip(x[1:],x[:-1]):
            dist.append(dist[-1] + np.linalg.norm(x1-x0))

        # using "free" boundary condition by setting N'''(u) continuous at second to last and second knot
        knot = [dist[0]]*4 + dist[2:-2] + [dist[-1]]*4
        basis2 = BSplineBasis(4, knot)

    n = len(curves)
    for i in range(n):
        for j in range(i+1,n):
            Curve.make_splines_identical(curves[i], curves[j])

    basis1 = curves[0].bases[0]
    m      = basis1.num_functions()
    u      = basis1.greville() # parametric interpolation points
    v      = dist              # parametric interpolation points

    # compute matrices
    Nu     = basis1(u)
    Nv     = basis2(v)
    Nu_inv = np.linalg.inv(Nu)
    Nv_inv = np.linalg.inv(Nv)

    # compute interpolation points in physical space
    x      = np.zeros((m,n, curves[0][0].size))
    for i in range(n):
        x[:,i,:] = Nu * curves[i].controlpoints

    # solve interpolation problem
    cp = np.tensordot(Nv_inv, x,  axes=(1,1))
    cp = np.tensordot(Nu_inv, cp, axes=(1,1))

    # re-order controlpoints so they match up with Surface constructor
    cp = cp.transpose((1, 0, 2))
    cp = cp.reshape(n*m, cp.shape[2])

    return Surface(basis1, basis2, cp, curves[0].rational)
Example #5
0
def loft(*curves):
    if len(curves) == 1:
        curves = curves[0]

    # clone input, so we don't change those references
    # make sure everything has the same dimension since we need to compute length
    curves = [c.clone().set_dimension(3) for c in curves]
    if len(curves)==2:
        return edge_curves(curves)
    elif len(curves)==3:
        # can't do cubic spline interpolation, so we'll do quadratic
        basis2 = BSplineBasis(3)
        dist  = basis2.greville()
    else:
        x = [c.center() for c in curves]

        # create knot vector from the euclidian length between the curves
        dist = [0]
        for (x1,x0) in zip(x[1:],x[:-1]):
            dist.append(dist[-1] + np.linalg.norm(x1-x0))

        # using "free" boundary condition by setting N'''(u) continuous at second to last and second knot
        knot = [dist[0]]*4 + dist[2:-2] + [dist[-1]]*4
        basis2 = BSplineBasis(4, knot)

    n = len(curves)
    for i in range(n):
        for j in range(i+1,n):
            Curve.make_splines_identical(curves[i], curves[j])
    
    basis1 = curves[0].bases[0]
    m      = basis1.num_functions()
    u      = basis1.greville() # parametric interpolation points
    v      = dist              # parametric interpolation points
    
    # compute matrices
    Nu     = basis1(u)
    Nv     = basis2(v)
    Nu_inv = np.linalg.inv(Nu)
    Nv_inv = np.linalg.inv(Nv)

    # compute interpolation points in physical space
    x      = np.zeros((m,n, curves[0][0].size))
    for i in range(n):
        x[:,i,:] = Nu * curves[i].controlpoints

    # solve interpolation problem
    cp = np.tensordot(Nv_inv, x,  axes=(1,1))
    cp = np.tensordot(Nu_inv, cp, axes=(1,1))

    # re-order controlpoints so they match up with Surface constructor
    cp = cp.transpose((1, 0, 2))
    cp = cp.reshape(n*m, cp.shape[2])

    return Surface(basis1, basis2, cp, curves[0].rational)
Example #6
0
 def test_bug44(self):
     # https://github.com/sintefmath/Splipy/issues/44
     b = BSplineBasis(4, [ 0.        , 0.        , 0.        , 0.        , 0.01092385, 0.02200375,
                           0.03316167, 0.04435861, 0.05526295, 0.0663331 , 0.07748615, 0.08868065,
                           0.09989588, 0.11080936, 0.12188408, 0.13303942, 0.14423507, 0.15545087,
                           0.16636463, 0.1774395 , 0.1885949 , 0.19979059, 0.2       , 0.2       ,
                           0.2       , 0.2     ])
     grvl = b.greville()
     # last greville point is 0.20000000000000004, and technically outside domain [0,.2]
     self.assertAlmostEqual(grvl[-1], 0.2)
     # should snap to 0.2 and evaluate to 1 for the last function
     self.assertAlmostEqual(b(grvl[-1])[-1,-1], 1.0)
Example #7
0
 def test_bug44(self):
     # https://github.com/sintefmath/Splipy/issues/44
     b = BSplineBasis(4, [ 0.        , 0.        , 0.        , 0.        , 0.01092385, 0.02200375,
                           0.03316167, 0.04435861, 0.05526295, 0.0663331 , 0.07748615, 0.08868065,
                           0.09989588, 0.11080936, 0.12188408, 0.13303942, 0.14423507, 0.15545087,
                           0.16636463, 0.1774395 , 0.1885949 , 0.19979059, 0.2       , 0.2       ,
                           0.2       , 0.2     ])
     grvl = b.greville()
     # last greville point is 0.20000000000000004, and technically outside domain [0,.2]
     self.assertAlmostEqual(grvl[-1], 0.2)
     # should snap to 0.2 and evaluate to 1 for the last function
     self.assertAlmostEqual(b(grvl[-1])[-1,-1], 1.0)
Example #8
0
 def test_curve_interpolation(self):
     basis = BSplineBasis(4, [0, 0, 0, 0, .3, .9, 1, 1, 1, 1])
     t = np.array(basis.greville())
     # create the mapping (x,y,z)=(t^2, 1-t, t^3+2*t)
     x_pts = np.zeros((len(t), 3))
     x_pts[:, 0] = t * t
     x_pts[:, 1] = 1 - t
     x_pts[:, 2] = t * t * t + 2 * t
     crv = CurveFactory.interpolate(x_pts, basis)
     self.assertEqual(crv.order(0), 4)
     self.assertAlmostEqual(crv(.4)[0], .4**2)  # x=t^2
     self.assertAlmostEqual(crv(.4)[1], 1 - .4)  # y=1-t
     self.assertAlmostEqual(crv(.4)[2], .4**3 + 2 * .4)  # z=t^3+2t
     self.assertAlmostEqual(crv(.5)[0], .5**2)  # x=t^2
     self.assertAlmostEqual(crv(.5)[1], 1 - .5)  # y=1-t
     self.assertAlmostEqual(crv(.5)[2], .5**3 + 2 * .5)  # z=t^3+2t
Example #9
0
 def test_curve_interpolation(self):
     basis = BSplineBasis(4, [0, 0, 0, 0, .3, .9, 1, 1, 1, 1])
     t = np.array(basis.greville())
     # create the mapping (x,y,z)=(t^2, 1-t, t^3+2*t)
     x_pts = np.zeros((len(t), 3))
     x_pts[:, 0] = t * t
     x_pts[:, 1] = 1 - t
     x_pts[:, 2] = t * t * t + 2 * t
     crv = cf.interpolate(x_pts, basis)
     self.assertEqual(crv.order(0), 4)
     self.assertAlmostEqual(crv(.4)[0], .4**2)  # x=t^2
     self.assertAlmostEqual(crv(.4)[1], 1 - .4)  # y=1-t
     self.assertAlmostEqual(crv(.4)[2], .4**3 + 2 * .4)  # z=t^3+2t
     self.assertAlmostEqual(crv(.5)[0], .5**2)  # x=t^2
     self.assertAlmostEqual(crv(.5)[1], 1 - .5)  # y=1-t
     self.assertAlmostEqual(crv(.5)[2], .5**3 + 2 * .5)  # z=t^3+2t
Example #10
0
    def test_lower_order(self):
        basis = BSplineBasis(4, [0,0,0,0,.2, .3, .3, .6, .9, 1,1,1,1])
        interp_pts = basis.greville()
        x = [[t*(1-t), t**2] for t in interp_pts]
        
        crv = CurveFactory.interpolate(x, basis) # function in space, exact representation kept

        crv2 = crv.lower_order(1) # still in space, crv2 is *also* exact

        t = np.linspace(0,1, 13)
        self.assertTrue(np.allclose( crv(t), crv2(t)) )
        self.assertEqual(crv.order(0),  4)
        self.assertEqual(crv2.order(0), 3)
        self.assertEqual(crv.continuity(0.3),  1)
        self.assertEqual(crv.continuity(0.6),  2)
        self.assertEqual(crv2.continuity(0.3), 1)
        self.assertEqual(crv2.continuity(0.6), 1)
Example #11
0
    def test_lower_order(self):
        b = BSplineBasis(4, [0, 0, 0, 0, .2, .3, .3, .6, .9, 1, 1, 1, 1])
        t = b.greville()
        Y, X, Z = np.meshgrid(t, t, t)
        cp = np.zeros((len(t), len(t), len(t), 3))
        cp[..., 0] = X * (1 - Y)
        cp[..., 1] = X * X
        cp[..., 2] = Z**2 + 2

        vol = vf.interpolate(cp, [b, b, b])
        vol2 = vol.lower_order(1)  # still in space, vol2 is *also* exact
        u = np.linspace(0, 1, 5)
        v = np.linspace(0, 1, 6)
        w = np.linspace(0, 1, 7)
        self.assertTrue(np.allclose(vol(u, v, w), vol2(u, v, w)))
        self.assertTupleEqual(vol.order(), (4, 4, 4))
        self.assertTupleEqual(vol2.order(), (3, 3, 3))
Example #12
0
    def test_lower_order(self):
        b = BSplineBasis(4, [0,0,0,0,.2, .3, .3, .6, .9, 1,1,1,1])
        t = b.greville()
        Y, X, Z = np.meshgrid(t,t,t)
        cp = np.zeros((len(t), len(t), len(t), 3))
        cp[...,0] = X*(1-Y)
        cp[...,1] = X*X
        cp[...,2] = Z**2 + 2

        vol  = VolumeFactory.interpolate(cp, [b,b,b])
        vol2 = vol.lower_order(1) # still in space, vol2 is *also* exact
        u = np.linspace(0,1, 5)
        v = np.linspace(0,1, 6)
        w = np.linspace(0,1, 7)
        self.assertTrue(np.allclose( vol(u,v,w), vol2(u,v,w) ))
        self.assertTupleEqual(vol.order(), (4,4,4))
        self.assertTupleEqual(vol2.order(), (3,3,3))
Example #13
0
    def test_lower_order(self):
        basis = BSplineBasis(4, [0, 0, 0, 0, .2, .3, .3, .6, .9, 1, 1, 1, 1])
        interp_pts = basis.greville()
        x = [[t * (1 - t), t**2] for t in interp_pts]

        crv = cf.interpolate(
            x, basis)  # function in space, exact representation kept

        crv2 = crv.lower_order(1)  # still in space, crv2 is *also* exact

        t = np.linspace(0, 1, 13)
        self.assertTrue(np.allclose(crv(t), crv2(t)))
        self.assertEqual(crv.order(0), 4)
        self.assertEqual(crv2.order(0), 3)
        self.assertEqual(crv.continuity(0.3), 1)
        self.assertEqual(crv.continuity(0.6), 2)
        self.assertEqual(crv2.continuity(0.3), 1)
        self.assertEqual(crv2.continuity(0.6), 1)
Example #14
0
def camber(M, P, order=5):
    """ Create the NACA centerline used for wing profiles. This is given as
    an exact quadratic piecewise polynomial y(x),
    see http://airfoiltools.com/airfoil/naca4digit. The method will produce
    one of two representations: For order<5 it will create x(t)=t and
    for order>4 it will create x(t) as qudratic in t and stretched towards the
    endpointspoints creating a more optimized parametrization.
    :param M: Max camber height (y) given as percentage 0% to 9% of length
    :type  M: Int 0<M<10
    :param P: Max camber position (x) given as percentage 0% to 90% of length
    :type  P: Int 0<P<10
    :return : Exact centerline representation
    :rtype  : Curve
    """
    # parametrized by x=t or x="t^2" if order>4
    M = M / 100.0
    P = P / 10.0
    basis = BSplineBasis(order)
    # basis.insert_knot([P]*(order-2)) # insert a C1-knot
    for i in range(order - 2):
        basis.insert_knot(P)

    t = basis.greville()
    n = len(t)
    x = np.zeros((n, 2))
    for i in range(n):
        if t[i] <= P:
            if order > 4:
                x[i, 0] = t[i]**2 / P
            else:
                x[i, 0] = t[i]
            x[i, 1] = M / P / P * (2 * P * x[i, 0] - x[i, 0] * x[i, 0])
        else:
            if order > 4:
                x[i, 0] = (t[i]**2 - 2 * t[i] + P) / (P - 1)
            else:
                x[i, 0] = t[i]
            x[i, 1] = M / (1 - P) / (1 - P) * (1 - 2 * P + 2 * P * x[i, 0] - x[i, 0] * x[i, 0])
    N = basis.evaluate(t)
    controlpoints = np.linalg.solve(N, x)
    return Curve(basis, controlpoints)
Example #15
0
def loft(*surfaces):
    if len(surfaces) == 1:
        surfaces = surfaces[0]

    # clone input, so we don't change those references
    # make sure everything has the same dimension since we need to compute length
    surfaces = [s.clone().set_dimension(3) for s in surfaces]
    if len(surfaces) == 2:
        return SurfaceFactory.edge_curves(surfaces)
    elif len(surfaces) == 3:
        # can't do cubic spline interpolation, so we'll do quadratic
        basis3 = BSplineBasis(3)
        dist = basis3.greville()
    else:
        x = [s.center() for s in surfaces]

        # create knot vector from the euclidian length between the surfaces
        dist = [0]
        for (x1, x0) in zip(x[1:], x[:-1]):
            dist.append(dist[-1] + np.linalg.norm(x1 - x0))

        # using "free" boundary condition by setting N'''(u) continuous at second to last and second knot
        knot = [dist[0]] * 4 + dist[2:-2] + [dist[-1]] * 4
        basis3 = BSplineBasis(4, knot)

    n = len(surfaces)
    for i in range(n):
        for j in range(i + 1, n):
            Surface.make_splines_identical(surfaces[i], surfaces[j])

    basis1 = surfaces[0].bases[0]
    basis2 = surfaces[0].bases[1]
    m1 = basis1.num_functions()
    m2 = basis2.num_functions()
    dim = len(surfaces[0][0])
    u = basis1.greville()  # parametric interpolation points
    v = basis2.greville()
    w = dist

    # compute matrices
    Nu = basis1(u)
    Nv = basis2(v)
    Nw = basis3(w)
    Nu_inv = np.linalg.inv(Nu)
    Nv_inv = np.linalg.inv(Nv)
    Nw_inv = np.linalg.inv(Nw)

    # compute interpolation points in physical space
    x = np.zeros((m1, m2, n, dim))
    for i in range(n):
        tmp = np.tensordot(Nv, surfaces[i].controlpoints, axes=(1, 1))
        x[:, :, i, :] = np.tensordot(Nu, tmp, axes=(1, 1))

    # solve interpolation problem
    cp = np.tensordot(Nw_inv, x, axes=(1, 2))
    cp = np.tensordot(Nv_inv, cp, axes=(1, 2))
    cp = np.tensordot(Nu_inv, cp, axes=(1, 2))

    # re-order controlpoints so they match up with Surface constructor
    cp = np.reshape(cp.transpose((2, 1, 0, 3)), (m1 * m2 * n, dim))

    return Volume(basis1, basis2, basis3, cp, surfaces[0].rational)
Example #16
0
def loft(*curves):
    """  Generate a surface by lofting a series of curves

    The resulting surface is interpolated at all input curves and a smooth transition
    between these curves is computed as a cubic spline interpolation in the lofting
    direction. In the case that insufficient curves are provided as input (less than 4
    curves), then a quadratic or linear interpolation is performed.

    Note that the order of input curves matter as they will be interpolated in this
    particular order. Also note that the curves need to be parametrized in the same
    direction, otherwise you will encounter self-intersecting result surface as it is
    wrapping in on itself.

    :param Curve curves:  A sequence of curves to be lofted
    :return: Lofted surface
    :rtype: Surface

    Examples:

    .. code:: python

        from splipy import curve_factory, surface_factory
        crv1 = curve_factory.circle(r=1)
        crv2 = 1.5 * crv1 + [0,0,1]
        crv3 = 1.0 * crv1 + [0,0,2]
        crv4 = 1.3 * crv1 + [0,0,4]
        srf = surface_factory.loft(crv1, crv2, crv3, crv4)

        # alternatively you can provide all input curves as a list
        all_my_curves = [crv1, crv2, crv3, crv4]
        srf = surface_factory.loft(all_my_curves)

    """
    if len(curves) == 1:
        curves = curves[0]

    # clone input, so we don't change those references
    # make sure everything has the same dimension since we need to compute length
    curves = [c.clone().set_dimension(3) for c in curves]
    if len(curves) == 2:
        return edge_curves(curves)
    elif len(curves) == 3:
        # can't do cubic spline interpolation, so we'll do quadratic
        basis2 = BSplineBasis(3)
        dist = basis2.greville()
    else:
        x = [c.center() for c in curves]

        # create knot vector from the euclidian length between the curves
        dist = [0]
        for (x1, x0) in zip(x[1:], x[:-1]):
            dist.append(dist[-1] + np.linalg.norm(x1 - x0))

        # using "free" boundary condition by setting N'''(u) continuous at second to last and second knot
        knot = [dist[0]] * 4 + dist[2:-2] + [dist[-1]] * 4
        basis2 = BSplineBasis(4, knot)

    n = len(curves)
    for i in range(n):
        for j in range(i + 1, n):
            Curve.make_splines_identical(curves[i], curves[j])

    basis1 = curves[0].bases[0]
    m = basis1.num_functions()
    u = basis1.greville()  # parametric interpolation points
    v = dist  # parametric interpolation points

    # compute matrices
    Nu = basis1(u)
    Nv = basis2(v)
    Nu_inv = np.linalg.inv(Nu)
    Nv_inv = np.linalg.inv(Nv)

    # compute interpolation points in physical space
    x = np.zeros((m, n, curves[0][0].size))
    for i in range(n):
        x[:, i, :] = Nu @ curves[i].controlpoints

    # solve interpolation problem
    cp = np.tensordot(Nv_inv, x, axes=(1, 1))
    cp = np.tensordot(Nu_inv, cp, axes=(1, 1))

    # re-order controlpoints so they match up with Surface constructor
    cp = cp.transpose((1, 0, 2))
    cp = cp.reshape(n * m, cp.shape[2])

    return Surface(basis1, basis2, cp, curves[0].rational)
Example #17
0
 def test_greville(self):
     b = BSplineBasis(4, [0, 0, 0, 0, 1, 2, 3, 3, 3, 3])
     self.assertAlmostEqual(b.greville(0), 0.0)
     self.assertAlmostEqual(b.greville(1), 1.0 / 3.0)
     self.assertAlmostEqual(b.greville(2), 1.0)
     self.assertAlmostEqual(b.greville(), [0.0, 1.0/3.0, 1.0, 2.0, 8.0/3.0, 3.0])
Example #18
0
def loft(*surfaces):
    if len(surfaces) == 1:
        surfaces = surfaces[0]

    # clone input, so we don't change those references
    # make sure everything has the same dimension since we need to compute length
    surfaces = [s.clone().set_dimension(3) for s in surfaces]
    if len(surfaces)==2:
        return SurfaceFactory.edge_curves(surfaces)
    elif len(surfaces)==3:
        # can't do cubic spline interpolation, so we'll do quadratic
        basis3 = BSplineBasis(3)
        dist  = basis3.greville()
    else:
        x = [s.center() for s in surfaces]

        # create knot vector from the euclidian length between the surfaces
        dist = [0]
        for (x1,x0) in zip(x[1:],x[:-1]):
            dist.append(dist[-1] + np.linalg.norm(x1-x0))

        # using "free" boundary condition by setting N'''(u) continuous at second to last and second knot
        knot = [dist[0]]*4 + dist[2:-2] + [dist[-1]]*4
        basis3 = BSplineBasis(4, knot)

    n = len(surfaces)
    for i in range(n):
        for j in range(i+1,n):
            Surface.make_splines_identical(surfaces[i], surfaces[j])

    basis1 = surfaces[0].bases[0]
    basis2 = surfaces[0].bases[1]
    m1     = basis1.num_functions()
    m2     = basis2.num_functions()
    dim    = len(surfaces[0][0])
    u      = basis1.greville() # parametric interpolation points
    v      = basis2.greville()
    w      = dist

    # compute matrices
    Nu     = basis1(u)
    Nv     = basis2(v)
    Nw     = basis3(w)
    Nu_inv = np.linalg.inv(Nu)
    Nv_inv = np.linalg.inv(Nv)
    Nw_inv = np.linalg.inv(Nw)

    # compute interpolation points in physical space
    x      = np.zeros((m1,m2,n, dim))
    for i in range(n):
        tmp        = np.tensordot(Nv, surfaces[i].controlpoints, axes=(1,1))
        x[:,:,i,:] = np.tensordot(Nu, tmp                      , axes=(1,1))

    # solve interpolation problem
    cp = np.tensordot(Nw_inv, x,  axes=(1,2))
    cp = np.tensordot(Nv_inv, cp, axes=(1,2))
    cp = np.tensordot(Nu_inv, cp, axes=(1,2))

    # re-order controlpoints so they match up with Surface constructor
    cp = np.reshape(cp.transpose((2, 1, 0, 3)), (m1*m2*n, dim))

    return Volume(basis1, basis2, basis3, cp, surfaces[0].rational)
Example #19
0
def loft(*surfaces):
    """  Generate a volume by lofting a series of surfaces

    The resulting volume is interpolated at all input surfaces and a smooth transition
    between these surfaces is computed as a cubic spline interpolation in the lofting
    direction. In the case that insufficient surfaces are provided as input (less than 4
    surfaces), then a quadratic or linear interpolation is performed.

    Note that the order of input surfaces matter as they will be interpolated in this
    particular order. Also note that the surfaces need to be parametrized in the same
    direction, otherwise you will encounter self-intersecting result volume as it is
    wrapping in on itself.

    :param Surfaces surfaces:  A sequence of surfaces to be lofted
    :return: Lofted volume
    :rtype: Volume

    Examples:

    .. code:: python

        from splipy import surface_factory, volume_factory
        srf1 = surface_factory.disc(r=1)
        srf2 = 1.5 * srf1 + [0,0,1]
        srf3 = 1.0 * srf1 + [0,0,2]
        srf4 = 1.3 * srf1 + [0,0,4]
        vol = volume_factory.loft(srf1, srf2, srf3, srf4)

        # alternatively you can provide all input curves as a list
        all_my_surfaces = [srf1, srf2, srf3, srf4]
        vol = volume_factory.loft(all_my_surfaces)

    """
    if len(surfaces) == 1:
        surfaces = surfaces[0]

    # clone input, so we don't change those references
    # make sure everything has the same dimension since we need to compute length
    surfaces = [s.clone().set_dimension(3) for s in surfaces]
    if len(surfaces) == 2:
        return SurfaceFactory.edge_curves(surfaces)
    elif len(surfaces) == 3:
        # can't do cubic spline interpolation, so we'll do quadratic
        basis3 = BSplineBasis(3)
        dist = basis3.greville()
    else:
        x = [s.center() for s in surfaces]

        # create knot vector from the euclidian length between the surfaces
        dist = [0]
        for (x1, x0) in zip(x[1:], x[:-1]):
            dist.append(dist[-1] + np.linalg.norm(x1 - x0))

        # using "free" boundary condition by setting N'''(u) continuous at second to last and second knot
        knot = [dist[0]] * 4 + dist[2:-2] + [dist[-1]] * 4
        basis3 = BSplineBasis(4, knot)

    n = len(surfaces)
    for i in range(n):
        for j in range(i + 1, n):
            Surface.make_splines_identical(surfaces[i], surfaces[j])

    basis1 = surfaces[0].bases[0]
    basis2 = surfaces[0].bases[1]
    m1 = basis1.num_functions()
    m2 = basis2.num_functions()
    dim = len(surfaces[0][0])
    u = basis1.greville()  # parametric interpolation points
    v = basis2.greville()
    w = dist

    # compute matrices
    Nu = basis1(u)
    Nv = basis2(v)
    Nw = basis3(w)
    Nu_inv = np.linalg.inv(Nu)
    Nv_inv = np.linalg.inv(Nv)
    Nw_inv = np.linalg.inv(Nw)

    # compute interpolation points in physical space
    x = np.zeros((m1, m2, n, dim))
    for i in range(n):
        tmp = np.tensordot(Nv, surfaces[i].controlpoints, axes=(1, 1))
        x[:, :, i, :] = np.tensordot(Nu, tmp, axes=(1, 1))

    # solve interpolation problem
    cp = np.tensordot(Nw_inv, x, axes=(1, 2))
    cp = np.tensordot(Nv_inv, cp, axes=(1, 2))
    cp = np.tensordot(Nu_inv, cp, axes=(1, 2))

    # re-order controlpoints so they match up with Surface constructor
    cp = np.reshape(cp.transpose((2, 1, 0, 3)), (m1 * m2 * n, dim))

    return Volume(basis1, basis2, basis3, cp, surfaces[0].rational)
Example #20
0
def fit(x, t0, t1, rtol=1e-4, atol=0.0):
    """  Computes an interpolation for a parametric curve up to a specified tolerance.
    The method will iteratively refine parts where needed resulting in a non-uniform
    knot vector with as optimized knot locations as possible.

    :param function x: callable function which takes as input a vector of evaluation
        points t and gives as output a matrix x where x[i,j] is component j evaluated
        at point t[i]
    :param float t0: start of parametric domain
    :param float t1: end of parametric domain
    :param float rtol: relative tolerance for stopping criterium. It is defined
        to be ||e||_L2 / D, where D is the length of the curve and ||e||_L2 is the
        L2-error (see Curve.error)
    :param float atol: absolute tolerance for stopping criterium. It is defined to
        be the maximal distance between the curve approximation and the exact curve
    :return: Curve Non-uniform cubic B-spline curve

    Examples:

    .. code:: python

        import numpy as np
        import splipy.curve_factory as curve_factory

        # gives a B-spline approximation to the circle with arclength parametrization
        # unlike curve_factory.circle which is exact, but not arclength
        def arclength_circle(t):
            return np.array( [np.cos(t), np.sin(t)] ).T
        crv = curve_factory.fit(arclength_circle, 0, 2*np.pi)
        print crv

        # approximates a difficult function with wild behaviour around t=0, but
        # this is overcome by a higher knot density around this point
        def one_over_t(t):
            eps = 1e-8 # to avoid 1/0 we add a small epsilon
            return np.array( [t, 1.0/(t+eps)] ).T
        crv = curve_factory.fit(one_over_t, 0, 1, rtol=1e-6)
        print crv # first knot span is ~1e-9, last knot is ~1e-1

        # one can specify the target curve in terms of existing Curve objects
        crv = curve_factory.circle(r=1)     # Curve-object, quadratic NURBS
        def move_along_tangent(t):
            return crv(t) + crv.tangent(t)  # can evaluate curve or its derivatives
        # fit() will create a B-spline approximation using non-uniform refinement
        crv2 = curve_factory.fit(move_along_tangent, crv.start(0), crv.end(0))
    """

    b = BSplineBasis(4, [t0,t0,t0,t0, t1,t1,t1,t1])
    t = np.array(b.greville())
    crv = interpolate(x(t), b, t)
    (err2, maxerr) = crv.error(x)
    # polynomial input (which can be exactly represented) only use one knot span
    if maxerr < 1e-13:
        return crv

    # for all other curves, start with 4 knot spans
    knot_vector = [t0,t0,t0,t0] + [i/5.0*(t1-t0)+t0 for i in range(1,5)] + [t1,t1,t1,t1]
    b = BSplineBasis(4, knot_vector)
    t = np.array(b.greville())
    crv = interpolate(x(t), b, t)
    (err2, maxerr) = crv.error(x)
    # this is technically false since we need the length of the target function *x*
    # and not our approximation *crv*, but we don't have the derivative of *x*, so
    # we can't compute it. This seems like a healthy compromise
    length = crv.length()
    while np.sqrt(np.sum(err2))/length > rtol and maxerr > atol:
        knot_span    = crv.knots(0) # knot vector without multiplicities
        target_error = (rtol*length)**2 / len(err2) # equidistribute error among all knot spans
        refinements  = []
        for i in range(len(err2)):
            # figure out how many new knots we require in this knot interval:
            # if we converge with *scale* and want an error of *target_error*
            # |e|^2 * (1/n)^scale = target_error^2

            conv_order = 4                   # cubic interpolateion is order=4
            square_conv_order = 2*conv_order # we are computing with square of error
            scale = square_conv_order + 4    # don't want to converge too quickly in case of highly non-uniform mesh refinement is required
            n = int(np.ceil(np.exp((np.log(err2[i]) - np.log(target_error))/scale)))

            # add *n* new interior knots to this knot span
            new_knots = np.linspace(knot_span[i], knot_span[i+1], n+1)
            knot_vector = knot_vector + list(new_knots[1:-1])

        # build new refined knot vector
        knot_vector.sort()
        b = BSplineBasis(4, knot_vector)
        # do interpolation and return result
        t = np.array(b.greville())
        crv = interpolate(x(t), b, t)
        (err2, maxerr) = crv.error(x)
        length = crv.length()

    return crv
Example #21
0
def cubic_curve(x, boundary=Boundary.FREE, t=None, tangents=None):
    """cubic_curve(x, [boundary=Boundary.FREE], [t=None], [tangents=None])

    Perform cubic spline interpolation on a provided basis.

    The valid boundary conditions are enumerated in :class:`Boundary`. The
    meaning of the `tangents` parameter depends on the specified boundary
    condition:

    - `TANGENT`: two points,
    - `TANGENTNATURAL`: one point,
    - `HERMITE`: *n* points

    :param matrix-like x: Matrix *X[i,j]* of interpolation points *x_i* with
        components *j*
    :param int boundary: Any value from :class:`Boundary`.
    :param array-like t: parametric values at interpolation points, defaults
        to Euclidean distance between evaluation points
    :param matrix-like tangents: Tangent information according to the boundary
        conditions.
    :return: Interpolated curve
    :rtype: Curve
    """

    # special case periodic curves to both allow repeated start/end or not
    if boundary == Boundary.PERIODIC and (
       np.allclose(x[0,:], x[-1,:],
                   rtol = state.controlpoint_relative_tolerance,
                   atol = state.controlpoint_absolute_tolerance)):
        # count the seam (start/end point) as only one point, otherwise the
        # interpolation matrix will have two identical rows corresponding to
        # this point
        x = x[:-1,:]
        if t is not None:
            t = t[:-1]

    n = len(x)
    if t is None:
        t = [0.0]
        for (x0,x1) in zip(x[:-1,:], x[1:,:]):
            # eucledian distance between two consecutive points 
            dist = np.linalg.norm(np.array(x1)-np.array(x0))
            t.append(t[-1]+dist)

    # modify knot vector for chosen boundary conditions
    knot = [t[0]]*3 + list(t) + [t[-1]]*3
    if boundary == Boundary.FREE:
        del knot[-5]
        del knot[4]
    elif boundary == Boundary.HERMITE:
        knot = sorted(knot + t[1:-1])

    # create the interpolation basis and interpolation matrix on this
    if boundary == Boundary.PERIODIC:
        knot[0]  = t[-3] - t[-1]
        knot[1]  = t[-2] - t[-1]
        knot[-2] = t[-1] + t[1]
        knot[-1] = t[-1] + t[2]
        basis = BSplineBasis(4, knot, 1)
        # since we can't interpolate at the end and start simultaneously, we
        # have to resample the interpolation points. We use the greville points
        t = basis.greville()
    else:
        basis = BSplineBasis(4, knot)
    N = basis(t)  # left-hand-side matrix

    # add derivative boundary conditions if applicable
    if boundary in [Boundary.TANGENT, Boundary.HERMITE, Boundary.TANGENTNATURAL]:
        if boundary == Boundary.TANGENT:
            dn = basis([t[0], t[-1]], d=1)
            N  = np.resize(N, (N.shape[0]+2, N.shape[1]))
            x  = np.resize(x, (x.shape[0]+2, x.shape[1]))
        elif boundary == Boundary.TANGENTNATURAL:
            dn = basis(t[0], d=1)
            N  = np.resize(N, (N.shape[0]+1, N.shape[1]))
            x  = np.resize(x, (x.shape[0]+1, x.shape[1]))
        elif boundary == Boundary.HERMITE:
            dn = getBasis(t, d=1)
            N  = np.resize(N, (N.shape[0]+n, N.shape[1]))
            x  = np.resize(x, (x.shape[0]+n, x.shape[1]))
        x[n:,:] = tangents
        N[n:,:] = dn

    # add double derivative boundary conditions if applicable
    if boundary in [Boundary.NATURAL, Boundary.TANGENTNATURAL]:
        if boundary == Boundary.NATURAL:
            ddn  = basis([t[0], t[-1]], d=2)
            new  = 2
        elif boundary == Boundary.TANGENTNATURAL:
            ddn  = basis(t[-1], d=2)
            new  = 1
        N  = np.resize(N, (N.shape[0]+new, N.shape[1]))
        x  = np.resize(x, (x.shape[0]+new, x.shape[1]))
        N[-new:,:] = ddn
        x[-new:,:] = 0

    # solve system to get controlpoints
    cp = np.linalg.solve(N,x)

    # wrap it all into a curve and return
    return Curve(basis, cp)