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