Skip to content

rohingosling/gaussian-isosurfaces

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

4 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

gaussian-isosurfaces

Python 3.14 NumPy PyVista VTK scikit-image MATLAB

Animated Gaussian metaball isosurface

Visualising superpositions of isotropic 3D Gaussian fields as isosurfaces (metaballs), animated over time.

๐Ÿ“‘ Contents

๐Ÿ”ญ Overview

This package builds, sums, and renders isotropic 3D Gaussian "metaballs" as animated isosurfaces. The central idea is superposition: several Gaussian centres are summed into one composite scalar field, and the field is contoured at a fixed level (isovalue 0.5). At that threshold neighbouring centres fuse into smooth, metaball-ish shapes โ€” metaballs โ€” so the rendered surface behaves like soft spheres melting into one another as the centres move.

Two animations ship as named scenes:

  • cinematic โ€” five coupled metaballs orbiting on hand-coded paths over a ~60 s timeline, with a keyframe-driven style / camera / resolution schedule (wireframe, shaded, oblique, red) and optional MP4 recording.
  • swarm โ€” eight ParameterRotate-driven particles on a fixed-resolution grid, slowly precessing in an orthographic box; the heavier scene, designed for headless recording.

This is a Python (PyVista / VTK) port of two MATLAB originals (Gaussian3DAnimation.m and Gaussian3DTest01.m). The original MATLAB sources are kept under src/matlab/ for reference; the kernel, motion, and schedules are reproduced to match them (including the deliberate "base-a" Gaussian form and a couple of load-bearing MATLAB quirks).

๐Ÿ” Per-frame pipeline

Each animation frame walks the same data pipeline. The sample grid is rebuilt only when the resolution step changes (so the constant-resolution swarm builds its grid once and reuses it), and time advances after the field is built, so the increment shows up only on the next frame.

flowchart LR
    P["Frame parameters<br/>centres, variance v, step, style"]
    G["Build or reuse<br/>cube sample grid"]
    F["Evaluate + sum<br/>Gaussian bumps -> scalar volume"]
    C["Contour @ isovalue 0.5<br/>(VTK or scikit-image)"]
    R["Render<br/>style, camera, lights"]
    P --> G --> F --> C --> R
    R --> W["Interactive window"]
    R --> M["MP4 frame (when recording)"]
    R --> T["Advance time"]
    T -. "next frame" .-> P
Loading

๐Ÿ“ฆ Install

The package targets Python >= 3.10 and is validated on CPython 3.14 (Windows). From the package directory src/python/, create and activate a virtual environment, then install the package into that venv:

cd src/python
python -m venv .venv

# Activate the venv:
#   Windows (PowerShell):  .venv\Scripts\Activate.ps1
#   Windows (cmd):         .venv\Scripts\activate.bat
#   Linux / macOS:         source .venv/bin/activate

# Install (with the test / dev extra, recommended):
pip install -e .[dev]        # or:  pip install -e .   (runtime only)

Installing into the venv puts the gaussian-isosurfaces console script inside that venv, so the commands below work whenever it is active. If the shell reports gaussian-isosurfaces is not recognized, you are in a different shell/venv than the one you installed into โ€” re-activate the venv above (or use the equivalent python -m gaussian_isosurfaces.cli ...).

Runtime dependencies: numpy, pyvista, vtk, imageio-ffmpeg. The [dev] extra adds scikit-image (the alternate contour backend, required by the backend-agreement tests), pytest, matplotlib, and ruff.

๐Ÿš€ Usage

With the virtual environment active (see Install), the package provides a console script, gaussian-isosurfaces (equivalently python -m gaussian_isosurfaces.cli). A scene is always required; every other flag falls back to the authoritative per-scene default when omitted.

Options

Flag Argument Default Description
--scene {cinematic, swarm} (required) Which animation to run.
--frames N scene default (cinematic 1200, swarm 600) Frame budget / stop condition.
--fps N scene default (cinematic 20, swarm 60) Animation and recording rate.
--resolution STEP scene default Override the mesh step (one constant step for every frame).
--isovalue V 0.5 Isosurface level.
--backend {vtk, skimage} vtk Contour engine.
--window-size W H 1048 800 Interactive / render image size in pixels.
--record [PATH] (off) Record an MP4. Bare --record uses the scene default filename (cinematic.mp4 / particle_swarm.mp4).
--output PATH (off) Record an MP4 to PATH (takes precedence over --record).
--offscreen (flag) off Headless render; the frame count is the authoritative stop.
--wireframe (flag) off Swarm: white wireframe instead of blue Phong.
--oblique (flag) off Swarm: view (45, 22.5) instead of (0, 0).
--hidden-lines (flag) off Hidden-line removal (MATLAB hidden on/off): hide wireframe edges occluded by the surface; --no-hidden-lines forces it off. Mainly affects wireframe.

Examples

# Interactive cinematic scene (live window; press ESC or close the window to stop).
gaussian-isosurfaces --scene cinematic

# Record the swarm scene to MP4 headlessly (the supported path for the 49^3 grid).
gaussian-isosurfaces --scene swarm --offscreen --record

# Swarm as a white wireframe seen from the oblique view, recorded to a named file.
gaussian-isosurfaces --scene swarm --offscreen --wireframe --oblique --output swarm_wire.mp4

