Note
Go to the end to download the full example code.
Signed Distance Fields#
Plotting 2D and 3D Signed Distance Fields
import matplotlib.pyplot as plt
import mesdf
import numpy as np
import pyvista as pv
import mirage as mr
import mirage.vis as mrv
obj = mr.SpaceObject('stanford_bunny.obj').clean()
obj.v -= np.mean(obj.v, axis=0)
f = mesdf.SDF(obj.v, obj.f)
grid_width = 1.3 * np.max(mr.vecnorm(obj.v))
grid_density = 150
grid = mr.r3_grid(grid_width, grid_density)
sdf_vals = -f(grid.points)
Plotting a 2D slide of the SDF
sdf_slice = sdf_vals.reshape(grid.dimensions)[:, grid_density // 2 + 10, :]
plt.figure()
plt.contour(
sdf_slice,
levels=np.linspace(np.min(sdf_slice), np.max(sdf_slice), 10),
colors='k',
extent=[-grid_width, grid_width, -grid_width, grid_width],
)
plt.imshow(
np.flipud(sdf_slice),
extent=[-grid_width, grid_width, -grid_width, grid_width],
cmap='RdBu',
)
mrv.texit('Signed Distance Field', 'x', 'y', grid=False)
plt.colorbar(label='Signed Distance', cax=mrv.get_cbar_ax())
plt.tight_layout()
plt.show()

Plotting the full 3D SDF
pl = pv.Plotter(window_size=(1000, 1000))
for cval in np.linspace(np.min(sdf_vals), -np.min(sdf_vals), 7):
mesh1, mesh2 = grid.contour([cval], sdf_vals, method='flying_edges').clip(
'y', origin=(0.0, 0.0, 0.0), return_clipped=True
)
if mesh1.points.shape[0] > 0:
pl.add_mesh(
mesh1,
opacity=1.0,
scalars=cval * np.ones(mesh1.n_points),
cmap='coolwarm',
scalar_bar_args=dict(title='SDF Value'),
clim=[np.min(sdf_vals), -np.min(sdf_vals)],
smooth_shading=True,
)
pl.camera.position = (0.0, 0.6, 0.0)
pl.show()

Animating an orbital path around the SDF
pl = pv.Plotter()
for cval in np.linspace(np.min(sdf_vals), -np.min(sdf_vals), 10):
mesh = grid.contour([cval], sdf_vals, method='flying_edges')
if mesh.points.shape[0] > 0:
pl.add_mesh(
mesh,
opacity=0.1,
scalars=cval * np.ones(mesh.n_points),
cmap='coolwarm',
scalar_bar_args=dict(title='SDF Value'),
)
pl.open_gif('sdf_orbit.gif')
path = pl.generate_orbital_path(n_points=36, shift=mesh.length / 3)
pl.orbit_on_path(path, write_frames=True)

Total running time of the script: (0 minutes 3.860 seconds)