import numpy as np import plotly.graph_objects as go """ Interactive 3D Plotly animation of the scalar line integral ∮_C f(x, y) ds with f(x, y) = x · y along the unit circle C, plotted on the surface z = f(x,y). No files are written; the animation is displayed with interactive controls. """ # Parameters t_frame = np.linspace(0.0, 2 * np.pi, 120) # Scalar field definition def scalar_field(x, y): return x * y # Surface mesh for scalar field lin = np.linspace(-1.2, 1.2, 50) X, Y = np.meshgrid(lin, lin) Z = scalar_field(X, Y) # Compute running integral along circle # Midpoint rule mid_ts = (t_frame[1:] + t_frame[:-1]) / 2 f_mid = scalar_field(np.cos(mid_ts), np.sin(mid_ts)) dt = t_frame[1:] - t_frame[:-1] running = np.concatenate([[0], np.cumsum(f_mid * dt)]) # Prepare static surface trace surface = go.Surface( x=X, y=Y, z=Z, colorscale="RdBu", cmin=-1, cmax=1, opacity=0.6, showscale=False ) # Full path on surface curve_x = np.cos(t_frame) curve_y = np.sin(t_frame) curve_z = scalar_field(curve_x, curve_y) path = go.Scatter3d( x=curve_x, y=curve_y, z=curve_z, mode="lines", line=dict(color="black", width=2) ) # Initial marker at t=0 t0 = t_frame[0] marker = go.Scatter3d( x=[np.cos(t0)], y=[np.sin(t0)], z=[scalar_field(np.cos(t0), np.sin(t0))], mode="markers", marker=dict(size=5, color="black"), ) # Build frames for animation frames = [] for i, t in enumerate(t_frame): x0, y0 = np.cos(t), np.sin(t) z0 = scalar_field(x0, y0) frame = go.Frame( data=[marker.update(x=[x0], y=[y0], z=[z0])], layout=go.Layout(title_text=f"Running integral: {running[i]:.3f}"), name=f"frame{i}", ) frames.append(frame) # Layout with sliders and buttons graph = go.Figure( data=[surface, path, marker], layout=go.Layout( title="Running integral: 0.000", scene=dict( xaxis=dict(range=[-1.3, 1.3]), yaxis=dict(range=[-1.3, 1.3]), zaxis=dict(range=[Z.min(), Z.max()]), ), updatemenus=[ dict( type="buttons", showactive=False, buttons=[ dict( label="Play", method="animate", args=[ None, dict( frame=dict(duration=50, redraw=True), fromcurrent=True, transition=dict(duration=0), ), ], ) ], ) ], ), frames=frames, ) # Add slider sliders = [ dict( steps=[ dict( method="animate", args=[ [f.name], dict( mode="immediate", frame=dict(duration=50, redraw=True), transition=dict(duration=0), ), ], label=str(i), ) for i, f in enumerate(frames) ], transition=dict(duration=0), x=0, y=0, currentvalue=dict(prefix="Frame: "), len=1.0, ) ] graph.update_layout(sliders=sliders) graph.show()