# Ticket #2012 (closed defect: moved)

## 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) (diff)

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 5 years ago 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.

**Note:**See TracTickets for help on using tickets.