This document describes a Java program that graphs functions. It includes abstract Function and GraphManager classes, and subclasses that implement specific functions. The GraphManager handles function selection and calculating graph extents. The GUI displays the functions, prompts the user to select one and input bounds, and calls GraphManager to draw the function on a Canvas between the bounds. Any errors from user input are caught and handled.
This is Function Class public abstract class Function { .pdf
1. //This is Function Class
public abstract class Function {
/**
* Calculates the value f(x) of the function at x
* @param x The x-value at which the function will be evaluated
* @return a double, the value of the function at x
*/
public abstract double fnValue(double x);
/**
* Translates f(x) to the display coordinate system. Note that Java graphics
* places (0,0) in the upper left, and (xmax, ymax) in the lower right of the
* display. A buffer of 5 pixels is kept at top and bottom.
* @param x the value at which f(x) will be evaluated
* @param d the height in pixels of the display
* @param minY the minimum f(x) to be displayed, over the extent of the x's
* @param maxY the maximum f(x) to be displayed, over the extent of the x's
* @return the value of f(x) translated to the display coordinate system
*/
public double fnValueToPlot(double x, double d, double minY, double maxY) {
double y = fnValue(x);
double yDraw = (d+5)-((y-minY)*(d/(maxY-minY)));
return yDraw;
}
/**
* Determines the display value where y=0
* @param height Height of the Canvas
* @param minY th=0e minimum height of the function within the chosen extents
* @param maxY the maximum height of the function within the chosen extents
* @return the value of y to display corresponding to y
*/
public double originToPlot(double height, double minY, double maxY) {
double yDraw = (height+5)-((0-minY)*(height/(maxY-minY)));
return yDraw;
2. }
}
//This is GraphManager Class
public class GraphManager implements GraphManagerInterface {
private double xLeft, xRight;
private double yTop = Integer.MIN_VALUE;
private double yBottom = Integer.MAX_VALUE;
private int functionChoice=-999;
Function function1, function2, function3, function4, function5, function6;
/**
* Constructor instantiates the functions specified.
*/
GraphManager() {
function1 = new Function1();
function2 = new Function2();
function3 = new Function3();
}
/**
* getFnValue calculates the value of the function requested by the user, at a specific x value.
* @param fnNum the choice of which function to evaluate
* @param x the value at which to evaluate the function
* @return the value of f(x)
*/
public double getFnValue (int fnNum, double x) {
switch(fnNum) {
case 1: return function1.fnValue(x);
case 2: return function2.fnValue(x);
case 3: return function3.fnValue(x);
default: return 0;
}
}
/**
* Sets the function choice
* @param choice an integer indexing the function desired
3. */
public void setFunctionChoice(int choice) {
functionChoice = choice;
}
/**
* Gets the function index previously selected by the user
* @return an index 1-4 corresponding to a function
*/
public int getFunctionChoice() {
return functionChoice;
}
/**
* Gets the actual function instance
* @param choice the index of the function desired
* @return an instance of a sub-class of the Function class
*/
public Function getFunction(int choice) {
switch(choice) {
case 1: return function1;
case 2: return function2;
case 3: return function3;
default: return null;
}
}
/**
* Sets the left and right extents to be considered, and computes and sets the minimum and
maximum
* values of f(x) for those left-right extents.
* @param xLeft user's desired left extent
* @param xRight user's desired right extent
* @param d width of the panel in pixels
*/
public void setExtents(double xLeft, double xRight, double d) {
4. this.xLeft = xLeft;
this.xRight = xRight;
double x0=xLeft, x1=0, y1;
Function fn = getFunction(functionChoice);
yTop = Integer.MIN_VALUE;
yBottom = Integer.MAX_VALUE;
y1 = fn.fnValue(x0);
if (y1>yTop && y1Integer.MIN_VALUE) yBottom=y1;
for (int i=1; iyTop && y1Integer.MIN_VALUE) yBottom=y1;
x0=x1;
}
System.out.println("xLeft = "+xLeft+"; xRight = "+xRight+" maxY = "+yTop+";
minY = "+yBottom+" gridWidth = "+d);
}
/**
* Gets the left extent of the function to be considered
* @return the x value of the left extent as a double
*/
public double getLeftExtent () {
return xLeft;
}
/**
* Gets the right extent of the function to be considered
* @return the x value of the right extent as a double
*/
public double getRightExtent () {
return xRight;
}
/**
* Gets the top extent of the function to be considered
* @return the maximum f(x) value that occurs from left to right
*/
public double getTopExtent () {
5. return yTop;
}
/**
* Gets the bottom extent of the function to be considered
* @return the minimum f(x) value that occurs from left to right
*/
public double getBottomExtent () {
return yBottom;
}
/**
* Overrides toString, creating a string describing the functions' formulas
*/
public String toString() {
String rtnString = "";
rtnString+="1. "+function1.toString()+" ";
rtnString+="2. "+function2.toString()+" ";
rtnString+="3. "+function3.toString()+" ";
return rtnString;
}
}
//This is Graph Manager Interface
public interface GraphManagerInterface {
/**
* getFnValue calculates the value of the function requested by the user, at a specific x value.
* @param fnNum the choice of which function to evaluate
* @param x the value at which to evaluate the function
* @return the value of f(x)
*/
public double getFnValue (int fnNum, double x);
/**
* Gets the actual function instance
* @param choice the index of the function desired
* @return an instance of a sub-class of the Function class
6. */
public Function getFunction(int choice);
/**
* Sets the left and right extents to be considered, and computes and sets the minimum and
maximum
* values of f(x) for those left-right extents. Note that these values are NOT transformed to fit
in the
* display grid, they are just the values of x and f(x)
* @param xLeft user's desired left extent
* @param xRight user's desired right extent
* @param gridWidth width of the panel in pixels
*/
public void setExtents(double xLeft, double xRight, double gridWidth);
/**
* Gets the left extent of the function to be considered
* @return the x value of the left extent as a double
*/
public double getLeftExtent ();
/**
* Gets the right extent of the function to be considered
* @return the x value of the right extent as a double
*/
public double getRightExtent ();
/**
* Gets the top extent of the function to be considered
* @return the maximum f(x) value that occurs from left to right
*/
public double getTopExtent ();
/**
* Gets the bottom extent of the function to be considered
* @return the minimum f(x) value that occurs from left to right
7. */
public double getBottomExtent ();
}
//THis is Graph Panel FX
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Paint;
/**
* GraphPanelFX creates the Canvas and draws the graph
* @author ralexander
*
*/
public class GraphPanelFX {
double gridWidth;
double gridHeight;
double xLeft, xRight, yTop, yBottom;
GraphManager graphMgr;
Canvas graphCanvas;
GraphicsContext gc;
GraphPanelFX(GraphManager graphManager, double CANVAS_WIDTH, double
CANVAS_HEIGHT) {
graphMgr = graphManager;
graphCanvas = new Canvas(CANVAS_WIDTH, CANVAS_HEIGHT);
gc = graphCanvas.getGraphicsContext2D();
}
public Canvas getGraphCanvas(GraphPanelFX graphPanel2) {
return graphCanvas;
}
/**
* drawGraph is called when the "Graph a Function" button is selected
*/
public void drawGraph() {
8. gridWidth = gc.getCanvas().getWidth();
gridHeight = gc.getCanvas().getHeight();
gc.clearRect(0,0,gridWidth,gridHeight);
System.out.println("in paintComponent(); width="+gridWidth+"; height="+gridHeight);
drawGraph(gridWidth, gridHeight-10, gc);
}
/**
* Draws line segments from left extent to right extent, pixel by pixel, transforming points
* to the coordinate system of the panel.
* @param gridWidth2 the width of the panel in pixels
* @param gridHeight the height of the panel in pixels
* @param g2 the Graphics2D instance generated by Java library classes and sent as a
argument of paintComponent
*/
public void drawGraph(double gridWidth2, double gridHeight, GraphicsContext gc) {
double x0=xLeft, y0, x1=0;
double x1Draw, x0Draw, y1Draw, y0Draw;
int functionChoice = graphMgr.getFunctionChoice();
Function fn = graphMgr.getFunction(functionChoice);
//check to make sure a function choice has been made before drawing
if(functionChoice>0) {
xLeft = graphMgr.getLeftExtent();
xRight = graphMgr.getRightExtent();
graphMgr.setExtents(xLeft, xRight, gridWidth2);
yTop = graphMgr.getTopExtent();
yBottom = graphMgr.getBottomExtent();
//draw a gray horizontal line at y=0
gc.setStroke(Paint.valueOf("Gray"));
y1Draw = fn.originToPlot(gridHeight, yBottom, yTop);
9. gc.strokeLine(0,y1Draw,gridWidth2,y1Draw);
//set the graphing color and width
gc.setStroke(Paint.valueOf("BLUE"));
gc.setLineWidth(2);
x0=xLeft;
y0 = graphMgr.getFnValue(functionChoice, x0);
//loop pixel by pixel, drawing the function between each value of x
for (int i=1; i
System.exit(0) );
//create the button to start graphing
graphFunctionButton = new Button("Graph a Function");
//graphFunctionButton.setToolTipText("Select a function and graph it");
/*
* When the button pushed was the graph function button, user is prompted to select one of
the functions,
* and is asked for the left and right limits (extents) to plot the function.
*/
graphFunctionButton.setOnAction(event -> {
graphCanvas.setVisible(false);
int choice = 0;
double left=0, right=0;
choice = askForFunction("Select one of the following functions to graph (by number):
"+graphManager.toString());
if(choice != 0) {
graphManager.setFunctionChoice(choice);
try {
left = askForExtent("Enter the left extent of the function domain");
right = askForExtent("Enter the right extent of the function domain");
graphManager.setExtents(left, right, graphCanvas.getWidth());
graphPanel.drawGraph();
} catch (NullPointerException e) {
JOptionPane.showMessageDialog(null, "No entry: exiting");
}
graphCanvas.setVisible(true);
10. }
});
//create the buttonPanel
buttonPanel = new HBox();
//buttonPanel.setPreferredSize(new Dimension(600,50));
buttonPanel.setVisible(true);
buttonPanel.setAlignment(Pos.CENTER);
HBox.setMargin(exitButton, new Insets(10,10,10,10));
HBox.setMargin(graphFunctionButton, new Insets(10,10,10,10));
buttonPanel.getChildren().addAll(exitButton, graphFunctionButton);
//buttonPanel.add(graphFunctionButton);
//add the panel to the bottom section of the main panel
mainPane.setBottom(buttonPanel);
//panel to hold the graph
graphPanel = new GraphPanelFX(graphManager, CANVAS_WIDTH, CANVAS_HEIGHT);
graphCanvas = graphPanel.getGraphCanvas(graphPanel);
graphCanvas.setVisible(true);
mainPane.setCenter(graphCanvas);
}
public GraphManager getGraphManager() {
return graphManager;
}
private int askForFunction(String str) {
boolean error=false;
int returnVal=0;
do {
try {
returnVal = Integer.parseInt(JOptionPane.showInputDialog
(null, str));
if (returnVal<1 || returnVal>5) {
error = true;
JOptionPane.showMessageDialog(null, "Choice of function must be an integer
between 1 and 5");
}
12. if (x==0.0)
return Double.MAX_VALUE;
else
return 1/x;
}
public String toString() {
return "1/x";
}
}
//This is Function2
public class Function2 extends Function{
@Override
public double fnValue(double x) {
return Math.sin(x);
}
public String toString() {
return "sin(x)";
}
}
//This is Function3
public class Function3 extends Function{
@Override
public double fnValue(double x) {
return (8*x-Math.sqrt(x))/(Math.pow(x,3) - (7*Math.pow(x,2)) + 15*x - 9);
}
public String toString () {
return "(8*x-sqrt(x))/x^3 - 7*x^2 + 15*x - 9";
}
}
13. Lab 9 – GUI, Data Manager and Data Elements Concepts tested by this program: NOTE: This
lab requires attention to detail. Inheritance Follow the following instructions carefully. Interfaces
Polymorphism Abstract classes Overriding methods Graphing with JavaFX When computing
limits, it is sometimes not obvious whether a limit exists simply by studying the function.
Graphing the function and viewing it may sometimes help. In this lab, you will use and extend an
application that graphs several functions. Refer to the Task #0 through Task #3 below for
specific instructions. Operation • When the user selects the “Graph a Function” button, the
application will display a list of functions and ask the user to select one of them to plot. • The
application will then ask the user for the left and right extents (i.e., between what values of x will
the function be plotted), from which it will calculate the maximum and minimum values of the
function that will be displayed. • Then the application will transform the function to the
coordinate system of the display and plot it. • The user may then re-graph the same or a different
function, or exit. Specifications Data Element – the data elements are each value of x, from left
extent to right extent, by increments of one pixel, and the corresponding values of f(x). The x and
f(x) transformed into the display coordinate system are an additional data element. Data
Structure – The abstract class Function holds an abstract method fnValue that is specific to each
sub-class function, and an implemented function fnValueToPlot that uses fnValue to transform
the values to the display coordinate system. The following three functions have been
implemented, each in a class that extends Function: 1. 2. 3. Each function has a toString()
method that returns a description of the function, preceded by its index. Data Manager – the
GraphManager class implements the methods of GraphManagerInterface. The GraphManager
does not use any GUI methods. The GraphManager selects the correct instance of the function
based on the integer chosen. It implements its toString method by calling the function toString
methods. It receives the user-selected extents of x (left and right) and the grid width in pixels and
computes the extents of f(x) (minimum and maximum). To do this it computes values of f(x)
over the extents of x from the left to the right extent in increments of one pixel (i goes from 1 to
gridWidth) in order to determine the maximum and minimum extent of f(x) Then the
GraphManager uses the selected function instance and calls the function’s method
fnValueToPlot inherited from the Function class to compute the value of the function within the
coordinate system of the panel. This value is returned to the GUI to be plotted. GUI Driver • At
start-up, a blank panel displays with an Exit Button and a Graph Function button. • The GUI
creates and uses a GraphManager object. • A JOptionPane is presented to prompt the user to
choose one of the functions. The GraphManager’s toString method is used to display the possible
functions. If the user enters a non-integer, the exception will be caught and displayed, and will
prompt for another entry. If the user enters an integer outside the range of 1 to 5, the user will be
prompted for another entry until the user enters a valid integer. Once the user enters a valid
14. integer, the GUI calls the GraphManager’s setFunctionChoice method. • A JOptionPane is used
to select the left and right extents of x (i.e., the minimum and maximum values of x). Exceptions
will be thrown and caught if the user enters a non-number, error messages will be printed if an
exception is thrown, and the user will be prompted for another entry until a valid number is
entered. Once the user enters a valid number, the GUI calls the GraphManager’s setExtents
method to store the left and right extents and computes the maximum and minimum extents of
the chosen function. • When the user selects the “Graph a Function” button and enters valid
choices, the GUI calls the drawGraph() method. The Pane that holds the graphs is a Canvas
class. It’s method getGraphicsContext2D() is used to get an object with which to draw on the
Canvas. For a GraphicsContext instance called gc, use the following method to draw a line
segment between the two points (x1,y1) and (x0,y0). gc.strokeLine(x0, y0, x1, y1); The draw
method iterates through each pixel from i=0 to i=gridWidth, and using the function instance
retrieved from the GraphManager, calls the method fnValueToPlot inherited from the Function
class to display the function within the coordinate system of the panel. It uses the above draw
method to plot a line segment from x0=i to x1= i+1. • The user will be allowed to repeat the
process for the same or another function. TASK #0 – Study the UML Class Diagram and the
code. Familiarize yourself with the code. Follow it from the class GraphDriverFX and its method
“start()”, to MainPaneFX, to GraphPanelFX and GraphManager. Run it and observe its behavior.
It will be incomplete when you first run it. TASK #1 – Implement the Line-drawing When you
first run the application, the graph will not be plotted, although the prompts will appear and be
handled. In the method drawGraph(double, double, GraphicsContext), add a call to
gc.strokeLine(x0Draw, y1Draw, x1Draw, y0Draw) to plot each line segment of the graph.
Experiment with all three functions provided, varying left and right extents. Notice the gray
horizontal line that is plotted at y=0 for reference. TASK #2 – Add another function Note that
function #3 is a complicated, fairly unpredictable function. Run it with several extents, and note
the singularities, where the graph goes to “infinity”. Then notice that the denominator of function
#3 is just a cubic polynomial. Implement the denominator of function #3 as function #4. Run
function #4 to estimate where the function goes to zero, which will of course make function #3
undefined. Include in your writeup an estimate of the point or points at which function #4 goes to
zero. TASK #3 – Add a fifth function Very interesting behavior happens with the function
f(x)=sin(1/x). Implement it, and run it with various extents. Include in your writeup what you
think is happening with the function. At what value of x does the limit not exist? Turn in the
following: The corrected java src directory in a compressed (zip or tar) file A document with
comments from Task #2 and #3
Solution
15. 3) To find sin(1/x) we can write a function as :
We can see that as x gets closer to zero, the function keeps wobbling (or oscillating) back and
forth between -1 and 1.
In fact, sin(1/x) wobbles between -1 and 1 an infinite number of times between 0 and any
positive x value, no matter how small.
To see this, consider that sin(x) is equal to zero at every multiple of pi, and it wobbles between 0
and 1 or -1 between each multiple. Hence, sin(1/x) will be zero at every x = 1/(pi k), where k is a
positive integer. In between each consecutive pair of these values, sin(1/x) wobbles from 0, to -1,
to 1 and back to 0.
There are an infinite number of these pairs, and they are all between 0 and 1/pi. What's more,
there are only finitely many between any positive x value and 1/pi, so there must be infinitely
many between that x and 0.
We can conclude that as x approaches 0 from the right, the function sin(1/x) does not settle down
on any value L, and so the limit as x approaches 0 from the right does not exist.
2) We will write another function and test to see where the grapho goes to infinity
tan x is undefined for values of x equal to pi/2 and -pi/2. However we need to understand the
behavior of the graph of tan x as x approches pi/2 and -pi/2. Let us look at the values of tan x for
x close to pi/2 such that x is smaller then pi/2.
We note that as x approaches pi/2 from the left (by values smaller than pi/2) tan x increases
undefinetely. We say that the graph of tan x has an asymptote at x = pi/2. It is represented by a
vertical broken red line x = pi/2 in the graph below.
We now look at the values of tan x for x close to -pi/2 such that x is larger then -pi/2.
xpi/2-0.5pi/2-0.1pi/2-0.01pi/2-0.001pi/2tan x1.810.0100.01000.0undefined