API Reference

Functions

isoext.marching_cubes(grid: isoext.isoext_ext.Grid, level: float = 0.0, method: str = 'nagae') tuple

Extract an iso-surface from a grid using the Marching Cubes algorithm.

Parameters:
  • grid – The input grid (UniformGrid or SparseGrid) containing scalar values.

  • level – The iso-value at which to extract the surface. Default is 0.0.

  • method – The marching cubes variant to use. Options are ‘nagae’ (default) or ‘lorensen’.

Returns:

A tuple (vertices, faces) where vertices is an (N, 3) float32 tensor of vertex positions and faces is an (M, 3) int32 tensor of triangle indices.

isoext.dual_contouring(grid: isoext.isoext_ext.Grid, level: float = 0.0, intersection: isoext.isoext_ext.Intersection | None = None, reg: float = 0.009999999776482582, svd_tol: float = 9.999999974752427e-07) tuple

Extract an iso-surface using the Dual Contouring algorithm.

Dual Contouring produces meshes with better-placed vertices than Marching Cubes, especially for sharp features. It solves a QEF (Quadric Error Function) per cell to find optimal vertex positions.

Parameters:
  • grid – The input grid containing scalar values.

  • level – The iso-value. Default is 0.0.

  • intersection – Optional Intersection data from get_intersection(). If not provided, intersections are computed automatically. If provided but normals not set, normals are computed automatically.

  • reg – Regularization weight for the QEF solver. Default is 0.01.

  • svd_tol – SVD tolerance for the QEF solver. Default is 1e-6.

Returns:

A tuple (vertices, faces) where vertices is an (N, 3) float32 tensor and faces is an (M, 3) int32 tensor of triangle indices.

isoext.get_intersection(grid: isoext.isoext_ext.Grid, level: float = 0.0, compute_normals: bool = False) isoext.isoext_ext.Intersection

Compute edge-surface intersections for dual contouring.

Finds where grid edges cross the iso-surface and computes intersection points using linear interpolation.

Parameters:
  • grid – The input grid containing scalar values.

  • level – The iso-value. Default is 0.0.

  • compute_normals – If True, compute normals from grid values. Default is False.

Returns:

An Intersection object containing points (and normals if compute_normals=True).

isoext.make_grid(aabb, res, device='cuda')

Create a uniform grid from an axis-aligned bounding box.

Parameters:
  • aabb (list[float]) – Axis-aligned bounding box as [x_min, y_min, z_min, x_max, y_max, z_max]

  • res (int | list[int]) – Resolution, either a single int or list of 3 ints for [x_res, y_res, z_res]

  • device (str) – Device to create the grid on (default: “cuda”)

Returns:

Grid tensor with shape (x_res, y_res, z_res, 3)

Return type:

Tensor

isoext.write_obj(obj_path, v, f)

Write vertices and faces to an OBJ file.

Parameters:
  • obj_path (str) – Path to the output OBJ file

  • v (Tensor) – Tensor of vertices with shape (N, 3)

  • f (Tensor) – Tensor of face indices with shape (M, 3)

Return type:

None

Viewer

Interactive visualization built on viser.

Interactive mesh viewing and scene export built on viser.

Typical use:

import isoext
from isoext import viewer

v, f = isoext.marching_cubes(grid)
server = viewer.show(v, f)   # open an interactive viewer in the browser
viewer.embed(v, f)           # inline scene for (statically hosted) notebooks
isoext.viewer.add_mesh(server, vertices, faces, *, name='/mesh', color=(0.71, 0.8, 1.0), flat_shading=False, wireframe=False)

Add a mesh from isoext output tensors to a viser scene.

Parameters:
  • server – A viser.ViserServer instance.

  • vertices (Tensor) – (N, 3) tensor of vertex positions.

  • faces (Tensor) – (M, 3) tensor of triangle indices.

  • name (str) – Scene tree name of the mesh.

  • color – RGB tuple with components in [0, 1], or a color name.

  • flat_shading (bool) – Shade each triangle with a constant normal.

  • wireframe (bool) – Render the mesh as a wireframe.

