/* java lab: phase transitions */ import java.awt.*; import java.applet.*; public class phase extends Applet implements Runnable{ public MediaTracker tracker; public Image offScreenImage; public Image theBox, theHeater; public boolean copyRight = false, auto = false, makePlot = false; public char cr = '\n'; Thread moveThread; private static final int MOVEPAUSE = 1000; public boolean myGo = false; public double dQdt = 0; // heating rate in kW public int xh = 0, yh = 302; // coordinates for rate plot radio button public int numMeas = 0; // number of temperature measurements public double heat = 0.0; // total heat inserted public double mass; // total mass of water in kg, set in init() public double temper = -50.0; // current temperature that's displayed public double measuredT[] = new double[5000]; // temperature mesurements public double measuredQ[] = new double[5000]; // corresp. Q measurements public long t1, t1old; public void start() { if(myGo) { if(moveThread == null){ moveThread = new Thread(this); moveThread.start(); } } } public void stop() { if(moveThread != null){ moveThread.stop(); try { moveThread.join(); } catch (InterruptedException e) { } } moveThread=null; } public void run() { while (true){ try { moveThread.sleep(MOVEPAUSE); }catch(InterruptedException e) { } repaint(); } } public void init() { super.init(); // load the background image tracker= new MediaTracker(this); theBox = this.getImage(getCodeBase(),"bg.jpg"); tracker.addImage(theBox,0); try { tracker.waitForID(0); } catch (InterruptedException e) { } theHeater = this.getImage(getCodeBase(),"heat.jpg"); tracker.addImage(theHeater,0); try { tracker.waitForID(0); } catch (InterruptedException e) { } offScreenImage = createImage(400,350); // set the total mass of the water randomly between 0.1 and 0.3 kg mass = 0.1 + 0.2*Math.random(); //{{INIT_CONTROLS setLayout(null); resize(550,350); textArea1 = new java.awt.TextArea(); textArea1.reshape(400,0,150,350); textArea1.setFont(new Font("Helvetica", Font.PLAIN, 10)); add(textArea1); //}} } public void update(Graphics g){ Graphics offg = offScreenImage.getGraphics(); paint(offg); g.drawImage(offScreenImage, 0, 0, this); } public void paint(Graphics g){ g.setColor(Color.white); g.fillRect(0,0,500,350); if (makePlot) { int x0 = 70; int y0 = 300; myGo = false; g.setColor(Color.black); g.drawRect(x0,y0-250,250,250); // scale in the plot: 1 pixel = 4 kJ, 1 pixel = 1°C // 0°C is at y0-50, 0 kJ is at x0 g.setFont(new Font("Helvetica", Font.BOLD, 12)); g.drawString("200",x0-30,y0-250); g.drawString("150",x0-30,y0-200); g.drawString("100",x0-30,y0-150); g.drawString(" 50",x0-30,y0-100); g.drawString(" 0",x0-30,y0- 50); g.drawString("-50",x0-30,y0 ); g.drawString("T (°C)",20,y0-280); g.drawString(" 0",x0-10,y0+20); g.drawString("200",x0+40,y0+20); g.drawString("400",x0+90,y0+20); g.drawString("600",x0+140,y0+20); g.drawString("800",x0+190,y0+20); g.drawString("1000",x0+240,y0+20); g.drawString("Q (kJ)",x0+120,y0+40); g.setColor(Color.gray); g.drawLine(x0,y0-200,x0+250,y0-200); g.drawLine(x0,y0-150,x0+250,y0-150); g.drawLine(x0,y0-100,x0+250,y0-100); g.drawLine(x0,y0- 50,x0+250,y0- 50); g.drawLine(x0+ 50,y0-250,x0+ 50,y0); g.drawLine(x0+100,y0-250,x0+100,y0); g.drawLine(x0+150,y0-250,x0+150,y0); g.drawLine(x0+200,y0-250,x0+200,y0); g.setColor(Color.green); for (int i=1; i<=numMeas; i++) { int xPlot = x0 + (int)(measuredQ[i] / 4.0); int yPlot = y0 - 50 - (int)(measuredT[i]); g.fillOval(xPlot-1,yPlot-1,3,3); } } else { g.drawImage(theBox,0,0,theBox.getWidth(this),theBox.getHeight(this),this); if (dQdt > 0) { // heating rate radio button: g.setColor(Color.red); g.fillOval(xh,yh,7,7); if (myGo) { g.drawImage(theHeater,168,218,theHeater.getWidth(this),theHeater.getHeight(this),this); t1 = System.currentTimeMillis(); if (t1 - t1old > 1000) { heat = heat + dQdt; t1old = t1; // Here we put in the equation of state for water to calculate the // temperature from the total heat put in. Remember, we start at -50°C // Specific heats: // c-ice = 2.22 kJ/kg K; c-water = 4.19 kJ/kg K; c-steam = 2.01 kJ/kg K // Latent heats: // L-freezing = 333 kJ/kg; L-boiling = 2263 kJ/kg temper = -50.0; double heatToMelt = 2.22 * (0-temper) * mass; double heatToWater = heatToMelt + 333.0*mass; double heatToBoil = heatToWater + 100.0 * 4.19 * mass; double heatToSteam = heatToBoil + 2263.0 * mass; if (heat < heatToMelt) { // still ice temper = temper + heat / (2.22 * mass); } else if (heat < heatToWater) { // ice-water mixture temper = 0; } else if (heat < heatToBoil) { // only water temper = (heat - heatToWater) / (4.19 * mass); } else if (heat < heatToSteam) { // steam-water mixture temper = 100.0; } else { temper = 100.0 + (heat - heatToSteam) / (2.01 * mass); } // now add some temperature fluctuation and display: if (!auto) { temper = temper + (1.-2.*Math.random())*dQdt; // assume simple linear error proportional to heating rate } // automatic operation: if (auto) { // display total mass: g.drawString(""+outString(""+mass,5)+" kg",133,83); // record a measurement: numMeas ++; measuredT[numMeas] = temper; measuredQ[numMeas] = heat + (0.5-Math.random())*dQdt; } // display the measurements in the text area: String derText = " Q (kJ) T (°C)"; for (int i=1; i<=numMeas; i++) { String ys = outString(""+measuredQ[i],5)+", "+outString(""+measuredT[i],5); derText = derText+cr+ys; } textArea1.setText(derText); } } } // output total heat: g.setColor(Color.yellow); g.setFont(new Font("Helvetica", Font.BOLD, 18)); g.drawString(""+outString(""+heat,5)+" kJ",180,334); // paint temperature bar g.setColor(Color.green); int ytop = (int)(300.0 - (temper+50.0)*251.0/200.0); g.fillRect(323,ytop,16,300-ytop); g.setColor(Color.red); g.drawLine(323,ytop,339,ytop); } // copyright notice: if (copyRight) { g.setColor(Color.green); g.fillRect(100,100,240,70); g.setColor(Color.white); g.setFont(new Font("Helvetica", Font.PLAIN, 18)); g.drawString("This applet was written by:",110,120); g.drawString("Wolfgang Bauer",110,140); g.drawString("Copyright: WB 1999",110,160); } // automatic switch-off condition if (temper > 170 || numMeas > 4998) { g.setColor(Color.blue); if (!makePlot) { g.setFont(new Font("Helvetica", Font.BOLD, 18)); g.drawString("End of run,",8,250); g.drawString("maximum T reached",8,270); } myGo = false; stop(); } } public boolean handleEvent(Event event) { if (event.id == Event.KEY_PRESS){ String thePressedKey = ""+(char)event.key; if (thePressedKey.equalsIgnoreCase("w")) { copyRight = !copyRight; repaint(); } if (thePressedKey.equalsIgnoreCase("a")) { auto = !auto; repaint(); } } if (event.target == this && event.id == Event.MOUSE_UP) { phase_MouseUp(event); return true; } return super.handleEvent(event); } //{{DECLARE_CONTROLS java.awt.TextArea textArea1; //}} public String outString(String inString, int l){ // returns the first l chars of string int ls = (int)Math.min(inString.length(),l); return inString.substring(0,ls); } void phase_MouseUp(Event event) { int xx = event.x; int yy = event.y; if (xx < 77 && yy > 68 && yy < 98 && !makePlot) { // plot button pushed dQdt = 0; myGo = false; makePlot = true; stop(); } else { makePlot = false; } //select heating rate if (yy > 299 && yy < 315) { if (xx > 63 && xx < 79) { dQdt = 8.0; xh = 67; } if (xx > 108 && xx < 124) { dQdt = 4.0; xh = 112; } if (xx > 153 && xx < 169) { dQdt = 2.0; xh = 157; } if (xx > 198 && xx < 214) { dQdt = 1.0; xh = 202; } if (xx > 243 && xx < 269) { dQdt = 0.5; xh = 247; } } if (xx > 320 && xx < 343 && yy < 311 && yy > 11) { // here we have a measurement of the temperature // temperature is measured by clicking the mouse numMeas ++; measuredT[numMeas] = -50.0 + (300.0-yy)*200.0/251.0; // heat is measured adding random fluctuations proportional to heaitng rate measuredQ[numMeas] = heat + (0.5-Math.random())*dQdt; } if (xx < 77 && yy < 34) { // start button pushed heat = 0.0; temper = -50.0; numMeas = 0; myGo = true; start(); } if (xx < 77 && yy > 36 && yy < 66) { // stop button pushed dQdt = 0; myGo = false; stop(); } repaint(); } }