// This file is part of PleoCommand: // Interactively control Pleo with psychobiological parameters // // Copyright (C) 2010 Oliver Hoffmann - Hoffmann_Oliver@gmx.de // // This program 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. // // This program 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 this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Boston, USA. package pleocmd.itfc.gui.dgr; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.Point2D; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; import pleocmd.itfc.gui.dgr.DiagramDataSet.DiagramType; public final class DiagramAxis { public enum AxisScale { LinearScale, LogarithmicScale } private final Diagram diagram; private String axisName; private String unitName = ""; private AxisScale scale = AxisScale.LinearScale; private double min = -Double.MAX_VALUE; private double max = Double.MAX_VALUE; private boolean reversed; private int subsPerUnit = 5; private double offset; private double cachedMinUnit; private double cachedMaxUnit; private double cachedMinVisUnit; private double cachedMaxVisUnit; private int cachedUnitsPerGrid; private double cachedPixelPerUnit; private double cachedPixelPerGrid; private double cachedPixelPerSubGrid; protected DiagramAxis(final Diagram diagram, final String axisName) { this.diagram = diagram; this.axisName = axisName; } public String getAxisName() { return axisName; } public void setAxisName(final String axisName) { synchronized (diagram) { this.axisName = axisName; diagram.repaint(); } } public String getUnitName() { return unitName; } public void setUnitName(final String unitName) { synchronized (diagram) { this.unitName = unitName; diagram.repaint(); } } public AxisScale getScale() { return scale; } public void setScale(final AxisScale scale) { synchronized (diagram) { this.scale = scale; diagram.repaint(); } } public double getMin() { return min; } public void setMin(final double min) { synchronized (diagram) { this.min = min; diagram.repaint(); } } public double getMax() { return max; } public void setMax(final double max) { synchronized (diagram) { this.max = max; diagram.repaint(); } } public boolean isReversed() { return reversed; } public void setReversed(final boolean reversed) { synchronized (diagram) { this.reversed = reversed; diagram.repaint(); } } public int getSubsPerUnit() { return subsPerUnit; } public void setSubsPerUnit(final int subsPerUnit) { synchronized (diagram) { this.subsPerUnit = subsPerUnit; diagram.repaint(); } } public double getOffset() { return offset; } public void setOffset(final double offset) { synchronized (diagram) { this.offset = offset; diagram.repaint(); } } void updateCache(final int availPixels) { cachedMinUnit = min; cachedMaxUnit = max; final DiagramAxis xAxis = diagram.getXAxis(); final boolean isXAxis = xAxis == this; if (max >= Double.MAX_VALUE - Double.MIN_NORMAL || min <= Double.MIN_NORMAL - Double.MAX_VALUE) { double low = Double.MAX_VALUE; double high = -Double.MAX_VALUE; for (final DiagramDataSet ds : diagram.getDataSets()) { if (!isXAxis && ds.getType() == DiagramType.IntersectionDiagram) continue; if (!ds.isValid()) continue; for (final Point2D.Double pt : ds.getPoints()) { final double unitX = pt.x / ds.getValuePerUnitX(); if (isXAxis) { // all values count low = Math.min(low, unitX); high = Math.max(high, unitX); } else { // only visible values count final double unitY = pt.y / ds.getValuePerUnitY(); if (unitX >= xAxis.getCachedMinVisUnit() && unitX <= xAxis.getCachedMaxVisUnit()) { low = Math.min(low, unitY); high = Math.max(high, unitY); } } } } if (low == Double.MAX_VALUE) low = 0; if (high == -Double.MAX_VALUE) high = 1; if (min <= Double.MIN_NORMAL - Double.MAX_VALUE) cachedMinUnit = low; if (max >= Double.MAX_VALUE - Double.MIN_NORMAL) cachedMaxUnit = high; } cachedPixelPerUnit = availPixels / (cachedMaxUnit - cachedMinUnit) * diagram.getZoom(); final double visibleUnits = availPixels / cachedPixelPerUnit; cachedMinVisUnit = cachedMinUnit + offset; cachedMaxVisUnit = cachedMinVisUnit + visibleUnits; cachedUnitsPerGrid = Math .max(1, (int) (Diagram.MIN_GRID_DELTA * Math.max(subsPerUnit, 1) / cachedPixelPerUnit)); cachedPixelPerGrid = cachedPixelPerUnit * cachedUnitsPerGrid; cachedPixelPerSubGrid = subsPerUnit > 1 ? cachedPixelPerGrid / subsPerUnit : 0; } double unitToPixel(final double unit) { return (unit - cachedMinVisUnit) * cachedPixelPerUnit; } double getCachedMinUnit() { return cachedMinUnit; } double getCachedMaxUnit() { return cachedMaxUnit; } double getCachedMinVisUnit() { return cachedMinVisUnit; } double getCachedMaxVisUnit() { return cachedMaxVisUnit; } int getCachedUnitsPerGrid() { return cachedUnitsPerGrid; } double getCachedPixelPerUnit() { return cachedPixelPerUnit; } double getCachedPixelPerGrid() { return cachedPixelPerGrid; } double getCachedPixelPerSubGrid() { return cachedPixelPerSubGrid; } protected void createMenu(final JPopupMenu parent) { final JMenu menu = new JMenu(getAxisName() + "-Axis"); parent.add(menu); JMenuItem item = new JMenuItem("Set Minimum"); menu.add(item); item.addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { final String newMin = JOptionPane.showInputDialog( String.format("New Minimum Of %s-Axis", getAxisName()), getCachedMinUnit()); if (newMin != null) setMin(Double.valueOf(newMin)); } }); item = new JMenuItem("Set Minimum To Automatic"); menu.add(item); item.addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { setMin(-Double.MAX_VALUE); } }); item = new JMenuItem("Set Maximum"); menu.add(item); item.addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { final String newMax = JOptionPane.showInputDialog( String.format("New Maximum Of %s-Axis", getAxisName()), getCachedMaxUnit()); if (newMax != null) setMax(Double.valueOf(newMax)); } }); item = new JMenuItem("Set Maximum To Automatic"); menu.add(item); item.addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { setMax(Double.MAX_VALUE); } }); item = new JMenuItem("Set Linear Scale"); menu.add(item); item.addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { setScale(AxisScale.LinearScale); } }); item = new JMenuItem("Set Logarithmic Scale"); menu.add(item); item.addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { setScale(AxisScale.LogarithmicScale); } }); } }