Skip to content

Conversation

@fewtarius
Copy link
Contributor

This PR changes analog input scaling to scale quadratically instead of linearly which makes small movement less abrupt for more precise control.

make movement less abrupt and smooth it out for more precise control.
@pastaq
Copy link
Contributor

pastaq commented Nov 11, 2025

Can you explain a bit more why this is necessary? As a global change I'm concerned that it might be too opinionated.

@fewtarius
Copy link
Contributor Author

Can you explain a bit more why this is necessary? As a global change I'm concerned that it might be too opinionated.

It's better for precise aiming as it changes the scaling algorithm to be more human response like, micro adjustment with small stick movements while keeping high sensitivity for large stick movements. It's not technically necessary, I'm happy to just keep it in my fork if you don't think it fits.

@ShadowApex
Copy link
Contributor

I think this would be a good option to have, but we should make this configurable (and off by default for now).

We could add this as a capability config option:

capability_map.rs

#[derive(Debug, Deserialize, Serialize, Clone, JsonSchema, PartialEq)]
#[serde(rename_all = "snake_case")]
pub struct AxisCapability {
    pub name: String,
    pub direction: Option<String>,
    pub deadzone: Option<f64>,
    pub quadratic_scaling: Option<bool>,
}

Then we can check for the option in the translate function:

value.rs

// Axis -> Axis
Gamepad::Axis(_) => {
    let use_scaling = target_config
        .gamepad
        .as_ref()
        .and_then(|gamepad| gamepad.axis.as_ref())
        .and_then(|axis| axis.quadratic_scaling)
        .unwrap_or(false);

    match use_scaling {
        false => Ok(self.clone()),
        true => match self {
            InputValue::Vector2 { x, y } => {
                let scaled_x = x.map(|v| v * v.abs());
                let scaled_y = y.map(|v| v * v.abs());
                Ok(InputValue::Vector2 {
                    x: scaled_x,
                    y: scaled_y,
                })
            }
            _ => Ok(self.clone()),
        },
    }
}

Copy link
Contributor

@pastaq pastaq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add as optional setting per @ShadowApex comment

@fewtarius
Copy link
Contributor Author

I think this would be a good option to have, but we should make this configurable (and off by default for now).

We could add this as a capability config option:

capability_map.rs

#[derive(Debug, Deserialize, Serialize, Clone, JsonSchema, PartialEq)]
#[serde(rename_all = "snake_case")]
pub struct AxisCapability {
    pub name: String,
    pub direction: Option<String>,
    pub deadzone: Option<f64>,
    pub quadratic_scaling: Option<bool>,
}

Then we can check for the option in the translate function:

value.rs

// Axis -> Axis
Gamepad::Axis(_) => {
    let use_scaling = target_config
        .gamepad
        .as_ref()
        .and_then(|gamepad| gamepad.axis.as_ref())
        .and_then(|axis| axis.quadratic_scaling)
        .unwrap_or(false);

    match use_scaling {
        false => Ok(self.clone()),
        true => match self {
            InputValue::Vector2 { x, y } => {
                let scaled_x = x.map(|v| v * v.abs());
                let scaled_y = y.map(|v| v * v.abs());
                Ok(InputValue::Vector2 {
                    x: scaled_x,
                    y: scaled_y,
                })
            }
            _ => Ok(self.clone()),
        },
    }
}

Alright, I'll submit the changes when I can.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants