Ticket #2012(new defect) — at Initial Version

Opened 6 years ago

Support general text specified axis class

Reported by: Owned by: bdezonia bdezonia major imagej-2.0.0 ImgLib2 serious

Description

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.

<code>
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";

}

}

}
</code>

Note: See TracTickets for help on using tickets.