Isosurface: marching cubes & metaballs

Pure-Python port of the 3-D core of BOSL2’s isosurface.scad: VNF.from_field() meshes the level set of a scalar field over a voxel grid (marching cubes) into a VNF; the mb_* functions are metaball field primitives; and VNF.from_metaballs() sums transformed primitives and meshes the result into a blobby surface:

VNF.from_field(field_fn, isovalue=1, bounding_box=60, voxel_size=2)
VNF.from_metaballs([(pos1, mb_sphere(12)), (pos2, mb_sphere(12))], bounding_box=box, voxel_size=2)

A field primitive returns a value that grows toward infinity at its center and falls off with distance; the surface is drawn where the summed field reaches isovalue (default 1). Because the fields add, overlapping metaballs bulge together into a smooth blob. The mb_* formulas are pinned point-for-point to real BOSL2 in tests/test_pybosl2_reorient.py; the meshes are watertight and verified geometrically.

The mesher uses the standard Paul Bourke marching-cubes triangle table, so its triangulation isn’t vertex-identical to BOSL2’s, but the surface it encloses is the same; face winding is fixed to outward via the VNF’s signed volume.

Coverage of BOSL2 isosurface.scad

BOSL2 function

Status

Notes

isosurface

ported

VNF.from_field() – marching cubes over a field callable or a precomputed 3-D array; voxel_size/voxel_count, closed, reverse, range isovalues [lo, hi] (collapsed to a one-sided threshold).

metaballs

ported

VNF.from_metaballs() – a list of (transform, metaball) pairs (or the BOSL2 flat form).

mb_sphere / mb_cuboid / mb_torus / mb_capsule / mb_disk / mb_octahedron / mb_connector

ported

the 3-D metaball field primitives, with cutoff / influence / negative.

mb_cyl

not ported

the revolved-profile (cone/rounded) field – a follow-up.

contour / metaballs2d / mb_circle / mb_rect / mb_trapezoid / mb_stadium / mb_ring / mb_connector2d

not ported

the 2-D analogues (marching squares) – a follow-up.

debug views, anchor/spin/orient

not ported

preview/attachment machinery.

Examples

Two spheres merging into a peanut:

from pybosl2 import VNF, mb_sphere

spec = [([-14, 0, 0], mb_sphere(12)), ([14, 0, 0], mb_sphere(12))]
VNF.from_metaballs(spec, bounding_box=[[-40, -20, -20], [40, 20, 20]], voxel_size=2).polyhedron().show()
Loading 3-D preview…

⬇ Download STL mesh

A ring metaball plus a connecting bar:

from pybosl2 import VNF, mb_connector, mb_torus

spec = [([0, 0, 0], mb_torus(14, 4)), ([-14, 0, 0], mb_connector([-14, 0, 0], [14, 0, 0], 4))]
VNF.from_metaballs(spec, bounding_box=[[-22, -22, -10], [22, 22, 10]], voxel_size=2).polyhedron().show()
Loading 3-D preview…

⬇ Download STL mesh

The level set of a custom field function:

from pybosl2 import VNF

def field(p):
    import numpy as np
    d = np.sqrt(p[:, 0]**2 + p[:, 1]**2 + p[:, 2]**2)
    return 18 / d + 3 * np.sin(p[:, 0] / 3) * np.cos(p[:, 1] / 3)
VNF.from_field(field, 1, bounding_box=70, voxel_size=2.5).polyhedron().show()
Loading 3-D preview…

⬇ Download STL mesh

API reference

Metaball field primitives for VNF isosurface meshing (BOSL2 metaballs3d.scad).

pybosl2.isosurface.MetaballSpec

alias of _MetaballSpec

pybosl2.isosurface.mb_sphere(radius=None, cutoff=inf, influence=1, negative=False, diameter=None)[source]

Return a spherical metaball field.

Parameters:
radius : float | None

Sphere radius (mutually exclusive with diameter).

cutoff : float

Distance beyond which the field is clamped to 0. inf = no cutoff.

influence : float

Blending strength (smaller = sharper).

negative : bool

If True, produce a subtractive metaball.

diameter : float | None

Sphere diameter.

Returns:

A _Metaball primitive.

Raises:

AssertionError – If no positive radius or diameter is given.

Return type:

_Metaball

Examples

from pybosl2 import mb_sphere, MetaballSpec
from pybosl2.bounds import Bounds3D
from pybosl2.vnf import VNF

spec = [MetaballSpec([0, 0, 0], mb_sphere(radius=15))]
VNF.from_metaballs(
    spec, Bounds3D(-20, -20, -20, 20, 20, 20, 40, 40, 40), voxel_size=2
).polyhedron().show()
Loading 3-D preview…

⬇ Download STL mesh

pybosl2.isosurface.mb_cuboid(size, squareness=0.5, cutoff=inf, influence=1, negative=False)[source]

Return a rounded-cuboid metaball field.

Parameters:
size : tuple[float, float, float] | float

A scalar (cube edge) or (dx, dy, dz) tuple.

squareness : float

0 = fully round, 1 = sharp square edges.

cutoff : float

Distance beyond which the field is clamped to 0.

influence : float

Blending strength.

negative : bool

If True, produce a subtractive metaball.

Returns:

A _Metaball primitive.

Raises:

AssertionError – If squareness is not in [0, 1].

Return type:

_Metaball

Examples

from pybosl2 import mb_cuboid, MetaballSpec
from pybosl2.bounds import Bounds3D
from pybosl2.vnf import VNF

spec = [MetaballSpec([-12, 0, 0], mb_cuboid(size=10, squareness=0.3)),
        MetaballSpec([12, 0, 0], mb_cuboid(size=10, squareness=0.3))]
