/* * Copyright (C) Justo Montiel, David Torres, Sergio Gomez, Alberto Fernandez * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see * <http://www.gnu.org/licenses/> */ package utils; /** * <p> * <b>MultiDendrograms</b> * </p> * * Calculate smart axis bounds and ticks * * @author Justo Montiel, David Torres, Sergio Gómez, Alberto Fernández * * @since JDK 6.0 */ public class SmartAxis { public enum NiceType {NICE_FLOOR, NICE_CEIL, NICE_ROUND} private double min, max; double saMin, saMax, saTicksSize; public SmartAxis(double min, double max) { this.min = min; this.max = max; calculateSmartAxis(); } public double smartMin() { return saMin; } public double smartMax() { return saMax; } public double smartTicksSize() { return saTicksSize; } public void calculateSmartAxis() { saMin = min; saMax = max; roundAxisLimits(); roundTicks(); } private void roundAxisLimits() { int nrange; if (min == max) { switch (sign(min)) { case 0: min = -1.0; max = +1.0; break; case 1: min /= 2.0; max *= 2.0; break; case -1: min *= 2.0; max /= 2.0; break; } } saMin = min; saMax = max; if (sign(saMin) == sign(saMax)) { nrange = (int) -Math.rint(Math.log10(Math.abs(2 * (saMax - saMin) / (saMax + saMin)))); nrange = Math.max(0, nrange); } else { nrange = 0; } saMin = niceNum(saMin, nrange, NiceType.NICE_FLOOR); saMax = niceNum(saMax, nrange, NiceType.NICE_CEIL); if (sign(saMin) == sign(saMax)) { if (saMax / saMin > 5.0) { saMin = 0.0; } else if (saMin / saMax > 5.0) { saMax = 0.0; } } } private void roundTicks() { final int numTicks = 10; saTicksSize = niceNum((saMax - saMin) / (numTicks - 1), 0, NiceType.NICE_ROUND); } private int sign(double x) { return (int) Math.round(Math.signum(x)); } public double niceNum(double x, int nrange, NiceType round) { long xsign; double f, y, fexp, rx, sx; if (x == 0.0) { return 0.0; } xsign = sign(x); x = Math.abs(x); fexp = Math.floor(Math.log10(x)) - nrange; sx = x / Math.pow(10.0, fexp) / 10.0; // scaled x rx = Math.floor(sx); // rounded x f = 10.0 * (sx - rx); // fraction between 0 and 10 if ((round == NiceType.NICE_FLOOR && xsign == +1) || (round == NiceType.NICE_CEIL && xsign == -1)) { y = (int) Math.floor(f); } else if ((round == NiceType.NICE_FLOOR && xsign == -1) || (round == NiceType.NICE_CEIL && xsign == +1)) { y = (int) Math.ceil(f); } else { // round == NiceType.NICE_ROUND if (f < 1.5) y = 1; else if (f < 3.0) y = 2; else if (f < 7.0) y = 5; else y = 10; } sx = rx + (double) y / 10.0; return xsign * sx * 10.0 * Math.pow(10.0, fexp); } }