/* * Copyright (C) 2014 Apocalypse Laboratories. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA */ package net.apocalypselabs.symat; import java.awt.Color; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileNameExtensionFilter; import org.matheclipse.core.eval.EvalUtilities; import org.matheclipse.parser.client.math.MathException; /** * * @author Skylar */ public class Graph extends javax.swing.JInternalFrame { private final JFileChooser fc = new JFileChooser(); private BufferedImage gpnl; private Graphics gg; // Main graphics object private boolean standalone = true; private boolean customName = false; private double xtimes = 15; private double ytimes = 15; private double scale = 1; /** * Creates new form Graph */ public Graph() { init(); } public Graph(boolean isInternal) { init(); standalone = !isInternal; } private void init() { initComponents(); gpnl = new BufferedImage(300, 300, BufferedImage.TYPE_INT_ARGB); FileFilter filter = new FileNameExtensionFilter("PNG image (.png)", "png"); fc.setFileFilter(filter); fc.addChoosableFileFilter(filter); filter = new FileNameExtensionFilter("JPEG image (.jpg)", "jpg"); fc.addChoosableFileFilter(filter); } @Override public void doDefaultCloseAction() { if (standalone) { dispose(); } else { hide(); } } public void forceClose() { dispose(); } /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always * regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // //GEN-BEGIN:initComponents private void initComponents() { inBox = new javax.swing.JTextField(); jLabel1 = new javax.swing.JLabel(); jButton1 = new javax.swing.JButton(); gLbl = new javax.swing.JLabel(); jMenuBar1 = new javax.swing.JMenuBar(); jMenu1 = new javax.swing.JMenu(); jMenuItem2 = new javax.swing.JMenuItem(); jMenu2 = new javax.swing.JMenu(); jMenuItem1 = new javax.swing.JMenuItem(); jMenuItem6 = new javax.swing.JMenuItem(); jMenuItem3 = new javax.swing.JMenuItem(); scaleLbl = new javax.swing.JMenu(); setClosable(true); setDefaultCloseOperation(javax.swing.WindowConstants.HIDE_ON_CLOSE); setIconifiable(true); setTitle("Graph"); setToolTipText(""); setFrameIcon(new javax.swing.ImageIcon(getClass().getResource("/net/apocalypselabs/symat/icons/graph.png"))); // NOI18N addMouseMotionListener(new java.awt.event.MouseMotionAdapter() { public void mouseDragged(java.awt.event.MouseEvent evt) { formMouseDragged(evt); } public void mouseMoved(java.awt.event.MouseEvent evt) { formMouseMoved(evt); } }); addComponentListener(new java.awt.event.ComponentAdapter() { public void componentMoved(java.awt.event.ComponentEvent evt) { formComponentMoved(evt); } public void componentShown(java.awt.event.ComponentEvent evt) { formComponentShown(evt); } }); inBox.addKeyListener(new java.awt.event.KeyAdapter() { public void keyTyped(java.awt.event.KeyEvent evt) { inBoxKeyTyped(evt); } }); jLabel1.setText("f(x)="); jButton1.setText(">>"); jButton1.setToolTipText(""); jButton1.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jButton1ActionPerformed(evt); } }); gLbl.setBackground(new java.awt.Color(255, 255, 255)); gLbl.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); gLbl.setMaximumSize(new java.awt.Dimension(300, 300)); gLbl.setMinimumSize(new java.awt.Dimension(300, 300)); gLbl.setOpaque(true); gLbl.setPreferredSize(new java.awt.Dimension(300, 300)); jMenu1.setText("File"); jMenuItem2.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.CTRL_MASK)); jMenuItem2.setText("Export graph..."); jMenuItem2.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jMenuItem2ActionPerformed(evt); } }); jMenu1.add(jMenuItem2); jMenuBar1.add(jMenu1); jMenu2.setText("Edit"); jMenuItem1.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_W, java.awt.event.InputEvent.CTRL_MASK)); jMenuItem1.setText("Clear Graph"); jMenuItem1.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jMenuItem1ActionPerformed(evt); } }); jMenu2.add(jMenuItem1); jMenuItem6.setText("Scale..."); jMenuItem6.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jMenuItem6ActionPerformed(evt); } }); jMenu2.add(jMenuItem6); jMenuItem3.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_T, java.awt.event.InputEvent.CTRL_MASK)); jMenuItem3.setText("Set Title..."); jMenuItem3.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jMenuItem3ActionPerformed(evt); } }); jMenu2.add(jMenuItem3); jMenuBar1.add(jMenu2); scaleLbl.setForeground(java.awt.Color.blue); scaleLbl.setText("Scale: 1 to 1"); scaleLbl.setEnabled(false); jMenuBar1.add(scaleLbl); setJMenuBar(jMenuBar1); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(jLabel1) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(inBox, javax.swing.GroupLayout.PREFERRED_SIZE, 215, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jButton1)) .addComponent(gLbl, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(gLbl, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(inBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jLabel1) .addComponent(jButton1)) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); pack(); }// //GEN-END:initComponents private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed CodeRunner cr = new CodeRunner(); String formula = inBox.getText().replaceAll("x", "\\$x"); EvalUtilities solver = new EvalUtilities(); double xx, yy, xo, yo, x; xo = 0; yo = 0; gg = gpnl.getGraphics(); drawAxes(); gg.setColor(Color.BLUE); for (x = (-10 * scale); x <= (10 * scale); x += (10 * scale * .01)) { try { cr.setVar("x", x); yy = (-(Double.parseDouble(solver.evaluate("$x=" + x + ";N[" + formula + "]").toString())) * ytimes) + (gpnl.getHeight() / 2); //System.err.println(solver.evaluate("$x="+x+";N["+formula+"]").toString()); xx = (x * xtimes) + (gpnl.getWidth() / 2); //gg.drawOval(xx-1, yy-1, 2, 2); if (x != (-10 * scale)) { gg.drawLine((int) xo, (int) yo, (int) xx, (int) yy); } xo = xx; yo = yy; } catch (MathException | NumberFormatException ex) { } } gg.translate(gpnl.getWidth() / 2, gpnl.getHeight() / 2); dispGraph(); if (!customName) { setTitle("Graph | " + inBox.getText()); } }//GEN-LAST:event_jButton1ActionPerformed private void formComponentShown(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentShown gg = gpnl.getGraphics(); drawAxes(); gg.translate(gpnl.getWidth() / 2, gpnl.getHeight() / 2); dispGraph(); }//GEN-LAST:event_formComponentShown /** * Set the zoom level. The larger the int, the more zoomed it is. * * @param zoomLevel Level to zoom. 0 is default (10x10). */ public void setZoom(int zoomLevel) { switch (zoomLevel) { case -1: xtimes = 7.5; ytimes = 7.5; scale = 2; case 1: xtimes = 30; ytimes = 30; scale = .5; break; case 2: xtimes = 60; ytimes = 60; scale = .25; break; case 3: xtimes = 120; ytimes = 120; scale = .125; break; default: xtimes = 15; ytimes = 15; scale = 1; } scaleLbl.setText("Scale: 1 to " + scale); clearDraw(); } private void dispGraph() { gLbl.setIcon(new ImageIcon(gpnl)); } public void drawDot(double x, double y) { gg.setColor(Color.RED); gg.drawOval((int) (x * xtimes - 2), (int) (y * -ytimes - 2), 4, 4); dispGraph(); } private void inBoxKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_inBoxKeyTyped if (evt.getKeyChar() == '\n') { jButton1ActionPerformed(null); } }//GEN-LAST:event_inBoxKeyTyped private void formComponentMoved(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentMoved }//GEN-LAST:event_formComponentMoved private void formMouseMoved(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseMoved // TODO add your handling code here: }//GEN-LAST:event_formMouseMoved private void formMouseDragged(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseDragged }//GEN-LAST:event_formMouseDragged private void jMenuItem1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem1ActionPerformed gpnl = new BufferedImage(300, 300, BufferedImage.TYPE_INT_ARGB); gg = gpnl.getGraphics(); drawAxes(); gg.translate(gpnl.getWidth() / 2, gpnl.getHeight() / 2); dispGraph(); setTitle("Graph"); }//GEN-LAST:event_jMenuItem1ActionPerformed private void jMenuItem2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem2ActionPerformed int r = fc.showSaveDialog(this); if (r == JFileChooser.APPROVE_OPTION) { try { File file = new File(addSaveExt(fc.getSelectedFile().toString())); if (file.toString().endsWith("png")) { ImageIO.write(gpnl, "png", file); } else if (file.toString().endsWith("jpg")) { ImageIO.write(gpnl, "jpg", file); } } catch (IOException ex) { JOptionPane.showInternalMessageDialog(this, "Error: Cannot save file: " + ex.getMessage()); } } }//GEN-LAST:event_jMenuItem2ActionPerformed private void jMenuItem3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem3ActionPerformed String wintitle = JOptionPane.showInternalInputDialog(this, "New window title:", "Rename", JOptionPane.QUESTION_MESSAGE); if (wintitle != null && !wintitle.equals("")) { setWindowTitle(wintitle); } }//GEN-LAST:event_jMenuItem3ActionPerformed private void jMenuItem6ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem6ActionPerformed try { int size = Integer.parseInt(JOptionPane.showInternalInputDialog(this, "Graph scale, -1 to 3 (-1 least detail, 3 most detail):", "Scale", JOptionPane.QUESTION_MESSAGE)); setZoom(size); } catch (Exception ex) { } }//GEN-LAST:event_jMenuItem6ActionPerformed /** * Graph the given function. Same as typing into input box and pressing * Enter. * * @param f f(x) = f */ public void graphFunction(String f) { inBox.setText(f); jButton1ActionPerformed(null); } private String addSaveExt(String path) { if (!path.matches(".*\\.(png|jpg)")) { path += ".png"; } return path; } /** * Set the wintitle of this graph window. * * @param t The wintitle. */ public void setWindowTitle(String t) { setTitle(t); customName = true; } /** * Get the wintitle of the window. * * @return the wintitle, stupid! */ public String getWindowTitle() { return getTitle(); } /** * Erase the graph. */ public void clearDraw() { jMenuItem1ActionPerformed(null); } private void drawAxes() { gg.setColor(Color.GRAY); gg.drawLine(150, 0, 150, 300); gg.drawLine(0, 150, 300, 150); // Draw points for (int i = 0; i <= 315; i += 15) { gg.drawOval(150 - 1, i - 1, 2, 2); gg.drawOval(i - 1, 150 - 1, 2, 2); } gg.setColor(Color.BLUE); } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JLabel gLbl; private javax.swing.JTextField inBox; private javax.swing.JButton jButton1; private javax.swing.JLabel jLabel1; private javax.swing.JMenu jMenu1; private javax.swing.JMenu jMenu2; private javax.swing.JMenuBar jMenuBar1; private javax.swing.JMenuItem jMenuItem1; private javax.swing.JMenuItem jMenuItem2; private javax.swing.JMenuItem jMenuItem3; private javax.swing.JMenuItem jMenuItem6; private javax.swing.JMenu scaleLbl; // End of variables declaration//GEN-END:variables }