Example #1
0
    def test_insert_knot(self):
        # more or less random 2D surface with p=[3,2] and n=[4,3]
        controlpoints = [[0, 0], [-1, 1], [0, 2], [1, -1], [1, 0], [1, 1],
                         [2, 1], [2, 2], [2, 3], [3, 0], [4, 1], [3, 2]]
        basis1 = BSplineBasis(4, [0, 0, 0, 0, 2, 2, 2, 2])
        basis2 = BSplineBasis(3, [0, 0, 0, 1, 1, 1])
        surf = Surface(basis1, basis2, controlpoints)

        # pick some evaluation point (could be anything)
        evaluation_point1 = surf(0.23, 0.37)

        surf.insert_knot(.22, 0)
        surf.insert_knot(.5, 0)
        surf.insert_knot(.7, 0)
        surf.insert_knot(.1, 1)
        surf.insert_knot(1.0 / 3, 1)
        knot1, knot2 = surf.knots(with_multiplicities=True)
        self.assertEqual(len(knot1), 11)  # 8 to start with, 3 new ones
        self.assertEqual(len(knot2), 8)  # 6 to start with, 2 new ones

        evaluation_point2 = surf(0.23, 0.37)

        # evaluation before and after InsertKnot should remain unchanged
        self.assertAlmostEqual(evaluation_point1[0], evaluation_point2[0])
        self.assertAlmostEqual(evaluation_point1[1], evaluation_point2[1])

        # test a rational 2D surface
        controlpoints = [[0, 0, 1], [-1, 1, .96], [0, 2, 1], [1, -1, 1],
                         [1, 0, .8], [1, 1, 1], [2, 1, .89], [2, 2, .9],
                         [2, 3, 1], [3, 0, 1], [4, 1, 1], [3, 2, 1]]
        basis1 = BSplineBasis(3, [0, 0, 0, .4, 1, 1, 1])
        basis2 = BSplineBasis(3, [0, 0, 0, 1, 1, 1])
        surf = Surface(basis1, basis2, controlpoints, True)

        evaluation_point1 = surf(0.23, 0.37)

        surf.insert_knot(.22, 0)
        surf.insert_knot(.5, 0)
        surf.insert_knot(.7, 0)
        surf.insert_knot(.1, 1)
        surf.insert_knot(1.0 / 3, 1)
        knot1, knot2 = surf.knots(with_multiplicities=True)
        self.assertEqual(len(knot1), 10)  # 7 to start with, 3 new ones
        self.assertEqual(len(knot2), 8)  # 6 to start with, 2 new ones

        evaluation_point2 = surf(0.23, 0.37)

        # evaluation before and after InsertKnot should remain unchanged
        self.assertAlmostEqual(evaluation_point1[0], evaluation_point2[0])
        self.assertAlmostEqual(evaluation_point1[1], evaluation_point2[1])

        # test errors and exceptions
        with self.assertRaises(TypeError):
            surf.insert_knot(1, 2, 3)  # too many arguments
        with self.assertRaises(ValueError):
            surf.insert_knot("tree-fiddy", .5)  # wrong argument type
        with self.assertRaises(ValueError):
            surf.insert_knot(0, -0.2)  # Outside-domain error
        with self.assertRaises(ValueError):
            surf.insert_knot(1, 1.4)  # Outside-domain error
Example #2
0
    def test_make_identical(self):
        basis1 = BSplineBasis(4, [-1, -1, 0, 0, 1, 2, 9, 9, 10, 10, 11, 12],
                              periodic=1)
        basis2 = BSplineBasis(2, [0, 0, .25, 1, 1])
        cp = [[0, 0, 0, 1], [1, 0, 0, 1.1], [2, 0, 0, 1], [0, 1, 0, .7],
              [1, 1, 0, .8], [2, 1, 0, 1], [0, 2, 0, 1], [1, 2, 0, 1.2],
              [2, 2, 0, 1]]
        surf1 = Surface(BSplineBasis(3), BSplineBasis(3), cp,
                        True)  # rational 3D
        surf2 = Surface(basis1, basis2)  # periodic 2D
        surf1.insert_knot([0.25, .5, .75], direction='v')

        Surface.make_splines_identical(surf1, surf2)

        for s in (surf1, surf2):
            self.assertEqual(s.periodic(0), False)
            self.assertEqual(s.periodic(1), False)
            self.assertEqual(s.dimension, 3)
            self.assertEqual(s.rational, True)
            self.assertEqual(s.order(), (4, 3))

            self.assertAlmostEqual(len(s.knots(0, True)), 12)
            self.assertAlmostEqual(len(s.knots(1, True)), 10)

            self.assertEqual(s.bases[1].continuity(.25), 0)
            self.assertEqual(s.bases[1].continuity(.75), 1)
            self.assertEqual(s.bases[0].continuity(.2), 2)
            self.assertEqual(s.bases[0].continuity(.9), 1)
Example #3
0
    def test_normal(self):
        surf = sf.sphere(1)
        surf.swap()
        u = np.linspace(surf.start(0) + 1e-3, surf.end(0) - 1e-3, 9)
        v = np.linspace(surf.start(1) + 1e-3, surf.end(1) - 1e-3, 9)

        xpts = surf(u, v, tensor=False)
        npts = surf.normal(u, v, tensor=False)

        self.assertEqual(npts.shape, (9, 3))

        # check that the normal is pointing out of the unit ball on a 9x9 evaluation grid
        for (x, n) in zip(xpts, npts):
            self.assertAlmostEqual(n[0], x[0])
            self.assertAlmostEqual(n[1], x[1])
            self.assertAlmostEqual(n[2], x[2])

        xpts = surf(u, v)
        npts = surf.normal(u, v)

        self.assertEqual(npts.shape, (9, 9, 3))

        # check that the normal is pointing out of the unit ball on a 9x9 evaluation grid
        for (i, j) in zip(xpts, npts):
            for (x, n) in zip(i, j):
                self.assertAlmostEqual(n[0], x[0])
                self.assertAlmostEqual(n[1], x[1])
                self.assertAlmostEqual(n[2], x[2])

        # check single value input
        n = surf.normal(0, 0)
        self.assertEqual(len(n), 3)

        # test 2D surface
        s = Surface()
        n = s.normal(.5, .5)
        self.assertEqual(len(n), 3)
        self.assertAlmostEqual(n[0], 0.0)
        self.assertAlmostEqual(n[1], 0.0)
        self.assertAlmostEqual(n[2], 1.0)
        n = s.normal([.25, .5], [.1, .2, .3, .4, .5, .6, .7, .8, .9])
        self.assertEqual(n.shape[0], 2)
        self.assertEqual(n.shape[1], 9)
        self.assertEqual(n.shape[2], 3)
        self.assertAlmostEqual(n[1, 4, 0], 0.0)
        self.assertAlmostEqual(n[1, 4, 1], 0.0)
        self.assertAlmostEqual(n[1, 4, 2], 1.0)

        n = s.normal([.25, .5, .75], [.3, .5, .9], tensor=False)
        self.assertEqual(n.shape, (3, 3))
        for i in range(3):
            for j in range(2):
                self.assertAlmostEqual(n[i, j], 0.0)
            self.assertAlmostEqual(n[i, 2], 1.0)

        # test errors
        s = Surface(BSplineBasis(3), BSplineBasis(3), [[0]] * 9)  # 1D-surface
        with self.assertRaises(RuntimeError):
            s.normal(.5, .5)