# A short cinematic clip using the scikit-image marching-cubes backend.
gaussian-isosurfaces --scene cinematic --backend skimage --frames 200 --offscreen --record

๐ŸŽฌ The two scenes

Parameter cinematic (Scene A) swarm (Scene B)
Source Gaussian3DAnimation.m Gaussian3DTest01.m
metaballss / particles 5 coupled metaballs 8 ParameterRotate particles
Domain (cube half-extent) [-8, 8]^3 [-6, 6]^3
Variance v 1.25 1.0
Isovalue 0.5 0.5
FPS / frames 20 fps, 1200 frames 60 fps, 600 frames
Resolution keyframe-scheduled (steps 2.0 / 1.0 / 0.75) constant step 0.25 -> 49^3
Projection perspective orthographic
Grid colour green (0, 0.75, 0) green (0, 0.5, 0)

Cinematic resolution + style schedule. The cinematic scene steps through keyframe bands at fps * {10, 20, 30, 40, 50, 60} (frames 200 / 400 / 600 / 800 / 1000 / 1200 at the default 20 fps). The mesh resolution coarsens and refines across these bands โ€” step 2.0 (9^3) before the first band, then 1.0 (17^3), 0.75 (22^3), back to 1.0, then 0.75 โ€” while the render style cycles through wireframe, solid blue, oblique, wireframe, and finally solid red. (A deliberate off-by-one between the style and resolution schedules at the kf4 boundary is reproduced from the original on purpose.)

Swarm constant grid + slow advance. The swarm uses a single fixed step of 0.25 โ€” a 49^3 grid built once and reused โ€” with the eight particles advancing by only pi / 8192 per frame, so the formation precesses slowly.

Interactive vs offscreen. Without --offscreen a live window opens and you can quit with ESC or by closing the window; the lighter cinematic grids play comfortably this way. The swarm's 49^3 grid is heavier, so offscreen recording is the supported path for it.

๐Ÿ“Š Performance

Representative per-frame timings measured on this machine, offscreen, over the full pipeline (grid + field sum + contour + render) at the standard 1048x800 window size. Numbers are mean ms/frame after warm-up; treat them as order-of-magnitude.

Scene Step Grid Backend Mean ms/frame
cinematic 2.0 9^3 vtk ~10
cinematic 1.0 17^3 vtk ~9
cinematic 0.75 22^3 vtk ~9
swarm 0.25 49^3 vtk ~30

At the cinematic grid sizes the per-frame cost is dominated by render/setup overhead, so the three steps land close together. The swarm's 49^3 volume is markedly heavier per frame; interactive playback adds window-event and present overhead on top of the figures above, so for the swarm prefer --offscreen --record rather than the live window. Times scale with grid size, window size, and your GPU/driver, so your own numbers will vary.

๐Ÿ“ Project structure

src/python/
โ”œโ”€โ”€ pyproject.toml                 deps + console script
โ”œโ”€โ”€ gaussian_isosurfaces/
โ”‚   โ”œโ”€โ”€ __init__.py                public API re-exports
โ”‚   โ”œโ”€โ”€ cli.py                     argparse front end + main ()
โ”‚   โ”œโ”€โ”€ config.py                  AppConfig (single source of truth)
โ”‚   โ”œโ”€โ”€ field.py                   Gaussian kernel + field superposition
โ”‚   โ”œโ”€โ”€ isosurface.py              VTK / scikit-image contour backends
โ”‚   โ”œโ”€โ”€ motion.py                  per-frame centre motion
โ”‚   โ”œโ”€โ”€ style.py                   keyframe style / resolution schedules
โ”‚   โ”œโ”€โ”€ renderer.py                IsosurfaceRenderer (PyVista / VTK)
โ”‚   โ””โ”€โ”€ scenes/
โ”‚       โ”œโ”€โ”€ base.py                shared per-frame loop
โ”‚       โ”œโ”€โ”€ cinematic.py           CinematicScene (5 metaballs)
โ”‚       โ””โ”€โ”€ swarm.py               SwarmScene (8 particles)
โ””โ”€โ”€ tests/                         pytest suite (math + offscreen render)

The curated public API is re-exported from the package root:

from gaussian_isosurfaces import (
    GaussianField, contour_field, marching_cubes_field,
    IsosurfaceRenderer, AppConfig, CinematicScene, SwarmScene, main,
    __version__,
)

๐Ÿงฎ MATLAB originals

The two animations began as MATLAB scripts; this package is a faithful Python / PyVista port. The originals โ€” Gaussian3DAnimation.m (cinematic) and Gaussian3DTest01.m (swarm), along with the lower-dimensional demos and the ParameterRotate / Gaussian3D helpers โ€” are kept under src/matlab/ for reference and comparison. The port reproduces their mathematics (notably the non-canonical base-a Gaussian kernel) and their per-frame ordering so the metaballs match at isovalue 0.5.

๐Ÿ“œ Licence

Released under the MIT License โ€” see LICENSE. Copyright ยฉ 2019 Rohin Gosling.

About

Visualising superpositions of isotropic 3D Gaussian fields as isosurfaces. i.e., Metaballs.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors