Skip to content

Instantly share code, notes, and snippets.

@salamander2
Created July 24, 2012 00:58
Show Gist options
  • Save salamander2/3167257 to your computer and use it in GitHub Desktop.
Save salamander2/3167257 to your computer and use it in GitHub Desktop.
Science Olympics -- Function Identify program. The main program is Function.java View the JApplet at http://quarkphysics.ca/temp5661 .
package sciOlympics;
import java.awt.*;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/* This class makes a pop-up dialog box. It works as an application or JApplet.
The code for this is based on http://www.jguru.com/faq/view.jsp?EID=27423
and uses the findParentFrame method in the Function class.
*/
/* Text of About Program box
This program is free to use.
Written by Michael Harwood, 2012 for London District Science Olympics
Email: harwood@quarkphysics.ca
*/
class AboutDialog extends JDialog {
//called by Function.startAbout(), which runs when you click the About help menu.
//parameters are just the standard ones needed to make a JDialog object.
public AboutDialog(final Frame f, final String s, final boolean b) {
super(f, s, b);
this.setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
// this.setModalityType(ModalityType.APPLICATION_MODAL);
this.setTitle("About Functions");
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setSize(300, 200);
this.add(Box.createRigidArea(new Dimension(0, 10))); //vertical spacer
// ImageIcon icon = new ImageIcon("notes.png"); //hard to load an image when used as a JApplet.
// JLabel label = new JLabel(icon);
// label.setAlignmentX(0.5f);
// this.add(label);
// this.add(Box.createRigidArea(new Dimension(0,10)));
JLabel name = new JLabel(" Identify Functions v.1");
name.setFont(new Font("Serif", Font.BOLD, 15));
name.setAlignmentX(0.5f);
this.add(name);
this.add(Box.createRigidArea(new Dimension(0, 10)));
this.add(new JLabel("Written by Michael Harwood, 2012 "));
this.add(new JLabel("for London District Science Olympics "));
JLabel email = new JLabel("Email: harwood@quarkphysics.ca ");
email.setFont(new Font("Arial", Font.ITALIC, 13));
this.add(email);
this.add(Box.createRigidArea(new Dimension(0, 30)));
JButton close = new JButton("Close");
close.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dispose();
}
});
close.setAlignmentX(0.5f);
this.add(close);
this.add(Box.createRigidArea(new Dimension(0, 10)));
this.pack();
this.setVisible(true);
}
}
package sciOlympics;
import java.util.ArrayList;
import javax.swing.*;
class EqnData {
//object variables
String eqnText;
String RPN;
int x1, y1, x2, y2;
//constructors. if no scale, then default to the standard scale
EqnData(final String e1, final String e2) {
this.eqnText = e1;
this.RPN = e2;
}
EqnData(String e1, String e2, int x1, int y1, int x2, int y2) {
this.eqnText = e1;
this.RPN = e2;
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
//this is set up to work for any of the equation types
/*********************************************************************************
Name: listEquations()
Purpose: pop up a JOptionPane with correct info about equation type selected.
User chooses which equation to display (by number). Invalid input or
'Random' displays a random equation.
Called by: MainMenuListener.actionPerformed()
Parameters: "source" is the arraylist of the chosen equation type
"t" is the text string of the chosen equation type
Calls: --
**********************************************************************************/
static void listEquations(ArrayList<EqnData> source, String t) {
int maxEq = 0;
String name = null;
maxEq = source.size(); //how many equations of that type there are.
name = JOptionPane.showInputDialog(null, "Enter 1-" + maxEq + " or Random", "Choose your " + t + " functions", JOptionPane.PLAIN_MESSAGE);
// if (t.equals("Linear")) {
// maxEq = linear.size();
// name = JOptionPane.showInputDialog(null, "Enter 1-" + maxEq + " or Random","Choose your " + t + " functions",JOptionPane.PLAIN_MESSAGE);
// }
//handle CANCEL option
if(name == null){
System.out.println("Cancel pressed"); //this goes to standard output
return;// null;
}
// convert name to integer. If it fails then choose random value
int n = 0;
try {
n = Integer.parseInt(name);
if (n > maxEq) n = maxEq;
} catch (NumberFormatException e) {
n = (int)(Math.random() * maxEq) + 1;
// System.out.println("n=random (" + n + ")");
}
// System.out.println(source.get(n-1).eqnText);
// if (Function.equation == null) Function.equation = new EqnData("","",0,0,0,0);
Function.equation.eqnText = source.get(n-1).eqnText;
Function.equation.RPN = source.get(n-1).RPN;
Function.equation.x1 = source.get(n-1).x1;
Function.equation.y1 = source.get(n-1).y1;
Function.equation.x2 = source.get(n-1).x2;
Function.equation.y2 = source.get(n-1).y2;
}
/*********************************************************************************
Name: evaluateEqn()
Purpose: takes the selected equation and evaluates it for the value of x passed in
It returns the y value.
The equation is written in RPN format (which is easier to code than postfix notation).
The keys are laid out according to the HP-11C arrangement, mapped to the QWERTY keyboard.
Called by: grpanel.paintComponent(), setValue()
Calls: --
**********************************************************************************/
static double evaluateEqn(double xin) {
/* RPN registers: x,y,z,t */
/* RPN function codes:
* # = Enter (not used)
* Q = x^2 q = sqrt
* t = CHS
* W = ln w = e^x
* E = log e = 10^x log10(x) = lnx / ln 10
* R = 1/x r = y^x
* S = sin-1 s = sin
* D = cos-1 d = cos
* F = tan-1 f = tan
*/
double x = 0.0, y = 0.0, z = 0.0, t = 0.0;
char c;
String eqn = Function.equation.RPN;
//parse each character in the RPN equation
for (int i = 0; i < eqn.length(); i++) {
c = eqn.charAt(i);
// System.out.println("i=" + i);
//This section added to process digits. Needed to handle possible multi-digit numbers and decimals.
//It assumes that no number starts with a .
//The resulting number is converted to a double and stored in x (after moving the stack up)
if (c >= '0' && c <= '9') {
String nn = ""; //only a few digits, so don't bother with StringBuilder
do {
nn = nn + c;
i++;
if (i == eqn.length()) {
System.out.println("break. i=" + i);
break;
}
c = eqn.charAt(i);
// System.out.println("i=" + i);
// System.out.println("c"+i+"="+c + " nn=" + nn);
} while ((c >= '0' && c <= '9') || c == '.');
t = z; z = y; y = x; //enter
x = Double.parseDouble(nn);
// System.out.println(x);
// continue;
}
//Process every other symbol that is not a number
switch (c) {
case '.':
//ERROR. numbers can't start with a period
break;
case 'x':
t = z; z = y; y = x;
x = xin;
// System.out.println(x);
break;
case '#':
t = z; z = y; y = x;
break;
/* case '0': //this was originally used to process single digits
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
t = z;z = y;y = x; //enter
x=Character.getNumericValue(c);
// System.out.println("x="+x+" c="+c);
//FIXME NEED TO HANDLE MULTIPLE DIGITS
break;
*/
//binary operators. Stack movement handled.
case '*':
x = y*x;
y = z; z = t;
break;
case '+':
x = y+x;
y = z; z = t;
break;
case '/':
x = y/x;
y = z; z = t;
break;
case '-':
x = y-x;
y = z; z = t;
break;
case 'r':
x = Math.pow(y, x);
y = z; z = t;
break;
//unary operators. No stack movement happens.
case 't':
x = -x;
break;
case 'R':
x = 1/x;
break;
case 'Q': //x^2
x = x*x;
// System.out.println(x);
break;
case 'q':
x = Math.sqrt(x);
break;
case 'W':
x = Math.log(x);
break;
case 'w':
x = Math.exp(x);
break;
case 'E':
x = Math.log(x) / Math.log(10.0);
break;
case 'e':
x = Math.pow(10, x);
break;
//(unary) trig functions
case 's':
x = Math.sin(x);
break;
case 'd':
x = Math.cos(x);
break;
case 'f':
x = Math.tan(x);
break;
}
// System.out.println(x);
}
return x;
} //end of evaluateEqn
/*********************************************************************************
Name: loadData()
Purpose: Loads each equation into the correct arraylist (equation type)
Called by: CreateAndShowGUI
Calls: --
Data format used in EqnData constructor:
Fields: "equation","rpn formula",scale(if any)
Examples: "y=4x","x4*" <-- no custom scale
"y=x-2","x2-",-3,0,6,6 <-- grid scale goes from (-3,0) to (6,6)
**********************************************************************************/
static void loadData() {
Function.linear = new ArrayList<EqnData>();
Function.linear.add(new EqnData("y=4","4"));
Function.linear.add(new EqnData("y=x-2","x2-"));
Function.linear.add(new EqnData("y=x+2","x2+",0,0,10,10));
Function.linear.add(new EqnData("y=x+9","x9+"));
Function.linear.add(new EqnData("y=2x","x2*"));
Function.linear.add(new EqnData("y=x/3","x3/"));
Function.linear.add(new EqnData("y=-x-2","xt2-"));
Function.linear.add(new EqnData("y=x+12","12x+",0,0,20,20));
Function.linear.add(new EqnData("y=1/2x","x0.5*",0,0,20,20));
Function.quadratic = new ArrayList<EqnData>();
Function.quadratic.add(new EqnData("y=x^2","xQ"));
Function.quadratic.add(new EqnData("y=1/2x^2","xQ2/"));
Function.quadratic.add(new EqnData("y=-x^2-2x+3","xQtx2*-3+"));
Function.quadratic.add(new EqnData("y=sqrt(x)+3","xq3+"));
Function.quadratic.add(new EqnData("y=sqrt(x+2)+3","x2+q3+"));
Function.polynomial = new ArrayList<EqnData>();
Function.polynomial.add(new EqnData("y=x^3","x3r"));
Function.polynomial.add(new EqnData("y=1/2x^3","x3r2/"));
Function.rational = new ArrayList<EqnData>();
Function.rational.add(new EqnData("y=1/x","xR"));
Function.rational.add(new EqnData("y=1/x^2","xQR"));
Function.rational.add(new EqnData("y=1/(x-2)^2","x2-QR"));
Function.rational.add(new EqnData("y=2/x^2","xQR2*"));
Function.trigonometric = new ArrayList<EqnData>();
Function.trigonometric.add(new EqnData("y=sin(x)","xs"));
Function.trigonometric.add(new EqnData("y=sin(x)","xs",0,-2,10,+2));
Function.trigonometric.add(new EqnData("y=cos(x)","xd"));
Function.trigonometric.add(new EqnData("y=cos(x)","xd",0,-2,10,+2));
Function.trigonometric.add(new EqnData("y=sin(2x)","x2*s"));
Function.trigonometric.add(new EqnData("y=cos(2x)","x2*d"));
Function.trigonometric.add(new EqnData("y=2sin(x)","xs2*"));
Function.trigonometric.add(new EqnData("y=2cos(x)","xd2*"));
Function.trigonometric.add(new EqnData("y=2sin(2x)","x2*s2*"));
Function.trigonometric.add(new EqnData("y=2cos(2x)","x2*d2*"));
Function.trigonometric.add(new EqnData("y=sin(x)+1","xs1+"));
Function.trigonometric.add(new EqnData("y=cos(x)+1","xd1+"));
Function.trigonometric.add(new EqnData("y=sin(x)+1","xs1+",0,-2,10,+2));
Function.trigonometric.add(new EqnData("y=cos(x)+1","xd1+",0,-2,10,+2));
Function.trigonometric.add(new EqnData("y=-sin(x)+1","xst1+",0,-2,10,+2));
Function.trigonometric.add(new EqnData("y=-cos(x)+1","xdt1+",0,-2,10,+2));
Function.trigonometric.add(new EqnData("y=sin(x)-1","xs1-"));
Function.trigonometric.add(new EqnData("y=cos(x)-1","xd1-"));
Function.trigonometric.add(new EqnData("y=sin(1/2x)","x2/s"));
Function.trigonometric.add(new EqnData("y=cos(1/2x)","x2/d"));
Function.trigonometric.add(new EqnData("y=tan(x)","xf"));
Function.trigonometric.add(new EqnData("y=-tan(x)","xft"));
Function.trigonometric.add(new EqnData("y=1/sin(x)","xsR"));
Function.trigonometric.add(new EqnData("y=1/cos(x)","xdR"));
Function.trigonometric.add(new EqnData("y=1/tan(x)","xfR"));
Function.expon_log = new ArrayList<EqnData>();
Function.expon_log.add(new EqnData("y=e^x","xw"));
Function.expon_log.add(new EqnData("y=log(x)","xE"));
Function.expon_log.add(new EqnData("y=ln(x)","xW"));
}
}
package sciOlympics;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.ArrayList;
// Version: 1.0
/* This program is to help students identify functions
based on graphs or table of values. It was written for
the London District Science Olympics at UWO. */
/******************************* Program flow *****************************
(1) Application: main --> constructor --> createAndShowGUI()
(2) Applet: constructor (does nothing) --> init() --> createAndShowGUI()
Applet documentation says that the program should be run from init() not the constructor.
***************************************************************************/
public class Function extends JApplet
{
// Start as Application
public static void main(String[] args){
ISAPPLET = false; //important!
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Function progObject = new Function();
}});
}
//constructor
public Function() { // Java documentation: "applet should avoid calling methods in java.applet.Applet in the constructor."
if (!ISAPPLET) createAndShowGUI();
}
public void init() {
createAndShowGUI();
}
//static variables
static boolean ISAPPLET = true;
static boolean HELPON = false; //needs to be static so that HELPPANEL can access it.
static final int NVALUES = 21;
static ArrayList<EqnData> linear;
static ArrayList<EqnData> quadratic;
static ArrayList<EqnData> polynomial;
static ArrayList<EqnData> rational;
static ArrayList<EqnData> trigonometric;
static ArrayList<EqnData> expon_log;
// static EqnData equation = null;
static EqnData equation = new EqnData("","",0,0,0,0);
//instance variables - these can't be used in the GraphPanel class (in other file)
private Container content;
private GraphPanel grpanel;
private HelpPanel helppanel;
private JPanel vpanel;
private JTextField txtInput;
private JLabel[] xvalues;
private JLabel[] yvalues;
private MainMenuListener mml;
private MyMenuItem aboutItem, helpItem;
private MyMenuItem zoomItem, gridItem, valueItem;
private MyMenuItem linearItem, quadItem, polyItem, rationItem, trigItem, expItem;
private boolean VALUEON = false;
private boolean eqnCorrect = false;
/*********************************************************************************
Name: createAndShowGUI()
Purpose: This function sets the new values for the zoom.
It alternates between (-10,-10)-(10,10) and (-5,-5)-(5,5) or a custom zoom
Called by: constructor (if application) or init() (if applet)
Calls: EqnData.loadData(),
**********************************************************************************/
private void createAndShowGUI() {
//local variables
JFrame frame = null;
EqnData.loadData();
if (ISAPPLET) {
this.setSize(800, 600);
content = getContentPane();
} else {
//JFrame.setDefaultLookAndFeelDecorated(true);
frame = new JFrame("Identifying Equations");
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setSize(800, 600);
frame.setResizable(true);
frame.setLocationRelativeTo(null);
content = frame.getContentPane();
}
content.setBackground(Color.BLACK);
content.setLayout(new BorderLayout(3,3));
//make menus
JMenuBar menuBar = new JMenuBar();
mml = new MainMenuListener();
if (ISAPPLET)
setJMenuBar(menuBar);
else
frame.setJMenuBar(menuBar); //not on contentpane
//View menu
JMenu mnuView = new JMenu("View"); mnuView.setMnemonic('V');
zoomItem = new MyMenuItem("Zoom in", 'Z'); mnuView.add(zoomItem);
gridItem = new MyMenuItem("Grid", 'G'); mnuView.add(gridItem);
valueItem = new MyMenuItem("Table of Values", 'V'); mnuView.add(valueItem);
menuBar.add(mnuView);
//Equation menu
JMenu mnuEqn = new JMenu("Equation type"); mnuEqn.setMnemonic('E');
linearItem = new MyMenuItem("Linear", 'L'); mnuEqn.add(linearItem);
quadItem = new MyMenuItem("Quadratic", 'Q'); mnuEqn.add(quadItem);
polyItem = new MyMenuItem("Polynomial", 'P'); mnuEqn.add(polyItem);
rationItem = new MyMenuItem("Rational", 'R'); mnuEqn.add(rationItem);
trigItem = new MyMenuItem("Trigonometric", 'T'); mnuEqn.add(trigItem);
expItem = new MyMenuItem("Exp & Log", 'E'); mnuEqn.add(expItem);
menuBar.add(mnuEqn);
//Move help menu to right side
menuBar.add(Box.createHorizontalGlue());
//Help Menu
JMenu mnuHelp = new JMenu("Help"); mnuHelp.setMnemonic('H');
helpItem = new MyMenuItem("General Help", 'H'); mnuHelp.add(helpItem);
aboutItem = new MyMenuItem("About", 'A'); mnuHelp.add(aboutItem);
menuBar.add(mnuHelp);
grpanel = new GraphPanel(); //must be AFTER scrW and scrH are set (huh? they're set in the constructor)
content.add(grpanel, BorderLayout.CENTER);
helppanel = new HelpPanel(this);
//panel for textfield
JPanel txtpanel = new JPanel();
txtpanel.add(new JLabel("Guess equation:"));
txtInput = new JTextField(40);
txtpanel.add(txtInput);
//TextFieldListener myTFL = new TextFieldListener();
txtInput.addActionListener(new TextFieldListener());
txtInput.addKeyListener(new TextKeyListener());
content.add(txtpanel, BorderLayout.SOUTH);
//panel for values
vpanel = new JPanel();
vpanel.setBackground(new Color(120,120,120));
vpanel.setLayout(new GridLayout(NVALUES+1,2,1,1));
//header buttons
JButton xbutton = new JButton("X");
xbutton.setPreferredSize(new Dimension(80,30));
vpanel.add(xbutton);
vpanel.add(new JButton("Y"));
//set up grid of JLabels for (x,y) values
xvalues = new JLabel[NVALUES];
yvalues = new JLabel[NVALUES];
for (int i = 0; i < NVALUES; i++) {
xvalues[i] = new JLabel(" 0");
yvalues[i] = new JLabel(" 0");
xvalues[i].setOpaque(true);
yvalues[i].setOpaque(true);
xvalues[i].setHorizontalAlignment(SwingConstants.CENTER);
yvalues[i].setHorizontalAlignment(SwingConstants.CENTER);
vpanel.add(xvalues[i]);
vpanel.add(yvalues[i]);
}
content.add(vpanel, BorderLayout.EAST);
content.validate();
if (!ISAPPLET) frame.setVisible(true);
if (!VALUEON) vpanel.setVisible(false);
} // end of createAndShowGUI
/*********************************************************************************
Name: setZoom()
Purpose: This function sets the new values for the zoom.
It alternates between (-10,-10)-(10,10) and (-5,-5)-(5,5) or a custom zoom
Called by: actionPerformed() in MainMenuListener class
Calls: ---
**********************************************************************************/
private void setZoom() {
//set to default scale
if (!grpanel.ZOOMON) {
grpanel.xMin = -10.0; grpanel.xMax = 10.0; grpanel.yMin = -10.0; grpanel.yMax = 10.0;
zoomItem.setText("Zoom in");
} else {
//zoom in to 2X
grpanel.xMin = -5.0; grpanel.xMax = 5.0; grpanel.yMin = -5.0; grpanel.yMax = 5.0;
zoomItem.setText("Zoom out");
//if there is a custom zoom, zoom to that instead
if (equation.x1 != equation.x2) {
grpanel.xMin = equation.x1; grpanel.xMax = equation.x2; grpanel.yMin = equation.y1; grpanel.yMax = equation.y2;
}
}
}
/*********************************************************************************
Name: setValues()
Purpose: This function sets the values in the table of values based on the current equation.
Called by: actionPerformed() in MainMenuListener class
Calls: EqnData.evaluateEqn()
**********************************************************************************/
private void setValues() {
if (equation.RPN == "") return;
double yval;
String yStr;
for (int i = 0; i < NVALUES; i++) {
xvalues[i].setText(" " + (i - NVALUES/2));
yval = EqnData.evaluateEqn(i - NVALUES/2);
// yStr = String.format(" %.4f",yval);
yStr = new java.text.DecimalFormat("#.####").format(yval); //this is better because it doesn't have trailing zeros
yvalues[i].setText(" " + yStr);
}
}
/*********************************************************************************
Name: toggleHelp()
Purpose: Toggles the help screen on and off (alternating helppanel with grpanel)
Called by: MainMenuListener.actionPerformed(), close button in helppanel object
Calls: helppanel.repaint(), grpanel.repaint()
**********************************************************************************/
void toggleHelp() {
HELPON = !HELPON;
if(HELPON) {
content.remove(grpanel);
content.add(helppanel, BorderLayout.CENTER);
helppanel.repaint();
} else {
content.remove(helppanel);
content.add(grpanel, BorderLayout.CENTER);
grpanel.repaint();
}
content.validate();
}
/*********************************************************************************
Name: startAbout()
Purpose: Starts the "About" dialog.
Called by: actionPerformed() in MainMenuListener class
Calls: findParentFrame()
**********************************************************************************/
private void startAbout() {
Frame f = findParentFrame();
AboutDialog ad = new AboutDialog(f,"hello",false);
//f == null if it is an application - for some reason.
//f != null for applets!
}
/*********************************************************************************
Name: findParentFrame()
Purpose: When run from an applet, we must work our way up to find the parent frame
before I can show the dialog.
This parentFrame needs to be passed to the constructor.
It would be really nice to put this in the AboutDialog class, but it can't
find the parent there!
Called by: startAbout()
Calls: --
**********************************************************************************/
private Frame findParentFrame(){
Container c = this;
while(c != null){
if (c instanceof Frame) return (Frame)c;
c = c.getParent();
}
return (Frame)null;
}
// *******INNER CLASSES***********
/* This class handles all clicks on menus */
class MainMenuListener implements ActionListener {
/************************************************************
* This method does the following:
* Clicking on:
About --> pop up About dialog box
Zoom --> toggle zoom variable, call zoom function and repaint.
Grid --> toggle grid variable and repaint.
Table of Values --> toggle variable, show or hide value panel
Equation -->
* get equation type
* call function to choose and load data for that equation
* if there is a custom scale, zoom to that scale (call function and repaint)
* call function to set data in table of values
* clear and reset txtInput
Calls: startAbout, setValues, setZoom, EqnData.listEquations
**************************************************************/
public void actionPerformed(ActionEvent e) {
if (e.getSource() == helpItem) {
toggleHelp();
return;
}
if (HELPON) return; // can't click on any other menus until the help screen is hidden.
if (e.getSource() == aboutItem) {
startAbout();
// AboutDialog ad = new AboutDialog();
// Frame f = findParentFrame();
// if (f != null) {
// AboutDialog ad = new AboutDialog(f,"hello",false);
// }
// ad.setVisible(true);
}
//handle zooming
// if (e.getActionCommand().startsWith("Zoom")) {
if (e.getSource() == zoomItem) { //zoom between -10,-10,10,10 and custom zoom.
grpanel.ZOOMON = !grpanel.ZOOMON;
setZoom();
grpanel.repaint();
}
if (e.getActionCommand().equals("Grid")) {
grpanel.GRIDON = !grpanel.GRIDON;
grpanel.repaint();
}
if (e.getSource() == valueItem) {
VALUEON = !VALUEON;
if (VALUEON)
vpanel.setVisible(true);
else
vpanel.setVisible(false);
}
//all equation menus
if (e.getSource() == linearItem || e.getSource() == quadItem
|| e.getSource() == polyItem || e.getSource() == rationItem
|| e.getSource() == trigItem || e.getSource() == expItem) {
String type = e.getActionCommand();
//no need for a return value. Global equation is set. Cancel works and doesn't need to return anything.
if (type.equals("Linear")) EqnData.listEquations(linear, type);
if (type.equals("Quadratic")) EqnData.listEquations(quadratic, type);
if (type.equals("Polynomial")) EqnData.listEquations(polynomial, type);
if (type.equals("Rational")) EqnData.listEquations(rational, type);
if (type.equals("Trigonometric")) EqnData.listEquations(trigonometric, type);
if (type.equals("Exp & Log")) EqnData.listEquations(expon_log, type);
if (equation.x1 == equation.x2) {
grpanel.ZOOMON = false;
} else {
grpanel.ZOOMON = true;
}
setZoom();
setValues();
eqnCorrect = false;
// txtInput.setEnabled(true);
txtInput.setBackground(Color.WHITE);
txtInput.setText("y=");
grpanel.repaint();
}
} //end of actionPerformed()
} // end of MainMenuListener class
/* do some simple repeated things to make menu items. */
class MyMenuItem extends JMenuItem {
MyMenuItem(String s, char c) {
super(s);
if (c != '\0') this.setMnemonic(c);
this.addActionListener(mml);
}
}
/* This class is used to check each keystroke for the textbox txtInput */
class TextKeyListener implements KeyListener {
public void keyReleased (KeyEvent e) {}
/* This is neeeded to stop the backspace from being executed
keyTyped can't stop it.
*/
public void keyPressed (KeyEvent e) {
if (eqnCorrect && (int)e.getKeyChar() == 8) e.consume();
}
/*******************************************************************
* This function does the following:
* if the equation is correct (eqnCorrec) then it doesn't allow any more typing
* if the key is a backspace or <ENTER> do nothing,
just pass it on to the next event handler
* if it is a letter, make it lowercase
* check that the key is in the list of valid letters
if it is not, consume the event so that the JTextField doesn't get updated
* valid letters will be displayed in txtInput.
The list of valid letters is: numbers: 0-9, symbols: =.*+-/()^
letters in the following words: xy sin cos tan e log ln sqr
No spaces.
********************************************************************/
public void keyTyped(KeyEvent e) {
if (eqnCorrect) { //if they have correctly guessed the equation, don't allow more typing
e.consume();
e.consume(); //second consume required for? <ENTER> ?
return;
}
char key = e.getKeyChar();
if ((int)key == 8) return; //backspace
if ((int)key == 10) return; //enter
//System.out.print ("~" + (int)key);
String valid = "0123456789.+-*/^()=xyaeiocglnqrst";
if (key >='A' && key <='Z') key = Character.toLowerCase(key);
if (valid.indexOf(key) == -1) {
e.consume();
return;
}
} // end of keyTyped()
} //end of class TextKeyListener
/*****************************************************************************************
This class is used when <ENTER> is pressed in the JTextField
If there is text in txtInput it checks to see if it matches the stored equation.
If it matches, then it sets eqnCorrect to TRUE and sets the background to green.
If it does not match, then it makes the background red for 1 second.
If you get 5 wrong guesses, it will tell you the equation. This is done because it may
simply be the user having trouble formatting the equation correctly to match it.
********************************************************************************************/
class TextFieldListener implements ActionListener {
private int numTries = 0;
public void actionPerformed(ActionEvent e) {
String s = txtInput.getText();
s = s.toLowerCase();
if (s.length() == 0) return;
if (equation.eqnText.equals(s)) {
eqnCorrect = true;
numTries = 0;
txtInput.setBackground(Color.GREEN);
// txtInput.setEnabled(false); //NO: this makes the colour of the text to be gray!!! Use a boolean instead.
// System.out.println("YES match");
} else {
eqnCorrect = false;
txtInput.setBackground(Color.RED);
//start a thread to make it back to white in 1 second
new Thread() {
public void run() {
try {
Thread.sleep(1000);
txtInput.setBackground(Color.WHITE);
numTries++;
if (numTries >= 5) {
txtInput.setText("Answer is: " + equation.eqnText);
eqnCorrect = true;
numTries = 0;
txtInput.setBackground(Color.GREEN);
}
} catch (InterruptedException xxx) {}
}}.start();
// System.out.println("NO match " + numTries);
}
} // end of actionPerformed()
} //end of class TextFieldListener
}
package sciOlympics;
import java.awt.*;
import javax.swing.*;
class GraphPanel extends JPanel
{
//GLOBAL VARIABLES used in GraphPanel
//These are the co-ordinates to specify the size of the screen (x and y axes).
static double xMin = -10.0, xMax = 10.0, yMin = -10.0, yMax = 10.0;
//Used to find the size of screen (panel or canvas) in pixels.
static int scrW, scrH;
//The size of the steps used in drawing pixel by pixel.
static double xStep, yStep;
static Color COLOURGRID = new Color(160, 160, 160, 128); //the colour for the grid lines
static Color COLOURPOINT = Color.BLUE; //the colour for the points that make up the graph
//INSTANCE VARIABLES for GraphPanel
boolean GRIDON = true; //is the grid displayed?
boolean ZOOMON = false; //is the graph zoomed in?
//constructor
GraphPanel() {
super(); //is this needed?
setBackground(Color.WHITE);
initGraphics();
}
/*********************************************************************************
initGraphics()
This function sets up the screen size variables.
It needs to be recalculated if the screen is resized, if the zoom changes,
or if the panel size changes due to Table of Values being shown.
Called from: GraphPanel constructor, paintComponent
**********************************************************************************/
void initGraphics() {
scrW = super.getSize().width;
scrH = super.getSize().height;
xStep = (xMax - xMin) / scrW;
yStep = (yMax - yMin) / scrH;
}
/*********************************************************************************
Name: paintComponent()
Purpose: This function paints the graph on the GraphPanel.
Called automatically by: repaint()
repaint() is called from: toggleHelp, MainMenuListener.actionPerformed(),
Calls: initGraphics, plotLine, plotPoint, EqnData.evaluateEqn
**********************************************************************************/
public void paintComponent(Graphics g) {
super.paintComponent(g);
initGraphics();
//draw labels and draw Grid (if needed)
for (int i=(int)xMin; i < (int)xMax; i++) {
if (GRIDON) plotLine(i, yMin, i, yMax, COLOURGRID, g); // draw grid?
//labels on axes. Calculation of the location is copied from plotPoint
int px1 = (int) (scrW * (i - xMin) / (xMax - xMin));
int y1 = 0;
int py1 = scrH - (int) (scrH * (y1 - yMin) / (yMax - yMin));
g.setColor(Color.BLACK);
g.drawString(i+"", px1+1, py1-1);
}
for (int j=(int)yMin; j < (int)yMax; j++) {
if (GRIDON) plotLine(xMin,j,xMax,j,COLOURGRID,g);
int x1 = 0;
int px1 = (int) (scrW * (x1 - xMin) / (xMax - xMin));
int py1 = scrH - (int) (scrH * (j - yMin) / (yMax - yMin));
g.setColor(Color.BLACK);
g.drawString(j+"", px1+2, py1-1);
}
//draw Axes
plotLine(xMin, 0, xMax, 0, Color.BLACK, g);
plotLine(0, yMin, 0, yMax, Color.BLACK, g);
if (!Function.equation.RPN.equals("")) {
for (double d = xMin; d < xMax; d += xStep) {
plotPoint(d, EqnData.evaluateEqn(d), COLOURPOINT, g);
}
}
} //end of paintComponent
/*********************************************************************************
Name: plotPoint()
Purpose: This function draws a point at the correct pixel based on the screen size
and the scale of the graph.
Parameters: x1 and y1 are the point coordinates in scale space, not screen pixel locations.
Called from: paintComponent()
Calls: --
**********************************************************************************/
public void plotPoint(double x1, double y1, Color col, Graphics g) {
// plotLine(x,y,x,y,col,g);
int px1 = (int) (scrW * (x1 - xMin) / (xMax - xMin));
int py1 = scrH - (int) (scrH * (y1 - yMin) / (yMax - yMin));
g.setColor (col);
g.drawLine (px1, py1, px1, py1);
// System.out.println(px1 + " " + py1);
}
/*********************************************************************************
Name: plotLine()
Purpose: This function draws lines on the screen based on the screen size
and the scale of the graph. This method screws up if the points are are too large or too small:
strange lines appear on the screen. I haven't bothered to try and duplicate it
in Java 1.6. It was a problem in Java 1.2.
Parameters: x1 and y1 are the point coordinates in scale space, not screen pixel locations.
Called from: paintComponent()
Calls: --
**********************************************************************************/
public void plotLine (double x1, double y1, double x2, double y2, Color col, Graphics g) {
/* This method screws up when a number results in an int that it too big or two small so that it wraps around.
e.g. plotLine(-9.46875,-1295.2,-9.5,-1306.625,Color.red);
c.print("\n\tpx1=" + px1 + "\tpy1=" + py1 + "\tpx2=" + px2 + "\tpy2=" + py2);
This results in py1 = 32630 py2 = 32915, px1 = 17, px2 = 16. A vertical line is drawn down the screen!!!
It looks like the real drawLine converts everything to a short, but I can't find any reference to this in the documentation.
*/
int px1 = (int) (scrW * (x1 - xMin) / (xMax - xMin));
int px2 = (int) (scrW * (x2 - xMin) / (xMax - xMin));
int py1 = scrH - (int) (scrH * (y1 - yMin) / (yMax - yMin));
int py2 = scrH - (int) (scrH * (y2 - yMin) / (yMax - yMin));
g.setColor (col);
//Test for integer wrapping.
/*if (px1 > Short.MAX_VALUE)
return;
if (px2 > Short.MAX_VALUE)
return;
if (py1 > Short.MAX_VALUE)
return;
if (py2 > Short.MAX_VALUE)
return; */
g.drawLine (px1, py1, px2, py2);
return;
} //end of PlotLine
} //end of class
package sciOlympics;
import java.awt.*;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
class HelpPanel extends JPanel
{
private Function objFunct;
/*********************************************************************************
Name: HelpPanel constructor
Purpose: Sets up the help screen. It makes a JEditorPane and puts it into a
JScrollPane to add the scrollbar. It also adds a CLOSE button at the bottom.
Parameters: requires the Function object created when the program starts.
This is needed so that Function.toggleHelp() can be called when it exits.
Called by: createAndShowGUI
Calls: toggleHelp
**********************************************************************************/
HelpPanel(final Function objFunct) {
super();
this.objFunct = objFunct;
String text = loadText();
setBackground(new Color(0, 200, 180));
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
JEditorPane textArea = new JEditorPane("text/html",text);
JScrollPane scrollPane = new JScrollPane(textArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
textArea.setBackground(new Color(230, 230, 255));
this.add(scrollPane);
this.add(Box.createRigidArea(new Dimension(0, 5)));
JButton btnClose = new JButton("EXIT HELP");
btnClose.setAlignmentX(Component.CENTER_ALIGNMENT);
btnClose.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
objFunct.toggleHelp();
}
});
this.add(btnClose);
this.add(Box.createRigidArea(new Dimension(0, 5)));
this.validate();
}
//Load the text of the help page in HTML format.
private String loadText() {
String t = "<center><h1>How to use this program</h1></center>"
+ "This program will see whether you can identify polynomials, rationals, "
+ "trig functions, exponentials, and logs. You choose the type of equation "
+ "and then can choose a specific function to display. The available numbers "
+ "are shown in the option box. If you type anything other than the available "
+ "numbers the computer will pick a random function for you."
+ "<ul><li>You can toggle the <b>grid</b> off and on and also toggle between two levels of <b>zoom</b>.</li>"
+ "<li> Viewing the <b>\"Table of values\"</b> will display (x,y) for -10&nbsp;<=&nbsp;x&nbsp;<=&nbsp;+10.</li>"
+ "<li> The program can be run as an application or as an applet in a webpage. "
+ "If it is an application you can resize the window to make the graph bigger.</li>"
+ "</ul>"
+ "<h2>Guessing the equation</h2>"
+ "To identify the equation, type the formula into the textbox at the bottom."
+ "The formula must be entered with specific syntax or it will be incorrect. Please read the following:"
+ "<ul><li>Formulas must be written as \"y= \".</li>"
+ "<li>The the following symbols are used for mathematical operations:<br>"
+ "<ul> ( ) = brackets<br>"
+ " + - = addition and subtraction<br>"
+ " / = division.<br>"
+ " <i>Multiplication is understood by writing symbols adjacent to each other<br>"
+ " e.g. y = 3x y= 2sin(3x) not: y = 3*x</i><br>"
+ " ^ = exponentiation<br>"
+ " e^x = exponential function<br>"
+ " sin( ), cos( ), tan( ) = trigonometric functions (in radians)<br>"
+ " log( ), ln( ) = logarithms, base 10 and base e<br>"
+ " sqrt( ) = square root<br>"
+ "</ul></li>"
+ "<li>Polynomials must be written with the highest order term first: y=ax^4 + bx^3 + cx^2 ..."
+ "<pre>Incorrect: y = 2-x Correct: y = -x+2</pre></li>"
+ "<li>Fractional coeffiecients must be written as fractions, not decimals.<br>"
+ "<pre>Incorrect: y = 0.5x^2 Correct: y = 1/2x^2</pre>"
+ "<i>This is to be consistent with things like y=2/3x which cannot be represented as an exact decimal.</i></li>"
+ "<li>Coefficients must be written before variables<br>"
+ "<pre>Incorrect: y = x/2 Correct: y = 1/2x</pre>"
+ "<i>which means y = (1/2) x . If you wanted y =1/(2x) you would have to use brackets.</i></li>"
+ "<li>The BEDMAS rule for order of operations is followed, so adding ( ) when not needed"
+ " will render the formula incorrect.<br>"
+ "<pre>Incorrect: y = 1/(x^2) Correct: y = 1/x^2</pre>"
+ "<i>These equations are the same, but since exponents have a higher precedence than division, brackets are not needed.</i><br>"
+ "As is expected, y = 1/x-2 is not the same as y = 1/(x-2) Both of these formulas are correct, but they are different equations."
+ "<pre>Incorrect: y = x^3/2 Correct: y = x^(3/2) or y = 1/2x^3</pre>"
+ "<i>If you want y=x^(3/2) then brackets are required because of operator precedence (BEDMAS).<br>"
+ "If you want y = (x^3)/2 you have to write the coefficient first.</i></li>"
+ "<li>When there are two operators with the same precedence, evaluation proceeds left to right.<br>"
+ "Thus y = 1/2x means y= 1/2 * x i.e. y = 0.5x and not y=1/(2*x)</li>"
+ "</ul>"
+ " If you get the formula correct, the textbar will turn green. You will not be able to edit the formula until you choose a new equation."
+ " If your formula is incorrect the textbar will flash red for a second."
+ "<p>"
+ " After 5 incorrect guesses, the formula will be displayed. This is done because the program "
+ " does not parse the equation you type in and there may be multiple ways of writing the same correct equation."
+ " The program just matches what you type with the stored equation. If it is not an exact match, it assumes that it is incorrect"
+ " when, in fact, your equation may be correct, just in an incorrect form.<p>"
+ "If you want a program to graph equations, please download <b>Graphmatica</b>.";
return t;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment