001package hw4;
002
003import java.awt.Canvas;
004import java.awt.Color;
005import java.awt.Graphics;
006
007/**
008 * <b>PolyGraph</b> is part of the graphical calculator that utilizes all of
009 * the other classes in package hw4. PolyGraph implements the graphing
010 * component.
011 *
012 * @author Felix Klock, Andreas Hofmann
013 * @version 1.0
014 */
015public final class PolyGraph extends Canvas {
016  Color col[] = new Color[4];
017
018  Color zeroline = new Color(0xe0, 0xe0, 0xff);
019
020  CalculatorFrame calcFrame;
021
022  // Serializable classes are supposed to have this
023  private static final long serialVersionUID = 24L;
024
025  public PolyGraph(CalculatorFrame cf) {
026    super();
027
028    calcFrame = cf;
029
030    col[0] = new Color(0xa0, 0, 0);
031    col[1] = new Color(0, 0, 0xFF);
032    col[2] = new Color(0, 0x80, 0);
033    col[3] = new Color(0, 0, 0);
034  }
035
036  @Override
037  public void update(Graphics g) {
038    paint(g);
039  }
040
041  @Override
042  public void paint(Graphics g) {
043    int w = getSize().width;
044    int h = getSize().height;
045
046    g.setColor(Color.white);
047    g.fillRect(0, 0, w, h);
048
049    /*
050     * g.setColor(Color.red); String msg= "Click to graph selected
051     * variable"; int wid= getFontMetrics(getFont()).stringWidth(msg);
052     * g.drawString(msg, (w-wid)/2, h/2);
053     */
054
055    // Draw axes and data
056    int numIncrements = 100;
057
058    float xMin = Float.parseFloat(calcFrame.jTextField1.getText());
059    float xMax = Float.parseFloat(calcFrame.jTextField2.getText());
060    float yMin = 0;
061    float yMax = 0;
062    float[] xValBuffer1 = null;
063    float[] yValBuffer1 = null;
064    float[] xValBuffer2 = null;
065    float[] yValBuffer2 = null;
066    float[] xValBuffer3 = null;
067    float[] yValBuffer3 = null;
068    float[] xValBuffer4 = null;
069    float[] yValBuffer4 = null;
070    float[] yExtrema;
071    String msg;
072
073    if (xMin >= xMax) {
074      g.setColor(Color.red);
075      msg = "Xmin must be greater than Xmax";
076      int wid = getFontMetrics(getFont()).stringWidth(msg);
077      g.drawString(msg, (w - wid) / 2, h / 2);
078      return;
079    }
080
081    // Get RatPoly
082    RatPoly currentRatPoly;
083
084    // Now fill in new information base on what's in stack.
085    // Note that size of stack must be checked.
086    if ((calcFrame.stack != null) && (calcFrame.stack.size() > 0)) {
087      currentRatPoly = calcFrame.stack.getNthFromTop(0);
088      xValBuffer1 = new float[numIncrements];
089      yValBuffer1 = new float[numIncrements];
090      yExtrema = new float[2];
091    } else {
092      g.setColor(Color.red);
093      msg = "Stack is empty";
094      int wid = getFontMetrics(getFont()).stringWidth(msg);
095      g.drawString(msg, (w - wid) / 2, h / 2);
096      return;
097    }
098
099    updatePlotBuffer(xMin, xMax, numIncrements, xValBuffer1, yValBuffer1,
100        yExtrema, currentRatPoly);
101
102    yMin = yExtrema[0];
103    yMax = yExtrema[1];
104
105    if (calcFrame.stack.size() > 1) {
106      currentRatPoly = calcFrame.stack.getNthFromTop(1);
107      xValBuffer2 = new float[numIncrements];
108      yValBuffer2 = new float[numIncrements];
109
110      updatePlotBuffer(xMin, xMax, numIncrements, xValBuffer2,
111          yValBuffer2, yExtrema, currentRatPoly);
112
113      if (yExtrema[0] < yMin) {
114        yMin = yExtrema[0];
115      }
116
117      if (yExtrema[1] > yMax) {
118        yMax = yExtrema[1];
119      }
120    }
121
122    if (calcFrame.stack.size() > 2) {
123      currentRatPoly = calcFrame.stack.getNthFromTop(2);
124      xValBuffer3 = new float[numIncrements];
125      yValBuffer3 = new float[numIncrements];
126
127      updatePlotBuffer(xMin, xMax, numIncrements, xValBuffer3,
128          yValBuffer3, yExtrema, currentRatPoly);
129
130      if (yExtrema[0] < yMin) {
131        yMin = yExtrema[0];
132      }
133
134      if (yExtrema[1] > yMax) {
135        yMax = yExtrema[1];
136      }
137    }
138
139    if (calcFrame.stack.size() > 3) {
140      currentRatPoly = calcFrame.stack.getNthFromTop(3);
141      xValBuffer4 = new float[numIncrements];
142      yValBuffer4 = new float[numIncrements];
143
144      updatePlotBuffer(xMin, xMax, numIncrements, xValBuffer4,
145          yValBuffer4, yExtrema, currentRatPoly);
146
147      if (yExtrema[0] < yMin) {
148        yMin = yExtrema[0];
149      }
150
151      if (yExtrema[1] > yMax) {
152        yMax = yExtrema[1];
153      }
154    }
155
156    // At this point, min and max have been computed, and buffers
157    // are full. Draw axes, then draw graph line.
158    int bord = 32;
159    g.setColor(Color.black);
160    g.drawLine(bord, h - bord, w - bord, h - bord); // horizontal axis
161    g.drawLine(bord, bord, bord, h - bord); // vertical axis
162
163    float gw = w - (2 * bord); // width of graph area inside axes
164    float gh = h - (2 * bord); // height of graph area inside axes
165
166    // Draw axis labels.
167    msg = Float.toString(xMin);
168    g.drawString(msg, bord, h - 8);
169
170    msg = Float.toString(xMax);
171    g.drawString(msg, w - bord, h - 8);
172
173    msg = Float.toString(yMin);
174    g.drawString(msg, 8, h - bord);
175
176    msg = Float.toString(yMax);
177    g.drawString(msg, 8, bord);
178
179    // Draw graph line.
180    g.setColor(Color.red);
181    drawPlot(xMin, xMax, yMin, yMax, xValBuffer1, yValBuffer1, gw, gh,
182        bord, numIncrements, h, g);
183
184    g.setColor(Color.blue);
185    if (calcFrame.stack.size() > 1) {
186      assert xValBuffer2 != null
187          : "@SuppressWarnings(nullness): dependent:  non-null if calcFrame.stack.size() > 1";
188      assert yValBuffer2 != null
189          : "@SuppressWarnings(nullness): dependent:  non-null if calcFrame.stack.size() > 1";
190      drawPlot(xMin, xMax, yMin, yMax, xValBuffer2, yValBuffer2, gw, gh,
191          bord, numIncrements, h, g);
192    }
193
194    g.setColor(Color.green);
195    if (calcFrame.stack.size() > 2) {
196      assert xValBuffer3 != null
197          : "@SuppressWarnings(nullness): dependent:  non-null if calcFrame.stack.size() > 2";
198      assert yValBuffer3 != null
199          : "@SuppressWarnings(nullness): dependent:  non-null if calcFrame.stack.size() > 2";
200      drawPlot(xMin, xMax, yMin, yMax, xValBuffer3, yValBuffer3, gw, gh,
201          bord, numIncrements, h, g);
202    }
203
204    g.setColor(Color.orange);
205    if (calcFrame.stack.size() > 3) {
206      assert xValBuffer4 != null
207          : "@SuppressWarnings(nullness): dependent:  non-null if calcFrame.stack.size() > 3";
208      assert yValBuffer4 != null
209          : "@SuppressWarnings(nullness): dependent:  non-null if calcFrame.stack.size() > 3";
210      drawPlot(xMin, xMax, yMin, yMax, xValBuffer4, yValBuffer4, gw, gh,
211          bord, numIncrements, h, g);
212    }
213
214    // Consider abstracting this better!
215  }
216
217  public void updatePlotBuffer(float xMin, float xMax, int numIncrements,
218      float xValBuffer[], float yValBuffer[], float yExtrema[],
219      RatPoly currentRatPoly) {
220    float delta = (xMax - xMin) / numIncrements;
221    float currentX = xMin;
222    boolean firstTime = true;
223    int i;
224    float yVal = 0;
225    float yMin = 0;
226    float yMax = 0;
227
228    for (i = 0; i < numIncrements; ++i) {
229      if (currentX < xMax) {
230        xValBuffer[i] = currentX;
231        yVal = (float) currentRatPoly.eval(currentX);
232        yValBuffer[i] = yVal;
233
234        if (firstTime) {
235          firstTime = false;
236          yMin = yVal;
237          yMax = yVal;
238        } else {
239          if (yVal < yMin) {
240            yMin = yVal;
241          }
242
243          if (yVal > yMax) {
244            yMax = yVal;
245          }
246        }
247
248        currentX += delta;
249      } else {
250        xValBuffer[i] = xValBuffer[i - 1];
251        yValBuffer[i] = yValBuffer[i - 1];
252      }
253    }
254
255    yExtrema[0] = yMin;
256    yExtrema[1] = yMax;
257  }
258
259  public void drawPlot(float xMin, float xMax, float yMin, float yMax,
260      float xValBuffer[], float yValBuffer[], float gw, float gh,
261      int bord, int numIncrements, int h, Graphics g) {
262    float xVal = 0;
263    float yVal = 0;
264    float previousX = 0;
265    float previousY = 0;
266    boolean firstTime = true;
267    float xRange = xMax - xMin;
268    float yRange = yMax - yMin;
269    int xPrevScaled = 0;
270    int yPrevScaled = 0;
271    int xScaled = 0;
272    int yScaled = 0;
273    int i;
274    for (i = 0; i < numIncrements; ++i) {
275      if (firstTime) {
276        firstTime = false;
277        xVal = xValBuffer[i];
278        yVal = yValBuffer[i];
279        previousX = xVal;
280        previousY = yVal;
281      } else {
282        previousX = xVal;
283        previousY = yVal;
284        xVal = xValBuffer[i];
285        yVal = yValBuffer[i];
286      }
287
288      // Now draw a line from previous to current.
289      xPrevScaled = Math.round(((previousX - xMin) * gw) / xRange);
290      yPrevScaled = Math.round(((previousY - yMin) * gh) / yRange);
291      xScaled = Math.round(((xVal - xMin) * gw) / xRange);
292      yScaled = Math.round(((yVal - yMin) * gh) / yRange);
293
294      g.drawLine(bord + xPrevScaled, h - bord - yPrevScaled, bord
295          + xScaled, h - bord - yScaled);
296    }
297  }
298
299}