Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support general text specified axis class #39

Open
ctrueden opened this Issue Dec 11, 2014 · 0 comments

Comments

Projects
None yet
1 participant
@ctrueden
Copy link
Member

ctrueden commented Dec 11, 2014

There are good reasons to support one kind of axis that has its 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";
        }

    }
}

Migrated-From: http://trac.imagej.net/ticket/2012

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.