PortLab

Management of web portals dedicated to research projects

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

FunctionNotesParameters
Sincsin(r)/r, the classic "hat"—
cos(r)/roscillating with damped singularity—
Elliptic paraboloidquadrica, b
Gaussianbellσ
Quadratic hyperbolic saddlehyperbolic paraboloida, b
Cone|r|—
Planeax + by + ca, b, c
Linear saddlek·x·yk
Rosenbrockbanana function, an optimisation benchmarka, b
Ackleymany local minima, a test for global optimisersa, b, c
Bessel J0J0(s·r), radial modess
Ripple 2Dinterference of waves along x and yfx, fy
Wave2D sinusoida, b
Bumpsperiodic maxima and minimaa, b, c
Pyramidsurface with edges—
Damped wavesinusoid with envelopekx, ky
Rastriginmultimodal with a regular grid of minima (optimisation test)A
Franke functionthe classic 2D interpolation test bed: two hills, a ridge and a dip—
Himmelblaufour identical global minima (optimisation test)—
Circular membrane (modes)standing wave of a drum Jn(s·r)·cos(nθ); the phase animates itn, s, phase
Müller‑Brown (PES)test potential energy surface for reaction pathsVmax
Dipole potentialV = 1/r₊ − 1/r₋ softened (diverging colormap)separation, ε

Closed surfaces (two-valued z: the page automatically draws the upper and lower branches):

FunctionParameters
Truncated topH, R
Hyperboloida

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):

FunctionNotesParameters
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 stripstrip with a half twistR, w
Knotted torus (p,q)tube around the (p,q) torus knotp, q, R, r, a
Ellipsoidx = a·sinθcosφ, y = b·sinθsinφ, z = c·cosθ (u = θ, v = φ): clean poles and equatora, b, c
Torusx = (R + r·cos v)·cos u, y = (R + r·cos v)·sin u, z = r·sin vR, r
Klein bottlefigure-eight immersion: closed non-orientable surfaceR
Catenoid ↔ Helicoidthe isometric family of minimal surfaces, with continuous morphing t=0→1t, c
Surface of revolutionprofile r(v) = R + A·sin(k·v) revolved around the z axisR, 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:

CurveNotesParameters
Cylindrical helixx = R·cos t, y = R·sin t, z = c·t; the pitch is 2πcR, c
Trefoil knotthe simplest non-trivial knot: a closed curve that cannot be undone without cutting ita
Lorenz trajectoryRunge-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

DatasetTypeContent
Ozono11×11 grid (bilinear)concentration field with reduced z scale
Tempotemporal grid, 10 frames 21×21surface fluctuating in time
Advected pufftemporal cloud [x, y, z, c]cloud moving, spreading and diluting
Rotating cloudtemporal cloud [x, y, z, c]rigid rotation with recirculation
Demo nearest / IDW / bilinear / RBFscattered points or griddemos of the first interpolation methods (all current 11 are selectable from the card)
Demo pointspointsdrawing of the points alone, no surface
2D heat kerneltemporal grid, 16 frames 41×41pure diffusion: a Gaussian spreading and flattening
Lorenz (ensemble)temporal cloud, 110 frames × 50 pointsensemble on the Lorenz attractor, colour = speed; with trails on you see the chaotic divergence
Rankine vortex + sinkplanar vector field 15×15rigidly rotating core, potential exterior, radial sink
Magnetic dipole (B)3D vector fieldfield lines of a dipole with moment along z (softened)
Maunga Whau volcano (DEM)real 87×61 grid, 10 m spacingthe classic R "volcano" dataset: elevations 94–195 m
Volcano — sparse sample260 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:

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

Moreno Comelli, CNR-IFAC, 2022-2026