Example #4
0
    def test_edge_surfaces(self):
        # test 3D surface vs 2D rational surface

        # more or less random 3D surface with p=[2,2] and n=[3,4]
        controlpoints = [[0, 0, 1], [-1, 1, 1], [0, 2, 1], [1, -1, 1], [1, 0, .5], [1, 1, 1],
                         [2, 1, 1], [2, 2, .5], [2, 3, 1], [3, 0, 1], [4, 1, 1], [3, 2, 1]]
        basis1 = BSplineBasis(3, [0, 0, 0, 1, 1, 1])
        basis2 = BSplineBasis(3, [0, 0, 0, .64, 2, 2, 2])
        top = Surface(basis1, basis2, controlpoints)

        # more or less random 2D rational surface with p=[1,2] and n=[3,4]
        controlpoints = [[0, 0, 1], [-1, 1, .96], [0, 2, 1], [1, -1, 1], [1, 0, .8], [1, 1, 1],
                         [2, 1, .89], [2, 2, .9], [2, 3, 1], [3, 0, 1], [4, 1, 1], [3, 2, 1]]
        basis1 = BSplineBasis(3, [0, 0, 0, 1, 1, 1])
        basis2 = BSplineBasis(2, [0, 0, .4, .44, 1, 1])
        bottom = Surface(basis1, basis2, controlpoints, True)

        vol = vf.edge_surfaces(bottom, top)

        # set parametric domain to [0,1]^2 for easier comparison
        top.reparam()
        bottom.reparam()

        # verify on 7x7x2 evaluation grid
        for u in np.linspace(0, 1, 7):
            for v in np.linspace(0, 1, 7):
                for w in np.linspace(0, 1, 2):  # rational basis, not linear in w-direction
                    self.assertAlmostEqual(
                        vol(u, v, w)[0], bottom(u, v)[0] *
                        (1 - w) + top(u, v)[0] * w)  # x-coordinate
                    self.assertAlmostEqual(
                        vol(u, v, w)[1], bottom(u, v)[1] *
                        (1 - w) + top(u, v)[1] * w)  # y-coordinate
                    self.assertAlmostEqual(
                        vol(u, v, w)[2], 0 * (1 - w) + top(u, v)[2] * w)  # z-coordinate

        # test 3D surface vs 2D surface
        controlpoints = [[0, 0], [-1, 1], [0, 2], [1, -1], [1, 0], [1, 1], [2, 1], [2, 2], [2, 3],
                         [3, 0], [4, 1], [3, 2]]
        basis1 = BSplineBasis(3, [0, 0, 0, 1, 1, 1])
        basis2 = BSplineBasis(2, [0, 0, .4, .44, 1, 1])
        bottom = Surface(basis1, basis2, controlpoints)  # non-rational!

        vol = vf.edge_surfaces(bottom, top)  # also non-rational!

        # verify on 5x5x7 evaluation grid
        for u in np.linspace(0, 1, 5):
            for v in np.linspace(0, 1, 5):
                for w in np.linspace(0, 1, 7):  # include inner evaluation points
                    self.assertAlmostEqual(
                        vol(u, v, w)[0], bottom(u, v)[0] *
                        (1 - w) + top(u, v)[0] * w)  # x-coordinate
                    self.assertAlmostEqual(
                        vol(u, v, w)[1], bottom(u, v)[1] *
                        (1 - w) + top(u, v)[1] * w)  # y-coordinate
                    self.assertAlmostEqual(
                        vol(u, v, w)[2], 0 * (1 - w) + top(u, v)[2] * w)  # z-coordinate
Example #5
0
    def test_revolve(self):
        # square torus
        square = Surface() + (1, 0)
        square.rotate(
            pi / 2,
            (1, 0, 0))  # in xz-plane with corners at (1,0),(2,0),(2,1),(1,1)

        vol = VolumeFactory.revolve(square)
        vol.reparam()  # set parametric space to (0,1)^3
        u = np.linspace(0, 1, 7)
        v = np.linspace(0, 1, 7)
        w = np.linspace(0, 1, 7)
        x = vol.evaluate(u, v, w)
        V, U, W = np.meshgrid(v, u, w)
        R = np.sqrt(x[:, :, :, 0]**2 + x[:, :, :, 1]**2)

        self.assertEqual(np.allclose(R, U + 1), True)
        self.assertEqual(np.allclose(x[:, :, :, 2], V), True)
        self.assertAlmostEqual(vol.volume(), 2 * pi * 1.5, places=3)

        # test incomplete reolve
        vol = VolumeFactory.revolve(square, theta=pi / 3)
        vol.reparam()  # set parametric space to (0,1)^3
        u = np.linspace(0, 1, 7)
        v = np.linspace(0, 1, 7)
        w = np.linspace(0, 1, 7)
        x = vol.evaluate(u, v, w)
        V, U, W = np.meshgrid(v, u, w)
        R = np.sqrt(x[:, :, :, 0]**2 + x[:, :, :, 1]**2)

        self.assertEqual(np.allclose(R, U + 1), True)
        self.assertEqual(np.allclose(x[:, :, :, 2], V), True)
        self.assertAlmostEqual(vol.volume(), 2 * pi * 1.5 / 6, places=3)
        self.assertTrue(np.all(x >= 0))  # completely contained in first octant

        # test axis revolve
        vol = VolumeFactory.revolve(Surface() + (1, 1),
                                    theta=pi / 3,
                                    axis=(1, 0, 0))
        vol.reparam()  # set parametric space to (0,1)^3
        u = np.linspace(0, 1, 7)
        v = np.linspace(0, 1, 7)
        w = np.linspace(0, 1, 7)
        x = vol.evaluate(u, v, w)
        V, U, W = np.meshgrid(v, u, w)
        R = np.sqrt(x[:, :, :, 1]**2 + x[:, :, :, 2]**2)

        self.assertEqual(np.allclose(R, V + 1), True)
        self.assertEqual(np.allclose(x[:, :, :, 0], U + 1), True)
        self.assertTrue(np.all(x >= 0))  # completely contained in first octant
