Functions and data series
The series of func3d come from two JavaScript registries:
SERIES_FUNCS (analytic functions, file js/local/func3d/series-funcs.js)
and SERIES_DATA (numerical datasets, file js/local/func3d/series-data.js).
A bridge module (series-bridge.js) registers every dataset as a samplable
"function" through an interpolation method, so analytic
surfaces and data-driven surfaces share the same drawing pipeline.
Implemented analytic functions
| Function | Notes | Parameters |
|---|---|---|
| Sinc | sin(r)/r, the classic "hat" | — |
| cos(r)/r | oscillating with damped singularity | — |
| Elliptic paraboloid | quadric | a, b |
| Gaussian | bell | σ |
| Quadratic hyperbolic saddle | hyperbolic paraboloid | a, b |
| Cone | |r| | — |
| Plane | ax + by + c | a, b, c |
| Linear saddle | k·x·y | k |
| Rosenbrock | banana function, an optimisation benchmark | a, b |
| Ackley | many local minima, a test for global optimisers | a, b, c |
| Bessel J0 | J0(s·r), radial modes | s |
| Ripple 2D | interference of waves along x and y | fx, fy |
| Wave | 2D sinusoid | a, b |
| Bumps | periodic maxima and minima | a, b, c |
| Pyramid | surface with edges | — |
| Damped wave | sinusoid with envelope | kx, ky |
| Rastrigin | multimodal with a regular grid of minima (optimisation test) | A |
| Franke function | the classic 2D interpolation test bed: two hills, a ridge and a dip | — |
| Himmelblau | four identical global minima (optimisation test) | — |
| Circular membrane (modes) | standing wave of a drum Jn(s·r)·cos(nθ); the phase animates it | n, s, phase |
| Müller‑Brown (PES) | test potential energy surface for reaction paths | Vmax |
| Dipole potential | V = 1/r₊ − 1/r₋ softened (diverging colormap) | separation, ε |
Closed surfaces (two-valued z: the page automatically draws the upper and lower branches):
| Function | Parameters |
|---|---|
| Truncated top | H, R |
| Hyperboloid | a |
The ellipsoid and the torus used to be in this list and are now parametric (below). Stitching two z(x,y) branches along the equator leaves a gap where the derivative diverges: at the junction the triangles of the two branches do not meet and the surface looks torn. In parametric form the problem does not arise, because there is a single mesh.
Parametric surfaces (u,v) → (x,y,z): objects that cannot be expressed as z(x,y), drawn in true proportions with uniform scale; colour can be based on the z height or on the u/v parameters (the Colour basis select in the card):
| Function | Notes | Parameters |
|---|---|---|
| Spherical harmonics |Ylm| | r(θ,φ) = |Ylm|: the orbital shapes (u=θ, v=φ) | l, m, scale |
| Hybrid orbital spⁿ | r = |ψ|² with ψ = (s + √λ·pz)/√(1+λ): the asymmetric hybrid lobe (λ=1,2,3) | λ, scale |
| Möbius strip | strip with a half twist | R, w |
| Knotted torus (p,q) | tube around the (p,q) torus knot | p, q, R, r, a |
| Ellipsoid | x = a·sinθcosφ, y = b·sinθsinφ, z = c·cosθ (u = θ, v = φ): clean poles and equator | a, b, c |
| Torus | x = (R + r·cos v)·cos u, y = (R + r·cos v)·sin u, z = r·sin v | R, r |
| Klein bottle | figure-eight immersion: closed non-orientable surface | R |
| Catenoid ↔ Helicoid | the isometric family of minimal surfaces, with continuous morphing t=0→1 | t, c |
| Surface of revolution | profile r(v) = R + A·sin(k·v) revolved around the z axis | R, A, k |
A parametric entry declares parametric: true, the parameter ranges in the
defaults (xMin/xMax = u, yMin/yMax = v), the sample counts
nU/nV and a build(params) returning
(u, v) => [x, y, z]. For these objects there are no isolines, 2D section
or zScale (there is no z(x,y)); the CSV export provides the 3D points of the grid.
Parametric curves t → (x,y,z): a curve is not a thin surface — it may pass over the same point of the plane more than once, and a height field cannot do that. Colour follows the parameter t, so the direction of travel is readable too; thickness is in real pixels (fat lines) and non-finite points break the line instead of closing it with an invented segment:
| Curve | Notes | Parameters |
|---|---|---|
| Cylindrical helix | x = R·cos t, y = R·sin t, z = c·t; the pitch is 2πc | R, c |
| Trefoil knot | the simplest non-trivial knot: a closed curve that cannot be undone without cutting it | a |
| Lorenz trajectory | Runge-Kutta 4 integration with σ=10, β=8/3 and adjustable ρ; t is time | ρ, step |
In the series card the fields become start t, end t, Samples and Line width: surface/wireframe mode, zScale, isolines, section and classes are not shown, because on a curve they mean nothing. If there is no surface in the scene, the axis ticks follow the extent of the curve.
These controls act in place: moving a parameter (the helix radius, the Lorenz ρ) re-samples the curve inside the existing group, with the same normalisation — the scale does not bounce during the gesture, and dragging the slider becomes a smooth animation. A full rebuild (any other control) re-normalises on the new data.
The image plane is not a function: it is a textured rectangle on one of the three coordinate planes (xy, xz, yz), movable along its own normal and resizable. It puts a photograph, a map, a scan or a video frame next to the data — not onto it, as draping does. The position is given, at your choice, in fractions of the axis box (−1…1, useful for a backdrop) or in data coordinates: a rectangle x0…x1, y0…y1 at level z, in the units of the first grid surface in the scene — the ortophoto-on-DEM case, where the image must sit where the metres are. In data coordinates the rectangle need not be square and is placed at the end of the build, when the reference surface's mapping exists; without a reference surface the coordinates are read as box units. The available images are those of the registry (the same ones used for draping, see below).
Example datasets
| Dataset | Type | Content |
|---|---|---|
| Ozono | 11×11 grid (bilinear) | concentration field with reduced z scale |
| Tempo | temporal grid, 10 frames 21×21 | surface fluctuating in time |
| Advected puff | temporal cloud [x, y, z, c] | cloud moving, spreading and diluting |
| Rotating cloud | temporal cloud [x, y, z, c] | rigid rotation with recirculation |
| Demo nearest / IDW / bilinear / RBF | scattered points or grid | demos of the first interpolation methods (all current 11 are selectable from the card) |
| Demo points | points | drawing of the points alone, no surface |
| 2D heat kernel | temporal grid, 16 frames 41×41 | pure diffusion: a Gaussian spreading and flattening |
| Lorenz (ensemble) | temporal cloud, 110 frames × 50 points | ensemble on the Lorenz attractor, colour = speed; with trails on you see the chaotic divergence |
| Rankine vortex + sink | planar vector field 15×15 | rigidly rotating core, potential exterior, radial sink |
| Magnetic dipole (B) | 3D vector field | field lines of a dipole with moment along z (softened) |
| Maunga Whau volcano (DEM) | real 87×61 grid, 10 m spacing | the classic R "volcano" dataset: elevations 94–195 m |
| Volcano — sparse sample | 260 points drawn from the DEM (fixed extraction) | interpolate them (natural, krige, rst…) and compare with the full DEM via "Difference (A − B)" |
Adding an analytic function (on file)
An entry of SERIES_FUNCS declares a label, the parameters (with defaults
and UI limits) and the factory build(params) returning the samplable
function:
function_name: {
label: 'My function',
params: [
{ name: 'k', label: 'k', type: 'number', step: 0.1, min: 0, max: 10, default: 1.0 }
],
defaults: { xMin: -8, xMax: 8, yMin: -8, yMax: 8, step: 0.3, zScale: 1.0 },
build: (p) => (x, y) => Math.sin(p.k * x) * Math.cos(p.k * y)
}
For a closed surface add closed: true; the build receives the branch in
params._branch (+1 upper, −1 lower).
Adding a dataset (on file)
An entry of SERIES_DATA carries the data and the method used to sample
them. Scattered points go in data (rows [x, y, z]), grids/frames in
values:
my_dataset: {
label: 'My dataset',
defaults: {
xMin: -3, xMax: 3, yMin: -3, yMax: 3, step: 0.15, zScale: 1.0,
mode: 'surface', colormap: { mode: 'preset', name: 'viridis' }
},
method: { name: 'idw', params: { power: 2 } }, // nearest | idw | bilinear | bicubic | lanczos | natural | rbf | rst | bspline | krige | points
data: [
[-2, -2, 1.0], [-2, 0, 0.3], [0, 0, 1.0], [2, 2, 0.1]
],
enabled: false, // true = appears among the series on load
onLoad: false // true = it is also plotted immediately
}
Variants:
- regular grid:
values= matrix of z + domain extents (bilinearmethod); - time series:
values= array of frames (matrices for grids, lists of points for clouds) with the time step in the defaults; - point cloud:
kind: 'cloud', rows [x, y, z, c] or [x, y, z, c1, c2] (c = colour, c2 = size), with fixed bounds and c range declared in the dataset; - vector field:
kind: 'vector', rows [x, y, z, u, v, w].
Adding a parametric curve or an image plane (on file)
A curve declares isCurve: true, the parameter range in
defaults (xMin/xMax = t), the number of samples nU and a
build(params) returning (t) => [x, y, z]:
elica: {
label: 'Cylindrical helix',
isCurve: true,
nU: 800,
params: [
{ name: 'R', label: 'R (radius)', type: 'number', step: 0.1, min: 0.1, max: 20, default: 1.0 },
{ name: 'c', label: 'c (pitch/2π)', type: 'number', step: 0.05, min: -5, max: 5, default: 0.25 }
],
defaults: { xMin: 0, xMax: 8 * Math.PI, wireWidth: 3, colormap: { mode: 'preset', name: 'turbo' } },
build: (p) => (t) => [p.R * Math.cos(t), p.R * Math.sin(t), p.c * t]
}
If the curve has no closed form (an integrated trajectory, say), integrate it
once inside build and let the returned function read the result by
interpolation: re-integrating at every sample would cost O(n²) and produce the very
same curve. The Lorenz trajectory is written that way.
An image plane declares isImagePlane: true and the three parameters
piano (xy|xz|yz), posizione and copertura; the
image itself is chosen in the card among those of the registry. build is
not used (it stays a placeholder).
The extra columns (further dimensions)
Research data usually has more than three columns: x, y, height, and then the concentration, the survey date, the measurement error, the sample number. Columns not assigned to a role during import are no longer discarded: they stay next to the data, aligned row by row, and appear in the card's Colour by select.
// how they look in the dataset (CSV import or database payload)
{
id: 'imp_1', label: 'campaign 2026',
data: [ [x, y, z], ... ], // geometry: the height
extra: {
names: ['concentration', 'error'],
values: [ [12.4, 0.3], ... ] // one row per point, in the same order
}
}
Picking one of these quantities leaves the surface as the height and lets the colour tell the other story. The field is interpolated on the same grid and with the same method (see Interpolation): if colour and shape came from different grids they would not be talking about the same points. Where the field is undefined the vertex stays grey, which is not the same as taking a colour from the scale — that would state a value that is not there.
User images and saved layers (the graph_editor profile)
Users with the graph_editor profile (or admins) get two extra blocks in
the Import/Export panel: My images — personal upload of drape images (resized
client-side, stored outside the docroot in
upload/graph/<user>/ with a per-user quota; they appear in the card
selector next to the system ones) — and Saved layers — DB storage
of the selected series (recombinable in
other charts) or of the whole chart snapshot, by name.
Images and layers are born private. Next to each own element there is the
sharing control, with the same semantics as the rest of the platform: switch
off = private; on with no profiles = public; on with chosen profiles = visible
to those only. Elements shared by others appear in the lists with the shared
tag and are read-only. The chart management tables live in the graph
schema of the boot DB.
The administrator is the exception: they see every user's content, including the private one (labelled with the owner's name), and can rename, re-share, overwrite and delete it — either from the page itself or, with an overview of all users, from the gest → database → func3d data console.
Importing from CSV (runtime)
The func3d Import/Export panel builds temporary datasets from CSV/TSV or the clipboard, with guided column mapping (x, y, z, c, c2, t, group, u, v, w). They are session datasets: to make them permanent, export the payload and save it as an administered dataset from the console.
Keywords: functions, data series, dataset, registry, payload, 3D charts