Functions in the state-machine-framework are defined as interfaces. The interface has one method, double f(double x), that defines the input and output of the function. Functions are used in AnalogInputScaler to scale inputs such as joystick values.

ftc/electronvolts/util/Function.java

package ftc.electronvolts.util;

/**
 * This file was made by the electronVolts, FTC team 7393
 *
 * An interface that defines a scaling function
 */
public interface Function {
    double f(double x);
}

To create Function implementations easily, use the Functions factory class. It has methods for creating Function objects that are polynomial, logarithmic, exponential, etc. Note: the squared() function retains the sign so it can be used for joystick scaling.

ftc/electronvolts/util/Functions.java

package ftc.electronvolts.util;

/**
 * This file was made by the electronVolts, FTC team 7393
 *
 * Factory class for InputScaler
 * These can be used for joystick scaling
 */
public class Functions {
    /**
     * A constant function y(x) = c
     * 
     * @param c the constant
     * @return the created Function
     */
    public static Function constant(final double c) {
        return new Function() {
            @Override
            public double f(double x) {
                return c;
            }
        };
    }

    /**
     * A linear function y(x) = mx + b
     * 
     * @param m the slope
     * @param b the y-intercept
     * @return the created Function
     */
    public static Function linear(final double m, final double b) {
        return new Function() {
            @Override
            public double f(double x) {
                return m * x + b;
            }
        };
    }

    /**
     * A quadratic function y(x) = ax^2 + bx + c
     * 
     * @param a the squared coefficient
     * @param b the linear coefficient
     * @param c the constant coefficient
     * @return the created Function
     */
    public static Function quadratic(final double a, final double b, final double c) {
        return new Function() {
            @Override
            public double f(double x) {
                return a * x * x + b * x + c;
            }
        };
    }

    /**
     * A cubic function y(x) = ax^3 + bx^2 + cx + d
     * 
     * @param a the cubed coefficient
     * @param b the squared coefficient
     * @param c the linear coefficient
     * @param d the constant coefficient
     * @return the created Function
     */
    public static Function cubic(final double a, final double b, final double c, final double d) {
        return new Function() {
            @Override
            public double f(double x) {
                return a * x * x * x + b * x * x + c * x + d;
            }
        };
    }

    /**
     * A polynomial function:
     * y(x) = a[n]*x^n + a[n-1]*x^(n-1) + ... + a[2]*x^2 + a[1]*x + a[0]
     * note: the first coefficient is the constant term, not the highest term
     * 
     * @param coefficients the constants for each x term
     * @return the created Function
     */
    public static Function polynomial(final double[] coefficients) {
        return new Function() {
            @Override
            public double f(double x) {
                double output = 0;
                double xPart = 1;
                for (int i = 0; i < coefficients.length; i++) {
                    output += coefficients[i] * xPart;
                    xPart *= x;
                }
                return output;
            }
        };
    }

    /**
     * The output = the input squared with the sign retained
     *
     * @return the created Function
     */
    public static Function squared() {
        return new Function() {
            @Override
            public double f(double x) {
                return x * x * Math.signum(x);
            }
        };
    }

    /**
     * The output = the input cubed
     *
     * @return the created Function
     */
    public static Function cubed() {
        return new Function() {
            @Override
            public double f(double x) {
                return x * x * x;
            }
        };
    }

    /**
     * @param deadZone the deadzone to use
     * @return the created Function
     */
    public static Function deadzone(final DeadZone deadZone) {
        return new Function() {
            @Override
            public double f(double x) {
                if (deadZone.isInside(x)) {
                    return 0;
                } else {
                    return x;
                }
            }
        };
    }

    /**
     * limits the input to between min and max
     *
     * @param min the min value
     * @param max the min value
     * @return the created Function
     */
    public static Function limit(final double min, final double max) {
        return new Function() {
            @Override
            public double f(double x) {
                return Utility.limit(x, min, max);
            }
        };
    }

    /**
     * Combines 2 Functions like a composite function b(a(x))
     *
     * @param inner a(x)
     * @param outer b(x)
     * @return the created Function
     */
    public static Function composite(final Function inner, final Function outer) {
        return new Function() {
            @Override
            public double f(double x) {
                return outer.f(inner.f(x));
            }
        };
    }

    /**
     * No modification to the input
     *
     * @return the created Function
     */
    public static Function none() {
        return new Function() {
            @Override
            public double f(double x) {
                return x;
            }
        };
    }

    /**
     * Multiplies the input by a constant
     *
     * @param m the slope
     * @return the created Function
     */
    public static Function linear(final double m) {
        return linear(m, 0);
    }

    /**
     * Logarithmic scaling
     *
     * @param logBase the base of the logarithm
     * @return the created Function
     */
    public static Function logarithmic(final double logBase) {
        return new Function() {
            @Override
            public double f(double x) {
                if (logBase > 0) {
                    double sign = Math.signum(x);
                    x = Math.abs(x);
                    // a log function including the points (0,0) and (1,1)
                    return sign * Math.log(logBase * x + 1) / Math.log(logBase + 1);
                } else {
                    return x;
                }
            }
        };
    }

    /**
     * e^ax based scaling
     * for a != 0
     * y(x) = signum(x) * (e^abs(ax)-1)/(e^a-1)
     * 
     * for a = 0
     * y(x) = x
     * 
     *
     * @param a constant value that determines how curved the function is
     * @return the created Function
     */
    public static Function eBased(final double a) {
        return new Function() {
            @Override
            public double f(double x) {
                if (a == 0) {
                    return x;
                } else {
                    double sign = Math.signum(x);
                    x = Math.abs(x);
                    // a e-based function including the points (0,0) and (1,1)
                    return sign * Math.expm1(a*x)/Math.expm1(a);
                }
            }
        };
    }

    /**
     * a line from (-1,-1) to (-x,-y) to (0,0) to (x,y) to (1,1)
     *
     * @param pointX x
     * @param pointY y
     * @param maxValue the maximum value of the input
     * @return the created Function
     */
    public static Function piecewise(double pointX, double pointY, double maxValue) {
        final double x = Utility.motorLimit(Math.abs(pointX));
        final double y = Utility.motorLimit(Math.abs(pointY));
        final double max = Utility.motorLimit(Math.abs(maxValue));
        final double slope1;
        if (x == 0) {
            slope1 = 0;
        } else {
            slope1 = y / x;
        }
        final double slope2;
        if (x == 1) {
            slope2 = 0;
        } else {
            slope2 = (y - max) / (x - 1);
        }
        return new Function() {
            public double f(double input) {
                if (Math.abs(input) < x) {
                    return slope1 * input;
                } else {
                    return Utility.motorLimit((slope2 * (Math.abs(input) - x) + y) * Math.signum(input));
                }
            }
        };
    }
}