Returns:

The viser mesh handle.

isoext.viewer.show(vertices, faces, *, port=8080, **mesh_kwargs)

Open an interactive viewer serving the given mesh.

The server keeps running until it is stopped or the process exits; the printed URL can be opened in any browser.

Parameters:
  • vertices (Tensor) – (N, 3) tensor of vertex positions.

  • faces (Tensor) – (M, 3) tensor of triangle indices.

  • port (int) – Port to serve on (the next free port is used if taken).

  • **mesh_kwargs – Forwarded to add_mesh.

Returns:

The running viser.ViserServer; call .stop() to shut it down.

isoext.viewer.serialize_scene(vertices, faces, **mesh_kwargs)

Serialize a scene containing the given mesh to .viser bytes.

The bytes can be written to a .viser file and played back offline by viser’s static client, e.g. embedded in a web page. See save_scene and embed for convenience wrappers.

Parameters:
  • vertices (Tensor)

  • faces (Tensor)

Return type:

bytes

isoext.viewer.save_scene(path, vertices, faces, **mesh_kwargs)

Serialize a scene with the given mesh and write it to a .viser file.

Parameters:
  • vertices (Tensor)

  • faces (Tensor)

Return type:

None

isoext.viewer.copy_client(directory)

Copy viser’s single-file static web client into a directory.

The client is a self-contained index.html that plays back .viser scene files passed via its ?playbackPath= URL parameter. The copy is refreshed whenever the installed viser ships a different build.

Returns:

The path of the copied index.html.

Return type:

Path

isoext.viewer.embed(vertices, faces, *, root='_static', height=420, **mesh_kwargs)

Display a mesh as a self-contained interactive scene in a notebook.

Unlike show, which starts a live server, this writes static assets – the viser client to root/viser/ and the serialized scene to root/scenes/ – and returns an IFrame referencing them with relative URLs. Rendered notebooks therefore stay interactive when hosted statically, for example on documentation pages.

Parameters:
  • vertices (Tensor) – (N, 3) tensor of vertex positions.

  • faces (Tensor) – (M, 3) tensor of triangle indices.

  • root – Directory for the static assets, relative to the notebook.

  • height (int) – Height of the embedded viewer in pixels.

  • **mesh_kwargs – Forwarded to add_mesh.

Returns:

An IPython IFrame displaying the scene.

Classes

UniformGrid

class isoext.UniformGrid(shape, aabb_min=[-1, -1, -1], aabb_max=[1, 1, 1], default_value=float_max)

A dense uniform grid for storing scalar values.

The grid divides a 3D axis-aligned bounding box into a regular lattice of cells. Each cell has 8 corner points where scalar values are stored.

Parameters:
  • shape (Sequence[int]) – The number of cells in each dimension (x, y, z).

  • aabb_min (Sequence[float]) – The minimum corner of the bounding box.

  • aabb_max (Sequence[float]) – The maximum corner of the bounding box.

  • default_value (float) – Initial scalar value for all points.

UniformGrid.get_points(self) torch.Tensor[dtype=float32, order='C', device='cuda']

Return the 3D coordinates of all grid points as a (X, Y, Z, 3) float32 tensor.

UniformGrid.get_values(self) torch.Tensor[dtype=float32, order='C', device='cuda']

Return the scalar values as a (X, Y, Z) float32 tensor.

UniformGrid.set_values(self, new_values: torch.Tensor[dtype=float32, shape=(*, *, *), order='C', device='cuda']) None

Set the scalar values from a (X, Y, Z) float32 tensor.

SparseGrid

class isoext.SparseGrid(shape, aabb_min=[-1, -1, -1], aabb_max=[1, 1, 1], default_value=float_max)

A sparse adaptive grid for storing scalar values.

Unlike UniformGrid, SparseGrid only allocates memory for cells that are explicitly added. This is useful for large domains where only a small region contains the iso-surface.

Parameters:
  • shape (Sequence[int]) – The maximum number of cells in each dimension (x, y, z).

  • aabb_min (Sequence[float]) – The minimum corner of the bounding box.

  • aabb_max (Sequence[float]) – The maximum corner of the bounding box.

  • default_value (float) – Default scalar value for unset points.

SparseGrid.get_num_cells(self) int

Return the number of active cells in the grid.

SparseGrid.get_num_points(self) int

Return the number of points in active cells (num_cells * 8).

SparseGrid.get_points(self) torch.Tensor[dtype=float32, order='C', device='cuda']

Return the 3D coordinates of points in active cells as an (N, 8, 3) float32 tensor.

SparseGrid.get_values(self) torch.Tensor[dtype=float32, order='C', device='cuda']

Return the scalar values at active cell corners as an (N, 8) float32 tensor.

SparseGrid.set_values(self, new_values: torch.Tensor[dtype=float32, shape=(*, 8), order='C', device='cuda']) None

Set the scalar values from an (N, 8) float32 tensor.

SparseGrid.get_cells(self) torch.Tensor[dtype=uint32, order='C', device='cuda']

Return the cell connectivity as point indices.

SparseGrid.add_cells(self, new_cell_indices: torch.Tensor[dtype=int32, shape=(*), order='C', device='cuda']) None

Add cells to the grid by their linear indices.

Parameters:

new_cell_indices – 1D int32 tensor of cell indices to add.

SparseGrid.remove_cells(self, new_cell_indices: torch.Tensor[dtype=int32, shape=(*), order='C', device='cuda']) None

Remove cells from the grid by their linear indices.

Parameters:

new_cell_indices – 1D int32 tensor of cell indices to remove.

SparseGrid.get_cell_indices(self) torch.Tensor[dtype=int32, order='C', device='cuda']

Return the linear indices of all active cells as a 1D int32 tensor.

SparseGrid.get_potential_cell_indices(self, arg: int, /) list[torch.Tensor[dtype=int32, order='C', device='cuda']]

Get potential cell indices in chunks for memory-efficient processing.

Parameters:

chunk_size – Maximum number of cells per chunk.

Returns:

A list of 1D int32 tensors, each containing cell indices for a chunk.

SparseGrid.get_points_by_cell_indices(self, cell_indices: torch.Tensor[dtype=int32, shape=(*), order='C', device='cuda']) torch.Tensor[dtype=float32, order='C', device='cuda']

Get the corner points of specified cells.

Parameters:

cell_indices – 1D int32 tensor of cell indices.

Returns:

An (N, 8, 3) float32 tensor of corner coordinates.

SparseGrid.filter_cell_indices(self, cell_indices: torch.Tensor[dtype=int32, shape=(*), order='C', device='cuda'], values: torch.Tensor[dtype=float32, shape=(*, 8), order='C', device='cuda'], level: float = 0.0) torch.Tensor[dtype=int32, order='C', device='cuda']

Filter cells to keep only those that cross the iso-surface.

Parameters:
  • cell_indices – 1D int32 tensor of cell indices to filter.

  • values – (N, 8) float32 tensor of scalar values at cell corners.

  • level – The iso-value to check against. Default is 0.0.

Returns:

A 1D int32 tensor of cell indices that cross the iso-surface.

Intersection

class isoext.Intersection

Stores edge-surface intersection points and normals for dual contouring.

Created by get_intersection() and used as input to dual_contouring(). You can modify the normals to control the surface reconstruction.

get_normals

Return the surface normals at intersection points as an (N, 3) float32 tensor.

get_points

Return the intersection points as an (N, 3) float32 tensor.

has_normals

Return True if normals have been set or computed.

set_normals

Set custom normals for the intersection points.

Parameters:

new_normals – (N, 3) float32 tensor of normal vectors.