Ticket #2012 (closed defect: moved)
Opened 2013-10-15T16:01:51-05:00
Last modified 2013-10-16T09:44:38-05:00
Support general text specified axis class
| Reported by: | bdezonia | Owned by: | bdezonia |
|---|---|---|---|
| Priority: | major | Milestone: | imagej-2.0.0 |
| Component: | ImgLib2 | Version: | |
| Severity: | serious | Keywords: | |
| Cc: | Blocked By: | ||
| Blocking: | #1400 |
Description (last modified by bdezonia)
There are good reasons to support one kind of axis that has it's equation defined by a string. I have mocked up a class below. This is not a simple endeavor but would eliminate a lot of (sometimes redundant) concrete CalibratedAxis implementations. See notes in code below for pointers.
package net.imglib2.meta.axis;
import net.imglib2.meta.AxisType;
/**
* A very general text driven axis class.
*
* @author Barry DeZonia
*/
public class UberAsciiAxis extends VariableAxis {
// I might be getting carried away but think it would be nice to define axes
// by equation alone. IJ1 has lots of one off definitions in CurveFitter that
// have offsets or not or powers or not. I've generalized some of this by
// always having an offset that can be 0. But really we'd like the flexibility
// of not needing a new concrete axis class every time someone comes up with a
// new formula. This class would parse the equation and build an appropriate
// function that can scale coords as needed. The nice thing here is that the
// parsed axis can be queried for variables and then a curve fitting algo can
// determine appropriate variable values. Curtis' idea of the EditAxes plugin
// directly tweaking the variables of a displayed equation is correct. There
// remains the trick of determining when an axis is linear. This can be done
// by testing that an axis.equals(some general linear axis string) as noted
// below.
// TODO - bad name: Equation is more general than our 1d case
private interface Equation {
// This might be involved to determine but it would be fun to write!
Equation inverse(); // if doesn't exist we will return a NullEquation
double eval(double input);
// TODO - maybe one String method rather than two.
String generalEquation();
String particularEquation(); // likely in an abstract class
}
// equation string can be things like:
// y = m*x + b
// y = 74*x + b
// y = q*sin(slope*x+19.4)^(33*power)+fred (here only x & y are predefined)
// constants and vars automatically detected
private final String equationString;
private final Equation eqn;
private final Equation eqnInverse;
public UberAsciiAxis(AxisType type, String unit, String equationString) {
super(type, unit);
this.eqn = parse(equationString);
this.eqnInverse = eqn.inverse();
this.equationString = equationString;
}
public UberAsciiAxis(AxisType type, String unit, Equation equation) {
super(type, unit);
this.eqn = equation;
this.eqnInverse = eqn.inverse();
this.equationString = equation.generalEquation(); // or particular?
}
@Override
public double calibratedValue(double rawValue) {
return eqn.eval(rawValue);
}
@Override
public double rawValue(double calibratedValue) {
return eqnInverse.eval(calibratedValue);
}
@Override
public String generalEquation() {
return equationString;
}
@Override
public UberAsciiAxis copy() {
return new UberAsciiAxis(type(), unit(), equationString);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof UberAsciiAxis)) return false;
UberAsciiAxis other = (UberAsciiAxis) o;
// TODO: do something really cool. compare syntax trees and variables of the
// two UberAxes. Even trickier: detect when vars of two eqns are the same
// due to coeffs being 1 or 0. For example y = 1*x + 0 is same as y = x
// though they would have different syntax trees.
// TEMP: only equal to self
return other == this;
}
// For generality and reuse elsewhere maybe make parser return multidim
// equation. We'd need to test that it has a single dim of input to qualify as
// a valid axis equation
private Equation parse(String equationString) {
// TODO
return new NullEquation();
}
// return one of these as an Equation::inverse() when it is not invertible
private class NullEquation implements Equation {
@Override
public double eval(double input) {
return Double.NaN;
}
@Override
public Equation inverse() {
return this;
}
@Override
public String generalEquation() {
return "y = NaN";
}
@Override
public String particularEquation() {
return "y = NaN";
}
}
}
Change History
comment:3 Changed 2013-10-16T09:44:38-05:00 by bdezonia
AFter discussing with Mark we determined this has some other benefits. Currently CalibratedAxis has no means of setting values. But this implementation as a base could have the equivalent of axis.setEquation("y=1.3*x + 43"). So it would be easier to calibrate things at runtime.
comment:4 Changed 2014-12-11T14:30:17-06:00 by curtis
- Status changed from new to closed
- Resolution set to moved