package jas.hist;
import jas.plot.DataArea;
import jas.plot.DoubleAxis;
import jas.plot.Legend;
import jas.plot.LegendEntry;
import jas.util.ColorMenu;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Enumeration;
import java.util.Vector;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenu;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.SwingUtilities;
import javax.swing.colorchooser.ColorSelectionModel;
import javax.swing.colorchooser.DefaultColorSelectionModel;
abstract class ScatterDataManager extends TwoDDataManager
{
ScatterDataManager(JASHist plot, final DataArea da, final Legend l, StatisticsBlock stats)
{
super(plot, da, l, stats);
m_defaultNumberOfBins = 40; // defaultNumberOfBins;
// Configure the Axes
xAxis = new DoubleAxis();
yAxis = new DoubleAxis();
xm.setDataManager(this,true, xAxis);
ym[0].setDataManager(this,true,yAxis);
//new DoubleAxisListener(xm);
xm.setBins(m_defaultNumberOfBins);
ym[0].setBins(m_defaultNumberOfBins);
}
void styleUpdate(final JASHistData source)
{
styleChanged = true;
JASHist2DHistogramStyle style = (JASHist2DHistogramStyle) source.getStyle();
if (oldStyle != style.getHistStyle())
{
boolean lego = style.getHistStyle() == style.STYLE_3DLEGOPLOT;
boolean surface = style.getHistStyle() == style.STYLE_3DSURFACEPLOT;
try
{
if (lego)
{
Class klass = Class.forName("gov.fnal.plot3d.jas.SpecialLego");
SpecialComponent special = (SpecialComponent) klass.newInstance();
special.setData(source.getDataSource());
da.setSpecialComponent(special.getDisplayComponent());
}
else if (surface)
{
Class klass = Class.forName("gov.fnal.plot3d.jas.SpecialSurface");
SpecialComponent special = (SpecialComponent) klass.newInstance();
special.setData(source.getDataSource());
da.setSpecialComponent(special.getDisplayComponent());
}
else da.setSpecialComponent(null);
}
catch (Throwable t)
{
t.printStackTrace();
da.setSpecialComponent(null);
}
oldStyle = style.getHistStyle();
}
SwingUtilities.invokeLater(this);
}
final void init()
{
computeXAxisRange();
XAxisUpdated();
computeYAxisRange();
YAxisUpdated();
computeZAxisRange();
isInit = true;
}
void axisChanged(final JASHistData source)
{
final int index = source.getYAxis();
if (ym[index] == null)
{
createYAxis(index);
((DoubleAxis) ym[index].getType()).setUseSuggestedRange(false);
}
else
ym[index].setAttentionNeeded();
SwingUtilities.invokeLater(this);
}
void update(final HistogramUpdate update, final JASHistData ds)
{
JASHist2DHistogramData source = (JASHist2DHistogramData) ds;
final int index = source.getYAxis();
if (update.isRangeUpdate())
{
if (update.axisIsSet(update.HORIZONTAL_AXIS))
{
xm.setAttentionNeeded();
}
if (update.axisIsSet(update.VERTICAL_AXIS))
{
ym[index].setAttentionNeeded();
}
}
else if (update.isReset())
{
//TODO: Fix this
//source.resetSent = true;
xm.setAttentionNeeded();
ym[index].setAttentionNeeded();
}
else if (update.isDataUpdate())
{
//TODO: Fix this
//source.dataChanged = true;
xm.setAttentionNeeded();
ym[index].setAttentionNeeded();
}
if (update.isFinalUpdate() || update.isReset())
SwingUtilities.invokeLater(this);
else
timer.start();
}
protected void doUpdate()
{
if (isInit)
{
boolean axisChanged = false;
if (xm.needsAttention())
{
computeXAxisRange();
XAxisUpdated();
axisChanged = true;
}
int index = 0; // Bug, what about the other axis
if (ym[index].needsAttention())
{
computeYAxisRange();
YAxisUpdated();
axisChanged = true;
}
computeZAxisRange();
final Enumeration e = data.elements();
while (e.hasMoreElements())
{
final Object o = e.nextElement();
if (o instanceof JASHist2DScatterData)
{
JASHist2DScatterData scatData = (JASHist2DScatterData) o;
//data.clearChanges();
final boolean needNewEnumeration = axisChanged || scatData.resetSent || scatData.onNewAxis;
if (needNewEnumeration || styleChanged)
{
scatData.restartImage(needNewEnumeration);
scatData.onNewAxis = false;
scatData.resetSent = false;
}
else if (scatData.dataChanged)
{
scatData.continueImage();
scatData.dataChanged = false;
}
}
}
da.validate(); // When do we need to do this?
da.repaint();
styleChanged = false;
}
}
JASHistData add(final DataSource ds)
{
da.setSpecialComponent(null);
oldStyle = -1;
if (ds instanceof HasScatterPlotData && ((HasScatterPlotData) ds).hasScatterPlotData())
{
HasScatterPlotData source = (HasScatterPlotData) ds;
final JASHist2DScatterData d = new JASHist2DScatterData(this, source);
data.addElement(d);
return d;
}
else if (ds instanceof Rebinnable2DHistogramData)
{
Rebinnable2DHistogramData source = (Rebinnable2DHistogramData) ds;
final JASHist2DHistogramData d = new JASHist2DHistogramData(this, source);
data.addElement(d);
return d;
}
else if (ds instanceof ScatterPlotSource)
{
ScatterPlotSource source = (ScatterPlotSource) ds;
final JASHist2DScatterData d = new JASHistScatterPlotData(this, source);
data.addElement(d);
return d;
}
else throw new RuntimeException("Unknown subtype of DataSource added to ScatterDataManager");
}
void computeXAxisRange()
{
if (!xm.needsAttention()) return;
xm.payingAttention(); // do first to avoid race conditions
if (data.isEmpty()) return;
if (!xm.getRangeAutomatic() || xm.isFixed())
{
xLow = xm.getMin();
xHigh = xm.getMax();
return;
}
int nShowing = 0;
xLow = 0;
xHigh = 0;
boolean hasRebinnables = false;
for (Enumeration e = data.elements(); e.hasMoreElements();)
{
JASHist2DHistogramData dw = (JASHist2DHistogramData) e.nextElement();
if (!dw.isShowing()) continue;
if (nShowing++ == 0)
{
xLow = dw.getXMin();
xHigh = dw.getXMax();
}
else
{
xLow = Math.min(xLow,dw.getXMin());
xHigh = Math.max(xHigh,dw.getXMax());
}
if (dw.isRebinnable()) hasRebinnables = true;
}
if (nShowing == 0) return;
xm.setBinned(hasRebinnables);
if (!xm.getAllowSuppressedZero())
{
if (xLow > 0) xLow = 0;
if (xHigh < 0) xHigh = 0;
}
if (xHigh <= xLow) xHigh = xLow + 1;
calcMinMaxXBins(xLow,xHigh);
}
protected void calcMinMaxXBins(double x1, double x2)
{
double oldXMin = xAxis.getPlotMin();
double oldXMax = xAxis.getPlotMax();
if (x1 != oldXMin || x2 != oldXMax)
{
xAxis.setMin(x1);
xAxis.setMax(x2);
xm.invalidate();
}
}
void computeYAxisRange()
{
int index = 0; // Bug, what about the other axis
if (!ym[index].needsAttention()) return;
ym[index].payingAttention(); // do first to avoid race conditions
if (data.isEmpty()) return;
if (!ym[index].getRangeAutomatic() || ym[index].isFixed())
{
yLow = ym[index].getMin();
yHigh = ym[index].getMax();
return;
}
int nShowing = 0;
yLow = 0;
yHigh = 0;
boolean hasRebinnables = false;
for (Enumeration e = data.elements(); e.hasMoreElements();)
{
JASHist2DHistogramData dw = (JASHist2DHistogramData) e.nextElement();
if (!dw.isShowing()) continue;
if (nShowing++ == 0)
{
yLow = dw.getYMin();
yHigh = dw.getYMax();
}
else
{
yLow = Math.min(yLow,dw.getYMin());
yHigh = Math.max(yHigh,dw.getYMax());
}
if (dw.isRebinnable()) hasRebinnables = true;
}
if (nShowing == 0) return;
ym[index].setBinned(hasRebinnables);
if (!ym[index].getAllowSuppressedZero())
{
if (yLow > 0) yLow = 0;
if (yHigh < 0) yHigh = 0;
}
if (yHigh <= yLow) yHigh = yLow + 1;
calcMinMaxYBins(yLow,yHigh);
}
protected void calcMinMaxYBins(double y1, double y2)
{
double oldYMin = yAxis.getPlotMin();
double oldYMax = yAxis.getPlotMax();
if (y1 != oldYMin || y2 != oldYMax)
{
yAxis.setMin(y1);
yAxis.setMax(y2);
ym[0].invalidate();
}
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
{
in.defaultReadObject();
data = new Vector();
}
final void requestShow(JASHistData data)
{
da.add(data.getOverlay());
nVisible++;
if (legend != null)
{
LegendEntry le = data.getLegendEntry();
if (le != null)
{
legend.add(le);
nVisibleLegend++;
showLegend();
}
}
if (stats != null)
{
stats.add(data);
}
if (isInit)
{
xm.setAttentionNeeded();
computeXAxisRange();
XAxisUpdated();
ym[0].setAttentionNeeded();
computeYAxisRange();
YAxisUpdated();
computeZAxisRange();
da.revalidate();
da.repaint();
}
//restartImages();
}
void requestHide(JASHistData data)
{
da.remove(data.getOverlay());
nVisible--;
if (legend != null)
{
LegendEntry le = data.getLegendEntry();
if (le != null)
{
legend.remove(le);
nVisibleLegend--;
showLegend();
}
}
if (stats != null)
{
stats.remove(data);
}
if (isInit)
{
xm.setAttentionNeeded();
computeXAxisRange();
XAxisUpdated();
ym[0].setAttentionNeeded();
computeYAxisRange();
YAxisUpdated();
computeZAxisRange();
da.revalidate();
da.repaint();
}
}
void XAxisUpdated()
{
for (Enumeration e = data.elements(); e.hasMoreElements();)
{
JASHist2DHistogramData dw = (JASHist2DHistogramData) e.nextElement();
if (dw.isShowing()) dw.setXRange(xm.getBins(),xLow,xHigh);
}
}
void YAxisUpdated()
{
for (Enumeration e = data.elements(); e.hasMoreElements();)
{
JASHist2DHistogramData dw = (JASHist2DHistogramData) e.nextElement();
if (dw.isShowing()) dw.setYRange(ym[0].getBins(),yLow,yHigh);
}
}
final private void computeZAxisRange()
{
for (Enumeration e = data.elements(); e.hasMoreElements();)
{
JASHist2DHistogramData dw = (JASHist2DHistogramData) e.nextElement();
if (dw.isShowing()) dw.calcZLimits();
}
}
private void restartImages()
{
final Enumeration e = data.elements();
while (e.hasMoreElements())
{
try
{
final JASHist2DScatterData data = (JASHist2DScatterData) e.nextElement();
if (data.isVisible)
data.restartImage(false);
}
catch (NullPointerException x)
{
// don't know where this comes from yet, but this works as is
}
}
}
private int oldStyle = -1;
private final int m_defaultNumberOfBins;
transient protected double xLow, xHigh; // todo: get rid of these
transient protected double yLow, yHigh;
private DoubleAxis xAxis;
private DoubleAxis yAxis;
transient private boolean styleChanged;
private SizeMenu m_sizeMenu;
private StyleMenu m_styleMenu;
private static boolean enabled3d;
static
{
try
{
// check that java 3d and the plot3d routines are both available
Class.forName("javax.media.j3d.Canvas3D");
Class.forName("gov.fnal.plot3d.jas.SpecialLego");
Class.forName("gov.fnal.plot3d.jas.SpecialSurface");
enabled3d = true;
if (JPopupMenu.getDefaultLightWeightPopupEnabled())
{
JPopupMenu.setDefaultLightWeightPopupEnabled(false);
}
}
catch (Throwable t)
{
enabled3d = false;
}
}
final void modifyPopupMenu(final JPopupMenu menu, final Component source)
{
if (menu.getComponentCount() > 0) menu.addSeparator();
boolean scat = false;
boolean allScat = true;
boolean allTwoD = true;
Enumeration enumer = getDataSources();
while (enumer.hasMoreElements())
{
JASHistData data = (JASHistData) enumer.nextElement();
if (data instanceof JASHist2DScatterData && ((JASHist2DScatterData) data).hasScatterPlotData())
{
scat = true;
JASHistScatterPlotStyle pstyle = (JASHistScatterPlotStyle) data.getStyle();
if (pstyle.getDisplayAsScatterPlot()) allTwoD = false;
else allScat = false;
}
}
if (scat)
{
JRadioButtonMenuItem b1 = new JRadioButtonMenuItem("Display As Scatter Plot");
JRadioButtonMenuItem b2 = new JRadioButtonMenuItem("Display As Binned Plot");
ButtonGroup bg = new ButtonGroup();
bg.add(b1);
bg.add(b2);
b1.setSelected(allScat);
b2.setSelected(allTwoD);
b1.addActionListener(new ScatterActionListener(true));
b2.addActionListener(new ScatterActionListener(false));
menu.add(b1);
menu.add(b2);
}
if (!scat || !allScat)
{
menu.add(addPerDataSourceMenu("Plot Style",new DataSourceMenuFactory()
{
public JMenu createMenu(String name, final JASHistData ds)
{
return new StyleMenu(name,ds);
}
}));
}
if (scat && !allTwoD)
{
if (m_sizeMenu == null) m_sizeMenu = new SizeMenu();
m_sizeMenu.init();
menu.add(m_sizeMenu);
menu.add(addPerDataSourceMenu("Point Color",new DataSourceMenuFactory()
{
public JMenu createMenu(String name, final JASHistData ds)
{
final JASHistScatterPlotStyle style = (JASHistScatterPlotStyle) ds.getStyle();
ColorSelectionModel cm = new DefaultColorSelectionModel()
{
public Color getSelectedColor()
{
return style.getDataPointColor();
}
public void setSelectedColor(Color c)
{
style.setDataPointColor(c);
}
};
return new ColorMenu(name,cm,true);
}
}));
}
super.modifyPopupMenu(menu,source);
}
final private class ScatterActionListener implements ActionListener
{
ScatterActionListener(boolean state)
{
this.state = state;
}
public void actionPerformed(ActionEvent e)
{
Enumeration enumer = getDataSources();
while (enumer.hasMoreElements())
{
JASHistData data = (JASHistData) enumer.nextElement();
if ((data instanceof JASHist2DScatterData))
{
JASHistScatterPlotStyle pstyle = (JASHistScatterPlotStyle) data.getStyle();
pstyle.setDisplayAsScatterPlot(state);
}
}
}
private boolean state;
}
final private class SizeMenu extends JMenu
{
public SizeMenu()
{
super("Point Size");
setMnemonic('S');
addButton("Huge",'H',20);
addButton("Large",'L',10);
addButton("Medium",'M',5);
addButton("Small",'S',3);
addButton("Tiny",'T',1);
}
private void addButton(String name, char mnemonic, final int size)
{
JRadioButtonMenuItem item = new SizeButton(name,size);
item.setMnemonic(mnemonic);
group.add(item);
this.add(item);
}
public void init()
{
int iSize = -1;
Enumeration enumer = getDataSources();
while (enumer.hasMoreElements())
{
JASHistData data = (JASHistData) enumer.nextElement();
if (!(data instanceof JASHist2DScatterData)) continue;
JASHistScatterPlotStyle style = (JASHistScatterPlotStyle) data.getStyle();
if (iSize == -1) iSize = style.getDataPointSize();
else if (iSize != style.getDataPointSize()) iSize = -2;
}
Enumeration e = group.getElements();
while (e.hasMoreElements())
{
SizeButton b = (SizeButton) e.nextElement();
b.setSize(iSize);
}
}
ButtonGroup group = new ButtonGroup();
}
final class SizeButton extends JRadioButtonMenuItem
{
SizeButton(String name, int size)
{
super(name);
this.size = size;
}
public void fireActionPerformed(ActionEvent e)
{
Enumeration enumer = getDataSources();
while (enumer.hasMoreElements())
{
JASHistData data = (JASHistData) enumer.nextElement();
JASHistScatterPlotStyle style = (JASHistScatterPlotStyle) data.getStyle();
style.setDataPointSize(size);
}
}
void setSize(int size)
{
setSelected(this.size == size);
}
private int size;
}
final private class StyleMenu extends JMenu
{
public StyleMenu(String name, JASHistData ds)
{
super(name);
this.data = ds;
JMenu map = new JMenu("Color Map");
map.setMnemonic('M');
StyleMenu.this.add(addButton("Box",'B',JASHist2DHistogramStyle.STYLE_BOX,-1));
StyleMenu.this.add(addButton("Ellipse",'E',JASHist2DHistogramStyle.STYLE_ELLIPSE,-1));
if (enabled3d)
{
StyleMenu.this.add(addButton("3D Lego Plot",'L',JASHist2DHistogramStyle.STYLE_3DLEGOPLOT,-1));
StyleMenu.this.add(addButton("3D Surface Plot",'S',JASHist2DHistogramStyle.STYLE_3DSURFACEPLOT,-1));
}
StyleMenu.this.add(map);
map.add(addButton("Warm",'W',JASHist2DHistogramStyle.STYLE_COLORMAP,JASHist2DHistogramStyle.COLORMAP_WARM));
map.add(addButton("Cool",'C',JASHist2DHistogramStyle.STYLE_COLORMAP,JASHist2DHistogramStyle.COLORMAP_COOL));
map.add(addButton("Thermal",'T',JASHist2DHistogramStyle.STYLE_COLORMAP,JASHist2DHistogramStyle.COLORMAP_THERMAL));
map.add(addButton("Rainbow",'B',JASHist2DHistogramStyle.STYLE_COLORMAP,JASHist2DHistogramStyle.COLORMAP_RAINBOW));
map.add(addButton("Gray Scale",'G',JASHist2DHistogramStyle.STYLE_COLORMAP,JASHist2DHistogramStyle.COLORMAP_GRAYSCALE));
final JCheckBoxMenuItem log = new JCheckBoxMenuItem("Logarithmic Z Axis");
log.setMnemonic('Z');
final JASHist2DHistogramStyle style = (JASHist2DHistogramStyle) data.getStyle();
log.setState(style.getLogZ());
log.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
style.setLogZ(log.getState());
}
});
StyleMenu.this.add(log);
init();
}
private JRadioButtonMenuItem addButton(String name, char mnemonic, final int iStyle, final int cStyle)
{
JRadioButtonMenuItem item = new StyleButton(name,iStyle,cStyle);
item.setMnemonic(mnemonic);
group.add(item);
return item;
}
private void init()
{
int iStyle = -1;
int cStyle = -1;
JASHist2DHistogramStyle style = (JASHist2DHistogramStyle) data.getStyle();
iStyle = style.getHistStyle();
cStyle = style.getColorMapScheme();
Enumeration e = group.getElements();
while (e.hasMoreElements())
{
StyleButton b = (StyleButton) e.nextElement();
b.setStyle(iStyle,cStyle);
}
}
ButtonGroup group = new ButtonGroup();
JASHistData data;
final class StyleButton extends JRadioButtonMenuItem
{
StyleButton(String name, int iStyle, int cStyle)
{
super(name);
this.iStyle = iStyle;
this.cStyle = cStyle;
}
public void fireActionPerformed(ActionEvent e)
{
JASHist2DHistogramStyle style = (JASHist2DHistogramStyle) data.getStyle();
style.setHistStyle(iStyle);
if (cStyle >= 0) style.setColorMapScheme(cStyle);
}
void setStyle(int iStyle, int cStyle)
{
this.setSelected(this.iStyle == iStyle && (this.cStyle == -1 || this.cStyle == cStyle));
}
private int iStyle;
private int cStyle;
}
}
}