net.sf.mzmine.chartbasics.ChartLogics.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.mzmine.chartbasics.ChartLogics.java

Source

/*
 * Copyright 2006-2018 The MZmine 2 Development Team
 * 
 * This file is part of MZmine 2.
 * 
 * MZmine 2 is free software; you can redistribute it and/or modify it under the terms of the GNU
 * General Public License as published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 * 
 * MZmine 2 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
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with MZmine 2; if not,
 * write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
 * USA
 */

package net.sf.mzmine.chartbasics;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.Point;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.logging.Logger;
import javax.swing.JPanel;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.Axis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.entity.AxisEntity;
import org.jfree.chart.entity.ChartEntity;
import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.CombinedRangeXYPlot;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.ui.RectangleEdge;
import org.jfree.data.Range;
import net.sf.mzmine.chartbasics.gui.swing.EChartPanel;

/**
 * Collection of methods for JFreeCharts <br>
 * Calculate mouseXY to plotXY <br>
 * Calc width and height for plots where domain and range axis share the same dimensions <br>
 * Zoom and shift axes by absolute or relative values
 * 
 * @author Robin Schmid (robinschmid@uni-muenster.de)
 */
public class ChartLogics {
    private static Logger logger = Logger.getLogger(ChartLogics.class.getName());

    /**
     * Translates mouse coordinates to chart coordinates (xy-axis)
     * 
     * @param myChart
     * @param mouseX
     * @param mouseY
     * @return Range as chart coordinates
     * @throws Exception
     */
    public static Point2D mouseXYToPlotXY(ChartPanel myChart, double mouseX, double mouseY) throws Exception {
        return mouseXYToPlotXY(myChart, (int) mouseX, (int) mouseY);
    }

    /**
     * Translates mouse coordinates to chart coordinates (xy-axis)
     * 
     * @param myChart
     * @param mouseX
     * @param mouseY
     * @return Range as chart coordinates
     * @throws Exception
     */
    public static Point2D mouseXYToPlotXY(ChartPanel myChart, int mouseX, int mouseY) throws Exception {
        Point2D p = myChart.translateScreenToJava2D(new Point(mouseX, mouseY));

        XYPlot plot = null;
        // find plot as parent of axis
        ChartEntity entity = findChartEntity(myChart, mouseX, mouseY);
        if (entity instanceof AxisEntity) {
            Axis a = ((AxisEntity) entity).getAxis();
            if (a.getPlot() instanceof XYPlot)
                plot = (XYPlot) a.getPlot();
        }

        ChartRenderingInfo info = myChart.getChartRenderingInfo();
        int subplot = info.getPlotInfo().getSubplotIndex(p);
        Rectangle2D dataArea = info.getPlotInfo().getDataArea();
        if (subplot != -1)
            dataArea = info.getPlotInfo().getSubplotInfo(subplot).getDataArea();

        if (plot == null)
            plot = findXYSubplot(myChart.getChart(), info, p.getX(), p.getY());

        // coordinates
        double cx = 0;
        double cy = 0;
        if (plot != null) {
            // find axis
            ValueAxis domainAxis = plot.getDomainAxis();
            ValueAxis rangeAxis = plot.getRangeAxis();
            RectangleEdge domainAxisEdge = plot.getDomainAxisEdge();
            RectangleEdge rangeAxisEdge = plot.getRangeAxisEdge();
            // parent?
            if (domainAxis == null && plot.getParent() != null && plot.getParent() instanceof XYPlot) {
                XYPlot pp = ((XYPlot) plot.getParent());
                domainAxis = pp.getDomainAxis();
                domainAxisEdge = pp.getDomainAxisEdge();
            }
            if (rangeAxis == null && plot.getParent() != null && plot.getParent() instanceof XYPlot) {
                XYPlot pp = ((XYPlot) plot.getParent());
                rangeAxis = pp.getRangeAxis();
                rangeAxisEdge = pp.getRangeAxisEdge();
            }

            if (domainAxis != null)
                cx = domainAxis.java2DToValue(p.getX(), dataArea, domainAxisEdge);
            if (rangeAxis != null)
                cy = rangeAxis.java2DToValue(p.getY(), dataArea, rangeAxisEdge);
        } else {
            throw new Exception("no xyplot found");
        }
        return new Point2D.Double(cx, cy);

    }

