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