SDF grinding in Newton
A small example of SDF-SDF contact and geometric material removal. It does not run a dynamics solver.
Overview
- The workpiece is a static ellipsoid stored as a texture SDF.
- The grinder is a kinematic cylinder. Both shapes have hydroelastic contact enabled.
- Each frame calls the hydroelastic collision pipeline directly.
- ViewerGL renders the contact surface and plots its integrated pressure.
- The grinder volume is subtracted from the workpiece with an SDF CSG difference.
- The zero isosurface is rebuilt with Marching Cubes and logged as a dynamic ViewerGL mesh.
Video
ViewerGL shows the moving tool, updated workpiece, pressure-colored contact field, and integrated normal-load plot.
Set up SDF-SDF contact
- Build the workpiece SDF explicitly so its samples can be edited later.
- Set
is_hydroelastic=Trueon both shape configurations. - Request the contact surface for ViewerGL.
- No solver is required to call the collision pipeline.
workpiece_sdf = workpiece_mesh.build_sdf(
max_resolution=256,
narrow_band_range=(-0.08, 0.08),
texture_format="float32",
paired_samples=False,
)
shape_cfg = newton.ModelBuilder.ShapeConfig(
is_hydroelastic=True,
kh=1.0e8,
gap=0.005,
margin=0.0,
)
pipeline = newton.CollisionPipeline(
model,
sdf_hydroelastic_config=newton.geometry.HydroelasticSDF.Config(
output_contact_surface=True,
),
)
contacts = pipeline.contacts()
pipeline.collide(state, contacts)
viewer.show_hydro_contact_surface = True
viewer.log_hydro_contact_surface(
pipeline.hydroelastic_sdf.get_contact_surface()
)
Remove material
- For an SDF difference, use
max(workpiece_distance, -tool_distance). - Apply the operation to the coarse samples and every allocated fine subgrid sample.
- Copy the modified arrays back into the
Texture3Dobjects. - Extract and log a new surface after each edit.
point_in_tool = wp.transform_point(
wp.transform_inverse(grinder_xform), point
)
tool_distance = _sdf_cylinder_z(
point_in_tool, grinder_radius, grinder_half_width
)
sdf_values[z, y, x] = wp.max(sdf_values[z, y, x], -tool_distance)
texture_data.coarse_texture.copy_from(coarse_values)
texture_data.subgrid_texture.copy_from(subgrid_values)
surface = workpiece_sdf.extract_isomesh(device=model.device)
viewer.log_mesh(
"/workpiece_sdf", points, indices, dynamic=True
)
Notes
- This is geometric removal only. Contact forces do not drive the grinder.
- The built-in contact-surface color is based on penetration depth. With the same linear stiffness on both shapes, it is proportional to pressure.
- The plotted value is integrated pressure, or normal load:
sum(max(-distance * stiffness, 0)). - A physical wear model could scale removal by pressure, slip speed, and time step.
- Higher SDF resolution increases both the number of edited samples and the cost of rebuilding the Marching Cubes mesh.
- The example requires a CUDA-capable device.
Run
cd <newton-checkout>
uv sync --extra examples
uv run python <path-to-download>/example_sdf_grinding.py
Full source
Loading example_sdf_grinding.py…