Пример #1
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
Пример #2
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
Пример #3
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)
Пример #4
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)
Пример #5
0
def thicken(curve, amount):
    """  Generate a surface by adding thickness to a curve.

    - For 2D curves this will generate a 2D planar surface with the curve
      through the center.

    - For 3D curves this will generate a surface "tube" which is open at both
      ends (that is, for a straight line it will generate a cylinder shell).

    The resulting surface is an approximation generated by interpolating at the
    Greville points. It will use the same discretization as the input curve.
    It does not check for self-intersecting geometries.

    :param Curve curve: The generating curve
    :param amount: The amount of thickness, either constant or variable (if
        variable, the function must accept parameters named *x*, *y*, *z* and/or *t*)
    :return: Surrounding surface
    :rtype: Surface
    """
    # NOTES: There are several pitfalls with this function
    #  * self intersection:
    #     could be handled more gracefully, but is here ignored
    #  * choice of discretization:
    #     the offset curve is computed from the velocity (tangent) which is of
    #     one continuity less than the original curve. In particular C1
    #     quadratic curves will get very noticeable C0-kinks in them. Currently
    #     this is completely ignored and we keep the high continuity of the
    #     original curve.
    #  * width given by function input
    #     could produce wild behaviour. Original discretization might not
    #     produce a satisfactory result

    curve = curve.clone()  # clone input curve, throw away input reference
    t = curve.bases[0].greville()
    if curve.dimension == 2:
        # linear parametrization across domain
        n = len(curve)
        left_points = np.zeros((n, 2))
        right_points = np.zeros((n, 2))
        linear = BSplineBasis(2)

        x = curve.evaluate(t)  # curve at interpolation points
        v = curve.derivative(t)  # velocity at interpolation points
        l = np.sqrt(v[:, 0]**2 + v[:, 1]**2)  # normalizing factor for velocity
        for i in range(n):
            if l[i] < 1e-13:  # in case of zero velocity, use neighbour instead
                if i > 0:
                    v[i, :] = v[i - 1, :]
                else:
                    v[i, :] = v[i + 1, :]
            else:
                v[i, :] /= l[i]

        if inspect.isfunction(amount):
            arg_names = inspect.signature(amount).parameters
            argc = len(arg_names)
            argv = [0] * argc
            for i in range(n):
                # build up the list of arguments (in case not all of (x,y,t) are specified)
                for j, name in enumerate(arg_names):
                    if name == 'x':
                        argv[j] = x[i, 0]
                    elif name == 'y':
                        argv[j] = x[i, 1]
                    elif name == 'z':
                        argv[j] = 0.0
                    elif name == 't':
                        argv[j] = t[i]
                # figure out the distane at this particular point
                dist = amount(*argv)

                # store interpolation points
                right_points[i, 0] = x[i, 0] - v[i, 1] * dist  # x at bottom
                right_points[i, 1] = x[i, 1] + v[i, 0] * dist  # y at bottom
                left_points[i, 0] = x[i, 0] + v[i, 1] * dist  # x at top
                left_points[i, 1] = x[i, 1] - v[i, 0] * dist  # y at top
        else:
            right_points[:, 0] = x[:, 0] - v[:, 1] * amount  # x at bottom
            right_points[:, 1] = x[:, 1] + v[:, 0] * amount  # y at bottom
            left_points[:, 0] = x[:, 0] + v[:, 1] * amount  # x at top
            left_points[:, 1] = x[:, 1] - v[:, 0] * amount  # y at top
        # perform interpolation on each side
        right = CurveFactory.interpolate(right_points, curve.bases[0])
        left = CurveFactory.interpolate(left_points, curve.bases[0])
        return edge_curves(right, left)

    else:  # dimension=3, we will create a surrounding tube
        return sweep(curve, CurveFactory.circle(r=amount))
