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 }