VNF.from_metaballs(
    spec, Bounds3D(-25, -15, -15, 25, 15, 15, 50, 30, 30), voxel_size=2
).polyhedron().show()
Loading 3-D preview…

⬇ Download STL mesh

pybosl2.isosurface.mb_torus(major_radius=None, minor_radius=None, cutoff=inf, influence=1, negative=False, major_diameter=None, minor_diameter=None)[source]

Return a torus metaball field.

Parameters:
major_radius : float | None

Distance from the origin to the tube centre.

minor_radius : float | None

Tube radius.

cutoff : float

Distance beyond which the field is clamped to 0.

influence : float

Blending strength.

negative : bool

If True, produce a subtractive metaball.

major_diameter : float | None

Overrides major_radius.

minor_diameter : float | None

Overrides minor_radius.

Returns:

A _Metaball primitive.

Raises:

AssertionError – If either radius is missing or non-positive.

Return type:

_Metaball

Examples

from pybosl2 import mb_torus, MetaballSpec
from pybosl2.bounds import Bounds3D
from pybosl2.vnf import VNF

spec = [MetaballSpec([0, 0, 0], mb_torus(major_radius=15, minor_radius=5))]
VNF.from_metaballs(
    spec, Bounds3D(-20, -20, -10, 20, 20, 10, 40, 40, 20), voxel_size=2
).polyhedron().show()
Loading 3-D preview…

⬇ Download STL mesh

pybosl2.isosurface.mb_capsule(height=None, radius=None, cutoff=inf, influence=1, negative=False, diameter=None)[source]

Return a capsule (round-ended cylinder) metaball field.

Parameters:
height : float | None

Total length including rounded ends.

radius : float | None

Shaft radius.

cutoff : float

Distance beyond which the field is clamped to 0.

influence : float

Blending strength.

negative : bool

If True, produce a subtractive metaball.

diameter : float | None

Shaft diameter.

Returns:

A _Metaball primitive.

Raises:

AssertionError – If height or radius is missing, non-positive, or shaft too short.

Return type:

_Metaball

pybosl2.isosurface.mb_disk(height=None, radius=None, cutoff=inf, influence=1, negative=False, diameter=None)[source]

Return a rounded-edge disk metaball field.

Parameters:
height : float | None

Disk thickness.

radius : float | None

Outer radius.

cutoff : float

Distance beyond which the field is clamped to 0.

influence : float

Blending strength.

negative : bool

If True, produce a subtractive metaball.

diameter : float | None

Outer diameter.

Returns:

A _Metaball primitive.

Raises:

AssertionError – If height or radius is missing, non-positive, or too thin.

Return type:

_Metaball

pybosl2.isosurface.mb_octahedron(size, squareness=0.5, cutoff=inf, influence=1, negative=False)[source]

Return a rounded-octahedron metaball field.

Parameters:
size : tuple[float, float, float] | float

A scalar (circumscribed cube edge) or (dx, dy, dz) tuple.

squareness : float

0 = round, 1 = sharp octahedron edges.

cutoff : float

Distance beyond which the field is clamped to 0.

influence : float

Blending strength.

negative : bool

If True, produce a subtractive metaball.

Returns:

A _Metaball primitive.

Raises:

AssertionError – If squareness is not in [0, 1].

Return type:

_Metaball

pybosl2.isosurface.mb_connector(p1, p2, radius=None, cutoff=inf, influence=1, negative=False, diameter=None)[source]

Return a capsule metaball field spanning from p1 to p2.

Parameters:
p1 : Point

Start Point.

p2 : Point

End Point (must be distinct from p1).

radius : float | None

Shaft radius.

cutoff : float

Distance beyond which the field is clamped to 0.

influence : float

Blending strength.

negative : bool

If True, produce a subtractive metaball.

diameter : float | None

Shaft diameter.

Returns:

A _Metaball primitive.

Raises:

AssertionError – If radius is missing, non-positive, or p1 equals p2.

Return type:

_Metaball

pybosl2.isosurface.metaballs2d(spec, bounding_box, pixel_size=None, pixel_count=None, isovalue=1, closed=True, exact_bounds=False)[source]

Generate 2-D contour paths from metaball field primitives.

The metaball spec uses the same 3-D transforms and field primitives as VNF.from_metaballs(), but evaluated on the z=0 plane to produce a 2-D contour via marching squares.

Parameters:
spec : list[_MetaballSpec]

A list of _MetaballSpec entries, each holding a 4×4 transform and a _Metaball.

bounding_box : Bounds2D

A Bounds2D.

pixel_size : float | None

Isotropic pixel size.

pixel_count : int | None

Approximate total pixel count.

isovalue : float

Field threshold. Defaults to 1.

closed : bool

If True, return only closed contour loops.

exact_bounds : bool

If True, use bounding_box exactly.

Returns:

A list of contour paths, each a list of [x, y] points.

Return type:

list[list[list[float]]]

Examples

import numpy as np
from pybosl2 import mb_sphere, MetaballSpec, metaballs2d, Bounds2D
from pybosl2.path2d import Path2D

spec = [
    MetaballSpec([-14, 0, 0], mb_sphere(12)),
    MetaballSpec([14, 0, 0], mb_sphere(12)),
]
paths = metaballs2d(spec, Bounds2D(-40, -20, 40, 20, 80, 40), pixel_size=2)
Path2D(paths[0]).stroke(width=0.5).linear_extrude(height=2).show()
Loading 3-D preview…

⬇ Download STL mesh

pybosl2.isosurface.Metaball

alias of _Metaball