Fill volume under the surface

I am trying to make visualization of Integration, so i want to fill the volume under the surface

from scipy.integrate import quad
import numpy as np
import plotly.express as px
import plotly.graph_objects as go

f=lambda x,y: np.cos(x)+np.sin(y)+2

#surface
x, y = np.meshgrid( 
    np.linspace(-5, 5, 200),
    np.linspace(-5, 5, 200)
)
z=f(x,y)

#patch of surface
lower_x=-1
upper_x=2
lower_y=-2
upper_y=4
int_x, int_y = np.meshgrid( 
    np.linspace(lower_x, upper_x, 200),
    np.linspace(lower_y, upper_y, 200)
)
int_z=f(int_x,int_y)

fig= go.Figure()
fig.add_surface(x=x,y=y,z=z, showscale=False, colorscale=px.colors.sequential.Pinkyl, opacity=0.4)
fig.add_surface(x=int_x,y=int_y,z=int_z, showscale=False, colorscale=px.colors.sequential.Agsunset, opacity=1)
fig.update_layout(showlegend=False)
fig.show()

So i want to fill volume under the patch of the surface that is highlighted
Screenshot 2022-06-09 at 3.22.34 PM
Similar to area under the curve shown below

Hi @lalit ,

You can add a trace of type go.Volume to your Figure, to point out that a double integral over the chosen rectangle is a volume (in the classical sense).
I modified your function, adding 4 instead of 2, because with 2 the graph is too close to z-plane and the filled volume isn’t so visible in this case.

import numpy as np
import plotly.express as px
import plotly.graph_objects as go

f = lambda x,y: np.cos(x)+np.sin(y)+4

#surface
x, y = np.meshgrid( 
    np.linspace(-5, 5, 100),
    np.linspace(-5, 5, 100)
)
z = f(x,y)

#patch of surface
lower_x, upper_x = -1, 2
lower_y, upper_y = -2, 4
int_x, int_y = np.meshgrid( 
    np.linspace(lower_x, upper_x, 50),
    np.linspace(lower_y, upper_y, 70)
)
int_z = f(int_x,int_y)

fig= go.Figure()
fig.add_surface(x=x[0], y=y[:, 0], z=z, showscale=False, 
                colorscale=px.colors.sequential.Pinkyl, opacity=0.6)
fig.add_surface(x=int_x[0],y=int_y[:, 0],z=int_z, showscale=False, colorscale=px.colors.sequential.Agsunset, opacity=1)
lower_z= 0

upper_z= int_z.max()

X, Y, Z = np.mgrid[lower_x:upper_x:50j, lower_y:upper_y:50j, lower_z:upper_z:75j]
vals = Z-f(X,Y)
fig.add_volume(x=X.flatten(), y=Y.flatten(), z=Z.flatten(), value= vals.flatten(),
                 surface_show=True, surface_count=2, 
                 colorscale=[[0, px.colors.sequential.Agsunset[4]],[1.0, px.colors.sequential.Agsunset[4]]],
                 showscale=False,
                 isomin=-upper_z, isomax=0) #isomin=-upper_z corresponds to z=f(x,y)-upper_z, and  isomax=0, to z=f(x,y)


fig.update_layout(height=600, width=600, scene_zaxis_range=[0, upper_z+0.5],
                                                            scene_camera_eye=dict(x=1.85, y=1.85, z=0.7))

The go.Volume trace is memory consumming, because X,Y Z and vals are 3d arrays defined by np.mgrid.
How is defined this volume?
Within the parallelipiped [lower_x, upper_x] x[lower_y, upper_y] x[lower_z, upper_z] are drawn two surfaces z=f(x,y)+c, where c must be chosen such that to get as the upper boundary of your volume the surface of equation int_z=f(int_x,int_y), and as a lower boundary, its projection onto the z-plane. For the second surface we perform a trick: choose c such that only the maximum value belong to z=0.
The surface int_z=f(int_x, int_y)-upper_z meets this requirement. Hence we are setting isomin=-upper_z, isomax=0, in the go.Volume definition.

3 Likes

thanks a lot buddy :love_you_gesture:

1 Like