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 }