    /**
     * Subplot or main plot at point
     * 
     * @param chart
     * @param info
     * @param mouseX
     * @param mouseY
     * @return
     */
    public static XYPlot findXYSubplot(JFreeChart chart, ChartRenderingInfo info, double mouseX, double mouseY) {
        int subplot = info.getPlotInfo().getSubplotIndex(new Point2D.Double(mouseX, mouseY));
        XYPlot plot = null;
        if (subplot != -1) {
            if (chart.getPlot() instanceof CombinedDomainXYPlot)
                plot = (XYPlot) ((CombinedDomainXYPlot) chart.getPlot()).getSubplots().get(subplot);
            else if (chart.getPlot() instanceof CombinedRangeXYPlot)
                plot = (XYPlot) ((CombinedRangeXYPlot) chart.getPlot()).getSubplots().get(subplot);
        }

        if (plot == null && chart.getPlot() instanceof XYPlot)
            plot = (XYPlot) chart.getPlot();

        return plot;
    }

    /**
     * Find chartentities like JFreeChartEntity, AxisEntity, PlotEntity, TitleEntity, XY...
     * 
     * @param chart
     * @param mx mouse coordinates
     * @param my mouse coordinates
     * @return
     */
    public static ChartEntity findChartEntity(ChartPanel chart, double mx, double my) {
        // TODO check if insets were needed
        // coordinates to find chart entities
        Insets insets = chart.getInsets();
        int x = (int) ((mx - insets.left) / chart.getScaleX());
        int y = (int) ((my - insets.top) / chart.getScaleY());

        ChartRenderingInfo info = chart.getChartRenderingInfo();
        ChartEntity entity = null;
        if (info != null) {
            EntityCollection entities = info.getEntityCollection();
            if (entities != null) {
                entity = entities.getEntity(x, y);
            }
        }
        return entity;
    }

    /**
     * Translates screen (pixel) values to plot values
     * 
     * @param myChart
     * @return width in data space for x and y
     * @throws Exception
     */
    public static Point2D screenValueToPlotValue(ChartPanel myChart, int val) throws Exception {
        Point2D p = mouseXYToPlotXY(myChart, 0, 0);
        Point2D p2 = mouseXYToPlotXY(myChart, val, val);
        // inverted y
        return new Point2D.Double(p2.getX() - p.getX(), p.getY() - p2.getY());
    }

    /**
     * Data width to pixel width on screen
     * 
     * @param myChart
     * @param dataWidth width of data
     * @param axis for width calculation
     * @return
     */
    public static double calcWidthOnScreen(ChartPanel myChart, double dataWidth, ValueAxis axis,
            RectangleEdge axisEdge) {
        XYPlot plot = (XYPlot) myChart.getChart().getPlot();
        ChartRenderingInfo info = myChart.getChartRenderingInfo();
        Rectangle2D dataArea = info.getPlotInfo().getDataArea();

        double width2D = axis.lengthToJava2D(dataWidth, dataArea, axisEdge);

        return width2D;
    }

    /**
     * Calculates the size of a chart for a given fixed plot width Domain and Range axes need to share
     * the same unit (e.g. mm)
     * 
     * @param chart
     * @param width
     * @return
     */
    public static Dimension calcSizeForPlotWidth(ChartPanel myChart, double plotWidth) {
        return calcSizeForPlotWidth(myChart, plotWidth, 4);
    }

    /**
     * Calculates the size of a chart for a given fixed plot width Domain and Range axes need to share
     * the same unit (e.g. mm)
     * 
     * @param chart
     * @param plotWidth
     * @return
     */
    public static Dimension calcSizeForPlotWidth(ChartPanel myChart, double plotWidth, int iterations) {
        // ranges
        XYPlot plot = (XYPlot) myChart.getChart().getPlot();
        ValueAxis domainAxis = plot.getDomainAxis();
        Range x = domainAxis.getRange();
        ValueAxis rangeAxis = plot.getRangeAxis();
        Range y = rangeAxis.getRange();

        // plot height is fixed
        double plotHeight = plotWidth / x.getLength() * y.getLength();
        return calcSizeForPlotSize(myChart, plotWidth, plotHeight, iterations);
    }

    /**
     * Calculates the size of a chart for a given fixed plot width and height
     * 
     * @param chart
     * @param plotWidth
     * @return
     */
    public static Dimension calcSizeForPlotSize(ChartPanel myChart, double plotWidth, double plotHeight) {
        return calcSizeForPlotSize(myChart, plotWidth, plotHeight, 4);
    }

