XelToFab
API Reference

process_from_sdf

Evaluate an SDF function and run the full pipeline

Import

from xeltofab import process_from_sdf
# or
from xeltofab.pipeline import process_from_sdf

Signature

def process_from_sdf(
    sdf_fn: SDFFunction,
    bounds: Bounds3D,
    resolution: int = 128,
    adaptive: bool = False,
    extraction_method: Literal["mc", "dc", "surfnets", "manifold"] = "dc",
    chunk_size: int | None = None,
    **pipeline_kwargs,
) -> PipelineState

Parameters

ParameterTypeDefaultDescription
sdf_fnCallable[[ndarray], ndarray]requiredSDF function: [N, 3] float64 → [N] float64 signed distances
boundstuple[float, ...]required(xmin, ymin, zmin, xmax, ymax, zmax) spatial region
resolutionint128Cells along the longest bounding box axis; shorter axes proportional
adaptiveboolFalseUse octree-accelerated evaluation — reduces evaluations from O(N³) to ~O(N²) by culling cells far from the surface
extraction_methodstr"dc"Extraction backend: mc, dc, surfnets, manifold
chunk_sizeint | NoneNoneMax points per sdf_fn call. None = entire Z-slab at once
**pipeline_kwargsForwarded to PipelineParams (e.g., smoothing_method, decimate_ratio)

Return value

Returns a PipelineState with extracted and post-processed mesh, identical to what process() returns.

What it does

process_from_sdf is the entry point for SDF functions (neural models, analytical formulas, or any callable). It:

  1. Validates the bounds (6 floats, each min < max)
  2. Evaluates the SDF function on a uniform grid (one Z-slab at a time for memory efficiency)
  3. Validates the first slab's output (shape, NaN/inf, dtype)
  4. Runs the full pipeline — extract → smooth → repair → remesh → decimate

The SDF function receives points as [N, 3] arrays in (x, y, z) order and must return [N] signed distances. Internally, the grid is stored as [Nz, Ny, Nx] to match the existing extraction pipeline.

Resolution semantics

resolution specifies cells along the longest bounding box axis. Shorter axes get proportionally fewer cells, preserving aspect ratio. For example, bounds=(0, 0, 0, 2, 1, 1) with resolution=128 produces a 128 x 64 x 64 grid.

Smart defaults

Since the input is an SDF function, process_from_sdf automatically sets field_type="sdf", which triggers the same smart defaults as grid-based SDF processing:

  • Direct extraction (no preprocessing)
  • Extraction level at 0.0 (zero level set)
  • Dual Contouring as the default extraction method (which in turn triggers bilateral smoothing with reduced Taubin iterations)

Adaptive evaluation

When adaptive=True, the evaluator uses octree-accelerated coarse-to-fine refinement instead of evaluating every grid point:

  1. Evaluate SDF at a coarse grid (resolution / 8)
  2. Cull cells far from the zero level set using a Lipschitz bound
  3. Subdivide only near-surface cells and repeat for log2(coarse_factor) levels
  4. Evaluate remaining fine cells at the target resolution

This reduces evaluations from O(N³) to ~O(N²). Unevaluated regions far from the surface are filled with +1.0 (positive = outside). If the entire domain is inside or outside the surface (no zero crossing found), the fill value is determined by evaluating the SDF at the domain center — avoiding false surfaces.

Known limitations of adaptive mode

  • Grid resolution may differ slightly from the requested resolution due to rounding during coarse cell computation (e.g., resolution=32 may produce a 33³ grid). The returned coordinate arrays reflect the actual grid dimensions.
  • The output is an extraction cache, not a true SDF. Deep interior regions are filled with +1.0 regardless of the actual SDF sign. This is correct for mesh extraction (MC/DC ignore these regions) but means octree_evaluate() output should not be used as a general-purpose SDF field.
  • Mesh vertices are in grid-index coordinates, not world-space. This is consistent with all extraction methods in the pipeline (MC, DC, SurfNets, manifold3d).

For finer control, use octree_evaluate() directly from xeltofab.sdf_eval:

ParameterTypeDefaultDescription
coarse_factorint8Fine-to-coarse ratio. Must be a power of 2.
lipschitzfloat1.1Culling threshold. Cells kept when min(|SDF|) < lipschitz * cell_diagonal

See the SDF Functions guide for details on when adaptive evaluation helps.

Examples

Analytical SDF

import numpy as np
from xeltofab import process_from_sdf, save_mesh

def sphere_sdf(points: np.ndarray) -> np.ndarray:
    return np.linalg.norm(points, axis=1) - 1.0

result = process_from_sdf(sphere_sdf, bounds=(-2, -2, -2, 2, 2, 2), resolution=64)
save_mesh(result, "sphere.stl")

PyTorch neural SDF

import numpy as np
import torch
from xeltofab import process_from_sdf, save_mesh

model = torch.load("nito_bridge.pt")

def bridge_sdf(points: np.ndarray) -> np.ndarray:
    with torch.no_grad():
        t = torch.from_numpy(points).float().cuda()
        return model(t).squeeze(-1).cpu().numpy()

result = process_from_sdf(bridge_sdf, bounds=(-1, -1, -1, 1, 1, 1), resolution=256)
save_mesh(result, "bridge.stl")

ONNX model (framework-free)

import numpy as np
import onnxruntime as ort
from xeltofab import process_from_sdf, save_mesh

session = ort.InferenceSession("model.onnx")

def onnx_sdf(points: np.ndarray) -> np.ndarray:
    # flatten() ensures [N] output even if model returns [N, 1]
    return session.run(None, {"points": points.astype(np.float32)})[0].flatten()

result = process_from_sdf(onnx_sdf, bounds=(-1, -1, -1, 1, 1, 1), resolution=256)
save_mesh(result, "output.stl")

Custom pipeline parameters

result = process_from_sdf(
    my_sdf,
    bounds=(-1, -1, -1, 1, 1, 1),
    resolution=128,
    extraction_method="mc",          # use Marching Cubes instead of DC
    smoothing_method="taubin",       # override bilateral default
    decimate_ratio=0.3,              # aggressive decimation
)

Adaptive evaluation (octree)

# Large grid with expensive neural SDF — adaptive skips far-from-surface cells
result = process_from_sdf(
    neural_sdf,
    bounds=(-1, -1, -1, 1, 1, 1),
    resolution=256,
    adaptive=True,
    chunk_size=50000,
)

For advanced octree control (coarse_factor, lipschitz):

from xeltofab.sdf_eval import octree_evaluate
from xeltofab.pipeline import process
from xeltofab.state import PipelineParams, PipelineState

grid, x, y, z = octree_evaluate(
    my_sdf,
    bounds=(-1, -1, -1, 1, 1, 1),
    resolution=256,
    coarse_factor=16,   # 4 refinement levels
    lipschitz=1.5,      # more conservative culling
)

params = PipelineParams(field_type="sdf")
state = PipelineState(field=grid, params=params)
result = process(state)

See also

Outline