Example #6
0
    def test_const_par_crv(self):
        # more or less random 2D surface with p=[2,2] and n=[4,3]
        controlpoints = [[0, 0], [-1, 1], [0, 2], [1, -1], [1, 0], [1, 1],
                         [2, 1], [2, 2], [2, 3], [3, 0], [4, 1], [3, 2]]
        basis1 = BSplineBasis(3, [0, 0, 0, .4, 1, 1, 1])
        basis2 = BSplineBasis(3, [0, 0, 0, 1, 1, 1])
        surf = Surface(basis1, basis2, controlpoints)
        surf.refine(1)

        # try general knot in u-direction
        crv = surf.const_par_curve(0.3, 'u')
        v = np.linspace(0, 1, 13)
        self.assertTrue(np.allclose(surf(0.3, v), crv(v)))

        # try existing knot in u-direction
        crv = surf.const_par_curve(0.4, 'u')
        v = np.linspace(0, 1, 13)
        self.assertTrue(np.allclose(surf(0.4, v), crv(v)))

        # try general knot in v-direction
        crv = surf.const_par_curve(0.3, 'v')
        u = np.linspace(0, 1, 13)
        self.assertTrue(np.allclose(surf(u, 0.3).reshape(13, 2), crv(u)))

        # try start-point
        crv = surf.const_par_curve(0.0, 'v')
        u = np.linspace(0, 1, 13)
        self.assertTrue(np.allclose(surf(u, 0.0).reshape(13, 2), crv(u)))

        # try end-point
        crv = surf.const_par_curve(1.0, 'v')
        u = np.linspace(0, 1, 13)
        self.assertTrue(np.allclose(surf(u, 1.0).reshape(13, 2), crv(u)))
Example #7
0
def disc(r=1,
         center=(0, 0, 0),
         normal=(0, 0, 1),
         type='radial',
         xaxis=(1, 0, 0)):
    """  Create a circular disc. The *type* parameter distinguishes between
    different parametrizations.

    :param float r: Radius
    :param array-like center: local origin
    :param array-like normal: local normal
    :param string type: The type of parametrization ('radial' or 'square')
    :param array-like xaxis: direction of sem, i.e. parametric start point v=0
    :return: The disc
    :rtype: Surface
    """
    if type == 'radial':
        c1 = CurveFactory.circle(r, center=center, normal=normal, xaxis=xaxis)
        c2 = flip_and_move_plane_geometry(c1 * 0, center, normal)
        result = edge_curves(c2, c1)
        result.swap()
        result.reparam((0, r), (0, 2 * pi))
        return result
    elif type == 'square':
        w = 1 / sqrt(2)
        cp = [[-r * w, -r * w, 1], [0, -r, w], [r * w, -r * w, 1], [-r, 0, w],
              [0, 0, 1], [r, 0, w], [-r * w, r * w, 1], [0, r, w],
              [r * w, r * w, 1]]
        basis1 = BSplineBasis(3)
        basis2 = BSplineBasis(3)
        result = Surface(basis1, basis2, cp, True)
        return flip_and_move_plane_geometry(result, center, normal)
    else:
        raise ValueError('invalid type argument')
Example #8
0
def interpolate(x, bases, u=None):
    """  Interpolate a surface on a set of regular gridded interpolation points `x`.

    The points can be either a matrix (in which case the first index is
    interpreted as a flat row-first index of the interpolation grid) or a 3D
    tensor. In both cases the last index is the physical coordinates.

    :param numpy.ndarray x: Grid of interpolation points
    :param [BSplineBasis] bases: The basis to interpolate on
    :param [array-like] u: Parametric interpolation points, defaults to
        Greville points of the basis
    :return: Interpolated surface
    :rtype: Surface
    """
    surf_shape = [b.num_functions() for b in bases]
    dim = x.shape[-1]
    if len(x.shape) == 2:
        x = x.reshape(surf_shape + [dim])
    if u is None:
        u = [b.greville() for b in bases]
    N_all = [b(t) for b, t in zip(bases, u)]
    N_all.reverse()
    cp = x
    for N in N_all:
        cp = np.tensordot(np.linalg.inv(N), cp, axes=(1, 1))

    return Surface(bases[0], bases[1],
                   cp.transpose(1, 0, 2).reshape((np.prod(surf_shape), dim)))
Example #9
0
def least_square_fit(x, bases, u):
    """  Perform a least-square fit of a point cloud `x` onto a spline basis.

    The points can be either a matrix (in which case the first index is
    interpreted as a flat row-first index of the interpolation grid) or a 3D
    tensor. In both cases the last index is the physical coordinates.

    There must be at least as many points as basis functions.

    :param numpy.ndarray x: Grid of evaluation points
    :param [BSplineBasis] bases: Basis on which to interpolate
    :param [array-like] u: Parametric values at evaluation points
    :return: Approximated surface
    :rtype: Surface
    """
    surf_shape = [b.num_functions() for b in bases]
    dim = x.shape[-1]
    if len(x.shape) == 2:
        x = x.reshape(surf_shape + [dim])
    N_all = [b(t) for b, t in zip(bases, u)]
    N_all.reverse()
    cp = x
    for N in N_all:
        cp = np.tensordot(N.T, cp, axes=(1, 1))
    for N in N_all:
        cp = np.tensordot(np.linalg.inv(N.T @ N), cp, axes=(1, 1))

    return Surface(bases[0], bases[1],
                   cp.transpose(1, 0, 2).reshape((np.prod(surf_shape), dim)))
