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