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    
092            // Draw axes and data
093            int numIncrements = 100;
094    
095            float xMin = Float.parseFloat(calcFrame.jTextField1.getText());
096            float xMax = Float.parseFloat(calcFrame.jTextField2.getText());
097            float yMin = 0;
098            float yMax = 0;
099            float[] xValBuffer1 = null;
100            float[] yValBuffer1 = null;
101            float[] xValBuffer2 = null;
102            float[] yValBuffer2 = null;
103            float[] xValBuffer3 = null;
104            float[] yValBuffer3 = null;
105            float[] xValBuffer4 = null;
106            float[] yValBuffer4 = null;
107            float[] yExtrema;
108            String msg;
109    
110            if (xMin >= xMax) {
111                g.setColor(Color.red);
112                msg = "Xmin must be greater than Xmax";
113                int wid = getFontMetrics(getFont()).stringWidth(msg);
114                g.drawString(msg, (w - wid) / 2, h / 2);
115                return;
116            }
117    
118            // Get RatPoly
119            RatPoly currentRatPoly;
120    
121            // Now fill in new information base on what's in stack.
122            // Note that size of stack must be checked.
123            if ((calcFrame.stack != null) && (calcFrame.stack.size() > 0)) {
124                currentRatPoly = calcFrame.stack.getNthFromTop(0);
125                xValBuffer1 = new float[numIncrements];
126                yValBuffer1 = new float[numIncrements];
127                yExtrema = new float[2];
128            } else {
129                g.setColor(Color.red);
130                msg = "Stack is empty";
131                int wid = getFontMetrics(getFont()).stringWidth(msg);
132                g.drawString(msg, (w - wid) / 2, h / 2);
133                return;
134            }
135    
136            updatePlotBuffer(xMin, xMax, numIncrements, xValBuffer1, yValBuffer1,
137                    yExtrema, currentRatPoly);
138    
139            yMin = yExtrema[0];
140            yMax = yExtrema[1];
141    
142            if (calcFrame.stack.size() > 1) {
143                currentRatPoly = calcFrame.stack.getNthFromTop(1);
144                xValBuffer2 = new float[numIncrements];
145                yValBuffer2 = new float[numIncrements];
146    
147                updatePlotBuffer(xMin, xMax, numIncrements, xValBuffer2,
148                        yValBuffer2, yExtrema, currentRatPoly);
149    
150                if (yExtrema[0] < yMin)
151                    yMin = yExtrema[0];
152    
153                if (yExtrema[1] > yMax)
154                    yMax = yExtrema[1];
155            }
156    
157            if (calcFrame.stack.size() > 2) {
158                currentRatPoly = calcFrame.stack.getNthFromTop(2);
159                xValBuffer3 = new float[numIncrements];
160                yValBuffer3 = new float[numIncrements];
161    
162                updatePlotBuffer(xMin, xMax, numIncrements, xValBuffer3,
163                        yValBuffer3, yExtrema, currentRatPoly);
164    
165                if (yExtrema[0] < yMin)
166                    yMin = yExtrema[0];
167    
168                if (yExtrema[1] > yMax)
169                    yMax = yExtrema[1];
170            }
171    
172            if (calcFrame.stack.size() > 3) {
173                currentRatPoly = calcFrame.stack.getNthFromTop(3);
174                xValBuffer4 = new float[numIncrements];
175                yValBuffer4 = new float[numIncrements];
176    
177                updatePlotBuffer(xMin, xMax, numIncrements, xValBuffer4,
178                        yValBuffer4, yExtrema, currentRatPoly);
179    
180                if (yExtrema[0] < yMin)
181                    yMin = yExtrema[0];
182    
183                if (yExtrema[1] > yMax)
184                    yMax = yExtrema[1];
185            }
186    
187            // At this point, min and max have been computed, and buffers
188            // are full. Draw axes, then draw graph line.
189            int bord = 32;
190            g.setColor(Color.black);
191            g.drawLine(bord, h - bord, w - bord, h - bord); // horizontal axis
192            g.drawLine(bord, bord, bord, h - bord); // vertical axis
193    
194            float gw = w - 2 * bord; // width of graph area inside axes
195            float gh = h - 2 * bord; // height of graph area inside axes
196    
197            // Draw axis labels.
198            msg = Float.toString(xMin);
199            g.drawString(msg, bord, h - 8);
200    
201            msg = Float.toString(xMax);
202            g.drawString(msg, w - bord, h - 8);
203    
204            msg = Float.toString(yMin);
205            g.drawString(msg, 8, h - bord);
206    
207            msg = Float.toString(yMax);
208            g.drawString(msg, 8, bord);
209    
210            // Draw graph line.
211            g.setColor(Color.red);
212            drawPlot(xMin, xMax, yMin, yMax, xValBuffer1, yValBuffer1, gw, gh,
213                    bord, numIncrements, h, g);
214    
215            g.setColor(Color.blue);
216            if (calcFrame.stack.size() > 1) {
217                drawPlot(xMin, xMax, yMin, yMax, xValBuffer2, yValBuffer2, gw, gh,
218                        bord, numIncrements, h, g);
219            }
220    
221            g.setColor(Color.green);
222            if (calcFrame.stack.size() > 2) {
223                drawPlot(xMin, xMax, yMin, yMax, xValBuffer3, yValBuffer3, gw, gh,
224                        bord, numIncrements, h, g);
225            }
226    
227            g.setColor(Color.orange);
228            if (calcFrame.stack.size() > 3) {
229                drawPlot(xMin, xMax, yMin, yMax, xValBuffer4, yValBuffer4, gw, gh,
230                        bord, numIncrements, h, g);
231            }
232    
233            // Consider abstracting this better!
234        }
235    
236        public void updatePlotBuffer(float xMin, float xMax, int numIncrements,
237                float xValBuffer[], float yValBuffer[], float yExtrema[],
238                RatPoly currentRatPoly) {
239            float delta = (xMax - xMin) / numIncrements;
240            float currentX = xMin;
241            boolean firstTime = true;
242            int i;
243            float yVal = 0;
244            float yMin = 0;
245            float yMax = 0;
246    
247            for (i = 0; i < numIncrements; ++i) {
248                if (currentX < xMax) {
249                    xValBuffer[i] = currentX;
250                    yVal = (float) currentRatPoly.eval(currentX);
251                    yValBuffer[i] = yVal;
252    
253                    if (firstTime) {
254                        firstTime = false;
255                        yMin = yVal;
256                        yMax = yVal;
257                    } else {
258                        if (yVal < yMin)
259                            yMin = yVal;
260    
261                        if (yVal > yMax)
262                            yMax = yVal;
263                    }
264    
265                    currentX += delta;
266                } else {
267                    xValBuffer[i] = xValBuffer[i - 1];
268                    yValBuffer[i] = yValBuffer[i - 1];
269                }
270            }
271    
272            yExtrema[0] = yMin;
273            yExtrema[1] = yMax;
274        }
275    
276        public void drawPlot(float xMin, float xMax, float yMin, float yMax,
277                float xValBuffer[], float yValBuffer[], float gw, float gh,
278                int bord, int numIncrements, int h, Graphics g) {
279            float xVal = 0;
280            float yVal = 0;
281            float previousX = 0;
282            float previousY = 0;
283            boolean firstTime = true;
284            float xRange = xMax - xMin;
285            float yRange = yMax - yMin;
286            int xPrevScaled = 0;
287            int yPrevScaled = 0;
288            int xScaled = 0;
289            int yScaled = 0;
290            int i;
291            for (i = 0; i < numIncrements; ++i) {
292                if (firstTime) {
293                    firstTime = false;
294                    xVal = xValBuffer[i];
295                    yVal = yValBuffer[i];
296                    previousX = xVal;
297                    previousY = yVal;
298                } else {
299                    previousX = xVal;
300                    previousY = yVal;
301                    xVal = xValBuffer[i];
302                    yVal = yValBuffer[i];
303                }
304    
305                // Now draw a line from previous to current.
306                xPrevScaled = Math.round((previousX - xMin) * gw / xRange);
307                yPrevScaled = Math.round((previousY - yMin) * gh / yRange);
308                xScaled = Math.round((xVal - xMin) * gw / xRange);
309                yScaled = Math.round((yVal - yMin) * gh / yRange);
310    
311                g.drawLine(bord + xPrevScaled, h - bord - yPrevScaled, bord
312                        + xScaled, h - bord - yScaled);
313            }
314        }
315    
316    }