Skip to content

Tunable parameters

Tunable parameters

A parameter is a number declared next to the code that reads it, tuned live in a panel, and saved for the next run. Parameters hold the constants a talk animates on, snippets the logic that turns them into motion, and both live in one namespace.

For scalar-like parameters that need tuning for animation, you can add a Params object: runtime-tunable, persistent parameters declared next to the code that uses them:

auto amp   = Params::Add("amplitude", 0.2, 0., 1.);  // slider in [0,1]
auto speed = Params::Add("speed", 1.);               // unconstrained drag

spot->setUpdater([=](TimeObject t) {
    deform(spot, (scalar)amp, (scalar)speed * t.inner_time);
});

Params::Add(name, default, min, max), also AddInt, AddBool, AddColor, AddVec2, AddVec, AddDir

min == max makes it unconstrained. AddAtLeast(name, default, min) sets a mininmum but uncapped value.

If you want to change method during live for instance, you can add choices among names as a parameter. Drawn as a dropdown:

auto side = Params::AddEnum("fig/yticks", {"left", "right", "none"}, "left");
if (side.is("none")) ...

The handle reads the live value (a plain conversion, usable in hot loops). Pressing A opens the Tuner panel, showing the parameters read by the current slide's updaters (a checkbox reveals all of them), grouped by their "group/name" prefix. The polyscope camera is not affected while you edit a value.

Edited values are saved with Ctrl+S to views/params.json. Only ever-edited parameters are written, so untouched ones keep following their code defaults. The file is loaded back on startup, and hot-reloaded when edited by hand.

A parameter can also be declared from a snippet with param("name", def, min, max) or from a shader's uniforms:, all in the common namespace.

Handles

A position is easier to aim than to type, so the geometric parameters carry a widget on top of their sliders:

Type Button Widget
vec3 3D a translation gizmo in the scene, the same one the T editor uses for transforms
dir - a ball oriented like the camera: the mouse aims the unit vector, right click flips the hemisphere
vec2 2D a crosshair dragged on the screen

all and none, next to the panel's checkbox, switch every handle of the listed parameters on or off at once. While a handle is live the slide stops taking mouse input, so dragging never spins the camera by accident, and closing the panel restores it.

A vec2 parameter is in screen coordinates: 0..1 across the window, y up. That is gl_FragCoord's convention, the opposite of the screen anchors', and it is what lets a shader put an object exactly under its handle:

#include <camera.glsl>
uniform vec2 center;

void main() {
    float d = length((screenPoint() - center) * iWindowSize);   // window pixels
    ...
}

screenPoint() reports where this fragment falls on the window, so the agreement holds wherever the shader is placed and whatever its resolution: is. It is the 2D counterpart of polyscopeRay for 3D scenes.

Overriding value

Params::setDefault(name, value) what it falls back to when nothing has edited it. A saved or edited value still takes precedence
Params::drive(name, value) drives it outright: never saved, and it outranks what the Tuner holds
Params::valueOf(name) reads it back

Writing one from code

A parameter is usually what your code reads, but it can be what your code writes too:

Params::write("cursor", p);   // by name
amp.set(0.4);                 // or through the handle

Use in a live demo

While params can be used to finetune animation parameter, you can decide that they should appear as part of your slides for a live demo. By default, its visibility is tied to the tuner panel, but you can change it:

Params::setVisible("speed",  Params::Visible::None);    // nothing, unless the Tuner is open
Params::setVisible("energy", Params::Visible::Panel);   // its widget, in a small window
Params::setVisible("grab",   Params::Visible::Handle);  // its manipulator, in the scene
Params::setVisible("center", Params::Visible::Both);    // the widget and the manipulator

None is what every parameter starts as. A manipulator is the handle of its type, a gizmo for a vec3 and a crosshair for a vec2.

From a deck the same four are spelled none, panel, handle and both:

deck.yaml
- shader: field.frag
  uniforms:
    grab: {type: vec3, visible: handle}