Isosurface: marching cubes & metaballs¶
Pure-Python port of the 3-D core of BOSL2’s isosurface.scad:
isosurface() 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 metaballs() sums transformed primitives and meshes the
result into a blobby surface:
isosurface(field_fn, isovalue=1, bounding_box=60, voxel_size=2)
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_bosl2_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 |
|---|---|---|
|
ported |
|
|
ported |
|
|
ported |
the 3-D metaball field primitives, with |
|
not ported |
the revolved-profile (cone/rounded) field – a follow-up. |
|
not ported |
the 2-D analogues (marching squares) – a follow-up. |
|
not ported |
preview/attachment machinery. |
Examples¶
Two spheres merging into a peanut:
spec = [([-14, 0, 0], mb_sphere(12)), ([14, 0, 0], mb_sphere(12))]
metaballs(spec, bounding_box=[[-40, -20, -20], [40, 20, 20]], voxel_size=2).polyhedron().show()
A ring metaball plus a connecting bar:
spec = [([0, 0, 0], mb_torus(14, 4)), ([-14, 0, 0], mb_connector([-14, 0, 0], [14, 0, 0], 4))]
metaballs(spec, bounding_box=[[-22, -22, -10], [22, 22, 10]], voxel_size=2).polyhedron().show()
The level set of a custom field function:
def field(p):
import numpy as np
diameter=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)
isosurface(field, 1, bounding_box=70, voxel_size=2.5).polyhedron().show()
API reference¶
- bosl2.isosurface.isosurface(f, isovalue, bounding_box=None, voxel_size=None, voxel_count=None, closed=True, reverse=False, exact_bounds=False)[source]¶
Mesh the level set of a scalar field f at isovalue into a VNF (BOSL2 isosurface()).
The solid is the region where
f >= isovalue(a single number) or, for a range[lo, hi], wherelo <= f <= hicollapses tof >= lo(hiunbounded) orf <= hi(lounbounded, i.e. reversed). f is a callable (vectorised(N,3)->(N,)or scalarpoint->value) or a precomputed 3-D array. Give voxel_size or voxel_count to control resolution; the bounding box grows to whole voxels unless exact_bounds.- Returns:
A
VNF.- Parameters:
closed (bool)
reverse (bool)
Examples
The level set of a wobbly sphere field:
def field(p): import numpy as np x, y, z = p[:, 0], p[:, 1], p[:, 2] return 20 / np.sqrt(x*x + y*y + z*z) + 3 * np.sin(x / 3) isosurface(field, 1, bounding_box=60, voxel_size=2).polyhedron().show()
- bosl2.isosurface.metaballs(spec, bounding_box, voxel_size=None, voxel_count=None, isovalue=1, closed=True, exact_bounds=False)[source]¶
Mesh a set of transformed metaball field primitives into a blobby surface (BOSL2 metaballs()).
spec is a list of
(transform, metaball)pairs (or the BOSL2 flat[transform, metaball, ...]form), where transform is a 4x4 matrix or a 3-vector position and metaball comes frommb_sphere/mb_cuboid/mb_torus/mb_capsule/mb_disk/mb_octahedron/mb_connector. The fields sum, and the surface is drawn where the total reaches isovalue.- Returns:
A
VNF.- Parameters:
closed (bool)
Examples
Two spheres merging into a peanut:
spec = [([-14, 0, 0], mb_sphere(12)), ([14, 0, 0], mb_sphere(12))] metaballs(spec, bounding_box=[[-40, -20, -20], [40, 20, 20]], voxel_size=2).polyhedron().show()
- bosl2.isosurface.mb_sphere(radius=None, cutoff=inf, influence=1, negative=False, diameter=None)[source]¶
A spherical metaball field of radius radius (BOSL2 mb_sphere()).
- Parameters:
radius (float | None)
cutoff (float)
influence (float)
negative (bool)
diameter (float | None)
- bosl2.isosurface.mb_cuboid(size, squareness=0.5, cutoff=inf, influence=1, negative=False)[source]¶
A rounded-cuboid metaball field (BOSL2 mb_cuboid()). squareness 0..1: 0 round, 1 square.
- Parameters:
cutoff (float)
influence (float)
negative (bool)
- bosl2.isosurface.mb_torus(major_radius=None, minor_radius=None, cutoff=inf, influence=1, negative=False, major_diameter=None, minor_diameter=None)[source]¶
A torus metaball field, major radius major_radius, tube radius minor_radius (BOSL2 mb_torus()).
- Parameters:
cutoff (float)
influence (float)
negative (bool)
- bosl2.isosurface.mb_capsule(height=None, radius=None, cutoff=inf, influence=1, negative=False, diameter=None)[source]¶
A capsule (round-ended cylinder) metaball field, total length height, radius radius (BOSL2 mb_capsule()).
- Parameters:
height (float | None)
radius (float | None)
cutoff (float)
influence (float)
negative (bool)
diameter (float | None)
- bosl2.isosurface.mb_disk(height=None, radius=None, cutoff=inf, influence=1, negative=False, diameter=None)[source]¶
A rounded-edge disk metaball field, thickness height, outer radius radius (BOSL2 mb_disk()).
- Parameters:
height (float | None)
radius (float | None)
cutoff (float)
influence (float)
negative (bool)
diameter (float | None)
- bosl2.isosurface.mb_octahedron(size, squareness=0.5, cutoff=inf, influence=1, negative=False)[source]¶
A rounded-octahedron metaball field (BOSL2 mb_octahedron()).
- Parameters:
cutoff (float)
influence (float)
negative (bool)
- bosl2.isosurface.mb_connector(p1, p2, radius=None, cutoff=inf, influence=1, negative=False, diameter=None)[source]¶
A capsule metaball field spanning from p1 to p2 with radius radius (BOSL2 mb_connector()).
- Parameters:
radius (float | None)
cutoff (float)
influence (float)
negative (bool)
diameter (float | None)
- class bosl2.isosurface.Metaball(field, neg=1)[source]¶
Bases:
objectA metaball field primitive: a vectorised scalar field
field(pts)over(N, 3)points, plus its sign (neg). Combine several withmetaballs().