import java.applet.Applet; import java.awt.*; import java.awt.event.*; import java.text.DecimalFormat; /* This Java-Applet has been written by * * Ralph Brinks * AG Volkssternwarte Hagen * 58001 Hagen. * * It may be distributed, changed and recompiled freely. * * NOTE: Neiter the AG Volksternwarte Hagen nor the author does not * give any warranty of any kind of the effects of this applet. * It is for educational purposes only. */ public class Eclipse extends Applet implements Runnable, ActionListener { //String Constants, modify to your language: final String RESBU = "Übernehmen"; //RestartButton final String PAUBU1 = "Anhalten"; //PauseButton final String PAUBU2 = "Weiter"; //UnpauseButton final String SISBU = "Einzelschritt"; //SingleStepButton final String ANGTXT = "Inklination i"; //InclinationAngle final String SEPTXT = "Trennung s"; //Separation final String CURTXT = "Aktuell dargestellte Werte"; //CurrentValues final String CURATXT = "Inklinationswinkel"; //CurrentAngle final String CURSTXT = "Trennung der Sterne"; //CurrentSeparation final String CURLTXT = "Helligkeit des Systems"; //CurrentLight final String ST1TXT = "Stern 1"; //Star1 final String ST2TXT = "Stern 2"; //Star2 final String LIGTXT = "Lichtkurve"; //LightCurve final String DEGTXT = "Grad"; //Degree final String SOLMASS = "[Sonnenmassen]"; //SolarMass final String SOLRAD = "[Sonnenradien]"; //SolarRadius final String SOLPOW = "[Sonnenleuchtkraft]"; //SolarPower final String CURPTXT = "Aktueller Bahnwinkel"; //PositionAngle (phi) final String SETTXT = "Einstellungen"; //Settings final String SLOMOTXT = "Verzögerung"; //SlowMotion //numerical constants final int MAXSEP = 12; //maximum of separation final public int ARCINCR = 2; //arc stepsize //physical variables private double mass1 = 2.0D; //mass of star private double mass2 = 1.0D; //mass of companion private double mass = mass1 + mass2; //total mass private double rad1 = 1.52D; //radius of star, r ~ M^0.6 private double rad2 = 1.0D; //radius of companion private double lig1 = 11.3D; //power of star, L ~ M^3.5 private double lig2 = 1.0D; //power of companion private double light; //power of whole system private double xmain, ymain; //location of main component private double xcomp, ycomp; //location of companion private Color col1 = Color.yellow; //color of star private Color col2 = Color.orange; //color of companion private double d1, d2; //distance of components to center of gravity private int ang; //angle of inclination private int sep; //distance of components in solar radii private int phi = 0; //rotation angle private int angLast; //corresponding last values for memory purposes private int sepLast; private int phiLast; private double phiRad; //phi in radians private double dist; //visual distance of centers of stars private double overl; //overlapping area when stars cross //thread to move stars private Thread motor; //graphics private Panel orbitP, lightP; private Graphics orbitG, lightG; private int orbitWidth = 220, orbitHeight = 220, lightWidth = 220, lightHeight = 220; //buffered off-screen images private Image imOrbit, imLight; private Graphics bufOrbitG, bufLightG; //variables private Point centerP = new Point(orbitWidth/2, orbitHeight/2); private double d2max = MAXSEP * mass1/mass; private double solRad2Pix = (double) orbitWidth /( 2.0D*(d2max + rad1)); private double scal; private int steps=360/ARCINCR; //steps needed for exactly one rotation //necessary only for pipe-store - best if <=180 private int lightVals[]; //pipe-storage for light-values (y-comp in pixels) private int valPoints = 0; //number of valid values in lightVals private long period; //measure for time of one rotation of the system private DecimalFormat df = new DecimalFormat("0.0#"); //buttons private Button rbu, pbu, sbu; //scrollbars private Scrollbar angl, sepa; //adjust inclination and separation //labels private Label angLabel, sepLabel, ligLabel, posLabel; //checkbox private Checkbox slomo; //Slowmotion private final long DILA = 150; //millisec for slowmotion dilation public void init() { //state: program start //general settings setLayout(null); setBackground(Color.lightGray); this.setBackground(Color.lightGray); Font bgFont = new Font("Dialog", Font.PLAIN, 14); //left hand side------------------------------------------ //panel of observation Panel pObs = new Panel(); pObs.setLayout(null); orbitP = new Panel(); pObs.add(orbitP); orbitP.setBackground(Color.lightGray); orbitP.setBounds(0, 0, orbitWidth, orbitHeight); //lightcurve Label label = new Label(LIGTXT+": "); label.setBounds(5, orbitHeight+5, 10*LIGTXT.length(), 15); label.setFont(bgFont); pObs.add(label); lightP = new Panel(); pObs.add(lightP); lightP.setBounds(0, orbitHeight+20, lightWidth, lightHeight); lightP.setBackground(Color.lightGray); pObs.setBounds(0,0,orbitWidth, orbitHeight+lightHeight+25); pObs.setBackground(Color.lightGray); add(pObs); orbitG = orbitP.getGraphics(); lightG = lightP.getGraphics(); try { imOrbit = createImage(orbitWidth, orbitHeight); bufOrbitG = imOrbit.getGraphics(); imLight = createImage(lightWidth, lightWidth); bufLightG = imLight.getGraphics(); } catch(Exception e) { bufLightG = null; bufOrbitG = null; } //origin = center of gravity bufOrbitG.translate((int)centerP.x, (int)centerP.y); bufLightG.translate((int)(0.1*lightWidth), (int)(0.8*lightHeight)); pObs = null; //right hand side----------------------------------------- //panel for current values Panel pCur = new Panel(); pCur.setLayout(null); int td = 10; label = new Label(CURTXT+":"); label.setFont(bgFont); label.setBounds(0,0, 12*CURTXT.length(),25); pCur.add(label); int maxLen = (CURATXT.length() > CURSTXT.length() ? CURATXT.length() : CURSTXT.length()); maxLen = (CURLTXT.length() > maxLen ? CURLTXT.length() : maxLen); maxLen = (CURPTXT.length() > maxLen ? CURPTXT.length() : maxLen); maxLen *= 6; //inclination angle label = new Label(CURATXT+": "); label.setBounds(5+td, 30, maxLen, 20); pCur.add(label); ang = 10; angLabel = new Label(String.valueOf(ang)); angLabel.setBounds(maxLen+10+td, 30, 30, 20); pCur.add(angLabel); label = new Label(DEGTXT); label.setBounds(maxLen+40+td, 30, 7*DEGTXT.length(), 20); pCur.add(label); //separation label = new Label(CURSTXT+": "); label.setBounds(5+td, 60, maxLen, 20); pCur.add(label); sep = 9; sepLabel = new Label(String.valueOf(sep)); sepLabel.setBounds(maxLen+10+td, 60, 35, 20); pCur.add(sepLabel); label = new Label(SOLRAD); label.setBounds(maxLen+45+td, 60, 7*SOLRAD.length(), 20); pCur.add(label); //light label = new Label(CURLTXT+": "); label.setBounds(5+td, 90, maxLen, 20); pCur.add(label); ligLabel = new Label(null); ligLabel.setBounds(maxLen+10+td, 90, 35, 20); pCur.add(ligLabel); label = new Label(SOLPOW); label.setBounds(maxLen+45+td, 90, 7*SOLPOW.length(), 20); pCur.add(label); //position label = new Label(CURPTXT+": "); label.setBounds(5+td, 120, maxLen, 20); pCur.add(label); posLabel = new Label(null); posLabel.setBounds(maxLen+10+td, 120, 35, 20); pCur.add(posLabel); label = new Label(DEGTXT); label.setBounds(maxLen+45+td, 120, 7*DEGTXT.length(), 20); pCur.add(label); pCur.setBounds(orbitWidth+10, 10, 550-orbitWidth-20, lightHeight-60); pCur.setBackground(Color.lightGray); add(pCur); pCur = null; //control part Panel pCon = new Panel(); pCon.setLayout(null); label = new Label(SETTXT+":"); label.setFont(bgFont); label.setBounds(0,0, 12*SETTXT.length(),25); pCon.add(label); //inclination and separation control maxLen = (ANGTXT.length() > SEPTXT.length() ? ANGTXT.length() : SEPTXT.length()); maxLen = (SLOMOTXT.length() > maxLen ? SLOMOTXT.length() : maxLen); maxLen *= 7; //inclination Font smlFont = new Font("Dialog",Font.PLAIN,10); label = new Label(ANGTXT); label.setBounds(5+td, 30, maxLen, 20); pCon.add(label); label = new Label("0"); label.setBounds(maxLen+5+td, 30, 8, 20); label.setFont(smlFont); pCon.add(label); angl = new Scrollbar(Scrollbar.HORIZONTAL, ang, 5, 0, 95); angl.setBounds(maxLen+18+td, 30, 80, 20); pCon.add(angl); label = new Label("90"); label.setFont(smlFont); label.setBounds(maxLen+103+td, 30, 15, 20); pCon.add(label); //separation label = new Label(SEPTXT); label.setBounds(5+td, 60, maxLen, 20); pCon.add(label); label = new Label("6"); label.setBounds(maxLen+5+td, 60, 8, 20); label.setFont(smlFont); pCon.add(label); sepa = new Scrollbar(Scrollbar.HORIZONTAL, sep, 1, 6, MAXSEP+1); sepa.setBounds(maxLen+18+td, 60, 80, 20); pCon.add(sepa); label = new Label(Integer.toString(MAXSEP)); label.setBounds(maxLen+103+td, 60, 15, 20); label.setFont(smlFont); pCon.add(label); smlFont = null; label = null; //restart button rbu = new Button(RESBU); pCon.add(rbu); rbu.addActionListener(this); rbu.setBounds((550-lightWidth-20)/4, 100, (550-lightWidth-20)/2, 20); //contol buttons maxLen = (SISBU.length() > PAUBU1.length() ? SISBU.length() : PAUBU1.length()); maxLen = (maxLen > PAUBU2.length() ? maxLen : PAUBU2.length()); maxLen *= 10; int st = (550-lightWidth-40-2*maxLen-5)/2; //slow motion check box slomo = new Checkbox(SLOMOTXT); slomo.setBackground(Color.lightGray); slomo.setBounds(st, 140 , 10*SLOMOTXT.length()+5, 20); pCon.add(slomo); //single step sbu = new Button(SISBU); sbu.setBounds(st, 170, maxLen+10, 20); sbu.setEnabled(false); sbu.addActionListener(this); pCon.add(sbu); //(un-)pause pbu = new Button(PAUBU1); pbu.setBounds(st+maxLen+15, 170, maxLen+10, 20); pbu.addActionListener(this); pCon.add(pbu); pCon.setBounds(orbitWidth+10, orbitHeight, 550-lightWidth-20, lightHeight+80); add(pCon); pCon.setBackground(Color.lightGray); pCon = null; //end of state program start //to force a new motor creation, set Last-values to nonsense angLast = -1; sepLast = -1; fetch_init_values(); } //end init private void fetch_init_values() { sep = sepa.getValue(); ang = angl.getValue(); if(sep != sepLast || ang != angLast) { //user modified separation and/or inclination //create a new system lightVals = null; valPoints = 0; phiLast = 0; lightVals = new int[steps]; sepLast = sep; angLast = ang; sepLabel.setText(Integer.toString(sep)); angLabel.setText(Integer.toString(ang)); } pbu.setLabel(PAUBU1); sbu.setEnabled(false); d1 = mass2/mass * sep; d2 = mass1/mass * sep; scal = Math.cos((90-ang)*Math.PI/180D); //scaling factor due to projection start(); return; } public void goForward() { //star motion bufOrbitG.setColor(Color.black); bufOrbitG.fillOval(-orbitWidth/2, -orbitHeight/2, orbitWidth, orbitHeight); phiRad = (Math.PI * (double) phi) / 180.0D; //calculate location of main component xmain = d1 * Math.cos(phiRad); ymain = -d1 * scal * Math.sin(phiRad); //calculate location of companion xcomp = - d2 * Math.cos(phiRad); ycomp = d2 * scal * Math.sin(phiRad); overl = calcOverlap(); if(phi >= 180) { //main component in front drawCompanion(); //draw center of gravity bufOrbitG.setColor(Color.green); drawCircle(0,1,1); drawMain(); //subtract hidden part of companion light = lig1 + lig2*(1.0D - overl/(Math.PI*rad2*rad2)); } else { //main in the back drawMain(); //draw center of gravity bufOrbitG.setColor(Color.green); drawCircle(0,1,1); drawCompanion(); //subtract hidden part of main component light = lig2 + lig1*(1.0D - overl/(Math.PI*rad1*rad1)); } ligLabel.setText(df.format(light)); posLabel.setText(Integer.toString(phi)); pushLight(light); drawLight(); } private void pushLight(double li) { li = li - 7.0D; int y = (int) Math.round(0.75*lightHeight*li/5.3D); if(valPoints < steps) { //just append new value lightVals[valPoints] = y; valPoints++; } else { //forget first entry for(int c = 1; c < steps; c++) { lightVals[c-1] = lightVals[c]; } lightVals[steps - 1] = y; } return; } private void drawLight() { int x = 2; bufLightG.setColor(Color.lightGray); bufLightG.fillRect(0, (int)(-lightHeight*0.8D), (int)(lightWidth*0.9), (int)(lightHeight*0.8)); bufLightG.setColor(Color.white); for(int c = 0; c < valPoints; c++, x++) { bufLightG.fillOval(x,-lightVals[c], 3,3); } bufLightG.setColor(Color.red); bufLightG.fillOval(x-1-1,-lightVals[valPoints-1]-1, 4,4); return; } public double calcOverlap() { double d2 = (xmain - xcomp)*(xmain - xcomp) + (ymain - ycomp)*(ymain - ycomp); dist = Math.sqrt(d2); if(dist > rad1 + rad2) { //no overlap return 0.0D; } else { //overlap if(dist <= rad1-rad2) { //full overlap return(Math.PI * rad2 * rad2); } else { //partial eclipse //dist > rad1-rad2 >=0 double alpha, beta; alpha = Math.acos(0.5*(rad1*rad1 + d2 - rad2*rad2)/(dist*rad1)); double a_m, a_c; a_m = rad1*rad1*alpha - dist*rad1*Math.sin(alpha); beta = Math.acos(0.5*(rad2*rad2 + d2 - rad1*rad1)/(dist*rad2)); a_c = rad2*rad2*beta; return(a_c + a_m); } } //end overlap }//end calcOverlap public void drawMain() { bufOrbitG.setColor(col1); drawCircle((int)Math.round(xmain*solRad2Pix), (int)Math.round(ymain*solRad2Pix),(int)(rad1*solRad2Pix)); } public void drawCompanion() { bufOrbitG.setColor(col2); drawCircle((int)Math.round(xcomp*solRad2Pix), (int)Math.round(ycomp*solRad2Pix),(int)Math.round(rad2*solRad2Pix)); } public void paint(Graphics g) { orbitG.drawImage(imOrbit, 0, 0, this); lightG.drawImage(imLight, 0, 0, this); } public void update(Graphics g) { paint(g); } //event-handling public void actionPerformed(ActionEvent event) { Object obj = event.getActionCommand(); if(obj instanceof String) { //restart button pressed? if(obj.equals(RESBU)) { motor = null; fetch_init_values(); } //pause button pressed? if(obj.equals(PAUBU1)) { phiLast = phi; pbu.setLabel(PAUBU2); sbu.setEnabled(true); //kill MotorThread motor = null; } //unpause button pressed? if(obj.equals(PAUBU2)) { pbu.setLabel(PAUBU1); fetch_init_values(); sbu.setEnabled(false); } //single-step button pressed? if(obj.equals(SISBU)) { oneStep(); } } } private void drawCircle(int xpix, int ypix, int rpix) { bufOrbitG.fillOval(xpix - rpix, ypix - rpix, 2*rpix, 2*rpix); return; } public void oneStep() { if(phi == (360 - ARCINCR)) { phi = 0; } else { phi += ARCINCR; } phiLast = phi; goForward(); repaint(); } public void start() { phi = phiLast; //Kepler's third law, T ~ r^(3/2) period = (long)Math.pow((double)sep, 1.5D); motor = new Thread(this); motor.start(); } public void run() { Thread me = Thread.currentThread(); while(motor == me) { try { Thread.sleep(period); } catch(Exception e) { } if(slomo.getState()) { //wait again try { Thread.sleep(DILA); } catch(Exception e) { } } oneStep(); } } //end run public void stop() { motor = null; } } //end class EclipBinStar