††††††††††
new blog =
√√√√√√√√√
dear hunt&gather,
goodbye
luv,
becca
import java.awt.*; import java.applet.*; import java.lang.*; import java.net.*; import java.io.*; /* PLEASE READ THIS: You may use this code for your own uses, including scavenging functions from it. However, I insist that I remain permanently acknowledged in your source code and, if possible, in the final product (executable, web page, what have you). In addition, please email me if you plan to use part of this code. I wrote this program in my spare time for fun. It is neither elegant nor polished. You have been warned. Chris Dolan (dolan@astro.wisc.edu) Nov 25, 1998 */ /* This code is written for Java 1.0 Note that I'm more of a C programmer than a C++ or Java programmer, so my use of object-oriented tools is less than elegant. */ /* There are three classes in this applet: - Constellations The main body which includes all of the initialization and event handling. - SkyWidget The canvas on which all of the graphics are produced. This includes the double-buffered rendering which displays the stars. The primary entrance is the paintSky function. This class also handles a small bit of mouse interactivity. - LoadThread The code that loads the databases of stars, constellation names, and constellation lines. This code runs in a parallel background thread so the user can interact with the applet while the data is being downloaded. This thread runs once at the beginning and then becomes inactive. The LoadThread also precalculates the trigonometric functions needed to project the stars and constellation lines onto the sky. This data is used in the SkyWidget functions. */ class LoadThread implements Runnable { private Constellations theApp; public LoadThread(Constellations app) { super(); theApp = app; } public void getStdLines() { theApp.numlines = getLines(theApp.linedataset, theApp.lines, theApp.lines_math); theApp.newStuff(); } public void getAltLines() { if (theApp.linedataset == theApp.altlinedataset) { if (theApp.numlines == 0) { theApp.uselines = false; theApp.constcheck.disable(); } theApp.altcheck.disable(); } else { theApp.altnumlines = getLines(theApp.altlinedataset, theApp.altlines, theApp.altlines_math); if (theApp.numlines == 0) { theApp.usealtlines = true; if (theApp.altnumlines == 0 ) { theApp.uselines = false; theApp.constcheck.disable(); } } theApp.deliverEvent(new Event(this, theApp.NEW_STUFF, this)); } } public int getLines(int catalog, double lines[][], double lines_math[][]) { int i = 0, j = 0; String s; try { if (catalog == 0) // s = "http://www.astro.wisc.edu/~dolan/java/data/lines.dat"; s = "data/lines.dat"; else // s = "http://www.astro.wisc.edu/~dolan/java/data/lines" + s = "data/lines" + String.valueOf(catalog) + ".dat"; System.out.println("Opening file " + s); URL url = new URL(theApp.getCodeBase(), s); URLConnection urlConnection = url.openConnection(); urlConnection.connect(); // if the file does not exist, the following will throw an exception: InputStream is = urlConnection.getInputStream(); StreamTokenizer st = new StreamTokenizer(is); while (st.nextToken() != st.TT_EOF) { lines[j][i] = st.nval; lines_math[j][2*i] = Math.sin(st.nval); lines_math[j][2*i+1] = Math.cos(st.nval); if ((++i) == 4) { j++; i = 0; } } } catch (Exception ex) { return 0; } System.out.println(String.valueOf(j) + " constellation lines added"); return j; } public void getConsts() { int i = 0, j = 0; String s; try { // s = "http://www.astro.wisc.edu/~dolan/java/data/averagebdys.dat"; s = "data/averagebdys.dat"; // System.out.println("Opening file " + s); URL url = new URL(theApp.getCodeBase(), s); URLConnection urlConnection = url.openConnection(); urlConnection.connect(); // if the file does not exist, the following will throw an exception: InputStream is = urlConnection.getInputStream(); StreamTokenizer st = new StreamTokenizer(is); while (st.nextToken() != st.TT_EOF) { if (i == 0) { theApp.constnames[j] = st.sval; } else if (i == 1) { theApp.constcoords[j][0] = st.nval*Math.PI/12.0; theApp.constcoords_math[j][0] = Math.sin(theApp.constcoords[j][0]); theApp.constcoords_math[j][1] = Math.cos(theApp.constcoords[j][0]); } else { theApp.constcoords[j][1] = st.nval*Math.PI/180.0; theApp.constcoords_math[j][2] = Math.sin(theApp.constcoords[j][1]); theApp.constcoords_math[j][3] = Math.cos(theApp.constcoords[j][1]); j++; i = -1; } i++; } } catch (Exception ex) { theApp.numconst = 0; theApp.usenames = false; theApp.namecheck.disable(); return; } theApp.numconst = j; System.out.println(String.valueOf(theApp.numconst) + " constellations added"); theApp.deliverEvent(new Event(this, theApp.NEW_STUFF, this)); } public void getData(int n) { int i; String url; switch (theApp.dataset) { case 0: if (n == 0) // url = "http://www.astro.wisc.edu/~dolan/java/data/bsc.dat"; url = "data/bsc.dat"; else // url = "http://www.astro.wisc.edu/~dolan/java/data/bsc" + url = "data/bsc" + String.valueOf(n) + ".dat"; break; case 1: if (n == 0) // url = "http://www.astro.wisc.edu/~dolan/java/data/YBS.dat"; url = "data/YBS.dat"; else // url = "http://www.astro.wisc.edu/~dolan/java/data/YBS" + url = "data/YBS" + String.valueOf(n) + ".dat"; break; default: System.out.println("Invalid data set requested"); return; } i = getFile(url, theApp.stars, theApp.numstars); if (i > 0) { theApp.numstars += i; System.out.println(String.valueOf(theApp.numstars) + " stars in database"); theApp.deliverEvent(new Event(this, theApp.NEW_STARS, this)); } } public int getFile(String s, double a[][], int n) { Double f; int i, j; try { // System.out.println("Accessing URL " + s); URL url = new URL(theApp.getCodeBase(), s); URLConnection urlConnection = url.openConnection(); urlConnection.connect(); // if the file does not exist, the following will throw an exception: InputStream is = urlConnection.getInputStream(); StreamTokenizer st = new StreamTokenizer(is); i = 0; j = 0; while (st.nextToken() != st.TT_EOF) { a[n+j][i] = st.nval; if (i != 2) { theApp.stars_math[n+j][2*i] = Math.sin(st.nval); theApp.stars_math[n+j][2*i+1] = Math.cos(st.nval); } if ((++i) == 3) { j++; i = 0; } } } catch (Exception ex) { return 0; } return j; } public void run() { getData(1); getData(2); getConsts(); getData(3); getStdLines(); getData(4); getAltLines(); getData(5); getData(6); getData(7); System.out.println("Done loading stars"); return; } } public class Constellations extends Applet { public final static int NEW_STUFF = 10000; public final static int NEW_STARS = 10001; // Variables used as parameters: must be predefined here! public double ra = 0.0, dec = 0.0; public double angle = 60.0; public int speed = 5; public int dataset = 0, linedataset = 0, altlinedataset = 0; // Global variables public double stars[][], stars_math[][]; public int numstars, usestars, starlists[], loadedlists = 0; public int width, height; public double lines[][], lines_math[][]; public int numlines; public double altlines[][], altlines_math[][]; public int altnumlines; public boolean uselines, usealtlines; public String constnames[]; public double constcoords[][], constcoords_math[][]; public int numconst; public boolean usenames; // Double buffering Objects public Image buf; // bitmap for double buffering public Graphics gBuf; // gc to draw on bitmap // GUI objects -- Should be private unless absolutely necessary public Checkbox constcheck, namecheck, altcheck; private LoadThread theLoadThread; private SkyWidget skyWidget; private Scrollbar scrollV, scrollH; private TextField fov; private Choice speedmenu; private Font font; private Button morebutton, lessbutton; // Internal variables private int expand; private double dexpand; public void init() { Dimension d = size(); width = d.width; height = d.height; setBackground(Color.lightGray); setFont(font = new Font("Helvetica", Font.PLAIN, 10)); setLayout(new BorderLayout()); getParams(); buf = createImage(width, height-100); gBuf = buf.getGraphics(); stars = new double[10000][3]; stars_math = new double[10000][4]; starlists = new int[7]; numstars = 0; usestars = 0; lines = new double[1000][4]; lines_math = new double[1000][8]; numlines = 0; altlines = new double[1000][4]; altlines_math = new double[1000][8]; altnumlines = 0; uselines = true; usealtlines = false; constnames = new String[100]; constcoords = new double[100][2]; constcoords_math = new double[100][4]; numconst = 0; usenames = true; expand = 1; dexpand = (double)expand; Panel center = new Panel(); center.setLayout(new BorderLayout()); add("Center", center); scrollV = new Scrollbar(Scrollbar.VERTICAL, (int)(dexpand*(-dec)*180.0/Math.PI), (int)(dexpand*angle*90.0/Math.PI), -90 * expand, 90 * expand); center.add("East", scrollV); scrollH = new Scrollbar(Scrollbar.HORIZONTAL, (int)(dexpand*(360.0-ra*180.0/Math.PI)), (int)(dexpand*angle*90.0/Math.PI), 0 * expand, 360 * expand); center.add("South", scrollH); center.add("Center", skyWidget = new SkyWidget(this)); Panel bottom = new Panel(); bottom.setLayout(new GridLayout(0,1)); add("South", bottom); Panel panel = new Panel(); panel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5)); bottom.add(panel); panel.add(new Label("Field of view (degrees)", Label.RIGHT)); fov = new TextField(String.valueOf(angle*180.0/Math.PI), 6); fov.setEditable(true); panel.add(fov); panel = new Panel(); panel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5)); bottom.add(panel); panel.add(morebutton = new Button("More Stars")); panel.add(lessbutton = new Button("Fewer Stars")); panel = new Panel(); panel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5)); bottom.add(panel); panel.add(constcheck = new Checkbox("Show constellations lines")); constcheck.setState(uselines); panel.add(namecheck = new Checkbox("Show constellation names")); namecheck.setState(usenames); panel = new Panel(); panel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5)); bottom.add(panel); panel.add(altcheck = new Checkbox("Use new constellations")); altcheck.setState(usealtlines); } public void start() { if (numstars == 0) { Thread theThread; Thread.currentThread().setPriority(Thread.MAX_PRIORITY); theLoadThread = new LoadThread(this); theThread = new Thread(theLoadThread); theThread.setPriority(Thread.MIN_PRIORITY); theThread.start(); } } public void destroy() { gBuf.dispose(); } public synchronized boolean handleEvent(Event event) { if (event.target == scrollV) { dec = -(double)scrollV.getValue() * Math.PI / (180.0 * dexpand); skyWidget.update(skyWidget.getGraphics()); return true; } else if (event.target == scrollH) { ra = (360.0-(double)scrollH.getValue()/dexpand) * Math.PI / 180.0; skyWidget.update(skyWidget.getGraphics()); return true; } else if (event.target == fov && event.id == Event.ACTION_EVENT) { double a = new Double(fov.getText()).doubleValue(); if (a < a =" 0.00001;"> 360.0) a = 360.0; /* Impose a limit of 120 degrees */ if (a > 120.0) a = 120.0; fov.setText(String.valueOf(a)); angle = a*Math.PI/180.0; skyWidget.update(skyWidget.getGraphics()); return true; } else if (event.target == constcheck && event.id == Event.ACTION_EVENT) { uselines = constcheck.getState(); skyWidget.update(skyWidget.getGraphics()); return true; } else if (event.target == namecheck && event.id == Event.ACTION_EVENT) { usenames = namecheck.getState(); skyWidget.update(skyWidget.getGraphics()); return true; } else if (event.target == altcheck && event.id == Event.ACTION_EVENT) { usealtlines = altcheck.getState(); skyWidget.update(skyWidget.getGraphics()); return true; } else if (event.target == morebutton && event.id == Event.ACTION_EVENT) { if (speed < usestars =" starlists[speed++];" speed ="=" target ="=" id ="="> 1) { usestars = starlists[--speed - 1]; skyWidget.update(skyWidget.getGraphics()); if (speed == 1) lessbutton.setLabel("--"); morebutton.setLabel("More Stars"); } return true; } else if (event.id == NEW_STARS) { starlists[loadedlists++] = numstars; if (loadedlists <= speed) { usestars = numstars; morebutton.setLabel("--"); if (speed > 1 && loadedlists > 1) { lessbutton.setLabel("Fewer Stars"); } else { lessbutton.setLabel("--"); } } else { morebutton.setLabel("More Stars"); } skyWidget.update(skyWidget.getGraphics()); return true; } else if (event.id == NEW_STUFF) { skyWidget.update(skyWidget.getGraphics()); return true; } else { return super.handleEvent(event); } } // Get parameters (for ra, dec, lat, long) public void getParams() { if (getParameter("ra") != null) { ra = new Double(new String(getParameter("ra"))).doubleValue(); if (ra < ra =" 0.0;"> 24.0) ra = 24.0; ra = ra * Math.PI / 12.0; } if (getParameter("dec") != null) { dec = new Double(new String(getParameter("dec"))).doubleValue(); if (dec < -90.0) dec = -90.0; else if (dec > 90.0) dec = 90.0; dec = dec * Math.PI / 180.0; } if (getParameter("angle") != null) { angle = new Double(new String(getParameter("angle"))).doubleValue(); if (angle < angle =" 0.00001;"> 360.0) angle = 360.0; angle = angle * Math.PI / 180.0; } if (getParameter("speed") != null) { speed = new Integer(new String(getParameter("speed"))).intValue(); if (speed < speed =" 1;"> 7) speed = 7; } if (getParameter("data") != null) { String s = getParameter("data"); if (s.compareTo("bsc") == 0) { dataset = 0; System.out.println("Using catalog \"" + s + "\""); } else if (s.compareTo("YBS") == 0) { dataset = 1; System.out.println("Using catalog \"" + s + "\""); } else { System.out.println("Invalid data set \"" + s + "\" requested"); System.out.println("Using default catalog"); dataset = 0; } } if (getParameter("lines") != null) { int s = new Integer(new String(getParameter("lines"))).intValue(); if (s >= 0) { linedataset = s; } } if (getParameter("altlines") != null) { int s = new Integer(new String(getParameter("altlines"))).intValue(); if (s >= 0) { altlinedataset = s; } } } } class SkyWidget extends Canvas { private boolean doubleBuffer=true; // or false of course private Dimension d; private Constellations top; private double brightnesses[] = {1.0, 0.9, 0.7, 1.0, 0.7, 0.4, 0.1}; private int xmid, ymid; public SkyWidget(Constellations parent) { top = parent; setBackground(Color.black); setForeground(Color.white); } private void drawCenteredString(Graphics g, String s, int x, int y) { FontMetrics f = getFontMetrics(getFont()); g.drawString(s, x - f.stringWidth(s)/2, y + f.getHeight()/2); } protected void paintStars(Graphics g) { int i, x, y; double brightness, dist, ex, ey, ez; double cr, sr, cd, sd, cr0, sr0, cd0, sd0; int b; dist = (double)Math.min(xmid,ymid)/Math.tan(top.angle/2.0); sr = Math.sin(top.ra); cr = Math.cos(top.ra); sd = Math.sin(top.dec); cd = Math.cos(top.dec); for (i = top.usestars-1; i >= 0; i--) { b = (int)top.stars[i][2]; b = b <> 6 ? 6 : b); g.setColor(Color.getHSBColor((float)0.0, (float)0.0, (float)brightnesses[b])); /* Projected view */ /* ex = -cos(top.stars[i][1]) * (sin(top.ra)*cos(top.stars[i][0]) - cos(top.ra)*sin(top.stars[i][0])); ey = -cos(top.stars[i][1])*sin(top.dec) * (cos(top.ra)*cos(top.stars[i][0]) + sin(top.ra)*sin(top.stars[i][0]) + cos(top.stars[i][1])*cos(top.dec); ez = cos(top.stars[i][1])*cos(top.dec) * (cos(top.ra)*cos(top.stars[i][0]) + sin(top.ra)*sin(top.stars[i][0]) + sin(top.stars[i][1])*sin(top.dec); */ sr0 = top.stars_math[i][0]; cr0 = top.stars_math[i][1]; sd0 = top.stars_math[i][2]; cd0 = top.stars_math[i][3]; ez = cd*cd0*(cr*cr0 + sr*sr0) + sd*sd0; if (ez > 0.1) { ey = -sd*cd0*(cr*cr0 + sr*sr0) + cd*sd0; ex = -cd0*(sr*cr0 - cr*sr0); x = xmid + (int)(-dist * ex/ez); y = ymid + (int)(-dist * ey/ez); switch (b) { case 0: g.drawLine(x-1,y,x+1,y); g.drawLine(x,y-1,x,y+1); break; case 1: case 2: g.drawLine(x,y,x+1,y); g.drawLine(x,y-1,x+1,y-1); break; default: g.drawLine(x,y,x,y); break; } } } } protected void paintNames(Graphics g) { int i, x, y; double dist, ex, ey, ez; double cr, sr, cd, sd, cr0, sr0, cd0, sd0; dist = (double)Math.min(xmid,ymid)/Math.tan(top.angle/2.0); g.setColor(Color.red); sr = Math.sin(top.ra); cr = Math.cos(top.ra); sd = Math.sin(top.dec); cd = Math.cos(top.dec); for (i = top.numconst-1; i >= 0; i--) { sr0 = top.constcoords_math[i][0]; cr0 = top.constcoords_math[i][1]; sd0 = top.constcoords_math[i][2]; cd0 = top.constcoords_math[i][3]; ez = cd*cd0*(cr*cr0 + sr*sr0) + sd*sd0; if (ez > 0.1) { ey = -sd*cd0*(cr*cr0 + sr*sr0) + cd*sd0; ex = -cd0*(sr*cr0 - cr*sr0); x = xmid + (int)(-dist * ex/ez); y = ymid + (int)(-dist * ey/ez); drawCenteredString(g, top.constnames[i], x, y); } } } protected void paintLines(Graphics g) { int i, x1, x2, y1, y2; double dist, ex1, ex2, ey1, ey2, ez1, ez2; double cr, sr, cd, sd, cr1, sr1, cd1, sd1, cr2, sr2, cd2, sd2; dist = (double)Math.min(xmid,ymid)/Math.tan(top.angle/2.0); g.setColor(Color.blue); sr = Math.sin(top.ra); cr = Math.cos(top.ra); sd = Math.sin(top.dec); cd = Math.cos(top.dec); for (i = top.numlines-1; i >= 0; i--) { sr1 = top.lines_math[i][0]; cr1 = top.lines_math[i][1]; sd1 = top.lines_math[i][2]; cd1 = top.lines_math[i][3]; sr2 = top.lines_math[i][4]; cr2 = top.lines_math[i][5]; sd2 = top.lines_math[i][6]; cd2 = top.lines_math[i][7]; ez1 = cd*cd1*(cr*cr1 + sr*sr1) + sd*sd1; ez2 = cd*cd2*(cr*cr2 + sr*sr2) + sd*sd2; if (ez1 > 0.1 && ez2 > 0.1) { ey1 = -sd*cd1*(cr*cr1 + sr*sr1) + cd*sd1; ey2 = -sd*cd2*(cr*cr2 + sr*sr2) + cd*sd2; ex1 = -cd1*(sr*cr1 - cr*sr1); ex2 = -cd2*(sr*cr2 - cr*sr2); x1 = xmid + (int)(-dist * ex1/ez1); x2 = xmid + (int)(-dist * ex2/ez2); y1 = ymid + (int)(-dist * ey1/ez1); y2 = ymid + (int)(-dist * ey2/ez2); g.drawLine(x1,y1,x2,y2); } } } protected void paintAltLines(Graphics g) { int i, x1, x2, y1, y2; double dist, ex1, ex2, ey1, ey2, ez1, ez2; double cr, sr, cd, sd, cr1, sr1, cd1, sd1, cr2, sr2, cd2, sd2; dist = (double)Math.min(xmid,ymid)/Math.tan(top.angle/2.0); g.setColor(Color.blue); sr = Math.sin(top.ra); cr = Math.cos(top.ra); sd = Math.sin(top.dec); cd = Math.cos(top.dec); for (i = top.altnumlines-1; i >= 0; i--) { sr1 = top.altlines_math[i][0]; cr1 = top.altlines_math[i][1]; sd1 = top.altlines_math[i][2]; cd1 = top.altlines_math[i][3]; sr2 = top.altlines_math[i][4]; cr2 = top.altlines_math[i][5]; sd2 = top.altlines_math[i][6]; cd2 = top.altlines_math[i][7]; ez1 = cd*cd1*(cr*cr1 + sr*sr1) + sd*sd1; ez2 = cd*cd2*(cr*cr2 + sr*sr2) + sd*sd2; if (ez1 > 0.1 && ez2 > 0.1) { ey1 = -sd*cd1*(cr*cr1 + sr*sr1) + cd*sd1; ey2 = -sd*cd2*(cr*cr2 + sr*sr2) + cd*sd2; ex1 = -cd1*(sr*cr1 - cr*sr1); ex2 = -cd2*(sr*cr2 - cr*sr2); x1 = xmid + (int)(-dist * ex1/ez1); x2 = xmid + (int)(-dist * ex2/ez2); y1 = ymid + (int)(-dist * ey1/ez1); y2 = ymid + (int)(-dist * ey2/ez2); g.drawLine(x1,y1,x2,y2); } } } protected void paintSky(Graphics g) { // pre-clear the bitmap or the applet // remove this if you paint the entire area anyway d = size(); xmid = d.width/2; ymid = d.height/2; g.setColor(Color.black); g.fillRect(0, 0, d.width, d.height); if (top.uselines) if (top.usealtlines) paintAltLines(g); else paintLines(g); if (top.usenames) paintNames(g); paintStars(g); } public void paint(Graphics g) { if (doubleBuffer) { paintSky(top.gBuf); g.drawImage(top.buf, 0, 0, this); } else { paintSky(g); } } public void update(Graphics g) { // override this because the default implementation always // calls clearRect first, causing unwanted flicker paint(g); } public boolean mouseDown(Event evt, int x, int y) { Dimension d; double ra, dec, dy, dx, r, rr; double dist, ex, ey, ez; double cr, sr, cd, sd; int rah,ram,ras,decd,decm,decs,xmid,ymid; String sign; Graphics g = this.getGraphics(); d = size(); xmid = d.width/2; ymid = d.height/2; dist = (double)Math.min(xmid,ymid)/Math.tan(top.angle/2.0); dx = -(double)(x-xmid); dy = -(double)(y-ymid); sr = Math.sin(top.ra); cr = Math.cos(top.ra); sd = Math.sin(top.dec); cd = Math.cos(top.dec); r = Math.sqrt(dx*dx + dy*dy + dist*dist); ex = ( dx*cr - dy*sd*sr + dist*cd*sr)/r; ey = ( dy*cd + dist*sd )/r; ez = (-dx*sr - dy*cr*sd + dist*cd*cr)/r; rr = Math.sqrt(ex*ex + ez*ez); ra = Math.atan2(ex,ez)*12.0/Math.PI; dec = Math.atan2(ey,rr)*180.0/Math.PI; if (ra < rah =" (int)ra;" ra =" (ra-(double)rah)*60.0;" ram =" (int)ra;" ra =" (ra-(double)ram)*60.0;" ras =" (int)ra;" sign = "-" dec =" -dec;" sign = "+" decd =" (int)dec;" dec =" (dec-(double)decd)*60.0;" decm =" (int)dec;" dec =" (dec-(double)decm)*60.0;" decs =" (int)dec;" g =" this.getGraphics();">