In this assignment, you will create a program for graphing simple functions. You will make heavy use of interfaces, both your own interfaces and ones provided by the Go standard library. There are two main components:
An Expr
(or expression) type, which represents a mathematical function of one variable, for example, x + 2
, or 3 * x * (x + 7)
. An expression can Eval
itself on a particular value of x
, returning a number, and it can String
itself (producing a string like “x + 2”).
A Graph
struct that implements the image.Image
interface. A Graph
holds an Expr
and some settings, like how big the graph should be and how its axes are arranged.
Declare an interface for expressions, Expr
. An expression should be able to String() string
itself, and Eval(x float64) float64
its value at a certain float64 value of x.
Implement each of the following expression types as you please:
Constant
, an expression representing a constant number, like 2.3 or -50. This should Eval
to itself, no matter what x
is. The String
function should just return a String version of this number. (Use fmt.Sprintf
.)
Negative
, an expression representing the negation of another expression. (You must, of course, store that other expression.) This should print out as -BLAH
, where BLAH
is whatever the subexpression prints out as. It should evaluate to the negative of whatever the other expression evaluates to.
BinaryOperation
, an expression consisting of two sub-expressions and a binary operation, either +
, -
, *
, or /
. This should print as (e1 op e2)
, where e1
and e2
are the string versions of the two sub-expressions, and op
is the operation.
Variable
, which represents a variable. (You might choose to implement this as type Variable string
, where the string is the name of the variable, like “x” or “y”. Just note that your program only supports one variable; you can call it x
or y
or whatever, but it’s all the same thing.) It evaluates to whatever x
is passed into the Eval
method; it prints out as x
(or whatever the variable name is).
Write a struct type, Graph
, that stores an expression, and some settings: a height and width in pixels, as well as a MinX, MaxX, MinY, and MaxY for the “axes” of the graph. Implement the three methods required for the Image interface:
ColorModel() color.Model
–you can just return color.GrayModel
.
Bounds() Rectangel
–use image.Rect(0, 0, w, h)
to return the width and height of your image in pixels.
At(x, y int) color.Color
–the hard one. Given an (x, y) coordinate, decide what color to paint that pixel of the image. To do this, you need to first figure out what “real value” x corresponds to. If x is the 5th pixel in a 100-pixel image, and the axis goes from MinX = 0 to MaxX = 20, then x = 5 really corresponds to x = 1, 1/20th of the way across the axis. Once you have this, you need to evaluate your Expr at this point. This will give you a y value. Calculate what pixel that y value would land on, based on the y axis’s scale. If that pixel number is the y value passed in to you, return black (color.Gray{0}
). Otherwise, return white (color.Gray{255}
).
In your main function, create a few expressions, and save them to files using code like the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | func main() { var e1 Expr = ... var e2 Expr = ... var graph1 = Graph{e1, 100, 100, ..., ..., ..., ...} var graph2 = Graph{e2, 100, 100, ..., ..., ..., ...} f1, err := os.Create("graph1.png") if err != nil { panic("Error opening graph1.png") } png.Encode(f1, graph1) f1.Close() f2, err := os.Create("graph2.png") if err != nil { panic("Error opening graph2.png") } png.Encode(f2, graph2) f2.Close() } |