package gov.nasa.arc.mct.fastplot.scatter;
import gov.nasa.arc.mct.fastplot.bridge.AbstractAxis;
import gov.nasa.arc.mct.fastplot.bridge.AbstractAxisBoundManager;
import gov.nasa.arc.mct.fastplot.bridge.AbstractPlottingPackage;
import gov.nasa.arc.mct.fastplot.bridge.PlotConstants.LimitAlarmState;
/**
* Manages plot axis boundary with "Fixed" behavior. Data out of range does not adjust
* the axes; such adjustments must be explicitly requested via "expand" and "collapse"
* methods (triggered by users via boundary arrows - "purple arrows")
*
* Note that this can be considered "normal" plot behavior from an implementation
* perspective: Other modes are most simply implemented as subclasses of this.
*
* @author vwoeltje
*
*/
public class NonTimeFixedBoundManager implements AbstractAxisBoundManager {
private AbstractAxis axis;
private AbstractPlottingPackage plot;
private boolean isMaximal;
private double extremum;
private double priorValue;
private long mostRecentAlarm;
private LimitAlarmState state;
public NonTimeFixedBoundManager(AbstractPlottingPackage plot, AbstractAxis axis, boolean isMaximal) {
super();
this.axis = axis;
this.plot = plot;
this.isMaximal = isMaximal;
this.extremum = isMaximal ? Double.MIN_VALUE : Double.MAX_VALUE;
this.priorValue = getCurrent();
this.state = LimitAlarmState.NO_ALARM;
}
@Override
public void informPointPlottedAtTime(long timestamp, double value) {
if (Double.isNaN(value) || Double.isInfinite(value)) {
return;
}
if (meetsOrExceedsExtremum(value)) {
extremum = value;
}
if (!meetsOrExceedsExtremum(getCurrent())) {
state = LimitAlarmState.ALARM_RAISED;
mostRecentAlarm = timestamp;
}
if (state != LimitAlarmState.ALARM_RAISED && mostRecentAlarm < plot.getMinTime()) {
state = LimitAlarmState.NO_ALARM;
}
}
@Override
public LimitAlarmState getState() {
return state;
}
@Override
public void expand() {
if (state == LimitAlarmState.NO_ALARM || state == LimitAlarmState.ALARM_CLOSED_BY_USER) {
priorValue = getCurrent();
}
if (!meetsOrExceedsExtremum(priorValue)) {
// Note that sign of "pad" will negate if axes are inverted, so the adjustments remain valid
// even when maximum is at start.
// Also note that the goal is for padding to be a percentage of the span AFTER adjustment
double p = getPadding();
double next = (getOpposite() * p - extremum) / (p - 1);
if (isStart()) {
axis.setStart(next);
} else {
axis.setEnd(next);
}
plot.notifyObserversAxisChanged(getAxis());
state = LimitAlarmState.ALARM_OPENED_BY_USER;
}
}
@Override
public void collapse() {
if (isStart()) {
axis.setStart(priorValue);
} else {
axis.setEnd(priorValue);
}
plot.notifyObserversAxisChanged(getAxis());
state = LimitAlarmState.ALARM_CLOSED_BY_USER;
}
@Override
public AbstractAxis getAxis() {
return axis;
}
@Override
public boolean isStart() {
return isMaximal ^ (axis.getEnd() > axis.getStart());
}
private double getCurrent() {
return isStart() ? axis.getStart() : axis.getEnd();
}
private double getOpposite() {
return isStart() ? axis.getEnd() : axis.getStart();
}
private boolean meetsOrExceedsExtremum(double value) {
return (Math.signum(value - extremum) != (isMaximal ? -1 : 1));
}
private double getPadding() {
return isMaximal ? plot.getNonTimeMaxPadding() : plot.getNonTimeMinPadding();
}
}