Preset templates

Parameterised presets with substitution and forEach expansion

Reference for FixoPresetTemplate — parameterised preset patterns the user fills in via a two-page wizard to produce a complete FixoPreset ready to build.

Why preset templates exist

Building a 24-actuator hexapod preset by clicking "Add actuator" 24 times is repetitive. A preset template captures the actuator pattern (3 joints × N legs), parameterises the leg count, and lets the wizard fill in the rest. Output: a fully-populated preset, board chosen, actuators tagged, stanza wired up via the matching stanza template.


Built-in templates

Three preset templates ship with libfixo:

rc-car

Parameters board (board-type), name (string)
Actuators 2 motors tagged ["wheel", "left"] and ["wheel", "right"]
Stanza One drive stanza using differential-drive stanza template
Result Drives with CarSteeringWidget on Remote

tracked-vehicle

Parameters board (board-type), name (string)
Actuators 2 actuators tagged ["track", "left"] and ["track", "right"]
Stanza One drive stanza using tracked-drive stanza template

hexapod

Parameters board (board-type), name (string), legCount (int, default 6)
Actuators legCount × 3 actuators per leg: coxa, femur, tibia tagged accordingly
Stanza One walk stanza using hexapod-walk stanza template
Notes Crank legCount to 4 for a quadruped or 8 for an octapod

Substitution language

The template's preset body is a JSON tree with placeholders. The instantiator does three things:

1. {{paramName}} substitution

Anywhere in any string field. The user-supplied parameter value is interpolated:

{
  "name":  "{{name}}",
  "board": "{{board}}"
}

Becomes, with name = "Hexy" and board = "arduino-mega2560":

{
  "name":  "Hexy",
  "board": "arduino-mega2560"
}

If a placeholder doesn't resolve to a known parameter, it's left intact — useful for placeholders meant to be filled in by the consumer at a later stage (e.g. stanza-template references).

2. Typed parameters

Each template declares parameters with a type and optional default:

"parameters": {
  "board":    {"type": "board-type", "default": "arduino-mega2560"},
  "name":     {"type": "string"},
  "legCount": {"type": "int", "default": 6}
}

Supported types:

type Wizard widget Notes
"board-type" Board dropdown populated from FixoBoardTypeBook The chosen value substitutes into {{board}}
"int" QSpinBox Default applied if missing; non-integer input rejected
"string" QLineEdit Default applied if missing

3. forEachActuators expansion

For per-leg / per-joint patterns, you don't write 18 actuators by hand. Instead:

"forEachActuators": [
  {
    "countParameter": "legCount",
    "pattern": {
      "type": "rc-servo",
      "name":         "leg_{{index}}_coxa",
      "semanticTags": ["leg_{{index}}", "coxa"]
    }
  },
  {
    "countParameter": "legCount",
    "pattern": {
      "type": "rc-servo",
      "name":         "leg_{{index}}_femur",
      "semanticTags": ["leg_{{index}}", "femur"]
    }
  },
  {
    "countParameter": "legCount",
    "pattern": {
      "type": "rc-servo",
      "name":         "leg_{{index}}_tibia",
      "semanticTags": ["leg_{{index}}", "tibia"]
    }
  }
]

The pattern is duplicated N = parameterValues[countParameter] times, with {{index}} substituting 0…N-1 per iteration. With legCount = 6 you get 18 actuators tagged leg_0_coxa, leg_0_femur, leg_0_tibia, leg_1_coxa, …, leg_5_tibia.

What is not supported

  • Arithmetic: {{legs * joints_per_leg}} is not a thing. Use a single int parameter for the count, or fix the per-iteration count (the hexapod has three joints per leg, so it uses three forEachActuators rules — one per joint).
  • Conditional fields: there's no {{if}} / {{unless}}. Author per-board variants by templating the JSON wholesale.
  • Generators: {{generate:fn(expr)}} is also out of scope.

The first pass is intentionally minimal; the goal is "make 80 % of common cases trivial without a Turing-complete templating layer". Custom templates that need arithmetic should pre-compute and expose the result as a separate parameter.


Stanza-template references

A preset template's stanzas typically reference a stanza template by name. The instantiator emits a stub stanza with lobeType prefixed "template:":

"stanzas": [
  {"id": "walk", "lobeType": "template:hexapod-walk"}
]

The consumer (typically the preset editor or FixoRuntime setup) sees the template: prefix and expands it via FixoStanzaTemplateBook::get("hexapod-walk")::instantiate(stanzaId, actuators). That step runs auto-connect against the just-expanded actuators, so the resulting FixoStanza has its mapping populated correctly.

This indirection means a preset template doesn't need to repeat the stanza-template's port list — it just names the template and lets the auto-connect do the wiring.


Wizard

The Studio's FixoPresetManager exposes a From template button that pushes FixoPresetTemplateWizardActivity:

Page Content
1 Template list with descriptions; user picks one
2 Parameter form: typed inputs per parameter (board dropdown, int spinbox, string line edit), all seeded with defaults; preset Name field seeded from template name with collision check against FixoPresetBook

On Accept, the wizard:

  1. Validates parameters (type + presence)
  2. Calls FixoPresetTemplate::instantiate(parameterValues, nameOverride)
  3. Adds the resulting FixoPreset to FixoPresetBook
  4. Pops back to the manager, with the new preset selected

See Use a preset template for the user-side walkthrough, and Author a preset template for writing your own.


Custom templates

Drop a fixo.json with fixoType: "preset-template" into <personality>/fixo/preset-templates/. FixoLibrary::scan() picks it up; it appears in the wizard alongside the built-ins.

The recommended workflow is duplicate-to-edit: open FixoPresetTemplateManager in the Studio, duplicate the closest built-in, rename, edit the JSON.


API

struct FixoPresetTemplate {
    QString name, title, description;
    bool builtin{false};
    QMap<QString, ParameterSpec> parameters;
    FixoPreset basePreset;                       // tree with placeholders
    QVector<ForEachActuators> forEachActuators;

    FixoPreset instantiate(
        const QMap<QString, QVariant> &parameterValues,
        const QString &nameOverride = {}) const;
};
class FixoPresetTemplateBook : public FixoBook {
    QStringList list();
    FixoPresetTemplate get(const QString &name);
    bool   create(const QString &name);
    bool   rename(const QString &oldName, const QString &newName);
    bool   remove(const QString &name);
    bool   importFile(const QString &path);
    bool   exportFile(const QString &name, const QString &path);
    bool   isBuiltin(const QString &name);
};

Source code

Each header has the name of its class, in the folder that the list names.

  • FixoPresetTemplate: libfixo/fixo/preset/
  • FixoPresetTemplateBook: libfixo/fixo/library/
  • FixoPresetTemplateWizardActivity: libfixo/fixo/studio/

See also

In this section
Topics
reference hardware fixo preset template wizard
See also