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