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 }