/*******************************************************************************
* Copyright (c) 2010 Haifeng Li
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package smile.plot;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import smile.math.Math;
import smile.projection.PCA;
import smile.swing.Button;
import smile.swing.FileChooser;
import smile.swing.Printer;
import smile.swing.Table;
/**
* Canvas for mathematical plots.
*
* For both 2D and 3D plot, the user can zoom in/out by mouse wheel. For 2D plot,
* the user can shift the coordinates by moving mouse after double click. The
* user can also select an area by mouse for detailed view. For 3D plot, the user
* can rotate the view by dragging mouse.
*
* @author Haifeng Li
*/
@SuppressWarnings("serial")
public class PlotCanvas extends JPanel {
/**
* The default (one side) margin portion.
*/
private static final double DEFAULT_MARGIN = 0.15;
/**
* The default font for rendering the title.
*/
private static final Font DEFAULT_TITLE_FONT = new Font("Arial", Font.BOLD, 16);
/**
* The default color for rendering the title.
*/
private static final Color DEFAULT_TITLE_COLOR = Color.BLACK;
/**
* The current coordinate base.
*/
Base base;
/**
* The graphics object associated with this canvas.
*/
Graphics graphics;
/**
* The portion of the canvas used for margin.
*/
double margin = DEFAULT_MARGIN;
/**
* The main title of plot.
*/
private String title;
/**
* The font for rendering the title.
*/
private Font titleFont = DEFAULT_TITLE_FONT;
/**
* The color for rendering the title.
*/
private Color titleColor = DEFAULT_TITLE_COLOR;
/**
* The coordinate base when the user start dragging the mouse.
*/
private Base backupBase;
/**
* The coordinate grid plot.
*/
private BaseGrid baseGrid;
/**
* The shapes in the canvas, e.g. label, plots, etc.
*/
private List shapes = new ArrayList<>();
/**
* The real canvas for plots.
*/
private MathCanvas canvas;
/**
* Optional tool bar to control plots.
*/
private JToolBar toolbar;
/**
* Right-click popup menu.
*/
private JPopupMenu popup;
/**
* Property table.
*/
private JTable propertyTable;
/**
* The real canvas for plotting.
*/
private class MathCanvas extends JComponent implements Printable, ComponentListener, MouseListener, MouseMotionListener, MouseWheelListener, ActionListener {
/**
* If the mouse double clicked.
*/
private boolean mouseDoubleClicked = false;
/**
* The x coordinate (in Java2D coordinate space) of mouse click.
*/
private int mouseClickX = -1;
/**
* The y coordinate (in Java2D coordinate space) of mouse click.
*/
private int mouseClickY = -1;
/**
* The x coordinate (in Java2D coordinate space) of mouse dragging.
*/
private int mouseDraggingX = -1;
/**
* The y coordinate (in Java2D coordinate space) of mouse dragging.
*/
private int mouseDraggingY = -1;
/**
* Constructor.
*/
MathCanvas() {
init();
}
/**
* Initialize the canvas.
*/
private void init() {
setBackground(Color.white);
setDoubleBuffered(true);
addComponentListener(this);
addMouseListener(this);
addMouseMotionListener(this);
addMouseWheelListener(this);
}
@Override
public void paintComponent(java.awt.Graphics g) {
Graphics2D g2d = (Graphics2D) g;
graphics.setGraphics(g2d);
Color color = g2d.getColor();
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getSize().width, getSize().height);
g2d.setColor(color);
baseGrid.paint(graphics);
// draw plot
graphics.clip();
int k = 0;
// with for-each loop, we will get a ConcurrentModificationException.
// Use for loop instead.
for (int i = 0; i < shapes.size(); i++) {
Shape s = shapes.get(i);
s.paint(graphics);
if (s instanceof Plot) {
Plot p = (Plot) s;
if (p.getID() != null) {
k++;
}
}
}
graphics.clearClip();
if (k > 1) {
Font font = g2d.getFont();
int x = (int) (getWidth() * (1 - margin) + 20);
int y = (int) (getHeight() * margin + 50);
int width = font.getSize();
int height = font.getSize();
for (int i = 0; i < shapes.size(); i++) {
Shape s = shapes.get(i);
if (s instanceof Plot) {
Plot p = (Plot) s;
if (p.getID() != null) {
g2d.setColor(p.getColor());
g2d.fillRect(x, y, width, height);
g2d.drawRect(x, y, width, height);
g2d.drawString(p.getID(), x + 2 * width, y + height);
y += 2 * width;
}
}
}
}
if (title != null) {
g2d.setFont(titleFont);
g2d.setColor(titleColor);
FontMetrics fm = g.getFontMetrics();
int x = (getWidth() - fm.stringWidth(title)) / 2;
int y = (int) (getHeight() * margin) / 2;
g2d.drawString(title, x, y);
}
if (mouseDraggingX >= 0 && mouseDraggingY >= 0) {
g.drawRect(Math.min(mouseClickX, mouseDraggingX),
Math.min(mouseClickY, mouseDraggingY),
Math.abs(mouseClickX - mouseDraggingX),
Math.abs(mouseClickY - mouseDraggingY));
}
g2d.setColor(color);
}
@Override
public int print(java.awt.Graphics g, PageFormat pf, int page) throws PrinterException {
if (page > 0) {
// We have only one page, and 'page' is zero-based
return NO_SUCH_PAGE;
}
Graphics2D g2d = (Graphics2D) g;
// User (0,0) is typically outside the imageable area, so we must
// translate by the X and Y values in the PageFormat to avoid clipping
g2d.translate(pf.getImageableX(), pf.getImageableY());
// Scale plots to paper size.
double scaleX = pf.getImageableWidth() / canvas.getWidth();
double scaleY = pf.getImageableHeight() / canvas.getHeight();
g2d.scale(scaleX, scaleY);
// Disable double buffering
canvas.setDoubleBuffered(false);
// Now we perform our rendering
canvas.print(g);
// Enable double buffering
canvas.setDoubleBuffered(true);
// tell the caller that this page is part of the printed document
return PAGE_EXISTS;
}
@Override
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger()) {
popup.show(e.getComponent(), e.getX(), e.getY());
e.consume();
} else {
mouseClickX = e.getX();
mouseClickY = e.getY();
e.consume();
}
}
@Override
public void mouseDragged(MouseEvent e) {
if (base.dimension == 2) {
mouseDraggingX = e.getX();
mouseDraggingY = e.getY();
repaint();
} else if (base.dimension == 3) {
graphics.rotate(e.getX() - mouseClickX, e.getY() - mouseClickY);
mouseClickX = e.getX();
mouseClickY = e.getY();
repaint();
}
e.consume();
}
@Override
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
popup.show(e.getComponent(), e.getX(), e.getY());
e.consume();
} else {
if (mouseDraggingX != -1 && mouseDraggingY != -1) {
mouseDraggingX = -1;
mouseDraggingY = -1;
if (base.dimension == 2) {
if (Math.abs(e.getX() - mouseClickX) > 20 && Math.abs(e.getY() - mouseClickY) > 20) {
double[] sc1 = ((Projection2D) (graphics.projection)).inverseProjection(mouseClickX, mouseClickY);
double[] sc2 = ((Projection2D) (graphics.projection)).inverseProjection(e.getX(), e.getY());
if (Math.min(sc1[0], sc2[0]) < base.upperBound[0]
&& Math.max(sc1[0], sc2[0]) > base.lowerBound[0]
&& Math.min(sc1[1], sc2[1]) < base.upperBound[1]
&& Math.max(sc1[1], sc2[1]) > base.lowerBound[1]) {
base.lowerBound[0] = Math.max(base.lowerBound[0], Math.min(sc1[0], sc2[0]));
base.upperBound[0] = Math.min(base.upperBound[0], Math.max(sc1[0], sc2[0]));
base.lowerBound[1] = Math.max(base.lowerBound[1], Math.min(sc1[1], sc2[1]));
base.upperBound[1] = Math.min(base.upperBound[1], Math.max(sc1[1], sc2[1]));
for (int i = 0; i < base.dimension; i++) {
base.setPrecisionUnit(i);
}
base.initBaseCoord();
graphics.projection.reset();
baseGrid.setBase(base);
}
}
}
repaint();
e.consume();
}
}
}
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
mouseDoubleClicked = true;
backupBase = base;
} else {
mouseDoubleClicked = false;
}
mouseClickX = e.getX();
mouseClickY = e.getY();
e.consume();
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void actionPerformed(ActionEvent e) {
}
@Override
public void mouseMoved(MouseEvent e) {
if (base.dimension == 2) {
if (mouseDoubleClicked) {
double x = mouseClickX - e.getX();
if (Math.abs(x) > 20) {
int s = baseGrid.getAxis(0).getLinearSlices();
x = x > 0 ? 1.0 / s : -1.0 / s;
x *= (backupBase.upperBound[0] - backupBase.lowerBound[0]);
base.lowerBound[0] = backupBase.lowerBound[0] + x;
base.upperBound[0] = backupBase.upperBound[0] + x;
mouseClickX = e.getX();
}
double y = mouseClickY - e.getY();
if (Math.abs(y) > 20) {
int s = baseGrid.getAxis(1).getLinearSlices();
y = y > 0 ? -1.0 / s : 1.0 / s;
y *= (backupBase.upperBound[1] - backupBase.lowerBound[1]);
base.lowerBound[1] = backupBase.lowerBound[1] + y;
base.upperBound[1] = backupBase.upperBound[1] + y;
mouseClickY = e.getY();
}
base.initBaseCoord();
graphics.projection.reset();
baseGrid.setBase(base);
repaint();
} else {
String tooltip = null;
double[] sc = ((Projection2D) (graphics.projection)).inverseProjection(e.getX(), e.getY());
String firstid = null;
for (Shape shape : shapes) {
if (shape instanceof Plot) {
Plot plot = (Plot) shape;
String s = plot.getToolTip(sc);
if (s != null) {
if (tooltip == null) {
tooltip = s;
firstid = plot.getID();
} else {
if (firstid != null) {
tooltip = "" + firstid + ":
" + tooltip;
firstid = null;
}
String id = plot.getID();
if (id != null) {
tooltip += "
" + id + ":
" + s;
} else {
tooltip += "
----------
" + s;
}
}
}
}
}
if (tooltip != null) {
setToolTipText(String.format("%s", tooltip));
} else {
setToolTipText(null);
}
}
}
e.consume();
}
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
if (e.getWheelRotation() == 0) {
return;
}
for (int i = 0; i < base.dimension; i++) {
int s = baseGrid.getAxis(i).getLinearSlices();
double r = e.getWheelRotation() > 0 ? 1.0 / s : -1.0 / s;
if (r > -0.5) {
double d = (base.upperBound[i] - base.lowerBound[i]) * r;
base.lowerBound[i] -= d;
base.upperBound[i] += d;
}
}
for (int i = 0; i < base.dimension; i++) {
base.setPrecisionUnit(i);
}
base.initBaseCoord();
graphics.projection.reset();
baseGrid.setBase(base);
repaint();
e.consume();
}
@Override
public void componentResized(ComponentEvent e) {
if (graphics != null) {
base.initBaseCoord();
graphics.projection.reset();
baseGrid.setBase(base);
}
repaint();
}
@Override
public void componentHidden(ComponentEvent e) {
}
@Override
public void componentMoved(ComponentEvent e) {
}
@Override
public void componentShown(ComponentEvent e) {
}
}
/**
* Constructor
*/
public PlotCanvas(double[] lowerBound, double[] upperBound) {
initCanvas();
initBase(lowerBound, upperBound);
initGraphics();
}
/**
* Constructor
*/
public PlotCanvas(double[] lowerBound, double[] upperBound, boolean extendBound) {
initCanvas();
initBase(lowerBound, upperBound, extendBound);
initGraphics();
}
/**
* Constructor
*/
public PlotCanvas(double[] lowerBound, double[] upperBound, String[] axisLabels) {
initCanvas();
initBase(lowerBound, upperBound, axisLabels);
initGraphics();
}
/**
* Constructor
*/
public PlotCanvas(double[] lowerBound, double[] upperBound, String[] axisLabels, boolean extendBound) {
initCanvas();
initBase(lowerBound, upperBound, axisLabels, extendBound);
initGraphics();
}
/**
* Returns a tool bar to control the plot.
* @return a tool bar to control the plot.
*/
public JComponent getToolbar() {
return toolbar;
}
/**
* Initialize the canvas.
*/
private void initCanvas() {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
if (ge.isHeadless()) {
setPreferredSize(new Dimension(1600,1200));
}
setLayout(new BorderLayout());
canvas = new MathCanvas();
add(canvas, BorderLayout.CENTER);
initContextMenauAndToolBar();
// set a new dismiss delay to a really big value, default is 4 sec.
//ToolTipManager.sharedInstance().setDismissDelay(Integer.MAX_VALUE);
}
/**
* Toolbar button actions.
*/
private transient Action saveAction = new SaveAction();
private transient Action printAction = new PrintAction();
private transient Action zoomInAction = new ZoomInAction();
private transient Action zoomOutAction = new ZoomOutAction();
private transient Action resetAction = new ResetAction();
private transient Action enlargePlotAreaAction = new EnlargePlotAreaAction();
private transient Action shrinkPlotAreaAction = new ShrinkPlotAreaAction();
private transient Action propertyAction = new PropertyAction();
private transient Action increaseHeightAction = new IncreaseHeightAction();
private transient Action increaseWidthAction = new IncreaseWidthAction();
private transient Action decreaseHeightAction = new DecreaseHeightAction();
private transient Action decreaseWidthAction = new DecreaseWidthAction();
private JScrollPane scrollPane;
/**
* Initialize context menu and toolbar.
*/
private void initContextMenauAndToolBar() {
toolbar = new JToolBar();
toolbar.add(new Button(saveAction));
toolbar.add(new Button(printAction));
toolbar.addSeparator();
toolbar.add(new Button(zoomInAction));
toolbar.add(new Button(zoomOutAction));
toolbar.add(new Button(resetAction));
toolbar.addSeparator();
toolbar.add(new Button(enlargePlotAreaAction));
toolbar.add(new Button(shrinkPlotAreaAction));
toolbar.add(new Button(increaseHeightAction));
toolbar.add(new Button(decreaseHeightAction));
toolbar.add(new Button(increaseWidthAction));
toolbar.add(new Button(decreaseWidthAction));
toolbar.addSeparator();
toolbar.add(new Button(propertyAction));
decreaseHeightAction.setEnabled(false);
decreaseWidthAction.setEnabled(false);
//Initialize popup menu.
popup = new JPopupMenu();
popup.add(new JMenuItem(saveAction));
popup.add(new JMenuItem(printAction));
popup.addSeparator();
popup.add(new JMenuItem(zoomInAction));
popup.add(new JMenuItem(zoomOutAction));
popup.add(new JMenuItem(resetAction));
popup.addSeparator();
popup.add(new JMenuItem(enlargePlotAreaAction));
popup.add(new JMenuItem(shrinkPlotAreaAction));
popup.add(new JMenuItem(increaseHeightAction));
popup.add(new JMenuItem(decreaseHeightAction));
popup.add(new JMenuItem(increaseWidthAction));
popup.add(new JMenuItem(decreaseWidthAction));
popup.addSeparator();
popup.add(new JMenuItem(propertyAction));
AncestorListener ancestorListener = new AncestorListener() {
@Override
public void ancestorAdded(AncestorEvent ae) {
boolean inScrollPane = false;
Container parent = getParent();
while (parent != null) {
if (parent instanceof JScrollPane) {
inScrollPane = true;
scrollPane = (JScrollPane) parent;
break;
}
parent = parent.getParent();
}
increaseHeightAction.setEnabled(inScrollPane);
increaseWidthAction.setEnabled(inScrollPane);
}
@Override
public void ancestorRemoved(AncestorEvent ae) {
}
@Override
public void ancestorMoved(AncestorEvent ae) {
}
};
addAncestorListener(ancestorListener);
}
private class SaveAction extends AbstractAction {
public SaveAction() {
super("Save", new ImageIcon(PlotCanvas.class.getResource("images/save16.png")));
}
@Override
public void actionPerformed(ActionEvent e) {
try {
save();
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
}
private class PrintAction extends AbstractAction {
public PrintAction() {
super("Print", new ImageIcon(PlotCanvas.class.getResource("images/print16.png")));
}
@Override
public void actionPerformed(ActionEvent e) {
print();
}
}
private class ZoomInAction extends AbstractAction {
public ZoomInAction() {
super("Zoom In", new ImageIcon(PlotCanvas.class.getResource("images/zoom-in16.png")));
}
@Override
public void actionPerformed(ActionEvent e) {
zoom(true);
}
}
private class ZoomOutAction extends AbstractAction {
public ZoomOutAction() {
super("Zoom Out", new ImageIcon(PlotCanvas.class.getResource("images/zoom-out16.png")));
}
@Override
public void actionPerformed(ActionEvent e) {
zoom(false);
}
}
private class ResetAction extends AbstractAction {
public ResetAction() {
super("Reset", new ImageIcon(PlotCanvas.class.getResource("images/refresh16.png")));
}
@Override
public void actionPerformed(ActionEvent e) {
reset();
}
}
private class EnlargePlotAreaAction extends AbstractAction {
public EnlargePlotAreaAction() {
super("Enlarge", new ImageIcon(PlotCanvas.class.getResource("images/resize-larger16.png")));
}
@Override
public void actionPerformed(ActionEvent e) {
if (margin > 0.05) {
margin -= 0.05;
graphics.projection.reset();
repaint();
}
if (margin <= 0.05) {
setEnabled(false);
}
if (!shrinkPlotAreaAction.isEnabled()) {
shrinkPlotAreaAction.setEnabled(true);
}
}
}
private class ShrinkPlotAreaAction extends AbstractAction {
public ShrinkPlotAreaAction() {
super("Shrink", new ImageIcon(PlotCanvas.class.getResource("images/resize-smaller16.png")));
}
@Override
public void actionPerformed(ActionEvent e) {
if (margin < 0.3) {
margin += 0.05;
graphics.projection.reset();
repaint();
}
if (margin >= 0.3) {
setEnabled(false);
}
if (!enlargePlotAreaAction.isEnabled()) {
enlargePlotAreaAction.setEnabled(true);
}
}
}
private class IncreaseWidthAction extends AbstractAction {
public IncreaseWidthAction() {
super("Increase Width", new ImageIcon(PlotCanvas.class.getResource("images/increase-width16.png")));
}
@Override
public void actionPerformed(ActionEvent e) {
Dimension d = getSize();
d.width += 100;
setPreferredSize(d);
invalidate();
scrollPane.getParent().validate();
if (!decreaseWidthAction.isEnabled()) {
decreaseWidthAction.setEnabled(true);
}
}
}
private class IncreaseHeightAction extends AbstractAction {
public IncreaseHeightAction() {
super("Increase Height", new ImageIcon(PlotCanvas.class.getResource("images/increase-height16.png")));
}
@Override
public void actionPerformed(ActionEvent e) {
Dimension d = getSize();
d.height += 100;
setPreferredSize(d);
invalidate();
scrollPane.getParent().validate();
if (!decreaseHeightAction.isEnabled()) {
decreaseHeightAction.setEnabled(true);
}
}
}
private class DecreaseWidthAction extends AbstractAction {
public DecreaseWidthAction() {
super("Decrease Width", new ImageIcon(PlotCanvas.class.getResource("images/decrease-width16.png")));
}
@Override
public void actionPerformed(ActionEvent e) {
Dimension d = getSize();
d.width -= 100;
Dimension vd = scrollPane.getViewport().getSize();
if (d.width <= vd.width) {
d.width = vd.width;
decreaseWidthAction.setEnabled(false);
}
setPreferredSize(d);
invalidate();
scrollPane.getParent().validate();
}
}
private class DecreaseHeightAction extends AbstractAction {
public DecreaseHeightAction() {
super("Decrease Height", new ImageIcon(PlotCanvas.class.getResource("images/decrease-height16.png")));
}
@Override
public void actionPerformed(ActionEvent e) {
Dimension d = getSize();
d.height -= 100;
Dimension vd = scrollPane.getViewport().getSize();
if (d.height <= vd.height) {
d.height = vd.height;
decreaseHeightAction.setEnabled(false);
}
setPreferredSize(d);
invalidate();
scrollPane.getParent().validate();
}
}
private class PropertyAction extends AbstractAction {
public PropertyAction() {
super("Properties", new ImageIcon(PlotCanvas.class.getResource("images/property16.png")));
}
@Override
public void actionPerformed(ActionEvent e) {
JDialog dialog = createPropertyDialog();
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
}
});
dialog.setVisible(true);
dialog.dispose();
dialog = null;
}
}
/**
* Creates the property dialog.
* @return the property dialog.
*/
private JDialog createPropertyDialog() {
Frame frame = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, this);
JDialog dialog = new JDialog(frame, "Plot Properties", true);
Action okAction = new PropertyDialogOKAction(dialog);
Action cancelAction = new PropertyDialogCancelAction(dialog);
JButton okButton = new JButton(okAction);
JButton cancelButton = new JButton(cancelAction);
JPanel buttonsPanel = new JPanel();
buttonsPanel.setLayout(new FlowLayout(FlowLayout.TRAILING));
buttonsPanel.add(okButton);
buttonsPanel.add(cancelButton);
buttonsPanel.setBorder(BorderFactory.createEmptyBorder(25, 0, 10, 10));
ActionMap actionMap = buttonsPanel.getActionMap();
actionMap.put(cancelAction.getValue(Action.DEFAULT), cancelAction);
actionMap.put(okAction.getValue(Action.DEFAULT), okAction);
InputMap inputMap = buttonsPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
inputMap.put(KeyStroke.getKeyStroke("ESCAPE"), cancelAction.getValue(Action.DEFAULT));
inputMap.put(KeyStroke.getKeyStroke("ENTER"), okAction.getValue(Action.DEFAULT));
String[] columnNames = {"Property", "Value"};
if (base.dimension == 2) {
Object[][] data = {
{"Title", title},
{"Title Font", titleFont},
{"Title Color", titleColor},
{"X Axis Title", getAxis(0).getAxisLabel()},
{"X Axis Range", new double[]{base.getLowerBounds()[0], base.getUpperBounds()[0]}},
{"Y Axis Title", getAxis(1).getAxisLabel()},
{"Y Axis Range", new double[]{base.getLowerBounds()[1], base.getUpperBounds()[1]}}
};
propertyTable = new Table(data, columnNames);
} else {
Object[][] data = {
{"Title", title},
{"Title Font", titleFont},
{"Title Color", titleColor},
{"X Axis Title", getAxis(0).getAxisLabel()},
{"X Axis Range", new double[]{base.getLowerBounds()[0], base.getUpperBounds()[0]}},
{"Y Axis Title", getAxis(1).getAxisLabel()},
{"Y Axis Range", new double[]{base.getLowerBounds()[1], base.getUpperBounds()[1]}},
{"Z Axis Title", getAxis(2).getAxisLabel()},
{"Z Axis Range", new double[]{base.getLowerBounds()[2], base.getUpperBounds()[2]}}
};
propertyTable = new Table(data, columnNames);
}
// There is a known issue with JTables whereby the changes made in a
// cell editor are not committed when focus is lost.
// This can result in the table staying in 'edit mode' with the stale
// value in the cell being edited still showing, although the change
// has not actually been committed to the model.
//
// In fact what should happen is for the method stopCellEditing()
// on CellEditor to be called when focus is lost.
// So the editor can choose whether to accept the new value and stop
// editing, or have the editing cancelled without committing.
// There is a magic property which you have to set on the JTable
// instance to turn this feature on.
propertyTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
propertyTable.setFillsViewportHeight(true);
JScrollPane tablePanel = new JScrollPane(propertyTable);
dialog.getContentPane().add(tablePanel, BorderLayout.CENTER);
dialog.getContentPane().add(buttonsPanel, BorderLayout.SOUTH);
dialog.pack();
dialog.setLocationRelativeTo(frame);
return dialog;
}
private class PropertyDialogOKAction extends AbstractAction {
protected static final String ACTION_NAME = "OK";
private JDialog dialog;
protected PropertyDialogOKAction(JDialog dialog) {
this.dialog = dialog;
putValue(Action.DEFAULT, ACTION_NAME);
putValue(Action.ACTION_COMMAND_KEY, ACTION_NAME);
putValue(Action.NAME, ACTION_NAME);
}
@Override
public void actionPerformed(ActionEvent e) {
setTitle((String) propertyTable.getValueAt(0, 1));
setTitleFont((Font) propertyTable.getValueAt(1, 1));
setTitleColor((Color) propertyTable.getValueAt(2, 1));
getAxis(0).setAxisLabel((String) propertyTable.getValueAt(3, 1));
double[] xbound = (double[]) propertyTable.getValueAt(4, 1);
base.lowerBound[0] = xbound[0];
base.upperBound[0] = xbound[1];
getAxis(1).setAxisLabel((String) propertyTable.getValueAt(5, 1));
double[] ybound = (double[]) propertyTable.getValueAt(6, 1);
base.lowerBound[1] = ybound[0];
base.upperBound[1] = ybound[1];
if (base.dimension > 2) {
getAxis(2).setAxisLabel((String) propertyTable.getValueAt(7, 1));
double[] zbound = (double[]) propertyTable.getValueAt(8, 1);
base.lowerBound[2] = zbound[0];
base.upperBound[2] = zbound[1];
}
for (int i = 0; i < base.dimension; i++) {
base.setPrecisionUnit(i);
}
base.initBaseCoord();
graphics.projection.reset();
baseGrid.setBase(base);
dialog.setVisible(false);
canvas.repaint();
}
}
private class PropertyDialogCancelAction extends AbstractAction {
protected static final String ACTION_NAME = "Cancel";
private JDialog dialog;
protected PropertyDialogCancelAction(JDialog dialog) {
this.dialog = dialog;
putValue(Action.DEFAULT, ACTION_NAME);
putValue(Action.ACTION_COMMAND_KEY, ACTION_NAME);
putValue(Action.NAME, ACTION_NAME);
}
@Override
public void actionPerformed(ActionEvent e) {
dialog.setVisible(false);
}
}
/**
* Shows a file chooser and exports the plot to the selected image file.
* @throws IOException if an error occurs during writing.
*/
public void save() throws IOException {
FileChooser.SimpleFileFilter filter = FileChooser.SimpleFileFilter.getWritableImageFIlter();
JFileChooser chooser = FileChooser.getInstance();
chooser.setFileFilter(filter);
chooser.setAcceptAllFileFilterUsed(false);
chooser.setSelectedFiles(new File[0]);
int returnVal = chooser.showSaveDialog(null);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = chooser.getSelectedFile();
if (!filter.accept(file)) {
file = new File(file.getParentFile(), file.getName() + ".png");
}
save(file);
}
}
/**
* Exports the plot to an image file.
* @param file the destination file.
* @throws IOException if an error occurs during writing.
*/
public void save(File file) throws IOException {
BufferedImage bi = new BufferedImage(canvas.getWidth(), canvas.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bi.createGraphics();
canvas.print(g2d);
ImageIO.write(bi, FileChooser.getExtension(file), file);
}
/**
* Prints the plot.
*/
public void print() {
Printer.getPrinter().print(canvas);
}
/**
* Zooms in/out the plot.
* @param inout true if zoom in. Otherwise, zoom out.
*/
public void zoom(boolean inout) {
for (int i = 0; i < base.dimension; i++) {
int s = baseGrid.getAxis(i).getLinearSlices();
double r = inout ? -1.0 / s : 1.0 / s;
double d = (base.upperBound[i] - base.lowerBound[i]) * r;
base.lowerBound[i] -= d;
base.upperBound[i] += d;
}
for (int i = 0; i < base.dimension; i++) {
base.setPrecisionUnit(i);
}
base.initBaseCoord();
graphics.projection.reset();
baseGrid.setBase(base);
canvas.repaint();
}
/**
* Resets the plot.
*/
public void reset() {
base.reset();
graphics.projection.reset();
baseGrid.setBase(base);
if (graphics.projection instanceof Projection3D) {
((Projection3D) graphics.projection).setDefaultView();
}
canvas.repaint();
}
/**
* Initialize the Graphics object.
*/
private void initGraphics() {
if (base.dimension == 2) {
graphics = new Graphics(new Projection2D(this));
} else {
graphics = new Graphics(new Projection3D(this));
}
}
/**
* Initialize a coordinate base.
*/
private void initBase(double[] lowerBound, double[] upperBound) {
base = new Base(lowerBound, upperBound);
backupBase = base;
baseGrid = new BaseGrid(base);
}
/**
* Initialize a coordinate base.
*/
private void initBase(double[] lowerBound, double[] upperBound, boolean extendBound) {
base = new Base(lowerBound, upperBound, extendBound);
backupBase = base;
baseGrid = new BaseGrid(base);
}
/**
* Initialize a coordinate base.
*/
private void initBase(double[] lowerBound, double[] upperBound, String[] axisLabels) {
base = new Base(lowerBound, upperBound);
backupBase = base;
baseGrid = new BaseGrid(base, axisLabels);
}
/**
* Initialize a coordinate base.
*/
private void initBase(double[] lowerBound, double[] upperBound, String[] axisLabels, boolean extendBound) {
base = new Base(lowerBound, upperBound, extendBound);
backupBase = base;
baseGrid = new BaseGrid(base, axisLabels);
}
/**
* Returns the size of margin, which is not used as plot area.
* Currently, all four sides have the same margin size.
* @return the size of margin.
*/
public double getMargin() {
return margin;
}
/**
* Sets the size of margin in [0.0, 0.3] on each side. Currently, all four
* sides have the same margin size.
* @param margin the size of margin.
*/
public PlotCanvas setMargin(double margin) {
if (margin < 0.0 || margin >= 0.3) {
throw new IllegalArgumentException("Invalid margin: " + margin);
}
this.margin = margin;
repaint();
return this;
}
/**
* Returns the coordinate base.
* @return the coordinate base.
*/
public Base getBase() {
return base;
}
/**
* Returns the main title of canvas.
*/
public String getTitle() {
return title;
}
/**
* Set the main title of canvas.
*/
public PlotCanvas setTitle(String title) {
this.title = title;
repaint();
return this;
}
/**
* Returns the font for title.
*/
public Font getTitleFont() {
return titleFont;
}
/**
* Returns the color for title.
*/
public Color getTitleColor() {
return titleColor;
}
/**
* Set the font for title.
*/
public PlotCanvas setTitleFont(Font font) {
this.titleFont = font;
repaint();
return this;
}
/**
* Set the color for title.
*/
public PlotCanvas setTitleColor(Color color) {
this.titleColor = color;
repaint();
return this;
}
/**
* Returns the i-th axis.
*/
public Axis getAxis(int i) {
return baseGrid.getAxis(i);
}
/**
* Returns the labels/legends of axes.
*/
public String[] getAxisLabels() {
String[] labels = new String[base.dimension];
for (int i = 0; i < base.dimension; i++) {
labels[i] = baseGrid.getAxis(i).getAxisLabel();
}
return labels;
}
/**
* Returns the label/legend of an axis.
*/
public String getAxisLabel(int axis) {
return baseGrid.getAxis(axis).getAxisLabel();
}
/**
* Sets the labels/legends of axes.
*/
public PlotCanvas setAxisLabels(String... labels) {
baseGrid.setAxisLabel(labels);
repaint();
return this;
}
/**
* Sets the label/legend of an axis.
*/
public PlotCanvas setAxisLabel(int axis, String label) {
baseGrid.setAxisLabel(axis, label);
repaint();
return this;
}
/**
* Returns the list of shapes in the canvas.
* @return the list of shapes in the canvas.
*/
public List getShapes() {
return shapes;
}
/**
* Add a graphical shape to the canvas.
*/
public void add(Shape p) {
shapes.add(p);
repaint();
}
/**
* Remove a graphical shape from the canvas.
*/
public void remove(Shape p) {
shapes.remove(p);
repaint();
}
/**
* Add a graphical shape to the canvas.
*/
public void add(Plot p) {
shapes.add(p);
JComponent[] tb = p.getToolBar();
if (tb != null) {
toolbar.addSeparator();
for (JComponent comp : tb) {
toolbar.add(comp);
}
}
repaint();
}
/**
* Remove a graphical shape from the canvas.
*/
public void remove(Plot p) {
shapes.remove(p);
JComponent[] tb = p.getToolBar();
if (tb != null) {
for (JComponent comp : tb) {
toolbar.remove(comp);
}
}
repaint();
}
/**
* Remove all graphic plots from the canvas.
*/
public void clear() {
shapes.clear();
repaint();
}
/**
* Returns the lower bounds.
*/
public double[] getLowerBounds() {
return base.lowerBound;
}
/**
* Returns the upper bounds.
*/
public double[] getUpperBounds() {
return base.upperBound;
}
/**
* Extend lower bounds.
*/
public void extendLowerBound(double[] bound) {
base.extendLowerBound(bound);
baseGrid.setBase(base);
repaint();
}
/**
* Extend upper bounds.
*/
public void extendUpperBound(double[] bound) {
base.extendUpperBound(bound);
baseGrid.setBase(base);
repaint();
}
/**
* Extend lower and upper bounds.
*/
public void extendBound(double[] lowerBound, double[] upperBound) {
base.extendBound(lowerBound, upperBound);
baseGrid.setBase(base);
repaint();
}
/**
* Adds a label to this canvas.
*/
public void label(String text, double... coord) {
add(new Label(text, coord));
}
/**
* Adds a label to this canvas.
*/
public void label(String text, Font font, double... coord) {
Label label = new Label(text, coord);
label.setFont(font);
add(label);
}
/**
* Adds a label to this canvas.
*/
public void label(String text, Color color, double... coord) {
Label label = new Label(text, coord);
label.setColor(color);
add(label);
}
/**
* Adds a label to this canvas.
*/
public void label(String text, Font font, Color color, double... coord) {
Label label = new Label(text, coord);
label.setFont(font);
label.setColor(color);
add(label);
}
/**
* Adds a point to this canvas.
*/
public void point(double... coord) {
add(new Point(coord));
}
/**
* Adds a point to this canvas.
*/
public void point(char legend, double... coord) {
add(new Point(legend, coord));
}
/**
* Adds a point to this canvas.
*/
public void point(Color color, double... coord) {
add(new Point(color, coord));
}
/**
* Adds a point to this canvas.
*/
public void point(char legend, Color color, double... coord) {
add(new Point(legend, color, coord));
}
/**
* Adds a scatter plot to this canvas.
* @param data a n-by-2 or n-by-3 matrix that describes coordinates of points.
* @return the scatter plot for the given points.
*/
public ScatterPlot points(double[]... data) {
return points(null, data);
}
/**
* Adds a scatter plot to this canvas.
* @param id the id of the plot.
* @param data a n-by-2 or n-by-3 matrix that describes coordinates of points.
* @return the scatter plot for the given points.
*/
public ScatterPlot points(String id, double[]... data) {
if (data[0].length != base.dimension) {
throw new IllegalArgumentException("Invalid data dimension: " + data[0].length);
}
double[] lowerBound = Math.colMin(data);
double[] upperBound = Math.colMax(data);
extendBound(lowerBound, upperBound);
ScatterPlot plot = new ScatterPlot(data);
plot.setID(id);
add(plot);
return plot;
}
/**
* Adds a scatter plot to this canvas.
* @param data a n-by-2 or n-by-3 matrix that describes coordinates of points.
* @param labels labels of points.
* @return the scatter plot for the given points.
*/
public ScatterPlot points(double[][] data, String[] labels) {
return points(null, data, labels);
}
/**
* Adds a scatter plot to this canvas.
* @param id the id of the plot.
* @param data a n-by-2 or n-by-3 matrix that describes coordinates of points.
* @param labels labels of points.
* @return the scatter plot for the given points.
*/
public ScatterPlot points(String id, double[][] data, String[] labels) {
if (data[0].length != base.dimension) {
throw new IllegalArgumentException("Invalid data dimension: " + data[0].length);
}
if (data.length != labels.length) {
throw new IllegalArgumentException("The number of points and that of labels are not same.");
}
double[] lowerBound = Math.colMin(data);
double[] upperBound = Math.colMax(data);
extendBound(lowerBound, upperBound);
ScatterPlot plot = new ScatterPlot(data, labels);
plot.setID(id);
add(plot);
return plot;
}
/**
* Adds a scatter plot to this canvas.
* @param data a n-by-2 or n-by-3 matrix that describes coordinates of points.
* @param legend the legend used to draw points.
*
* - . : dot
*
- + : +
*
- - : -
*
- | : |
*
- * : star
*
- x : x
*
- o : circle
*
- O : large circle
*
- @ : solid circle
*
- # : large solid circle
*
- s : square
*
- S : large square
*
- q : solid square
*
- Q : large solid square
*
- others : dot
*
* @return the scatter plot for the given points.
*/
public ScatterPlot points(double[][] data, char legend) {
return points(null, data, legend);
}
/**
* Adds a scatter plot to this canvas.
* @param id the id of the plot.
* @param data a n-by-2 or n-by-3 matrix that describes coordinates of points.
* @param legend the legend used to draw points.
*
* - . : dot
*
- + : +
*
- - : -
*
- | : |
*
- * : star
*
- x : x
*
- o : circle
*
- O : large circle
*
- @ : solid circle
*
- # : large solid circle
*
- s : square
*
- S : large square
*
- q : solid square
*
- Q : large solid square
*
- others : dot
*
* @return the scatter plot for the given points.
*/
public ScatterPlot points(String id, double[][] data, char legend) {
if (data[0].length != base.dimension) {
throw new IllegalArgumentException("Invalid data dimension: " + data[0].length);
}
double[] lowerBound = Math.colMin(data);
double[] upperBound = Math.colMax(data);
extendBound(lowerBound, upperBound);
ScatterPlot plot = new ScatterPlot(data, legend);
plot.setID(id);
add(plot);
return plot;
}
/**
* Adds a scatter plot to this canvas.
* @param data a n-by-2 or n-by-3 matrix that describes coordinates of points.
* @param color the color used to draw points.
* @return the scatter plot for the given points.
*/
public ScatterPlot points(double[][] data, Color color) {
return points(null, data, color);
}
/**
* Adds a scatter plot to this canvas.
* @param id the id of the plot.
* @param data a n-by-2 or n-by-3 matrix that describes coordinates of points.
* @param color the color used to draw points.
* @return the scatter plot for the given points.
*/
public ScatterPlot points(String id, double[][] data, Color color) {
if (data[0].length != base.dimension) {
throw new IllegalArgumentException("Invalid data dimension: " + data[0].length);
}
double[] lowerBound = Math.colMin(data);
double[] upperBound = Math.colMax(data);
extendBound(lowerBound, upperBound);
ScatterPlot plot = new ScatterPlot(data);
plot.setID(id);
plot.setColor(color);
add(plot);
return plot;
}
/**
* Adds a scatter plot to this canvas.
* @param data a n-by-2 or n-by-3 matrix that describes coordinates of points.
* @param color the color used to draw points.
* @param legend the legend used to draw points.
*
* - . : dot
*
- + : +
*
- - : -
*
- | : |
*
- * : star
*
- x : x
*
- o : circle
*
- O : large circle
*
- @ : solid circle
*
- # : large solid circle
*
- s : square
*
- S : large square
*
- q : solid square
*
- Q : large solid square
*
- others : dot
*
* @return the scatter plot for the given points.
*/
public ScatterPlot points(double[][] data, char legend, Color color) {
return points(null, data, legend, color);
}
/**
* Adds a scatter plot to this canvas.
* @param id the id of the plot.
* @param data a n-by-2 or n-by-3 matrix that describes coordinates of points.
* @param color the color used to draw points.
* @param legend the legend used to draw points.
*
* - . : dot
*
- + : +
*
- - : -
*
- | : |
*
- * : star
*
- x : x
*
- o : circle
*
- O : large circle
*
- @ : solid circle
*
- # : large solid circle
*
- s : square
*
- S : large square
*
- q : solid square
*
- Q : large solid square
*
- others : dot
*
* @return the scatter plot for the given points.
*/
public ScatterPlot points(String id, double[][] data, char legend, Color color) {
if (data[0].length != base.dimension) {
throw new IllegalArgumentException("Invalid data dimension: " + data[0].length);
}
double[] lowerBound = Math.colMin(data);
double[] upperBound = Math.colMax(data);
extendBound(lowerBound, upperBound);
ScatterPlot plot = new ScatterPlot(data, legend);
plot.setID(id);
plot.setColor(color);
add(plot);
return plot;
}
/**
* Adds a poly line plot to this canvas.
* @param y a data vector that describes y coordinates of points. The x
* coordinates will be [0, n), where n is the length of y.
*/
public LinePlot line(double[] y) {
return line(null, y);
}
/**
* Adds a poly line plot to this canvas.
* @param id the id of the plot.
* @param y a data vector that describes y coordinates of points. The x
* coordinates will be [0, n), where n is the length of y.
*/
public LinePlot line(String id, double[] y) {
double[] lowerBound = {0, Math.min(y)};
double[] upperBound = {y.length, Math.max(y)};
extendBound(lowerBound, upperBound);
double[][] data = new double[y.length][2];
for (int i = 0; i < data.length; i++) {
data[i][0] = i;
data[i][1] = y[i];
}
LinePlot plot = new LinePlot(data);
plot.setID(id);
add(plot);
return plot;
}
/**
* Adds a poly line plot to this canvas.
* @param y a data vector that describes y coordinates of points. The x
* coordinates will be [0, n), where n is the length of y.
* @param color the color of line.
*/
public LinePlot line(double[] y, Color color) {
return line(null, y, color);
}
/**
* Adds a poly line plot to this canvas.
* @param id the id of the plot.
* @param y a data vector that describes y coordinates of points. The x
* coordinates will be [0, n), where n is the length of y.
* @param color the color of line.
*/
public LinePlot line(String id, double[] y, Color color) {
double[] lowerBound = {0, Math.min(y)};
double[] upperBound = {y.length, Math.max(y)};
extendBound(lowerBound, upperBound);
double[][] data = new double[y.length][2];
for (int i = 0; i < data.length; i++) {
data[i][0] = i;
data[i][1] = y[i];
}
LinePlot plot = new LinePlot(data);
plot.setID(id);
plot.setColor(color);
add(plot);
return plot;
}
/**
* Add a poly line plot of given data into the current canvas.
* @param y a data vector that describes y coordinates of points. The x
* coordinates will be [0, n), where n is the length of y.
* @param style the stroke style of line.
*/
public LinePlot line(double[] y, Line.Style style) {
return line(null, y, style);
}
/**
* Adds a poly line plot to this canvas.
* @param id the id of the plot.
* @param y a data vector that describes y coordinates of points. The x
* coordinates will be [0, n), where n is the length of y.
* @param style the stroke style of line.
*/
public LinePlot line(String id, double[] y, Line.Style style) {
double[] lowerBound = {0, Math.min(y)};
double[] upperBound = {y.length, Math.max(y)};
extendBound(lowerBound, upperBound);
double[][] data = new double[y.length][2];
for (int i = 0; i < data.length; i++) {
data[i][0] = i;
data[i][1] = y[i];
}
LinePlot plot = new LinePlot(data, style);
plot.setID(id);
add(plot);
return plot;
}
/**
* Adds a poly line plot to this canvas.
* @param y a data vector that describes y coordinates of points. The x
* coordinates will be [0, n), where n is the length of y.
* @param style the stroke style of line.
* @param color the color of line.
*/
public LinePlot line(double[] y, Line.Style style, Color color) {
return line(null, y, style, color);
}
/**
* Add a poly line plot of given data into the current canvas.
* @param id the id of the plot.
* @param y a data vector that describes y coordinates of points. The x
* coordinates will be [0, n), where n is the length of y.
* @param style the stroke style of line.
* @param color the color of line.
*/
public LinePlot line(String id, double[] y, Line.Style style, Color color) {
double[] lowerBound = {0, Math.min(y)};
double[] upperBound = {y.length, Math.max(y)};
extendBound(lowerBound, upperBound);
double[][] data = new double[y.length][2];
for (int i = 0; i < data.length; i++) {
data[i][0] = i;
data[i][1] = y[i];
}
LinePlot plot = new LinePlot(data, style);
plot.setID(id);
plot.setColor(color);
add(plot);
return plot;
}
/**
* Adds a poly line plot to this canvas.
* @param data a n-by-2 or n-by-3 matrix that describes coordinates of points.
*/
public LinePlot line(double[]... data) {
return line(null, data);
}
/**
* Adds a poly line plot to this canvas.
* @param id the id of the plot.
* @param data a n-by-2 or n-by-3 matrix that describes coordinates of points.
*/
public LinePlot line(String id, double[]... data) {
if (data[0].length != base.dimension) {
throw new IllegalArgumentException("Invalid data dimension: " + data[0].length);
}
double[] lowerBound = Math.colMin(data);
double[] upperBound = Math.colMax(data);
extendBound(lowerBound, upperBound);
LinePlot plot = new LinePlot(data);
plot.setID(id);
add(plot);
return plot;
}
/**
* Adds a poly line plot to this canvas.
* @param data a n-by-2 or n-by-3 matrix that describes coordinates of points.
* @param color the color of line.
*/
public LinePlot line(double[][] data, Color color) {
return line(null, data, color);
}
/**
* Adds a poly line plot to this canvas.
* @param id the id of the plot.
* @param data a n-by-2 or n-by-3 matrix that describes coordinates of points.
* @param color the color of line.
*/
public LinePlot line(String id, double[][] data, Color color) {
if (data[0].length != base.dimension) {
throw new IllegalArgumentException("Invalid data dimension: " + data[0].length);
}
double[] lowerBound = Math.colMin(data);
double[] upperBound = Math.colMax(data);
extendBound(lowerBound, upperBound);
LinePlot plot = new LinePlot(data);
plot.setID(id);
plot.setColor(color);
add(plot);
return plot;
}
/**
* Adds a poly line plot to this canvas.
* @param data a n-by-2 or n-by-3 matrix that describes coordinates of points.
* @param style the stroke style of line.
*/
public LinePlot line(double[][] data, Line.Style style) {
return line(null, data, style);
}
/**
* Adds a poly line plot to this canvas.
* @param id the id of the plot.
* @param data a n-by-2 or n-by-3 matrix that describes coordinates of points.
* @param style the stroke style of line.
*/
public LinePlot line(String id, double[][] data, Line.Style style) {
if (data[0].length != base.dimension) {
throw new IllegalArgumentException("Invalid data dimension: " + data[0].length);
}
double[] lowerBound = Math.colMin(data);
double[] upperBound = Math.colMax(data);
extendBound(lowerBound, upperBound);
LinePlot plot = new LinePlot(data, style);
plot.setID(id);
add(plot);
return plot;
}
/**
* Adds a poly line plot to this canvas.
* @param data a n x 2 or n x 3 matrix that describes coordinates of points.
* @param style the stroke style of line.
* @param color the color of line.
*/
public LinePlot line(double[][] data, Line.Style style, Color color) {
return line(null, data, style, color);
}
/**
* Adds a poly line plot to this canvas.
* @param id the id of the plot.
* @param data a n x 2 or n x 3 matrix that describes coordinates of points.
* @param style the stroke style of line.
* @param color the color of line.
*/
public LinePlot line(String id, double[][] data, Line.Style style, Color color) {
if (data[0].length != base.dimension) {
throw new IllegalArgumentException("Invalid data dimension: " + data[0].length);
}
double[] lowerBound = Math.colMin(data);
double[] upperBound = Math.colMax(data);
extendBound(lowerBound, upperBound);
LinePlot plot = new LinePlot(data, style);
plot.setID(id);
plot.setColor(color);
add(plot);
return plot;
}
/**
* Adds a staircase line plot to this canvas.
* @param data a n x 2 or n x 3 matrix that describes coordinates of points.
* @param color the color of line.
*/
public StaircasePlot staircase(double[][] data, Color color) {
return staircase(null, data, color);
}
/**
* Adds a staircase line plot to this canvas.
* @param id the id of the plot.
* @param data a n x 2 or n x 3 matrix that describes coordinates of points.
* @param color the color of line.
*/
public StaircasePlot staircase(String id, double[][] data, Color color) {
if (data[0].length != 2 && data[0].length != 3) {
throw new IllegalArgumentException("Invalid data dimension: " + data[0].length);
}
StaircasePlot plot = new StaircasePlot(data);
plot.setID(id);
plot.setColor(color);
add(plot);
return plot;
}
/**
* Adds a histogram to this canvas.
* @param data a sample set.
*/
public Histogram histogram(double[] data, Color color) {
return histogram((String) null, data, color);
}
/**
* Adds a histogram to this canvas.
* @param id the id of the plot.
* @param data a sample set.
*/
public Histogram histogram(String id, double[] data, Color color) {
if (base.dimension != 2) {
throw new IllegalArgumentException("Histogram can be only painted in a 2D canvas.");
}
Histogram histogram = new Histogram(data);
histogram.setID(id);
histogram.setColor(color);
double[] lowerBound = {Math.min(data), 0};
double[] upperBound = {Math.max(data), 0};
double[][] freq = histogram.getHistogram();
for (int i = 0; i < freq.length; i++) {
if (freq[i][1] > upperBound[1]) {
upperBound[1] = freq[i][1];
}
}
extendBound(lowerBound, upperBound);
add(histogram);
return histogram;
}
/**
* Adds a histogram to this canvas.
* @param data a sample set.
* @param k the number of bins.
*/
public Histogram histogram(double[] data, int k, Color color) {
return histogram(null, data, k, color);
}
/**
* Adds a histogram to this canvas.
* @param id the id of the plot.
* @param data a sample set.
* @param k the number of bins.
*/
public Histogram histogram(String id, double[] data, int k, Color color) {
if (base.dimension != 2) {
throw new IllegalArgumentException("Histogram can be only painted in a 2D canvas.");
}
Histogram histogram = new Histogram(data, k);
histogram.setID(id);
histogram.setColor(color);
double[] lowerBound = {Math.min(data), 0};
double[] upperBound = {Math.max(data), 0};
double[][] freq = histogram.getHistogram();
for (int i = 0; i < freq.length; i++) {
if (freq[i][1] > upperBound[1]) {
upperBound[1] = freq[i][1];
}
}
extendBound(lowerBound, upperBound);
add(histogram);
return histogram;
}
/**
* Adds a histogram to this canvas.
* @param data a sample set.
* @param breaks an array of size k+1 giving the breakpoints between
* histogram cells. Must be in ascending order.
*/
public Histogram histogram(double[] data, double[] breaks, Color color) {
return histogram(null, data, breaks, color);
}
/**
* Adds a histogram to this canvas.
* @param id the id of the plot.
* @param data a sample set.
* @param breaks an array of size k+1 giving the breakpoints between
* histogram cells. Must be in ascending order.
*/
public Histogram histogram(String id, double[] data, double[] breaks, Color color) {
if (base.dimension != 2) {
throw new IllegalArgumentException("Histogram can be only painted in a 2D canvas.");
}
Histogram histogram = new Histogram(data, breaks);
histogram.setID(id);
histogram.setColor(color);
double[] lowerBound = {Math.min(data), 0};
double[] upperBound = {Math.max(data), 0};
double[][] freq = histogram.getHistogram();
for (int i = 0; i < freq.length; i++) {
if (freq[i][1] > upperBound[1]) {
upperBound[1] = freq[i][1];
}
}
extendBound(lowerBound, upperBound);
add(histogram);
return histogram;
}
/**
* Adds a histogram to this canvas.
* @param data a sample set.
*/
public Histogram histogram(int[] data, Color color) {
return histogram((String) null, data, color);
}
/**
* Adds a histogram to this canvas.
* @param id the id of the plot.
* @param data a sample set.
*/
public Histogram histogram(String id, int[] data, Color color) {
if (base.dimension != 2) {
throw new IllegalArgumentException("Histogram can be only painted in a 2D canvas.");
}
Histogram histogram = new Histogram(data);
histogram.setID(id);
histogram.setColor(color);
double[] lowerBound = {Math.min(data) - 0.5, 0};
double[] upperBound = {Math.max(data) + 0.5, 0};
double[][] freq = histogram.getHistogram();
for (int i = 0; i < freq.length; i++) {
if (freq[i][1] > upperBound[1]) {
upperBound[1] = freq[i][1];
}
}
extendBound(lowerBound, upperBound);
add(histogram);
return histogram;
}
/**
* Adds a histogram to this canvas.
* @param data a sample set.
* @param k the number of bins.
*/
public Histogram histogram(int[] data, int k, Color color) {
return histogram(null, data, k, color);
}
/**
* Adds a histogram to this canvas.
* @param id the id of the plot.
* @param data a sample set.
* @param k the number of bins.
*/
public Histogram histogram(String id, int[] data, int k, Color color) {
if (base.dimension != 2) {
throw new IllegalArgumentException("Histogram can be only painted in a 2D canvas.");
}
Histogram histogram = new Histogram(data, k);
histogram.setID(id);
histogram.setColor(color);
double[] lowerBound = {Math.min(data) - 0.5, 0};
double[] upperBound = {Math.max(data) + 0.5, 0};
double[][] freq = histogram.getHistogram();
for (int i = 0; i < freq.length; i++) {
if (freq[i][1] > upperBound[1]) {
upperBound[1] = freq[i][1];
}
}
extendBound(lowerBound, upperBound);
add(histogram);
return histogram;
}
/**
* Adds a histogram to this canvas.
* @param data a sample set.
* @param breaks an array of size k+1 giving the breakpoints between
* histogram cells. Must be in ascending order.
*/
public Histogram histogram(int[] data, double[] breaks, Color color) {
return histogram(null, data, breaks, color);
}
/**
* Adds a histogram to this canvas.
* @param id the id of the plot.
* @param data a sample set.
* @param breaks an array of size k+1 giving the breakpoints between
* histogram cells. Must be in ascending order.
*/
public Histogram histogram(String id, int[] data, double[] breaks, Color color) {
if (base.dimension != 2) {
throw new IllegalArgumentException("Histogram can be only painted in a 2D canvas.");
}
Histogram histogram = new Histogram(data, breaks);
histogram.setID(id);
histogram.setColor(color);
double[] lowerBound = {Math.min(data) - 0.5, 0};
double[] upperBound = {Math.max(data) + 0.5, 0};
double[][] freq = histogram.getHistogram();
for (int i = 0; i < freq.length; i++) {
if (freq[i][1] > upperBound[1]) {
upperBound[1] = freq[i][1];
}
}
extendBound(lowerBound, upperBound);
add(histogram);
return histogram;
}
/**
* Adds a 2D grid plot to the canvas.
* @param data an m x n x 2 array which are coordinates of m x n grid.
*/
public Grid grid(double[][][] data) {
if (base.dimension != 2) {
throw new IllegalArgumentException("Grid can be only painted in a 2D canvas. Try surface() for 3D grid.");
}
Grid grid = new Grid(data);
add(grid);
return grid;
}
/**
* Create a scree plot for PCA.
* @param pca principal component analysis object.
*/
public static PlotCanvas screeplot(PCA pca) {
int n = pca.getVarianceProportion().length;
double[] lowerBound = {0, 0.0};
double[] upperBound = {n + 1, 1.0};
PlotCanvas canvas = new PlotCanvas(lowerBound, upperBound, false);
canvas.setAxisLabels("Principal Component", "Proportion of Variance");
String[] labels = new String[n];
double[] x = new double[n];
double[][] data = new double[n][2];
double[][] data2 = new double[n][2];
for (int i = 0; i < n; i++) {
labels[i] = "PC" + (i + 1);
x[i] = i + 1;
data[i][0] = x[i];
data[i][1] = pca.getVarianceProportion()[i];
data2[i][0] = x[i];
data2[i][1] = pca.getCumulativeVarianceProportion()[i];
}
LinePlot plot = new LinePlot(data);
plot.setID("Variance");
plot.setColor(Color.RED);
plot.setLegend('@');
canvas.add(plot);
canvas.getAxis(0).addLabel(labels, x);
LinePlot plot2 = new LinePlot(data2);
plot2.setID("Cumulative Variance");
plot2.setColor(Color.BLUE);
plot2.setLegend('@');
canvas.add(plot2);
return canvas;
}
}
Ads help maintain this website.