> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/opentrack/opentrack/llms.txt
> Use this file to discover all available pages before exploring further.

# Mapping Curves

> Customize head tracking response with spline-based mapping curves

Mapping curves transform filtered tracking data to final output, allowing you to customize how head movements translate to in-game view changes. This is one of OpenTrack's most powerful features.

## Mapping System Overview

OpenTrack uses spline curves to map input ranges to output ranges:

```cpp theme={null}
struct Map {
    axis_opts& opts;           // Axis configuration
    QString name, alt_name;    // Spline names
    spline spline_main;        // Primary spline curve
    spline spline_alt;         // Alternate spline (for negative values)
};

class Mappings {
private:
    Map axes[6];  // One map per axis
public:
    Map& operator()(int i) { return axes[i]; }
};
```

## How Mapping Works

The pipeline applies mapping curves after filtering:

```cpp theme={null}
if (s.apply_mapping_curves) {
    // CAVEAT: rotation only, before reltrans
    for (int i = 3; i < 6; i++)
        value(i) = map(value(i), m(i));
}

value = apply_reltrans(value, disabled, center_ordered);

if (s.apply_mapping_curves) {
    // CAVEAT: translation only, after reltrans  
    for (int i = 0; i < 3; i++)
        value(i) = map(value(i), m(i));
}
```

<Note>
  Rotation axes are mapped **before** relative translation, while position axes are mapped **after**. This ensures proper coordinate transformations.
</Note>

### Map Function

```cpp theme={null}
double pipeline::map(double pos, const Map& axis) {
    bool altp = (pos < 0) && axis.opts.altp;
    
    axis.spline_main.set_tracking_active(!altp);
    axis.spline_alt.set_tracking_active(altp);
    
    auto& fc = altp ? axis.spline_alt : axis.spline_main;
    return fc.get_value(pos);
}
```

## Spline Curves

OpenTrack uses Catmull-Rom splines for smooth interpolation:

```cpp theme={null}
class spline {
    static constexpr unsigned value_count = 16384;  // Resolution
    static constexpr double c_interp = 5;           // Interpolation multiplier
    
    mutable std::vector<double> data;               // Pre-computed values
    mutable points_t points;                        // Control points
};
```

### Catmull-Rom Interpolation

```cpp theme={null}
// Catmull-Rom spline basis
const double cx[4] = {
    2 * p1_x,                           // 1
    -p0_x + p2_x,                       // t
    2*p0_x - 5*p1_x + 4*p2_x - p3_x,   // t²
    -p0_x + 3*p1_x - 3*p2_x + p3_x     // t³
};

const double cy[4] = {
    2 * p1_y,
    -p0_y + p2_y,
    2*p0_y - 5*p1_y + 4*p2_y - p3_y,
    -p0_y + 3*p1_y - 3*p2_y + p3_y
};

// Evaluate at parameter t [0,1]
const double t2 = t*t;
const double t3 = t*t*t;

x = 0.5 * (cx[0] + cx[1]*t + cx[2]*t2 + cx[3]*t3);
y = 0.5 * (cy[0] + cy[1]*t + cy[2]*t2 + cy[3]*t3);
```

<Note>
  Splines are pre-computed into a lookup table for performance. The curve is evaluated at 250Hz, so fast lookups are critical.
</Note>

## Axis Configuration

Each axis has configurable parameters:

```cpp theme={null}
struct axis_opts {
    value<double> zero;           // Zero position offset
    value<int> src;               // Source axis (-1 = disabled, 0-5 = axis)
    value<bool> invert_pre;       // Invert before centering
    value<bool> invert_post;      // Invert after mapping
    value<bool> altp;             // Use alternate spline for negative
    value<max_clamp> clamp_x_;    // Max input value
    value<max_clamp> clamp_y_;    // Max output value
};
```

### Input Range (clamp\_x\_)