Example #10
0
    def test_edge_curves(self):
        # create an arrow-like 2D geometry with the pointy end at (-1,1) towards up and left
        # mixes rational and non-rational curves with different parametrization spaces
        c1 = cf.circle_segment(pi / 2)
        c2 = Curve(BSplineBasis(2, [0, 0, 1, 2, 2]),
                   [[0, 1], [-1, 1], [-1, 0]])
        c3 = cf.circle_segment(pi / 2)
        c3.rotate(pi)
        c4 = Curve(BSplineBasis(2), [[0, -1], [1, 0]])

        surf = sf.edge_curves(c1, c2, c3, c4)

        # srf spits out parametric space (0,1)^2, so we sync these up to input curves
        c3.reverse()
        c4.reverse()
        c1.reparam()
        c2.reparam()
        c3.reparam()
        c4.reparam()

        for u in np.linspace(0, 1, 7):
            self.assertAlmostEqual(surf(u, 0)[0],
                                   c1(u)[0])  # x-coord, bottom crv
            self.assertAlmostEqual(surf(u, 0)[1],
                                   c1(u)[1])  # y-coord, bottom crv
        for u in np.linspace(0, 1, 7):
            self.assertAlmostEqual(surf(u, 1)[0], c3(u)[0])  # x-coord, top crv
            self.assertAlmostEqual(surf(u, 1)[1], c3(u)[1])  # y-coord, top crv
        for v in np.linspace(0, 1, 7):
            self.assertAlmostEqual(surf(0, v)[0],
                                   c4(v)[0])  # x-coord, left crv
            self.assertAlmostEqual(surf(0, v)[1],
                                   c4(v)[1])  # y-coord, left crv
        for v in np.linspace(0, 1, 7):
            self.assertAlmostEqual(surf(1, v)[0],
                                   c2(v)[0])  # x-coord, right crv
            self.assertAlmostEqual(surf(1, v)[1],
                                   c2(v)[1])  # y-coord, right crv

        # add a case where opposing sites have mis-matching rationality
        crvs = Surface().edges()  # returned in order umin, umax, vmin, vmax
        crvs[0].force_rational()
        crvs[1].reverse()
        crvs[2].reverse()
        # input curves should be clockwise oriented closed loop
        srf = sf.edge_curves(crvs[0], crvs[3], crvs[1], crvs[2])
        crvs[1].reverse()
        u = np.linspace(0, 1, 7)
        self.assertTrue(np.allclose(srf(u, 0).reshape((7, 2)), crvs[0](u)))
        self.assertTrue(np.allclose(srf(u, 1).reshape((7, 2)), crvs[1](u)))

        # test self-organizing curve ordering when they are not sequential
        srf = sf.edge_curves(crvs[0], crvs[2].reverse(), crvs[3], crvs[1])
        u = np.linspace(0, 1, 7)
        self.assertTrue(np.allclose(srf(u, 0).reshape((7, 2)), crvs[0](u)))
        self.assertTrue(np.allclose(srf(u, 1).reshape((7, 2)), crvs[1](u)))

        # test error handling
        with self.assertRaises(ValueError):
            srf = sf.edge_curves(crvs + (Curve(), ))  # 5 input curves
Example #11
0
    def read(self):
        lines = self.lines()

        version = next(lines).split()
        assert version[0] == 'C'
        assert version[3] == '0'  # No support for rational SPL yet
        pardim = int(version[1])
        physdim = int(version[2])

        orders = [int(k) for k in islice(lines, pardim)]
        ncoeffs = [int(k) for k in islice(lines, pardim)]
        totcoeffs = int(np.prod(ncoeffs))
        nknots = [a + b for a, b in zip(orders, ncoeffs)]

        next(lines)  # Skip spline accuracy

        knots = [[float(k) for k in islice(lines, nkts)] for nkts in nknots]
        bases = [BSplineBasis(p, kts, -1) for p, kts in zip(orders, knots)]

        cpts = np.array([float(k) for k in islice(lines, totcoeffs * physdim)])
        cpts = cpts.reshape(physdim, *(ncoeffs[::-1])).transpose()

        if pardim == 1:
            patch = Curve(*bases, controlpoints=cpts, raw=True)
        elif pardim == 2:
            patch = Surface(*bases, controlpoints=cpts, raw=True)
        elif pardim == 3:
            patch = Volume(*bases, controlpoints=cpts, raw=True)
        else:
            patch = SplineObject(bases, controlpoints=cpts, raw=True)

        return [patch]
Example #12
0
def teapot():
    """  Generate the Utah teapot as 32 cubic bezier patches. This teapot has a
    rim, but no bottom. It is also self-intersecting making it unsuitable for
    perfect-match multipatch modeling.

    The data is picked from http://www.holmes3d.net/graphics/teapot/

    :return: The utah teapot
    :rtype: List of Surface
    """
    path = join(dirname(realpath(__file__)), 'templates', 'teapot.bpt')
    with open(path) as f:
        results = []
        numb_patches = int(f.readline())
        for i in range(numb_patches):
            p = np.fromstring(f.readline(), dtype=np.uint8, count=2, sep=' ')
            basis1 = BSplineBasis(p[0] + 1)
            basis2 = BSplineBasis(p[1] + 1)

            ncp = basis1.num_functions() * basis2.num_functions()
            cp = [
                np.fromstring(f.readline(), dtype=np.float, count=3, sep=' ')
                for j in range(ncp)
            ]
            results.append(Surface(basis1, basis2, cp))

    return results
Example #13
0
def coons_patch(bottom, right, top, left):
    # coons patch (https://en.wikipedia.org/wiki/Coons_patch)
    top.reverse()
    left.reverse()

    # create linear interpolation between opposing sides
    s1 = edge_curves(bottom, top)
    s2 = edge_curves(left, right)
    s2.swap()
    # create (linear,linear) corner parametrization
    linear = BSplineBasis(2)
    rat = s1.rational  # using control-points from top/bottom, so need to know if these are rational
    if rat:
        bottom = bottom.clone().force_rational() # don't mess with the input curve, make clone
        top.force_rational()                     # this is already a clone
    s3 = Surface(linear, linear, [bottom[0], bottom[-1], top[0], top[-1]], rat)

    # in order to add spline surfaces, they need identical parametrization
    Surface.make_splines_identical(s1, s2)
    Surface.make_splines_identical(s1, s3)
    Surface.make_splines_identical(s2, s3)

    result = s1
    result.controlpoints += s2.controlpoints
    result.controlpoints -= s3.controlpoints
    return result
