Search code examples
pythonscipyspline

How can I calculate arbitrary values from a spline created with scipy.interpolate.Rbf?


I have several data points in 3 dimensional space (x, y, z) and have interpolated them using scipy.interpolate.Rbf. This gives me a spline nicely representing the surface of my 3D object. I would now like to determine several x and y pairs that have the same, arbitrary z value. I would like to do that in order to compute the cross section of my 3D object at any given value of z. Does someone know how to do that? Maybe there is also a better way to do that instead of using scipy.interpolate.Rbf.

Up to now I have evaluated the cross sections by making a contour plot using matplotlib.pyplot and extracting the displayed segments. 3D points and interpolated spline segments extracted using a contour plot


Solution

  • I was able to solve the problem. I have calculated the area by triangulating the x-y data and cutting the triangles with the z-plane I wanted to calculate the cross-sectional area of (z=z0). Specifically, I have searched for those triangles whose z-values are both above and below z0. Then I have calculated the x and y values of the sides of these triangles where the sides are equal to z0. Then I use scipy.spatial.ConvexHull to sort the intersected points. Using the shoelace formula I can then determine the area.

    I have attached the example code here:

    import numpy as np
    from scipy import spatial
    import matplotlib.pyplot as plt
    
    # Generation of random test data
    n = 500
    x = np.random.random(n) 
    y = np.random.random(n)
    z = np.exp(-2*(x-.5)**2-4*(y-.5)**2)
    
    z0 = .75
    
    # Triangulation of the test data
    triang= spatial.Delaunay(np.array([x, y]).T)
    
    
    # Determine all triangles where not all points are above or below z0, i.e. the triangles that intersect z0
    tri_inter=np.zeros_like(triang.simplices, dtype=np.int) # The triangles which intersect the plane at z0, filled below
    
    i = 0
    for tri in triang.simplices:    
        if ~np.all(z[tri] > z0) and ~np.all(z[tri] < z0):
            tri_inter[i,:] = tri
            i += 1
    tri_inter = tri_inter[~np.all(tri_inter==0, axis=1)] # Remove all rows with only 0 
    
    # The number of interpolated values for x and y has twice the length of the triangles
    # Because each triangle intersects the plane at z0 twice
    x_inter = np.zeros(tri_inter.shape[0]*2)
    y_inter = np.zeros(tri_inter.shape[0]*2)
    
    for j, tri in enumerate(tri_inter):
    
        # Determine which of the three points are above and which are below z0
        points_above = []
        points_below = []
    
        for i in tri:    
            if z[i] > z0:
                points_above.append(i)
            else:
                points_below.append(i)     
    
        # Calculate the intersections and put the values into x_inter and y_inter
        t = (z0-z[points_below[0]])/(z[points_above[0]]-z[points_below[0]])
        x_new = t * (x[points_above[0]]-x[points_below[0]]) + x[points_below[0]]
        y_new = t * (y[points_above[0]]-y[points_below[0]]) + y[points_below[0]]
    
        x_inter[j*2] = x_new
        y_inter[j*2] = y_new
    
        if len(points_above) > len(points_below): 
            t = (z0-z[points_below[0]])/(z[points_above[1]]-z[points_below[0]])
            x_new = t * (x[points_above[1]]-x[points_below[0]]) + x[points_below[0]]
            y_new = t * (y[points_above[1]]-y[points_below[0]]) + y[points_below[0]]
    
        else:
            t = (z0-z[points_below[1]])/(z[points_above[0]]-z[points_below[1]])
            x_new = t * (x[points_above[0]]-x[points_below[1]]) + x[points_below[1]]
            y_new = t * (y[points_above[0]]-y[points_below[1]]) + y[points_below[1]]
    
        x_inter[j*2+1] = x_new
        y_inter[j*2+1] = y_new
    
    # sort points to calculate area
    hull = spatial.ConvexHull(np.array([x_inter, y_inter]).T)
    
    x_hull, y_hull = x_inter[hull.vertices], y_inter[hull.vertices]
    
    # Calculation of are using the shoelace formula
    area = 0.5*np.abs(np.dot(x_hull,np.roll(y_hull,1))-np.dot(y_hull,np.roll(x_hull,1)))
    
    print('Area:', area)
    
    plt.figure()
    plt.plot(x_inter, y_inter, 'ro')
    plt.plot(x_hull, y_hull, 'b--')
    plt.triplot(x, y, triangles=tri_inter, color='k')
    plt.show()