<Tabs>
  <Tab title="Rotation">
    ```cpp theme={null}
    enum max_clamp {
        r180 = 180,   // Yaw, Roll
        r90 = 90,     // Pitch (typical)
        r60 = 60,
        r45 = 45,
        r30 = 30,
        r20 = 20,
        r15 = 15,
        r10 = 10
    };
    ```

    Default ranges:

    * **Yaw**: ±180°
    * **Pitch**: ±90°
    * **Roll**: ±180°
  </Tab>

  <Tab title="Translation">
    ```cpp theme={null}
    enum max_clamp {
        t600 = 600,   // cm (very large)
        t300 = 300,
        t150 = 150,
        t100 = 100,
        t75 = 75,
        t30 = 30,     // Default
        t20 = 20,
        t15 = 15,
        t10 = 10
    };
    ```

    Default range: ±30cm for X, Y, Z
  </Tab>
</Tabs>

### Output Range (clamp\_y\_)

Similar ranges, but can be negative to invert:

```cpp theme={null}
enum max_clamp {
    o_r180 = -180,  // Inverted rotation
    o_r90 = -90,
    o_t75 = -75,    // Inverted translation
    o_t100 = -100,
    // ... etc
};
```

## Common Curve Types

<Tabs>
  <Tab title="Linear (1:1)">
    ```
    Input  →  Output
    ──────────────────
      0    →    0
     45    →   45
     90    →   90
    ```

    **Use case**: Direct mapping, no modification

    **Configuration**:

    * Two points: (0, 0) and (max\_input, max\_output)
    * Input range = Output range
  </Tab>

  <Tab title="Amplified (2:1)">
    ```
    Input  →  Output
    ──────────────────
      0    →    0
     30    →   60
     45    →   90
    ```

    **Use case**: Increase sensitivity, see more with less movement

    **Configuration**:

    * Points: (0, 0), (45, 90)
    * Output = 2 × Input

    <Warning>
      High amplification can make precise aiming difficult.
    </Warning>
  </Tab>

  <Tab title="Reduced (1:2)">
    ```
    Input  →  Output
    ──────────────────
      0    →    0
     60    →   30
     90    →   45
    ```

    **Use case**: Reduce sensitivity, more precise control

    **Configuration**:

    * Points: (0, 0), (90, 45)
    * Output = 0.5 × Input
  </Tab>

  <Tab title="S-Curve">
    ```
    Input  →  Output
    ──────────────────
      0    →    0
     30    →   15     (slow near center)
     60    →   60     (1:1 mid-range)
     90    →   85     (fast at extremes)
    ```

    **Use case**: Precise control in center, increased range at edges

    **Configuration**:

    * Points: (0,0), (30,15), (60,60), (90,85)
    * Smooth curve through points

    <Note>
      S-curves provide the best balance for most users.
    </Note>
  </Tab>

  <Tab title="Deadzone">
    ```
    Input  →  Output
    ──────────────────
      0    →    0
     10    →    0     (deadzone)
     20    →   15
     90    →   90
    ```

    **Use case**: Ignore small movements, reduce jitter

    **Configuration**:

    * Points: (0,0), (10,0), (20,15), (90,90)
    * Flat region at start
  </Tab>
</Tabs>

## Practical Mapping Examples

### Flight Simulation

<Steps>
  <Step title="Yaw (Left/Right)">
    ```
    Input (°)  →  Output (°)
    ─────────────────────────
        0      →      0
       45      →     60      (1.33:1)
       90      →    110      (1.22:1)
      135      →    150      (1.11:1)
      180      →    180      (1:1)
    ```

    Amplified in center for better awareness, tapers to 1:1 at extremes.
  </Step>

  <Step title="Pitch (Up/Down)">
    ```
    Input (°)  →  Output (°)
    ─────────────────────────
        0      →      0
       20      →     15      (0.75:1)
       45      →     40      (0.89:1)
       70      →     65      (0.93:1)
       90      →     90      (1:1)
    ```

    Reduced sensitivity to prevent excessive up/down look.
  </Step>

  <Step title="Roll">
    ```
    Input (°)  →  Output (°)
    ─────────────────────────
        0      →      0
       45      →     22      (0.5:1)
       90      →     45      (0.5:1)
    ```

    Very reduced - roll is often uncomfortable in cockpit view.
  </Step>

  <Step title="Translation">
    ```
    All axes: 0.5:1 to 0.75:1
    ```

    Moderate translation to maintain cockpit reference.
  </Step>