Example #14
0
    def read_surface(self, nsrf):
        knotsu = [0]
        for i in nsrf.KnotsU:
            knotsu.append(i)
        knotsu.append(knotsu[len(knotsu) - 1])
        knotsu[0] = knotsu[1]

        knotsv = [0]
        for i in nsrf.KnotsV:
            knotsv.append(i)
        knotsv.append(knotsv[len(knotsv) - 1])
        knotsv[0] = knotsv[1]

        basisu = BSplineBasis(nsrf.OrderU, knotsu, -1)
        basisv = BSplineBasis(nsrf.OrderV, knotsv, -1)
        cpts = []

        cpts = np.ndarray(
            (nsrf.Points.CountU * nsrf.Points.CountV, 3 + nsrf.IsRational))
        for v in range(0, nsrf.Points.CountV):
            for u in range(0, nsrf.Points.CountU):
                cpts[u + v * nsrf.Points.CountU, 0] = nsrf.Points[u, v].X
                cpts[u + v * nsrf.Points.CountU, 1] = nsrf.Points[u, v].Y
                cpts[u + v * nsrf.Points.CountU, 2] = nsrf.Points[u, v].Z
                if nsrf.IsRational:
                    cpts[u + v * nsrf.Points.CountU, 3] = nsrf.Points[u, v].W

        return Surface(basisu, basisv, cpts, nsrf.IsRational)
Example #15
0
    def test_derivative(self):
        # knot vector [t_1, t_2, ... t_{n+p+1}]
        # polynomial degree p (order-1)
        # n basis functions N_i(t), for i=1...n
        # the power basis {1,t,t^2,t^3,...} can be expressed as:
        # 1     = sum         N_i(t)
        # t     = sum ts_i  * N_i(t)
        # t^2   = sum t2s_i * N_i(t)
        # ts_i  = sum_{j=i+1}^{i+p}   t_j / p
        # t2s_i = sum_{j=i+1}^{i+p-1} sum_{k=j+1}^{i+p} t_j*t_k / (p 2)
        # (p 2) = binomial coefficient

        # creating the mapping:
        #   x(u,v) = u^2*v + u(1-v)
        #   y(u,v) = v
        controlpoints = [[0, 0], [1.0 / 4, 0], [3.0 / 4, 0], [.75, 0], [0, 1],
                         [0, 1], [.5, 1], [1, 1]]
        basis1 = BSplineBasis(3, [0, 0, 0, .5, 1, 1, 1])
        basis2 = BSplineBasis(2, [0, 0, 1, 1])
        surf = Surface(basis1, basis2, controlpoints)

        # call evaluation at a 5x4 grid of points
        val = surf.derivative([0, .2, .5, .6, 1], [0, .2, .4, 1], d=(1, 0))
        self.assertEqual(len(val.shape),
                         3)  # result should be wrapped in 3-index tensor
        self.assertEqual(val.shape[0], 5)  # 5 evaluation points in u-direction
        self.assertEqual(val.shape[1], 4)  # 4 evaluation points in v-direction
        self.assertEqual(val.shape[2], 2)  # 2 coordinates (x,y)

        self.assertAlmostEqual(surf.derivative(.2, .2, d=(1, 0))[0],
                               .88)  # dx/du=2uv+(1-v)
        self.assertAlmostEqual(surf.derivative(.2, .2, d=(1, 0))[1],
                               0)  # dy/du=0
        self.assertAlmostEqual(surf.derivative(.2, .2, d=(0, 1))[0],
                               -.16)  # dx/dv=u^2-u
        self.assertAlmostEqual(surf.derivative(.2, .2, d=(0, 1))[1],
                               1)  # dy/dv=1
        self.assertAlmostEqual(surf.derivative(.2, .2, d=(1, 1))[0],
                               -.60)  # d2x/dudv=2u-1
        self.assertAlmostEqual(surf.derivative(.2, .2, d=(2, 0))[0],
                               0.40)  # d2x/dudu=2v
        self.assertAlmostEqual(surf.derivative(.2, .2, d=(3, 0))[0],
                               0.00)  # d3x/du3=0
        self.assertAlmostEqual(surf.derivative(.2, .2, d=(0, 2))[0],
                               0.00)  # d2y/dv2=0

        # test errors and exceptions
        with self.assertRaises(ValueError):
            val = surf.derivative(-10,
                                  .5)  # evalaute outside parametric domain
        with self.assertRaises(ValueError):
            val = surf.derivative(+10,
                                  .3)  # evalaute outside parametric domain
        with self.assertRaises(ValueError):
            val = surf.derivative(.5,
                                  -10)  # evalaute outside parametric domain
        with self.assertRaises(ValueError):
            val = surf.derivative(.5,
                                  +10)  # evalaute outside parametric domain
Example #16
0
def poisson_patch(bottom, right, top, left):
    from nutils import version
    if version != '3.0':
        raise ImportError('Outdated nutils version detected, only v3.0 supported. Upgrade by \"pip install --upgrade nutils\"')

    from nutils import mesh, function as fn
    from nutils import _, log

    # error test input
    if left.rational or right.rational or top.rational or bottom.rational:
        raise RuntimeError('poisson_patch not supported for rational splines')

    # these are given as a oriented loop, so make all run in positive parametric direction
    top.reverse()
    left.reverse()

    # in order to add spline surfaces, they need identical parametrization
    Curve.make_splines_identical(top, bottom)
    Curve.make_splines_identical(left, right)

    # create computational (nutils) mesh
    p1 = bottom.order(0)
    p2 = left.order(0)
    n1 = len(bottom)
    n2 = len(left)
    dim= left.dimension
    k1 = bottom.knots(0)
    k2 = left.knots(0)
    m1 = [bottom.order(0) - bottom.continuity(k) - 1 for k in k1]
    m2 = [left.order(0)   - left.continuity(k)   - 1 for k in k2]
    domain, geom = mesh.rectilinear([k1, k2])
    basis = domain.basis('spline', [p1-1, p2-1], knotmultiplicities=[m1,m2])

    # assemble system matrix
    grad      = basis.grad(geom)
    outer     = fn.outer(grad,grad)
    integrand = outer.sum(-1)
    matrix = domain.integrate(integrand, geometry=geom, ischeme='gauss'+str(max(p1,p2)+1))

    # initialize variables
    controlpoints = np.zeros((n1,n2,dim))
    rhs           = np.zeros((n1*n2))
    constraints = np.array([[np.nan]*n2]*n1)

    # treat all dimensions independently
    for d in range(dim):
        # add boundary conditions
        constraints[ 0, :] = left[  :,d]
        constraints[-1, :] = right[ :,d]
        constraints[ :, 0] = bottom[:,d]
        constraints[ :,-1] = top[   :,d]

        # solve system
        lhs = matrix.solve(rhs, constrain=np.ndarray.flatten(constraints), solver='cg', tol=state.controlpoint_absolute_tolerance)

        # wrap results into splipy datastructures
        controlpoints[:,:,d] = np.reshape(lhs, (n1,n2), order='C')

    return Surface(bottom.bases[0], left.bases[0], controlpoints, bottom.rational, raw=True)
Example #17
0
 def test_multiplicity(self):
     surf = Surface()
     surf.refine(1)
     surf.raise_order(1)
     surf.refine(1)
     self.assertTrue(
         np.allclose(multiplicities(surf),
                     [[3, 1, 2, 1, 3], [3, 1, 2, 1, 3]]))
Example #18
0
def get_spline(spline, n, p, rational=False):
    basis = BSplineBasis(p, [0]*(p-1) + list(range(n-p+2)) + [n-p+1]*(p-1))
    if spline == 'curve':
        return Curve(basis, rational=rational)
    elif spline == 'surface':
        return Surface(basis, basis, rational=rational)
    elif spline == 'volume':
        return Volume(basis, basis, basis, rational=rational)
    return None
Example #19
0
    def test_raise_order(self):
        # more or less random 2D surface with p=[2,2] and n=[4,3]
        controlpoints = [[0, 0], [-1, 1], [0, 2], [1, -1], [1, 0], [1, 1],
                         [2, 1], [2, 2], [2, 3], [3, 0], [4, 1], [3, 2]]
        basis1 = BSplineBasis(3, [0, 0, 0, .4, 1, 1, 1])
        basis2 = BSplineBasis(3, [0, 0, 0, 1, 1, 1])
        surf = Surface(basis1, basis2, controlpoints)

        self.assertEqual(surf.order()[0], 3)
        self.assertEqual(surf.order()[1], 3)
        evaluation_point1 = surf(
            0.23, 0.37)  # pick some evaluation point (could be anything)

        surf.raise_order(1, 2)

        self.assertEqual(surf.order()[0], 4)
        self.assertEqual(surf.order()[1], 5)
        evaluation_point2 = surf(0.23, 0.37)

        # evaluation before and after RaiseOrder should remain unchanged
        self.assertAlmostEqual(evaluation_point1[0], evaluation_point2[0])
        self.assertAlmostEqual(evaluation_point1[1], evaluation_point2[1])

        # test a rational 2D surface
        controlpoints = [[0, 0, 1], [-1, 1, .96], [0, 2, 1], [1, -1, 1],
                         [1, 0, .8], [1, 1, 1], [2, 1, .89], [2, 2, .9],
                         [2, 3, 1], [3, 0, 1], [4, 1, 1], [3, 2, 1]]
        basis1 = BSplineBasis(3, [0, 0, 0, .4, 1, 1, 1])
        basis2 = BSplineBasis(3, [0, 0, 0, 1, 1, 1])
        surf = Surface(basis1, basis2, controlpoints, True)

        self.assertEqual(surf.order()[0], 3)
        self.assertEqual(surf.order()[1], 3)
        evaluation_point1 = surf(0.23, 0.37)

        surf.raise_order(1, 2)

        self.assertEqual(surf.order()[0], 4)
        self.assertEqual(surf.order()[1], 5)
        evaluation_point2 = surf(0.23, 0.37)

        # evaluation before and after RaiseOrder should remain unchanged
        self.assertAlmostEqual(evaluation_point1[0], evaluation_point2[0])
        self.assertAlmostEqual(evaluation_point1[1], evaluation_point2[1])
Example #20
0
 def test_repr(self):
     major, minor, patch = np.version.version.split('.')
     if int(major) <= 1 and int(minor) <= 13:
         self.assertEqual(
             repr(Surface()), 'p=2, [ 0.  0.  1.  1.]\n'
             'p=2, [ 0.  0.  1.  1.]\n'
             '[ 0.  0.]\n'
             '[ 1.  0.]\n'
             '[ 0.  1.]\n'
             '[ 1.  1.]\n')
Example #21
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 #22
0
    def test_evaluate(self):
        # knot vector [t_1, t_2, ... t_{n+p+1}]
        # polynomial degree p (order-1)
        # n basis functions N_i(t), for i=1...n
        # the power basis {1,t,t^2,t^3,...} can be expressed as:
        # 1     = sum         N_i(t)
        # t     = sum ts_i  * N_i(t)
        # t^2   = sum t2s_i * N_i(t)
        # ts_i  = sum_{j=i+1}^{i+p}   t_j / p
        # t2s_i = sum_{j=i+1}^{i+p-1} sum_{k=j+1}^{i+p} t_j*t_k / (p 2)
        # (p 2) = binomial coefficient

        # creating the mapping:
        #   x(u,v) = u^2*v + u(1-v)
        #   y(u,v) = v
        controlpoints = [[0, 0], [1.0 / 4, 0], [3.0 / 4, 0], [.75, 0], [0, 1],
                         [0, 1], [.5, 1], [1, 1]]
        basis1 = BSplineBasis(3, [0, 0, 0, .5, 1, 1, 1])
        basis2 = BSplineBasis(2, [0, 0, 1, 1])
        surf = Surface(basis1, basis2, controlpoints)

        # call evaluation at a 5x4 grid of points
        val = surf([0, .2, .5, .6, 1], [0, .2, .4, 1])
        self.assertEqual(len(val.shape),
                         3)  # result should be wrapped in 3-index tensor
        self.assertEqual(val.shape[0], 5)  # 5 evaluation points in u-direction
        self.assertEqual(val.shape[1], 4)  # 4 evaluation points in v-direction
        self.assertEqual(val.shape[2], 2)  # 2 coordinates (x,y)

        # check evaluation at (0,0)
        self.assertAlmostEqual(val[0][0][0], 0.0)
        self.assertAlmostEqual(val[0][0][1], 0.0)
        # check evaluation at (.2,0)
        self.assertAlmostEqual(val[1][0][0], 0.2)
        self.assertAlmostEqual(val[1][0][1], 0.0)
        # check evaluation at (.2,.2)
        self.assertAlmostEqual(val[1][1][0], 0.168)
        self.assertAlmostEqual(val[1][1][1], 0.2)
        # check evaluation at (.5,.4)
        self.assertAlmostEqual(val[2][2][0], 0.4)
        self.assertAlmostEqual(val[2][2][1], 0.4)
        # check evaluation at (.6,1)
        self.assertAlmostEqual(val[3][3][0], 0.36)
        self.assertAlmostEqual(val[3][3][1], 1)

        # test errors and exceptions
        with self.assertRaises(ValueError):
            val = surf(-10, .5)  # evalaute outside parametric domain
        with self.assertRaises(ValueError):
            val = surf(+10, .3)  # evalaute outside parametric domain
        with self.assertRaises(ValueError):
            val = surf(.5, -10)  # evalaute outside parametric domain
        with self.assertRaises(ValueError):
            val = surf(.5, +10)  # evalaute outside parametric domain
Example #23
0
    def test_edges(self):
        (umin, umax, vmin, vmax) = Surface().edges()
        # check controlpoints
        self.assertAlmostEqual(umin[0, 0], 0)
        self.assertAlmostEqual(umin[0, 1], 0)
        self.assertAlmostEqual(umin[1, 0], 0)
        self.assertAlmostEqual(umin[1, 1], 1)

        self.assertAlmostEqual(umax[0, 0], 1)
        self.assertAlmostEqual(umax[0, 1], 0)
        self.assertAlmostEqual(umax[1, 0], 1)
        self.assertAlmostEqual(umax[1, 1], 1)

        self.assertAlmostEqual(vmin[0, 0], 0)
        self.assertAlmostEqual(vmin[0, 1], 0)
        self.assertAlmostEqual(vmin[1, 0], 1)
        self.assertAlmostEqual(vmin[1, 1], 0)

        # check a slightly more general surface
        cp = [[0, 0], [.5, -.5], [1, 0], [-.6, 1], [1, 1], [2, 1.4], [0, 2],
              [.8, 3], [2, 2.4]]
        surf = Surface(BSplineBasis(3), BSplineBasis(3), cp)
        edg = surf.edges()
        u = np.linspace(0, 1, 9)
        v = np.linspace(0, 1, 9)

        pt = surf(0, v)
        pt2 = edg[0](v)
        self.assertAlmostEqual(np.linalg.norm(pt - pt2), 0.0)

        pt = surf(1, v)
        pt2 = edg[1](v)
        self.assertAlmostEqual(np.linalg.norm(pt - pt2), 0.0)

        pt = surf(u, 0).reshape(9, 2)
        pt2 = edg[2](u)
        self.assertAlmostEqual(np.linalg.norm(pt - pt2), 0.0)

        pt = surf(u, 1).reshape(9, 2)
        pt2 = edg[3](u)
        self.assertAlmostEqual(np.linalg.norm(pt - pt2), 0.0)
Example #24
0
def square(size=1, lower_left=(0, 0)):
    """  Create a square with parametric origin at *(0,0)*.

    :param float size: Size(s), either a single scalar or a tuple of scalars per axis
    :param array-like lower_left: local origin, the lower left corner of the square
    :return: A linear parametrized square
    :rtype: Surface
    """
    result = Surface()  # unit square
    result.scale(size)
    result += lower_left
    return result
Example #25
0
    def test_3d_self_connection(self):
        square = Surface() + [1, 0]
        square = square.rotate(np.pi / 2, (1, 0, 0))
        vol = volume_factory.revolve(square)
        vol = vol.split(vol.knots('w')[0], direction='w')  # break periodicity
        model = SplineModel(3, 3)
        model.add(vol, raise_on_twins=False)

        writer = IFEMWriter(model)
        expected = [IFEMConnection(1, 1, 5, 6, 0)]
        for connection, want in zip(writer.connections(), expected):
            self.assertEqual(connection, want)
Example #26
0
    def test_reverse(self):
        basis1 = BSplineBasis(4, [2, 2, 2, 2, 3, 6, 7, 7, 7, 7])
        basis2 = BSplineBasis(3, [-3, -3, -3, 20, 30, 31, 31, 31])
        surf = Surface(basis1, basis2)
        surf2 = Surface(basis1, basis2)
        surf3 = Surface(basis1, basis2)

        surf2.reverse('v')
        surf3.reverse('u')

        for i in range(6):
            # loop over surf forward, and surf2 backward (in 'v'-direction)
            for (cp1, cp2) in zip(surf[i, :, :], surf2[i, ::-1, :]):
                self.assertAlmostEqual(cp1[0], cp2[0])
                self.assertAlmostEqual(cp1[1], cp2[1])

        for j in range(5):
            # loop over surf forward, and surf3 backward (in 'u'-direction)
            for (cp1, cp2) in zip(surf[:, j, :], surf3[::-1, j, :]):
                self.assertAlmostEqual(cp1[0], cp2[0])
                self.assertAlmostEqual(cp1[1], cp2[1])
Example #27
0
    def test_constructor(self):
        # test 3D constructor
        cp = [[0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0]]
        surf = Surface(controlpoints=cp)
        val = surf(0.5, 0.5)
        self.assertEqual(val[0], 0.5)
        self.assertEqual(len(surf[0]), 3)

        # test 2D constructor
        cp = [[0, 0], [1, 0], [0, 1], [1, 1]]
        surf2 = Surface(controlpoints=cp)
        val = surf2(0.5, 0.5)
        self.assertEqual(val[0], 0.5)
        self.assertEqual(len(surf2[0]), 2)

        # test rational 2D constructor
        cp = [[0, 0, 1], [1, 0, 1], [0, 1, 1], [1, 1, 1]]
        surf3 = Surface(controlpoints=cp, rational=True)
        val = surf3(0.5, 0.5)
        self.assertEqual(val[0], 0.5)
        self.assertEqual(len(surf3[0]), 3)

        # test rational 3D constructor
        cp = [[0, 0, 0, 1], [1, 0, 0, 1], [0, 1, 0, 1], [1, 1, 0, 1]]
        surf4 = Surface(controlpoints=cp, rational=True)
        val = surf4(0.5, 0.5)
        self.assertEqual(val[0], 0.5)
        self.assertEqual(len(surf4[0]), 4)

        # test constructor with single basis
        b = BSplineBasis(4)
        surf = Surface(b, b)
        surf.insert_knot(.3, 'u')  # change one, but not the other
        self.assertEqual(len(surf.knots('u')), 3)
        self.assertEqual(len(surf.knots('v')), 2)

        # TODO: Include a default constructor specifying nothing, or just polynomial degrees, or just knot vectors.
        #       This should create identity mappings

        # test errors and exceptions
        controlpoints = [[0, 0, 1], [1, 0, 1], [0, 1, 1], [1, 1, 1]]
        with self.assertRaises(ValueError):
            basis1 = BSplineBasis(2, [1, 1, 0, 0])
            basis2 = BSplineBasis(2, [0, 0, 1, 1])
            surf = Surface(basis1, basis2,
                           controlpoints)  # illegal knot vector
        with self.assertRaises(ValueError):
            basis1 = BSplineBasis(2, [0, 0, .5, 1, 1])
            basis2 = BSplineBasis(2, [0, 0, 1, 1])
            surf = Surface(basis1, basis2,
                           controlpoints)  # too few controlpoints