    /**
     * Calculates the size of a chart for a given fixed plot width and height
     * 
     * @param chart
     * @param plotWidth
     * @return
     */
    public static Dimension calcSizeForPlotSize(ChartPanel myChart, double plotWidth, double plotHeight,
            int iterations) {
        makeChartResizable(myChart);

        // estimate plotwidth / height
        double estimatedChartWidth = plotWidth + 200;
        double estimatedChartHeight = plotHeight + 200;

        double lastW = estimatedChartWidth;
        double lastH = estimatedChartHeight;

        // paint and get closer
        try {
            for (int i = 0; i < iterations; i++) {
                // paint on ghost panel with estimated height (if copy panel==true)
                myChart.setSize((int) estimatedChartWidth, (int) estimatedChartHeight);
                myChart.paintImmediately(myChart.getBounds());

                // rendering info
                ChartRenderingInfo info = myChart.getChartRenderingInfo();
                Rectangle2D dataArea = info.getPlotInfo().getDataArea();
                Rectangle2D chartArea = info.getChartArea();

                // // calc title space: will be added later to the right plot size
                // double titleWidth = chartArea.getWidth()-dataArea.getWidth();
                // double titleHeight = chartArea.getHeight()-dataArea.getHeight();

                // calc width and height
                estimatedChartWidth = estimatedChartWidth - dataArea.getWidth() + plotWidth;
                estimatedChartHeight = estimatedChartHeight - dataArea.getHeight() + plotHeight;

                if ((int) lastW == (int) estimatedChartWidth && (int) lastH == (int) estimatedChartHeight)
                    break;
                else {
                    lastW = estimatedChartWidth;
                    lastH = estimatedChartHeight;
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return new Dimension((int) estimatedChartWidth, (int) estimatedChartHeight);
    }

    /**
     * calls this method twice (2 iterations) with an estimated chartHeight of 3*chartWidth Domain and
     * Range axes need to share the same unit (e.g. mm)
     * 
     * @param myChart
     * @param copyToNewPanel
     * @param dataWidth width of data
     * @param axis for width calculation
     * @return
     */
    public static double calcHeightToWidth(ChartPanel myChart, double chartWidth, boolean copyToNewPanel) {
        return calcHeightToWidth(myChart, chartWidth, chartWidth * 3, 4, copyToNewPanel);
    }

    /**
     * calculates the correct height with multiple iterations Domain and Range axes need to share the
     * same unit (e.g. mm)
     * 
     * @param myChart
     * @param copyToNewPanel
     * @param dataWidth width of data
     * @param axis for width calculation
     * @return
     */
    public static double calcHeightToWidth(ChartPanel myChart, double chartWidth, double estimatedHeight,
            int iterations, boolean copyToNewPanel) {
        // if(myChart.getChartRenderingInfo()==null ||
        // myChart.getChartRenderingInfo().getChartArea()==null ||
        // myChart.getChartRenderingInfo().getChartArea().getWidth()==0)
        // result
        double height = estimatedHeight;
        double lastH = height;

        makeChartResizable(myChart);

        // paint on a ghost panel
        JPanel parent = (JPanel) myChart.getParent();
        JPanel p = copyToNewPanel ? new JPanel() : parent;
        if (copyToNewPanel)
            p.add(myChart, BorderLayout.CENTER);
        try {
            for (int i = 0; i < iterations; i++) {
                // paint on ghost panel with estimated height (if copy panel==true)
                myChart.setSize((int) chartWidth, (int) estimatedHeight);
                myChart.paintImmediately(myChart.getBounds());

                XYPlot plot = (XYPlot) myChart.getChart().getPlot();
                ChartRenderingInfo info = myChart.getChartRenderingInfo();
                Rectangle2D dataArea = info.getPlotInfo().getDataArea();
                Rectangle2D chartArea = info.getChartArea();

                // calc title space: will be added later to the right plot size
                double titleWidth = chartArea.getWidth() - dataArea.getWidth();
                double titleHeight = chartArea.getHeight() - dataArea.getHeight();

                // calc right plot size with axis dim.
                // real plot width is given by factor;
                double realPW = chartWidth - titleWidth;

                // ranges
                ValueAxis domainAxis = plot.getDomainAxis();
                org.jfree.data.Range x = domainAxis.getRange();
                ValueAxis rangeAxis = plot.getRangeAxis();
                org.jfree.data.Range y = rangeAxis.getRange();

                // real plot height can be calculated by
                double realPH = realPW / x.getLength() * y.getLength();

                // the real height
                height = realPH + titleHeight;

                // for next iteration
                estimatedHeight = height;
                if ((int) lastH == (int) height)
                    break;
                else
                    lastH = height;
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        if (copyToNewPanel) {
            // reset to frame
            p.removeAll();
            parent.add(myChart);
        }

        return height;
    }

    /**
     * Removes draw size restrictions
     * 
     * @param myChart
     */
    public static void makeChartResizable(ChartPanel myChart) {
        myChart.setMaximumDrawWidth(1000000000);
        myChart.setMinimumDrawWidth(0);
        myChart.setMaximumDrawHeight(1000000000);
        myChart.setMinimumDrawHeight(0);
    }

    /**
     * 
     * Domain and Range axes need to share the same unit (e.g. mm)
     * 
     * @param myChart
     * @return
     */
    public static double calcWidthToHeight(ChartPanel myChart, double chartHeight) {
        makeChartResizable(myChart);
        // paint on a ghost panel
        JPanel parent = (JPanel) myChart.getParent();
        JPanel p = new JPanel();
        p.removeAll();
        p.add(myChart, BorderLayout.CENTER);
        p.setBounds(myChart.getBounds());
        myChart.paintImmediately(myChart.getBounds());
        p.removeAll();
        parent.add(myChart);

        XYPlot plot = (XYPlot) myChart.getChart().getPlot();
        ChartRenderingInfo info = myChart.getChartRenderingInfo();
        Rectangle2D dataArea = info.getPlotInfo().getDataArea();
        Rectangle2D chartArea = info.getChartArea();

        // calc title space: will be added later to the right plot size
        double titleWidth = chartArea.getWidth() - dataArea.getWidth();
        double titleHeight = chartArea.getHeight() - dataArea.getHeight();

        // calc right plot size with axis dim.
        // real plot width is given by factor;
        double realPH = chartHeight - titleHeight;

        // ranges
        ValueAxis domainAxis = plot.getDomainAxis();
        org.jfree.data.Range x = domainAxis.getRange();
        ValueAxis rangeAxis = plot.getRangeAxis();
        org.jfree.data.Range y = rangeAxis.getRange();

        // real plot height can be calculated by
        double realPW = realPH / y.getLength() * x.getLength();

        double width = realPW + titleWidth;

        return width;
    }

    /**
     * Returns dimensions for limiting factor width or height
     * 
     * @param myChart
     * @return
     */
    public static Dimension calcMaxSize(ChartPanel myChart, double chartWidth, double chartHeight) {
        makeChartResizable(myChart);
        // paint on a ghost panel
        JPanel parent = (JPanel) myChart.getParent();
        JPanel p = new JPanel();
        p.removeAll();
        p.add(myChart, BorderLayout.CENTER);
        p.setBounds(myChart.getBounds());
        myChart.paintImmediately(myChart.getBounds());
        p.removeAll();
        parent.add(myChart);

        XYPlot plot = (XYPlot) myChart.getChart().getPlot();
        ChartRenderingInfo info = myChart.getChartRenderingInfo();
        Rectangle2D dataArea = info.getPlotInfo().getDataArea();
        Rectangle2D chartArea = info.getChartArea();

        // calc title space: will be added later to the right plot size
        double titleWidth = chartArea.getWidth() - dataArea.getWidth();
        double titleHeight = chartArea.getHeight() - dataArea.getHeight();

        // calculatig width for max height

        // calc right plot size with axis dim.
        // real plot width is given by factor;
        double realPH = chartHeight - titleHeight;

        // ranges
        ValueAxis domainAxis = plot.getDomainAxis();
        org.jfree.data.Range x = domainAxis.getRange();
        ValueAxis rangeAxis = plot.getRangeAxis();
        org.jfree.data.Range y = rangeAxis.getRange();

        // real plot height can be calculated by
        double realPW = realPH / y.getLength() * x.getLength();

        double width = realPW + titleWidth;
        // if width is higher than given chartWidth then calc height for chartWidth
        if (width > chartWidth) {
            // calc right plot size with axis dim.
            // real plot width is given by factor;
            realPW = chartWidth - titleWidth;

            // real plot height can be calculated by
            realPH = realPW / x.getLength() * y.getLength();

            double height = realPH + titleHeight;
            // Return size
            return new Dimension((int) chartWidth, (int) height);
        } else {
            // Return size
            return new Dimension((int) width, (int) chartHeight);
        }
    }

    /**
     * 
     * @param myChart
     * @return Range the domainAxis zoom (X-axis)
     */
    public static Range getZoomDomainAxis(ChartPanel myChart) {
        XYPlot plot = (XYPlot) myChart.getChart().getPlot();
        ValueAxis domainAxis = plot.getDomainAxis();

        return new Range(domainAxis.getLowerBound(), domainAxis.getUpperBound());
    }

    /**
     * Zoom into a chart panel
     * 
     * @param myChart
     * @param zoom
     * @param autoRangeY if true the range (Y) axis auto bounds will be restored
     */
    public static void setZoomDomainAxis(ChartPanel myChart, Range zoom, boolean autoRangeY) {
        XYPlot plot = (XYPlot) myChart.getChart().getPlot();
        ValueAxis domainAxis = plot.getDomainAxis();
        setZoomAxis(domainAxis, keepRangeWithinAutoBounds(domainAxis, zoom));

        if (autoRangeY) {
            autoRangeAxis(myChart);
        }
    }

    /**
     * Zoom into a chart panel
     * 
     * @param myChart
     * @param zoom
     * @param autoRangeY if true the range (Y) axis auto bounds will be restored
     */
    public static void setZoomAxis(ValueAxis axis, Range zoom) {
        axis.setRange(zoom);
    }

    /**
     * Auto range the range axis
     * 
     * @param myChart
     * @param zoom
     * @param autoRangeY if true the range (Y) axis auto bounds will be restored
     */
    public static void autoRangeAxis(ChartPanel myChart) {
        XYPlot plot = (XYPlot) myChart.getChart().getPlot();
        NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
        // trick. Otherwise auto range will fail sometimes
        rangeAxis.setRange(rangeAxis.getRange());
        rangeAxis.setAutoRangeIncludesZero(true);
        myChart.restoreAutoRangeBounds();
    }

    /**
     * Auto range the range axis
     * 
     * @param myChart
     * @param zoom
     * @param autoRangeY if true the range (Y) axis auto bounds will be restored
     */
    public static void autoDomainAxis(ChartPanel myChart) {
        XYPlot plot = (XYPlot) myChart.getChart().getPlot();
        NumberAxis axis = (NumberAxis) plot.getDomainAxis();
        // trick. Otherwise auto range will fail sometimes
        axis.setRange(axis.getRange());
        axis.setAutoRangeIncludesZero(false);
        myChart.restoreAutoDomainBounds();
    }

    /**
     * Auto range the axis
     * 
     * @param axis
     */
    public static void autoAxis(ValueAxis axis) {
        axis.resizeRange(0);
    }

    public static void autoAxes(ChartPanel cp) {
        autoRangeAxis(cp);
        autoDomainAxis(cp);
    }

    /**
     * Move a chart by a percentage x-offset if xoffset is <0 the shift will be negativ (xoffset>0
     * results in a positive shift)
     * 
     * @param myChart
     * @param xoffset in percent
     * @param autoRangeY if true the range (Y) axis auto bounds will be restored
     */
    public static void offsetDomainAxis(ChartPanel myChart, double xoffset, boolean autoRangeY) {
        XYPlot plot = (XYPlot) myChart.getChart().getPlot();
        ValueAxis domainAxis = plot.getDomainAxis();
        // apply offset on x
        double distance = (domainAxis.getUpperBound() - domainAxis.getLowerBound()) * xoffset;

        Range range = new Range(domainAxis.getLowerBound() + distance, domainAxis.getUpperBound() + distance);
        setZoomDomainAxis(myChart, keepRangeWithinAutoBounds(domainAxis, range), autoRangeY);
    }

    /**
     * Apply an absolute offset to domain (x) axis and move it
     * 
     * @param myChart
     * @param xoffset
     * @param autoRangeY
     */
    public static void offsetDomainAxisAbsolute(ChartPanel myChart, double xoffset, boolean autoRangeY) {
        XYPlot plot = (XYPlot) myChart.getChart().getPlot();
        ValueAxis domainAxis = plot.getDomainAxis();
        // apply offset on x

        Range range = new Range(domainAxis.getLowerBound() + xoffset, domainAxis.getUpperBound() + xoffset);
        setZoomDomainAxis(myChart, keepRangeWithinAutoBounds(domainAxis, range), autoRangeY);
    }

    /**
     * Apply an absolute offset to an axis and move it
     * 
     * @param myChart
     * @param offset
     */
    public static void offsetAxisAbsolute(ValueAxis axis, double offset) {
        Range range = new Range(axis.getLowerBound() + offset, axis.getUpperBound() + offset);
        setZoomAxis(axis, keepRangeWithinAutoBounds(axis, range));
    }

    /**
     * Apply an relative offset to an axis and move it. LowerBound and UpperBound are defined by
     * {@link ValueAxis#getDefaultAutoRange()}
     * 
     * @param myChart
     * @param offset percentage
     */
    public static void offsetAxis(ValueAxis axis, double offset) {
        double distance = (axis.getUpperBound() - axis.getLowerBound()) * offset;
        Range range = new Range(axis.getLowerBound() + distance, axis.getUpperBound() + distance);
        setZoomAxis(axis, keepRangeWithinAutoBounds(axis, range));
    }

    public static Range keepRangeWithinAutoBounds(ValueAxis axis, Range range) {
        // keep within auto range bounds
        // Range auto = axis.getDefaultAutoRange();
        // if(range.getLowerBound()<auto.getLowerBound()){
        // double negative = range.getLowerBound()-auto.getLowerBound();
        // range = new Range(auto.getLowerBound(), range.getUpperBound()-negative);
        // }
        // if(range.getUpperBound()>auto.getUpperBound()) {
        // double positive = range.getUpperBound()-auto.getUpperBound();
        // range = new Range(range.getLowerBound()-positive, auto.getUpperBound());
        // }
        return range;
    }

    /**
     * Zoom in (negative yzoom) or zoom out of range axis.
     * 
     * @param myChart
     * @param yzoom percentage zoom factor
     * @param holdLowerBound if true only the upper bound will be zoomed
     */
    public static void zoomRangeAxis(JFreeChart myChart, double yzoom, boolean holdLowerBound) {
        zoomAxis(myChart.getXYPlot().getRangeAxis(), yzoom, holdLowerBound);
    }

    /**
     * Zoom in (negative zoom) or zoom out of axis.
     * 
     * @param myChart
     * @param zoom percentage zoom factor
     * @param holdLowerBound if true only the upper bound will be zoomed
     */
    public static void zoomAxis(ValueAxis axis, double zoom, boolean holdLowerBound) {
        double lower = axis.getLowerBound();
        double upper = axis.getUpperBound();
        double dist = upper - lower;

        if (holdLowerBound) {
            if (zoom == 0)
                return;
            upper += dist * zoom;
        } else {
            lower -= dist * zoom / 2;
            upper += dist * zoom / 2;
        }

        if (lower < upper) {
            logger.info("Set zoom:" + lower + ", " + upper + " (keep lower:" + holdLowerBound + ")");
            Range range = new Range(lower, upper);
            setZoomAxis(axis, keepRangeWithinAutoBounds(axis, range));
        }
    }

    /**
     * Zoom in (negative zoom) or zoom out of axis.
     * 
     * @param myChart
     * @param zoom percentage zoom factor
     * @param start point on this range (first click/pressed event), used as center
     */
    public static void zoomAxis(ValueAxis axis, double zoom, double start) {
        double lower = axis.getLowerBound();
        double upper = axis.getUpperBound();
        double dist = upper - lower;
        double f = (start - lower) / dist;

        lower -= dist * zoom * f;
        upper += dist * zoom * (1 - f);

        if (lower < upper) {
            Range range = new Range(lower, upper);
            setZoomAxis(axis, keepRangeWithinAutoBounds(axis, range));
        }
    }

    /**
     * 
     * @param chartPanel
     * @return
     */
    public static boolean isMouseZoomable(ChartPanel chartPanel) {
        return chartPanel instanceof EChartPanel ? ((EChartPanel) chartPanel).isMouseZoomable()
                : chartPanel.isRangeZoomable() && chartPanel.isDomainZoomable();
    }

}