This page explains the conceptual model behind HoloField.
HoloField uses the standard mathematical spherical coordinate convention where the Z axis is the polar axis. In this convention, the polar angle θ (theta) is measured from the positive Z direction (0 ≤ θ ≤ π), while the azimuth angle φ (phi) describes rotation around the Z axis in the XY plane (0 ≤ φ < 2π). This convention is commonly used in mathematics and physics and defines the sphere orientation used by the Latitude - Longitude Sampler, for example.
Some graphics-oriented implementations instead use the Y axis as the polar axis, treating Y as the "up" direction. This is also a valid convention and is common in 3D graphics, but it represents a rotation of the coordinate system rather than a different sphere. We use Z as the polar axis to follow the mathematical convention and to keep spherical sampling definitions explicit and consistent.
HoloField is divided into an engine side and a browser side. This is a separation of responsibilities rather than a server/client distinction.
The engine contains the implementation of the projection pipeline and is intended to be independent of the webpage that uses it. It defines the mathematical and data-processing components used to transform boundary data into renderable datasets:
The engine components operate on their defined data structures and contracts and do not manage the webpage, DOM, UI controls, or rendering lifecycle.
pipeline.ts belongs to this side. It acts as a dispatcher that connects the engine components and exposes higher-level operations to the application, but it does not start the engine or execute the application by itself.
The browser side is responsible for the application that uses the engine. It connects the engine to the webpage and handles concerns that are specific to the browser environment, including:
viewer.js is the primary orchestration layer on the browser side. It responds to user interaction, invokes the appropriate engine pipeline operations, and presents the resulting datasets through the application's viewers.
The browser side interact with the engine through the pipeline orchestrator functions rather than reaching into individual engine implementations. The engine therefore remains concerned with what is generated and how it is generated, while the browser application is concerned with when generation is requested and how the results are presented.
This separation allows the engine to be developed and documented as an independent projection system while the browser application acts as one possible consumer of that system.
The engine consists of four major subsystems:
Mathematical calculations are separated from the storage and management of generated data.
The calculation pipeline is designed around three distinct responsibilities:
This separation keeps calculations deterministic and reusable while allowing generated datasets to be cached, inspected, compared, and passed to different consumers.
The general data flow is:
A calculation stage receives input data or configuration and produces a new data representation. It does not own the resulting dataset.
Datasets are instanced objects that can be stored, reused, displayed, or transformed further.
Calculation functions represent mathematical operations.
Examples:
These operations are intended to be deterministic:
input + configuration → output
They do not maintain runtime state or store generated results.
Example: Boundary Field → Scalar value at coordinate
The engine generates data going backwards from the projection sampling requirementa to the boundary field. Intermediate calculations are not stored; only meaningful results become datasets.
A field defines a scalar function (over a surface domain). It answers:
Given a coordinate in the field domain, what scalar value exists at this location?
It does not know:
The primary use case is spherical surfaces, where a Sampler provides normalized coordinates derived from a sphere. However, the abstraction is not limited to spherical geometry.
The boundary field remains independent of the geometry used to generate the coordinates.
A decoder transforms a mathematical representation into a dataset representation.
A decoder defines how a continuous mathematical function becomes a sampled representation. For example, the Radial Decoder calculates scalar values, but the resulting scalar field is stored by a dataset container.
A decoder acts like a mathematical lens: it interprets the information stored on the boundary surface and projects it into a volumetric representation that can be sampled in 3D space.
A sampler converts a continuous spatial representation into a discrete dataset.
A sampler defines how the continuous field produced by a decoder is evaluated and represented at discrete spatial positions. For example, the Uniform Voxel Sampler evaluates the decoder on a regular three-dimensional grid, while point-cloud samplers select individual positions according to their sampling criteria.
A sampler acts as a spatial measurement process: it determines where the decoded field is observed and how those observations are organized into a dataset, without changing the underlying field itself.
A mesh generator extracts a geometric surface from a volumetric dataset.
A mesh generator interprets the scalar values stored in a voxel dataset and determines where the requested isosurface lies within the sampled volume. The resulting geometry is represented as a mesh dataset containing vertices and triangles, which can then be passed to the rendering side of HoloField.
Different mesh generators can interpret the same voxel dataset using different surface extraction strategies. HoloField currently provides implementations of Marching Cubes and Dual Contouring, allowing the effects of different extraction methods to be explored without changing the preceding field, decoder, or sampling selections.
A mesh generator therefore acts as the geometric interpretation stage of the pipeline: it converts a discrete volumetric representation into an explicit surface representation suitable for rendering or further geometric processing.
Datasets represent generated results.
Unlike calculation functions, datasets are persistent objects.
Examples:
Datasets may:
The dataset contains the result of a computation, not the computation itself.