Example #28
0
    def test_reparam(self):
        # identity mapping, control points generated from knot vector
        basis1 = BSplineBasis(4, [2, 2, 2, 2, 3, 6, 7, 7, 7, 7])
        basis2 = BSplineBasis(3, [-3, -3, -3, 20, 30, 31, 31, 31])
        surf = Surface(basis1, basis2)

        self.assertAlmostEqual(surf.start(0), 2)
        self.assertAlmostEqual(surf.end(0), 7)
        self.assertAlmostEqual(surf.start(1), -3)
        self.assertAlmostEqual(surf.end(1), 31)

        surf.reparam((4, 10), (0, 9))
        self.assertAlmostEqual(surf.start(0), 4)
        self.assertAlmostEqual(surf.end(0), 10)
        self.assertAlmostEqual(surf.start(1), 0)
        self.assertAlmostEqual(surf.end(1), 9)

        surf.reparam((5, 11), direction=0)
        self.assertAlmostEqual(surf.start(0), 5)
        self.assertAlmostEqual(surf.end(0), 11)
        self.assertAlmostEqual(surf.start(1), 0)
        self.assertAlmostEqual(surf.end(1), 9)

        surf.reparam((5, 11), direction='v')
        self.assertAlmostEqual(surf.start(0), 5)
        self.assertAlmostEqual(surf.end(0), 11)
        self.assertAlmostEqual(surf.start(1), 5)
        self.assertAlmostEqual(surf.end(1), 11)

        surf.reparam((-9, 9))
        self.assertAlmostEqual(surf.start(0), -9)
        self.assertAlmostEqual(surf.end(0), 9)
        self.assertAlmostEqual(surf.start(1), 0)
        self.assertAlmostEqual(surf.end(1), 1)

        surf.reparam()
        self.assertAlmostEqual(surf.start(0), 0)
        self.assertAlmostEqual(surf.end(0), 1)
        self.assertAlmostEqual(surf.start(1), 0)
        self.assertAlmostEqual(surf.end(1), 1)

        surf.reparam((4, 10), (0, 9))
        surf.reparam(direction=1)
        self.assertAlmostEqual(surf.start(0), 4)
        self.assertAlmostEqual(surf.end(0), 10)
        self.assertAlmostEqual(surf.start(1), 0)
        self.assertAlmostEqual(surf.end(1), 1)
Example #29
0
def extrude(curve, amount):
    """  Extrude a curve by sweeping it to a given height.

    :param Curve curve: Curve to extrude
    :param array-like amount: 3-component vector of sweeping amount and
                               direction
    :return: The extruded curve
    :rtype: Surface
    """
    curve = curve.clone()  # clone input curve, throw away input reference
    curve.set_dimension(3)  # add z-components (if not already present)
    n = len(curve)  # number of control points of the curve
    cp = np.zeros((2 * n, curve.dimension + curve.rational))
    cp[:n, :] = curve.controlpoints  # the first control points form the bottom
    curve += amount
    cp[n:, :] = curve.controlpoints  # the last control points form the top
    return Surface(curve.bases[0], BSplineBasis(2), cp, curve.rational)
Example #30
0
    def test_split(self):
        # test a rational 2D surface
        controlpoints = [[0, 0, 1], [-1, 1, .96], [0, 2, 1], [1, -1, 1],
                         [1, 0, .8], [1, 1, 1], [2, 1, .89], [2, 2, .9],
                         [2, 3, 1], [3, 0, 1], [4, 1, 1], [3, 2, 1]]
        basis1 = BSplineBasis(3, [1, 1, 1, 1.4, 5, 5, 5])
        basis2 = BSplineBasis(3, [2, 2, 2, 7, 7, 7])
        surf = Surface(basis1, basis2, controlpoints, True)

        split_u_surf = surf.split([1.1, 1.6, 4], 0)
        split_v_surf = surf.split(3.1, 1)

        self.assertEqual(len(split_u_surf), 4)
        self.assertEqual(len(split_v_surf), 2)

        # check that the u-vector is properly split
        self.assertAlmostEqual(split_u_surf[0].start()[0], 1.0)
        self.assertAlmostEqual(split_u_surf[0].end()[0], 1.1)
        self.assertAlmostEqual(split_u_surf[1].start()[0], 1.1)
        self.assertAlmostEqual(split_u_surf[1].end()[0], 1.6)
        self.assertAlmostEqual(split_u_surf[2].start()[0], 1.6)
        self.assertAlmostEqual(split_u_surf[2].end()[0], 4.0)
        self.assertAlmostEqual(split_u_surf[3].start()[0], 4.0)
        self.assertAlmostEqual(split_u_surf[3].end()[0], 5.0)
        # check that the v-vectors remain unchanged
        self.assertAlmostEqual(split_u_surf[2].start()[1], 2.0)
        self.assertAlmostEqual(split_u_surf[2].end()[1], 7.0)
        # check that the v-vector is properly split
        self.assertAlmostEqual(split_v_surf[0].start()[1], 2.0)
        self.assertAlmostEqual(split_v_surf[0].end()[1], 3.1)
        self.assertAlmostEqual(split_v_surf[1].start()[1], 3.1)
        self.assertAlmostEqual(split_v_surf[1].end()[1], 7.0)
        # check that the u-vector remain unchanged
        self.assertAlmostEqual(split_v_surf[1].start()[0], 1.0)
        self.assertAlmostEqual(split_v_surf[1].end()[0], 5.0)

        # check that evaluations remain unchanged
        pt1 = surf(3.23, 2.12)

        self.assertAlmostEqual(split_u_surf[2].evaluate(3.23, 2.12)[0], pt1[0])
        self.assertAlmostEqual(split_u_surf[2].evaluate(3.23, 2.12)[1], pt1[1])

        self.assertAlmostEqual(split_v_surf[0].evaluate(3.23, 2.12)[0], pt1[0])
        self.assertAlmostEqual(split_v_surf[0].evaluate(3.23, 2.12)[1], pt1[1])