openFrameworks addon
Manages ofParameters groups on presets, interpolates between them, and plays sequences
No GUI, JSON file format storage, tested on OF 0.12
- Save and load parameter values as presets in JSON files
- Support multiple parameter groups
- Apply preset values by smooth interpolation
- Easy value mutation
- Provide events to follow the preset application and sequence steps
- A [sequencer] to play a sequence of presets, with handy syntax
- steps: 1, 2, 3, 4
- ranges: 1 - 5
- random preset: 1, ?, 5
- preset mutation: 5*
- randomized repetition: ? - 3
The usual ofx addons procedure: clone this repo or download it and uncompress it on your addons folder.
No other dependencies are required.
Tested on oF 0.12, Windows, VS 2022 (v143 toolset)
Overall steps to use the addon:
- Import
#include "ofxPresets.h"- Define your ofParameters and group them
ofParameterGroup group;
ofParameter<int> x;
...
group.setName("myParams");
group.add(x.set("x", 0, 100));The most standard way is using an ofParameterGroup, (see alternatives)
- Create the presets manager and add the parameter group to be managed
ofxPresets manager;
...
manager.setup( myParams );It can load multiple groups see how]
- Add the manager to the update loop
void ofApp::update(){
manager.update();
}- Save and apply presets
savePreset(int presetID)to save the current values into the preset json fileapplyPreset(int presetID, float interpolationDuration)to load the preset json file and apply the values into the preset objects
- Control sequences and mutation
Common usage snippet:
void ofApp::keyReleased(ofKeyEventArgs& e) {
if (e.keycode >= '1' && e.keycode <= '9') {
int index = e.keycode - '1' + 1; // convert to int by removing the ascii offset
if (e.hasModifier(OF_KEY_SHIFT)) {
manager.savePreset(index);
}
else {
manager.applyPreset(index);
}
}
if (e.keycode == 'S') {
manager.loadSequence(ofToString(sequenceInput.get()));
manager.playSequence();
}
if (e.keycode == 'C') {
manager.stop();
}
if (e.keycode == 'M') {
manager.mutate();
}
}Interpolation happens when applyPreset is called, including while sequencing.
Set the interpolation time by assigning a value to the interpolationDuration parameter. This is a float value in seconds.
Within that time, all parameter values are interpolated using an easing function. QuadInOut is the default.
Easing function can be changed. Use the included ofxSEeasing class, or write your own.
// setEasingFunction(std::function<float(float)> func);
manager.setEasingFunction(ofxSEeasing::easeInOutCubic);The desired parameters to be managed as presets should be provided in groups, using the setup() method.
manager.setup( myParams );You can provide a single parameter group or an std::vector<> of parameter groups.
Two different group types are supported: the standard ofParameterGroup and the provided ofxPresetsParametersBase,
Currently supports this parameter types:
ofParameter<int>ofParameter<float>ofParameter<bool>, stored as an int 0 or 1 valueofParameter<ofColor>, which stored as an int value, and a separated alpha int value
The well known ofParameterGroup to group your parameters,
Requires to set the group name and initialize all parameters before loading them to the manager:
ofParameterGroup group;
group.setName("myParams");
ofParameter<int> x;
group.add(x.set("x pos", 0, 100));This is a wrapper class that may be handy to add extra logic to the group, like initializing the parameters, create resets, etc. Example:
struct Params : public ofxPresetsParametersBase {
public:
ofParameter<int> x;
Params() {
groupName = "myParams";
parameterMap["x"] = &x.set("x pos", 0);
parameterMap["y"] = &y.set("y pos", 0);
}
void reset() {
x = 0;
y = 0;
}
void swap() {
if (x>0 && y>0) { std::swap(x, y); }
else { reset(); }
}
}The addon uses ofxParameterBase structures internally to store all parameter references and during the interpolation
The sequencer allows to playback a sequence of presets.
Main methods:
.loadSequence(std::string)to load a sequence string see sequence string].playSequence()to play the loaded sequence.stopSequence()to stop only the sequence playback.stop()to stop all playback and interpolation (the interpolation and sequence playing)
There are two main time values to control the sequencer:
- The transition time when applying presets:
manager.interpolationDuration - The time spent between steps, meaning the time a preset waits until a new transition start:
manager.sequencePresetDuration
The sequence string is a regular string with the comma separated step presets: 1, 2, 3, 4
Must be loaded using loadSequence(std::string) method or modifying the internal sequence vector]
There are special syntax tokens for:
- Ranges:
1 - 5, will play presets 1, 2, 3, 4, 5 and loops from the begining - Random preset:
1, ?, 5, will take ? and replace it for a random available preset - Randomized range:
? - 3, will repeat a random preset 3 times - Preset mutation:
5*, will apply the preset #5 in a mutated fashion
When loading a sequence string, the manager will parse it and store the unrolled sequence in an internal vector of integers.
The unrolled sequence is available in the manager in ofParameter<std::vector<int>> sequence;
When unrolling the special tokens are replaced in the internal sequence vector:
?is stored as0*is stored as the negative preset number (i.e.3*will be-3)
A mutation feature is available to apply small changes to parameters without the need to save a new preset.
Can be called from the current parameter state (without an actual preset applied or defined!)
void mutate();
void mutate(float percentage = 0.1f);or from a preset:
void mutateFromPreset(int id, float percentage);The default mutation percentage can be defined by updating ofParameter<float> mutationPercentage;
The mutation uses a simple gauss random distribution to add a small random value to the current parameter value as follows
float range = maxValue - minValue;
float mutation = ofRandomGaussian(0.0f, mutationPercentage / 4) * range;
float mutatedValue = currentValue + mutation;To follow the workflow of presets and sequence steps, there are a couple of handy events you can listen to:
ofEvent<void> sequencePresetFinishedNotifies after the defined time for a preset in the sequencer ended see sequencer timesofEvent<void> sequenceFinished;Notifies when the sequence playback is finishedofEvent<void> presetAppicationStarted;Notifies when a preset interpolation startsofEvent<void> transitionFinished;Notifies when the interpolation transition fully finished, both for direct preset application and sequencer step
No data is send on the notification (notification logs are printed), but useful information can be retrieved from the manager:
ofParameter<std::vector<int>> sequence;
int getCurrentPreset();
bool isInterpolating();
bool isPlayingSequence();
int getSequenceIndex();Example of using events:
ofAddListener(manager.transitionFinished, this, &ofApp::onPresetChanged);
...
void ofApp::onPresetChanged() {
ofLog() << "Receiving preset changed event, now #" << manager.getCurrentPreset();
}Each preset is saved in two digits json file: 01.json, 02.json, etc.
Json files contains all the parameters and parameter groups in the same parameter file.
The json file follows the same structure as the parameter group, with the first level being the group name.
Nested groups not supported.
JSON example:
{
"simulation": {
"particles": 5000,
"mass": 3,
"thermostat": 0.1
},
"render": {
"color": 7247871,
"color_alpha": 128
}
}Set json file path with manager.setPresetPath(std::string path);
This addon is born from working on esencia (a reinterpreted interactive art installation) and the need of a simple way to handle presets and interpolation. ofParameters and ofParameterGroup are great for working with parameters, but they missed interpolation. The rest of the features were usefull for esencia, and I thought they could be useful for others.