Пример #6
0
def thicken(curve, amount):
    """Generate a surface by adding thickness to a curve.

    - For 2D curves this will generate a 2D planar surface with the curve
      through the center.

    - For 3D curves this will generate a surface "tube" which is open at both
      ends (that is, for a straight line it will generate a cylinder shell).

    The resulting surface is an approximation generated by interpolating at the
    Greville points. It will use the same discretization as the input curve.
    It does not check for self-intersecting geometries.

    :param Curve curve: The generating curve
    :param amount: The amount of thickness, either constant or variable (if
        variable, the function must accept parameters named *x*, *y*, *z* and/or *t*)
    :return: Surrounding surface
    :rtype: Surface
    """
    # NOTES: There are several pitfalls with this function
    #  * self intersection:
    #     could be handled more gracefully, but is here ignored
    #  * choice of discretization:
    #     the offset curve is computed from the velocity (tangent) which is of
    #     one continuity less than the original curve. In particular C1
    #     quadratic curves will get very noticable C0-kinks in them. Currently
    #     this is completely ignored and we keep the high continuity of the
    #     original curve.
    #  * width given by function input
    #     could produce wild behaviour. Original discretization might not
    #     produce a satisfactory result
    #  * 3D tube geometries:
    #     unimplemented as of now. Would like to see the three points above
    #     resolved before this is implemented. Rough idea is to compute the
    #     acceleration and binormal vectors to the curve and sketch out a
    #     circle in the plane defined by these two vectors

    curve = curve.clone()  # clone input curve, throw away input reference
    t = curve.bases[0].greville()
    if curve.dimension == 2:
        # linear parametrization across domain
        n = len(curve)
        left_points = np.matrix(np.zeros((n, 2)))
        right_points = np.matrix(np.zeros((n, 2)))
        linear = BSplineBasis(2)

        x = np.matrix(curve.evaluate(t))  # curve at interpolation points
        v = curve.tangent(t)              # velocity at interpolation points
        l = np.sqrt(v[:, 0]**2 + v[:, 1]**2)  # normalizing factor for velocity
        v[:, 0] = v[:, 0] / l
        v[:, 1] = v[:, 1] / l
        for i in range(n):
            if l[i] < 1e-13: # in case of zero velocity, use neighbour instead
                if i>0:
                    v[i,:] = v[i-1,:]
                else:
                    v[i,:] = v[i+1,:]
                
        v = np.matrix(v)
        if inspect.isfunction(amount):
            arg_names = inspect.getargspec(amount).args
            argc = len(arg_names)
            argv = [0] * argc
            for i in range(n):
                # build up the list of arguments (in case not all of (x,y,t) are specified)
                for j in range(argc):
                    if arg_names[j] == 'x':
                        argv[j] = x[i, 0]
                    elif arg_names[j] == 'y':
                        argv[j] = x[i, 1]
                    elif arg_names[j] == 'z':
                        argv[j] = 0.0
                    elif arg_names[j] == 't':
                        argv[j] = t[i]
                # figure out the distane at this particular point
                dist = amount(*argv)

                # store interpolation points
                right_points[i, 0] = x[i, 0] - v[i, 1] * dist  # x at bottom
                right_points[i, 1] = x[i, 1] + v[i, 0] * dist  # y at bottom
                left_points[ i, 0] = x[i, 0] + v[i, 1] * dist  # x at top
                left_points[ i, 1] = x[i, 1] - v[i, 0] * dist  # y at top
        else:
            right_points[:, 0] = x[:, 0] - v[:, 1] * amount  # x at bottom
            right_points[:, 1] = x[:, 1] + v[:, 0] * amount  # y at bottom
            left_points[ :, 0] = x[:, 0] + v[:, 1] * amount  # x at top
            left_points[ :, 1] = x[:, 1] - v[:, 0] * amount  # y at top
        # perform interpolation on each side
        right = CurveFactory.interpolate(right_points, curve.bases[0])
        left  = CurveFactory.interpolate(left_points,  curve.bases[0])
        return edge_curves(right, left)

    else:  # dimension=3, we will create a surrounding tube
        raise NotImplementedError('Currently only 2D supported. See comments in source code')