package de.mpicbg.knime.scripting.r.node.plot; import java.awt.Dimension; import java.awt.Graphics; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import javax.swing.JPanel; import org.rosuda.REngine.Rserve.RConnection; import de.mpicbg.knime.scripting.core.ImageClipper; import de.mpicbg.knime.scripting.r.RUtils; import de.mpicbg.knime.scripting.r.plots.AbstractRPlotNodeModel; /** * A renderer which allows to display r-plots. It automatically adapts to the panel size by rescaling the figures on * resize. Figure will be recreated by mouse click * * @author Holger Brandl, Antje Janosch */ @SuppressWarnings("serial") public class RPlotCanvas extends JPanel { private BufferedImage m_baseImage; private BufferedImage m_scaledImage; private AbstractRPlotNodeModel m_plotModel; // flag to mark the process of recreating the image private boolean isReCreatingImage = false; // NOTE: Failed to trigger repainting as soon as the mouse is released after resizing // instead: rescale while resizing and use single mouse click to recreate the plot /** * constructor * @param plotModel */ public RPlotCanvas(AbstractRPlotNodeModel plotModel) { setFocusable(true); setPreferredSize(new Dimension(plotModel.getDefWidth(), plotModel.getDefHeight())); this.m_plotModel = plotModel; m_baseImage = plotModel.getImage(); // if component resized addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { if (!isVisible()) { return; } // scale image AffineTransform at = AffineTransform.getScaleInstance((double) getWidth() / m_baseImage.getWidth(null), (double) getHeight() / m_baseImage.getHeight(null)); AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR); m_scaledImage = op.filter(m_baseImage, null); } }); // single click addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent mouseEvent) { if (!isReCreatingImage) { isReCreatingImage = true; recreateImage(); isReCreatingImage = false; invalidate(); repaint(); } } }); // keys: copy => copy to clipboard addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_C && e.isMetaDown()) new ImageClipper().copyToClipboard(RPlotCanvas.this.m_baseImage); //nice idea: save image from view //if (e.getKeyCode() == KeyEvent.VK_S && e.isControlDown()) //showSaveImageDialog(); } }); } /** * runs R code again to recreate the image with the panel dimensions */ public void recreateImage() { RConnection connection = null; try { connection = RUtils.createConnection(); RUtils.loadWorkspace(m_plotModel.getWSFile(), connection); String script = m_plotModel.prepareScript(); BufferedImage image = AbstractRPlotNodeModel.createImage(connection, script, getWidth(), getHeight(), m_plotModel.getDevice()); connection.close(); m_baseImage = image; m_scaledImage = null; } catch (Exception e1) { if (connection != null) { connection.close(); } throw new RuntimeException(e1); } } /** * draw image */ public void paint(Graphics g) { g.drawImage(m_scaledImage != null ? m_scaledImage : m_baseImage, 0, 0, null); } }