Ticket #2012(closed defect: moved)

Opened 6 years ago

Support general text specified axis class

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

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:1 Changed 6 years ago by bdezonia

• Description modified (diff)

comment:2 Changed 6 years ago by bdezonia

• Blocking 1400 added

comment:3 Changed 6 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.

Last edited 6 years ago by bdezonia (previous) (diff)

comment:4 Changed 5 years ago by curtis

• Status changed from new to closed
• Resolution set to moved
Note: See TracTickets for help on using tickets.