</Steps>

### Racing Simulation

<Steps>
  <Step title="Yaw">
    ```
    Heavy amplification for apex visibility:
    (0,0), (30,60), (60,100), (90,140)
    ```
  </Step>

  <Step title="Pitch">
    ```
    Minimal - usually looking forward:
    (0,0), (30,15), (60,30)
    ```
  </Step>

  <Step title="Roll">
    ```
    Moderate to feel car body roll:
    (0,0), (30,20), (60,40)
    ```
  </Step>
</Steps>

### First-Person Shooter

<Steps>
  <Step title="Yaw & Pitch">
    ```
    1:1 or slight reduction:
    (0,0), (45,40), (90,80)
    ```

    Need precise aiming, minimal amplification.
  </Step>

  <Step title="Translation">
    ```
    Very reduced or disabled:
    Maximum 0.3:1 ratio
    ```

    Translation can be disorienting in FPS games.
  </Step>
</Steps>

## Alternate Splines

Use different curves for negative values:

```cpp theme={null}
value<bool> altp;  // Enable alternate spline

spline spline_main;  // For positive input
spline spline_alt;   // For negative input
```

**Use cases**:

* Asymmetric workspace (easier to turn one direction)
* Compensate for tracking issues on one side
* Different game mechanics per direction

<Accordion title="Example: Asymmetric Yaw">
  ```cpp theme={null}
  // Looking right (positive) - normal sensitivity
  spline_main: (0,0), (45,60), (90,110)

  // Looking left (negative) - reduced sensitivity
  spline_alt: (0,0), (45,45), (90,90)
  ```

  Useful if your tracking setup has better performance looking one direction.
</Accordion>

## Source Axis Mapping

Remap which physical axis controls each output:

```cpp theme={null}
value<int> src;  // -1 = disabled, 0-5 = TX,TY,TZ,Yaw,Pitch,Roll
```

<Tabs>
  <Tab title="Standard">
    ```cpp theme={null}
    Output  ←  Source
    ──────────────────
    TX      ←  TX
    TY      ←  TY
    TZ      ←  TZ
    Yaw     ←  Yaw
    Pitch   ←  Pitch
    Roll    ←  Roll
    ```
  </Tab>

  <Tab title="Swap Axes">
    ```cpp theme={null}
    // Rotate coordinate system 90°
    Output  ←  Source
    ──────────────────
    TX      ←  TY     // Right ← Forward
    TY      ←  TX     // Forward ← Right
    TZ      ←  TZ     // Up ← Up
    ```
  </Tab>

  <Tab title="Disable Axis">
    ```cpp theme={null}
    // Disable roll output
    Roll.src = -1;  // or 6
    ```
  </Tab>
</Tabs>

## Zero Position Offset

Shift the zero point of an axis:

```cpp theme={null}
value<double> zero;  // Offset in degrees or cm

Pose pipeline::apply_zero_pos(Pose value) const {
    for (int i = 0; i < 6; i++)
        value(i) += m(i).opts.zero * (m(i).opts.invert_pre ? -1 : 1);
    return value;
}
```

**Use cases**:

* Adjust for monitor not being centered
* Compensate for permanent head tilt
* Offset default view position in game

## Inversion Options

