/*
* Copyright 2013-2016 Cel Skeggs
*
* This file is part of the CCRE, the Common Chicken Runtime Engine.
*
* The CCRE is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* The CCRE is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the CCRE. If not, see <http://www.gnu.org/licenses/>.
*/
package ccre.channel;
import ccre.util.Utils;
import ccre.verifier.FlowPhase;
import ccre.verifier.SetupPhase;
/**
* A FloatFilter is a stateless transformer that can be wrapped around any
* Output or Input.
*
* @author skeggsc
*/
public abstract class FloatFilter {
/**
* A FloatFilter that negates the value.
*/
public static final FloatFilter negate = new FloatFilter() {
@Override
public float filter(float input) {
return -input;
}
};
/**
* A FloatFilter that makes values positive.
*/
public static final FloatFilter absolute = new FloatFilter() {
@Override
public float filter(float input) {
return Math.abs(input);
}
};
/**
* Filter this value according to the subclass's implementation.
*
* @param input The input to filter.
* @return The filtered value.
*/
@FlowPhase
public abstract float filter(float input);
/**
* Returns a FloatInput representing the filtered version of the specified
* input.
*
* @param input The input to filter.
* @return the filtered input.
*/
@SetupPhase
public FloatInput wrap(final FloatInput input) {
if (input == null) {
throw new NullPointerException();
}
FloatCell out = new FloatCell(filter(input.get()));
input.send(wrap((FloatOutput) out));
return out;
}
/**
* Returns a FloatOutput that, when written to, writes the filtered version
* of the value through to the specified output.
*
* @param output the output to write filtered values to.
* @return the output to write values to in order to filter them.
*/
@SetupPhase
public FloatOutput wrap(final FloatOutput output) {
if (output == null) {
throw new NullPointerException();
}
return value -> output.set(FloatFilter.this.filter(value));
}
/**
* Return a Filter that applies the specified-size deadzone as defined in
* Utils.deadzone.
*
* @param deadzone The deadzone size to apply, which must be greater than
* zero and less than infinity.
* @return The filter representing this deadzone size.
* @see ccre.util.Utils#deadzone(float, float)
*/
@SetupPhase
public static FloatFilter deadzone(final float deadzone) {
if (!Float.isFinite(deadzone) || deadzone < 0) {
throw new IllegalArgumentException("deadzones cannot be NaN, infinite, or less than or equal to zero!");
}
return new FloatFilter() {
@Override
public float filter(float input) {
return Utils.deadzone(input, deadzone);
}
};
}
/**
* Return a Filter that applies the specified limitation to the value.
*
* If the original is NaN, the filtered result is always NaN. If either
* bound is NaN, then it will be ignored.
*
* @param minimum The minimum value to limit to. Use Float.NEGATIVE_INFINITY
* if you want no lower bound.
* @param maximum The maximum value to limit to. Use Float.POSITIVE_INFINITY
* if you want no upper bound.
* @return The filter representing the specified limit.
* @throws IllegalArgumentException if maximum is less than minimum
*/
@SetupPhase
public static FloatFilter limit(final float minimum, final float maximum) throws IllegalArgumentException {
if (maximum < minimum) {
throw new IllegalArgumentException("Maximum is smaller than minimum!");
}
return new FloatFilter() {
@Override
public float filter(float input) {
if (input < minimum) {
return minimum;
} else if (input > maximum) {
return maximum;
} else {
return input;
}
}
};
}
}