<Tabs>
  <Tab title="Invert Pre">
    ```cpp theme={null}
    value<bool> invert_pre;  // Invert before centering
    ```

    Applied early in pipeline:

    ```cpp theme={null}
    for (int i = 0; i < 6; i++)
        if (m(i).opts.invert_pre)
            value(i) = -value(i);
    ```
  </Tab>

  <Tab title="Invert Post">
    ```cpp theme={null}
    value<bool> invert_post;  // Invert after mapping
    ```

    Applied at end of pipeline:

    ```cpp theme={null}
    for (int i = 0; i < 6; i++)
        if (m(i).opts.invert_post)
            value(i) = -value(i);
    ```
  </Tab>
</Tabs>

<Note>
  Use **invert\_pre** for fixing tracker orientation.
  Use **invert\_post** for game-specific requirements.
</Note>

## Spline Point Management

Manipulate curve points programmatically:

```cpp theme={null}
class spline {
public:
    void add_point(double x, double y);
    void add_point(QPointF pt);
    void move_point(int idx, QPointF pt);
    void remove_point(int i);
    void clear();
    
    const points_t& get_points() const;
    int get_point_count() const;
};
```

### Example: Create Linear Curve

```cpp theme={null}
spline s;
s.clear();
s.add_point(0, 0);
s.add_point(90, 90);
s.save();
```

### Example: Create S-Curve

```cpp theme={null}
spline s;
s.clear();
s.add_point(0, 0);
s.add_point(30, 15);   // Slow in center
s.add_point(60, 60);   // 1:1 at midpoint
s.add_point(90, 85);   // Fast at edge
s.save();
```

## Performance Considerations

Splines are highly optimized:

```cpp theme={null}
static constexpr unsigned value_count = 16384;  // Lookup table size

double spline::get_value(double x) const {
    double q = x * bucket_size_coefficient(points);
    int xi = (int)q;
    double yi = get_value_internal(xi);
    double yiplus1 = get_value_internal(xi+1);
    double f = (q-xi);
    return yiplus1 * f + yi * (1 - f);  // Linear interpolation
}
```

<Note>
  The spline is pre-computed into a 16384-entry lookup table. Runtime evaluation is just a lookup and linear interpolation - very fast.
</Note>

## Troubleshooting

<Accordion title="Mapping not applying">
  1. Check "Apply mapping curves" is enabled:
     ```cpp theme={null}
     value<bool> apply_mapping_curves {b, "apply-mapping-curves", true};
     ```
  2. Verify curve has points defined
  3. Check input range (clamp\_x\_) covers your movement range
  4. Restart tracking after changing curves
</Accordion>

<Accordion title="Curve feels wrong">
  1. Check you're editing the correct axis
  2. Verify curve points are sorted by X value
  3. Ensure first point is at or near (0, 0)
  4. Check for overlapping points (auto-removed but can cause confusion)
</Accordion>

<Accordion title="Jumps or discontinuities">
  1. Splines should be smooth - check for too-sharp corners
  2. Verify input range isn't being clamped unexpectedly
  3. Check for axis remapping conflicts
  4. Look for points very close together
</Accordion>

<Accordion title="No output on one side">
  1. Check if alternate spline is enabled but not configured
  2. Verify input range is symmetric (±X, not just 0-X)
  3. Check inversion settings
  4. Look for centering issues
</Accordion>

## Best Practices

<Check>
  **Start simple** - Begin with linear 1:1 curves, then adjust
</Check>

<Check>
  **Test incrementally** - Change one axis at a time
</Check>

<Check>
  **Use S-curves** - Generally provide best comfort and precision
</Check>

<Check>
  **Match your game** - Racing needs different curves than flight sim
</Check>

<Warning>
  **Avoid extremes** - Very high amplification (>3:1) or reduction (\<0.3:1) can be uncomfortable
</Warning>

## Next Steps

<CardGroup cols={2}>
  <Card title="Configuration" icon="gear" href="/guides/configuration">
    Advanced tracking settings
  </Card>

  <Card title="Filters" icon="sliders" href="/guides/filters">
    Smooth data before mapping
  </Card>

  <Card title="Relative Translation" icon="arrows" href="/guides/configuration#relative-translation">
    Advanced translation compensation
  </Card>
</CardGroup>
